import { Layout } from '@superdoc/contracts'; import { DomPositionIndex } from './DomPositionIndex.js'; /** * A rectangle representing a selection highlight in document layout coordinates. */ export type LayoutRect = { x: number; y: number; width: number; height: number; pageIndex: number; }; /** * A caret position in page-local coordinates. */ export type PageLocalCaretPosition = { pageIndex: number; x: number; y: number; }; /** * Options for computing selection rectangles from DOM. */ export type ComputeSelectionRectsFromDomOptions = { /** The DOM container hosting the rendered pages */ painterHost: HTMLElement | null; /** The layout object containing page and fragment information */ layout: Layout | null; /** Index mapping PM positions to DOM elements */ domPositionIndex: DomPositionIndex; /** Function to rebuild the DOM position index when stale */ rebuildDomPositionIndex: () => void; /** Current zoom level (1.0 = 100%) */ zoom: number; /** Height of each page in layout units */ pageHeight: number; /** Gap between pages in layout units */ pageGap: number; }; /** * Computes visual selection rectangles by querying the browser's DOM Range API. * * This function maps a ProseMirror selection range (from, to) to DOM elements using the * DomPositionIndex, creates a DOM Range spanning those elements, and extracts the visual * bounding rectangles. The rectangles are returned in document layout coordinates (not * viewport coordinates), suitable for rendering custom selection overlays. * * @param options - Configuration including painter host, layout, position index, and zoom * @param from - Start position of the selection in ProseMirror coordinates * @param to - End position of the selection in ProseMirror coordinates * @returns Array of rectangles representing the selection, or null if computation fails * * @remarks * - Automatically rebuilds the DOM position index if it's stale due to virtualization * - Deduplicates overlapping rectangles to avoid "double selection" visual artifacts * - Returns null on error (invalid positions, stale DOM, or Range API failures) * - Returns empty array if from === to (collapsed selection) * - Clamps selection to page boundaries using fragment PM ranges from layout */ export declare function computeSelectionRectsFromDom(options: ComputeSelectionRectsFromDomOptions, from: number, to: number): LayoutRect[] | null; /** * Options for computing caret position in page-local coordinates. */ export type ComputeDomCaretPageLocalOptions = { /** The DOM container hosting the rendered pages */ painterHost: HTMLElement | null; /** Index mapping PM positions to DOM elements */ domPositionIndex: DomPositionIndex; /** Function to rebuild the DOM position index when stale */ rebuildDomPositionIndex: () => void; /** Current zoom level (1.0 = 100%) */ zoom: number; }; /** * Computes the visual caret position for a given ProseMirror position. * * Uses the DOM position index to locate the element corresponding to the given PM position, * then queries the browser's Range API to get the exact pixel coordinates of the caret. * Returns coordinates in page-local space (relative to the page element, accounting for zoom). * * @param options - Configuration including painter host, position index, and zoom * @param pos - The ProseMirror position to compute caret coordinates for * @returns Page-local caret position (pageIndex, x, y), or null if computation fails * * @remarks * - Automatically rebuilds the DOM position index if the entry's element is disconnected * - For text nodes, creates a collapsed range at the character offset to get precise coordinates * - For non-text elements, uses the element's bounding rect * - Uses line element bounds for vertical positioning to align caret with line height */ export declare function computeDomCaretPageLocal(options: ComputeDomCaretPageLocalOptions, pos: number): PageLocalCaretPosition | null; /** * Deduplicates overlapping rectangles returned by Range.getClientRects(). * * The browser can return multiple rects for the same visual line when the Range * spans across element boundaries (e.g., from one to another). This happens * because getClientRects() returns rects for both: * 1. The line-box (containing element's box) * 2. The text content within that box * * These rects have nearly identical y coordinates but slightly different heights * and widths, causing a "double selection" visual artifact when rendered. * * This function detects rects that overlap significantly on the same visual line * and keeps only one, preferring the smaller (text-content) rect. * * @param rects - Array of DOMRect objects returned by Range.getClientRects(). Input is not mutated. * @returns A new array containing deduplicated rectangles, sorted by position * * @remarks * Algorithm phases: * * **Phase 1: Group rects by Y coordinate (same line)** * - Sorts all rects by y coordinate (then by x within same y) * - Groups rects that are within Y_SAME_LINE_THRESHOLD_PX (3px) of each other * - Rects on the same visual line will be in the same group * * **Phase 2: Within each group, deduplicate** * - First pass: Remove exact duplicates (same x, y, width, height within epsilon thresholds) * - X_DUPLICATE_EPS_PX (1px) for x-coordinate matching * - Y_SAME_LINE_THRESHOLD_PX (3px) for y-coordinate matching * - SIZE_EPS_PX (0.5px) for width/height matching * - Second pass: Filter out larger container rects * - For rects with >80% horizontal overlap (HORIZONTAL_OVERLAP_THRESHOLD) * - Marks and removes rects that are larger in width or height (by SIZE_EPS_PX) * * **Edge cases handled:** * - Zero-height/zero-width rects: Processed normally, larger ones removed in favor of smaller * - Multiple words on same line: Preserved if horizontal overlap <80% * - Unsorted input: Automatically sorted before processing * - Sub-pixel rendering differences: Handled via epsilon thresholds */ export declare function deduplicateOverlappingRects(rects: DOMRect[]): DOMRect[]; //# sourceMappingURL=DomSelectionGeometry.d.ts.map