Chase Hendrick
Claude Opus 5.5 (1M context)
Unroll 1.7.0: Live sheet, stream any public segment and volume and see the volume on the sheet
7391956 unverified Download sw.js from SharpMeow/unroll: direct link, hf CLI and curl.
- Browser
- Download file 5.11 kB
-
https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/sw.js
- Command line
-
hf download hf://spaces/SharpMeow/unroll/sw.js
-
curl -L -o sw.js https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/sw.js
5.11 kB
| // Unroll service worker: caches the app shell and the default scroll so an installed copy works offline. | |
| // The page registers it as sw.js?v=<version>-<sha> from version.json, so every stamped build gets fresh cache names | |
| // and the caches of older versions are deleted when the new worker activates. | |
| const VERSION = new URL(self.location.href).searchParams.get('v') || 'dev'; | |
| const SHELL = `unroll-shell-${VERSION}`; | |
| const DATA = `unroll-data-${VERSION}`; | |
| const RUNTIME = `unroll-runtime-${VERSION}`; | |
| const OURS = [SHELL, DATA, RUNTIME]; | |
| const SHELL_FILES = [ | |
| './', 'index.html', 'manifest.webmanifest', 'version.json', 'glossary.json', | |
| 'style.css', 'bench.css', 'ui.css', | |
| 'app.js', 'polish.js', 'ui.js', 'tear.js', 'damage.js', | |
| 'live.html', 'lib/livesheet.js', | |
| 'lib/scrolls.js', 'lib/manifest.js', 'lib/openown.js', 'lib/formats.js', 'lib/example.js', | |
| 'lib/bench-core.js', 'lib/bench.js', 'lib/bench-worker.js', | |
| 'lib/dragres.js', 'lib/ctwl.js', 'lib/ctsource.js', 'lib/ctvolume.js', 'lib/ctlabels.js', 'lib/ctapi.js', 'lib/api.js', 'lib/apispec.js', 'lib/embed.js', 'lib/version.js', 'api.json', | |
| 'icons/icon.svg', 'icons/icon-192.png', 'icons/icon-512.png', 'icons/maskable-512.png', 'icons/apple-touch-icon.png', | |
| ]; | |
| const THREE = 'https://cdn.jsdelivr.net/npm/three@0.160.0/'; | |
| const CDN_FILES = [ | |
| 'build/three.module.js', 'examples/jsm/controls/OrbitControls.js', | |
| 'examples/jsm/postprocessing/EffectComposer.js', 'examples/jsm/postprocessing/RenderPass.js', | |
| 'examples/jsm/postprocessing/UnrealBloomPass.js', 'examples/jsm/postprocessing/OutputPass.js', | |
| ].map((p) => THREE + p); | |
| // the default scroll: the same files index.html preloads | |
| const DATA_FILES = [ | |
| 'scrolls/index.json', 'scrolls/PHerc0139/manifest.json', | |
| 'data/scroll.json', 'data/scroll.i16p.gz', 'data/atlas_ink.webp', 'data/atlas_ct.webp', | |
| ]; | |
| async function addAll(name, urls, opts) { | |
| const c = await caches.open(name); | |
| await Promise.all(urls.map(async (u) => { | |
| try { const r = await fetch(new Request(u, opts)); if (r.ok || r.type === 'opaque') await c.put(u, r); } | |
| catch (e) { /* one missing file must not block the install; it is fetched on first use instead */ } | |
| })); | |
| } | |
| self.addEventListener('install', (e) => { | |
| e.waitUntil((async () => { | |
| const shell = await caches.open(SHELL); | |
| await shell.addAll(SHELL_FILES.map((u) => new Request(u, { cache: 'reload' }))); | |
| await addAll(SHELL, CDN_FILES, { mode: 'cors' }); | |
| await addAll(DATA, DATA_FILES, { cache: 'reload' }); | |
| await self.skipWaiting(); | |
| })()); | |
| }); | |
| self.addEventListener('activate', (e) => { | |
| e.waitUntil((async () => { | |
| for (const k of await caches.keys()) if (k.startsWith('unroll-') && !OURS.includes(k)) await caches.delete(k); | |
| await self.clients.claim(); | |
| })()); | |
| }); | |
| self.addEventListener('message', (e) => { if (e.data === 'version') e.source?.postMessage({ type: 'unroll-sw', version: VERSION }); }); | |
| const isData = (url) => /\/(data|scrolls)\//.test(url.pathname); | |
| async function fromCache(req) { | |
| // exact match only: a new release asks for file.js?v=<new>, and answering that with the old file.js?v=<old> mixes | |
| // versions on the first load after an update (an old app.js against a new index.html) | |
| return caches.match(req); | |
| } | |
| // offline fallback: any cached copy of the same path, whatever its ?v= | |
| const anyCopy = (req) => caches.match(req, { ignoreSearch: true }); | |
| self.addEventListener('fetch', (e) => { | |
| const req = e.request; | |
| if (req.method !== 'GET' || req.headers.has('range')) return; | |
| const url = new URL(req.url); | |
| const same = url.origin === self.location.origin; | |
| const cdn = url.href.startsWith(THREE) || url.hostname === 'fonts.googleapis.com' || url.hostname === 'fonts.gstatic.com'; | |
| if (!same && !cdn) return; | |
| if (req.mode === 'navigate') { | |
| // network first so a new release shows up at once; the cached page when offline | |
| e.respondWith(fetch(req).then((r) => { if (r.ok) caches.open(SHELL).then((c) => c.put('index.html', r.clone())); return r; }) | |
| .catch(async () => (await caches.match('index.html')) || (await caches.match('./')) || Response.error())); | |
| return; | |
| } | |
| if (same && isData(url)) { | |
| // scroll data never changes under one version: cache first, keep what was viewed for offline use | |
| e.respondWith(fromCache(req).then((hit) => hit || fetch(req).then((r) => { | |
| if (r.ok && r.status === 200) { const copy = r.clone(); caches.open(url.pathname.includes('/data/hi/') || !DATA_FILES.some((f) => url.pathname.endsWith(f)) ? RUNTIME : DATA).then((c) => c.put(req, copy)); } | |
| return r; | |
| }).catch(async () => (await anyCopy(req)) || Response.error()))); | |
| return; | |
| } | |
| // shell, three.js and fonts: stale while revalidate | |
| e.respondWith(fromCache(req).then((hit) => { | |
| const net = fetch(req).then((r) => { | |
| if (r.ok || r.type === 'opaque') { const copy = r.clone(); caches.open(same ? SHELL : RUNTIME).then((c) => c.put(req, copy)); } | |
| return r; | |
| }).catch(async () => hit || (await anyCopy(req)) || Response.error()); | |
| if (hit) { e.waitUntil(net.catch(() => {})); return hit; } | |
| return net; | |
| })); | |
| }); | |