feat(pdftract-3mdb7): implement hover tooltips for inspector

- Update app.js setupTooltips() to show span attributes
  - Display text/font/confidence/bbox when available
  - Display block-ref/MCID/reading-idx when available server-side
  - Add edge detection for repositioning near viewport edges
  - Use 8px offset from cursor
- Update style.css tooltip styling per spec:
  - Light background (rgba(255,255,255,0.95))
  - Border: 1px solid #ccc
  - Monospace font family
  - 12px font size
  - No CSS transitions for 50ms appearance

Acceptance criteria:
- Tooltip appears within 50ms (no CSS transitions)
- Shows available data-* attrs as formatted rows
- mouseleave hides tooltip
- Auto-repositions near right/bottom edges
- XSS-safe via textContent (no innerHTML)

Phase: 7.9.6
This commit is contained in:
jedarden 2026-05-31 23:24:15 -04:00
parent b93bb53ac2
commit ba03d03f90
2 changed files with 48 additions and 10 deletions

View file

@ -354,29 +354,67 @@ function loadFragment(){
function setupTooltips(svg){
const tooltip=document.getElementById('tooltip');
const OFFSET=8;
svg.addEventListener('mouseover',e=>{
const target=e.target.closest('[data-text], [data-kind]');
if(!target)return;
let content='';
if(target.dataset.spanIndex!==undefined){
content=`Text: ${target.dataset.text}\nFont: ${target.dataset.font}\nSize: ${target.dataset.size}pt\nConfidence: ${target.dataset.confidence||'N/A'}\nSpan index: ${target.dataset.spanIndex}`
const lines=[];
if(target.dataset.text)lines.push(`Text: ${target.dataset.text}`);
if(target.dataset.font){
const size=target.dataset.size?` ${target.dataset.size}pt`:'';
lines.push(`Font: ${target.dataset.font}${size}`);
}
if(target.dataset.confidence&&target.dataset.confidence!==''){
lines.push(`Confidence: ${target.dataset.confidence}`);
}
if(target.dataset.bbox)lines.push(`Bbox: ${target.dataset.bbox}`);
if(target.dataset.blockRef)lines.push(`Block: ${target.dataset.blockRef}`);
if(target.dataset.mcid)lines.push(`MCID: ${target.dataset.mcid}`);
if(target.dataset.readingIdx)lines.push(`Reading idx: ${target.dataset.readingIdx}`);
content=lines.join('\n');
}else if(target.dataset.blockIndex!==undefined){
content=`Block index: ${target.dataset.blockIndex}\nKind: ${target.dataset.kind}\nText: ${target.dataset.text}\nLevel: ${target.dataset.level||'N/A'}\nTable index: ${target.dataset.tableIndex||'N/A'}`
}
tooltip.hidden=false;
tooltip.textContent=content;
tooltip.style.left=e.pageX+10+'px';
tooltip.style.top=e.pageY+10+'px'
if(content){
tooltip.textContent=content;
tooltip.hidden=false;
positionTooltip(e.pageX,e.pageY);
}
});
svg.addEventListener('mouseout',e=>{
if(e.target.closest('[data-text], [data-kind]'))tooltip.hidden=true
});
svg.addEventListener('mousemove',e=>{
if(!tooltip.hidden){
tooltip.style.left=e.pageX+10+'px';
tooltip.style.top=e.pageY+10+'px'
if(!tooltip.hidden)positionTooltip(e.pageX,e.pageY)
});
function positionTooltip(x,y){
const tooltipRect=tooltip.getBoundingClientRect();
const viewportWidth=window.innerWidth;
const viewportHeight=window.innerHeight;
let left=x+OFFSET;
let top=y+OFFSET;
if(left+tooltipRect.width>viewportWidth){
left=x-tooltipRect.width-OFFSET;
}
})
if(top+tooltipRect.height>viewportHeight){
top=y-tooltipRect.height-OFFSET;
}
left=Math.max(OFFSET,Math.min(left,viewportWidth-tooltipRect.width-OFFSET));
top=Math.max(OFFSET,Math.min(top,viewportHeight-tooltipRect.height-OFFSET));
tooltip.style.left=left+'px';
tooltip.style.top=top+'px'
}
}
document.addEventListener('DOMContentLoaded',init);

View file

@ -27,7 +27,7 @@ body{font-family:system-ui,-apple-system,sans-serif;font-size:14px;line-height:1
.panel-header{padding:12px;border-bottom:1px solid #ddd;font-weight:600;background:#f9f9f9}
.json-tree{flex:1;overflow:auto;padding:12px;font-size:12px;font-family:ui-monospace,monospace;white-space:pre-wrap;word-break:break-all}
.loading{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:16px;color:#666}
.tooltip{position:absolute;background:#333;color:#fff;padding:6px 10px;border-radius:4px;font-size:12px;pointer-events:none;z-index:1000;max-width:300px;white-space:pre-wrap;word-break:break-word}
.tooltip{position:absolute;background:rgba(255,255,255,.95);border:1px solid #ccc;padding:6px 10px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;font-size:12px;pointer-events:none;z-index:1000;max-width:400px;white-space:pre;line-height:1.4}
.layer-spans,.layer-blocks,.layer-columns,.layer-reading-order,.layer-confidence-heatmap,.layer-ocr,.layer-mcid,.layer-anchors,.layer-diff{display:none}
html[data-layers~="spans"] .layer-spans,html[data-layers~="blocks"] .layer-blocks,html[data-layers~="columns"] .layer-columns,html[data-layers~="reading-order"] .layer-reading-order,html[data-layers~="confidence-heatmap"] .layer-confidence-heatmap,html[data-layers~="ocr"] .layer-ocr,html[data-layers~="mcid"] .layer-mcid,html[data-layers~="anchors"] .layer-anchors,html[data-layers~="diff"] .layer-diff{display:block}
.tooltip-key{color:#8f8}