// panel_agent_runs.cpp — wraps the registry fn_viz::render_agent_runs_timeline. // // HTTP polling against agent_runner_api:8486. If the API is offline the // panel shows `disconnected` and the table stays empty — never blocks the // UI thread (calls happen lazily via Refresh button). #include "panels.h" #include "core/icons_tabler.h" #include "viz/agent_runs_timeline.h" #include #include namespace kanban_cpp { namespace { fn_viz::TimelineState& state_singleton() { static fn_viz::TimelineState s; static bool inited = false; if (!inited) { s.sse_url = "http://127.0.0.1:8486/api/runs/stream"; s.connection_status = "disconnected"; inited = true; } return s; } void poll_runs(AppState& app) { auto& ts = state_singleton(); std::string err; auto runs = list_runs(app.cfg, err); if (!err.empty()) { std::lock_guard lk(ts.runs_mutex); ts.connection_status = "disconnected"; return; } std::lock_guard lk(ts.runs_mutex); ts.runs.clear(); for (const auto& r : runs) { fn_viz::AgentRun ar; ar.id = r.id; ar.app = "kanban_cpp"; ar.card_id = r.card_id; ar.branch = r.branch; ar.status = r.status; ar.started_at = r.started_at; ar.finished_at = r.finished_at; ts.runs.push_back(ar); } ts.connection_status = "connected"; } } // namespace void draw_agent_runs(AppState& app, bool* p_open) { if (!ImGui::Begin(TI_ROBOT " Agent runs", p_open)) { ImGui::End(); return; } auto& ts = state_singleton(); if (ImGui::Button(TI_REFRESH " Poll agent_runner_api")) poll_runs(app); ImGui::SameLine(); ImGui::TextDisabled("%s", ts.connection_status.c_str()); ImGui::Separator(); fn_viz::render_agent_runs_timeline(ts); ImGui::End(); } } // namespace kanban_cpp