CE-WP-0006/0007: Capture view polish, workplans, and UX refinements

- Blob URL stability, scroll centre, strip-only visual guide
- Focus-gated linking, unlink clears overlay, field badge tooltips
- Capture layout (viewer centre), grey guide lines, Add field button
- Workplans CE-WP-0006 (done) and CE-WP-0007 (T01-T09 done, T10-T12 todo)
- Integration tests and viewer-url helpers
This commit is contained in:
2026-06-08 00:37:34 +02:00
parent d25b01f6d5
commit 2fd085b65e
26 changed files with 1767 additions and 356 deletions

View File

@@ -15,6 +15,7 @@
import { useCallback, useMemo } from "react";
import { PdfSpikeViewer, type StoredAnnotation } from "@anchor/index";
import { resolvePdfViewerUrl } from "@source/pdf/viewer-url";
import type { AnnotationId } from "@shared/ids";
import {
useActiveDocument,
@@ -22,12 +23,14 @@ import {
useEngineEventTick,
useLastActivatedEvidence,
usePendingSelection,
usePdfByteStore,
useScrollToAnnotation,
} from "./EngineContext";
import { useDebugFlag } from "./useDebugFlags";
export function ViewerShell() {
const engine = useEngine();
const byteStore = usePdfByteStore();
const { document, representation } = useActiveDocument();
const { set: setPending } = usePendingSelection();
const { id: scrollToId, version: scrollVersion, scrollTo } = useScrollToAnnotation();
@@ -62,10 +65,11 @@ export function ViewerShell() {
const fileUrl = useMemo(() => {
if (!document) return null;
if (document.uri) return document.uri;
const titleOrId = document.title ?? document.id;
return `/fixtures/pdfs/${encodeURIComponent(titleOrId)}`;
}, [document]);
return resolvePdfViewerUrl(document, byteStore);
}, [document, byteStore]);
const scrollRequestKey =
scrollToId !== null ? `${scrollToId}:${scrollVersion}` : null;
const handleHighlightClicked = useCallback(
(annotationId: string) => {
@@ -112,13 +116,10 @@ export function ViewerShell() {
>
<div style={{ flex: 1, overflow: "hidden", position: "relative" }}>
<PdfSpikeViewer
// Re-key on scrollVersion + debug flags so toggling any of
// them remounts the viewer (the CSS classes are on a parent,
// but a re-render is the simplest way to make sure the layers
// get re-laid-out).
// Re-key on document + debug flags only — scroll requests must
// not remount the viewer (that re-fetches the PDF blob).
key={[
document.id,
scrollVersion,
debugTextLayer ? "d" : "n",
hideCanvas ? "hc" : "",
hideTextLayer ? "ht" : "",
@@ -128,6 +129,7 @@ export function ViewerShell() {
pdfUrl={fileUrl}
storedAnnotations={annotations}
{...(scrollToId ? { scrollToAnnotationId: scrollToId } : {})}
{...(scrollRequestKey ? { scrollRequestKey } : {})}
activeAnnotationId={activeAnnotationId}
onHighlightClicked={handleHighlightClicked}
debugTextLayer={debugTextLayer}