Why commands?
The command pattern provides:Undo/redo support
Every operation can be reversed and re-applied.
State encapsulation
Commands save necessary state for reversal.
Batch operations
Multiple commands can be executed as a single undoable unit.
History tracking
Complete history of all operations for debugging and analysis.
Command interface
All commands extend the baseCommand class:
apps/web/src/lib/commands/base-command.ts
CommandManager API
The CommandManager handles command execution and history:apps/web/src/core/managers/commands.ts
Using commands
Basic usage
Through manager methods
Most operations are wrapped by manager methods:timeline.splitElements() does:
apps/web/src/core/managers/timeline-manager.ts
Creating a command
Here’s a complete example of implementing a command:Batch commands
Combine multiple commands into a single undoable operation:apps/web/src/lib/commands/batch-command.ts
Using batch commands
Command organization
Commands are organized by domain in the codebase:Execute vs push
The CommandManager has two methods for adding commands to history:- execute()
- push()
Use when the command hasn’t been executed yet:This is the most common case.
Preview operations
Some operations support a preview mode before committing:Command best practices
Save minimal state
Only save the state necessary to undo. Don’t save the entire editor state.
Immutable operations
Use immutable updates. Don’t mutate saved state directly.
Use batch for groups
Combine related operations into a BatchCommand for single undo.
Test undo/redo
Always test that undo properly reverses the operation.
Relationship with actions
Actions and commands work together:Actions are “what triggered this” (user intent), while commands are “how to do it and undo it” (implementation).
Related concepts
- EditorCore - The CommandManager is accessed via EditorCore
- Actions - Actions trigger commands for user operations
- Timeline - Timeline operations are implemented as commands
- Scenes - Scene operations are implemented as commands