Skip to content

Instantly share code, notes, and snippets.

@JavaGT
Created June 11, 2026 10:30
Show Gist options
  • Select an option

  • Save JavaGT/cbc15e38b91aa1604339e588cc1d7b37 to your computer and use it in GitHub Desktop.

Select an option

Save JavaGT/cbc15e38b91aa1604339e588cc1d7b37 to your computer and use it in GitHub Desktop.
Scope 2 — Align Undo/Redo with Google Docs Behavior

Scope 2 — Fix Report: Aligning Undo/Redo with Google Docs Behavior

Reference Material

Summary of Discrepancies Found

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).


Phase 1 — Must Fix: Redo Stack NOT Cleared by New Local Edit

Current Scope 2 Behavior (Bug)

  1. User edits a segment → "ALPHA"
  2. User presses ⌘Z (undo) → segment reverts
  3. User edits a different segment → "BETA"
  4. 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.

Desired Google Docs Behavior (Test 12)

  1. User types "Hello" → presses ⌘Z (undo) → text removed
  2. User types "World" (new edit after undo)
  3. 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.

Implementation Guidance

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 = [])
  • 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:

  1. Edit A → undoStack=[A], redoStack=[]
  2. Undo → undoStack=[A], redoStack=[A] (A moved to redo)
  3. Edit B → undoStack=[A, B], redoStack=[] (redoStack cleared)
  4. 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.


Phase 2 — Future: Real-Time Sync (Architectural)

Current Scope 2 Behavior

  • 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

Desired Google Docs Behavior

  • Tab A types → Tab B sees characters appear in real-time (<100ms latency)
  • Changes pushed via WebSocket/WebRTC
  • Operational Transformation (OT) resolves concurrent edits

Required Changes

  • 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.


Phase 3 — Future: Undo Stack Persistence Across Refresh

Current Scope 2 Behavior

  • User edits, undoes, redoes → all in-memory
  • Page refresh → undo stack is empty, only latest committed state loads

Desired Google Docs Behavior

  • Undo/redo history survives page navigation within the same session
  • Google Docs maintains two undo stacks per session:
    1. Client-side: immediate undo/redo for latency
    2. Server-side (OT): undo history transformed against concurrent ops

Required Changes

  • 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)

Appendix: All Behaviors That Already Match Google Docs (No Changes Needed)

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)

Appendix: Keyboard Shortcut Reference for Testing

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment