Skip to main content
Reference status

This page predates the current package split. Use the current package guides for checked installation, public imports, and onboarding examples. The detailed examples below have not all been revalidated.

Editor Core API

The Editor Core API provides the main Editor class and related interfaces for managing editor state, commands, extensions, and events.

Editor Class​

The main editor class that manages document state, selection, commands, and extensions.

Constructor​

new Editor(options?: EditorOptions)

Parameters:

  • options?: EditorOptions - Editor configuration options

Options:

interface EditorOptions {
content?: DocumentState; // Initial document content
extensions?: Extension[]; // Extensions to register
editable?: boolean; // Whether editor is editable (default: true)
history?: HistoryManagerOptions; // History manager options
model?: ModelOptions; // Model options
contentEditableElement?: HTMLElement; // ContentEditable element
dataStore?: DataStore; // Custom DataStore instance
schema?: Schema; // Schema for validation
}

Example:

import { Editor } from '@barocss/editor-core';
import { createCoreExtensions } from '@barocss/extensions';

const editor = new Editor({
extensions: createCoreExtensions(),
editable: true,
history: { maxSize: 100 }
});

Properties​

document: DocumentState​

Current document state (read-only).

const doc = editor.document;
// { type: 'document', content: [...], version: number, ... }

selection: ModelSelection | null​

Current selection (read-only).

const sel = editor.selection;
// { type: 'range', startNodeId: '...', startOffset: 0, ... }

dataStore: DataStore​

DataStore instance (read-only).

const node = editor.dataStore.getNode('node-id');

isFocused: boolean​

Whether editor is currently focused.

if (editor.isFocused) {
// Editor is focused
}

isEditable: boolean​

Whether editor is editable.

if (editor.isEditable) {
// Editor is editable
}

keybindings: KeybindingRegistry​

Keybinding registry for managing keyboard shortcuts.

editor.keybindings.register({
key: 'Mod-b',
command: 'toggleBold'
});

selectionManager: SelectionManager​

Selection manager instance.

editor.selectionManager.setSelection(selection);

Methods​

setContent(content: DocumentState): void​

Sets the document content.

Parameters:

  • content: DocumentState - New document content

Behavior:

  • Triggers onBeforeContentChange hooks (extensions can intercept/modify)
  • Updates document state
  • Emits editor:content.change event
  • Triggers onContentChange hooks

Example:

editor.setContent({
type: 'document',
content: [
{ id: 'p1', type: 'paragraph', text: 'Hello' }
],
version: 1,
createdAt: new Date(),
updatedAt: new Date()
});

updateSelection(selection: EditorSelectionModelPayload | null): void​

Updates the current selection.

Parameters:

  • selection - a ModelSelection, a NoSelection ({ type: 'none' }), an EditorSelectionModelEventPayload wrapping either, or null to clear.

Selection is always in model coordinates — node id plus offset. A DOM selection is converted by the view layer (fromDOMSelection) before it reaches this method; there is no DOM-shaped overload.

Behavior:

  • Triggers onBeforeSelectionChange hooks (extensions can intercept/modify)
  • Updates selection state
  • Emits editor:selection.change or editor:selection.model event
  • Triggers onSelectionChange hooks

Example:

editor.updateSelection({
type: 'range',
startNodeId: 'text-1',
startOffset: 0,
endNodeId: 'text-1',
endOffset: 5
});

// Clear the selection
editor.updateSelection(null);

A second, DOM-shaped overload used to be documented here as "SelectionState format (legacy)" — { anchorNode, anchorOffset, focusNode, focusOffset }. It never worked. SelectionState had no type field, so the isModelSelection guard (type !== 'none') answered true for it and the DOM branch was unreachable; nothing in the repository ever called updateSelection with that shape. The type is gone.

executeCommand(command: string, payload?: any): Promise<boolean>​

Executes a command.

Parameters:

  • command: string - Command name
  • payload?: any - Optional command payload

Returns:

  • Promise<boolean> - true if command executed successfully

Behavior:

  • Checks if command exists
  • Validates with canExecute if provided
  • Emits editor:command.before event
  • Calls command's before hook
  • Executes command
  • Calls command's after hook
  • Emits editor:command.after and editor:command.execute events

Example:

const success = await editor.executeCommand('toggleBold');
const result = await editor.executeCommand('insertText', { text: 'Hello' });

canExecuteCommand(command: string, payload?: any): boolean​

Checks if a command can be executed.

Parameters:

  • command: string - Command name
  • payload?: any - Optional command payload

Returns:

  • boolean - true if command can be executed

Example:

if (editor.canExecuteCommand('toggleBold')) {
await editor.executeCommand('toggleBold');
}

registerCommand(command: Command): void​

Registers a new command.

Parameters:

  • command: Command - Command definition

Example:

editor.registerCommand({
name: 'myCommand',
execute: async (editor, payload) => {
// Command logic
return true;
},
canExecute: (editor, payload) => {
// Check if command can execute
return true;
}
});

use(extension: Extension): void​

Registers an extension.

Parameters:

  • extension: Extension - Extension to register

Behavior:

  • Calls onBeforeCreate hook
  • Calls onCreate hook
  • Registers extension commands
  • Registers extension keybindings
  • Emits extension:add event

Example:

import { BoldExtension } from '@barocss/extensions';

editor.use(new BoldExtension());

unuse(extension: Extension): void​

Unregisters an extension.

Parameters:

  • extension: Extension - Extension to unregister

Behavior:

  • Calls onDestroy hook
  • Unregisters extension commands
  • Unregisters extension keybindings
  • Emits extension:remove event

Example:

const extension = new BoldExtension();
editor.use(extension);
// Later...
editor.unuse(extension);

setContext(key: string, value: unknown): void​

Sets a context value (VS Code style).

Parameters:

  • key: string - Context key
  • value: unknown - Context value (set to null or undefined to remove)

Behavior:

  • Updates context
  • Emits editor:context.change event
  • Emits editor:context.change:${key} event

Example:

editor.setContext('readOnly', true);
editor.setContext('myExtension.enabled', true);

getContext(key?: string): Record<string, unknown> | unknown​

Gets context value(s).

Parameters:

  • key?: string - Optional context key

Returns:

  • If key provided: context value for that key
  • If key not provided: all context values

Example:

const readOnly = editor.getContext('readOnly');
const allContext = editor.getContext();

onContextChange(key: string, callback: Function): () => void​

Subscribes to changes of a specific context key.

Parameters:

  • key: string - Context key to watch
  • callback: Function - Callback function

Returns:

  • Unsubscribe function

Example:

const unsubscribe = editor.onContextChange('readOnly', ({ value, oldValue }) => {
console.log('readOnly changed:', value);
});

// Later...
unsubscribe();

on(event: EditorEventType, callback: Function): void​

Subscribes to an editor event.

Parameters:

  • event: EditorEventType - Event name
  • callback: Function - Callback function

Example:

editor.on('editor:content.change', ({ content, transaction, from, skipRender, rootId, inputDebug }) => {
console.log('Content changed:', content);
if (transaction) {
console.log('Transaction:', transaction.operations.length);
} else {
console.log('No transaction payload (load/set content path)');
}
if (from) {
console.log('Source:', from);
}
if (typeof skipRender === 'boolean') {
console.log('skipRender:', skipRender);
}
if (rootId) {
console.log('rootId:', rootId);
}
if (inputDebug) {
console.log('inputDebug:', inputDebug);
}
});

editor.on('editor:command.execute', ({ command, payload, success }) => {
console.log('Command executed:', command, success);
});

off(event: string, callback: Function): void​

Unsubscribes from an editor event.

Parameters:

  • event: string - Event name
  • callback: Function - Callback function to remove

Example:

const handler = ({ content }) => console.log(content);
editor.on('editor:content.change', handler);
// Later...
editor.off('editor:content.change', handler);

emit(event: string, data?: any): void​

Emits an editor event.

Parameters:

  • event: string - Event name
  • data?: any - Event data

Example:

editor.emit('user:customEvent', { data: 'value' });

undo(): void​

Undoes the last operation.

Behavior:

  • Reverts to previous history state
  • Emits editor:history.undo event
  • Emits editor:history.change event

Example:

if (editor.canUndo()) {
editor.undo();
}

redo(): void​

Redoes the last undone operation.

Behavior:

  • Re-executes undone operation
  • Emits editor:history.redo event
  • Emits editor:history.change event

Example:

if (editor.canRedo()) {
editor.redo();
}

canUndo(): boolean​

Checks if undo is possible.

Returns:

  • boolean - true if undo is possible

canRedo(): boolean​

Checks if redo is possible.

Returns:

  • boolean - true if redo is possible

setEditable(editable: boolean): void​

Sets whether editor is editable.

Parameters:

  • editable: boolean - Whether editor is editable

Behavior:

  • Updates isEditable property
  • Updates editorEditable context
  • Emits editor:editable.change event

Example:

editor.setEditable(false); // Make read-only

loadDocument(treeDocument: any, sessionId?: string): void​

Loads a document from tree format.

Parameters:

  • treeDocument: any - Document in tree format
  • sessionId?: string - Session ID (default: 'editor-session')

Example:

editor.loadDocument({
stype: 'document',
content: [
{ stype: 'paragraph', text: 'Hello' }
]
});

exportDocument(rootId?: string): any | null​

Exports document to tree format.

Parameters:

  • rootId?: string - Root node ID (uses default if not provided)

Returns:

  • any | null - Document in tree format

Example:

const doc = editor.exportDocument();

getDocumentProxy(rootId?: string): any | null​

Gets document as Proxy (lazy evaluation).

Parameters:

  • rootId?: string - Root node ID (uses default if not provided)

Returns:

  • any | null - Proxy-wrapped document

Example:

const proxy = editor.getDocumentProxy();
// Accessing proxy.content converts IDs to nodes on-demand

chain(): CommandChain​

Creates a command chain for chaining multiple commands.

Returns:

  • CommandChain - Command chain instance

Example:

await editor.chain()
.insertText('Hello')
.toggleBold()
.run();

destroy(): void​

Destroys the editor instance.

Behavior:

  • Calls onDestroy for all extensions
  • Cleans up event listeners
  • Emits editor:destroy event

Example:

editor.destroy();

Extension Interface​

Extensions add functionality to the editor.

interface Extension {
name: string;
priority?: number; // Execution priority (higher = first)
dependencies?: string[]; // Extension dependencies

// Lifecycle
onBeforeCreate?(editor: Editor): void;
onCreate?(editor: Editor): void;
onDestroy?(editor: Editor): void;

// Command registration
commands?: Command[];

// Before hooks (intercept and modify core model changes)
onBeforeTransaction?(editor: Editor, transaction: Transaction): Transaction | null | void;
onBeforeSelectionChange?(editor: Editor, selection: ModelSelection): ModelSelection | null | void;
onBeforeContentChange?(editor: Editor, content: DocumentState): DocumentState | null | void;

// After hooks (notification for core model changes)
onTransaction?(editor: Editor, transaction: Transaction): void;
onSelectionChange?(editor: Editor, selection: MaybeSelection): void;
onContentChange?(editor: Editor, content: DocumentState): void;
}

Lifecycle Hooks​

onBeforeCreate(editor: Editor): void​

Called before extension is created (before onCreate).

onCreate(editor: Editor): void​

Called when extension is registered.

Example:

onCreate(editor: Editor) {
// Register commands
editor.registerCommand({
name: 'myCommand',
execute: async (editor) => {
// ...
}
});

// Register keybindings
editor.keybindings.register({
key: 'Mod-k',
command: 'myCommand'
});
}

onDestroy(editor: Editor): void​

Called when extension is unregistered.

Example:

onDestroy(editor: Editor) {
// Cleanup
editor.off('editor:content.change', this.handler);
}

Before Hooks​

Before hooks allow extensions to intercept and modify core model changes.

onBeforeTransaction(editor: Editor, transaction: Transaction): Transaction | null | void​

Intercepts transactions before execution.

Returns:

  • Transaction - Modified transaction (use this instead)
  • null - Cancel transaction
  • void - Continue with original transaction

Example:

onBeforeTransaction(editor: Editor, transaction: Transaction) {
if (editor.getContext('readOnly')) {
return null; // Cancel transaction
}

// Modify transaction
const ops = transaction.getOperations();
// ... modify operations
return transaction; // Use modified transaction
}

onBeforeSelectionChange(editor: Editor, selection: ModelSelection): ModelSelection | null | void​

Intercepts selection changes.

Returns:

  • ModelSelection - Modified selection (use this instead)
  • null - Cancel selection change
  • void - Continue with original selection

onBeforeContentChange(editor: Editor, content: DocumentState): DocumentState | null | void​

Intercepts content changes.

Returns:

  • DocumentState - Modified content (use this instead)
  • null - Cancel content change
  • void - Continue with original content

After Hooks​

After hooks provide notifications for core model changes.

onTransaction(editor: Editor, transaction: Transaction): void​

Called after transaction is executed.

onSelectionChange(editor: Editor, selection: MaybeSelection): void​

Called after selection changes. MaybeSelection is ModelSelection | NoSelection — the selection going away is also a selection change, and it arrives as { type: 'none' }.

onContentChange(editor: Editor, content: DocumentState): void​

Called after content changes.

Note: For other changes (Node, Command, History, etc.), use editor.on() events instead.


Command Interface​

Commands define executable actions.

interface Command {
name: string;
execute: (editor: Editor, payload?: any) => boolean | Promise<boolean>;
canExecute?: (editor: Editor, payload?: any) => boolean;
before?: (editor: Editor, payload?: any) => void;
after?: (editor: Editor, payload?: any) => void;
}

Properties​

name: string​

Command name (must be unique).

execute: (editor: Editor, payload?: any) => boolean | Promise<boolean>​

Command execution function.

Returns:

  • boolean - true if successful, false otherwise

Example:

execute: async (editor: Editor, payload?: { text: string }) => {
const text = payload?.text || '';
// Execute command logic
return true;
}

canExecute?: (editor: Editor, payload?: any) => boolean​

Optional function to check if command can execute.

Example:

canExecute: (editor: Editor) => {
return editor.selection !== null;
}

before?: (editor: Editor, payload?: any) => void​

Optional hook called before command execution.

Note: This is not a true hook (cannot intercept). Use editor:command.before event or onBeforeTransaction for interception.

after?: (editor: Editor, payload?: any) => void​

Optional hook called after command execution.

Note: This is not a true hook (cannot intercept). Use editor:command.after event for notification.


SelectionManager​

Manages selection state at the model level.

Methods​

getCurrentSelection(): ModelSelection | null​

Gets current selection.

Returns:

  • ModelSelection | null - Current selection or null

setSelection(selection: ModelSelection | null): void​

Sets selection.

Parameters:

  • selection: ModelSelection | null - Selection to set

clearSelection(): void​

Clears selection.

isEmpty(): boolean​

Checks if selection is empty.

isInNode(nodeId: string): boolean​

Checks if selection is in a specific node.

isAtPosition(nodeId: string, position: number): boolean​

Checks if selection is at a specific position.

isInRange(nodeId: string, start: number, end: number): boolean​

Checks if selection is in a specific range.


HistoryManager​

Manages undo/redo history.

Methods​

push(entry: Omit<HistoryEntry, 'id' | 'timestamp'>): void​

Adds entry to history.

undo(): HistoryEntry | null​

Undoes last operation.

Returns:

  • HistoryEntry | null - Undone entry or null

redo(): HistoryEntry | null​

Redoes last undone operation.

Returns:

  • HistoryEntry | null - Redone entry or null

canUndo(): boolean​

Checks if undo is possible.

canRedo(): boolean​

Checks if redo is possible.

getStats(): HistoryStats​

Gets history statistics.

Returns:

{
totalEntries: number;
currentIndex: number;
canUndo: boolean;
canRedo: boolean;
}

KeybindingRegistry​

Manages keyboard shortcuts.

Methods​

register(binding: Keybinding): void​

Registers a keybinding.

Parameters:

interface Keybinding {
key: string; // Key combination (e.g., 'Mod-b', 'Ctrl-k Ctrl-s')
command: string; // Command name
args?: unknown; // Optional command arguments
when?: string; // When clause (VS Code style)
source?: KeybindingSource; // 'core' | 'extension' | 'user'
}

Example:

editor.keybindings.register({
key: 'Mod-b',
command: 'toggleBold',
when: 'editorTextFocus && !editorReadonly'
});

unregister(binding: Keybinding): void​

Unregisters a keybinding.

clear(source?: KeybindingSource): void​

Clears keybindings (optionally by source).

resolve(key: string, context?: Record<string, unknown>): Array<{ command: string; args?: unknown }>​

Resolves keybinding to commands.

Returns:

  • Array of matching commands

Editor Events​

Editor emits various events for extension integration.

Event Types​

Content Events​

  • editor:content.change - Content changed
  • editor:node.create - Node created
  • editor:node.update - Node updated
  • editor:node.delete - Node deleted

Selection Events​

  • editor:selection.change - Selection changed
  • editor:selection.focus - Selection focused
  • editor:selection.blur - Selection blurred
  • editor:selection.model - Model selection changed

Command Events​

  • editor:command.execute - Command executed
  • editor:command.before - Before command execution
  • editor:command.after - After command execution

History Events​

  • editor:history.change - History state changed
  • editor:history.undo - Undo performed
  • editor:history.redo - Redo performed

Context Events​

  • editor:context.change - Context changed
  • editor:context.change:${key} - Specific context key changed

Extension Events​

  • extension:add - Extension added
  • extension:remove - Extension removed
  • extension:enable - Extension enabled
  • extension:disable - Extension disabled

Error Events​

  • error:selection - Selection error
  • error:command - Command error
  • error:extension - Extension error

Lifecycle Events​

  • editor:create - Editor created
  • editor:destroy - Editor destroyed
  • editor:editable.change - Editable state changed

Event Subscription​

// Subscribe
editor.on('editor:content.change', ({ content, transaction, from, skipRender, rootId }) => {
console.log('Content changed:', content);
if (transaction) {
console.log('Operations:', transaction.operations.length);
if (from) {
console.log('Source:', from);
}
}
if (typeof skipRender === 'boolean') {
console.log('skipRender:', skipRender);
}
if (rootId) {
console.log('rootId:', rootId);
}
});

// Unsubscribe
const handler = ({ content }) => console.log(content);
editor.on('editor:content.change', handler);
editor.off('editor:content.change', handler);