zebra-report: ship local preview tile for screen-share publisher

Mirror zebra-spaces fix + refreshed page-integrity stamps from
git.unturf.com/engineering/unturf/zebra-report d9852e6.
This commit is contained in:
russell@unturf.com 2026-06-01 19:45:30 -04:00
parent ab79242a50
commit 39b74ef622
No known key found for this signature in database
5 changed files with 33 additions and 20 deletions

View file

@ -440,9 +440,9 @@ handle /zebra-spaces-sfu* { reverse_proxy localhost:8092 { flush_interval -1
</p>
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-05-31</span><br>
md5 <span class="stamp-md5">b112cc063dcc4337a086775c97cdfc59</span><br>
sha256 <span class="stamp-sha">6c4d87467ef58fe408d5a8cf55a34c4d51f13ad3f8f21178977177d6ca2560f5</span><br>
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-01</span><br>
md5 <span class="stamp-md5">2d8b20ca6a7d4caceac7402f434d6bb0</span><br>
sha256 <span class="stamp-sha">70abdd694c78aa485d01d76fdd5e577dadce901c5aef7876ce74dadc597ece11</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 it against these hashes; point it at your own servers with ?signal= and ?turncred=</span>
</footer>

View file

@ -274,9 +274,9 @@
</p>
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-05-31</span><br>
md5 <span class="stamp-md5">1d9238a8fffdd3a5b8505dc92b8ad25d</span><br>
sha256 <span class="stamp-sha">f34c07111239b7c9b3901ea3af342fdc4df1f43d580bb5b2fa81cdcd19903bd4</span><br>
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-01</span><br>
md5 <span class="stamp-md5">d7b8cf7bb3d0de305227d877211e1c68</span><br>
sha256 <span class="stamp-sha">a173bbc0c29dfd8bd11c94f66e561ba31b9c1fc7534ce2032659cd0a5e43d147</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 it against these hashes; point it 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>

View file

@ -1652,9 +1652,9 @@ logLine('sys', 'chat content lives in encrypted SRTP audio. no IP packets carry
})();
</script>
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-05-31</span><br>
md5 <span class="stamp-md5">cd40b6b56af82a7e46a937e7f3b7692a</span><br>
sha256 <span class="stamp-sha">b0cd349f8abe8e898a95446ae7147769849916269c06c5e5a14862e596a9448a</span><br>
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-01</span><br>
md5 <span class="stamp-md5">f1d9169e022a422b9b2f1fa166b1da3d</span><br>
sha256 <span class="stamp-sha">e8af1dceb904c649a681670056c1c664c352f0e0102ffdf202d007dbb50e0182</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 it against these hashes; point it 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>

View file

@ -659,9 +659,9 @@ else wirePuppet();
</section>
<footer style="max-width:560px;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">533949e030e36803366e2e20b85b4e00</span><br>
sha256 <span class="stamp-sha">eb60ddbf8848f2f7f36248dc77810f862396483db111ce639da8663e3b223387</span><br>
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-01</span><br>
md5 <span class="stamp-md5">913f10bc546c13c1216bf7605a1b5afe</span><br>
sha256 <span class="stamp-sha">90b1c453c194824f79b77a72d5d52343baa79053894da6d2cf094c40ee4e9d48</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 it against these hashes; point it 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>

View file

@ -608,17 +608,24 @@ function flushSfuStreams(){
/* render one screen-share tile (or update its srcObject if it already
* exists). Removed on peer-left / boot / SFU track removal via
* removeScreenTile. */
function renderScreenTile(pubHex, stream){
/* find the matching member for the title */
function renderScreenTile(pubHex, stream, opts){
const local = !!(opts && opts.local);
/* find the matching member for the title; self isn't in members map */
let label = shortHex(pubHex);
for (const [, mm] of members){
try { if (mm.pubkey && hex(unb64(mm.pubkey)) === pubHex){ label = mm.handle || label; break; } } catch(_){}
if (local){
label = (myHandle || label) + ' (you)';
} else {
for (const [, mm] of members){
try { if (mm.pubkey && hex(unb64(mm.pubkey)) === pubHex){ label = mm.handle || label; break; } } catch(_){}
}
}
let entry = screenVideos.get(pubHex);
if (!entry){
const tile = document.createElement('div'); tile.className = 'screen-tile';
const video = document.createElement('video');
video.autoplay = true; video.playsInline = true; video.muted = false;
/* local preview must be muted — playing back our own captured audio
* would feed back through the mic */
video.autoplay = true; video.playsInline = true; video.muted = local;
const meta = document.createElement('div'); meta.className = 'screen-meta';
const who = document.createElement('span'); who.textContent = 'screen: '+label;
const pop = document.createElement('button'); pop.className = 'small';
@ -632,7 +639,7 @@ function renderScreenTile(pubHex, stream){
entry.video.srcObject = stream;
try { const p = entry.video.play(); if (p && p.catch) p.catch(()=>{}); } catch(_){}
$('sec-screens').classList.remove('hidden');
logLine('', 'screen share: receiving '+label);
logLine('', 'screen share: '+(local?'local preview ':'receiving ')+label);
}
function removeScreenTile(pubHex){
const entry = screenVideos.get(pubHex);
@ -694,12 +701,16 @@ async function sfuPublishScreen(){
await pc.setRemoteDescription({ type:'answer', sdp: ans.sdp });
sfuScreenPC = pc; sfuScreenPeerID = ans.peer_id;
logLine('', 'sfu: sharing screen as '+shortHex(sfuScreenPeerID));
/* render a muted local preview so the publisher sees what they're
* sharing — SFU does not echo the publisher's own stream back */
renderScreenTile(myKeys.pubHex, stream, { local: true });
$('btn-screen-share').classList.add('hidden');
$('btn-screen-stop').classList.remove('hidden');
}
async function sfuUnpublishScreen(){
if (!sfuScreenPC && !sfuScreenStream) return;
const pid = sfuScreenPeerID;
if (myKeys) removeScreenTile(myKeys.pubHex);
if (sfuScreenStream){ sfuScreenStream.getTracks().forEach(t=>t.stop()); sfuScreenStream = null; }
if (sfuScreenPC){ try { sfuScreenPC.close(); } catch(_){} sfuScreenPC = null; sfuScreenPeerID = null; }
if (pid && roomID){
@ -730,6 +741,8 @@ async function sfuSubscribe(){
* Route screens to the video tile renderer; mics to the audio path. */
if (sid.endsWith(':screen')){
const pubHex = sid.slice(0, -':screen'.length);
/* skip echo of our own screen — we already render a local preview */
if (myKeys && pubHex === myKeys.pubHex) return;
screenStreams.set(pubHex, ev.streams[0]);
renderScreenTile(pubHex, ev.streams[0]);
return;
@ -1569,8 +1582,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-06-01</span><br>
md5 <span class="stamp-md5">008a832a8d63dfcdc90c233c788d06ed</span><br>
sha256 <span class="stamp-sha">da27ddaf0020e72ece6bdb1c15c32bcdfc63de5b286e09c1602bffdb4109d137</span><br>
md5 <span class="stamp-md5">3b4349329d1441a1ded19fb38dee687b</span><br>
sha256 <span class="stamp-sha">0e6bf4dceb9230fb54fa401f532b65a8c624dcd6a04adb73eab5455e8fdeafa2</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>