evidence-anchor/README.md
tegwick 5b77dbd314 EANCH-WP-0001 T04: extract PDF adapter + scroll/highlight helpers
Move pdf-viewer-adapter-spike.tsx, scroll-job.ts (+test), highlight-styles.css
and debug-textlayer.css into src/pdf/ (the adapter boundary). Rewrite the
spike's @shared import to @citation-evidence/engine/shared and its ./types to
../types. Add src/css.d.ts so tsc accepts side-effect .css imports.

Decisions:
- adapter stays the explicitly-named PdfSpikeViewer spike; promotion to a
  production PDFViewerAdapter is registered as T06 follow-on work
- viewer libs (pdfjs-dist, react, react-pdf-highlighter-plus) confined to
  src/pdf/ and exposed only via the evidence-anchor/pdf subpath; the root
  entrypoint stays pure. Boundary enforced by eslint no-restricted-imports.
- ported unit suites (pdf-selector-math round-trip + scroll-job retry) serve
  as the inspectable harness for capture->selectors->resolve->scroll.

Verified: pnpm test (30 passed), typecheck, lint all green. README layout updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:44:52 +02:00

87 lines
4.1 KiB
Markdown

# evidence-anchor
Selector creation, resolution, and the `DocumentViewerAdapter` contract that
every document viewer in the citation-evidence workspace implements. This repo
turns annotations from static marks into durable, reopenable source references.
- **Owns:** selector *behavior*`createSelectors`, `resolveSelectors`, PDF
selector math, the viewer-adapter contract, and highlight/scroll helpers.
- **Does not own:** selector *type interfaces* — those live in `citation-engine`
(`shared/selector`). See `ADR-0006` and `SharedContracts.md` §8.
- **May depend on:** `citation-engine` only (DependencyMap §4). Nothing from
`binder/`, `source/`, or `work/` may flow back into it.
See `SCOPE.md` for the boundary and `INTENT.md` for the long-range intent.
## Status: extracting from the umbrella
The concrete anchor slice currently lives upstream in
[`../citation-evidence/src/anchor/`](../citation-evidence/src/anchor/). Workplan
`EANCH-WP-0001` moves it here as a standalone TypeScript package, wires the
umbrella to consume this package, and verifies the round-trip. Shared-contract
changes still happen in the umbrella (`citation-evidence/wiki/`), not here.
## Install model
Sibling-checkout, linked-package model — this repo is checked out next to its
consumers and consumed via a local link (e.g. `link:../evidence-anchor`), not
published to a registry during MVP. Its only shared-type dependency is
`citation-engine`, imported through the engine's public `shared` entrypoint
(`@citation-evidence/engine/shared`) rather than umbrella-only `@shared/*`
aliases.
## Package layout (initial extracted version)
```text
src/
index.ts public entrypoint — pure surface only
types.ts adapter-side types: SelectionCapture,
ResolvedAnchorTarget, AnchorResolution,
HighlightRenderOptions, DocumentViewerAdapter
css.d.ts ambient decl for side-effect .css imports
selectors/ pure core — no viewer/UI deps
index.ts createSelectors, resolveSelectors, DEFAULT_CONTEXT_CHARS
create.ts selector creation from a captured selection
resolve.ts resolution + the exact-match confidence ladder
create.test.ts
resolve.test.ts
pdf/ adapter boundary — the only place viewer libs live
index.ts subpath entry `evidence-anchor/pdf`
pdf-selector-math.ts pure page + normalized-rect math (capture↔selectors)
pdf-selector-math.test.ts
pdf-viewer-adapter-spike.tsx concrete PDF adapter (PdfSpikeViewer)
scroll-job.ts retryable scroll-to-highlight helper
scroll-job.test.ts
highlight-styles.css highlight rendering styles
debug-textlayer.css optional text-layer debugging styles
```
Boundary rules for the layout (enforced by `eslint.config.js`):
- viewer-library imports (`pdfjs-dist`, `react`, `react-pdf-highlighter-plus`)
are confined to `src/pdf/` — the pure zone (`src/selectors/**`, `src/types.ts`)
may not import them;
- `src/selectors/` is pure and depends only on `citation-engine` shared types;
- the **root** entrypoint (`evidence-anchor`) exposes only the pure surface —
`createSelectors`, `resolveSelectors`, the selector/resolution types, and the
`DocumentViewerAdapter` contract;
- the concrete PDF adapter is reached through the **`evidence-anchor/pdf`**
subpath, so pure consumers never pull PDF.js or React into their bundle. That
adapter is still the explicitly-named `PdfSpikeViewer` spike; promoting it to
a production `PDFViewerAdapter` is registered follow-on work (T06).
## Public API (target surface)
```ts
import {
createSelectors,
resolveSelectors,
type DocumentViewerAdapter,
type AnchorResolution,
} from "evidence-anchor";
```
Resolution is explicit about uncertainty — `AnchorResolution.status` is one of
`resolved` / `ambiguous` / `unresolved` / `stale` with a `0..1` confidence, so a
caller can highlight, ask the user to confirm, or mark a citation stale rather
than silently highlight the wrong passage.