// Puzzle Trainer — Service Worker const CACHE = "pt-v2"; const SHELL = ["/", "/queens", "/tango", "/zip", "/sudoku", "/patches", "/stats", "/settings"]; self.addEventListener("install", e => { e.waitUntil( caches.open(CACHE).then(c => c.addAll(SHELL)).then(() => self.skipWaiting()) ); }); 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 => { // Only handle same-origin GET requests if (e.request.method !== "GET") return; const url = new URL(e.request.url); if (url.origin !== self.location.origin) return; // Network-first for HTML pages (fresh content), cache-first for assets const isPage = e.request.mode === "navigate" || e.request.headers.get("accept")?.includes("text/html"); const isAsset = url.pathname.startsWith("/_next/static/"); if (isAsset) { // Cache-first for static assets (they're content-hashed) e.respondWith( caches.match(e.request).then(cached => cached || fetch(e.request).then(res => { if (res.ok) caches.open(CACHE).then(c => c.put(e.request, res.clone())); return res; })) ); } else if (isPage) { // Network-first for pages, fallback to cache e.respondWith( fetch(e.request) .then(res => { if (res.ok) caches.open(CACHE).then(c => c.put(e.request, res.clone())); return res; }) .catch(() => caches.match(e.request).then(cached => cached || caches.match("/"))) ); } });