Skip to Content
API Referencereferences

@stesura/references

Framework-free footnotes, endnotes and cross-references: plugins, commands, helpers and the note-panel store. The UI is in @stesura/references-react; the nodes are in the core schema. See the references guide for how the feature behaves.

Plugins

referencesPlugins

Every plugin the feature needs, in order. Place them after the core numbering plugins, so cross-references see counters updated in the same transaction.

import { referencesPlugins } from '@stesura/references' const plugins = [...stesuraPlugins(schema, editorId), ...referencesPlugins()]

The cross-reference and note-panel binding plugins are only available through referencesPlugins(), which keeps their order right.

notesPlugin

Note numbering on its own, for hosts that want neither cross-references nor the note panels. Takes an optional list of NoteKindConfigs; the default is footnotes and endnotes.

Per note kind: mints missing refs, lists the notes in anchor order (the anchors’ numbers are noteAnchorStore’s) and keeps the bodies in the doc-level container matched to them, creating missing bodies and deleting orphans and duplicates. One doc walk and one appended transaction for all kinds.

Parameters:
NameTypeDefault
kindsNoteKindConfig[]
Returns:
Plugin<NotesPluginState>

getFootnotes / getEndnotes

Exported from @stesura/core/plugins.

Footnotes in anchor (= numbering) order, from the merged notes plugin state.

Parameters:
NameTypeDefault
stateEditorState
Returns:
FootnoteItem[]

Endnotes in anchor (= numbering) order, from the merged notes plugin state.

Parameters:
NameTypeDefault
stateEditorState
Returns:
EndnoteItem[]

Commands

insertCrossReference

Replaces the selection with a cross-reference (or inserts it at the caret) and node-selects it. The references store derives its label from the target (docs/partially-executed/derived-state-stores-plan.md, DS-9), except where it is stored: a text reference’s bookmark text and a page reference’s page.

Returns false if targetId or refTo is empty, if a text or page reference has no text (an empty label renders as an invisible node), or if canInsertCrossReference fails.

Parameters:
NameTypeDefault
__0{ targetId: string; text?: string; refTo: CrossRefType; separator: string | null; }

What a text reference shows (the target’s text) or a page reference’s page until pagination lays it out; ignored by the other kinds.

Returns:
Command

canInsertCrossReference

Whether a cross-reference could replace the current selection: the positional half of insertCrossReference’s guard, usable before a target is picked. The toolbar probes it.

Parameters:
NameTypeDefault
stateEditorState
dispatch(tr: Transaction) => void
viewEditorView
Returns:
boolean

goToCrossRefTarget

Selects the node a cross-reference points at and scrolls it into view.

Returns false for a missing, unresolvable or missingReference-flagged target, so the caller’s default handling still runs (REF-44).

Parameters:
NameTypeDefault
referenceCrossRefLink
Returns:
Command

insertFootnote

Inserts a footnote anchor at the end of the selection. The notes plugin numbers it and creates its body. See createInsertNoteAnchor.

Parameters:
NameTypeDefault
refstring
onSuccess(ref: string) => void
Returns:
Command

insertEndnote

Inserts an endnote anchor at the end of the selection. The notes plugin numbers it and creates its body (and the endnotes container if missing). See createInsertNoteAnchor.

Parameters:
NameTypeDefault
refstring
onSuccess(ref: string) => void
Returns:
Command

deleteFootnote / deleteEndnote

Deletes a footnote: its body and every anchor referencing it, as one undoable step. Backs the note panel banner’s “Delete footnote”.

Parameters:
NameTypeDefault
refstring
Returns:
Command

Deletes an endnote: its body and every anchor referencing it, as one undoable step. Backs the note panel banner’s “Delete endnote”.

Parameters:
NameTypeDefault
refstring
Returns:
Command

Selects the footnote anchor before (-1) or after (1) footnoteRef in numbering order, then calls onNavigate with its ref. Doesn’t wrap: false at either end and for an unknown ref.

Parameters:
NameTypeDefault
noteRefstring
direction1 | -1
onNavigate(ref: string) => void
Returns:
Command

Selects the endnote anchor before (-1) or after (1) endnoteRef in numbering order, then calls onNavigate with its ref. Doesn’t wrap: false at either end and for an unknown ref.

Parameters:
NameTypeDefault
noteRefstring
direction1 | -1
onNavigate(ref: string) => void
Returns:
Command

nextFootnote / previousFootnote / nextEndnote / previousEndnote

Selects the next footnote anchor after the selection, wrapping around. The probe is true whenever the doc has a navigable footnote anchor.

Parameters:
NameTypeDefault
onSuccess(ref: string) => void
Returns:
Command

Selects the footnote anchor before the selection, wrapping around. The probe is true whenever the doc has a navigable footnote anchor.

Parameters:
NameTypeDefault
onSuccess(ref: string) => void
Returns:
Command

Selects the next endnote anchor after the selection, wrapping around. The probe is true whenever the doc has a navigable endnote anchor.

Parameters:
NameTypeDefault
onSuccess(ref: string) => void
Returns:
Command

Selects the endnote anchor before the selection, wrapping around. The probe is true whenever the doc has a navigable endnote anchor.

Parameters:
NameTypeDefault
onSuccess(ref: string) => void
Returns:
Command

Helpers

isCrossRefSelected

Whether the selection covers exactly one cross_reference.

Parameters:
NameTypeDefault
stateEditorState
Returns:
boolean

getCrossRef

The cross_reference node the selection covers exactly, or null. “Edit” replaces from..to, so a selection running past the node would lose that text (A1).

Parameters:
NameTypeDefault
stateEditorState
Returns:
Node | null

findCrossRefTargetWithPos

The node a cross-reference points at, with its position, or undefined.

Mirrors crossReferencesPlugin’s target map so the label, the preview and the jump agree: numbered types only, header/footer regions and hidden content skipped (REF-44), last match wins on a duplicate id.

Parameters:
NameTypeDefault
docNode
targetIdstring
Returns:
NodeWithPos | undefined

computeFullContext

The fullContext label of a numbered target: each level of its counter path rendered with the level’s own segment of the list’s level text (see ownSegment), joined with separator. Owning segments is what keeps "%1.%2."-style level texts from repeating the parent chain.

Bullet levels, levels without a config and level texts that don’t name their own placeholder contribute nothing. The path stops at the first non-numeric segment (0 is valid).

Parameters:
NameTypeDefault
stateEditorState
listRefstring

List whose level configs format the path.

pathstring

Dot-separated counter path, e.g. "1.2.3".

languagestring

BCP 47 tag for localized formats; falls back to en-GB.

separatorstring

Inserted between levels. Defaults to "".

Returns:
string

E.g. "1.2.a", or "" when nothing resolves.

getAboveBelowText

The localized “above”/“below” label of an aboveBelow cross-reference.

Parameters:
NameTypeDefault
languagestring

BCP 47 tag of the target; unknown languages fall back to English.

isAboveboolean

Whether the target precedes the reference.

Returns:
string

findFootnotes / findEndnotes

Returns the node and position of the doc-level footnotes container.

Parameters:
NameTypeDefault
docNode
Returns:
NodeWithPos | undefined

Returns the node and position of the doc-level endnotes container, if it exists (it is created lazily by the notes plugin when the first endnote is inserted).

Parameters:
NameTypeDefault
docNode
Returns:
NodeWithPos | undefined

Also exported from @stesura/references: findFootnoteAnchor, findFootnoteBodyPos, findEndnoteBodyPos, the kind-generic findNoteAnchor / findNoteBodyPos / findNoteContainer, and isHeaderFooterRegion / isSkippedNoteRegion.

Agent issues

The IssueProvider that hands broken cross-references to the agent’s issue tools; pass it to createIssueTools in @stesura/ai.

referenceIssues

Cross-references whose target is gone (Word’s “Error! Reference source not found.”), for createIssueTools. No automatic fix: the agent or the user points them at a new target.

NameTypeDefault
sourcestring
collect(state: EditorState) => AiIssue[]

The current findings, in document order.

apply(state: EditorState, fixes: readonly AiIssueFix[]) => AiIssueApplyResult

Applies fixes to issues collect returns. Tracked fixes come back as a plain edit the caller turns into suggestions; direct ones as the change itself. Omitted: nothing is fixable.

Note-panel store

The panel atoms are exported from @stesura/core/stores (the page footer opens notes too); the registries and kinds from the package root. Which note is open is tracked per editor and per kind ("footnote" | "endnote").

ExportPurpose
openNotePanel(editorId, kind, ref, options?)Opens the panel on ref and closes the other kind’s. { focusOnOpen: false } leaves focus where it is.
closeNotePanel(editorId, kind)Closes the panel.
activeNoteRefAtom(editorId, kind)Atom holding the open note’s ref, or null. Stable per editor and kind.
noteFocusRequestAtom(editorId, kind){ id, focus }, with a new id on every open and close. Consumed by <FocusOnRequest>.
footnoteEditorRegistry / endnoteEditorRegistryMounted note sub-editors.
footnoteKind / endnoteKindSub-editor kind configs, for useSubEditor in a custom panel.
Last updated on