From 2665792cafc0e8747b5c31a02cb32416c6672734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20T=C3=B6rnberg?= Date: Tue, 22 Sep 2026 14:35:38 +0200 Subject: [PATCH] fix styles --- public/app.js | 80 +++++++++++++++++++++++++++++++++++++++++++++++ public/index.html | 20 +++++++----- public/style.css | 75 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 152 insertions(+), 23 deletions(-) diff --git a/public/app.js b/public/app.js index bc37dd3..7e8bdbc 100644 --- a/public/app.js +++ b/public/app.js @@ -1648,6 +1648,86 @@ els.btnMenu.onclick = () => els.sidebar.classList.contains('open') ? closeSideba els.sidebarBackdrop.onclick = closeSidebar; document.querySelectorAll('.channel.text').forEach((el) => el.addEventListener('click', closeSidebar)); +// --------------------------------------------------------------------------- +// desktop panel resizing + sidebar collapse +// --------------------------------------------------------------------------- +const LS_SIDEBAR_W = 'voicepoc.sidebarWidth'; +const LS_SIDEBAR_COLLAPSED = 'voicepoc.sidebarCollapsed'; +const LS_SPATIAL_H = 'voicepoc.spatialHeight'; +const SIDEBAR_MIN = 180, SIDEBAR_MAX = 420; +const SPATIAL_MIN = 140, SPATIAL_MAX_RATIO = 0.75; +const isDesktop = () => window.matchMedia('(min-width: 861px)').matches; + +function dragResize({ handle, axis, onMove }) { + handle.addEventListener('mousedown', (e) => { + if (!isDesktop()) return; + e.preventDefault(); + handle.classList.add('dragging'); + const start = axis === 'x' ? e.clientX : e.clientY; + const onMouseMove = (ev) => onMove((axis === 'x' ? ev.clientX : ev.clientY) - start); + const onMouseUp = () => { + handle.classList.remove('dragging'); + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + }); +} + +// sidebar width +const sidebarResizer = $('#sidebar-resizer'); +const savedSidebarW = parseInt(localStorage.getItem(LS_SIDEBAR_W), 10); +if (isDesktop() && savedSidebarW) els.sidebar.style.width = `${savedSidebarW}px`; +let sidebarStartW = 0; +dragResize({ + handle: sidebarResizer, axis: 'x', + onMove: (dx) => { + if (!sidebarStartW) sidebarStartW = els.sidebar.getBoundingClientRect().width; + const w = Math.min(SIDEBAR_MAX, Math.max(SIDEBAR_MIN, sidebarStartW + dx)); + els.sidebar.style.width = `${w}px`; + localStorage.setItem(LS_SIDEBAR_W, String(Math.round(w))); + }, +}); +sidebarResizer.addEventListener('mousedown', () => { sidebarStartW = 0; }); + +// sidebar collapse/expand +const btnSidebarCollapse = $('#btn-sidebar-collapse'); +const btnSidebarExpand = $('#btn-sidebar-expand'); +function setSidebarCollapsed(collapsed) { + document.body.classList.toggle('sidebar-collapsed', collapsed); + localStorage.setItem(LS_SIDEBAR_COLLAPSED, collapsed ? '1' : '0'); +} +btnSidebarCollapse.onclick = () => setSidebarCollapsed(true); +btnSidebarExpand.onclick = () => setSidebarCollapsed(false); +if (isDesktop() && localStorage.getItem(LS_SIDEBAR_COLLAPSED) === '1') setSidebarCollapsed(true); + +// spatial pane height +const spatialResizer = $('#spatial-resizer'); +const spatialWrap = $('#spatial-wrap'); +const savedSpatialH = parseInt(localStorage.getItem(LS_SPATIAL_H), 10); +if (isDesktop() && savedSpatialH) spatialWrap.style.height = `${savedSpatialH}px`; +let spatialStartH = 0; +dragResize({ + handle: spatialResizer, axis: 'y', + onMove: (dy) => { + if (!spatialStartH) spatialStartH = spatialWrap.getBoundingClientRect().height; + const max = window.innerHeight * SPATIAL_MAX_RATIO; + const h = Math.min(max, Math.max(SPATIAL_MIN, spatialStartH - dy)); + spatialWrap.style.height = `${h}px`; + localStorage.setItem(LS_SPATIAL_H, String(Math.round(h))); + if (typeof cy !== 'undefined') cy.resize(); + }, +}); +spatialResizer.addEventListener('mousedown', () => { spatialStartH = 0; }); + +// dropping below the desktop breakpoint: let mobile's own CSS take over sizing +window.addEventListener('resize', () => { + if (isDesktop()) return; + els.sidebar.style.width = ''; + spatialWrap.style.height = ''; +}); + // Initial roster fetch and lightweight periodic sync (especially for unjoined clients) updateMicButton(); updateDeafenButton(); diff --git a/public/index.html b/public/index.html index 3c65256..f7d4096 100644 --- a/public/index.html +++ b/public/index.html @@ -44,7 +44,8 @@
+
@@ -88,6 +91,7 @@
+
SPATIAL AUDIO diff --git a/public/style.css b/public/style.css index 755d08b..a675cc6 100644 --- a/public/style.css +++ b/public/style.css @@ -65,11 +65,38 @@ body { position: relative; z-index: 10; } #server-name { - padding: 14px 16px; font-weight: 700; font-size: 15px; + padding: 14px 10px 14px 16px; font-weight: 700; font-size: 15px; border-bottom: 1px solid rgba(0,0,0,.25); box-shadow: 0 1px 0 rgba(0,0,0,.06); display: flex; align-items: center; gap: 8px; } #server-logo, #mobile-logo { border-radius: 6px; flex-shrink: 0; } +.server-name-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.icon-btn { + background: transparent; border: none; color: var(--text-2); cursor: pointer; + width: 26px; height: 26px; border-radius: 6px; display: grid; place-items: center; + flex-shrink: 0; padding: 0; transition: background .12s, color .12s; +} +.icon-btn .material-symbols-outlined { font-size: 18px; } +.icon-btn:hover { background: var(--bg-3); color: var(--text-0); } + +/* sidebar drag-to-resize handle */ +#sidebar-resizer { + position: absolute; top: 0; right: -3px; width: 6px; height: 100%; + cursor: col-resize; z-index: 15; background: transparent; +} +#sidebar-resizer:hover, #sidebar-resizer.dragging { background: var(--brand); opacity: .5; } +body.sidebar-collapsed #sidebar { display: none; } + +/* floating button to bring the sidebar back once collapsed */ +#btn-sidebar-expand { + display: none; position: absolute; top: 12px; left: 12px; z-index: 20; + background: var(--bg-1); border: 1px solid var(--bg-4); color: var(--text-1); + width: 32px; height: 32px; border-radius: 6px; cursor: pointer; place-items: center; + box-shadow: 0 2px 6px rgba(0,0,0,.35); +} +#btn-sidebar-expand .material-symbols-outlined { font-size: 18px; } +#btn-sidebar-expand:hover { background: var(--bg-3); color: var(--text-0); } +body.sidebar-collapsed #btn-sidebar-expand { display: grid; } .poc-tag { font-size: 10px; background: var(--yellow); color: #000; border-radius: 4px; padding: 1px 5px; vertical-align: 2px; margin-left: 6px; font-weight: 800; @@ -92,10 +119,27 @@ body { #user-panel { margin-top: auto; background: var(--bg-0); padding: 8px; - display: flex; align-items: center; justify-content: space-between; gap: 6px; - min-height: 52px; + display: flex; flex-direction: column; gap: 6px; } -.me { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; } + +/* row 1: available tools */ +.user-buttons { + display: flex; align-items: center; justify-content: center; gap: 4px; + padding-bottom: 6px; border-bottom: 1px solid rgba(255,255,255,.06); +} +.ctl { + background: transparent; border: none; color: var(--text-1); font-size: 15px; + width: 34px; height: 30px; border-radius: 6px; cursor: pointer; position: relative; + display: grid; place-items: center; padding: 0; transition: background .12s, color .12s; +} +.ctl .material-symbols-outlined { font-size: 19px; } +.ctl:hover { background: var(--bg-3); color: var(--text-0); } +.ctl.off { color: var(--red); } +.ctl:disabled { opacity: .35; cursor: not-allowed; } +.ctl.active { color: var(--green); } + +/* row 2: user info + help */ +.me { display: flex; align-items: center; gap: 8px; min-width: 0; } .me-meta { display: flex; flex-direction: column; min-width: 0; flex: 1; overflow: hidden; } #nick { background: transparent; border: none; color: var(--text-0); font: inherit; @@ -116,17 +160,7 @@ body { #voice-status { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.user-buttons { display: flex; gap: 2px; flex-shrink: 0; } -.ctl { - background: transparent; border: none; color: var(--text-1); font-size: 15px; - width: 28px; height: 28px; border-radius: 6px; cursor: pointer; position: relative; - display: grid; place-items: center; padding: 0; -} -.ctl .material-symbols-outlined { font-size: 20px; } -.ctl:hover { background: var(--bg-3); color: var(--text-0); } -.ctl.off { color: var(--red); } -.ctl:disabled { opacity: .35; cursor: not-allowed; } -.ctl.active { color: var(--green); } +.help-ctl { flex-shrink: 0; color: var(--text-2); } /* main */ #main { flex: 1; display: flex; flex-direction: column; min-width: 0; position: relative; height: 100%; } @@ -281,6 +315,13 @@ body { } /* spatial plane */ +#spatial-resizer { + height: 6px; flex-shrink: 0; cursor: row-resize; background: transparent; position: relative; +} +#spatial-resizer::after { + content: ''; position: absolute; left: 0; right: 0; top: 2px; height: 2px; +} +#spatial-resizer:hover, #spatial-resizer.dragging { background: var(--brand); opacity: .5; } #spatial-wrap { height: 34vh; min-height: 220px; background: var(--bg-1); border-top: 1px solid rgba(0,0,0,.35); display: flex; flex-direction: column; @@ -547,6 +588,10 @@ code { font-family: ui-monospace, monospace; font-size: 12px; } transform: translateX(-100%); transition: transform .2s ease; box-shadow: 8px 0 24px rgba(0,0,0,.5); } #sidebar.open { transform: translateX(0); } + /* desktop sidebar collapse/resize is irrelevant here — the drawer always exists, + it's just toggled on/off screen via .open */ + body.sidebar-collapsed #sidebar { display: flex; } + #sidebar-resizer, #spatial-resizer, #btn-sidebar-expand, #btn-sidebar-collapse { display: none !important; } #main { width: 100%; padding-bottom: 64px; } /* room for the fixed bottom controls */ #stage { padding: 10px; gap: 10px; }