Skip to main content

Model Operation DSL API

DSL (Domain-Specific Language) helpers provide a simple, declarative API for defining operations. They return { type, payload } objects that are used in transactions.

Overview

DSL helpers are defined using defineOperationDSL() and are used with control() to create transaction operations.

Usage Pattern

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

// DSL helper returns { type, payload }
const op = insertText(5, 'Hello');
// { type: 'insertText', payload: { pos: 5, text: 'Hello' } }

// With control (injects nodeId)
const ops = control('text-1', [
insertText(5, 'Hello')
]);
// [{ type: 'insertText', payload: { nodeId: 'text-1', pos: 5, text: 'Hello' } }]

// Use in transaction
const result = await transaction(editor, ops).commit();

Control Function

The control() function injects a nodeId into operation payloads:

control(nodeId, [
insertText(5, 'Hello'),
toggleMark('bold', [0, 5])
])

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

Text Operations

insertText

Inserts text at a position.

Signature:

insertText(pos: number, text: string): DSLOperationDescriptor
insertText(nodeId: string, pos: number, text: string): DSLOperationDescriptor

Returns:

{
type: 'insertText';
payload: { pos: number; text: string; nodeId?: string };
}

Example:

// In control chain
control('text-1', [
insertText(5, 'Hello')
])

// Direct call
insertText('text-1', 5, 'Hello')

deleteTextRange

Deletes text in a range.

Signature:

deleteTextRange(startPosition: number, endPosition: number): DSLOperationDescriptor
deleteTextRange(nodeId: string, startPosition: number, endPosition: number): DSLOperationDescriptor

Returns:

{
type: 'deleteTextRange';
payload: { startPosition: number; endPosition: number; nodeId?: string };
}

Example:

control('text-1', [
deleteTextRange(0, 5)
])

replaceText

Replaces text in a range.

Signature:

replaceText(newText: string): DSLOperationDescriptor
replaceText(nodeId: string, newText: string): DSLOperationDescriptor
replaceText(nodeId: string, startPosition: number, endPosition: number, newText: string): DSLOperationDescriptor

Returns:

{
type: 'replaceText';
payload: { newText: string; nodeId?: string; startPosition?: number; endPosition?: number };
}

Example:

control('text-1', [
replaceText('New text')
])

// Or with range
replaceText('text-1', 0, 5, 'New text')

Node Operations

create

Creates a node with children.

Signature:

create(stype: string, attributes?: Record<string, any>, content?: INode[]): DSLOperationDescriptor

Returns:

{
type: 'create';
payload: { node: INode; options?: any };
}

Example:

// Create paragraph with text
create('paragraph', {}, [
{ stype: 'inline-text', text: 'Hello' }
])

// Create heading
create('heading', { level: 1 }, [
{ stype: 'inline-text', text: 'Title' }
])

delete

Deletes a node.

Signature:

delete(): DSLOperationDescriptor
delete(nodeId: string): DSLOperationDescriptor

Returns:

{
type: 'delete';
payload: { nodeId?: string };
}

Example:

// In control chain
control('node-1', [
delete()
])

// Direct call
delete('node-1')

update

Updates a node with partial changes.

Signature:

update(updates: Partial<INode>): DSLOperationDescriptor

Returns:

{
type: 'update';
payload: { updates: Partial<INode>; nodeId?: string };
}

Example:

control('text-1', [
update({ text: 'Updated text' })
])

control('node-1', [
update({ attributes: { level: 2 } })
])

Content Operations

addChild

Adds a child to a parent.

Signature:

addChild(child: INode | string, position?: number): DSLOperationDescriptor
addChild(nodeId: string, child: INode | string, position?: number): DSLOperationDescriptor

Returns:

{
type: 'addChild';
payload: { child: INode | string; position?: number; nodeId?: string };
}

Example:

// Add existing node
control('parent-1', [
addChild('child-1', 0)
])

// Create and add new node
control('parent-1', [
addChild({ stype: 'paragraph', text: 'New' }, 0)
])

removeChild

Removes a child from parent.

Signature:

removeChild(childId: string): DSLOperationDescriptor
removeChild(nodeId: string, childId: string): DSLOperationDescriptor

Returns:

{
type: 'removeChild';
payload: { childId: string; nodeId?: string };
}

Example:

control('parent-1', [
removeChild('child-1')
])

moveNode

Moves a node to a new parent.

Signature:

moveNode(newParentId: string, position?: number): DSLOperationDescriptor
moveNode(nodeId: string, newParentId: string, position?: number): DSLOperationDescriptor

Returns:

{
type: 'moveNode';
payload: { newParentId: string; position?: number; nodeId?: string };
}

Example:

control('node-1', [
moveNode('new-parent-1', 0)
])

reorderChildren

Reorders children in a parent.

Signature:

reorderChildren(childIds: string[]): DSLOperationDescriptor
reorderChildren(nodeId: string, childIds: string[]): DSLOperationDescriptor

Returns:

{
type: 'reorderChildren';
payload: { childIds: string[]; nodeId?: string };
}

Example:

control('parent-1', [
reorderChildren(['child-3', 'child-1', 'child-2'])
])

Mark Operations

applyMark

Applies a mark to a range.

Signature:

applyMark(markType: string, range: [number, number], attrs?: Record<string, any>): DSLOperationDescriptor
applyMark(nodeId: string, markType: string, range: [number, number], attrs?: Record<string, any>): DSLOperationDescriptor

Returns:

{
type: 'applyMark';
payload: { markType: string; range: [number, number]; attrs?: Record<string, any>; nodeId?: string };
}

Example:

control('text-1', [
applyMark('bold', [0, 5]),
applyMark('link', [0, 5], { href: 'https://example.com' })
])

removeMark

Removes a mark from a range.

Signature:

removeMark(markType: string, range?: [number, number]): DSLOperationDescriptor
removeMark(nodeId: string, markType: string, range?: [number, number]): DSLOperationDescriptor

Returns:

{
type: 'removeMark';
payload: { markType: string; range?: [number, number]; nodeId?: string };
}

Example:

control('text-1', [
removeMark('bold', [0, 5])
])

toggleMark

Toggles a mark on/off.

Signature:

toggleMark(markType: string, range: [number, number], attrs?: Record<string, any>): DSLOperationDescriptor
toggleMark(nodeId: string, markType: string, range: [number, number], attrs?: Record<string, any>): DSLOperationDescriptor

Returns:

{
type: 'toggleMark';
payload: { markType: string; range: [number, number]; attrs?: Record<string, any>; nodeId?: string };
}

Example:

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

Split/Merge Operations

splitTextNode

Splits a text node at a position.

Signature:

splitTextNode(splitPosition: number): DSLOperationDescriptor
splitTextNode(nodeId: string, splitPosition: number): DSLOperationDescriptor

Returns:

{
type: 'splitTextNode';
payload: { splitPosition: number; nodeId?: string };
}

Example:

control('text-1', [
splitTextNode(5)
])

mergeTextNodes

Merges two adjacent text nodes.

Signature:

mergeTextNodes(rightNodeId: string): DSLOperationDescriptor
mergeTextNodes(nodeId: string, rightNodeId: string): DSLOperationDescriptor

Returns:

{
type: 'mergeTextNodes';
payload: { rightNodeId: string; nodeId?: string };
}

Example:

control('text-1', [
mergeTextNodes('text-2')
])

splitBlockNode

Splits a block node at a position.

Signature:

splitBlockNode(splitPosition: number): DSLOperationDescriptor
splitBlockNode(nodeId: string, splitPosition: number): DSLOperationDescriptor

Example:

control('block-1', [
splitBlockNode(2)
])

mergeBlockNodes

Merges two adjacent block nodes.

Signature:

mergeBlockNodes(rightNodeId: string): DSLOperationDescriptor
mergeBlockNodes(nodeId: string, rightNodeId: string): DSLOperationDescriptor

Example:

control('block-1', [
mergeBlockNodes('block-2')
])

Transform Operations

wrap

Wraps a range with a new node.

Signature:

wrap(wrapperType: string, wrapperAttrs?: Record<string, any>): DSLOperationDescriptor
wrap(nodeId: string, endNodeId: string, wrapperType: string, wrapperAttrs?: Record<string, any>): DSLOperationDescriptor

Example:

// Wrap selection
wrap('blockquote', {})

// Or with explicit range
wrap('text-1', 'text-3', 'blockquote', {})

unwrap

Removes wrapper node.

Signature:

unwrap(): DSLOperationDescriptor
unwrap(nodeId: string): DSLOperationDescriptor

Example:

control('wrapper-1', [
unwrap()
])

indentNode / outdentNode

Indents or outdents a block node.

Signature:

indentNode(): DSLOperationDescriptor
indentNode(nodeId: string): DSLOperationDescriptor

outdentNode(): DSLOperationDescriptor
outdentNode(nodeId: string): DSLOperationDescriptor

Example:

control('block-1', [
indentNode()
])

control('block-1', [
outdentNode()
])

indentText / outdentText

Indents or outdents text.

Signature:

indentText(prefix?: string): DSLOperationDescriptor
indentText(nodeId: string, prefix?: string): DSLOperationDescriptor

outdentText(prefix?: string): DSLOperationDescriptor
outdentText(nodeId: string, prefix?: string): DSLOperationDescriptor

Example:

control('text-1', [
indentText(' ')
])

control('text-1', [
outdentText(' ')
])

Clipboard Operations

copy

Copies selected content.

Signature:

copy(): DSLOperationDescriptor

Example:

// Uses current selection
const ops = [copy()];

cut

Cuts selected content.

Signature:

cut(): DSLOperationDescriptor

Example:

// Uses current selection
const ops = [cut()];

paste

Pastes content at selection.

Signature:

paste(content?: INode[]): DSLOperationDescriptor

Example:

// Paste from clipboard
const ops = [paste()];

// Paste specific content
const ops = [paste([
{ stype: 'paragraph', text: 'Pasted' }
])];

Utility Operations

autoMergeTextNodes

Automatically merges adjacent text nodes.

Signature:

autoMergeTextNodes(): DSLOperationDescriptor
autoMergeTextNodes(nodeId: string): DSLOperationDescriptor

Example:

control('text-1', [
autoMergeTextNodes()
])

cloneNodeWithChildren

Clones a node with all its children.

Signature:

cloneNodeWithChildren(newParentId?: string): DSLOperationDescriptor
cloneNodeWithChildren(nodeId: string, newParentId?: string): DSLOperationDescriptor

Example:

control('node-1', [
cloneNodeWithChildren('new-parent-1')
])

Complete Example

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

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

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

// Move node
...control('node-1', [
moveNode('new-parent-1', 0)
])
]).commit();