/** * Reversible operations and their undo/redo history — engine-neutral. * * The app a command acts on is a **type parameter**, not a fixed type. That is * the whole point: `Command` used to name `MolvisApp` directly, so anything * wanting a command had to depend on the 3D engine. `sketch` could not, and * re-implemented the same semantics locally (`SketchCommand` / * `SketchHistory`, whose own comment read "mirrors core CommandManager … * without importing core"). * * Commands keep their app handle and their bodies; only the handle's type is * now supplied by whoever binds it. */ /** Emitted whenever the undo/redo stacks change. */ export interface HistoryChange { canUndo: boolean; canRedo: boolean; } /** * What {@link CommandManager} needs of its host — nothing more. * * Structural on purpose: it is satisfied by any app with an event emitter, * without core knowing what an app is. */ export interface CommandHost { readonly events: { emit(name: "history-change", payload: HistoryChange): void; }; } /** * Anything the history can drive. * * Declared separately from {@link Command} because an app handle is a * convenience for subclasses, not something the history needs — `sketch`'s * commands capture what they edit and take no app at all. */ export interface Reversible { do(): TResult | Promise; undo(): unknown; } /** * A reversible operation bound to an app. * * `do()` performs it, `undo()` reverses it. Both may be async; the manager * awaits them. */ export declare abstract class Command implements Reversible { protected app: TApp; constructor(app: TApp); abstract do(): TResult | Promise; abstract undo(): Command | Promise> | void | Promise; } /** Optional debug sink; hosts pass their own logger, core stays dependency-free. */ export type CommandLog = (message: string) => void; export declare class CommandManager { private readonly app; private readonly log; private undoStack; private redoStack; constructor(app: TApp, log?: CommandLog); /** * Run a command, push it on the undo stack, and drop any redo history. * * **Stays synchronous for a synchronous command.** A 2D gesture handler * applies an edit and reads `canUndo()` in the same tick; forcing every * command through a microtask left the stacks briefly disagreeing with the * document. Callers with async commands simply `await` as before. */ execute(command: Reversible): T | Promise; undo(): boolean | Promise; redo(): boolean | Promise; private commit; private settle; clearHistory(): void; canUndo(): boolean; canRedo(): boolean; private emitHistoryChange; }