feat(monitor): Monitor tab as primary landing + errors KPI + recent executions + date filter

Rebrand "Claude Usage" tab to "Monitor" and promote it to first/default tab in
the main TabBar. Monitor is the landing for the reactive loop (construir →
ejecutar → recopilar → analizar → mejorar).

UI additions:
- Local toolbar inside Monitor with date preset combo (1h / 24h / 7d / 30d /
  All), manual Refresh button, and live LED + last-event-ts indicator.
- 5 KPI cards (was 4): added "Errors" derived from COUNT(*) FROM calls WHERE
  success = 0 filtered by the active window.
- New sub-tab "Recent Executions" (first sub-tab) with columns: When,
  Function, Tool, ms, OK (check/X colored), Error class. Backed by calls
  table, sorted by ts DESC, limit 100, filtered by window.
- Violations sub-tab gains "When" column with formatted ts.

Data layer:
- data.h: RecentExecutionRow + window_secs + ws_connected/last_event_ts /
  last_seen_call_id watermark on ClaudeUsageData.
- data_http.{h,cpp}: load_claude_usage_http now takes window_secs and embeds
  ts_filter() in calls/errors/violations queries. total_errors populated.
  recent_executions populated up to 100 rows. New standalone
  load_recent_executions_http() for future WS-driven partial refetch.
- main.cpp: reload_data preserves window_secs across reloads; new
  reload_monitor() does a Monitor-only refetch when the user changes the
  window or clicks Refresh, without re-querying the full registry.

Wiring:
- views.h: draw_monitor + monitor_consume_reload_request() +
  monitor_set_ws_state() exported. draw_claude_usage removed.
- views.cpp: render() consumes monitor_consume_reload_request each frame
  and dispatches reload_monitor().

This is the UI half of issue 0086. The server-side WebSocket endpoint in
sqlite_api and the C++ WS client are next; the LED is wired to
monitor_set_ws_state but stays gray until those land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 00:26:29 +02:00
parent 33d50aacdd
commit 87b7ef45ff
6 changed files with 592 additions and 3 deletions
+23
View File
@@ -22,13 +22,21 @@ static bool g_loaded = false;
static bool g_using_http = false;
static void reload_data() {
// Conservar la ventana del Monitor entre recargas (no se pierde al refrescar).
int prev_window = g_data.claude.window_secs;
if (prev_window == 0 && g_data.claude.total_calls == 0) prev_window = 86400;
g_data = RegistryData{};
g_data.claude.window_secs = prev_window;
// Try HTTP API first
if (!g_api_url.empty()) {
g_loaded = load_registry_data_http(g_api_url, g_data);
if (g_loaded) {
g_using_http = true;
// Issue 0085d: best-effort load of Claude telemetry from
// ops:call_monitor. Falla silenciosamente si no esta disponible
// (la tab Monitor mostrara placeholder).
load_claude_usage_http(g_api_url, g_data, g_data.claude.window_secs);
return;
}
fprintf(stderr, "HTTP API failed, falling back to SQLite\n");
@@ -44,12 +52,27 @@ static void reload_data() {
}
}
// Refetch SOLO de telemetria del Monitor. Se dispara al cambiar la ventana
// temporal o al recibir un evento WS que invalide el snapshot. No toca el
// registry general.
static void reload_monitor() {
if (g_api_url.empty() || !g_loaded) return;
load_claude_usage_http(g_api_url, g_data, g_data.claude.window_secs);
}
static void render() {
if (ImGui::GetIO().UserData != nullptr) {
ImGui::GetIO().UserData = nullptr;
reload_data();
}
// Issue 0086: el Monitor pide refetch parcial cuando el usuario cambia la
// ventana temporal o pulsa Refresh. No pasa por reload_data() para no
// tirar abajo todo el dataset del registry.
if (monitor_consume_reload_request()) {
reload_monitor();
}
if (!g_loaded) {
fullscreen_window_begin("##error");
ImGui::TextColored(ImVec4(1, 0.3f, 0.3f, 1),