auto(0129): agents_dashboard — secret_store_cpp_infra + CMakeLists register #4
@@ -0,0 +1,110 @@
|
||||
#include "viz/agent_runs_timeline_helpers.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// Tabler icon macros live in core/icons_tabler.h. They are short (3-byte
|
||||
// utf-8) string literals like "\xee\xa9\x9e". We don't depend on that header
|
||||
// here directly to keep helpers buildable without the icon font tree — but
|
||||
// the values below are pulled from cpp/functions/core/icons_tabler.h
|
||||
// (Tabler v3.41). If a glyph moves upstream, update this table.
|
||||
|
||||
namespace fn_viz {
|
||||
namespace timeline {
|
||||
|
||||
namespace {
|
||||
|
||||
// Mirror of icons_tabler.h entries used by the timeline. Kept inline so the
|
||||
// helpers TU compiles standalone (tests don't pull the full icon font).
|
||||
constexpr const char* TI_CLOCK_ = "\xee\xa9\xb0"; // U+EA70
|
||||
constexpr const char* TI_LOADER_ = "\xee\xb2\xa3"; // U+ECA3 spinning loader
|
||||
constexpr const char* TI_CIRCLE_CHECK_ = "\xee\xa9\xa7"; // U+EA67
|
||||
constexpr const char* TI_CHECKS_ = "\xee\xae\xaa"; // U+EBAA
|
||||
constexpr const char* TI_GIT_MERGE_ = "\xee\xaa\xb5"; // U+EAB5
|
||||
constexpr const char* TI_BAN_ = "\xee\xa8\xae"; // U+EA2E
|
||||
constexpr const char* TI_CIRCLE_X_ = "\xee\xa9\xaa"; // U+EA6A
|
||||
constexpr const char* TI_HOURGLASS_ = "\xee\xbe\x93"; // U+EF93
|
||||
|
||||
bool contains(const std::vector<std::string>& v, const std::string& needle) {
|
||||
for (const auto& s : v) {
|
||||
if (s == needle) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool passes_filter(const AgentRun& r, const TimelineFilter& f) {
|
||||
if (!f.apps.empty() && !contains(f.apps, r.app)) return false;
|
||||
if (!f.statuses.empty() && !contains(f.statuses, r.status)) return false;
|
||||
if (f.since_ts > 0 && r.started_at < f.since_ts) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<AgentRun> filter_and_sort(const std::vector<AgentRun>& runs,
|
||||
const TimelineFilter& f) {
|
||||
std::vector<AgentRun> out;
|
||||
out.reserve(runs.size());
|
||||
for (const auto& r : runs) {
|
||||
if (passes_filter(r, f)) out.push_back(r);
|
||||
}
|
||||
std::sort(out.begin(), out.end(), [](const AgentRun& a, const AgentRun& b) {
|
||||
if (a.started_at != b.started_at) return a.started_at > b.started_at;
|
||||
return a.id < b.id;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string format_duration(int64_t started_at, int64_t finished_at) {
|
||||
if (finished_at == 0) return "running";
|
||||
int64_t dur = finished_at - started_at;
|
||||
if (dur < 0) return "—";
|
||||
|
||||
char buf[32];
|
||||
if (dur < 60) {
|
||||
std::snprintf(buf, sizeof(buf), "%llds", (long long)dur);
|
||||
} else if (dur < 3600) {
|
||||
long long m = dur / 60;
|
||||
long long s = dur % 60;
|
||||
std::snprintf(buf, sizeof(buf), "%lldm%02llds", m, s);
|
||||
} else {
|
||||
long long h = dur / 3600;
|
||||
long long m = (dur % 3600) / 60;
|
||||
std::snprintf(buf, sizeof(buf), "%lldh%02lldm", h, m);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
int status_color_token(const std::string& status) {
|
||||
if (status == "pending") return 1; // info
|
||||
if (status == "running") return 1; // info
|
||||
if (status == "done") return 2; // success
|
||||
if (status == "validated") return 2; // success
|
||||
if (status == "merged") return 3; // primary
|
||||
if (status == "aborted") return 5; // warning
|
||||
if (status == "failed") return 6; // danger
|
||||
return 0; // neutral
|
||||
}
|
||||
|
||||
std::string status_icon_id(const std::string& status) {
|
||||
if (status == "pending") return TI_CLOCK_;
|
||||
if (status == "running") return TI_LOADER_;
|
||||
if (status == "done") return TI_CIRCLE_CHECK_;
|
||||
if (status == "validated") return TI_CHECKS_;
|
||||
if (status == "merged") return TI_GIT_MERGE_;
|
||||
if (status == "aborted") return TI_BAN_;
|
||||
if (status == "failed") return TI_CIRCLE_X_;
|
||||
return TI_HOURGLASS_;
|
||||
}
|
||||
|
||||
std::string app_chip_hex(const std::string& app) {
|
||||
// Hardcoded palette. Keep aligned with the apps' icon.accent in app.md.
|
||||
if (app == "kanban_cpp") return "#a855f7"; // violet
|
||||
if (app == "skill_tree") return "#0ea5e9"; // sky
|
||||
if (app == "graph_explorer") return "#16a34a"; // green
|
||||
if (app == "shaders_lab") return "#f97316"; // orange
|
||||
if (app == "registry_dashboard") return "#0ea5e9";
|
||||
return "#5C5F66"; // neutral gray (matches fn_tokens::border_strong)
|
||||
}
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace fn_viz
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
//
|
||||
// agent_runs_timeline_helpers.h — pure helpers for the agent runs timeline panel.
|
||||
//
|
||||
// These helpers operate on AgentRun + TimelineFilter from agent_runs_timeline.h
|
||||
// but never call ImGui. They are unit-testable in isolation (see
|
||||
// cpp/tests/test_agent_runs_timeline.cpp).
|
||||
//
|
||||
// Issue 0118.
|
||||
|
||||
#include "viz/agent_runs_timeline.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace fn_viz {
|
||||
namespace timeline {
|
||||
|
||||
// True iff `r` matches every active sub-filter:
|
||||
// - filter.apps empty => any app
|
||||
// - filter.statuses empty => any status
|
||||
// - filter.since_ts == 0 => any started_at
|
||||
// All filters are AND-combined.
|
||||
bool passes_filter(const AgentRun& r, const TimelineFilter& f);
|
||||
|
||||
// Returns a copy of `runs` with `passes_filter` applied and sorted by
|
||||
// `started_at` descending (most recent first). Stable for runs with equal
|
||||
// started_at: ties broken by id ascending.
|
||||
std::vector<AgentRun> filter_and_sort(const std::vector<AgentRun>& runs,
|
||||
const TimelineFilter& f);
|
||||
|
||||
// Human-friendly duration string built from two unix-epoch SECOND timestamps.
|
||||
// - finished_at == 0 => "running"
|
||||
// - duration < 60s => "Ns" (e.g. "45s")
|
||||
// - duration < 3600s => "MmSs" (e.g. "12m05s")
|
||||
// - else => "HhMm" (e.g. "1h12m")
|
||||
// Negative or inverted (finished < started) => "—".
|
||||
std::string format_duration(int64_t started_at, int64_t finished_at);
|
||||
|
||||
// Returns a small integer color token (0..7) per status. The render layer
|
||||
// maps this token to an actual ImVec4 using fn_tokens::colors.
|
||||
//
|
||||
// pending -> 1 (info)
|
||||
// running -> 1 (info)
|
||||
// done -> 2 (success)
|
||||
// validated -> 2 (success)
|
||||
// merged -> 3 (primary)
|
||||
// aborted -> 5 (warning)
|
||||
// failed -> 6 (danger)
|
||||
// <other> -> 0 (neutral)
|
||||
int status_color_token(const std::string& status);
|
||||
|
||||
// Returns the TI_* macro string for the given status (already utf-8 encoded
|
||||
// 3-byte sequence). Render layer uses these directly in ImGui::Text.
|
||||
std::string status_icon_id(const std::string& status);
|
||||
|
||||
// Hardcoded chip color hex for known app ids. Returns "#5C5F66" (border_strong
|
||||
// / neutral gray) for unknown apps. Render layer parses the hex to ImVec4.
|
||||
std::string app_chip_hex(const std::string& app);
|
||||
|
||||
} // namespace timeline
|
||||
} // namespace fn_viz
|
||||
Reference in New Issue
Block a user