- Google Docs undo/redotest results: https://gist.github.com/JavaGT/e80e4c344847f910e7b5a24188126a9a
- Scope 2 current test results: https://gist.github.com/JavaGT/b6f92ed211a2893157ff813bbd549aab
Testing Scope 2's Studio (transcript editing) against Google Docs' behavior identified 4 discrepancies. Three are architectural (Phase 2/3), one is a focused bug fix (Phase 1).
- User edits a segment → "ALPHA"
- User presses ⌘Z (undo) → segment reverts
- User edits a different segment → "BETA"
- User presses ⌘⇧Z (redo) → "ALPHA" is restored on the original segment
This is wrong. The redo stack should have been cleared by the new edit in step 3.
- User types "Hello" → presses ⌘Z (undo) → text removed
- User types "World" (new edit after undo)
- User presses ⌘Y (redo) → nothing happens (redo was a no-op)
Rule: Any new local edit (text change, speaker assignment, code application, deletion) must clear the entire redo stack. Only undo is allowed before making a new edit if you want to preserve the redo path.
Find the undo/redo manager (likely in the Svelte store or a composable). Currently it likely uses a simple stack pointer:
undoStack = [A, B, C] // C = most recent
redoStack = [D] // D = undone action
undoIndex = 2 // points to C
When a new edit E is committed:
- If
undoIndex < undoStack.length - 1(meaning some actions have been undone):- Clear the redo stack entirely (
redoStack = [])
- Clear the redo stack entirely (
- Then push E onto the undo stack
In Google Docs (and standard undo systems), the redo stack is a branch that is discarded when you make a new edit. The sequence is:
- Edit A → undoStack=[A], redoStack=[]
- Undo → undoStack=[A], redoStack=[A] (A moved to redo)
- Edit B → undoStack=[A, B], redoStack=[] (redoStack cleared)
- Redo → nothing to redo
The bug is likely that the current code does not clear redoStack when undoIndex is not at the end (i.e., when undos have been performed before the new edit).
Look for code resembling:
function commitAction(action) {
// BUG: missing this check:
// if (undoIndex < undoStack.length) { redoStack = []; }
undoStack.push(action);
undoIndex = undoStack.length;
}Fix: Before pushing a new action, if the user has undone any actions (undoIndex < undoStack.length), truncate the undoStack AND clear the redoStack.
- Tab A edits a segment → change persists to server
- Tab B (same user, same project) does NOT see the change until manual page refresh
- No WebSocket, no OT, no CRDT
- Tab A types → Tab B sees characters appear in real-time (<100ms latency)
- Changes pushed via WebSocket/WebRTC
- Operational Transformation (OT) resolves concurrent edits
- Add WebSocket server (e.g., via Phoenix Channels, Socket.IO, or built-in SvelteKit WebSocket support)
- Implement OT or CRDT library (e.g., sharedb, yjs, automerge)
- Replace simple REST save-on-edit with real-time sync pipeline
- Convert per-segment save model to operation-based model
This is the largest change and likely deserves a separate design doc.
- User edits, undoes, redoes → all in-memory
- Page refresh → undo stack is empty, only latest committed state loads
- Undo/redo history survives page navigation within the same session
- Google Docs maintains two undo stacks per session:
- Client-side: immediate undo/redo for latency
- Server-side (OT): undo history transformed against concurrent ops
- Persist undo/redo operations to server as an ordered log
- On page load, replay undo/redo log to reconstruct the stack
- Each tab needs its own undo stack (per-session, not per-document)
| Behavior | Status |
|---|---|
| Basic undo (⌘Z) reverts last action | ✅ Correct |
| Redo (⌘⇧Z) restores undone action | ✅ Correct |
| LIFO stack order (last in, first undone) | ✅ Correct |
| Per-tab/per-session undo stacks | ✅ Correct |
| Undo commits to server | ✅ Correct |
| Speaker assignment is separate undo entry | ✅ Correct |
| Text edit is separate undo entry from speaker | ✅ Correct |
| Undo requires focus on editor area | ✅ Correct (same as Google Docs' iframe issue) |
| Shortcut | Scope 2 Function | Google Docs Equivalent |
|---|---|---|
| ⌘Z | Undo | ⌘Z Undo |
| ⌘⇧Z | Redo | ⌘⇧Z or ⌘Y Redo |
| E | Edit segment text | N/A (free-form typing) |
| ⌘⏎ | Save edit | N/A (auto-saves) |
| Escape | Cancel edit | Escape |
| D/N/B/M/O/P | Speaker hotkeys | N/A |
| Space | Play/pause | N/A |
| A | Open codes menu | N/A |
| C | Add comment | ⌘⌥M Comment |