Editing & block API
Everything in Ezynota flows through transactions — all-or-nothing units of work with mechanical undo. The public block API is a thin, safe layer over that pipeline, and the DOM is only a view of the document state.
Reading the document
// Frozen snapshot of the current document
const doc = editor.getSnapshot();
// Flat list of block references (including nested children, flattened)
const refs = editor.getBlocks();
// [{ id, type, data, tunes, ... }, ...]
const ref = editor.getBlockById("b1");
const index = editor.getBlockIndex("b1");BlockRef objects are lightweight, readonly views — always re-read after a change rather than caching them.
Creating and modifying blocks
// Insert at the end; returns the new block id
const id = editor.insertBlock("paragraph", { content: [] });
// Control placement & focus
editor.insertBlock(
"heading",
{ level: 2, content: [{ type: "text", text: "Title" }] },
{ after: id, focus: true }
);
// Update data (one undoable transaction)
editor.updateBlock(id, { level: 3, content: [] });
// Duplicate / convert / move / remove
const copyId = editor.duplicateBlock(id);
editor.convertBlock(id, "quote");
editor.moveBlock(id, { index: 0 });
editor.removeBlock(id);insertBlock options: { index?, after?, before?, focus? }.
Save & render
// Collect every tool's save() output, validate, and return the document.
// Rejects with EZ_INVALID_DATA if any block is invalid.
const doc = await editor.save();
// Replace the entire document (migrates schema, resets history)
await editor.render(otherDocument);
// Remove everything in one transaction
editor.clear();Undo / redo
History is per-note in workspace mode and automatically coalesced (rapid typing collapses into single undo steps; a paste is one step).
editor.undo();
editor.redo();
editor.canUndo(); // boolean
editor.canRedo(); // boolean
// Per-note history can be exported/imported for persistence
const state = editor.exportHistoryState();
editor.importHistoryState(state);Focus & selection
editor.focus(); // sensible default focus
editor.focus({ at: "end" }); // "start" | "end" | "default"
editor.focus({ blockId: id }); // focus a specific blockRead-only mode
editor.setReadOnly(true);
editor.readOnly; // true (also true while the workspace is loading)Emits readOnly:changed.
Slash menu & block picker
Typing / opens the searchable block picker. Programmatically:
An empty paragraph shows a focused hint for this shortcut. Open Block actions → Turn into… to search for a different block type; Duplicate, Move and Delete stay in the shorter actions menu. Explicit conversion preserves literal text beginning with /.
Conversion controls offer declared built-in text conversions and explicit tool-provided data mappers. A compound or custom block without a mapper is not offered a conversion that could discard its data. Use Add content below or the slash picker to add images, tables and other new blocks without replacing existing text.
editor.openBlockPicker(); // pick for the active block
editor.openBlockPicker(blockId); // convert a specific block
editor.openBlockPicker(blockId, true); // insert mode (adds after target)Commands
All built-in behavior is also reachable through the command bus — useful for custom toolbars:
editor.dispatch("EZ_UNDO");
editor.dispatch("EZ_INSERT_BLOCK", { type: "callout", data: { variant: "info", content: [] } });See the Commands reference.
Find & replace
In workspace and document modes, open Find in document to search without changing your content. The match counter and Previous/Next controls show where you are. In the Find field, Enter moves forward and Shift+Enter moves backward; Escape closes the bar and returns focus. Replacement only runs through Replace, Replace all, or Enter in the Replace with field. Replace targets the current match, and Replace all is one Undo step.
Matching is literal and case-sensitive, within individual text runs. A phrase split across formatting boundaries and text inside nested child blocks is not included. Search highlights are a view over the document and do not become saved formatting.
editor.findReplace("TODO", "DONE"); // replace first match
editor.findReplace("TODO", "DONE", true); // replace all (one undo step)In workspace mode, editor.workspace.findInDocument(...) wraps the same engine.
Editing in a narrow container
The notes sidebar becomes a drawer when the editor container is 900px or narrower, including embeds on wide pages. Use Toggle notes sidebar to open it. Escape or Close notes sidebar returns you to the document. On wider containers, the sidebar keeps your collapsed/expanded preference.
Document mode hides note navigation and uses the topbar title to rename the active document. Workspace mode keeps the workspace name in the topbar and the note title above the content.
Workspace convenience
editor.setDocumentTitle("Meeting notes");
editor.currentTitle();
editor.wordCount(); // workspace mode
editor.search("query"); // workspace modeError handling
API calls throw typed EzynotaError instances with a code:
try {
editor.updateBlock("missing", {});
} catch (e) {
if (e.code === "EZ_BLOCK_NOT_FOUND") { /* … */ }
}