feat: redesign process-résumé panel, fix hover tooltip scrollbars, open résumé from table view

This commit is contained in:
2026-09-13 13:53:21 +02:00
parent 24a2676859
commit b9eb6d441e
+56 -29
View File
@@ -138,6 +138,7 @@
font:inherit; font-size:12.5px; padding:7px 12px; border-radius:8px; cursor:pointer; font:inherit; font-size:12.5px; padding:7px 12px; border-radius:8px; cursor:pointer;
} }
.dropdown-btn:hover{ border-color:var(--accent); color:var(--ink); } .dropdown-btn:hover{ border-color:var(--accent); color:var(--ink); }
.dropdown-btn:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.dropdown-btn .arrow{ font-size:10px; opacity:.6; } .dropdown-btn .arrow{ font-size:10px; opacity:.6; }
.dropdown-btn[aria-expanded="true"]{ border-color:var(--accent); background:var(--accent-wash); color:var(--ink); } .dropdown-btn[aria-expanded="true"]{ border-color:var(--accent); background:var(--accent-wash); color:var(--ink); }
.dropdown-btn .tag{ background:var(--accent); color:#fff; border-radius:999px; font-size:10.5px; padding:1px 6px; } .dropdown-btn .tag{ background:var(--accent); color:#fff; border-radius:999px; font-size:10.5px; padding:1px 6px; }
@@ -156,6 +157,7 @@
border:0; background:none; color:var(--accent); font:inherit; font-size:12px; cursor:pointer; padding:0; border:0; background:none; color:var(--accent); font:inherit; font-size:12px; cursor:pointer; padding:0;
} }
.dropdown-actions button:hover{ text-decoration:underline; } .dropdown-actions button:hover{ text-decoration:underline; }
.dropdown-actions button:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; border-radius:3px; }
.dropdown-list{ max-height:260px; overflow-y:auto; display:flex; flex-direction:column; gap:1px; } .dropdown-list{ max-height:260px; overflow-y:auto; display:flex; flex-direction:column; gap:1px; }
.type-row{ .type-row{
display:flex; align-items:center; gap:8px; padding:5px 4px; border-radius:6px; cursor:pointer; font-size:12.5px; display:flex; align-items:center; gap:8px; padding:5px 4px; border-radius:6px; cursor:pointer; font-size:12.5px;
@@ -173,6 +175,8 @@
border:1px solid var(--border); background:var(--page); color:var(--ink-2); border:1px solid var(--border); background:var(--page); color:var(--ink-2);
font:inherit; font-size:11px; padding:3px 8px; border-radius:999px; cursor:pointer; font:inherit; font-size:11px; padding:3px 8px; border-radius:999px; cursor:pointer;
} }
.role-chip:hover{ border-color:var(--accent); color:var(--ink); }
.role-chip:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.role-chip[aria-pressed="true"]{ background:var(--accent-wash); border-color:var(--accent); color:var(--ink); } .role-chip[aria-pressed="true"]{ background:var(--accent-wash); border-color:var(--accent); color:var(--ink); }
.type-row .role-badge{ .type-row .role-badge{
flex:none; font-size:10px; color:var(--ink-3); background:var(--page); flex:none; font-size:10px; color:var(--ink-3); background:var(--page);
@@ -185,10 +189,13 @@
.segmented{ display:flex; border:1px solid var(--border); border-radius:8px; overflow:hidden; } .segmented{ display:flex; border:1px solid var(--border); border-radius:8px; overflow:hidden; }
.segmented button{ .segmented button{
border:0; background:var(--surface); color:var(--ink-2); font:inherit; font-size:12.5px; border:0; background:var(--surface); color:var(--ink-2); font:inherit; font-size:12.5px;
padding:7px 12px; cursor:pointer; padding:7px 12px; cursor:pointer; transition:background .15s ease, color .15s ease;
} }
.segmented button + button{ border-left:1px solid var(--border); } .segmented button + button{ border-left:1px solid var(--border); }
.segmented button:hover{ background:var(--accent-wash); color:var(--ink); }
.segmented button:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }
.segmented button[aria-pressed="true"]{ background:var(--accent); color:#fff; } .segmented button[aria-pressed="true"]{ background:var(--accent); color:#fff; }
.segmented button[aria-pressed="true"]:hover{ background:var(--accent); }
.presets{ display:flex; gap:6px; } .presets{ display:flex; gap:6px; }
.presets button{ .presets button{
@@ -196,6 +203,7 @@
font:inherit; font-size:12.5px; padding:6px 10px; border-radius:7px; cursor:pointer; font:inherit; font-size:12.5px; padding:6px 10px; border-radius:7px; cursor:pointer;
} }
.presets button:hover{ border-color:var(--accent); color:var(--ink); } .presets button:hover{ border-color:var(--accent); color:var(--ink); }
.presets button:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.presets button[aria-pressed="true"]{ background:var(--accent-wash); border-color:var(--accent); color:var(--ink); } .presets button[aria-pressed="true"]{ background:var(--accent-wash); border-color:var(--accent); color:var(--ink); }
label.check{ display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--ink-2); cursor:pointer; } label.check{ display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--ink-2); cursor:pointer; }
@@ -229,7 +237,6 @@
border-radius:999px; padding:2px 9px; margin-bottom:10px; border-radius:999px; padding:2px 9px; margin-bottom:10px;
} }
.resumo-text{ font-size:13.5px; line-height:1.6; color:var(--ink); margin:0 0 14px; } .resumo-text{ font-size:13.5px; line-height:1.6; color:var(--ink); margin:0 0 14px; }
.resumo-block{ margin-top:12px; padding-top:12px; border-top:1px solid var(--grid); }
.resumo-label{ font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-3); margin-bottom:6px; } .resumo-label{ font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--ink-3); margin-bottom:6px; }
.resumo-people{ display:flex; flex-wrap:wrap; gap:5px; } .resumo-people{ display:flex; flex-wrap:wrap; gap:5px; }
.resumo-people span{ .resumo-people span{
@@ -239,15 +246,15 @@
.resumo-file{ font-size:10.5px; color:var(--ink-3); line-height:1.5; overflow-wrap:anywhere; } .resumo-file{ font-size:10.5px; color:var(--ink-3); line-height:1.5; overflow-wrap:anywhere; }
.resumo-processo-btn{ .resumo-processo-btn{
width:100%; text-align:left; font:inherit; font-size:12.5px; font-weight:600; color:var(--accent); width:100%; text-align:center; font:inherit; font-size:12.5px; font-weight:600; color:var(--accent);
background:var(--accent-wash); border:1px solid var(--border); border-radius:8px; padding:8px 10px; background:var(--accent-wash); border:1px solid var(--border); border-radius:8px; padding:8px 10px;
cursor:pointer; cursor:pointer; margin-top:12px;
} }
.resumo-processo-btn:hover{ border-color:var(--accent); } .resumo-processo-btn:hover{ border-color:var(--accent); }
#resumoProcessoNum{ display:block; text-align:center; margin-top:2px; }
.resumo-processo-content{ .resumo-processo-content{
margin-top:10px; max-height:46vh; overflow-y:auto; overflow-wrap:anywhere; font-size:12px; margin-top:14px; max-height:60vh; overflow-y:auto; overflow-wrap:anywhere;
line-height:1.55; color:var(--ink-2); font-size:14px; line-height:1.65; color:var(--ink-2);
background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:16px 18px;
} }
.resumo-processo-content h1, .resumo-processo-content h2, .resumo-processo-content h3{ .resumo-processo-content h1, .resumo-processo-content h2, .resumo-processo-content h3{
font-family:"Source Serif 4",serif; color:var(--ink); margin:12px 0 4px; line-height:1.3; font-family:"Source Serif 4",serif; color:var(--ink); margin:12px 0 4px; line-height:1.3;
@@ -298,7 +305,10 @@
} }
.selection-ring{ border:2px solid var(--accent); box-shadow:0 0 0 3px var(--accent-wash); } .selection-ring{ border:2px solid var(--accent); box-shadow:0 0 0 3px var(--accent-wash); }
#tooltip{ #tooltip{
position:absolute; pointer-events:none; max-width:320px; z-index:5; /* fixed (not absolute-in-wrap) so a tall/near-edge tooltip overlays the
page instead of expanding .detail-card's scrollable box and popping a
scrollbar */
position:fixed; pointer-events:none; max-width:320px; z-index:5;
background:var(--surface); border:1px solid var(--border); border-radius:9px; background:var(--surface); border:1px solid var(--border); border-radius:9px;
box-shadow:0 6px 20px rgba(0,0,0,.16); padding:10px 12px; font-size:12.5px; box-shadow:0 6px 20px rgba(0,0,0,.16); padding:10px 12px; font-size:12.5px;
color:var(--ink-2); line-height:1.5; display:none; color:var(--ink-2); line-height:1.5; display:none;
@@ -332,7 +342,11 @@
td.type{ white-space:nowrap; color:var(--ink-2); } td.type{ white-space:nowrap; color:var(--ink-2); }
td.people{ color:var(--ink-2); max-width:220px; } td.people{ color:var(--ink-2); max-width:220px; }
td.text{ color:var(--ink); max-width:520px; } td.text{ color:var(--ink); max-width:520px; }
td.file{ font-family:"IBM Plex Mono",monospace; font-size:10.5px; color:var(--ink-3); max-width:220px; overflow-wrap:anywhere; } td.file{
font-family:"IBM Plex Mono",monospace; font-size:10.5px; color:var(--ink-3); max-width:220px;
overflow-wrap:anywhere; cursor:pointer;
}
td.file:hover{ color:var(--accent); text-decoration:underline; }
.table-foot{ display:flex; align-items:center; justify-content:center; gap:12px; padding:14px; } .table-foot{ display:flex; align-items:center; justify-content:center; gap:12px; padding:14px; }
.table-foot button{ .table-foot button{
border:1px solid var(--border); background:var(--page); color:var(--ink); border:1px solid var(--border); background:var(--page); color:var(--ink);
@@ -384,8 +398,8 @@
Linha do tempo interativa da <span class="mono">Pet16704</span> (STF) — um ponto por documento, agrupado Linha do tempo interativa da <span class="mono">Pet16704</span> (STF) — um ponto por documento, agrupado
pela instituição de origem e posicionado pela data real de juntada — cruzada com o resumo de cada um dos pela instituição de origem e posicionado pela data real de juntada — cruzada com o resumo de cada um dos
<strong>53 processos conexos</strong> (o inquérito-mãe <span class="mono">Inq 5026</span> — Operação <strong>53 processos conexos</strong> (o inquérito-mãe <span class="mono">Inq 5026</span> — Operação
Compliance Zero — e os demais inquéritos e petições apartadas, reunidos em <span class="mono">parte1/</span> Compliance Zero — e os demais inquéritos e petições apartadas, reunidos em <span class="mono">resumos/parte1/</span>
e <span class="mono">parte2/</span>). Clique em qualquer ponto da linha do tempo para abrir o resumo do e <span class="mono">resumos/parte2/</span>). Clique em qualquer ponto da linha do tempo para abrir o resumo do
documento e, dali, o resumo completo do processo a que ele pertence. documento e, dali, o resumo completo do processo a que ele pertence.
</p> </p>
<div class="stats" id="stats"></div> <div class="stats" id="stats"></div>
@@ -471,22 +485,22 @@
<div class="resumo-id mono" id="resumoId"></div> <div class="resumo-id mono" id="resumoId"></div>
<div class="resumo-type" id="resumoType"></div> <div class="resumo-type" id="resumoType"></div>
<p class="resumo-text" id="resumoText"></p> <p class="resumo-text" id="resumoText"></p>
<div class="resumo-block" id="resumoPeopleBlock" hidden> <div id="resumoPeopleBlock" hidden>
<div class="resumo-label">Pessoas mencionadas</div> <div class="resumo-label">Pessoas mencionadas</div>
<div class="resumo-people" id="resumoPeople"></div> <div class="resumo-people" id="resumoPeople"></div>
</div> </div>
<div class="resumo-block"> <div>
<div class="resumo-label">Arquivo-fonte</div> <div class="resumo-label">Arquivo-fonte</div>
<div class="resumo-file mono" id="resumoFile"></div> <div class="resumo-file mono" id="resumoFile"></div>
</div> </div>
<div class="resumo-block"> <div>
<button type="button" class="resumo-processo-btn" id="resumoProcessoBtn" aria-expanded="false"> <button type="button" class="resumo-processo-btn" id="resumoProcessoBtn" aria-expanded="false">
Ver resumo do processo <span class="mono" id="resumoProcessoNum"></span> Ver resumo do processo <span class="mono" id="resumoProcessoNum"></span>
</button> </button>
<div class="resumo-processo-content" id="resumoProcessoContent" hidden></div>
</div> </div>
</aside> </aside>
</div> </div>
<div class="resumo-processo-content" id="resumoProcessoContent" hidden></div>
</section> </section>
<section id="tableView" hidden> <section id="tableView" hidden>
@@ -514,8 +528,7 @@
<footer> <footer>
<p> <p>
Linha do tempo: <span class="mono">TIMELINE.md</span> e <span class="mono">gitgraph.md</span> (atribuição de Da leva original de 615 documentos sem data de juntada identificada, 366 tiveram
instituição por documento). Da leva original de 615 documentos sem data de juntada identificada, 366 tiveram
uma data recuperada a partir do próprio texto do documento (data de assinatura/expedição, ex. "Brasília, 10 de uma data recuperada a partir do próprio texto do documento (data de assinatura/expedição, ex. "Brasília, 10 de
abril de 2026") — marcados no resumo do documento como "data lida no texto", já que não é necessariamente a abril de 2026") — marcados no resumo do documento como "data lida no texto", já que não é necessariamente a
data de juntada nos autos. Os 249 restantes ficam de fora do gráfico e aparecem só na tabela. As 292 pessoas do data de juntada nos autos. Os 249 restantes ficam de fora do gráfico e aparecem só na tabela. As 292 pessoas do
@@ -526,7 +539,7 @@
</p> </p>
<p> <p>
Resumo dos processos: ao abrir o resumo de um documento, o botão "Ver resumo do processo" busca o arquivo Resumo dos processos: ao abrir o resumo de um documento, o botão "Ver resumo do processo" busca o arquivo
correspondente em <span class="mono">parte1/</span> ou <span class="mono">parte2/</span> — 53 processos correspondente em <span class="mono">resumos/parte1/</span> ou <span class="mono">resumos/parte2/</span> — 53 processos
vinculados à Pet16704, lidos e resumidos a partir dos autos originais. vinculados à Pet16704, lidos e resumidos a partir dos autos originais.
</p> </p>
<p> <p>
@@ -1223,10 +1236,17 @@
var textEl = document.createElement("div"); textEl.className = "text"; textEl.textContent = p.text; var textEl = document.createElement("div"); textEl.className = "text"; textEl.textContent = p.text;
tt.appendChild(row1); tt.appendChild(idEl); tt.appendChild(textEl); tt.appendChild(row1); tt.appendChild(idEl); tt.appendChild(textEl);
var left = x+16, top = y-10; // tt is position:fixed, so convert the canvas-local x/y into viewport
if (left + 330 > dtWidth) left = x-330-8; // coordinates via the canvas's own rect, then clamp to the viewport
// (not the scrollable card) so it never grows .detail-card's overflow.
var rect = dtCanvas.getBoundingClientRect();
var vx = rect.left+x, vy = rect.top+y;
var left = vx+16, top = vy-10;
if (left + 330 > window.innerWidth) left = vx-330-8;
var ttH = tt.offsetHeight;
if (top + ttH > window.innerHeight) top = window.innerHeight-ttH-8;
tt.style.left = left+"px"; tt.style.left = left+"px";
tt.style.top = Math.max(0,top)+"px"; tt.style.top = Math.max(8,top)+"px";
} }
function clearHover(){ function clearHover(){
state.hover = null; state.hover = null;
@@ -1281,6 +1301,7 @@
function closeResumo(){ function closeResumo(){
state.selected = null; state.selected = null;
document.getElementById("resumoPanel").hidden = true; document.getElementById("resumoPanel").hidden = true;
document.getElementById("resumoProcessoContent").hidden = true;
document.getElementById("selectionRing").style.display = "none"; document.getElementById("selectionRing").style.display = "none";
resizeAll(); resizeAll();
} }
@@ -1302,7 +1323,7 @@
if (e.key==="Escape" && !document.getElementById("resumoPanel").hidden) closeResumo(); if (e.key==="Escape" && !document.getElementById("resumoPanel").hidden) closeResumo();
}); });
/* ---------------- per-process résumé (parte1/parte2 .md, fetched on demand) ---------------- */ /* ---------------- per-process résumé (resumos/parte1,parte2 .md, fetched on demand) ---------------- */
function escapeHtml(s){ function escapeHtml(s){
return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"); return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
@@ -1316,7 +1337,7 @@
s = s.replace(/`([^`]+)`/g, "<code>$1</code>"); s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
return s; return s;
} }
// small, purpose-built markdown→HTML pass for the parte1/parte2 résumé // small, purpose-built markdown→HTML pass for the resumos/parte1,parte2 résumé
// files (headers, bold, inline code, links, "- " lists, paragraphs) — not // files (headers, bold, inline code, links, "- " lists, paragraphs) — not
// a general CommonMark renderer, just enough for these fairly plain files. // a general CommonMark renderer, just enough for these fairly plain files.
function mdToHtml(md){ function mdToHtml(md){
@@ -1349,14 +1370,15 @@
} }
var processoCache = {}; // process number -> rendered HTML, or null if not found var processoCache = {}; // process number -> rendered HTML, or null if not found
// parte1/ and parte2/ live one level above web/ in the intended (repo-root) // resumos/parte1/ and resumos/parte2/ live one level above web/ in the
// deployment, but a local server started from inside web/ (as the README's // intended (repo-root) deployment, but a local server started from inside
// own quickstart does) has no parent directory to serve — so try both // web/ (as the README's own quickstart does) has no parent directory to
// "../parteN/" and "parteN/" for each folder and use whichever resolves. // serve — so try both "../resumos/parteN/" and "resumos/parteN/" for each
// folder and use whichever resolves.
function fetchProcessoMd(num){ function fetchProcessoMd(num){
var candidates = [ var candidates = [
"../parte2/"+num+".md", "../parte1/"+num+".md", "../resumos/parte2/"+num+".md", "../resumos/parte1/"+num+".md",
"parte2/"+num+".md", "parte1/"+num+".md" "resumos/parte2/"+num+".md", "resumos/parte1/"+num+".md"
]; ];
function tryNext(i){ function tryNext(i){
if (i >= candidates.length) return null; if (i >= candidates.length) return null;
@@ -1632,6 +1654,11 @@
tr.appendChild(tdPeople); tr.appendChild(tdPeople);
var tdFile = document.createElement("td"); tdFile.className="file"; tdFile.textContent = r.file; var tdFile = document.createElement("td"); tdFile.className="file"; tdFile.textContent = r.file;
tdFile.title = "Ver resumo deste documento";
tdFile.addEventListener("click", function(){
setView("chart");
openResumo(r, laneIndex[r.lane]);
});
tr.appendChild(tdFile); tr.appendChild(tdFile);
frag.appendChild(tr); frag.appendChild(tr);