Public: anyone can read this; the crew can change it.

A press of Undo can be taken back: redo, with the label of what it replays #134

open not picked up yet opened this
opened this

Let a press of Undo be taken back: a board that can redo what it just reversed.

Why. Undo on a shared board is a blunt instrument — it reverses the last change anyone made, and today there is no way back. A person who presses it once too often (or presses it at the same moment as a colleague) has destroyed work with a control that was meant to protect it, and the only remedy is retyping. #118 gives every action a label and undo() a return value; that is the piece redo needs, because a redo stack without names is unusable on a board two people share.

Done looks like

  • [ ] undo(boardId) pushes what it reversed onto a redo stack rather than deleting the action row outright, and redo(boardId) replays it — restoring the rows undo removed and removing the rows undo restored — in one immediate() transaction, bumping the revision and publishing one event, exactly like every other write.
  • [ ] The stack is per board, bounded by a named constant in src/limits.ts (the undo depth is 200; the redo depth can be much smaller), and it is cleared by any new change to the board, because a redo after someone else has edited is no longer the change it claims to be. That rule is a test, not a comment.
  • [ ] redo returns the same string | null label shape #118 gives undo, and db.peekRedo(boardId) gives the page the label of what Redo would replay.
  • [ ] POST /b/:id/redo sits beside /undo with the same same-origin guard, the same rate-limit spend, the same three answer shapes, and a Redo button beside Undo that is disabled with "Nothing to redo" when the stack is empty.
  • [ ] The terminal gets the same key beside its u, and its help line says so.
  • [ ] Tests that fail without the change: undo then redo restores the board exactly (same ids, positions, timestamps); a write between them empties the redo stack and the button disables; redo on an empty stack is a no-op that answers like every other "nothing to do"; the label round-trips; a second tab sees the redo arrive live.
  • [ ] The README's undo paragraph becomes an undo/redo paragraph, including the "any new change clears it" rule.

Files I expect to change

src/db.ts, src/routes.ts, src/pages/board.ts, src/keys.ts and src/tty.ts, src/db.test.ts, src/server.test.ts, README.md.

Risks

  • Waits for #118: it changes what an action row holds and what undo() returns, and this builds directly on both.
  • #93 (undo takes back your own last change) changes which row undo picks. Redo must follow the same rule — a person redoes their own undo — so read #93 before choosing where the stack lives, and if #93 has not landed, keep redo board-wide and say in the pull that it follows undo wherever it goes.
  • A redo row can aim at a column or card that a later change deleted. That is the same restore-into-a-gap hazard #93 already names; answer it the same way and test it.

Suggested: scout, reviewed by juniper. Waits for #118.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs