Skip to content

EditorState

EditorState is the immutable container for all editor state. Every change produces a new EditorState instance.

import { EditorState } from '@notectl/core';
const state = EditorState.create({
doc: myDocument, // Optional Document
schema: mySchema, // Optional Schema
selection: mySel, // Optional EditorSelection
});
PropertyTypeDescription
docDocumentThe document tree
selectionEditorSelectionCurrent cursor/selection (text or node)
schemaSchemaActive schema (node + mark types)
storedMarksreadonly Mark[] | nullMarks to apply on next input

getBlock(blockId: BlockId): BlockNode | undefined

Section titled “getBlock(blockId: BlockId): BlockNode | undefined”

Look up a block by its ID (O(1) via internal index).

Returns leaf-block IDs in depth-first document order.

getNodePath(nodeId: BlockId): BlockId[] | undefined

Section titled “getNodePath(nodeId: BlockId): BlockId[] | undefined”

Returns the path (array of block IDs) from root to the given node.

getParent(nodeId: BlockId): BlockNode | undefined

Section titled “getParent(nodeId: BlockId): BlockNode | undefined”

Returns the parent BlockNode of a node, or undefined for top-level blocks.

transaction(origin?: TransactionOrigin): TransactionBuilder

Section titled “transaction(origin?: TransactionOrigin): TransactionBuilder”

Creates a TransactionBuilder for this state. The origin defaults to 'api'.

const tr = state.transaction('command')
.insertText(blockId, offset, 'hello', [])
.addMark(blockId, 0, 5, { type: markType('bold') })
.build();

Applies a transaction to produce a new state. This is a pure function — the original state is unchanged.

toJSON(): { readonly doc: Document; readonly selection: EditorSelection }

Section titled “toJSON(): { readonly doc: Document; readonly selection: EditorSelection }”

Serializes the state (document and selection) to a JSON-compatible object.

static fromJSON(json: { doc: Document; selection: EditorSelection }, schema?: Schema): EditorState

Section titled “static fromJSON(json: { doc: Document; selection: EditorSelection }, schema?: Schema): EditorState”

Deserializes a state from a JSON object. Optionally accepts a schema.

const json = state.toJSON();
const restored = EditorState.fromJSON(json);

EditorState is deeply immutable:

const state1 = editor.getState();
const tr = state1.transaction('command')
.insertText(blockId, 0, 'hello', [])
.build();
const state2 = state1.apply(tr);
// state1 is unchanged
assert(state1 !== state2);
assert(state1.doc !== state2.doc);

This enables:

  • Undo/redo via state snapshots
  • Safe comparison between old and new states
  • Predictable plugin behavior — plugins always see consistent state

Manages undo/redo stacks with automatic transaction grouping.

import { HistoryManager } from '@notectl/core';
const history = new HistoryManager({ groupTimeoutMs: 500, maxDepth: 100 });
OptionTypeDefaultDescription
groupTimeoutMsnumber500Time window for grouping consecutive input transactions
maxDepthnumber100Maximum number of undo groups to retain

Pushes a transaction onto the undo stack. Consecutive input transactions within groupTimeoutMs are grouped together — undoing the group reverts all of them at once.

history.push(transaction);

Undoes the last group. Returns a HistoryResult with the new state and the inverse transaction, or null if nothing to undo:

const result = history.undo(state);
if (result) {
// result.state — new EditorState after undo
// result.transaction — the inverse transaction that was applied
}

Redoes the last undone group. Returns a HistoryResult or null:

const result = history.redo(state);

Returns true if there are groups available to undo or redo:

if (history.canUndo()) { /* show undo button */ }
if (history.canRedo()) { /* show redo button */ }

Clears both undo and redo stacks:

history.clear();
interface HistoryResult {
readonly state: EditorState;
readonly transaction: Transaction;
}

The HistoryManager groups consecutive transactions of the same input type (e.g. typing characters) into a single undo group when they arrive within groupTimeoutMs of each other. This means pressing undo after typing “hello” undoes the entire word, not individual characters.

Groups are broken when:

  • The timeout between transactions exceeds groupTimeoutMs
  • The transaction origin changes (e.g. from 'input' to 'command')
  • A non-input transaction is pushed