Make the app installable as a PWA #9

Merged
kyuno merged 2 commits from feature/pwa into master 2026-09-16 15:52:13 +02:00
Owner

Summary

  • Adds a web app manifest, hand-authored icons (regular + Android maskable), and the iOS-specific meta tags, so the app can be added to a phone's home screen and opened without browser chrome.
  • No service worker: not required for installability (confirmed against MDN), and this app has nothing worth caching offline — every page is either live party/socket state or a proxied Emby catalog, both of which would just go stale if cached.
  • build.mjs copies client/src/public/* (manifest + icons) into dist/ alongside the existing index.html copy; express.static already serves all of dist unconditionally, so no server changes were needed.

Context

This was requested as a follow-up after PR #8 (Picture-in-Picture): I'd explained that PiP can't be triggered automatically on backgrounding because browsers require a fresh user gesture for requestPictureInPicture() — confirmed empirically by calling it from a real visibilitychange listener in a real Chromium tab and getting NotAllowedError, even with navigator.userActivation.hasBeenActive === true from an earlier real click.

Worth being explicit about: installing this app as a PWA does not change that outcome — the gesture requirement is part of the Picture-in-Picture spec itself, not tied to running in a tab vs. a standalone window. So this PR is a standalone, independently useful feature (home-screen icon, no URL bar, OS-level app-switcher entry) rather than a fix for the previous limitation. Full reasoning in docs/architecture.md's new "Progressive Web App" section.

Verification

  • Full typecheck/build (client only).
  • Confirmed directly against a real running instance of the server: manifest.webmanifest and all 3 manifest icons resolve 200 with correct application/manifest+json/image/png content types, apple-touch-icon resolves 200, generated PNGs are genuinely the declared dimensions (32/180/192/512), no manifest-related console warnings.
  • Maskable icon's safe-zone math checked by computing the actual farthest-content-point distance, not eyeballed.
  • Not verified: an actual "Add to Home Screen" prompt or the installed standalone window on a real device — no device reachable from this sandbox, same standing gap as this project's other browser-native integrations.

Test plan

  • Open the deployed URL on an Android phone in Chrome, confirm an install prompt/"Add to Home Screen" option appears
  • Install it, confirm it opens standalone (no URL bar) with the correct icon
  • Check the maskable icon doesn't look clipped/off-center on a real launcher
  • Repeat on iOS Safari (Share → Add to Home Screen) since iOS ignores the manifest and relies on the separate meta tags

🤖 Generated with Claude Code

## Summary - Adds a web app manifest, hand-authored icons (regular + Android maskable), and the iOS-specific meta tags, so the app can be added to a phone's home screen and opened without browser chrome. - No service worker: not required for installability (confirmed against MDN), and this app has nothing worth caching offline — every page is either live party/socket state or a proxied Emby catalog, both of which would just go stale if cached. - `build.mjs` copies `client/src/public/*` (manifest + icons) into `dist/` alongside the existing `index.html` copy; `express.static` already serves all of `dist` unconditionally, so no server changes were needed. ## Context This was requested as a follow-up after [PR #8](../../pulls/8) (Picture-in-Picture): I'd explained that PiP can't be triggered automatically on backgrounding because browsers require a *fresh* user gesture for `requestPictureInPicture()` — confirmed empirically by calling it from a real `visibilitychange` listener in a real Chromium tab and getting `NotAllowedError`, even with `navigator.userActivation.hasBeenActive === true` from an earlier real click. **Worth being explicit about**: installing this app as a PWA does not change that outcome — the gesture requirement is part of the Picture-in-Picture spec itself, not tied to running in a tab vs. a standalone window. So this PR is a standalone, independently useful feature (home-screen icon, no URL bar, OS-level app-switcher entry) rather than a fix for the previous limitation. Full reasoning in docs/architecture.md's new "Progressive Web App" section. ## Verification - Full `typecheck`/`build` (client only). - Confirmed directly against a real running instance of the server: `manifest.webmanifest` and all 3 manifest icons resolve `200` with correct `application/manifest+json`/`image/png` content types, `apple-touch-icon` resolves `200`, generated PNGs are genuinely the declared dimensions (32/180/192/512), no manifest-related console warnings. - Maskable icon's safe-zone math checked by computing the actual farthest-content-point distance, not eyeballed. - **Not verified**: an actual "Add to Home Screen" prompt or the installed standalone window on a real device — no device reachable from this sandbox, same standing gap as this project's other browser-native integrations. ## Test plan - [ ] Open the deployed URL on an Android phone in Chrome, confirm an install prompt/"Add to Home Screen" option appears - [ ] Install it, confirm it opens standalone (no URL bar) with the correct icon - [ ] Check the maskable icon doesn't look clipped/off-center on a real launcher - [ ] Repeat on iOS Safari (Share → Add to Home Screen) since iOS ignores the manifest and relies on the separate meta tags 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-16 14:08:00 +02:00
Make the app installable as a PWA
Some checks failed
Publier l'image Docker / test (pull_request) Failing after 4s
Publier l'image Docker / publish (pull_request) Has been skipped
75a9fef18a
Manifest + hand-authored icons (any + maskable) + iOS meta tags, so it
can be added to a phone's home screen and opened without browser
chrome. No service worker - explored as a possible route to automatic
Picture-in-Picture on backgrounding, but confirmed (by actually calling
requestPictureInPicture() from a real visibilitychange listener) that
install state doesn't change that API's fresh-gesture requirement, so
this is a standalone feature rather than a fix for that limitation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno added 1 commit 2026-09-16 15:52:06 +02:00
Merge remote-tracking branch 'origin/master' into feature/pwa
Some checks failed
Publier l'image Docker / test (pull_request) Failing after 3s
Publier l'image Docker / publish (pull_request) Has been skipped
c8655fc7c3
# Conflicts:
#	README.md
kyuno merged commit b5a9a0bc7d into master 2026-09-16 15:52:13 +02:00
kyuno deleted branch feature/pwa 2026-09-16 15:52:13 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/watchparty#9
No description provided.