@barocss/model
The Model package provides high-level model operations and a transaction DSL for editing operations. It's the abstraction layer over DataStore operations.
Purpose
High-level model operations and transaction DSL. Provides a declarative way to modify documents through transactions.
Key Exports
transaction()- Transaction DSL buildercontrol()- Control flow in transactionsinsertText()- Insert text operationdefineOperation()- Define custom operationsdefineOperationDSL()- Define operations with DSL helpers
Basic Usage
import { transaction, control, insertText } from '@barocss/model';
// Execute a transaction
await transaction(editor, control('text-1', [
insertText({ text: 'Hello' })
])).commit();
Transaction DSL
Transactions are declarative operations that group multiple operations together:
import { transaction, node, textNode, mark } from '@barocss/model';
// Describe nodes with DSL
const para = node('paragraph', { align: 'left' }, [
textNode('inline-text', 'Hello ', [mark('bold')]),
textNode('inline-text', 'World')
]);
// Execute transaction
const result = await transaction(editor, [
{ type: 'create', payload: { node: para } }
]).commit();
if (!result.success) {
console.error('Transaction failed:', result.error);
}
Transaction lifecycle:
- Acquire global lock from DataStore (FIFO queue)
- Start transaction → DataStore.begin()
- Execute each operation
- End transaction → DataStore.end()
- Commit → DataStore.commit() (or rollback on error)
- Release lock
Transaction result:
interface TransactionResult {
success: boolean;
error?: string;
operations?: TransactionOperation[];
transactionId?: string;
}
Operation Helpers
Pre-built operations for common tasks:
import {
insertText, deleteText, applyMark, removeMark, toggleMark,
create, update, delete: deleteNode, moveNode,
transformNode, wrap, unwrap,
indentNode, outdentNode, moveBlockUp, moveBlockDown
} from '@barocss/model';
// Text operations
insertText({ text: 'Hello', nodeId: 'text-1', offset: 0 })
deleteText({ nodeId: 'text-1', range: [0, 5] })
replaceText({ nodeId: 'text-1', start: 0, end: 5, newText: 'Hi' })
// Mark operations
applyMark({ nodeId: 'text-1', mark: 'bold', range: [0, 5] })
removeMark({ nodeId: 'text-1', mark: 'bold', range: [0, 5] })
toggleMark({ nodeId: 'text-1', mark: 'bold', range: [0, 5] })
// Node operations
create({ node: paragraphNode })
update({ nodeId: 'p1', changes: { text: 'Updated' } })
deleteNode({ nodeId: 'p1' })
moveNode({ nodeId: 'p1', targetParentId: 'doc-1', targetIndex: 0 })
// Transform operations
transformNode({ nodeId: 'p1', newType: 'heading', attrs: { level: 1 } })
wrap({ nodeId: 'p1', wrapperType: 'blockquote' })
unwrap({ nodeId: 'p1' })
// Block operations
indentNode({ nodeId: 'p1' })
outdentNode({ nodeId: 'p1' })
moveBlockUp({ nodeId: 'p1' })
moveBlockDown({ nodeId: 'p1' })
Control Helper
The control() helper groups operations for a specific node:
import { control, toggleMark, replaceText } from '@barocss/model';
const ops = [
...control('text-1', [
replaceText({ start: 6, end: 11, newText: 'Universe' }),
toggleMark('bold', [0, 5])
])
];
await transaction(editor, ops).commit();
Custom Operations
Define your own operations:
Runtime Implementation
import { defineOperation } from '@barocss/model/operations';
defineOperation('highlightText', async (operation, context) => {
const { nodeId, range } = operation.payload;
await context.dataStore.applyMark(nodeId, 'highlight', range);
return { ok: true };
});
DSL Helper
import { defineOperationDSL } from '@barocss/model/operations';
// Define DSL helper
export const highlightText = defineOperationDSL((nodeId: string, range: [number, number]) => {
return {
type: 'highlightText',
payload: { nodeId, range }
};
});
// Use in transaction
await transaction(editor, [
highlightText('text-1', [0, 5])
]).commit();
Operation context:
interface TransactionContext {
dataStore: DataStore;
schema: Schema;
selection: SelectionManager;
editor: Editor;
}
PositionCalculator
Calculate absolute positions and find nodes by position:
import { PositionCalculator } from '@barocss/model';
const calc = new PositionCalculator(editor.dataStore);
// Calculate absolute position from node + offset
const absPos = calc.calculateAbsolutePosition('text-1', 3);
// Find node by absolute position
const pos = calc.findNodeByAbsolutePosition(absPos);
// Returns: { nodeId: 'text-1', offset: 3 }
Use cases:
- Selection management
- Range operations
- Position-based queries
Transaction Features
- Atomic: All operations succeed or fail together
- Undoable: Transactions can be undone
- Schema-aware: Validates against schema
- Type-safe: Full TypeScript support
- Lock-based: Uses DataStore lock for concurrency
When to Use
- Command Implementation: Extensions use transactions in commands
- Batch Operations: Group multiple operations together
- Custom Operations: Define domain-specific operations
Integration
Model package works with:
- DataStore: All operations go through DataStore
- Editor: Commands execute transactions
- Extensions: Extensions use transactions
Related
- Core Concepts: Schema & Model - Understanding the model
- DataStore Package - The storage layer
- Editor Core Package - How editor orchestrates transactions
- Extension Design - How extensions use transactions