UNPKG

collaborative-editor

Version:

JSON CRDT str node bindings to any generic plain text editor.

136 lines (135 loc) 5.64 kB
import type { StrApi } from 'json-joy/lib/json-crdt'; import type { Selection } from './Selection'; /** * Represents a single change in the editor. It is a 3-tuple of `[position, * remove, insert]`, where `position` is the position of the change, `remove` * is the number of characters removed, and `insert` is the string inserted. */ export type SimpleChange = [position: number, remove: number, insert: string]; /** * A 3-tuple of `[position, remove, insert]`, which represents a single range or * caret selection in the editor. Where `start` and `end` are character offsets * in the text, and `direction` is either `-1`, `0`, or `1`, where `-1` * indicates that the selection is backwards, `1` indicates that the * selection is forwards, and `0` is used for all other cases. */ export type EditorSelection = [start: number, end: number, direction: -1 | 0 | 1]; /** * A facade for the editor, which is used by the binding to communicate with * the editor. The editor can be any plain text editor, such as a textarea * or an input element, or a code editor, such as CodeMirror or Monaco. */ export interface EditorFacade { /** * Returns the text content of the editor. */ get(): string; /** * Overwrites the editor content with the given text. * @param text Raw text to set. */ set(text: string): void; /** * Inserts text at the given position. When implemented, this method is used * for granular model-to-editor sync of remote changes. * @param position Position to insert text at. * @param text Raw text to insert. */ ins?(position: number, text: string): void; /** * Deletes text at the given position. When implemented, this method is used * for granular model-to-editor sync of remote changes. * @param position Position to delete text at. * @param length Number of characters to delete. */ del?(position: number, length: number): void; /** * Emits a change event when the text changes. The event is emitted with * a `SimpleChange` tuple, which is a tuple of `[position, remove, insert]`, * where `position` is the position of the change, `remove` is the number * of characters removed, and `insert` is the string inserted. * * If a change happened, but it is too complex or impossible to represent by * the `SimpleChange` tuple, the `void` value can be emitted instead. For the * most basic implementation, one can always emit `null` on every change. */ onchange?: (change: SimpleChange[] | void, verify?: boolean) => void; /** * Length of text. Should return the same result as `.get().length`, * but it is possible to implement length retrieval in a more efficient way * here. */ getLength?(): number; /** * Called when the selection changes. */ onselection?: () => void; /** * Returns the current selection. */ getSelection?(): EditorSelection | null; /** * Sets the editor selection. */ setSelection?(start: number, end: number, direction: -1 | 0 | 1): void; /** * This property does not have to be set, it is set by the binding once it is * created. It store the last know selection, which can be used to generate * `SimpleChange` events. */ selection?: Selection; /** * Binding calls this method when it is no longer needed. This method should * clean up any allocated resources, such as event listeners. */ dispose?(): void; } /** * Represents the model of a collaborative (JSON CRDT) string. */ export type CollaborativeStr = Pick<StrApi, 'view' | 'ins' | 'del' | 'findId' | 'findPos'> & { api: Pick<StrApi['api'], 'transaction'> & { onChange: { listen: (callback: () => void) => () => void; }; model: { tick: number; }; }; }; /** * Ephemeral data that is broadcasted about other peers in the editor. */ export type PeerData = [peerId: string, selections?: Record<DocumentId, Record<DocumentId, JsonCrdtSelection[]>>]; /** * Document ID, which is a globally unique identifier for a document. */ export type DocumentId = string; /** * Location ID within the UI, in case some JSON CRDT is bound to multiple * places in the UI. */ export type UiLocationId = string; export type JsonCrdtSelection = RgaSelection | AnySelection | ObjSelection | VecSelection; export type NodeSelection<Type extends number> = [type: Type, nodeId: JsonCrdtId]; /** Selection within an RGA node, such as a "str", "arr", or "bin" nodes. */ export type RgaSelection = [...NodeSelection<0>, ...JsonCrdtCursor]; /** Selects a whole JSON CRDT node, any node. */ export type AnySelection = [...NodeSelection<1>]; /** Selects an "obj" node with ability to select a specific node. */ export type ObjSelection = [...NodeSelection<2>, key?: string]; /** Selects a "vec" node with ability to select a specific index. */ export type VecSelection = [...NodeSelection<3>, index?: number]; /** * The selection range of a peer. `anchor` is the starting point of the selection, * and `focus` is the ending point of the selection. If `focus` is not provided, * the selection is a caret (a single point). */ export type JsonCrdtCursor = [anchor: JsonCrdtRgaPoint, focus?: JsonCrdtRgaPoint]; export type JsonCrdtRgaPoint = [ id: JsonCrdtIdShorthand, /** Zero means point is anchored to the left of the character, one means right. */ anchor?: 0 | 1 ]; export type JsonCrdtId = [time: number, sid: number]; export type JsonCrdtIdShorthand = [time: number, sid?: number];