chore: auto-commit (4 archivos)

- app.md
- appicon.ico
- views.cpp
- work_tab.cpp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 00:31:34 +02:00
parent ca436aa6cc
commit ff67e4e069
4 changed files with 143 additions and 72 deletions
+11 -12
View File
@@ -2,6 +2,7 @@
name: registry_dashboard name: registry_dashboard
lang: cpp lang: cpp
domain: tui domain: tui
version: 0.1.0
description: "Dashboard ImGui para visualizar el estado del fn_registry. Consume datos via sqlite_api HTTP (fallback a SQLite directo). KPIs, charts, tablas, desglose por lenguaje/dominio/pureza." description: "Dashboard ImGui para visualizar el estado del fn_registry. Consume datos via sqlite_api HTTP (fallback a SQLite directo). KPIs, charts, tablas, desglose por lenguaje/dominio/pureza."
tags: [dashboard, imgui, visualization, registry, http] tags: [dashboard, imgui, visualization, registry, http]
uses_functions: uses_functions:
@@ -11,18 +12,6 @@ uses_functions:
- pie_chart_cpp_viz - pie_chart_cpp_viz
- table_view_cpp_viz - table_view_cpp_viz
- sparkline_cpp_viz - sparkline_cpp_viz
# data_table stack (issue 0081-J)
- data_table_cpp_viz
- viz_render_cpp_viz
- compute_stage_cpp_core
- compute_pipeline_cpp_core
- tql_emit_cpp_core
- tql_apply_cpp_core
- tql_to_sql_cpp_core
- lua_engine_cpp_core
- join_tables_cpp_core
- auto_detect_type_cpp_core
- compute_column_stats_cpp_core
# core (dashboard primitives) # core (dashboard primitives)
- dashboard_panel_cpp_core - dashboard_panel_cpp_core
- dashboard_grid_cpp_core - dashboard_grid_cpp_core
@@ -214,3 +203,13 @@ Decisiones de scope:
- `fn_ui::app_menubar` reemplaza el item plano `Settings...` por un `BeginMenu("Settings")` con dos subitems: `Settings...` (existente) y `About...` (nuevo modulo `app_about_cpp_core`). El registry_dashboard cablea la info via `fn_ui::about_window_set_info("fn_registry Dashboard", "0.2.0", "Dashboard ImGui...")` antes de `fn::run_app`. - `fn_ui::app_menubar` reemplaza el item plano `Settings...` por un `BeginMenu("Settings")` con dos subitems: `Settings...` (existente) y `About...` (nuevo modulo `app_about_cpp_core`). El registry_dashboard cablea la info via `fn_ui::about_window_set_info("fn_registry Dashboard", "0.2.0", "Dashboard ImGui...")` antes de `fn::run_app`.
- Tabla `Apps` gana columna **Git**: `remote` si `repo_url` esta poblado en `apps.repo_url`, `local` si existe `<dir_path>/.git/`, `-` si nada. `AppRow` extendido con `repo_url` y `dir_path`; SELECT en `data.cpp` y `data_http.cpp` ampliado a 8 columnas. - Tabla `Apps` gana columna **Git**: `remote` si `repo_url` esta poblado en `apps.repo_url`, `local` si existe `<dir_path>/.git/`, `-` si nada. `AppRow` extendido con `repo_url` y `dir_path`; SELECT en `data.cpp` y `data_http.cpp` ampliado a 8 columnas.
- Build OK: `cmake --build build --target registry_dashboard` (Linux). La columna "Git" se ve sin reindexar. - Build OK: `cmake --build build --target registry_dashboard` (Linux). La columna "Git" se ve sin reindexar.
## Capability growth log
Una linea por bump SemVer. Bump-type segun `.claude/commands/version.md`:
- `major`: breaking observable (CLI args, schema BBDD propia, formato wire).
- `minor`: feature aditiva (nuevo panel, endpoint, opcion).
- `patch`: bugfix sin cambio observable.
- v0.1.0 (2026-05-18) — baseline.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+5
View File
@@ -377,6 +377,7 @@ void draw_kpi_row(const RegistryData& data) {
const float* spark_data = data.date_values.empty() ? nullptr : data.date_values.data(); const float* spark_data = data.date_values.empty() ? nullptr : data.date_values.data();
const int spark_count = static_cast<int>(data.date_values.size()); const int spark_count = static_cast<int>(data.date_values.size());
// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.
if (ImGui::BeginTable("##kpi_grid", 4, flags)) { if (ImGui::BeginTable("##kpi_grid", 4, flags)) {
struct KPI { const char* label; float value; const char* fmt; const char* icon; }; struct KPI { const char* label; float value; const char* fmt; const char* icon; };
const KPI cards[8] = { const KPI cards[8] = {
@@ -433,6 +434,7 @@ void draw_charts(RegistryData& data, float height) {
| ImGuiTableFlags_NoPadOuterX; | ImGuiTableFlags_NoPadOuterX;
const float plot_h = height - 48.0f; const float plot_h = height - 48.0f;
// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.
if (ImGui::BeginTable("##chart_grid", 4, flags)) { if (ImGui::BeginTable("##chart_grid", 4, flags)) {
ImGui::TableNextRow(); ImGui::TableNextRow();
@@ -644,6 +646,7 @@ void draw_monitor(RegistryData& data) {
// 7 KPI cards: Calls / MCP / Reg% / Errors / Violations / Copies / Versions // 7 KPI cards: Calls / MCP / Reg% / Errors / Violations / Copies / Versions
// "MCP" = calls Claude lanza via tools registry-aware (mcp / fn_cli_run / // "MCP" = calls Claude lanza via tools registry-aware (mcp / fn_cli_run /
// heredoc). "Reg %" = porcentaje del total con function_id no vacio. // heredoc). "Reg %" = porcentaje del total con function_id no vacio.
// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.
const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX; const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX;
if (ImGui::BeginTable("##monitor_kpi", 7, flags)) { if (ImGui::BeginTable("##monitor_kpi", 7, flags)) {
struct KPI { const char* label; float value; const char* icon; const char* fmt; }; struct KPI { const char* label; float value; const char* icon; const char* fmt; };
@@ -1105,6 +1108,7 @@ void draw_projects_list(RegistryData& data) {
} }
// Dos columnas: izquierda arbol, derecha detalle. // Dos columnas: izquierda arbol, derecha detalle.
// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.
const ImGuiTableFlags flags = ImGuiTableFlags_Resizable const ImGuiTableFlags flags = ImGuiTableFlags_Resizable
| ImGuiTableFlags_SizingStretchProp; | ImGuiTableFlags_SizingStretchProp;
if (!ImGui::BeginTable("##proj_layout", 2, flags)) return; if (!ImGui::BeginTable("##proj_layout", 2, flags)) return;
@@ -1443,6 +1447,7 @@ void draw_functions_explorer() {
return; return;
} }
// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.
const ImGuiTableFlags flags = ImGuiTableFlags_Resizable const ImGuiTableFlags flags = ImGuiTableFlags_Resizable
| ImGuiTableFlags_SizingStretchProp; | ImGuiTableFlags_SizingStretchProp;
if (!ImGui::BeginTable("##explorer_layout", 2, flags)) return; if (!ImGui::BeginTable("##explorer_layout", 2, flags)) return;
+126 -59
View File
@@ -8,6 +8,8 @@
#include "core/page_header.h" #include "core/page_header.h"
#include "core/empty_state.h" #include "core/empty_state.h"
#include "core/badge.h" #include "core/badge.h"
#include "data_table/data_table.h"
#include "core/data_table_types.h"
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include <chrono> #include <chrono>
@@ -231,37 +233,64 @@ void draw_work_tab() {
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
// Flows table // Flows table — migrado a data_table::render (issue 0107g)
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::text_muted); ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::text_muted);
ImGui::TextUnformatted("Flows"); ImGui::TextUnformatted("Flows");
ImGui::PopStyleColor(); ImGui::PopStyleColor();
if (ImGui::BeginTable("##flows_work", 8, {
ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | static data_table::State g_st_flows;
ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable)) { static std::vector<std::string> g_back_flows;
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 50.0f); static std::vector<const char*> g_ptrs_flows;
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Pattern", ImGuiTableColumnFlags_WidthFixed, 110.0f);
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 90.0f);
ImGui::TableSetupColumn("Risk", ImGuiTableColumnFlags_WidthFixed, 70.0f);
ImGui::TableSetupColumn("Accept", ImGuiTableColumnFlags_WidthFixed, 60.0f);
ImGui::TableSetupColumn("DoD", ImGuiTableColumnFlags_WidthFixed, 60.0f);
ImGui::TableSetupColumn("UserFace", ImGuiTableColumnFlags_WidthFixed, 70.0f);
ImGui::TableHeadersRow();
std::string buf; g_back_flows.clear();
for (auto& f : g_data.flows) { std::string tmp_buf;
ImGui::TableNextRow(); for (const auto& f : g_data.flows) {
ImGui::TableNextColumn(); ImGui::TextUnformatted(f.id.c_str()); g_back_flows.push_back(f.id);
ImGui::TableNextColumn(); ImGui::TextUnformatted(f.name.c_str()); g_back_flows.push_back(f.name);
ImGui::TableNextColumn(); ImGui::TextUnformatted(f.pattern.empty() ? "-" : f.pattern.c_str()); g_back_flows.push_back(f.pattern.empty() ? "-" : f.pattern);
ImGui::TableNextColumn(); ImGui::TextUnformatted(f.status.c_str()); g_back_flows.push_back(f.status);
ImGui::TableNextColumn(); ImGui::TextUnformatted(f.risk.c_str()); g_back_flows.push_back(f.risk);
ImGui::TableNextColumn(); ImGui::Text("%d%%", f.acceptance_pct); g_back_flows.push_back(std::to_string(f.acceptance_pct) + "%");
ImGui::TableNextColumn(); ImGui::Text("%d%%", f.dod_pct); g_back_flows.push_back(std::to_string(f.dod_pct) + "%");
ImGui::TableNextColumn(); ImGui::Text("%d%%", f.user_facing_pct); g_back_flows.push_back(std::to_string(f.user_facing_pct) + "%");
} }
ImGui::EndTable(); g_ptrs_flows.clear();
for (const auto& s : g_back_flows) g_ptrs_flows.push_back(s.c_str());
data_table::TableInput tbl;
tbl.name = "flows_work";
tbl.headers = {"ID", "Name", "Pattern", "Status", "Risk", "Accept", "DoD", "UserFace"};
tbl.types = {
data_table::ColumnType::String, data_table::ColumnType::String,
data_table::ColumnType::String, data_table::ColumnType::String,
data_table::ColumnType::String, data_table::ColumnType::String,
data_table::ColumnType::String, data_table::ColumnType::String,
};
tbl.cells = g_ptrs_flows.empty() ? nullptr : g_ptrs_flows.data();
tbl.rows = (int)g_data.flows.size();
tbl.cols = 8;
tbl.column_specs.resize(tbl.cols);
for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i];
// Status → CategoricalChip
tbl.column_specs[3].renderer = data_table::CellRenderer::CategoricalChip;
tbl.column_specs[3].chips = {
{"activo", "#22c55e"}, {"done", "#22c55e"},
{"in-progress","#f59e0b"}, {"bloqueado","#ef4444"},
{"pendiente", "#a3a3a3"},
};
// Risk → CategoricalChip
tbl.column_specs[4].renderer = data_table::CellRenderer::CategoricalChip;
tbl.column_specs[4].chips = {
{"alta", "#ef4444"}, {"high", "#ef4444"},
{"media", "#f59e0b"}, {"medium", "#f59e0b"},
{"baja", "#22c55e"}, {"low", "#22c55e"},
};
ImGui::BeginChild("##flows_work_host", ImVec2(-1, 220));
data_table::render("##flows_work_dt", {tbl}, g_st_flows, nullptr);
ImGui::EndChild();
} }
ImGui::Spacing(); ImGui::Spacing();
@@ -269,43 +298,81 @@ void draw_work_tab() {
ImGui::TextUnformatted("Top issues (priority alta, not done)"); ImGui::TextUnformatted("Top issues (priority alta, not done)");
ImGui::PopStyleColor(); ImGui::PopStyleColor();
if (ImGui::BeginTable("##top_issues_work", 7, // Top issues table — migrado a data_table::render (issue 0107g)
ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | // Nota: la columna Deps original tenia colores condicionales (verde/amber segun deps_resolved).
ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable)) { // Mapeado a CategoricalChip: "-" (gris), "OK" (verde), "blocked" (amber).
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 70.0f); {
ImGui::TableSetupColumn("Title"); static data_table::State g_st_issues;
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 80.0f); static std::vector<std::string> g_back_issues;
ImGui::TableSetupColumn("Domain", ImGuiTableColumnFlags_WidthFixed, 140.0f); static std::vector<const char*> g_ptrs_issues;
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 90.0f);
ImGui::TableSetupColumn("Deps", ImGuiTableColumnFlags_WidthFixed, 110.0f);
ImGui::TableSetupColumn("Prio", ImGuiTableColumnFlags_WidthFixed, 60.0f);
ImGui::TableHeadersRow();
std::string buf; g_back_issues.clear();
for (auto& i : g_data.top_issues) { std::string dom_buf;
ImGui::TableNextRow(); for (const auto& iss : g_data.top_issues) {
ImGui::TableNextColumn(); ImGui::TextUnformatted(i.id.c_str()); g_back_issues.push_back(iss.id);
ImGui::TableNextColumn(); ImGui::TextUnformatted(i.title.c_str()); g_back_issues.push_back(iss.title);
ImGui::TableNextColumn(); ImGui::TextUnformatted(i.type.c_str()); g_back_issues.push_back(iss.type);
ImGui::TableNextColumn(); ImGui::TextUnformatted(join_domain(i.domain, buf)); // domain: join con coma
ImGui::TableNextColumn(); ImGui::TextUnformatted(i.status.c_str()); dom_buf.clear();
ImGui::TableNextColumn(); for (size_t k = 0; k < iss.domain.size(); ++k) {
if (i.depends.empty()) { if (k) dom_buf += ",";
ImGui::TextUnformatted("-"); dom_buf += iss.domain[k];
} else if (i.deps_resolved) { }
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::success); g_back_issues.push_back(dom_buf);
ImGui::TextUnformatted("OK"); g_back_issues.push_back(iss.status);
ImGui::PopStyleColor(); // deps: mostrar "-" / "OK" / "blocked"
if (iss.depends.empty()) {
g_back_issues.push_back("-");
} else if (iss.deps_resolved) {
g_back_issues.push_back("OK");
} else { } else {
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::warning); g_back_issues.push_back("blocked");
ImGui::Text("blocked");
ImGui::PopStyleColor();
} }
ImGui::TableNextColumn(); g_back_issues.push_back(iss.priority);
ImGui::PushStyleColor(ImGuiCol_Text, prio_color(i.priority));
ImGui::TextUnformatted(i.priority.c_str());
ImGui::PopStyleColor();
} }
ImGui::EndTable(); g_ptrs_issues.clear();
for (const auto& s : g_back_issues) g_ptrs_issues.push_back(s.c_str());
data_table::TableInput tbl;
tbl.name = "top_issues_work";
tbl.headers = {"ID", "Title", "Type", "Domain", "Status", "Deps", "Prio"};
tbl.types = {
data_table::ColumnType::String, data_table::ColumnType::String,
data_table::ColumnType::String, data_table::ColumnType::String,
data_table::ColumnType::String, data_table::ColumnType::String,
data_table::ColumnType::String,
};
tbl.cells = g_ptrs_issues.empty() ? nullptr : g_ptrs_issues.data();
tbl.rows = (int)g_data.top_issues.size();
tbl.cols = 7;
tbl.column_specs.resize(tbl.cols);
for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i];
// Status → CategoricalChip
tbl.column_specs[4].renderer = data_table::CellRenderer::CategoricalChip;
tbl.column_specs[4].chips = {
{"pendiente", "#a3a3a3"},
{"in-progress", "#f59e0b"},
{"bloqueado", "#ef4444"},
{"completado", "#22c55e"},
};
// Deps → CategoricalChip (verde OK / amber blocked / gris -)
tbl.column_specs[5].renderer = data_table::CellRenderer::CategoricalChip;
tbl.column_specs[5].chips = {
{"OK", "#22c55e"},
{"blocked", "#f59e0b"},
{"-", "#a3a3a3"},
};
// Prio → CategoricalChip
tbl.column_specs[6].renderer = data_table::CellRenderer::CategoricalChip;
tbl.column_specs[6].chips = {
{"alta", "#ef4444"}, {"high", "#ef4444"},
{"media", "#f59e0b"}, {"medium", "#f59e0b"},
{"baja", "#22c55e"}, {"low", "#22c55e"},
};
ImGui::BeginChild("##top_issues_work_host", ImVec2(-1, -1));
data_table::render("##top_issues_work_dt", {tbl}, g_st_issues, nullptr);
ImGui::EndChild();
} }
} }