From 4f96cd9c1d414b775a5469517e317dc5a3b07bd0 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Tue, 24 Mar 2026 00:31:11 -0400 Subject: [PATCH 1/2] feat: add service worker for offline capability - Create sw.js with cache-first strategy for static assets and CDN files - Precaches index.html, app.js, style.css, manifest.json, ws-client.js, Three.js module - Network-first strategy for /api/ routes to keep live data fresh - Cleans up stale caches on activation - Register sw.js in index.html via inline script Fixes #212 Co-Authored-By: Claude Sonnet 4.6 --- index.html | 5 ++++ sw.js | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 sw.js diff --git a/index.html b/index.html index 795f24e..21efab9 100644 --- a/index.html +++ b/index.html @@ -48,6 +48,11 @@
⚡ SOVEREIGNTY ⚡
+
diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..47e3b9b --- /dev/null +++ b/sw.js @@ -0,0 +1,88 @@ +// The Nexus — Service Worker +// Cache-first for assets, network-first for API calls + +const CACHE_NAME = 'nexus-v1'; +const ASSET_CACHE = 'nexus-assets-v1'; + +const PRECACHE_ASSETS = [ + '/', + '/index.html', + '/app.js', + '/style.css', + '/manifest.json', + '/ws-client.js', + 'https://unpkg.com/three@0.183.0/build/three.module.js', +]; + +// Install: precache core assets +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(ASSET_CACHE).then((cache) => cache.addAll(PRECACHE_ASSETS)) + ); + 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)) + ) + ) + ); + 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/') || + 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' }); + } +} -- 2.43.0 From 333214ae7c9a5c7ea1628bb78390d3ed06ecf454 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Tue, 24 Mar 2026 00:31:42 -0400 Subject: [PATCH 2/2] feat: add service worker for offline capability - Create sw.js with cache-first strategy for static assets and CDN - Network-first strategy for API calls with offline fallback - Pre-cache core assets on install: index.html, app.js, style.css, Three.js - Register service worker in index.html Fixes #212 Co-Authored-By: Claude Sonnet 4.6 --- sw.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sw.js b/sw.js index 47e3b9b..551584b 100644 --- a/sw.js +++ b/sw.js @@ -4,7 +4,7 @@ const CACHE_NAME = 'nexus-v1'; const ASSET_CACHE = 'nexus-assets-v1'; -const PRECACHE_ASSETS = [ +const CORE_ASSETS = [ '/', '/index.html', '/app.js', @@ -12,14 +12,21 @@ const PRECACHE_ASSETS = [ '/manifest.json', '/ws-client.js', 'https://unpkg.com/three@0.183.0/build/three.module.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/controls/OrbitControls.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/postprocessing/EffectComposer.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/postprocessing/RenderPass.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/postprocessing/UnrealBloomPass.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/postprocessing/ShaderPass.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/shaders/CopyShader.js', + 'https://unpkg.com/three@0.183.0/examples/jsm/shaders/LuminosityHighPassShader.js', ]; // Install: precache core assets self.addEventListener('install', (event) => { event.waitUntil( - caches.open(ASSET_CACHE).then((cache) => cache.addAll(PRECACHE_ASSETS)) + caches.open(ASSET_CACHE).then((cache) => cache.addAll(CORE_ASSETS)) + .then(() => self.skipWaiting()) ); - self.skipWaiting(); }); // Activate: clean up old caches @@ -31,9 +38,8 @@ self.addEventListener('activate', (event) => { .filter((key) => key !== CACHE_NAME && key !== ASSET_CACHE) .map((key) => caches.delete(key)) ) - ) + ).then(() => self.clients.claim()) ); - self.clients.claim(); }); self.addEventListener('fetch', (event) => { @@ -43,6 +49,7 @@ self.addEventListener('fetch', (event) => { // 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)); @@ -56,6 +63,7 @@ self.addEventListener('fetch', (event) => { async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; + try { const response = await fetch(request); if (response.ok) { -- 2.43.0