commit 3adf094bdf2130b4f8276069eecc0fa9b8cbb9e2
parent 563de227050e169948ce7d3510a43c967d3e912f
Author: Hunter
Date: Fri, 11 Jul 2025 15:57:22 -0400
implement PWA support
Diffstat:
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