We will implement the Memento design pattern to make the app capable of undoing commands.

Creating the pattern

Create the file webapp/memento.js with the following content:

import { TodoList } from "./classes.js"

export const TodoHistory = {
    history: [],
    push(state) { 
        if (state) {
            this.history.push(new Set([...state])) 
        }
    },
    pop() { 
        if (this.history.length>1) {
            this.history.pop();
            return this.history.pop();
        }
     }
}

TodoList.getInstance().addObserver(()=> {
    TodoHistory.push(TodoList.getInstance().items);
})

Adding a new command

Change the command.js file, first adding a new command

export const Commands = {
    //...
    UNDO: "undo",
}

And then adding the controller code in the switch:

// ...
case Commands.UNDO:
    const previousList = TodoHistory.pop();
    if (previousList) {
        todoList.replaceList(previousList);
    }
// ...    

Adding a new keyboard shortcut

if (event.ctrlKey && event.key === 'z') {
        event.preventDefault(); 
        const cmd = new Command(Commands.UNDO);
        CommandExecutor.execute(cmd);
    }