b9716a7cd6
Snapshot de WIP acumulado de sesiones previas antes de merge wave 1 del flow 0008 (kanban_cpp + agent_runner_api + DoD schema). Incluye: - dev/flows/0008-kanban-cpp-and-agent-workflows.md - dev/issues/0112-0119*.md (7 sub-issues) - WIP previo en cmd/fn/doctor.go, registry/*, modules/, cpp/, etc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
210 lines
12 KiB
Markdown
210 lines
12 KiB
Markdown
---
|
|
id: "0107g"
|
|
title: "Migrar inline ImGui::BeginTable a data_table::render en apps con tablas de datos reales"
|
|
status: en-progreso
|
|
type: refactor
|
|
domain:
|
|
- cpp-stack
|
|
- meta
|
|
scope: multi-app
|
|
priority: media
|
|
depends:
|
|
- "0107c"
|
|
blocks: []
|
|
related:
|
|
- "0107"
|
|
created: 2026-05-17
|
|
updated: 2026-05-17
|
|
tags: [modules, data-table, drift, audit, inline-begintable]
|
|
---
|
|
|
|
# 0107g — Migrar inline BeginTable a `data_table::render` (data tables reales)
|
|
|
|
Parte del issue principal [0107](0107-modules-standardization.md). Detectado por `audit_data_table_usage_go_infra` (output en `dev/data_table_integration_audit.md`).
|
|
|
|
## Problema
|
|
|
|
Audit automatico identifica ~12 hits de `ImGui::BeginTable` inline en apps que YA declaran `uses_modules: [data_table_cpp]`. Mezcla legitimos + bugs:
|
|
|
|
- **Legitimos** (NO migrar): KPI grids, schema forms k/v, layout 2-col splitters, chart grids. NO son tablas de datos.
|
|
- **Bugs reales**: tablas de datos con filas dinamicas + sort/filter potencial que reinventan logica que el modulo provee.
|
|
|
|
Resultado: codigo duplicado, comportamiento inconsistente, color/badge/sort/filter "casi-pero-no" igual entre apps. Conexiones raras: cada app personaliza su tabla a mano.
|
|
|
|
## Decision
|
|
|
|
Migrar los hits identificados como bugs reales a `data_table::render`. Dejar los legitimos como excepciones documentadas en `docs/MODULES_API.md::Cuando usar data_table::render vs BeginTable directo`.
|
|
|
|
## Tabla de migracion
|
|
|
|
| App | Path | Linea | Es bug? | Accion |
|
|
|---|---|---|---|---|
|
|
| dag_engine_ui | apps/dag_engine_ui/tabs.cpp | 382 | **BUG** (`##dt_run_steps`, 6 cols, scroll Y, runs dinamicas) | Migrar |
|
|
| dag_engine_ui | apps/dag_engine_ui/tabs.cpp | 731 | LEGITIMO (`##health_kpis`, 4 cols stretch same, KPI grid) | Dejar + comentar |
|
|
| navegator_dashboard | projects/navegator/apps/navegator_dashboard/autoextract_panel.cpp | 528 | **BUG** (`##ax_schema`, 5 cols, filas dinamicas schema) | Migrar |
|
|
| navegator_dashboard | projects/navegator/apps/navegator_dashboard/recipes_panel.cpp | 238 | **BUG** (`##recipes_tbl`, 6 cols, filas dinamicas recipes) | Migrar |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/extract_panel.cpp | 981 | **BUG** (`##ents`, 5 cols, filas dinamicas entities) | Migrar |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/extract_panel.cpp | 1027 | **BUG** (`##rels`, 5 cols, filas dinamicas relations) | Migrar |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/main.cpp | 1127 | LEGITIMO (`##enr_params`, form k/v) | Dejar + comentar |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 885 | LEGITIMO (`##insp_id`, inspector form k/v) | Dejar + comentar |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 958 | LEGITIMO (`##insp_fields`, inspector form) | Dejar + comentar |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 1546 | INFO comment, ya migrado a data_table::render | Ignorar (es comentario) |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 1854 | **BUG** (`##te_rows`, col_count dinamico, data table type explorer) | Migrar (segunda fase de la migration ya empezada) |
|
|
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 2292 | DISCUTIBLE (`##te_fields`, 5 cols, fields de un tipo — semi-dinamico) | Evaluar; si rows >20 migrar, sino dejar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 380 | LEGITIMO (`##kpi_grid`, KPI cards) | Dejar + comentar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 436 | LEGITIMO (`##chart_grid`, plots grid) | Dejar + comentar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 648 | LEGITIMO (`##monitor_kpi`, KPI cards) | Dejar + comentar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 1110 | LEGITIMO (`##proj_layout`, 2-col splitter) | Dejar + comentar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 1448 | LEGITIMO (`##explorer_layout`, 2-col splitter) | Dejar + comentar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/work_tab.cpp | 239 | **BUG** (`##flows_work`, 8 cols, filas dinamicas flows) | Migrar |
|
|
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/work_tab.cpp | 272 | **BUG** (`##top_issues_work`, 7 cols, filas dinamicas issues) | Migrar |
|
|
| app_gestion | apps/app_gestion/main.cpp | 722 | DISCUTIBLE (`##linked_tbl`, 4 cols, lista de modulos linked — semi-dinamico, rows <20) | Evaluar; bias a dejar como esta |
|
|
|
|
**Total a migrar (BUGs): 8 tablas en 4 apps.**
|
|
**Total LEGITIMOS (dejar + comentar): 9.**
|
|
**Total DISCUTIBLES: 2 — decision contextual.**
|
|
**Total comentarios/already-migrated: 1.**
|
|
|
|
## Tareas
|
|
|
|
- [x] **1.1** Migrar `dag_engine_ui/tabs.cpp:382` (`##dt_run_steps`) → `data_table::render`. HECHO. Function col → Button action="open_fn"; Status → CategoricalChip; Duration → Duration renderer.
|
|
- [~] **1.2** Migrar `navegator_dashboard/autoextract_panel.cpp:528` (`##ax_schema`). ABORTADO: form editor con InputText/Checkbox editables inline en cada fila (field, selector, type, keep). data_table::render no soporta CellEdit como InputText inline. Comentado con LAYOUT-TABLE.
|
|
- [x] **1.3** Migrar `navegator_dashboard/recipes_panel.cpp:238` (`##recipes_tbl`). HECHO. Patron B: 4 columnas Button (run/edit/delete/open_df) + ev.row para indexar yaml_path. last_status → CategoricalChip.
|
|
- [~] **1.4** Migrar `graph_explorer/extract_panel.cpp:981` (`##ents`). ABORTADO: form editor con InputText editables por fila (type_buf, name_buf) + Checkbox "sel". Mutacion directa de structs entities[i]. No mapeable a data_table. Comentado con LAYOUT-TABLE.
|
|
- [~] **1.5** Migrar `graph_explorer/extract_panel.cpp:1027` (`##rels`). ABORTADO: form editor con Checkbox "sel" + inmutabilidad necesaria (relations[i].selected se muta inline). Comentado con LAYOUT-TABLE.
|
|
- [~] **1.6** Migrar `graph_explorer/views.cpp:1854` (`##te_rows`). ABORTADO: interactividad app-específica no mapeable — Selectable + single-click ramificado por estado de promocion (promoted/unpromoted), dblclick promote-flow, PopupContextItem con promote/demote/focus condicionales, SmallButton Promote-out-of-group, paginacion manual. Equivalente exact en data_table events no existe. Comentado explicando razon.
|
|
- [x] **1.7** Migrar `registry_dashboard/work_tab.cpp:239` (`##flows_work`). HECHO. 8 cols. Status + Risk → CategoricalChip. BeginChild host 220px.
|
|
- [x] **1.8** Migrar `registry_dashboard/work_tab.cpp:272` (`##top_issues_work`). HECHO. 7 cols. Status + Deps + Prio → CategoricalChip. Deps string "-"/"OK"/"blocked" preserva logica de color original. BeginChild host -1.
|
|
- [x] **2.1** Anadir comentario `// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.` encima de los 9 hits LEGITIMOS para que `audit_data_table_usage` los excluya en proximas pasadas. HECHO en: ##health_kpis, ##enr_params, ##insp_id, ##insp_fields, ##kpi_grid, ##chart_grid, ##monitor_kpi, ##proj_layout, ##explorer_layout. Los 3 ABORTADOS tambien comentados con razon tecnica.
|
|
- [ ] **2.2** Actualizar `audit_data_table_usage_go_infra` para leer ese comentario y filtrar `[warn] -> [ignored:declared_layout_table]`.
|
|
- [x] **3.1** Decidir los 2 DISCUTIBLES (`te_fields`, `linked_tbl`) con criterio "si rows pueden crecer > 50, migrar". Decision: DEJAR. `te_fields` max ~20 fields por tipo; `linked_tbl` max ~10 modules linked. Rows no escalan.
|
|
- [x] **4.1** Envolver TODAS las llamadas a `data_table::render` en `ImGui::BeginChild` host. HECHO en las 3 tablas migradas: flows_work (220px), top_issues_work (-1), recipes_tbl (300px), dt_run_steps (usa el BeginChild preexistente ##run_steps_wrap).
|
|
- [ ] **5.1** Re-ejecutar audit:
|
|
```
|
|
./fn run audit_data_table_usage
|
|
```
|
|
Verificar: 0 BUG hits, 9 LEGITIMOS comentados, 11 `no_child_host` resueltos o documentados como excepcion.
|
|
- [x] **5.2** Build de las 4 apps modificadas. HECHO: dag_engine_ui, registry_dashboard, graph_explorer compilan OK (Linux). navegator_dashboard es Windows-only (CMakeLists.txt retorna en non-WIN32); sintaxis verificada via g++ -fsyntax-only sin errores.
|
|
|
|
## Patrones de migracion canonicos
|
|
|
|
### Patron A: ImGui::BeginTable inline → data_table::render basico
|
|
|
|
```cpp
|
|
// ANTES
|
|
if (ImGui::BeginTable("##recipes_tbl", 6, flags)) {
|
|
ImGui::TableSetupColumn("name");
|
|
ImGui::TableSetupColumn("url_pattern");
|
|
// ...
|
|
for (const auto& r : recipes) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn(); ImGui::TextUnformatted(r.name.c_str());
|
|
ImGui::TableNextColumn(); ImGui::TextUnformatted(r.url_pattern.c_str());
|
|
// ...
|
|
}
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
// DESPUES
|
|
static data_table::State g_st_recipes;
|
|
static std::vector<std::string> g_back_recipes; // backing
|
|
static std::vector<const char*> g_ptrs_recipes; // ptrs row-major
|
|
|
|
g_back_recipes.clear();
|
|
for (const auto& r : recipes) {
|
|
g_back_recipes.push_back(r.name);
|
|
g_back_recipes.push_back(r.url_pattern);
|
|
// ... resto cols
|
|
}
|
|
g_ptrs_recipes.clear();
|
|
for (auto& s : g_back_recipes) g_ptrs_recipes.push_back(s.c_str());
|
|
|
|
data_table::TableInput tbl;
|
|
tbl.name = "recipes";
|
|
tbl.headers = {"name", "url_pattern", "last_status", "last_at", "tries", "ok"};
|
|
tbl.types = {data_table::ColumnType::String, data_table::ColumnType::String,
|
|
data_table::ColumnType::String, data_table::ColumnType::Date,
|
|
data_table::ColumnType::Int, data_table::ColumnType::Bool};
|
|
tbl.cells = g_ptrs_recipes.data();
|
|
tbl.rows = (int)recipes.size();
|
|
tbl.cols = 6;
|
|
|
|
// Status como CategoricalChip (ganancia inmediata sobre BeginTable)
|
|
tbl.column_specs.resize(tbl.cols);
|
|
for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i];
|
|
tbl.column_specs[2].renderer = data_table::CellRenderer::CategoricalChip;
|
|
tbl.column_specs[2].chips = {{"ok","#22c55e"},{"error","#ef4444"},{"pending","#a3a3a3"}};
|
|
|
|
std::vector<data_table::TableEvent> events;
|
|
ImGui::BeginChild("##recipes_host", ImVec2(-1, -1));
|
|
data_table::render("##recipes_dt", {tbl}, g_st_recipes, &events);
|
|
ImGui::EndChild();
|
|
|
|
for (const auto& ev : events) {
|
|
if (ev.kind == data_table::TableEventKind::RowDoubleClick) {
|
|
open_recipe_detail(ev.row);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Patron B: BeginTable inline con interactividad (boton por fila)
|
|
|
|
Si la BeginTable inline tiene un boton "Delete" / "Edit" por fila → migrar usando `CellRenderer::Button` + `action_id`:
|
|
|
|
```cpp
|
|
// ANTES
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::SmallButton(("Delete##" + r.id).c_str())) { delete_recipe(r.id); }
|
|
|
|
// DESPUES — anadir columna actions con button renderer
|
|
tbl.headers.push_back("actions");
|
|
tbl.types.push_back(data_table::ColumnType::String);
|
|
data_table::ColumnSpec actions_spec;
|
|
actions_spec.id = "actions";
|
|
actions_spec.renderer = data_table::CellRenderer::Button;
|
|
actions_spec.button_action = "delete_recipe";
|
|
actions_spec.button_label = "Delete";
|
|
actions_spec.button_color_hex = "#ef4444";
|
|
tbl.column_specs.push_back(actions_spec);
|
|
tbl.cols++;
|
|
|
|
// Backing extra
|
|
for (const auto& r : recipes) {
|
|
g_back_recipes.push_back(r.id); // celda actions = el id (consumido en ev.value)
|
|
}
|
|
|
|
// Handler
|
|
for (const auto& ev : events) {
|
|
if (ev.kind == data_table::TableEventKind::ButtonClick && ev.action_id == "delete_recipe") {
|
|
delete_recipe(ev.value); // ev.value == r.id de la fila clicada
|
|
}
|
|
}
|
|
```
|
|
|
|
## Riesgos
|
|
|
|
- **Backing storage**: las apps deben mantener `std::vector<std::string>` (estable) + `std::vector<const char*>` (ptrs row-major). Helper `cells_to_ptrs()` ya esta usado en data_factory — generalizar como `cpp/functions/core/cells_to_ptrs.cpp` si patron se repite >2 veces (ya pasa).
|
|
- **State persistente**: cada migracion requiere `static data_table::State g_st_<name>;`. Si la app tiene N tablas, N states.
|
|
- **Comportamiento sutil**: filter/sort/freeze ahora son user-toggle, no controlados por la app. La app pierde control fino, pero gana consistencia.
|
|
|
|
## Bonus: nueva funcion del registry `cells_to_ptrs_cpp_core`
|
|
|
|
Patron `g_back + g_ptrs` aparece en data_factory + (post 0107g) en 4 apps mas. Promover a funcion del registry:
|
|
|
|
```cpp
|
|
// cpp/functions/core/cells_to_ptrs.h
|
|
namespace fn {
|
|
// Converts a row-major flat vector<string> to a row-major vector<const char*>
|
|
// pointing into the backing storage. Stable pointers — backing must not be
|
|
// resized while ptrs are in use.
|
|
void cells_to_ptrs(const std::vector<std::string>& back,
|
|
std::vector<const char*>& ptrs);
|
|
}
|
|
```
|
|
|
|
Issue separado o sub-task de 0107g segun apetito.
|
|
|
|
## Notas
|
|
|
|
- El audit `audit_data_table_usage_go_infra` ya existe (FRESH 7d). Se referencia desde `fn doctor modules` (0107a) para mostrar drift en CI/dashboard.
|