export interface ProseNode { value: string; } export interface ProseView { /** Concatenation of the nodes' values. No markers, ever. */ readonly text: string; /** Offsets in `text` where one source node ends and the next begins (interior boundaries only; length = nodes.length - 1; may contain duplicates when nodes are empty). */ readonly boundaries: readonly number[]; /** True iff a node edge falls at exactly this offset. O(log n) binary search. */ hasBoundary(offset: number): boolean; /** Queue an edit replacing [start, end) with `text`. Edits must not overlap; enforced at commit. */ replace(start: number, end: number, text: string, opts?: { bind?: "left" | "right"; }): void; /** Apply queued edits back onto the source nodes (mutating their `value`s), then recompute `text`/`boundaries`. The view supports arbitrarily many queue/commit cycles. */ commit(): void; } export declare function buildProseView(nodes: ProseNode[]): ProseView; /** * Partitions `items` into contiguous groups, breaking before every index in * `breakBefore` (indices 1..n-1). With no break indices the whole list is one * group; an empty list yields no groups. Used to isolate opaque-delimited * segments so a pass never sees text as adjacent across removed content. */ export declare function splitAtIndices(items: readonly T[], breakBefore: ReadonlySet): T[][]; export interface ReplaceAllOptions { /** Opt-in: allow a match that contains an interior node boundary. Default: such matches are skipped. */ allowBoundaries?: (match: RegExpExecArray, view: ProseView) => boolean; /** For pure-insertion edits produced by the replacer (rare), bind side. */ bind?: "left" | "right"; } /** Smallest node boundary strictly inside (start, end), or -1 when none. */ export declare function firstInteriorBoundary(view: ProseView, start: number, end: number): number; /** Largest node boundary strictly less than `offset`, or -1 when none. O(log n). */ export declare function lastBoundaryBefore(view: ProseView, offset: number): number; /** * True when every node boundary strictly inside (start, end) is one of * `allowedSlots`. Seeks to the span with binary search, so it scans only the * boundaries in range rather than the whole array on every call — the linear * alternative is O(matches × nodes) on inline-element-heavy views. */ export declare function interiorBoundariesWithin(view: ProseView, start: number, end: number, allowedSlots: readonly number[]): boolean; /** Count of node boundaries that fall at exactly `offset` (empty nodes stack). */ export declare function boundaryCountAt(view: ProseView, offset: number): number; /** * True when more than one node boundary is stacked at `offset` — i.e. the * position exceeds the single-boundary tolerance the passes allow in an * editing slot. */ export declare function exceedsSingleBoundary(view: ProseView, offset: number): boolean; export declare function replaceAllInView(view: ProseView, regex: RegExp, replacer: (match: RegExpExecArray, view: ProseView) => string | null, options?: ReplaceAllOptions): void; /** * String↔view bridge: builds a single-node ProseView over `text`, runs `run` * against it, commits any queued edits, and returns the resulting text. */ export declare function withProseView(text: string, run: (view: ProseView) => void): string; /** * A pass with the same dual-input shape as the built-ins (`niceQuotes`, * `hyphenReplace`, ...): string in, transformed string out; ProseView in, * edits committed onto it in place. */ export type ProsePass = { (input: string): string; (input: ProseView): void; }; export interface DefinePassOptions { /** * How matches that contain an interior node boundary are handled: * * - `"skip"` (default, safe): such matches are left untouched, so a pass * never rewrites text that spans two source nodes. * - `"allow"`: such matches are always replaced; the replacement text lands * in the node containing the match start, collapsing the boundary after it. * - predicate: forwarded as `allowBoundaries` to {@link replaceAllInView}; * return `true` to allow that particular boundary-spanning match. */ boundaries?: "skip" | "allow" | ((match: RegExpExecArray, view: ProseView) => boolean); } /** * Defines a boundary-aware pass with the same dual-input shape as the * built-in passes, suitable for composing with them (e.g. via `applyPasses` * from `punctilio/rehype`). * * `replacement` is either a template string (supporting `$$`, `$&`, * `$1`–`$99`, and `$`; any other `$` form throws at definition time) * or a replacer `(match, view) => string | null`, where `null` leaves that * match untouched. * * Patterns that can match the empty string (e.g. pure lookarounds) insert * the replacement at every position they match; prefer patterns that * consume at least one character. */ export declare function definePass(pattern: RegExp, replacement: string | ((match: RegExpExecArray, view: ProseView) => string | null), options?: DefinePassOptions): ProsePass; /** * Dual-input driver shared by the public passes: a string runs through a * single-node view and returns the transformed string; a ProseView has the * pass's edits queued and committed onto it in place (returns nothing). */ export declare function overInput(input: string, run: (view: ProseView) => void): string; export declare function overInput(input: ProseView, run: (view: ProseView) => void): void; export declare function overInput(input: string | ProseView, run: (view: ProseView) => void): string | void; /** * Wraps a view runner as a dual-input {@link ProsePass}: the returned function * transforms a string and returns it, or edits a ProseView in place. Collapses * the string/ProseView overload triple that every zero-option public pass would * otherwise repeat. */ export declare function makeProsePass(run: (view: ProseView) => void): ProsePass; //# sourceMappingURL=prose-view.d.ts.map