feat(app): add PWA support with service worker and update prompt#31279
feat(app): add PWA support with service worker and update prompt#31279shoootyou wants to merge 32 commits into
Conversation
|
The following comment was made by an LLM, it may be inaccurate: Potential related PRs found:
These are not duplicates of PR #31279 but represent overlapping work on the same system (PWA, service workers, caching). The PR author is already aware of the #30399 overlap and has offered to coordinate. |
|
Tested locally Screen.Recording.2026-06-07.at.22.14.49.mov |
Adds full PWA support to packages/app/. - Service worker via vite-plugin-pwa@1.3.0 with registerType: 'prompt' - PwaUpdatePrompt component — non-blocking update banner - Full site.webmanifest with all required fields and screenshots - iOS meta tags + apple-touch-icon for iPhone, iPad, iPad Pro - navigateFallbackAllowlist — only SPA routes get the SW fallback - Cache-Control headers (immutable on assets, no-cache on root) - crossorigin=use-credentials on manifest link for auth-proxy compat - OG meta tags + screenshot labels - Tests: B1-B9 component tests, manifest field tests Upstream PR: anomalyco#31279 Upstream issues: anomalyco#19174, anomalyco#19119, anomalyco#27933, anomalyco#27931, anomalyco#30405, anomalyco#19301 Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
f8c8092 to
2d32334
Compare
- install vite-plugin-pwa@1.3.0 and workbox-window - configure VitePWA with prompt registration and NetworkOnly for localhost:4096 - implement PwaUpdatePrompt component (B1-B8) using SolidJS programmatic API - mount PwaUpdatePrompt outside provider tree in entry.tsx - add vite-plugin-pwa/solid types to tsconfig - add solid-web-browser-shim.ts preload to fix bun test JSX resolution (bun test uses node condition for solid-js; shim redirects to browser builds and provides React.createElement shim for JSX compat) - update bunfig.toml to load solid-web-browser-shim.ts in test preload Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
- set id: '/' to stabilize app identity across domains - set start_url: '/' to fix Lighthouse 'start_url is not valid' error Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
- resolve from src/ → app/public/, not packages/public/ Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
useRegisterSW returns needRefresh as [Accessor<boolean>, Setter<boolean>] not a bare accessor. Calling it directly threw TypeError at runtime. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Real API returns needRefresh as [Accessor<boolean>, Setter<boolean>]. Previous bare-accessor mock hid the runtime TypeError. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Import JSX type directly from solid-js instead of the invalid
typeof import('solid-js').JSX.Element namespace reference.
Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Navigation requests are relative paths in production; the absolute URL pattern for localhost:4096 was misleading and had no effect. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Without this, the manifest is not in the SW precache and a network miss during install eligibility check silently prevents the install prompt. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
…gnal gate - call SolidJS render dispose() to prevent reactive tree leaks per test - extract findButton() helper with exact label matching - B9 now pins that needRefresh() participates in visibility gate Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
- add apple-mobile-web-app-capable for standalone mode on pre-iOS-17 - add apple-mobile-web-app-status-bar-style and title - add 152x152 and 167x167 apple-touch-icon links for iPad and iPad Pro Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
- add scope: '/' for explicit PWA scope declaration - add purpose:any entry for 192x192 icon (Android older launchers) - add SVG icon entry for Chromium desktop crisp rendering Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Resized from apple-touch-icon-v3.png (180x180) for: - 152x152: iPad / iPad mini (2x) - 167x167: iPad Pro retina Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Use space-separated 'maskable any' per W3C spec instead of two separate entries — avoids Lighthouse audit warnings. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
…rompt Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Add onRegisterError callback to show the banner again if the service worker update fails, preventing silent prompt loss. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
apple-touch-icon.png, favicon-96x96.png, favicon.ico, favicon.svg are all superseded by their -v3 counterparts referenced in index.html. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
…ations - B10: verify banner reappears after SW registration error (onRegisterError recovery path) - remove '(fails until E5)' suffixes from manifest test names Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Use portable path resolution instead of machine-specific absolute path. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
role=status does not universally imply a live region across all AT. Explicit aria-live=polite ensures screen readers announce the update banner when it appears. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
…waUpdatePrompt - replace neutral-* with semantic design system tokens for theme compat - remove aria-live=polite (redundant — role=status implies it per ARIA spec) Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
It is a build-time tool only — workbox-window stays in dependencies as it is imported at runtime via virtual:pwa-register/solid. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
- 'maskable any' combined purpose discouraged per Chrome/Lighthouse - SVG icon removed from manifest (fails to load in SW context) - use separate maskable/any entries for 512x512 PNG - replace PWA screenshots with real captures (1440x940 desktop, 390x844 mobile) Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
- add crossorigin=use-credentials to manifest link for auth proxy compat - add Cache-Control: immutable to /assets/* in _headers for edge caching Addresses anomalyco#30405, anomalyco#19301, anomalyco#27931 Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
…nylist - remove onRegisterError: setShow(true) was dead code (needRefresh() stays false after handleReload, so the show() && needRefresh() gate never passed) - add purpose:any entry for 192x192 icon in manifest - add /pty/* to navigateFallbackDenylist Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
The component's onRegisterError set show(true) but needRefresh() stays false after handleReload, so show() && needRefresh() never passed. The behavior was untestable in production. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
bunfig.toml already declares both happydom.ts and solid-web-browser-shim.ts as test preloads. The explicit --preload flags in package.json scripts caused GlobalRegistrator.register() to run twice per test process. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
…x192 any icon test - add og:title, og:description, og:url to index.html - add label field to both screenshots in site.webmanifest - add Cache-Control: no-cache to root-level JS/CSS/MJS in _headers - pin 192x192 any-purpose icon in manifest test Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
2d32334 to
f9a6463
Compare
…ow deps Lockfile was missing vite-plugin-pwa@1.3.0 and workbox-window@7.4.1 entries after cherry-pick rebase from origin/dev (which lacks these deps). Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
|
👋 Heads-up: I've opened a fresh PR that supersedes this one — #32162. This branch predates the v1.17.x changes to the app build/serving layer and no longer applies/builds cleanly against the current Continuing the discussion in #32162. 🙏 |
Issue for this PR
Closes #19174
Closes #19119
Closes #27933
Addresses #27931
Addresses #30405
Addresses #19301
Note: #30405 is also addressed by the open PR #30399 — both add
crossorigin="use-credentials"to the manifest link. Happy to drop that commit from this PR if #30399 merges first.Type of change
What does this PR do?
The web UI had no service worker, so every reload re-fetched all assets and the app couldn't be installed as a PWA.
Service worker — adds
vite-plugin-pwawithregisterType: 'prompt'and Workbox precaching for the static shell (JS, CSS, fonts, icons). Only explicit SPA routes receive the navigation fallback — all other paths pass to the network by default (navigateFallbackAllowlist). SW is disabled in dev to preserve Vite HMR.Update prompt —
PwaUpdatePromptcomponent shows a non-blocking banner when a new SW is waiting. Reload activates it; Dismiss defers it. Mounted outsideAppBaseProvidersso it survives ErrorBoundary and connection errors.Manifest fixes — adds
id,scope,start_url,description, andscreenshotsfields. Fixestheme_colorfrom#ffffffto#F8F7F7. Separatemaskableandanyicon entries per Chrome/Lighthouse guidance.iOS — adds
apple-mobile-web-app-capable, status bar style, andapple-touch-iconlinks for iPad (152×152, 167×167) alongside the existing 180×180.Cache-Control —
Cache-Control: immutableon/assets/*in_headers,no-cacheon root-level assets.Proxy fix —
crossorigin="use-credentials"on the manifest link (same fix as #30399).How did you verify your code works?
bun testinpackages/apppasses (component tests B1–B9, manifest field tests).bun run build+bun run serveinpackages/app— install prompt appeared, SW registered and cached the shell, update banner showed after a second build and reload.Screenshots / recordings
Screenshots of the install prompt, update banner, and Lighthouse audit available on request.
Checklist