API / @aionbuilders/nabu/extend

Block

Class Stability: stable Stable contract

The base class of every block. A block wraps a node of the document tree (Loro) and holds the behaviour of one block type: the capability flags, the tree accessors, and the handlers for typing, keys, splitting, merging, pasting and serialization that a subclass overrides.

Related guide: Extensions →

Import

TypeScript
import { Block } from '@aionbuilders/nabu/extend';

Constructor

TypeScript
new Block(nabu: Nabu, node: NabuNode)
Parameters of the constructor
ParameterTypeDescription
nabuNabun/a
nodeNabuNoden/a

Static members

static fromHTML

TypeScript
static fromHTML: ((el: Element, helpers: HtmlHelpers, rule: HtmlRule, siblings: Element[]) => PasteBlock | PasteBlock[] | null) | undefined

Make the block(s) of an element matched by one of htmlRules; null / [] declines. siblings are the following elements htmlSiblings claimed.

static fromMarkdown

TypeScript
static fromMarkdown: ((lines: string[], helpers: MarkdownHelpers, rule: MarkdownRule) => PasteBlock | PasteBlock[] | null) | undefined

Make the block(s) of the lines a markdownRules rule consumed; null declines (the next rule is tried).

static htmlRules

TypeScript
static htmlRules: HtmlRule[] | undefined

Which HTML elements this block reads on paste (see HtmlRule). Declare it together with fromHTML.

static htmlSiblings

TypeScript
static htmlSiblings: ((el: Element, rule: HtmlRule, helpers: HtmlHelpers) => Element[]) | undefined

Ask to absorb the elements that follow el (a structure that spans several elements, a list in Google Docs); returns them in order.

static markdownRules

TypeScript
static markdownRules: MarkdownRule[] | undefined

How this block starts and how many lines it takes in markdown (see MarkdownRule). Declare it together with fromMarkdown.

static toHtml

TypeScript
static toHtml: ((pb: PasteBlock, ctx: SerializeContext) => string) | undefined

The HTML of a block of this type, for the clipboard's text/html.

static toMarkdown

TypeScript
static toMarkdown: ((pb: PasteBlock, ctx: SerializeContext) => string) | undefined

The markdown of a block of this type, for the clipboard's text/plain and for containers that serialize their children.

static create()

TypeScript
create(
  nabu: Nabu,
  type: string,
  props?: Record<string, any>,
  parentId?: string | null,
  index?: number | null
): Block
Parameters of create
ParameterTypeDefaultDescription
nabuNabun/an/a
typestringn/an/a
propsoptionalRecord<string, any>{}n/a
parentIdoptionalstring | nullnulln/a
indexoptionalnumber | nullnulln/a

static load()

TypeScript
load(nabu: Nabu, node: NabuNode): Block
Parameters of load
ParameterTypeDescription
nabuNabun/a
nodeNabuNoden/a

Properties

behaviors

TypeScript
behaviors: SvelteMap<string, any>

The behaviors of the block by name; a text block has 'text' (a TextBehavior).

component

TypeScript
component: Component<any, any, string> | null

element

TypeScript
element: HTMLElement | null

id

TypeScript
id: `${number}@${number}`

The Loro tree id of the node (also the key in nabu.blocks).

index

TypeScript
index: number

isIntermediate

TypeScript
isIntermediate: boolean

The selection covers this block and neither starts nor ends in it (reactive).

isolatesSelection

TypeScript
isolatesSelection: boolean

When true, the text of this block is an ISOLATED FIELD (like a form input embedded in the document): a text selection that does not lie entirely inside this block never touches its text. For such a selection (the engine's rules, see container.utils.js, selection.svelte.js):

  • range deletion, typing over it, cut and the marks skip the text of this block, and delete neither this block nor its container; the clipboard gets the TEXT blocks only (the block's own text and its container are not copied);
  • two text blocks on either side of an isolated block, or in different "isolation owners" (the nearest ancestor that isolates), never merge, so a deletion across them clears both sides without joining them;
  • the block is not highlighted (data-nabu-isolated + the root's data-nabu-range), and a selection end that would land in it is moved past it, in the direction of travel; a selection that STARTS in it stays bounded to it;
  • the caret enters it at its start going down / right, at its end going up / left; Select All inside it selects its text.

isSelectionEnd

TypeScript
isSelectionEnd: boolean

The selection ends in this block (reactive).

isSelectionStart

TypeScript
isSelectionStart: boolean

The selection starts in this block (reactive).

marksEnabled

TypeScript
marksEnabled: boolean

When false, inline marks (bold/italic/...) are never applied to this block.

nabu

TypeScript
nabu: Nabu

The editor this block belongs to.

next

TypeScript
next: Block | null

node

TypeScript
node: NabuNode

The block's node in the document tree (Loro): node.data holds its type and props, node.parent() / node.children() the structure.

parent

TypeScript
parent: MegaBlock | null

parents

TypeScript
parents: MegaBlock[]

pastesAsPlainText

TypeScript
pastesAsPlainText: boolean

When true, pasting inside this block inserts the clipboard's text/plain verbatim (replacing the selection), skipping every paste interpreter: no blocks created, no markdown/HTML interpretation.

placeholder

TypeScript
placeholder: string

Resolved placeholder to render in data-placeholder ('' = none). Combines the Nabu placeholders option, the block's emptiness and — with show: 'focus' (default) — whether it holds the collapsed cursor.

The default text is declared by each block class as static placeholder (string, false, or (block) => string | false), overridable per type through the placeholders.text option. Blocks without a text behavior never show one. (No static placeholder is declared here: Svelte forbids a static and a reactive field with the same name in one class.)

positions

TypeScript
positions: number

previous

TypeScript
previous: Block | null

selected

TypeScript
selected: boolean

The selection covers this block (reactive).

selection

TypeScript
selection: { direction: 'none' | 'forward' | 'backward'; from: number; to: number } | null

serializers

TypeScript
serializers: SvelteMap<string, (arg0: any) => any>

Serializers of this block by format: 'markdown', 'json', 'application/x-nabu+json' (the clipboard) or one of your own. Each receives the context of the call ({ from, to, partial } for the clipboard) and returns the serialized block.

splitsOnEnter

TypeScript
splitsOnEnter: boolean

When false, Enter inserts a literal "\n" in place instead of splitting the block.

transformsEnabled

TypeScript
transformsEnabled: boolean

When false, onBeforeInput markdown-shortcut hooks and inputRules are skipped in this block.

type

TypeScript
type: string

The block type, as registered by its extension ('paragraph', 'callout'...). Read from the node when the block is built; retype() changes the node, and the engine builds a new block.

wordCount

TypeScript
wordCount: number

Number of words contained in this block. Overridden by text blocks (own text) and containers (sum of children) so that parents can aggregate without re-traversing the whole subtree.

adoptsPastedTypes readonly

TypeScript
readonly adoptsPastedTypes: boolean

Whether an EMPTY child of this block takes the type of a whole block pasted into it (a # Title pasted into an empty paragraph makes it a heading). Always true for root blocks; a container with structural rules about its children (a list) keeps it false, a container with a free-form body (a toggle) opts in.

hidesChildren readonly

TypeScript
readonly hidesChildren: boolean

True when the block currently hides its children (they are not rendered, e.g. a collapsed foldable block): navigation (findForward / findBackward / findLastDescendant, caret normalisation) never enters them. Override with a getter in a subclass.

requiredParent readonly

TypeScript
readonly requiredParent: { props: () => Record<string, any>; type: string } | null

Declares a required parent type for structural integrity. Override in subclasses (e.g. ListItem requires a "list" parent). Used by wrapOrphan() after block relocations.

Methods

absorbs()

TypeScript
absorbs(block: Block): any
Parameters of absorbs
ParameterTypeDescription
blockBlockn/a

acceptPaste()

TypeScript
acceptPaste(blocks: PasteBlock[], ctx: object): { block: Block | null; offset: number } | null

Paste extension point: lets the block that holds the caret decide how a pasted fragment lands, instead of the engine's default (first block merged into the caret block, the rest inserted after it as siblings). A block that lives in a container with its own rules about what it can hold overrides it (e.g. a list item turns pasted paragraphs into siblings items and splits its list around a block it cannot hold). The engine has already removed the selection; it commits the whole paste as ONE transaction (one undo step) and puts the caret at the returned point.

Contract: return null to DECLINE, and then touch nothing (the default insertion runs). Otherwise apply the whole paste on the tree (the blocks are created through nabu.createPasteBlock / nabu.insertPasteBlock), do NOT commit, and return where the caret goes.

Parameters of acceptPaste
ParameterTypeDescription
blocksPasteBlock[]

the sanitized fragment

ctxobject

the caret offset in this block's text

ctx.offsetnumbern/a

Returns { block: Block | null; offset: number } | null

block: null leaves the caret to the engine

ascend()

TypeScript
ascend(eventName: string, event: Event | null, data?: Object): any
Parameters of ascend
ParameterTypeDefaultDescription
eventNamestringn/an/a
eventEvent | nulln/an/a
dataoptionalObject{}n/a

beforeinput()

TypeScript
beforeinput(event: InputEvent): any
Parameters of beforeinput
ParameterTypeDescription
eventInputEventn/a

clearSelection()

TypeScript
clearSelection(): void

commit()

TypeScript
commit(): Promise<void>

consume()

TypeScript
consume(otherBlock: Block, direction?: 'into' | 'from'): Block

Consumes another block, handling children relocation intelligently.

Parameters of consume
ParameterTypeDefaultDescription
otherBlockBlockn/a

The block to consume or be consumed by

directionoptional'into' | 'from''from'

'into' = this merges into other, 'from' = other merges into this

Returns Block

The surviving block

delete()

TypeScript
delete(deletion?: object): void
Parameters of delete
ParameterTypeDescription
deletionoptionalobjectn/a
deletion.fromoptionalnumbern/a
deletion.indexoptionalnumbern/a
deletion.lengthoptionalnumbern/a
deletion.tooptionalnumbern/a

destroy()

TypeScript
destroy(): void

dragRange()

TypeScript
dragRange(): Block[]

The blocks that travel together when THIS block is dragged by its handle (P-81): this block and anything the drag must carry with it, in document order, all in the same parent. The default is the block alone (a container carries its own children: they are its tree children). A block whose "extent" is not its subtree overrides it (an extension may widen the range). The drop rules are the same for any range: it cannot land inside itself.

findBackward()

TypeScript
findBackward(predicate: (block: Block) => boolean): Block | null
Parameters of findBackward
ParameterTypeDescription
predicate(block: Block) => booleann/a

findForward()

TypeScript
findForward(predicate: (block: Block) => boolean): Block | null

The next block in document order (own descendants first, then the following siblings with their descendants, then those of each ancestor) that satisfies predicate, or null. A block that hides its children is not entered. LINEAR: every block is visited at most once (the following siblings are only DESCENDED into, never searched "forward" again, which would revisit everything after them).

Parameters of findForward
ParameterTypeDescription
predicate(block: Block) => booleann/a

findLastDescendant()

TypeScript
findLastDescendant(predicate: (block: Block) => boolean): Block | null

Helper pour trouver le dernier descendant profond qui matche

Parameters of findLastDescendant
ParameterTypeDescription
predicate(block: Block) => booleann/a

focus()

TypeScript
focus(
  options?: { end?: number; offset?: number; start?: number } | null,
  passive?: boolean
): { end: { node: Node; offset: number } | null; options: { endOffset: number; startOffset: number }; start: { node: Node; offset: number } | null }
Parameters of focus
ParameterTypeDefaultDescription
optionsoptional{ end?: number; offset?: number; start?: number } | null{}n/a
passiveoptionalbooleanfalsen/a

getAdjacentSiblings()

TypeScript
getAdjacentSiblings(): {
    next: null;
    nextNode?: undefined;
    previous: null;
    previousNode?: undefined;
  } | {
    next: Block | null | undefined;
    nextNode: LoroTreeNode<{ type: string } & Record<string, any>> | LoroTreeNode<Record<string, unknown>> | null;
    previous: Block | null | undefined;
    previousNode: LoroTreeNode<{ type: string } & Record<string, any>> | LoroTreeNode<Record<string, unknown>> | null;
  }

Returns real, uncommitted siblings, which is useful for extensions that want to check the document structure before the transaction is committed.

getDOMPoint()

TypeScript
getDOMPoint(offset: number): { node: Node; offset: number } | null
Parameters of getDOMPoint
ParameterTypeDescription
offsetnumbern/a

insert()

TypeScript
insert(index: number, text: string): void
Parameters of insert
ParameterTypeDescription
indexnumbern/a
textstringn/a

keydown()

TypeScript
keydown(event: KeyboardEvent): any
Parameters of keydown
ParameterTypeDescription
eventKeyboardEventn/a

mergeWith()

TypeScript
mergeWith(block: Block): any
Parameters of mergeWith
ParameterTypeDescription
blockBlockn/a

resolveOffset()

TypeScript
resolveOffset(localOffset: number): { block: Block; offset: number } | null

Resolve a document-local offset to { block, offset } within this block. Leaf blocks with text return themselves directly. Override in MegaBlock (pure containers) and mixed blocks (e.g. ListItem).

Parameters of resolveOffset
ParameterTypeDescription
localOffsetnumbern/a

retype()

TypeScript
retype(newType: string, props?: Record<string, any>): void

The mutations of a type change, without the commit: the caller owns the transaction (transformTo, the block:transform action…). By default the block is retyped IN PLACE (same node: its id, text and position survive). Override it when a block cannot simply change type where it stands (e.g. because its parent only accepts some children): do the same job with Loro mutations only.

Parameters of retype
ParameterTypeDefaultDescription
newTypestringn/an/a
propsoptionalRecord<string, any>{}n/a

serialize()

TypeScript
serialize(format: string, context?: Record<string, any>): any
Parameters of serialize
ParameterTypeDefaultDescription
formatstringn/an/a
contextoptionalRecord<string, any>{}n/a

split()

TypeScript
split(options: object): { block: Block } | null
Parameters of split
ParameterTypeDescription
optionsobjectn/a
options.fromoptionalnumbern/a
options.indexoptionalnumbern/a
options.lengthoptionalnumbern/a
options.offsetoptionalnumbern/a
options.tooptionalnumbern/a

transformTo()

TypeScript
transformTo(newType: string, props?: Record<string, any>): void

Transforme ce bloc en un autre type de bloc.

Parameters of transformTo
ParameterTypeDefaultDescription
newTypestringn/an/a
propsoptionalRecord<string, any>{}n/a

Source: src/lib/blocks/block.svelte.js:32