Multi-adapter mobile UI for AI coding assistants. Supports Claude Code, Codex CLI, and Gemini CLI through one interface. Features: - Real-time bidirectional sync via tmux + WebSocket - Cross-AI review (send one AI's output to another for review) - Multi-review tabs with minimize/expand - Push notifications (PWA) with smart session-aware filtering - Three-channel event system (hooks, file watcher, pane monitor) - Voice input, image paste, draft persistence - Terminal-native design (JetBrains Mono, dark theme, pixel art claw) - CLI with --adapter flag on every command - Zero-overhead fire-and-forget hooks
6.1 KiB
PWA Optimization Design Spec
Date: 2026-03-26 Goal: Bring CodeTap's PWA to production-grade quality — proper viewport handling, splash screens, install prompts, SW updates, badge management, draft persistence, and navigation history.
Current State
CodeTap already has solid PWA foundations:
- Service Worker with Workbox precaching (vite-plugin-pwa, injectManifest)
- Web App Manifest (standalone, portrait, dark theme)
- Push notifications with badge support
- iOS meta tags (capable, black-translucent status bar)
- Offline detection + OfflineView
- overscroll-behavior: none, 16px inputs, h-dvh, safe-bottom
What's Missing
High Priority
1. Viewport & Safe Areas
Problem: Missing viewport-fit=cover. Only bottom safe area handled — notch/Dynamic Island area not accounted for.
Solution:
- Add
viewport-fit=coverto viewport meta tag inindex.html - Add CSS for top safe area: headers get
padding-top: env(safe-area-inset-top) - The standalone PWA mode on iOS with
black-translucentstatus bar needs the content to extend behind the status bar —viewport-fit=coverenables this
Files: index.html, src/index.css
2. Splash Screen / Launch Images
Problem: White flash on app startup — no branded loading experience.
Solution:
- Add
apple-mobile-web-app-startup-imagemeta tags covering major iPhone sizes - Use
mediaattribute withdevice-width,device-height, anddevice-pixel-ratioqueries - Background:
#09090b(matches theme), centered CodeTap mascot/logo - Generate splash images as data URIs or static PNGs in
/public/splash/ - Minimum coverage: iPhone SE, iPhone 14/15, iPhone 14/15 Pro Max, iPhone 16 Pro Max
Files: index.html, public/splash/ (new directory)
3. Android Install Prompt
Problem: No handling of beforeinstallprompt event — Android users never see install prompt.
Solution:
- Listen for
beforeinstallpromptin App.tsx, store the event in state - Show a dismissible install banner in SessionsView (below header)
- Banner text: "Install CodeTap for a better experience" with Install/Dismiss buttons
- On Install click: call
event.prompt(), hide banner - On Dismiss: hide banner, store dismissal in
localStorageso it doesn't reappear - After successful install (
appinstalledevent): hide banner permanently
Files: src/App.tsx, src/components/SessionsView.tsx
4. Service Worker Update Notification
Problem: SW updates silently — user doesn't know a new version is available.
Solution:
- Listen for
controllerchangeonnavigator.serviceWorkerin App.tsx - When detected, show a toast at bottom: "New version available" with Refresh button
- On click:
window.location.reload() - Toast auto-dismisses after 10s but can be manually dismissed
Files: src/App.tsx
Medium Priority
5. Badge Clear on Focus
Problem: App badge persists even when user is actively looking at the app.
Solution:
- In App.tsx, listen for
visibilitychangeevent - When
document.visibilityState === 'visible': callnavigator.clearAppBadge()(with feature check) - This ensures badge is cleared whenever user switches back to the app
Files: src/App.tsx
6. Manifest Shortcuts
Problem: No quick actions from home screen long-press.
Solution:
- Add
shortcutsarray to manifest in vite.config.ts:- "New Chat" — url:
/?action=newchat, icon:chat-bubble-icon
- "New Chat" — url:
- In App.tsx, check for
?action=newchatparam and navigate accordingly
Files: vite.config.ts, src/App.tsx
7. Input Draft Auto-Save
Problem: Typed text lost if app is backgrounded or crashes.
Solution:
- In ShimmerInput: on every input change, debounce-save to
localStoragewith keycodetap:draft:{sessionId} - On mount: restore draft from localStorage if present
- On successful send or explicit clear: delete the draft
- Debounce: 500ms to avoid excessive writes
Files: src/components/ShimmerInput.tsx
8. Manifest Screenshots
Problem: Missing screenshots for app stores and install prompts.
Solution:
- Add
screenshotsarray to manifest in vite.config.ts - Provide at minimum:
- 1 narrow screenshot (phone, 1080x1920) — chat view
- 1 wide screenshot (tablet/desktop, 1920x1080) — sessions view
- Store in
public/screenshots/
Files: vite.config.ts, public/screenshots/ (new directory)
Low Priority
9. Slow Network Detection
Problem: No feedback when on slow connection.
Solution:
- Check
navigator.connection?.effectiveType(with feature detection) - When
2gorslow-2g: show a subtle indicator in StatusBar ("Slow connection") - Re-check on
changeevent ofnavigator.connection
Files: src/components/StatusBar.tsx
10. History API Navigation
Problem: Browser back gesture doesn't work — app uses sessionStorage for view state, no history stack.
Solution:
- In App.tsx, use
history.pushState()when navigating between views - Listen for
popstateevent to handle back navigation - Map each view to a history entry:
sessions,chat/{sessionId},settings,newchat/{cwd} - This enables iOS swipe-back gesture and Android back button in standalone mode
Files: src/App.tsx
11. OpenGraph Meta Tags
Problem: No social sharing metadata.
Solution:
- Add to
index.html:og:title: "CodeTap"og:description: "Use Claude Code from your phone"og:image: link to a social card imageog:type: "website"twitter:card: "summary"
Files: index.html
Design Principles
- Progressive enhancement — All PWA features use feature detection. App works fine without them.
- No new dependencies — Everything is native Web APIs or existing vite-plugin-pwa config.
- Minimal UI additions — Install banner and SW update toast are the only new UI elements.
- Respect user choice — Install prompt is dismissible and remembers dismissal.
Out of Scope
- Full offline-first with background sync (current offline detection is sufficient)
- Push notification permission prompt UI (current flow works)
- Image compression before upload
- Orientation lock via Screen Orientation API