feat(web): PWA install bottom sheet on phone when not running installed #144
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!144
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/142"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the settled #142 design: on a phone browser that is not running as the installed PWA, a bottom sheet offers installing lab to the Home Screen.
What ships
web/src/lib/install.ts— headless logic as Solid signals + derivations (lib/ tested-seam convention).beforeinstallpromptis captured at module init withpreventDefault()(suppresses Chrome's mini-infobar) and stashed;appinstalleddrops the stash and closes the sheet + Settings row. Eligibility truth table: not standalone (display-mode+ legacynavigator.standalone), coarse pointer, not dismissed, variant available. iOS detection unmasks Mac-UA iPadOS viamaxTouchPoints. Android is eligible only with the event in hand — if it never fires, nothing shows (no dead Install button, no fallback instructions).promptInstall()treats the Chromium event as one-shot: fire, drop, close.web/src/components/InstallSheet.tsx— the repo's first modal primitive: scrim + card sliding up from the bottom edge (thumb-reachable; on iOS adjacent to Safari's bottom-toolbar Share button the instructions point at). Android variant: Install + Not now. iOS variant: the two illustrated Share → Add to Home Screen steps (Lucideshare/square-plusglyphs vendored into Icon.tsx) + Not now. Scrim and card are siblings so the dismiss handler lives on the scrim alone.role="dialog",aria-modal, labelled heading; reduced-motion disables the slide-up.lab.install-dismissed; the Settings row is the sole re-entry.Verification
npx vitest run: 40 files, 657 tests green — 25 new lib tests (full eligibility truth table, capture/dismiss/appinstalled/promptInstall, matchMedia-absent degradation), 8 InstallSheet component tests (both variants, scrim-vs-card, a11y), 2 Settings integration tests driving the real singleton end to end (event lands → row appears → sheet opens → Not now persists the flag).tsc --noEmit,eslint .,prettier --check .,vite buildall clean; Go side (go build ./... && go test ./..., CGO_ENABLED=0) untouched and green.window.matchMedia, so the lib treats a missing implementation as "no match" — the singleton stays inert and throw-free under every other jsdom suite that mounts AppShell.beforeinstallpromptordisplay-mode: standalonewithout testing a mock.Closes #142
🤖 Generated with Claude Code
https://claude.ai/code/session_0111erhhwzsJneW6D72kLh8F