commit 3adf094bdf2130b4f8276069eecc0fa9b8cbb9e2
parent 563de227050e169948ce7d3510a43c967d3e912f
Author: Hunter
Date:   Fri, 11 Jul 2025 15:57:22 -0400

implement PWA support

Diffstat:
Aimages/rollerskate.png | 0
Mindex.html | 14++++++++++++++
Amanifest.json | 23+++++++++++++++++++++++
Asw.js | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 135 insertions(+), 0 deletions(-)

diff --git a/images/rollerskate.png b/images/rollerskate.png Binary files differ. diff --git a/index.html b/index.html @@ -5,6 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Web Workshop</title> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛼</text></svg>"> + <link rel="manifest" href="manifest.json"> <script type="module"> import {EditorView, keymap, placeholder} from "https://esm.sh/@codemirror/view@6" import {EditorState} from "https://esm.sh/@codemirror/state@6" @@ -289,6 +290,19 @@ // Initialize when page loads initializeCodeMirror(); + + // Register service worker + if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('/sw.js') + .then(registration => { + console.log('SW registered: ', registration); + }) + .catch(registrationError => { + console.log('SW registration failed: ', registrationError); + }); + }); + } </script> </body> </html> \ No newline at end of file diff --git a/manifest.json b/manifest.json @@ -0,0 +1,22 @@ +{ + "name": "Web Workshop", + "short_name": "WW", + "description": "HTML editor and live preview tool", + "start_url": "/", + "display": "standalone", + "background_color": "#16181b", + "theme_color": "#16181b", + "orientation": "any", + "icons": [ + { + "src": "images/rollerskate.png", + "sizes": "160x160", + "type": "image/png" + }, + { + "src": "images/rollerskate.png", + "sizes": "192x192", + "type": "image/png" + } + ] +} +\ No newline at end of file diff --git a/sw.js b/sw.js @@ -0,0 +1,97 @@ +const CACHE_NAME = 'web-workshop-v1'; +const urlsToCache = [ + '/', + '/index.html', + '/manifest.json' +]; + +// Function to discover and cache all files in directories +async function cacheDirectoryFiles(cache, directories) { + for (const dir of directories) { + try { + // Try to fetch directory listing + const response = await fetch(dir); + if (response.ok) { + const html = await response.text(); + // Extract file links from directory listing + const links = html.match(/href="([^"]*\.(png|jpg|jpeg|gif|svg|webp|html|css|js|json|txt|md))"/gi); + if (links) { + const files = links.map(link => { + const match = link.match(/href="([^"]*)"/); + return match ? dir + match[1] : null; + }).filter(Boolean); + + // Cache each file + for (const file of files) { + try { + await cache.add(file); + } catch (e) { + console.log(`Failed to cache ${file}:`, e); + } + } + } + } + } catch (e) { + console.log(`Failed to cache directory ${dir}:`, e); + } + } +} + +// Install service worker and cache resources +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(async cache => { + // Cache core files + await cache.addAll(urlsToCache); + // Cache directory files + await cacheDirectoryFiles(cache, ['/images/', '/pages/']); + }) + .then(() => self.skipWaiting()) + ); +}); + +// Activate service worker and clean up old caches +self.addEventListener('activate', event => { + event.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== CACHE_NAME) { + return caches.delete(cacheName); + } + }) + ); + }).then(() => self.clients.claim()) + ); +}); + +// Fetch strategy: Network first, then cache (for updates when online) +self.addEventListener('fetch', event => { + event.respondWith( + fetch(event.request) + .then(response => { + // If we got a response, add it to the cache + if (response.status === 200) { + const responseClone = response.clone(); + caches.open(CACHE_NAME) + .then(cache => cache.put(event.request, responseClone)); + } + return response; + }) + .catch(() => { + // Network failed, try cache + return caches.match(event.request) + .then(response => { + if (response) { + return response; + } + // If not in cache and it's a navigation request, serve index.html + if (event.request.mode === 'navigate') { + return caches.match('/index.html'); + } + throw new Error('No cached version available'); + }); + }) + ); +}); +\ No newline at end of file