048a4a1457
Issue 0118. fn_viz::render_agent_runs_timeline(TimelineState&): - Filtros: multi-select apps, multi-select statuses, Since (days). - Connection badge (● green / ◐ amber / ○ red) por state.connection_status. - Tabla 7 cols: status icon | app chip | issue/card | branch | dod badge | duration | started. Selectable SpanAllColumns dispara on_select callback. - Footer: contadores per-status sobre el set completo. Thread-safe: snapshot bajo runs_mutex al inicio del frame. SSE client NO implementado — poll_sse_runs() es stub documentado en .md ## Gotchas. Consumer puede usar http_request_cpp_core para polling fallback contra GET /api/runs hasta que un endpoint /api/runs/stream estable aparezca. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
83 lines
2.9 KiB
C++
83 lines
2.9 KiB
C++
#pragma once
|
|
//
|
|
// agent_runs_timeline.h — ImGui panel: cross-app timeline of agent runs.
|
|
//
|
|
// API surface declared per issue 0118. Render is impure (uses ImGui); pure
|
|
// helpers live in agent_runs_timeline_helpers.{h,cpp} so unit tests can
|
|
// exercise filter / sort / formatting without a GL context.
|
|
//
|
|
// Typical wiring (consumer side):
|
|
//
|
|
// fn_viz::TimelineState g_state;
|
|
// g_state.sse_url = "http://127.0.0.1:8497/api/runs/stream";
|
|
// g_state.on_select = [](const std::string& id){ open_detail_panel(id); };
|
|
//
|
|
// ImGui::Begin("Agent runs");
|
|
// fn_viz::render_agent_runs_timeline(g_state);
|
|
// ImGui::End();
|
|
//
|
|
// // Whenever a worker thread (real SSE client, polling fallback, fixture)
|
|
// // wants to update the list:
|
|
// {
|
|
// std::lock_guard<std::mutex> lk(g_state.runs_mutex);
|
|
// g_state.runs = new_runs;
|
|
// g_state.connection_status = "connected";
|
|
// }
|
|
//
|
|
// SSE client itself is OUT OF SCOPE for this issue — see the documented stub
|
|
// in poll_sse_runs() and the .md ## Gotchas section.
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace fn_viz {
|
|
|
|
struct AgentRun {
|
|
std::string id;
|
|
std::string app; // kanban_cpp | skill_tree | ...
|
|
std::string issue_id;
|
|
std::string card_id;
|
|
std::string branch;
|
|
std::string status; // pending|running|done|validated|merged|aborted|failed
|
|
int64_t started_at = 0; // unix epoch seconds
|
|
int64_t finished_at = 0; // 0 if still running
|
|
int dod_total = 0;
|
|
int dod_done = 0;
|
|
int dod_validated = 0;
|
|
};
|
|
|
|
struct TimelineFilter {
|
|
std::vector<std::string> apps;
|
|
std::vector<std::string> statuses;
|
|
int64_t since_ts = 0; // 0 means "no lower bound"
|
|
};
|
|
|
|
struct TimelineState {
|
|
std::string sse_url;
|
|
std::vector<AgentRun> runs;
|
|
TimelineFilter filter;
|
|
std::function<void(const std::string& run_id)> on_select;
|
|
std::string selected_run_id;
|
|
std::string connection_status; // "connected"|"disconnected"|"connecting"
|
|
std::mutex runs_mutex; // SSE thread writes, UI reads
|
|
};
|
|
|
|
// Renders the timeline INLINE inside the current ImGui window. The caller
|
|
// owns ImGui::Begin/End — this function only draws filters, badge, table
|
|
// and footer counts. Safe to call every frame; cost is O(N) over runs.
|
|
void render_agent_runs_timeline(TimelineState& state);
|
|
|
|
// STUB. Documented in agent_runs_timeline.md ## Gotchas.
|
|
// Today this is a no-op. The intent is that a future issue wires either:
|
|
// (a) a real SSE client (libcurl multi + custom read callback), or
|
|
// (b) a polling fallback that calls fn_core::http_request("GET", url, ...)
|
|
// every N seconds and replaces state.runs under runs_mutex.
|
|
// Until then, the consumer is responsible for populating state.runs (e.g.
|
|
// test fixtures, or an in-process orchestrator that owns the data already).
|
|
void poll_sse_runs(TimelineState& state);
|
|
|
|
} // namespace fn_viz
|