---
phase: 26-pwa-performance
plan: 03
subsystem: ui
tags: [pwa, offline, indexeddb, idb, install-prompt, service-worker, react-hooks]

requires:
  - phase: 26-00
    provides: idb dependency installed, pwa.d.ts types, BeforeInstallPromptEvent interface
  - phase: 26-02
    provides: ChatPanel with MobileChatView mobile/desktop split, useMediaQuery hook

provides:
  - useOnlineStatus hook — reactive navigator.onLine boolean
  - useInstallPrompt hook — captures beforeinstallprompt, iOS detection, promptInstall()
  - useOfflineQueue hook — IndexedDB message queue with auto-flush on online event
  - InstallPromptBanner component — PWA install CTA with iOS fallback and 7-day dismiss cooldown
  - OfflineBanner component — amber offline status with queued message count
  - ChatPanel offline integration — enqueues messages when offline via useOfflineQueue

affects: [26-04, ChatPanel, MobileChatView, pwa-push-notifications]

tech-stack:
  added: ["idb@^8.0.3"]
  patterns:
    - "useOfflineQueue uses openDB with autoIncrement store for message replay"
    - "OfflineBanner uses dark: prefix for light/dark amber theme switching"
    - "InstallPromptBanner checks localStorage timestamp for 7-day cooldown"
    - "handleSend in ChatPanel guards on isOnline before proceeding to network calls"

key-files:
  created:
    - ui/src/hooks/useOnlineStatus.ts
    - ui/src/hooks/useInstallPrompt.ts
    - ui/src/hooks/useOfflineQueue.ts
    - ui/src/components/InstallPromptBanner.tsx
    - ui/src/components/OfflineBanner.tsx
    - ui/src/types/pwa.d.ts
  modified:
    - ui/src/components/ChatPanel.tsx
    - ui/package.json
    - pnpm-lock.yaml

key-decisions:
  - "idb + pwa.d.ts added inline in this plan — parallel 26-00 worktree commits were not on gsd/phase-26-pwa-performance branch"
  - "OfflineBanner uses dark: Tailwind prefix for amber light/dark theming — consistent with design system approach"
  - "ChatPanel offline guard only enqueues when activeConversationId exists — new conversation creation requires network"
  - "useOfflineQueue flush stops on first failure — prevents partial replay; retries on next online event"

patterns-established:
  - "Offline-first guard at handleSend entry: check isOnline before any network operation"
  - "IndexedDB queue pattern: openDB with autoIncrement, getAllKeys/get/delete for sequential flush"

requirements-completed: [PWA-01, PWA-02, PWA-08]

duration: 5min
completed: 2026-04-02
---

# Phase 26 Plan 03: PWA Install Prompt, Offline Banner, and Message Queue Summary

**PWA install prompt with iOS fallback, amber offline status banner, and IndexedDB message queue that auto-flushes on reconnection**

## Performance

- **Duration:** ~5 min
- **Started:** 2026-04-02T02:15:40Z
- **Completed:** 2026-04-02T02:20:35Z
- **Tasks:** 2
- **Files modified:** 8

## Accomplishments
- Created three production hooks: `useOnlineStatus` (reactive online state), `useInstallPrompt` (captures beforeinstallprompt + iOS detection), `useOfflineQueue` (IndexedDB-backed queue with auto-flush)
- Built `InstallPromptBanner` component with 7-day localStorage dismiss cooldown, iOS Share menu instruction text, and "Add to Home Screen" CTA
- Built `OfflineBanner` component with amber theming (dark/light), queued message count display, and 3-second auto-dismiss on reconnection
- Wired `ChatPanel` to enqueue messages to IndexedDB when offline and display a queued toast notification

## Task Commits

1. **Task 1: useOnlineStatus, useInstallPrompt, useOfflineQueue hooks** - `2b172bda` (feat)
2. **Task 2: InstallPromptBanner, OfflineBanner, ChatPanel wiring** - `427e6c80` (feat)

## Files Created/Modified
- `ui/src/hooks/useOnlineStatus.ts` — Reactive boolean from navigator.onLine with online/offline event listeners
- `ui/src/hooks/useInstallPrompt.ts` — Captures beforeinstallprompt, iOS detection via userAgent, promptInstall() callback
- `ui/src/hooks/useOfflineQueue.ts` — openDB nexus-offline/message_queue store, enqueue/flush with stop-on-failure strategy
- `ui/src/components/InstallPromptBanner.tsx` — Fixed-position banner with 7-day localStorage cooldown and iOS text variant
- `ui/src/components/OfflineBanner.tsx` — Fixed top-0 amber banner showing queue count, 3s auto-dismiss on reconnect
- `ui/src/components/ChatPanel.tsx` — Added isOnline guard in handleSend, enqueue call, OfflineBanner and InstallPromptBanner renders
- `ui/src/types/pwa.d.ts` — BeforeInstallPromptEvent interface with global WindowEventMap extension
- `ui/package.json` — Added idb@^8.0.3 dependency

## Decisions Made
- `idb` and `pwa.d.ts` were added here as a deviation — the parallel 26-00 worktree commits were not merged into `gsd/phase-26-pwa-performance` branch
- Offline guard in `handleSend` only enqueues when `activeConversationId` exists — creating a new conversation requires network, so we skip enqueue for that path
- `useOfflineQueue.flush()` breaks on first failure — stops partial replay and retries on next `online` event

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Added idb dependency and pwa.d.ts that were missing from branch**
- **Found during:** Task 1 (creating hooks)
- **Issue:** The `gsd/phase-26-pwa-performance` branch was missing the `idb` package and `ui/src/types/pwa.d.ts` that were created in a parallel 26-00 worktree but never merged to the main phase branch
- **Fix:** Added `idb@^8.0.3` to `ui/package.json`, ran `pnpm install`, created `ui/src/types/pwa.d.ts` with BeforeInstallPromptEvent interface
- **Files modified:** ui/package.json, pnpm-lock.yaml, ui/src/types/pwa.d.ts
- **Verification:** `import { openDB } from "idb"` compiles without errors
- **Committed in:** `2b172bda` (Task 1 commit)

---

**Total deviations:** 1 auto-fixed (1 blocking)
**Impact on plan:** Required for task completion. No scope creep.

## Issues Encountered
- Pre-existing TypeScript errors in `packages/shared` (`zod` module not found) and `packages/adapters` prevent `pnpm --filter @paperclipai/ui build` from passing at the workspace level, but `tsc -b` within the `ui` directory itself produces zero errors. These are pre-existing issues unrelated to this plan.

## User Setup Required
None - no external service configuration required.

## Next Phase Readiness
- PWA install prompt, offline banner, and message queue all functional
- `useOfflineQueue` relies on `chatApi.postMessage` for replay — the flush will restore messages when reconnected
- Plan 26-04 (push notifications) can build on the same `useOnlineStatus` pattern

## Self-Check: PASSED

- FOUND: ui/src/hooks/useOnlineStatus.ts
- FOUND: ui/src/hooks/useInstallPrompt.ts
- FOUND: ui/src/hooks/useOfflineQueue.ts
- FOUND: ui/src/components/InstallPromptBanner.tsx
- FOUND: ui/src/components/OfflineBanner.tsx
- FOUND: ui/src/types/pwa.d.ts
- FOUND commit: 2b172bda (Task 1)
- FOUND commit: 427e6c80 (Task 2)

---
*Phase: 26-pwa-performance*
*Completed: 2026-04-02*
