Vision
Multiple users editing the same work concurrently on different devices should feel as fluid as Google Docs or Figma — real-time cursor positions, instant text sync, and clear visual feedback of who changed what.
Current State (Phase 1 COMPLETE)
- Text sync: WORKING — edits propagate between sessions
- Awareness/presence: WORKING — bar shows who is viewing the same work with pulsing indicators
- Cursors: WORKING — colored carets with name labels, different colors per user
- Selection: PARTIAL — cursor stays at start of selection
- Conflict resolution: WORKING — CRDT handles merges automatically
Known Issues (Phase 1 refinements)
1. Cursor position at line boundaries
Problem: When cursor is at the start of a line (after a newline), the remote marker may appear at the end of the previous line. This is a contentEditable limitation — <br> and <div> elements don't map cleanly to \n characters in the character index.
Fix needed: Custom character-index-to-DOM-position mapping that accounts for block-level elements. ProseMirror/TipTap use dedicated position mapping layers for this. Options:
- Walk all DOM nodes (text + br + div + p) counting newlines
- Use
innerText offset mapping
- Switch to a position-mapping library (e.g.,
prosemirror-state's TextSelection)
Workaround: Cursor works correctly for mid-line positions. Only the first character of each line may be off by one.
2. Awareness polling (2s latency)
Problem: Cursor positions update every 2 seconds via polling instead of real-time event push.
Fix needed: Handle CrdtAwarenessUpdate events in the client's onMessage handler (#52 optimization #2).
Plan
Phase 1: Real-time cursors and selections (DONE)
Phase 2: Change highlights and attribution
Phase 3: Performance and latency
Phase 4: Social presence
Phase 5: Polish
Vision
Multiple users editing the same work concurrently on different devices should feel as fluid as Google Docs or Figma — real-time cursor positions, instant text sync, and clear visual feedback of who changed what.
Current State (Phase 1 COMPLETE)
Known Issues (Phase 1 refinements)
1. Cursor position at line boundaries
Problem: When cursor is at the start of a line (after a newline), the remote marker may appear at the end of the previous line. This is a contentEditable limitation —
<br>and<div>elements don't map cleanly to\ncharacters in the character index.Fix needed: Custom character-index-to-DOM-position mapping that accounts for block-level elements. ProseMirror/TipTap use dedicated position mapping layers for this. Options:
innerTextoffset mappingprosemirror-state'sTextSelection)Workaround: Cursor works correctly for mid-line positions. Only the first character of each line may be off by one.
2. Awareness polling (2s latency)
Problem: Cursor positions update every 2 seconds via polling instead of real-time event push.
Fix needed: Handle
CrdtAwarenessUpdateevents in the client'sonMessagehandler (#52 optimization #2).Plan
Phase 1: Real-time cursors and selections (DONE)
Phase 2: Change highlights and attribution
Phase 3: Performance and latency
Phase 4: Social presence
Phase 5: Polish