From e1e2c8daa9ae822af009d8a8e8ea6a6e1543288b Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 3 Jun 2026 19:38:52 -0400 Subject: [PATCH] zebra-spaces: stream toggle into its own top-row column (no vertical stretch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .stream-toggle CSS class and `strm` grid column were already in place but the JS still appended the button into .acts-primary — which lives in the bottom-row mod-actions stack. That meant every speaker row (including self-monitor on non-mod rows) grew a third sub-row just to host the single ◉ glyph, adding scroll height. Render it as a sibling of micEl in the row so it lands in the `strm` grid column on the TOP row, alongside the mic icon. Single character text (○ / ◉) keeps the column tight, and the .stream-toggle styles already shipped pick it up. For rows that don't qualify (listeners, non-host-viewers on other speakers), the streamEl is null → not appended → strm column collapses to its 1.4rem track width. No row-height penalty. The mod-actions stack stays as-is for actual mod actions (mic-invite, role transitions, kick, ban). Self rows and non-mod self-monitor rows no longer carry an empty mod-actions row at all. --- web/zebra-spaces.html | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index f2a6c1b..4953e72 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -4153,7 +4153,14 @@ function renderRoom(){ * - OTHER rows: host only (room-wide control) * Speakers/cohosts get self-monitor only. Listeners get NO button * at all — they're locked into DJ mode unconditionally for the - * glitch-free playback fox: 'epic, never choppy'. */ + * glitch-free playback fox: 'epic, never choppy'. + * + * Lives in its own `strm` grid column on the TOP row (alongside the + * mic icon), NOT in the .acts-primary stack — putting it there made + * every speaker row grow a third sub-row of mod-actions even for + * non-mods (just-self-monitor self-rows), which fox flagged as + * causing vertical scroll. Single-char glyph keeps the column 1.4rem. */ + let streamEl = null; if (canSpeak(m.role) && m.pubkey){ let mPubHex = ''; try { mPubHex = hex(unb64(m.pubkey)); } catch(_){} @@ -4161,21 +4168,20 @@ function renderRoom(){ const isSelf = (m.uuid === myUUID); const canStreamThisRow = isSelf || myRole === 'host'; if (canStreamThisRow){ - const sb = document.createElement('button'); sb.className = 'small'; const on = streamMode.has(mPubHex); - sb.textContent = on ? '◉ stream' : '○ stream'; + streamEl = document.createElement('button'); + streamEl.className = 'stream-toggle' + (on ? ' on' : ''); + streamEl.textContent = on ? '◉' : '○'; if (isSelf){ - sb.title = on - ? 'monitoring your own DJ stream — click to stop (feedback risk on open speakers!)' + streamEl.title = on + ? 'monitoring your DJ stream — click to stop (feedback risk on open speakers!)' : 'preview what listeners hear of YOUR mic (~2s delay) — headphones recommended'; } else { - sb.title = on - ? 'streaming — click to switch back to live (low-latency WebRTC)' - : 'switch to HTTP Ogg/Opus stream (~2s delay, glitch-free for DJ sets)'; + streamEl.title = on + ? 'streaming — click to switch back to live WebRTC' + : 'switch this speaker to HTTP Ogg/Opus stream (~2s delay, glitch-free)'; } - if (on) sb.classList.add('invert'); - sb.onclick = () => toggleStreamFor(m.uuid, mPubHex); - actsPrimary.appendChild(sb); + streamEl.onclick = () => toggleStreamFor(m.uuid, mPubHex); } } } @@ -4221,7 +4227,13 @@ function renderRoom(){ acts.appendChild(actsPrimary); acts.appendChild(actsRemoval); row.appendChild(badge); row.appendChild(handle); row.appendChild(pub); - row.appendChild(micEl); row.appendChild(meter); row.appendChild(acts); + row.appendChild(micEl); + /* stream toggle (when rendered) goes between mic and meter so it + * lands in the `strm` grid column on the top row. If absent the + * column collapses to its 1.4rem track width (small whitespace + * gap, no row-height change). */ + if (streamEl) row.appendChild(streamEl); + row.appendChild(meter); row.appendChild(acts); wrap.appendChild(row); } /* keep listener UI in sync */ @@ -4697,8 +4709,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');