298 lines
7.1 KiB
Markdown
298 lines
7.1 KiB
Markdown
# Mobile App Plan
|
|
|
|
## App Type Decision
|
|
|
|
Build Hermes Mobile as a native Android app from the start, using Capacitor around a shared React UI.
|
|
|
|
PWA will still exist as a fallback/dev target, but it should not be the primary product experience.
|
|
|
|
## Why Native Android First
|
|
|
|
A PWA can get surprisingly far on Android: installable icon, offline shell, camera/mic, file picker, Web Push, and a standalone window. But for this project, “suffices” is not the bar. The goal is that Hermes feels like a genuine phone app designed around agent work.
|
|
|
|
Native Android via Capacitor gives us:
|
|
- Real APK install path.
|
|
- Native splash screen and status bar control.
|
|
- More reliable push notifications.
|
|
- Better haptics.
|
|
- Better file/share integration.
|
|
- Easier future “Share to Hermes” target from Android apps.
|
|
- Better control over permissions and app lifecycle.
|
|
- Native-feeling back button behavior.
|
|
- More credible “this is an app” feel than browser-installed PWA.
|
|
|
|
## Role of PWA
|
|
|
|
Keep PWA support for:
|
|
- Fast development loop.
|
|
- Desktop/LAN access.
|
|
- Emergency fallback if APK is not installed.
|
|
- Users who do not want to sideload.
|
|
|
|
But feature priority should be:
|
|
1. Android app / Capacitor
|
|
2. PWA fallback
|
|
3. Desktop web convenience
|
|
|
|
## Frontend Stack
|
|
|
|
Recommended:
|
|
- Vite + React + TypeScript
|
|
- Tailwind CSS
|
|
- Capacitor Android
|
|
- TanStack Query for server data
|
|
- Zustand for local UI/session state
|
|
- Framer Motion for app-like motion where useful
|
|
- Zod for API schema validation
|
|
- Vite PWA plugin for fallback web install
|
|
|
|
Capacitor plugins:
|
|
- `@capacitor/android`
|
|
- `@capacitor/app` for lifecycle/back button
|
|
- `@capacitor/haptics`
|
|
- `@capacitor/status-bar`
|
|
- `@capacitor/splash-screen`
|
|
- `@capacitor/push-notifications`
|
|
- `@capacitor/filesystem`
|
|
- `@capacitor/share`
|
|
- `@capacitor/preferences`
|
|
|
|
Potential later:
|
|
- Biometric auth plugin
|
|
- Camera plugin
|
|
- Background task/upload plugin if needed
|
|
|
|
## Build Targets
|
|
|
|
```text
|
|
apps/mobile/ shared React UI
|
|
apps/mobile/android/ Capacitor Android project
|
|
apps/mobile/dist/ web build consumed by Capacitor and PWA
|
|
```
|
|
|
|
Commands later:
|
|
|
|
```bash
|
|
pnpm mobile:dev # browser dev server
|
|
pnpm mobile:build # web build
|
|
pnpm android:sync # capacitor sync
|
|
pnpm android:apk # debug/release APK
|
|
```
|
|
|
|
## App Package Structure
|
|
|
|
```text
|
|
apps/mobile/
|
|
capacitor.config.ts
|
|
android/ # generated Capacitor Android project
|
|
src/
|
|
app/
|
|
App.tsx
|
|
router.tsx
|
|
providers.tsx
|
|
native.ts # Capacitor runtime helpers
|
|
screens/
|
|
AskScreen/
|
|
ActivityScreen/
|
|
SessionsScreen/
|
|
CronScreen/
|
|
FilesScreen/
|
|
SettingsScreen/
|
|
components/
|
|
agent-status-orb/
|
|
composer/
|
|
tool-card/
|
|
event-timeline/
|
|
upload-tray/
|
|
voice-recorder/
|
|
cron-job-card/
|
|
approval-card/
|
|
bottom-nav/
|
|
app-shell/
|
|
features/
|
|
tasks/
|
|
sessions/
|
|
cron/
|
|
files/
|
|
approvals/
|
|
notifications/
|
|
pairing/
|
|
settings/
|
|
native/
|
|
haptics.ts
|
|
push.ts
|
|
share.ts
|
|
statusBar.ts
|
|
filesystem.ts
|
|
backButton.ts
|
|
lib/
|
|
api-client.ts
|
|
realtime.ts
|
|
audio.ts
|
|
file-utils.ts
|
|
format.ts
|
|
styles/
|
|
globals.css
|
|
tokens.css
|
|
assets/
|
|
icons/
|
|
splash/
|
|
main.tsx
|
|
public/
|
|
manifest.webmanifest
|
|
service-worker.ts
|
|
```
|
|
|
|
## Navigation
|
|
|
|
Bottom tabs:
|
|
- Ask
|
|
- Activity
|
|
- Cron
|
|
- Files
|
|
- Settings
|
|
|
|
Native back behavior:
|
|
- If a sheet/modal is open, close it.
|
|
- Else if not on Ask, go back to previous tab/screen.
|
|
- Else prompt/minimize/exit according to Android convention.
|
|
|
|
## Ask Screen Behavior
|
|
|
|
States:
|
|
- idle
|
|
- composing
|
|
- uploading
|
|
- sending
|
|
- running
|
|
- waiting_approval
|
|
- finished
|
|
- failed
|
|
|
|
Composer:
|
|
- Text input
|
|
- Attach file button
|
|
- Hold/tap voice recording button
|
|
- Send button
|
|
- Optional mode chips: New / Continue / Background
|
|
|
|
When task starts:
|
|
- Input collapses to bottom.
|
|
- Task timeline appears.
|
|
- Assistant response streams.
|
|
- Tool cards appear in chronological order.
|
|
- Subtle haptic on send, completion, error, and approval required.
|
|
|
|
## Realtime
|
|
|
|
Use WebSocket first:
|
|
- `/api/realtime?token=...`
|
|
- Subscribe to task/session events.
|
|
|
|
Fallback:
|
|
- SSE `/api/tasks/:id/events/stream`
|
|
- Polling last resort.
|
|
|
|
Frontend event store should append events idempotently by event ID.
|
|
|
|
## Push Notifications
|
|
|
|
Native Android priority:
|
|
- Use Capacitor Push Notifications / FCM where practical.
|
|
- Companion server stores device push token.
|
|
- Notifications for task completion, failure, approval required, cron completion.
|
|
|
|
Fallbacks:
|
|
- Web Push for PWA.
|
|
- ntfy/Gotify if native push is too annoying for self-hosted early builds.
|
|
|
|
Important: notification provider must be abstracted so the app can start with ntfy/Web Push and later use FCM/native without rewriting task logic.
|
|
|
|
## Voice Recording
|
|
|
|
Prefer native-friendly implementation:
|
|
- First version can use browser MediaRecorder inside Capacitor WebView.
|
|
- If recording quality/lifecycle is poor, move to native audio recording plugin.
|
|
|
|
Preferred mime order for web implementation:
|
|
- `audio/webm;codecs=opus`
|
|
- `audio/ogg;codecs=opus`
|
|
- browser default fallback
|
|
|
|
UI:
|
|
- Press/tap mic to start.
|
|
- Big native-feeling recording sheet with timer/waveform.
|
|
- Haptic on start/stop.
|
|
- Cancel / send.
|
|
- Upload progress.
|
|
|
|
## File Upload UX
|
|
|
|
Priority:
|
|
- Use standard file input first inside WebView.
|
|
- Add Capacitor Filesystem/Share integration for better Android feel.
|
|
- Later add Android share target so user can share files/text/images into Hermes Mobile from other apps.
|
|
|
|
UI:
|
|
- Preview thumbnails for images.
|
|
- File chips for archives/docs.
|
|
- Upload before task send or as part of task multipart.
|
|
|
|
## Offline/Background Behavior
|
|
|
|
- App shell should load offline.
|
|
- Draft prompt persists locally via Capacitor Preferences/local storage.
|
|
- Running task state reloads from server after reconnect.
|
|
- Notifications tell user when background tasks finish.
|
|
- No attempt to run Hermes offline.
|
|
|
|
## Settings UX
|
|
|
|
First launch:
|
|
1. Enter companion URL or open pairing deep link/QR.
|
|
2. Pair device.
|
|
3. Test connection.
|
|
4. Enable notifications.
|
|
5. Land on Ask.
|
|
|
|
Settings fields:
|
|
- Companion URL
|
|
- Device name
|
|
- Notification status/test
|
|
- Theme
|
|
- Default task mode
|
|
- Upload retention display
|
|
- Hermes health
|
|
- App version/build channel
|
|
|
|
## Native Polish Checklist
|
|
|
|
- Proper Android package ID, e.g. `cloud.molberg.hermesmobile`.
|
|
- Adaptive icon.
|
|
- Native splash screen.
|
|
- Status/navigation bar colors match theme.
|
|
- Edge-to-edge layout with safe-area handling.
|
|
- Keyboard does not break composer layout.
|
|
- Native back button behavior.
|
|
- Haptics on important interactions.
|
|
- Notification permission onboarding.
|
|
- Share target later.
|
|
- Deep links for pairing.
|
|
- Avoid browser pull-to-refresh in PWA fallback; app should have explicit refresh controls.
|
|
|
|
## Accessibility
|
|
|
|
- Visible focus states.
|
|
- Reduced motion mode.
|
|
- ARIA labels for icon buttons.
|
|
- Captions/transcript for voice notes.
|
|
- Minimum 44px tap targets.
|
|
|
|
## PWA Sufficiency Summary
|
|
|
|
PWA is enough for a functional prototype.
|
|
|
|
Native Android is better for the product Zeb described.
|
|
|
|
Decision: build shared React app + Capacitor Android from day one, while keeping PWA fallback essentially free.
|