class Stack {
#items = [];
push(item) { this.#items.push(item); }
pop() { return this.#items.pop(); }
peek() { return this.#items[this.#items.length - 1]; }
isEmpty() { return this.#items.length === 0; }
size() { return this.#items.length; }
clear() { this.#items = []; }
}
const undoStack = new Stack();
const redoStack = new Stack();
function performAction(newState) {
undoStack.push(currentState); // Save before changing
redoStack.clear(); // New action invalidates redo
currentState = newState;
render();
}
function undo() {
if (undoStack.isEmpty()) return;
redoStack.push(currentState);
currentState = undoStack.pop();
render();
}
function redo() {
if (redoStack.isEmpty()) return;
undoStack.push(currentState);
currentState = redoStack.pop();
render();
}
Ctrl+Z = undo, Ctrl+Y = redo. Remember event.preventDefault() to stop the browser's default undo behavior from Session 20.
Display: "5 undos available | 2 redos available"
Update the count after every action, undo, and redo. Grey out the buttons when their stack is empty.
Test sequence: apply 10 filters → undo 5 → redo 3 → new action → verify redo cleared.
| Step | Undo Stack | Redo Stack |
|---|---|---|
| Apply 10 filters | 10 items | 0 items |
| Undo 5 times | 5 items | 5 items |
| Redo 3 times | 8 items | 2 items |
| New action | 9 items | 0 items (cleared!) |
Memory management: max 50 undos. When the undo stack exceeds 50, remove the oldest item from the bottom. This prevents PixelCraft from consuming gigabytes of RAM.
git switch -c feature/PIXELCRAFT-022-undo-redo
git add src/scripts/
git commit -m "Implement undo/redo with Stack data structure (PIXELCRAFT-022)"
git push origin feature/PIXELCRAFT-022-undo-redo
# PR → Review → Merge → Close ticket ✅
Stacks appear everywhere in computing.
The undo pattern you just built is used by every editor, every IDE, every spreadsheet. You implemented the Command Pattern — one of the most famous in software engineering.