diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index 8dd8665..813657e 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -3481,6 +3481,20 @@ async function handleSignal(raw){ * present-member-offers: existing speaker offers when a new speaker * arrives. deterministic by uuid string compare. */ if (canSpeak(myRole) && canSpeak(m.role)) connectToPeer(m.uuid, /*weOffer*/ myUUID < m.uuid); + /* if we're in self-listener mode and a new speaker just arrived, + * pull them onto the buffered HTTP stream too so we hear them on + * the same path as everyone else */ + if (selfListenerMode && canSpeak(m.role) && m.uuid !== myUUID && m.pubkey){ + try { + const ph = hex(unb64(m.pubkey)); + if (ph && !streamMode.has(ph)){ + streamMode.add(ph); + startStream(m.uuid, ph); + const w = remoteAudio.get(m.uuid); + if (w) try { w.muted = true; } catch(_){} + } + } catch(_){} + } renderRoom(); break; case 'peer-left': @@ -3819,6 +3833,10 @@ async function onRoleChanged(prev, next){ * people out of the mesh automatically'. The mesh connections * stay up as a bonus low-latency audio path; they get GC'd * naturally when the other end leaves or also demotes. */ + /* self-listener flag is meaningless once we're a listener (the + * row's stream toggle disappears); flip it OFF so its streamMode + * entries get torn down cleanly with the rest of our state. */ + if (selfListenerMode) disableSelfListenerMode(); dropMic(); muted = false; await sfuUnpublish(); await sfuUnpublishScreen(); @@ -4076,6 +4094,12 @@ const streamAudio = new Map(); // uuid ->