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;
};