migrate Monitor Recent Executions + Failed Functions to data_table::render with Badge/Duration renderers (issue 0081-J)

- Replace ##monitor_recent BeginTable (6 cols) with data_table::render:
  - Duration renderer on duration_ms col (warn=500ms, error=2000ms)
  - Badge renderer on Status col: ok=#22c55e, error=#ef4444, running=#3b82f6, timeout=#f59e0b
  - success bool encoded as "ok"/"error" string for badge matching
- Replace ##monitor_failed_fns BeginTable (5 cols) with data_table::render:
  - Badge renderer on Error Class col: sqlite/network/timeout/not_found/auth/parse/io/permission/unknown
  - empty error_class normalized to "unknown" for badge match
- Add g_dt_monitor_recent + g_dt_monitor_failed persistent State members
- Remove all inline coloring helpers for these two tables (covered by renderers)
- fn doctor cpp-apps: registry_dashboard BeginTable inline count 7 -> 5
  (5 remaining are layout splitters: kpi_grid/chart_grid/monitor_kpi/proj_layout/explorer_layout)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 16:43:51 +02:00
parent 55a9c07e46
commit 3f8c12db89
+144 -97
View File
@@ -118,6 +118,8 @@ static data_table::State g_dt_vaults;
static data_table::State g_dt_top_fn;
static data_table::State g_dt_violations;
static data_table::State g_dt_copies;
static data_table::State g_dt_monitor_recent; // issue 0081-J
static data_table::State g_dt_monitor_failed; // issue 0081-J
// ---------------------------------------------------------------------------
// Helpers: build a data_table::TableInput from registry row vectors
@@ -769,68 +771,83 @@ void draw_monitor(RegistryData& data) {
if (cu.recent_executions.empty()) {
ImGui::TextDisabled("No executions in this window. Try widening (7d/30d/All) or wait for the next call.");
} else {
const ImGuiTableFlags tf = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders
| ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("##monitor_recent", 6, tf, ImVec2(0, 0))) {
ImGui::TableSetupColumn("When");
ImGui::TableSetupColumn("Function");
ImGui::TableSetupColumn("Tool");
ImGui::TableSetupColumn("ms");
ImGui::TableSetupColumn("OK");
ImGui::TableSetupColumn("Error");
ImGui::TableHeadersRow();
for (const auto& r : cu.recent_executions) {
if (g_recent_only_registry && r.function_id.empty()) continue;
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted(format_ts(r.ts).c_str());
ImGui::TableSetColumnIndex(1);
if (!r.function_id.empty()) {
// Call de registry — destacada en color normal.
ImGui::TextUnformatted(r.function_id.c_str());
} else if (!r.command_snippet.empty()) {
// Tool generica (Bash/heredoc): muestra prefijo `$` + snippet
// truncado en muted para distinguirla visualmente de calls registry.
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::text_muted);
// Truncado visual a ~80 chars para no romper layout.
char buf[88];
std::snprintf(buf, sizeof(buf), "$ %.80s%s",
r.command_snippet.c_str(),
r.command_snippet.size() > 80 ? "..." : "");
ImGui::TextUnformatted(buf);
// Hover tooltip con snippet completo (hasta 200 chars).
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(560.0f);
ImGui::TextUnformatted(r.command_snippet.c_str());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
ImGui::PopStyleColor();
} else {
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::text_muted);
ImGui::TextUnformatted("-");
ImGui::PopStyleColor();
}
ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted(r.tool_used.c_str());
ImGui::TableSetColumnIndex(3);
ImGui::Text("%d", r.duration_ms);
ImGui::TableSetColumnIndex(4);
if (r.success) {
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::success);
ImGui::TextUnformatted(TI_CHECK);
ImGui::PopStyleColor();
} else {
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::error);
ImGui::TextUnformatted(TI_X);
ImGui::PopStyleColor();
}
ImGui::TableSetColumnIndex(5);
ImGui::TextUnformatted(r.error_class.c_str());
// Build TableInput for data_table::render (issue 0081-J).
// Columns: When | Function | Tool | Duration(ms) | Status
// Status = "ok" | "error" (from r.success) → Badge renderer.
// Duration → Duration renderer (warn=500ms, error=2000ms).
data_table::TableInput ti;
ti.name = "monitor_recent";
ti.headers = {"When", "Function", "Tool", "Duration", "Status"};
ti.types = {
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::Float,
data_table::ColumnType::String,
};
// column_specs: parallel to headers (5 specs)
ti.column_specs.resize(5);
// col 0 When: Text (default)
ti.column_specs[0].renderer = data_table::CellRenderer::Text;
// col 1 Function: Text (default)
ti.column_specs[1].renderer = data_table::CellRenderer::Text;
// col 2 Tool: Text (default)
ti.column_specs[2].renderer = data_table::CellRenderer::Text;
// col 3 Duration: Duration renderer
ti.column_specs[3].renderer = data_table::CellRenderer::Duration;
ti.column_specs[3].duration_warn_ms = 500.0f;
ti.column_specs[3].duration_error_ms = 2000.0f;
// col 4 Status: Badge renderer
ti.column_specs[4].renderer = data_table::CellRenderer::Badge;
ti.column_specs[4].badges = {
{"ok", "#22c55e", "OK"},
{"error", "#ef4444", "Error"},
{"running", "#3b82f6", "Running"},
{"timeout", "#f59e0b", "Timeout"},
};
std::vector<std::string> backing;
backing.reserve(cu.recent_executions.size() * 5);
char dur_buf[24];
for (const auto& r : cu.recent_executions) {
if (g_recent_only_registry && r.function_id.empty()) continue;
// When
backing.push_back(format_ts(r.ts));
// Function — registry call or $ snippet or "-"
if (!r.function_id.empty()) {
backing.push_back(r.function_id);
} else if (!r.command_snippet.empty()) {
char sbuf[88];
std::snprintf(sbuf, sizeof(sbuf), "$ %.80s%s",
r.command_snippet.c_str(),
r.command_snippet.size() > 80 ? "..." : "");
backing.push_back(sbuf);
} else {
backing.push_back("-");
}
ImGui::EndTable();
// Tool
backing.push_back(r.tool_used);
// Duration (as numeric string for Float column)
std::snprintf(dur_buf, sizeof(dur_buf), "%d", r.duration_ms);
backing.push_back(dur_buf);
// Status
backing.push_back(r.success ? "ok" : "error");
}
int nrows = 0;
for (const auto& r : cu.recent_executions)
if (!g_recent_only_registry || !r.function_id.empty()) nrows++;
ti.rows = nrows;
ti.cols = static_cast<int>(ti.headers.size());
std::vector<const char*> ptrs;
cells_to_ptrs(backing, ptrs);
ti.cells = ptrs.data();
ImGui::BeginChild("##monitor_recent_wrap", ImVec2(-1, -1));
data_table::render("##dt_monitor_recent", {ti}, g_dt_monitor_recent);
ImGui::EndChild();
}
ImGui::EndTabItem();
}
@@ -926,45 +943,75 @@ void draw_monitor(RegistryData& data) {
if (failed.empty()) {
ImGui::TextDisabled("No registry-function failures in this window. Healthy.");
} else {
const ImGuiTableFlags tf = ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders
| ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("##monitor_failed_fns", 5, tf, ImVec2(0, 0))) {
ImGui::TableSetupColumn("When");
ImGui::TableSetupColumn("Function");
ImGui::TableSetupColumn("Tool");
ImGui::TableSetupColumn("Error class");
ImGui::TableSetupColumn("Error snippet");
ImGui::TableHeadersRow();
for (const auto* p : failed) {
const auto& r = *p;
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::TextUnformatted(format_ts(r.ts).c_str());
ImGui::TableSetColumnIndex(1);
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::error);
ImGui::TextUnformatted(r.function_id.c_str());
ImGui::PopStyleColor();
ImGui::TableSetColumnIndex(2); ImGui::TextUnformatted(r.tool_used.c_str());
ImGui::TableSetColumnIndex(3); ImGui::TextUnformatted(r.error_class.c_str());
ImGui::TableSetColumnIndex(4);
if (r.error_snippet.empty()) {
ImGui::TextDisabled("-");
} else {
char buf[120];
std::snprintf(buf, sizeof(buf), "%.110s%s",
r.error_snippet.c_str(),
r.error_snippet.size() > 110 ? "..." : "");
ImGui::TextUnformatted(buf);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(560.0f);
ImGui::TextUnformatted(r.error_snippet.c_str());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
// Build TableInput for data_table::render (issue 0081-J).
// Columns: When | Function | Tool | Error Class | Snippet
// Error Class → Badge renderer with common error class colors.
data_table::TableInput ti;
ti.name = "monitor_failed";
ti.headers = {"When", "Function", "Tool", "Error Class", "Snippet"};
ti.types = {
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
};
// column_specs: parallel to headers (5 specs)
ti.column_specs.resize(5);
// col 0 When: Text (default)
ti.column_specs[0].renderer = data_table::CellRenderer::Text;
// col 1 Function: Text (default) — already in error context, no extra badge needed
ti.column_specs[1].renderer = data_table::CellRenderer::Text;
// col 2 Tool: Text (default)
ti.column_specs[2].renderer = data_table::CellRenderer::Text;
// col 3 Error Class: Badge renderer
ti.column_specs[3].renderer = data_table::CellRenderer::Badge;
ti.column_specs[3].badges = {
{"sqlite", "#f59e0b", "SQLite"},
{"network", "#3b82f6", "Network"},
{"timeout", "#f59e0b", "Timeout"},
{"not_found", "#8b5cf6", "Not Found"},
{"auth", "#ef4444", "Auth"},
{"parse", "#ec4899", "Parse"},
{"io", "#06b6d4", "I/O"},
{"permission", "#ef4444", "Permission"},
{"unknown", "#6b7280", "Unknown"},
};
// col 4 Snippet: Text (default)
ti.column_specs[4].renderer = data_table::CellRenderer::Text;
std::vector<std::string> backing;
backing.reserve(failed.size() * 5);
for (const auto* p : failed) {
const auto& r = *p;
backing.push_back(format_ts(r.ts));
backing.push_back(r.function_id);
backing.push_back(r.tool_used);
// error_class: normalize empty to "unknown" for badge matching
backing.push_back(r.error_class.empty() ? "unknown" : r.error_class);
// snippet: truncate to ~110 chars for table display
if (r.error_snippet.empty()) {
backing.push_back("-");
} else {
char sbuf[120];
std::snprintf(sbuf, sizeof(sbuf), "%.110s%s",
r.error_snippet.c_str(),
r.error_snippet.size() > 110 ? "..." : "");
backing.push_back(sbuf);
}
ImGui::EndTable();
}
ti.rows = static_cast<int>(failed.size());
ti.cols = static_cast<int>(ti.headers.size());
std::vector<const char*> ptrs;
cells_to_ptrs(backing, ptrs);
ti.cells = ptrs.data();
ImGui::BeginChild("##monitor_failed_wrap", ImVec2(-1, -1));
data_table::render("##dt_monitor_failed", {ti}, g_dt_monitor_failed);
ImGui::EndChild();
}
ImGui::EndTabItem();
}