const elements = { scanButton: document.getElementById("scan-button"), clearButton: document.getElementById("clear-button"), status: document.getElementById("status"), summary: document.getElementById("summary"), scannedCount: document.getElementById("scanned-count"), flaggedCount: document.getElementById("flagged-count"), minimumScore: document.getElementById("minimum-score"), pageLabel: document.getElementById("page-label"), results: document.getElementById("results"), apiUrl: document.getElementById("api-url"), minimumScoreInput: document.getElementById("minimum-score-input"), maxItems: document.getElementById("max-items"), saveSettings: document.getElementById("save-settings"), testConnection: document.getElementById("test-connection"), }; function setStatus(message, tone = "neutral") { elements.status.textContent = message; elements.status.dataset.tone = tone; } function setBusy(busy) { elements.scanButton.disabled = busy; elements.clearButton.disabled = busy; elements.scanButton.textContent = busy ? "Scanning page..." : "Scan current page"; } function sendMessage(message) { return chrome.runtime.sendMessage(message); } function createFinding(result) { const card = document.createElement("article"); card.className = "finding"; card.dataset.severity = result.severity || "medium"; const header = document.createElement("div"); header.className = "finding-header"; const type = document.createElement("span"); type.className = "finding-type"; type.textContent = result.prediction; const score = document.createElement("span"); score.className = "score"; score.textContent = `${result.confidence}% ${result.confidenceBand}`; header.append(type, score); const evidence = document.createElement("p"); evidence.className = "evidence"; evidence.textContent = result.text; const phrases = document.createElement("div"); phrases.className = "phrases"; const phraseText = (result.explanation || []) .map((item) => item.phrase) .join(", "); phrases.textContent = phraseText ? `Trigger phrases: ${phraseText}` : "No stable word-level explanation available."; const locate = document.createElement("button"); locate.className = "locate"; locate.textContent = "Locate on page"; locate.addEventListener("click", async () => { const response = await sendMessage({ type: "FOCUS_RESULT", elementId: result.id, }); if (!response?.ok) { setStatus(response?.error || "Could not locate the element.", "error"); } }); card.append(header, evidence, phrases, locate); return card; } function renderScan(response) { elements.summary.hidden = false; elements.scannedCount.textContent = response.scanned; elements.flaggedCount.textContent = response.flagged; elements.minimumScore.textContent = `${response.minimumScore || 0}%`; elements.pageLabel.textContent = response.page?.title || ""; elements.results.replaceChildren(); if (!response.results?.length) { const empty = document.createElement("p"); empty.className = "empty"; empty.textContent = "No visible text exceeded the current review threshold."; elements.results.appendChild(empty); return; } for (const result of response.results) { elements.results.appendChild(createFinding(result)); } } async function runScan() { setBusy(true); setStatus("Collecting visible interface text and running the NLP model..."); try { const response = await sendMessage({ type: "RUN_SCAN" }); if (!response?.ok) { throw new Error(response?.error || "Scan failed."); } renderScan(response); setStatus( `Audit complete. ${response.flagged} of ${response.scanned} texts require review.`, "success", ); } catch (error) { setStatus(error.message, "error"); } finally { setBusy(false); } } async function clearHighlights() { const response = await sendMessage({ type: "CLEAR_HIGHLIGHTS" }); if (!response?.ok) { setStatus(response?.error || "Could not clear highlights.", "error"); return; } setStatus("Page highlights cleared.", "success"); } async function loadSettings() { const response = await sendMessage({ type: "GET_SETTINGS" }); if (!response?.ok) { setStatus(response?.error || "Could not load settings.", "error"); return; } elements.apiUrl.value = response.apiUrl; elements.minimumScoreInput.value = response.minimumScore; elements.maxItems.value = response.maxItems; } async function saveSettings() { const response = await sendMessage({ type: "SAVE_SETTINGS", settings: { apiUrl: elements.apiUrl.value, minimumScore: elements.minimumScoreInput.value, maxItems: elements.maxItems.value, }, }); if (!response?.ok) { setStatus(response?.error || "Could not save settings.", "error"); return; } setStatus("Settings saved.", "success"); } async function testConnection() { setStatus("Testing local API connection..."); const response = await sendMessage({ type: "TEST_CONNECTION" }); if (!response?.ok) { setStatus(response?.error || "API connection failed.", "error"); return; } setStatus("Local NLP API is ready and calibrated.", "success"); } elements.scanButton.addEventListener("click", runScan); elements.clearButton.addEventListener("click", clearHighlights); elements.saveSettings.addEventListener("click", saveSettings); elements.testConnection.addEventListener("click", testConnection); loadSettings();