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();