undo(steps = 1) {
        if (this.isExecuting || this.undoStack.length === 0) return;
        this.isExecuting = true;

        try {
            const undoneCommands = [];
            for (let i = 0; i < steps && this.undoStack.length > 0; i++) {
                const command = this.undoStack.pop();
                command.undo();
                undoneCommands.unshift(command);
            }
            this.redoStack.push(...undoneCommands);
            this.notifyListeners();
        } catch (error) {
            console.error("Error undoing command:", error);
            this.recoverFromSnapshot();
        } finally {
            this.isExecuting = false;
        }
    }
Undoes a specified number of commands from the undo stack.

@param {number} [steps=1] - The number of commands to undo. Defaults to 1 if not specified.
@throws {Error} If an error occurs during the undo operation.

@description
This method performs the following steps:
1. Checks if undo operation is possible (not currently executing and undo stack is not empty).
2. Undoes the specified number of commands or as many as possible if fewer are available.
3. Moves undone commands to the redo stack.
4. Notifies listeners of the state change.
5. If an error occurs, attempts to recover from the last valid snapshot.

@example
// Undo the last command
undoRedoManager.undo();

// Undo the last 3 commands
undoRedoManager.undo(3);