Skip to Content
API Referencecorecore/sub-editor

@stesura/core/sub-editor

Shared machinery behind nested editors (footnotes, endnotes, headers/footers). One SubEditorKindConfig per kind drives the inner schema (buildSubEditorSchema), the inner state (createSubEditorState) and the main-to-inner binding plugin (createSubEditorBindingPlugin). Edits flow back to the main editor through forwardInnerSteps.

Functions & values

buildSubEditorSchema

Parameters:
NameTypeDefault
baseSchema<any, any>
configPick<SubEditorKindConfig<K>, "kind" | "wrapperName" | "regionNodeName" | "contentExpr" | "filterOutNodes">
Returns:
Schema<any, any>

createSubEditorBindingPlugin

Parameters:
NameTypeDefault
configSubEditorKindConfig<K>
Returns:
Plugin<any>

createSubEditorRegistry

Parameters:
NameTypeDefault
keyId(key: K) => string
Returns:
SubEditorRegistry<K>

createSubEditorState

Parameters:
NameTypeDefault
pluginsPlugin<any>[]
schemaSchema<any, any>
configCreateDocConfig
wrapperAttrsAttrs
blockContentNode[] | Fragment
Returns:
EditorState | null

describeSubEditorFailure

Parameters:
NameTypeDefault
failureSubEditorBuildFailure
Returns:
string

findUnrepresentableNodeTypes

Parameters:
NameTypeDefault
contentFragment
innerSchema<any, any>
Returns:
string[]

forwardInnerSteps

Parameters:
NameTypeDefault
__0{ outerTr: Transaction; outerSchema: Schema<any, any>; transactions: readonly Transaction[]; regionPos: number; logContext: string; }
Returns:
boolean

getAllSubEditors

Parameters:
NameTypeDefault
mainEditorIdstring
Returns:
AnySubEditor[]

recoverSubEditorAfterFailedForward

Parameters:
NameTypeDefault
configSubEditorKindConfig<K>
editorActiveSubEditor<K>
outerStateEditorState
Returns:
void

subscribeSubEditors

Parameters:
NameTypeDefault
listener() => void
Returns:
() => void

Types

ActiveSubEditor

NameTypeDefault
viewEditorView
keyK
mainEditorIdstring

The main editor this sub-editor belongs to. Registries are page-global, so without it two editors showing the same note/section would collide and a binding plugin would push its edits into the other editor’s panel.

AnySubEditor

NameTypeDefault
viewEditorView
mainEditorIdstring

SubEditorBuildFailure

NameTypeDefault
reason"schema" | "unrepresentable"

SubEditorDocResult

NameTypeDefault
okboolean

SubEditorKindConfig

NameTypeDefault
kindstring

Human-readable kind name, used in log messages (“headerFooter”, “footnote”, …).

pluginKeyPluginKey<any>

The binding plugin’s key. MUST keep its existing string label — sub-editor plugin exclusion lists and host plugin filtering match on it.

registrySubEditorRegistry<K>

Registry of currently mounted sub-editors of this kind.

wrapperNamestring

Inner wrapper node name; its spec is the main section’s, so section CSS vars and plugins keep working. E.g. “section_footer_header”, “section”.

regionNodeNamestring

The main-doc node this kind binds to (“header_footer_content”, “footnote_body”, …). The inner section_content takes ITS content expression, so the two can never drift; buildSubEditorSchema also asserts the derived schema can hold everything this node allows.

contentExprstring | ((base: Schema<any, any>) => string)

Override for the inner section_content expression. Leave unset (no built-in kind sets it): the region node’s own expression keeps the schemas in step. An override must be at least as strict as the region node, or inner edits produce steps the main doc rejects.

filterOutNodesstring[]

Node names stripped from the base schema when deriving the inner schema.

validateContentAgainststring

Inner node whose spec validates the initial content in createSubEditorState (default “section_content”). Notes use their body node type.

findRegion(state: EditorState, key: K) => SubEditorRegion | null

Locate the bound region in a main-editor state.

isRegionNode(node: Node, key: K) => boolean

Recognize the region node of a given key among removed nodes, so the binding can react when the region disappears (e.g. a remote user deletes the note whose body is being edited). Omit when removal is handled elsewhere (the header/footer panel watches the main state itself).

onRegionRemoved(key: K, state: EditorState) => void

Called (outside the render cycle) after the binding unregisters an editor whose region was removed. Feature-level cleanup: close the panel, toast.

onForwardRegionMissing(key: K) => void

Called when inner→outer forwarding can’t find the region in the main doc (defensive — normally the binding’s removal path fires first).

prepareOuterTr(tr: Transaction) => void

Stamp kind-specific metas on the outer transaction built from inner steps (headers/footers add enableHeaderFooterEdits for the guard plugin). fromSubEditor and skipTrackChanges are always set by the machinery.

extraExcludedPluginsstring[]

Plugin key labels to exclude from the sub-editor ON TOP of the shared base list.

extraPlugins(schema: Schema<any, any>) => Plugin<any>[]

Kind-specific plugins, placed AHEAD of the host’s filtered plugins so they win for the same key binding — headers/footers use this to suppress document-wide keys (Mod-A, Mod-F).

editorId(key: K, mainEditorId: string) => string

The sub-editor’s editorId (store scoping, toolbar routing). MUST include mainEditorId: the store registry is page-global, so a bare key would cross-wire two editors open on the same note.

matchesSearchRegion(region: SearchMatchRegion, key: K) => boolean

True when a classified search-match region belongs to the sub-editor key.

viewSpec((view: EditorView) => PluginView) | undefined

Plugin view spec for the binding plugin on the MAIN editor. Notes use it to turn a NodeSelection into a TextSelection when a panel opens (the anchor may be deleted concurrently).

SubEditorRegion

NameTypeDefault
posnumber
nodeNode

SubEditorRegistry

NameTypeDefault
atomWritableAtom<ActiveSubEditor<K>[]>
keyId(key: K) => string

Stable identity for a key within one main editor.

register(entry: ActiveSubEditor<K>) => void
unregister(mainEditorId: string, key: K) => void
getAll(mainEditorId: string) => ActiveSubEditor<K>[]

Entries belonging to mainEditorId only.

get(mainEditorId: string, key: K) => ActiveSubEditor<K> | undefined

The registered editor bound to key under mainEditorId, if mounted.

Other exports

ExportTypeDescription
SUB_EDITOR_EXCLUDED_PLUGINSstring[]Plugin key labels stripped from the main editor’s plugins before mounting any sub-editor. Kinds add more via SubEditorKindConfig.extraExcludedPlugins.
Last updated on