const analyzeBtn = document.getElementById("analyzeBtn"); const codeInput = document.getElementById("codeInput"); const statusEl = document.getElementById("status"); const indexInfoEl = document.getElementById("indexInfo"); const progressEl = document.getElementById("progress"); const progressBarEl = progressEl ? progressEl.querySelector(".progress-bar") : null; const showIdenticalEl = document.getElementById("showIdentical"); const identicalLabelEl = document.getElementById("identicalLabel"); const excludeInputEl = document.getElementById("excludeModelInput"); const excludeListEl = document.getElementById("excludeModelList"); const modelOptionsEl = document.getElementById("modelOptions"); const copyAgentReportBtn = document.getElementById("copyAgentReport"); const reconstructionSummaryEl = document.getElementById("reconstructionSummary"); const moduleListEl = document.getElementById("moduleList"); const flowComparisonEl = document.getElementById("flowComparison"); const astQueryEl = document.getElementById("astQuery"); const astMatchEl = document.getElementById("astMatch"); const astQuerySummaryEl = document.getElementById("astQuerySummary"); const astMatchSummaryEl = document.getElementById("astMatchSummary"); const overallEl = document.getElementById("overall"); let lastAgentReport = ""; let lastAnalysis = null; let availableModels = new Set(); let excludedModels = new Set(); if (showIdenticalEl) { showIdenticalEl.disabled = true; } if (excludeInputEl) { excludeInputEl.disabled = true; } function setStatus(message) { if (statusEl) { statusEl.textContent = message; } } function setProgress(visible) { if (!progressEl) return; progressEl.classList.toggle("is-hidden", !visible); } function setProgressValue(progress, total) { if (!progressBarEl) return; const safeTotal = total && total > 0 ? total : 1; const ratio = Math.max(0, Math.min(1, progress / safeTotal)); progressBarEl.style.width = `${(ratio * 100).toFixed(1)}%`; } function renderIndexInfo(info) { if (!indexInfoEl) return; if (!info) { indexInfoEl.textContent = ""; return; } const requested = info.requested_granularity || "method"; const resolved = info.resolved_granularity || requested; const origin = info.index_origin; const dir = info.index_dir; const pieces = [`Using ${resolved} index`]; if (requested !== resolved) { pieces.push(`(fallback from ${requested})`); } if (origin === "hub") { pieces.push("from Hub"); } else if (origin) { pieces.push(`from ${origin}`); } if (dir) { pieces.push(`@ ${dir}`); } indexInfoEl.textContent = pieces.join(" "); } function renderExcludedModels() { if (!excludeListEl) return; excludeListEl.innerHTML = ""; Array.from(excludedModels).sort().forEach((name) => { const chip = document.createElement("span"); chip.className = "chip"; chip.textContent = name; const removeBtn = document.createElement("button"); removeBtn.type = "button"; removeBtn.textContent = "×"; removeBtn.addEventListener("click", () => { excludedModels.delete(name); renderExcludedModels(); applyExclusions(); }); chip.appendChild(removeBtn); excludeListEl.appendChild(chip); }); } function applyExclusions() { if (!lastAnalysis) return; const showAll = showIdenticalEl?.checked; const baseByClass = showAll ? lastAnalysis.by_class_all : lastAnalysis.by_class; const baseOverall = showAll ? lastAnalysis.overall_all : lastAnalysis.overall; if (!excludedModels.size) { renderBlueprint(baseByClass); renderOverall(baseOverall); renderSummaryPill(baseOverall); return; } const excluded = new Set(Array.from(excludedModels).map((name) => name.toLowerCase())); const filteredByClass = {}; Object.entries(baseByClass || {}).forEach(([qcls, matches]) => { const filtered = (matches || []).filter((row) => { const root = row.relative_path?.split("/")[0]?.toLowerCase(); return root && !excluded.has(root); }); if (filtered.length) { filteredByClass[qcls] = filtered; } }); const filteredOverall = (baseOverall || []).filter((row) => { const root = row.relative_path?.split("/")[0]?.toLowerCase(); return root && !excluded.has(root); }); renderBlueprint(filteredByClass); renderOverall(filteredOverall); renderSummaryPill(filteredOverall); } async function loadModels() { if (!modelOptionsEl) return; try { const response = await fetch("/api/models"); if (!response.ok) return; const data = await response.json(); const models = data.models || []; availableModels = new Set(models); modelOptionsEl.innerHTML = ""; models.forEach((name) => { const option = document.createElement("option"); option.value = name; modelOptionsEl.appendChild(option); }); } catch (error) { } } function formatSummary(summary) { if (!summary || !summary.summary) return "No structural summary."; const nodes = (summary.summary.node_counts || []) .map((item) => `${item.name}(${item.count})`) .join(", "); const calls = (summary.summary.calls || []) .map((item) => `${item.name}(${item.count})`) .join(", "); const flow = summary.flow ? `Flow: ${summary.flow}` : ""; const parts = []; if (flow) parts.push(flow); if (nodes) parts.push(`Nodes: ${nodes}`); if (calls) parts.push(`Calls: ${calls}`); return parts.join(" · ") || "No structural summary."; } function escapeHtml(text) { if (!text) return ""; return text .replace(/&/g, "&") .replace(//g, ">") .replace(/\"/g, """) .replace(/'/g, "'"); } function diffLCS(text1, text2) { const lines1 = text1 ? text1.split("\n") : []; const lines2 = text2 ? text2.split("\n") : []; const n = lines1.length; const m = lines2.length; const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0)); for (let i = 1; i <= n; i += 1) { for (let j = 1; j <= m; j += 1) { if (lines1[i - 1] === lines2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } let i = n; let j = m; const result = []; while (i > 0 || j > 0) { if (i > 0 && j > 0 && lines1[i - 1] === lines2[j - 1]) { result.unshift({ type: "same", text: lines1[i - 1] }); i -= 1; j -= 1; } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) { result.unshift({ type: "add", text: lines2[j - 1] }); j -= 1; } else { result.unshift({ type: "del", text: lines1[i - 1] }); i -= 1; } } return result; } function renderDiff(text1, text2) { const diff = diffLCS(text1, text2); return diff .map((part) => { const cls = part.type === "add" ? "diff-add" : part.type === "del" ? "diff-del" : ""; const safe = escapeHtml(part.text || " "); return `
${safe}
`; }) .join(""); } function setAst(queryAst, matchAst, querySummary, matchSummary) { if (astQueryEl) { astQueryEl.textContent = queryAst || "AST not found."; } if (astMatchEl) { astMatchEl.textContent = matchAst || "AST not found."; } if (astQuerySummaryEl) { astQuerySummaryEl.textContent = formatSummary(querySummary); } if (astMatchSummaryEl) { astMatchSummaryEl.textContent = formatSummary(matchSummary); } if (flowComparisonEl) { const queryFlow = querySummary?.flow ? querySummary.flow : "unavailable"; const matchFlow = matchSummary?.flow ? matchSummary.flow : "unavailable"; flowComparisonEl.textContent = `Selected flow:\n${queryFlow}\n\nMatch flow:\n${matchFlow}`; } const diffContainer = document.querySelector(".code-diff-view"); if (diffContainer) { diffContainer.innerHTML = ""; if (queryAst && matchAst) { const diffWrapper = document.createElement("div"); diffWrapper.className = "diff-wrapper"; diffWrapper.innerHTML = renderDiff(queryAst, matchAst); diffContainer.appendChild(diffWrapper); } else { const left = document.createElement("pre"); left.className = "code-block"; left.textContent = queryAst || ""; const right = document.createElement("pre"); right.className = "code-block"; right.textContent = matchAst || ""; diffContainer.appendChild(left); diffContainer.appendChild(right); } } } async function loadAst(symbol, matchIdentifier) { const code = codeInput.value.trim(); if (!code || !symbol) return; if (astQueryEl) astQueryEl.textContent = "Loading AST..."; if (astMatchEl) astMatchEl.textContent = "Loading AST..."; const response = await fetch("/api/ast", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code, symbol, match_identifier: matchIdentifier }), }); if (!response.ok) { setAst("Failed to load AST.", "Failed to load AST."); return; } const data = await response.json(); setAst(data.query_ast, data.match_ast, data.query_summary, data.match_summary); } function renderOverall(overall) { if (!overallEl) return; overallEl.innerHTML = ""; if (!overall || overall.length === 0) { overallEl.textContent = "No aggregate matches yet."; return; } const slice = overall.slice(0, 6); for (const entry of slice) { const div = document.createElement("div"); div.className = "overall-item"; const title = document.createElement("div"); title.className = "overall-title"; title.textContent = entry.relative_path; const meta = document.createElement("div"); meta.className = "overall-meta"; const parts = [`score ${entry.score.toFixed(4)}`]; if (entry.count) { parts.push(`${entry.count} hits`); } meta.textContent = parts.join(" · "); div.appendChild(title); div.appendChild(meta); overallEl.appendChild(div); } } function renderSummaryPill(overall) { if (!reconstructionSummaryEl) return; if (!overall || overall.length === 0) { reconstructionSummaryEl.textContent = "No primary ancestor found."; return; } const best = overall[0]; reconstructionSummaryEl.textContent = `Primary ancestor: ${best.relative_path} · ${best.score.toFixed(4)}`; } function renderBlueprint(byClass) { if (!moduleListEl) return; moduleListEl.innerHTML = ""; if (!byClass || Object.keys(byClass).length === 0) { moduleListEl.textContent = "No class-level suggestions."; return; } const sortedClasses = Object.entries(byClass).sort((a, b) => { const scoreA = a[1]?.[0]?.score || 0; const scoreB = b[1]?.[0]?.score || 0; return scoreB - scoreA; }); sortedClasses.forEach(([qcls, matches], index) => { if (!matches || matches.length === 0) return; const top = matches[0]; const item = document.createElement("div"); item.className = `blueprint-item ${top.score > 0.9 ? "high-match" : ""}`; const title = document.createElement("div"); title.className = "blueprint-name"; title.textContent = qcls; const suggestion = document.createElement("div"); suggestion.className = "blueprint-suggestion"; suggestion.textContent = `→ ${top.class_name}`; const meta = document.createElement("div"); meta.className = "blueprint-meta"; const coverage = top.coverage_pct !== undefined ? `${Math.round(top.coverage_pct * 100)}% coverage` : `${top.coverage} defs`; meta.textContent = `${top.relative_path} · ${top.score.toFixed(4)} · ${coverage}`; const evidence = document.createElement("div"); evidence.className = "blueprint-evidence"; evidence.textContent = (top.top_contributors || []) .map((c) => `${c.query.split(".").pop()} ← ${c.match.split(":").pop()}`) .slice(0, 2) .join(" · "); item.appendChild(title); item.appendChild(suggestion); item.appendChild(meta); if (evidence.textContent) { item.appendChild(evidence); } item.addEventListener("click", () => { document.querySelectorAll(".blueprint-item").forEach((el) => el.classList.remove("is-active")); item.classList.add("is-active"); const matchIdentifier = top.identifier; const activeName = document.getElementById("activeModuleName"); if (activeName) { activeName.textContent = `${qcls} vs ${top.class_name}`; } loadAst(qcls, matchIdentifier); }); moduleListEl.appendChild(item); if (index === 0) { item.click(); } }); } async function copyAgentReport() { if (!lastAgentReport) { setStatus("Run an analysis first."); return; } if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(lastAgentReport); setStatus("Agent report copied."); } else { setStatus("Clipboard unavailable in this context."); } } if (copyAgentReportBtn) { copyAgentReportBtn.addEventListener("click", () => { copyAgentReport().catch((error) => { setStatus(`Copy failed: ${error.message || error}`); }); }); } if (excludeInputEl) { excludeInputEl.addEventListener("keydown", (event) => { if (event.key !== "Enter" && event.key !== "Tab") return; const value = excludeInputEl.value.trim(); if (!value) return; if (!availableModels.has(value)) { setStatus(`Unknown model: ${value}`); event.preventDefault(); return; } excludedModels.add(value); excludeInputEl.value = ""; renderExcludedModels(); applyExclusions(); event.preventDefault(); }); } loadModels(); analyzeBtn.addEventListener("click", async () => { const code = codeInput.value.trim(); if (!code) { setStatus("Paste some code first."); return; } renderIndexInfo(null); setStatus("Analyzing... this can take a bit on first run."); setProgress(true); setProgressValue(0, 1); analyzeBtn.disabled = true; try { const payload = { code, top_k: Number(document.getElementById("topK").value || 5), granularity: document.getElementById("granularity").value, precision: "float32", exclude_models: Array.from(excludedModels), }; const response = await fetch("/api/analyze", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!response.ok) { const detail = await response.text(); throw new Error(detail || "Request failed"); } const start = await response.json(); const jobId = start.job_id; if (!jobId) { throw new Error("Missing job id."); } const pollInterval = 600; const poller = setInterval(async () => { try { const progressRes = await fetch(`/api/progress/${jobId}`); if (!progressRes.ok) { throw new Error("Failed to read progress."); } const progressData = await progressRes.json(); const done = progressData.progress || 0; const total = progressData.total || 1; setProgressValue(done, total); const message = progressData.message ? ` · ${progressData.message}` : ""; const errorDetail = progressData.error ? ` · ${progressData.error}` : ""; setStatus(`Analyzing... ${done}/${total}${message}${errorDetail}`); if (progressData.status === "done") { clearInterval(poller); const resultRes = await fetch(`/api/result/${jobId}`); if (!resultRes.ok) { const detail = await resultRes.text(); throw new Error(detail || "Failed to fetch result."); } const data = await resultRes.json(); lastAnalysis = data; lastAgentReport = data.agent_report || ""; setAst("Select a module to compare.", "Select a module to compare.", null, null); renderBlueprint(data.by_class); renderOverall(data.overall); renderSummaryPill(data.overall); renderIndexInfo(data.index_info); if (identicalLabelEl) { const filtered = data.identical_filtered || 0; const label = identicalLabelEl.querySelector("span"); if (label) { label.textContent = `Show identical matches (${filtered})`; } } if (showIdenticalEl) { showIdenticalEl.disabled = false; } if (excludeInputEl) { excludeInputEl.disabled = false; } setStatus("Done."); setProgress(false); analyzeBtn.disabled = false; } else if (progressData.status === "error") { clearInterval(poller); const detail = progressData.error ? ` ${progressData.error}` : ""; setStatus(`Analysis failed.${detail}`); setProgress(false); analyzeBtn.disabled = false; } } catch (error) { clearInterval(poller); setStatus(`Error: ${error.message || error}`); setProgress(false); analyzeBtn.disabled = false; } }, pollInterval); } catch (error) { setStatus(`Error: ${error.message || error}`); setProgress(false); analyzeBtn.disabled = false; } finally { } }); if (showIdenticalEl) { showIdenticalEl.addEventListener("change", () => { if (!lastAnalysis) return; applyExclusions(); }); }