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>
This commit is contained in:
2026-05-18 18:17:08 +02:00
parent ddb5366884
commit b9716a7cd6
119 changed files with 14929 additions and 3084 deletions
+81 -4
View File
@@ -3,10 +3,10 @@ name: data_table
kind: function
lang: cpp
domain: viz
version: "1.4.0"
version: "1.5.0"
purity: impure
signature: "void data_table::render(const char* id, const std::vector<TableInput>& tables, State& st, std::vector<TableEvent>* events_out = nullptr, bool show_chrome = true)"
description: "Render UI completa de tabla TQL: chips bar, tabla, viz panels, column-stats inline, drill, color rules, joins, TQL editor, Ask AI, Button renderer, event sink (ButtonClick/RowDoubleClick/RowRightClick), tooltip per-cell, column_specs persisted in TQL. Dots renderer para sparkline-like de status (v1.3.0). CategoricalChip (dot izquierda + text, siempre visible) y ColorScale (gradient N-color en fondo de celda) en v1.4.0. Entry-point publica del stack data_table. Muta State segun interaccion del usuario."
description: "Render UI completa de tabla TQL: chips bar, tabla, viz panels, column-stats inline, drill, color rules, joins, TQL editor, Ask AI, Button renderer, event sink (ButtonClick/RowDoubleClick/RowRightClick), tooltip per-cell, column_specs persisted in TQL. Dots renderer para sparkline-like de status (v1.3.0). CategoricalChip + ColorScale en v1.4.0. v1.5.0: stats/seleccion/drill-history/row-inspector pasan a vivir en State (por tabla, antes eran globales — multiples tablas en pantalla compartian estado). Conditional color extendido con tres modos: CellBg (legacy), CategoricalDot (autopalette por valor) y NumericRange (gradiente N-color). Hover del menu contextual de header restaurado. Entry-point publica del stack data_table. Muta State segun interaccion del usuario."
tags: [tables, viz, ui, imgui, tql, cpp-tables]
uses_functions:
- compute_stage_cpp_core
@@ -40,8 +40,7 @@ uses_types:
- Aggregation_cpp_core
- SortClause_cpp_core
- ColumnType_cpp_core
- TableEvent_cpp_core
- TableEventKind_cpp_core
- ColorRule_cpp_core
returns: []
returns_optional: false
error_type: "error_go_core"
@@ -149,6 +148,82 @@ Cuando una app necesita tabla con filtros + agregaciones + viz + joins sobre dat
Usar `CategoricalChip` cuando quieras un indicador visual (dot de color) siempre visible a la izquierda del texto de la celda, para columnas categoricas (estado, tipo, severidad). Mas discreto que Badge y sin hover-only. Usar `ColorScale` cuando la columna sea numerica continua y quieras dar contexto visual de "alto/bajo/medio" con un fondo tintado proporcional al valor — util para latencias, scores, porcentajes, metricas.
## Per-table state (v1.5.0)
Antes de v1.5.0 los siguientes campos vivian en un singleton `UiState` interno y se aplicaban a TODAS las tablas a la vez. Desde v1.5.0 viven en `State` (parametro `st`), por tabla:
| Campo | Significado |
|---|---|
| `stats_mode`, `stats_cache`, `stats_last_*` | Toggle "Show stats" + cache de stats por col. |
| `sel_anchor_row/col`, `sel_end_row/col`, `sel_active`, `sel_dragging` | Seleccion rectangular drag para Ctrl+C (TSV). |
| `inspect_row`, `inspect_open` | Modal "Inspect row..." del menu contextual. |
| `drill_back`, `drill_forward` | Historial de drill back/forward. |
Estado modal (un solo popup abierto a la vez en toda la app) sigue en el singleton interno: edit-chip popups, addfilter/addbreakout/addagg/addsort, Ask AI, Custom column formula, TQL show/apply, drill popup, header context. Estos NO necesitan separacion por tabla porque ImGui solo permite un popup abierto a la vez.
## Conditional color rules (v1.5.0)
`State::color_rules` (tipo `std::vector<ColorRule>`) acepta tres modos via `ColorRule::kind` (enum `ColorRuleKind`):
| Modo | Cuando usar | Campos relevantes |
|---|---|---|
| `CellBg` (legacy) | Resaltar bg de celda completa cuando valor == `equals`. Un color por regla, multiples reglas por col. | `equals`, `color` |
| `CategoricalDot` | Categoricas con muchos valores distintos. Dibuja un dot circular a la izquierda del texto. Color autoasignado por hash (palette de 12 vibrant Tailwind-500) o por mapeo explicito `dot_map`. | `dot_alpha`, `dot_radius_px`, `dot_map` |
| `NumericRange` | Numericas continuas. Gradiente N-color sobre bg de celda. Valores fuera de `[range_min, range_max]` saturan en los stops extremos. | `range_min`, `range_max`, `range_alpha`, `range_stops` |
### Menu UI
`Right-click cabecera → Conditional color → [Cell bg / Categorical dot / Numeric range]`. La auto-deteccion preselecciona NumericRange para columnas Int/Float, CellBg para el resto. Cada Apply REEMPLAZA la regla existente del mismo `kind` en esa columna (puedes combinar las tres en la misma columna). "Clear col" borra todas las reglas de la columna.
### Ejemplo programatico
```cpp
data_table::State st;
// Cell bg cuando status == "error" -> rojo.
data_table::ColorRule r1;
r1.col = 3;
r1.kind = data_table::ColorRuleKind::CellBg;
r1.equals = "error";
r1.color = IM_COL32(220, 60, 60, 120);
st.color_rules.push_back(r1);
// Dot autoasignado por categoria de `region`.
data_table::ColorRule r2;
r2.col = 1;
r2.kind = data_table::ColorRuleKind::CategoricalDot;
r2.dot_alpha = 1.0f;
r2.dot_radius_px = 4.0f;
// (dot_map vacio -> autopalette por hash. Para mapeo fijo:)
// r2.dot_map = {{"EU","#3b82f6"},{"NA","#22c55e"},{"AS","#f59e0b"}};
st.color_rules.push_back(r2);
// Gradient verde→ambar→rojo sobre latency_ms en [0, 500].
data_table::ColorRule r3;
r3.col = 5;
r3.kind = data_table::ColorRuleKind::NumericRange;
r3.range_min = 0.0;
r3.range_max = 500.0;
r3.range_alpha = 0.25f;
r3.range_stops = {
{0.0f, "#22c55e"},
{0.5f, "#f59e0b"},
{1.0f, "#ef4444"},
};
st.color_rules.push_back(r3);
```
### CategoricalDot vs CategoricalChip renderer
Hay dos rutas para dots categoricos. Cuando elegir cada una:
| Mecanismo | Donde se define | Cuando |
|---|---|---|
| `ColorRule::CategoricalDot` (v1.5.0) | `State::color_rules` (ad-hoc por usuario via menu) | Exploracion interactiva: el usuario decide en runtime si pintar dots y con que palette. Autopalette por hash sin definir mapeo. |
| `CellRenderer::CategoricalChip` (v1.4.0) | `TableInput::column_specs[c]` (declarativo por el caller) | App quiere dots SIEMPRE para una columna especifica con mapeo explicito `chips: [{match, color}]`. |
Ambos son compatibles — pueden coexistir sin colision.
## Gotchas
- **ImGui + ImPlot context activos**: `render()` llama a APIs de ambas librerias. Llamar fuera de un frame activo causa UB.
@@ -206,5 +281,7 @@ v1.3.6 (2026-05-15) — Selection (drag-range) also paints via TableSetBgColor
v1.4.0 (2026-05-16) — new renderers: `CategoricalChip` (dot izquierda + text, always visible, replaces hover-only color-on-text for categorical) + `ColorScale` (continuous N-color LERP gradient for numeric cells, configurable `range_min`/`range_max`/`range_stops`/`range_alpha`). New types: `ChipRule{match,color}` + `ColorStop{position,color}` in `data_table_types.h`. TQL roundtrip (emit+apply) for both renderers. 4 headless tests added to `test_column_specs.cpp`.
v1.5.0 (2026-05-17) — per-table state isolation: `stats_mode`, `stats_cache`, `stats_last_*`, `sel_*`, `inspect_*`, `drill_back/forward` movidos de singleton `UiState` interno a `State`. Antes: toggle "Show stats" / drag-select / Inspect row se aplicaban a TODAS las tablas visibles a la vez. Ahora cada tabla los lleva. Conditional color extendido: nuevo enum `ColorRuleKind { CellBg, CategoricalDot, NumericRange }` + campos en `ColorRule` (`dot_alpha`, `dot_radius_px`, `dot_map`, `range_min/max/alpha/stops`). Menu del header autodetecta el modo por tipo de columna (Int/Float → NumericRange; resto → CellBg). CategoricalDot autoasigna colores desde palette de 12 hash-deterministico — no hace falta mapear cada valor a mano. Fix: hover del menu contextual del header invisible cuando las table-styles pisaban `HeaderHovered` a transparente — restaurado dentro del scope del popup. **Breaking en `data_table_types.h`**: ColStop + DrillStep movidos arriba para que State pueda contenerlos por valor; consumidores que redefinian `ColStats` localmente (playground `data_table_logic.h`) eliminados — usar el del registry (`compute_column_stats.h`, ya transitivamente incluido).
---
Promovido desde `cpp/apps/primitives_gallery/playground/tables/data_table.{h,cpp}` — issue 0081-H.