fix(rc+guest): coordinate/keyboard/UI for remote control; guest media + panel fixes (batch71)

Remote control (connect.html viewer):
- Coordinate offset fixed: map clicks to the actual video CONTENT rect (object-fit
  letterbox-aware), not the element rect — cursor now lands where you click.
- Keyboard: capture at document level while a session is live (the <video> lost
  focus on bar clicks → keys did nothing). Skips the chat input.
- Control bar moved to bottom-right with tiny modern icons; video fills the viewport.
- Smoother cursor (mousemove ~60/s).

Guest meetings:
- Guest mic inaudible + guest invisible in the participant list + outsider screen
  share not showing (#1/#8/#11): root cause was the SFU media→tile map keyed on
  identity==uid, but guests had a random LiveKit identity and a null signaling uid.
  Guests now carry ONE stable id across signaling (meeting-join guestId) and the
  LiveKit token identity, so their media attaches and they appear to everyone.
- Guests can't add participants (#9) and don't see the transcript button (#12).
- Search box in the schedule participant list (#10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 14:28:37 +05:30
parent e562099344
commit bd488b3286
4 changed files with 60 additions and 26 deletions
+7 -1
View File
@@ -79,7 +79,13 @@ function handle(ws, m, req) {
let hostUserId = roomHost.get(room);
if (hostUserId === undefined) { try { const s = R.scheduledMeetings.byCode(room); if (s) { hostUserId = s.created_by; roomHost.set(room, hostUserId); } } catch (_) {} }
const ju = currentUser(req);
ws._meetingUserId = ju ? ju.id : null; // for per-user transcript ownership
// Identity used to map LiveKit media → this tile (peerIdForUid). Logged-in users use their user id
// (their LiveKit token identity is the same). GUESTS have no session, so they pass a stable client
// guest id here that ALSO becomes their LiveKit token identity — otherwise their media never maps
// to a tile and they're invisible/inaudible to others (and vice-versa for their screen share).
let mUid = ju ? ju.id : null;
if (!mUid && typeof m.guestId === 'string' && /^guest-[a-z0-9]+$/i.test(m.guestId)) mUid = m.guestId.slice(0, 64);
ws._meetingUserId = mUid; // for per-user transcript ownership + SFU media mapping
const avatar = (ju && ju.avatar_url) ? ju.avatar_url : null; // for participant-tile profile pics
const isHost = !!(ju && hostUserId && ju.id === hostUserId);
// Tell the newcomer who's already here (they initiate offers to existing peers)…