Undo and history groups
What one undo step holds, what undo and redo restore, and how to make several of your own commands one undo step.
Every edit the user makes is one undo step. Undo restores the slide and the selection from before the step, and redo restores the slide and the selection from after it. Run editor.exec({ type: 'undo' }) and editor.exec({ type: 'redo' }), or check first with editor.can(command).
What one undo step holds
| User action | Undo steps |
|---|---|
| Typing at a caret | One step for the whole burst. A caret move, a click, a selection change, any command, undo, and redo end the burst. |
| Backspace, Delete, Enter, or typing over a selected range | One step each |
| Moving, resizing, rotating, or cropping with the pointer | One step for the whole drag, from press to release |
| Dragging a table grid line | One step for the whole drag |
| Arrow keys that nudge the selected shapes | One step for consecutive nudges of the same selection, also with key repeat. Another selection or another command starts a new step. |
| Any other command | One step |
A command that has nothing to do adds no step. When the browser cancels a pointer drag (pointercancel), the drag ends where it is and keeps one step.
While the pointer moves, a drag that moves, resizes, or rotates shapes shows a preview and does not change the deck. The release writes the drag as one edit, so subscribe listeners and collaboration peers see one change. Press Escape before the release, or move the focus out of the window, to cancel the drag. The deck then does not change. An undo, a redo, or a change to a dragged shape or to its group also cancels the drag. The change can come from a command or from a collaborator. A change to another shape keeps the drag. Crop drags and table grid line drags still change the deck at each pointer move, and Escape does not cancel them. Each of these drags is still one undo step.
Group your own commands
Use a history group to make several commands one undo step. Open one with editor.beginHistoryGroup(), pass it to each command as { historyGroup }, and call end() when the gesture ends:
import type { Editor } from '@pptx-editor.dev/core';
function moveAndTurn(editor: Editor) {
const group = editor.beginHistoryGroup();
try {
const moved = editor.exec({ type: 'nudge', dx: 914400, dy: 0 }, { historyGroup: group });
if (!moved.ok) return moved;
return editor.exec({ type: 'rotateObjects', degrees: 90 }, { historyGroup: group });
} finally {
group.end();
}
}
// One `editor.exec({ type: 'undo' })` reverts the move and the turn.Call end() in a finally block, so a refused command or an exception does not leave the group open. end() keeps the edits already applied, and you can call it more than once.
These rules apply to a group:
- Undo, redo, and a command with another group or with no group split the step. A later command with the same open group starts a new step.
- A selection change, and a command that the editor refuses before it writes, do not split the step.
- Redo restores the selection after the last command of the group.
group.stateis'open'untilend().load()anddestroy()close every group.execandcanrefuse a closed group, a group from another editor, and any other value witherrors.unsupported. The document does not change.
Group a slider or a stepper
Toolbar value slots take a group as the last argument. A host slider sends each value while the pointer moves, and one undo step reverts the whole drag. In React:
import { useEditorValueCommand, useHistoryGroup } from '@pptx-editor.dev/react';
export function FontSizeSlider() {
const size = useEditorValueCommand('font.size');
const group = useHistoryGroup({ kind: 'range' });
return (
<input
ref={group.ref}
type="range"
aria-label="Font size"
min={8}
max={96}
value={size.value ?? 18}
disabled={!size.isEnabled}
onChange={(event) => {
size.execute(Number(event.currentTarget.value), group.options());
}}
/>
);
}font.size takes points. The hook groups pointer and keyboard gestures and releases its binding when the control unmounts.
Vue provides the same useHistoryGroup({ kind: 'range' }) pattern. Bind group.ref to the control and pass group.options() with each write.
For adapter examples, see React hooks and Vue composables.
Collaboration
With the collaboration module from @pptx-editor.dev/pro, undo reverts only your own steps, in the room. A group is one step in the room too, also when a collaborator edits the deck in the middle of your gesture. Without the module, a store transaction with the remote origin clears the local undo and redo history, because undo restores whole slides and would revert that edit.
Next steps
- See Tables and cells for the table commands, which follow the same undo rules.
- See Toolbar customization for the value slots and
useEditorValueCommand.