# Officer Mobile App Plan

> This document is written by Claude for Claude. It contains the full plan for native mobile apps (Kotlin/Android + Swift/iOS) for the Officer platform, based on a thorough study of the monorepo. When resuming this work, read this document first, then explain the plan to the user before starting implementation.

---

## Table of Contents

1. [Current Web App Summary](#1-current-web-app-summary)
2. [What to Include vs Exclude](#2-what-to-include-vs-exclude)
3. [Architecture Overview](#3-architecture-overview)
4. [Authentication](#4-authentication)
5. [Site Map](#5-site-map)
6. [Screen-by-Screen Breakdown](#6-screen-by-screen-breakdown)
7. [Tablet Adaptations](#7-tablet-adaptations)
8. [Technical Challenges](#8-technical-challenges)
9. [API Integration Notes](#9-api-integration-notes)
10. [Shared Design Language](#10-shared-design-language)
11. [Implementation Phases](#11-implementation-phases)

---

## 1. Current Web App Summary

Officer is an AI-assisted personal backend for life management. The web app (`officer-web`) is a React 19 SPA talking to a Hono API server (port 5000). Key features:

| Feature | Description |
|---------|-------------|
| **Chat** | Multi-provider AI chat (Claude, OpenCode, Pi-Mono) via WebSockets. Sessions persisted as JSON files. Supports images, file attachments, model selection, working directory context. |
| **File Browser** | Full file manager with upload/download, rename, copy/move, search, archive extraction, git clone, yt-dlp download, OCR, TTS, transcription, audio extraction, video transcoding. Multiple "roots" (home, ~, officer.dev). |
| **Workspaces** | Multi-panel layouts combining chat, files, terminal, code editor. Panels can be split horizontally/vertically. State persisted per workspace. |
| **Terminal** | Sandboxed Docker terminals and host terminals (Super Admin). WebSocket-based PTY. |
| **Code Editor** | Text editor with syntax highlighting (PrismJS). |
| **Dashboard** | Widget panel (clock, weather, pomodoro, daily goals, quick notes) + workspace launcher + chat launcher. |
| **Automation** | Builder for workflows, pipelines, processes, services, cron jobs, tasks, skills. |
| **Tasks** | File-based task definitions (TASK.md) that can be triggered on files/directories. Native/global/user scopes. |
| **Skills** | AI skill library (SKILL.md files) injected into chat system prompts. |
| **Settings** | User profile, system settings, server resources, AI harness configuration. |
| **Auth** | Email/password + passkey (WebAuthn). JWT tokens. Role-based access (Member, Admin, Owner, Super Admin). |

The API is at `/api/*` with JWT auth on all protected routes. WebSockets for chat (`/api/harness/*/ws`) and terminal (`/api/terminal/ws`).

---

## 2. What to Include vs Exclude

### Include (core mobile experience)

| Feature | Rationale |
|---------|-----------|
| **Chat** | The #1 use case. Having an AI assistant in your pocket is the killer feature. Voice input makes it even better on mobile. |
| **File Browser** | Essential for viewing/managing files. Mobile adds camera upload, share sheet integration. Simplified but fully functional. |
| **Dashboard / Widgets** | Perfect for mobile - quick glance at clock, weather, goals, notes. Widget-style info is native to mobile platforms. |
| **Notifications** | Mobile-exclusive feature. Push notifications for chat completions, task results, long downloads finishing. |
| **Settings** | Profile, preferences. Subset of web settings. |
| **Session History** | Browse and resume past chat sessions. |
| **Quick Actions** | Share sheet integration (share URL/text/image to Officer for chat or file upload). |

### Include (reduced scope)

| Feature | What changes |
|---------|-------------|
| **File Viewer** | View files (text, images, PDFs, audio, video) but no full code editor. Read-only text with syntax highlighting. |
| **Tasks** | View and trigger tasks, see logs. No task creation/editing (too complex for mobile). |
| **Skills** | Browse skill library. No editing. |
| **Workspaces** | View workspace list, open a workspace (but as a single-panel experience, not multi-panel). |

### Exclude (not suitable for phone)

| Feature | Rationale |
|---------|-----------|
| **Terminal** | Typing shell commands on a phone keyboard is miserable. The sandboxed Docker terminal is a power-user web feature. |
| **Code Editor (write mode)** | Writing code on a phone is impractical. Read-only viewing with syntax highlighting is fine. |
| **Multi-panel workspaces** | Phone screens can't meaningfully split into panels. Tablet gets a simplified version (see section 7). |
| **Automation Builder** | Complex drag-and-drop/form-heavy workflow builder. Keep this web-only. |
| **Server/Resource Management** | Admin infrastructure management (installing databases, configuring services). Web-only. |
| **System Settings** | Server configuration, AI harness setup, plugin management. Web-only. |

### Mobile-Exclusive Features (not on web)

| Feature | Description |
|---------|-------------|
| **Push Notifications** | FCM (Android) / APNs (iOS) for chat completions, task results, downloads. |
| **Voice Input** | Native speech-to-text for chat messages. Much better than web speech API. |
| **Camera Integration** | Take photo and send to chat or upload to file browser directly. |
| **Share Sheet** | Receive shared content (URLs, text, images) from other apps. |
| **Biometric Auth** | Fingerprint/Face ID as alternative to passkey/password. |
| **Home Screen Widgets** | Android widgets / iOS widgets for quick notes, pomodoro, weather. |
| **Offline Queue** | Queue chat messages when offline, send when reconnected. |
| **Background Downloads** | File downloads and yt-dlp downloads continue in background. |

---

## 3. Architecture Overview

### Platform Strategy

Two fully native apps sharing no code, but sharing identical:
- API contract (same REST + WebSocket endpoints)
- UX flows and information architecture
- Design language (adapted to each platform's conventions)

```
┌──────────────┐     ┌──────────────┐
│  Android App │     │   iOS App    │
│   (Kotlin)   │     │   (Swift)    │
│              │     │              │
│  Jetpack     │     │  SwiftUI     │
│  Compose     │     │              │
│  Material 3  │     │  Native iOS  │
│              │     │  Components  │
└──────┬───────┘     └──────┬───────┘
       │                    │
       └────────┬───────────┘
                │
         ┌──────┴──────┐
         │  Officer API │
         │  (Hono)     │
         │  port 5000  │
         └─────────────┘
```

### Android Stack

| Layer | Technology |
|-------|-----------|
| UI | Jetpack Compose + Material 3 |
| Navigation | Compose Navigation |
| Networking | Ktor Client (HTTP + WebSocket) |
| State | ViewModel + StateFlow |
| DI | Hilt |
| Storage | DataStore (preferences), Room (offline cache) |
| Images | Coil |
| Push | Firebase Cloud Messaging |
| Auth | BiometricPrompt API |
| Background | WorkManager |

### iOS Stack

| Layer | Technology |
|-------|-----------|
| UI | SwiftUI |
| Navigation | NavigationStack (iOS 16+) |
| Networking | URLSession + native WebSocket (URLSessionWebSocketTask) |
| State | @Observable (iOS 17+) / ObservableObject |
| DI | Swift native (Environment, protocol-based) |
| Storage | UserDefaults, SwiftData (offline cache) |
| Images | AsyncImage + custom cache |
| Push | APNs |
| Auth | LocalAuthentication (Face ID / Touch ID) |
| Background | BGTaskScheduler |

### Minimum OS Versions

- **Android**: API 26 (Android 8.0) - covers 95%+ of devices, gives us WorkManager, BiometricPrompt, etc.
- **iOS**: iOS 16 - gives us NavigationStack, modern SwiftUI features. iOS 17 for @Observable if we want it.

---

## 4. Authentication

### Flow

```
App Launch
    │
    ├─ Has stored JWT? ──Yes──► Validate token (GET /api/auth/me)
    │                              │
    │                          Valid? ──Yes──► Home Screen
    │                              │
    │                          No ──► Login Screen
    │
    └─ No ──► Login Screen
                  │
                  ├─ Email/Password
                  │     POST /api/auth/signin
                  │     ► Store JWT securely
                  │     ► Home Screen
                  │
                  ├─ Passkey (WebAuthn)
                  │     ► Platform passkey flow
                  │     ► Store JWT securely
                  │
                  └─ Biometric Unlock (returning user)
                        ► Decrypt stored JWT
                        ► Validate & proceed
```

### Token Storage

- **Android**: EncryptedSharedPreferences (AndroidX Security)
- **iOS**: Keychain Services

### Biometric Integration

On first successful login, offer to enable biometric unlock:
1. User logs in with email/password or passkey
2. App prompts: "Enable Face ID / Fingerprint for quick access?"
3. If yes: encrypt JWT with biometric-bound key, store in Keychain/Keystore
4. On next launch: biometric prompt → decrypt JWT → validate → proceed

### Server URL Configuration

The mobile app needs to know where the Officer API lives. First-launch flow:

1. "Enter your Officer server URL" input
2. Validate by hitting `GET /api/server-settings/plugins` (public endpoint)
3. Store URL in app preferences
4. Proceed to login

This is important because Officer is self-hosted - there's no central server.

---

## 5. Site Map

```
App
├── Login
│   ├── Server URL Setup (first launch)
│   ├── Email/Password
│   ├── Passkey
│   └── Biometric Unlock
│
├── Home (Tab 1)
│   ├── Widget Dashboard
│   │   ├── Clock
│   │   ├── Weather
│   │   ├── Pomodoro Timer
│   │   ├── Daily Goals
│   │   └── Quick Notes
│   └── Workspace Quick Access
│
├── Chat (Tab 2)
│   ├── Session List
│   │   ├── Active Sessions
│   │   └── Archived Sessions
│   ├── New Chat
│   └── Chat Session
│       ├── Message Thread
│       ├── Model Selector
│       ├── Image Attachment
│       ├── Voice Input
│       └── Tool Use Display
│
├── Files (Tab 3)
│   ├── Directory Browser
│   │   ├── Grid/List View
│   │   ├── Search
│   │   ├── Sort/Filter
│   │   └── Context Actions
│   │       ├── Open/Preview
│   │       ├── Share
│   │       ├── Rename
│   │       ├── Copy/Move
│   │       ├── Delete
│   │       ├── Download to Device
│   │       └── Upload (camera, gallery, files)
│   └── File Viewer
│       ├── Text (syntax highlighted, read-only)
│       ├── Image
│       ├── PDF
│       ├── Audio Player
│       ├── Video Player
│       └── AI Actions (OCR, TTS, Transcribe)
│
├── Activity (Tab 4)
│   ├── Task List
│   │   ├── Native Tasks
│   │   ├── Global Tasks
│   │   └── User Tasks
│   ├── Task Logs
│   └── Running Processes
│
└── Settings (Tab 5 / Profile)
    ├── Profile
    │   ├── Name, Username, Avatar
    │   ├── Password Change
    │   └── Passkey Management
    ├── App Preferences
    │   ├── Theme (Light/Dark/System)
    │   ├── Notification Settings
    │   ├── Default Chat Model
    │   ├── Biometric Lock
    │   └── Server URL
    └── About / Logout
```

### Navigation Pattern

**Bottom Tab Bar** with 4 primary tabs + profile access:

```
┌─────────────────────────────────────┐
│                                     │
│          [Screen Content]           │
│                                     │
├─────┬─────┬─────┬─────┬────────────┤
│ Home│ Chat│Files│ Act.│  Settings  │
│  🏠 │ 💬  │ 📁  │ ⚡  │    ⚙️     │
└─────┴─────┴─────┴─────┴────────────┘
```

- **Home**: Dashboard with widgets
- **Chat**: Session list → chat view
- **Files**: File browser → file viewer
- **Activity**: Tasks, logs, processes
- **Settings**: Profile and preferences

On Android: Material 3 NavigationBar. On iOS: UITabBarController / SwiftUI TabView.

---

## 6. Screen-by-Screen Breakdown

### 6.1 Login Screen

```
┌─────────────────────────┐
│                         │
│      [Officer Logo]     │
│                         │
│  ┌───────────────────┐  │
│  │ Server URL        │  │ ← Only on first launch or
│  └───────────────────┘  │   tap "Change server"
│                         │
│  ┌───────────────────┐  │
│  │ Email             │  │
│  └───────────────────┘  │
│  ┌───────────────────┐  │
│  │ Password          │  │
│  └───────────────────┘  │
│                         │
│  [      Sign In       ] │
│                         │
│  ── or ──               │
│                         │
│  [  Sign in with Passkey ] │
│                         │
│  Forgot password?       │
│                         │
└─────────────────────────┘
```

**Behavior:**
- Server URL field shown on first launch or when tapped
- Email/password fields with proper keyboard types
- "Sign in with Passkey" triggers platform WebAuthn flow
- On success: store JWT, navigate to Home
- On returning user with biometric enabled: show biometric prompt immediately, with "Use password instead" fallback

---

### 6.2 Home / Dashboard

```
┌─────────────────────────┐
│ Officer          [Avatar]│
├─────────────────────────┤
│                         │
│  ┌──────┐  ┌──────────┐│
│  │ 14:32│  │ Weather  ││
│  │ Fri  │  │ 22° ☀️   ││
│  └──────┘  └──────────┘│
│                         │
│  ┌──────────────────┐   │
│  │ 🍅 Pomodoro      │   │
│  │ 18:42 remaining  │   │
│  │ [Start] [Reset]  │   │
│  └──────────────────┘   │
│                         │
│  ┌──────────────────┐   │
│  │ Daily Goals      │   │
│  │ ☑ Exercise       │   │
│  │ ☐ Read 30min     │   │
│  │ ☐ Journal        │   │
│  │ [+ Add goal]     │   │
│  └──────────────────┘   │
│                         │
│  ┌──────────────────┐   │
│  │ Quick Notes      │   │
│  │ Tap to add...    │   │
│  └──────────────────┘   │
│                         │
│  Workspaces             │
│  ┌────┐ ┌────┐ ┌────┐  │
│  │ WS1│ │ WS2│ │ WS3│  │
│  └────┘ └────┘ └────┘  │
│                         │
└─────────────────────────┘
```

**Behavior:**
- Scrollable vertical layout
- Widgets are cards that can be reordered (long-press drag)
- Widget data syncs with web app via user settings API
- Workspace cards show name + icon, tap opens workspace (single-panel mode on phone)
- Pull-to-refresh updates widget data
- Avatar tap opens Settings

**Widget Implementation:**
- Clock: Local device time (no API needed)
- Weather: Can use device location + weather API, or sync from web app settings
- Pomodoro: Local timer with state synced to server (so it persists across devices)
- Daily Goals: Stored in user settings, synced
- Quick Notes: Stored in user settings, synced

---

### 6.3 Chat - Session List

```
┌─────────────────────────┐
│ Chat             [+ New]│
├─────────────────────────┤
│ [🔍 Search sessions...] │
├─────────────────────────┤
│                         │
│  Today                  │
│  ┌──────────────────┐   │
│  │ 🤖 Debug login   │   │
│  │ Claude · 2h ago  │   │
│  │ Last: "The issue  │   │
│  │ was in the..."   │   │
│  └──────────────────┘   │
│  ┌──────────────────┐   │
│  │ 🤖 Refactor API  │   │
│  │ Pi-Mono · 5h ago │   │
│  │ Last: "I've upda- │   │
│  │ ted the router..." │  │
│  └──────────────────┘   │
│                         │
│  Yesterday              │
│  ┌──────────────────┐   │
│  │ 🤖 Recipe ideas  │   │
│  │ Claude · 1d ago  │   │
│  └──────────────────┘   │
│                         │
│  Archived ▸             │
│                         │
└─────────────────────────┘
```

**Behavior:**
- Sessions fetched from `GET /api/sessions?provider=all`
- Grouped by date (Today, Yesterday, This Week, Older)
- Each card shows: session name, provider icon, time, last message preview
- Swipe left to delete, swipe right to archive
- Long-press for context menu (rename, archive, delete)
- "Archived" section expandable
- Search filters sessions by name
- FAB or top-right button for new chat

**Data Source:**
- `GET /api/sessions?provider=claude` (and opencode, pi-mono)
- Sessions are JSON files on the server, API returns metadata
- Message preview from last assistant message

---

### 6.4 Chat - Conversation

```
┌─────────────────────────┐
│ ← Debug login    [···]  │
│    Claude 3.5 Sonnet ▾  │
├─────────────────────────┤
│                         │
│  ┌─ You ────────────┐   │
│  │ The login page    │   │
│  │ throws a 401...   │   │
│  └──────────────────┘   │
│                         │
│  ┌─ Claude ─────────┐   │
│  │ Looking at the    │   │
│  │ auth middleware... │   │
│  │                   │   │
│  │ ┌─ Tool Use ────┐ │   │
│  │ │ 📄 Read file  │ │   │
│  │ │ auth/jwt.ts   │ │   │
│  │ └──────────────┘ │   │
│  │                   │   │
│  │ The issue is in   │   │
│  │ the token verify  │   │
│  │ ```ts             │   │
│  │ if (!jti) ...     │   │
│  │ ```               │   │
│  └──────────────────┘   │
│                         │
├─────────────────────────┤
│ ┌───────────────┐ 📎 🎤│
│ │ Message...    │ [Send]│
│ └───────────────┘       │
└─────────────────────────┘
```

**Behavior:**
- WebSocket connection to `/api/harness/{provider}/ws`
- Messages stream in real-time (assistant:partial → assistant:text)
- Tool use blocks shown as collapsible cards (collapsed by default)
- Code blocks with syntax highlighting and copy button
- Markdown rendering for assistant messages
- Model selector dropdown in the header (or a bottom sheet)
- Attachment button (📎) opens picker: Camera, Photo Library, Files
- Microphone button (🎤) for voice input (speech-to-text → send as text)
- Send button disabled while assistant is generating
- "Stop" button appears during generation
- Pull-down to load older messages (if session has many)
- `[···]` menu: Rename session, View in files, Delete, Share

**WebSocket Message Protocol:**
```
Client sends:
  { type: 'chat', prompt, sessionId, model, images?, attachmentIds? }
  { type: 'resume', sessionId }
  { type: 'stop' }

Server sends:
  { type: 'session:init', sessionId, name }
  { type: 'assistant:partial', text }
  { type: 'assistant:text', text }
  { type: 'tool:use', name, input }
  { type: 'tool:result', result }
  { type: 'result', cost?, duration?, turns? }
  { type: 'error', message }
  { type: 'stopped' }
```

**Offline Behavior:**
- If offline when sending, queue message locally
- Show "Queued - will send when online" indicator
- On reconnect, send queued messages in order
- Previously loaded messages remain visible offline (cached in local DB)

**Voice Input:**
- Tap and hold microphone for continuous recording
- Or tap once to start, tap again to stop
- Platform native speech recognition
- Transcribed text placed in input field for review before sending
- User can edit before sending

---

### 6.5 Files - Directory Browser

```
┌─────────────────────────┐
│ ← Files          [···]  │
│ / > Documents > Work    │
├─────────────────────────┤
│ [🔍 Search files...]    │
├─────────────────────────┤
│ Sort: Name ▾   [≡] [⊞] │
├─────────────────────────┤
│                         │
│  📁 Projects         →  │
│  📁 Reports          →  │
│  📄 notes.md      3KB   │
│  🖼️ photo.jpg    1.2MB  │
│  🎵 song.mp3     4.5MB  │
│  📹 demo.mp4    45.2MB  │
│  📦 backup.zip  12.0MB  │
│                         │
│         [+ Upload]      │
│                         │
└─────────────────────────┘
```

**Behavior:**
- Breadcrumb navigation at top (scrollable horizontal)
- Tap folder to navigate in, tap file to open viewer
- Long-press for context menu:
  - Open / Preview
  - Share (native share sheet)
  - Rename
  - Copy / Move (enter selection mode)
  - Delete
  - Download to device
  - AI actions (OCR, TTS, Transcribe - based on file type)
- Pull-to-refresh
- FAB or bottom button for upload (Camera, Photo Library, Files picker)
- Multi-select mode (long-press first item, then tap others)
- Swipe actions: left = delete, right = share
- Grid/List view toggle
- Sort by name/date/size
- Search with debounce (calls `/api/file-browser/search`)

**Root Selection:**
- Dropdown or segmented control at top for root selection
- Only show non-home roots for Super Admin users
- Default to "home"

**Upload Sources:**
- Device file picker
- Camera (take photo)
- Photo library
- Share sheet (from other apps)
- Drag & drop (tablet only)

---

### 6.6 Files - File Viewer

```
┌─────────────────────────┐
│ ← notes.md    [↗] [···]│
├─────────────────────────┤
│                         │
│  # Meeting Notes        │
│                         │
│  ## Action Items        │
│  - Fix login bug        │
│  - Update docs          │
│  - Review PR #42        │
│                         │
│  ## Discussion          │
│  We talked about the    │
│  new mobile app and...  │
│                         │
│                         │
│                         │
│                         │
└─────────────────────────┘
```

**Viewer Types:**

| File Type | Viewer | Notes |
|-----------|--------|-------|
| `.md` | Rendered Markdown | With proper styling |
| `.txt`, `.json`, `.ts`, `.js`, etc. | Syntax-highlighted text | Read-only, with line numbers |
| `.jpg`, `.png`, `.gif`, `.svg`, `.webp` | Image viewer | Pinch to zoom, pan |
| `.pdf` | PDF viewer | Native PDF rendering |
| `.mp3`, `.wav`, `.flac`, `.ogg` | Audio player | Play/pause, seek, speed control |
| `.mp4`, `.webm`, `.mov` | Video player | Native player, fullscreen support |
| `.mkv`, `.avi` | Video player (transcoded) | Uses `/api/file-browser/transcode` endpoint |
| Other | Download prompt | "This file type can't be previewed. Download?" |

**Header Actions:**
- [↗] Share via native share sheet
- [···] Menu: Download to device, AI actions (contextual), Open in chat, Delete

**AI Actions (contextual):**
- Images: OCR (extract text)
- Text files: TTS (read aloud)
- Audio/Video: Transcribe
- Video: Extract audio

---

### 6.7 Activity

```
┌─────────────────────────┐
│ Activity                │
├─────────────────────────┤
│ [Tasks] [Logs] [Procs]  │
├─────────────────────────┤
│                         │
│  Native Tasks           │
│  ┌──────────────────┐   │
│  │ 📋 Format Code   │   │
│  │ Formats source   │   │
│  │ files with...    │   │
│  │         [Run ▶]  │   │
│  └──────────────────┘   │
│  ┌──────────────────┐   │
│  │ 📋 Compress Imgs │   │
│  │ Optimizes image  │   │
│  │ files in...      │   │
│  │         [Run ▶]  │   │
│  └──────────────────┘   │
│                         │
│  User Tasks             │
│  ┌──────────────────┐   │
│  │ 📋 Backup DB     │   │
│  │ Runs pg_dump...  │   │
│  │         [Run ▶]  │   │
│  └──────────────────┘   │
│                         │
└─────────────────────────┘
```

**Tabs:**
1. **Tasks**: List of available tasks (native/global/user). Each shows name, description, trigger types. "Run" button opens a sheet to select target file/directory.
2. **Logs**: Recent task execution logs. Each shows task name, status (success/failure), timestamp, duration. Tap to see full log output.
3. **Processes**: Currently running background processes. Show name, status, uptime. No start/stop controls (too dangerous on mobile - manage via web).

**Task Runner:**
- When "Run" is tapped, show a bottom sheet:
  - Select target file/directory (mini file browser)
  - Confirm and run
  - Show real-time output in a scrollable log view
  - Toast on completion

---

### 6.8 Settings

```
┌─────────────────────────┐
│ Settings                │
├─────────────────────────┤
│                         │
│  ┌──────────────────┐   │
│  │ 👤 John Doe      │   │
│  │ john@example.com │   │
│  │ Super Admin      │   │
│  └──────────────────┘   │
│                         │
│  Profile                │
│  Name, Username, Avatar │
│  Change Password      → │
│  Passkeys             → │
│                         │
│  ─────────────────────  │
│                         │
│  App                    │
│  Theme           System │
│  Notifications       → │
│  Default Model  Claude  │
│  Biometric Lock    [🔘] │
│                         │
│  ─────────────────────  │
│                         │
│  Connection             │
│  Server  192.168.1.5 → │
│  Status     Connected 🟢│
│                         │
│  ─────────────────────  │
│                         │
│  [      Sign Out      ] │
│                         │
│  Officer v1.0.0         │
│                         │
└─────────────────────────┘
```

**Sections:**
1. **Profile card**: Avatar, name, email, role. Tap to edit.
2. **Profile**: Name/username editing, avatar upload (camera or gallery), password change, passkey management.
3. **App**: Theme (light/dark/system), notification preferences, default chat model/provider, biometric lock toggle.
4. **Connection**: Current server URL (editable), connection status indicator, last sync time.
5. **Sign Out**: Clears JWT, biometric keys, navigates to login.

---

## 7. Tablet Adaptations

Tablets (iPad, Android tablets/foldables) get an enhanced layout while using the same app binary. Detection via screen width:
- **Phone**: < 600dp (Android) / Compact (iOS)
- **Tablet**: >= 600dp (Android) / Regular (iOS)

### 7.1 Navigation

**Phone**: Bottom tab bar (5 tabs)
**Tablet**: Side navigation rail (Android) / Sidebar (iOS)

```
Tablet Layout:
┌────┬──────────────────────────────┐
│    │                              │
│ 🏠 │                              │
│ 💬 │       [Main Content]         │
│ 📁 │                              │
│ ⚡ │                              │
│    │                              │
│    │                              │
│ ⚙️ │                              │
└────┴──────────────────────────────┘
```

### 7.2 Chat on Tablet

**Split view**: Session list on left (1/3), conversation on right (2/3).

```
┌────┬──────────┬───────────────────┐
│    │Sessions  │ Debug login       │
│ 🏠 │──────────│ Claude 3.5 Sonnet │
│ 💬 │ Debug..  │───────────────────│
│ 📁 │ Refact.. │                   │
│ ⚡ │ Recipe.. │ [Messages...]     │
│    │          │                   │
│    │          │                   │
│    │          ├───────────────────│
│ ⚙️ │          │ [Input] [Send]    │
└────┴──────────┴───────────────────┘
```

### 7.3 Files on Tablet

**Split view**: Directory browser on left, file viewer on right. Tap a file to preview it in the right pane without navigating away.

```
┌────┬──────────┬───────────────────┐
│    │ / > Docs │ notes.md          │
│ 🏠 │──────────│───────────────────│
│ 💬 │ 📁 Proj  │ # Meeting Notes   │
│ 📁 │ 📁 Reps  │                   │
│ ⚡ │ 📄 note  │ ## Action Items   │
│    │ 🖼️ phot  │ - Fix login bug   │
│    │ 🎵 song  │ - Update docs     │
│    │          │                   │
│ ⚙️ │          │                   │
└────┴──────────┴───────────────────┘
```

### 7.4 Dashboard on Tablet

Widgets in a 2-3 column grid instead of single column. More widgets visible at once.

```
┌────┬──────────────────────────────┐
│    │ ┌──────┐ ┌──────┐ ┌──────┐  │
│ 🏠 │ │Clock │ │Weath.│ │Pomo  │  │
│ 💬 │ └──────┘ └──────┘ └──────┘  │
│ 📁 │ ┌─────────────┐ ┌──────────┐│
│ ⚡ │ │ Daily Goals  │ │ Quick    ││
│    │ │ ☑ Exercise   │ │ Notes    ││
│    │ │ ☐ Read 30min │ │ ...      ││
│    │ └─────────────┘ └──────────┘│
│ ⚙️ │ Workspaces                   │
│    │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
└────┴──────────────────────────────┘
```

### 7.5 Tablet-Only Features

| Feature | Description |
|---------|-------------|
| **Drag & Drop** | Drag files between panes, drag text into chat |
| **Keyboard Shortcuts** | External keyboard support with shortcuts (Cmd+N for new chat, etc.) |
| **Picture-in-Picture** | Video playback continues in PiP when navigating away |
| **Multi-Window** | Android: Split screen support. iPad: Stage Manager support. |

---

## 8. Technical Challenges

### 8.1 WebSocket Lifecycle on Mobile

**Problem**: Mobile OSes aggressively kill background connections. WebSocket to chat/terminal will disconnect when app is backgrounded.

**Solution**:
- Detect app going to background → gracefully close WebSocket
- On foreground → reconnect WebSocket, resume session
- Use `{ type: 'resume', sessionId }` to restore chat state
- Buffer any pending assistant output server-side (the server already does this - messages are saved to JSON files)
- Show "Reconnecting..." indicator when connection drops
- Implement exponential backoff for reconnection attempts

**Android specifics:**
- Use `ProcessLifecycleOwner` to detect app lifecycle
- Consider foreground service for active chat sessions (shows notification "Chat in progress")

**iOS specifics:**
- Use `UIApplication.willResignActiveNotification` / `didBecomeActiveNotification`
- Background URL session for critical requests
- Use `BGAppRefreshTask` for periodic sync

### 8.2 Push Notifications

**Problem**: The Officer API server currently has no push notification infrastructure.

**Required Backend Changes:**
1. New endpoint: `POST /api/devices/register` — stores FCM/APNs token per user
2. New database table: `DeviceTokens` (userId, token, platform, createdAt)
3. Server-side notification triggers:
   - Chat: When assistant finishes responding and user's WebSocket is disconnected
   - Tasks: When a long-running task completes
   - Downloads: When yt-dlp or git clone finishes
4. Firebase Admin SDK (Android) + APNs library (iOS) on the server

**Notification Types:**
| Type | Title | Body | Action |
|------|-------|------|--------|
| Chat complete | "Claude responded" | First 100 chars of response | Open chat session |
| Task complete | "Task finished" | "Format Code completed successfully" | Open task log |
| Download complete | "Download ready" | "video.mp4 downloaded" | Open file browser |
| Error | "Task failed" | "Backup DB failed: connection refused" | Open task log |

### 8.3 File Upload from Mobile

**Problem**: Mobile file uploads have different sources than web (camera, photo library, share sheet).

**Solution**:
- Use platform file pickers that support multiple sources
- Camera capture: Take photo → upload via `POST /api/file-browser/upload`
- Compress images before upload (configurable quality setting)
- Show upload progress (the existing XHR-based upload in `useFiles.ts` supports progress)
- Background upload for large files using WorkManager (Android) / BGTaskScheduler (iOS)
- Resume interrupted uploads if possible

### 8.4 Offline Support

**Problem**: Users may open the app with no connectivity.

**Strategy**: Read-heavy offline, write-queued.

| Feature | Offline Behavior |
|---------|-----------------|
| Dashboard | Cached widget data, local timer for pomodoro |
| Chat history | Cached session list and recent messages |
| New chat | Queue message, show "Will send when online" |
| File browser | Show last cached directory listing |
| File viewer | Show cached/downloaded files only |
| Settings | Show cached profile, edits queued |

**Local Database Schema (Room / SwiftData):**
```
ChatSessions: id, name, provider, lastMessagePreview, updatedAt, isArchived
ChatMessages: id, sessionId, role, content, timestamp (cache recent N per session)
DirectoryCache: path, root, entriesJson, cachedAt
FileCache: path, root, localPath, size, cachedAt (for downloaded/viewed files)
PendingActions: id, type, payload, createdAt, status
```

### 8.5 Deep Linking & Share Sheet

**URL Scheme**: `officer://`
**Universal Links**: `https://officer.dev/app/...` (if applicable)

**Deep Link Routes:**
- `officer://chat/{sessionId}` — open specific chat
- `officer://files?path=/Documents` — open file browser at path
- `officer://chat/new?prompt=...` — start new chat with prefilled text

**Share Sheet Integration (receiving):**
- Register app as share target for: text, URLs, images, files
- When content is shared to Officer:
  1. Show a sheet: "Send to Chat" or "Upload to Files"
  2. If Chat: open new chat with shared content as first message
  3. If Files: open file browser with upload in current directory

### 8.6 Image Handling in Chat

**Problem**: The web app sends images as base64 in the WebSocket message. Large images on mobile could cause memory issues.

**Solution**:
1. Before sending, resize images to max 2048px on longest edge
2. Compress to JPEG at 85% quality (unless PNG is needed for screenshots)
3. Convert to base64 and include in chat message
4. Show thumbnail in the message thread, tap to view full-size
5. Consider using the upload endpoint first, then sending attachment IDs instead of base64 (check if API supports this path — it does via `attachmentIds` in the chat message)

### 8.7 Markdown & Code Rendering

**Problem**: Rendering rich markdown with code blocks, tables, and images in native views.

**Android Solution:**
- Use a Markdown library (Markwon or compose-markdown)
- Code blocks: custom span with monospace font + syntax highlighting
- Consider a lightweight WebView-based renderer for complex markdown as fallback

**iOS Solution:**
- Use `AttributedString` with custom markdown parsing
- Or use a SwiftUI markdown library (MarkdownUI)
- Code blocks: custom view with monospace font + syntax highlighting

**Code Syntax Highlighting:**
- Android: Highlight.js via WebView, or a native library like CodeView
- iOS: Splash or Highlightr library

### 8.8 Streaming Text Display

**Problem**: Chat responses stream token-by-token via WebSocket. Need smooth text rendering without layout thrashing.

**Solution**:
- Buffer incoming tokens (e.g., collect for 50ms before rendering)
- Use a single mutable text state that appends
- Avoid re-rendering the entire message list on each token
- Only the last (active) message bubble needs updating
- Auto-scroll to bottom during streaming, unless user has scrolled up

### 8.9 Security

**Concerns:**
- JWT stored on device must be encrypted
- Biometric key binding for stored credentials
- Certificate pinning for API connections (optional but recommended for self-hosted)
- No sensitive data in logs
- Clear all data on sign out
- Jailbreak/root detection (optional)

**Android:**
- EncryptedSharedPreferences for tokens
- Android Keystore for biometric-bound keys
- ProGuard/R8 for release builds

**iOS:**
- Keychain with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`
- Secure Enclave for biometric keys
- App Transport Security (ATS) enforced

### 8.10 Self-Hosted Server Discovery

**Problem**: Officer is self-hosted. Users need to point the app at their server. The server might be on a local network, behind a VPN, or on the public internet.

**Solution**:
- First-launch: manual URL entry with validation
- Local network: mDNS/Bonjour discovery (optional nice-to-have)
- QR code scanning from web app settings page (generate QR with server URL + optional auth token)
- Store multiple server profiles (for users who have dev/prod setups)
- Connection status indicator in settings

---

## 9. API Integration Notes

### Endpoints the Mobile App Will Use

**Auth:**
- `POST /api/auth/signin` — login
- `POST /api/auth/signup` — register (if enabled)
- `POST /api/auth/signout` — logout
- `GET /api/auth/me` — validate token, get user info
- `POST /api/auth/change-password` — password change
- `*/passkeys/*` — passkey registration/auth

**Chat:**
- `GET /api/sessions?provider={provider}` — list sessions
- `DELETE /api/sessions/{provider}/{id}` — delete session
- `POST /api/sessions/{provider}/{id}/rename` — rename session
- `POST /api/sessions/{provider}/{id}/archive` — archive session
- WebSocket `/api/harness/{provider}/ws` — chat communication

**Files:**
- `GET /api/file-browser/ls?path={path}&root={root}` — list directory
- `GET /api/file-browser/read?path={path}` — read text file
- `GET /api/file-browser/raw?path={path}` — raw file (binary, supports Range)
- `GET /api/file-browser/search?q={query}` — search files
- `POST /api/file-browser/mkdir` — create directory
- `POST /api/file-browser/write` — write file
- `POST /api/file-browser/upload` — upload files (multipart)
- `POST /api/file-browser/rename` — rename
- `POST /api/file-browser/copy` — copy
- `POST /api/file-browser/move` — move
- `DELETE /api/file-browser/rm` — delete
- `GET /api/file-browser/download?path={path}` — download file
- `POST /api/file-browser/download` — download multiple as zip
- `GET /api/file-browser/transcode?path={path}` — transcode video
- `POST /api/file-browser/tts` — text-to-speech
- `POST /api/file-browser/ocr` — OCR image
- `POST /api/file-browser/transcribe` — transcribe audio
- `POST /api/file-browser/extract-audio` — extract audio from video
- `POST /api/file-browser/extract` — extract archive
- `POST /api/file-browser/git-clone` — clone git repo
- `POST /api/file-browser/download-video` — yt-dlp download

**Tasks:**
- `GET /api/tasks` — list tasks
- `GET /api/tasks/{name}` — task details
- `POST /api/tasks/{name}/run` — run task

**Task Logs:**
- `GET /api/task-logs` — list logs
- `GET /api/task-logs/{id}` — log details

**Processes:**
- `GET /api/processes` — list processes

**Settings:**
- `GET /api/user/settings` — user settings
- `PUT /api/user/settings` — update settings
- `PUT /api/auth/users` — update profile

**Upload:**
- `POST /api/upload` — upload attachments (for chat)

**Scrape:**
- `POST /api/scrape` — scrape URL (for sharing URLs to chat)

### New Endpoints Needed

| Endpoint | Purpose |
|----------|---------|
| `POST /api/devices/register` | Register push notification token |
| `DELETE /api/devices/{token}` | Unregister device on logout |
| `GET /api/sessions/{provider}/{id}/messages` | Get messages for a session (currently read from files, need API endpoint) |

### Authentication Header

All requests include: `Authorization: Bearer {jwt_token}`

WebSocket connections pass token as query parameter: `?token={jwt_token}`

---

## 10. Shared Design Language

While each platform follows its native design guidelines (Material 3 for Android, Human Interface Guidelines for iOS), some design elements should be consistent:

### Color Palette

From the web app's Tailwind theme:
- **Primary (Teal)**: `duck-teal` — used for primary actions, active states
- **Accent (Yellow)**: `duck-yellow` — used for text on primary, highlights
- **Dark**: `duck-dark` — text, icons
- **Background**: Light/dark mode adaptive

Both apps should use these brand colors while adapting to platform conventions:
- Android: Material 3 dynamic color with Officer palette as seed
- iOS: Officer palette with system-adaptive backgrounds

### Typography

- **Android**: Roboto (system default), with monospace for code
- **iOS**: SF Pro (system default), with SF Mono for code
- Both: Similar size scale, consistent heading hierarchy

### Iconography

- **Android**: Material Symbols (outlined)
- **iOS**: SF Symbols
- Map lucide-react icons used in web to platform equivalents

### Animations

- **Android**: Material Motion (shared element transitions, container transforms)
- **iOS**: Spring animations, matched geometry effects
- Both: Smooth transitions between screens, subtle feedback on interactions

---

## 11. Implementation Phases

### Phase 1: Foundation (MVP)

**Goal**: Core functionality that makes the app useful enough to ship.

| Feature | Details |
|---------|---------|
| Auth | Email/password login, token storage, biometric unlock |
| Server setup | URL configuration, validation |
| Chat | New chat, session list, real-time conversation with streaming |
| Chat basics | Text messages, model selection, session management |
| Settings | Profile view, theme, logout |

**Estimated screens**: Login, Home (simple), Chat List, Chat Conversation, Settings

### Phase 2: Files & Polish

**Goal**: File management and mobile-specific enhancements.

| Feature | Details |
|---------|---------|
| File browser | Directory listing, navigation, search |
| File viewer | Text, images, PDF, audio, video |
| File actions | Upload (camera, files), download, rename, delete |
| Chat attachments | Image attachment, voice input |
| Push notifications | Basic chat completion notifications |

**Estimated screens**: File Browser, File Viewer, enhanced Chat

### Phase 3: Productivity

**Goal**: Dashboard, tasks, and deeper integration.

| Feature | Details |
|---------|---------|
| Dashboard | Widget system (clock, weather, pomodoro, goals, notes) |
| Tasks | Task list, run tasks, view logs |
| Workspace quick access | Open workspaces (single-panel) |
| Share sheet | Receive content from other apps |
| Offline support | Cached data, queued actions |

**Estimated screens**: Dashboard, Task List, Task Logs, enhanced Home

### Phase 4: Tablet & Advanced

**Goal**: Tablet optimization and advanced features.

| Feature | Details |
|---------|---------|
| Tablet layouts | Split views for chat, files |
| Tablet navigation | Side rail / sidebar |
| Home screen widgets | Android widgets, iOS widgets |
| AI file actions | OCR, TTS, Transcribe from mobile |
| Multi-select | Batch file operations |
| Deep linking | URL scheme, universal links |

### Phase 5: Nice-to-Haves

| Feature | Details |
|---------|---------|
| QR code server setup | Scan from web to configure |
| Local network discovery | mDNS/Bonjour |
| Picture-in-Picture | Video PiP on tablet |
| Passkey auth | Full WebAuthn support |
| Keyboard shortcuts | External keyboard on tablet |
| Skills browser | View and search skills library |
| Background downloads | yt-dlp and git clone in background |

---

## Appendix: Decision Log

| Decision | Rationale |
|----------|-----------|
| Native (Kotlin + Swift) over cross-platform | User preference. Also gives best performance, platform-native UX, and full access to platform APIs (biometrics, push, background tasks, share sheet). |
| Exclude terminal | Phone keyboards make shell commands impractical. The sandboxed Docker setup is inherently desktop-oriented. |
| Exclude code editor (write) | Writing code on a phone is poor UX. Read-only with syntax highlighting is sufficient for review. |
| Exclude automation builder | Complex form-heavy UI that doesn't translate to small screens. |
| Include chat as primary feature | AI assistant in your pocket is the strongest mobile use case. Voice input makes it even more compelling on mobile. |
| Bottom tabs (phone) / Side rail (tablet) | Standard navigation patterns for each form factor. 5 top-level destinations fits within guidelines. |
| Offline-first for reads, queue for writes | Mobile connectivity is unreliable. Showing cached data is better than empty screens. |
| Phase 1 = Auth + Chat | Chat is the killer feature. Get it right first, everything else is complementary. |
