Skip to Content
Schema

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.

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, sortable, borders, hasHeaderRow, processed (internal: column widths fitted).
table_rowtable_cell*Attributes: height, heightRule, isConditional, isValid, errorMessage, codeTs.
table_cellblock+Attributes: colspan, rowspan, colwidth, verticalAlign, bgColor, borders, isHeader (derived from the table’s hasHeaderRow; header cells serialize as <th scope="col">).
code_blocktext*blockSyntax-highlighted code. Admits only the track-changes marks. Attributes: codeLanguage, theme, showLineNumbers.
horizontal_ruleblockDivider. Attribute: border (one border side’s options; null = default hairline).
imageblockAttributes: 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.
upload_blockblockUpload placeholder. Attribute: uploadType.

Table placement

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

Consequences:

  • insertTable is refused inside a table, a note body or a code block.
  • Pasting a table into a cell never nests: ProseMirror’s fitting relocates or flattens the content instead.
  • Legacy documents containing nested tables are healed on load (createNormalizeTransaction): the inner table is flattened into its cell’s blocks, with steps that converge across collab clients.
  • DOCX import flattens nested tables (and tables inside footnotes/endnotes) with an import warning.

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.
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, wrap, side, offsetX, offsetY, crop, transform.
math_inlineinline mathInline equation (atom, LaTeX as text content). Attribute: mathMarks.
footnote_anchorinlineFootnote reference. Attributes: footnoteRef, index, isFirstOccurrence.
endnote_anchorinlineEndnote reference. Attributes: endnoteRef, index, isFirstOccurrence.
page_numberinlinePage number field. Attribute: pageType (currentPage | totalPages).
cross_referenceinlineReference to another node. Attributes: targetId, text, refTo, separator, missingReference.
comment_openinlineComment range start. Attributes: anchorId, threadId, originEditorId (clipboard only).
comment_closeinlineComment range end. Attributes: anchorId, threadId, originEditorId (clipboard only).
counter_nodeList number, admitted by name as the first child of paragraph/heading. Attributes: _text, _path, _fullText (derived by the numbering plugin, never round-tripped) and prevText.
boundary_open_node / boundary_close_nodeConditional-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
strongBold.
emItalic.
underlinevariant, colorVariants: single, double, thick, dotted, dottedHeavy, dash, dashedHeavy, wave, wavyHeavy.
strikethroughStrikethrough.
superscriptExcludes subscript.
subscriptExcludes superscript.
codeInline 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, lineHeightRule, 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 and table to those.

Spacing Roles

The spacing plugin replaces CSS margin collapse with computed _marginTop attributes (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.

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