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.

Model API

The Model API provides high-level model operations, transaction DSL, and operation definitions.

Transaction DSL​

transaction(editor, operations)​

Creates a transaction builder.

Parameters:

  • editor: Editor - Editor instance
  • operations: (TransactionOperation | TransactionOperation[] | OpFunction)[] - Operations to execute

Returns:

  • TransactionBuilder - Transaction builder instance

Example:

import { transaction, control, insertText } from '@barocss/model';

const result = await transaction(editor, [
...control('text-1', [
insertText(5, 'Hello')
])
]).commit();

TransactionBuilder​

Transaction builder interface.

commit(): Promise<TransactionResult>​

Commits the transaction.

Returns:

  • Promise<TransactionResult> - Transaction result

Behavior:

  • Triggers onBeforeTransaction hooks (extensions can intercept/modify)
  • Executes all operations
  • Commits to DataStore
  • Adds to history
  • Emits editor:content.change event
  • Triggers onTransaction hooks

Example:

const result = await transaction(editor, operations).commit();

if (result.success) {
console.log('Transaction succeeded:', result.transactionId);
} else {
console.error('Transaction failed:', result.errors);
}

TransactionResult​

interface TransactionResult {
success: boolean; // Whether transaction succeeded
errors: string[]; // Array of error messages
data?: any; // Transaction data
transactionId?: string; // Transaction ID
operations?: TransactionOperation[]; // Executed operations
selectionBefore?: ModelSelection | null; // Selection before transaction
selectionAfter?: ModelSelection | null; // Selection after transaction
}

Control Function​

control(target, actions)​

Injects nodeId into operation payloads.

Parameters:

  • target: HandleOrId - Target node ID
  • actions: Array<{ type: string; payload?: any }> - Operations to inject nodeId into

Returns:

  • TransactionOperation[] - Operations with injected nodeId

Example:

control('text-1', [
insertText(5, 'Hello'),
toggleMark('bold', [0, 10])
])

// Becomes:
[
{ type: 'insertText', payload: { nodeId: 'text-1', pos: 5, text: 'Hello' } },
{ type: 'toggleMark', payload: { nodeId: 'text-1', markType: 'bold', range: [0, 10] } }
]

Node Creation Helpers​

node(stype, attributes?, content?)​

Creates a container node.

Parameters:

  • stype: string - Node type
  • attributes?: Record<string, any> - Optional attributes
  • content?: INode[] - Optional child nodes

Returns:

  • INode - Node object

Example:

const paragraph = node('paragraph', {}, [
textNode('inline-text', 'Hello')
]);

textNode(stype, text, marks?, attributes?)​

Creates a text node.

Overloads:

textNode(stype: string, text: string): INode
textNode(stype: string, text: string, marks: MarkDescriptor[]): INode
textNode(stype: string, text: string, attributes: Record<string, any>): INode
textNode(stype: string, text: string, marks: MarkDescriptor[], attributes: Record<string, any>): INode

Parameters:

  • stype: string - Node type
  • text: string - Text content
  • marks?: MarkDescriptor[] - Optional marks
  • attributes?: Record<string, any> - Optional attributes

Returns:

  • INode - Text node object

Example:

// Basic text node
const text = textNode('inline-text', 'Hello');

// Text node with marks
const boldText = textNode('inline-text', 'Hello', [
mark('bold', { range: [0, 5] })
]);

// Text node with attributes
const styledText = textNode('inline-text', 'Hello', {
color: 'red'
});

// Text node with marks and attributes
const richText = textNode('inline-text', 'Hello', [
mark('bold', { range: [0, 5] })
], {
color: 'red'
});

mark(stype, attrs?)​

Creates a mark descriptor.

Parameters:

  • stype: string - Mark type
  • attrs?: Record<string, any> - Optional attributes (can include range)

Returns:

  • MarkDescriptor - Mark descriptor

Example:

// Basic mark
const boldMark = mark('bold');

// Mark with attributes
const linkMark = mark('link', {
href: 'https://example.com',
target: '_blank'
});

// Mark with range
const boldRange = mark('bold', {
range: [0, 5]
});

Operation Definition​

defineOperation(name, executor, selectionMapper?)​

Defines a custom operation.

Parameters:

  • name: string - Operation name
  • executor: (operation: T, context: TransactionContext) => Promise<void | INode> - Operation executor
  • selectionMapper?: (operation: T, context: TransactionContext) => any - Optional selection mapper

Example:

import { defineOperation } from '@barocss/model';
import type { TransactionContext } from '@barocss/model';

defineOperation('customOp', async (operation, context) => {
const { nodeId, data } = operation.payload;

// Access DataStore
const node = context.dataStore.getNode(nodeId);

// Perform operation
context.dataStore.updateNode(nodeId, data);

// Map selection if needed
if (context.selection?.current) {
// Update selection
}

// Return result with inverse
return {
ok: true,
data: context.dataStore.getNode(nodeId),
inverse: { type: 'customOp', payload: { nodeId, data: oldData } }
};
});

See: Custom Operations Guide for detailed examples.

defineOperationDSL(builder, options?)​

Defines a DSL helper for an operation.

Parameters:

  • builder: BuilderFn<Args, P> - Builder function
  • options?: DefineOperationDSLOptions - Optional options

Returns:

  • DSL helper function

Example:

import { defineOperationDSL } from '@barocss/model';

export const customOp = defineOperationDSL(
(nodeId: string, data: Record<string, any>) => ({
type: 'customOp',
payload: { nodeId, data }
}),
{ atom: false, category: 'custom' }
);

// Usage
const ops = control('node-1', [
customOp({ key: 'value' })
]);

Options:

interface DefineOperationDSLOptions {
atom?: boolean; // Whether operation is atomic
category?: string; // Operation category
}

See: Model Operation DSL API for complete DSL helper reference.


Functional DSL​

op(operationFn)​

Creates a functional operation.

Parameters:

  • operationFn: (context: TransactionContext) => OpResult | void | Promise<OpResult | void> - Operation function

Returns:

  • OpFunction - Functional operation

Example:

import { op } from '@barocss/model';

const result = await transaction(editor, [
op(async (context) => {
// Custom logic
const node = context.dataStore.getNode('node-1');
context.dataStore.updateNode('node-1', { text: 'Updated' });

return {
success: true,
data: context.dataStore.getNode('node-1'),
inverse: { type: 'update', payload: { nodeId: 'node-1', data: node } }
};
})
]).commit();

OpResult​

interface OpResult {
success: boolean;
data?: any;
error?: string;
inverse?: TransactionOperation; // Inverse operation for undo
}

Transaction Context​

Operations receive a TransactionContext:

interface TransactionContext {
dataStore: DataStore; // DataStore instance
selectionManager: SelectionManager; // SelectionManager instance
selection: SelectionContext; // Selection context (before/current)
schema?: Schema; // Active schema
}

SelectionContext​

class SelectionContext {
before: ModelSelection | null; // Selection before transaction
current: ModelSelection | null; // Current selection (mutable)
}

Example:

defineOperation('myOp', async (operation, context) => {
// Access selection
const before = context.selection.before;
const current = context.selection.current;

// Update selection
if (context.selection.current) {
context.selection.current.startOffset += 5;
}

// Access DataStore
const node = context.dataStore.getNode('node-1');

// Access schema
const nodeType = context.schema?.getNodeType('paragraph');
});

Transaction Manager​

TransactionManager Class​

Manages transaction execution.

Constructor​

new TransactionManager(editor: Editor)

Parameters:

  • editor: Editor - Editor instance

Methods​

execute(operations: (TransactionOperation | OpFunction)[]): Promise<TransactionResult>​

Executes a transaction.

Parameters:

  • operations: (TransactionOperation | OpFunction)[] - Operations to execute

Returns:

  • Promise<TransactionResult> - Transaction result

Behavior:

  1. Acquires global lock
  2. Begins transaction
  3. Starts DataStore overlay
  4. Executes all operations
  5. Ends overlay and commits
  6. Adds to history
  7. Emits events
  8. Calls after hooks

Note: Usually called via transaction().commit(), not directly.

setSchema(schema: Schema): void​

Sets the active schema.

Parameters:

  • schema: Schema - Schema instance

Position Calculator​

Calculates positions in the document.

PositionCalculator Class​

import { PositionCalculator } from '@barocss/model';

Constructor​

new PositionCalculator(dataStore: DataStore)

Parameters:

  • dataStore: DataStore - DataStore instance

Methods​

calculateAbsolutePosition(nodeId: string, offset: number): number​

Calculates absolute position from node ID and offset.

Parameters:

  • nodeId: string - Node ID
  • offset: number - Offset in node

Returns:

  • number - Absolute position

resolveAbsolutePosition(absolutePos: number): { nodeId: string; offset: number } | null​

Resolves absolute position to node ID and offset.

Parameters:

  • absolutePos: number - Absolute position

Returns:

  • { nodeId: string; offset: number } | null - Node ID and offset or null

Operation Registry​

Global Operation Registry​

Operations are registered in a global registry.

globalOperationRegistry​

Global operation registry instance.

Methods:

  • register(name: string, definition: OperationDefinition): void
  • get(name: string): OperationDefinition | undefined
  • getAll(): Map<string, OperationDefinition>
  • clear(): void

Example:

import { globalOperationRegistry } from '@barocss/model';

// Get all operations
const allOps = globalOperationRegistry.getAll();

// Get specific operation
const insertTextOp = globalOperationRegistry.get('insertText');

DSL Operation Registry​

DSL operations are registered separately.

getDefinedDSLOperations(): ReadonlyMap<string, { options?: DefineOperationDSLOptions }>​

Gets all defined DSL operations.

Returns:

  • ReadonlyMap<string, { options?: DefineOperationDSLOptions }> - Map of DSL operations

Example:

import { getDefinedDSLOperations } from '@barocss/model';

const dslOps = getDefinedDSLOperations();
for (const [name, config] of dslOps) {
console.log(`DSL Operation: ${name}`, config);
}

Complete Examples​

Example 1: Simple Transaction​

import { transaction, control, insertText } from '@barocss/model';

const result = await transaction(editor, [
...control('text-1', [
insertText(5, 'Hello')
])
]).commit();

if (result.success) {
console.log('Text inserted successfully');
}

Example 2: Complex Transaction​

import { transaction, control, insertText, toggleMark, create } from '@barocss/model';

const result = await transaction(editor, [
// Create new paragraph
create('paragraph', {}, [
{ stype: 'inline-text', text: 'New paragraph' }
]),

// Insert text and apply mark
...control('text-1', [
insertText(5, ' World'),
toggleMark('bold', [0, 11])
])
]).commit();

Example 3: Custom Operation​

import { defineOperation, defineOperationDSL } from '@barocss/model';

// Define operation
defineOperation('highlight', async (operation, context) => {
const { nodeId, range, color } = operation.payload;

// Apply highlight
context.dataStore.mark.setMarks(nodeId, [
{
type: 'highlight',
range,
attrs: { color }
}
]);

return {
ok: true,
data: context.dataStore.getNode(nodeId),
inverse: { type: 'removeHighlight', payload: { nodeId, range } }
};
});

// Define DSL helper
export const highlight = defineOperationDSL(
(range: [number, number], color: string) => ({
type: 'highlight',
payload: { range, color }
})
);

// Use in transaction
const result = await transaction(editor, [
...control('text-1', [
highlight([0, 10], 'yellow')
])
]).commit();

Example 4: Functional Operation​

import { transaction, op } from '@barocss/model';

const result = await transaction(editor, [
op(async (context) => {
// Complex multi-step operation
const node1 = context.dataStore.getNode('node-1');
const node2 = context.dataStore.getNode('node-2');

// Swap nodes
context.dataStore.content.moveNode('node-1', node2.parentId, 0);
context.dataStore.content.moveNode('node-2', node1.parentId, 0);

return {
success: true,
data: { swapped: true },
inverse: {
type: 'swapNodes',
payload: { node1: 'node-1', node2: 'node-2' }
}
};
})
]).commit();