feat(embedding,vision): candle embeddings with offline fallback, on-demand clipboard vision capture, and concurrency audit

This commit is contained in:
Riz Ashraf committed 2026-10-07 06:36:09 +01:00
1 parent 5bd8b1587a
commit e4a0fe72df
47 files changed
+6292 -3503

No files matched your search

+106 -147
View File
@@ -23,36 +23,24 @@ function switchTab(tabId, btn) {
case "task-tab":
loadTasks();
break;
case "preferences-tab":
loadPreferences();
break;
case "sticky-tab":
loadStickyNotes();
break;
case "techdebt-tab":
loadTechDebt();
break;
case "adrs-tab":
loadADRs();
break;
case "workspaces-tab":
loadWorkspaces();
break;
case "pinned-tab":
loadPinned();
break;
case "memos-tab":
loadMemos();
break;
case "snippets-tab":
loadSnippets();
break;
case "pr-tab":
loadPRs();
break;
case "terminal-tab":
loadTerminal();
break;
case "ledger-tab":
loadLedger();
break;
}
}
function toggleTheme() {
@@ -89,7 +77,10 @@ function getColorForType(type) {
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%)` };
return {
background: `hsl(${hue}, 70%, 60%)`,
border: `hsl(${hue}, 70%, 40%)`
};
}
function zoomGraph(step) {
if (!network)
@@ -100,7 +91,9 @@ function zoomGraph(step) {
function resetGraph() {
if (!network)
return;
network.fit({ animation: { duration: 500, easingFunction: "easeInOutQuad" } });
network.fit({
animation: { duration: 500, easingFunction: "easeInOutQuad" }
});
}
function closeInspector() {
const inspector = document.getElementById("inspector-panel");
@@ -176,7 +169,9 @@ function updateGraphData() {
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" }
font: {
color: document.documentElement.getAttribute("data-theme") === "dark" ? "#eee" : "#333"
}
});
nodeIds.add(name);
}
@@ -188,7 +183,10 @@ Type: ${entity.entity_type}`,
to: r.to,
label: r.relation_type,
arrows: "to",
font: { color: document.documentElement.getAttribute("data-theme") === "dark" ? "#aaa" : "#666", strokeWidth: 0 }
font: {
color: document.documentElement.getAttribute("data-theme") === "dark" ? "#aaa" : "#666",
strokeWidth: 0
}
});
}
});
@@ -218,12 +216,28 @@ async function loadGraph() {
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" } },
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 }
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 }
};
@@ -466,7 +480,17 @@ function parseActivityPayload(item) {
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;
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 {
@@ -579,15 +603,11 @@ function requestDomainRefresh(domain) {
const tabMap = {
graph: "graph-tab",
task: "task-tab",
sticky: "sticky-tab",
techdebt: "techdebt-tab",
snippets: "snippets-tab",
terminal: "terminal-tab",
memos: "memos-tab",
workspaces: "workspaces-tab",
pinned: "pinned-tab",
pr: "pr-tab",
preferences: "preferences-tab"
ledger: "ledger-tab",
memos: "memos-tab"
};
const targetTab = tabMap[domain];
if (!targetTab || targetTab !== currentTabId) {
@@ -605,9 +625,6 @@ function requestDomainRefresh(domain) {
case "task":
loadTasks();
break;
case "sticky":
loadStickyNotes();
break;
case "techdebt":
loadTechDebt();
break;
@@ -617,21 +634,12 @@ function requestDomainRefresh(domain) {
case "terminal":
loadTerminal();
break;
case "ledger":
loadLedger();
break;
case "memos":
loadMemos();
break;
case "workspaces":
loadWorkspaces();
break;
case "pinned":
loadPinned();
break;
case "pr":
loadPRs();
break;
case "preferences":
loadPreferences();
break;
}
}, 250);
}
@@ -650,24 +658,16 @@ function handleIncomingActivity(payload) {
requestDomainRefresh("graph");
} else if (method === "notifications/task/completed" || category.startsWith("TASK")) {
requestDomainRefresh("task");
} else if (category === "STICKY_NOTE" || category === "STICKY") {
requestDomainRefresh("sticky");
} 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");
} else if (category === "WORKSPACE" || category === "CONTEXT_WORKSPACE") {
requestDomainRefresh("workspaces");
} else if (category === "PINNED" || category === "PINNED_FILE") {
requestDomainRefresh("pinned");
} else if (category === "PR" || category === "PR_CHECKLIST") {
requestDomainRefresh("pr");
} else if (category === "PREFERENCE" || category === "PREFERENCES") {
requestDomainRefresh("preferences");
}
const feed = document.getElementById("activity-feed");
if (feed) {
@@ -725,51 +725,6 @@ function setupWS() {
setupSSE();
};
}
async function loadPreferences() {
try {
const res = await fetch("/api/preferences");
const data = await res.json();
const container = document.getElementById("preferences-container");
if (!container)
return;
container.innerHTML = "";
if (!data || Object.keys(data).length === 0) {
container.innerHTML = '<div style="color:var(--text-secondary); text-align:center; padding:20px;">No global preferences found.</div>';
return;
}
for (const [key, pref] of Object.entries(data)) {
const date = new Date(pref.updated_at * 1000).toLocaleString();
container.innerHTML += `
<div style="background:var(--card-bg); border:1px solid var(--border-color); border-radius:6px; padding:15px; box-shadow:0 1px 3px rgba(0,0,0,0.1);">
<strong style="color:#3498db; display:block; margin-bottom:5px; font-size:1.1em;">${key}</strong>
<div style="margin-bottom:10px;">${pref.value}</div>
<div style="font-size:0.8em; color:var(--text-secondary); text-align:right;">Last Updated: ${date}</div>
</div>`;
}
} catch (e) {
console.error("Error loading preferences:", e);
}
}
async function loadStickyNotes() {
try {
const res = await fetch("/api/sticky");
const sticky = await res.json();
const container = document.getElementById("sticky-notes-container");
if (!container)
return;
container.innerHTML = "";
sticky.forEach((note) => {
const card = document.createElement("div");
card.className = "sticky-note";
const date = new Date(note.timestamp * 1000).toLocaleString();
card.innerHTML = `<div style="font-size:0.75em; color:#7f8c8d; margin-bottom:8px;">${date}</div>
<div>${note.content}</div>`;
container.appendChild(card);
});
} catch (err) {
console.error("Failed to load sticky notes", err);
}
}
async function loadGenericList(endpoint, containerId, formatter) {
try {
const res = await fetch(endpoint);
@@ -792,6 +747,50 @@ async function loadGenericList(endpoint, containerId, formatter) {
console.error(`Failed to load ${endpoint}`, err);
}
}
function loadLedger() {
loadGenericList("/api/ledger?limit=100", "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) => `<span style="background:var(--card-bg); padding:1px 6px; border-radius:3px; font-size:0.75em; border:1px solid var(--border-color); font-family:monospace;">${escapeHtml(s)}</span>`).join(" ") : "";
const authorBadge = item.author ? `<span style="background:var(--canvas-bg); padding:2px 8px; border-radius:4px; border:1px solid var(--border-color); font-size:0.8em; color:var(--text-secondary);">\uD83D\uDC64 ${escapeHtml(item.author)}</span>` : "";
const lineBadge = item.line_range ? `<span style="background:var(--canvas-bg); padding:2px 8px; border-radius:4px; border:1px solid var(--border-color); font-size:0.8em; font-family:monospace;">${escapeHtml(item.line_range)}</span>` : "";
return `
<div style="display:flex; justify-content:space-between; align-items:flex-start; gap:10px;">
<span style="font-weight:bold; font-size:1.05em; color:var(--text-primary); font-family:monospace;">${escapeHtml(item.file_path || "Unknown")}</span>
<div style="display:flex; gap:6px; align-items:center;">
<span style="background:${vcsColor}; color:#fff; font-size:0.75em; font-weight:bold; padding:2px 6px; border-radius:4px; text-transform:uppercase;">${vcs}</span>
<span style="background:var(--canvas-bg); color:${kindColor}; border:1px solid ${kindColor}; font-size:0.75em; font-weight:bold; padding:2px 6px; border-radius:4px;">${kindLabel}</span>
</div>
</div>
<div style="margin-top:6px; font-size:0.95em; color:var(--text-primary);">${escapeHtml(item.description || "")}</div>
${symbols ? `<div style="margin-top:8px; display:flex; gap:5px; flex-wrap:wrap; align-items:center;"><span style="font-size:0.8em; color:var(--text-secondary);">Symbols:</span> ${symbols}</div>` : ""}
<div style="margin-top:10px; font-size:0.85em; display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
<span style="background:var(--canvas-bg); padding:2px 8px; border-radius:4px; font-weight:bold; border:1px solid var(--border-color); color:var(--text-primary);">\uD83D\uDCE6 ${escapeHtml(repo)}</span>
<span style="background:var(--canvas-bg); padding:2px 8px; border-radius:4px; font-family:monospace; border:1px solid var(--border-color);">${revLabel}: ${rev ? escapeHtml(rev.substring(0, 10)) : "None"}</span>
<span style="background:var(--canvas-bg); padding:2px 8px; border-radius:4px; border:1px solid var(--border-color);">\uD83C\uDF3F ${escapeHtml(branch)}</span>
${lineBadge}
${authorBadge}
</div>
`;
});
}
function loadTerminal() {
loadGenericList("/api/terminal/history", "terminal-container", (item) => `
<span style="font-weight:bold; font-size:1.05em; color:var(--text-primary);">${item.command}</span>
@@ -828,19 +827,6 @@ function loadADRs() {
${item.supersedes ? `<div style="font-size:0.9em; font-weight:bold; color:var(--warning-color);">Supersedes: ${item.supersedes}</div>` : ""}
`);
}
function loadWorkspaces() {
loadGenericList("/api/context_workspaces", "workspaces-container", (item) => `
<strong>${item.name}</strong>
<div style="margin-bottom:8px;">${item.description || ""}</div>
<div style="font-family:monospace; font-size:0.85em; color:#3498db;">${(item.paths || []).join(", ")}</div>
`);
}
function loadPinned() {
loadGenericList("/api/pinned_files", "pinned-container", (item) => `
<strong>${item.path || item.file_path || item.id}</strong>
<div style="margin-top:5px; font-size:0.85em; color:var(--text-secondary);">${item.reason || item.description || "Pinned"}</div>
`);
}
function loadMemos() {
loadGenericList("/api/handoff_memos", "memos-container", (item) => `
<strong>Memo from ${item.author || "System"}</strong>
@@ -858,27 +844,13 @@ function loadSnippets() {
<pre style="background:#111417; color:#d1d8e0; padding:10px; border-radius:4px; overflow-x:auto; font-size:0.85em;">${item.content || item.code || ""}</pre>
`);
}
function loadPRs() {
loadGenericList("/api/pr_checklists", "pr-container", (item) => `
<strong>${item.name || "Checklist"}</strong>
<ul style="padding-left:20px; font-size:0.9em;">
${(item.items || []).map((i) => {
const check = i.is_completed ? "☑" : "☐";
const strike = i.is_completed ? "text-decoration:line-through; color:var(--text-secondary);" : "";
return `<li style="${strike}">${check} ${i.description}</li>`;
}).join("")}
</ul>
`);
}
function loadAllExtras() {
loadTerminal();
loadTechDebt();
loadADRs();
loadWorkspaces();
loadPinned();
loadMemos();
loadSnippets();
loadPRs();
loadLedger();
}
async function testClipboard() {
const modal = document.getElementById("clipboard-modal");
@@ -962,35 +934,23 @@ function refreshActiveTab() {
case "task-tab":
loadTasks();
break;
case "sticky-tab":
loadStickyNotes();
break;
case "techdebt-tab":
loadTechDebt();
break;
case "adrs-tab":
loadADRs();
break;
case "workspaces-tab":
loadWorkspaces();
break;
case "pinned-tab":
loadPinned();
break;
case "memos-tab":
loadMemos();
break;
case "snippets-tab":
loadSnippets();
break;
case "pr-tab":
loadPRs();
break;
case "terminal-tab":
loadTerminal();
break;
case "preferences-tab":
loadPreferences();
case "ledger-tab":
loadLedger();
break;
case "activity-tab":
loadActivityHistory();
@@ -1020,17 +980,13 @@ Object.assign(window, {
requestDomainRefresh,
handleIncomingActivity,
setupWS,
loadPreferences,
loadStickyNotes,
loadGenericList,
loadTerminal,
loadLedger,
loadTechDebt,
loadADRs,
loadWorkspaces,
loadPinned,
loadMemos,
loadSnippets,
loadPRs,
loadAllExtras,
testClipboard,
loadVersion,
@@ -1041,4 +997,7 @@ loadVersion();
loadActivityHistory();
setupWS();
var observer = new MutationObserver(() => updateGraphData());
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"]
});