function qs(name) { const url = new URL(window.location.href); return url.searchParams.get(name); } async function fetchJson(url) { const res = await fetch(url, { cache: "no-store" }); const text = await res.text(); let data; try { data = JSON.parse(text); } catch { throw new Error(`Bad JSON from ${url}: ${text.slice(0, 200)}`); } if (!res.ok) { const msg = data && data.error ? data.error : `HTTP ${res.status}`; throw new Error(msg); } return data; } function fmt(n, digits = 1) { if (typeof n !== "number" || !isFinite(n)) return "-"; return n.toFixed(digits); } async function updateIndex() { const body = document.getElementById("graphs-body"); if (!body) return; let graphs = []; try { graphs = await fetchJson("/api/graphs"); } catch (e) { body.innerHTML = `${e.message}`; return; } body.innerHTML = ""; for (const g of graphs) { const tr = document.createElement("tr"); const statusCls = g.running ? "status-running" : "status-stopped"; tr.innerHTML = ` ${g.name} ${g.running ? "Running" : "Stopped"} ${fmt(g.total_fps)} `; body.appendChild(tr); } } async function updateGraph() { const name = qs("name"); const nodesBody = document.getElementById("nodes-body"); const edgesBody = document.getElementById("edges-body"); if (!name || !nodesBody || !edgesBody) return; const title = document.getElementById("graph-title"); if (title) title.textContent = `Graph: ${name}`; let g; try { g = await fetchJson(`/api/graphs/${encodeURIComponent(name)}`); } catch (e) { nodesBody.innerHTML = `${e.message}`; edgesBody.innerHTML = `${e.message}`; return; } const meta = document.getElementById("graph-meta"); if (meta) { meta.textContent = `running=${g.running} total_fps=${fmt(g.total_fps)} ts_ms=${g.timestamp_ms}`; } nodesBody.innerHTML = ""; for (const n of g.nodes) { const tr = document.createElement("tr"); tr.innerHTML = ` ${n.id} ${n.type} ${n.role || ""} ${fmt(n.input_fps)} ${fmt(n.output_fps)} ${n.input_queue ? `${n.input_queue.size}/${n.input_queue.capacity}` : "-"} ${n.drop_total} ${n.error_total} ${fmt(n.avg_process_time_ms, 3)} `; nodesBody.appendChild(tr); } edgesBody.innerHTML = ""; for (const e of g.edges) { const tr = document.createElement("tr"); tr.innerHTML = ` ${e.from} ${e.to} ${e.queue.size} ${e.queue.capacity} ${e.queue.dropped_total} ${fmt(e.queue.pushed_fps)} ${fmt(e.queue.popped_fps)} `; edgesBody.appendChild(tr); } } function boot() { updateIndex(); updateGraph(); setInterval(updateIndex, 2000); setInterval(updateGraph, 1000); } window.addEventListener("load", boot);