← All packages

@searsuite/sear-domain-doc

Document nodes

A rich document is a tree of RichDocNode values. Paragraphs, text runs, tables, rows, and cells are node types in that tree. DocNodeRegistry creates nodes from schema data; Schema, NodeSchema, and the node guards describe which shapes the runtime accepts. Use the built-in node types when reading or constructing document content, then add custom node behavior through the registry used by your Doc assembly.

The tree is document data. Layout measurements and painted nodes are computed later by the editor and render packages, so storing a document does not require a visual host.

classRichDocNode

Base class for rich-document nodes used by the document layout engine.

propertyattrs: Record<string, any>

Node attributes such as text styles or block metadata.

propertycontent: RichDocNode[] | TextNode[]

Child rich-document nodes.

propertyid: string | undefined

Stable structural identity for layout, retained render nodes, and collaboration mapping.

propertytype: string

Domain node type identifier.

methodcreate(_schema: Schema): RichDocNodestatic

Creates a concrete rich-document node from persisted schema.

classDocNodeRegistry

Registry that maps persisted rich-document node types to constructors.

constructorDocNodeRegistry(): DocNodeRegistry
propertyreadonly symbol: typeof symbolstatic

DI token for resolving the document node registry.

methodcreate(schema: Schema): RichDocNode

Instance method: delegates to the static registry; used by DI-injected consumers.

methodisBlockType(type: string): boolean

Instance method: returns whether the persisted type has been registered as a block container.

methodisTextType(type: string): boolean

Instance method: returns whether the persisted type has been registered as text-like.

methodregister(definition: DocNodeDefinition): void

Registers a node factory on this registry instance.

methodrequireDefaultBlockType(): string

Instance method: returns the registered default block type for generic text editing.

methodrequireDefaultTextType(): string

Instance method: returns the registered default text type for generic text editing.

methodcreate(schema: Schema): RichDocNodestatic

Creates a rich-document node from persisted schema.

methodisBlockType(type: string): booleanstatic

Returns whether the persisted type has been registered as a block container.

methodisTextType(type: string): booleanstatic

Returns whether the persisted type has been registered as text-like.

methodregisterstatic
register(type: string, creator: (schema: Schema): RichDocNode): void register(definition: DocNodeDefinition): void

Registers a node factory for a persisted node type.

methodrequireDefaultBlockType(): stringstatic

Returns the registered default block type for generic text editing.

methodrequireDefaultTextType(): stringstatic

Returns the registered default text type for generic text editing.

classParagraphNode
class ParagraphNode extends RichDocNode

Built-in paragraph node.

constructorParagraphNode(schema: NodeSchema): ParagraphNode

Creates a paragraph node from persisted schema.

propertyreadonly nodeType: "paragraph"static

Persisted schema type owned by the paragraph document feature.

Inherited members 5

Inherited from RichDocNode.

classTableCellNode
class TableCellNode extends RichDocNode

Built-in table cell node.

constructorTableCellNode(schema: NodeSchema): TableCellNode

Creates a table cell node from persisted schema.

propertyreadonly nodeType: "tableCell"static

Persisted schema type owned by the table document feature.

Inherited members 5

Inherited from RichDocNode.

classTableNode
class TableNode extends RichDocNode

Built-in table block node.

constructorTableNode(schema: NodeSchema): TableNode

Creates a table node from persisted schema.

propertyreadonly nodeType: "table"static

Persisted schema type owned by the table document feature.

Inherited members 5

Inherited from RichDocNode.

classTableRowNode
class TableRowNode extends RichDocNode

Built-in table row node.

constructorTableRowNode(schema: NodeSchema): TableRowNode

Creates a table row node from persisted schema.

propertyreadonly nodeType: "tableRow"static

Persisted schema type owned by the table document feature.

Inherited members 5

Inherited from RichDocNode.

classTextNode
class TextNode extends RichDocNode

Built-in text leaf node.

constructorconstructor
TextNode(schema: NodeSchema): TextNode TextNode(schema: TextSchema): TextNode

Creates a text node from persisted schema.

propertytext?: string

Text content carried by this leaf node.

propertyreadonly nodeType: "text"static

Persisted schema type owned by the text input document feature.

Inherited members 5

Inherited from RichDocNode.

interfaceDocNodeDefinition

Registration contract for one persisted rich-document node type.

propertyreadonly create: (schema: Schema): RichDocNode

Factory that creates the runtime node instance for this persisted schema.

propertyreadonly defaultBlock?: boolean

Whether this block is the default container for plain text editing.

propertyreadonly defaultText?: boolean

Whether this text-like node is the default leaf for plain text editing.

propertyreadonly role?: DocNodeRole

Generic role consumed by core editing/layout helpers.

propertyreadonly type: string

Persisted schema type owned by a document feature plugin.

interfaceRichNodeFactory

Minimal factory interface for creating document nodes. Implemented by DocNodeRegistry (static or injected).

typeDocNodeId
type DocNodeId = string & { [docNodeIdBrand]: true }

Stable identity used by document layout and retained render nodes.

typeDocNodeRole
type DocNodeRole = typeof DocNodeRole[keyof typeof DocNodeRole] DocNodeRole: { Block: "block"; Text: "text" }
typeRichTextChar
type RichTextChar = unknown

Measured inline character/token used by rich-text line wrapping.

propertychar: string
propertyfontAscent: number
propertyfontDescent?: number
propertyfontLineGap?: number
propertyheight: number
propertystyle?: RichTextInlineStyle
propertytextNodeIndex?: number

Direct child index of the source text node within its paragraph.

propertytextNodeOffset?: number

Character offset within the source text node.

propertywidth: number
functioncloneRichDocument
cloneRichDocument(document: readonly RichDocNode[]): RichDocNode[]

Returns a detached mutable snapshot of a rich document.

functioncreateDocNodeId
createDocNodeId(value: string): DocNodeId

Creates a typed document node id from a non-empty string value.

functioncreateDocNodeIdFromPath
createDocNodeIdFromPath(path: readonly string | number[]): DocNodeId

Creates a fallback document node id from a rich-document path.

functioncreateEmptyParagraph
createEmptyParagraph(): RichDocNode

Create a single empty paragraph containing one empty text node.

functiongetTextNodeLength
getTextNodeLength(doc: RichDocNode[], path: number[]): number

Character length of the text node at path. Returns 0 for missing / non-text nodes.

functionisDocSemanticallyEmpty
isDocSemanticallyEmpty(doc: RichDocNode[]): boolean

Returns true if every paragraph in doc contains no meaningful text.

functionisTextNode
isTextNode(node: TextNode | RichDocNode): node

Returns true when a rich-document node is a text leaf.

functionnormalizeDocForEdit
normalizeDocForEdit(doc: RichDocNode[]): RichDocNode[]

Ensure doc has at least one paragraph. Returns the same array if already valid; otherwise [createEmptyParagraph()].

functionparaText
paraText(para: RichDocNode): string

Extract the concatenated plain text of a paragraph node.

functionparaTextLength
paraTextLength(para: RichDocNode): number

Total plain-text length of a paragraph node (sums all direct text children).

functionregisterParagraphDocNode
registerParagraphDocNode(registry: DocNodeRegistrar): void

Registers the paragraph node type owned by paragraph-oriented document features.

functionregisterTableDocNodes
registerTableDocNodes(registry: DocNodeRegistrar): void

Registers the table node types owned by table-oriented document features.

functionregisterTextDocNode
registerTextDocNode(registry: DocNodeRegistrar): void

Registers the text node type owned by text-input document features.

functionresolveDocNodeId
resolveDocNodeId(node: { id?: string } | undefined, path: readonly string | number[]): DocNodeId

Resolves a block-level document id, preferring persisted node identity over path fallback.

functionresolveNode
resolveNode(doc: RichDocNode[], path: number[]): RichDocNode | undefined

Resolve the text node at path inside doc (treated as the root content array). Returns undefined if the path is invalid.

functionstringToRichDoc
stringToRichDoc(text: string): RichDocNode[]

Convert a plain string to a single-paragraph RichDocNode[]. Used for backward-compat when cell data is still stored as a plain string.

interfaceNodeSchema

Persisted rich-document node schema shared by document runtime, commands, and OT.

propertyattrs?: Record<string, any>

Node attributes for styles, formatting, and feature-owned metadata.

propertycontent?: Schema[]

Child rich-document nodes.

propertyid?: string

Stable structural identity for layout, retained render nodes, and collaboration mapping.

propertytext?: string

Optional text content for leaf text nodes.

propertytype: string

Domain node type identifier.

typeSchema
type Schema = NodeSchema | TextSchema

Persisted rich-document schema node.

interfaceTextSchema
interface TextSchema extends NodeSchema

Persisted text-bearing rich-document node schema.

propertytext: string

Optional text content for leaf text nodes.

Inherited members 4

Inherited from NodeSchema.

Selection and positions

DocCursorPos identifies a text node with a path from the document root and an offset inside that node. DocSelection holds an anchor and a head; when they point to the same position, the selection is a cursor. Commands use these positions to target edits, and OT paths follow the same tree structure.

Start with DocSelection when moving a caret or selecting text. Use the layout contracts only when a host needs geometry for rendering or hit testing.

interfaceDocSelection

Anchor + head selection model. Collapsed selection (cursor only): anchor === head. head moves freely; anchor is fixed until a non-Shift action resets it.

interfaceDocCursorPos

Cursor position inside a RichDocNode tree.

path is an array of integer indices that navigates from the implicit document root (the RichDocNode[] array treated as root.content) down to the text node that contains the cursor.

Examples for a flat document: [0, 0] → paragraph 0 → text node 0 (the most common case in P0) [1, 2] → paragraph 1 → inline span 2

The path aligns with OT operation paths: a StringInsert at this cursor becomes { p: [...path, offset], si: text } applied to the root wrapper { type: &#39;doc&#39;, content: doc }.

offset is the character position within the leaf text node: 0 = before first character n = after the nth character (= text.length means "end of node")

propertyoffset: number

Character offset within the leaf text node.

propertypath: number[]

Path from root content[] to the leaf text node.

functioncompareCursor
compareCursor(a: DocCursorPos, b: DocCursorPos): -1 | 0 | 1

Lexicographic comparison of two cursor positions. Compares path element-by-element, then offset. Returns -1 | 0 | 1.

functiongetDocumentOffset
getDocumentOffset(doc: RichDocNode[], position: DocCursorPos): number

Converts a cursor position into a zero-based offset in the document's plain text, using one newline character between paragraphs.

functionisCollapsed
isCollapsed(sel: DocSelection): boolean
functionmapInlineTextRangePatchCursor
mapInlineTextRangePatchCursor(cursor: DocCursorPos, patches: readonly InlineTextRangePatch[]): DocCursorPos

Repositions a cursor after an inline text range patch plan has been applied.

functionnormalizeSelection
normalizeSelection(sel: DocSelection): { end: DocCursorPos; start: DocCursorPos }

Normalize selection so start ≤ end regardless of direction.

functionresolveTextContent
resolveTextContent(doc: RichDocNode[], path: number[]): string

Get the plain-text string of the node at path. Convenience wrapper around resolveNode.

Text formatting

Inline style functions patch a text range while preserving the surrounding document tree.

classRichTextInlineStyleRegistry

Ordered registry for plugin-owned rich-text inline style readers and mutations.

constructorRichTextInlineStyleRegistry(): RichTextInlineStyleRegistry
propertyreadonly symbol: typeof symbolstatic

DI token for resolving the inline style registry.

methodenforceWriteBoundary(assertWrite: (): void): void
methodgetMutation(styleKey: string): RichTextInlineStyleMutation | undefined

Resolves a plugin-owned persisted inline style mutation by style key when registered.

methodgetMutationKeys(): readonly string[]

Returns all registered persisted inline-style mutation keys in registration order.

methodreadTextNodeStyle(node: TextNode): Readonly<Record<string, unknown>> | undefined

Reads all registered style slices from a text node.

methodregister(contribution: RichTextInlineStyleContribution): (): void

Registers one plugin-owned inline style contribution.

methodrequireMutation(styleKey: string): RichTextInlineStyleMutation

Resolves a plugin-owned persisted inline style mutation by style key.

methodsameStyle(styleA: Readonly<Record<string, unknown>> | undefined, styleB: Readonly<Record<string, unknown>> | undefined): boolean

Compares two style bags using stable key/value serialization.

interfaceInlineTextRangePatchContext

Context supplied when patching one selected text-node segment.

interfaceRichTextInlineStyleContribution

Plugin-owned contribution for one inline text style concern.

propertyreadonly key: string

Stable contribution key used for duplicate detection and unregister.

propertymutations?: readonly RichTextInlineStyleMutation[]

Persisted inline text-style mutations owned by this contribution.

methodread(input: RichTextInlineStyleReadInput): Readonly<Record<string, unknown>> | undefined

Reads this contribution's style slice from a text node.

interfaceRichTextInlineStyleMutation

Plugin-owned mutation for one persisted inline text-style concern.

propertyreadonly styleKey: string

Stable style key used by commands to resolve this mutation.

methodapply(input: RichTextInlineStyleMutationInput): Record<string, unknown>

Applies the normalized style value to a text node's persisted attrs.

interfaceRichTextInlineStyleMutationInput

Input supplied when an inline-style contribution mutates persisted text-node attrs.

interfaceRichTextInlineStyleReadInput

Input supplied when an inline-style contribution reads document attrs.

interfaceTextAttributeRange
interface TextAttributeRange<TAttributes>

A generic text attribute range in document-relative character offsets.

propertyattrs: TAttributes
propertyend: number
propertystart: number
interfaceTextGraphemeRange

One grapheme range expressed in UTF-16 source offsets.

typeInlineTextRangeAttrsPatcher
type InlineTextRangeAttrsPatcher = (attrs: Record<string, unknown>, context: InlineTextRangePatchContext): Record<string, unknown>

Patches the attrs for the selected segment of one text node.

typeRichTextInlineStyle
type RichTextInlineStyle = Readonly<Record<string, unknown>>

Domain-neutral inline style bag carried by measured rich-text chars.

typeTextAttributePatch
type TextAttributePatch = Readonly<Record<string, unknown>>

Attributes merged into a text node for one presentation range.

variableRichTextParagraphAttribute
RichTextParagraphAttribute: { LineSpacing: "lineSpacing"; SpacingAfter: "spacingAfter" }

Persisted paragraph attribute keys owned by the paragraph feature.

functionapplyInlineTextRangePatchPlan
applyInlineTextRangePatchPlan(doc: readonly RichDocNode[], patches: readonly InlineTextRangePatch[]): RichDocNode[]

Applies an inline text range patch plan to an immutable document snapshot.

functionapplyInlineTextStylePropertyMutation
applyInlineTextStylePropertyMutation(attrs: Readonly<Record<string, unknown>>, property: string, value: unknown): Record<string, unknown>

Applies or removes one persisted inline-text style property.

functionapplyRichTextFormat
applyRichTextFormat(document: readonly Schema[], format: Readonly<Record<string, unknown>>, registry: RichTextInlineStyleRegistry): Schema[]

Replaces registered styles while retaining text, structure and unrelated attributes.

functionapplyTextAttributeRanges
applyTextAttributeRanges(doc: RichDocNode[], ranges: readonly TextAttributeRange<Readonly<Record<string, unknown>>>[]): RichDocNode[]

Applies presentation attributes to text ranges without owning their meaning. Plugins decide how to interpret the resulting text-node attrs.

functionbuildInlineTextRangePatchPlan
buildInlineTextRangePatchPlan(doc: readonly RichDocNode[], selection: DocSelection, patchAttrs: InlineTextRangeAttrsPatcher): InlineTextRangePatch[]

Builds reusable text-node split/replacement patches for a document selection.

functioncaptureRichTextFormat
captureRichTextFormat(document: readonly Schema[], registry: RichTextInlineStyleRegistry): Readonly<Record<string, unknown>>

Captures the leading text run's explicit registered styles, excluding host defaults.

functioncreateInlineTextStyleAttrsPatcher
createInlineTextStyleAttrsPatcher(property: string, value: unknown): InlineTextRangeAttrsPatcher

Creates the compatibility inline text-style attrs patcher for property/value callers.

functionreadTextStyleAttrs
readTextStyleAttrs(attrs: Record<string, unknown>): Record<string, unknown>

Reads the inline text-style attribute bag from a node's attributes.

functionreplaceInlineTextStyle
replaceInlineTextStyle(doc: RichDocNode[], selection: DocSelection, property: string, value: unknown, patchAttrs?: InlineTextRangeAttrsPatcher): RichDocNode[]

Replaces one inline text-style property on the selected document range.

functionsegmentTextGraphemes
segmentTextGraphemes(text: string): readonly TextGraphemeRange[]

Segments text into grapheme ranges, with a code-point fallback for older hosts.

functionupdateInlineTextStyleAttrs
updateInlineTextStyleAttrs(current: unknown, property: string, value: unknown): Record<string, unknown>

Updates one entry in a text node's style attrs bag.

Text layout

classRichTextLineBoxRegistry

Ordered registry for plugin-owned rich-text line box resolution.

constructorRichTextLineBoxRegistry(): RichTextLineBoxRegistry
propertyreadonly symbol: typeof symbolstatic

DI token for resolving the rich-text line box registry.

methodenforceWriteBoundary(assertWrite: (): void): void
methodregister(contribution: RichTextLineBoxContribution): (): void

Registers one plugin-owned line box contribution.

methodresolve(input: { chars: readonly RichTextChar[]; lineSpacing?: RichTextLineSpacing }): RichTextLineBox

Resolves the final line box for one measured line.

interfaceRichTextBlockFlowContribution

Plugin-owned contribution that can refine block flow metrics.

interfaceRichTextBlockFlowResolveInput

Input used to resolve the flow spacing after one top-level document block.

interfaceRichTextBlockLayoutProvider

Domain-side contract for services that provide measured rich-text block layout.

methodcalculateLayoutPerParagraph(width: number): RichTextLine[][]
methodsetBaseFont(font: string): void

Updates the transient font used when measuring unstyled text.

interfaceRichTextLineBoxContribution

Plugin-owned contribution that can refine rich-text line flow metrics.

propertyreadonly key: string

Stable contribution key used for duplicate detection and unregister.

methodresolve(input: RichTextLineBoxResolveInput): RichTextLineBox

Resolves the next line box from the current accumulated line box.

interfaceRichTextLineBoxResolveInput

Input used to resolve the flow box for one measured rich-text line.

interfaceRichTextLineSpacing

Persisted or default line-spacing policy resolved by paragraph layout.

interfaceRichTextParagraphLayout

Resolved paragraph layout values consumed by line and block flow.

interfaceRichTextParagraphLayoutDefaults

Default paragraph layout values supplied by the owning document runtime.

typeRichTextLine
type RichTextLine = unknown

One laid-out rich-text line with aggregate dimensions.

propertychars: RichTextChar[]
propertyfontAscent: number

Max measured font ascent across all chars on this line; used for stable vertical centering.

propertyfontDescent?: number

Max measured font descent across all chars on this line.

propertyheight: number
propertylineBox?: RichTextLineBox

Plugin-resolved line box used for flow and baseline placement.

propertywidth: number
typeRichTextLineSpacingMode
type RichTextLineSpacingMode = "auto" | "multiple" | "atLeast" | "exact"

Supported paragraph line-spacing semantics.

variabledefaultRichTextParagraphLayoutDefaults
defaultRichTextParagraphLayoutDefaults: RichTextParagraphLayoutDefaults

Neutral defaults for runtimes that do not configure document paragraph spacing.

functioncomputeFirstBaselineY
computeFirstBaselineY(boxHeight: number, cellPadding: number, fontAscent: number, lineHeight: number, offsetY?: number): number

Compute the Y coordinate of the first text line's baseline for vertical centering.

Text painters in this engine position runs by their alphabetic baseline, so the correct formula to visually center a line inside a padded box is:

baseline = center + (fontAscent - fontDescent) / 2

Since lineHeight = fontAscent + fontDescent, this simplifies to:

baseline = offsetY + cellPadding + (boxHeight - 2·cellPadding + 2·fontAscent - lineHeight) / 2

boxHeight

Total pixel height of the containing box (e.g. cell height).

cellPadding

Padding on each side of the box.

fontAscent

Measured font ascent of the first line (stable across all glyphs in the font).

lineHeight

Total line height (fontAscent + fontDescent) of the first line.

offsetY

Absolute Y offset of the box top-left (default 0 for relative coordinates).

functioncreateDefaultRichTextBlockFlowContribution
createDefaultRichTextBlockFlowContribution(): RichTextBlockFlowContribution

Creates the paragraph-owned default after-spacing contribution.

functioncreateDefaultRichTextLineBoxContribution
createDefaultRichTextLineBoxContribution(): RichTextLineBoxContribution

Creates the default line box contribution used by document composition roots.

functioncreateRichTextLineSpacingContribution
createRichTextLineSpacingContribution(): RichTextLineBoxContribution

Creates the paragraph-owned line-spacing contribution.

functionparseRichTextLineSpacing
parseRichTextLineSpacing(value: unknown): RichTextLineSpacing | null

Parses a persisted line-spacing override, returning null when invalid.

functionparseRichTextParagraphSpacing
parseRichTextParagraphSpacing(value: unknown): number | null

Parses a persisted paragraph-spacing override, returning null when invalid.

functionresolveRichTextParagraphLayout
resolveRichTextParagraphLayout(node: RichDocNode | undefined, defaults?: RichTextParagraphLayoutDefaults): RichTextParagraphLayout

Resolves paragraph-owned overrides over the runtime defaults.