Files
gibbz/public/index.html
T
matsandClaude Sonnet 5 7795cf84a3 Add a mobile-responsive layout and mirrored call controls
- Sidebar (channel list + nickname) becomes an off-canvas drawer below
  860px, opened via a new fixed top bar (hamburger + live status) and
  closed by tapping the backdrop or picking a channel.
- Mic/deafen/share get an always-visible bottom control bar so they're
  reachable mid-call without opening the drawer; updateMicButton/
  updateDeafenButton and their click wiring now target both the
  sidebar and mirror buttons via shared NodeLists instead of single
  elements.
- Per-user volume popup (previously hover-only) now also toggles on
  tap, and repositions below the row instead of to the side, which
  would overflow a narrow drawer.
- Screen-share tiles stack full-width, touch targets are enlarged,
  and the spatial map panel shrinks to leave room for the stage.

Also includes the working tree's prior swap of the spatial view from
Konva to Cytoscape.js (drag, scroll-to-zoom, fit-to-view, distance
edges) — not authored in this change, carried along since it was
already uncommitted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 11:39:05 +02:00

171 lines
6.8 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>gibbz voice — POC</title>
<link rel="stylesheet" href="style.css" />
<script src="https://otel.k6n.net/static/otel-rum.js"></script>
<script>
window.rum = window.OtelRUM ? OtelRUM.init({
serviceName: "gibbz",
baseUrl: "https://otel.k6n.net",
release: window.__APP_RELEASE__ || "dev",
environment: "production",
traceHeaderOrigins: [window.location.origin]
}) : null;
</script>
</head>
<body>
<!-- mobile top bar: hamburger opens the channel drawer, shows live status -->
<header id="mobile-topbar">
<button id="btn-menu" title="Channels">&#9776;</button>
<span id="mobile-title">gibbz voice</span>
<span class="status-row" id="mobile-status-row">
<span class="status-indicator" id="mobile-status-dot"></span>
<span id="mobile-status-text">Disconnected</span>
</span>
</header>
<div id="sidebar-backdrop"></div>
<div id="app">
<!-- server rail -->
<nav id="rail">
<div class="rail-icon home" title="gibbz voice">G</div>
<div class="rail-sep"></div>
<div class="rail-icon" title="POC server">VP</div>
</nav>
<!-- channel sidebar -->
<aside id="sidebar">
<header id="server-name">gibbz voice <span class="poc-tag">POC</span></header>
<div class="channel-group-label">Text channels</div>
<div class="channel text" data-mx-room="gibbz-general">#
general <span class="hint">matrix</span></div>
<div class="channel-group-label">Voice channels</div>
<div class="channel voice" data-room="general">&#128266; General <span class="count" data-room-count="general"></span></div>
<div class="channel voice" data-room="gaming">&#128266; Gaming <span class="count" data-room-count="gaming"></span></div>
<div class="channel voice" data-room="lounge">&#128266; Lounge <span class="count" data-room-count="lounge"></span></div>
<footer id="user-panel">
<div class="me">
<div class="avatar" id="me-avatar">?</div>
<div class="me-meta">
<input id="nick" maxlength="24" spellcheck="false" title="Click to edit nickname" />
<div class="status-row" id="status-row" title="Not connected">
<span class="status-indicator" id="status-dot"></span>
<span id="voice-status">Disconnected</span>
</div>
</div>
</div>
<div class="user-buttons">
<button id="btn-mic" class="ctl" title="Toggle mic (M)">&#127908;</button>
<button id="btn-deafen" class="ctl" title="Toggle deafen (D)">&#128064;</button>
<button id="btn-share" class="ctl" title="Share your screen (S)">&#128421;&#65039;</button>
<button id="btn-settings" class="ctl" title="Voice &amp; ICE settings">&#9881;&#65039;</button>
<button id="btn-help" class="ctl" title="How to use gibbz voice">&#10067;</button>
</div>
</footer>
</aside>
<!-- main stage -->
<main id="main">
<div id="stage">
<div id="screens"></div>
<div id="stage-empty">
<div class="big-icon">&#128421;&#65039;</div>
<h2>No active screen shares</h2>
<p>When someone shares their screen, it will appear here. Click the screen button below or press <b>S</b> to share.</p>
</div>
</div>
<div id="spatial-wrap">
<div class="spatial-label">
<span>SPATIAL AUDIO</span>
<span class="hint">drag your avatar to move; drag peers for local positioning &bull; scroll to zoom</span>
<button id="btn-spatial-fit" class="spatial-action-btn" title="Fit all users in view">&#x26F6; Fit View</button>
</div>
<div id="spatial"></div>
</div>
</main>
</div>
<!-- mobile bottom bar: always-reachable call controls, mirror the sidebar's -->
<div id="mobile-controls">
<button class="ctl mirror-mic" title="Toggle mic (M)">&#127908;</button>
<button class="ctl mirror-deafen" title="Toggle deafen (D)">&#128064;</button>
<button class="ctl mirror-share" title="Share your screen (S)">&#128421;&#65039;</button>
</div>
<!-- settings modal -->
<div id="modal-backdrop" class="hidden">
<div id="settings-modal" role="dialog" aria-modal="true">
<h2>Voice &amp; ICE settings</h2>
<p class="hint">Overrides the server-provided ICE configuration (stored in <code>localStorage</code>, applied on next join).</p>
<label class="toggle-row">
<input type="checkbox" id="override-enabled" />
<span>Use custom STUN/TURN (JSON below)</span>
</label>
<textarea id="ice-json" rows="12" spellcheck="false"></textarea>
<div class="modal-actions">
<button id="ice-reset" class="ghost">Reset to server defaults</button>
<span class="spacer"></span>
<button id="ice-cancel" class="ghost">Cancel</button>
<button id="ice-save">Save</button>
</div>
<h3>Server defaults</h3>
<pre id="ice-server-default" class="hint"></pre>
</div>
</div>
<!-- welcome / nickname / how-to-use modal -->
<div id="welcome-backdrop" class="hidden">
<div id="welcome-modal" role="dialog" aria-modal="true">
<h2>Welcome to gibbz voice</h2>
<p class="hint">Pick a name so others can recognize you.</p>
<label class="field-label" for="welcome-nick">Nickname</label>
<input id="welcome-nick" maxlength="24" spellcheck="false" autocomplete="off" placeholder="e.g. alex" />
<div id="welcome-nick-error" class="field-error hidden">Enter a nickname to continue.</div>
<h3>Quick start</h3>
<ul class="howto-list">
<li>Click a <b>voice channel</b> in the sidebar to join it — click another to switch.</li>
<li><b>Mic</b>, <b>Deafen</b> and <b>Share screen</b> are the buttons bottom-left, or press <b>M</b> / <b>D</b> / <b>S</b>.</li>
<li>On the <b>spatial audio</b> map, drag your dot around — you hear people relative to where they stand.</li>
<li>Screen shares show up at the top of the main area; pop one into its own window with <b>&#10726;</b>.</li>
</ul>
<div class="modal-actions">
<span class="spacer"></span>
<button id="welcome-continue">Continue</button>
</div>
</div>
</div>
<div id="mx-panel" class="hidden">
<div class="mx-header">
<span>#general <span class="poc-tag">matrix · experimental</span></span>
<button id="mx-close" class="ghost" title="Close">&times;</button>
</div>
<div id="mx-messages"></div>
<div class="mx-composer">
<textarea id="mx-input" rows="1" placeholder="Message #general (Enter to send)"></textarea>
<button id="mx-send">Send</button>
</div>
</div>
<div id="reconnect-banner" class="hidden" role="alert">
<div class="reconnect-spinner"></div>
<span id="reconnect-text">Connection lost</span>
<div class="reconnect-actions">
<button id="reconnect-retry">Retry now</button>
<button id="reconnect-dismiss" class="ghost">Dismiss</button>
</div>
</div>
<div id="toasts"></div>
<script type="module" src="/app.js"></script>
</body>
</html>