diff --git a/.gitignore b/.gitignore index f5bfd943..ddfa4732 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ dist node_modules +serviceworker.js + package-lock.json /*.js diff --git a/gulpfile.esm.js b/gulpfile.esm.js index ed814d35..8ec0ea11 100644 --- a/gulpfile.esm.js +++ b/gulpfile.esm.js @@ -80,6 +80,9 @@ const buildweb = gulp.parallel( '!./node_modules/emglken/build/bocfel-core.wasm', './node_modules/jquery/dist/jquery.min.js', 'src/upstream/glkote/waiting.gif', + 'src/web/manifest.json', + 'src/web/*.png', + 'src/web/*.svg', ], target: 'web', }), diff --git a/index.html b/index.html index 8b83df82..6f30c053 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,21 @@ + +
diff --git a/src/web/icon-192.png b/src/web/icon-192.png new file mode 100644 index 00000000..c3230e64 Binary files /dev/null and b/src/web/icon-192.png differ diff --git a/src/web/icon-512.png b/src/web/icon-512.png new file mode 100644 index 00000000..1aa9fe22 Binary files /dev/null and b/src/web/icon-512.png differ diff --git a/src/web/icon.svg b/src/web/icon.svg new file mode 100644 index 00000000..43467170 --- /dev/null +++ b/src/web/icon.svg @@ -0,0 +1,555 @@ + + + diff --git a/src/web/manifest.json b/src/web/manifest.json new file mode 100644 index 00000000..83fea3fe --- /dev/null +++ b/src/web/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "Parchment", + "short_name": "Parchment", + "icons": [ + { + "src": "icon-192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "icon-512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "../../index.html", + "display": "standalone", + "background_color": "#000000", + "theme_color": "#000000" +} \ No newline at end of file diff --git a/tools/generate-service-worker.mjs b/tools/generate-service-worker.mjs new file mode 100755 index 00000000..e5711433 --- /dev/null +++ b/tools/generate-service-worker.mjs @@ -0,0 +1,57 @@ +#!/usr/bin/env node + +import {readdir, writeFile} from 'fs/promises'; + +const cacheKey = Date.now(); +const files = await readdir('dist/web'); + +const code = ` +const CACHE_NAME="${cacheKey}"; +const urls = [".", ${files.map(file => `"dist/web/${file}"`).join(',')}]; + +self.addEventListener('install', function(event) { + event.waitUntil( + caches.open(CACHE_NAME) + .then(function(cache) { + return cache.addAll(urls); + }).then(() => console.log("[ServiceWorker] installed, possibly waiting")) + ); +}); + +self.addEventListener('fetch', function(event) { + event.respondWith((async () => { + if (event.request.mode === "navigate" && + event.request.method === "GET" && + registration.waiting && + (await clients.matchAll()).length < 2 + ) { + registration.waiting.postMessage('skipWaiting'); + console.log("[ServiceWorker] refreshing and skipping waiting"); + return new Response("", {headers: {"Refresh": "0"}}); + } + return await caches.match(event.request) || + fetch(event.request); + })()); +}); + +self.addEventListener('activate', function(e) { + console.log('[ServiceWorker] Activate'); + e.waitUntil( + caches.keys().then(keyList => Promise.all(keyList.map(key => { + if (key !== CACHE_NAME) { + console.log("[ServiceWorker] deleting", key); + return caches.delete(key); + } + }))) + ); +}); + +self.addEventListener('message', event => { + if (event.data === 'skipWaiting') { + console.log("[ServiceWorker]", CACHE_NAME, 'skipWaiting'); + skipWaiting(); + } +}); +`; + +await writeFile('serviceworker.js', code, 'utf8');