Files
fn_registry/dev/issues/completed/0107g-migrate-inline-begintable.md
egutierrez b9716a7cd6 chore: snapshot WIP previo + flow 0008 + 7 sub-issues (0112-0119)
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>
2026-05-18 18:17:08 +02:00

12 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0107g Migrar inline ImGui::BeginTable a data_table::render en apps con tablas de datos reales en-progreso refactor
cpp-stack
meta
multi-app media
0107c
0107
2026-05-17 2026-05-17
modules
data-table
drift
audit
inline-begintable

0107g — Migrar inline BeginTable a data_table::render (data tables reales)

Parte del issue principal 0107. 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

  • 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.
  • 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.
  • 1.7 Migrar registry_dashboard/work_tab.cpp:239 (##flows_work). HECHO. 8 cols. Status + Risk → CategoricalChip. BeginChild host 220px.
  • 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.
  • 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].
  • 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.
  • 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.
  • 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

// 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:

// 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/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.