zebra-report: zebra-spaces grant-mic shortcut + sfu publish diagnostic

This commit is contained in:
russell@unturf.com 2026-05-31 13:25:52 -04:00
parent ce42275c07
commit 1119d4a8ed
No known key found for this signature in database

View file

@ -449,7 +449,12 @@ let sfuPubPC = null, sfuPubPeerID = null;
let sfuSubPC = null, sfuSubPeerID = null, sfuSubEvents = null;
async function sfuPublish(){
if (sfuPubPC || !micStream || !myKeys || !roomID) return;
if (sfuPubPC){ logLine('','sfu publish: already publishing'); return; }
if (!micStream || !myKeys || !roomID){
logLine('err','sfu publish skipped: mic='+(!!micStream)+' keys='+(!!myKeys)+' room='+(!!roomID));
return;
}
logLine('', 'sfu publish: starting (base='+SFU_BASE+')');
const pc = new RTCPeerConnection(rtcConfig);
for (const tr of micStream.getTracks()){ tagTrack(tr); pc.addTrack(tr, micStream); }
setSenderBitrate(pc.getSenders().find(s=>s.track && s.track.kind==='audio'));
@ -1011,6 +1016,12 @@ async function modInvite(uuid){
const sig = await signBytes(sigAction(roomID, roomEpoch, 'mic-invite', uuid));
send({ type:'mic-invite', to: uuid, epoch: roomEpoch, sig });
}
/* grant-mic: hand-raised listener doesn't need to accept — server promotes
* them directly to speaker. Use modInvite for cold (unsolicited) invites. */
async function modGrant(uuid){
const sig = await signBytes(sigAction(roomID, roomEpoch, 'grant-mic', uuid));
send({ type:'grant-mic', to: uuid, epoch: roomEpoch, sig });
}
async function modPromote(uuid){
const sig = await signBytes(sigAction(roomID, roomEpoch, 'promote', uuid, 'cohost'));
send({ type:'promote', target: uuid, to: 'cohost', epoch: roomEpoch, sig });
@ -1067,8 +1078,11 @@ function renderRoom(){
if (isMod(myRole) && m.uuid !== myUUID){
if (m.role === 'listener'){
const b = document.createElement('button'); b.className='small invert';
b.textContent = handraise.has(m.uuid) ? 'give the mic' : 'invite mic';
b.onclick = () => modInvite(m.uuid).catch(e=>logLine('err','invite: '+e.message));
const raised = handraise.has(m.uuid);
b.textContent = raised ? 'give the mic' : 'invite mic';
b.onclick = raised
? () => modGrant(m.uuid).catch(e=>logLine('err','grant: '+e.message))
: () => modInvite(m.uuid).catch(e=>logLine('err','invite: '+e.message));
acts.appendChild(b);
}
if (m.role === 'speaker' && myRole === 'host'){
@ -1197,8 +1211,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-05-31</span><br>
md5 <span class="stamp-md5">b285faeda4dec4f749f7cb5a7ebe8d4e</span><br>
sha256 <span class="stamp-sha">0f0d84caf0280fdbe9e4fb75b42fd63a7b198526c6b6ffa411bc58937f357e0d</span><br>
md5 <span class="stamp-md5">54c383d65ea2c7aee396c9444aa28490</span><br>
sha256 <span class="stamp-sha">018f444f698e38d36c8b3666d0188633d0dff052b66a3e19cd33385fccbdb7b2</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
</footer>