From 24eb0c4e49d4b706030fc1c875a777a765f41529 Mon Sep 17 00:00:00 2001 From: Julien Maille Date: Tue, 23 Dec 2025 15:46:13 +0100 Subject: [PATCH 1/3] feat(pwa): implement image caching and expand precache list in service worker --- sw.js | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/sw.js b/sw.js index f91848d..74c5843 100644 --- a/sw.js +++ b/sw.js @@ -1,5 +1,6 @@ //sw.js -const CACHE_NAME = 'monochrome-v1'; +const CACHE_NAME = 'monochrome-v3'; +const IMAGE_CACHE_NAME = 'monochrome-images-v1'; const urlsToCache = [ '/', '/index.html', @@ -11,10 +12,20 @@ const urlsToCache = [ '/js/ui.js', '/js/utils.js', '/js/cache.js', - '/manifest.json' + '/js/router.js', + '/js/events.js', + '/js/ui-interactions.js', + '/js/settings.js', + '/js/lastfm.js', + '/js/lyrics.js', + '/js/downloads.js', + '/manifest.json', + '/assets/logo.svg', + '/assets/appicon.png' ]; self.addEventListener('install', event => { + self.skipWaiting(); event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(urlsToCache)) @@ -22,14 +33,40 @@ self.addEventListener('install', event => { }); self.addEventListener('fetch', event => { + const url = new URL(event.request.url); + + // Cache Images (Cache-First) + if (url.hostname === 'resources.tidal.com' || url.hostname === 'picsum.photos') { + event.respondWith( + caches.open(IMAGE_CACHE_NAME).then(cache => { + return cache.match(event.request).then(response => { + return response || fetch(event.request).then(networkResponse => { + if (networkResponse.ok) { + cache.put(event.request, networkResponse.clone()); + } + return networkResponse; + }); + }); + }) + ); + return; + } + + // Static Assets & App Shell (Cache-First) event.respondWith( caches.match(event.request) - .then(response => response || fetch(event.request)) + .then(response => { + // Return cached response if found + if (response) return response; + + // Otherwise fetch from network + return fetch(event.request); + }) ); }); self.addEventListener('activate', event => { - const cacheWhitelist = [CACHE_NAME]; + const cacheWhitelist = [CACHE_NAME, IMAGE_CACHE_NAME]; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( @@ -38,7 +75,7 @@ self.addEventListener('activate', event => { return caches.delete(cacheName); } }) - ); + ).then(() => self.clients.claim()); }) ); }); \ No newline at end of file From f123efabdb0c8da2c6e3eb5f53166ec46e7e54b8 Mon Sep 17 00:00:00 2001 From: Julien Maille Date: Tue, 23 Dec 2025 15:47:16 +0100 Subject: [PATCH 2/3] feat(pwa): implement window controls overlay for desktop --- manifest.json | 3 +++ styles.css | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/manifest.json b/manifest.json index 5453afc..f7588b4 100644 --- a/manifest.json +++ b/manifest.json @@ -4,6 +4,9 @@ "description": "A minimalist music streaming application", "start_url": "/", "display": "standalone", + "display_override": [ + "window-controls-overlay" + ], "background_color": "#000000", "theme_color": "#000000", "orientation": "portrait-primary", diff --git a/styles.css b/styles.css index bf91bcd..347e5d1 100644 --- a/styles.css +++ b/styles.css @@ -2812,3 +2812,22 @@ input:checked + .slider::before { font-size: 4rem; } } + +/* Window Controls Overlay */ +@media (display-mode: window-controls-overlay) { + .app-container { + margin-top: env(titlebar-area-height, 0); + } + + .main-header { + -webkit-app-region: drag; + } + + .main-header * { + -webkit-app-region: no-drag; + } + + .sidebar { + padding-top: max(1.5rem, env(titlebar-area-height, 0)); + } +} From 97a932fc0d1cbe163f999a981837b6326a5e6bf3 Mon Sep 17 00:00:00 2001 From: Julien Maille Date: Tue, 23 Dec 2025 16:12:04 +0100 Subject: [PATCH 3/3] fix(pwa): stop repeating install prompt after dismissal and bump sw version --- js/app.js | 5 ++++- sw.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/js/app.js b/js/app.js index 8119b5f..9f65dcf 100644 --- a/js/app.js +++ b/js/app.js @@ -545,7 +545,9 @@ document.addEventListener('DOMContentLoaded', async () => { window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; - showInstallPrompt(deferredPrompt); + if (!localStorage.getItem('installPromptDismissed')) { + showInstallPrompt(deferredPrompt); + } }); if (!localStorage.getItem('shortcuts-shown')) { @@ -596,6 +598,7 @@ function showInstallPrompt(deferredPrompt) { document.getElementById('dismiss-install').addEventListener('click', () => { notification.remove(); + localStorage.setItem('installPromptDismissed', 'true'); }); } diff --git a/sw.js b/sw.js index 74c5843..f4882e1 100644 --- a/sw.js +++ b/sw.js @@ -1,5 +1,5 @@ //sw.js -const CACHE_NAME = 'monochrome-v3'; +const CACHE_NAME = 'monochrome-v4'; const IMAGE_CACHE_NAME = 'monochrome-images-v1'; const urlsToCache = [ '/',