diff --git a/public/manifest.json b/public/manifest.json index 9962870..448ffe8 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -9,41 +9,53 @@ "orientation": "any", "icons": [ { - "purpose": "maskable", + "purpose": "any", "sizes": "48x48", "src": "/icons/maskable_icon_x48.png", "type": "image/png" }, { - "purpose": "maskable", + "purpose": "any", "sizes": "72x72", "src": "/icons/maskable_icon_x72.png", "type": "image/png" }, { - "purpose": "maskable", + "purpose": "any", "sizes": "96x96", "src": "/icons/maskable_icon_x96.png", "type": "image/png" }, { - "purpose": "maskable", + "purpose": "any", "sizes": "128x128", "src": "/icons/maskable_icon_x128.png", "type": "image/png" }, { - "purpose": "maskable", + "purpose": "any", "sizes": "192x192", "src": "/icons/maskable_icon_x192.png", "type": "image/png" }, { - "purpose": "maskable", + "purpose": "any", "sizes": "384x384", "src": "/icons/maskable_icon_x384.png", "type": "image/png" }, + { + "purpose": "any", + "sizes": "512x512", + "src": "/icons/maskable_icon_x512.png", + "type": "image/png" + }, + { + "purpose": "maskable", + "sizes": "192x192", + "src": "/icons/maskable_icon_x192.png", + "type": "image/png" + }, { "purpose": "maskable", "sizes": "512x512", diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..3fa0429 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,60 @@ +/** + * Minimal Service Worker for PWA installability + * This is required for the app to be installable as a PWA + */ + +const CACHE_NAME = 'framed-v1'; + +/** + * Install event - cache essential resources + */ +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => { + return cache.addAll([ + '/', + '/index.html' + ]); + }) + ); + self.skipWaiting(); +}); + +/** + * Activate event - clean up old caches + */ +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames + .filter((name) => name !== CACHE_NAME) + .map((name) => caches.delete(name)) + ); + }) + ); + self.clients.claim(); +}); + +/** + * Fetch event - network first, fallback to cache + */ +self.addEventListener('fetch', (event) => { + event.respondWith( + fetch(event.request) + .then((response) => { + // Clone and cache successful responses + if (response.status === 200) { + const responseClone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseClone); + }); + } + return response; + }) + .catch(() => { + // If network fails, try cache + return caches.match(event.request); + }) + ); +}); diff --git a/src/main.js b/src/main.js index 84fa500..37be519 100644 --- a/src/main.js +++ b/src/main.js @@ -10,3 +10,16 @@ import './assets/styles/main.css'; const app = createApp(App); app.use(VueKonva); app.mount('#app'); + +/** + * Register service worker for PWA support + */ +if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker + .register('/sw.js') + .catch((error) => { + console.error('Service Worker registration failed:', error); + }); + }); +}