Yjs UndoManager tracks waypoints, no-go areas, and notes. Only local mutations (origin "local") are undoable — remote changes from other users are not. - Keyboard shortcuts: Ctrl/Cmd+Z (undo), Ctrl/Cmd+Shift+Z/Y (redo) - Shortcuts suppressed in text inputs (browser-native undo there) - Undo/redo buttons in header with disabled state - stopCapturing on drag to isolate drag as one undo step - All mutation sites wrapped with "local" origin - 5 unit tests for UndoManager behavior - Archived undo-redo change Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.1 KiB
3.1 KiB
1. Core: UndoManager Setup
- 1.1 Create
apps/planner/app/lib/use-undo.tshook that takes aY.UndoManagerand exposescanUndo,canRedo,undo(),redo()via UndoManager event listeners (stack-item-added,stack-item-popped,stack-item-updated) - 1.2 Create the
Y.UndoManagerinuse-yjs.ts, tracking[waypoints, noGoAreas, notes]withcaptureTimeout: 500andtrackedOrigins: new Set(["local"]). Expose it on theYjsStateinterface. Destroy it in the cleanup function. - 1.3 Add
"local"origin to all mutation sites:PlannerMap.tsx: wrapaddWaypoint,insertWaypointAtSegment,moveWaypoint,deleteWaypointindoc.transact(() => { ... }, "local")WaypointSidebar.tsx: wrapdeleteWaypointandmoveWaypointindoc.transact(() => { ... }, "local")NoGoAreaLayer.tsx: add"local"origin to thepm:createand contextmenu delete transactionsNotesPanel.tsx: add"local"origin to the existingdoc.transact()calluse-yjs.ts: use"init"(not"local") as origin for initial waypoint seeding so it is not undoable
2. Keyboard Shortcuts
- 2.1 Create a
useUndoShortcutshook (inuse-undo.tsor separate file) that registers a globalkeydownlistener for Ctrl+Z / Cmd+Z (undo) and Ctrl+Shift+Z / Cmd+Shift+Z / Ctrl+Y (redo). CallsundoManager.undo()/undoManager.redo()and callse.preventDefault(). - 2.2 Suppress the shortcut when the active element is
<input>,<textarea>, or[contenteditable]so browser-native undo works in text fields (especially the notes textarea).
3. UI: Topbar Buttons
- 3.1 Add undo and redo icon buttons to the
SessionView.tsxheader, in the left group near the participant list. Use inline SVG arrows. Wire toundo()/redo()from theuseUndohook. - 3.2 Disable buttons when
canUndo/canRedois false (gray out, setdisabledattribute). Addtitletooltips showing the keyboard shortcut (platform-aware: Cmd on macOS, Ctrl elsewhere).
4. Drag Operation Grouping
- 4.1 In
PlannerMap.tsx, callundoManager.stopCapturing()before the drag transaction to ensure the drag is isolated as its own undo step (separated from any preceding action within the capture timeout window). AccessundoManagerfrom theyjsprop.
5. i18n
- 5.1 Add translation keys for undo/redo button tooltips (
undo.tooltip,redo.tooltip) in both English and German translation files for the planner namespace.
6. Testing
- 6.1 Unit tests for
use-undo.ts: verifycanUndo/canRedoreactivity, verifyundo()andredo()call through to UndoManager, verify state updates on stack events. Use a realY.Doc+Y.UndoManager(not mocks). - 6.2 Unit test for transaction origins: create a Y.Doc with UndoManager, perform mutations with
"local"origin, verify they appear on the undo stack. Perform mutations with no origin or"init"origin, verify they do not appear on the undo stack. - 6.3 E2E test: add two waypoints, press Ctrl+Z, verify last waypoint is removed. Press Ctrl+Shift+Z, verify it reappears. Test undo/redo buttons in the topbar (click, verify disabled state).