Skip to Content
PackagesCoreSchema

Schema

The schema defines every node and mark the editor supports. stesuraSchema() from @stesura/core/schema builds it: the core node specs, plus shared attributes (numbering, formatting, spacing, …) composed onto them by the global-attribute system.

Document Structure

doc → section+ footnotes endnotes? section → section_header section_content section_footer section_content → (block | table | page_break | toc | file)+

endnotes is created lazily, when the first endnote is inserted.

doc attributes:

AttributeDescription
docBgColorCanvas color behind the pages (default #FFFFFF). Distinct from blockStyling’s bgColor.
listConfigsList definitions, keyed by list ref. See Numbering.
styleSheetThe stylesheet definitions map. A block references it through styleId.
endnotesOptionsshowAt, format, continuous, startAt.
languageDefault proofing language: the bottom of paragraph.proofingLanguage ?? style.language ?? doc.language.
dirDefault text direction ("ltr"): the bottom of paragraph.dir ?? section.dir ?? doc.dir.
pageBordersSurroundHeader, pageBordersSurroundFooterWhether text-measured page borders enclose the header / footer (default true). Document-wide, as in Word’s settings.

Nodes

Block Nodes

NodeContentGroupDescription
paragraphcounter_node? inline*blockStandard text block.
headingcounter_node? inline*blockAttribute: level (1–9). Levels 7–9 render as <p role="heading" aria-level>.
tabletable_row+—Not in the block group; hosts admit it by name (see Table placement). Attributes: align, width (auto, fixed px or pct), layout (Word’s fixed/autofit, not rendered), sortable, borders, headerRowCount (leading header rows), repeatHeaderRows (repeat them on every page the table continues onto, Word’s “Repeat Header Rows”), marginStart.
table_rowtable_cell*—Attributes: height, heightRule, isConditional, isValid, errorMessage, codeTs.
table_cell(block | table)* block—A table may sit in a cell, but never last. Attributes: colspan, rowspan, colwidth, verticalAlign, bgColor, borders, isHeader (derived: the cell starts in one of the table’s headerRowCount rows; header cells render and serialize as <th scope="col">).
code_blocktext*blockSyntax-highlighted code. Admits only the track-changes marks. Attributes: codeLanguage, theme, showLineNumbers.
horizontal_rule—blockDivider. Attribute: border (one border side’s options; null = default hairline).
image—blockAttributes: src, alt, decorative, width, height, maxWidth, aspectRatio, align, wrap, crop, transform.
math_displaytext*block mathDisplay equation. The LaTeX is the node’s text; edited in a floating editor (atom). Attribute: mathMarks.
calloutblock+blockA kind-tagged aside or a quotation (see Callouts). Attributes: kind (note, tip, warning, danger, quote, or any lowercase identifier), title (plain text or null). Kept on one page when it fits.
upload_block—blockUpload placeholder. Attribute: uploadType.

Table placement

table is deliberately not in the block group. The containers that host tables admit them by name: section_content, header_footer_content, wrapper nodes that opt in ((block | table)+), and table_cell ((block | table)* block). Plain block+ containers (footnote_body, endnote_body) therefore exclude tables structurally.

A table can sit in a table cell, one level deep: a table in a nested table’s cell is not supported. A content expression can’t state depth, so the guards enforce it (see @stesura/core/guards). A cell never ends in a table, so the caret always has a paragraph after it, as in Word.

Consequences:

  • insertTable in a table cell sizes the new table to that cell. It is refused inside a nested table, a note body, a code block, or a cell too narrow for the columns.
  • Pasting a table into a cell nests it. A paste that would put a table three levels deep flattens that table into its cell’s blocks.
  • Documents with a table too deep, or in a note body, are healed on load (prepareDocForLoad locally, createNormalizeTransaction in collab): the table is flattened into its cell’s blocks, with steps that converge across collab clients.
  • DOCX import keeps a table in a table cell and flattens deeper ones (and tables inside footnotes/endnotes) with an import warning.
  • Pagination breaks a nested table between its rows, never through one (see Pagination).

A custom container that should host tables must use (block | table)+. See Extending the Schema.

Section Nodes

NodeContentDescription
sectionsection_header section_content section_footerPage container. Attributes: orientation, margin, pageSize, headerFromTop, footerFromBottom, lineNumbers, dir, pageNumbering, columns, pageBorders, watermark.
section_headerheader_footer_content+Header area.
section_footerheader_footer_content+Footer area.
section_content(block | table | page_break | toc | file)+Main content area.
header_footer_content(block | table)+One header/footer variant. Attribute: variant.

section has no parse rule: pasting a section brings its blocks, not its page setup.

Inline Nodes

NodeGroupDescription
textinlinePlain text.
hard_breakinlineManual line break (Shift+Enter).
image_anchoredinlineImage anchored to a text position (Word-style wrapping). Declares its own id. Attributes: src, alt, decorative, width, height, aspectRatio, crop, transform, plus the floating family: wrap, anchorPosition, wrapDistance, zIndex.
floating_boxinlineA text box or frame: blocks (block+, no tables) in an atom anchored in a textblock, edited through a sub-editor. Declares its own id. Attributes: origin (textbox
math_inlineinline mathInline equation (atom, LaTeX as text content). Attribute: mathMarks.
footnote_anchorinlineFootnote reference. Attributes: id (this occurrence’s), footnoteRef (the note, shared by repeated anchors). Its number is derived (noteAnchorStore, @stesura/references).
endnote_anchorinlineEndnote reference. Attributes: id, endnoteRef. Its number is derived, as for footnotes.
page_numberinlinePage number field. Attribute: pageType (currentPage | totalPages).
cross_referenceinlineReference to another node. Attributes: id, targetId, refTo, separator, textCase, bookmarkText (a text reference’s content), pageLabel (a page reference’s cached page), missingLabel (the label kept while the target is missing). The label it shows is derived (crossReferenceStore).
comment_openinlineComment range start. Attributes: anchorId, threadId, originEditorId (clipboard only).
comment_closeinlineComment range end. Attributes: anchorId, threadId, originEditorId (clipboard only).
counter_node—List number, admitted by name as the first child of paragraph/heading. No attributes: its label is the numbering store’s.
boundary_open_node / boundary_close_node—Conditional-content boundary markers. No core content expression admits them.

Special Nodes

Not in the block group; section_content admits them by name, so they cannot appear in table cells, note bodies or headers/footers.

NodeDescription
page_breakManual page break.
tocTable of contents (atom). Attributes: showPageNumbers, alignNumberRight, showLeaderDots, scope (document | section).
fileEmbedded PDF. Serialized as a link; the node view renders a preview when allowed (see Embedding files). Attributes: src, height, name, mimeType, size, rotation, displayPreview.

Footnote / Endnote Nodes

NodeContentDescription
footnotesfootnote_body*Hidden doc-level container for footnote bodies.
footnote_bodyblock+One footnote. Attributes: footnoteRef, index.
endnotesendnote_body*Hidden doc-level container for endnote bodies. Body order is not meaningful: numbering follows anchor order.
endnote_bodyblock+One endnote. Attributes: endnoteRef, index.

Marks

Text Formatting

MarkAttributesDescription
strong—Bold.
em—Italic.
underlinevariant, colorVariants: single, double, thick, dotted, dottedHeavy, dash, dashedHeavy, wave, wavyHeavy.
strikethrough—Strikethrough.
superscript—Excludes subscript.
subscript—Excludes superscript.
code—Inline code.

Styling

MarkAttributesDescription
textStylecolor, fontSize, fontFamily, caps ("all" | "small" | "none"), bgColor, off (marks this run negates against its paragraph style)Character styling. Ordered to wrap the other marks, so they inherit its style.
highlightcolorHighlight. Default #FFFF00.
borderstyle, color, size, spaceInline text border.
linkurlHyperlink. Non-inclusive: typing at its edge doesn’t extend it.

Track Changes

MarkAttributesExcludesDescription
insertionid, date, userId, userNameinsertion, modificationSuggested insertion.
deletionid, date, userId, userNamedeletionSuggested deletion. May stack on another user’s insertion.
modificationid, type, attrName, previousValue, newValue, date, userId, userNameinsertionSuggested formatting change (added/removed mark). Stacks, so each change resolves independently.

Utility

MarkAttributesDescription
proofReadIgnore_pri_lengthText skipped by spell checking.

Global Attributes

Many node attributes come from the global-attribute system rather than the node spec. stesuraSchema applies them at construction, and its globalAttributes option changes which nodes carry each one. See Global Attributes.

Default bindings:

AttributeGlobal attributeDefault nodesDescription
iduniqueIdEvery non-inline nodeUnique id, maintained by uniqueIdPlugin.
numberingnumberingparagraph, headingList numbering; null inherits from the style. See Numbering.
indent, lineHeight, lineHeightAtLeast, tabStopsformattingFORMATTED_NODESIndentation, line height, tab stops.
textAligntextAlignFORMATTED_NODESstart (null), center, end, justify.
dirdirectionFORMATTED_NODESltr | rtl | null (inherit).
suppressLineNumberssuppressLineNumbersFORMATTED_NODESSkips the block’s lines in the section’s line numbering.
spacingspacingSPACED_NODESBefore/after spacing.
styleIdstyleIdparagraph, headingParagraph style (Normal, Heading1, …).
bgColor, bordersblockStylingparagraph, headingBlock fill and borders.
paginationpaginationparagraph, heading, code_blockkeepWithNext, keepLines, widowOrphan, pageBreakBefore.
proofingLanguageproofingLanguageparagraph, headingSpell-check language; null inherits.
trackChanges, trackChangesModificationtrackChanges, trackChangesModificationBLOCK_TRACK_CHANGES_NODESBlock-level suggestions.
nodeCommentsnodeCommentnone (opt-in)Comment threads on a whole node.

FORMATTED_NODES is heading, paragraph, image, toc and horizontal_rule. SPACED_NODES adds code_block, file, table and callout to those.

Spacing Roles

The spacing plugin replaces CSS margin collapse with computed top margins (the spacing store’s, never stored in the document) (see Global Attributes). It groups blocks into collapse chains: margins collapse between consecutive members of a chain. A container’s spacing role sets how it interacts with chains:

  • Spaced (nodes carrying spacing: paragraphs, headings, tables, images, …): the node is one opaque chain member. Its children never collapse with the outside.
  • Pass-through: the node’s children join the surrounding chain, so a paragraph after the wrapper collapses against the last paragraph inside it.
  • Boundary: the node’s children form an isolated chain, as in a table cell.

A callout is spaced (it collapses with its neighbors as one block) and a boundary inside (its first child keeps its own spacing).

The role is inferred: a wrapper-shaped node (block node with block content, not an atom) is pass-through, unless it is spaced or a core flow boundary (section scaffolding, table/row/cell, note containers and bodies). A custom wrapper node therefore works with no registration.

To override the inference, declare spacingRole on the node spec (typed by StesuraNodeSpec from @stesura/core/schema):

import type { StesuraNodeSpec } from "@stesura/core/schema"; const sidebarSpec: StesuraNodeSpec = { content: "block+", group: "block", spacingRole: "boundary", // isolate children from the surrounding flow // ... };

spacingRole: "passThrough" on a spaced node is ignored with a console error. See Extending the Schema for a full wrapper-node example.

Constants

import { NODE_NAMES, // node type names; prefer these over string literals } from "@stesura/core/schema"; import { DEFAULT_TEXT_STYLE, // { fontSize: 10, fontFamily: "Arimo", color: null, ... } DEFAULT_LINE_HEIGHT, // 1.5 DEFAULT_SPACING, // { before: 0, after: 8 } PAGE_FORMATS, // A4 (default), A3, A5, B5, envelopes, Legal, Letter HIGHLIGHT_COLORS, // yellow, lime, aqua, fuchsia, red UNDERLINE_TYPES, // CSS for each underline variant FONT_FAMILIES, // available font families } from "@stesura/core/constants";

Next Steps

Last updated on