WP-0002: stale vs unresolved resolution, orphaned flag/helper, bounded fuzzy quote recovery, and PdfViewerAdapter promotion with resolveSelectors integration. WP-0003: DomSelectionCapture, DOM create/resolve ladder, HtmlViewerAdapter under evidence-anchor/dom, and finished workplans. Verification: 42 anchor tests, citation-evidence typecheck + 51 tests green.
115 lines
4.3 KiB
TypeScript
115 lines
4.3 KiB
TypeScript
/**
|
|
* 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<void>;
|
|
getCurrentSelection(): Promise<SelectionCapture | null>;
|
|
createSelectorsFromSelection(selection: SelectionCapture): Promise<Selector[]>;
|
|
resolveSelectors(selectors: readonly Selector[]): Promise<AnchorResolution>;
|
|
scrollToResolvedTarget(
|
|
target: ResolvedAnchorTarget,
|
|
opts?: { readonly center?: boolean; readonly behavior?: "auto" | "smooth" },
|
|
): Promise<void>;
|
|
renderHighlight(
|
|
target: ResolvedAnchorTarget,
|
|
opts?: HighlightRenderOptions,
|
|
): Promise<void>;
|
|
getHighlightClientRects(annotationId: string): Promise<readonly DOMRect[]>;
|
|
}
|