- room:leave event: removes the player from the room (not just disconnected),
reassigns host if the host leaves, deletes the room when empty
- store leaveRoom() emits it and clears the session (reset)
- room header gets a "Quitter" button (always available); end-screen "Quitter"
now also leaves cleanly
Verified e2e: leaver removed from snapshot, host reassigned to the next player.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- each player gets a reconnectToken; room:create/join return it
- new room:rejoin event: re-attaches the existing player (by id+token) to the
new socket, broadcasts player:rejoined + room:state, and resyncs the current
round to that socket (engine.resync re-emits round:start with their view)
- client persists the session (roomCode/playerId/token/name) in localStorage;
bootstraps the store from it and auto-rejoins on every socket connect;
clears it on reset/quit or failed rejoin
Verified e2e: wrong token rejected, correct token re-attaches (player present
+ connected).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restructure onboarding (option B):
- home is just Create / Join (code) — no name field
- name is chosen in the room: room:create/join no longer require a name;
new player:setName event. Nameless players are hidden from the snapshot
(players/scores/submissions) and excluded from eligible voters and the
3-player blindtest gate until they pick a pseudo.
- PseudoScreen with live DiceBear avatar preview gates RoomPage until set
- /join/:code auto-joins then redirects to the room (seamless invite links)
- RoomCode gains a "copier le lien" button (origin/join/CODE)
Verified e2e: nameless players excluded from the room until setName.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>