mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
- Persistence: settings, shortcuts, recent projects, imported media, saved projects and auto-saves live in OPFS (IndexedDB fallback) through an in-memory mirror flushed in the background (apps/effectcraft-web/src/store.rs, persist.rs); ConfigStore gains FileOps so auto-save / recovery work in the browser store; the session snapshot restores the last project + editor state on reload; File > Open Recent works from storage. - Audio: Web Audio output (AudioWorklet, ScriptProcessor fallback) fed by AudioPlayback::pump on wasm32; A/V sync from AudioContext.currentTime; meters; unlock on first gesture. - Background jobs: engine::offload (WorkerRequest/WorkerReply protocol, Offload trait, run_request); Render Queue, tracker, mask tracker, Warp Stabilizer and Roto Freeze run in Web Workers (a second engine instance on the shared compiled module), progress streamed back; cancel terminates the worker. - GPU: WebGPU viewer frames drawn straight from the compositor's texture (gpu_display on wasm32 when the viewer draws plainly); async readback (Gpu::read_display_async) for pixels. - PWA: manifest, service worker precaching the build (xtask fills list/version), icons copied from assets/app-icon; COOP/COEP kept from cached responses; offline reload works. - window.effectcraft: saveToBrowser, listStored, removeStored, flush; info() reports storage, audio, gpu, workers. - Tests: native store + protocol + offload (render, track, cancel) tests; smoke.mjs extended (GPU path, worker render responsiveness, audio gesture + playback, reload persistence, Open Recent, offline reload). Co-Authored-By: Claude Opus 5.5 <[email protected]>
46 lines
1.7 KiB
JavaScript
46 lines
1.7 KiB
JavaScript
// EffectCraft service worker: the app works offline once loaded, and installs as an app.
|
|
//
|
|
// `cargo xtask web` fills in the build's file list and version. Every file is precached into a
|
|
// cache named after the version, served cache-first (responses keep the server's headers, so
|
|
// the page stays cross-origin isolated: COOP/COEP), and old versions are dropped once the new
|
|
// one activates. Anything else same-origin is fetched from the network and cached for next time.
|
|
const VERSION = "__EC_VERSION__";
|
|
const FILES = __EC_FILES__;
|
|
const CACHE = `effectcraft-${VERSION}`;
|
|
|
|
self.addEventListener("install", (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.open(CACHE)
|
|
.then((c) => c.addAll(FILES.map((f) => new Request(f, { cache: "reload" }))))
|
|
.then(() => self.skipWaiting()),
|
|
);
|
|
});
|
|
|
|
self.addEventListener("activate", (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.keys()
|
|
.then((keys) => Promise.all(keys.filter((k) => k.startsWith("effectcraft-") && k !== CACHE).map((k) => caches.delete(k))))
|
|
.then(() => self.clients.claim()),
|
|
);
|
|
});
|
|
|
|
self.addEventListener("fetch", (event) => {
|
|
const req = event.request;
|
|
const url = new URL(req.url);
|
|
if (req.method !== "GET" || url.origin !== self.location.origin) return;
|
|
event.respondWith(
|
|
(async () => {
|
|
const cache = await caches.open(CACHE);
|
|
// The page itself regardless of its query (?empty, ?demo…).
|
|
const key = req.mode === "navigate" ? new URL("./", self.registration.scope).href : req;
|
|
const hit = await cache.match(key, { ignoreSearch: req.mode === "navigate" });
|
|
if (hit) return hit;
|
|
const res = await fetch(req);
|
|
if (res.ok && res.type === "basic") cache.put(req, res.clone());
|
|
return res;
|
|
})(),
|
|
);
|
|
});
|