# Command and target API

Command drafts, rejection results, external edits and captured ranges.

Signatures come from public package exports. Descriptions are checked against current JSDoc; identifiers retain their original names.

## Command context

Commands are pure and synchronous. Use props.commands for nested work; see [Commands](/en/guide/commands).

### CommandProps

Context of a pure author command. All nested commands share its transaction draft.

```ts
export interface CommandProps {
    readonly state: EditorState;
    readonly tr: SourceTransaction;
    readonly editor: MsfeEditor;
    readonly commands: SingleCommands;
    readonly dispatch: (() => void) | undefined;
    chain(): ChainedCommands;
    can(): SingleCommands;
}
```

**state** — Current draft state; never the stale mounted state during a chain.

**tr** — Source-native transaction builder.

**editor** — Canonical editor; commands must use props.commands for nested mutations.

**commands** — Nested commands apply to this same unpublished draft.

**dispatch** — Undefined during can; publication remains owned by the command manager.

**chain** — @returns A nested atomic chain on this same draft.

**can** — @returns Pure queries starting at this draft, without changing it.

## Source transactions

Each step uses the current draft coordinates, not stale initial offsets.

### SourceTransaction

MSFE transaction builder; deliberately not a ProseMirror Transaction.

```ts
export interface SourceTransaction {
    readonly state: EditorState;
    insertText(text: string): boolean;
    replace(from: number, to: number, insert: string): boolean;
    setSelection(anchor: number, head?: number): boolean;
    effect<T>(effect: Effect<T>, value: T): boolean;
    setMeta<T>(metadata: Metadata<T>, value: T): boolean;
    apply(spec: TransactionSpec): boolean;
}
```

**state** — Current immutable draft after all preceding steps.

**insertText** — @param text - Exact text to insert at every selected range.
@returns Whether accepted into the draft.

**replace** — @param from - Inclusive source offset.
@param to - Exclusive source offset.
@param insert - Replacement text.
@returns Validity.

**setSelection** — @param anchor - Directed range start.
@param head - Directed range end, default anchor.
@returns Validity.

**effect** — @param effect - Typed effect.
@param value - Payload in the current draft coordinates.
@returns Validity.

**setMeta** — @param metadata - Typed token.
@param value - Correlation value.
@returns Validity.

**apply** — Explicit backend escape hatch.
@param spec - CodeMirror draft step.
@returns Validity.

## Planner adapter

Adapt a pure CodeMirror planner to the same command chain.

### sourceCommand

Adapts an existing pure CodeMirror planner into the same author-command transaction.
@param planner - Backend source planner.
@returns Curried command body suitable for addCommands or chain.command.

```ts
function sourceCommand(planner: MsfeCommand): Command
```

## Semantic format commands

`toggleSourceMark({ node: 'StrongEmphasis', marker: '**', markerNode: 'EmphasisMark' })`
returns a pure planner for `editor.run()` or `sourceCommand()`. Features declare grammar; Core handles
paired markers, directed selections and complete shells, then verifies both the requested format and
existing paired semantics. A complete body selection may expand over hidden shells. When no lossless
plan is proved, the command returns `null` without changing source or history. Partial mark removal and
crossing ranges are not automatically split. Empty selections still seed paired markers for free source input.

`querySourceInline(state, range)` reads endpoint ancestors without parsing, scanning the document or
running a format command. Use it for structural availability. `reason === null` permits planning but
does not guarantee a specific combination; eligible blank prose may have no node. Explicit planning
is limited to one inline owner of at most 20,000 characters, with a 12ms parse-validation budget.
Failure to verify in that budget also refuses. Multiple ranges are planned before one atomic commit.
The low-level `toggleDelimiter` remains a literal source tool.

### SourceMarkFormat

Grammar owned by a semantic paired-mark feature.

```ts
export interface SourceMarkFormat {
    readonly node: string;
    readonly marker: string;
    readonly markerNode: string;
}
```

**node** — Parser node representing the requested format.

**marker** — Preferred source spelling when adding the format.

**markerNode** — Direct opening/closing child node name.
### SourceInlineQuery

One common inline owner, or an explanation of why formatting is unavailable.

```ts
export interface SourceInlineQuery {
    readonly node: SyntaxNode | null;
    readonly reason: "readonly" | "unparsed" | "structure" | "literal" | "selection" | null;
}
```

**node** — Accepted paragraph, heading or single table cell.

**reason** — A structural refusal; null means this range is eligible for an explicit command.
### querySourceInline

Reads only source endpoints and their ancestors; never starts parsing or serializes the document.
@param state - Accepted source state.
@param range - One intended inline range.
@returns A common inline owner or a structural refusal.

```ts
function querySourceInline(state: EditorState, range: { readonly from: number; readonly to: number; }): SourceInlineQuery
```
### toggleSourceMark

Toggles a parser-confirmed format without replacing Markdown with another document model.
@param format - Owning feature's syntax and source spelling.
@returns A pure atomic command; unsafe combinations leave source and history unchanged.

```ts
function toggleSourceMark(format: SourceMarkFormat): MsfeCommand
```

## Result branches

Named commands return booleans; inspect lastCommandResult for details.

### MsfeResult

Explicit mutation rejection instead of silently retargeting user intent.

```ts
export type MsfeResult = {
    readonly ok: true;
} | {
    readonly ok: false;
    readonly reason: "destroyed" | "not-mounted" | "readonly" | "stale-target" | "not-applicable" | "invalid-change" | "composing" | "not-installed" | "source-provider-locked";
};
```

## External changes

baseRevision is the local source revision, not a server ETag.

### MsfeExternalChange

Versioned external source mutation.

```ts
export interface MsfeExternalChange {
    readonly documentId: string;
    readonly baseRevision: number;
    readonly changes: ChangeSpec;
    readonly origin?: string;
}
```

**documentId** — Expected document identity.

**baseRevision** — Exact revision to which the offsets refer.

**changes** — CodeMirror logical source changes.

**origin** — Correlation source for host echo suppression.

## Exact targets

Suitable for short menus; source changes can invalidate the target.

### MsfeTarget

Captured toolbar target, valid only for its exact document revision.

```ts
export interface MsfeTarget {
    readonly instanceId: string;
    readonly documentId: string;
    readonly revision: number;
    readonly selection: EditorSelection;
}
```

**instanceId** — Instance identity prevents applying a target captured in a different editor.

**documentId** — Document identity when the UI action began.

**revision** — Source version when the UI action began.

**selection** — Original directed selection.

## Mapped intent

See [Selection and captured intent](/en/guide/selection) for the complete async helper.

### SourceAnchor

Document-generation-scoped intent with explicit position remapping.

```ts
export interface SourceAnchor {
    getRange(): MsfeSourceRange | null;
    target(): MsfeTarget | null;
    release(): void;
}
```

**getRange** — @returns Current mapped interval, or null after removal, release or document replacement.

**target** — @returns Fresh target only while captured text remains unchanged; never silently retargets modified content.

**release** — Ends ownership without changing text or history.

## Mapping policy

Choose endpoint and deletion semantics for the feature independently of presentation.

### RangePolicy

Explicit boundary affinity and deletion policy, independent of how a range is painted.

```ts
export interface RangePolicy {
    readonly inclusiveStart?: boolean;
    readonly inclusiveEnd?: boolean;
    readonly onDelete?: "drop" | "keep";
}
```

**inclusiveStart** — Include text inserted exactly at the start; default false.

**inclusiveEnd** — Include text inserted exactly at the end; default false.

**onDelete** — Fully deleted/replaced ranges disappear by default; keep collapses to a point if needed.

## Source block commands

Resolve complete syntax blocks and run commands with a captured `MsfeTarget`. Planners preserve the source payload, use one undo step and reject cross-flow moves.

### SourceBlock

Exact source block and its sibling flow; never a second document model.

```ts
export interface SourceBlock extends MsfeSourceRange {
    readonly name: string;
    readonly scope: MsfeSourceRange;
}
```

**name** — Accepted parser node name.

**scope** — Range in which complete sibling blocks may be rearranged.

**from** — Inclusive source start.

**to** — Exclusive source end.

### SourceBlockSide

Source insertion side relative to a complete block.

```ts
export type SourceBlockSide = "before" | "after";
```

### sourceBlockAt

Resolves only the pointer/caret ancestor chain, without scanning sibling content.
@param state - Accepted source and parser snapshot.
@param position - Logical position inside a block.
@param scope - Optional required sibling flow, used to lift a drop target out of nested content.
@returns Nearest complete block in an admitted sibling flow.

```ts
function sourceBlockAt(state: EditorState, position: number, scope?: SourceBlock["scope"]): SourceBlock | null
```

### sourceBlockSibling

@param state - Accepted parser.
@param block - Current complete block.
@param direction - Previous or next sibling.
@returns Adjacent structural block without enumerating the document.

```ts
function sourceBlockSibling(state: EditorState, block: SourceBlock, direction: -1 | 1): SourceBlock | null
```

### sameSourceBlockScope

@param a - First block.
@param b - Second block.
@returns Whether both belong to exactly the same source flow.

```ts
function sameSourceBlockScope(a: SourceBlock, b: SourceBlock): boolean
```

### insertSourceBlock

@param block - Explicit adjacent block.
@param side - Source boundary.
@param markdown - New complete Markdown.
@returns One atomic source insertion; delimiters in the payload remain unchanged.

```ts
function insertSourceBlock(block: SourceBlock, side: SourceBlockSide, markdown: string): MsfeCommand
```

`replaceSourceBlock(block, markdown)` replaces a complete block atomically, preserving required
sibling separators and validating enclosing container boundaries. Run it with
`editor.run(command, capturedTarget)` to reject stale menu intents.

### replaceSourceBlock

@param block - Complete captured source block.
@param markdown - Canonical replacement.
@returns One guarded replacement preserving sibling and container boundaries.

```ts
function replaceSourceBlock(block: SourceBlock, markdown: string): MsfeCommand
```

### SourceBlankLine

An empty physical line in an admitted block flow, without a synthetic syntax node.

```ts
export interface SourceBlankLine extends MsfeSourceRange {
    readonly scope: MsfeSourceRange;
}
```

**scope** — Editable sibling flow excluding the enclosing delimiters.

**from** — Inclusive source start.

**to** — Exclusive source end.

### sourceBlankLineAt

@param state - Accepted source and syntax.
@param position - Pointer or caret position.
@returns A blank line in an admitted flow, never whitespace owned by code or list syntax.

```ts
function sourceBlankLineAt(state: EditorState, position: number): SourceBlankLine | null
```

### insertAtSourceBlankLine

@param line - Captured editable blank line.
@param markdown - Complete canonical payload.
@returns One guarded replacement preserving sibling separators and enclosing containers.

```ts
function insertAtSourceBlankLine(line: SourceBlankLine, markdown: string): MsfeCommand
```

### removeSourceBlock

@param block - Complete captured block.
@returns One source deletion, validated again against the command draft.

```ts
function removeSourceBlock(block: SourceBlock): MsfeCommand
```

### moveSourceBlock

Moves exact source, with one accepted transaction and one undo event through editor.run.
@param block - Source block captured when the gesture began.
@param target - Complete sibling at the final pointer position.
@param side - Before or after that sibling.
@returns A planner rejecting self/descendant/cross-flow and obsolete targets.

```ts
function moveSourceBlock(block: SourceBlock, target: SourceBlock, side: SourceBlockSide): MsfeCommand
```

## Editing boundaries

Features declare semantic ranges of hidden source through `addEditingBoundaries()` or the
`editingBoundaries` descriptor field. The same option on `MdEditor`, `createMdEditor` and
`createMdEditorExtensions` lets the host override defaults by node. See
[Caret and editing boundaries](/en/guide/md-editor#caret-and-editing-boundaries) for integration.
These declarations govern navigation, selection and native input protection. Structural actions
for Enter, Shift-Enter and Mod-Enter use the `addBlockInput()` contract below.

### Ranges and policies

All offsets belong to the current `state.doc` and must satisfy
`0 ≤ from ≤ contentFrom ≤ contentTo ≤ to ≤ state.doc.length`. Optional
`selectFrom/selectTo` must enclose `from/to` within the document. An invalid range throws
`invalid-editing-boundary` when the rule resolves. Return `null` when syntax cannot be confirmed.

| `kind` | Meaning and typical use |
| --- | --- |
| `style` | Bold, emphasis, code, links and other inline formats with editable content inside paired shells |
| `block` | Code or containers; shell protection includes adjacent line breaks needed to retain structure |
| `prefix` | Heading, list or quote prefixes; the caret enters the content after the prefix |
| `separator` | Compressed structural gaps skipped by navigation; the product permits native deletion to join text |
| `syntax` | Table pipes or alignment rows; `selectFrom/selectTo` can identify the complete table |
| `atom` | Whole components or empty fences; the caret stays outside and deletion at an edge first selects the object |

`navigation: 'content'` is the default inline entry policy; `split` keeps both inside and outside
stops. Blocks can explicitly declare `content` to clamp native line endpoints to their content,
as table rows do for outer pipes. Horizontal arrows still skip duplicate stops, and deletion
protection is unchanged. Omitted block policies retain the default navigation across shells.
Currently `navigation: 'native'` opts the entire syntax rule out of the shared runtime,
including input protection. `deletion: 'unwrap'` removes the full style or prefix at its relevant
edge; `protect` retains the shell; `deletion: 'native'` delegates editing while keeping navigation
rules. Declare the deletion policy explicitly. A complete owner selection permits whole removal;
partial selections retain its shell.

### EditingBoundary

Current source interval and its editable interior, never a second content model.

```ts
export interface EditingBoundary {
    readonly from: number;
    readonly to: number;
    readonly contentFrom: number;
    readonly contentTo: number;
    readonly kind: EditingBoundaryKind;
    readonly selectFrom?: number;
    readonly selectTo?: number;
    readonly navigation?: "content" | "split" | "native";
    readonly deletion?: "unwrap" | "protect" | "native";
}
```

**from** — Start of the complete semantic owner.

**to** — End of the complete semantic owner.

**contentFrom** — First editable source position.

**contentTo** — Last editable source position.

**kind** — Ownership determines exterior navigation and whole-object deletion.

**selectFrom** — Start of a larger owner required for whole-object removal, such as a table containing this separator.

**selectTo** — End of the same whole-object removal owner. Defaults to this boundary's end.

**navigation** — Content enters styles directly and clamps block shells to content; split keeps exterior and interior style stops.

**deletion** — Unwrap removes the shell at a collapsed edge; protect preserves it; native delegates edits.

### EditingBoundaryKind

Semantic ownership of hidden source, independent of DOM decoration shape.

```ts
export type EditingBoundaryKind = "style" | "block" | "prefix" | "separator" | "syntax" | "atom";
```

### Queries and priority

`nodes` names parser nodes. `resolve` is a pure query over current source: do not dispatch, retain
old node offsets or infer positions from visible line counts. Navigation queries endpoints and
ancestors, and editing queries syntax intersecting the actual changes. Custom resolvers should
keep their work within those same bounds.

The first rule for each node in extension-priority order wins. Host overrides precede feature
defaults. Rules replace whole declarations rather than merging fields; returning `null` opts
that syntax rule out without falling back to the next one. Materialized block components
independently contribute whole-object ranges, superseding syntax rules within them.

The default `when: 'hidden'` enables rules when presentation hides syntax globally. `when: 'prose'`
also queries rules in Markdown editing. To protect unrevealed code or similar structures, first
check the specific range through `isSourceRevealed(state, node)` and return `null` when revealed.
Boundary normalization is disabled in source mode and during composition.

### EditingBoundaryRule

Feature-owned facts consumed by the shared navigation and editing runtime.

```ts
export interface EditingBoundaryRule {
    readonly nodes: readonly string[];
    readonly when?: "hidden" | "prose";
    readonly resolve: (context: EditingBoundaryContext) => EditingBoundary | null;
}
```

**nodes** — Syntax owners; the first declaration for a name wins by extension precedence.

**when** — Hidden syntax only by default; prose also covers always-collapsed separators.

**resolve** — @param context - Current source facts.
@returns A complete owner, or null when syntax remains literal.

### EditingBoundaryContext

Pure feature query at one canonical endpoint.

```ts
export interface EditingBoundaryContext {
    readonly state: EditorState;
    readonly node: SyntaxNode;
    readonly position: number;
}
```

**state** — Accepted immutable Markdown state.

**node** — Confirmed Markdown node, excluding embedded-language overlays.

**position** — Endpoint or changed-range position being resolved.

### Installation and lifecycle

Ordinary features install automatically through `addEditingBoundaries()`. Advanced CM integrations
can install `editingBoundaries(rules)` from `addCodeMirrorExtensions()`; multiple installations
reuse the same runtime. Empty `rules` returns an empty extension without uninstalling other
features' rules. MD Editor's `editingBoundaries: []` also retains defaults. Options are read on
creation; updating Vue props does not reinstall the rules.

Input protection plans complete changes before accepting the transaction, preserving annotations
and mapping effects. Moving the caret neither writes source nor adds history. Explicit structure
commands, external document updates and history retain their own authority. Transactions that
bypass filtering only receive selection recovery when needed, guarded by current state identity
and disposal. Read-only mode does not accept text writes.

### editingBoundaries

@param rules - Feature-owned syntax contracts, highest precedence first.
@returns Shared navigation, input protection and endpoint recovery; duplicate installations reuse one runtime.

```ts
function editingBoundaries(rules: readonly EditingBoundaryRule[]): Extension
```

## Block-owned Enter

Declare syntax ownership with `addBlockInput()`; see [Commands](/en/guide/commands). `planBlockInput` is the pure planner entry point. A `null` result leaves native handling to the caller.

### BlockInputRule

Unique syntax ownership; omitted intents delegate, while unregistered block nodes stop routing.

```ts
export interface BlockInputRule {
    readonly nodes: readonly string[];
    readonly enter?: BlockInputBehavior;
    readonly lineBreak?: BlockInputBehavior;
    readonly exit?: BlockInputBehavior;
}
```

**nodes** — Exact syntax node names owned by this declaration.

**enter** — Enter behavior, decided from current source rather than a key-press counter.

**lineBreak** — Shift-Enter behavior for an explicit line break.

**exit** — Mod-Enter behavior for leaving a block.

### BlockInputContext

Immutable whole-selection input; handlers plan without dispatching or mutating storage.

```ts
export interface BlockInputContext {
    readonly state: EditorState;
    readonly intent: BlockInputIntent;
    readonly targets: readonly BlockInputTarget[];
}
```

**state** — Canonical source state before any input changes.

**intent** — Requested semantic keyboard gesture.

**targets** — All endpoints, resolved to owners sharing this handler; nonempty ranges contribute both ends.

### BlockInputTarget

A selection endpoint and the current syntax owner resolved by the shared router.

```ts
export interface BlockInputTarget {
    readonly range: SelectionRange;
    readonly position: number;
    readonly node: SyntaxNode;
}
```

**range** — Original full range, retaining selection direction.

**position** — Endpoint in the unchanged source document.

**node** — Nearest participating block for this endpoint.

### BlockInputIntent

Semantic gestures mapped from Enter, Shift-Enter and Mod-Enter.

```ts
export type BlockInputIntent = "enter" | "lineBreak" | "exit";
```

### BlockInputResult

One atomic source edit, delegation to an ancestor, or native keyboard fallback.

```ts
export type BlockInputResult = TransactionSpec | "parent" | "native";
```

### BlockInputHandler

@param context - Validated whole-selection input.
@returns A pure decision for the entire gesture.

```ts
export type BlockInputHandler = (context: BlockInputContext) => BlockInputResult;
```

### BlockInputBehavior

A planner or a fixed ownership decision, requiring no per-key callback.

```ts
export type BlockInputBehavior = BlockInputHandler | "parent" | "native";
```

### planBlockInput

Resolves ownership without parsing, dispatching or scanning unrelated blocks.
@param state - Canonical immutable source state.
@param intent - Requested keyboard gesture.
@returns One whole-selection edit, or native fallback with no partial mutation.

```ts
function planBlockInput(state: EditorState, intent: BlockInputIntent): TransactionSpec | null
```
