# Commands and atomic transactions

Call, compose, query and author commands with failure and history semantics.

## Three ways to invoke a command

These calls assume `StarterKit` is installed. A visible TypeScript command does not prove that its runtime feature is installed.

~~~ts
const applied = editor.commands.toggleBold()
const enabled = editor.can().toggleBold()
const committed = editor.chain().insertText('A').insertText('B').run()
if (!committed) console.warn(editor.lastCommandResult)
~~~

Commands return booleans. `can()` runs a disposable draft without publishing or changing `lastCommandResult`. A chain executes sequentially on one draft and publishes once at `run()`. Any failed step discards the complete chain. Chains cannot contain asynchronous steps.

## Author a chainable business command

~~~ts
import { Extension } from '@quirekit/msfe-core'

declare module '@quirekit/msfe-core' {
  interface Commands<ReturnType> {
    /** Commands installed by the signature feature. */
    signature: {
      /** @param name - Literal author name. @returns Command outcome. */
      insertSignature(name: string): ReturnType
    }
  }
}
export const Signature = Extension.create({
  name: 'signature',
  addCommands() {
    return {
      insertSignature: (name: string) => ({ tr }) => {
        if (!name.trim() || /[\r\n]/.test(name)) return false
        return tr.insertText(`— ${name.trim()}`)
      },
    }
  },
})
~~~

Install `Signature` before calling `editor.can().insertSignature('Ada')`. If user text must remain literal Markdown content, encode the relevant field or constrain allowed characters; do not confuse text with a trusted syntax template.

## Draft state and nested commands

A command factory receives business arguments and returns a synchronous pure function. Read `props.state` or `tr.state`, which includes preceding chain steps. `props.editor.state` still represents the mounted state before publication.

Continue the draft through `props.commands`, `props.chain()` and `tr.replace()`. Never call `editor.commands` to dispatch another mutation from a command, invoke `dispatch` yourself, make requests, open dialogs or mutate storage. Applicability queries execute your code too.

`tr.replace(from, to, insert)` takes coordinates in the current draft. Recompute or map offsets after preceding steps change the length. The advanced `tr.apply()` CodeMirror entry follows the same coordinate rule.

## Adapt an existing planner

~~~ts
import { sourceCommand } from '@quirekit/msfe-core'

editor.chain().command(sourceCommand(state => ({
  changes: { from: state.selection.main.from, to: state.selection.main.to, insert: 'approved' },
}))).run()
~~~

`editor.run(planner, target?)` runs a pure planner directly and returns `MsfeResult`. Return `null` when inapplicable. DOM mutations are not a substitute for transactions.

## Handle failures

| Reason | Meaning | Host response |
| --- | --- | --- |
| `not-installed` | The runtime feature is absent | Check preset exclusions and installation |
| `not-applicable` | Syntax or selection does not admit the action | Disable the control or preserve content |
| `readonly` | Explicit readonly or locked mode | Disable writing |
| `stale-target` | Instance, document or revision changed | Reconfirm intent; never replay old offsets blindly |
| `invalid-change` | Invalid range or transaction | Check coordinates and field encoding |
| `composing` | Native composition owns input | Wait for composition; do not steal focus |
| `destroyed` / `not-mounted` | Invalid lifetime | Stop and release resources |

## Shortcuts, history and verification

Declare structural Enter behavior through `addBlockInput()`: `enter` maps to Enter, `lineBreak` to Shift-Enter, and `exit` to Mod-Enter. Keep ordinary business shortcuts in `addKeyboardShortcuts()`.

Handlers decide from current source, selection and block boundaries, without counting key presses. Return one `TransactionSpec` to handle the whole gesture, `'parent'` to delegate to an enclosing block, or `'native'` to resume the existing keymap chain. The latter two can also be constant rule values. Omitted intents delegate; unregistered syntax Block nodes prevent outer defaults from taking ownership. Explicitly delegate ordinary paragraph leaves so lists, quotes and custom containers can handle their contents.

The shared router checks mode, readonly, composition and accepted syntax before following each selection endpoint's ancestors. It invokes a handler once only when every endpoint resolves to the same function; otherwise the whole gesture falls through. `targets` contains endpoints and their owners, with two endpoints for each nonempty range. Plan edits from `state.selection`. Handlers must be pure: never dispatch, call `editor.commands` or mutate storage. The shared layer commits and undoes the edit atomically.

Each parser node has one declared owner. Override it with the existing extension's `.extend()` instead of installing competing owners. This minimal example starts a paragraph after a top-level level-two heading; install it with `StarterKit.configure({ heading: SectionHeading })`. MD Editor already provides complete defaults for paragraphs, headings, lists, quotes, containers, code and tables.

~~~ts
import { Heading } from '@quirekit/msfe-heading'

export const SectionHeading = Heading.extend({
  /** @returns A pure, source-owned heading boundary behavior. */
  addBlockInput() {
    return [{
      nodes: ['ATXHeading2'],
      enter: ({ state, targets }) => {
        const range = state.selection.main
        if (state.selection.ranges.length !== 1 || !range.empty
          || !targets[0]!.node.parent?.type.isTop
          || range.head !== targets[0]!.node.to) return 'native'
        return {
          changes: { from: range.head, insert: '\n\n' },
          selection: { anchor: range.head + 2 },
          scrollIntoView: true,
          userEvent: 'input',
        }
      },
      lineBreak: 'native',
    }]
  },
})
~~~

An extension may return `{ 'Mod-Shift-s': () => this.editor.commands.insertSignature('Ada') }` from `addKeyboardShortcuts()`. Returning `false` lets other bindings handle the key. Preserve usable browser and input-method behavior.

Submit one user action through one chain where practical. Native typing still follows editor history grouping. Verify standalone and chained calls, complete rollback on failure, side-effect-free `can()`, readonly rejection, exact spelling after undo and explicit multiple-selection behavior.

For asynchronous dialogs, read [Selection and captured intent](/en/guide/selection). Exact contracts are in [Command API](/en/api/commands).
