mirror of
https://github.com/PR0M3TH3AN/unredact.git
synced 2026-10-27 08:40:29 +00:00
feat: clickable tier badges with associate detail popup
Badge is now a button — clicking it opens a popup listing all matching associates with their tier description (flight logs / inner circle / named in files), category, and match type. Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
1 parent
921fafc828
commit
368df1a35b
2 files changed
+151
-11
No files matched your search
+63
-8
@@ -909,6 +909,56 @@ function tierLabel(tier) {
|
||||
return "T3";
|
||||
}
|
||||
|
||||
function tierDescription(tier) {
|
||||
if (tier === 1) return "Flight logs — traveled with Epstein";
|
||||
if (tier === 2) return "Inner circle — staff, financial, or frequently named";
|
||||
return "Named in Epstein case files";
|
||||
}
|
||||
|
||||
function showAssocDetail(assocMatches, anchorEl) {
|
||||
// Remove any existing popup
|
||||
const old = document.getElementById("assoc-detail");
|
||||
if (old) old.remove();
|
||||
|
||||
const popup = document.createElement("div");
|
||||
popup.id = "assoc-detail";
|
||||
|
||||
let html = '<div class="assoc-detail-header">Possible associates<button class="assoc-detail-close">X</button></div>';
|
||||
html += '<div class="assoc-detail-list">';
|
||||
|
||||
for (const m of assocMatches) {
|
||||
const cls = tierBadgeClass(m.tier);
|
||||
html += `<div class="assoc-detail-item">
|
||||
<span class="assoc-badge ${cls}">${tierLabel(m.tier)}</span>
|
||||
<div class="assoc-detail-info">
|
||||
<div class="assoc-detail-name">${escapeHtml(m.personName)}</div>
|
||||
<div class="assoc-detail-meta">${escapeHtml(tierDescription(m.tier))} · ${escapeHtml(m.category)} · matched on ${escapeHtml(m.matchType)}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
popup.innerHTML = html;
|
||||
|
||||
// Position near the badge
|
||||
solvePanel.appendChild(popup);
|
||||
|
||||
// Close handlers
|
||||
popup.querySelector(".assoc-detail-close").addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
popup.remove();
|
||||
});
|
||||
|
||||
// Close on outside click
|
||||
const closeOnOutside = (e) => {
|
||||
if (!popup.contains(e.target)) {
|
||||
popup.remove();
|
||||
document.removeEventListener("click", closeOnOutside, true);
|
||||
}
|
||||
};
|
||||
setTimeout(() => document.addEventListener("click", closeOnOutside, true), 0);
|
||||
}
|
||||
|
||||
solveStart.addEventListener("click", startSolve);
|
||||
solveStop.addEventListener("click", stopSolve);
|
||||
|
||||
@@ -1020,18 +1070,23 @@ function handleSolveEvent(data, gapIdx) {
|
||||
div.dataset.assocScore = topMatch.score;
|
||||
}
|
||||
|
||||
let badgeHtml = "";
|
||||
if (topMatch) {
|
||||
const cls = tierBadgeClass(topMatch.tier);
|
||||
const tooltip = `${topMatch.personName} (${topMatch.category}) — ${topMatch.matchType} match`;
|
||||
badgeHtml = `<span class="assoc-badge ${cls}" title="${escapeHtml(tooltip)}">${tierLabel(topMatch.tier)}</span>`;
|
||||
}
|
||||
|
||||
div.innerHTML = `
|
||||
${badgeHtml}
|
||||
<span class="result-text">${escapeHtml(data.text)}</span>
|
||||
<span class="result-error">${data.error_px.toFixed(1)}px ${data.source || ""}</span>
|
||||
`;
|
||||
|
||||
if (assocMatches.length > 0) {
|
||||
const badge = document.createElement("button");
|
||||
badge.className = `assoc-badge ${tierBadgeClass(topMatch.tier)}`;
|
||||
badge.textContent = tierLabel(topMatch.tier);
|
||||
badge.title = "Click for details";
|
||||
badge.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
showAssocDetail(assocMatches, badge);
|
||||
});
|
||||
div.prepend(badge);
|
||||
}
|
||||
|
||||
div.addEventListener("click", () => {
|
||||
const override = ensureOverride();
|
||||
override.gapPreviews[gapIdx] = data.text;
|
||||
|
||||
@@ -487,16 +487,23 @@ main {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Associate badges */
|
||||
/* Associate badges (clickable) */
|
||||
.assoc-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 1px 5px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
margin-right: 4px;
|
||||
border: 1px solid transparent;
|
||||
transition: filter 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.assoc-badge:hover {
|
||||
filter: brightness(1.3);
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
.assoc-badge.tier-1 {
|
||||
@@ -513,3 +520,81 @@ main {
|
||||
background: #f9a825;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* Associate detail popup */
|
||||
#assoc-detail {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
max-height: 60%;
|
||||
background: rgba(16, 16, 32, 0.97);
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
z-index: 40;
|
||||
overflow-y: auto;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.assoc-detail-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
color: #00d474;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.assoc-detail-close {
|
||||
background: #0f3460;
|
||||
color: #e0e0e0;
|
||||
border: 1px solid #1a3a6e;
|
||||
border-radius: 4px;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.assoc-detail-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.assoc-detail-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.assoc-detail-item .assoc-badge {
|
||||
margin-top: 2px;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.assoc-detail-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.assoc-detail-name {
|
||||
color: #e0e0e0;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.assoc-detail-meta {
|
||||
color: #888;
|
||||
font-size: 0.7rem;
|
||||
margin-top: 2px;
|
||||
}
|
||||
Reference in new issue
Block a user