const CACHE = 'birdoogle-v1'; const STATIC = [ '/', '/index.html', '/manifest.json', '/icons/icon-192.png', '/icons/icon-512.png', 'https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Lato:wght@400;700&display=swap', 'https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js', 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js', 'https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.2/babel.min.js' ]; // Install — cache all static assets self.addEventListener('install', e => { e.waitUntil( caches.open(CACHE).then(cache => cache.addAll(STATIC)).then(() => self.skipWaiting()) ); }); // Activate — clean up old caches 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()) ); }); // Fetch — cache-first for static, network-first for API calls self.addEventListener('fetch', e => { const url = new URL(e.request.url); // Never intercept Anthropic API calls if (url.hostname === 'api.anthropic.com') return; // Network-first for Google Fonts (so updates come through) if (url.hostname === 'fonts.googleapis.com' || url.hostname === 'fonts.gstatic.com') { e.respondWith( fetch(e.request).catch(() => caches.match(e.request)) ); return; } // Cache-first for everything else e.respondWith( caches.match(e.request).then(cached => { if (cached) return cached; return fetch(e.request).then(res => { if (res && res.status === 200 && res.type !== 'opaque') { const clone = res.clone(); caches.open(CACHE).then(cache => cache.put(e.request, clone)); } return res; }).catch(() => { // Offline fallback for navigation requests if (e.request.mode === 'navigate') return caches.match('/index.html'); }); }) ); });