Strengthen text-layer debug + log highlight render path

The first cut of "Debug text layer" only painted direct `<span>`
children of `.textLayer`. PDF.js 4.x wraps marked content in nested
spans/divs, so the entire selectable area wasn't visible — making it
hard to tell whether a region is "no text layer at all" vs. "text
layer present but small/dense".

Changes:
- CSS now targets every descendant of `.textLayer`, dims the canvas
  underneath, and outlines the `.textLayer` container itself so its
  full extent is obvious.
- TextHighlight rectangles flip to green in debug mode so saved
  highlights don't get washed out by the debug yellow.
- The viewer now logs:
    [ce] viewer highlights        — which annotations rendered, which
                                    were skipped, with rects + page
    [ce] scrollToAnnotation       — whether the target was found in
                                    the highlights array when an
                                    activation arrived

This is the diagnostic loop for the "viewport scrolls but the
highlight doesn't appear" report — if highlight count is > 0 in the
first log but the green rectangle is off-screen, the saved rects
inherited the same text-layer misalignment that caused the partial
selection captures in the first place.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 22:05:13 +02:00
parent 0638c441c4
commit f0af8887d1
2 changed files with 47 additions and 8 deletions

View File

@@ -11,14 +11,30 @@
*/
.ce-debug-textlayer .textLayer {
outline: 1px dashed rgba(255, 0, 0, 0.5);
outline: 2px dashed rgba(255, 0, 0, 0.5);
background: rgba(255, 0, 0, 0.05);
}
.ce-debug-textlayer .textLayer > span,
.ce-debug-textlayer .textLayer > br {
background: rgba(255, 220, 0, 0.35) !important;
/* PDF.js 4.x wraps marked content in nested spans/divs — cover every
descendant so the entire selectable area is visible regardless of how
the renderer nested things. */
.ce-debug-textlayer .textLayer * {
background: rgba(255, 220, 0, 0.45) !important;
color: rgba(0, 0, 100, 0.85) !important;
opacity: 1 !important;
/* Reveal the per-glyph span borders so misalignment is visible. */
outline: 1px solid rgba(0, 100, 255, 0.25);
outline: 1px solid rgba(0, 100, 255, 0.3);
}
/* Make the canvas-rendered layer dim so the text-layer overlay stands
out by contrast. */
.ce-debug-textlayer canvas {
opacity: 0.35;
}
/* Make any existing TextHighlight rectangles obvious even in debug
mode (the highlighter's own yellow gets washed out by our debug
yellow). */
.ce-debug-textlayer .TextHighlight__part {
background: rgba(0, 200, 0, 0.45) !important;
outline: 2px solid rgba(0, 120, 0, 0.7) !important;
}

View File

@@ -194,21 +194,44 @@ export function PdfSpikeViewer(props: PdfSpikeViewerProps) {
const highlights = useMemo<Highlight[]>(() => {
const out: Highlight[] = [];
const skipped: { id: string; reason: string }[] = [];
for (const a of storedAnnotations) {
const h = highlightFromSelectors(a.id, a.text, a.selectors);
if (h) out.push(h);
else skipped.push({ id: a.id, reason: "no PdfRectSelector / empty boundingRect" });
}
if (debugTextLayer) {
console.log("[ce] viewer highlights", {
in: storedAnnotations.length,
rendered: out.length,
rendered_detail: out.map((h) => ({
id: h.id,
page: h.position.boundingRect.pageNumber,
bounding: h.position.boundingRect,
rectCount: h.position.rects.length,
})),
skipped,
});
}
return out;
}, [storedAnnotations]);
}, [storedAnnotations, debugTextLayer]);
useEffect(() => {
if (!scrollToAnnotationId || didScroll === scrollToAnnotationId) return;
const utils = utilsRef.current;
const target = highlights.find((h) => h.id === scrollToAnnotationId);
if (debugTextLayer) {
console.log("[ce] scrollToAnnotation requested", {
id: scrollToAnnotationId,
utilsAvailable: !!utils,
targetFound: !!target,
knownIds: highlights.map((h) => h.id),
});
}
if (!utils || !target) return;
utils.scrollToHighlight(target);
setDidScroll(scrollToAnnotationId);
}, [scrollToAnnotationId, highlights, didScroll]);
}, [scrollToAnnotationId, highlights, didScroll, debugTextLayer]);
return (
<div