fix(mobile): harden native-chat resize suppression

This commit is contained in:
Brennan Benson
2026-07-22 15:41:51 -07:00
parent 59e5c48aad
commit fc3916b641
5 changed files with 11 additions and 2 deletions
@@ -7,6 +7,7 @@ export type TerminalViewportRefitTargetState = {
expectedHandle: string
currentRef: unknown
expectedRef: unknown
nativeChatCovered: boolean
disposed: boolean
runSeq: number
currentRunSeq: number
@@ -94,6 +95,7 @@ export function isTerminalViewportRefitTargetCurrent(
state: TerminalViewportRefitTargetState
): boolean {
return (
!state.nativeChatCovered &&
!state.disposed &&
state.runSeq === state.currentRunSeq &&
state.activeHandle === state.expectedHandle &&
@@ -313,6 +313,7 @@ describe('terminal viewport refit', () => {
expectedHandle: 'term-1',
currentRef: expectedRef,
expectedRef,
nativeChatCovered: false,
disposed: false,
runSeq: 2,
currentRunSeq: 2
@@ -325,5 +326,8 @@ describe('terminal viewport refit', () => {
).toBe(false)
expect(isTerminalViewportRefitTargetCurrent({ ...current, currentRunSeq: 3 })).toBe(false)
expect(isTerminalViewportRefitTargetCurrent({ ...current, disposed: true })).toBe(false)
expect(isTerminalViewportRefitTargetCurrent({ ...current, nativeChatCovered: true })).toBe(
false
)
})
})
@@ -116,6 +116,7 @@ export function useTerminalViewportRefit(
expectedHandle: handle,
currentRef: terminalRefs.current.get(handle),
expectedRef: ref,
nativeChatCovered: nativeChatCoveredRef.current,
disposed: disposedRef.current,
runSeq,
currentRunSeq: refitRunSeqRef.current
+2 -1
View File
@@ -2540,7 +2540,8 @@ export const TERMINAL_METHODS: RpcAnyMethod[] = [
.then(() => runtime.cleanupSubscription(subscriptionId))
.catch(() => runtime.cleanupSubscription(subscriptionId))
try {
await runtime.handleMobileSubscribe(ptyId, clientId, params.viewport)
// Why: a lease-only subscriber has no terminal view, so its cached viewport must never phone-fit the PTY.
await runtime.handleMobileSubscribe(ptyId, clientId, undefined)
if (closed || signal?.aborted) {
// Why: a disconnect can win the awaited subscribe and resurrect mobile presence after cleanup already released it.
runtime.handleMobileUnsubscribe(ptyId, clientId)
@@ -12,12 +12,13 @@ const request: RpcRequest = {
params: {
terminal: 'terminal-1',
client: { id: 'phone-1', type: 'mobile' },
viewport: { cols: 40, rows: 20 },
capabilities: { terminalBinaryStream: 1, mobileInputLeaseOnly: 1 }
}
}
describe('terminal lease-only subscription', () => {
it('keeps mobile input ownership without registering output delivery', async () => {
it('keeps mobile input ownership without viewport resize or output delivery', async () => {
const messages: string[] = []
const cleanups = new Map<string, () => void>()
const runtime = {