diff --git a/index.html b/index.html
index dd4d42d..a3eee3c 100644
--- a/index.html
+++ b/index.html
@@ -19,6 +19,8 @@
The Nexus — Timmy's Sovereign Home
+
+
@@ -172,6 +174,12 @@
+
+
{
+ event.waitUntil(
+ caches.open(ASSET_CACHE).then((cache) => cache.addAll(CORE_ASSETS))
+ .then(() => self.skipWaiting())
+ );
+});
+
+// Activate: clean up old caches
+self.addEventListener('activate', (event) => {
+ event.waitUntil(
+ caches.keys().then((keys) =>
+ Promise.all(
+ keys
+ .filter((key) => key !== CACHE_NAME && key !== ASSET_CACHE)
+ .map((key) => caches.delete(key))
+ )
+ ).then(() => self.clients.claim())
+ );
+});
+
+self.addEventListener('fetch', (event) => {
+ const { request } = event;
+ const url = new URL(request.url);
+
+ // Network-first for API calls (Gitea / WebSocket upgrades / portals.json live data)
+ if (
+ url.pathname.startsWith('/api/') ||
+ url.hostname.includes('143.198.27.163') ||
+ request.headers.get('Upgrade') === 'websocket'
+ ) {
+ event.respondWith(networkFirst(request));
+ return;
+ }
+
+ // Cache-first for everything else (local assets + CDN)
+ event.respondWith(cacheFirst(request));
+});
+
+async function cacheFirst(request) {
+ const cached = await caches.match(request);
+ if (cached) return cached;
+
+ try {
+ const response = await fetch(request);
+ if (response.ok) {
+ const cache = await caches.open(ASSET_CACHE);
+ cache.put(request, response.clone());
+ }
+ return response;
+ } catch {
+ // Offline and not cached — return a minimal fallback for navigation
+ if (request.mode === 'navigate') {
+ const fallback = await caches.match('/index.html');
+ if (fallback) return fallback;
+ }
+ return new Response('Offline', { status: 503, statusText: 'Service Unavailable' });
+ }
+}
+
+async function networkFirst(request) {
+ try {
+ const response = await fetch(request);
+ if (response.ok) {
+ const cache = await caches.open(CACHE_NAME);
+ cache.put(request, response.clone());
+ }
+ return response;
+ } catch {
+ const cached = await caches.match(request);
+ return cached || new Response('Offline', { status: 503, statusText: 'Service Unavailable' });
+ }
+}