API / @aionbuilders/nabu

Nabu

Class Stability: stable Stable contract

The editor engine. It owns the document (a Loro CRDT), the block tree and its registry, the selection, the commands and the extensions, and runs the event, clipboard and drag and drop pipelines; NabuEditor renders it. Create one with createEditor (or a preset factory such as createDocumentEditor) rather than new Nabu(...), then read and write the content through serialize, importMarkdown, importJSON, exec and transact. The members documented as public are the stable contract of @aionbuilders/nabu/extend; internals is the unstable door to the rest.

Related guide: Your first editor →

Import

TypeScript
import { Nabu } from '@aionbuilders/nabu';

Constructor

TypeScript
new Nabu(init?: NabuInit)
Parameters of the constructor
ParameterTypeDefaultDescription
initoptionalNabuInit{}n/a

Static members

static BREAK

TypeScript
static BREAK: symbol

What a hook or an onBeforeInput handler returns to stop the engine (and the hooks after it).

static CONTINUE

TypeScript
static CONTINUE: symbol

What a hook returns to let the engine go on (same as returning nothing).

Properties

blocks

TypeScript
blocks: SvelteMap<string, Block>

Every live block of the document, by tree id.

blocksByType

TypeScript
blocksByType: SvelteMap<string, SvelteSet<Block>>

The live blocks of each type (reactive sets, by block type).

BREAK

TypeScript
BREAK: symbol

Nabu.BREAK, reachable from the instance a hook receives.

children

TypeScript
children: Block[]

The root blocks of the document, in order (reactive).

commands

TypeScript
commands: CommandRegistry

Commands and keyboard shortcuts (ADR 017): list(), get(id), has(id), run(id, data), offer(id). A plain snapshot API; the set of commands is fixed once the editor is built.

components

TypeScript
components: SvelteMap<string, Component<any, any, string>>

The Svelte components registered by the loaded extensions, by block type.

CONTINUE

TypeScript
CONTINUE: symbol

Nabu.CONTINUE, reachable from the instance a hook receives.

debugging

TypeScript
debugging: boolean

When true, nabu.warn() logs to the console ([Nabu]).

defaultBlockType

TypeScript
defaultBlockType: string | null

Type of the default text block (declared by an extension with defaultBlock: true): created by the engine when it needs an empty text block of its own, e.g. a caret after a trailing void block.

doc

TypeScript
doc: LoroDoc<Record<string, Container>>

The Loro document behind the editor (public: connect it to a server with doc.subscribeLocalUpdates and importRemote).

extensions

TypeScript
extensions: Extension[]

The extensions the editor was built with, in order.

placeholders

TypeScript
placeholders: ResolvedPlaceholders

Resolved placeholders option (see NabuInit). Reassign to change it at runtime.

positions

TypeScript
positions: number

Number of caret positions in the document (the sum of the root blocks').

registry

TypeScript
registry: SvelteMap<string, typeof Block>

The block classes registered by the loaded extensions, by block type.

selection

TypeScript
selection: NabuSelection

The text selection of the editor (a reactive wrapper of the DOM selection, in terms of blocks).

serializers

TypeScript
serializers: Map<string, (nabu: Nabu) => any>

Root-level serializers. Each function receives the Nabu instance and returns the serialized document.

slashCommands

TypeScript
slashCommands: SlashCommand[]

Slash-menu entries contributed by extensions (Extension#slashCommands); the slash menu lists ONLY these (plus the integrator's extras), sorted by group / order, keeping those whose when(engine, block) holds at the caret.

spellcheck

TypeScript
spellcheck: boolean

Resolved spellcheck option (see NabuInit). Reactive: assign to change it at runtime.

systems

TypeScript
systems: SvelteMap<string, { start?: () => void; stop?: () => void; [key: string]: any }>

Registered subsystems. Each system should implement:

  • start(): void — called when the editor mounts
  • stop(): void — called when the editor unmounts

tabHint

TypeScript
tabHint: string | false

Announcement of the Escape-then-Tab way out (option tabHint), false for none. Reactive.

tree

TypeScript
tree: LoroTree<NabuNodeData>

The document tree (Loro): one node per block, node.data holds the block's type and props.

commandAnnouncement readonly

TypeScript
readonly commandAnnouncement: string

Text of the polite live region of the editor (prompts and outcomes of confirmations). Reactive.

composing readonly

TypeScript
readonly composing: boolean

True during an IME composition: the browser owns the DOM, the caret must not be touched.

composingBlock readonly

TypeScript
readonly composingBlock: Block | null

The block holding the running composition, or null. Reactive.

internals Unstable readonly

TypeScript
readonly internals: NabuInternals

The door to everything the engine uses internally that the stable contract (the members tagged public in their JSDoc) does not cover: the command-confirm controller, the action bus, the hook and rule registries, the clipboard / drop / event pipelines, undo capture, stored marks, the selection's repair machinery. They are the same references the engine runs on, not copies.

The door itself is part of the stable contract; @unstable is everything behind it: nothing reached through internals is a promise, it may change, move or disappear in ANY minor version. Prefer the stable members; use this when you must, and pin the version of the package. The set of keys is frozen by a test, so a change shows up in the changelog.

isEmpty readonly

TypeScript
readonly isEmpty: boolean

True when the document has no block.

lang

TypeScript
lang: string | undefined

Resolved lang option (see NabuInit): a valid BCP 47 tag, or undefined (inherit the page language). Reactive: assign to change it at runtime; an invalid value is ignored (warning) and leaves it unchanged.

lastVisibleBlock readonly

TypeScript
readonly lastVisibleBlock: Block | null

The deepest last block of the document that can hold a caret in the rendered document: follows the last child at each level, but never enters a block that hides its children.

pendingCommand readonly

TypeScript
readonly pendingCommand: PendingCommand | null

The command waiting for the user's confirmation (ADR 017 §3.4), reactive; null when none: { id, strategy: 'hold' | 'invite' | 'choose', label, display, message, target?, progress, options?, selectedIndex }. progress (hold, 0 → 1) and selectedIndex (choose) are reactive fields of that object.

readonly

TypeScript
readonly: boolean

Read-only mode (see the readonly option of NabuInit). Reactive: assign to switch it on or off at runtime. While true the user cannot change the document: input, paste, cut, drop, drag moves and commands (shortcuts, commands.run, exec of a command, undo / redo) are ignored by the engine, and the UI components hide themselves. Programmatic edits by the host (insert, importJSON...) are not blocked. View state that is not part of the document (a toggle opened to read it) stays possible and is never written to the document.

tab

TypeScript
tab: TabMode

Resolved tab option (see NabuInit): what Tab / Shift+Tab do in a plain text block. Reactive: assign to change it at runtime; an unknown value warns and gives the default of the editor (its preset).

Methods

cancelPendingCommand()

TypeScript
cancelPendingCommand(): void

Cancel the pending confirmation, if any: nothing runs.

commit()

TypeScript
commit(options?: object): Promise<void>

Commit the pending changes as one Loro transaction.

Parameters of commit
ParameterTypeDefaultDescription
optionsoptionalobject{}

origin tags the transaction; an origin starting with nabu:silent is not recorded as an undo step (background completions that must not be undone on their own).

options.originoptionalstringn/an/a

createPasteBlock()

TypeScript
createPasteBlock(pb: PasteBlock, parentId: string): Block | null

Creates a block inside a container, recursively handling nested children. Public: part of the paste toolbox a block's acceptPaste hook builds on. No commit.

Parameters of createPasteBlock
ParameterTypeDescription
pbPasteBlockn/a
parentIdstringn/a

Returns Block | null

the created block

delete()

TypeScript
delete(block: Block): void

Delete a block (and its children) from the tree. No commit: call commit() afterwards.

Parameters of delete
ParameterTypeDescription
blockBlockn/a

exec()

TypeScript
exec(topic: string, data?: any): Promise<PulseEvent>

Dispatch an action to all registered handlers for the given topic. Re-syncs the model selection from the DOM first: selectionchange is async, so right after a keyboard-only selection change (Shift+Arrow, Home/End, Ctrl+A) the model would be stale.

Parameters of exec
ParameterTypeDescription
topicstringn/a
dataoptionalanyn/a

focus()

TypeScript
focus(options?: object): Promise<void>

Place the DOM + model selection. Asynchronous by nature: it waits for Svelte to flush pending state (tick()), because the target blocks' DOM may not be rendered yet (e.g. right after a commit/insert). The returned promise resolves once the selection has been set, i.e. once nabu.selection reflects it: await nabu.focus(...) before exec(...) / typing. Fire-and-forget callers are unaffected.

Parameters of focus
ParameterTypeDescription
optionsoptionalobjectn/a
options.fromoptional{ block?: Block; offset?: number }n/a
options.from.blockoptionalBlockn/a
options.from.offsetoptionalnumbern/a
options.offsetoptionalnumbern/a
options.tooptional{ block?: Block; offset?: number }n/a
options.to.blockoptionalBlockn/a
options.to.offsetoptionalnumbern/a

importFragment()

TypeScript
importFragment(fragment: PasteFragment): Promise<void>

Replace the entire document with the given PasteFragment. Unlike insertFragment(), this does not depend on a live DOM selection: blocks are created at the root through the same registry pipeline as paste.

Parameters of importFragment
ParameterTypeDescription
fragmentPasteFragmentn/a

importJSON()

TypeScript
importJSON(json: string | { assets?: Record<string, EmbeddedAsset>; blocks: {
        children?: any[];
        content?: string | { text: string; [mark: string]: any }[];
        props?: Record<string, any>;
        type: string;
        [key: string]: any;
      }[]; version?: string | number }): Promise<void>

Replace the entire document with a serialize('json') export. An export of a version this editor does not know is refused with a warning (the document is left untouched), like the clipboard does; a block type that is not registered is degraded to the default text block (degradeUnknownBlocks), nothing is dropped.

Parameters of importJSON
ParameterTypeDescription
jsonstring | { assets?: Record<string, EmbeddedAsset>; blocks: { children?: any[]; content?: string | { text: string; [mark: string]: any }[]; props?: Record<string, any>; type: string; [key: string]: any; }[]; version?: string | number }n/a

importMarkdown()

TypeScript
importMarkdown(markdown: string): Promise<void>

Replace the entire document with content parsed from a Markdown string. Requires MarkdownPasteExtension (or any text/plain interpreter) to be loaded.

Parameters of importMarkdown
ParameterTypeDescription
markdownstringn/a

importRemote()

TypeScript
importRemote(bytes: Uint8Array<ArrayBufferLike>): ImportStatus

Import changes made ELSEWHERE (another tab, a peer, a stored copy of the document: ADR 010 multi-tab sync). They are the document's own history, not the user's actions: Loro's UndoManager records local commits only, so an import is never an undo step (undoing the user's own edits afterwards still works, Loro transforms them). The DOM follows through the normal Loro events. The caret of the local user stays on the same CHARACTER: it is captured as Loro cursors before the import and put back after the render (a text node rewritten by Svelte would otherwise send it to the start of the block); the page is never scrolled for it. Idempotent (importing what the document already has changes nothing). Called DURING an IME composition it is applied at once (not held): the model is safe, because the composition anchor is a Loro cursor that follows the edit, and the caret restore is skipped meanwhile (the browser owns it). (persistence/sync.js still holds the updates it receives while composing, which keeps the composing text node untouched by the render.)

Parameters of importRemote
ParameterTypeDescription
bytesUint8Array<ArrayBufferLike>

a Loro snapshot or update

insert()

TypeScript
insert(
  type: string,
  props?: Record<string, any>,
  parentId?: string | null,
  index?: number | null
): Block

Insert a new block of a registered type and commit (throws when the type is not registered).

Parameters of insert
ParameterTypeDefaultDescription
typestringn/a

Block type ('paragraph', or the name of an extension that declares a block)

propsoptionalRecord<string, any>{}

Initial props, read by the class's static create (text, delta...)

parentIdoptionalstring | nullnull

Id of the parent block (null: the document root)

indexoptionalnumber | nullnull

Position among the siblings (null: last)

Returns Block

The created block

insertPasteBlock()

TypeScript
insertPasteBlock(pb: PasteBlock, afterNode: any): any

Inserts a PasteBlock after afterNode. Handles both leaf blocks (wrapOrphan) and container blocks (creates container, recurses into children). Returns the outer node to use as the next prevNode. Public: part of the paste toolbox a block's acceptPaste hook builds on. No commit.

Parameters of insertPasteBlock
ParameterTypeDescription
pbPasteBlockn/a
afterNodeanyn/a

isMarkActive()

TypeScript
isMarkActive(markName: string): boolean

True when every selected text run carries the given mark. Reactive: reads block.selected and the behaviors' selections/deltas, so it can be used directly in component templates (e.g. FloatingToolbar).

Parameters of isMarkActive
ParameterTypeDescription
markNamestringn/a

isolateUndoStep()

TypeScript
isolateUndoStep(): void

Make the NEXT commit its own undo step (and the typing right after it another one), even though it holds no structural op: for a non-structural change that must undo on its own, e.g. a flag toggled from the keyboard, which would otherwise merge with the typing around it. Call it just before commit().

openTrailingCaret()

TypeScript
openTrailingCaret(): Promise<Block | null>

The document must always offer a caret after its last block. Call it on user intent (ArrowDown / ArrowRight leaving the document, a click below the last block): when the last block holds text, the caret goes to its end; when it does not (a void block such as a divider / image, or a container whose end is not a text position), an empty default text block is appended at the root, in one undo step, and takes the caret. Never called eagerly on load.

Returns Promise<Block | null>

The block now holding the caret

pendingCommandOn()

TypeScript
pendingCommandOn(target: any): PendingCommand | null

The pending confirmation when it is ABOUT target (what confirm.target(ctx) returned: usually a block), else null. Reactive: a block that draws its own hold progress reads nabu.pendingCommandOn(block)?.progress.

Parameters of pendingCommandOn
ParameterTypeDescription
targetanyn/a

redo()

TypeScript
redo(): void

Redo the last undone operation. The selection comes back where that operation ended.

resolveDocumentOffset()

TypeScript
resolveDocumentOffset(globalOffset: number): { block: Block; offset: number } | null

The block and local offset that hold a document-wide caret offset (positions counted from the start of the document).

Parameters of resolveDocumentOffset
ParameterTypeDescription
globalOffsetnumbern/a

serialize()

TypeScript
serialize(format: string): any

Serialize the document with the root serializer registered for format ('markdown', 'json', or one an extension adds with serializers); null (and a warning) for an unknown format.

Parameters of serialize
ParameterTypeDescription
formatstringn/a

shortcut()

TypeScript
shortcut(block: Block, trigger: string, transform: (behavior: any) => void): Promise<void>

Apply a markdown BLOCK shortcut (# , - , > , --- …) so that undo gives back exactly what the user typed, trigger included: one Ctrl+Z after # leaves a paragraph "# " with the caret after the space; a second one removes the typing as usual.

  1. trigger (the swallowed space) is inserted at the caret and committed like normal typing;
  2. transform runs (delete the prefix incl. the trigger, change/replace the block…) and is committed as its own structural undo step (see commit()), whose stored caret is right after the trigger. Both commits run in the same synchronous turn, so the intermediate "# " is never painted.

For a trigger that is not text (``` + Enter), pass trigger = '': nothing is inserted and undo restores the text typed before the key (the fence line, without the Enter).

Parameters of shortcut
ParameterTypeDescription
blockBlock

the block the shortcut was typed in

triggerstring

text of the trigger key, inserted before transforming ('' = none)

transform(behavior: any) => void

mutations only (it may commit itself, e.g. via transformTo)

Returns Promise<void>

resolves immediately, doing nothing, when block.transformsEnabled === false

transact()

TypeScript
transact(mutateFn: () => void, cursorIntent?: { block: Block; offset: number } | null): Promise<void>

Run Loro mutations as a single transaction, then optionally restore the cursor. Centralizes the "mutate → commit → setCursor" dance used by most actions.

Parameters of transact
ParameterTypeDefaultDescription
mutateFn() => voidn/a

Mutations Loro ici. Ne PAS appeler commit() dedans.

cursorIntentoptional{ block: Block; offset: number } | nullnulln/a

trigger()

TypeScript
trigger(hookName: string, ...args: any[]): void

Run the hookName hooks of every extension, in order, until one returns BREAK. Each is called as hook(nabu, ...args).

Parameters of trigger
ParameterTypeDescription
hookNamestringn/a
...argsany[]n/a

undo()

TypeScript
undo(): void

Undo the last operation. The selection as it was before that operation comes back (see ADR 015).

warn()

TypeScript
warn(...args: any[]): void

Log a diagnostic to the console when debugging is on. Silent otherwise.

Parameters of warn
ParameterTypeDescription
...argsany[]n/a

Source: src/lib/blocks/nabu.svelte.js:160