/** * Adapter-side types owned by `evidence-anchor`. * * Implements the contract surface from `wiki/SharedContracts.md` §5 and the * resolution result shape from `wiki/ArchitectureOverview.md` §3.3 / §7. * * Anything that mentions a concrete viewer library (pdfjs, react-pdf-highlighter-plus) * lives *behind* this surface, never on it. `src/shared/` and `src/engine/` * must never import this file. */ import type { Document, DocumentRepresentation } from "@citation-evidence/engine/shared"; import type { Selector } from "@citation-evidence/engine/shared"; import type { AnnotationResolutionStatus } from "@citation-evidence/engine/shared"; import type { DomNodePath, NormalizedRect, StructuralPathSegment, } from "@citation-evidence/engine/shared"; /** * The raw selection captured from a viewer adapter — an opaque payload that * the adapter understands. The shape is intentionally permissive: each * concrete adapter narrows the `kind` discriminator and adds its own * payload. The shared layer never inspects the payload directly. */ export type SelectionCapture = | PdfSelectionCapture | DomSelectionCapture; export interface PdfSelectionCapture { readonly kind: "pdf"; /** Verbatim selected text, before canonical normalisation. */ readonly text: string; /** 1-indexed physical page number the selection started on. */ readonly page: number; /** Page-relative normalized rectangles covering the selection (0..1). */ readonly rects: readonly NormalizedRect[]; /** Optional bounding rectangle (page-relative, normalized). */ readonly boundingRect?: NormalizedRect; } /** Selection captured from a rendered HTML/Markdown viewer. */ export interface DomSelectionCapture { readonly kind: "dom"; /** Verbatim selected text, before canonical normalisation. */ readonly text: string; readonly startPath: DomNodePath; readonly startOffset: number; readonly endPath: DomNodePath; readonly endOffset: number; readonly structuralPath?: readonly StructuralPathSegment[]; } /** * A passage located inside a representation, ready to be scrolled to and * highlighted. */ export interface ResolvedAnchorTarget { readonly representationId: string; /** 1-indexed page (PDF) or undefined for HTML/Markdown. */ readonly page?: number; /** Page-relative normalized rectangles to highlight. */ readonly rects?: readonly NormalizedRect[]; /** Canonical-text offsets, when known. */ readonly textPosition?: { readonly start: number; readonly end: number }; } /** * The outcome of asking the adapter to resolve a `Selector[]`. * Matches `wiki/ArchitectureOverview.md` §3.3. */ export interface AnchorResolution { readonly status: AnnotationResolutionStatus; /** 0..1 confidence in the best candidate. */ readonly confidence: number; readonly candidates: readonly ResolvedAnchorTarget[]; /** Names of the selector kinds that produced a usable candidate. */ readonly usedSelectorTypes: readonly string[]; readonly warnings?: readonly string[]; /** * True when no selector could place the anchor at all. Distinct from `stale`, * which signals representation drift and may still return fuzzy candidates. */ readonly orphaned?: boolean; } export interface HighlightRenderOptions { readonly color?: string; readonly opacity?: number; } /** * The format-neutral viewer adapter contract from `wiki/SharedContracts.md` §5. * * Concrete implementations live alongside the viewer they wrap (e.g. the * PDF spike in `src/anchor/pdf-viewer-adapter-spike.tsx`). The shared/engine * layers depend only on this interface. */ export interface DocumentViewerAdapter { readonly mediaTypes: readonly string[]; load(document: Document, representation?: DocumentRepresentation): Promise; getCurrentSelection(): Promise; createSelectorsFromSelection(selection: SelectionCapture): Promise; resolveSelectors(selectors: readonly Selector[]): Promise; scrollToResolvedTarget( target: ResolvedAnchorTarget, opts?: { readonly center?: boolean; readonly behavior?: "auto" | "smooth" }, ): Promise; renderHighlight( target: ResolvedAnchorTarget, opts?: HighlightRenderOptions, ): Promise; getHighlightClientRects(annotationId: string): Promise; }