/** * Shared utilities for list marker positioning and text start calculation. * * This module provides a unified implementation of list marker text positioning logic * that is used consistently across the measuring and layout subsystems. The core * function `resolveListTextStartPx` determines where paragraph text begins after * accounting for list markers, tabs, and various justification modes. * * This module is extracted to ensure consistency across: * - remeasure.ts (fast canvas-based remeasurement) * - list-indent-utils.ts (layout bridge utilities) * - measuring/dom/src/index.ts (full typography measurement) */ /** * Minimal marker run formatting information for text measurement. */ export type MinimalMarkerRun = { fontFamily?: string; fontSize?: number; bold?: boolean; italic?: boolean; color?: string; letterSpacing?: number; vanish?: boolean; }; /** * Minimal marker information required for text start calculation. * * This type represents the essential properties needed from a marker object * to calculate where text should start after the marker. It's designed to be * compatible with various marker representations across different subsystems. */ export type MinimalMarker = { /** Pre-measured width of the entire marker box in pixels */ markerBoxWidthPx?: number; /** Pre-measured width of the marker glyph/text in pixels */ glyphWidthPx?: number; /** Horizontal position where marker is drawn (used in firstLineIndentMode) */ markerX?: number; /** Horizontal position where text should start (used in firstLineIndentMode) */ textStartX?: number; /** Width of the gutter between marker and text (used for center/right justification) */ gutterWidthPx?: number; /** Marker justification: 'left', 'center', or 'right' */ justification?: 'left' | 'center' | 'right'; /** What follows the marker: 'tab', 'space', or 'nothing' */ suffix?: 'tab' | 'space' | 'nothing'; /** The text content of the marker (for measurement if glyphWidthPx not available) */ markerText?: string; /** Formatting information for the marker (for measurement if needed) */ run?: MinimalMarkerRun; }; /** * Minimal word layout configuration for text start calculation. * * Contains the subset of word layout properties needed to determine text positioning. */ export type MinimalWordLayout = { /** Whether this list uses first-line indent mode (input-rule created lists) */ firstLineIndentMode?: boolean; /** Pre-calculated horizontal position where text should start */ textStartPx?: number; /** Left indent in pixels from word-layout marker positioning */ indentLeftPx?: number; /** Array of tab stop positions in pixels (for firstLineIndentMode) */ tabsPx?: number[]; /** Marker information */ marker?: MinimalMarker; }; /** * Function type for measuring marker text width. * * Different subsystems use different text measurement approaches: * - remeasure.ts: Canvas-based measurement with getCtx() * - measuring/dom: Canvas-based measurement with cached context * - list-indent-utils: May not have access to canvas, provides markerWidth parameter */ export type MarkerTextMeasurer = (markerText: string, marker: MinimalMarker) => number; /** * Resolved list prefix geometry for a single numbered or bulleted paragraph line. * * All coordinates are measured from the left edge of the paragraph content area. */ export type ResolvedListMarkerGeometry = { /** Left edge where the visible marker glyph should be painted. */ markerStartPx: number; /** Visible marker glyph width in pixels. */ markerTextWidthPx: number; /** Horizontal position where paragraph text begins after marker + suffix. */ textStartPx: number; /** Width contributed by the suffix separator (tab, space, or nothing). */ suffixWidthPx: number; }; /** * Resolves full marker geometry for a list prefix on a paragraph line. * * This is the canonical geometry source for marker start, suffix width, and * paragraph text start. Measurement and painting should both use this helper * so they stay aligned on numbered-list first lines. * * @param wordLayout - Word list layout metadata for the paragraph * @param indentLeft - Paragraph left indent in pixels * @param firstLine - Paragraph first-line indent in pixels * @param hanging - Paragraph hanging indent in pixels * @param measureMarkerText - Callback used when marker glyph width is not precomputed * @returns Resolved list prefix geometry, or undefined when the paragraph has no marker */ export declare function resolveListMarkerGeometry(wordLayout: MinimalWordLayout | undefined, indentLeft: number, firstLine: number, hanging: number, measureMarkerText: MarkerTextMeasurer): ResolvedListMarkerGeometry | undefined; /** * Convenience wrapper that returns only the text-start X coordinate from * {@link resolveListMarkerGeometry}. Falls back to `wordLayout.textStartPx` * for firstLineIndentMode paragraphs that have no marker. * * @param wordLayout - Word list layout metadata for the paragraph * @param indentLeft - Paragraph left indent in pixels * @param firstLine - Paragraph first-line indent in pixels * @param hanging - Paragraph hanging indent in pixels * @param measureMarkerText - Callback used when marker glyph width is not precomputed * @returns Horizontal pixel position where text content should begin, or undefined if no marker present */ export declare function resolveListTextStartPx(wordLayout: MinimalWordLayout | undefined, indentLeft: number, firstLine: number, hanging: number, measureMarkerText: MarkerTextMeasurer): number | undefined; /** * Type guard to check if a value is a valid MinimalWordLayout object. * * Validates that the object has the expected structure for MinimalWordLayout * without unsafe type assertions. Shared across resolver and painter. */ export declare function isMinimalWordLayout(value: unknown): value is MinimalWordLayout; /** * Compute the width of the tab separator between a list marker and its text content. * * Used for marker modes whose rendering contract differs from the shared geometry * helper, such as right/center-justified markers and firstLineIndentMode paragraphs. */ export type MarkerIndent = { anchorIndentPx: number; firstLinePx: number; hangingPx: number; }; export declare function resolveMarkerIndent(indent: { left?: number; right?: number; firstLine?: number; hanging?: number; } | undefined, isRtl: boolean): MarkerIndent; export declare function computeTabWidth(currentPos: number, justification: string, tabs: number[] | undefined, hangingIndent: number | undefined, firstLineIndent: number | undefined, leftIndent: number): number;