---
title: "edytor"
description: "Every name edytor exports, with its declaration and documentation (generated from the published types)."
---

{/* Generated by scripts/api-report.mjs (pnpm api:report) from the published declarations: edit the JSDoc, not this page. */}

Every name `edytor` exports, generated from the package's published type declarations, with the documentation its JSDoc carries. The guides explain how the pieces fit; this page is the exhaustive list. 381 names.

## ActorId

The durable per-block attribution record a replica sees.

```ts
export type ActorId = string;
```

## ActorProfile

Replicated actor profile record payload (`u/` values).

```ts
export type ActorProfile = {
    name?: string;
    color?: string;
};
```

## AfterOperationPayload

What `onAfterOperation` receives: a `ChangePayload` without
`prevent` (the operation already ran). The `Omit` distributes over the
union, so `operation` still narrows `payload` (and `text`).

```ts
export type AfterOperationPayload = ChangePayload extends infer C ? C extends unknown ? Omit<C, 'prevent'> : never : never;
```

## AnchorAffinity

Selection-endpoint affinity — which side of a position the anchor binds
to (see `T.atomAnchorAt`):

- `'left'` — bound to the atom BEFORE the position; resolves right after
  it, so a concurrent insert exactly at the position lands to the
  anchor's right. Used for carets and range ENDS.
- `'right'` — bound to the atom AT the position; resolves right before
  it, so a concurrent insert lands to the anchor's left — outside a
  range starting here. Used for range STARTS.

```ts
export type AnchorAffinity = 'left' | 'right';
```

## Announcer

```ts
export declare class Announcer {
    #private;
    private edytor;
    message: string;
    serial: number;
    constructor(edytor: Edytor);
    say: (message: string) => void;
    what: (blocks: readonly Block[]) => string;
    moved: (request: BlockMoveRequest, moved: readonly Block[]) => void;
    deleted: (what: string) => void;
}
```

- `message`: The last announcement.
- `serial`: Bumped by each announcement: the live region renders it as a new node.
- `say`: Announce `message` (politely: after what the screen reader is saying).
- `what`: `blocks` (one or several) as words: a kind's label for one, a count for several.
- `moved`: `request`'s blocks moved (`moveBlocks` answered them).
- `deleted`: Blocks were deleted: `what` names them, read before they went.

## attachDocument

See `DocumentBinding.attachDocument`.

```ts
declare const attachDocument: (doc: YDoc, options?: DocumentOptions) => EdytorDocument;
```

## AttributionActor

The stable application-level author identity (mirrors `DocumentActor`).

```ts
export type AttributionActor = {
    id: string;
    name?: string;
    color?: string;
};
```

## Awareness

Simple shared state protocol for non-persistent data (cursor, username,
status…). Semantics are verbatim upstream: clock-ordered LWW per client,
remote `null` states mark peers offline, local state is re-published when
outdated, and remote states expire after `outdatedTimeout`.

```ts
export declare class Awareness extends IsolatedObservable<AwarenessEvents> {
    #private;
    doc: AwarenessDoc;
    clientID: number;
    states: AwarenessStates;
    meta: Map<number, MetaClientState>;
    private _destroyed;
    constructor(doc: AwarenessDoc);
    destroy(): void;
    getLocalState(): Record<string, unknown> | null;
    setLocalState(state: Record<string, unknown> | null): void;
    setLocalStateField(field: string, value: unknown): void;
    getStates(): AwarenessStates;
}
```

- `states`: Maps from client id to client state

## AwarenessLike

Structural awareness surface used by `publishPresence` — the real
`Awareness` satisfies it; tests may substitute stubs.

```ts
export type AwarenessLike = {
    getLocalState: () => Record<string, unknown> | null;
    setLocalState: (state: Record<string, unknown> | null) => void;
};
```

## AwarenessStates

```ts
export type AwarenessStates = Map<number, Record<string, unknown>>;
```

## AwarenessUpdate

```ts
export type AwarenessUpdate = {
    added: number[];
    updated: number[];
    removed: number[];
};
```

## Block

An id-only, read-only block handle: every getter reads the document index
(transaction-aware). Writes are the view's operations, with the block's id
(`edytor.splitBlock(block.id, …)`). The view keeps one handle per id
(`edytor.idToBlock`); `node` is the element the core renders for it.

A reactive reader (a template, a `$derived`, an `$effect`) of a getter
depends on the cells the answer comes from (this block's, its parent's,
the root's list), so it re-runs when a commit changes them, as `data`
does; a read anywhere else is a plain read of the document.

```ts
export declare class Block {
    #private;
    readonly: boolean;
    readonly edytor: Edytor;
    readonly id: string;
    node?: HTMLElement;
    constructor(edytor: Edytor, id: string);
    get isRoot(): boolean;
    get parent(): Block | undefined;
    get children(): Block[];
    get content(): (Text | InlineBlock)[];
    get type(): string;
    get data(): Readonly<Record<string, any>>;
    get definition(): BlockDefinition;
    get selected(): boolean;
    get focused(): boolean;
    get movable(): boolean;
    get convertible(): boolean;
    get index(): number;
    get depth(): number;
    get path(): number[];
    get isInTree(): boolean;
    get nextBlock(): Block | null;
    get previousBlock(): Block | null;
    get closestPreviousBlock(): Block | null;
    get closestNextBlock(): Block | null;
    get hasChildren(): boolean;
    get hasContent(): boolean;
    get isEmpty(): boolean;
    get isNested(): boolean;
    get value(): JSONBlock;
    isChildOf(block: Block): boolean;
    get rendersContent(): boolean;
    get isContainer(): boolean;
    get isListItem(): boolean;
    get list(): Block | undefined;
    get firstText(): Text | undefined;
    get lastText(): Text | undefined;
    void: (node: HTMLElement) => void;
    textAtOffset: (offset: number) => {
        text: Text;
        offset: number;
    } | null;
}
```

- `parent`: The display parent (the root for a top-level block); none for the root or a dead block.
- `content`: Text segments and inline atoms, alternating, starting and ending with a text.
- `data`: The block's `data`, read-your-writes; a reactive reader depends on the block's cell.
- `movable`: May this block move at all: `canPlace` without a destination (drag handles).
- `convertible`: May text-level structural commands (convert, markdown shortcut, slash menu) apply: the block is movable — or the virtual paragraph of an emptied document, which a conversion creates with its kind — has no role of its own and renders its own content — a container (a list) is never converted, its items are.
- `isInTree`: The block is visible in the document (a dead handle answers false).
- `hasContent`: It shows content: text or an inline atom (a block holding only a mention is not empty).
- `isEmpty`: It holds nothing: no content and no children (the emptiness Turn into reads, `holdsNothing`).
- `value`: This block's JSON — the document's one serializer (`facade.blockJSON`).
- `rendersContent`: Whether this kind renders its own content slot — the adopted capability.
- `isContainer`: A container: it shows only its children (a list), neither void nor an island — the document's container rule.
- `isListItem`: An item of a list: its parent is a container whose items are a kind of their own (a `list-item` in an `unordered-list`), not the document's default — a column of paragraphs is no list.
- `list`: The list this block shows in: its container when it is an item (`isListItem`), or — an item outdented out of a nested list into the item holding it — the list of the items of its kind it sits under.
- `firstText`: The first text of this block's own content — none for a kind that does not render its content (a list container, a divider): its slot is never displayed, so no caret or endpoint may land there.
- `lastText`: The last text of this block's own content (see `firstText`).
- `void`: Mark an element inside the block's markup as non-editable chrome (a header, a caption bar).
- `textAtOffset`: The text segment that displays block offset `offset`, and the offset in it — the one offset → segment mapper over the committed parts (a boundary before an atom reads the text before it; past the end, the last text's end).

## BLOCK_ACTIVATE_EVENT

The DOM event a grip click dispatches on the editor (the one way to
answer it: the block menu's, or your own). A menu that takes the keyboard answers it with `preventDefault()`
(the block menu does); unanswered, the editor takes the focus, so the keys
act on the selected block (Backspace deletes it, Shift+arrows extend it).

```ts
declare const BLOCK_ACTIVATE_EVENT = "edytor-block-activate";
```

## BLOCK_ADD_EVENT

The DOM event the `+` dispatches on the editor. A menu answers it with
`preventDefault()` (the slash menu does), adds nothing until a row is
picked, then calls `insert(then)`; unanswered, the `+` inserts at once.

```ts
declare const BLOCK_ADD_EVENT = "edytor-block-add";
```

## BLOCK_COLORS

Notion's palette: the colour names a block may hold, in menu order ("Default" is none).

```ts
declare const BLOCK_COLORS: readonly [
    "gray",
    "brown",
    "orange",
    "yellow",
    "green",
    "blue",
    "purple",
    "pink",
    "red"
];
```

## BlockActivation

```ts
export type BlockActivation = {
    block: Block;
    anchor: HTMLElement;
};
```

## BlockAddition

```ts
export type BlockAddition = {
    block: Block;
    anchor: HTMLElement | null;
    insert: (then?: () => unknown) => unknown;
};
```

- `anchor`: The `+` clicked, else the block's element.
- `insert`: Add an empty block of the parent's default kind below `block` (above with Alt; an empty block of that kind is reused), put the caret in it and run `then`, as one undo step.

## BlockAttribution

```ts
export type BlockAttribution = {
    createdBy?: ActorId;
    contributors: ReadonlySet<ActorId>;
    lastChangedBy?: ActorId;
};
```

- `createdBy`: The actor that created the block — assigned at creation, never rewritten.
- `contributors`: Union set of actors that committed an edit addressing this block id, plus the pre-split snapshot a split/merge copies to siblings — block-identity LINEAGE, add-only.
- `lastChangedBy`: The actor whose state change currently wins the LWW resolution.

## BlockColorField

A block's colour fields: its text colour and its background.

```ts
export type BlockColorField = 'color' | 'background';
```

## BlockDefinition

Defines the structure and behavior of a block type.

```ts
export type BlockDefinition = {
    snippet?: Snippet<[
        BlockSnippetPayload<any>
    ]>;
    element?: BlockElement | ((data: Record<string, any>, id?: string) => BlockElement);
    contentElement?: BlockElement | ((data: Record<string, any>, id?: string) => BlockElement);
    viewState?: string[];
    void?: boolean;
    island?: boolean;
    lines?: boolean;
    layout?: boolean;
    table?: boolean;
    menu?: (block: Block) => KindMenuAction[];
    atomic?: readonly (string | readonly string[])[];
    rendersContent?: boolean;
    upload?: KindUpload;
    continues?: boolean;
    container?: boolean;
    body?: boolean;
    defaultChild?: string;
    itemKind?: string;
    presets?: KindPreset[];
    empty?: Pick<JSONBlock, 'content' | 'children'>;
    html?: string | ((block: JSONBlock, content: string, children: string) => string);
    plain?: (block: JSONBlock, content: string, children: string) => string;
    parse?: (element: HTMLElement) => Record<string, SerializableContent> | undefined;
    transformText?: TextTransform;
    onFocus?: (payload: {
        block: Block;
    }) => void;
    onBlur?: (payload: {
        block: Block;
    }) => void;
    onSelect?: (payload: {
        block: Block;
    }) => void;
    onDeselect?: (payload: {
        block: Block;
    }) => void;
    normalizeContent?: (payload: {
        block: Block;
    }) => (() => void) | void;
    normalizeChildren?: (payload: {
        block: Block;
    }) => (() => void) | void;
};
```

- `snippet`: The markup inside the block element; a kind whose element takes no content (`hr`) has none.
- `element`: The element the core renders for the block: a tag, or tag and attributes, possibly from the block's data and id (the second argument: view state the kind keeps per block, such as a column's width while a resize drags; reactive state it reads re-renders the element; absent when HTML import reads a kind's tag for data alone).
- `contentElement`: The element the core wraps around the block's own text (`content()`), possibly from the block's data and id: a heading's `h1`–`h3`, a quote's `blockquote`.
- `viewState`: Attributes of the block element the browser or the user own (`open` on a `details`): declared view state, never inverted.
- `void`: A void kind (an image, a divider): one unit the editor never edits as text and never merges.
- `island`: An island kind (a code block, a table): editable inside, structurally sealed.
- `lines`: An island of lines (the code block): each direct child shows as this kind's `defaultChild` and holds no children, even when an undo or a peer's edit puts another kind or a nested block there.
- `layout`: A layout (the columns plugin's `columns`): its `defaultChild` is its item kind (a column, a container that holds any block), shown side by side.
- `table`: A table (the table plugin's `table`): its `defaultChild` is its row kind, a container whose own `defaultChild` is the cell kind (a text island).
- `menu`: Rows the block menu adds for a block of this kind, when it is opened on that block alone (a table's Header row switch), after Turn into and Color.
- `atomic`: Data paths written as one value (`data.atomic`): a top-level key, or an array of keys for a nested one.
- `rendersContent`: Whether the snippet renders the block's own content (`content()`).
- `upload`: The files the kind takes (the image's `image/*`, the file block's any): with the uploads plugin listed, an empty block's Upload sends one and a pasted or dropped file becomes a block of this kind (`media.files`).
- `continues`: A list-like kind (Notion's bullets, numbers, to-dos, toggles): Enter at the start or end of a non-empty block opens another block of this kind (with its first preset's data), and Enter in an empty one ends the run — it outdents under a parent of a continuing kind, else turns into the parent's default child in place (at the top level, in a callout).
- `container`: A container kind (Notion's toggles, callouts, quotes): its content is a header over its children.
- `body`: A container whose children are a body that shows even while it is empty (Notion's callout: a title over its content): Enter at the end of its header opens its first child, as an open toggle's does, and a paste there leads that body.
- `defaultChild`: The type a new child of this block takes by default (Enter inside a child, a split, an island merged out into it).
- `itemKind`: A list container's flat counterpart (`numbered-list-item` for an `ordered-list`): its items show as this kind, so the menus name them by it and turning one into it keeps it in the list.
- `presets`: Catalogue rows: one per way to create this kind — the slash menu, markdown shortcuts and block menus are generated from them (`edytor.kinds`).
- `empty`: The content and children a conversion into this kind creates (a void has none; an island starts with a first child).
- `html`: Clipboard HTML form: a tag wrapping content then children, or a function of the block and its serialized content and children.
- `plain`: Clipboard plain-text form; default content then children, one per line.
- `parse`: HTML import: the block's data when `element` is this kind, else `undefined`.
- `transformText`: Transform text content within the block
- `onFocus`: Called when the block receives focus
- `onBlur`: Called when the block loses focus
- `onSelect`: Called when the block is selected
- `onDeselect`: Called when the block is deselected
- `normalizeContent`: Normalize block content Called at the end of the transaction of each operation that wrote the block's content: the block is an id-only handle whose reads show the operation's writes.
- `normalizeChildren`: Normalize block children Called at the end of the transaction of each operation that changed the block's children: the block is an id-only handle whose reads show the operation's writes.

## BlockElement

The block element a kind declares: a tag, or a tag and attributes.

```ts
export type BlockElement = string | {
    tag: string;
    attributes?: Record<string, string | undefined>;
};
```

## BlockHandleController

```ts
export declare class BlockHandleController {
    #private;
    private edytor;
    private options;
    private readonly owner;
    private readonly targets;
    private readonly registered;
    dragging: string | null;
    private scrolling;
    private group;
    private readonly indicator;
    private held;
    private handle;
    private gripped;
    constructor(edytor: Edytor, options?: BlockHandleControllerOptions);
    get readonly(): boolean;
    get draggable(): boolean;
    labelOf: (block: Block) => string;
    opened: (block: Block, control: PopupOpener["control"]) => Popup | undefined;
    grip: (block: Block) => Attachment<HTMLElement>;
    plus: (block: Block) => Attachment<HTMLElement>;
    addsColumn(block: Block): boolean;
    addBlock(block: Block, alt?: boolean, anchor?: HTMLElement | null): void;
    registerHandle(element: HTMLElement, block: Block): () => void;
    addDropTarget(node: HTMLElement, target: Block): () => void;
    private gutter;
    private margin;
    private halves;
    private pick;
    private reorder;
    private moving;
    private covered;
    dragBlocks(source: Block): Block[];
    private select;
    private fits;
    private rowAt;
    private get layouts();
    private besideOf;
    private layoutOf;
    private beside;
    private inside;
    private shown;
    private emptied;
    private zones;
    private placement;
    private carrier;
}
```

- `dragging`: The block whose handle is the source of the drag in progress (its handle stays mounted).
- `labelOf`: The block's kind as people read it (`kindLabel`: "Heading 1"), its handle's name; reactive through the block's cell (a Turn into renames it).
- `opened`: The popup the `+` or the grip of `block` opened, while it is open (`edytor.popups`).
- `grip`: The block's grip (`{@attach handle.grip}`): dragging (when draggable), a click that selects the block and opens its menu, the Alt+arrow moves (`aria-keyshortcuts`), and the ARIA of the menu it opened.
- `plus`: The block's `+` (`{@attach handle.plus}`): a click offers what to add below (Alt+click above, or a column beside: `addBlock`) under it; a press keeps the editor's focus; the ARIA of the menu it opened.
- `addsColumn`: Whether the `+`'s Alt+click on `block` adds a column right of its column (a block directly in a layout's column, as Notion) rather than a block above it.
- `addBlock`: The `+`: offers the new block to a menu (`BLOCK_ADD_EVENT`), which adds it once the user picks what to insert; with none answering, adds it now (`BlockAddition.insert`).
- `addDropTarget`: A candidate drop target; registered at once when our drag is in progress.
- `dragBlocks`: The blocks a drag (or an Alt+arrow) from `source`'s handle moves (Notion): the outermost of those the selection covers (`covered`), in document order — a block's children and a closed toggle's hidden body travel with it; else `source` alone.

## BlockHandleSnippetPayload

What a `handle` snippet receives.

```ts
export type BlockHandleSnippetPayload = {
    block: Block;
    label: string;
    grip: Attachment<HTMLElement>;
    plus: Attachment<HTMLElement>;
    add: (alt?: boolean, anchor?: HTMLElement | null) => void;
    beside: boolean;
    readonly: boolean;
    draggable: boolean;
};
```

- `label`: The block's kind as people read it ("Heading 1", its preset's label): name your controls with it.
- `grip`: The grip's attachment (`{@attach handle.grip}`): the drag source, a click that opens the block's menu, the Alt+arrow moves (`aria-keyshortcuts`), and the ARIA of the menu it opened (`aria-expanded`, `aria-controls`, `aria-haspopup`).
- `plus`: The `+`'s attachment (`{@attach handle.plus}`): a click calls `add` (Alt+click with `true`) with the `+` as the anchor, a press keeps the editor's focus, and the ARIA of the menu it opened.
- `add`: The `+`: the slash menu offers what to add below; with `true` (Alt+click) above, or, for a block directly in a layout's column, in a new column right of its column.
- `beside`: Alt+click on the `+` adds a column right of the block's (a block directly in a layout's column): name your `+` with it.

## BlockHandlesOptions

```ts
export type BlockHandlesOptions = {
    draggable?: boolean;
    handle?: Snippet<[
        BlockHandleSnippetPayload
    ]>;
};
```

- `draggable`: Keep the handle and its keyboard actions, but omit pointer dragging and drop targets.
- `handle`: The `+` and ⋮⋮ beside each block, and their words (without it, no handle shows and no block drags); placement and hover are the plugin's.

## BlockId

Logical block identifier — caller-assigned, immutable per block.

```ts
export type BlockId = string;
```

## BlockMenuAction

```ts
export type BlockMenuAction = {
    id: string;
    label: string;
    icon: string;
    hint?: string;
    danger?: boolean;
    checked?: boolean;
    submenu?: 'turn' | 'color';
    isEnabled?: () => boolean;
    run?: () => unknown;
};
```

- `checked`: A switch's state (a kind's row, `BlockDefinition.menu`): the row is a `menuitemcheckbox`.
- `submenu`: Opens a flyout instead of running: the kinds (`turn`) or the colours (`color`).

## BlockMenuColor

A row of the Color flyout: a text colour or a background, `value` `null` for the default.

```ts
export type BlockMenuColor = {
    id: string;
    label: string;
    field: BlockColorField;
    value: (typeof BLOCK_COLORS)[number] | null;
};
```

- `id`: `color.<name>` or `background.<name>` (`color.default`, `background.default`).
- `label`: Its words in the view's labels (`colorText`, `colorBackground`): "Red text", "Red background", "Default text".

## BlockMenuController

```ts
export declare class BlockMenuController {
    #private;
    private edytor;
    private options;
    block: Block | null;
    blocks: Block[];
    anchor: HTMLElement | null;
    query: string;
    index: number;
    flyout: false | "color" | "turn";
    flyoutIndex: number;
    constructor(edytor: Edytor, options?: BlockMenuOptions);
    get isOpen(): boolean;
    get readonly(): boolean;
    get menuId(): string;
    get flyoutId(): string;
    rowId: (row: BlockMenuAction | KindRow | BlockMenuColor, flyout?: boolean) => string;
    popup: Attachment<HTMLElement>;
    flyoutMenu: Attachment<HTMLElement>;
    keys: Attachment<HTMLElement>;
    option: (index: number, flyout?: boolean) => Attachment<HTMLElement>;
    search(query: string): void;
    get members(): Block[];
    get kinds(): KindRow[];
    get colorable(): Block[];
    get colors(): BlockMenuColor[];
    isCurrentColor: (row: BlockMenuColor) => boolean;
    get flyoutRows(): Array<KindRow | BlockMenuColor>;
    get currentKind(): KindRow | undefined;
    get actions(): BlockMenuAction[];
    get matchingColors(): BlockMenuColor[];
    get rows(): Array<BlockMenuAction | KindRow | BlockMenuColor>;
    open(block: Block, anchor: HTMLElement): void;
    close(restoreCaret?: boolean): void;
    move(direction: 'up' | 'down'): void;
    turnInto(kind: KindRow): void;
    paint(row: BlockMenuColor): void;
    duplicate(blocks: Block[], select: boolean): void;
    remove(): void;
    get linked(): Block | undefined;
    copyLink(): Promise<void>;
    run(row: BlockMenuAction | KindRow | BlockMenuColor | undefined): void;
    openFlyout(which?: 'turn' | 'color'): void;
    private restore;
    private caret;
    private focus;
}
```

- `block`: The block whose grip opened the menu.
- `blocks`: The blocks the actions apply to, in document order: the block selection when it holds `block` (Notion), else `block` alone — as clicked, so a grip-selected list is one block (Move, Duplicate, Copy link, the caret after).
- `index`: The keyboard's row.
- `flyout`: The open flyout: the kinds (`turn`), the colours (`color`), or none.
- `flyoutIndex`: The keyboard's row in the open flyout.
- `readonly`: The editor is readonly: the menu closes.
- `menuId`: The menu's and the Turn into flyout's element ids (page-unique), for `aria-controls`.
- `rowId`: A row's element id (page-unique; a flyout's with `flyout`), for `aria-activedescendant`.
- `popup`: The menu element (`{@attach controller.popup}`, the one with the rows): it takes the menu's id, its `menu` role and name unless it has its own, keeps the focus where it is on a press (a field takes its own), and is published to the view's root (`edytor.popups`) while it is in the page, the grip that opened it told so.
- `flyoutMenu`: An open flyout (`{@attach controller.flyoutMenu}`, named by your markup): its id, `menu` role, the overlay's placement beside the menu, and presses that keep the focus.
- `keys`: The element that holds the keyboard (`{@attach controller.keys}`: the search field): focused when it mounts, it takes every key of the menu (the arrows, Home and End walk the rows, → opens a row's flyout and ← closes it, Enter runs the row, Delete removes the blocks while the query is empty, Escape closes the flyout, then the menu), and names the open menu and its highlighted row (`aria-controls`, `aria-activedescendant`, once that row is in the page).
- `option`: The attachment of the row at `index` (of the open flyout's rows with `flyout`): `{@attach controller.option(index)}` on its element.
- `search`: The search field typed in: the rows match `query`, the first one highlighted, no flyout open.
- `members`: What Delete and Turn into act on: `blocks`, a list or a code block with its subtree (`selectedMembers`).
- `kinds`: The Turn into rows: the kinds, then the commands that turn `blocks` into something (`turnCommands`).
- `colorable`: The blocks the Color flyout paints: the `members` that take a colour (`colorable`).
- `colors`: The Color flyout's rows: Notion's text colours, then its backgrounds.
- `isCurrentColor`: Whether every block the flyout paints holds `row`'s colour (its ✓).
- `flyoutRows`: The rows of the open flyout.
- `currentKind`: The row naming the open block.
- `matchingColors`: With a query, the colours it names join the list after the kinds (Notion's "Color" results: `red` lists "Red text" and "Red background"), when the blocks take one (`colorable`).
- `rows`: Every keyboard row: actions, then the matching kinds, then the matching colours.
- `turnInto`: Convert the open block, or every member (a list's items) as one undo step (they stay selected); one block's conversion places the caret (refused: the caret returns).
- `paint`: Paint the open blocks (`colorable`, a list's items too) with `row`'s colour or background as one undo step (`setBlockColor`); the menu closes and the blocks stay selected, as in Notion.
- `duplicate`: Duplicate `blocks` as one undo step, each copy after its block (a block inside another of them is copied with it; a vetoed one is skipped, `dispatcher.each`); the copies are selected (`select`), else the first copy takes the caret (refused: its block keeps it).
- `remove`: Delete the open blocks' `members` (a list with its items) as the keyboard's block delete does (`deleteSelectedBlocks`): `onDeleteSelectedBlocks` may keep them, then one command (`deleteBlocks`, one plan, so a veto keeps them all), unselected children taking their parent's place, the caret where that delete puts it (kept or refused: the caret or the selection returns).
- `linked`: The one block "Copy link" names: the open block, or the layout its block selection covers whole (`liftLayouts`); none for several.
- `run`: Run a row of the menu or of its open flyout (a click; Enter runs the keyboard's): paint a colour, convert to a kind, open an action's flyout or run it.
- `openFlyout`: Open a flyout on its first row (keyboard or mouse): the "Turn into" kinds (default) or the colours; that flyout already open stays as it is.

## BlockMenuLabels

The block menu's actions (its search matches them) and the block colours' commands.

```ts
export type BlockMenuLabels = {
    turnInto: string;
    color: string;
    colors: Record<string, string>;
    colorText: (color: string) => string;
    colorBackground: (color: string) => string;
    copyLink: string;
    duplicate: string;
    moveUp: string;
    moveDown: string;
    delete: string;
    ctrl: string;
    shift: string;
    deleteKey: string;
};
```

- `color`: The Color row.
- `colors`: A color's name, by its `BLOCK_COLORS` name (`red`), `default` for none.
- `colorText`: A text color row and command, from the color's name ("Red text").
- `colorBackground`: A background row and command, from the color's name ("Red background").
- `ctrl`: Key names in the shortcuts shown off a Mac.

## BlockMenuOptions

```ts
export type BlockMenuOptions = {
    linkTo?: (block: Block) => string;
    menu?: Snippet<[
        BlockMenuController
    ]>;
};
```

- `linkTo`: A link to the block, for "Copy link to block" (the row is hidden without it).
- `menu`: The menu's markup and its words; it renders while `controller.isOpen`, placed beside the handle (mark your panel `data-edytor-block-menu` for placement; without it, nothing shows).

## BlockMoveDirection

One relative step: `up`/`down` pass the previous/next sibling, never
entering its children, and past the first/last sibling leave the parent
(before/after it; a layout's column: the layout, `layout.fits`); `in` =
last child of the previous sibling; `out` =
after the parent, the siblings after the last moved block becoming its
children (the document's outdent, `unNestBlocks`).

```ts
export type BlockMoveDirection = 'up' | 'down' | 'in' | 'out';
```

## BlockMovePosition

Where a move places the blocks relative to its target: `before`, `after`,
`inside` (its last child), or `left`/`right` of it, side by side
(`prepare.placeBeside`, `layout.place-beside`: a new column of the layout
the target is in, else a new layout wrapping the target).

```ts
export type BlockMovePosition = 'before' | 'after' | 'inside' | 'left' | 'right';
```

## BlockMoveRequest

```ts
export type BlockMoveRequest = {
    blocks: Block[];
} & ({
    target: Block;
    position: BlockMovePosition;
} | {
    direction: BlockMoveDirection;
});
```

## BlockRole

Structural role of a block type (plugin-side policy, resolved per op).

```ts
export type BlockRole = {
    void?: boolean;
    island?: boolean;
    lines?: boolean;
    layout?: boolean;
    table?: boolean;
    atomic?: readonly (string | readonly string[])[];
};
```

- `void`: Not editable through normal structural flow; no children, no merges.
- `island`: Editable, but its subtree is structurally sealed from outside blocks.
- `lines`: An island of lines (code): each direct child displays as its `defaultChild` kind and holds no children.
- `layout`: A layout (columns): it displays only its items — its `defaultChild` kind, a container that renders no content — side by side, and only while it shows two or more (`layout.*` in the delete contract).
- `table`: A table: it displays only its rows — its `defaultChild` kind, a container whose own `defaultChild` is the cell kind (a text island) — and each row only its cells, in the order of the table's `data.columns` (`table.*` in the delete contract).
- `atomic`: Data paths written as one leaf (`data.atomic`): a top-level key, or an array of keys for a nested one (`['link', ['media', 'source']]`).

## BlockSnippetPayload

Represents the payload for block snippets.

```ts
export type BlockSnippetPayload<D = Record<string, any>> = {
    block: BlockView<D>;
    content: Snippet;
    children: Snippet | null;
};
```

## BlockSpec

Declarative block description used for inserts (and by seeders).

```ts
export type BlockSpec = {
    id: BlockId;
    type: string;
    data?: Record<string, unknown>;
    content?: ContentItem[];
    children?: BlockSpec[];
};
```

## BlockView

What a block snippet receives: declared
values read from the block's cell and the selection — reactive, never the
block's handle itself — plus the handle for document reads and commands.

```ts
export type BlockView<D = Record<string, any>> = {
    readonly id: string;
    readonly type: string;
    readonly data: Readonly<D>;
    readonly selected: boolean;
    readonly focused: boolean;
    readonly handle: Block | undefined;
    void: Block['void'];
};
```

- `handle`: The block's id-only, read-only handle (its getters, reactive in a template); `undefined` in a suggestion's preview, which is no block of the document.
- `void`: Marks inner chrome (a header, a toolbar) non-editable; the block element is the core's.

## BookmarkCardPayload

What a bookmark's `card` snippet receives: the page it links to, as `unfurl` described it.

```ts
export type BookmarkCardPayload = {
    block: Block | undefined;
    url: string;
    title: string | undefined;
    description: string | undefined;
    image: string | undefined;
    icon: string | undefined;
    link: Attachment<HTMLElement>;
};
```

- `block`: The bookmark; `undefined` in a suggestion's preview.
- `url`: The page's URL (`http(s)`).
- `title`: Its title, description, cover image and icon, when `unfurl` answered them.
- `link`: The card's link (`<a href={card.url} {@attach card.link}>`): a new tab without the opener, never dragged out of the editor.

## BookmarkPluginOptions

```ts
export type BookmarkPluginOptions = {
    unfurl?: (url: string) => Promise<BookmarkPreview | null | undefined>;
    menu?: Snippet<[
        UrlPasteController
    ]>;
    card?: Snippet<[
        BookmarkCardPayload
    ]>;
};
```

- `unfurl`: Read a page's title, description and images, usually through your server (a browser cannot fetch most pages).
- `menu`: The menu a URL pasted on an empty line opens (Link, Embed, Bookmark); it renders while `controller.open`, under the line (without it, the link is kept and nothing shows).
- `card`: The card of a bookmark with a link (default: Notion's, the title, description and URL beside the cover): `<a href={card.url} {@attach card.link}>`.

## BookmarkPreview

What an `unfurl` answers about a page; every field optional.

```ts
export type BookmarkPreview = {
    title?: string;
    description?: string;
    image?: string;
    icon?: string;
};
```

- `image`: The page's cover image (`og:image`): an `http(s)` URL.
- `icon`: The page's icon: an `http(s)` URL.

## CALLOUT_ICONS

The picker's choices unless `createCalloutIconsPlugin({ icons })` names others.

```ts
declare const CALLOUT_ICONS: readonly string[];
```

## CalloutIconPayload

What a callout's `calloutIcon` snippet receives.

```ts
export type CalloutIconPayload = {
    readonly block: Block | undefined;
    readonly icon: string;
    readonly editable: boolean;
    readonly expanded: boolean;
    readonly label: string;
    toggle: () => void;
    readonly control: Attachment<HTMLElement>;
};
```

- `block`: The callout; `undefined` in a suggestion's preview.
- `icon`: Its icon: its `data.icon`, the view's default without one, `''` for none (reactive).
- `editable`: The icon opens the view's picker: an editable view listing a picker, not a suggestion's preview.
- `expanded`: The picker is open on this callout (reactive).
- `label`: The button's accessible name: Change icon, or Add icon over an empty slot.
- `toggle`: Open the view's picker on this callout, or close it.
- `control`: The icon's attachment (`{@attach icon.control}`): its marker (`data-edytor-callout-icon`, which the picker opens under), `data-empty` without an icon, `contenteditable="false"`; editable, its `aria-label`, `aria-haspopup`, `aria-expanded`, `aria-controls`, a press that keeps the editor's selection and a click that toggles the picker; else `aria-hidden`.

## CalloutIconPicker

A view's callout icon picker: what a `callout.picker` snippet receives.
The callout it is open on (`block`), the choices (`icons`) and the icon
that callout shows (`current`); `set(icon)` and `remove()` write it and
close, `close()` closes with nothing written. Put `{@attach picker.keys}`
and `{@attach picker.popup}` on the picker's element (it takes the focus
and the keys, names the keyboard's row and is published to the view's
root), attach `picker.option(i)` to each row (`i` in `icons`,
`icons.length` for Remove icon) and run `picker.pick(i)` on its click.

```ts
export declare class CalloutIconPicker extends ChromeList {
    #private;
    readonly icons: readonly string[];
    target: string | null;
    readonly columns = 8;
    constructor(edytor: Edytor, icons?: readonly string[]);
    get isOpen(): boolean;
    get block(): Block | undefined;
    get current(): string | undefined;
    protected get count(): number;
    get readonly(): boolean;
    toggle: (block: Block) => void;
    open: (block: Block) => void;
    close: (keys?: boolean) => void;
    set: (icon: string) => void;
    remove: () => void;
    pick: (index: number) => void;
    option(index: number): Attachment<HTMLElement>;
    keys: Attachment<HTMLElement>;
}
```

- `icons`: The picker's choices.
- `target`: The callout the picker is open on (its id), `null` when closed.
- `columns`: The choices on a row: ArrowUp and ArrowDown move by a row, so lay the icons out in as many columns.
- `isOpen`: Whether the picker is open (reactive).
- `block`: The callout it is open on (reactive).
- `current`: The icon that callout shows (`''`: none; reactive).
- `count`: The keyboard's rows: the icons, then Remove icon.
- `readonly`: The view is readonly (reactive): the picker closes.
- `toggle`: Open the picker on `block`, or close it when it is open there.
- `open`: Open the picker on the callout `block`, its icon the keyboard's row (refused in a view that may not write).
- `close`: Close the picker; with `keys` (by default), the editor takes the keys back, with its selection.
- `set`: Write `icon` on the callout (one undo step), then close.
- `remove`: Leave the callout without an icon (`data.icon` is `''`), then close.
- `pick`: Pick row `index`: its icon, or Remove icon (`icons.length`).
- `option`: Row `index`'s attachment (`icons.length`: Remove icon), as every menu's rows: its id, `menuitemradio` role and `aria-checked` (`menuitem` for Remove icon); its highlight is the markup's (`index`).
- `keys`: The picker's element (`{@attach picker.keys}`): it takes the focus as it opens and names the keyboard's row (`aria-activedescendant`).

## CalloutIconsOptions

```ts
export type CalloutIconsOptions = {
    picker?: Snippet<[
        CalloutIconPicker
    ]>;
    icons?: readonly string[];
};
```

- `picker`: The icon picker's markup and its words: it renders while the picker is open, placed under the icon (without it, the icon opens nothing).
- `icons`: The picker's choices (`CALLOUT_ICONS` by default).

## Caret

A caret in block offsets: the block and the offset in its content, the
offsets `edytor.document.facade` takes (an inline atom counts 1). The
public form of a collapsed text selection (`selection.caret`), and what
`selection.setCaret` and `dispatcher.caret` take.

```ts
export type Caret = {
    block: Block;
    offset: number;
};
```

## Cell

```ts
export type Cell = {
    readonly id: BlockId;
    readonly type: string;
    readonly data: Data;
    readonly childIds: readonly BlockId[];
    readonly runs: readonly ContentRun[];
};
```

## Cells

```ts
export type Cells = {
    readonly rootIds: readonly BlockId[];
    readonly size: number;
    get: (id: BlockId) => Cell | undefined;
    apply: (report: CellReport) => Patched;
    remount: (id: BlockId) => void;
    epoch: (id: BlockId) => number;
    dispose: () => void;
};
```

- `rootIds`: The root's visible child ids.
- `apply`: Patch from one change report.
- `remount`: Re-create a block's text elements from its cell (the observer's repair of foreign damage).
- `epoch`: Bumped by `remount`: part of the block's text element keys.

## ChangePayload

What `onBeforeOperation` receives: a document write under its name with
its arguments (`payload`: `Parameters<DocumentWrites[K]>`), or a view
command with its payload (`Commands`); `operation` narrows `payload`.

```ts
export type ChangePayload = {
    [K in keyof DocumentWrites]: Change<K, Parameters<DocumentWrites[K]>>;
}[keyof DocumentWrites] | {
    [K in keyof Commands]: Change<K, Commands[K]>;
}[keyof Commands];
```

## clearDocument

```ts
declare const clearDocument: ProviderStack['clearDocument'];
```

## CODE_LANGUAGES

The default languages, after Notion's list, sorted by label (Plain text first).

```ts
declare const CODE_LANGUAGES: readonly CodeLanguage[];
```

## CodeHeader

What a code plugin's `header` snippet receives: the code block's
language (`language`, its id; `label`, as shown), whether the language may be changed here
(`editable`: an editable view, not a suggestion's preview), the list's
state (`expanded`), the actions (`toggle`, the list's own `pick`, `copy`, and
`copied` for a moment after a copy) and the plugin's `labels`. Put
`{@attach header.button}` on the element that opens the language list:
it opens it on a click, ArrowDown or ArrowUp, Escape gives the keys back
to the editor, the toolbar's shortcut at a caret in the block reaches it, and it carries
the ARIA (`aria-haspopup`, `aria-expanded`, `aria-controls`, its name).

```ts
export declare class CodeHeader {
    #private;
    copied: boolean;
    get block(): Block | undefined;
    get language(): string;
    get label(): string;
    get readonly(): boolean;
    get editable(): boolean;
    get expanded(): boolean;
    toggle: (keys?: boolean) => void;
    copy: () => Promise<void>;
    button: Attachment<HTMLElement>;
}
```

- `copied`: Copy just copied: the button may read Copied (reactive, for a moment).
- `block`: The code block; `undefined` in a suggestion's preview.
- `language`: The block's language id: its `data.language`, else the plugin's default (reactive).
- `label`: The language's label; a language the plugin does not list shows its id (reactive).
- `readonly`: The view is readonly (reactive).
- `editable`: The language may be changed here: an editable view, not a suggestion's preview (reactive).
- `expanded`: The language list is open on this block (reactive).
- `toggle`: Open the language list under the header's button, or close it (`keys`: the keyboard opened it, so the keys go back to the button after).
- `copy`: Copy the block's code to the clipboard; `copied` reads true for a moment.
- `button`: The element that opens the language list (`{@attach header.button}`, a `button`): a click opens or closes it (from Enter or Space, the keys opened it and get it back), ArrowDown or ArrowUp opens it, Escape gives the keys back to the editor; a press keeps the editor's selection.

## CodeLabels

```ts
export type CodeLabels = {
    code: string;
};
```

- `code`: The preset (a language's name is its `CodePluginOptions.languages` row's).

## CodeLanguage

A language a code block may be in.

```ts
export type CodeLanguage = {
    id: string;
    label: string;
    grammar?: string;
    load?: () => Promise<readonly LanguageDefinition[]>;
};
```

- `id`: Stored in the code block's `data.language`.
- `label`: Shown in the header and its picker.
- `grammar`: The name of the grammar its lines are tokenized with; absent, its lines are plain text (no tokens).
- `load`: Loads the grammar, then the grammars it embeds (HTML's styles and scripts).

## CodePluginOptions

```ts
export type CodePluginOptions = {
    languages?: readonly CodeLanguage[];
    defaultLanguage?: string;
    header?: Snippet<[
        CodeHeader
    ]>;
    menu?: Snippet<[
        LanguageMenu
    ]>;
};
```

- `languages`: The languages a code block may be in, in the header's order, with their names.
- `defaultLanguage`: The language of a code block whose `data.language` is unset.
- `header`: A code block's header (the language's button and Copy) and its words: it renders above the code, in the block's non-editable part (without it, no header).
- `menu`: The language list: it renders under the header's button while `menu.open` is set (without it, nothing shows).

## ColumnsLabels

```ts
export type ColumnsLabels = {
    columns: (count: number) => string;
    resize: string;
};
```

- `columns`: The `columns.<n>` command.
- `resize`: A resize band's accessible name.

## ColumnsPluginOptions

```ts
export type ColumnsPluginOptions = {
    minWidth?: number;
};
```

- `minWidth`: The narrowest a column may be resized to, as a fraction of its layout's width (default `0.1`).

## CommandResult

The result of the last command a view ran (`edytor.dispatcher.last`): its
operation and its status.

```ts
export type CommandResult = {
    operation: string;
    status: 'refused' | 'noop' | 'applied' | 'failed';
    error?: unknown;
    selection?: SelectionValue;
};
```

- `selection`: The result selection the command authored, selected once.

## Commands

The view's commands that are no single document write, each one prepared
plan of them, with their payloads: a hook sees each by its name, then its
planned steps as the writes they perform.

```ts
export interface Commands {
    acceptSuggestion: {
        suggestion: {
            id: string;
            at: ResolvedAt;
            content: readonly JSONBlock[];
        };
    };
    replaceMatches: {
        matches: {
            block: string;
            offset: number;
            length: number;
        }[];
        replacement: string;
    };
    addComment: {
        thread: string;
        runs: {
            block: string;
            offset: number;
            length: number;
        }[];
    };
    moveText: {
        from: {
            start: {
                block: string;
                offset: number;
            };
            end: {
                block: string;
                offset: number;
            };
        };
        to: {
            block: string;
            offset: number;
        };
        copy: boolean;
    };
    inlineMarkdown: {
        value: string;
        start: number;
        end: number;
    };
    insertDivider: Record<string, never>;
    insertTableRow: {
        index: number;
    };
    deleteTableRows: {
        rows: Block[];
    };
    insertTableColumn: {
        index: number;
        width?: number;
    };
    deleteTableColumn: {
        column: string | number;
    };
    moveTableColumn: {
        column: string | number;
        to: number;
    };
    moveTableRows: {
        rows: Block[];
        to: number;
    };
    fillTableCell: {
        row: string;
        column: string | number;
    };
}
```

- `acceptSuggestion`: A suggestion placed in the document (`edytor.suggestions`, `suggestion.accept()`).
- `replaceMatches`: Find and replace (`plugins/find`): each match's text replaced by `replacement`, one plan, one undo step.
- `addComment`: A comment thread's anchor (`plugins/comments`): its `comment:<thread>` mark over each run of the text it was made on, one plan, one undo step.
- `moveText`: A text range dragged inside the editor and dropped at `to` (block offsets): its content placed at `to`, then the range deleted — or, with `copy` (Alt), only placed.
- `inlineMarkdown`: A closing marker typed after marked-up text (`**bold**`): the mark set, the markers removed; `value` typed at `start` of the text.
- `insertDivider`: A divider at the caret: its steps are the conversion, insertion or split it plans.
- `insertTableRow`: A table's row at `index`, one empty cell per column (`table.insert-row`).
- `deleteTableRows`: A table's rows, with their cells (`table.delete-row`).
- `insertTableColumn`: A table's column at `index`, an empty cell in every row (`table.insert-column`).
- `deleteTableColumn`: A table's column, by id or position, with its cells (`table.delete-column`).
- `moveTableColumn`: A table's column moved to position `to` (`table.move-column`).
- `moveTableRows`: A table's rows moved to position `to` among its rows (`table.move-row`).
- `fillTableCell`: The cell a row shows none of in `column`, created (`table.pad`).

## commentAnchors

Every thread's anchor in `doc`: the runs of text its `comment:<id>` mark
covers, in document order (adjacent runs of one block joined). A split
or a merge moves the marked text, never copies it, so a thread keeps its
runs through both; a thread whose marked text was deleted has none.

```ts
declare const commentAnchors: (doc: CommentAnchorSource) => Map<string, CommentRun[]>;
```

## CommentCardPayload

What a `card` snippet receives: one thread (`null`: the comment being
written, `comments.draft`), the view's comments controller, and whether
it is the active one.

```ts
export type CommentCardPayload = {
    thread: PlacedThread | null;
    comments: CommentsController;
    active: boolean;
};
```

- `thread`: The thread, placed beside its text (its comments, its anchor's text, `resolved`); `null` for the comment being written (its text `comments.draft.quote`: post it with `comments.submit(body)`, give it up with `comments.cancel()`).
- `comments`: The view's comments: the actions (reply, resolve, delete, `activate`), the user.
- `active`: It is the active thread (its text is selected, or its card was pressed).

## CommentChange

What one request changed (the room broadcasts it, `onComment` receives it).

```ts
export type CommentChange = {
    type: 'added' | 'replied' | 'resolved' | 'reopened' | 'deleted' | 'removed';
    thread: CommentThread;
    comment: ThreadComment | null;
    user: string;
    at: number;
    seq: number;
};
```

- `type`: `added` a thread, `replied` to one, `resolved` or `reopened` one, `deleted` a reply, or `removed` a whole thread (its anchor marks go with it).
- `thread`: The thread after the change; for `removed`, as it was.
- `comment`: The comment added (`added`, `replied`) or deleted (`deleted`), else `null`.
- `user`: Who made the change.
- `seq`: The change's sequence number in its document (the thread's new `rev`).

## CommentDocument

What a client reads and writes of a document: its blocks' content, and its marks (a removal).

```ts
export type CommentDocument = CommentAnchorSource & {
    formatRange(block: string, offset: number, length: number, marks: Record<string, unknown>): unknown;
    transact<T>(fn: () => T): T;
};
```

## CommentDraft

The thread being written: the text it will anchor, held by anchors so edits move it.

```ts
export type CommentDraft = {
    id: string;
    quote: string;
    block: string;
    spans: {
        start: DocAnchor;
        end: DocAnchor;
    }[];
};
```

- `id`: The new thread's id (its anchor will be `comment:<id>`).
- `quote`: The text it was started on (a preview: the server keeps it as the thread's quote).
- `block`: The block it starts in.
- `spans`: Each run of selected text, by anchors.

## CommentFeed

What a client's `subscribe` delivers: every thread (at a (re)connect), or one change.

```ts
export type CommentFeed = Exclude<CommentMessage, {
    type: 'subscribe';
}>;
```

## CommentMessage

A comment message, decoded.

```ts
export type CommentMessage = {
    type: 'subscribe';
    on: boolean;
} | ({
    type: 'snapshot';
} & CommentSnapshot) | {
    type: 'change';
    change: CommentChange;
};
```

## CommentNotice

What `onComment` receives with each change: whether the current user made it (here or elsewhere).

```ts
export type CommentNotice = {
    own: boolean;
};
```

## CommentRequest

A request to a document's comments, by the user a store verified.

```ts
export type CommentRequest = {
    op: 'add';
    thread: string;
    body: string;
    quote?: string;
    block?: string | null;
    comment?: string;
} | {
    op: 'reply';
    thread: string;
    body: string;
    comment?: string;
} | {
    op: 'resolve';
    thread: string;
} | {
    op: 'reopen';
    thread: string;
} | {
    op: 'delete';
    thread: string;
    comment?: string;
};
```

## CommentRequestError

A comments request the server refused: `status` is its HTTP status (`400`, `403`, `404`, `409`…).

```ts
export declare class CommentRequestError extends Error {
    readonly status: number;
    constructor(status: number, message: string);
}
```

## CommentResult

What a request did: `applied` (the change) or `noop` (the thread, unchanged).

```ts
export type CommentResult = {
    status: 'applied';
    change: CommentChange;
} | {
    status: 'noop';
    thread: CommentThread;
};
```

## CommentRun

One run of text a thread's anchor mark covers: `length` units from `offset` of `block`.

```ts
export type CommentRun = {
    block: string;
    offset: number;
    length: number;
};
```

## CommentsClient

A document's comment threads, as the comments plugin reads and writes them.

```ts
export type CommentsClient = {
    list(): Promise<CommentSnapshot>;
    send(request: CommentRequest): Promise<CommentResult>;
    subscribe?(listener: (message: CommentFeed) => void): () => void;
    attach?(document: CommentDocument): (() => void) | void;
};
```

- `list`: Every thread, with the document's last sequence number.
- `send`: One request (add, reply, resolve, reopen, delete) as the client's user.
- `subscribe`: Hear the changes anyone makes (and every thread again after a reconnect).
- `attach`: The document a view shows, given by the comments plugin when it mounts: a client whose store has no document of its own (the memory client) removes a removed thread's anchor marks from it.

## CommentsClientOptions

Options of `createCommentsClient`.

```ts
export type CommentsClientOptions = {
    server: string;
    room: string;
    socket?: string;
    params?: Record<string, string> | (() => Record<string, string>);
    fetch?: typeof fetch;
};
```

- `server`: The base URL of the comments route: requests go to `<server>/<room>`, the room percent-encoded as one path segment (`ws:`/`wss:` read as `http:`/`https:`).
- `room`: The document's id.
- `socket`: The websocket sync's server URL (the `server` of `<Edytor>` or `createWebsocketSync`), whose socket hears the room's changes.
- `params`: Query parameters sent with each request (a token): an object, or a function read at every request.
- `fetch`: The fetch to use (default the global one).

## commentsController

The comments plugin's controller of `edytor`; `undefined` when the plugin is not listed.

```ts
declare const commentsController: (edytor: Edytor) => CommentsController | undefined;
```

## CommentsController

The comments plugin's state and actions (`plugins/comments`): the
threads the client hears (merged by their newest change), each thread's
anchor read from the document (its `comment:<id>` mark), the thread being
written, the active one (the caret's, or the one clicked), and the
requests. The anchor mark is written by one dispatcher command
(`addComment`) once the server accepted the thread; a removed thread's
marks are its store's to remove (the room, or the memory client).

```ts
export declare class CommentsController {
    #private;
    private edytor;
    private options;
    active: string | null;
    draft: CommentDraft | null;
    showResolved: boolean;
    error: string | null;
    readonly client: CommentsClient;
    readonly user: CommentUser;
    constructor(edytor: Edytor, options: CommentsControllerOptions);
    readonly anchors: Map<string, CommentRun[]>;
    readonly threads: PlacedThread[];
    readonly open: PlacedThread[];
    readonly resolved: PlacedThread[];
    readonly shown: PlacedThread[];
    get canComment(): boolean;
    thread(id: string): PlacedThread | undefined;
    userOf: (id: string) => CommentUser;
    mayDelete: (thread: CommentThread, comment: {
        author: string;
    }) => boolean;
    connect: () => void;
    destroy: () => void;
    start: () => boolean;
    cancel: () => void;
    draftRuns: (draft?: CommentDraft | null) => CommentRun[];
    submit: (body: string) => Promise<boolean>;
    reply: (thread: string, body: string) => Promise<boolean>;
    resolve: (thread: string) => Promise<boolean>;
    reopen: (thread: string) => Promise<boolean>;
    remove: (thread: string, comment?: string) => Promise<boolean>;
    focus: (thread: string | null) => void;
    follow: () => void;
    readonly css: string;
}
```

- `active`: The thread the caret is in, or the one picked in the sidebar.
- `draft`: The thread being written, before it is posted.
- `showResolved`: The sidebar also shows the resolved threads.
- `error`: The last request that failed (its message), until the next one succeeds.
- `anchors`: Every thread's anchor runs, in document order (maintained per commit).
- `threads`: Every thread, in the order of its anchor in the document (threads with none last).
- `open`: The open threads whose text shows (what the sidebar places by default).
- `resolved`: The resolved threads.
- `shown`: The threads the sidebar shows: the open ones, the resolved ones on demand, the active one.
- `canComment`: The view may write: it may start, answer, resolve and delete comments.
- `thread`: One thread, by id.
- `userOf`: A user as the sidebar shows them: their name (else their id), avatar and color.
- `mayDelete`: Whether the current user may delete `comment` of `thread` (its author).
- `connect`: Start hearing the threads: the client's list, its changes, the document's commits.
- `destroy`: Stop hearing them (the editor is going away).
- `start`: Start a thread on the selected text (the toolbar's Comment button, Mod+Shift+M): each selected run, held by anchors while it is written; at a caret, its block's whole text.
- `cancel`: Drop the thread being written; the editor takes the focus back.
- `draftRuns`: The runs the draft's anchors cover now.
- `submit`: Post the thread being written: the server keeps it, then its anchor mark is written over the draft's text (one `addComment` command, one undo step).
- `remove`: Delete one comment; without `comment` (or its first), the whole thread.
- `focus`: Make `thread` the active one (a click on its card); its text scrolls into view.
- `follow`: The caret moved (`onSelectionChange`): the thread whose text holds it becomes the active one (an open one first), or none.
- `css`: The highlight of the threads' text: open threads, the active one stronger.

## CommentsLabels

The comments plugin (`createCommentsPlugin`): its toolbar button and its sidebar.

```ts
export type CommentsLabels = {
    comment: string;
    sidebar: string;
};
```

- `comment`: The toolbar button, and a thread's card's accessible name.
- `sidebar`: The sidebar's accessible name.

## CommentSnapshot

The document's threads at sequence number `seq`.

```ts
export type CommentSnapshot = {
    seq: number;
    threads: CommentThread[];
};
```

## CommentsPluginOptions

```ts
export type CommentsPluginOptions = {
    client: CommentsClient;
    user: string | CommentUser;
    users?: (id: string) => Omit<CommentUser, 'id'> | undefined;
    onComment?: (change: CommentChange, notice: CommentNotice) => void;
    card?: Snippet<[
        CommentCardPayload
    ]>;
    header?: Snippet<[
        CommentsController
    ]>;
};
```

- `client`: Where the threads live: `createCommentsClient({ server, room })` for the room's (`routeDocumentComments`), `createMemoryCommentsClient` for threads in memory, or any object with `list` and `send`.
- `user`: The current user: their id (the identity your server verifies; it decides who may delete a comment) or `{ id, name, avatar, color }`.
- `users`: How another user shows: their name, avatar and color, by id (default: the id).
- `onComment`: Notifications: each change this view hears (a thread added, a reply, a resolve or reopen, a delete), once; `own` when the current user made it (in this view or another).
- `card`: A card's markup: a thread's (its comments, a reply field, Resolve) or the comment being written (`thread: null`).
- `header`: The top of the sidebar's margin (Notion's "Show resolved"): the cards stack under it.

## CommentThread

A comment thread: its first comment starts it, the others reply.

```ts
export type CommentThread = {
    id: string;
    block: string | null;
    quote: string;
    createdBy: string;
    createdAt: number;
    resolved: {
        by: string;
        at: number;
    } | null;
    comments: ThreadComment[];
    rev: number;
};
```

- `id`: The thread's id, which its anchor mark names (`comment:<id>`).
- `block`: The block it was started in (`null`: unknown).
- `quote`: The text it was started on, as it read then (at most `MAX_QUOTE_LENGTH`).
- `resolved`: Who resolved it and when; `null` while it is open.
- `comments`: Oldest first; never empty.
- `rev`: The sequence number of the last change to it (a later change has a greater one).

## CommentUser

Who a comment's author is, as the sidebar shows them.

```ts
export type CommentUser = {
    id: string;
    name?: string;
    avatar?: string;
    color?: string;
};
```

- `name`: Shown in place of the id.
- `avatar`: An avatar image URL; without one, the name's initial on `color`.

## ContentItem

One inline content element in a block spec / projection.

```ts
export type ContentItem = {
    kind: 'text';
    text: string;
    marks?: Record<string, unknown>;
} | {
    kind: 'inline';
    id: string;
    type: string;
    data?: Record<string, unknown>;
};
```

## ContentRun

One visible run of a block — the maintained form of `ContentItem`.
`marks`/`data` are interned: equal payloads share one frozen instance,
so `===` compares formatting without deep walks. A run carries no
authorship: runs never split at author boundaries, and durable
attribution is the compact per-block record (`blockattr`).

```ts
export type ContentRun = {
    kind: 'text';
    text: string;
    marks?: Record<string, unknown>;
} | {
    kind: 'inline';
    id: string;
    type: string;
    data?: Record<string, unknown>;
};
```

## convertToKind

Convert `block` to a row's kind as one command. With `caret` (by default
when the conversion replaces the content) the caret lands at the start of
the converted block, or of its first child when the conversion creates one.
A replacing kind (a divider, a code block) converts in place only a block
that holds nothing (`holdsNothing`); after text, atoms or children it is
inserted after the block instead, which stays intact. The kind lands where
it fits (`placing`): a list's item turned into another kind leaves the
list — out of every list it sits in directly (a list nested right in a
list) — where Shift+Tab lifts it, as a bullet turned into
a heading stops being a bullet in Notion; a block's own kind (only its
data changes) never moves it. Each is one plan: one refusal or veto keeps
everything, one undo step. Answers whether it applied.

```ts
declare const convertToKind: (edytor: Edytor, block: Block | null | undefined, row: Pick<KindRow, "value" | "replaces">, caret?: boolean) => boolean;
```

## createArrowMovePlugin

Block moves from the keyboard (Mod+Up/Down, Mod+Shift+Up/Down): a default plugin.

```ts
declare const createArrowMovePlugin: () => Plugin;
```

## createAudioPlugin

Notion's audio block: an audio file (an `http(s)` link, or an upload
through `upload`) in the browser's player, with an editable caption.
Void: its only text is the caption.

```ts
declare const createAudioPlugin: () => Plugin;
```

## createBlockHandlesPlugin

Block handles in the overlay, created lazily: a handle mounts
for a block near the viewport (the overlay's `near` top-level blocks and
the blocks inside them, not hidden; every block without layout),
under the pointer, selected, focused or dragged; drop targets exist only
during our own drag. Hover is one delegated listener on the editor and
one on the overlay (a block's handle keeps it hovered).

```ts
declare const createBlockHandlesPlugin: (options?: BlockHandlesOptions) => Plugin;
```

## createBlockMenuPlugin

Notion's block menu: a handle click
opens it beside the handle — search, Turn into, Color, Duplicate, Move,
Delete, and Copy link when `linkTo` is given. Also binds Mod+D (duplicate
the selected blocks, or the caret's block), and adds the colours to the
slash menu by name (`/red`: "Red text", "Red background").

```ts
declare const createBlockMenuPlugin: (options?: BlockMenuOptions) => Plugin;
```

## createBookmarkPlugin

Notion's web bookmark: a card linking to a page, with its title,
description and images when your `unfurl` answers them, and an
editable caption. Void: its only text is the caption. Pasting an
`http(s)` link on an empty line offers "Bookmark".

```ts
declare const createBookmarkPlugin: (options?: BookmarkPluginOptions) => Plugin;
```

## createCalloutIconsPlugin

Callout icons (Notion's): a callout's icon opens the view's picker, and
its block menu has a Change icon row (rich text's, while a picker is
listed), the keyboard's way to it. The first plugin listed claims the
view's picker.

```ts
declare const createCalloutIconsPlugin: ({ picker: menu, icons }?: CalloutIconsOptions) => Plugin;
```

## createCodePlugin

Code blocks, in the languages `options` lists (`code.language`): the
header names the block's language and picks another; each line is
highlighted with its block's language, its grammar loaded the first
time a line of it renders.

```ts
declare const createCodePlugin: (options?: CodePluginOptions) => Plugin;
```

## createColumnsPlugin

Notion's columns (docs/columns-plan.md): a `columns` layout shows its
`column`s side by side, each holding any blocks; a column grows by its
weight (`data.width`). The roles are the document's (`layoutKinds`,
`layout.*` in the delete contract): a layout showing one column shows
as its blocks, an empty column does not show. `columns.css` lays them
out and stacks them below a 480px wide layout.

```ts
declare const createColumnsPlugin: (options?: ColumnsPluginOptions) => Plugin;
```

## createCommentsClient

The default `CommentsClient`: the requests `routeDocumentComments`
answers (`GET <url>` lists, `POST <url>` sends a request as JSON), and
the room's changes heard on the socket of the document's websocket sync
(`subscribe`: while a listener is subscribed, that socket asks the room
for them). Throws a `TypeError` at once for a room id no request can
carry.

```ts
declare const createCommentsClient: (options: CommentsClientOptions) => CommentsClient;
```

## createCommentsPlugin

Comments (Notion's): select text and press Comment in the toolbar (or
Mod+Shift+M) to start a thread on it. The thread's text carries its
anchor, a `comment:<id>` mark the document keeps through splits and
merges and that copy and cut leave behind; its comments live in the
client's store (the room's `threads` and `comments` tables). Open threads
are highlighted, and a sidebar beside the text (under it, on a narrow
screen) places a `card` for each one.

```ts
declare const createCommentsPlugin: (options: CommentsPluginOptions) => Plugin;
```

## createDocument

See `DocumentBinding.createDocument`.

```ts
declare const createDocument: (options?: CreateDocumentOptions) => EdytorDocument;
```

## CreateDocumentOptions

```ts
export type CreateDocumentOptions = DocumentOptions & {
    value?: JSONDoc;
};
```

## createEmbedPlugin

Notion's embed block: an "Embed a link" panel until it has a link, then
the provider's player in a sandboxed frame, with an editable caption.
Only an allowlisted provider (`providers`) plays: the frame's `src` is
the player URL the provider derives from the stored link, never the
stored value, and it has a `sandbox` and no `srcdoc`. Void: its only
text is the caption. Pasting a link a provider plays on an empty line
offers "Embed".

```ts
declare const createEmbedPlugin: (options?: EmbedPluginOptions) => Plugin;
```

## createEquationPlugin

Equations (Notion's): a block equation (`equation`, void, its TeX in
`data.expression`) and an inline equation atom (`inlineEquation`), both
drawn by KaTeX, loaded the first time an equation shows in a browser:
from jsDelivr by default, or as `katex` says (a URL, your loader). A
click on one, or Enter on a selected one, opens its TeX source in an
editor under it; the equation is the live preview. "Block equation"
and "Inline equation" are slash commands, `$$…$$` typed in text is an
inline equation, and Mod+Shift+E turns the selected text into one.
Copied, an equation is MathML carrying its TeX; pasted HTML with KaTeX
or MathML math becomes equations again.

```ts
declare const createEquationPlugin: (options?: EquationPluginOptions) => Plugin;
```

## createFilePlugin

Notion's file block: an attachment, uploaded through `upload` (or
linked), shown as its name and size, linking to the file, with an
editable caption. Void: its only text is the caption.

```ts
declare const createFilePlugin: (options?: FilePluginOptions) => Plugin;
```

## createFindPlugin

Find and replace (Notion's find in page), opt-in: Mod+F opens a bar over
the editor's top right. It searches the document's text in reading order,
a closed toggle's body included (the toggle opens when a match there
becomes the current one), highlights the matches in the overlay, and
replaces one or all as one command (`replaceMatches`) and one undo step.

```ts
declare const createFindPlugin: (options?: FindPluginOptions) => Plugin;
```

## createHistoryClient

The default `HistoryClient`: the requests `routeDocumentHistory`
answers. `GET <url>` lists, `GET <url>?key=` reads (`404`: `null`),
`POST <url>?restore=` restores (`404`: `refused`), `POST <url>?undo`
undoes; any other refusal throws a `HistoryRequestError`. Throws
a `TypeError` at once for a room id no request can carry.

```ts
declare const createHistoryClient: (options: HistoryClientOptions) => HistoryClient;
```

## createImagePlugin

Notion's image block: an "Add an image" panel until it has a source (a
pasted link, or an upload when `upload` is given), then the image with
an editable caption, its alt text, width and alignment set from the
chrome the plugin shows over it. Void: its only text is the caption.

```ts
declare const createImagePlugin: () => Plugin;
```

## createImageToolbarPlugin

The image toolbar: your `toolbar` over the image the chrome is on. The first listed claims the view.

```ts
declare const createImageToolbarPlugin: ({ toolbar }: ImageToolbarOptions) => Plugin;
```

## createIndexeddbSync

```ts
declare const createIndexeddbSync: (name: string) => EdytorSync;
```

## createMarkdownShortcutsPlugin

Markdown prefixes come from the kind catalogue: a row whose shortcut the typed character completes.

```ts
declare const createMarkdownShortcutsPlugin: () => Plugin;
```

## createMarqueePlugin

Block selection with a rectangle (Notion's rubber band), opt-in: a press
in the editor's empty area — the margins beside the blocks (the handle
gutter, not a handle), the space below the last block, an optional
`container` around the editor — then a drag draws a rectangle and selects,
live, the blocks it meets. Shift or Mod adds to the selection there was;
Escape gives it back. On release the block selection stays and the editor
has the keys. A click without a drag keeps what a press there did (below
the last block, the trailing paragraph).

```ts
declare const createMarqueePlugin: (options?: MarqueePluginOptions) => Plugin;
```

## createMediaEmptyPlugin

The empty image and media blocks' chrome: your `empty` snippet for each
of them (without the plugin, an empty block shows nothing). The first
listed claims the view.

```ts
declare const createMediaEmptyPlugin: ({ empty }: MediaEmptyOptions) => Plugin;
```

## createMemoryCommentsClient

Comment threads kept in memory, under the rules the room applies
(`decideComment`): for tests, demos and a document without a room. Its
siblings (`as(user)`) share the threads; each change reaches every
subscriber, asynchronously as over a network. A removed thread's anchor
marks leave the last document a plugin attached.

```ts
declare const createMemoryCommentsClient: (options: MemoryCommentsClientOptions) => MemoryCommentsClient;
```

## createMentionPlugin

People mentions, as in Notion: `@` opens a menu of the people your app's
`items(query)` answers; picking one replaces `@query` with a `mention`
atom (`{ id, label }`) and puts the caret after it, in one undo step.

```ts
declare const createMentionPlugin: (options: MentionPluginOptions) => Plugin;
```

## createPageLinkPlugin

Links to your app's pages, as Notion's `[[`: it opens a menu of the pages
your app's `search(query)` answers; picking one replaces `[[query` with
a `pageLink` atom (`{ id, title, href, icon }`), an `<a>` to the page,
and puts the caret after it, in one undo step.

```ts
declare const createPageLinkPlugin: (options: PageLinkPluginOptions) => Plugin;
```

## createPagePlugin

Notion's subpage block: a `page` block links to another document by
id (`data.pageId`), and shows its title (`data.title`, the cache your
`title` lookup refreshes). Each page is a document of its own (a room
per page): moving a subpage is moving this block, and the page itself,
its content and history, never moves. A click opens it (`open`), and so
does Enter over it selected alone; a modified or middle click opens it
in a new tab (`{ newTab: true }`, the browser's own on an `href`). With `create`, the "Page"
command (slash menu, `+`) creates a page, inserts a block linking to it
where the caret is (an empty line becomes it) and opens it.

```ts
createPagePlugin({
  open: (pageId) => goto(`/p/$\{pageId\}`),
  href: (pageId) => `/p/$\{pageId\}`,
  title: (pageId) => pages.get(pageId)?.title,
  create: () => crypto.randomUUID()
});
```

```ts
declare const createPagePlugin: (options?: PagePluginOptions) => Plugin;
```

## createRichTextPlugin

The rich text kinds and marks, their words and slash keywords the
view's (`labels`, English by default).

```ts
declare const createRichTextPlugin: (options?: RichTextPluginOptions) => Plugin;
```

## createSlashMenuPlugin

The slash menu, drawn by your `menu` snippet.

```ts
declare const createSlashMenuPlugin: (options?: SlashMenuOptions) => Plugin;
```

## createSuggestionBarPlugin

The suggestion bar (Notion AI's): your `bar` under each block
suggestion (Accept, Discard, and Try again when the app passed
`onRetry`); its keys are the suggestions plugin's.

```ts
declare const createSuggestionBarPlugin: ({ bar }: SuggestionBarOptions) => Plugin;
```

## createSuggestionsPlugin

The keys of `edytor.suggestions` (Notion AI's), a default plugin: for the
latest one, Mod+Enter accepts and Escape discards; Tab accepts an `end`
suggestion at the caret. Its bar is `createSuggestionBarPlugin`'s.

```ts
declare const createSuggestionsPlugin: () => Plugin;
```

## createTablePlugin

Notion's simple table (`table.*` in the delete contract): a `table` of
`tableRow`s of `tableCell`s, each cell a text island whose lines are line
breaks (Enter is a line break in a cell). The columns are the table's
`data.columns` (ids and widths), each cell names its column, and the
header flags are `data.headerRow` and `data.headerColumn`. The table is a
CSS grid (`table.css`): a row shows its cells in its table's column
order, a padded cell (a peer's row inserted while a column was) as an
empty placeholder whose first press creates it. Tab and Shift+Tab walk
the cells (Tab in the last one adds a row), ArrowUp and ArrowDown go to
the cell above and below. On hover, a `+` under and beside the table adds
a row or a column, the grips of a row and a column open their menus
(insert, move, delete), and a band on each column's edge resizes it. The
block menu of a table has its Header row and Header column switches.
Not a default plugin; its roles are in `defaultSemantics`.

```ts
declare const createTablePlugin: (options?: TablePluginOptions) => Plugin;
```

## createTocPlugin

Notion's table of contents: a `toc` block lists the document's
headings, live, indented by level; a click scrolls to one (opening the
closed toggles it sits in). Void: it holds no text, and its list is
never stored, only drawn. Its preset in the slash menu turns an empty
line into one, or inserts it after the line; its words are the view's
`toc` labels (English by default).

```ts
declare const createTocPlugin: (options?: TocPluginOptions) => Plugin;
```

## createToolbarPlugin

The selection toolbar, drawn by your `toolbar` snippet, and the link card by your `card`.

```ts
declare const createToolbarPlugin: (options?: ToolbarOptions) => Plugin;
```

## createUploadsPlugin

File uploads (Notion's): the view's one uploader, for every kind whose
record takes files (`upload`: the image, file, video and audio kinds).
Without it, only links are embedded. The first listed claims the view.

```ts
declare const createUploadsPlugin: ({ upload }: UploadsOptions) => Plugin;
```

## createVideoPlugin

Notion's video block: a video file (an `http(s)` link, or an upload
through `upload`) in the browser's player, with an editable caption.
A YouTube or Vimeo link is an embed (`createEmbedPlugin()`). Void: its only
text is the caption.

```ts
declare const createVideoPlugin: () => Plugin;
```

## createWebsocketSync

```ts
declare const createWebsocketSync: (options: WebsocketSyncOptions) => WebsocketSync;
```

## DataPatch

One data edit at `path` (object keys and array indexes from the data's
root): `splice` takes `Array.prototype.splice`'s arguments and `order`
the array's current indexes in their new order; else it sets `value`, or
deletes the key or removes the item when there is none.

```ts
export type DataPatch = {
    readonly path: readonly string[];
    readonly value?: unknown;
    readonly splice?: readonly [
        start: number,
        deleteCount?: number,
        ...items: unknown[]
    ];
    readonly order?: readonly number[];
};
```

## DataTarget

What a data patch edits: a block, one of its inline atoms, or the document (`null`).

```ts
export type DataTarget = BlockId | null | {
    block: BlockId;
    atom: string;
};
```

## DEFAULT_PRESENCE_THROTTLE

The least ms between two presence writes of a view by default: a
caret held down or a drag publishes about 20 writes a second instead of
one per selection change, which every peer and the room relay.

```ts
declare const DEFAULT_PRESENCE_THROTTLE = 50;
```

## defaultSemantics

The rich-text, code, image, media, columns, page, table of contents,
table and equation plugins' block roles together (and the rich-text and
comment mark edges) — what the room and a headless
`createDocument`/`loadDocument` adopt by default (`semantics: {}` checks
none). The media, columns, page, table of contents, table and equation
plugins are not default plugins of `<Edytor>`, but their roles are here so their
blocks read the same on every replica.

```ts
declare const defaultSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
} & {
    marks: Record<string, {
        edge: MarkEdge;
    }>;
};
```

## deleteTableColumn

Delete a column (`table.delete-column`, by id or position among the
grid's): it leaves `data.columns` (every entry naming it) and every
shown cell of it is deleted, in one plan; a table left with no column
goes with it. A column the table shows but no longer lists
(`table.cell`) loses its cells. `ids`: the table.

```ts
declare const deleteTableColumn: (doc: Omit<DocumentOperations, "transact">, table: BlockId, column: string | number) => Prepared;
```

## deleteTableRows

Delete `rows` with their cells (`table.delete-row`): one plan; a table
left with no row goes with them. `ids`: the rows.

```ts
declare const deleteTableRows: (doc: Omit<DocumentOperations, "transact">, rows: readonly BlockId[]) => Prepared;
```

## Destination

Where a block goes: `parent` = logical block id or `null` for root.

```ts
export type Destination = {
    parent: BlockId | null;
    index: number;
};
```

## Dispatcher

```ts
export declare class Dispatcher {
    #private;
    private edytor;
    get last(): CommandResult | null;
    private active;
    private running;
    private kind;
    private applied;
    private depth;
    private replacing;
    private queue;
    private current;
    private leading;
    private patched;
    private untracked;
    outside: <T>(body: () => T) => T;
    permits: () => boolean;
    run: <T>(kind: string, body: () => T) => T | undefined;
    each: <T, R>(kind: string, parts: readonly T[], part: (item: T) => R) => (R | undefined)[];
    dispatch: <P, R>(operation: string, payload: P, context: {
        block: unknown;
        text?: Text;
    }, body: (payload: P, plan?: Prepared) => R, prepare?: (payload: P) => Prepared) => R | undefined;
    plan: <P>(operation: string, payload: P, prepare: (payload: P) => Prepared, block?: Block, text?: Text) => OpResult;
    lead: <T>(lead: Prepared, body: () => T) => {
        out: T | undefined;
        taken: boolean;
    };
    caret: {
        (text: Text | null | undefined, offset: number, ops?: () => OpResult): OpResult | undefined;
        (caret: Caret | DocPosition | null | undefined, ops?: () => OpResult): OpResult | undefined;
    };
    private policy;
    private decide;
    private settle;
    private replace;
    private steps;
}
```

- `last`: The last operation's result (read-only: the dispatcher records it).
- `outside`: Run `body`'s commands outside the undo history (one transaction of their own, under an origin no history tracks): admitted, shown to hooks and recorded in `last` as any command, but they cut no step and enter none.
- `permits`: Whether this view accepts writes now: not readonly, its document writable, and decided when it requires hydration.
- `run`: A user command: admission, the undo policy around it, and one prevention scope.
- `each`: A user command over several parts (Turn into over several blocks, Tab over several runs of siblings): each part is its own prevention scope, so a part the document refuses or an extension vetoes is skipped and the others still run, as one undo step.
- `dispatch`: Dispatch one operation.
- `plan`: Dispatch `operation` as the plan `prepare` makes of `payload` (a hook's replacement payload is prepared again), about `block` (the root by default): hooks see it and each planned step before any write; one transaction; the blocks it touched normalized and the blocks it moved shown (closed toggles open); the caret it decides (`plan.at`: a range deletion, a placed flow) selected.
- `lead`: Run `body` with `lead` (a plan prepared now) composed into the first operation it dispatches: one plan, so a veto of any step, or a refusal, refuses both (a slash or markdown trigger removal with its conversion).
- `caret`: A command's result caret: selected once and recorded on the result; the projector displays it after the flush.

## DocAnchor

A selection endpoint: `b` is the home block of the backing text the
position lives in (NOT necessarily the block that displays it — merges and
splits reroute display while the anchor stays on the same items), `a` an
engine relative position whose `a` carries the side (`< 0` left, `>= 0`
right). The containing stream and the side are two facts in two fields.
JSON-serializable — the presence and history wire shape.

```ts
export type DocAnchor = {
    b: BlockId;
    a: Anchor;
};
```

## DocChange

One committed transaction's semantic diff — the payload `EdytorDoc.onChange`
subscribers receive. Every collection names the affected ids; `order`
carries the NEW child-id list per changed parent so a mirror can apply the
diff without re-reading the doc. The report is shared: every subscriber
of the document (each view over it, its cells included) receives the same
object, so it is read-only.

```ts
export type DocChange = {
    origin: unknown;
    local: boolean;
    version: number;
    added: ReadonlyMap<BlockId, ProjectedBlock>;
    removed: ReadonlySet<BlockId>;
    moved: ReadonlySet<BlockId>;
    meta: ReadonlyMap<BlockId, {
        type: string;
        data?: Record<string, unknown>;
    }>;
    content: ReadonlyMap<BlockId, readonly ContentRun[]>;
    order: ReadonlyMap<BlockId | null, readonly BlockId[]>;
    data?: Record<string, unknown>;
};
```

- `origin`: Transaction origin (local origin object, remote marker, …).
- `local`: `transaction.local` — false for remote-applied updates.
- `version`: Monotonic event counter per facade.
- `added`: Newly visible blocks → full projected subtree (incl.
- `removed`: Ids no longer visible (deleted, merged-away, or hidden with subtree).
- `moved`: Ids whose display parent or sibling index changed.
- `meta`: Ids whose `type`/`data` payload changed → new values.
- `content`: Ids whose visible content changed → the new maintained runs.
- `order`: Parents (`null` = root) whose visible child list changed → new order (frozen — shared with the retained snapshot baseline).
- `data`: The document's own data (`docData()`), when this commit changed it.

## DocPosition

A position in one document version: a block and a display offset.

```ts
export type DocPosition = {
    block: BlockId;
    offset: number;
};
```

## DocumentActor

Local actor identity — durable across replicas, independent of presence profiles.

```ts
export type DocumentActor = {
    id: string;
    name?: string;
    color?: string;
};
```

- `id`: Stable author identifier (used by awareness and, later, native attribution).

## DocumentAttribution

The document-level attribution surface. `actors`/`actorOf`/`block`
read replicated doc state directly — they resolve remote-applied
records identically and stay correct as old replicas append records.

```ts
export type DocumentAttribution = {
    readonly actors: ReadonlyMap<string, ActorProfile>;
    actorOf(client: number): string | undefined;
    setProfile(profile: ActorProfile): void;
    block(blockId: string): BlockAttribution | undefined;
};
```

- `actors`: actorId → profile — the replicated actor dictionary (`u/` records).
- `actorOf`: Resolve an item's inserting replica (clientID) to an actor id (`c/` records).
- `setProfile`: Re-publish the local actor's profile record (`u/<actorId>` — LWW).
- `block`: Compact per-block attribution for `blockId` (`{createdBy, contributors, lastChangedBy}` — durable actor ids, never replica clientIDs), or `undefined` for system/foreign blocks that carry no record.

## DocumentDestroyedError

Raised when an operation runs against a torn-down document — every
attach reference has been released and the facade/history/awareness
teardown already ran. Distinct from `DocumentNotReadyError`:
destroyed is terminal, pending may still resolve.

```ts
export declare class DocumentDestroyedError extends Error {
    constructor(service?: string);
}
```

## DocumentNotReadyError

Raised when a readiness-gated service is used on a pending document.

```ts
export declare class DocumentNotReadyError extends Error {
    constructor(service: string);
}
```

## DocumentOperations

The document's public operations: the writes, each also prepared
(`prepare.<op>(…)`, pure, then `apply(plan)`; `compose` joins plans),
the reads, `transact`, `onChange` and `version`. See the document API
reference.

```ts
export interface DocumentOperations extends DocumentWrites, DocumentReads {
    readonly prepare: {
        readonly [K in keyof DocumentWrites]: (...args: Parameters<DocumentWrites[K]>) => Prepared;
    };
    apply(plan: Prepared): OpResult;
    compose(...parts: Prepared[]): Prepared;
    transact<R>(fn: () => R, origin?: unknown): R;
    onChange(listener: (change: DocChange) => void): () => void;
    readonly version: number;
}
```

- `prepare`: Every write, prepared: a plan of named steps and its effect, or `refused`.
- `apply`: Write a prepared plan exactly (a refusal passes through).
- `compose`: One plan of `parts`, applied in order.
- `transact`: Group writes in one transaction and one change event (no rollback on a throw).
- `onChange`: Hear each transaction's change; answers the unsubscribe.
- `version`: Changes on every write that changed the tree.

## DocumentOptions

```ts
export type DocumentOptions = {
    actor?: DocumentActor;
    semantics?: DocumentSemanticsConfig;
    awareness?: Awareness;
    history?: {
        captureTimeout?: number;
        limit?: number;
    };
    requireHydration?: boolean;
};
```

- `actor`: Local actor — anonymous opaque id when absent.
- `semantics`: Up-front semantic configuration (views may also seed via `adoptSemantics`).
- `awareness`: Borrow an existing awareness instance instead of creating one — `destroy()` will NOT destroy it (same owned/borrowed rule as the doc).
- `history`: History tuning — applied when the document's undo manager attaches.
- `requireHydration`: Decide an EMPTY document only from a provider that synced: the room answered, or a local store hydrated.

## DocumentReadiness

How the document's content state was decided — the distinction the
readiness contract requires:
- `pending` — undecided (created without a value or attached before
  `sync()`; a provider may still hydrate — nothing bootstrapped,
  nothing broadcast). Reads/attaches/`sync()` legal, `history`
  refuses;
- `local` — seeded locally (`createDocument({value})`, or `sync()` on a
  still-fresh doc — seed-if-empty bootstrap included). Full surface
  legal;
- `hydrated` — the doc was already initialized when `sync()` ran:
  provider-applied remote state or a `loadDocument` restore (schema
  asserted, content untouched). Full surface legal.

`sync()` is the only transition out of `pending`, and it is the
document-side admission re-check: a doc that arrived in a problem
state (unversioned/unsupported/foreign — e.g. via raw
`applyUpdate` bypass writes) refuses there with a typed error, stays
`pending`, and preserves the state — never silently becoming `local`.

```ts
export type DocumentReadiness = 'pending' | 'local' | 'hydrated';
```

## DocumentReads

The document's reads (transaction-aware where the reference says so).

```ts
export interface DocumentReads {
    toJSON(): JSONDoc;
    docData(): JsonObj;
    dataItemIds(target: DataTarget, path: readonly string[]): string[];
    project(): ProjectedDoc;
    blockJSON(id: BlockId): JSONBlock;
    childrenIds(parent: BlockId | null): BlockId[];
    parentOf(id: BlockId): BlockId | null;
    ancestorsOf(id: BlockId): BlockId[];
    positionOf(id: BlockId): Destination | null;
    pathOf(id: BlockId): number[] | null;
    blockTypeOf(id: BlockId): string | undefined;
    blockDataOf(id: BlockId): Record<string, unknown> | undefined;
    blockText(id: BlockId): string | null;
    contentItems(id: BlockId): ContentItem[];
    displayLength(id: BlockId): number;
    runs(id: BlockId): readonly ContentRun[];
    hasBlock(id: BlockId): boolean;
    isVisibleBlock(id: BlockId): boolean;
    listBlockIds(): BlockId[];
    blockAttribution(id: BlockId): BlockAttribution | undefined;
    isVoid(id: BlockId): boolean;
    isIsland(id: BlockId): boolean;
    isLines(id: BlockId): boolean;
    islandOf(id: BlockId): BlockId | null;
    insideIsland(id: BlockId): boolean;
    isLayout(id: BlockId): boolean;
    isLayoutItem(id: BlockId): boolean;
    besideAt(id: BlockId, kind?: string): BlockId;
    isTable(id: BlockId): boolean;
    isTableRow(id: BlockId): boolean;
    isTableCell(id: BlockId): boolean;
    tableOf(id: BlockId): BlockId | null;
    tableColumns(id: BlockId): {
        id: string;
        width?: number;
    }[] | null;
    tableGrid(id: BlockId): {
        columns: string[];
        rows: {
            id: BlockId;
            cells: (BlockId | null)[];
        }[];
    } | null;
    order(): readonly BlockId[];
    compare(a: BlockId, b: BlockId): number;
    next(id: BlockId, policy?: OrderPolicy): BlockId | null;
    previous(id: BlockId, policy?: OrderPolicy): BlockId | null;
    canPlace(ids: readonly BlockId[], parent?: BlockId | null): boolean;
    canMerge(from: BlockId, into: BlockId): boolean;
    fits(parent: BlockId | null, kind: string): boolean;
    nestParent(ids: readonly BlockId[], parent: BlockId): BlockId;
    landingOf(id: BlockId, kind: string, after?: boolean): {
        parent: BlockId | null;
        levels: BlockId[];
    };
    anchorAt(id: BlockId, offset: number, affinity?: AnchorAffinity): DocAnchor | null;
    resolveAnchor(anchor: DocAnchor): {
        blockId: BlockId;
        offset: number;
    } | null;
}
```

## DocumentSemanticsConfig

Document-level semantic configuration — the structural subset of what
plugin `BlockDefinition`s carry (roles, `rendersContent`, `defaultChild`)
plus the default block type. Snippets/DOM hooks are deliberately NOT
part of this: they are view-side rendering policy.

```ts
export type DocumentSemanticsConfig = {
    roles?: Record<string, BlockRole>;
    rendersContent?: Record<string, boolean>;
    defaultChild?: Record<string, string>;
    defaultType?: string;
    marks?: Record<string, {
        edge?: MarkEdge;
    }>;
};
```

- `roles`: Structural role per block type (`{void?, island?, lines?, layout?}` — absent flags mean false).
- `rendersContent`: Whether a kind renders its own content slot (undeclared kinds do).
- `defaultChild`: Default child type per parent type (undeclared parents take `defaultType`).
- `defaultType`: Default block type — the root's default child and the bootstrap block.
- `marks`: Per mark, its edge (its record's `edge`) — where a concurrent insert at each end of a mark lands.

## documentSnapshot

A room's document as JSON (one authorized HTTP request), for `<Edytor snapshot>`.

```ts
declare const documentSnapshot: typeof snapshotOf;
```

## DocumentWrites

The document's writes: each one transaction, answering what it did (`OpResult`).

```ts
export interface DocumentWrites {
    insertBlocks(dest: Destination, specs: readonly (JSONBlock | BlockSpec)[]): OpResult;
    moveBlocks(ids: readonly BlockId[], dest: Destination): OpResult;
    nestBlock(id: BlockId, parent: BlockId): OpResult;
    unNestBlock(id: BlockId): OpResult;
    unNestBlocks(ids: readonly BlockId[]): OpResult;
    liftOut(id: BlockId, kind: string, options?: {
        keep?: boolean;
        after?: readonly BlockSpec[];
    }): OpResult;
    wrapInLayout(ids: readonly BlockId[], kind?: string, columns?: number): OpResult;
    placeBeside(ids: readonly BlockId[], target: BlockId, side: 'left' | 'right', kind?: string): OpResult;
    splitBlock(id: BlockId, offset: number, newId: BlockId, tail?: SplitTail): OpResult;
    mergeBlocks(fromId: BlockId, intoId: BlockId): OpResult;
    mergeBackward(id: BlockId): OpResult;
    mergeForward(id: BlockId): OpResult;
    deleteBlock(id: BlockId, options?: {
        keepChildren?: boolean;
    }): OpResult;
    deleteBlocks(ids: readonly BlockId[]): OpResult;
    setBlock(id: BlockId, value: {
        type?: string;
        data?: Record<string, unknown>;
        content?: (ContentItem | JSONText | JSONInlineBlock)[];
        children?: (BlockSpec | JSONBlock)[];
    }): OpResult;
    setBlockType(id: BlockId, type: string): OpResult;
    patchData(target: DataTarget, patches: readonly DataPatch[]): OpResult;
    duplicateBlock(id: BlockId, freshId?: (oldId: string, kind: 'block' | 'inline') => string): OpResult;
    insertText(id: BlockId, offset: number, text: string, marks?: Record<string, unknown>): OpResult;
    deleteText(id: BlockId, offset: number, length: number): OpResult;
    formatRange(id: BlockId, offset: number, length: number, marks: Record<string, unknown>): OpResult;
    clearMarks(id: BlockId, offset: number, length: number): OpResult;
    insertInline(id: BlockId, offset: number, atom: NewInline): OpResult;
    removeInline(id: BlockId, inlineId: string): OpResult;
    deleteRange(from: DocPosition, to: DocPosition, view?: RangeView): OpResult;
    replaceRange(from: DocPosition, to: DocPosition, view?: RangeView): OpResult;
    insertFlow(target: FlowTarget, flow: Flow, view?: FlowView): OpResult;
}
```

- `duplicateBlock`: `freshId` names each copy (fresh ids by default).
- `insertInline`: An atom without an id gets a fresh one.

## EditorCommand

```ts
export type EditorCommand = {
    id: string;
    label: string;
    icon?: string;
    keywords?: string[];
    group?: string;
    hint?: string;
    isEnabled?: (edytor: Edytor) => boolean;
    run: (edytor: Edytor) => unknown | Promise<unknown>;
    turnsInto?: (blocks: Block[]) => boolean;
    searchOnly?: boolean;
};
```

- `hint`: A shortcut shown beside the label (a markdown prefix such as `##`).
- `turnsInto`: Whether the block menu's Turn into offers the command over `blocks` (the block selection as clicked); the row runs it, over that selection (the columns plugin's `columns.<n>` over `n` sibling blocks wraps them).
- `searchOnly`: Listed by the slash and `+` menus only once a query names it (the block colours: `/red`), never in the bare list.

## EditorLabels

What the view itself says: its live announcements and a suggestion's default name.

```ts
export type EditorLabels = {
    suggestion: string;
    block: (kind: string) => string;
    blocks: (count: number) => string;
    movedUp: (what: string) => string;
    movedDown: (what: string) => string;
    indented: (what: string) => string;
    outdented: (what: string) => string;
    moved: (what: string) => string;
    deleted: (what: string) => string;
};
```

- `suggestion`: A block suggestion's accessible name when it has no `label`.
- `block`: One block in an announcement, by its kind's label ("Heading 1 block").
- `blocks`: Several blocks in an announcement ("3 blocks").
- `moved`: A move to a place (a drop).

## Edytor

```ts
declare const Edytor: import("svelte").Component<EdytorProps, {}, "edytor">;
type Edytor = ReturnType<typeof Edytor>;
```

## EdytorDocData

The document's properties (`edytor.data`). Augment it to type yours:
`declare module 'edytor' { interface EdytorDocData { title?: string } }`.

```ts
export interface EdytorDocData extends Record<string, any> {
}
```

## EdytorDocument

The document's operations and reads are its own members
(`document.insertBlocks(…)`, `document.toJSON()`): see `DocumentOperations`.
`transact` is the document's (its local origin by default).

```ts
export interface EdytorDocument extends Omit<DocumentOperations, 'transact'> {
}
export declare class EdytorDocument {
    #private;
    readonly doc: YDoc;
    readonly awareness: Awareness;
    readonly actor: DocumentActor;
    get attribution(): DocumentAttribution;
    readonly transaction: TRANSACTION;
    private _destroyed;
    readonly requireHydration: boolean;
    get clientID(): number;
    get ready(): boolean;
    get readiness(): DocumentReadiness;
    get destroyed(): boolean;
    get writable(): boolean;
    onWritableChange: (listener: (writable: boolean) => void) => (() => void);
    get historyLimit(): number;
    get semantics(): {
        defaultType: string;
        roles: Map<string, BlockRole>;
        rendersContent: Map<string, boolean>;
        defaultChild: Map<string, string>;
        marks: Map<string, MarkEdge>;
    };
    defaultChild: (parentType: string | null) => string;
    rendersContent: (type: string) => boolean;
    adoptSemantics: (config: DocumentSemanticsConfig) => void;
    sync: (value?: JSONDoc) => void;
    get history(): YUndoManager;
    trackOrigin: (origin: unknown) => void;
    untrackOrigin: (origin: unknown) => void;
    clearHistory: () => void;
    transact: <T>(cb: () => T, origin?: unknown) => T;
    onReady: (listener: () => void) => (() => void);
    get syncPending(): boolean;
    get syncRefusal(): SyncRefusedError | undefined;
    onSyncRefused: (listener: (refusal: SyncRefusedError) => void) => (() => void);
    private _decide;
    attachSync: (sync: EdytorSync, opts?: {
        value?: JSONDoc;
    }) => EdytorSyncCleanup | void;
    encode: () => Uint8Array;
    retain: () => (() => void);
    destroy: () => void;
}
```

- `doc`: The underlying engine doc — raw reads/providers borrow it; they never own it.
- `awareness`: One shared awareness instance for every view/provider of this document.
- `actor`: The local actor identity (anonymous opaque id when none was supplied).
- `attribution`: The attribution surface — compact per-block records and the replicated actor dictionary (`u/` profiles, `c/` replica bindings).
- `transaction`: The document's own local-edit transaction origin — headless edits via `transact` carry it so history capture attributes them.
- `requireHydration`: `DocumentOptions.requireHydration`: an empty document waits for a provider's `synced`.
- `clientID`: The replica id (`doc.clientID`) — identifies the replica, not the actor.
- `ready`: `true` once the document's content state is decided — seeded locally or hydrated then synced.
- `readiness`: `pending` | `local` | `hydrated` — distinguishes a locally-seeded ready from a provider-hydrated/restored ready (the contract consumers need to tell "I decided this content" from "sync brought it").
- `writable`: `false` while the document carries a schema stamp this build cannot own (a foreign stamp got in despite the transport's inbound refusal): every write refuses with a `SchemaMismatchError`, and the providers neither persist nor broadcast it.
- `onWritableChange`: Subscribe to `writable` transitions — the visible signal that the document turned read-only (once per transition, not per refused edit).
- `historyLimit`: The undo steps this document's history keeps: the configured `history.limit`, else `DEFAULT_HISTORY_LIMIT`.
- `semantics`: The document-level semantic configuration (snapshot).
- `defaultChild`: The default child type under a parent of `parentType` (`null` = the root) — the one answer split, paragraph insert, merge-unnest, clear and root normalization apply against the new block's actual parent.
- `rendersContent`: Whether kind `type` renders its own content slot (undeclared kinds do).
- `adoptSemantics`: Contribute document-level semantics — the seam views use to seed the structural roles implied by their plugin block definitions.
- `sync`: The readiness transition — decide the document's content state.
- `history`: The default local history (the registry-scoped undo manager — `undo`/`redo`/`stopCapturing`/stacks).
- `trackOrigin`: Track a local-edit transaction origin for history capture — the seam views use to enroll their own `TRANSACTION` instance in this document's history.
- `untrackOrigin`: Release a previously-tracked origin — the view-teardown counterpart of `trackOrigin`.
- `clearHistory`: Drop the undo/redo stacks.
- `transact`: Local document transaction — headless edits, one undo step per call (`origin`: another origin, which the document's history tracks only when it is tracked).
- `onReady`: Subscribe to the readiness transition — fires once, synchronously, inside `sync` when `_readiness` leaves `'pending'` (every decision path goes through `sync()`: provider `synced`, local seed, explicit call).
- `syncPending`: Whether an attached provider has neither settled nor reached its bound.
- `syncRefusal`: The server's refusal of a provider (a `1008`/`4xxx` close — see `SyncRefusedError`), the latest still standing: that provider stopped.
- `onSyncRefused`: Subscribe to provider refusals (`syncRefusal`).
- `attachSync`: Attach a provider sync factory to this document (headless `EdytorSync` path — the same contract views use).
- `encode`: Full replicated state as a v14 update — `loadDocument` restores it on a fresh replica.
- `retain`: Acquire one more attach reference and return its ONE-SHOT release capability.
- `destroy`: Release ONE attach reference.

## EdytorInstance

The view writes with the document's operations (`DocumentWrites`), under
the same names and arguments as a bare `EdytorDocument`, dispatched:
readonly and `document.writable` admit them, every extension hook sees
them and their planned steps first, each is one transaction and the undo
policy's step, the blocks it touched are normalized, and the caret it
decides (a range deletion, a placed flow) is selected. Each answers its
`OpResult` (`refused` when the view, a hook or the document refused it);
`dispatcher.last` records it.

```ts
export interface Edytor extends DocumentWrites {
}
export declare class Edytor {
    #private;
    node?: HTMLElement;
    marks: Map<string, MarkDefinition>;
    blocks: Map<string, BlockDefinition>;
    inlineBlocks: Map<string, InlineBlockDefinition>;
    commands: Map<string, EditorCommand>;
    kinds: KindRow[];
    idToBlock: Handles;
    readonly: boolean;
    root: Block | undefined;
    get synced(): boolean;
    selection: EdytorSelection;
    cells: Cells | undefined;
    get data(): Readonly<EdytorDocData>;
    readonly overlay: Overlay;
    readonly popups: Popups;
    readonly announcer: AnnouncerState;
    readonly labels: PartialLabels;
    private off;
    private onChange?;
    private onDocChange?;
    placeholder?: Placeholder;
    get isComposing(): boolean;
    readonly suggestions: Suggestions;
    document: EdytorDocument;
    get doc(): YDoc;
    private lens?;
    blockAfter: (block: Block, policy?: OrderPolicy) => Block | null;
    blockBefore: (block: Block, policy?: OrderPolicy) => Block | null;
    compareBlocks: (a: Block, b: Block) => number;
    get awareness(): Awareness;
    readonly dispatcher: Dispatcher;
    transact: <T>(cb: () => T) => T;
    canMove: (request: BlockMoveRequest) => boolean;
    move: (request: BlockMoveRequest) => Block[];
    historyUndo: () => void;
    historyRedo: () => void;
    constructor(options: EdytorOptions);
    definitionOf: (type: string) => BlockDefinition;
    private _valueCache;
    get value(): JSONDoc;
    runCommand: (id: string) => Promise<boolean>;
    defaultChild: (parent: Block) => string;
    focus: () => void;
    clear: () => boolean;
    presence: PresenceWriter;
    destroy: () => void;
}
```

- `kinds`: The kind catalogue: one row per preset of each kind record (slash, markdown, block menus).
- `idToBlock`: The view's handles: one id-only `Block` per live id, texts and atoms by position and id.
- `synced`: The view is bound to its decided document (its root is built).
- `cells`: What the components render: one cell per visible block, patched from change reports.
- `data`: The document's properties, read-your-writes and reactive.
- `overlay`: The chrome layer outside the host: handles, menus, remote carets.
- `popups`: The chrome popups open on this view (`surface/popups`): what the root's ARIA names.
- `announcer`: What this view announces to assistive technology (`session/announcer`): its block moves and deletes.
- `labels`: The words the view was given (`<Edytor labels>`); its plugins read their section through `labelsOf`.
- `isComposing`: A composition session is live.
- `suggestions`: The view's suggestions (`session/suggestions`): proposed content, shown here until accepted.
- `dispatcher`: The view's command dispatcher: every mutation this view makes goes through it.
- `transact`: One transaction of this view.
- `canMove`: Whether a block move (relative step or beside/inside a target) is structurally allowed.
- `move`: Move blocks one relative step, or before, after, inside or beside a live target block: resolved to the document's move (`moveBlocks`, an outdent's `unNestBlocks`, `placeBeside`) and announced.
- `historyUndo`: Undo/redo through THIS view: the only history entry points that restore a view's selection (its recorded `before`/`after`); sibling views and a headless `document.history.undo()` restore none (their carets ride the change, as for a remote undo).
- `definitionOf`: The definition of block kind `type` — the one lookup (render, surface, handles).
- `value`: Full-document JSON export — memoized on `facade.version` (the model-state token the facade bumps on every write and every committed update) + the root wrapper identity, so repeated reads inside one version share the one computed tree.
- `defaultChild`: The adopted default type for a new child of `parent` — its actual parent.
- `focus`: Focus the editor, as a click in it would, without scrolling: the selection it has stays, and with none the caret goes to the start of the first text.
- `clear`: Replace the whole document with one empty block and put the caret in it, as one undo step.
- `presence`: This view's presence writer: what it shares (`share`) and how often (`throttle`, ms); both settable.
- `destroy`: Edytor-lifetime teardown.

## EdytorOptions

```ts
export type EdytorOptions = {
    readonly?: boolean;
    hotkeys?: Partial<Record<HotKeyCombination, HotKey>>;
    plugins?: Plugin[];
    document?: EdytorDocument;
    doc?: YDoc;
    awareness?: Awareness;
    actor?: DocumentActor;
    requireHydration?: boolean;
    presence?: PresenceOptions;
    sync?: boolean;
    value?: JSONDoc;
    onChange?: (value: JSONDoc) => void;
    onDocChange?: (change: DocChange) => void;
    onSelectionChange?: (selection: EdytorSelection) => void;
    labels?: PartialLabels;
};
```

- `hotkeys`: Chords (`mod+s`, `shift+alt+enter`) the view binds before plugins and built-ins.
- `document`: The assembled document this view renders — shared facade, history, awareness and semantics live on it (see `crdt/document.ts`).
- `actor`: The local author of a view-owned document (with `document`, set it there).
- `requireHydration`: A view-owned document's `requireHydration` (with `document`, set it there).
- `presence`: What this view shares of its selection with peers, and how often (`edytor.presence`).
- `onChange`: After every commit that changed the visible document: the value, in the shape `value` takes (a whole-document export per commit).
- `onDocChange`: After every commit that changed the visible document: what it changed (blocks added, removed, moved, retyped, edited), with no export.
- `labels`: The words the package shows and says in this view, over the English ones: any entry of any section (`richText`, `table`, `editor`…), and the commands' slash `keywords` by id.

## EdytorProps

```ts
export type EdytorProps = {
    plugins?: Plugin[];
    defaultPlugins?: boolean;
    class?: string;
    'aria-label'?: string;
    'aria-labelledby'?: string;
    'aria-describedby'?: string;
    id?: string;
    edytor?: EdytorClass;
    document?: EdytorDocument;
    doc?: YDoc;
    awareness?: Awareness;
    actor?: DocumentActor;
    presence?: PresenceOptions;
    readonly?: boolean;
    hotkeys?: Partial<Record<HotKeyCombination, HotKey>>;
    onChange?: (value: JSONDoc) => void;
    onDocChange?: (change: DocChange) => void;
    onSelectionChange?: (selection: EdytorSelection) => void;
    value?: JSONDoc;
    labels?: PartialLabels;
    translate?: 'yes' | 'no';
    spellcheck?: boolean;
    autocorrect?: 'on' | 'off';
    autocomplete?: 'on' | 'off';
    autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters';
    inputmode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
    enterkeyhint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send';
    room?: string;
    server?: string;
    params?: Record<string, string>;
    onSyncExpired?: WebsocketSyncOptions['onExpired'];
    onSyncRefused?: (refusal: SyncRefusedError) => void;
    sync?: EdytorSync;
    requireHydration?: boolean;
    snapshot?: JSONDoc;
};
```

- `defaultPlugins`: Add rich text, arrow moves, the suggestion UI and images unless `plugins` lists them (default `true`).
- `'aria-label'`: The root textbox's accessible name (or name it with `aria-labelledby`).
- `'aria-labelledby'`: The id(s) of the element(s) naming the root textbox (a page title, a field label).
- `'aria-describedby'`: The id(s) of the element(s) describing the root textbox (a hint, an error).
- `id`: The root textbox's id (a `<label for>`, a skip link).
- `document`: The assembled document this view renders (see `crdt/document.ts`).
- `actor`: The local author (id, name, color) of the document this view owns: attribution and presence.
- `presence`: What this view shares of its selection with peers (`share`: `'caret'`, `'block'` or `'none'`) and how often (`throttle`, ms).
- `readonly`: Refuse this view's edits.
- `hotkeys`: Chords (`mod+s`, `shift+alt+enter`) the view binds before plugins and built-ins.
- `onChange`: After every commit that changed the visible document: a `JSONDoc`, the type `value` takes.
- `onDocChange`: After every commit that changed the visible document: the change report (`DocChange`), with no whole-document export.
- `value`: The initial content, read once (not bindable): follow edits with `onChange` or `edytor.value`.
- `labels`: The words the package shows and says in this view, over the English ones: any entry of any section (`editor`, `richText`, `table`…) and the commands' slash `keywords` by id.
- `inputmode`: Virtual-keyboard hint — forwarded to the root `inputmode` attribute; omitted from the DOM when unset (browser default).
- `enterkeyhint`: Virtual-keyboard action-key label — forwarded to the root `enterkeyhint` attribute; omitted from the DOM when unset.
- `room`: The document's id.
- `server`: The sync server's base URL (`wss://…/rooms`); the view dials `<server>/<room>`.
- `params`: Query parameters sent with each dial (auth token…).
- `onSyncExpired`: With `server`: the room closed the connection with `4401` (expired credentials).
- `onSyncRefused`: The server refused a provider of this view's document for good (see `document.syncRefusal`): a refusal standing at mount, then each new one.
- `sync`: Advanced: a custom sync factory.
- `requireHydration`: Keep the view read-only, and seed nothing, until its document was fetched once (a provider synced): a first visit offline shows no `value` that the room's content would later duplicate.
- `snapshot`: The document as JSON, shown read-only until this view's document is ready: a first visit to a room-backed document paints at once — server-side too — instead of waiting for the room.

## EdytorSelection

```ts
export declare class EdytorSelection {
    #private;
    private edytorOnSelectionChange?;
    edytor: Edytor;
    get focusedBlocks(): ReadonlySet<Block>;
    get selectedBlocks(): ReadonlySet<Block>;
    get selectedMembers(): Block[];
    selectedInlineBlock: SvelteSet<InlineBlock>;
    get value(): SelectionValue;
    constructor(edytor: Edytor, edytorOnSelectionChange?: ((selection: EdytorSelection) => void) | undefined);
    get projection(): SelectionProjection;
    select: (next: SelectionValue) => void;
    get caret(): Caret | null;
    setCaret: (caret: Caret | null | undefined) => void;
    get pending(): PendingMarks | undefined;
    stage: (pending: Marks | undefined) => void;
    textValue: (startText: Text, yStart: number, endText?: Text, yEnd?: number, isReversed?: boolean, startAffinity?: "left" | "right", mint?: (text: Text, offset: number, affinity?: "left" | "right") => TextAnchor | null) => SelectionValue;
    get dragging(): boolean;
    clearInlineBlockSelection: () => void;
    selectInlineBlock: (inlineBlock: InlineBlock, from?: AtomSide) => void;
    createTextAnchor: (text: Text, offset: number, affinity?: "left" | "right") => TextAnchor | null;
    resolveTextAnchor: (anchor: TextAnchor) => {
        text: Text;
        offset: number;
    } | null;
    displayable: (id: string) => boolean;
    hidden: (block: Block, removed?: ReadonlySet<Block>) => boolean;
    shown: (block: Block, step: "blockBefore" | "blockAfter", { sealed, removed }?: {
        sealed?: boolean;
        removed?: ReadonlySet<Block>;
    }) => Block | null;
    rtl: (text: Text) => boolean;
}
```

- `focusedBlocks`: The blocks the selection touches (read-only: `select()` keeps it).
- `selectedBlocks`: The selected blocks as clicked: a grip-selected list is one block (read-only: `select()` keeps it).
- `selectedMembers`: The blocks a command over the block selection acts on, in document order: the selected blocks, a selected list or code block with its whole subtree (`sel.blocks.exact`).
- `value`: The selection: a value — none, a text range of two anchors, one inline atom, or a set of block ids.
- `projection`: The projection of the current value at the current document version.
- `select`: Replace the selection with `next` (none, a text range of two anchors, one inline atom, or a set of block ids), keeping the selected and focused blocks, presence and `onSelectionChange` in step.
- `caret`: The collapsed text caret in block offsets (`{ block, offset }`, an inline atom counting 1), or `null` when the selection is not a caret (a range, an atom, blocks, none).
- `setCaret`: Select a caret at `caret.offset` of `caret.block` (block offsets, an inline atom counting 1; clamped to the block's content).
- `pending`: The marks the next insertion at the caret takes (values kept).
- `stage`: Stage (or, with `undefined`, clear) the caret's pending marks: a new value, same anchors.
- `textValue`: A text value from wrapper endpoints (offsets inside text segments): the start binds right (boundary inserts stay outside), the end and a caret bind left; `none` when the start text is not bound to the document.
- `dragging`: A pointer drag is in progress (a press in the host, until its release): the projector does not display under it, and the overlay chrome (block handles, column resize bands) takes no pointer, so the native selection is computed against the host.
- `clearInlineBlockSelection`: Leave an atom selection: a caret at the atom's start (the end of the text before it), else none.
- `createTextAnchor`: Anchor a text position into the BACKING text that owns the atom at that display position — the v14 replacement for `Y.createRelativePositionFromTypeIndex(yText, offset, -1)`.
- `resolveTextAnchor`: Resolve a `TextAnchor` → `{text, offset}` (offset within the text segment).
- `displayable`: Displayable (a fact of the rendering layer): the block's own content is mounted and not hidden by view state — a collapsed toggle's body, a `hidden` subtree.
- `hidden`: The view's visibility rule (`selection/visibility.ts`), for extensions: hidden by view state, and the shown neighbour.
- `rtl`: The text's computed direction (a Surface fact): arrow and word keys are visual.

## EdytorSync

```ts
export type EdytorSync = ((payload: EdytorSyncPayload) => void | EdytorSyncCleanup) & {
    bound?: number;
    target?: string;
};
```

## EdytorSyncCleanup

```ts
export type EdytorSyncCleanup = () => void | Promise<void>;
```

## EdytorSyncPayload

Payload a sync factory receives — the v13 contract plus `failed`:
the terminal half of the sync lifecycle. A provider that can never reach
`synced` (destroyed before syncing, persistence load failure, refused
hydration, denied auth) reports it here exactly once; a transient
disconnect or a provider that already synced never does. A
`SyncRefusedError` is the exception: the server refused this
client for good, reported synced or not, and an empty document does not
seed over it.

```ts
export type EdytorSyncPayload = {
    doc: YDoc;
    awareness: Awareness;
    synced: (provider?: unknown) => void;
    failed?: (error: unknown, provider: unknown) => void;
    armBound?: () => void;
    holdBound?: () => void;
    attach?: (sync: EdytorSync) => EdytorSyncCleanup | void;
};
```

- `armBound`: (Re)start this provider's readiness bound (`DEFAULT_READINESS_BOUND` ms from now) — for a factory whose `bound` is `Infinity` because only its transport knows when waiting starts to count (the websocket sync: from the socket's open, or its first failed dial).
- `holdBound`: Stop this provider's readiness bound: an empty document waits for it to settle (or for the next `armBound()`), as the websocket sync does after expired credentials on a first visit.
- `attach`: Attach another sync to the same document as its own provider (its own target, bound and settle): how a sync brings a companion, e.g.

## EMBED_ALLOW

The frame's permissions (`allow`): what a player needs, nothing that reads the viewer.

```ts
declare const EMBED_ALLOW = "fullscreen; picture-in-picture; encrypted-media; clipboard-write";
```

## EMBED_PROVIDERS

The default allowlist: YouTube (through `youtube-nocookie.com`), Vimeo,
Loom, Figma, CodePen and Spotify. A link no provider plays renders no
frame. Extend it with `createEmbedPlugin({ providers: [...EMBED_PROVIDERS, mine] })`.

```ts
declare const EMBED_PROVIDERS: readonly EmbedProvider[];
```

## EMBED_SANDBOX

The frame's `sandbox`: scripts and the provider's own origin, popups that leave it; no top navigation, modals, downloads or forms.

```ts
declare const EMBED_SANDBOX = "allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-presentation";
```

## EmbedFallbackPayload

What an embed's `fallback` snippet receives: a link no allowed provider plays.

```ts
export type EmbedFallbackPayload = {
    block: Block | undefined;
    url: string;
    link: Attachment<HTMLElement>;
};
```

- `block`: The embed; `undefined` in a suggestion's preview.
- `url`: The link (`http(s)`), shown as a link, never in a frame.
- `link`: Its link (`<a href={fallback.url} {@attach fallback.link}>`): a new tab without the opener.

## EmbedPluginOptions

```ts
export type EmbedPluginOptions = {
    providers?: readonly EmbedProvider[];
    menu?: Snippet<[
        UrlPasteController
    ]>;
    fallback?: Snippet<[
        EmbedFallbackPayload
    ]>;
};
```

- `providers`: The allowlist (default `EMBED_PROVIDERS`); a link none of them plays shows no frame.
- `menu`: The menu a URL pasted on an empty line opens (Link, Embed, Bookmark); it renders while `controller.open`, under the line (without it, the link is kept and nothing shows).
- `fallback`: An embed whose link no allowed provider plays (a peer may store any), or a suggestion's preview: a plain link, never a frame (default: the URL as a link): `<a href={fallback.url} {@attach fallback.link}>`.

## EmbedProvider

One allowlisted embed provider: which links it plays, and its player.

```ts
export type EmbedProvider = {
    name: string;
    embed: (url: URL) => string | null;
    aspectRatio?: string;
    height?: number;
};
```

- `name`: The provider's name: the frame's `title`.
- `embed`: The player's URL for a link of this provider, else `null`.
- `aspectRatio`: The frame's CSS `aspect-ratio` (default `16 / 9`); ignored with `height`.
- `height`: A fixed frame height in pixels (a music player).

## embedSourceOf

The player for `value` (a stored or pasted link): the first provider of
`providers` that plays it, and its `https:` player URL. `null` when the
link is not a `safeWebUrl`, no provider plays it, or the player URL
a provider answers is not `https:` or is on the page's own origin. The frame's `src` is always this
answer, never the stored value.

```ts
declare const embedSourceOf: (value: unknown, providers?: readonly EmbedProvider[]) => {
    src: string;
    provider: EmbedProvider;
} | null;
```

## EmptyBodyPayload

What the `emptyBody` snippet of an open toggle's or a callout's empty body receives.

```ts
export type EmptyBodyPayload = {
    readonly block: Block;
    readonly kind: 'toggle' | 'callout';
    readonly label: string;
    open: () => void;
    readonly control: Attachment<HTMLElement>;
};
```

- `block`: The toggle or callout.
- `kind`: Which kind's body.
- `label`: The hint (the view's `richText.emptyBody`).
- `open`: Start the body: a first child, the caret in it (one command, one undo step).
- `control`: The hint's attachment (`{@attach hint.control}`): its marker (`data-edytor-empty-body`, where a dragged block drops inside), `contenteditable="false"`, `aria-hidden` (Enter at the end of the header is the keyboard's way in) and a click that starts the body.

## englishLabels

The English dictionary: the default of every section. Frozen: start
yours from a copy (`{ ...englishLabels.richText, checkbox: '…' }`). Each
plugin reads only its own section, so an app ships the words of the
plugins it lists.

```ts
declare const englishLabels: Labels;
```

## EquationData

The data of a block equation and of an inline one: its TeX source.

```ts
export type EquationData = {
    expression?: string;
};
```

## EquationEditor

One view's equation editor (Notion's): a click on an equation of an
editable view (Enter on a selected one, or a new empty one) opens its
TeX source in a field in the overlay, under it. Each keystroke writes
`data.expression` (one `patchData` command; the dispatcher groups the
typing into one undo step), so the equation itself is the live preview.
Enter (Shift+Enter: a newline in a block equation), Escape, Done, a
press or a focus outside close it; an inline equation closed empty is
removed. The editor then gives the keys back: a block equation stays
selected, the caret goes after an inline one.

What a `panel` snippet receives: the equation being edited (`target`,
`display` for a block one), its source (`expression`, written by
`set`), KaTeX's error (`error`) and `close()`.
Put `{@attach editor.field}` on the source's field: it takes the focus
as the editor opens, Enter (Shift+Enter: a newline in a block equation)
and Escape close the editor, and focus leaving the panel closes it.

```ts
export declare class EquationEditor {
    #private;
    private edytor;
    target: EquationTarget | null;
    get readonly(): boolean;
    get display(): boolean;
    get expression(): string;
    get error(): string | null;
    open: (target: EquationTarget) => void;
    editing: (target: EquationTarget) => boolean;
    set: (expression: string) => void;
    close: (keys?: boolean) => void;
    field: Attachment<HTMLTextAreaElement | HTMLInputElement>;
}
```

- `target`: The equation being edited (reactive).
- `readonly`: The view is readonly (reactive): the editor closes.
- `display`: A block equation is edited (a field of several lines).
- `expression`: The edited equation's TeX source (reactive through its block's cell).
- `error`: What the source draws as: KaTeX's error, if it cannot read it.
- `open`: Open the editor on `target`: nothing in a readonly view.
- `editing`: Whether `target` is the one being edited.
- `set`: Write the source as it is typed: one `patchData` command per keystroke, one undo step.
- `close`: Close the editor.
- `field`: The source's field (`{@attach editor.field}`, a `textarea` or an `input`): it takes the focus as the editor opens, the caret at the end of the source.

## EquationErrorPayload

What an equation's `error` snippet receives.

```ts
export type EquationErrorPayload = {
    display: boolean;
    expression: string;
    error: string;
    label: string;
};
```

- `display`: A block equation (`true`) or an inline one.
- `expression`: Its TeX source.
- `error`: KaTeX's message.
- `label`: The view's word for it (`equation.invalid`).

## EquationLabels

The block and inline equations (`createEquationPlugin`).

```ts
export type EquationLabels = {
    block: string;
    inline: string;
    blockPlaceholder: string;
    inlinePlaceholder: string;
    invalid: string;
};
```

- `block`: The block preset, and the block's accessible name.
- `inline`: The inline command.
- `blockPlaceholder`: An empty block equation.
- `inlinePlaceholder`: An empty inline equation.
- `invalid`: TeX that does not render: shown as the equation's title, with KaTeX's message.

## EquationPlaceholderPayload

What an equation's `placeholder` snippet receives.

```ts
export type EquationPlaceholderPayload = {
    display: boolean;
    label: string;
};
```

- `display`: A block equation (`true`) or an inline one.
- `label`: The view's placeholder for it (`equation.blockPlaceholder`, `inlinePlaceholder`).

## EquationPluginOptions

```ts
export type EquationPluginOptions = {
    katex?: KatexLoader | string | false;
    macros?: Record<string, string>;
    panel?: Snippet<[
        EquationEditor
    ]>;
    placeholder?: Snippet<[
        EquationPlaceholderPayload
    ]>;
    error?: Snippet<[
        EquationErrorPayload
    ]>;
};
```

- `katex`: Where KaTeX comes from, loaded the first time an equation is drawn in a browser (a server render shows the TeX source and loads nothing):
- `macros`: TeX macros every equation reads (`{ '\\RR': '\\mathbb{R}' }`).
- `panel`: The editor's panel and its words (the source's field, KaTeX's error, Done): it renders under the edited equation while `editor.target` is set (centered under a block equation; without it, nothing shows).
- `placeholder`: An empty equation's placeholder (default: `√x` and the view's `equation.blockPlaceholder` or `inlinePlaceholder`).
- `error`: An equation KaTeX cannot read (default: its source, marked invalid, KaTeX's message in its title).

## EquationTarget

The equation being edited: a block equation, or an inline one (`atom`) in block `block`.

```ts
export type EquationTarget = {
    block: string;
    atom?: string;
};
```

## FilePluginOptions

```ts
export type FilePluginOptions = {
    row?: Snippet<[
        FileRowPayload
    ]>;
};
```

- `row`: A file's row once it has a source (default: Notion's, a paperclip, its name and size): `<a href={row.src} {@attach row.link}>`, which downloads the file.

## FileRowPayload

What a file block's `row` snippet receives: the file it links to.

```ts
export type FileRowPayload = {
    block: Block | undefined;
    src: string;
    name: string;
    size: number | undefined;
    sizeLabel: string;
    link: Attachment<HTMLElement>;
};
```

- `block`: The file block; `undefined` in a suggestion's preview.
- `src`: The file's URL.
- `name`: Its name: the uploaded file's, else its URL's last segment.
- `size`: Its size in bytes, when it was uploaded.
- `sizeLabel`: That size in the view's words (`media.fileSize`), `''` without one.
- `link`: The row's link (`<a href={row.src} {@attach row.link}>`): it downloads the file under its name, in a new tab without the opener, never dragged out of the editor.

## fillTableCell

Create the cell `row` shows none of for `column` (an id, or a
position among the grid's; `table.pad`: the first edit in a padded
cell), empty, in one plan. A column the table shows but no longer
lists (`table.cell`) is listed again, last. `ids`: the new cell.

```ts
declare const fillTableCell: (doc: Omit<DocumentOperations, "transact">, row: BlockId, column: string | number) => Prepared;
```

## findController

The find plugin's controller of `edytor`; `undefined` when the plugin is not listed.

```ts
declare const findController: (edytor: Edytor) => FindController | undefined;
```

## FindController

The find bar's state and actions (`plugins/find`): the query, the matches
in reading order, the current one and the replacements. It reads the
document through the facade and the handles and writes it through one
dispatcher command; the bar and the highlights render from it.

```ts
export declare class FindController {
    #private;
    private edytor;
    isOpen: boolean;
    replacement: string;
    constructor(edytor: Edytor);
    get query(): string;
    set query(value: string);
    get caseSensitive(): boolean;
    set caseSensitive(value: boolean);
    readonly matches: FindMatch[];
    readonly current: number;
    get canReplace(): boolean;
    open: (query?: string) => void;
    close: ({ select }?: {
        select?: boolean;
    }) => void;
    next: () => void;
    previous: () => void;
    replace: () => CommandResult["status"];
    replaceAll: () => CommandResult["status"];
    field: Attachment<HTMLInputElement>;
    replaceField: Attachment<HTMLInputElement>;
    destroy: () => void;
}
```

- `replacement`: The replacement text.
- `query`: What the bar searches for; setting it shows the current match.
- `query`: What the bar searches for; setting it shows the current match.
- `caseSensitive`: Match case exactly (default: ignore it).
- `caseSensitive`: Match case exactly (default: ignore it).
- `matches`: Every match while the bar is open, in reading order (none while closed).
- `current`: The current match's index in `matches`; `-1` when there is none.
- `canReplace`: Replacing is possible: an editable view of a writable document.
- `open`: Open the bar (or focus it again) and search from the caret.
- `close`: Close the bar.
- `next`: The next match becomes the current one (after the last, the first).
- `previous`: The previous match becomes the current one (before the first, the last).
- `replace`: Replace the current match, then go to the next one.
- `replaceAll`: Replace every match: one command, one undo step.
- `field`: The query field (`{@attach find.field}`): focused on each Mod+F; Enter, Shift+Enter, Escape, Mod+F.
- `replaceField`: The replacement field (`{@attach find.replaceField}`): Enter replaces, Escape closes.
- `destroy`: Stop following the document (the editor is going away).

## FindMatch

One occurrence: `length` characters of block `block` from display offset `offset` (atoms count 1).

```ts
export type FindMatch = {
    block: string;
    offset: number;
    length: number;
};
```

## findMatches

Every occurrence of `query` in the document's text, in reading order
(pre-order, a closed toggle's body included: what hides it is view state,
not the document). A match lies in one text segment: an inline atom
(a mention) ends it, and marks never do. Matches do not overlap.

```ts
declare const findMatches: (edytor: Edytor, query: string, { caseSensitive }?: FindOptions) => FindMatch[];
```

## FindOptions

```ts
export type FindOptions = {
    caseSensitive?: boolean;
};
```

- `caseSensitive`: Match case exactly (default: ignore it, as Notion and the browsers do).

## FindPluginOptions

```ts
export type FindPluginOptions = {
    bar?: Snippet<[
        FindController
    ]>;
};
```

- `bar`: The bar's markup and its words; it renders while `find.isOpen`, placed at the editor's top right (without it, Mod+F searches and highlights, and nothing else shows).

## Flow

An admitted flow: lines in order, ids fresh; `whole`: a block-selection copy.

```ts
export type Flow = {
    lines: FlowLine[];
    whole?: boolean;
};
```

## FlowLine

One line of a flow: a kinded block, or an inline run when it has no `type`.

```ts
export type FlowLine = Omit<BlockSpec, 'type'> & {
    type?: string;
};
```

## FlowTarget

A position, the blocks the flow replaces (`flow.slot`), or a slot (`flow.place`).

```ts
export type FlowTarget = DocPosition | {
    replace: readonly BlockId[];
} | {
    slot: Destination;
};
```

## FlowView

What the view tells flow placement beyond range deletion's view: a
container's flat item kind (`numbered-list-item` for an `ordered-list`).

```ts
export type FlowView = RangeView & {
    itemKind?: (parent: BlockId) => string | undefined;
    header?: (id: BlockId) => boolean | 'closed';
};
```

## Handles

```ts
export declare class Handles {
    #private;
    private edytor;
    readonly root: Block;
    constructor(edytor: Edytor);
    block: (id: string) => Block;
    get: (id: string) => Block | undefined;
    has: (id: string) => boolean;
    text: (blockId: string, ordinal: number) => Text;
    textById: (id: string) => Text | undefined;
    atom: (blockId: string, atomId: string) => InlineBlock;
    parts: (id: string) => ContentPart[];
    prune: (change: DocChange) => void;
    clear: () => void;
}
```

- `block`: The handle of block `id`, live or not (the one a caller held stays the same object).
- `get`: The handle of live block `id`; none for a dead or unknown id.
- `text`: The `ordinal`-th text segment of block `blockId`.
- `textById`: The text whose id is `id` (`t:<block>:<ordinal>`), while its segment exists.
- `atom`: The handle of atom `atomId`, shown in block `blockId`.
- `parts`: Block `id`'s content parts — from its runs, which the index keeps per block and reads-your-writes (a write elsewhere re-derives nothing here).
- `prune`: Forget the handles of the blocks a commit removed.

## HistoryClient

A room's version history, as the history panel reads and restores it.

```ts
export type HistoryClient = {
    list(): Promise<HistoryVersion[]>;
    read(key: string): Promise<JSONDoc | null>;
    restore(key: string): Promise<HistoryRestoreResult>;
    undo(): Promise<HistoryUndoResult>;
};
```

- `list`: The versions, newest first.
- `read`: A version as JSON, or `null` when the room holds no such version.
- `restore`: Restore a version as a forward edit every client receives.
- `undo`: Undo the last restore (one level).

## HistoryClientOptions

Options of `createHistoryClient`.

```ts
export type HistoryClientOptions = {
    server: string;
    room: string;
    params?: Record<string, string> | (() => Record<string, string>);
    fetch?: typeof fetch;
};
```

- `server`: The base URL of the history route: requests go to `<server>/<room>`, the room percent-encoded as one path segment (`ws:`/`wss:` read as `http:`/`https:`).
- `room`: The document's id.
- `params`: Query parameters sent with each request (a token): an object, or a function read at every request.
- `fetch`: The fetch to use (default the global one).

## HistoryRequestError

A history request the server refused: `status` is its HTTP status (`401`, `403`, `404`, `503`…).

```ts
export declare class HistoryRequestError extends Error {
    readonly status: number;
    constructor(status: number, message: string);
}
```

## HistoryRestoreResult

What a restore did: `refused` when the room holds no such version.

```ts
export type HistoryRestoreResult = {
    status: 'applied' | 'noop' | 'refused';
    key: string;
    revived?: number;
    moved?: number;
    rewritten?: number;
    created?: number;
    deleted?: number;
};
```

## HistoryUndoResult

What undoing the last restore did: `noop` when there is none to undo.

```ts
export type HistoryUndoResult = {
    status: 'applied' | 'noop';
};
```

## HistoryVersion

One stored version, as the room lists it (`edytor/cloudflare`'s `HistoryEntry`).

```ts
export type HistoryVersion = {
    key: string;
    date: string;
    slot: 'am' | 'pm';
    bytes: number;
    blocks: number;
    editors: string[];
    more: number;
    at: number;
    expiresAt: number | null;
};
```

- `key`: The version's key, which `read` and `restore` take.
- `date`: The local date of its slot, `YYYY-MM-DD`, in the room's time zone.
- `slot`: Its half of the day: `am` before local noon, `pm` until midnight.
- `bytes`: Stored (compressed) bytes.
- `blocks`: Visible blocks.
- `editors`: The users whose edits the room stored during the slot.
- `more`: Editors left out of `editors` to keep the metadata small.
- `at`: When the version was written (ms since the epoch).
- `expiresAt`: When it expires (ms since the epoch), `null` when its store does not say.

## HotKey

```ts
export type HotKey = (payload: {
    event?: KeyboardEvent;
    edytor: Edytor;
    prevent: (cb?: () => void) => void;
}) => void;
```

## HotKeyCombination

A chord: a key, after up to three modifiers in any order (matched case-insensitively).

```ts
export type HotKeyCombination = Key | `${Modifiers}+${Key}`;
```

## ImageBox

The hovered image, layer-relative: the `img` box, and its frame's width (the most it may take).

```ts
export type ImageBox = {
    id: string;
    x: number;
    y: number;
    width: number;
    height: number;
    align: ImageAlign;
};
```

## ImageControls

The image chrome (Notion's), in the overlay: on the image under
the pointer of an editable view, a resize handle on each side and a
toolbar (alignment, alt text). Dragging a handle resizes the image live
and writes nothing: the width is a view-only preview the kind's snippet
reads (`width`), so only this view sees it and no peer gets a frame; the
release writes `data.width` (CSS px, one `patchData` command, one undo
step). A centered image grows on both sides (twice the pointer's move),
an image aligned left or right from the side the handle is on. It is
held between `MIN_IMAGE_WIDTH` and its block's width. Escape, a cancel or
the view turning readonly drop the preview at once. Positions are read in
the overlay's measure, once per frame.

```ts
export declare class ImageControls {
    #private;
    hovered: string | null;
    box: ImageBox | null;
    constructor(edytor: Edytor);
    get drag(): Drag | null;
    get readonly(): boolean;
    get selecting(): boolean;
    get selected(): string | null;
    get target(): string | null;
    get shown(): boolean;
}
```

- `hovered`: The image block under the pointer.
- `box`: The measured box of the image the chrome is on.
- `drag`: The handle being dragged.
- `readonly`: The view is readonly (reactive).
- `selecting`: A text selection is in progress: the chrome takes no pointer.
- `selected`: The image block that is the whole block selection, if any (reactive): the keyboard's image.
- `target`: The image the chrome is on: the dragged one, the one whose alt is edited, the hovered one, else the selected one (no pointer needed).
- `shown`: Whether the chrome shows: over an image of an editable view, or while a handle drags.

## ImageLabels

```ts
export type ImageLabels = {
    image: string;
};
```

- `image`: The preset.

## ImageToolbar

What a `toolbar` snippet receives: the image the chrome is on (`box`),
its alignment (`align`) and alt text (`alt`, `setAlt`), the alt field's
state (`editing`, `toggleAlt`, `closeAlt`) and the attachments that give
your markup its behavior (`bar`, `altField`).

```ts
export declare class ImageToolbar {
    #private;
    constructor(controls: ImageControls);
    get box(): import("./controls.svelte.js").ImageBox | null;
    get readonly(): boolean;
    get editing(): string | null;
    set editing(id: string | null);
    align: (align: ImageAlign) => void;
    get alt(): string;
    setAlt: (alt: string) => void;
    toggleAlt: () => void;
    bar: Attachment<HTMLElement>;
    altField: Attachment<HTMLInputElement>;
    closeAlt: (keys?: boolean) => void;
}
```

- `box`: The measured image the toolbar is on (`null`: none).
- `readonly`: The view is readonly (reactive).
- `editing`: The image whose alt field is open: the chrome stays on it.
- `editing`: The image whose alt field is open: the chrome stays on it.
- `align`: Set the target image's alignment: one `patchData` command.
- `alt`: The target image's alt text.
- `setAlt`: Write the alt text as it is typed: the dispatcher groups the keystrokes into one step.
- `toggleAlt`: Open (or close) the alt field of the target image.
- `bar`: The image's toolbar (`{@attach controls.bar}`, named by your markup): its `toolbar` role unless it has its own, the mark a press outside the alt field reads (a press on the toolbar keeps the field open, so its Alt button toggles it), and presses that keep the editor's focus.
- `altField`: The alt text field (`{@attach controls.altField}`): focused when it opens; Enter or Escape closes it and gives the editor its keys back, focus moving out of it (or of the element marked `data-edytor-image-alt-panel` around it) closes it, and a press on it keeps it open.
- `closeAlt`: Close the alt field; with `keys`, the editor takes the keys back.

## ImageToolbarOptions

```ts
export type ImageToolbarOptions = {
    toolbar: Snippet<[
        ImageToolbar
    ]>;
};
```

- `toolbar`: The toolbar over the hovered image (the alignments and the alt field); it renders while the image chrome shows and no handle drags, at the image's top right.

## IndexeddbSyncOptions

```ts
export type IndexeddbSyncOptions = CrdtIndexeddbSyncOptions;
```

## InitializedPlugin

```ts
export type InitializedPlugin = ReturnType<Plugin>;
```

## InlineBlock

An id-only, read-only inline-atom handle: atom `id`, shown in block
`blockId`. Getters read the document index; writes are the view's
operations (`edytor.patchData({ block: atom.blockId, atom: atom.id }, …)`).
`node` is the element that renders it (a Surface fact). A reactive reader
of a getter depends on the block's cell, so it re-runs when a commit
changes the block's content.

```ts
export declare class InlineBlock {
    #private;
    readonly: boolean;
    readonly edytor: Edytor;
    readonly id: string;
    blockId: string;
    node: HTMLElement | undefined;
    constructor(edytor: Edytor, blockId: string, id: string);
    get parent(): Block;
    get selected(): boolean;
    get type(): string;
    get data(): Readonly<Record<string, any>>;
    get definition(): InlineBlockDefinition;
    get index(): number;
    get isInDocument(): boolean;
    get value(): JSONInlineBlock;
}
```

- `blockId`: The block that shows the atom (the view updates it when the atom moves).
- `data`: The atom's `data`; a reactive reader depends on its block's cell.
- `index`: Position among the block's content parts.
- `isInDocument`: The atom is shown by a live block.

## InlineBlockDefinition

```ts
export type InlineBlockDefinition = {
    snippet: Snippet<[
        InlineBlockSnippetPayload<any>
    ]>;
    plain?: (data: JSONInlineBlock['data']) => string;
    html?: (data: JSONInlineBlock['data']) => string;
    parse?: (element: HTMLElement) => Record<string, SerializableContent> | undefined;
};
```

- `plain`: Clipboard plain-text form of the atom; default none.
- `html`: Clipboard HTML form of the atom (inline markup, escaped by you); default an empty `<span data-edytor-inline-block>`.
- `parse`: HTML import: the atom's data when `element`, met in a line's inline content, is this atom, else `undefined`.

## InlineBlockSnippetPayload

```ts
export type InlineBlockSnippetPayload<D = Record<string, any>> = {
    block: InlineBlockView<D>;
};
```

## InlineBlockView

What an inline-atom snippet receives: declared values; a suggestion's atom is never selected.

```ts
export type InlineBlockView<D = Record<string, any>> = {
    readonly id: string;
    readonly type: string;
    readonly data: Readonly<D>;
    readonly selected: boolean;
    readonly handle: InlineBlock | undefined;
};
```

- `handle`: The atom's id-only handle; a suggested atom has none.

## InlineSpec

```ts
export type InlineSpec = {
    id: string;
    type: string;
    data?: Record<string, unknown>;
};
```

## InputRule

Text typed at a caret that matches `find` is replaced (`:smile:` → 😄,
`## ` → a heading): Notion's markdown and autoformat. Rules run for text
typed at a collapsed caret (one `insertText` at it, an IME's commit
excluded), in plugin order then list order; the first that writes wins.
They do not run in the lines of a code block.

```ts
export type InputRule = {
    find: RegExp;
    replace: (match: RegExpExecArray, ctx: InputRuleContext) => string | boolean | null | void;
};
```

- `find`: Tested on the caret's text segment up to the caret, followed by the typed text; a match must end at the typed text's end (end it with `$`).
- `replace`: The replacement.

## InputRuleContext

What an input rule's `replace` receives besides the match.

```ts
export type InputRuleContext = TextRuleContext & {
    typed: string;
    remove: (then?: () => unknown) => boolean;
};
```

## insertTableColumn

Insert a column at `index` among `table`'s listed columns
(`table.insert-column`): a new id in `data.columns` and an empty cell
for it in every row the table shows, in one plan. `width`: the new
column's. `ids`: the table.

```ts
declare const insertTableColumn: (doc: Omit<DocumentOperations, "transact">, table: BlockId, index: number, width?: number) => Prepared;
```

## insertTableRow

Insert a row at `index` among `table`'s rows (`table.insert-row`): one
empty cell per column, in one plan. `ids`: the new row.

```ts
declare const insertTableRow: (doc: Omit<DocumentOperations, "transact">, table: BlockId, index: number) => Prepared;
```

## isPrevention

Whether `error` is a veto (a `PreventionError`).

```ts
declare const isPrevention: (error: unknown) => error is PreventionError;
```

## JSONBlock

```ts
export type JSONBlock = {
    type: string;
    id?: string;
    data?: Record<string, SerializableContent>;
    children?: JSONBlock[];
    content?: (JSONText | JSONInlineBlock)[];
};
```

## JSONDoc

```ts
export type JSONDoc = {
    data?: Record<string, SerializableContent>;
    children: JSONBlock[];
} & Partial<Record<string, unknown>>;
```

## JSONInlineBlock

```ts
export type JSONInlineBlock = {
    id?: string;
    data?: any;
    type: string;
};
```

## JSONText

```ts
export type JSONText = {
    text: string;
    marks?: Record<string, SerializableContent>;
};
```

## KATEX_CDN

Where the default loader fetches KaTeX: jsDelivr's copy of the npm
package's `dist`, at `KATEX_VERSION` exactly (`katex.mjs`,
`katex.min.css`, and the fonts the stylesheet names).

```ts
declare const KATEX_CDN = "https://cdn.jsdelivr.net/npm/katex@0.18.7/dist/";
```

## KATEX_VERSION

The KaTeX version the default loader fetches: the one edytor is tested with.

```ts
declare const KATEX_VERSION = "0.18.7";
```

## KatexLike

The part of KaTeX the plugin uses: `katex.renderToString`.

```ts
export type KatexLike = {
    renderToString: (tex: string, options?: Record<string, unknown>) => string;
};
```

## KatexLoader

Loads KaTeX when an equation first shows: `() => import('katex')` from your bundle.

```ts
export type KatexLoader = () => Promise<KatexLike | {
    default: KatexLike;
}>;
```

## Key

A key a chord names: a one-character key or a named one, lower case.

```ts
export type Key = Chars<typeof CHARS> | (typeof NAMED)[number];
```

## KindMenuAction

A row a kind adds to the block menu (`BlockDefinition.menu`).

```ts
export type KindMenuAction = {
    id: string;
    label: string;
    icon?: string;
    checked?: boolean;
    isEnabled?: () => boolean;
    run: () => unknown;
};
```

- `icon`: An icon id (`iconOf`), else none.
- `checked`: A switch's state: the row is a `menuitemcheckbox`, and the menu stays open when it runs.
- `run`: The row's action.

## KindPreset

A way to create a block kind: one slash command, markdown prefixes, one block-menu row.

```ts
export type KindPreset = {
    label: string;
    icon?: string;
    keywords?: string[];
    data?: Record<string, SerializableContent>;
    markdown?: string[];
    markdownFrom?: Record<string, string[]>;
    group?: string;
};
```

- `data`: The new block's data.
- `markdown`: Typed at the start of a block's first text, each converts it (the last character triggers).
- `markdownFrom`: Shortcuts that convert a block of another kind into this preset, by that kind, typed as `markdown` is: `{ toggle: ['# '] }` makes a toggle list a toggle heading.
- `group`: The slash menu section (default `Basic blocks`).

## KindRecord

A kind record as a plugin declares it (`plugin.blocks[type]`): its
structural row with whatever else it holds (a snippet, an element,
presets), or a bare snippet, a plain kind.

```ts
export type KindRecord = KindSemantics | ((...args: never[]) => unknown);
```

## KindRow

The kind catalogue: one row per preset of each registered kind
record, in registration order. The slash menu, markdown shortcuts and
block menus read it; nothing else names a kind.

```ts
export type KindRow = KindPreset & {
    id: string;
    value: JSONBlock;
    replaces: boolean;
    command?: EditorCommand;
};
```

## KindSemantics

One kind's structural row — the fields of its `BlockDefinition` a document adopts.

```ts
export type KindSemantics = BlockRole & {
    rendersContent?: boolean;
    defaultChild?: string;
};
```

## KindUpload

What files a kind takes (its record's `upload`, `BlockDefinition`): which,
and the data an upload's answer writes. The view's uploader, if the
uploads plugin is listed, sends them.

```ts
export type KindUpload = {
    accepts: (file: File) => boolean;
    fallback?: boolean;
    data: (file: File, answer: string) => Record<string, unknown> | null;
    filled: (data: Record<string, unknown>) => boolean;
    preview?: boolean;
};
```

- `accepts`: Whether it takes `file`.
- `fallback`: It takes any file no other kind takes (the file block): asked last.
- `data`: The data the answered URL writes (`null`: no source this kind stores).
- `filled`: Whether the block has a source already (a peer's, a link's): its upload then writes nothing.
- `preview`: Show the file while it uploads (an image's `blob:` preview).

## Labels

Every section of the dictionary.

```ts
export type Labels = {
    editor: EditorLabels;
    richText: RichTextLabels;
    blockMenu: BlockMenuLabels;
    image: ImageLabels;
    media: MediaLabels;
    code: CodeLabels;
    columns: ColumnsLabels;
    page: PageLabels;
    toc: TocLabels;
    pageLink: PageLinkLabels;
    comments: CommentsLabels;
    table: TableLabels;
    equation: EquationLabels;
};
```

## LanguageMenu

One view's code language list (Notion's): the header's language button
opens it in the overlay under the button, a search field over the
languages (by label or id), the block's own marked. The field holds the
keys: the arrows move the highlighted row, Enter picks it, Escape
closes. A pick writes the block's `data.language` (one `patchData`
command, one undo step). Opened by the keys, the list gives them back to
its button; opened by a press, to the editor. A press or a focus outside
closes it, as readonly does.

What a `menu` snippet receives: the open list (`open`), the query
and its rows (`query`, `items`, each a `MenuItemPayload`), the keyboard's
row (`index`), `search`, `pick` and `close`, and
the attachments that give it the keys and the ARIA: `keys` on the
search field, `popup` on the list, `option(index)` on each row.

```ts
export declare class LanguageMenu extends ChromeList {
    #private;
    open: {
        block: string;
        keys: boolean;
    } | null;
    query: string;
    get readonly(): boolean;
    get current(): string | null;
    get rows(): readonly LanguageRow[];
    get items(): LanguageMenuItem[];
    protected get count(): number;
    isOpenFor: (block: string) => boolean;
    toggle: (block: string, keys: boolean) => void;
    search: (query: string) => void;
    pick: (row?: LanguageRow | undefined) => void;
    close: (keys?: "button" | "editor" | null) => void;
    keys: Attachment<HTMLElement>;
}
```

- `open`: The code block whose list is open, and whether the keys opened it (reactive).
- `query`: The search field's text.
- `readonly`: The view is readonly (reactive): the list closes.
- `current`: The open block's language id.
- `rows`: The rows the query keeps: a language the plugin does not list first (the block's own), then the list.
- `items`: The rows for your `menu`: each the row shape every menu shares (`run` picks it, `select` highlights it, `option` its attributes), and whether it is the block's language (`current`).
- `count`: How many rows the keyboard walks.
- `isOpenFor`: Whether `block`'s list is open.
- `toggle`: Open `block`'s list (`keys`: the keyboard opened it), or close it when it is open.
- `search`: A new query: the first row it keeps is highlighted.
- `pick`: Pick `row` (the highlighted one): the block's language, one undo step; the list closes.
- `close`: Close the list; the keys go back to its button (`button`), to the editor (`editor`: its caret where it was), or stay where a press or a focus put them (`null`).
- `keys`: The search field (`{@attach menu.keys}`): it takes the focus as the list opens and is its `combobox` (unless it has a role), naming the list and the highlighted row (`aria-controls`, `aria-activedescendant`).

## LanguageMenuItem

One row of the language list, for your `menu`: the row shape every
menu shares (`item` is the language, `run` picks it), and whether it is
the block's own language (`current`, the demo's ✓).

```ts
export type LanguageMenuItem = MenuItemPayload<LanguageRow> & {
    current: boolean;
};
```

## LanguageRow

A row of the language list: a language id and its label as shown.

```ts
export type LanguageRow = {
    id: string;
    label: string;
};
```

## lastUpdated

When a room last stored a change: one authorized HTTP request, no document opened.

```ts
declare const lastUpdated: typeof lastUpdatedOf;
```

## LastUpdatedOptions

What `lastUpdated` takes: the room it asks, and the `params` (a token) its dial would carry.

```ts
export type LastUpdatedOptions = WebsocketTarget & {
    params?: Record<string, string>;
    fetch?: typeof fetch;
};
```

## loadCodeLanguage

Load `language`'s grammar (and those it embeds); resolves once its lines highlight.

```ts
declare const loadCodeLanguage: (language: CodeLanguage) => Promise<void>;
```

## loadDocument

See `DocumentBinding.loadDocument`.

```ts
declare const loadDocument: (update: Uint8Array, options?: LoadDocumentOptions) => EdytorDocument;
```

## LoadDocumentOptions

```ts
export type LoadDocumentOptions = DocumentOptions;
```

## MarkDefinition

```ts
export type MarkDefinition = {
    snippet?: Snippet<[
        MarkSnippetPayload<any>
    ]>;
    tag?: string;
    attributes?: (value: any) => Record<string, string | undefined>;
    parse?: (element: HTMLElement) => SerializableContent | undefined;
    void?: boolean;
    edge?: MarkEdge;
    copy?: boolean;
    toolbar?: {
        label: string;
        icon: string;
        run?: (edytor: Edytor) => void;
    };
};
```

- `snippet`: Custom markup inside a core `<span data-edytor-mark>`; without one the core renders `tag`.
- `tag`: The mark's element: rendered by the core (`<tag data-edytor-mark>`), written by the clipboard's HTML export and read back by HTML import — one tag, so render and export cannot disagree.
- `attributes`: The element's attributes from the mark's value (sanitized here); `undefined` omits one.
- `parse`: HTML import: the mark's value when `element` carries it, else `undefined`.
- `edge`: Whether typing at the mark's edges extends it (`marksForInsertion`); default `inclusive`.
- `copy`: Whether copy and cut carry the mark (default `true`): `false` copies the text without it (a comment's anchor: the copy is not commented).
- `toolbar`: A selection-toolbar button: it toggles the mark, or runs `run` instead (the comments plugin's Comment button opens its composer).

## MarkEdge

Whether a mark grows at its edges — always, never, or at its start only.

```ts
export type MarkEdge = 'inclusive' | 'exclusive' | 'side-dependent';
```

## MarkSnippetPayload

Represents the payload for mark snippets with generic serializable content.

```ts
export type MarkSnippetPayload<D extends SerializableContent = SerializableContent> = {
    content: Snippet;
    mark: D;
    text: Text;
};
```

## MarqueeBoxPayload

What a `box` snippet receives: the rectangle and what it selects (reactive).

```ts
export type MarqueeBoxPayload = {
    rect: DOMRectReadOnly;
    count: number;
    ids: readonly string[];
    adding: boolean;
};
```

- `rect`: The rectangle, in viewport (client) coordinates.
- `count`: How many blocks are selected (the ones Shift or Mod kept included).
- `ids`: The selected blocks' ids, in the order the selection lists them.
- `adding`: Shift or Mod was held at the press: the rectangle adds to the selection there was.

## MarqueePluginOptions

```ts
export type MarqueePluginOptions = {
    container?: string | ((editor: HTMLElement) => Element | null | undefined);
    threshold?: number;
    box?: Snippet<[
        MarqueeBoxPayload
    ]>;
};
```

- `container`: An element around the editor whose empty area starts a marquee too (the page, as in Notion): a CSS selector the editor's element is matched against with `closest()`, or a function of the editor's element.
- `threshold`: How far (px) the pointer moves before the rectangle starts: a shorter press is a click.
- `box`: The rectangle's markup; it renders in a host placed and sized to the rectangle (without it, the blocks are selected and no rectangle shows).

## MAX_INLINE_IMAGE_BYTES

The largest inline image (`data:` URL) an image block takes: 1 MiB of its
URL. An inline image is stored in the document and sent in every
update and catch-up that carries the block, so a larger one would bloat
the room's frames and storage: host the file instead (the plugin's
`upload` option) and embed its URL.

```ts
declare const MAX_INLINE_IMAGE_BYTES: number;
```

## Measure

Reads layout with the layer's origin; returns the writes to apply after every read.

```ts
export type Measure = (origin: DOMRect) => (() => void) | void;
```

## MediaEmptyController

An empty image or media block's state (an image, an embed, a bookmark, a
file, a video, an audio track: Notion's "Add …"): the panel's `open`
state, the link field's `draft`, why the last link or upload was not
embedded (`failed`), and the upload of a pasted or dropped file
(`pending`); `embed` and `upload` set the source. What an image or media
plugin's `empty` snippet receives (one per empty block shown).

```ts
export declare class MediaEmptyController {
    #private;
    readonly block: Block | undefined;
    readonly kind: MediaKind | 'image';
    private view;
    open: boolean;
    draft: string;
    failed: MediaEmptyFailure | null;
    constructor(block: Block | undefined, kind: MediaKind | 'image', view: EmptyView | undefined);
    get icon(): string;
    get accept(): string | undefined;
    get readonly(): boolean;
    get canUpload(): boolean;
    get pending(): FileUpload | undefined;
    embed: (value?: string) => void;
    upload: (picked: File) => Promise<void>;
    field: Attachment<HTMLInputElement>;
}
```

- `block`: The empty block; none in a suggestion's preview.
- `kind`: Which kind it is (your words for it: its button, placeholder, errors).
- `open`: The link panel is open.
- `draft`: The link field's text.
- `failed`: Why the last link or upload was not embedded (`null`: it was, or none was tried).
- `icon`: The kind's icon (an emoji).
- `accept`: The file picker's `accept`: `image/*`, `video/*`, `audio/*`, else any file.
- `readonly`: No control may write: a readonly view, or a suggestion's preview (no block).
- `canUpload`: The view lists the uploads plugin and the kind takes files: offer Upload.
- `pending`: The upload of a pasted or dropped file into this block, if any (its `status`, `progress`, `preview`).
- `embed`: Embed `value` (default the `draft`): the block takes the link, or `failed` says why not.
- `upload`: Upload `file` with the view's uploader (`createUploadsPlugin`) and store the URL it answers, only on the block as it was picked from: still in the tree, of its kind and empty (a peer may have set a source, or deleted it, meanwhile).
- `field`: The link field (`{@attach empty.field}`): Enter embeds its `draft`.

## MediaEmptyFailure

Why the last link or upload was not embedded: no source the kind takes
(`invalid`), an inline image over the cap (`inline`), a failed upload.

```ts
export type MediaEmptyFailure = 'invalid' | 'inline' | 'upload';
```

## MediaEmptyOptions

```ts
export type MediaEmptyOptions = {
    empty: Snippet<[
        MediaEmptyController
    ]>;
};
```

- `empty`: An empty image's or media block's markup and its words (Notion's "Add an image", "Embed a video"… and their link panel, a pasted or dropped file's upload, or a readonly view's placeholder): it receives the block's `MediaEmptyController` (`kind` says which: image, embed, bookmark, file, video, audio).

## MediaKind

The media kinds.

```ts
export type MediaKind = 'embed' | 'bookmark' | 'file' | 'video' | 'audio';
```

## MediaLabels

The media kinds' presets, and a file's size.

```ts
export type MediaLabels = {
    embed: string;
    bookmark: string;
    file: string;
    video: string;
    audio: string;
    fileSize: (bytes: number) => string;
};
```

- `fileSize`: A file's size, in bytes.

## MemoryCommentsClient

A memory client, and a sibling sharing its threads as another user (`as`).

```ts
export type MemoryCommentsClient = CommentsClient & {
    as(user: string, options?: {
        moderator?: boolean;
    }): MemoryCommentsClient;
};
```

## MemoryCommentsClientOptions

Options of `createMemoryCommentsClient`.

```ts
export type MemoryCommentsClientOptions = {
    user: string;
    moderator?: boolean;
    now?: () => number;
};
```

- `user`: The user its requests are made as.
- `moderator`: May delete anyone's comment.
- `now`: The clock (default `Date.now`).

## MentionData

What a `mention` atom stores.

```ts
export type MentionData = {
    id: string;
    label: string;
};
```

## MentionItem

A person the mention menu offers: what your app's directory answers.

```ts
export type MentionItem = {
    id: string;
    label: string;
    description?: string;
    avatar?: string;
};
```

- `id`: The person's id in your app: stored in the atom.
- `label`: The name shown in the menu and in the atom.
- `description`: A line under the name in the menu (an email, a role).
- `avatar`: An avatar image URL (http, https or data).

## MentionPluginOptions

```ts
export type MentionPluginOptions = {
    items: (query: string, ctx: TriggerContext) => readonly MentionItem[] | Promise<readonly MentionItem[]>;
    char?: string;
    menu?: Snippet<[
        TriggerMenuController<MentionItem>
    ]>;
};
```

- `items`: The people matching `query` (the text typed after `@`), at once or as a promise.
- `char`: The character that opens the menu (default `@`).
- `menu`: The menu's markup and its words; it renders while `controller.isOpen`, at the caret (without it, nothing shows).

## MenuItemPayload

One row of a chrome list a controller builds for the app's markup: the
same shape in every menu (a table grip's, the code language list's).

```ts
export type MenuItemPayload<T> = {
    item: T;
    id: string;
    selected: boolean;
    run: () => void;
    select: () => void;
    option: Attachment<HTMLElement>;
};
```

- `item`: What the row stands for: a command, a trigger's row, a version.
- `id`: The row's element id (page-unique): the keyboard's owner names the highlighted row by it.
- `selected`: It is the keyboard's row.
- `run`: Run it (pick the row).
- `select`: Make it the keyboard's row (hover).
- `option`: The row's attachment (`{@attach item.option}`): its `id`, `role`, `aria-selected` (or `aria-checked`) and `tabindex`, and the keyboard's row scrolled into view.

## MergedSemantics

The merged config's fields, each present (empty when no config named one).

```ts
export type MergedSemantics = {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
    marks: Record<string, {
        edge?: MarkEdge;
    }>;
    defaultType?: string;
};
```

## mergeSemantics

Several configs as one, deeply frozen, merged field by field (`roles`,
`rendersContent`, `defaultChild`, `marks`, `defaultType`): the room's or
a headless document's roles for an app's own kinds beside the bundled
ones, `mergeSemantics(defaultSemantics, semanticsOf(myKinds))`. A
top-level spread of two configs would keep only the last one's fields.
A kind (or mark, or the default type) two configs declare otherwise
throws `SemanticConflictError`, as a view's conflicting plugin does
on a document; an equal declaration merges.

```ts
declare const mergeSemantics: (...configs: DocumentSemanticsConfig[]) => MergedSemantics;
```

## MetaClientState

```ts
export type MetaClientState = {
    clock: number;
    lastUpdated: number;
};
```

- `lastUpdated`: unix timestamp

## Modifier

A chord's modifier (`mod`: Cmd on Apple platforms, Ctrl elsewhere).

```ts
export type Modifier = 'mod' | 'alt' | 'ctrl' | 'shift';
```

## Modifiers

One to three distinct modifiers, in any order.

```ts
export type Modifiers = {
    [A in Modifier]: A | {
        [B in Exclude<Modifier, A>]: `${A}+${B}` | `${A}+${B}+${Exclude<Modifier, A | B>}`;
    }[Exclude<Modifier, A>];
}[Modifier];
```

## moveTableColumn

Move a listed column to position `to` among them (`table.move-column`):
one `order` patch of `data.columns`; no cell moves, every row shows its
cells in the new order. `ids`: the table.

```ts
declare const moveTableColumn: (doc: Omit<DocumentOperations, "transact">, table: BlockId, column: string | number, to: number) => Prepared;
```

## moveTableRows

Move rows to position `to` among their table's rows
(`table.move-row`): a move. Refused unless every one is a row of the
same table. `ids`: the rows.

```ts
declare const moveTableRows: (doc: Omit<DocumentOperations, "transact">, rows: readonly BlockId[], to: number) => Prepared;
```

## OpResult

The observed outcome of one document operation — the one result
shape every op returns, empty inputs included. `refused`: the op did not
apply and wrote nothing; `noop`: it applied and changed nothing; `applied`:
the transaction wrote. Read from the transaction's effects, never predicted.

```ts
export type OpResult = {
    readonly status: 'refused' | 'noop' | 'applied';
    readonly ids: readonly BlockId[];
    readonly reason?: string;
};
```

- `ids`: What the op is about — created, moved, merge target, or its target; empty unless applied.
- `reason`: Why a refused op refused, when it names a reason (`'id-collision'`).

## OrderPolicy

Island-sealing policy for a walk in document order (see `next`).

```ts
export type OrderPolicy = {
    sealed?: boolean;
};
```

## Overlay

```ts
export declare class Overlay {
    #private;
    layer: HTMLElement | null;
    observe: (node: Element) => () => void;
    add: (measure: Measure) => () => undefined;
    invalidate: () => void;
    mount: <Props extends Record<string, unknown>>(component: Component<Props>, props: Props, name: string, zIndex: number, measure: (host: HTMLElement, origin: DOMRect) => (() => void) | void) => () => void;
    attach: (host: HTMLElement) => () => void;
}
```

- `layer`: The layer: absolutely positioned at its static place right after the host.
- `observe`: Invalidate whenever `node` (a block's element) changes size: what lays the blocks out again with no commit, no scroll and no resize of the host — a font or a stylesheet that loads, a kind's markup drawn later (KaTeX, an image), a host whose height its page fixes — moves the blocks under the chrome.
- `add`: Run `measure` on every invalidated frame, starting with the next one.
- `invalidate`: Position the chrome in the next frame (once, however many invalidations).
- `mount`: Mount `component` in a fixed host of the layer (`data-<name>`), placed by `measure` on every invalidated frame — a measure like any other: it reads (the host, the layer's origin) and returns the host's style writes.
- `attach`: Create the layer after `host`; invalidated by resizes (the host's, its blocks', the window's) and by scrolls of any container.

## PageBlockLinkPayload

What a page block's `link` snippet receives: the page it links to.

```ts
export type PageBlockLinkPayload = {
    block: Block | undefined;
    pageId: string | null;
    title: string | undefined;
    label: string;
    href: string | undefined;
    openable: boolean;
    open: (newTab?: boolean) => void;
    control: Attachment<HTMLElement>;
};
```

- `block`: The page block; `undefined` in a suggestion's preview (it opens nothing).
- `pageId`: The page's id (`null`: the block names none).
- `title`: Its title: your `title` lookup's answer, else the one the block stores.
- `label`: What it reads: its title, else the view's `page.untitled`.
- `href`: The page's URL (`href`), if any: draw an `a`.
- `openable`: A click may open it: it names a page and the plugin has `open`.
- `open`: Open the page (`open`), in a new tab when `newTab`.
- `control`: The link's attachment (`{@attach link.control}`): its marker (`data-edytor-page-link`), the click and middle click that open it; on an `a`, `draggable="false"`; on a `button`, `disabled` when it cannot open and a press that keeps the editor's selection.

## PageLabels

The page block (`createPagePlugin`).

```ts
export type PageLabels = {
    page: string;
    untitled: string;
};
```

- `page`: The "Page" command.
- `untitled`: A page without a title: the block, its plain text and HTML export.

## PageLinkData

What a `pageLink` atom stores.

```ts
export type PageLinkData = {
    id: string;
    title: string;
    href?: string;
    icon?: string;
};
```

## PageLinkItem

A page the page-link menu offers: what your app's search answers.

```ts
export type PageLinkItem = {
    id: string;
    title: string;
    href?: string;
    icon?: string;
};
```

- `id`: The page's id in your app: stored in the atom.
- `title`: The page's title, shown in the menu and in the atom.
- `href`: Where the link goes (absolute, or relative to your app); default `href(page)`.
- `icon`: A short icon (an emoji) shown before the title.

## PageLinkLabels

The `[[` page link atom (`createPageLinkPlugin`).

```ts
export type PageLinkLabels = {
    untitled: string;
};
```

- `untitled`: An atom whose page has no title.

## PageLinkPluginOptions

```ts
export type PageLinkPluginOptions = {
    search: (query: string, ctx: TriggerContext) => readonly PageLinkItem[] | Promise<readonly PageLinkItem[]>;
    href?: (page: PageLinkItem) => string | undefined;
    char?: string;
    menu?: Snippet<[
        TriggerMenuController<PageLinkItem>
    ]>;
};
```

- `search`: The pages matching `query` (the text typed after `[[`), at once or as a promise.
- `href`: A page's URL when the search answers none (`/pages/<id>`, say).
- `char`: The text that opens the menu (default `[[`).
- `menu`: The menu's markup and its words; it renders while `controller.isOpen`, at the caret (without it, nothing shows).

## PagePluginOptions

The page plugin's options: how your app opens, names, links and creates pages.

```ts
export type PagePluginOptions = {
    open?: (pageId: string, how: {
        newTab: boolean;
    }) => void;
    title?: (pageId: string) => string | null | undefined | Promise<string | null | undefined>;
    href?: (pageId: string) => string;
    create?: () => string | {
        pageId: string;
        title?: string;
    } | null | undefined;
    link?: Snippet<[
        PageBlockLinkPayload
    ]>;
};
```

- `open`: Open the page (navigate to it): a click on a page block, or Enter over it selected alone; `newTab` for a modified click (Mod, Shift) or a middle click, which a link (`href`) leaves to the browser.
- `title`: The page's title now, or a promise of it; `null` or `undefined` while unknown (the block shows the title it stores).
- `href`: A URL for the page: the block is a link to it (a modified click opens a new tab), and its HTML export links it.
- `create`: Create a new page and answer its id (and its title): the "Page" command (slash menu, `+`) inserts a block linking to it, then opens it.
- `link`: The block's link to its page (default: Notion's row, a page icon and the title).

## PartialLabels

A view's words (`<Edytor labels>`): any entry of any section, and any
entry of a nested group (`placeholders`, `kinds`) without the others;
`keywords` replaces a command's slash keywords by command id (list the
English ones in it to keep them).

```ts
export type PartialLabels = {
    [K in keyof Labels]?: Overrides<Labels[K]>;
} & {
    keywords?: Partial<Record<string, string[]>>;
};
```

## PlacedThread

A thread as the sidebar places it: its anchor's runs (none: its text was deleted).

```ts
export type PlacedThread = CommentThread & {
    runs: CommentRun[];
};
```

## Placeholder

An empty block's placeholder text: one string, or one per block of the view (`null`: none).

```ts
export type Placeholder = string | ((view: PlaceholderView, edytor: Edytor) => string | null);
```

## PlaceholderView

What a placeholder function receives: the empty block's declared values.

```ts
export type PlaceholderView = {
    type: string;
    data: Readonly<Record<string, unknown>>;
    focused: boolean;
    empty: boolean;
};
```

## Plan

A prepared operation: its steps, their effect, the ids the op
is about, and the document version it was prepared against — valid only
there, applied in the same synchronous turn.

```ts
export type Plan = {
    readonly ids: readonly BlockId[];
    readonly writes: readonly PlanStep[];
    readonly effect: PlanEffect;
    readonly version: number;
    readonly at?: DocPosition;
};
```

- `at`: Where the op leaves the caret, when it decides one (`deleteRange`).

## PlanEffect

What applying a plan does: blocks created, removed (they leave the
document), merged (`[from, into]`), moved (a placement written), retyped
or given new data (`meta`), and the text ranges written. Derived from the
plan's steps; the applied transaction changes exactly this.

```ts
export type PlanEffect = {
    creates: BlockId[];
    removes: BlockId[];
    merges: [
        from: BlockId,
        into: BlockId
    ][];
    moves: BlockId[];
    meta: BlockId[];
    textRanges: TextRange[];
};
```

## PlanStep

One planned write, named by the document operation that
performs it — the name a hook matches. Steps carry everything
their write needs (ranks, marks, offsets), decided at prepare time.

```ts
export type PlanStep = {
    op: 'insertBlocks';
    parent: BlockId | null;
    index: number;
    specs: BlockSpec[];
    ranks: string[];
} | {
    op: 'moveBlocks';
    ids: BlockId[];
    parent: BlockId | null;
    index: number;
    ranks: string[];
} | {
    op: 'deleteBlock';
    id: BlockId;
    marks: BlockId[];
    removes: BlockId[];
} | {
    op: 'splitBlock';
    id: BlockId;
    offset: number;
    length: number;
    newId: BlockId;
    tail: SplitTail;
    parent: BlockId | null;
    rank: string;
} | {
    op: 'mergeBlocks';
    from: BlockId;
    into: BlockId;
    at: number;
    length: number;
} | {
    op: 'setBlockType';
    id: BlockId;
    type: string;
} | {
    op: 'patchData';
    id?: BlockId;
    inlineId?: string;
    offset?: number;
    ops: DataPatch[];
    leaves: LeafWrite[];
} | {
    op: 'insertText';
    id: BlockId;
    offset: number;
    text: string;
    marks?: Record<string, unknown>;
} | {
    op: 'insertInline';
    id: BlockId;
    offset: number;
    atom: InlineSpec;
} | {
    op: 'deleteText';
    id: BlockId;
    offset: number;
    length: number;
} | {
    op: 'removeInline';
    id: BlockId;
    offset: number;
    inlineId: string;
} | {
    op: 'formatRange';
    id: BlockId;
    offset: number;
    length: number;
    marks: Record<string, unknown>;
};
```

## Plugin

Plugin factory function type that creates plugin definitions and operations.

```ts
export type Plugin = (editor: Edytor) => PluginDefinitions & PluginOperations;
```

## PluginDefinitions

Defines the structure of plugin definitions including marks, blocks, inline blocks, and hotkeys.

```ts
export type PluginDefinitions = {
    marks?: Record<string, MarkDefinition | Snippet<[
        MarkSnippetPayload<any>
    ]>>;
    blocks?: Record<string, BlockDefinition | Snippet<[
        BlockSnippetPayload<any>
    ]>>;
    inlineBlocks?: Record<string, InlineBlockDefinition | Snippet<[
        InlineBlockSnippetPayload<any>
    ]>>;
    hotkeys?: Partial<Record<HotKeyCombination, HotKey>>;
    commands?: EditorCommand[];
};
```

## PluginOperations

Defines the available operations and hooks for plugins.

```ts
export type PluginOperations = {
    onBeforeOperation?: <C extends ChangePayload>(payload: C) => C['payload'] | void;
    onAfterOperation?: (payload: AfterOperationPayload) => void;
    onChange?: (value: JSONDoc) => void;
    onDocChange?: (change: DocChange) => void;
    onSelectionChange?: (selection: EdytorSelection) => void;
    placeholder?: Placeholder;
    onEdytorAttached?: (payload: {
        node: HTMLElement;
    }) => (() => void) | void;
    onBlockAttached?: (payload: {
        node: HTMLElement;
        block: Block;
    }) => (() => void) | void;
    onTextAttached?: (payload: {
        node: HTMLElement;
        text: Text;
    }) => (() => void) | void;
    onDeleteSelectedBlocks?: (payload: {
        prevent: Prevent;
        selectedBlocks: Block[];
    }) => void;
    onBeforeInput?: (payload: {
        prevent: Prevent;
        e: InputEvent;
    }) => void;
    onCopy?: (payload: {
        prevent: Prevent;
        e: ClipboardEvent;
    }) => void;
    onCut?: (payload: {
        prevent: Prevent;
        e: ClipboardEvent;
    }) => void;
    onPaste?: (payload: {
        prevent: Prevent;
        e: ClipboardEvent;
    }) => void;
};
```

- `onBeforeOperation`: Called before an operation is executed
- `onAfterOperation`: Called after an operation is executed: `dispatcher.last.status` is set; `operation` narrows `payload`.
- `onChange`: Called when the editor value changes: the same `JSONDoc` shape `<Edytor value>` takes.
- `onDocChange`: Called after every commit that changed the visible document, with what it changed (the change report), without exporting the document.
- `onSelectionChange`: Called when the selection changes
- `placeholder`: The placeholder of an empty block: rendered by a `::before` rule the library ships.
- `onEdytorAttached`: Called when the editor is attached to the DOM; may return a cleanup, run on detach.
- `onBlockAttached`: Called when a block element is attached to the DOM; may return a cleanup.
- `onTextAttached`: Called when a text element is attached to the DOM; may return a cleanup.
- `onDeleteSelectedBlocks`: Before a block selection is removed: Backspace or Delete (a word or line delete too), a cut, the block menu's Delete, and typing, a composition or a paste over it.
- `onBeforeInput`: Called before input is processed
- `onCopy`: Called when a copy event is detected
- `onCut`: Called when a cut event is detected
- `onPaste`: Called when a paste event is detected

## Popup

```ts
export type Popup = {
    id: string;
    haspopup?: 'listbox' | 'menu' | 'dialog';
    active?: string;
    keys?: string;
    opener?: PopupOpener;
};
```

- `id`: The popup element's id.
- `haspopup`: What it is, for `aria-haspopup` (none for a toolbar).
- `active`: Its highlighted row's id, when the root holds the keyboard.
- `keys`: The keys that move the focus from the root into it (`aria-keyshortcuts`).
- `opener`: The handle control that opened it.

## PopupOpener

A handle control that opens a popup: its `+` or its grip.

```ts
export type PopupOpener = {
    block: string;
    control: 'add' | 'grip';
};
```

## Popups

```ts
export declare class Popups {
    #private;
    idOf: (name: string) => string;
    set: (owner: string, popup: Popup | null) => void;
    get current(): Popup | undefined;
    openedBy: (block: string, control: PopupOpener["control"]) => Popup | undefined;
}
```

- `idOf`: This view's element id for `name` (any string: what an IDREF cannot hold is replaced).
- `set`: Publish `owner`'s popup, or withdraw it with `null`.
- `current`: The newest popup open, if any (reactive).
- `openedBy`: The popup the handle `control` of `block` opened, if one is open (reactive).

## prefetch

Keep a document's local copy fresh without opening it (sync once, then close).

```ts
declare const prefetch: typeof prefetchStack;
```

## PrefetchOptions

What `bindProviders`' `prefetch` takes: a websocket sync's target and options.

```ts
export type PrefetchOptions = (WebsocketSyncOptions extends infer O ? O extends unknown ? Omit<O, 'onExpired' | 'persist' | 'disableBc'> : never : never) & {
    timeout?: number;
};
```

## PrefetchResult

What `prefetch` found: whether the room sent the store anything it lacked.

```ts
export type PrefetchResult = {
    updated: boolean;
};
```

## Prepared

`prepare`'s answer: a plan, or the op's refusal.

```ts
export type Prepared = Plan | OpResult;
```

## PresenceOptions

```ts
export type PresenceOptions = {
    share?: PresenceShare;
    throttle?: number;
};
```

- `share`: `'caret'` (default): the selection; `'block'`: the focused block only, so moving inside it publishes nothing; `'none'`: nothing.
- `throttle`: Minimum ms between two presence writes (default `DEFAULT_PRESENCE_THROTTLE`, 50; `0`: every change).

## PresenceSelection

The presence payload: anchors only. A text range publishes its
endpoints in document order with collapsed/reversed; a block set its ids;
an atom its block and id; `none` publishes nothing.

```ts
export type PresenceSelection = {
    start: DocAnchor;
    end: DocAnchor;
    collapsed: boolean;
    reversed: boolean;
} | {
    blocks: BlockId[];
} | {
    atom: string;
    block: BlockId;
};
```

## PresenceShare

What a view shares of its selection: the selection itself, the block holding its focus, or nothing.

```ts
export type PresenceShare = 'caret' | 'block' | 'none';
```

## PresenceWriter

One view's presence writer: the only writer of its key, under the
view's `share` and `throttle`. `select()` hands it the full payload; the
view's teardown calls `clear()`.

```ts
export declare class PresenceWriter {
    #private;
    private readonly awareness;
    private readonly key;
    throttle: number;
    constructor(awareness: AwarenessLike, key: string, { share, throttle }?: PresenceOptions);
    get share(): PresenceShare;
    set share(share: PresenceShare);
    write(payload: PresenceSelection | null, focus?: string | null): void;
    clear(): void;
}
```

- `write`: Publish `payload` (the full selection; `focus`: the block holding its focus) as this view shares it, within the throttle.
- `clear`: Remove this view's entry and drop a held write (the view's teardown).

## Prevent

A hook's `prevent`: veto the gesture, and optionally run `cb` in its place.

```ts
export type Prevent = (cb?: () => void) => void;
```

## PreventionError

A hook's veto as the dispatcher carries it: what `prevent(cb?)` recorded,
and the extension that recorded it. Hooks never see one thrown at them:
`prevent` records and returns. Throwing one from a hook (the
earlier form) still vetoes, for the transition.

```ts
export declare class PreventionError extends Error {
    cb?: () => void;
    by?: unknown;
    constructor(cb?: () => void);
}
```

- `by`: The extension whose hook prevented (set by the dispatcher).

## ProjectedBlock

Canonical projected block — the comparison surface for convergence.

```ts
export type ProjectedBlock = {
    id: BlockId;
    type: string;
    data?: Record<string, unknown>;
    content: ContentItem[];
    children: ProjectedBlock[];
    malformed?: true;
};
```

- `malformed`: Set when the registry node could not be read completely.

## ProjectedDoc

```ts
export type ProjectedDoc = {
    children: ProjectedBlock[];
};
```

## ProtocolMismatch

```ts
export type ProtocolMismatch = {
    expected: number;
    found: number | null;
};
```

## RangeView

What the view knows of a range (view state the document does not hold):
whether it hides a block, and, given `removed`, whether the block stays
hidden once those go (a removed closed toggle's children take its place, shown).

```ts
export type RangeView = {
    hidden?: (id: BlockId, removed?: ReadonlySet<BlockId>) => boolean;
};
```

## ReplaceMatches

What `replaceMatches` (one command, one undo step) writes: each match replaced by `replacement`.

```ts
export type ReplaceMatches = Commands['replaceMatches'];
```

## ResolvedAt

A position as resolved: block ids, or a text range's anchors.

```ts
export type ResolvedAt = Exclude<SuggestionAt, {
    replace: 'selection';
}>;
```

## RichTextLabels

The rich text kinds and marks: their names, the document's own words, placeholders.

```ts
export type RichTextLabels = {
    kinds: {
        paragraph: string;
        heading1: string;
        heading2: string;
        heading3: string;
        toggleHeading1: string;
        toggleHeading2: string;
        toggleHeading3: string;
        bulletedList: string;
        numberedList: string;
        todoList: string;
        toggleList: string;
        callout: string;
        quote: string;
        divider: string;
    };
    marks: {
        bold: string;
        italic: string;
        underline: string;
        strike: string;
        code: string;
    };
    checkbox: string;
    emptyBody: {
        toggle: string;
        callout: string;
    };
    calloutIcon: {
        change: string;
        add: string;
    };
    placeholders: {
        heading: (level: 1 | 2 | 3) => string;
        toggleHeading: (level: 1 | 2 | 3) => string;
        list: string;
        todo: string;
        toggle: string;
        quote: string;
        callout: string;
        caption: string;
        empty: string;
    };
};
```

- `kinds`: Each preset's label: the slash menu, Turn into, the handles' names.
- `marks`: The marks' toolbar buttons (`MarkDefinition.toolbar.label`).
- `checkbox`: A to-do's checkbox.
- `emptyBody`: The hint an empty body shows (an open toggle's, a callout's): a press there starts it.
- `calloutIcon`: A callout's icon button: `change` its icon, `add` one when it has none.
- `placeholders`: `richTextPlaceholder`'s texts.

## RichTextLink

```ts
export type RichTextLink = {
    href: string;
    target?: string;
};
```

## RichTextMark

```ts
export type RichTextMark = 'bold' | 'italic' | 'underline' | 'code' | 'link' | 'strike' | 'superscript' | 'subscript' | 'color' | 'highlight';
```

## richTextOperations

```ts
declare const richTextOperations: (edytor: Edytor) => {
    insertDividerAtSelection: () => import("../..").Block | null;
    removeAllMarksAtRange: () => void;
    setLinkAtRange: (link: RichTextLink) => void;
    removeLinkAtRange: () => void;
    linkText: (text: Text, start: number, end: number, href: string) => void;
    unlinkText: (text: Text, start: number, end: number) => void;
    removeMarkAtRange: (mark: RichTextMark) => void;
    setMarkValueAtRange: (mark: RichTextMark, value: SerializableContent) => void;
    setMarkAtRange: (mark: RichTextMark | (string & {}), value?: SerializableContent) => void;
};
```

## richTextPlaceholder

Notion's placeholders, in the view's `richText` labels (English by
default): headings, lists, to-dos, toggles and quotes name their kind
while empty; a paragraph invites a command only while focused.

```ts
declare const richTextPlaceholder: ({ type, data, focused }: PlaceholderView, edytor?: Edytor) => string | null;
```

## RichTextPluginOptions

```ts
export type RichTextPluginOptions = {
    callout?: {
        icon?: string;
    };
    checkbox?: Snippet<[
        TodoCheckboxPayload
    ]>;
    calloutIcon?: Snippet<[
        CalloutIconPayload
    ]>;
    emptyBody?: Snippet<[
        EmptyBodyPayload
    ]>;
};
```

- `callout`: Callouts' icons (their picker is `createCalloutIconsPlugin`'s).
- `checkbox`: A to-do's checkbox (default: a native `input type="checkbox"`).
- `calloutIcon`: A callout's icon (default: a `button` showing it, or the icon alone where it cannot be picked).
- `emptyBody`: The hint an open toggle's or a callout's empty body shows (default: a `div` with the view's `richText.emptyBody` words).

## safeImageSrc

An image source the block may render: http(s), blob, or an inline image.

```ts
declare const safeImageSrc: (value: unknown) => string | null;
```

## safeMediaSrc

A media file's source (a video, an audio track, an attachment): a
`safeWebUrl`, or a `blob:` URL an upload answered. Never `data:`:
an inline file would be stored in the document and sent in every update.

```ts
declare const safeMediaSrc: (value: unknown) => string | null;
```

## safeWebUrl

A web page's URL a media block may store or render: the link sanitizer's
reading of it (`sanitizeLinkHref`: ASCII control characters and spaces stripped,
tab/newline anywhere), then only an absolute `http:`/`https:` URL with a
host. `null` for anything else: a script or `data:` URL, a `mailto:`, a
relative path, a bare domain.

```ts
declare const safeWebUrl: (value: unknown) => string | null;
```

## SchemaMismatchDetail

Inbound refusal detail — emitted on `'schema-mismatch'` (and mirrored
through `'message-error'` as a `SchemaMismatchError`) when a
received update would write a foreign schema stamp (`unsupported`
version, `foreign` manifest, or a deleted stamp — `unversioned`). The
update is never integrated, persisted, or rebroadcast.

```ts
export type SchemaMismatchDetail = {
    docName: string;
    problem: SchemaProblem;
};
```

## SchemaMismatchError

Error raised by `assertSchema` — carries the detected problem.

```ts
export declare class SchemaMismatchError extends Error {
    readonly docName: string;
    readonly problem: SchemaProblem;
    constructor(docName: string, problem: SchemaProblem);
}
```

## SchemaProblem

A document's relationship to this build's schema:

- `unversioned` — registry state exists but `meta.v` is absent. Someone
  wrote replicated content without running the schema path (a rogue or
  v13-era write). This state must NEVER be persisted/broadcast as a
  document update by a provider.
- `unsupported` — `meta.v` names a version this build does not speak
  (e.g. `99` written by a future build). Content still applies (a replica
  cannot refuse structs it already shares a protocol with), but providers
  surface a `schema-mismatch` signal so operators can detect skew.
- `foreign` — `meta.v` is the supported version but `meta.schema` is
  missing or names a DIFFERENT manifest (`'not-edytor'`, a next-gen name,
  an app-local doc kind). A valid version number alone does not make the
  payload an edytor document — the manifest name is part of the contract
  (the version-only check let foreign `meta.schema` values
  cross the staging boundary).

```ts
export type SchemaProblem = {
    kind: 'unversioned' | 'unsupported' | 'foreign';
    version?: number;
    schema?: unknown;
};
```

- `version`: The observed `meta.v` (undefined for `unversioned`).
- `schema`: The observed `meta.schema` (undefined when absent).

## SelectionPoint

A position within one document version: block id + display offset (an atom counts 1).

```ts
export type SelectionPoint = {
    block: BlockId;
    offset: number;
};
```

## SelectionProjection

```ts
export type SelectionProjection = {
    kind: SelectionValue['kind'];
    start: SelectionPoint | null;
    end: SelectionPoint | null;
    isCollapsed: boolean;
    isReversed: boolean;
    blocks: BlockId[];
    segments: SelectionSegment[];
    isAtStartOfText: boolean;
    isAtEndOfText: boolean;
    isAtStartOfBlock: boolean;
    isAtEndOfBlock: boolean;
    isTextSpanning: boolean;
    isBlockSpanning: boolean;
    islandRoot: BlockId | null;
    voidRoot: BlockId | null;
    readonly marks: Marks;
    readonly content: string;
};
```

- `start`: Endpoints in document order; `null` when no endpoint resolves at this version.
- `blocks`: Blocks from the start block to the end block in document order.
- `segments`: Content parts from the start point's text segment to the end point's.
- `marks`: Lazy: the marks at the caret (the character before it; offset 0 → the first run) or at the range's start edge.
- `content`: Lazy: the selected text (text segments only).

## SelectionSegment

One content part of a block: a text segment (between atoms, possibly empty) or an inline atom.

```ts
export type SelectionSegment = {
    block: BlockId;
    kind: 'text';
    segOrd: number;
    start: number;
    end: number;
    runs: TextRun[];
} | {
    block: BlockId;
    kind: 'inline';
    id: string;
    start: number;
    end: number;
};
```

## SelectionValue

```ts
export type SelectionValue = {
    readonly kind: 'none';
} | {
    readonly kind: 'text';
    readonly anchor: DocAnchor;
    readonly focus: DocAnchor;
    readonly pending?: Marks;
} | {
    readonly kind: 'atom';
    readonly blockId: BlockId;
    readonly atomId: string;
    readonly from: AtomSide;
} | {
    readonly kind: 'blocks';
    readonly ids: readonly BlockId[];
};
```

## SemanticConflictError

Raised when two declarations of one kind's structural facts disagree —
a view's plugin and the document (`adoptSemantics`), or two configs
`mergeSemantics` is given: no replica may silently read another
rule than its peers.

```ts
export declare class SemanticConflictError extends Error {
    readonly subject: string;
    readonly existing: unknown;
    readonly incoming: unknown;
    constructor(subject: string, existing: unknown, incoming: unknown);
}
```

- `subject`: What conflicted (e.g.

## semanticsOf

Kind tables (`type → row`) as one `DocumentSemanticsConfig`, deeply
frozen: `semanticsOf({ embed: { void: true, rendersContent: false } })`.
A row may be a plugin's kind record (`plugin.blocks`): only its
structural fields are read (`void`, `island`, `lines`, `layout`,
`table`, `atomic`, `rendersContent`, `defaultChild`), and a bare snippet is a
plain kind. To add kinds to a bundled config, use `mergeSemantics`.

```ts
declare const semanticsOf: (...tables: Record<string, KindRecord>[]) => {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## setBlockColor

Set `field` of every colourable block of `blocks` to `value` (`null`
removes it: the default), as one command and one undo step; each block
is its own data write (`patchData`), so an extension vetoing one keeps
that block as it was. Answers whether any block changed.

```ts
declare const setBlockColor: (edytor: Edytor, blocks: Iterable<Block>, field: BlockColorField, value: string | null) => boolean;
```

## SlashMenuController

The slash menu: the `/` trigger (`TextTriggerController`: the range, the
query, the keys, the pick), whose rows are the editor's commands, and the
menu a handle's `+` opens with a search field of its own (`addition`).

```ts
export declare class SlashMenuController extends TextTriggerController {
    addition: BlockAddition | null;
    private held;
    constructor(edytor: Edytor);
    protected popupOf(active: string | undefined): Popup;
    keys: Attachment<HTMLElement>;
    protected get count(): number;
    protected admits(block: Block): boolean;
    protected queried(query: string): void;
    get commands(): EditorCommand[];
    private enabled;
    close(): void;
    offer(addition: BlockAddition): void;
    search(query: string): void;
    dismiss(focus?: boolean): void;
    runSelected(): Promise<boolean>;
    run(command: EditorCommand | undefined): Promise<boolean>;
}
```

- `addition`: Opened by a handle's `+` (`BLOCK_ADD_EVENT`): no `/` in the text, the query is the menu's own field, and a picked row adds the block first.
- `popupOf`: A `+`'s menu names its highlighted row from its own field, and tells the `+` it opened it.
- `keys`: The element that holds a `+` menu's keyboard (`{@attach controller.keys}`: its field, or the menu element itself).
- `count`: How many rows the keyboard walks.
- `admits`: A `/` opens the menu in a block a kind can replace (`convertible`).
- `queried`: A query no command matches is prose (a URL, a path), and so is one that opens with whitespace (`yes / no`) or holds only hyphens (`/-`): the menu closes.
- `commands`: Matching commands, grouped (groups in first-seen order): the menu's rows and keyboard order.
- `offer`: Open for a `+`: nothing is added until a row is picked.
- `search`: The `+`'s menu typed in its own field (a query that matches nothing shows "No results").
- `dismiss`: Close without picking.

## SlashMenuOptions

```ts
export type SlashMenuOptions = {
    menu?: Snippet<[
        SlashMenuController
    ]>;
};
```

- `menu`: The menu's markup and its words; it renders while `controller.isOpen` (without it, nothing shows).

## SplitTail

The tail block's type/data, decided once by a split (default: copied from the source).

```ts
export type SplitTail = {
    type: string;
    data?: Record<string, unknown>;
};
```

## storableImageSrc

An image source a block may store: `safeImageSrc`'s, but no
inline image over `MAX_INLINE_IMAGE_BYTES`. Rendering still takes
any safe source, so a document that holds a larger one shows it.

```ts
declare const storableImageSrc: (value: unknown) => string | null;
```

## Suggestion

```ts
export declare class Suggestion {
    #private;
    readonly id: string;
    readonly at: ResolvedAt;
    readonly label: string | undefined;
    constructor(layer: Suggestions, at: ResolvedAt, kind: string, options: SuggestionOptions, status: SuggestionStatus);
    get content(): readonly JSONBlock[];
    get status(): SuggestionStatus;
    get retryable(): boolean;
    get live(): boolean;
    update: (content: SuggestionContent) => void;
    append: (text: string) => void;
    done: () => void;
    accept: () => OpResult;
    discard: () => void;
    retry: () => void;
}
```

- `at`: The position, resolved: `replace: 'selection'` is the selection's blocks or text range.
- `content`: The proposed blocks (reactive).
- `retryable`: An `onRetry` was given: the UI offers "Try again".
- `live`: Listed: not accepted, discarded or dropped.
- `update`: Replace the content (call it on each streamed chunk); no-op once not live.
- `append`: Append streamed text to the last block's text (a new paragraph when there is none); away from an `end`, a blank line starts a new paragraph.
- `done`: The stream ended: `status` is `ready`.
- `accept`: Place the content in the document: one command, one undo step (see `Suggestions`).
- `discard`: Drop it; writes nothing.
- `retry`: Empty it, stream again, and ask the app for another answer (`onRetry`).

## SuggestionAt

Where a suggestion goes. `replace: 'selection'` resolves at creation (see `Suggestion.at`).

```ts
export type SuggestionAt = {
    readonly after: BlockId;
} | {
    readonly before: BlockId;
} | {
    readonly inside: BlockId;
} | {
    readonly end: BlockId;
} | {
    readonly replace: readonly BlockId[] | 'selection' | SuggestionRange;
};
```

## SuggestionBarOptions

```ts
export type SuggestionBarOptions = {
    bar: Snippet<[
        SuggestionBarPayload
    ]>;
};
```

- `bar`: The bar under each block suggestion and its words; its placement is the plugin's.

## SuggestionBarPayload

What a `bar` snippet receives: one block suggestion and its actions.

```ts
export type SuggestionBarPayload = {
    suggestion: Suggestion;
    accept: () => void;
    discard: () => void;
    retry: (() => void) | undefined;
    readonly: boolean;
    mac: boolean;
};
```

- `accept`: Place it in the document and give the editor its focus back.
- `retry`: Ask for another answer (`onRetry`); `undefined` without one.
- `mac`: The view's keys are a Mac's: Mod is ⌘ (name the Accept key with it).

## SuggestionContent

Blocks, or a string: paragraphs split on blank lines (one text run at an `end`).

```ts
export type SuggestionContent = string | JSONBlock[];
```

## SuggestionOptions

```ts
export type SuggestionOptions = {
    id?: string;
    status?: SuggestionStatus;
    label?: string;
    onRetry?: (suggestion: Suggestion) => void;
};
```

- `id`: The suggestion's id (default: minted).
- `status`: Default `ready` with content, `streaming` without.
- `label`: A name for the UI (an app's suggestion bar shows it).
- `onRetry`: Offer "Try again": called by `retry()` once the content is emptied.

## SuggestionRange

A text range, by its anchors (a text selection's).

```ts
export type SuggestionRange = {
    readonly anchor: DocAnchor;
    readonly focus: DocAnchor;
};
```

## Suggestions

The view's suggestions (`edytor.suggestions`).

```ts
export declare class Suggestions {
    #private;
    private edytor;
    revision: number;
    constructor(edytor: Edytor);
    get list(): readonly Suggestion[];
    get latest(): Suggestion | undefined;
    get: (id: string) => Suggestion | undefined;
    add: (at: SuggestionAt, content?: SuggestionContent, options?: SuggestionOptions) => Suggestion;
    clear: () => void;
    prune: () => void;
    at: (id: BlockId) => SuggestionsAt;
    accept: (suggestion: Suggestion) => OpResult;
}
```

- `revision`: Bumped by every change of a suggestion: the surface's passes re-run.
- `list`: Every live suggestion, oldest first (reactive).
- `latest`: The newest one: what the keys act on.
- `add`: Propose `content` at `at`.
- `clear`: Discard every suggestion.
- `prune`: After a commit: a suggestion whose block (or range) died is dropped.
- `at`: The suggestions shown at block `id`.
- `accept`: The one command (`acceptSuggestion`) that writes a suggestion: refused by a readonly view or a vetoing hook (the suggestion stays); else the content goes in with fresh ids, the suggestion goes, and the caret ends the content.

## SuggestionsAt

What a block position shows: before it, after it, as its last children, after its text.

```ts
export type SuggestionsAt = {
    readonly before: readonly Suggestion[];
    readonly after: readonly Suggestion[];
    readonly inside: readonly Suggestion[];
    readonly end: readonly Suggestion[];
    readonly replaced: boolean;
    readonly inPlace: boolean;
};
```

- `replaced`: A `replace` removes this block.
- `inPlace`: It removes only blocks that hold nothing (an empty line): the preview shows in their place.

## SuggestionStatus

```ts
export type SuggestionStatus = 'streaming' | 'ready';
```

## SyncRefusedError

A terminal refusal: the server closed the socket with a policy code
(`isRefusal`), so the next dial would be refused the same way (a
stale generation, another user's replica, a foreign stamp). The provider
stops dialing and the document does not seed.

```ts
export declare class SyncRefusedError extends Error {
    code: number;
    reason: string;
    constructor(code: number, reason?: string);
}
```

- `code`: The close code.
- `reason`: The close reason the server gave, e.g.

## TableAddPayload

What an `add` snippet receives: the `+` under the hovered table (a row at
its end) or beside it (a column at its end). The plugin places it along
the table's edge, as wide (tall) as the table shows; a press on it keeps
the editor's selection.

```ts
export type TableAddPayload = {
    kind: Kind;
    add: () => void;
};
```

- `add`: Add the row or the column.

## tableBlock

A table block in the document JSON: `rows × columns` cells, the table's
`columns` and each cell's `column` set. It names no block id, so a value
holding it seeds the same table every time.

```ts
createDocument({
	value: { children: [tableBlock({ cells: [['Name', 'Role'], ['Ada', 'Eng']], headerRow: true })] }
});
```

```ts
declare const tableBlock: (options?: TableBlockOptions) => JSONBlock;
```

## TableBlockOptions

What `tableBlock` builds.

```ts
export type TableBlockOptions = {
    rows?: number;
    columns?: number;
    cells?: readonly (readonly (string | readonly JSONText[])[])[];
    headerRow?: boolean;
    headerColumn?: boolean;
    widths?: readonly (number | undefined)[];
};
```

- `rows`: Rows (default: the rows of `cells`, else 2).
- `columns`: Columns (default: the widest row of `cells`, else 2).
- `cells`: Each cell's text, by row then column (a string, or rich text).
- `headerRow`: The first row is a header row.
- `headerColumn`: The first column is a header column.
- `widths`: Column widths in pixels, by position (absent: the default width).

## TableChrome

The table's chrome (`table.*`), in the overlay: while the pointer is over
a table of an editable view, a `+` under it (a row at its end) and one
beside it (a column at its end), a grip left of the hovered row and one
above the hovered column, each opening its menu (insert, move, delete),
and a resize band on each column's right edge. Positions are read in the
overlay's measure, once per frame. A resize drags a view-only preview
width (`width`), which the table's grid reads; the release writes the
column's `data.columns[i].width` (one command, one undo step), Escape
drops the preview. Every action is a table command (`table.ts`): refused
when the view is readonly.

What a `menu` snippet receives: the open menu (`menu`), its rows
(`items`, each a `MenuItemPayload`), the keyboard's row (`index`), the
attachments that give it the keys and the ARIA (`popup` and `keys` on
the menu's element, `option(index)` on each row) and `close()`.
A grip's own attachment is its payload's `grip`.

```ts
export declare class TableChrome extends ChromeList {
    #private;
    menu: TableMenu | null;
    constructor(edytor: Edytor, columnWidth: number, minColumnWidth: number);
    protected get count(): number;
    get readonly(): boolean;
    toggle: (kind: Kind) => void;
    get isOpen(): boolean;
    close: () => void;
    get items(): TableMenuItem[];
    option(index: number): Attachment<HTMLElement>;
    keys: Attachment<HTMLElement>;
    gripOf: (kind: Kind) => Attachment<HTMLElement>;
}
```

- `menu`: What the open grip menu acts on (`null`: none is open).
- `count`: How many rows the keyboard walks.
- `readonly`: The view is readonly (reactive): the chrome shows nothing.
- `toggle`: Open the `kind` grip's menu on the row or column it stands for, its first row that can run the keyboard's, or close it when it is open there (a grip's click).
- `isOpen`: Whether a menu is open (reactive).
- `close`: Close the menu; the editor takes the keys back.
- `items`: The open menu's rows for your `menu`: each the row shape every menu shares (`run` runs it and closes the menu, `select` makes it the keyboard's, `option` its attributes), whether it can run now (`disabled`) and whether it deletes (`danger`).
- `option`: The attachment of the open menu's row at `index` (`{@attach chrome.option(index)}`).
- `keys`: The element that holds the menu's keyboard (`{@attach chrome.keys}`, the menu's own): it takes the focus when it mounts and names the keyboard's row (`aria-activedescendant`); ArrowDown and ArrowUp walk the rows that can run (around the ends), Home and End reach the first and last, Enter or Space runs the row, Escape and Tab close the menu and give the keys back to the editor.
- `gripOf`: The `kind` grip's attachment (`{@attach}` it on a grip's element; a `grip` snippet's payload carries it): in an editable view, dragging it moves its row or column within its table (`table.drag`); a click with no drag opens or closes its menu; ArrowDown opens the menu, Escape closes it and gives the keys back to the editor.

## TableGripPayload

What a `grip` snippet receives: the grip of the hovered row (left of the
table) or column (above it). Put `{@attach grip}` on its element (a
`button`): it drags the row or the column, a click opens its menu,
ArrowDown opens it too and Escape closes it, and it carries the ARIA
(`aria-haspopup`, `aria-expanded`, `aria-controls` while its menu is
open; name it). The plugin centers it where the grip stands.

```ts
export type TableGripPayload = {
    kind: Kind;
    id: string;
    index: number;
    expanded: boolean;
    dragging: boolean;
    toggle: () => void;
    grip: Attachment<HTMLElement>;
    chrome: TableChrome;
};
```

- `id`: The row's block id, or the column's id.
- `index`: Its position among the table's rows (columns).
- `expanded`: Its menu is open.
- `dragging`: It is being dragged.
- `toggle`: Open its menu, or close it when it is open (what a click does).
- `grip`: The grip's behavior and ARIA: `{@attach grip}`.
- `chrome`: The table's chrome (its menu, its actions).

## TableLabels

The table block (`createTablePlugin`).

```ts
export type TableLabels = {
    table: string;
    headerRow: string;
    headerColumn: string;
    insertRowAbove: string;
    insertRowBelow: string;
    insertColumnLeft: string;
    insertColumnRight: string;
    deleteRow: string;
    deleteColumn: string;
    resize: string;
    emptyCell: string;
};
```

- `table`: The "Table" command, and a table's accessible name.
- `headerRow`: The block menu's switches.
- `insertRowAbove`: The block menu's rows: at the row and column of the cell the caret was in (else the last ones).
- `resize`: A column's resize band.
- `emptyCell`: A padded cell (a row showing none in a column): its first press creates it.

## TableMenu

What a grip's menu acts on: a row (its block id) or a column (its id) of a table.

```ts
export type TableMenu = {
    kind: Kind;
    table: string;
    id: string;
};
```

## TableMenuItem

One row of a grip's menu, for your `menu`: the row shape every menu
shares (`item` is the row, `run` runs it and closes the menu), with
whether it can run now and whether it deletes.

```ts
export type TableMenuItem = MenuItemPayload<TableMenuRow> & {
    disabled: boolean;
    danger: boolean;
};
```

## TableMenuRow

A row of a grip's menu: `insert-above`, `insert-below`, `move-up`,
`move-down`, `delete-row`; `insert-left`, `insert-right`, `move-left`,
`move-right`, `delete-column`.

```ts
export type TableMenuRow = {
    id: string;
};
```

## TablePluginOptions

```ts
export type TablePluginOptions = {
    rows?: number;
    columns?: number;
    columnWidth?: number;
    minColumnWidth?: number;
    grip?: Snippet<[
        TableGripPayload
    ]>;
    menu?: Snippet<[
        TableChrome
    ]>;
    add?: Snippet<[
        TableAddPayload
    ]>;
};
```

- `rows`: The rows a new table has (default 3).
- `columns`: The columns a new table has (default 3).
- `columnWidth`: A column's width when it sets none, in pixels (default 120, Notion's).
- `minColumnWidth`: The narrowest a column may be resized to, in pixels (default 48).
- `grip`: A row's or a column's grip: it renders centered where the grip stands (left of the hovered row, above the hovered column; without it, no grip).
- `menu`: A grip's menu: it renders under its grip while `chrome.menu` is set (without it, nothing shows).
- `add`: The `+` under the table (a row) and beside it (a column): it renders along that edge (without it, no `+`).

## Text

An id-only, read-only text handle: the `ordinal`-th text segment of block
`blockId` — no identity across commits beyond that position. Getters read
the document index; writes are the view's operations in block offsets
(`edytor.insertText(text.parent.id, text.segStart + offset, …)`). `node`
is the element that renders the segment (a Surface fact). A reactive
reader of a getter depends on the block's cell, so it re-runs when a
commit changes the block's content.

```ts
export declare class Text {
    #private;
    readonly: boolean;
    readonly edytor: Edytor;
    readonly blockId: string;
    readonly ordinal: number;
    readonly id: string;
    node: HTMLElement | undefined;
    constructor(edytor: Edytor, blockId: string, ordinal: number);
    get parent(): Block;
    get index(): number;
    get value(): JSONText[];
    get stringContent(): string;
    get isEmpty(): boolean;
    get endsWithNewline(): boolean;
    get length(): number;
    get segStart(): number;
    get isInDocument(): boolean;
    refreshFromModel: () => void;
    getMarksAtRange: (start: number, end: number) => JSONText[];
}
```

- `ordinal`: The segment's position among its block's text segments.
- `index`: Position among the block's content parts.
- `stringContent`: The segment's text.
- `segStart`: Display offset of this segment inside its block's content (atoms count 1).
- `isInDocument`: The segment is shown by a live block.
- `refreshFromModel`: Re-create this block's text elements from its cell — the repair of a DOM the browser or a foreign script changed (typing and model commits never remount).
- `getMarksAtRange`: The runs of `[start, end)` (segment offsets), each with a copy of its marks: the MODEL runs (`value`), never the rendered ones — a kind's `transformText` decorations (code tokens) are no marks of the text.

## TextAnchor

CRDT-stable anchor for a text position — `{b}` is the home block id of
the BACKING text the bound atom lives in (not necessarily the block that
displays it — merges/splits reroute display while the anchor stays on
the same atoms), `a` the engine anchor carrying the endpoint affinity in
its `a` field (`a < 0` = left, `a >= 0` = right). JSON-serializable;
replaces the v13 `RelativePosition`. Same wire shape as `DocAnchor` on
the facade (`facade.anchorAt`/`facade.resolveAnchor`).

```ts
export type TextAnchor = {
    b: string;
    a: Anchor;
};
```

## TextRange

A text range an op writes, in display offsets before the write.

```ts
export type TextRange = {
    block: BlockId;
    offset: number;
    length: number;
};
```

## TextRuleContext

What an input rule's `replace` and a trigger's `onPick` receive: where the
matched text is, in block offsets (the offsets `edytor.document.facade`
takes, an inline atom counting 1).

```ts
export type TextRuleContext = {
    edytor: Edytor;
    block: Block;
    from: number;
    to: number;
    caret: (offset: number) => void;
};
```

- `block`: The block the text is in.
- `from`: Where the matched text starts.
- `to`: Where it ends in the document: the caret, for an input rule (the typed text that completed the match is not written yet); the query's end, for a trigger.
- `caret`: Set the command's result caret at `offset` of `block` (block offsets).

## textToBlocks

`text` as blocks (see the module comment).

```ts
declare const textToBlocks: (text: string, { markdown, type }?: TextToBlocksOptions) => JSONBlock[];
```

## TextToBlocksOptions

```ts
export type TextToBlocksOptions = {
    markdown?: boolean;
    type?: string;
};
```

- `markdown`: Read markdown (default `true`); `false` keeps every character as text.
- `type`: The kind of a paragraph (default `paragraph`).

## ThreadComment

One comment of a thread.

```ts
export type ThreadComment = {
    id: string;
    author: string;
    body: string;
    createdAt: number;
};
```

- `author`: The verified user who wrote it.
- `body`: Plain text (line breaks kept).
- `createdAt`: ms since the epoch.

## TocEmptyPayload

What a table of contents' `empty` snippet receives.

```ts
export type TocEmptyPayload = {
    label: string;
};
```

- `label`: The view's words for it (`toc.empty`).

## TocEntryPayload

What a table of contents' `entry` snippet receives: one heading.

```ts
export type TocEntryPayload = {
    id: string;
    level: number;
    depth: number;
    text: string;
    label: string;
    go: () => void;
    control: Attachment<HTMLElement>;
};
```

- `id`: The heading's block id.
- `level`: Its level (`headingLevel`'s answer, 1 the outermost).
- `depth`: How many steps in it sits: its level less the shallowest shown.
- `text`: Its text (`''` when it has none).
- `label`: What it reads: its text, else the view's `toc.untitled`.
- `go`: Scroll its heading into view (opening the closed toggles it sits in).
- `control`: The entry's attachment (`{@attach entry.control}`): its marker (`data-edytor-toc-entry`), a press that keeps the editor's selection, and a click that scrolls to it.

## TocHeadingLevel

A block's level in the table of contents (1 the outermost), or `null`
when it is not a heading: read from its kind and data.

```ts
export type TocHeadingLevel = (block: {
    type: string;
    data: Readonly<Record<string, unknown>>;
}) => number | null;
```

## TocLabels

The table of contents block (`createTocPlugin`).

```ts
export type TocLabels = {
    toc: string;
    untitled: string;
    empty: string;
};
```

- `toc`: The preset, and the block's accessible name.
- `untitled`: An entry for an empty heading.
- `empty`: The hint shown with no heading.

## TocPluginOptions

The table of contents plugin's options.

```ts
export type TocPluginOptions = {
    headingLevel?: TocHeadingLevel;
    entry?: Snippet<[
        TocEntryPayload
    ]>;
    empty?: Snippet<[
        TocEmptyPayload
    ]>;
};
```

- `headingLevel`: Which blocks the table lists, and how deep: by default the rich text headings and toggle headings, by their `data.level` (`h1` 1, `h2` 2, any other 3, as they are drawn).
- `entry`: One entry (default: a `button` with the heading's text, one nesting step in per level under the shallowest).
- `empty`: What shows while the document has no heading (default: a `p` with the view's `toc.empty`).

## TodoCheckboxPayload

What a to-do's `checkbox` snippet receives.

```ts
export type TodoCheckboxPayload = {
    readonly block: Block | undefined;
    readonly checked: boolean;
    readonly label: string;
    toggle: () => void;
    readonly control: Attachment<HTMLElement>;
};
```

- `block`: The to-do; `undefined` in a suggestion's preview (its box writes nothing).
- `checked`: It is checked: its `data.checked` (reactive).
- `label`: The checkbox's accessible name (the view's `richText.checkbox`).
- `toggle`: Check or uncheck it: one data write, refused in a readonly view.
- `control`: The control's attachment (`{@attach box.control}`), on an `input type="checkbox"` (give it `checked={box.checked}`) or a `button` (it gets `role="checkbox"` and `aria-checked`): its marker (`data-edytor-todo-checkbox`), `contenteditable="false"`, its `aria-label`, a press that keeps the editor's selection and a click that toggles the to-do.

## ToolbarController

```ts
export declare class ToolbarController {
    #private;
    private edytor;
    private shown;
    linkUrl: string;
    panel: "color" | "link" | "turn" | null;
    hovered: HTMLAnchorElement | null;
    constructor(edytor: Edytor);
    get isVisible(): boolean;
    get barId(): string;
    get linkFieldId(): string;
    popup: Attachment<HTMLElement>;
    keys: Attachment<HTMLElement>;
    linkField: Attachment<HTMLInputElement>;
    release(): void;
    updateFromSelection(selection?: EdytorSelection): void;
    setLinkUrl(value: string): void;
    get marks(): {
        label: string;
        icon: string;
        run?: (edytor: Edytor) => void;
        mark: string;
    }[];
    isActive(mark: string): boolean;
    get kinds(): KindRow[];
    get currentKind(): KindRow | undefined;
    togglePanel(panel: 'turn' | 'link' | 'color'): void;
    takeFieldFocus(): boolean;
    openLinkPanel(): boolean;
    closePanel(focus?: boolean): void;
    get card(): boolean;
    get hoveredHref(): string;
    hover(anchor: HTMLAnchorElement | null): void;
    openHovered(): void;
    editHovered(): void;
    removeHovered(): void;
    turnInto(kind: KindRow): void;
    setColor(mark: 'color' | 'highlight', value: string | null): void;
    toggleMark(mark: string): void;
    applyLink(): void;
    removeLink(): void;
}
```

- `panel`: The open panel: the kind menu, the link field or the colors.
- `hovered`: The link under the pointer, for the link card (`link.card`).
- `barId`: The bar's element id (page-unique), for the root's `aria-controls`.
- `linkFieldId`: The link panel's field id (page-unique): a `label`'s `for`.
- `popup`: The bar (`{@attach controller.popup}`, named by your markup): it takes the bar's id, its `toolbar` role unless it has its own, the placement's mark (`data-edytor-toolbar-bar`, which the toolbar's shortcut also reads), and is published to the view's root (`edytor.popups`, with that shortcut as its `aria-keyshortcuts`) while it is in the page.
- `keys`: The bar's keys (`{@attach controller.keys}`, WAI-ARIA toolbar): one tab stop, the control it last held (`tabindex="0"`, every other `-1`); the arrows, Home and End walk its controls, Escape gives the focus back to the editor over the held selection (`release`).
- `linkField`: The link panel's field (`{@attach controller.linkField}`): its id (`linkFieldId`), the focus when the panel was just opened (Mod+K, Edit, the Link button), Enter applies the link and Escape closes the panel, both giving the editor its focus and the selection back.
- `release`: Give the keyboard back to the editor (Escape in the bar), over the held selection.
- `marks`: The mark records declaring a toolbar button, in registration order.
- `isActive`: Whether `mark` covers every character of the selection (its button shows pressed).
- `kinds`: The kinds the selection's block may turn into (conversions that keep its content).
- `currentKind`: The row naming the selection's block, for the kind button's label.
- `takeFieldFocus`: Whether the link field mounting now takes the focus (once per opening).
- `openLinkPanel`: Mod+K (`link.mod-k`): the link panel over the selected text, or, at a caret inside a link, over that link, selected first.
- `closePanel`: Close the open panel.
- `card`: The link card shows: a link is hovered in an editable view.
- `hoveredHref`: The hovered link's URL, as stored (the card shows it).
- `hover`: Show the card for `anchor`, or hide it (`null`).
- `openHovered`: The card's Open: the hovered link in a new tab.
- `editHovered`: The card's Edit: select the hovered link and open the link panel on it.
- `removeHovered`: The card's Remove: unlink the whole hovered link (one undo step); the selection stays.
- `turnInto`: Convert every block the selection touches (Notion), as one undo step.
- `setColor`: Set (or clear, with `null`) the text color or background of the selection.

## ToolbarOptions

```ts
export type ToolbarOptions = {
    toolbar?: Snippet<[
        ToolbarController
    ]>;
    card?: Snippet<[
        ToolbarController
    ]>;
};
```

- `toolbar`: The toolbar's markup; it renders while `controller.isVisible`, placed above the selection (without it, nothing shows).
- `card`: The card shown under a hovered link (`link.card`): it renders while `controller.card`, just below the hovered link (`controller.hoveredHref`, and `openHovered`, `editHovered`, `removeHovered`); without it, no card.

## Trigger

A character typed at a text's start or after whitespace opens a menu at
the caret (`@` for people, `[[` for pages); the text typed after it is
the query. Picking a row replaces the trigger and its query (`onPick`).
The menu's keys are the editor's while it is open: &lt;kbd>↑&lt;/kbd>/&lt;kbd>↓&lt;/kbd>
move, &lt;kbd>Enter&lt;/kbd> picks, &lt;kbd>Escape&lt;/kbd> closes it and keeps the text.

```ts
export type Trigger<T = any> = {
    char: string;
    items: (query: string, ctx: TriggerContext) => readonly T[] | Promise<readonly T[]>;
    onPick: (item: T, ctx: TriggerContext) => unknown;
    label?: (item: T) => string;
    key?: (item: T) => string;
    menu?: Snippet<[
        TriggerMenuController<T>
    ]>;
    enabled?: (block: Block) => boolean;
};
```

- `char`: The text that opens the menu (one or two characters: `@`, `[[`).
- `items`: The rows for `query`, at once or as a promise (a search); the newest query wins.
- `onPick`: Write the picked row: the trigger and its query (`from` to `to`) are removed in the same plan as the first operation it runs (one undo step; a refusal keeps them).
- `label`: A row's label: default the item itself when it is a string, else its `label`, `title` or `name`.
- `key`: A row's key, which keeps its markup across queries: default its `id`, else its label.
- `menu`: The menu's markup and its words; it renders while `controller.isOpen`, placed at the caret (without it, nothing shows).
- `enabled`: Whether the menu may open in `block` (default: a block that is not void and not in a code block).

## TriggerContext

What a trigger's `items` and `onPick` receive.

```ts
export type TriggerContext = TextRuleContext & {
    query: string;
};
```

## TriggerMenuController

A plugin's trigger (`triggers`): rows from its `items(query)`, a pick its `onPick`.

```ts
export declare class TriggerMenuController<T = unknown> extends TextTriggerController {
    #private;
    readonly trigger: Trigger<T>;
    items: readonly T[];
    loading: boolean;
    constructor(edytor: Edytor, trigger: Trigger<T>, owner: string);
    protected get count(): number;
    protected admits(block: Block): boolean;
    labelOf: (item: T) => string;
    keyOf: (item: T) => string;
    protected queried(query: string): void;
    private answered;
    close(): void;
    pickSelected(): Promise<unknown>;
    pick(item: T): Promise<unknown>;
}
```

- `items`: The rows for the query (the newest answered; a pending search keeps the last).
- `loading`: A search for the query is pending.
- `count`: How many rows the keyboard walks.
- `admits`: Whether a trigger typed in `block` opens the menu.
- `queried`: The query changed (or was set again): close the menu, or load its rows.
- `pickSelected`: Pick the keyboard's row.
- `pick`: Replace the trigger and its query with `item` (`onPick`).

## TriggerRange

Where the trigger is now: its text, the trigger's start and the query's end in it.

```ts
export type TriggerRange = {
    text: Text;
    triggerStart: number;
    queryEnd: number;
};
```

## turnCommands

The commands a Turn into over `blocks` (as clicked) offers besides the
kinds (`EditorCommand.turnsInto`), as rows that run them: "3 columns" over
three sibling blocks.

```ts
declare const turnCommands: (edytor: Edytor, blocks: Block[]) => KindRow[];
```

## UndecodableUpdateError

Raised when a candidate update cannot even be decoded or integrated —
refused before the schema question is ever asked. Distinct from the
schema refusals: this payload was never document-shaped.

```ts
export declare class UndecodableUpdateError extends Error {
    readonly docName: string;
    readonly cause: unknown;
    constructor(docName: string, cause: unknown);
}
```

- `docName`: What the update was offered as (e.g.

## UnsupportedDocError

Error raised by `assertUsableDoc` — the doc cannot host a v14 facade:
a foreign engine object (a v13 `yjs` Doc — a different CRDT implementation
entirely).

```ts
export declare class UnsupportedDocError extends Error {
    readonly kind: 'foreign';
    constructor(kind: 'foreign');
}
```

## Uploader

Upload a file and answer its URL. `report.progress` (optional to call)
draws the block's bar; without it the bar is indeterminate.

```ts
export type Uploader = (file: File, report: UploadReport) => Promise<string>;
```

## UploadReport

What an `upload` may report while it runs.

```ts
export type UploadReport = {
    progress(fraction: number): void;
};
```

- `progress`: The share sent so far, `0` to `1`: the block shows it as a bar.

## UploadsOptions

```ts
export type UploadsOptions = {
    upload: Uploader;
};
```

- `upload`: Upload a file and answer its URL: an empty image or media block's Upload, and the pasted and dropped files a kind takes, each placed as a block of that kind at once (one undo step) and filled with the URL this answers (the block shows the progress it reports).

## UrlPasteController

Pasting a bare URL on an empty line (Notion): the URL lands as a link at
once (one `insertFromPaste` step, its own undo step) and a menu under the
line offers to keep it ("Link", the highlighted row) or to turn the line
into a kind an offer names (`Embed`, `Bookmark`), one `setBlock` command
(its own undo step, which gives the link back). Anything but a pick
closes the menu and keeps the link: Escape, a press outside, an
operation, a caret that leaves the URL's end, readonly.

One controller per view, shared by the plugins that make offers: the
first one listed carries its hooks (`urlPaste`).

```ts
export declare class UrlPasteController extends ChromeList {
    #private;
    readonly offers: UrlPasteOffer[];
    open: {
        block: string;
        url: string;
        options: UrlPasteOption[];
    } | null;
    constructor(edytor: Edytor);
    protected get count(): number;
    get readonly(): boolean;
    close: () => void;
    pick: (option?: UrlPasteOption | undefined) => void;
}
```

- `open`: The open menu: the line, the URL, the rows.
- `count`: How many rows the keyboard walks.
- `pick`: Pick a row: keep the link, or turn the line into the offer's kind.

## UrlPasteOffer

A kind a pasted URL can turn its line into (the embed's, the bookmark's).

```ts
export type UrlPasteOffer = {
    type: string;
    icon: string;
    data: (url: string) => Record<string, SerializableContent> | null;
    created?: (block: Block, url: string) => void;
};
```

- `type`: The kind the line becomes.
- `data`: The block's data for `url`, or `null` when this kind takes no such URL (no provider plays it).
- `created`: After the line became the kind (a bookmark's unfurl).

## UrlPasteOption

One row of the menu: keep the link (`link`), or an offer (its kind).

```ts
export type UrlPasteOption = {
    id: string;
    icon: string;
    offer?: UrlPasteOffer;
};
```

## useEdytor

```ts
declare const useEdytor: () => Edytor;
```

## VersionChange

How a block of a version differs from the current document.

```ts
export type VersionChange = 'added' | 'removed' | 'changed';
```

## versionDiff

The block-level diff of `version` against `current`: which blocks were
added, removed or changed since the version, and the preview showing
them all. Neither input is modified.

```ts
declare const versionDiff: (version: JSONDoc, current: JSONDoc) => VersionDiff;
```

## VersionDiff

What `versionDiff` answers.

```ts
export type VersionDiff = {
    preview: JSONDoc;
    changes: Map<string, VersionChange>;
    added: number;
    removed: number;
    changed: number;
};
```

- `preview`: The version, with each block added since inserted where the current document has it: after its nearest earlier sibling the preview holds under the same parent, else first under that parent.
- `changes`: Each differing block's change, by block id.
- `added`: Blocks added since the version.
- `removed`: Blocks removed since the version.
- `changed`: Blocks changed since the version.

## VersionHistory

A view of a room's versions, made in a component's script (its effects
list the versions, and follow the live document's commits). Read its
state reactively; `select`, `restore`, `undo` and `refresh` act.

```ts
export declare class VersionHistory {
    #private;
    versions: HistoryVersion[] | null;
    selected: string | null;
    shown: JSONDoc | null;
    reading: boolean;
    busy: boolean;
    undoable: boolean;
    status: VersionHistoryStatus;
    failed: boolean;
    highlight: boolean;
    constructor(options: VersionHistoryOptions);
    diff: import("./diff.js").VersionDiff | null;
    preview: JSONDoc | null;
    previewKey: string | null;
    changes: ReadonlyMap<string, VersionChange>;
    previewPlugins: Plugin[];
    selectedVersion: HistoryVersion | null;
    refresh: () => Promise<void>;
    select: (key: string | null) => Promise<void>;
    restore: () => Promise<void>;
    undo: () => Promise<void>;
    dateOf: (entry: HistoryVersion) => string;
    timeOf: (entry: HistoryVersion) => string;
    option: (index: number) => Attachment<HTMLElement>;
    keys: Attachment<HTMLElement>;
}
```

- `versions`: The versions, newest first (`null` while the first list loads).
- `selected`: The previewed version's key.
- `shown`: The previewed version's JSON, once read (`null` while it loads or when it is gone).
- `reading`: The previewed version is being read.
- `busy`: A restore or an undo is in flight.
- `undoable`: A restore this view applied, which `undo` can take back.
- `status`: The last outcome, to announce (`aria-live`) in your words.
- `failed`: The last outcome failed.
- `highlight`: Highlight the blocks added, removed and changed since the version.
- `diff`: The previewed version against the live document: its counts and the changed blocks.
- `preview`: What the preview renders: the version, with the blocks removed since when highlighting.
- `previewKey`: A key for the preview: it remounts when what it renders changes (`value` is read once).
- `changes`: The blocks changed since the version, by id, while highlighting (else none).
- `previewPlugins`: The preview's plugins: the highlights of the changes (read live), then the app's.
- `selectedVersion`: The previewed version, as listed.
- `refresh`: List the versions again; keeps the selection when it is still listed, else selects the newest.
- `select`: Preview the version `key` (`null`: none).
- `restore`: Restore the previewed version (the room writes it as one change).
- `undo`: Take back the restore this view applied.
- `dateOf`: A version's day (`Oct 8, 2026`), in `locale`; its half of the day is `entry.slot`.
- `timeOf`: When a version was saved, in `locale`.
- `option`: The attachment of the version at `index`'s row (`{@attach history.option(index)}`, on its focusable element): `role="option"`, its id, `aria-selected`, the list's one tab stop when it is selected, else the first.
- `keys`: The list's attachment (`{@attach history.keys}`): the arrows, Home and End move the selection and the focus.

## VersionHistoryOptions

```ts
export type VersionHistoryOptions = {
    client: HistoryClient;
    document?: EdytorDocument;
    plugins?: Plugin[];
    locale?: string | string[];
    id?: string;
    onRestore?: (result: HistoryRestoreResult) => void;
    onUndo?: (result: HistoryUndoResult) => void;
    onError?: (error: unknown) => void;
};
```

- `client`: Where the versions come from: `createHistoryClient(…)`, or any object with `list`, `read`, `restore` and `undo`.
- `document`: The live document the preview is compared with.
- `plugins`: The preview's plugins: the ones the live view uses, so every kind renders as it does there.
- `locale`: The locale dates and times are written in (default the browser's).
- `id`: The id prefix of the list's rows (page-unique: `$props.id()`).
- `onRestore`: After a restore the room answered.
- `onUndo`: After an undo the room answered.
- `onError`: A request failed (`status` is `denied` or `failed`).

## VersionHistoryStatus

The last outcome to announce (`''`: none): a restore in flight or done
(`unchanged`: the page already matched), the previewed version gone
(`unavailable`), an undo done or with nothing to undo, a request refused
(`denied`: a `401` or `403`) or failed. Your panel says it in its words.

```ts
export type VersionHistoryStatus = '' | 'restoring' | 'restored' | 'unchanged' | 'unavailable' | 'undone' | 'nothingToUndo' | 'denied' | 'failed';
```

## WebsocketSync

A websocket sync; `persistName` names its local store (for `clearDocument`), unset without one.

```ts
export type WebsocketSync = EdytorSync & {
    persistName?: string;
};
```

## WebsocketSyncOptions

```ts
export type WebsocketSyncOptions = WithoutPolyfill<CrdtWebsocketSyncOptions> & {
    WebSocketPolyfill?: typeof WebSocket;
};
```

## WebsocketTarget

Where the socket dials: `<server>/<room>`, the names `<Edytor server room>` uses.

```ts
export type WebsocketTarget = {
    server: string;
    room: string;
};
```

- `server`: The sync server's base URL (`wss://…/rooms`).
- `room`: The room (document) id.

## withRulesAndTriggers

`plugin` with `inputRules` (text typed at the caret that a rule
replaces: markdown, `:smile:` → 😄) and `triggers` (characters that open
a menu of rows at the caret: `@` mentions) run through its own hooks.
A plugin declares them by returning this:
`(edytor) => withRulesAndTriggers(edytor, { inlineBlocks }, { triggers: [mention] })`.

```ts
declare const withRulesAndTriggers: (edytor: Edytor, plugin: InitializedPlugin, { inputRules, triggers }: {
    inputRules?: InputRule[];
    triggers?: Trigger[];
}) => InitializedPlugin;
```

## wrapBlocks

Wrap `blocks` in a new layout of `columns` items (default one per block),
at the first one's place (`layout.wrap`, Notion's Turn into N columns): the
blocks one per item, then one empty block per further item; the document's
`wrapInLayout`, one undo step. The blocks stay selected. Answers whether
it applied.

```ts
declare const wrapBlocks: (edytor: Edytor, blocks: Block[], kind?: string, columns?: number) => boolean;
```

## wrappable

Whether `blocks` may be wrapped in a new layout of `columns` items
(`layout.wrap`: sibling blocks, none a layout, a column or a block holding
one, at a place a layout fits and outside any column; default one item per
block, at least two).

```ts
declare const wrappable: (edytor: Edytor, blocks: Block[], kind?: string, columns?: number) => boolean;
```
