Unify capture/edit form, thicker active document border, layer-hide toggles

Three UX iterations rolled into one:

1. Unified evidence form
   - New EvidenceFormBody is the single source for "citation +
     commentary" editing. Both InlineCaptureForm (creating fresh
     evidence from a selection) and the EvidenceCard edit mode render
     this body with their own save/cancel labels + badge/helper text.
   - The capture form now exposes the citation as an editable
     textarea — pre-filled with the selection text — so the user can
     refine a partial capture before saving without re-selecting.
   - Old testid prefixes are unchanged for the inline-capture flow
     (`inline-capture-quote/commentary/save/cancel`); edit-mode
     testids are now `evidence-edit-<id>-{quote,commentary,save,cancel}`.

2. Active document card
   - The blue background alone was the only "this is open" cue. Added
     a 3px #0050b3 border (matching the evidence-card thick-border
     pattern, but in the documents-are-blue palette) plus a
     `data-active` attribute.

3. PDF layer-hide diagnostics
   - New debug flags `hideCanvas`, `hideTextLayer`, `hideAnnotationLayer`,
     `hideXfaLayer` — applied as `.ce-hide-<layer>` classes on the viewer
     wrapper, each `display: none`-ing the matching PDF.js layer.
   - SessionMenu groups the toggles under a "PDF diagnostics" header
     with a new shared DebugCheckbox helper. The existing "Debug text
     layer" highlight toggle now lives in the same group.
   - Lets the user isolate stacking issues by elimination — e.g.
     "hide text layer, can I now see the canvas content underneath?".

Tests
   - citation-card-export-e2e + session-export-reimport switched from
     placeholder/role-name lookups to the inline-capture testids so
     they survive form-copy changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 23:27:08 +02:00
parent f42b4ec87c
commit bef2725fdd
11 changed files with 309 additions and 148 deletions

View File

@@ -33,3 +33,27 @@
.ce-debug-textlayer canvas {
opacity: 0.4;
}
/*
* Layer-visibility toggles. Each `.ce-hide-<layer>` class is applied
* to the same viewer-wrapper element so a single parent can hide any
* combination of layers. Useful for diagnosing layer stacking issues
* (e.g. "is the textLayer covering the canvas?") by elimination.
*/
.ce-hide-canvas canvas {
display: none !important;
}
.ce-hide-text-layer .textLayer {
display: none !important;
}
.ce-hide-annotation-layer .annotationLayer,
.ce-hide-annotation-layer .annotationEditorLayer {
display: none !important;
}
.ce-hide-xfa-layer .xfaLayer {
display: none !important;
}

View File

@@ -199,6 +199,15 @@ export interface PdfSpikeViewerProps {
* image-only. Also logs every onSelection event to the console.
*/
readonly debugTextLayer?: boolean;
/**
* Hide specific PDF.js layers so you can see what sits underneath.
* Helps diagnose layer-stacking issues (e.g. "is the text layer
* covering the canvas content?").
*/
readonly hideCanvas?: boolean;
readonly hideTextLayer?: boolean;
readonly hideAnnotationLayer?: boolean;
readonly hideXfaLayer?: boolean;
}
export interface StoredAnnotation {
@@ -222,11 +231,24 @@ export function PdfSpikeViewer(props: PdfSpikeViewerProps) {
activeAnnotationId,
onHighlightClicked,
debugTextLayer,
hideCanvas,
hideTextLayer,
hideAnnotationLayer,
hideXfaLayer,
} = props;
const HighlightContainer = useMemo(
() => makeSpikeHighlightContainer({ activeAnnotationId, onHighlightClicked }),
[activeAnnotationId, onHighlightClicked],
);
const wrapperClasses = [
debugTextLayer ? "ce-debug-textlayer" : null,
hideCanvas ? "ce-hide-canvas" : null,
hideTextLayer ? "ce-hide-text-layer" : null,
hideAnnotationLayer ? "ce-hide-annotation-layer" : null,
hideXfaLayer ? "ce-hide-xfa-layer" : null,
]
.filter((c): c is string => c !== null)
.join(" ");
const utilsRef = useRef<PdfHighlighterUtils | null>(null);
const [didScroll, setDidScroll] = useState<string | null>(null);
@@ -273,7 +295,7 @@ export function PdfSpikeViewer(props: PdfSpikeViewerProps) {
return (
<div
className={debugTextLayer ? "ce-debug-textlayer" : undefined}
className={wrapperClasses.length > 0 ? wrapperClasses : undefined}
style={{ height: "100%" }}
>
<PdfLoader document={pdfUrl}>