Overview
Professional editors require features that go beyond content manipulation. This section covers complex capabilities often needed for production apps.
Search & Replace
Do not use window.find(). It
affects selection and is limited. Instead,
implement model-based search.
- Search: Traverse the model text nodes, matching regex. Collect paths/ranges.
- Highlight: Apply a temporary "decoration" or "mark" to the matched ranges.
- Replace: Dispatch a transaction replacing the range content.
function searchModel(doc, query) {
const matches = [];
doc.descendants((node, pos) => {
if (node.isText) {
// Regex match on node.text
// push { from: pos + match.index, to: ... }
}
});
return matches;
}Real-time Collaboration
Modern collaboration uses CRDTs (Conflict-free Replicated Data Types) like Yjs or Automerge.
Integration Strategy
Bind your editor model to a Yjs Y.XmlFragment. Listen for Yjs updates to patch the
editor, and broadcast editor
transactions to Yjs.
// 1. Editor -> Yjs
editor.on('transaction', (tr) => {
yDoc.transact(() => {
applyStepsToYjs(tr.steps, yXmlFragment);
});
});
// 2. Yjs -> Editor
yXmlFragment.observe((event) => {
const tr = convertYjsEventsToSteps(event);
editor.dispatch(tr);
});History Visualizer
For debugging undo/redo stacks, create a visualizer that renders the transaction history.
- State: Capture a snapshot of the document at each step.
- Diff: Highlight what changed in that transaction.
- Selection: Show where the user cursor was.