import { get } from "./api.js"; export const state = { me: undefined, projects: [], tasks: new Map(), sessions: new Map(), inbox: [], selectedTaskId: undefined, transcript: [], lastTranscriptSeq: 0, connected: false, }; const listeners = new Map(); export function on(topic, fn) { let set = listeners.get(topic); if (!set) { set = new Set(); listeners.set(topic, set); } set.add(fn); return () => set.delete(fn); } export function onEach(topics, fn) { const offs = topics.map((t) => on(t, fn)); return () => { for (const off of offs) { off(); } }; } export function activeTasks() { return state.projects.flatMap((project) => (state.tasks.get(project.id) ?? []).filter((t) => t.status === "active").map((task) => ({ project, task }))); } export function notify(topic, detail) { for (const fn of listeners.get(topic) ?? []) { fn(detail); } } export function findTask(id) { for (const tasks of state.tasks.values()) { const task = tasks.find((t) => t.id === id); if (task) { return task; } } return undefined; } export function currentSession(taskId) { return state.sessions.get(taskId)?.findLast((s) => s.state !== "completed") ?? state.sessions.get(taskId)?.at(-1); } export async function loadMe() { state.me = await get("/api/me"); notify("me"); } export async function loadProjects() { state.projects = await get("/api/projects"); await Promise.all(state.projects.map((p) => loadTasks(p.id))); notify("projects"); } export async function loadTasks(projectId) { const tasks = await get("/api/projects/{projectId}/tasks", { path: { projectId } }); state.tasks.set(projectId, tasks); await Promise.all(tasks.filter((t) => t.status === "active").map((t) => loadSessions(t.id))); notify("tasks"); } export async function loadSessions(taskId) { state.sessions.set(taskId, await get("/api/tasks/{taskId}/sessions", { path: { taskId } })); notify("sessions", taskId); } export async function loadInbox() { state.inbox = await get("/api/inbox"); notify("inbox"); } let liveDuringLoad; export async function selectTask(taskId) { state.selectedTaskId = taskId; state.transcript = []; state.lastTranscriptSeq = 0; liveDuringLoad = undefined; notify("selection"); if (!taskId) { return; } const held = []; liveDuringLoad = held; for (;;) { const page = await get("/api/tasks/{taskId}/events", { path: { taskId }, query: { after: state.lastTranscriptSeq, limit: 1000 } }); if (state.selectedTaskId !== taskId || liveDuringLoad !== held) { return; } appendTranscript(page); if (page.length < 1000) { break; } } liveDuringLoad = undefined; appendTranscript(held); } function appendTranscript(events) { const fresh = events.filter((ev) => ev.seq > state.lastTranscriptSeq); if (fresh.length === 0) { return; } state.transcript.push(...fresh); state.lastTranscriptSeq = fresh.at(-1).seq; notify("transcript", fresh); } export function handleEvent(ev) { if (ev.seq === 0) { if (ev.task_id && ev.task_id === state.selectedTaskId && !liveDuringLoad) { notify("stream", ev); } return; } if (ev.task_id && ev.task_id === state.selectedTaskId) { if (liveDuringLoad) { liveDuringLoad.push(ev); } else { appendTranscript([ev]); } } switch (ev.kind) { case "user.updated": if (ev.payload["login"] === state.me?.login) { void loadMe(); } break; case "project.created": void loadProjects(); break; case "task.created": case "task.merge_title": case "task.merged": case "task.discarded": if (ev.project_id) { void loadTasks(ev.project_id); } break; case "session.created": case "session.state": case "session.model": if (ev.task_id) { void loadSessions(ev.task_id); } break; case "inbox.created": case "inbox.updated": void loadInbox(); break; } notify("event", ev); } //# sourceMappingURL=state.js.map