json-joy
Version: 
Collection of libraries for building collaborative editing apps.
354 lines (353 loc) • 13 kB
TypeScript
import type { Position as EditorPosition } from '../../json-crdt-extensions/peritext/editor/types';
import type { SliceType } from '../../json-crdt-extensions/peritext/slice/types';
import type { Patch } from '../../json-crdt-patch';
/**
 * Dispatched every time any other event is dispatched.
 */
export interface ChangeDetail {
    ev?: CustomEvent<InsertDetail | DeleteDetail | CursorDetail | FormatDetail | MarkerDetail>;
}
/**
 * Event dispatched to insert text into the document.
 */
export interface InsertDetail {
    text: string;
}
/**
 * Event dispatched to delete text from the document.
 */
export interface DeleteDetail {
    /**
     * Specifies the amount of text to delete. If the value is negative, the
     * deletion will be backwards. If positive, the deletion will be forwards.
     * If `0`, the deletion will execute in both directions.
     *
     * For example, if the cursor is in the middle of a word and the length is
     * set to `0`, the whole word will be deleted by expanding the selection
     * in both directions.
     *
     * ```js
     * {
     *   len: 0,
     *   unit: 'word',
     * }
     * ```
     *
     * Or, delete a single character forwards:
     *
     * ```js
     * {
     *   len: 1,
     * }
     * ```
     *
     * @default -1
     */
    len?: number;
    /**
     * Specifies the unit of the deletion. If `'char'`, the deletion will be
     * executed by `len` characters. If `'word'`, the deletion will be executed
     * by one word in the direction specified by `len`. If `'line'`, the deletion
     * will be executed to the beginning or end of line, in direction specified
     * by `len`.
     *
     * @default 'char'
     */
    unit?: 'char' | 'word' | 'line';
    /**
     * Position in the document to start the deletion from. If not specified, the
     * deletion is executed for all cursors in the document at their current
     * positions. If specified, only one cursor will be placed at the specified
     * position and the deletion will be executed from that position (while all
     * other cursors will be removed).
     *
     * @default undefined
     */
    at?: Position;
}
/**
 * The `cursor` event is emitted when caret or selection is changed. The event
 * is applied to all cursors in the document.
 *
 * ## Scenarios
 *
 * Move caret to a specific position in text:
 *
 * ```ts
 * {at: 10}
 * ```
 *
 * Move caret relative to current position:
 *
 * ```ts
 * {len: 5}
 * ```
 *
 * Move caret to the beginning of the word at a specific position:
 *
 * ```ts
 * {at: 10, len: -1, unit: 'word'}
 * ```
 *
 * Move caret to the end of word starting from the current position:
 *
 * ```ts
 * {len: 1, unit: 'word'}
 * ```
 *
 * Move *anchor* edge of the selection to the beginning of the current line:
 *
 * ```ts
 * {len: -1, unit: 'line', edge: 'anchor'}
 * ```
 *
 * Move *focus* edge of the selection to the end of a block at a specific position:
 *
 * ```ts
 * {at: 10, len: 1, unit: 'block', edge: 'focus'}
 * ```
 *
 * Select the whole document:
 *
 * ```ts
 * {unit: 'all'}
 * ```
 *
 * The editor supports multiple cursors. To create a new cursor, set the `edge`
 * field to `'new'`. The `at` field specifies the position of the new cursor.
 * The `len` field is ignored when the `edge` field is set to `'new'`. For
 * example:
 *
 * ```ts
 * {at: 10, edge: 'new'}
 * ```
 */
export interface CursorDetail {
    /**
     * Position in the document to move the cursor to. If `-1` or undefined, the
     * current cursor position will be used as the starting point and `len` will
     * be used to determine the new position.
     *
     * If 2-tuple is provided, the first element is the character index and the
     * second `0 | 1` member specifies the anchor point of the character: `0`
     * for the beginning of the character and `1` for the end of the character.
     */
    at?: Position;
    /**
     * Specify the length of the movement or selection in units specified by the
     * `unit` field. If the `at` field is set, the `at` field specifies the
     * absolute selection position and the `len` field specifies the length of
     * the selection. If the `at` field is not set, the `len` field specifies
     * the relative movement of the cursor.
     */
    len?: number;
    /**
     * Specifies the unit of the movement. If `'char'`, the cursor will be
     * moved by `len` characters. If `'word'`, the cursor will be moved by
     * one word in the direction specified by `len`. If `'line'`, the cursor
     * will be moved to the beginning or end of line, in direction specified
     * by `len`.
     *
     * - `'point'`: Moves by one Peritext anchor point. Each character has two
     *     anchor points, one from each side of the character.
     * - `'char'`: Moves by one character. Skips one visible character.
     * - `'word'`: Moves by one word. Skips all visible characters until the end
     *     of a word.
     * - `'line'`: Moves to the beginning or end of line. If UI API is provided,
     *     the line end is determined by a visual line wrap.
     * - `'vert'`: Moves cursor up or down by one line, works if UI
     *     API is provided. Determines the best position in the target line by
     *     finding the position which has the closest relative offset from the
     *     beginning of the line.
     * - `'block'`: Moves to the beginning or end of block, i.e. paragraph,
     *     blockequote, etc.
     * - `'all'`: Moves to the beginning or end of the document.
     *
     * @default 'char'
     */
    unit?: 'point' | 'char' | 'word' | 'line' | 'vert' | 'block' | 'all';
    /**
     * Specifies which edge of the selection to move. If `'focus'`, the focus
     * edge will be moved. If `'anchor'`, the anchor edge will be moved. If
     * `'both'`, the whole selection will be moved. Defaults to `'both'`.
     *
     * When the value is set to `'new'`, a new cursor will be created at the
     * position specified by the `at` field.
     */
    edge?: 'focus' | 'anchor' | 'both' | 'new';
}
/**
 * Event dispatched to insert an inline rich-text annotation into the document.
 */
export interface FormatDetail {
    /**
     * Type of the annotation. The type is used to determine the visual style of
     * the annotation, for example, the type `'bold'` may render the text in bold.
     *
     * For common formatting use the {@link CommonSliceType} enum. It contains
     * a unique numeric value for each common formatting types. Numeric values
     * are best for performance and memory usage. Values in the rage -64 to 64 are
     * reserved for common formatting types.
     *
     * For custom formatting, you can use a string value, for example,
     * `'highlight'`. Or use an integer with absolute value greater than 64.
     *
     * Inline formatting types are restricted to a single string or integer value.
     * Nester formatting, say `['p', 'blockquote', 'h1']` is reserved for block
     * formatting, in which case a nested structure like
     * `<p><blockquote><h1>text</h1></blockquote></p>` is created.
     */
    type?: number | string;
    /**
     * Arbitrary data associated with the formatting. Usually, stored with
     * annotations of "stack" behavior, for example, an "<a>" tag annotation may
     * store the href attribute in this field.
     *
     * @default undefined
     */
    data?: unknown;
    /**
     * Specifies the behavior of the annotation. If `'many'`, the annotation of
     * this type will be stacked on top of each other, and all of them will be
     * applied to the text, with the last annotation on top. If `'one'`,
     * the annotation is not stacked, only one such annotation can be applied per
     * character. The `'erase'` behavior is used to remove the `'many`' or
     * `'one'` annotation from the the given range.
     *
     * The special `'clear'` behavior is used to remove all annotations
     * that intersect with any part of any of the cursors in the document. Usage:
     *
     * ```js
     * {type: 'clear'}
     * ```
     *
     * @default 'one'
     */
    behavior?: 'one' | 'many' | 'erase' | 'clear';
    /**
     * The slice set where the annotation will be stored. `'saved'` is the main
     * document, which is persisted and replicated across all clients. `'extra'`
     * is an ephemeral document, which is not persisted but can be replicated
     * across clients. `'local'` is a local document, which is accessible only to
     * the local client, for example, for storing cursor or selection information.
     *
     * @default 'saved'
     */
    store?: 'saved' | 'extra' | 'local';
}
/**
 * The "marker" event manages block marker insertion, removal, and update
 * actions. For example, inserting a marker in the middle of a paragraph
 * is a "split" action, it creates two new paragraph blocks from the original
 * block. Removing a marker results into a "join" action, which merges two
 * adjacent blocks into one.
 */
export interface MarkerDetail {
    /**
     * The action to perform.
     *
     * @default 'tog'
     */
    action?: 'tog' | 'ins' | 'del' | 'upd';
    /**
     * The type tag applied to the new block, if the action is `'ins'`. If the
     * action is `'upd'`, the type tag is used to update the block type.
     *
     * @default SliceType.Paragraph
     */
    type?: SliceType;
    /**
     * Block-specific custom data. For example, a paragraph block may store
     * the alignment and indentation information in this field.
     *
     * @default undefined
     */
    data?: unknown;
}
/**
 * The "buffer" event manages clipboard buffer actions: cut, copy, and paste.
 */
export interface BufferDetail {
    /**
     * The action to perform. The `'cut'` and `'copy'` actions generally work
     * the same way, the only difference is that the `'cut'` action removes the
     * text from the current selection and collapses the cursor.
     */
    action: 'cut' | 'copy' | 'paste';
    /**
     * The format in which the data is stored or retrieved from the clipboard.
     *
     * - `auto`: Automatically determine the format based on the data in the
     *   clipboard.
     * - `json`: Specifies the default Peritext {@link Editor} export/import
     *   format in JSON POJO format.
     * - `jsonml`: HTML markup in JSONML format.
     * - `hast`: HTML markup in HAST format.
     * - `text`: Plain text format. Copy and paste text only.
     * - `html`: HTML format. Will copy a range of text with formatting
     *   information in HTML format.
     * - `mdast`: Specifies MDAST (Markdown Abstract Syntax Tree) format.
     * - `markdown`: Markdown format. Will copy a range of text with formatting
     *   information in Markdown format.
     * - `style`: Formatting only. Used to copy and paste formatting information
     *   only, without the text content.
     *
     * @default 'auto'
     */
    format?: 'auto' | 'text' | 'json' | 'jsonml' | 'hast' | 'html' | 'mdast' | 'md' | 'fragment' | 'style';
    /**
     * The range of text to cut or copy. If not specified, the first selection of
     * the current cursor is used. If not specified and there is no cursor, the
     * whole document is used.
     */
    range?: [start: Position, end: Position];
    /**
     * The data to paste into the document, when `action` is `"paste"`. If not
     * specified, an attempt is made to retrieve the data from the clipboard.
     */
    data?: {
        text?: string;
        html?: string;
    };
}
/**
 * The "annals" event manages undo and redo actions, typically triggered by
 * common keyboard shortcuts like `Ctrl+Z` and `Ctrl+Shift+Z`.
 */
export interface AnnalsDetail {
    /** The action to perform. */
    action: 'undo' | 'redo';
    /**
     * The list of {@link Patch} that will be applied to the document to undo or
     * redo the action, unless the action is cancelled.
     */
    batch: Patch[];
}
/**
 * Position represents a caret position in the document. The position can either
 * be an instance of {@link Point} or a numeric position in the document, which
 * will be immediately converted to a {@link Point} instance.
 *
 * If a number is provided, the number represents the character index in the
 * document, where `0` is the beginning of the document and `1` is the position
 * right after the first character, etc.
 *
 * If 2-tuple is provided, the first element is the character index and the
 * second `0 | 1` member specifies the anchor point of the character: `0`
 * for the beginning of the character and `1` for the end of the character.
 */
export type Position = EditorPosition<string>;
/**
 * A map of all Peritext rendering surface event types and their corresponding
 * detail types.
 */
export type PeritextEventDetailMap = {
    change: ChangeDetail;
    insert: InsertDetail;
    delete: DeleteDetail;
    cursor: CursorDetail;
    format: FormatDetail;
    marker: MarkerDetail;
    buffer: BufferDetail;
    annals: AnnalsDetail;
};