From e3adfc212643af576c498e86cc33055ee46abfd4 Mon Sep 17 00:00:00 2001 From: fermdez Date: Fri, 3 Oct 2025 00:05:21 +0200 Subject: [PATCH] =?UTF-8?q?A=C3=B1adidos=20manifiestos=20para=20PWA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.webmanifest | 21 ++++++++++++ sw.js | 77 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 manifest.webmanifest create mode 100644 sw.js diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000..a02edfe --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "FerMdez - Juegos", + "short_name": "Juegos", + "start_url": "./", + "scope": "./", + "display": "standalone", + "background_color": "#e9defa", + "theme_color": "#3d5afe", + "description": "Colección de mini juegos online de Fernando Méndez.", + "icons": [ + { + "src": "media/favicon.ico", + "sizes": "192x192", + "type": "image/x-icon", + "purpose": "maskable any" + } + ], + "categories": ["games", "education", "entertainment"], + "lang": "es-ES", + "prefer_related_applications": false +} \ No newline at end of file diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..15425fd --- /dev/null +++ b/sw.js @@ -0,0 +1,77 @@ +const CACHE_VERSION = 'v1'; +const CACHE_NAME = `fermdez-games-${CACHE_VERSION}`; +const CORE_ASSETS = [ + '/', + '/index.html', + '/media/favicon.ico', + '/manifest.webmanifest' +]; + +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => cache.addAll(CORE_ASSETS)) + .then(() => self.skipWaiting()) + ); +}); + +self.addEventListener('activate', event => { + event.waitUntil( + (async () => { + const keys = await caches.keys(); + await Promise.all(keys.map(k => k !== CACHE_NAME && caches.delete(k))); + await self.clients.claim(); + })() + ); +}); + +const cacheFirst = async (req) => { + const cache = await caches.open(CACHE_NAME); + const cached = await cache.match(req); + if (cached) return cached; + const resp = await fetch(req); + if (resp && resp.status === 200 && req.method === 'GET') { + cache.put(req, resp.clone()); + } + return resp; +}; + +const networkFirst = async (req) => { + const cache = await caches.open(CACHE_NAME); + try { + const resp = await fetch(req); + if (resp && resp.status === 200) { + cache.put(req, resp.clone()); + } + return resp; + } catch (err) { + const cached = await cache.match(req); + if (cached) return cached; + if (req.mode === 'navigate') { + return cache.match('/index.html'); + } + throw err; + } +}; + +self.addEventListener('fetch', event => { + const { request } = event; + if (request.method !== 'GET') return; + const accept = request.headers.get('accept') || ''; + const url = new URL(request.url); + const isSameOrigin = url.origin === self.location.origin; + const isHTML = accept.includes('text/html'); + const isStaticAsset = /\.(?:css|js|png|jpg|jpeg|svg|ico|webmanifest|mp3|wav)$/.test(url.pathname); + if (isHTML) { + event.respondWith(networkFirst(request)); + } else if (isSameOrigin && isStaticAsset) { + event.respondWith(cacheFirst(request)); + } +}); + +// Optional: handle message to trigger skipWaiting from client +self.addEventListener('message', event => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); \ No newline at end of file