Files
rehlert f09384d2e6 pwa / offline mode
- @vite-pwa/sveltekit: workbox precache (incl. pdfjs worker), generated manifest, prompt-style updates via PwaUpdatePrompt toast
- prerendered routes for offline navigation, PWA icons, theme-color + apple-touch-icon
- pwa offline/update e2e suite (pnpm test:pwa, playwright.pwa.config.ts)
- datenschutz: offline/install section; prettier cleanup of shadcn components
2026-10-04 12:55:46 +02:00

5.9 KiB
Raw Permalink Blame History

Plan: PWA / Offline-Fähigkeit

Goal

The app already processes everything locally — but the browser still has to fetch the app shell from the server on every load. Making beA-Edit an installable, offline-capable PWA reinforces the privacy story ("works without internet after first load"), enables home-screen/desktop installation, and is essentially free given there are no network calls to cache-around.


Open decisions (defaults marked ✅)

D1 — Implementation approach

  • ✅ A. @vite-pwa/sveltekit (new dev dependency). Mature integration: precaching of the built assets via Workbox, generated manifest, virtual RegisterSW module, works with adapter-node builds. The dependency is justified (agents.md allows deps when the task cannot reasonably be done without) — hand-rolling a correct service worker + update flow is error-prone.
  • B. Hand-written minimal sw.js in static/ + manual manifest. Zero deps but custom update logic, manual asset hashing, more long-term maintenance.

D2 — Update UX

  • ✅ A. Auto-update with a toast. New versions activate on next navigation; a small toast "Neue Version verfügbar – Seite neu laden" appears when an update is waiting (Workbox onNeedRefresh/onOfflineReady hooks). Never force-reload mid-work (users may have unsaved archive state).
  • B. Silent auto-reload. Rejected: could interrupt an in-progress merge.
  • ✅ Review required, not auto-edited. A service worker + install prompt is client-side storage/behavior; datenschutz/+page.svelte should likely gain a sentence. Flag for the user; do not rewrite legal copy unilaterally (project constraint).

Technical approach

Vite config

// vite.config.ts
import { SvelteKitPWA } from '@vite-pwa/sveltekit';

export default defineConfig({
	plugins: [
		sveltekit(),
		tailwindcss(),
		SvelteKitPWA({
			registerType: 'prompt',
			manifest: {
				name: 'beA-Edit',
				short_name: 'beA-Edit',
				description: 'PDF-Werkzeuge für die beA-Kanzlei – 100 % lokal im Browser.',
				lang: 'de',
				start_url: '/',
				display: 'standalone',
				background_color: '#f2f5f8', // align with theme background token
				theme_color: '#…', // align with primary token
				icons: [
					/* 192 + 512 + maskable, derived from static logo */
				]
			},
			workbox: {
				globPatterns: ['**/*.{js,css,html,svg,woff2}'],
				navigateFallback: '/' // SPA-style offline fallback for app routes
			},
			devOptions: { enabled: false }
		})
	]
});

Notes:

  • pdfjs worker chunk (pdf.worker.mjs?url) is part of the built assets → precached by glob.
  • Legal routes (/impressum, /datenschutz) precache like any route; offline navigation works via the fallback.
  • No runtime caching strategies needed: the app makes no network requests by design.

Registration UI

  • Root +layout.svelte: import the virtual virtual:pwa-register module (SSR-guarded), wire onNeedRefresh → toast state; render a small dismissible toast component (build with existing card/button styling; no new UI dep) with "Neu laden" / "Später".
  • onOfflineReady → optional one-time hint "beA-Edit funktioniert jetzt offline." (keep subtle).
  • Install prompt: rely on the browser's native install affordance (manifest makes it appear); optionally an "Installieren" hint on the start page later — out of scope for v1.

Icons

  • Generate 192/512/maskable PNGs from the existing logo (static/ logo asset) once, committed to static/icons/. Use an offline tool/imagemagick during implementation; no runtime dep.

Docker / adapter-node

  • Service worker + manifest are static build output → served by node build/index.js without changes. Verify Cache-Control headers don't block SW updates (adapter-node defaults are fine; the SW itself must not be cached aggressively — Vite PWA emits the right filenames).

Files changed

  • vite.config.ts — SvelteKitPWA plugin + manifest.
  • package.json / lockfile — @vite-pwa/sveltekit dev dependency (via pnpm only).
  • src/routes/+layout.svelte — registration + update toast.
  • static/icons/* — generated icons; static/manifest.webmanifest handled by plugin.
  • src/app.html — theme-color meta if plugin doesn't inject.
  • agents.md — document PWA setup + update behavior.
  • datenschutz/+page.svelte — only after explicit user approval (D3).

Implementation phases

  1. Add dependency, configure plugin + manifest + icons.
  2. Registration + update toast in root layout.
  3. Build + pnpm preview: verify / loads, then devtools → Application → service worker registered; toggle "Offline" → reload → app still works.
  4. Production image smoke test (Docker build, offline reload).
  5. Update agents.md; flag datenschutz review.

Verification

  • Fresh load registers the SW (devtools), onOfflineReady path reachable.
  • Offline reload after precache: start page + all tool routes render; dropping a ZIP still works fully (entire processing chain offline).
  • New build deployed → toast offers reload; choosing it loads the new version; declining does not interrupt current state.
  • No console errors on iOS Safari (manifest parsing quirks) — best-effort check.
  • pnpm check, pnpm lint, pnpm build pass.

Risks

  • Stale cache confusion during development: keep devOptions.enabled: false; document in agents.md that preview/production test the SW, not dev mode.
  • Large precache manifest: the pdfjs worker + fonts add a few MB to the first precache — acceptable for an installable tool; monitor build output stats.
  • SW + SvelteKit hydration edge cases on first offline visit are known upstream; pin a current stable plugin version and test the exact offline-reload sequence.

Out of scope

  • Background sync (nothing to sync — no network by design).
  • App-store packaging.
  • Custom install button UI beyond the native affordance.