Skip to Content
PackagesCoreCommandsCommands: Sections & Track Changes

Commands: Sections & Track Changes

Page layout commands (sections, page breaks, line numbers, headers and footers) from core, and the track-changes commands from @stesura/track-changes.

// section, page-break and header/footer commands ship in core import { addSection, removeSectionBreak, setSectionFormat } from "@stesura/core/commands"; // track-changes commands ship in the track-changes feature pack import { enableTrackChanges, disableTrackChanges, toggleTrackChanges, toggleTrackChangesVisibility, resolveTrackChanges, resolveAllTrackChanges, selectTrackChanges, } from "@stesura/track-changes";

Track changes also requires transactionModifier={trackChangesTransactionModifier} on StesuraEditor so transactions flow through the diff layer:

import { trackChangesTransactionModifier } from "@stesura/track-changes"; <StesuraEditor /* ... */ transactionModifier={trackChangesTransactionModifier} />

Sections

addSection

Split the current section after the top-level block holding the cursor. The blocks after it move into a new section that inherits the current one’s page setup and header/footer, and the cursor moves into the new section.

addSection(): Command

removeSectionBreak / removeSectionBreakAt

Remove a section break as Word does: the section before the break moves its body to the start of the section after it, which keeps its page setup, header, footer and id. Blocks on either side of the break stay separate. removeSectionBreak removes the break that ends the section holding the selection (in the last section, the break above it); removeSectionBreakAt takes the index of the section before the break. Refused with a toast when the doc has one section, or while track changes is suggesting. Backspace at the start of a section’s body and Delete at the end of the previous one do the same.

removeSectionBreak: Command removeSectionBreakAt(index: number): Command

deleteAcrossSections

Delete (or type over, cut, paste over) a selection whose ends lie in different sections. Such a selection always covers whole blocks; the covered blocks go with every break inside the range, so the last section touched survives. While suggesting, the blocks are tracked as deletions and every break stays. Backspace, Delete, typing, cut and paste already route through it.

deleteAcrossSections(replacement?: { text: string } | { slice: Slice }): Command

setSectionFormat

Set the current section’s orientation, page size and/or margins in one step. A page size whose aspect contradicts the orientation is transposed to match it, and an orientation change without a size swaps the page’s width and height. Refused when nothing changes.

setSectionFormat(options: SectionOptions): Command type SectionOptions = { orientation?: "portrait" | "landscape"; pageSize?: { width: number; height: number }; margin?: { // merged over the current margins top?: number; bottom?: number; left?: number; right?: number; layout?: "narrow" | "moderate" | "wide" | "custom"; }; };

There is no section background colour. The canvas behind the pages is set with setDocBgColor(color) (the docBgColor doc attr).

setPageBorders

Set the current section’s page borders, Word’s Page Border tab; null removes them. scope: "document" sets every section. Refused when nothing changes. Borders are a per-page overlay: they never move text, and print with the page.

setPageBorders(pageBorders: PageBorders | null, options?: SetPageBordersOptions): Command type PageBorders = { top: PageBorderSide | null; // { style, size (px), color, space (pt) } right: PageBorderSide | null; // left/right are inline start/end, like margins bottom: PageBorderSide | null; left: PageBorderSide | null; offsetFrom: "page" | "text"; // what `space` is measured from display: "allPages" | "firstPage" | "notFirstPage"; zOrder: "front" | "back"; // round-tripped only; always painted in front }; type SetPageBordersOptions = { scope?: "section" | "document"; sectionId?: string; // target when a header/footer sub-editor has focus };

DEFAULT_PAGE_BORDERS (from @stesura/core/constants) is Word’s “Box”: ½pt single black, 24pt in from the page edge.

setPageBordersSurround

Whether text-measured page borders enclose the header and footer. Document-wide (pageBordersSurroundHeader / pageBordersSurroundFooter doc attrs), like Word’s w:bordersDoNotSurroundHeader/Footer settings. Refused when nothing changes.

setPageBordersSurround(surround: { header?: boolean; footer?: boolean }): Command

setWatermark

Set the current section’s watermark, Word’s “Printed Watermark”; null removes it. scope: "document" sets every section. Refused when nothing changes. The watermark is painted behind the text of every page of the section (not when pagination is off), is not document content (search, selection, copy and markdown export never see it), and round-trips through DOCX as Word’s VML watermark.

setWatermark(watermark: Watermark | null, options?: SetWatermarkOptions): Command type Watermark = | { kind: "text"; text: string; // trimmed, at most 200 characters fontFamily: string | null; // null: the document's Normal font fontSize: "auto" | number; // pt, 8–400; "auto" fills the text area color: string; semitransparent: boolean; layout: "diagonal" | "horizontal"; } | { kind: "picture"; src: string; // an image src, like an image node's scale: "auto" | number; // percent of the picture's own size, 10–500 washout: boolean; }; type SetWatermarkOptions = { scope?: "section" | "document"; sectionId?: string; // target when a header/footer sub-editor has focus };

DEFAULT_TEXT_WATERMARK (from @stesura/core/constants) is Word’s default: “CONFIDENTIAL”, diagonal, semitransparent silver, auto size, the document’s font.

setOrientation

Switch the current section between portrait and landscape, swapping page width and height. Refused when already in that orientation.

setOrientation(orientation: "portrait" | "landscape"): Command

insertPageBreak

Insert a hard page break at the cursor (Word’s Ctrl+Enter). The paragraph is split around the break and the cursor lands after it; at the end of a block, the split leaves an empty paragraph for the cursor. Refused inside tables and outside section content.

insertPageBreak: Command

insertPageNumber

Insert a page-number field at the cursor. Refused inside code blocks.

insertPageNumber({ pageType: "currentPage" | "totalPages" }): Command

insertPageOfPage

Insert a “1 of 12” pair of page-number fields. middleText is the bare separator word (default "of"); a space is added on each side.

insertPageOfPage(middleText?: string): Command

Line Numbers

setLineNumbers

Merge attributes into the section’s line numbering (starting from the defaults when it is off), clamped to bounds.

setLineNumbers(attrs: Partial<{ countBy: number; // show only every Nth number (default 1) distance: number; // gutter gap in px (default 10) restart: "continuous" | "newPage" | "newSection"; // default "continuous" start: number; // first line's number, 1-based (default 1) }>): Command

toggleLineNumbers

Turn the section’s line numbering on (with the defaults) or off.

toggleLineNumbers(): Command

toggleSuppressLineNumbers

Toggle suppressLineNumbers on every suppressible block in the selection. Suppressed lines are skipped by the counter (Word semantics), not merely hidden. Pass suppress to force a value.

toggleSuppressLineNumbers(suppress?: boolean): Command

lineNumbersSuppressed

State reader, not a command: true when every suppressible block in the selection is suppressed.

lineNumbersSuppressed(state: EditorState): boolean

Headers & Footers

setHeaderFooterDistance

Set the section’s headerFromTop / footerFromBottom (px). Pass sectionId when dispatching on the main editor while a header/footer sub-editor holds focus. Refused when nothing changes.

setHeaderFooterDistance(options: { headerFromTop?: number; footerFromBottom?: number; }, sectionId?: string): Command

insertHeaderFooterContent / deleteHeaderFooterContent

Add or remove a header_footer_content variant on both the section’s header and footer. Inserting adds one empty paragraph wherever the variant is missing. Deleting the "default" variant is refused. insertHeaderFooterContentTr(tr, state, sectionId, variant) in @stesura/core/commands/internal is the composable form.

type HeaderFooterVariant = "default" | "first" | "even"; insertHeaderFooterContent(sectionId: string, variant: HeaderFooterVariant): Command deleteHeaderFooterContent(sectionId: string, variant: HeaderFooterVariant): Command

insertPageNumberPreset

Insert a page number into a section’s header (top) or footer (bottom) as a Word-style preset: a borderless 1×3 table with left-, centre- and right-aligned columns. Creates the variant if needed, all in one transaction. Dispatch it on the main editor, not a header/footer sub-editor.

insertPageNumberPreset({ sectionId: string, variant?: HeaderFooterVariant, // default "default" position: { vertical: "top" | "bottom"; horizontal: "left" | "center" | "right" }, pageType?: "currentPage" | "totalPages" // default "currentPage" }): Command

fixHeadersFooters

Repair header/footer structure: resolve null and duplicate variants, mirror each variant on both header and footer (always including "default"), and give every content node at least one paragraph. Refused when nothing needs fixing.

fixHeadersFooters: Command

Which variants a section displays (different first page, odd/even) follows from which header_footer_content nodes exist, so insertHeaderFooterContent / deleteHeaderFooterContent toggle them — see Header & Footer.

Borders

Border commands (setBorders, toggleOutline, insertHorizontalRule) are documented on the tables page. They also apply to styled blocks and horizontal rules.

Track Changes

enableTrackChanges / disableTrackChanges / toggleTrackChanges

Turn track-changes recording on or off. The optional show argument also sets visibility at the same time. Omitted, turning tracking on also turns visibility on, and turning it off leaves visibility as it is. In review mode, disableTrackChanges and toggleTrackChanges return false.

enableTrackChanges(show?: boolean): Command disableTrackChanges(show?: boolean): Command toggleTrackChanges(show?: boolean): Command

toggleTrackChangesVisibility

Flip whether pending suggestions are displayed. Tracking itself is untouched — edits keep being recorded while the markup is hidden.

toggleTrackChangesVisibility: Command

selectTrackChanges

Move the selection to cover the tracked change with the given id — inline marks and block-level suggestions (a deleted row, a pending attribute change) alike.

selectTrackChanges(selectionId: string): Command

resolveTrackChanges

Accept or reject the suggestion under the cursor (or covering the selection). With an empty selection it resolves the whole same-user, same-burst change; a drawn selection resolves every suggestion it touches, in full. moveToNext jumps to the next suggestion afterwards.

Dry runs (toolbar probes) answer false on a document with no pending changes, so Accept/Reject buttons disable themselves when there is nothing to review — the same applies to resolveAllTrackChanges and disableTrackChanges, which return false rather than dispatching when they would change nothing.

resolveTrackChanges(action: "approve" | "reject", moveToNext?: boolean): Command

resolveAllTrackChanges

Accept or reject every suggestion in the document at once.

resolveAllTrackChanges(action: "approve" | "reject"): Command

Track Changes Marks

Tracked inline edits are stored as marks from @stesura/core/schema:

  • insertion: pending inserted text, rendered as <ins>.
  • deletion: pending deleted text, kept in the document and rendered as <del>.
  • modification: a pending inline mark change. type is "addMark" (newValue holds the added mark’s JSON) or "removeMark" (previousValue holds the removed one).

Each mark carries id, date, userId and userName. Block-level changes live on node attributes instead: trackChanges (block insertion or deletion) and trackChangesModification (attribute changes).

Next Steps

Last updated on