generated from coulomb/repo-seed
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:
@@ -11,14 +11,30 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.ce-debug-textlayer .textLayer {
|
.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,
|
/* PDF.js 4.x wraps marked content in nested spans/divs — cover every
|
||||||
.ce-debug-textlayer .textLayer > br {
|
descendant so the entire selectable area is visible regardless of how
|
||||||
background: rgba(255, 220, 0, 0.35) !important;
|
the renderer nested things. */
|
||||||
|
.ce-debug-textlayer .textLayer * {
|
||||||
|
background: rgba(255, 220, 0, 0.45) !important;
|
||||||
color: rgba(0, 0, 100, 0.85) !important;
|
color: rgba(0, 0, 100, 0.85) !important;
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
/* Reveal the per-glyph span borders so misalignment is visible. */
|
outline: 1px solid rgba(0, 100, 255, 0.3);
|
||||||
outline: 1px solid rgba(0, 100, 255, 0.25);
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,21 +194,44 @@ export function PdfSpikeViewer(props: PdfSpikeViewerProps) {
|
|||||||
|
|
||||||
const highlights = useMemo<Highlight[]>(() => {
|
const highlights = useMemo<Highlight[]>(() => {
|
||||||
const out: Highlight[] = [];
|
const out: Highlight[] = [];
|
||||||
|
const skipped: { id: string; reason: string }[] = [];
|
||||||
for (const a of storedAnnotations) {
|
for (const a of storedAnnotations) {
|
||||||
const h = highlightFromSelectors(a.id, a.text, a.selectors);
|
const h = highlightFromSelectors(a.id, a.text, a.selectors);
|
||||||
if (h) out.push(h);
|
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;
|
return out;
|
||||||
}, [storedAnnotations]);
|
}, [storedAnnotations, debugTextLayer]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!scrollToAnnotationId || didScroll === scrollToAnnotationId) return;
|
if (!scrollToAnnotationId || didScroll === scrollToAnnotationId) return;
|
||||||
const utils = utilsRef.current;
|
const utils = utilsRef.current;
|
||||||
const target = highlights.find((h) => h.id === scrollToAnnotationId);
|
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;
|
if (!utils || !target) return;
|
||||||
utils.scrollToHighlight(target);
|
utils.scrollToHighlight(target);
|
||||||
setDidScroll(scrollToAnnotationId);
|
setDidScroll(scrollToAnnotationId);
|
||||||
}, [scrollToAnnotationId, highlights, didScroll]);
|
}, [scrollToAnnotationId, highlights, didScroll, debugTextLayer]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user