diff --git a/favicon.svg b/favicon.svg index 976ca42..4ebff3b 100644 --- a/favicon.svg +++ b/favicon.svg @@ -1 +1,8 @@ - + + + + + + + + diff --git a/icons/icon.html b/icons/icon.html new file mode 100644 index 0000000..e890749 --- /dev/null +++ b/icons/icon.html @@ -0,0 +1,5 @@ + + +icon + + diff --git a/icons/icon.svg b/icons/icon.svg new file mode 100644 index 0000000..fa854cd --- /dev/null +++ b/icons/icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..6af38ba --- /dev/null +++ b/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "gandora", + "short_name": "gandora", + "description": "Type a seed. Get a station. Every song is made for you as it plays.", + "start_url": "/", + "scope": "/", + "display": "standalone", + "orientation": "portrait", + "background_color": "#0b0b0e", + "theme_color": "#0b0b0e", + "icons": [ + { "src": "icons/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable" }, + { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, + { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" } + ] +} diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..2ca8288 --- /dev/null +++ b/sw.js @@ -0,0 +1,13 @@ +// gandora's service worker: the shell is cached so the app opens and the library plays with no signal. +// Tracks live in IndexedDB already; the DJ and new renders need the network and the page says so. +const SHELL = "gandora-shell-v1"; +const FILES = ["/", "/index.html", "/brain.js", "/favicon.svg", "/manifest.json", "/icons/icon.svg"]; +self.addEventListener("install", (e) => { e.waitUntil(caches.open(SHELL).then((c) => c.addAll(FILES)).then(() => self.skipWaiting())); }); +self.addEventListener("activate", (e) => { e.waitUntil(caches.keys().then((ks) => Promise.all(ks.filter((k) => k !== SHELL).map((k) => caches.delete(k)))).then(() => self.clients.claim())); }); +// same-origin GETs: network first (so a deploy shows up on the next open), cache as the fallback; everything else untouched +self.addEventListener("fetch", (e) => { + const u = new URL(e.request.url); + if (e.request.method !== "GET" || u.origin !== location.origin) return; + e.respondWith(fetch(e.request).then((r) => { if (r.ok) caches.open(SHELL).then((c) => c.put(e.request, r.clone())); return r; }) + .catch(() => caches.match(e.request, { ignoreSearch: true }).then((r) => r || caches.match("/")))); +});