import type { AppState, HistorySnapshot } from "@editor/state"; import type { CommandContext } from "./command"; import { commandIds } from "./ids"; import type { CommandId, CommandPayloads } from "./payloads"; import type { CommandRegistry } from "./registry"; export type Dispatch = (commandId: TCommandId, payload: CommandPayloads[TCommandId]) => AppState; export type CommandDispatcher = { dispatch: Dispatch; }; export function createCommandDispatcher(options: { registry: CommandRegistry; getState: () => AppState; setState: (state: AppState) => void; }): CommandDispatcher { return { dispatch(commandId, payload) { const command = options.registry.get(commandId); if (!command) { throw new Error(`Unknown command: ${commandId}`); } const currentState = options.getState(); const context: CommandContext = { state: currentState }; const executedState = command.execute(context, payload); if (executedState === currentState) { return currentState; } const nextState = shouldRecordHistory(commandId, currentState, executedState) ? recordHistory(currentState, executedState) : executedState; options.setState(nextState); return nextState; }, }; } function shouldRecordHistory(commandId: CommandId, currentState: AppState, nextState: AppState) { if (commandId === commandIds.historyUndo || commandId === commandIds.historyRedo) return false; return currentState.document !== nextState.document; } function recordHistory(currentState: AppState, nextState: AppState): AppState { return { ...nextState, history: { past: [...currentState.history.past, snapshot(currentState)].slice(-100), future: [], }, }; } function snapshot(state: AppState): HistorySnapshot { return { document: state.document, editor: { ...state.editor, brushPreview: undefined, brushStrokePreview: undefined, }, }; }