diff --git a/mobile/app/h/[hostId]/session/[worktreeId].tsx b/mobile/app/h/[hostId]/session/[worktreeId].tsx index 60fccf736b9..d1140466592 100644 --- a/mobile/app/h/[hostId]/session/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/session/[worktreeId].tsx @@ -71,6 +71,7 @@ const ACCESSORY_KEYS: AccessoryKey[] = [ // Capped to avoid unbounded growth over long app sessions. const MAX_PERSISTED_WORKTREES = 50 const persistedFittedHandles = new Map>() +const FIT_LAYOUT_SETTLE_MS = 250 const STATUS_LABELS: Record = { connecting: 'Connecting', @@ -81,6 +82,16 @@ const STATUS_LABELS: Record = { 'auth-failed': 'Auth failed' } +function waitForFitLayoutSettle(): Promise { + return new Promise((resolve) => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + setTimeout(resolve, FIT_LAYOUT_SETTLE_MS) + }) + }) + }) +} + function TerminalPaneView({ handle, active, @@ -286,6 +297,10 @@ export default function SessionScreen() { if (!clientId) return const seq = subscribeSeqRef.current.get(handle) + // Why: the first session render can measure the WebView before the + // accessory/input chrome has fully settled. Waiting matches the remount + // path, where phone-fit already gets the correct unobscured height. + await waitForFitLayoutSettle() const dims = await getTerminalRef(handle)?.measureFitDimensions() if (!dims) return if (