When the desktop restored a terminal from mobile-fit, the notification
only reached the Electron renderer via IPC. Mobile WebSocket clients
were never informed, leaving the phone UI stuck in fitted mode.
Add fit-override change listeners to the runtime (parallel to existing
dataListeners) and emit fit-override-changed events through the
terminal.subscribe stream. The mobile session screen handles the event
by clearing fitted state and resubscribing for a fresh scrollback
snapshot at the restored desktop dimensions.
Co-authored-by: Orca <help@stably.ai>
- Add network interface selector so users can choose LAN or overlay
network (Tailscale, ZeroTier) address for QR code pairing
- Fall back to OS-assigned port when preferred port is in use
- Filter paired devices list to only show actually connected devices
- Poll device list after QR generation until phone connects
- Mark device lastSeenAt on first WebSocket connection
- Make CustomKeyModal toggle and button monochrome (remove blue accent)
- Make clear filters text and group options monochrome
Co-authored-by: Orca <help@stably.ai>
- Redesign home screen with "Welcome back" hero and usage stats cards
(agents spawned, agent time, PRs created) fetched via stats.summary RPC
- Add dedicated notifications settings page with monochrome push toggle
- Add troubleshooting page with diagnostics and common issues accordion
- Add splash screen with Orca logo using expo-splash-screen
- Fix pin sync: prioritize pinned/unread worktrees in compareWorktreePs
sort so they survive the 200-worktree truncation limit in worktree.ps
- Sync local pin state from server on each poll to reflect desktop pins
- Add isPinned support to worktree.set RPC handler
- Add stats.summary and notification subscription RPC methods
- Clean up debug logging from notification handlers
Co-authored-by: Orca <help@stably.ai>
- BottomDrawer: shift drawer up when keyboard opens using Keyboard events,
add ScrollView for content overflow
- NewWorktreeModal: replace FlatList with plain map to avoid nested
VirtualizedList error
- Home: replace Server icon with Monitor, hide IP from cards (show in
action sheet), add connection status subtitle, update hero copy
- About: remove redundant link labels, use neutral text color for URLs
Co-authored-by: Orca <help@stably.ai>
Remove duplicate onPtyData call in pty.ts that caused every daemon-backed
PTY data event to be dispatched twice through dataListeners, doubling all
terminal output sent to mobile clients.
Also: clean up registerSubscriptionCleanup to evict stale listeners on
reconnect, add regression tests, fix CI lint/type errors, add mobile UX
improvements (worktree creation, settings, about screen).
Co-authored-by: Orca <help@stably.ai>
- Add shared BottomDrawer component with pan gesture tracking via reanimated
- Drawer follows finger on swipe down, dismisses past threshold or on velocity
- Rubber-band resistance on upward pull for tactile feel
- Migrate all 5 modal components to use BottomDrawer
- Replace plain text QR instructions with numbered steps with icons
Co-authored-by: Orca <help@stably.ai>
Use performAndroidHapticsAsync on Android which uses HapticFeedbackConstants
instead of the Vibrator API. This fixes haptics not working on Android and
removes the VIBRATE permission requirement.
Co-authored-by: Orca <help@stably.ai>
- Add expo-haptics medium impact feedback on all long-press interactions
(terminal tabs, worktree cards, host cards)
- Add "Sleep" option with Moon icon to worktree action sheet, calling
worktree.sleep RPC method
- Change send button from bright blue (accentBlue) to subtle dark
(bgRaised) background with textSecondary icon color
Co-authored-by: Orca <help@stably.ai>
Replace the disabled TLS cert pinning approach with application-layer
E2EE using tweetnacl (Curve25519 ECDH + XSalsa20-Poly1305). The
WebSocket stays plain ws:// but all RPC traffic is encrypted after
a one-round-trip handshake.
Desktop: generate persistent Curve25519 keypair, E2EE channel module
handles handshake state machine and transparent encrypt/decrypt.
Mobile: ephemeral keypair per connection for forward secrecy, expo-crypto
PRNG polyfill for Hermes compatibility.
Pairing schema updated from v1 (certFingerprint) to v2 (publicKeyB64).
Mock server updated with E2EE support. 24 tests covering crypto,
channel handshake, and integration.
Co-authored-by: Orca <help@stably.ai>
Add module-level Map to persist which terminals have been phone-fitted,
so navigating away and back auto-refits them instead of reverting to
desktop dimensions. Also adds fitPendingRef to prevent duplicate fits.
Co-authored-by: Orca <help@stably.ai>
Replace native WebView scroll/zoom with custom touch handlers that
work correctly with the CSS scale transform:
- Single-finger scroll with scale-compensated sensitivity and momentum
- Pinch-to-zoom (1x-5x) centered on pinch point, with pan when zoomed
- Tap/click suppression to prevent cursor jumping
- Row fill: resize xterm viewport to fill WebView height (display-only)
- Disable native zoom (user-scalable=no) since custom handler replaces it
Co-authored-by: Orca <help@stably.ai>
Add end-to-end mobile-fit override system that lets a phone client resize
a PTY to phone dimensions and restore to desktop dimensions on disconnect
or user request.
Key changes:
- Runtime: resizeForClient() manages override lifecycle with ownership,
clamping, and auto-restore on client disconnect. Always resizes PTY
directly on restore instead of relying on fragile renderer chain.
- RPC: terminal.resizeForClient method for mobile clients via WebSocket.
- Renderer: safeFit() respects active overrides, desktop banner with
Restore button, override change listener with rAF + fallback safety net.
- Mobile: fit-to-phone with CSS scale zoom, auto-fit only for terminals
created during the mobile session (not existing desktop terminals).
- Tests: 14 unit tests + 3 integration tests for override lifecycle.
Co-authored-by: Orca <help@stably.ai>
Use the WebView's native viewport scaling and pinch-to-zoom instead of
custom CSS transforms and touch handlers. The terminal renders at
desktop dimensions with initial-scale=0.5 for a readable default, and
the browser's built-in zoom handles pinch correctly (center point,
scroll directions, no swipe conflicts).
Co-authored-by: Orca <help@stably.ai>
Adds the Orca Mobile companion app (Expo SDK 55 / React Native 0.83) with:
- QR code pairing flow: scan orca://pair# URL, validate auth, save host profile
- WebSocket RPC client with auto-reconnect, exponential backoff, streaming support
- Three screens: host list, worktree list, terminal session with live output
- Mock WebSocket server for development without a running Orca desktop instance
- Desktop-side: TLS certificate management, device registry, mobile IPC handlers,
WebSocket transport layer, and pairing offer encode/decode with Zod validation
- Unit tests for WebSocket transport and pairing codec
Co-authored-by: Orca <help@stably.ai>