Custom UI

Handle unavailable commands

Turn command state, stable reasons, and mutation receipts into clear custom-UI behavior.

Custom controls must distinguish a temporarily disabled action from a capability the current host does not support. Read command state before execution, then inspect the execution result. Neither check replaces the other.

Explain the current state

Map the stable reason value to product language. Keep a fallback so newer reasons remain understandable before your application adds tailored copy:

import type { CommandExecutionResult, CommandState, SuperDocUIReason } from 'superdoc/ui';

export const explainUnavailableCommand = (state: CommandState): string | null => {
  if (state.enabled) return null;

  const messages: Partial<Record<SuperDocUIReason, string>> = {
    'not-ready': 'The document is still loading.',
    'document-readonly': 'Switch from viewing mode before editing.',
    'selection-required': 'Place the cursor in the document first.',
    'range-selection-required': 'Select some text first.',
    'table-context-unavailable': 'Place the cursor inside a table first.',
    'content-control-locked': 'This field does not allow formatting changes.',
    'command-unsupported': 'This action is not available in this SuperDoc build.',
  };

  return state.reason ? messages[state.reason] ?? 'This action is currently unavailable.' : 'This action is unavailable.';
};

export const explainCommandResult = (result: CommandExecutionResult): string => {
  if (result === false) return 'The command could not be routed.';
  if (result === true) return 'The command completed.';
  if (!result.success) return `${result.failure.code}: ${result.failure.message}`;
  return 'The command completed.';
};

Use the state fields together:

  • enabled: false means the action cannot run now.
  • supported: false means the controller cannot route the command on this host.
  • reason explains the current cause without requiring string parsing from an error message.
  • active and value describe current selection or control state. They do not imply that the next execution will succeed.

Show unavailable actions only when their presence helps the reader understand what can change. Disable a familiar formatting action with a short reason. Hide an integration that the current product never provides. Do not render every ID from the command catalog as a toolbar.

Inspect the final result

State is a snapshot, not a guarantee. The document can change mode, selection, revision, or capability between the check and execution. Call executeAsync() and inspect its result:

  • false means the controller could not route the action.
  • true means a legacy or host action completed without a structured receipt.
  • A receipt with success: false contains a machine-readable failure code and message.
  • A receipt with success: true confirms the Document API mutation.

Branch on stable reason strings and failure codes. Show the supplied message to developers or adapt it for users, but do not infer success from the absence of an exception.

For document-operation capability checks and receipt failure codes, read Receipts and errors. For the complete reactive state contract, read Commands and state.

On this page