/** * The binding itself: a `LoroDoc` and an `EditorState`, kept equal. * * Two directions, each with one rule that took a spike to learn. * * **ProseMirror → Loro** happens in `appendTransaction`, and is a * reconciliation (`write.ts`), not a rewrite. Every write is stamped with the * `PM_ORIGIN` commit origin, which is the only thing that distinguishes "this * editor already knows" from "this editor must be told" — `event.by === 'local'` * does not, because an undo, a restore, and any `handle.transact()` from app * code are all local and all have to reach the editor. * * **Loro → ProseMirror** rebuilds the projection (reusing every untouched * subtree by reference), diffs it into the **minimal** set of steps * (`diff.ts`), and restores the caret from an anchor minted *before* the * import (`cursor.ts`) — in the same transaction, not a `setTimeout` later. */ import type { Node as PmNode, Schema } from 'prosemirror-model'; import { Plugin, PluginKey, type EditorState } from 'prosemirror-state'; import type { Transaction } from 'prosemirror-state'; import type { LoroDoc } from 'loro-crdt'; import type { Actor } from '../types.js'; import { LoroPmMapping, type CrdtBinding } from './types.js'; /** * The editor surface the binding drives. * * An `EditorView` satisfies it as-is. It is an interface rather than the view * itself so the binding can be exercised — and reasoned about — without a DOM, * which is what the convergence tests do. */ export interface PmHost { readonly state: EditorState; dispatch(tr: Transaction): void; readonly isDestroyed?: boolean; } export interface LoroSyncOptions { /** The open document. A `CrdtHandle` from `/client` satisfies this. */ crdt: CrdtBinding; /** Recorded on updates this editor produces. Defaults to the client's. */ actor?: Actor; } export interface LoroSyncState { binding: LoroPmBinding; } /** Meta set on every transaction the binding dispatches for a remote change. */ export interface LoroSyncMeta { remote: true; } export declare const loroSyncKey: PluginKey; /** * Drive a `LoroDoc` from a bare document, with no client, transport or * persistence behind it. * * For a Durable Object deriving a `pm_doc`, and for tests. In an app, pass the * `CrdtHandle` — it is what makes an edit durable and sent. */ export declare function crdtBindingFromDoc(doc: LoroDoc): CrdtBinding; /** * Write a `pm_doc` into a document as at most three commits. * * Almost always one: the second and third only happen when a node is missing a * container the encoding needs, which is the first character typed into an * empty block and nothing else. When that does happen the containers are * created under {@link PM_STRUCTURE_ORIGIN}, which the undo manager excludes — * see `createScaffolding` for the data loss that prevents. */ export declare function commitPmDoc(crdt: CrdtBinding, pm_doc: PmNode, mapping: LoroPmMapping, opts?: { actor?: Actor; /** * Omit to have the binding **project the result back into the editor** — * which is what a restore wants, and what any write the editor did not * itself originate wants. Pass `PM_ORIGIN` for a write that came *from* * the editor, so it is not echoed back. */ origin?: string; }): void; export declare class LoroPmBinding { #private; readonly crdt: CrdtBinding; readonly schema: Schema; readonly mapping: LoroPmMapping; constructor(options: { crdt: CrdtBinding; schema: Schema; actor?: Actor; }); get attached(): boolean; /** * True while the Loro document is checked out to an old version. * * Time travel is a read: nothing may be typed into a detached document, * because the ops would be written at a point history has already moved past. * Getting back is `checkoutToLatest()`, and getting the old version *back* * into the document is a restore, which writes forward. */ get detached(): boolean; /** * Take over a host, seeding whichever side is empty. * * **Await `handle.ready()` first.** `transact()` throws `bootstrap_pending` * while a document is still opening, and seeding an empty CRDT from an empty * editor before the first sync is exactly the failure the bootstrap gate * exists to prevent. */ attach(host: PmHost): () => void; detach(): void; /** Reconcile the CRDT with a locally-edited `pm_doc`. */ writeFromPm(pm_doc: PmNode, origin?: string): void; /** * Mint the caret anchors for the current selection. * * Called on every transaction. That cadence is the point: the anchor must * always be older than the next remote import, and the editor is * single-threaded, so "taken while handling a transaction" guarantees it. */ captureCaret(state: EditorState): void; /** Project the current CRDT state into the host, minimally. */ projectToPm(): void; } /** * The sync plugin. * * Install it **before** {@link loroUndo}, which reads this plugin's state to * find the document. `loroPlugins()` does that for you. */ export declare function loroSync(options: LoroSyncOptions): Plugin; //# sourceMappingURL=sync.d.ts.map