diff --git a/public/index.html b/public/index.html index 42da382..4751cfb 100644 --- a/public/index.html +++ b/public/index.html @@ -5,6 +5,15 @@ gibbz voice — POC + + + + + + + + + + diff --git a/public/static/favicon.ico b/public/static/favicon.ico new file mode 100644 index 0000000..1be6013 Binary files /dev/null and b/public/static/favicon.ico differ diff --git a/public/static/icons/apple-touch-icon.png b/public/static/icons/apple-touch-icon.png new file mode 100644 index 0000000..6466226 Binary files /dev/null and b/public/static/icons/apple-touch-icon.png differ diff --git a/public/static/icons/icon-192.png b/public/static/icons/icon-192.png new file mode 100644 index 0000000..0337f01 Binary files /dev/null and b/public/static/icons/icon-192.png differ diff --git a/public/static/icons/icon-512.png b/public/static/icons/icon-512.png new file mode 100644 index 0000000..15c6d00 Binary files /dev/null and b/public/static/icons/icon-512.png differ diff --git a/public/static/icons/icon.svg b/public/static/icons/icon.svg new file mode 100644 index 0000000..2abf119 --- /dev/null +++ b/public/static/icons/icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/icons/maskable-192.png b/public/static/icons/maskable-192.png new file mode 100644 index 0000000..0337f01 Binary files /dev/null and b/public/static/icons/maskable-192.png differ diff --git a/public/static/icons/maskable-512.png b/public/static/icons/maskable-512.png new file mode 100644 index 0000000..15c6d00 Binary files /dev/null and b/public/static/icons/maskable-512.png differ diff --git a/public/static/manifest.webmanifest b/public/static/manifest.webmanifest new file mode 100644 index 0000000..2c3f746 --- /dev/null +++ b/public/static/manifest.webmanifest @@ -0,0 +1,17 @@ +{ + "name": "gibbz voice", + "short_name": "gibbz", + "description": "Discord-style voice chat with screen sharing and spatial audio", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#1e1f22", + "theme_color": "#1e1f22", + "orientation": "any", + "icons": [ + { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, + { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, + { "src": "/icons/maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, + { "src": "/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } + ] +} diff --git a/public/static/sw.js b/public/static/sw.js new file mode 100644 index 0000000..98c76b2 --- /dev/null +++ b/public/static/sw.js @@ -0,0 +1,33 @@ +const CACHE = 'gibbz-shell-v1'; +const SHELL = ['/', '/index.html', '/style.css', '/app.js', '/manifest.webmanifest']; + +self.addEventListener('install', (event) => { + event.waitUntil(caches.open(CACHE).then((cache) => cache.addAll(SHELL))); + self.skipWaiting(); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) + ); + self.clients.claim(); +}); + +// Never touch the signaling socket or live API data — only cache the static +// app shell so the UI can still load (offline/flaky network), not room state. +self.addEventListener('fetch', (event) => { + const { request } = event; + const url = new URL(request.url); + if (request.method !== 'GET' || url.origin !== self.location.origin) return; + if (url.pathname.startsWith('/api/') || url.pathname === '/ws') return; + + event.respondWith( + fetch(request) + .then((response) => { + const copy = response.clone(); + caches.open(CACHE).then((cache) => cache.put(request, copy)); + return response; + }) + .catch(() => caches.match(request).then((cached) => cached || caches.match('/index.html'))) + ); +}); diff --git a/public/style.css b/public/style.css index a538707..0e5c805 100644 --- a/public/style.css +++ b/public/style.css @@ -1,6 +1,6 @@ /* Discord-ish dark theme (POC) */ :root { - --bg-0: #1e1f22; /* rail */ + --bg-0: #1e1f22; /* user panel / topbar */ --bg-1: #2b2d31; /* sidebar */ --bg-2: #313338; /* main */ --bg-3: #383a40; /* hover */ @@ -24,19 +24,6 @@ body { } #app { display: flex; height: 100vh; } -/* rail */ -#rail { - width: 68px; background: var(--bg-0); padding: 12px 0; - display: flex; flex-direction: column; align-items: center; gap: 8px; flex-shrink: 0; -} -.rail-icon { - width: 44px; height: 44px; border-radius: 50%; background: var(--bg-3); - display: grid; place-items: center; font-weight: 700; color: var(--text-1); - cursor: pointer; transition: border-radius .15s, background .15s; -} -.rail-icon:hover, .rail-icon.home { border-radius: 16px; background: var(--brand); color: #fff; } -.rail-sep { width: 28px; height: 2px; background: var(--bg-3); border-radius: 1px; } - /* sidebar */ #sidebar { width: 250px; background: var(--bg-1); display: flex; flex-direction: column; flex-shrink: 0; @@ -45,7 +32,9 @@ body { #server-name { padding: 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; } .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; @@ -422,13 +411,11 @@ code { font-family: ui-monospace, monospace; font-size: 12px; } /* --------------------------------------------------------------------------- mobile layout - - rail is dropped (decorative only in this POC) - sidebar becomes an off-canvas drawer opened from #mobile-topbar - mic/deafen/share get an always-visible mirror bar pinned to the bottom --------------------------------------------------------------------------- */ @media (max-width: 860px) { #app { padding-top: 48px; } /* room for the fixed topbar */ - #rail { display: none; } #mobile-topbar { display: flex; align-items: center; gap: 10px; diff --git a/server.js b/server.js index 22a824a..2575cce 100644 --- a/server.js +++ b/server.js @@ -96,6 +96,7 @@ const MIME = { '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', + '.webmanifest': 'application/manifest+json', '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon', diff --git a/vite.config.mjs b/vite.config.mjs index 242d2c2..2d73226 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -4,6 +4,7 @@ import path from 'path'; export default defineConfig({ root: 'public', + publicDir: 'static', plugins: [ nodePolyfills({ include: ['buffer', 'crypto', 'events', 'stream', 'util'],