This plan details how to implement the diff window functionality when clicking a peer's bubble in the collaborative IDE, following the "Google Docs" presence model.
We will update the payload schemas for join_room and room_joined so that initial file states are shared as users enter the room.
- Add
content: stringto thejoin_roomClientMessage. - Add
currentContent: stringandseq: numberto the peers array in theroom_joinedServerMessage. - Add
currentContent: stringandseq: numberto thepeer_joinedServerMessage.
The server will now maintain a lightweight shadow document of each user's file content to serve to newly joining peers.
- Change
roomsfromMap<string, Set<AuthenticatedSocket>>toMap<string, Map<AuthenticatedSocket, { content: string; seq: number }>>. - Update
joinRoomto accept and store the initialcontentand initializeseq. - Update
getRoomPeersto returncurrentContentandseqalongsideusernameandavatarUrl.
- Introduce a helper
applyPatches(text: string, patches: DiffPatch[]): stringto apply Monaco's differential patches. - In
onJoinRoom, pass the client's payloadcontenttojoinRoom. - In
onDiffUpdate, fetch the user's state, applymsg.patchesto their trackedcontent, update theirseq, and then broadcast to peers.
The frontend will maintain a reconstructed document per peer so that it is instantly available for diffing without network requests.
- Add a
peerDocuments: Map<string, PeerDoc>wherePeerDoc = { username, color, content, lastSeq }. - Write the
applyPatcheshelper to apply changes synchronously. - Update
setPeers,peerJoined,peerLeft, andpeerDiffactions to initialize and patchpeerDocumentscontents appropriately. - Ensure that if
peerLeftfires forselectedPeerUsername,selectedPeerUsernameis set tonullto automatically close the diff window.
We will introduce a new DiffWindow component and hook it into the main IDE layout.
- Update the
join_roompayload to pass the activefileContentfrom the editor.
- A new component that wraps
monaco.editor.createDiffEditor. - Uses
myContent(from local editor) andpeerContent(fromcollabStore'speerDocuments) to initialize the models. - Uses a debounced effect (e.g. 1.5s delay based on
seqstability) to update the modified model when the peer pushes new changes. - Contains an overlay UI or standard layout to provide a "Close Diff" button.
- If
selectedPeerUsernameis active, render<PeerDiffWindow>instead of<CollabEditor>. - Make sure to pass the local file content to
useRoomso it is included when sending thejoin_roomsignal.
- Ensure the
useEffectrecalculates and re-renders decorations when the file view is refreshed by guarding against stale paths. (As per standard file navigation behaviors).
- We'll implement closing the diff window on peer file change (handled inherently by clearing
selectedPeerUsernamewhen theirpeerLeftevent fires). Is there any specific UX you want when the diff window forcibly closes, or is just dropping them back into the normal editor sufficient?
- Open the same file in two separate browser sessions (User A and User B).
- User A types a few lines.
- User B clicks User A's bubble in the presence bar.
- User B should see a side-by-side diff: Left = User B's state, Right = User A's state.
- User A types more while User B holds the diff window open. User B's diff should update (debounced) without interrupting their UX.
- User A navigates to a different file in the tree. Result: User A's bubble vanishes for User B, and User B's diff window automatically closes, returning them to their normal local editor view.