Commands: Core
Generic commands for marks, node types, content insertion and attributes.
All commands follow the ProseMirror signature (state, dispatch?, view?) => boolean. Calling without dispatch is a dry run that returns whether the command would apply.
import { toggleBold, setMark, insertContent } from "@stesura/core/commands";Marks
toggleMark
Toggle a mark on the selection. With a bare cursor inside a word the whole word is toggled; at a word boundary the mark is stored for the next keystroke. On a range the mark is added if any part is missing it, and removed if it is present everywhere. Returns false when markType is undefined or the mark cannot apply.
toggleMark(markType: MarkType | undefined, attrs?: Attrs | null, options?: ToggleMarkOptions): CommandOptions:
expandSelectionToWord(defaulttrue): a bare cursor inside a word toggles the whole word.enterInlineAtoms(defaulttrue):falseskips the content of inline atoms fully covered by the range.onlyToggleOnAttrsMatch(defaulttrue): a mark with different attrs counts as missing, so toggling replaces it rather than removing it.removeWhenPresentandincludeWhitespaceare accepted but currently ignored.
toggleMarkOnEntireWord
toggleMark by mark name with expandSelectionToWord on. Returns false when the mark is not in the schema.
toggleMarkOnEntireWord(markName: string, attrs?: Attrs | null): CommandsetMark
Apply a mark, merging attributes into any existing mark of that type. If the merge leaves only null attrs, the mark is removed. A cursor inside a word marks the whole word; at a word boundary the mark is stored.
setMark(typeOrName: string | MarkType | undefined, attributes?: Attrs): CommandunsetMark
Remove a mark from the selection and from the stored marks.
unsetMark(typeOrName: string | MarkType | undefined, options?: { extendEmptyMarkRange?: boolean }): CommandextendEmptyMarkRange makes a bare cursor remove the mark across its whole contiguous range (e.g. unlink the entire link under the caret).
Convenience Mark Toggles
Built on toggleMarkOnEntireWord:
toggleBold: Command // strong; refused while an equation is selected
toggleItalic: Command // em; refused while an equation is selected
toggleUnderline: Command // underline
toggleStrike: Command // strikethrough
toggleSubscript: Command // subscript
toggleSuperscript: Command // superscript
toggleCode: Command // code; refuses to APPLY where the selection touches a link (removal still works)Nodes
setNode
Turn the selected blocks into a textblock type. When the selection sits in one parent, that parent’s attrs are carried over. If the type can’t be set directly, the blocks are first lifted out of their wrappers, in the same transaction. Non-textblock types return false.
setNode(typeOrName: string | NodeType, attributes?: Attrs): CommandtoggleNode
Switch the selected blocks to typeOrName, or back to toggleTypeOrName when they already are that type. When the selection sits in one parent, its attrs carry over to the new node, so overwrite any attr that must not be inherited in attributes.
toggleNode(
typeOrName: string | NodeType,
toggleTypeOrName: string | NodeType,
attributes?: Attrs
): CommandtoggleHeading
Toggle the selected blocks between heading at level and paragraph. The styleId is set to Heading<level> on the way up and reset to Normal on the way down. Returns false for a missing level.
toggleHeading(level: number | undefined): CommandinsertOrWrapNode
Insert a node of the given type, or wrap the selection in it. Block types wrap the selected blocks, or at a bare cursor insert an instance holding a placeholder paragraph. Inline types wrap the selected inline content, or insert an instance holding placeholder text, which is then selected. Returns false when the type is not in the schema, can’t hold the content, or (inline) the selection crosses a block boundary.
insertOrWrapNode(nodeName: string, attributes?: Attrs): CommanddeleteNode
Delete the node at a document position. Returns false when pos is null or holds no node.
deleteNode(pos: number | null): CommandContent Insertion
insertContent
Insert a node, a fragment, or HTML/JSON content (parsed against the schema) at the selection, leaving the caret after it. Plain text keeps the current marks. Block content inserted into an empty textblock replaces that block. Refused inside code blocks.
insertContent(value: Content | Node | Fragment): CommandinsertSymbol
Insert a literal character at the selection, keeping the current marks.
insertSymbol(symbol: string): CommandAttributes
updateAttributes
Set attributes on every node or mark of the given type in the selection, plus the innermost enclosing node of that type. On a collapsed selection a matching stored mark is updated too. Keys the type does not declare, and values that are already equal, are skipped. A no-op update returns false.
A value can be a resolver (current, node?) => next, for attrs whose new value depends on the old one.
updateAttributes({
attributes: Record<string, unknown | ((current: unknown, node?: Node) => unknown)>,
typeOrName: string | NodeType | MarkType
}): CommandupdateAttributeAt
Set a single attribute on the node at a known position. Returns false when no node sits at pos, or when type is given and doesn’t match.
updateAttributeAt({
attribute: string,
value: unknown,
pos: number,
type?: string | NodeType
}): CommandresetAttributes
Reset the named attributes to their schema defaults on every node or mark of the given type in the selection.
resetAttributes({
attributes: string | string[],
typeOrName: string | NodeType | MarkType
}): CommandFormatting
clearFormatting
Remove every mark from the selection except the track-changes marks (insertion, deletion, modification), so pending suggestions survive. Selected equations are cleared too. Refused inside code blocks.
clearFormatting(options?: {
expandSelectionToWord?: boolean; // default true: a bare cursor clears the surrounding word
clearTextBlockFormatting?: boolean; // also reset the paragraph/heading's indent, alignment,
// background, borders, line height, tab stops and spacing
resetStyleToNormal?: boolean; // also set styleId to Normal (a heading becomes a paragraph);
// only applies with clearTextBlockFormatting
}): CommandComposing Commands
composeCommands runs several commands against one transaction, so the group undoes in a single step and each command sees what the previous ones wrote:
import { composeCommands, setTextAlign, setSpacing, toggleBold } from "@stesura/core/commands";
const formatAsTitle = composeCommands(
toggleBold,
setTextAlign("center"),
setSpacing({ before: 24, after: 12 })
);
formatAsTitle(view.state, view.dispatch);See Composing Commands for sequenceCommands and the limits that apply to a composed group.
Helpers
All from @stesura/core/helpers.
State Inspection
isMarkActive, isNodeActive, getAttributes, getMarkAttributes, getNodeAttributes, getNodeType, getMarkType: read the active marks and nodes, and resolve type names against the schema.
Document Inspection
docChanged, getChangedRanges, findNode, findNodes, findChildrenInRange, forEachNode: walk and diff documents.
Transaction Classification
isHistoryTransaction(tr): boolean
isCollabTransaction(tr): boolean
isRemoteCollabTransaction(tr): boolean
isCollabOrHistoryTransaction(tr): booleanUtilities
isTextSelection, isWholeTableSelected, posToDOMRect, unWrap, createNodeFromContent, selectionToInsertionEnd: selection and DOM utilities.
Next Steps
- Commands: Formatting: alignment, spacing, indentation
- Commands: Text Style: font size, color, family
- Commands: Tables: table manipulation