# Commands and shared history

Grafloria puts user gestures and user-directed programmatic edits on the same command history, then lets each framework binding return the changed models to your application state.

```mermaid
flowchart LR
  G["Gesture"] --> C["Command"]
  P["Programmatic user edit"] --> C
  C --> M["CommandManager"]
  M --> D["DiagramModel"]
  D --> B["Framework binding"]
  B --> S["Application state"]
```

## Gestures are already commands

When you mount a canvas, dragging, connecting, deleting, pasting, and grouping use the engine's command path. A drag is committed as one history step, so keyboard undo returns the node to the position where that gesture began rather than undoing individual pointer updates. Redo reapplies the gesture.

The binding owns the canvas, but the history belongs to its engine. In React and Vue, reach it through [`DiagramInstance`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-renderer-instance-diagraminstance#diagraminstance) and `getEngine()`. Angular's `DiagramCanvasComponent` also exposes mirrored `undo()` and `redo()` methods. `undo()` is not a renderer-instance method; see [How Grafloria works](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/how-grafloria-works) for the layer boundary.

## Put your edit on the same stack

Mount the framework component, capture its instance, and send an edit made on the user's behalf through the engine's command manager. The following React example renders two nodes, adds a third node through [`AddNodeCommand`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-commands-classes-a-r#addnodecommand), and updates the visible node count when the binding receives the model change.

```tsx
import { useRef, useState, type ReactElement } from 'react';
import { GrafloriaFlow } from '@grafloria/react';
import { AddNodeCommand, NodeModel } from '@grafloria/engine';
import type { DiagramInstance, NodeSpec } from '@grafloria/renderer';

const nodes: NodeSpec[] = [
  { id: 'start', position: { x: 80, y: 100 }, size: { width: 140, height: 60 }, data: { label: 'Start' } },
  { id: 'finish', position: { x: 360, y: 100 }, size: { width: 140, height: 60 }, data: { label: 'Finish' } },
];

export function CommandHistoryExample(): ReactElement {
  const instanceRef = useRef<DiagramInstance | null>(null);
  const [nodeCount, setNodeCount] = useState(nodes.length);

  const addNode = async (): Promise<void> => {
    const instance = instanceRef.current;
    if (!instance) return;

    const node = new NodeModel({
      id: 'review',
      type: 'default',
      position: { x: 220, y: 240 },
      size: { width: 140, height: 60 },
    });
    await instance.getEngine().commandManager.execute(new AddNodeCommand(node));
  };

  const undo = async (): Promise<void> => {
    const instance = instanceRef.current;
    if (instance) await instance.getEngine().commandManager.undo();
  };

  return (
    <div>
      <div style={{ display: 'flex', gap: '8px' }}>
        <button type="button" onClick={addNode}>Add review node</button>
        <button type="button" onClick={undo}>Undo</button>
        <span>Nodes: {nodeCount}</span>
      </div>
      <div style={{ height: '320px', width: '640px' }}>
        <GrafloriaFlow
          defaultNodes={nodes}
          onInit={(instance): void => { instanceRef.current = instance; }}
          onNodesChange={(changed): void => { setNodeCount(changed.length); }}
        />
      </div>
    </div>
  );
}
```

Click **Add review node** to see a third node and a count of three. Click **Undo** to remove that node and receive a count of two. The call to `commandManager.execute()` records the edit; the binding's `onNodesChange` callback receives the resulting `NodeModel[]` after both execution and undo.

## Keep setup separate from user edits

There are two intents:

- Build, load, import, or synchronize a document with direct model mutations. Those mutations establish the starting document and do not become undo steps.
- Change the document on the user's behalf with [`CommandManager`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-commands-classes-a-r#commandmanager). `execute()` runs the command asynchronously and records an undoable command when it succeeds.

This distinction prevents loading a saved file from filling the user's undo history. It also makes a toolbar action or automated suggestion behave like a gesture: the user can undo it with the same keyboard shortcut and the same history controls.

## Group edits into one step

Use [`BatchCommand`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-commands-classes-a-r#batchcommand) when one user action performs several changes. Construct it with a name and the commands it contains, then execute it through the same command manager. One undo calls the batch's undo operation and reverses the whole group, rather than exposing each child as a separate step.

Commands can also refuse execution through `canExecute(context)`. A refused command does not enter history, so there is no misleading later undo for an action that never happened. A command that does execute supplies `undo(context)` and can supply `redo(context)`; the default redo re-executes the command.

## History returns through bindings

Undo changes the live models in the engine. The bindings then report that changed model state through their normal data surfaces: React calls `onNodesChange` with the reverted `NodeModel[]`; Angular writes the result through `[(nodes)]`; Vue updates `v-model:nodes`. Keep one application state path connected to those binding updates instead of maintaining a second undo stack.

The engine keeps the history on [`DiagramEngine`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-engine#diagramengine), whose `commandManager` also exposes `canUndo()`, `canRedo()`, `getHistory()`, and history-size controls. Use those methods to drive disabled states and history UI; use the mounted instance or framework component to render the result.

## Where this fits

Use direct model operations for initial data and synchronization. Use commands for actions the user needs to reverse. Start at the framework binding, use [`DiagramInstance`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-renderer-instance-diagraminstance#diagraminstance) for the mounted diagram, and reach the engine only for behavior such as history. Continue with [the model and documents](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/model-and-documents), [the instance and lifecycle](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/instance-and-lifecycle), or [collaboration](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-collab).
