Bugs corregidos: - Bug 1: Navegación teclado - cambio de keyPressed() a navKeyPressed() - Bug 2: Sorting real - implementado sortRows() con bubble sort estable Funcionalidades añadidas de Table: - Multi-row selection (bit array 1024 rows, Ctrl+click, Shift+click, Ctrl+A) - Incremental search (type-to-search con timeout 1000ms) - Cell validation tracking (256 celdas con mensajes de error) Nuevas funciones en AdvancedTableState: - isRowSelected, addRowToSelection, removeRowFromSelection - toggleRowSelection, clearRowSelection, selectAllRows - selectRowRange, getSelectedRowCount, getSelectedRows, selectSingleRow - addSearchChar, getSearchTerm, clearSearch, startsWithIgnoreCase - hasCellError, addCellError, clearCellError, clearAllCellErrors - hasAnyCellErrors, getLastValidationMessage Cambios en types.zig: - CellValue.compare() para ordenación - allow_multi_select en TableConfig Tests: 379 passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
10 KiB
AdvancedTable - Plan de Mejoras y Corrección de Bugs
Fecha: 2025-12-17 Estado: ✅ COMPLETADO Consensuado: Sí
Contexto
Primera prueba real de AdvancedTable en zsimifactu (WHO panel) reveló 2 bugs críticos. Además, el widget Table existente tiene funcionalidades que AdvancedTable debería incorporar.
Decisión: Incorporar funcionalidades de Table mientras corregimos los bugs.
Bugs Detectados
| # | Bug | Severidad | Descripción |
|---|---|---|---|
| 1 | Navegación teclado | ALTA | Flechas ↑↓←→ no mueven selección |
| 2 | Sorting | MEDIA | Click en header no reordena filas (solo cambia estado visual) |
Funcionalidades a Incorporar de Table
Fase A: Multi-Select + Search (~160 LOC)
Origen: src/widgets/table/state.zig, src/widgets/table/keyboard.zig
A1. Multi-Row Selection
// Añadir a AdvancedTableState
selected_rows: [128]u8 = [_]u8{0} ** 128, // Bit array (1024 rows)
selection_anchor: i32 = -1, // Para Shift+click
// Funciones a añadir
pub fn isRowSelected(row: usize) bool
pub fn addRowToSelection(row: usize) void
pub fn removeRowFromSelection(row: usize) void
pub fn toggleRowSelection(row: usize) void
pub fn clearRowSelection() void
pub fn selectAllRows() void
pub fn selectRowRange(from: usize, to: usize) void
pub fn getSelectedRowCount() usize
pub fn getSelectedRows(buffer: []usize) usize
Keyboard:
- Ctrl+click → toggle individual row
- Shift+click → select range from anchor
- Ctrl+A → select all (si config.allow_multi_select)
LOC estimadas: ~90
A2. Incremental Search (Type-to-Search)
// Añadir a AdvancedTableState
search_buffer: [64]u8 = [_]u8{0} ** 64,
search_len: usize = 0,
search_last_time: u64 = 0,
search_timeout_ms: u64 = 1000, // Reset después de 1s
// Funciones a añadir
pub fn addSearchChar(char: u8, current_time: u64) []const u8
pub fn getSearchTerm() []const u8
pub fn clearSearch() void
Helper:
// En advanced_table.zig o utils
pub fn startsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool
Comportamiento:
- Usuario teclea sin Ctrl/Alt → acumula en search_buffer
- Salta a primera fila cuya columna 0 empieza con el término
- Timeout 1s → reset buffer
LOC estimadas: ~70
Fase B: Cell Validation (~80 LOC)
Origen: src/widgets/table/state.zig
// Añadir a AdvancedTableState
validation_errors: [256]u32 = [_]u32{0xFFFFFFFF} ** 256, // cell IDs
validation_error_count: usize = 0,
last_validation_message: [128]u8 = [_]u8{0} ** 128,
last_validation_message_len: usize = 0,
// Funciones a añadir
pub fn hasCellError(row: usize, col: usize) bool
pub fn addCellError(row: usize, col: usize, message: []const u8) void
pub fn clearCellError(row: usize, col: usize) void
pub fn clearAllCellErrors() void
pub fn hasAnyCellErrors() bool
pub fn getLastValidationMessage() []const u8
Visual:
- Celdas con error: borde rojo + fondo rojizo
LOC estimadas: ~80
Plan de Implementación
Paso 1: Fase A1 - Multi-Row Selection
Archivos a modificar:
src/widgets/advanced_table/state.zig- Añadir campos y funcionessrc/widgets/advanced_table/advanced_table.zig- Keyboard handlingsrc/widgets/advanced_table/types.zig- Añadir config.allow_multi_select
Tests a añadir:
test "AdvancedTableState multi-row selection"
Paso 2: Fase A2 - Incremental Search
Archivos a modificar:
src/widgets/advanced_table/state.zig- Añadir campos y funcionessrc/widgets/advanced_table/advanced_table.zig- handleKeyboard
Tests a añadir:
test "AdvancedTableState incremental search"test "startsWithIgnoreCase"
Paso 3: Bug 1 - Navegación Teclado
Diagnóstico durante Fase A: Al incorporar el código de keyboard handling de Table, comparar línea por línea con AdvancedTable para identificar por qué no funciona.
Posibles causas:
has_focusno es true cuando deberíactx.input.keyPressed()no detecta las teclasconfig.keyboard_naves falsehandleKeyboard()no se está llamando
Verificación:
// Debug temporal
if (has_focus) {
std.debug.print("AdvancedTable has_focus=true, keyboard_nav={}\n", .{config.keyboard_nav});
}
Paso 4: Bug 2 - Sorting Real
Problema: toggleSort() solo cambia sort_column y sort_direction pero NO reordena state.rows.
Solución:
// En advanced_table.zig, después de toggleSort()
if (result.sort_changed) {
sortRows(table_state, table_schema, result.sort_column.?, result.sort_direction);
}
fn sortRows(
state: *AdvancedTableState,
schema: *const TableSchema,
col_idx: usize,
direction: SortDirection,
) void {
if (direction == .none) {
// Restaurar orden original si guardado
return;
}
const col_name = schema.columns[col_idx].name;
// Sort using std.mem.sort with comparator
std.mem.sort(Row, state.rows.items, .{}, struct {
fn lessThan(context: anytype, a: Row, b: Row) bool {
const val_a = a.get(context.col_name);
const val_b = b.get(context.col_name);
return val_a.compare(val_b) < 0;
}
}.lessThan);
// Si descending, invertir
if (direction == .descending) {
std.mem.reverse(Row, state.rows.items);
}
}
Nota: Hay que sincronizar los state maps (dirty_rows, new_rows, etc.) después del sort.
Paso 5: Fase B - Cell Validation (Opcional)
Implementar si hay tiempo después de resolver bugs.
Orden de Ejecución
┌─────────────────────────────────────────┐
│ 1. Fase A1: Multi-Row Selection │
│ - Copiar código de table/state.zig │
│ - Adaptar a AdvancedTableState │
│ - Añadir keyboard handling │
├─────────────────────────────────────────┤
│ 2. Fase A2: Incremental Search │
│ - Copiar código de table/state.zig │
│ - Añadir startsWithIgnoreCase │
│ - Integrar en handleKeyboard │
├─────────────────────────────────────────┤
│ 3. Bug 1: Diagnosticar Teclado │
│ - Comparar con Table funcional │
│ - Añadir debug prints │
│ - Identificar causa raíz │
├─────────────────────────────────────────┤
│ 4. Bug 2: Sorting Real │
│ - Implementar sortRows() │
│ - Sincronizar state maps │
│ - Guardar/restaurar orden original │
├─────────────────────────────────────────┤
│ 5. Tests y Verificación │
│ - Todos los tests pasan │
│ - Probar en zsimifactu │
└─────────────────────────────────────────┘
Estimación
| Tarea | LOC | Tiempo estimado |
|---|---|---|
| Fase A1 (multi-select) | ~90 | - |
| Fase A2 (search) | ~70 | - |
| Bug 1 (teclado) | ~20 | - |
| Bug 2 (sorting) | ~60 | - |
| Tests | ~40 | - |
| Total | ~280 | - |
Criterios de Éxito
- ✅ Flechas ↑↓←→ mueven selección en AdvancedTable
- ✅ Click en header ordena las filas visualmente
- ✅ Ctrl+click selecciona múltiples filas
- ✅ Shift+click selecciona rango
- ✅ Ctrl+A selecciona todas las filas
- ✅ Teclear busca en primera columna
- ✅ Todos los tests pasan
- ✅ zsimifactu WHO panel funciona correctamente
Resultados de Implementación
Bug 1: Navegación teclado - ✅ CORREGIDO
Causa raíz: AdvancedTable usaba ctx.input.keyPressed() que solo detecta el primer press, no key repeats. Table usa ctx.input.navKeyPressed() que incluye key repeats.
Solución: Cambiar handleKeyboard() para usar navKeyPressed() con switch exhaustivo.
Bug 2: Sorting real - ✅ CORREGIDO
Causa raíz: toggleSort() solo cambiaba el estado visual pero no reordenaba state.rows.
Solución: Implementar función sortRows() con bubble sort estable + sincronización de state maps.
Fase A1: Multi-Row Selection - ✅ IMPLEMENTADO
Añadido a AdvancedTableState:
selected_rows: [128]u8- bit array para 1024 filasselection_anchor: i32- para Shift+click- Funciones:
isRowSelected,addRowToSelection,removeRowFromSelection,toggleRowSelection,clearRowSelection,selectAllRows,selectRowRange,getSelectedRowCount,getSelectedRows,selectSingleRow
Fase A2: Incremental Search - ✅ IMPLEMENTADO
Añadido a AdvancedTableState:
search_buffer: [64]u8- buffer de búsquedasearch_len,search_last_time,search_timeout_ms(1000ms)- Funciones:
addSearchChar,getSearchTerm,clearSearch - Helper:
startsWithIgnoreCase()
Fase B: Cell Validation - ✅ IMPLEMENTADO
Añadido a AdvancedTableState:
cell_validation_errors: [256]u32- cell IDs con errorcell_validation_error_count,last_validation_message- Funciones:
hasCellError,addCellError,clearCellError,clearAllCellErrors,hasAnyCellErrors,getLastValidationMessage
Tests añadidos
test "AdvancedTableState multi-row selection"test "AdvancedTableState select row range"test "AdvancedTableState incremental search"test "AdvancedTableState cell validation"test "CellValue compare"test "startsWithIgnoreCase"
Total tests: 379 (todos pasan)
LOC añadidas
| Archivo | LOC |
|---|---|
state.zig |
~200 |
advanced_table.zig |
~100 |
types.zig |
~45 |
| Total | ~345 |
Plan creado por: Claude Code (Opus 4.5) Fecha: 2025-12-17 Estado: ✅ COMPLETADO