/**
 * Comment anchor helpers — convert between Yjs **relative positions** and
 * the Base64 wire format the comments REST API speaks.
 *
 * Why relative positions: storing absolute offsets (e.g. char 42–58) is
 * fatal under concurrent editing because someone inserting text before
 * char 42 silently shifts the meaning of every saved anchor. Yjs ships
 * `createRelativePositionFromTypeIndex` /
 * `createAbsolutePositionFromRelativePosition` for exactly this case —
 * they identify a slot in the CRDT graph that survives concurrent edits.
 *
 * The wire shape is `{ from: string; to: string }` where each end is the
 * Base64 of `Y.encodeRelativePosition(rel)`. The slice 4 server treats
 * the field as opaque; this module is the only place in the workspace
 * that interprets it.
 *
 * ## Choosing the right Yjs type to anchor against
 *
 * `createRelativePositionFromTypeIndex(type, index)` interprets `index`
 * differently depending on the type:
 *
 * - `Y.XmlText` / `Y.Text` — index is the character offset within the
 *   text. **This is what comment anchors use** because character offsets
 *   are the natural unit for a text selection.
 * - `Y.XmlFragment` / `Y.XmlElement` — index counts *children*. Useful
 *   for "before/after this child" anchors but not what we want here.
 *
 * Slice 5's TipTap binding maps a PM selection to the underlying
 * `Y.XmlText` instance + character offset before calling `encodeAnchor`,
 * and inverts that mapping after `decodeAnchor` returns. The y-prosemirror
 * plugin keeps a `mapping` for this conversion; consumers that aren't
 * using y-prosemirror can talk to the Y types directly.
 */
import * as Y from 'yjs';
import type { CommentAnchor } from './comments-client.js';
export interface CreateAnchorInput {
    /** Live Yjs document the editor is bound to. */
    doc: Y.Doc;
    /**
     * Yjs text type the selection lives in. Almost always a `Y.XmlText`
     * for TipTap; we keep the bound a little loose so plain `Y.Text` works
     * for non-rich-text editors.
     */
    yType: Y.XmlText | Y.Text;
    /** Character offset (inclusive) of the selection start within `yType`. */
    from: number;
    /** Character offset (exclusive) of the selection end within `yType`. */
    to: number;
}
/**
 * Build a comment anchor from an editor selection. Returns `null` when
 * the range is empty or inverted — the caller should not save anchors
 * that would resolve to nothing.
 */
export declare function encodeAnchor(input: CreateAnchorInput): CommentAnchor | null;
export interface ResolveAnchorInput {
    doc: Y.Doc;
    anchor: CommentAnchor;
}
export interface ResolvedAnchor {
    /**
     * The text type the relative position landed on. Comparing identity
     * (`resolved.yType === editorBoundText`) lets the caller decide
     * whether a comment is still attached to the expected text node.
     */
    yType: Y.AbstractType<unknown>;
    /** Character offset of the saved selection start, after concurrent edits. */
    from: number;
    /** Character offset of the saved selection end. */
    to: number;
}
/**
 * Resolve a stored anchor back to live editor positions. Returns `null`
 * when:
 *
 * - the anchor bytes are not valid base64 / not a Yjs relative position,
 * - the relative position cannot be mapped (the anchored content was
 *   deleted entirely), or
 * - the two ends collapse to the same offset (text between them was
 *   deleted, effectively detaching the comment).
 *
 * Slice 5 surfaces detached comments in the side panel with a small
 * "anchor lost" badge so editors can still resolve / discuss them.
 */
export declare function decodeAnchor(input: ResolveAnchorInput): ResolvedAnchor | null;
//# sourceMappingURL=comment-anchor.d.ts.map