* Add a mobile native-chat opt-in so users pick terminal vs chat once
Mirror the notifications one-time opt-in for the native-chat default view.
After pairing, a full-screen modal (modeled on notification-opt-in) lets the
user choose whether supported agent sessions open in the terminal or in native
chat, then persists the choice to the existing orca:defaultSessionView key.
- Expose readDefaultSessionViewPreference() (tri-state; absent key = undecided)
so the gate can prompt exactly once; loadDefaultSessionView() is unchanged.
- shouldPresentSessionViewOptIn() gates the screen; the home focus effect shows
it after the notification opt-in.
- Settings -> Native chat toggle (already shipped) remains the recovery path.
* fix(mobile): preserve onboarding flow after pairing
* refine mobile session view opt-in copy
* Unify mobile onboarding prompts
Re-pairing a desktop that was already paired created a duplicate host card (STA-1840). Pairing now resolves the durable host identity by the desktop's pinned publicKeyB64 and reuses the existing id/name, collapses any already-stored duplicates for that key, clears stale relay overlays on a direct-only re-pair, fails closed on unreadable storage, and closes the host's client on pairing success so a reused id reconnects on the newly-paired endpoint. Mobile only. Full mobile suite (1,719 tests) + typecheck/lint/format pass.
* Add worktree-list sidebar for mobile tablet/foldable layouts
On wide canvases (tablet/foldable, >=700pt) the per-host worktree list now
renders as a persistent left sidebar with the routed screens (terminal,
source control, review, accounts, tasks) shown in a detail pane to its
right — mirroring the desktop's sidebar + center layout. Phones keep the
existing single-pane stack navigation unchanged.
- Reuse the existing worktree-list screen as the sidebar via an `embedded`
mode (props for hostId/action, mount-driven fetch since a sidebar is never
the focused route, hide-sidebar control in place of the back button, and
open-into-detail-pane navigation that replaces rather than stacks).
- The default host route renders an empty WorkspaceDetailPlaceholder on wide
layouts (the list lives in the sidebar) and the full screen on phones, so
exactly one instance mounts either way.
- Hide button collapses the sidebar to give the detail pane full width; an
elevated reveal tab brings it back.
- Detail-pane screen transitions use `animation: 'none'` while the split is
active so workspaces swap instantly instead of sliding and flashing the
screen beneath.
- Drag the sidebar's right edge to resize (clamped 280-560pt, detail pane
kept >=320pt, tap-transparent so list rows still work); width persists via
AsyncStorage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Address PR review feedback (#5505)
- Clamp the restored/persisted sidebar width against the current window and
re-clamp when the window shrinks, so a width saved on a larger device can't
starve the detail pane below MIN_DETAIL_WIDTH. Extract a shared
clampSidebarToWindow helper reused by load, window-resize, and drag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Polish mobile tablet sidebar
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
When pairing fails (e.g. broken Tailscale route, firewall blocking the
WS, expired token, etc.) the mobile app previously got stuck on
'Connecting…' forever with no signal about *where* it stalled.
Adds a structured connection-log stream from the rpc client and renders
it under the 'Connecting…' spinner on both pair-scan and pair-confirm.
Each phase emits a timestamped, color-coded entry: Opening WebSocket,
WebSocket open, Sent e2ee_hello, Received e2ee_ready, Authenticated,
WebSocket closed / Reconnect scheduled / Connect timeout / Handshake
timeout, etc. The log auto-scrolls and stays visible after a failure so
the user can see the last successful step.
Also fixes the silent infinite spinner: pairing now has a 25s overall
timeout — rpc-client retries forever by design (right behaviour for
live sessions), but for the *initial* pair we want a hard ceiling that
surfaces an actionable error with the log visible instead of spinning.
- Adds ConnectionLogEntry / ConnectionLogSink to transport/types.ts
- connect() now accepts { onStateChange, onLog } (legacy fn form kept)
- New ConnectionLog component (mono, level-coloured, +Xs elapsed)
- Pair flows track logs in a ref and render them in connecting/error
- 25s pairing-overall timeout that closes the client and surfaces a
'see log below for where it stalled' error
Co-authored-by: Orca <help@stably.ai>