Spaces:
Sleeping
Sleeping
File size: 5,015 Bytes
9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 bdba541 9548e93 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | /**
* ClauseGuard — Side Panel (redesigned)
*/
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.",
};
// SVG icons for severity
const SEV_ICONS = {
HIGH: '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
MEDIUM: '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4"/><path d="M12 16h.01"/></svg>',
LOW: '<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',
};
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";
// Counts
const counts = { 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]++; }));
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 = '<div style="text-align:center;padding:24px;color:#a1a1aa;font-size:12px;">No clauses match this filter.</div>';
return;
}
list.innerHTML = filtered.map((clause, i) => {
const maxSev = clause.categories.reduce((m, c) => {
const o = { HIGH: 3, MEDIUM: 2, LOW: 1 };
return (o[c.severity] || 0) > (o[m] || 0) ? c.severity : m;
}, "LOW");
const tagMap = { HIGH: "tag-high", MEDIUM: "tag-medium", LOW: "tag-low" };
const tags = clause.categories.map(c =>
`<span class="tag ${tagMap[c.severity] || "tag-medium"}">${SEV_ICONS[c.severity] || ""} ${c.name}</span>`
).join("");
const descs = clause.categories.map(c =>
`<div class="clause-desc">${DESCS[c.name] || c.name}</div>`
).join("");
const text = clause.text.length > 200 ? clause.text.slice(0, 200) + "…" : clause.text;
return `
<div class="clause-card sev-${maxSev.toLowerCase()}">
<div class="clause-tags">${tags}</div>
<div class="clause-text">${text}</div>
${descs}
</div>
`;
}).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();
|