feat(ui): interactive graph, strip context menus, days by activity, message times, limit status words

This commit is contained in:
hh
2026-09-02 04:24:15 +02:00
parent 025f2dbc3f
commit c6401f1114
27 changed files with 1619 additions and 381 deletions
+24 -1
View File
@@ -1,9 +1,17 @@
import type { BusEvent, LimitsResponse, LimitWindow } from "./api/types";
import type {
BusEvent,
JobsResponse,
LimitsResponse,
LimitWindow,
} from "./api/types";
import { panelCache } from "./panel/cache";
import { ConversationIndex } from "./panel/index.svelte";
import { session } from "./session.svelte";
const TAPE_SIZE = 120;
const QUIET = new Set(["stream", "hello"]);
const JOBS_KEY = "jobs";
const cache = panelCache("beaver.admin");
// Gateway-wide live state for the admin: the conversation index, the last
// non-chatty events as a tape, and the quota windows updated the moment
@@ -11,11 +19,22 @@ const QUIET = new Set(["stream", "hello"]);
class Gateway extends ConversationIndex {
tape = $state<BusEvent[]>([]);
limits = $state<LimitsResponse | null>(null);
// The scheduler snapshot: what was last seen, shown at once, then refreshed.
jobs = $state<JobsResponse | null>(cache.get<JobsResponse>(JOBS_KEY) ?? null);
constructor() {
super(() => session.client);
}
async refreshJobs(): Promise<void> {
const client = this.client();
if (!client) {
return;
}
this.jobs = await client.jobs();
cache.set(JOBS_KEY, this.jobs);
}
override async load(): Promise<void> {
const client = this.client();
if (!client) {
@@ -28,6 +47,7 @@ class Gateway extends ConversationIndex {
if (limits) {
this.limits = limits;
}
this.refreshJobs().catch(() => undefined);
}
async refreshLimits(): Promise<void> {
@@ -48,6 +68,9 @@ class Gateway extends ConversationIndex {
this.applyLimit(event);
return;
}
if (event.type.startsWith("job.") || event.type.startsWith("schedule.")) {
this.refreshJobs().catch(() => undefined);
}
super.apply(event);
}