Clean, dependency-light front page. Only three things ship here: - index.html — two-button landing: Mobile -> mobile.html, Desktop -> pm_e-2.html - mobile.html — touch-first PWA (+ mobile-sessions.html practice journal) - pm_e-2.html — engraved-notation editor build.sh/deploy.sh trimmed to just these; deploy mirrors dist/ to the web root with rsync --delete. README/CLAUDE.md rewritten for the slim scope. The full project (PM_E-1 editor, embeddable widget, all hardware form-factor pages, Pico firmware editions, the Rust port, and the KiCad/SPICE hardware design) is preserved on the `concepts` branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
1.8 KiB
JavaScript
52 lines
1.8 KiB
JavaScript
/* Service worker for the PolyMeter mobile app (mobile.html).
|
|
*
|
|
* Deliberately minimal and non-intrusive: it only manages its OWN app-shell URLs
|
|
* (the page, manifest, icons). For every other request it does NOT call
|
|
* respondWith(), so the rest of the site behaves exactly as if no SW existed.
|
|
*
|
|
* Strategy for the shell: network-first, fall back to cache. The page is a single
|
|
* self-contained file that is version-stamped on deploy, so when the device is
|
|
* online it always gets the freshest build; offline it still launches from cache.
|
|
*/
|
|
const CACHE = "polymeter-mobile-v2";
|
|
const SHELL = [
|
|
"/mobile.html",
|
|
"/mobile-sessions.html",
|
|
"/manifest.webmanifest",
|
|
"/icon-192.png",
|
|
"/icon-512.png",
|
|
"/icon-180.png",
|
|
];
|
|
const SHELL_PATHS = new Set(SHELL);
|
|
|
|
self.addEventListener("install", (e) => {
|
|
self.skipWaiting();
|
|
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(SHELL)).catch(() => {}));
|
|
});
|
|
|
|
self.addEventListener("activate", (e) => {
|
|
e.waitUntil(
|
|
caches.keys()
|
|
.then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
|
|
.then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
self.addEventListener("fetch", (e) => {
|
|
const req = e.request;
|
|
if (req.method !== "GET") return;
|
|
const url = new URL(req.url);
|
|
if (url.origin !== self.location.origin) return;
|
|
// Treat any navigation to /mobile.html (with or without ?standalone=1 etc.) as the shell.
|
|
const path = url.pathname;
|
|
if (!SHELL_PATHS.has(path)) return; // not ours — let the browser handle it
|
|
|
|
e.respondWith(
|
|
fetch(req)
|
|
.then((res) => {
|
|
if (res && res.ok) { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(path, copy)); }
|
|
return res;
|
|
})
|
|
.catch(() => caches.match(path).then((hit) => hit || caches.match("/mobile.html")))
|
|
);
|
|
});
|