- Extraer AdvancedTableResult a result.zig (73 LOC)
- Extraer funciones de map shifting a state_helpers.zig (141 LOC con tests)
- Reducir state.zig de 1235 LOC a 1123 LOC
- Añadir swapMapEntries para operaciones de move row
- Mantener re-exports para compatibilidad
- Remove injected_row_idx, injected_committed from VirtualAdvancedTableState
- Remove is_injected, injection_index from RowEditBuffer and RowCommitInfo
- Remove startInjectedEdit() method
- Simplify PagedDataSource (no injection offset)
- Simplify drawRowsWithDataSource (no injection handling)
- Add is_insertion_mode, insertion_session_active to State
- Add enterInsertionMode(), exitInsertionMode(), isInInsertionMode() methods
- Ctrl+N now emits insert_row_requested for Panel to handle
Part of Modo Inserción Cronológico implementation.
- PagedDataSource ahora se crea en ctx.frameAllocator()
- Memoria estable durante todo el frame de rendering
- Eliminado cached_paged_datasource del state (punteros auto-ref peligrosos)
- Fix secundario para estabilidad (no era causa del crash Ctrl+N)
Bug: Tab within injected row was triggering immediate row commit.
This happened because RowIdGetter.getRowId() returned the REAL row ID
for the injected row index, instead of NEW_ROW_ID.
Fix:
- RowIdGetter now takes injected_idx parameter
- Returns NEW_ROW_ID for injected row
- Adjusts indices for rows after injection (row-1)
- num_rows calculation now accounts for injection (+1)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When an injected row has been committed (injected_committed=true)
and scrolling moves it out of the visible area:
- Sets needs_reload=true in result
- Clears injection state
This triggers the panel to refetch data from DB, where the
new row will appear in its proper sorted position.
RowCommitInfo now includes:
- is_injected: bool (was this an injected row)
- injection_index: ?usize (where it was injected)
buildCommitInfo() copies these from RowEditBuffer.
VirtualAdvancedTable sets result.injection_committed and
result.injection_row_idx when committing an injected row,
for both Tab navigation and selection change commits.
This allows the panel to know when to INSERT vs UPDATE and
handle the special case of locally injected rows.
- Ctrl+N creates injected row below current selection
- Uses startInjection() to initialize injection state
- Automatically starts editing first column
- Updates selection to show injected row as selected
- Adjusts visible row count when injection is active
No longer emits insert_row_requested - injection is handled
internally by the widget.
When an injected row exists (Ctrl+N between lines):
- getRowCount(): adds 1 to total count
- getCellValueInto(): returns edit buffer values for injected row,
shifts indices for rows after injection
- getRowId(): returns injected_row_id for injected row,
shifts indices for rows after injection
This enables transparent rendering of injected rows without
modifying the drawing code.
State fields:
- injected_row_idx: visual index of injected row
- injected_row_id: ID after commit (NEW_ROW_ID until saved)
- injected_committed: true when saved to DB
Result fields:
- injection_committed: panel should INSERT
- injection_row_idx: where it was injected
- needs_reload: table should refetch from DB
Helper methods:
- hasInjection(), startInjection(), clearInjection()
- markInjectionCommitted(), isInjectionVisible()
- Insert at selected_row + 1 instead of at selected_row
- Use startInjectedEdit() for proper injection tracking
- Row appears below cursor, more intuitive UX
- Add is_injected: bool to distinguish injected rows
- Add injection_index: ?usize for insertion position
- Add startInjectedEdit() method for Ctrl+N between lines
- Update startEdit/clear to reset new fields
- Add row_edit_buffer to AdvancedTableState
- Connect edit_buffer to rendering via drawRowsWithDataSource
- Use DRY planTabNavigation for Tab navigation + auto-commit
- Add Excel-style result fields: row_committed, row_changes, etc.
- Both widgets now share identical commit-on-row-change behavior
- Add planTabNavigation() in table_core.zig: central function for Tab navigation with auto-commit
- Uses row_id comparison (not indices) to detect row changes - robust for virtual tables
- Returns TabAction enum: move, move_with_commit, exit, exit_with_commit
- Integrates in virtual_advanced_table.zig with RowIdGetter wrapper
- Removes obsolete tab_out commit logic
- Fix: Tab at end of ghost row now commits before wrap
🤖 Generated with Claude Code
Laravel-inspired color system for semantic panel coloring:
- Add blendTowards(target, percent) for color washing
- Add Laravel color constants (red, blue, green, amber, cyan, etc.)
- Add soft_black/soft_white for better aesthetics
- Add ThemeMode enum (dark/light)
- Add PanelColorScheme with 10 documented fields
- Implement derivePanelPalette(base, mode) to derive full palette
- deriveDarkPalette: 5% tint on dark background
- deriveLightPalette: 3% tint on light background
- Tests for blendTowards and derivePanelPalette
This enables deriving a complete 10-color panel palette from
a single base color, achieving Laravel Forge/Nova aesthetics.
- Añade table_tips[] con 8 tips de atajos
- Añade tip_index a VirtualAdvancedTableState
- Rota tip cada 600 frames (~10s @ 60fps)
- result.current_tip contiene tip a mostrar
- Solo muestra tips con focus y sin edición activa
- Añade selection_start/selection_end a CellEditState
- F2/Space selecciona todo el texto al entrar en edición
- Typing con selección reemplaza texto seleccionado
- Backspace/Delete borra selección si existe
- Flechas/Home/End limpian selección
- Dibuja highlight azul en ambas tablas (Advanced + Virtual)
- Añadido deleted_row_id: i64 a VirtualAdvancedTableResult
- handleKeyboard setea el ID de la fila seleccionada
- El panel puede usar esto para eliminar de BD
Arquitectura diseñada por Gemini:
- TODA la lógica de decisión en table_core.zig
- Widgets solo pasan eventos y reaccionan a flags
- Cualquier tabla nueva hereda automáticamente
Cambios:
- TableEventResult: struct con TODOS los flags de acciones
- processTableEvents(): función maestra que procesa teclado
- Soporta: navegación, CRUD (Ctrl+N/B/Del), ordenación (Ctrl+Shift+1-9), edición
- VirtualAdvancedTable refactorizado al patrón Brain-in-Core
- Nuevos campos result: insert_row_requested, delete_row_requested, sort_column_index
- Añadido dirty_row_id: ?i64 a DrawRowsConfig
- drawRowsWithDataSource aplica blendColor(state_modified, 0.25) si dirty
- VirtualAdvancedTable pasa row_edit_buffer.row_id cuando has_changes
- Añadido VerticalScrollbarParams y drawVerticalScrollbar
- Añadido HorizontalScrollbarParams y drawHorizontalScrollbar
- AdvancedTable y VirtualAdvancedTable ahora usan funciones unificadas
- Elimina duplicación de código de renderizado de scrollbars
- Add TableDataSource interface documentation
- Add adaptadores (MemoryDataSource, PagedDataSource)
- Add drawRowsWithDataSource function docs
- Update file structure with new datasource files
- Add section 'Cómo Crear un Nuevo Tipo de Tabla'
- Añadir RowState enum a table_core.zig
- Añadir getRowState a TableDataSource.VTable (opcional)
- Añadir colores de estado a RowRenderColors
- Añadir draw_row_borders a DrawRowsConfig
- Añadir getRowState a MemoryDataSource
- Nueva función handleRowClicks() separando input de rendering
- AdvancedTable usa drawRowsWithDataSource (sin bucle for propio)
- Eliminar drawRow() y drawStateIndicator() locales (~160 líneas)
Objetivo cumplido: un solo bloque de código para renderizar filas
Add unified row rendering function to table_core.zig that uses
TableDataSource interface for data access.
Changes:
- Add ColumnRenderDef, RowRenderColors, DrawRowsConfig types
- Add drawRowsWithDataSource() unified rendering function
- Update VirtualAdvancedTable.drawRows to use unified function
with PagedDataSource
Note: AdvancedTable not yet integrated due to additional complexity
(state indicators, click handling interleaved with drawing).
See PLAN_REFACTOR_TABLES_CONTINUIDAD.md for details.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add MemoryDataSource for AdvancedTable (in-memory ArrayList) and
PagedDataSource for VirtualAdvancedTable (paged DB data).
Both implement TableDataSource interface from table_core.zig,
enabling unified rendering patterns while respecting memory ownership.
New files:
- advanced_table/datasource.zig - MemoryDataSource
- virtual_advanced_table/paged_datasource.zig - PagedDataSource
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- TableDataSource: vtable interface for data abstraction
- getCellValueInto: buffer-based pattern to avoid memory issues
- getRowCount, getRowId: basic data access methods
- isCellEditable, invalidate: optional methods
- makeTableDataSource: helper to create from concrete types
- isGhostRow: convenience method for new row detection
This interface enables unified rendering for both memory and
paginated tables while enforcing safe memory patterns.
FASE 2 del refactor de tablas:
- AdvancedTableState: Embed cell_edit, delegate editing methods
- VirtualAdvancedTableState: Embed cell_edit, replace editing_cell/edit_buffer
- Update advanced_table.zig to use isEditing() and cell_edit.*
- Update virtual_advanced_table.zig to use getEditingCell()
- Update cell_editor.zig to use cell_edit.*
Reduces code duplication, centralizes editing logic in table_core
Document the Tab double-processing bug and its solution:
- Symptom: Row color alternating on Tab press
- Root cause: Tab processed twice (widget + app level)
- Solution: NavigateDirection + handled flag + app check
- General rule for keyboard handling in immediate-mode widgets
Also updated EditKeyboardResult API documentation with new fields.
- Check navigate_direction before setting tab_out in handleKeyboard
- Export NavigateDirection from virtual_advanced_table module
- Use cell_editor.NavigateDirection in VirtualAdvancedTableResult
This fixes the bug where Tab was processed twice: once by CellEditor
(for cell navigation) and again by handleKeyboard (for tab_out).
- Import and use table_core for keyboard handling
- Re-export NavigateDirection from table_core
- Map table_core.EditKeyboardResult to CellEditorResult
- Remove ~100 lines of duplicated keyboard handling code
- Add 'handled' field to CellEditorResult
- Add NavigateDirection enum (none, next_cell, prev_cell, next_row, prev_row)
- Replace navigate_next/navigate_prev bools with single navigate field
- Add 'handled' flag to prevent double processing of Tab
- Add Up/Down arrow handling for row navigation
- Use getKeyEvents() loop instead of keyPressed() for consistency
Complete documentation covering:
- When to use AdvancedTable vs VirtualAdvancedTable
- Architecture diagram (table_core.zig as single source)
- How to extend functionality (always in table_core.zig)
- Complete API reference for table_core.zig
- Code examples and model differences
Norma #6: Abstract to library level
- Tab navigation with wrap now in library, not application
- AdvancedTableState.tabToNextCell/tabToPrevCell for in-memory data
- VirtualAdvancedTableState.tabToNextCell/tabToPrevCell for paginated data
- DRY: same logic available in both table widgets