import { FlowBlock, Layout, LayoutBlockRef, LayoutFragmentId, LayoutSourceIdentity, LayoutStoryLocator, Measure, SourceAnchor, LAYOUT_BOUNDARY_SCHEMA } from '../../contracts/src/index.js'; import { ClickToPositionGeometryOptions, PositionHit, Point } from './position-hit.js'; /** Container-space rectangle (mirrors `Rect` from this package's `index.ts`). */ export type LayoutRect = { x: number; y: number; width: number; height: number; pageIndex: number; }; /** * Layout-side representation of a hit-tested point. * * Carries everything an editor-neutral consumer needs to address the * rendered fragment under a click. Legacy PM fields (`pmPosition`, * `layoutEpoch`, `lineIndex`, `column`) are surfaced under `legacyPm` for * v1 callers that still need them. */ export type LayoutHit = { /** Schema version for this hit-test result. */ schema: typeof LAYOUT_BOUNDARY_SCHEMA; /** Layout revision (today: `layout.layoutEpoch`). */ layoutRevision: number; /** Composite editor-neutral identity for the hit fragment. */ identity: LayoutSourceIdentity; /** Story locator for the hit fragment. */ story: LayoutStoryLocator; /** Source block reference (today: producer's `blockId`). */ blockRef: LayoutBlockRef; /** Stable opaque fragment id. */ fragmentId: LayoutFragmentId; /** 0-based page index containing the hit. */ pageIndex: number; /** Optional cross-reference to the DOCX source anchor. */ sourceAnchor?: SourceAnchor; /** Diagnostics for partially supported or rejected hits. */ diagnostics?: LayoutHitDiagnostic[]; /** * Legacy PM-shaped position hit. * * AIDEV-NOTE: compat-fallback - v1 callers (PresentationEditor, * super-editor selection) still consume `pmPosition`. Retire once the v2 * provider stops relying on PM positions to map this hit back to a v2 * ref. Do not gate new editor-neutral behavior on this field. */ legacyPm?: PositionHit; }; export type LayoutHitDiagnostic = { code: 'no-page-hit'; } | { code: 'no-fragment-hit'; } | { code: 'pm-position-unavailable'; } | { code: 'unsupported-fragment-kind'; fragmentKind: string; }; /** * Subrange of a single fragment, in editor-neutral terms. * * `inlineFromOpaque` / `inlineToOpaque` are placeholders for the offsets a * future neutral text-offset model will carry. They are intentionally * optional because the current producer can only describe character offsets * via PM positions, and PM-required fields are not allowed on this contract. * v1 consumers that need pixel rects should use `selectionToRects` directly; * they should not gate behavior on this neutral surface yet. */ export type LayoutFragmentSubrange = { identity: LayoutSourceIdentity; story: LayoutStoryLocator; blockRef: LayoutBlockRef; fragmentId: LayoutFragmentId; pageIndex: number; /** Container-space rectangle covered by the fragment slice. */ rect: LayoutRect; /** Optional opaque inline-offset start (may be omitted). */ inlineFromOpaque?: string; /** Optional opaque inline-offset end (may be omitted). */ inlineToOpaque?: string; }; /** * PM-free rendered range for the current neutral subset. * * This maps one or more already-known rendered fragment ids to their full * fragment rectangles. It intentionally does not model text offsets; v1 PM * range slicing remains available through `PmOpaqueRange`. */ export type LayoutFragmentOpaqueRange = { fragmentIds: readonly LayoutFragmentId[]; }; /** * Result of mapping an opaque source range to rendered fragments. * * The opaque range type is intentionally `unknown` here; today the only * concrete instantiation is `{ pmFrom: number; pmTo: number }`, but the * contract names neither, so a future v2 source range can substitute * without reopening the layout boundary. */ export type LayoutRangeMapping = { schema: typeof LAYOUT_BOUNDARY_SCHEMA; layoutRevision: number; fragments: LayoutFragmentSubrange[]; diagnostics?: LayoutHitDiagnostic[]; }; /** * Concrete opaque-range instantiation for v1 consumers using PM positions. * * Kept here (and not in `@superdoc/contracts`) because nothing about it is * editor-neutral. v2 consumers should not import this type; they should * define their own opaque-range shape and the same `mapRangeToFragmentsNeutral` * entry point will accept it (via the function overload defined below). */ export type PmOpaqueRange = { pmFrom: number; pmTo: number; }; /** * Hit-test a container-space coordinate and return an editor-neutral * `LayoutHit`. * * Today the implementation derives the underlying mapping by calling * `clickToPositionGeometry` and projecting the result onto the neutral * shape. The `legacyPm` field is populated so v1 callers continue to work. * * Returns `null` only when no page can be hit at all; partial hits emit * diagnostics rather than failing closed so consumers can distinguish * "outside content" from "fragment unrecognized". */ export declare function hitTestNeutral(layout: Layout, blocks: FlowBlock[], measures: Measure[], containerPoint: Point, options?: ClickToPositionGeometryOptions): LayoutHit | null; /** * Project rendered selection rectangles into editor-neutral fragment * subranges. * * Accepts a `PmOpaqueRange` today; future neutral range shapes can be * accepted via overloads without reopening this contract. * * The `selectionToRects` produces one `Rect` per visual line; this helper * groups them by page and surfaces the underlying fragment identity for * each rect. v1 callers that need raw pixel rects should continue to call * `selectionToRects` directly — this is the editor-neutral adapter. */ export declare function mapRangeToFragmentsNeutral(layout: Layout, blocks: FlowBlock[], measures: Measure[], range: PmOpaqueRange, selectionToRectsFn: (layout: Layout, blocks: FlowBlock[], measures: Measure[], from: number, to: number) => LayoutRect[]): LayoutRangeMapping; export declare function mapRangeToFragmentsNeutral(layout: Layout, blocks: FlowBlock[], measures: Measure[], range: LayoutFragmentOpaqueRange): LayoutRangeMapping; //# sourceMappingURL=neutral-hit.d.ts.map