/** * ClauseGuard — Side Panel v4.3 * * FIXED v4.3: Added CRITICAL severity support (filter, cards, icons, descriptions). * FIXED v4.3: Severity ordering now uses numeric mapping consistently. */ const DESCS = { "Limitation of liability": "Company limits or excludes liability for losses or damages.", "Unilateral termination": "They can close your account without reason.", "Unilateral change": "Terms can change without your consent.", "Content removal": "Your content can be deleted without notice.", "Contract by using": "You agree just by visiting or using the site.", "Choice of law": "Foreign law applies instead of your local protections.", "Jurisdiction": "Disputes handled in their preferred court.", "Arbitration": "You waive your right to sue in court.", }; // Severity numeric ordering (higher = more severe) const SEV_ORDER = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 }; // SVG icons for severity const SEV_ICONS = { CRITICAL: '', HIGH: '', MEDIUM: '', LOW: '', }; let allClauses = []; let currentFilter = "all"; async function loadResults() { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab?.id) return; const results = await chrome.runtime.sendMessage({ type: "GET_RESULTS", tabId: tab.id }); if (!results || !results.results) { document.getElementById("empty-state").style.display = "block"; return; } document.getElementById("empty-state").style.display = "none"; document.getElementById("score-bar").style.display = "flex"; document.getElementById("filters").style.display = "flex"; // Meta document.getElementById("meta").textContent = `${results.total_clauses} clauses · ${results.flagged_count} flagged`; // Grade const gb = document.getElementById("grade-box"); gb.textContent = results.grade; gb.className = `grade-box grade-${results.grade.toLowerCase()}`; // Score document.getElementById("score-num").textContent = `${results.risk_score} / 100`; const pf = document.getElementById("progress-fill"); pf.style.width = `${results.risk_score}%`; pf.style.background = results.risk_score >= 60 ? "#ef4444" : results.risk_score >= 30 ? "#f59e0b" : "#22c55e"; // FIX v4.3: Count CRITICAL severity too const counts = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 }; const flagged = results.results.filter(r => r.categories?.length > 0); flagged.forEach(r => r.categories.forEach(c => { if (counts[c.severity] !== undefined) counts[c.severity]++; else counts.MEDIUM++; // Default unknown to MEDIUM })); // Show CRITICAL count in the filter if any exist const fcCrit = document.getElementById("fc-crit"); const critFilter = document.getElementById("filter-critical"); if (fcCrit) fcCrit.textContent = counts.CRITICAL; if (critFilter) critFilter.style.display = counts.CRITICAL > 0 ? "flex" : "none"; document.getElementById("fc-high").textContent = counts.HIGH; document.getElementById("fc-med").textContent = counts.MEDIUM; document.getElementById("fc-low").textContent = counts.LOW; allClauses = flagged; renderClauses(); } function renderClauses() { const list = document.getElementById("clause-list"); const filtered = currentFilter === "all" ? allClauses : allClauses.filter(c => c.categories.some(cat => cat.severity === currentFilter)); if (filtered.length === 0) { list.innerHTML = '
No clauses match this filter.
'; return; } list.innerHTML = filtered.map((clause, i) => { const maxSev = clause.categories.reduce((m, c) => { return (SEV_ORDER[c.severity] || 0) > (SEV_ORDER[m] || 0) ? c.severity : m; }, "LOW"); const tagMap = { CRITICAL: "tag-critical", HIGH: "tag-high", MEDIUM: "tag-medium", LOW: "tag-low" }; const tags = clause.categories.map(c => `${SEV_ICONS[c.severity] || ""} ${c.name}` ).join(""); const descs = clause.categories.map(c => `
${DESCS[c.name] || c.name}
` ).join(""); const text = clause.text.length > 200 ? clause.text.slice(0, 200) + "…" : clause.text; return `
${tags}
${text}
${descs}
`; }).join(""); } // Filters document.getElementById("filters").addEventListener("click", (e) => { const btn = e.target.closest(".filter-btn"); if (!btn) return; document.querySelectorAll(".filter-btn").forEach(b => b.classList.remove("active")); btn.classList.add("active"); currentFilter = btn.dataset.filter; renderClauses(); }); // Listen for updates chrome.storage.onChanged.addListener((changes) => { for (const key of Object.keys(changes)) { if (key.startsWith("results_")) { loadResults(); break; } } }); loadResults();