// server/src/dashboard.ts
var currentTabId = "activity-tab";
function switchTab(tabId, btn) {
currentTabId = tabId;
document.querySelectorAll(".tab-content").forEach((el) => el.classList.remove("active"));
document.querySelectorAll(".tab-button").forEach((el) => el.classList.remove("active"));
const targetTab = document.getElementById(tabId);
if (targetTab)
targetTab.classList.add("active");
const targetBtn = btn || document.querySelector(`.tab-button[onclick*="'${tabId}'"]`);
if (targetBtn)
targetBtn.classList.add("active");
switch (tabId) {
case "graph-tab":
if (network) {
network.redraw();
} else {
loadGraph();
}
break;
case "activity-tab":
loadActivityHistory();
break;
case "task-tab":
loadTasks();
break;
case "techdebt-tab":
loadTechDebt();
break;
case "adrs-tab":
loadADRs();
break;
case "memos-tab":
loadMemos();
break;
case "snippets-tab":
loadSnippets();
break;
case "terminal-tab":
loadTerminal();
break;
case "ledger-tab":
loadLedger();
break;
case "env-tab":
loadEnvironment();
break;
}
}
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", newTheme);
localStorage.setItem("theme", newTheme);
}
if (localStorage.getItem("theme") === "dark" || !localStorage.getItem("theme") && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.documentElement.setAttribute("data-theme", "dark");
}
var network = null;
var nodesData = new vis.DataSet;
var edgesData = new vis.DataSet;
var rawEntities = {};
var rawRelations = [];
var activeFilters = new Set;
var allTypes = new Set;
var typeColors = {
Concept: { bg: "#9b59b6", border: "#8e44ad" },
Technology: { bg: "#3498db", border: "#2980b9" },
Infrastructure: { bg: "#e67e22", border: "#d35400" },
Database_Table: { bg: "#f1c40f", border: "#f39c12" },
Service: { bg: "#1abc9c", border: "#f39c12" },
Tool: { bg: "#e74c3c", border: "#c0392b" },
Integration: { bg: "#2ecc71", border: "#27ae60" },
System: { bg: "#34495e", border: "#2c3e50" },
Rule: { bg: "#fd79a8", border: "#e84393" }
};
function getColorForType(type) {
if (typeColors[type])
return typeColors[type];
let hash = 0;
for (let i = 0;i < type.length; i++)
hash = type.charCodeAt(i) + ((hash << 5) - hash);
const hue = Math.abs(hash) % 360;
return {
background: `hsl(${hue}, 70%, 60%)`,
border: `hsl(${hue}, 70%, 40%)`
};
}
function zoomGraph(step) {
if (!network)
return;
const currentScale = network.getScale();
network.moveTo({ scale: currentScale * (1 + step) });
}
function resetGraph() {
if (!network)
return;
network.fit({
animation: { duration: 500, easingFunction: "easeInOutQuad" }
});
}
function closeInspector() {
const inspector = document.getElementById("inspector-panel");
if (inspector)
inspector.classList.remove("open");
if (network)
network.unselectAll();
}
function showInspector(nodeId) {
const entity = rawEntities[nodeId];
if (!entity)
return;
const titleEl = document.getElementById("inspector-title");
if (titleEl)
titleEl.innerText = entity.name;
const metaEl = document.querySelector(".inspector-meta");
if (metaEl) {
const createdStr = entity.created_at ? new Date(entity.created_at * 1000).toISOString().replace("T", " ").substring(0, 19) : "";
const updatedStr = entity.updated_at ? new Date(entity.updated_at * 1000).toISOString().replace("T", " ").substring(0, 19) : "";
metaEl.innerHTML = `
Type: ${escapeHtml(entity.entity_type)}
Namespace: ${escapeHtml(entity.namespace || "global")}
${entity.repo_name ? `Repository: ${escapeHtml(entity.repo_name)}
` : ""}
${entity.file_path ? `File: ${escapeHtml(entity.file_path)}
` : ""}
${entity.git_branch ? `Branch: ${escapeHtml(entity.git_branch)}
` : ""}
${createdStr ? `Created: ${escapeHtml(createdStr)}
` : ""}
${updatedStr ? `Updated: ${escapeHtml(updatedStr)}
` : ""}
`;
} else {
const typeEl = document.getElementById("inspector-type");
if (typeEl)
typeEl.innerText = entity.entity_type;
const nsEl = document.getElementById("inspector-namespace");
if (nsEl)
nsEl.innerText = entity.namespace || "global";
}
const obsHtml = (entity.observations || []).map((o) => `${escapeHtml(o)} `).join("");
const obsEl = document.getElementById("inspector-observations");
if (obsEl)
obsEl.innerHTML = obsHtml || "No observations recorded. ";
const rels = rawRelations.filter((r) => r.from === nodeId || r.to === nodeId);
const relHtml = rels.map((r) => {
if (r.from === nodeId)
return `→ ${escapeHtml(r.relation_type)} → ${escapeHtml(r.to)} `;
return `← ${escapeHtml(r.relation_type)} ← ${escapeHtml(r.from)} `;
}).join("");
const relEl = document.getElementById("inspector-relations");
if (relEl)
relEl.innerHTML = relHtml || "No direct relations. ";
const inspector = document.getElementById("inspector-panel");
if (inspector)
inspector.classList.add("open");
}
function toggleFilter(type) {
if (activeFilters.has(type))
activeFilters.delete(type);
else
activeFilters.add(type);
renderFilters();
updateGraphData();
}
function renderFilters() {
const container = document.getElementById("graph-filters");
if (!container)
return;
container.innerHTML = Array.from(allTypes).sort().map((type) => {
const isActive = activeFilters.has(type);
const color = getColorForType(type).bg || getColorForType(type).background;
return `
${type}
`;
}).join("");
}
var lastGraphJson = "";
function updateGraphData() {
const newNodes = [];
const newEdges = [];
const nodeIds = new Set;
for (const [name, entity] of Object.entries(rawEntities)) {
allTypes.add(entity.entity_type);
if (activeFilters.size > 0 && !activeFilters.has(entity.entity_type))
continue;
const color = getColorForType(entity.entity_type);
newNodes.push({
id: name,
label: name,
title: `${name}
Type: ${entity.entity_type}`,
color: { background: color.bg || color.background, border: color.border },
font: {
color: document.documentElement.getAttribute("data-theme") === "dark" ? "#eee" : "#333"
}
});
nodeIds.add(name);
}
rawRelations.forEach((r, idx) => {
if (nodeIds.has(r.from) && nodeIds.has(r.to)) {
newEdges.push({
id: `${r.from}-${r.to}-${r.relation_type}-${idx}`,
from: r.from,
to: r.to,
label: r.relation_type,
arrows: "to",
font: {
color: document.documentElement.getAttribute("data-theme") === "dark" ? "#aaa" : "#666",
strokeWidth: 0
}
});
}
});
nodesData.clear();
if (newNodes.length > 0)
nodesData.add(newNodes);
edgesData.clear();
if (newEdges.length > 0)
edgesData.add(newEdges);
renderFilters();
}
async function loadGraph() {
try {
const res = await fetch("/api/graph", { cache: "no-store" });
const dataText = await res.text();
if (dataText === lastGraphJson && network) {
return;
}
lastGraphJson = dataText;
const data = JSON.parse(dataText);
rawEntities = data.entities || {};
rawRelations = data.relations || [];
updateGraphData();
if (!network) {
const container = document.getElementById("network-container");
if (!container)
return;
const options = {
nodes: { shape: "dot", size: 16, font: { size: 12 } },
edges: {
color: { inherit: "from", opacity: 0.6 },
font: { size: 10, align: "middle" },
smooth: { type: "continuous" }
},
physics: {
enabled: true,
solver: "barnesHut",
barnesHut: {
gravitationalConstant: -3000,
centralGravity: 0.3,
springLength: 95,
springConstant: 0.04,
damping: 0.09,
avoidOverlap: 0.1
},
stabilization: {
enabled: true,
iterations: 150,
updateInterval: 25,
fit: true
}
},
interaction: { hover: true, tooltipDelay: 100, zoomView: false }
};
network = new vis.Network(container, { nodes: nodesData, edges: edgesData }, options);
network.once("stabilizationIterationsDone", function() {
network.setOptions({ physics: { enabled: false } });
});
network.on("dragStart", function() {
network.setOptions({ physics: { enabled: true } });
});
network.on("dragEnd", function() {
network.setOptions({ physics: { enabled: false } });
});
container.addEventListener("wheel", function(event) {
event.preventDefault();
const direction = event.deltaY > 0 ? -0.15 : 0.15;
zoomGraph(direction);
}, { passive: false });
network.on("selectNode", function(params) {
if (params.nodes.length > 0)
showInspector(params.nodes[0]);
});
network.on("deselectNode", function(params) {
if (params.nodes.length === 0)
closeInspector();
});
}
} catch (err) {
console.error("Failed to load graph", err);
const container = document.getElementById("network-container");
if (container) {
container.innerHTML = 'Error loading graph: ' + err.message + "
";
}
}
}
async function completeTask(id) {
try {
if (activeWS && activeWS.readyState === WebSocket.OPEN) {
await sendWsRpc("tasks/complete", { id });
} else {
await fetch(`/api/tasks/${encodeURIComponent(id)}/complete`, { method: "POST" });
}
loadTasks();
} catch (e) {
console.error("Failed to complete task via WS, falling back to HTTP", e);
try {
await fetch(`/api/tasks/${encodeURIComponent(id)}/complete`, { method: "POST" });
loadTasks();
} catch (err) {
console.error("Failed to complete task via HTTP fallback", err);
}
}
}
function buildTaskTreeHTML(tasks, parentId, depth = 0) {
let html = "";
const children = tasks.filter((t) => {
const pid = t.parent_id || t.parentId;
if (!parentId)
return !pid;
return pid === parentId;
});
if (children.length === 0)
return html;
children.forEach((t) => {
const isCompleted = t.status === "completed" || t.status === "done";
const isCancelled = t.status === "cancelled" || t.status === "abandoned";
let cardClass = "task-card";
if (isCompleted)
cardClass += " completed";
if (isCancelled)
cardClass += " cancelled";
let isBlocked = false;
let blockers = [];
const deps = t.dependencies || [];
deps.forEach((depId) => {
const depTask = tasks.find((dt) => dt.id === depId);
if (depTask && depTask.status !== "completed" && depTask.status !== "done") {
isBlocked = true;
blockers.push(depTask.title);
}
});
const allChildren = tasks.filter((ct) => (ct.parentId || ct.parent_id) === t.id);
const completedChildren = allChildren.filter((ct) => ct.status === "completed" || ct.status === "done");
let progressHtml = "";
if (allChildren.length > 0) {
const pct = Math.round(completedChildren.length / allChildren.length * 100);
progressHtml = `
${pct}% (${completedChildren.length}/${allChildren.length} child tasks)
`;
if (completedChildren.length < allChildren.length) {
isBlocked = true;
}
}
const prio = (t.priority || "").toLowerCase();
let prioBadge = "";
if (prio === "critical" || prio === "urgent") {
prioBadge = `CRITICAL `;
} else if (prio === "high") {
prioBadge = `HIGH `;
} else if (prio === "medium" || prio === "med") {
prioBadge = `MEDIUM `;
} else if (prio === "low") {
prioBadge = `LOW `;
}
html += ``;
html += `
`;
html += `
`;
if (prioBadge)
html += prioBadge;
if (t.assigned_agent) {
html += `\uD83E\uDD16 ${escapeHtml(t.assigned_agent)} `;
}
if (isBlocked && !isCompleted && !isCancelled) {
html += `BLOCKED `;
}
if (isCancelled) {
html += `CANCELLED `;
}
html += `
`;
if (!isCompleted && !isCancelled && !isBlocked) {
html += `
✓ `;
}
html += `
`;
if (isBlocked && blockers.length > 0 && !isCompleted && !isCancelled) {
html += `
Waiting on: ${escapeHtml(blockers.join(", "))}
`;
}
html += `
${escapeHtml(t.title)} `;
if (t.description) {
html += `
${escapeHtml(t.description)}
`;
}
if (t.verification_command) {
html += `
\uD83E\uDDEA Test: ${escapeHtml(t.verification_command)}
`;
}
const criteria = t.acceptance_criteria || [];
if (criteria.length > 0) {
html += `
`;
let unmetCriteria = false;
criteria.forEach((c) => {
const isMet = c.is_met;
if (!isMet)
unmetCriteria = true;
const check = isMet ? "☑" : "☐";
const strike = isMet ? "text-decoration: line-through;" : "";
html += `${check} ${escapeHtml(c.description)}${c.verification_command ? ` (${escapeHtml(c.verification_command)})` : ""} `;
});
html += ` `;
if (unmetCriteria && !isCompleted && !isCancelled)
isBlocked = true;
}
if (t.git_branch || t.repo_name) {
html += `
`;
if (t.repo_name)
html += `\uD83D\uDCE6 ${escapeHtml(t.repo_name)} `;
if (t.git_branch)
html += `\uD83C\uDF3F ${escapeHtml(t.git_branch)} `;
html += `
`;
}
html += progressHtml;
if (allChildren.length > 0) {
html += `
`;
html += buildTaskTreeHTML(tasks, t.id, 0);
html += `
`;
}
html += `
`;
});
return html;
}
var cachedTasks = [];
var taskFilterQuery = "";
var taskStatusFilter = "";
function handleTaskFilter(val) {
taskFilterQuery = val.toLowerCase().trim();
applyTaskFilters();
}
function handleTaskStatusFilter(val) {
taskStatusFilter = val.toLowerCase().trim();
applyTaskFilters();
}
function applyTaskFilters() {
const taskContainer = document.getElementById("task-tree-container");
if (!taskContainer)
return;
let filtered = cachedTasks;
if (taskFilterQuery) {
filtered = filtered.filter((t) => t.title && t.title.toLowerCase().includes(taskFilterQuery) || t.description && t.description.toLowerCase().includes(taskFilterQuery) || t.id && t.id.toLowerCase().includes(taskFilterQuery) || t.assigned_agent && t.assigned_agent.toLowerCase().includes(taskFilterQuery));
}
if (taskStatusFilter) {
if (taskStatusFilter === "blocked") {
filtered = filtered.filter((t) => {
const deps = t.dependencies || [];
return deps.some((dId) => {
const dt = cachedTasks.find((x) => x.id === dId);
return dt && dt.status !== "completed" && dt.status !== "done";
});
});
} else {
filtered = filtered.filter((t) => (t.status || "pending").toLowerCase() === taskStatusFilter);
}
}
const rootHtml = buildTaskTreeHTML(filtered, null, 0);
if (!rootHtml) {
if (cachedTasks.length === 0) {
taskContainer.innerHTML = 'No active tasks.
';
} else {
taskContainer.innerHTML = 'No tasks matching current filter.
';
}
} else {
taskContainer.innerHTML = rootHtml;
}
}
async function loadTasks() {
try {
const res = await fetch("/api/tasks", { cache: "no-store" });
cachedTasks = await res.json();
applyTaskFilters();
} catch (err) {
console.error("Failed to load tasks", err);
}
}
function highlightText(text, query) {
if (!query)
return text;
const regex = new RegExp(`(${query})`, "gi");
return text.replace(regex, '$1 ');
}
var searchDebounceTimer = null;
var activeSearchAbortController = null;
async function handleSearch(e) {
const input = e.target;
const q = input.value.trim();
if (searchDebounceTimer)
clearTimeout(searchDebounceTimer);
if (!q) {
if (activeSearchAbortController)
activeSearchAbortController.abort();
const resultsEl = document.getElementById("search-results");
if (resultsEl)
resultsEl.innerHTML = "";
return;
}
const delay = e.key === "Enter" ? 0 : 250;
searchDebounceTimer = setTimeout(async () => {
if (activeSearchAbortController)
activeSearchAbortController.abort();
activeSearchAbortController = new AbortController;
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, {
signal: activeSearchAbortController.signal
});
const data = await res.json();
const container = document.getElementById("search-results");
if (!container)
return;
if (!data.results || data.results.length === 0) {
container.innerHTML = 'No results found.
';
return;
}
container.innerHTML = data.results.map((r) => `
${highlightText(r.title, q)}
${r.type_name}
${r.score.toFixed(2)}
${highlightText(r.content.substring(0, 150), q)}${r.content.length > 150 ? "..." : ""}
ID: ${r.id}
`).join("");
} catch (err) {
if (err.name !== "AbortError") {
console.error("Search error:", err);
}
}
}, delay);
}
var MAX_ACTIVITY_HISTORY = 100;
function escapeHtml(str) {
if (str === null || str === undefined)
return "";
return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'");
}
function parseActivityPayload(item) {
let category = "TOOL";
let summary = "";
let details = "";
let timestamp = Date.now();
if (typeof item === "string") {
try {
const parsed = JSON.parse(item);
return parseActivityPayload(parsed);
} catch (e) {
summary = item;
}
} else if (typeof item === "object" && item !== null) {
if (item.method === "notifications/activity" && item.params) {
return parseActivityPayload(item.params);
}
category = (item.category || item.type || "TOOL").toUpperCase();
let rawSum = item.summary || item.message || item.description || item.data;
if (rawSum !== undefined && rawSum !== null) {
if (typeof rawSum === "object") {
summary = JSON.stringify(rawSum);
} else {
summary = String(rawSum);
}
}
if (!summary) {
if (item.title)
summary = String(item.title);
else if (item.name)
summary = String(item.name);
else if (item.action)
summary = String(item.action);
else if (item.content)
summary = String(item.content);
else {
const {
timestamp: _t,
time: _tm,
updated_at: _u,
category: _c,
type: _tp,
method: _m,
params: _p,
jsonrpc: _j,
...rest
} = item;
if (Object.keys(rest).length > 0) {
summary = JSON.stringify(rest);
} else {
summary = JSON.stringify(item);
}
}
}
let rawDet = item.details;
if (rawDet !== undefined && rawDet !== null) {
if (typeof rawDet === "object") {
details = JSON.stringify(rawDet);
} else {
details = String(rawDet);
}
}
timestamp = item.timestamp || item.time || item.updated_at || timestamp;
if (typeof timestamp === "number" && timestamp < 10000000000) {
timestamp = timestamp * 1000;
}
}
const catUpper = category.toUpperCase();
const colorMap = {
GRAPH: "#2ecc71",
DECISION: "#f39c12",
CODE: "#3498db",
CODE_CHANGE: "#3498db",
TASK: "#1abc9c",
TASK_CREATE: "#1abc9c",
TASK_UPDATE: "#1abc9c",
CLIPBOARD: "#9b59b6",
HANDOFF_MEMO: "#e67e22",
CHECKPOINT: "#e74c3c",
ERROR_FIX: "#e74c3c",
TECH_DEBT: "#d35400",
SUBAGENT: "#8e44ad",
AGENT_SIGNAL: "#8e44ad",
SNIPPET: "#16a085",
TOOL: "#3498db",
EDITOR: "#9b59b6",
TERMINAL: "#27ae60",
SNAPSHOT: "#f1c40f",
NAMESPACE: "#e67e22",
SYSTEM: "#95a5a6"
};
const badgeColor = colorMap[catUpper] || "#3498db";
const dateObj = new Date(timestamp);
const validDate = isNaN(dateObj.getTime()) ? new Date : dateObj;
const timeStr = validDate.toLocaleString([], {
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit"
});
let html = `[${timeStr}] ${escapeHtml(catUpper)} ${escapeHtml(summary)}`;
if (details) {
html += `${escapeHtml(details)}
`;
}
return html;
}
async function loadActivityHistory() {
try {
const response = await fetch("/api/activity");
const history = await response.json();
const feed = document.getElementById("activity-feed");
if (!feed)
return;
feed.innerHTML = "";
if (!history || history.length === 0) {
feed.innerHTML = 'No recent activity recorded yet.Live activity will stream here in real-time as MCP tools and system events execute.
';
return;
}
const getMillis = (item) => {
if (!item)
return 0;
if (typeof item === "string") {
try {
item = JSON.parse(item);
} catch (e) {
return 0;
}
}
if (item.method === "notifications/activity" && item.params) {
item = item.params;
}
let val = item.timestamp || item.time || item.updated_at || 0;
if (typeof val === "number" && val < 10000000000) {
val = val * 1000;
}
return val;
};
const sorted = [...history].sort((a, b) => getMillis(b) - getMillis(a));
sorted.forEach((item) => {
const div = document.createElement("div");
div.className = "feed-entry";
div.innerHTML = parseActivityPayload(item);
feed.appendChild(div);
});
if (sorted.length > 0) {
feed.scrollTop = 0;
}
} catch (e) {
console.error("Failed to load activity history", e);
}
}
var wsConnected = false;
var activeSSE = null;
var activeWS = null;
var wsRpcId = 1;
var pendingWsRequests = new Map;
function sendWsRpc(method, params = {}, timeoutMs = 5000) {
return new Promise((resolve, reject) => {
if (!activeWS || activeWS.readyState !== WebSocket.OPEN) {
return reject(new Error("WebSocket not open"));
}
const id = ++wsRpcId;
const timer = setTimeout(() => {
pendingWsRequests.delete(id);
reject(new Error(`WebSocket RPC timeout for ${method}`));
}, timeoutMs);
pendingWsRequests.set(id, { resolve, reject, timer });
try {
activeWS.send(JSON.stringify({
jsonrpc: "2.0",
id,
method,
params
}));
} catch (e) {
clearTimeout(timer);
pendingWsRequests.delete(id);
reject(e);
}
});
}
var tabRefreshDebounceTimers = {};
function requestDomainRefresh(domain) {
const tabMap = {
graph: "graph-tab",
task: "task-tab",
techdebt: "techdebt-tab",
adrs: "adrs-tab",
snippets: "snippets-tab",
terminal: "terminal-tab",
ledger: "ledger-tab",
memos: "memos-tab",
env: "env-tab",
gates: "env-tab"
};
const targetTab = tabMap[domain];
if (!targetTab || targetTab !== currentTabId) {
return;
}
if (tabRefreshDebounceTimers[domain]) {
clearTimeout(tabRefreshDebounceTimers[domain]);
}
tabRefreshDebounceTimers[domain] = setTimeout(() => {
delete tabRefreshDebounceTimers[domain];
switch (domain) {
case "graph":
loadGraph();
break;
case "task":
loadTasks();
break;
case "techdebt":
loadTechDebt();
break;
case "adrs":
loadADRs();
break;
case "snippets":
loadSnippets();
break;
case "terminal":
loadTerminal();
break;
case "ledger":
loadLedger();
break;
case "memos":
loadMemos();
break;
case "env":
case "gates":
loadEnvironment();
break;
}
}, 250);
}
function handleIncomingActivity(payload) {
let category = "";
let method = "";
if (typeof payload === "object" && payload !== null) {
method = payload.method || "";
if (payload.params) {
category = (payload.params.category || payload.params.type || "").toUpperCase();
} else {
category = (payload.category || payload.type || "").toUpperCase();
}
}
if (method === "notifications/resources/updated" || category === "GRAPH") {
requestDomainRefresh("graph");
} else if (category === "DECISION" || category === "ADR") {
requestDomainRefresh("adrs");
requestDomainRefresh("graph");
} else if (method === "notifications/task/completed" || category.startsWith("TASK")) {
requestDomainRefresh("task");
} else if (category === "GATE" || category === "QUALITY_GATE" || category === "ENVIRONMENT" || category === "ENV") {
requestDomainRefresh("gates");
requestDomainRefresh("env");
} else if (category === "TECH_DEBT" || category === "ERROR_FIX" || category === "CHECKPOINT") {
requestDomainRefresh("techdebt");
} else if (category === "SNIPPET") {
requestDomainRefresh("snippets");
} else if (category === "TERMINAL") {
requestDomainRefresh("terminal");
} else if (category === "CODE_CHANGE" || category === "LEDGER") {
requestDomainRefresh("ledger");
} else if (category === "MEMO" || category === "MILESTONE" || category === "HANDOFF_MEMO") {
requestDomainRefresh("memos");
}
const feed = document.getElementById("activity-feed");
if (feed) {
if (feed.querySelector(".feed-entry") === null) {
feed.innerHTML = "";
}
const isScrolledToTop = feed.scrollTop <= 20;
const div = document.createElement("div");
div.className = "feed-entry";
div.innerHTML = parseActivityPayload(payload);
feed.prepend(div);
while (feed.children.length > MAX_ACTIVITY_HISTORY && feed.lastChild) {
feed.removeChild(feed.lastChild);
}
if (isScrolledToTop)
feed.scrollTop = 0;
}
}
function setupWS() {
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
let ws;
try {
ws = new WebSocket(`${protocol}//${location.host}/ws?client=ui`);
} catch (e) {
console.error("WebSocket creation error", e);
setupSSE();
return;
}
ws.onopen = function() {
wsConnected = true;
activeWS = ws;
if (activeSSE) {
activeSSE.close();
activeSSE = null;
}
};
ws.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
if (data.id !== undefined && pendingWsRequests.has(data.id)) {
const handler = pendingWsRequests.get(data.id);
pendingWsRequests.delete(data.id);
clearTimeout(handler.timer);
if (data.error) {
handler.reject(new Error(data.error.message || JSON.stringify(data.error)));
} else {
handler.resolve(data.result);
}
return;
}
if (data.type === "activity" || data.type === "gate" || data.type === "task" || data.type === "tech_debt" || data.type === "nvim_telemetry" || data.method === "notifications/activity" || data.method === "notifications/task/completed" || data.method === "notifications/resources/updated") {
const payload = data.params || data.data || data;
handleIncomingActivity(payload);
}
} catch (e) {
console.error("WebSocket message parse error", e);
}
};
ws.onclose = function() {
wsConnected = false;
activeWS = null;
pendingWsRequests.forEach((handler) => {
clearTimeout(handler.timer);
handler.reject(new Error("WebSocket disconnected"));
});
pendingWsRequests.clear();
console.log("WebSocket closed, falling back to SSE and retrying WS in 5s...");
setupSSE();
setTimeout(setupWS, 5000);
};
ws.onerror = function() {
wsConnected = false;
activeWS = null;
pendingWsRequests.forEach((handler) => {
clearTimeout(handler.timer);
handler.reject(new Error("WebSocket error"));
});
pendingWsRequests.clear();
setupSSE();
};
}
var listControllers = new Map;
function applyListFiltering(containerId) {
const state = listControllers.get(containerId);
if (!state)
return;
let result = state.items.slice();
const q = (state.searchQuery || "").trim().toLowerCase();
if (q) {
result = result.filter((item) => {
if (state.options.searchFields) {
const fields = state.options.searchFields(item);
return fields.some((f) => f && String(f).toLowerCase().includes(q));
}
return JSON.stringify(item).toLowerCase().includes(q);
});
}
if (state.options.filters) {
for (const filterDef of state.options.filters) {
const activeVal = state.activeFilters[filterDef.id];
if (activeVal !== undefined && activeVal !== "") {
result = result.filter((item) => filterDef.predicate(item, activeVal));
}
}
}
const factor = state.sortOrder === "desc" ? -1 : 1;
const extractor = state.options.sortExtractor || ((item) => {
if (state.options.timestampExtractor) {
const ts = state.options.timestampExtractor(item);
if (ts != null)
return ts;
}
return item.timestamp || item.created_at || item.id || 0;
});
result.sort((a, b) => {
const valA = extractor(a);
const valB = extractor(b);
if (typeof valA === "number" && typeof valB === "number") {
return (valA - valB) * factor;
}
return String(valA).localeCompare(String(valB)) * factor;
});
state.filteredItems = result;
}
function renderListCardsAndPagination(containerId) {
const state = listControllers.get(containerId);
if (!state)
return;
const totalItems = state.filteredItems.length;
const pageSize = state.pageSize;
const totalPages = pageSize <= 0 ? 1 : Math.max(1, Math.ceil(totalItems / pageSize));
if (state.currentPage > totalPages) {
state.currentPage = totalPages;
}
if (state.currentPage < 1) {
state.currentPage = 1;
}
const startIndex = totalItems === 0 ? 0 : (state.currentPage - 1) * (pageSize > 0 ? pageSize : totalItems) + 1;
const endIndex = pageSize <= 0 ? totalItems : Math.min(state.currentPage * pageSize, totalItems);
const metaEl = document.getElementById(`${containerId}-meta`);
if (metaEl) {
const itemNoun = state.options.itemNoun || "items";
let summaryText = "";
if (totalItems === 0) {
summaryText = `0 ${itemNoun}`;
} else {
summaryText = `Showing ${startIndex}–${endIndex} of ${totalItems} ${itemNoun}`;
}
if (totalItems !== state.items.length) {
summaryText += ` (filtered from ${state.items.length} total) `;
}
metaEl.innerHTML = `${summaryText}
Page ${state.currentPage} of ${totalPages}
`;
}
const cardsEl = document.getElementById(`${containerId}-cards`);
if (cardsEl) {
if (totalItems === 0) {
const itemNoun = state.options.itemNoun || "items";
if (state.items.length === 0) {
cardsEl.innerHTML = `No ${itemNoun} recorded.
`;
} else {
cardsEl.innerHTML = `
\uD83D\uDD0D No matching ${itemNoun} found for current filter.
Reset Filters
`;
}
} else {
const pageSlice = pageSize <= 0 ? state.filteredItems : state.filteredItems.slice((state.currentPage - 1) * pageSize, state.currentPage * pageSize);
cardsEl.innerHTML = pageSlice.map((item) => {
const ts = state.options.timestampExtractor ? state.options.timestampExtractor(item) : item.timestamp || item.created_at || null;
const dateStr = ts ? new Date(ts * 1000).toLocaleString() : "";
const borderCol = state.options.cardBorderColor || "#9b59b6";
return `
${dateStr ? `
${dateStr}
` : ""}
${state.formatter(item)}
`;
}).join("");
}
}
const paginationEl = document.getElementById(`${containerId}-pagination`);
if (paginationEl) {
if (totalPages <= 1) {
paginationEl.style.display = "none";
paginationEl.innerHTML = "";
} else {
paginationEl.style.display = "flex";
let pagHtml = "";
pagHtml += `⏮ `;
pagHtml += `◀ `;
const cur = state.currentPage;
const startP = Math.max(1, cur - 2);
const endP = Math.min(totalPages, cur + 2);
if (startP > 1) {
pagHtml += `1 `;
if (startP > 2)
pagHtml += `… `;
}
for (let p = startP;p <= endP; p++) {
pagHtml += `${p} `;
}
if (endP < totalPages) {
if (endP < totalPages - 1)
pagHtml += `… `;
pagHtml += `${totalPages} `;
}
pagHtml += `▶ `;
pagHtml += `⏭ `;
paginationEl.innerHTML = pagHtml;
}
}
const clearBtn = document.getElementById(`${containerId}-clear`);
if (clearBtn) {
clearBtn.style.display = state.searchQuery ? "block" : "none";
}
const sortBtn = document.getElementById(`${containerId}-sort-btn`);
if (sortBtn) {
sortBtn.innerHTML = state.sortOrder === "desc" ? "⬇️ Newest First" : "⬆️ Oldest First";
}
}
function renderListStructure(containerId) {
const state = listControllers.get(containerId);
if (!state)
return;
const container = document.getElementById(containerId);
if (!container)
return;
const itemNoun = state.options.itemNoun || "items";
let filtersHtml = "";
if (state.options.filters && state.options.filters.length > 0) {
for (const f of state.options.filters) {
const activeVal = state.activeFilters[f.id] || "";
filtersHtml += `
${f.options.map((opt) => `${escapeHtml(opt.label)} `).join("")}
`;
}
}
const curPageSize = state.pageSize;
const pageSizeOptions = [
{ label: "10 / page", value: "10" },
{ label: "25 / page", value: "25" },
{ label: "50 / page", value: "50" },
{ label: "100 / page", value: "100" },
{ label: "All items", value: "-1" }
];
const pageSizeHtml = `
${pageSizeOptions.map((opt) => `${opt.label} `).join("")}
`;
container.innerHTML = `
`;
renderListCardsAndPagination(containerId);
}
function onListSearchInput(containerId, value) {
const state = listControllers.get(containerId);
if (!state)
return;
if (state.debounceTimer)
clearTimeout(state.debounceTimer);
state.searchQuery = value;
state.debounceTimer = setTimeout(() => {
state.currentPage = 1;
applyListFiltering(containerId);
renderListCardsAndPagination(containerId);
}, 150);
}
function onListClearSearch(containerId) {
const state = listControllers.get(containerId);
if (!state)
return;
state.searchQuery = "";
const inputEl = document.getElementById(`${containerId}-search`);
if (inputEl) {
inputEl.value = "";
inputEl.focus();
}
state.currentPage = 1;
applyListFiltering(containerId);
renderListCardsAndPagination(containerId);
}
function onListFilterChange(containerId, filterId, value) {
const state = listControllers.get(containerId);
if (!state)
return;
state.activeFilters[filterId] = value;
state.currentPage = 1;
applyListFiltering(containerId);
renderListCardsAndPagination(containerId);
}
function onListToggleSort(containerId) {
const state = listControllers.get(containerId);
if (!state)
return;
state.sortOrder = state.sortOrder === "desc" ? "asc" : "desc";
applyListFiltering(containerId);
renderListCardsAndPagination(containerId);
}
function onListPageSizeChange(containerId, value) {
const state = listControllers.get(containerId);
if (!state)
return;
state.pageSize = parseInt(value, 10);
state.currentPage = 1;
renderListCardsAndPagination(containerId);
}
function onListPageChange(containerId, page) {
const state = listControllers.get(containerId);
if (!state)
return;
state.currentPage = page;
renderListCardsAndPagination(containerId);
const container = document.getElementById(containerId);
if (container) {
container.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
}
function onListResetFilters(containerId) {
const state = listControllers.get(containerId);
if (!state)
return;
state.searchQuery = "";
state.activeFilters = {};
state.sortOrder = state.options.defaultSort || "desc";
state.pageSize = state.options.pageSize || 25;
state.currentPage = 1;
applyListFiltering(containerId);
renderListStructure(containerId);
}
async function loadGenericList(endpoint, containerId, formatter, options) {
const container = document.getElementById(containerId);
if (!container)
return;
const resolvedOptions = options || {};
const existingState = listControllers.get(containerId);
const state = existingState || {
endpoint,
containerId,
formatter,
options: resolvedOptions,
items: [],
filteredItems: [],
searchQuery: "",
activeFilters: {},
sortOrder: resolvedOptions.defaultSort || "desc",
currentPage: 1,
pageSize: resolvedOptions.pageSize || 25,
debounceTimer: null
};
state.options = resolvedOptions;
state.formatter = formatter;
listControllers.set(containerId, state);
try {
const res = await fetch(endpoint, { cache: "no-store" });
if (!res.ok) {
container.innerHTML = `⚠️ Failed to load ${escapeHtml(endpoint)} (${res.status} ${escapeHtml(res.statusText)})
`;
return;
}
const data = await res.json();
state.items = Array.isArray(data) ? data : [];
applyListFiltering(containerId);
const toolbarExists = document.getElementById(`${containerId}-toolbar`);
if (!toolbarExists) {
renderListStructure(containerId);
} else {
renderListCardsAndPagination(containerId);
}
} catch (err) {
console.error(`Failed to load ${endpoint}`, err);
container.innerHTML = `⚠️ Failed to load ${escapeHtml(endpoint)} (${escapeHtml(String(err))})
`;
}
}
function loadLedger() {
loadGenericList("/api/ledger?limit=500", "ledger-container", (item) => {
const vcs = (item.vcs_type || (item.revision && item.revision.startsWith("r") ? "svn" : "git")).toUpperCase();
const vcsColor = vcs === "SVN" ? "#e67e22" : "#3498db";
const rev = item.revision || item.git_commit;
const revLabel = vcs === "SVN" ? "Rev" : "Commit";
const branch = item.branch || item.git_branch || "None";
const repo = item.repo_name || item.namespace || "global";
const changeKind = (item.change_kind || "modified").toLowerCase();
let kindColor = "var(--accent-color)";
let kindLabel = "Modified";
if (changeKind === "added") {
kindColor = "var(--success-color)";
kindLabel = "+ Added";
} else if (changeKind === "deleted") {
kindColor = "var(--error-color)";
kindLabel = "- Deleted";
} else if (changeKind === "renamed") {
kindColor = "#e67e22";
kindLabel = "→ Renamed";
}
const symbols = Array.isArray(item.symbols) && item.symbols.length > 0 ? item.symbols.map((s) => `${escapeHtml(s)} `).join(" ") : "";
const authorBadge = item.author ? `\uD83D\uDC64 ${escapeHtml(item.author)} ` : "";
const lineBadge = item.line_range ? `${escapeHtml(item.line_range)} ` : "";
return `
${escapeHtml(item.file_path || "Unknown")}
${vcs}
${kindLabel}
${escapeHtml(item.description || "")}
${symbols ? `Symbols: ${symbols}
` : ""}
\uD83D\uDCE6 ${escapeHtml(repo)}
${revLabel}: ${rev ? escapeHtml(rev.substring(0, 10)) : "None"}
\uD83C\uDF3F ${escapeHtml(branch)}
${lineBadge}
${authorBadge}
`;
}, {
itemNoun: "Ledger Entries",
cardBorderColor: "#3498db",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.file_path,
item.description,
item.author,
item.repo_name,
item.branch,
item.git_commit,
item.revision,
Array.isArray(item.symbols) ? item.symbols.join(" ") : ""
],
filters: [
{
id: "vcs",
label: "VCS",
options: [
{ label: "All VCS", value: "" },
{ label: "Git", value: "git" },
{ label: "SVN", value: "svn" }
],
predicate: (item, val) => {
const vcs = (item.vcs_type || (item.revision && item.revision.startsWith("r") ? "svn" : "git")).toLowerCase();
return vcs === val.toLowerCase();
}
},
{
id: "kind",
label: "Change Kind",
options: [
{ label: "All Types", value: "" },
{ label: "+ Added", value: "added" },
{ label: "Modified", value: "modified" },
{ label: "- Deleted", value: "deleted" },
{ label: "→ Renamed", value: "renamed" }
],
predicate: (item, val) => (item.change_kind || "modified").toLowerCase() === val.toLowerCase()
}
]
});
}
function loadTerminal() {
loadGenericList("/api/terminal/history", "terminal-container", (item) => {
const isSuccess = item.exit_code === 0;
const statusColor = isSuccess ? "var(--success-color)" : "var(--error-color)";
const statusBg = isSuccess ? "rgba(46, 204, 113, 0.12)" : "rgba(231, 76, 60, 0.12)";
const durationStr = item.duration_ms != null ? item.duration_ms < 1000 ? `${item.duration_ms}ms` : `${(item.duration_ms / 1000).toFixed(2)}s` : "";
return `
$ ${escapeHtml(item.command)}
${durationStr ? `⏱️ ${escapeHtml(durationStr)} ` : ""}
Exit ${item.exit_code}
${item.shell ? `\uD83D\uDC1A ${escapeHtml(item.shell)} ` : ""}
OS: ${escapeHtml(item.os || "Unknown")}
\uD83D\uDCC2 ${escapeHtml(item.cwd || "Unknown")}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
${item.invoker ? `\uD83D\uDC64 ${escapeHtml(item.invoker)} ` : ""}
${item.status_reason ? `Reason: ${escapeHtml(item.status_reason)}
` : ""}
${item.stdout_summary ? `Output: ${escapeHtml(item.stdout_summary)}
` : ""}
${item.error_output ? `
Error Output
${escapeHtml(item.error_output)}
` : ""}
`;
}, {
itemNoun: "Commands",
cardBorderColor: "#1abc9c",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.command,
item.cwd,
item.repo_name,
item.invoker,
item.shell,
item.stdout_summary,
item.error_output
],
filters: [
{
id: "status",
label: "Exit Code",
options: [
{ label: "All Results", value: "" },
{ label: "Success (0)", value: "0" },
{ label: "Failed (!= 0)", value: "err" }
],
predicate: (item, val) => {
if (val === "0")
return item.exit_code === 0;
if (val === "err")
return item.exit_code !== 0;
return true;
}
}
]
});
}
function loadTechDebt() {
loadGenericList("/api/tech_debts", "techdebt-container", (item) => {
const sev = (item.severity || "medium").toLowerCase();
let sevBg = "rgba(241, 196, 15, 0.15)";
let sevColor = "#f1c40f";
let sevBorder = "rgba(241, 196, 15, 0.4)";
if (sev === "critical" || sev === "blocker") {
sevBg = "rgba(231, 76, 60, 0.18)";
sevColor = "#e74c3c";
sevBorder = "rgba(231, 76, 60, 0.5)";
} else if (sev === "high") {
sevBg = "rgba(230, 126, 34, 0.18)";
sevColor = "#e67e22";
sevBorder = "rgba(230, 126, 34, 0.5)";
} else if (sev === "low" || sev === "trivial") {
sevBg = "rgba(52, 152, 219, 0.15)";
sevColor = "#3498db";
sevBorder = "rgba(52, 152, 219, 0.4)";
}
const statusBadge = item.is_resolved ? '✓ Resolved ' : '● Open ';
const fileLoc = item.file_path ? `${item.file_path}${item.line_range ? `:${item.line_range}` : ""}` : "";
return `
${escapeHtml(item.id)}
${escapeHtml(item.severity || "medium")}
${item.effort_estimate ? `⏳ ${escapeHtml(item.effort_estimate)} ` : ""}
${statusBadge}
${!item.is_resolved ? `
✓ ` : ""}
${fileLoc ? `\uD83D\uDCC4 ${escapeHtml(fileLoc)}
` : ""}
Description: ${escapeHtml(item.description || "")}
${item.workaround ? `Workaround: ${escapeHtml(item.workaround)}
` : ""}
Ideal Solution: ${escapeHtml(item.ideal_solution || "")}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
Commit: ${item.git_commit ? escapeHtml(item.git_commit.substring(0, 8)) : "None"}
\uD83C\uDF3F ${escapeHtml(item.git_branch || "None")}
\uD83C\uDFF7️ ${escapeHtml(item.namespace || "global")}
`;
}, {
itemNoun: "Tech Debts",
cardBorderColor: "#e67e22",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.id,
item.description,
item.ideal_solution,
item.workaround,
item.file_path,
item.repo_name,
item.severity
],
filters: [
{
id: "status",
label: "Status",
options: [
{ label: "All Statuses", value: "" },
{ label: "Open", value: "open" },
{ label: "Resolved", value: "resolved" }
],
predicate: (item, val) => {
if (val === "resolved")
return !!item.is_resolved;
if (val === "open")
return !item.is_resolved;
return true;
}
},
{
id: "severity",
label: "Severity",
options: [
{ label: "All Severities", value: "" },
{ label: "Critical", value: "critical" },
{ label: "High", value: "high" },
{ label: "Medium", value: "medium" },
{ label: "Low", value: "low" }
],
predicate: (item, val) => String(item.severity || "").toLowerCase() === val.toLowerCase()
}
]
});
loadGenericList("/api/error_fixes", "errorfixes-container", (item) => `
${escapeHtml(item.signature || "Error")}
${item.error_category ? `${escapeHtml(item.error_category)} ` : ""}
${item.toolchain ? `\uD83D\uDD27 ${escapeHtml(item.toolchain)} ` : ""}
✓ Resolved
Solution: ${escapeHtml(item.solution || "")}
${item.stack_trace ? `
Stack Trace
${escapeHtml(item.stack_trace)}
` : ""}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
Commit: ${item.git_commit ? escapeHtml(item.git_commit.substring(0, 8)) : "None"}
${item.git_branch ? `\uD83C\uDF3F ${escapeHtml(item.git_branch)} ` : ""}
`, {
itemNoun: "Error Fixes",
cardBorderColor: "#2ecc71",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.signature,
item.solution,
item.toolchain,
item.error_category,
item.repo_name,
item.stack_trace
],
filters: [
{
id: "category",
label: "Category",
options: [
{ label: "All Categories", value: "" },
{ label: "Compilation", value: "compilation" },
{ label: "Runtime", value: "runtime" },
{ label: "Test", value: "test" },
{ label: "Configuration", value: "configuration" }
],
predicate: (item, val) => String(item.error_category || "").toLowerCase() === val.toLowerCase()
}
]
});
}
function loadADRs() {
loadGenericList("/api/adrs", "adrs-container", (item) => {
const statusLower = (item.status || "").toLowerCase();
let badgeStyle = "color:var(--accent-color); border-color:rgba(88, 166, 255, 0.4); background:rgba(88, 166, 255, 0.12);";
let badgeText = `● ${escapeHtml(item.status || "accepted")}`;
if (statusLower === "implemented" || statusLower === "resolved") {
badgeStyle = "color:var(--success-color); border-color:rgba(46, 160, 67, 0.4); background:rgba(46, 160, 67, 0.15); font-weight:600;";
badgeText = `✓ ${escapeHtml(item.status)}`;
} else if (statusLower === "superseded") {
badgeStyle = "text-decoration: line-through; color:var(--error-color); border-color:rgba(248, 81, 73, 0.4); background:rgba(248, 81, 73, 0.12);";
badgeText = `✕ ${escapeHtml(item.status)}`;
} else if (statusLower === "proposed" || statusLower === "draft") {
badgeStyle = "color:var(--warning-color); border-color:rgba(210, 153, 34, 0.4); background:rgba(210, 153, 34, 0.12);";
badgeText = `○ ${escapeHtml(item.status)}`;
}
const resolvedDate = item.resolved_at ? new Date(item.resolved_at * 1000).toISOString().replace("T", " ").substring(0, 19) : "";
return `
${escapeHtml(item.id)} | ${escapeHtml(item.title)}
${badgeText}
${item.git_commit || item.git_branch || item.task_id || item.resolved_at ? `
${item.git_branch || item.git_commit ? `\uD83D\uDCE6 ${escapeHtml(item.git_branch || "repo")} @ ${escapeHtml((item.git_commit || "").substring(0, 8))} ` : ""}
${item.task_id ? `\uD83C\uDFAF Task: ${escapeHtml(item.task_id)} ` : ""}
${resolvedDate ? `✓ Resolved: ${escapeHtml(resolvedDate)} ` : ""}
` : ""}
Context: ${escapeHtml(item.context || "")}
Decision: ${escapeHtml(item.decision || "")}
Consequence: ${escapeHtml(item.consequence || "")}
${item.supersedes ? `Supersedes: ${escapeHtml(item.supersedes)}
` : ""}
`;
}, {
itemNoun: "ADRs",
cardBorderColor: "#3498db",
defaultSort: "desc",
sortExtractor: (item) => {
const match = String(item.id || "").match(/\d+/);
if (match)
return parseInt(match[0], 10);
return item.timestamp || 0;
},
searchFields: (item) => [
item.id,
item.title,
item.context,
item.decision,
item.consequence,
item.status,
item.git_branch,
item.task_id
],
filters: [
{
id: "status",
label: "Status",
options: [
{ label: "All Statuses", value: "" },
{ label: "Accepted", value: "accepted" },
{ label: "Implemented", value: "implemented" },
{ label: "Proposed", value: "proposed" },
{ label: "Draft", value: "draft" },
{ label: "Superseded", value: "superseded" }
],
predicate: (item, val) => String(item.status || "").toLowerCase() === val.toLowerCase()
}
]
});
}
function loadMemos() {
loadGenericList("/api/handoff_memos", "memos-container", (item) => {
const blockers = Array.isArray(item.blockers) && item.blockers.length > 0 ? item.blockers : [];
const actionItems = Array.isArray(item.action_items) && item.action_items.length > 0 ? item.action_items : [];
return `
\uD83D\uDC64 Memo from ${escapeHtml(item.author || "System")}
${escapeHtml(item.namespace || "global")}
${item.git_branch || item.vcs_revision || item.session_id ? `
${item.git_branch ? `\uD83C\uDF3F ${escapeHtml(item.git_branch)} ` : ""}
${item.vcs_revision ? `Rev: ${escapeHtml(item.vcs_revision.substring(0, 8))} ` : ""}
${item.session_id ? `Session: ${escapeHtml(item.session_id)} ` : ""}
` : ""}
${escapeHtml(item.content || item.summary || "")}
${blockers.length > 0 ? `
\uD83D\uDEAB Blockers:
${blockers.map((b) => `${escapeHtml(b)} `).join("")}
` : ""}
${actionItems.length > 0 ? `
\uD83D\uDCCB Action Items:
${actionItems.map((a) => `${escapeHtml(a)} `).join("")}
` : ""}
`;
}, {
itemNoun: "Memos",
cardBorderColor: "#9b59b6",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.author,
item.content,
item.summary,
item.namespace,
item.git_branch
]
});
loadGenericList("/api/session_summaries", "summaries-container", (item) => `
Session ${item.session_id ? escapeHtml(item.session_id) : "Summary"}
${escapeHtml(item.namespace || "global")}
${item.git_commit || item.git_branch ? `${escapeHtml(item.git_branch || "")} @ ${escapeHtml((item.git_commit || "").substring(0, 8))}
` : ""}
${escapeHtml(item.summary || "")}
${item.tasks_completed && item.tasks_completed.length > 0 ? `Tasks Completed: ${item.tasks_completed.map((t) => `${escapeHtml(t)} `).join("")} ` : ""}
${item.next_steps && item.next_steps.length > 0 ? `Next Steps: ${item.next_steps.map((s) => `${escapeHtml(s)} `).join("")} ` : ""}
`, {
itemNoun: "Summaries",
cardBorderColor: "#34495e",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.session_id,
item.summary,
item.namespace,
item.git_branch
]
});
loadGenericList("/api/milestones", "milestones-container", (item) => {
const statusLower = (item.status || "pending").toLowerCase();
let statusBg = "rgba(52, 152, 219, 0.15)";
let statusColor = "var(--accent-color)";
let statusLabel = item.status || "Pending";
if (statusLower === "completed" || statusLower === "done") {
statusBg = "rgba(46, 204, 113, 0.15)";
statusColor = "var(--success-color)";
statusLabel = "✓ Completed";
} else if (statusLower === "in_progress" || statusLower === "active") {
statusBg = "rgba(230, 126, 34, 0.15)";
statusColor = "var(--open-color)";
statusLabel = "● In Progress";
}
const targetDate = item.target_date ? new Date(item.target_date * 1000).toLocaleDateString() : "";
const completedDate = item.completed_at ? new Date(item.completed_at * 1000).toLocaleDateString() : "";
const deliverables = Array.isArray(item.deliverables) && item.deliverables.length > 0 ? item.deliverables : [];
const taskIds = Array.isArray(item.task_ids) && item.task_ids.length > 0 ? item.task_ids : [];
return `
${escapeHtml(item.title || item.name || "Milestone")}
${escapeHtml(statusLabel)}
${targetDate || completedDate ? `
${targetDate ? `\uD83C\uDFAF Target: ${escapeHtml(targetDate)} ` : ""}
${completedDate ? `✓ Finished: ${escapeHtml(completedDate)} ` : ""}
` : ""}
${escapeHtml(item.description || "")}
${deliverables.length > 0 ? `
Deliverables:
${deliverables.map((d) => `${escapeHtml(d)} `).join("")}
` : ""}
${taskIds.length > 0 ? `
Linked Tasks:
${taskIds.map((tid) => `${escapeHtml(tid)} `).join("")}
` : ""}
`;
}, {
itemNoun: "Milestones",
cardBorderColor: "#f39c12",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp || item.target_date,
searchFields: (item) => [
item.title,
item.name,
item.description,
item.status
],
filters: [
{
id: "status",
label: "Status",
options: [
{ label: "All Statuses", value: "" },
{ label: "Pending", value: "pending" },
{ label: "In Progress", value: "in_progress" },
{ label: "Completed", value: "completed" }
],
predicate: (item, val) => String(item.status || "pending").toLowerCase() === val.toLowerCase()
}
]
});
}
function loadSnippets() {
loadGenericList("/api/snippets", "snippets-container", (item) => {
const code = item.code || item.content || "";
const tags = Array.isArray(item.tags) ? item.tags : [];
const origin = item.origin_file ? `${item.origin_file}${item.line_range ? `:${item.line_range}` : ""}` : "";
return `
${escapeHtml(item.name || item.description || "Snippet")}
${item.language ? `${escapeHtml(item.language)} ` : ""}
\uD83D\uDCCB Copy
${item.description && item.description !== item.name ? `${escapeHtml(item.description)}
` : ""}
${origin ? `\uD83D\uDCC4 ${escapeHtml(origin)}
` : ""}
${escapeHtml(code)}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
${tags.map((t) => `#${escapeHtml(t)} `).join(" ")}
`;
}, {
itemNoun: "Snippets",
cardBorderColor: "#27ae60",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [
item.name,
item.description,
item.code,
item.language,
item.origin_file,
Array.isArray(item.tags) ? item.tags.join(" ") : ""
],
filters: [
{
id: "language",
label: "Language",
options: [
{ label: "All Languages", value: "" },
{ label: "Rust", value: "rust" },
{ label: "TypeScript / JS", value: "typescript" },
{ label: "Python", value: "python" },
{ label: "SQL", value: "sql" },
{ label: "Shell / Bash", value: "bash" },
{ label: "Markdown / Text", value: "markdown" }
],
predicate: (item, val) => {
const lang = String(item.language || "").toLowerCase();
if (val === "typescript")
return lang === "typescript" || lang === "javascript" || lang === "ts" || lang === "js";
if (val === "bash")
return lang === "bash" || lang === "sh" || lang === "pwsh" || lang === "powershell";
return lang === val.toLowerCase();
}
}
]
});
}
function loadEnvironment() {
loadGenericList("/api/environments", "environments-container", (item) => {
const isVpn = item.requires_vpn;
const envType = (item.env_type || "dev").toLowerCase();
let typeColor = "#3498db";
if (envType === "prod")
typeColor = "#e74c3c";
else if (envType === "staging")
typeColor = "#f39c12";
else if (envType === "qa")
typeColor = "#9b59b6";
return `
${escapeHtml(item.name || "Environment")}
${escapeHtml(envType)}
${isVpn ? '\uD83D\uDD12 VPN Required ' : ""}
${escapeHtml(item.namespace || "default")}
${item.url ? `` : ""}
${item.description ? `${escapeHtml(item.description)}
` : ""}
${item.ssh_host ? `SSH: ${escapeHtml(item.ssh_host)} ` : ""}
${item.healthcheck_endpoint ? `Health: ${escapeHtml(item.healthcheck_endpoint)} ` : ""}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
`;
}, {
itemNoun: "Environments",
cardBorderColor: "#3498db",
defaultSort: "desc",
timestampExtractor: (item) => item.updated_at,
searchFields: (item) => [item.name, item.url, item.description, item.env_type, item.ssh_host, item.namespace]
});
loadGenericList("/api/env_fingerprints", "fingerprints-container", (item) => {
const toolVersions = item.tool_versions || {};
const tools = Object.entries(toolVersions).map(([k, v]) => `${escapeHtml(k)}: ${escapeHtml(String(v))} `).join(" ");
return `
\uD83D\uDCBB ${escapeHtml(item.os || "Unknown OS")} (${escapeHtml(item.shell || "shell")})
${escapeHtml(item.namespace || "default")}
${tools ? `${tools}
` : 'No tool versions registered
'}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
`;
}, {
itemNoun: "Fingerprints",
cardBorderColor: "#1abc9c",
defaultSort: "desc",
timestampExtractor: (item) => item.updated_at,
searchFields: (item) => [item.namespace, item.os, item.shell, item.repo_name, JSON.stringify(item.tool_versions || {})]
});
loadGenericList("/api/env_requirements", "requirements-container", (item) => {
const isSecret = item.is_secret;
return `
${escapeHtml(item.key)}
${isSecret ? '\uD83D\uDD12 Secret ' : 'Public '}
${escapeHtml(item.namespace || "default")}
${item.description ? `${escapeHtml(item.description)}
` : ""}
${item.default_value ? `Default: ${escapeHtml(item.default_value)} ` : ""}
${item.validation_regex ? `Regex: ${escapeHtml(item.validation_regex)} ` : ""}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
`;
}, {
itemNoun: "Requirements",
cardBorderColor: "#e67e22",
defaultSort: "desc",
searchFields: (item) => [item.key, item.description, item.default_value, item.namespace, item.repo_name]
});
loadGenericList("/api/gates", "gates-container", (item) => {
const status = (item.status || "pending").toLowerCase();
let statusColor = "var(--accent-color)";
if (status === "authorized")
statusColor = "var(--success-color)";
else if (status === "blocked")
statusColor = "var(--error-color)";
return `
${escapeHtml(item.action)} → ${escapeHtml(item.target)}
${escapeHtml(status)}
${escapeHtml(item.namespace || "global")}
${status !== "authorized" ? `✓ Authorize ` : ""}
${status !== "blocked" ? `\uD83D\uDEAB Block ` : ""}
✕
${item.reason ? `${escapeHtml(item.reason)}
` : ""}
${item.gate_type ? `Type: ${escapeHtml(item.gate_type)} ` : ""}
${item.enforcer ? `Enforcer: ${escapeHtml(item.enforcer)} ` : ""}
${item.repo_name ? `\uD83D\uDCE6 ${escapeHtml(item.repo_name)} ` : ""}
`;
}, {
itemNoun: "Gates",
cardBorderColor: "#e74c3c",
defaultSort: "desc",
timestampExtractor: (item) => item.timestamp,
searchFields: (item) => [item.action, item.target, item.status, item.reason, item.enforcer]
});
}
function loadAllExtras() {
loadTerminal();
loadTechDebt();
loadADRs();
loadMemos();
loadSnippets();
loadLedger();
}
async function testClipboard() {
const modal = document.getElementById("clipboard-modal");
const resultDiv = document.getElementById("clipboard-result");
if (modal)
modal.style.display = "flex";
if (resultDiv)
resultDiv.innerHTML = 'Analyzing your clipboard...
';
try {
const res = await fetch("/api/clipboard/capture", { method: "POST" });
const data = await res.json();
if (resultDiv) {
if (data.success) {
resultDiv.innerHTML = ` `;
} else {
resultDiv.innerHTML = `Failed to read clipboard: ${data.error}
`;
}
}
} catch (err) {
if (resultDiv) {
resultDiv.innerHTML = `Error calling endpoint: ${err.message}
`;
}
}
}
async function loadVersion() {
try {
const res = await fetch("/api/version");
const data = await res.json();
const verEl = document.getElementById("app-version");
if (verEl) {
verEl.innerHTML = `v${data.version} `;
}
} catch (err) {
console.error("Failed to load version", err);
}
}
function setupSSE() {
if (wsConnected || activeSSE)
return;
try {
activeSSE = new EventSource("/api/activity/stream");
activeSSE.onmessage = function(event) {
if (event.data && !wsConnected) {
try {
const parsed = JSON.parse(event.data);
handleIncomingActivity(parsed);
} catch (e) {
handleIncomingActivity(event.data);
}
}
};
activeSSE.onerror = function() {
if (activeSSE) {
activeSSE.close();
activeSSE = null;
}
};
} catch (e) {
console.error("SSE initialization error", e);
}
}
async function resolveTechDebt(id) {
try {
if (activeWS && activeWS.readyState === WebSocket.OPEN) {
await sendWsRpc("tech_debts/resolve", { id });
} else {
const res = await fetch(`/api/tech_debts/${encodeURIComponent(id)}/resolve`, { method: "POST" });
if (!res.ok)
console.error("Failed to resolve tech debt", await res.text());
}
loadTechDebt();
} catch (err) {
console.error("Error resolving tech debt via WS, trying HTTP fallback", err);
try {
const res = await fetch(`/api/tech_debts/${encodeURIComponent(id)}/resolve`, { method: "POST" });
if (!res.ok)
console.error("Failed to resolve tech debt", await res.text());
loadTechDebt();
} catch (fallbackErr) {
console.error("Error resolving tech debt", fallbackErr);
}
}
}
async function authorizeGate(id) {
try {
if (activeWS && activeWS.readyState === WebSocket.OPEN) {
await sendWsRpc("gates/authorize", { id });
} else {
const res = await fetch(`/api/gates/${encodeURIComponent(id)}/authorize`, { method: "POST" });
if (!res.ok)
console.error("Failed to authorize gate", await res.text());
}
loadEnvironment();
} catch (err) {
console.error("Error authorizing gate via WS, trying HTTP fallback", err);
try {
const res = await fetch(`/api/gates/${encodeURIComponent(id)}/authorize`, { method: "POST" });
if (!res.ok)
console.error("Failed to authorize gate", await res.text());
loadEnvironment();
} catch (fallbackErr) {
console.error("Error authorizing gate", fallbackErr);
}
}
}
async function blockGate(id) {
try {
if (activeWS && activeWS.readyState === WebSocket.OPEN) {
await sendWsRpc("gates/block", { id });
} else {
const res = await fetch(`/api/gates/${encodeURIComponent(id)}/block`, { method: "POST" });
if (!res.ok)
console.error("Failed to block gate", await res.text());
}
loadEnvironment();
} catch (err) {
console.error("Error blocking gate via WS, trying HTTP fallback", err);
try {
const res = await fetch(`/api/gates/${encodeURIComponent(id)}/block`, { method: "POST" });
if (!res.ok)
console.error("Failed to block gate", await res.text());
loadEnvironment();
} catch (fallbackErr) {
console.error("Error blocking gate", fallbackErr);
}
}
}
async function deleteGate(id) {
try {
if (activeWS && activeWS.readyState === WebSocket.OPEN) {
await sendWsRpc("gates/delete", { id });
} else {
const res = await fetch(`/api/gates/${encodeURIComponent(id)}`, { method: "DELETE" });
if (!res.ok)
console.error("Failed to delete gate", await res.text());
}
loadEnvironment();
} catch (err) {
console.error("Error deleting gate via WS, trying HTTP fallback", err);
try {
const res = await fetch(`/api/gates/${encodeURIComponent(id)}`, { method: "DELETE" });
if (!res.ok)
console.error("Failed to delete gate", await res.text());
loadEnvironment();
} catch (fallbackErr) {
console.error("Error deleting gate", fallbackErr);
}
}
}
var paletteItems = [];
var filteredPaletteItems = [];
var paletteSelectedIndex = 0;
var paletteDataLoaded = false;
async function preloadPaletteData() {
try {
const [tasksRes, debtsRes, adrsRes, envsRes, gatesRes] = await Promise.all([
fetch("/api/tasks", { cache: "no-store" }).then((r) => r.json()).catch(() => []),
fetch("/api/tech_debts", { cache: "no-store" }).then((r) => r.json()).catch(() => []),
fetch("/api/adrs", { cache: "no-store" }).then((r) => r.json()).catch(() => []),
fetch("/api/environments", { cache: "no-store" }).then((r) => r.json()).catch(() => []),
fetch("/api/gates", { cache: "no-store" }).then((r) => r.json()).catch(() => [])
]);
const items = [
{ id: "nav-activity", category: "nav", icon: "⚡", title: "Live Activity Feed", sub: "Inspect real-time agent signals & SSE", badge: "VIEW", tabId: "activity-tab" },
{ id: "nav-ledger", category: "nav", icon: "\uD83D\uDCD6", title: "Code Ledger", sub: "View logged code changes & commits", badge: "VIEW", tabId: "ledger-tab" },
{ id: "nav-graph", category: "nav", icon: "\uD83D\uDD78️", title: "Knowledge Graph Explorer", sub: "Explore nodes, relationships & dependencies", badge: "VIEW", tabId: "graph-tab" },
{ id: "nav-search", category: "nav", icon: "\uD83D\uDD0D", title: "Omni-Search", sub: "Full-text cross-entity search", badge: "VIEW", tabId: "search-tab" },
{ id: "nav-tasks", category: "nav", icon: "\uD83D\uDCCB", title: "Task Board", sub: "HTN / DAG task hierarchies", badge: "VIEW", tabId: "task-tab" },
{ id: "nav-techdebt", category: "nav", icon: "⚡", title: "Tech Debt & Error Fixes", sub: "Known tech debt and error fixes", badge: "VIEW", tabId: "techdebt-tab" },
{ id: "nav-adrs", category: "nav", icon: "\uD83D\uDCDC", title: "Architectural Decision Records", sub: "ADRs and design choices", badge: "VIEW", tabId: "adrs-tab" },
{ id: "nav-memos", category: "nav", icon: "\uD83D\uDCDD", title: "Handoff Memos", sub: "Agent handoffs & session summaries", badge: "VIEW", tabId: "memos-tab" },
{ id: "nav-snippets", category: "nav", icon: "\uD83D\uDCCE", title: "Code Snippets", sub: "Indexed snippets & patterns", badge: "VIEW", tabId: "snippets-tab" },
{ id: "nav-terminal", category: "nav", icon: "\uD83D\uDCBB", title: "Terminal History", sub: "Command execution log & exit codes", badge: "VIEW", tabId: "terminal-tab" },
{ id: "nav-env", category: "nav", icon: "\uD83C\uDF10", title: "Environment & Quality Gates", sub: "Deploy targets, fingerprints, requirements & gates", badge: "VIEW", tabId: "env-tab" }
];
if (Array.isArray(tasksRes)) {
tasksRes.forEach((t) => {
items.push({
id: t.id,
category: "task",
icon: t.status === "completed" ? "✓" : "\uD83D\uDCCB",
title: t.title || t.id,
sub: t.description || `Task (${t.status || "pending"})`,
badge: t.status || "TASK",
tabId: "task-tab"
});
});
}
if (Array.isArray(debtsRes)) {
debtsRes.forEach((d) => {
items.push({
id: d.id,
category: "techdebt",
icon: "⚡",
title: `${d.id}: ${d.description || ""}`.trim(),
sub: d.file_path ? `${d.file_path} - Severity: ${d.severity || "medium"}` : `Severity: ${d.severity || "medium"}`,
badge: d.is_resolved ? "RESOLVED" : d.severity || "DEBT",
tabId: "techdebt-tab"
});
});
}
if (Array.isArray(adrsRes)) {
adrsRes.forEach((a) => {
items.push({
id: a.id,
category: "adr",
icon: "\uD83D\uDCDC",
title: `${a.id}: ${a.title || ""}`.trim(),
sub: a.decision || `Status: ${a.status || "accepted"}`,
badge: a.status || "ADR",
tabId: "adrs-tab"
});
});
}
if (Array.isArray(envsRes)) {
envsRes.forEach((e) => {
items.push({
id: e.name || e.url,
category: "env",
icon: "\uD83C\uDF10",
title: e.name || "Target Environment",
sub: `${e.url || ""} ${e.description ? "• " + e.description : ""}`.trim(),
badge: e.env_type || "ENV",
tabId: "env-tab"
});
});
}
if (Array.isArray(gatesRes)) {
gatesRes.forEach((g) => {
items.push({
id: g.id,
category: "gate",
icon: g.status === "authorized" ? "\uD83D\uDFE2" : g.status === "blocked" ? "\uD83D\uDD34" : "\uD83D\uDFE1",
title: `${g.action} → ${g.target}`,
sub: g.reason || `Enforcer: ${g.enforcer || "gate"}`,
badge: g.status || "GATE",
tabId: "env-tab"
});
});
}
paletteItems = items;
paletteDataLoaded = true;
} catch (err) {
console.error("Failed to preload palette data", err);
}
}
function openCommandPalette() {
const backdrop = document.getElementById("cmd-palette-backdrop");
const input = document.getElementById("palette-input");
if (!backdrop || !input)
return;
backdrop.style.display = "flex";
input.value = "";
paletteSelectedIndex = 0;
preloadPaletteData().then(() => {
onPaletteSearch("");
input.focus();
});
}
function closeCommandPalette() {
const backdrop = document.getElementById("cmd-palette-backdrop");
if (backdrop)
backdrop.style.display = "none";
}
function onPaletteBackdropClick(e) {
if (e.target === document.getElementById("cmd-palette-backdrop")) {
closeCommandPalette();
}
}
function onPaletteSearch(query) {
const q = query.toLowerCase().trim();
if (!q) {
filteredPaletteItems = paletteItems.slice(0, 30);
} else {
filteredPaletteItems = paletteItems.filter((item) => item.title.toLowerCase().includes(q) || item.sub.toLowerCase().includes(q) || item.badge.toLowerCase().includes(q) || item.category.toLowerCase().includes(q)).slice(0, 40);
}
paletteSelectedIndex = 0;
renderPaletteResults();
}
function renderPaletteResults() {
const container = document.getElementById("palette-results");
const countEl = document.getElementById("palette-count");
if (!container)
return;
if (countEl)
countEl.innerText = `${filteredPaletteItems.length} results`;
if (filteredPaletteItems.length === 0) {
container.innerHTML = 'No matching items found. ';
return;
}
container.innerHTML = filteredPaletteItems.map((item, idx) => {
const isSel = idx === paletteSelectedIndex;
return `
${escapeHtml(item.icon)}
${escapeHtml(item.title)}
${escapeHtml(item.sub)}
${escapeHtml(item.badge)}
`;
}).join("");
const selectedEl = container.querySelector(`.palette-item[data-index="${paletteSelectedIndex}"]`);
if (selectedEl) {
selectedEl.scrollIntoView({ block: "nearest" });
}
}
function selectPaletteItem(idx) {
const item = filteredPaletteItems[idx];
if (!item)
return;
closeCommandPalette();
const tabBtn = document.querySelector(`button[onclick*="${item.tabId}"]`);
if (tabBtn) {
switchTab(item.tabId, tabBtn);
}
}
function onPaletteKeyDown(e) {
if (e.key === "ArrowDown") {
e.preventDefault();
if (filteredPaletteItems.length > 0) {
paletteSelectedIndex = (paletteSelectedIndex + 1) % filteredPaletteItems.length;
renderPaletteResults();
}
} else if (e.key === "ArrowUp") {
e.preventDefault();
if (filteredPaletteItems.length > 0) {
paletteSelectedIndex = (paletteSelectedIndex - 1 + filteredPaletteItems.length) % filteredPaletteItems.length;
renderPaletteResults();
}
} else if (e.key === "Enter") {
e.preventDefault();
selectPaletteItem(paletteSelectedIndex);
} else if (e.key === "Escape") {
e.preventDefault();
closeCommandPalette();
}
}
document.addEventListener("keydown", function(e) {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
e.preventDefault();
const backdrop = document.getElementById("cmd-palette-backdrop");
if (backdrop && backdrop.style.display === "flex") {
closeCommandPalette();
} else {
openCommandPalette();
}
return;
}
if (e.key === "Escape") {
const backdrop = document.getElementById("cmd-palette-backdrop");
if (backdrop && backdrop.style.display === "flex") {
closeCommandPalette();
return;
}
}
if (e.key === "/" && document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
const backdrop = document.getElementById("cmd-palette-backdrop");
if (backdrop && backdrop.style.display === "flex")
return;
const activeSearch = document.querySelector(".tab-content.active .enterprise-search-input, .tab-content.active #task-filter-input");
if (activeSearch) {
e.preventDefault();
activeSearch.focus();
activeSearch.select();
}
}
});
function refreshActiveTab() {
switch (currentTabId) {
case "graph-tab":
loadGraph();
break;
case "task-tab":
loadTasks();
break;
case "techdebt-tab":
loadTechDebt();
break;
case "adrs-tab":
loadADRs();
break;
case "memos-tab":
loadMemos();
break;
case "snippets-tab":
loadSnippets();
break;
case "terminal-tab":
loadTerminal();
break;
case "ledger-tab":
loadLedger();
break;
case "env-tab":
loadEnvironment();
break;
case "activity-tab":
loadActivityHistory();
break;
}
}
Object.assign(window, {
switchTab,
toggleTheme,
getColorForType,
zoomGraph,
resetGraph,
closeInspector,
showInspector,
toggleFilter,
renderFilters,
updateGraphData,
loadGraph,
completeTask,
buildTaskTreeHTML,
loadTasks,
handleTaskFilter,
handleTaskStatusFilter,
highlightText,
handleSearch,
escapeHtml,
parseActivityPayload,
loadActivityHistory,
requestDomainRefresh,
handleIncomingActivity,
setupWS,
sendWsRpc,
loadGenericList,
onListSearchInput,
onListClearSearch,
onListFilterChange,
onListToggleSort,
onListPageSizeChange,
onListPageChange,
onListResetFilters,
loadTerminal,
loadLedger,
loadTechDebt,
loadADRs,
loadMemos,
loadSnippets,
loadAllExtras,
loadEnvironment,
resolveTechDebt,
authorizeGate,
blockGate,
deleteGate,
openCommandPalette,
closeCommandPalette,
onPaletteBackdropClick,
onPaletteSearch,
onPaletteKeyDown,
selectPaletteItem,
testClipboard,
loadVersion,
setupSSE,
refreshActiveTab
});
loadVersion();
loadActivityHistory();
setupWS();
var observer = new MutationObserver(() => updateGraphData());
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"]
});