diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index f1e113f..f46ce2d 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1243,7 +1243,16 @@ browser-language detection — crawlers render with arbitrary languages). The fa mock build. The worker intercepts only top-level navigations (network-first with a cached-shell fallback), leaving `/assets/*` and the Connect stream untouched; it exists to satisfy Chromium's installability requirement (a registered SW, needed for install on Android) and is the single -growth point for a future opt-in offline mode. The gateway registers the `.webmanifest` MIME type +growth point for the opt-in **offline mode** (in progress): a deliberate, device-scoped Settings +toggle — distinct from the transient gateway-reachability signal — that tints the header blue with +an *Offline* chip and confines play to on-device `vs_ai` games. To have data ready before the +switch, the **Profile advertises the current dictionary version per variant** (`dict_versions`, +filled from the registry on the existing cold-start profile request — no extra round-trip), and an +eligible installed PWA (standalone web + confirmed email) **background-preloads** those dictionaries +— on lobby entry and on a variant-preference change — through the same three-tier loader, retried +with backoff and honouring the session miss-breaker; the move generator, the loader and the preload +orchestration stay in lazy chunks. A first-lobby preload failure shows a *poor-connection* notice in +the ad-banner slot. The gateway registers the `.webmanifest` MIME type in-process (the distroless image has no `/etc/mime.types`). Hash-named `/assets/*` are served `immutable` (a relaunch is a cache hit, not a re-download); the HTML shells are `no-cache` so a new deploy is picked up — both containers apply the same caching. An diff --git a/ui/scripts/bundle-size.mjs b/ui/scripts/bundle-size.mjs index 0403740..2cf27c9 100644 --- a/ui/scripts/bundle-size.mjs +++ b/ui/scripts/bundle-size.mjs @@ -20,10 +20,12 @@ import { join } from 'node:path'; const DIST = 'dist'; // Per-chunk gzip budgets in KB. The app entry was raised to 110 for the local move-preview -// wiring, then to 112 for the PWA install feature (the install CTA + the pwa detection / -// service-worker registration live in the app entry; the heavy dict subsystem stays in lazy -// chunks). Its scoped CSS lands in the CSS chunk, not this JS budget. -const BUDGET = { app: 112, shared: 30, landing: 5 }; +// wiring, to 112 for the PWA install feature, then to 113 for the offline-mode wiring: the +// dictionary-preload trigger and the offline-state reactives live in the app entry (the Header +// reads them, the lobby/profile screens fire the trigger), while the heavy parts — the dict +// loader, the move generator and the preload orchestration — stay in lazy chunks. Scoped CSS +// lands in the CSS chunk, not this JS budget. +const BUDGET = { app: 113, shared: 30, landing: 5 }; // gzipped returns the gzipped byte size of a built asset, or 0 when the reference is not a // local file (e.g. the Telegram SDK loaded from a CDN) or is missing. diff --git a/ui/src/components/Header.svelte b/ui/src/components/Header.svelte index 6e14256..3e385f2 100644 --- a/ui/src/components/Header.svelte +++ b/ui/src/components/Header.svelte @@ -1,7 +1,7 @@