From 5b8cce81dd43b7f530ea316c7cf924ca3a160846 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Wed, 3 Jun 2026 14:42:59 -0400 Subject: [PATCH] =?UTF-8?q?zebra-report:=20deploy=20=E2=80=94=20leave=20+?= =?UTF-8?q?=20step-down=20chimes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zebra-report/zebra-spaces.html | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html index c33b982..62db2e4 100644 --- a/zebra-report/zebra-spaces.html +++ b/zebra-report/zebra-spaces.html @@ -2945,6 +2945,18 @@ function playToneRaise(){ /* single A5 bell-ish tone — light, attention-getting */ playToneBlip(880.0, 260, 'triangle'); } +function playToneLeave(){ + /* E5 → C5 descending major third — mirror of playToneJoin, conveys + * 'departed'. Fires on peer-left + peer-booted. */ + playToneBlip(659.25, 140, 'sine'); + setTimeout(() => playToneBlip(523.25, 200, 'sine'), 120); +} +function playToneStepDown(){ + /* single G4 low blip — softer than join/leave, conveys 'still here + * but stepped off the stage'. Fires on canSpeak -> listener role + * transitions for other peers. */ + playToneBlip(392.0, 220, 'sine'); +} /* ================================================================== * room state mirror (server is source of truth, we mirror locally for @@ -3164,6 +3176,9 @@ async function handleSignal(raw){ const left = members.get(m.uuid); const pub = pubHexFromMsg(m, 'pubkey') || (left && left.pubkey ? pubHexFromMsg({pubkey:left.pubkey}, 'pubkey') : ''); if (left || pub) logLine('', idTag(m.uuid, pub)+' left'); + /* doorman chime — someone actually left (post hiccup-grace). + * server never sends peer-left for self so no self-filter. */ + playToneLeave(); /* if they were sharing screen or camera, drop the tiles */ if (left && left.pubkey){ try { const h = hex(unb64(left.pubkey)); removeScreenTile(h); removeCameraTile(h); } catch(_){} @@ -3293,6 +3308,9 @@ async function handleSignal(raw){ removeCameraTile(subjPub); removeVideoTile('gameshare', subjPub); } catch(_){} + /* 'stepped off the stage' chime — they're still in the + * room but lost mic privileges. */ + playToneStepDown(); } } renderRoom(); @@ -3308,6 +3326,8 @@ async function handleSignal(raw){ || (mm && mm.pubkey ? pubHexFromMsg({pubkey:mm.pubkey},'pubkey') : ''); const byPub = pubHexFromMsg(m, 'by_pubkey'); logLine('', idTag(m.uuid, victPub)+' was removed by '+(by||byPub?idTag(m.by, byPub):'a mod')); + /* same departure chime as peer-left — they're gone either way. */ + if (m.uuid !== myUUID) playToneLeave(); if (m.uuid === myUUID){ /* server will close our socket; surface a clear notice and prevent * the WS reconnect loop from auto-rejoining into a boot loop. Tear @@ -4097,8 +4117,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');