pwa
No files matched your search
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#6b74f5"/>
|
||||
<stop offset="1" stop-color="#4752c4"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="512" height="512" fill="url(#bg)"/>
|
||||
<!-- soundwave rings, centered, kept inside the ~80% maskable safe zone -->
|
||||
<g stroke="#ffffff" stroke-linecap="round" fill="none">
|
||||
<path d="M 168 196 A 96 96 0 0 0 168 316" stroke-width="16" opacity="0.55"/>
|
||||
<path d="M 344 196 A 96 96 0 0 1 344 316" stroke-width="16" opacity="0.55"/>
|
||||
<path d="M 200 222 A 60 60 0 0 0 200 290" stroke-width="16" opacity="0.85"/>
|
||||
<path d="M 312 222 A 60 60 0 0 1 312 290" stroke-width="16" opacity="0.85"/>
|
||||
</g>
|
||||
<!-- mic body -->
|
||||
<g fill="#ffffff">
|
||||
<rect x="226" y="150" width="60" height="110" rx="30"/>
|
||||
<path d="M 196 234 v 12 a 60 60 0 0 0 120 0 v -12 h -16 v 12 a 44 44 0 0 1 -88 0 v -12 z" />
|
||||
<rect x="248" y="318" width="16" height="34" rx="4"/>
|
||||
<rect x="220" y="352" width="72" height="14" rx="6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -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" }
|
||||
]
|
||||
}
|
||||
@@ -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')))
|
||||
);
|
||||
});
|
||||