From aab793114299b9344416430852110a19b2b9b729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20T=C3=B6rnberg?= Date: Tue, 22 Sep 2026 14:24:14 +0200 Subject: [PATCH] icons --- public/app.js | 119 +++++++++++++++++++++++++++----- public/index.html | 87 +++++++++++++----------- public/style.css | 170 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 286 insertions(+), 90 deletions(-) diff --git a/public/app.js b/public/app.js index b1e66f8..bc37dd3 100644 --- a/public/app.js +++ b/public/app.js @@ -34,6 +34,7 @@ window.__dbg = S; // POC debug handle const $ = (sel) => document.querySelector(sel); const els = { + voiceView: $('#voice-view'), stage: $('#stage'), screens: $('#screens'), spatial: $('#spatial'), status: $('#voice-status'), statusDot: $('#status-dot'), statusRow: $('#status-row'), nick: $('#nick'), @@ -50,6 +51,13 @@ const shareButtons = [...document.querySelectorAll('#btn-share, .mirror-share')] const statusTextEls = [...document.querySelectorAll('#voice-status, #mobile-status-text')]; const statusDotEls = [...document.querySelectorAll('#status-dot, #mobile-status-dot')]; +function mIcon(name, extraClass = '') { + const span = document.createElement('span'); + span.className = `material-symbols-outlined${extraClass ? ` ${extraClass}` : ''}`; + span.textContent = name; + return span; +} + // --------------------------------------------------------------------------- // helpers // --------------------------------------------------------------------------- @@ -784,9 +792,27 @@ function renderTiles() { const meBadges = document.createElement('span'); meBadges.className = 'user-line-badges'; - if (S.muted) meBadges.innerHTML += '🎤️‍❌'; - if (S.deafened) meBadges.innerHTML += '👂❌'; - if (S.sharing) meBadges.innerHTML += '🖥️'; + if (S.muted) { + const b = document.createElement('span'); + b.className = 'badge-mute'; + b.title = 'Muted'; + b.appendChild(mIcon('mic_off')); + meBadges.appendChild(b); + } + if (S.deafened) { + const b = document.createElement('span'); + b.className = 'badge-deafen'; + b.title = 'Deafened'; + b.appendChild(mIcon('headset_off')); + meBadges.appendChild(b); + } + if (S.sharing) { + const b = document.createElement('span'); + b.className = 'badge-share'; + b.title = 'Sharing screen'; + b.appendChild(mIcon('present_to_all')); + meBadges.appendChild(b); + } meItem.append(meAv, meName, meBadges); roster.appendChild(meItem); @@ -808,8 +834,20 @@ function renderTiles() { if (!peer.pc || peer.pc.connectionState !== 'connected') { badges.innerHTML += '…'; } - if (peer.state.muted) badges.innerHTML += '🎤️‍❌'; - if (peer.state.sharing) badges.innerHTML += '🖥️'; + if (peer.state.muted) { + const b = document.createElement('span'); + b.className = 'badge-mute'; + b.title = 'Muted'; + b.appendChild(mIcon('mic_off')); + badges.appendChild(b); + } + if (peer.state.sharing) { + const b = document.createElement('span'); + b.className = 'badge-share'; + b.title = 'Sharing screen'; + b.appendChild(mIcon('present_to_all')); + badges.appendChild(b); + } // Volume hover popup const volPop = document.createElement('div'); @@ -858,8 +896,20 @@ function renderTiles() { const badges = document.createElement('span'); badges.className = 'user-line-badges'; - if (user.state?.muted) badges.innerHTML += '🎤️‍❌'; - if (user.state?.sharing) badges.innerHTML += '🖥️'; + if (user.state?.muted) { + const b = document.createElement('span'); + b.className = 'badge-mute'; + b.title = 'Muted'; + b.appendChild(mIcon('mic_off')); + badges.appendChild(b); + } + if (user.state?.sharing) { + const b = document.createElement('span'); + b.className = 'badge-share'; + b.title = 'Sharing screen'; + b.appendChild(mIcon('present_to_all')); + badges.appendChild(b); + } item.append(av, name, badges); roster.appendChild(item); @@ -878,7 +928,9 @@ function activeShares() { const shares = []; if (S.sharing && S.screenStream) shares.push({ key: 'me', name: `${S.name} (you)`, stream: S.screenStream }); for (const peer of S.peers.values()) { - if (peer.screenStream) shares.push({ key: peer.id, name: peer.name, stream: peer.screenStream }); + if (peer.state.sharing && peer.screenStream) { + shares.push({ key: peer.id, name: peer.name, stream: peer.screenStream }); + } } return shares; } @@ -893,6 +945,8 @@ function renderScreens() { } } + if (!els.screens) return; + els.screens.innerHTML = ''; els.screens.classList.toggle('multi', activeShares().length > 1); for (const share of activeShares()) { @@ -902,7 +956,8 @@ function renderScreens() { ph.className = 'screen-card popped'; const btn = document.createElement('button'); btn.className = 'pop-btn'; - btn.textContent = '⧉ re-embed'; + btn.appendChild(mIcon('open_in_new')); + btn.appendChild(document.createTextNode(' re-embed')); btn.onclick = () => closePopout(share.key); const label = document.createElement('div'); label.className = 'label'; @@ -926,11 +981,12 @@ function renderScreens() { label.appendChild(document.createTextNode(share.name)); const micBadge = document.createElement('span'); micBadge.className = 'tile-mic-badge'; + micBadge.appendChild(mIcon('mic_off')); label.appendChild(micBadge); const pop = document.createElement('button'); pop.className = 'pop-btn'; pop.title = 'Pop out in new window'; - pop.textContent = '⧉'; + pop.appendChild(mIcon('open_in_new')); pop.onclick = () => popOutShare(share); card.append(video, label, pop); els.screens.appendChild(card); @@ -1428,8 +1484,24 @@ function mxRenderTimeline() { els.mxMessages.scrollTop = els.mxMessages.scrollHeight; } -function mxShowPanel() { +function switchToChatView() { + if (els.voiceView) els.voiceView.classList.add('hidden'); if (els.mxPanel) els.mxPanel.classList.remove('hidden'); + document.querySelectorAll('.channel.text').forEach((c) => c.classList.add('active')); + if (els.mxMessages) els.mxMessages.scrollTop = els.mxMessages.scrollHeight; + if (els.mxInput) els.mxInput.focus(); +} + +function switchToVoiceView() { + if (els.mxPanel) els.mxPanel.classList.add('hidden'); + if (els.voiceView) els.voiceView.classList.remove('hidden'); + document.querySelectorAll('.channel.text').forEach((c) => c.classList.remove('active')); + // Trigger Cytoscape redraw/resize because voice-view was unhidden + if (typeof cy !== 'undefined') cy.resize(); +} + +function mxShowPanel() { + switchToChatView(); } function mxSend(text) { @@ -1462,12 +1534,13 @@ mxInit(); document.querySelectorAll('.channel.text').forEach((el) => { el.onclick = () => { - if (Mx.roomId) { mxShowPanel(); return; } + closeSidebar(); + if (Mx.roomId) { switchToChatView(); return; } if (Mx.client) { toast('Connecting to Matrix…'); return; } mxStartLogin(); }; }); -$('#mx-close')?.addEventListener('click', () => els.mxPanel.classList.add('hidden')); +$('#mx-close')?.addEventListener('click', () => switchToVoiceView()); $('#mx-send')?.addEventListener('click', () => { mxSend(els.mxInput.value); els.mxInput.value = ''; @@ -1486,12 +1559,15 @@ function updateMicButton() { const off = S.muted || !S.micStream; for (const btn of micButtons) { btn.classList.toggle('off', off); - btn.textContent = off ? '\u{1F507}' : '\u{1F3A4}'; // 🔇 muted/no-mic vs 🎤 live + btn.replaceChildren(mIcon(off ? 'mic_off' : 'mic')); btn.disabled = !S.micStream; } } function updateDeafenButton() { - for (const btn of deafenButtons) btn.classList.toggle('off', S.deafened); + for (const btn of deafenButtons) { + btn.classList.toggle('off', S.deafened); + btn.replaceChildren(mIcon(S.deafened ? 'headset_off' : 'headphones')); + } } function sendState() { send({ type: 'state', state: { muted: S.muted, deafened: S.deafened, sharing: S.sharing } }); @@ -1524,9 +1600,15 @@ $('#btn-settings').onclick = openSettings; els.nick.value = S.name; els.nick.addEventListener('change', () => { - const trimmed = els.nick.value.trim(); - if (!trimmed) { els.nick.value = S.name; return; } // nickname can't be blanked out - S.name = trimmed; + const v = els.nick.value.trim(); + if (v) S.name = v; + els.nick.value = S.name; + localStorage.setItem(LS_NAME, S.name); + setAvatar(els.meAvatar, S.name, S.myId || 'me'); + if (S.ws && S.ws.readyState === 1) send({ type: 'rename', name: S.name }); + renderTiles(); +}); +els.nick.addEventListener('blur', () => { els.nick.value = S.name; localStorage.setItem(LS_NAME, S.name); setAvatar(els.meAvatar, S.name, S.myId || 'me'); @@ -1536,6 +1618,7 @@ setAvatar(els.meAvatar, S.name, 'me'); document.querySelectorAll('.channel.voice').forEach((el) => { el.onclick = () => { closeSidebar(); + switchToVoiceView(); if (S.room === el.dataset.room) return; cancelReconnect(); document.querySelectorAll('.channel.voice').forEach((c) => c.classList.remove('active')); diff --git a/public/index.html b/public/index.html index 4751cfb..3c65256 100644 --- a/public/index.html +++ b/public/index.html @@ -5,6 +5,7 @@ gibbz voice — POC + @@ -28,7 +29,7 @@
- + gibbz voice @@ -46,12 +47,12 @@ gibbz voice POC
Text channels
-
# +
tag general matrix
Voice channels
-
🔊 General
-
🔊 Gaming
-
🔊 Lounge
+
volume_up General
+
volume_up Gaming
+
volume_up Lounge
@@ -65,42 +66,62 @@
- - - - - + + + + +
- +
-
-
-
-
🖥️
-

No active screen shares

-

When someone shares their screen, it will appear here. Click the screen button below or press S to share.

+ +
+
+
+
+
present_to_all
+

No active screen shares

+

When someone shares their screen, it will appear here. Click the screen button below or press S to share.

+
+
+ +
+
+ SPATIAL AUDIO + drag your avatar to move; drag peers for local positioning • scroll to zoom + +
+
-
-
- SPATIAL AUDIO - drag your avatar to move; drag peers for local positioning • scroll to zoom - + +
- - - + + +
@@ -138,7 +159,7 @@
  • Click a voice channel in the sidebar to join it — click another to switch.
  • Mic, Deafen and Share screen are the buttons bottom-left, or press M / D / S.
  • On the spatial audio map, drag your dot around — you hear people relative to where they stand.
  • -
  • Screen shares show up at the top of the main area; pop one into its own window with ⧦.
  • +
  • Screen shares show up at the top of the main area; pop one into its own window with open_in_new.
  • - -