From 3f8c12db897ff6ec086e92559b153f906e1f01b6 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Fri, 15 May 2026 16:43:51 +0200 Subject: [PATCH] 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 --- views.cpp | 241 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 144 insertions(+), 97 deletions(-) diff --git a/views.cpp b/views.cpp index 11c49a5..ba0491a 100644 --- a/views.cpp +++ b/views.cpp @@ -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 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(ti.headers.size()); + + std::vector 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 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(failed.size()); + ti.cols = static_cast(ti.headers.size()); + + std::vector 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(); }