fix(mobile): preserve terminal creation through reconnect

This commit is contained in:
Brennan Benson
2026-07-29 18:16:45 -07:00
parent 216f1d49a4
commit e04db518b2
3 changed files with 50 additions and 4 deletions
@@ -44,7 +44,8 @@ describe('mobile session startup', () => {
expect(autoCreateHookSource).toContain('shouldAutoCreateInitialSessionTerminal({')
expect(autoCreateHookSource).toContain('stateRef.current.autoCreatedForWorktree === worktreeId')
expect(autoCreateHookSource).toContain('stateRef.current.autoCreatedForWorktree = worktreeId')
expect(autoCreateHookSource).toContain('newlyCreatedWorkspace && terminalsLoaded')
expect(autoCreateHookSource).toContain("connState === 'connected'")
expect(autoCreateHookSource).toContain('(visibleTabCount > 0 || activeHandle !== null)')
// Why: both callbacks are re-created every render, so the effect must reach them
// through useEffectEvent rather than deps or a render-time ref write.
expect(autoCreateHookSource).toContain('useEffectEvent(args.consumeCreationRoute)')
@@ -11,6 +11,8 @@ type HarnessProps = {
terminalsLoaded: boolean
visibleTabCount: number
worktreeId: string
connected?: boolean
hasClient?: boolean
}
describe('useInitialSessionTerminalAutoCreate', () => {
@@ -24,9 +26,9 @@ describe('useInitialSessionTerminalAutoCreate', () => {
function Harness(props: HarnessProps): null {
useInitialSessionTerminalAutoCreate({
client: {},
client: props.hasClient === false ? null : {},
newlyCreatedWorkspace: props.newlyCreatedWorkspace,
connState: 'connected',
connState: props.connected === false ? 'disconnected' : 'connected',
terminalsLoaded: props.terminalsLoaded,
visibleTabCount: props.visibleTabCount,
activeHandle: null,
@@ -91,6 +93,15 @@ describe('useInitialSessionTerminalAutoCreate', () => {
visibleTabCount: 0,
worktreeId: 'new'
})
expect(consumeCreationRoute).not.toHaveBeenCalled()
expect(createTerminal).toHaveBeenCalledOnce()
await render({
newlyCreatedWorkspace: true,
terminalsLoaded: true,
visibleTabCount: 1,
worktreeId: 'new'
})
expect(consumeCreationRoute).toHaveBeenCalledOnce()
expect(createTerminal).toHaveBeenCalledOnce()
})
@@ -118,6 +129,34 @@ describe('useInitialSessionTerminalAutoCreate', () => {
})
expect(createTerminal).not.toHaveBeenCalled()
})
it.each([
{ connected: false, hasClient: true },
{ connected: true, hasClient: false }
])(
'keeps the creation route armed until a client reconnects (connected=$connected, hasClient=$hasClient)',
async ({ connected, hasClient }) => {
await render({
newlyCreatedWorkspace: true,
terminalsLoaded: true,
visibleTabCount: 0,
worktreeId: 'new',
connected,
hasClient
})
expect(consumeCreationRoute).not.toHaveBeenCalled()
expect(createTerminal).not.toHaveBeenCalled()
await render({
newlyCreatedWorkspace: true,
terminalsLoaded: true,
visibleTabCount: 0,
worktreeId: 'new'
})
expect(consumeCreationRoute).not.toHaveBeenCalled()
expect(createTerminal).toHaveBeenCalledOnce()
}
)
})
describe('useWorktreeSessionTabsLoaded', () => {
@@ -52,7 +52,13 @@ export function useInitialSessionTerminalAutoCreate(
const createTerminal = useEffectEvent(args.createTerminal)
useEffect(() => {
if (newlyCreatedWorkspace && terminalsLoaded) {
if (
newlyCreatedWorkspace &&
client &&
connState === 'connected' &&
terminalsLoaded &&
(visibleTabCount > 0 || activeHandle !== null)
) {
consumeCreationRoute()
}
if (