feat(M4): realtime multiplayer sync + presence over Supabase Realtime#8
Merged
Conversation
Adds a custom Yjs network provider that transports document updates and awareness over a Supabase Realtime broadcast channel scoped per board, plus live cursors and identity for collaborators. - packages/sync: SupabaseProvider (Yjs sync protocol state-vector handshake + awareness), per-board provider/awareness singletons, deterministic presence colors. Remote updates are applied with the provider as transaction origin so they neither echo back nor enter the UndoManager. - packages/canvas: shapeStore.configureRealtime wires the provider in after IndexedDB sync; CanvasStage publishes the local cursor/selection to awareness and renders remote cursors via a new PresenceLayer. - apps/web: useIdentity derives a name (signed-in email local-part, else Guest) and stable color; Board enables realtime when Supabase is configured. Degrades to local-only mode unchanged when Supabase env vars are absent.
| this.boardId = opts.boardId; | ||
| this.doc = opts.doc; | ||
| this.awareness = opts.awareness; | ||
| this.sessionId = Math.random().toString(36).slice(2); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Milestone 4 — Realtime collaboration
Networks the existing Yjs document so edits and presence sync live between everyone on a board. The document was already a CRDT persisted only to IndexedDB; this adds a network transport and awareness on top, with no change to the local-first behavior.
What's included
PresenceLayer.How it fits the existing code
UndoManager, which only tracks local null-origin transactions. No change touseUndoManagerwas needed.IndexeddbPersistence.whenSynced, so the handshake advertises a complete local state vector and exchanges only a minimal diff.getIndexedDbProvider), so React StrictMode's double-invoke never opens two channels or duplicatesdoc.on("update")handlers.Local-only mode preserved
When
VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEYare unset,BoardpassesnulltoconfigureRealtime, the provider is skipped,_awarenessstays null, and the board works exactly as before against IndexedDB.Key files
packages/sync/src/supabaseProvider.ts(new) — provider, sync + awareness, base64 encoding, origin handlingpackages/sync/src/identity.ts(new) — deterministic presence colorspackages/canvas/src/store/shapeStore.ts—configureRealtime,_awareness, wires provider after IndexedDB syncpackages/canvas/src/layers/PresenceLayer.tsx+hooks/useRemoteCursors.ts+hooks/useAwareness.ts(new)packages/canvas/src/CanvasStage.tsx— publishes local cursor/selection, mountsPresenceLayerapps/web/src/features/canvas/useIdentity.ts(new) +apps/web/src/routes/Board.tsxOut of scope (noted for follow-up)
snapshotstable). Late-joiners rely on connected peers; a joiner with empty local data and no peer online sees an empty board.console.warns past ~200 KB. Chunking the initial sync is a future improvement.Verification
pnpm typecheck— passes across all workspace projectspnpm build— passesConfig note
Realtime must be reachable by the
anonrole fornotux-board-*broadcast topics. The default (no-authorization) Realtime mode works out of the box; if Realtime Authorization is later enabled, add a policy allowinganonbroadcast on those topics.https://claude.ai/code/session_01U8yLDREdFzmyTk3SxPoTBj
Generated by Claude Code