fix: suppress false zero-dimensions terminal error for hidden panes (#6475) (#6576)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil 2026-06-28 16:15:50 -07:00 committed by GitHub
parent b6c0cb4f62
commit 5ac7119c13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 77 additions and 5 deletions

View File

@ -95,6 +95,7 @@ import { isPrimarySelectionEnabled, readPrimarySelectionText } from '@/lib/prima
import { APP_MENU_PASTE_EVENT } from '@/lib/app-menu-paste'
import { WORKSPACE_FILE_PATH_MIME, WORKSPACE_FILE_PATHS_MIME } from '@/lib/workspace-file-drag'
import { isTerminalSessionStateSaveFailure } from '../../../../shared/terminal-session-state-save-failure'
import { isTerminalZeroDimensionsDiagnostic } from '../../../../shared/terminal-zero-dimensions-diagnostic'
import {
isSyntheticSinglePaneTitle,
sanitizeTerminalLayoutPaneTitles
@ -612,6 +613,12 @@ export default function TerminalPane({
// after first visibility lets inactive agent tabs refit and SIGWINCH.
setShouldMeasureHiddenStartup(false)
}
if (isVisible) {
// Why: a hidden pane that connected at 0×0 self-heals via the pane resize
// observer once shown, so clear that stale diagnostic. Scoped to the
// zero-dimensions message so genuine paste/save-failure errors survive.
setTerminalError((prev) => (prev && isTerminalZeroDimensionsDiagnostic(prev) ? null : prev))
}
}, [isVisible, shouldMeasureHiddenStartup])
const clearSessionRestoredBannerForPane = useCallback((paneId: number): void => {

View File

@ -667,6 +667,42 @@ describe('connectPanePty', () => {
logSpy.mockRestore()
}, 30_000)
// Why: orchestration workers and CLI `terminal create` (no --focus) mount
// hidden panes that legitimately connect at 0×0 and refit when shown, so the
// zero-dimensions diagnostic must stay silent while the pane is not visible.
it('does not surface the zero-dimensions diagnostic for a hidden pane', async () => {
const { connectPanePty } = await import('./pty-connection')
const transport = createMockTransport()
transportFactoryQueue.push(transport)
const pane = createPane(1)
pane.terminal.cols = 0
pane.terminal.rows = 0
const deps = createDeps({ isVisibleRef: { current: false } })
connectPanePty(pane as never, createManager(1) as never, deps as never)
await flushAsyncTicks()
expect(deps.onPtyErrorRef.current).not.toHaveBeenCalled()
})
it('still surfaces the zero-dimensions diagnostic for a visible pane', async () => {
const { connectPanePty } = await import('./pty-connection')
const transport = createMockTransport()
transportFactoryQueue.push(transport)
const pane = createPane(1)
pane.terminal.cols = 0
pane.terminal.rows = 0
const deps = createDeps({ isVisibleRef: { current: true } })
connectPanePty(pane as never, createManager(1) as never, deps as never)
await flushAsyncTicks()
expect(deps.onPtyErrorRef.current).toHaveBeenCalledWith(
pane.id,
expect.stringContaining('Terminal has zero dimensions (0×0)')
)
})
it('threads the resolved local project runtime into IPC terminal transport options', async () => {
const { connectPanePty } = await import('./pty-connection')
const transport = createMockTransport()

View File

@ -10,6 +10,7 @@ import { scheduleRuntimeGraphSync } from '@/runtime/sync-runtime-graph'
import { useAppStore } from '@/store'
import { getWorktreeMapFromState } from '@/store/selectors'
import { parseWorkspaceKey } from '../../../../shared/workspace-scope'
import { createTerminalZeroDimensionsMessage } from '../../../../shared/terminal-zero-dimensions-diagnostic'
import type { PtyBufferSnapshot, PtyConnectResult } from './pty-transport'
import { createIpcPtyTransport } from './pty-transport'
import { createRemoteRuntimePtyTransport } from './remote-runtime-pty-transport'
@ -2203,11 +2204,12 @@ export function connectPanePty(
// Why: if fitAddon resolved to 0×0, the container likely has no layout
// dimensions (display:none, unmounted, or zero-size parent). Surface a
// diagnostic so the user sees something instead of a blank pane.
if (cols === 0 || rows === 0) {
deps.onPtyErrorRef?.current?.(
pane.id,
`Terminal has zero dimensions (${cols}×${rows}). The pane container may not be visible.`
)
// Gate on visibility: background/hidden tabs (orchestration workers, CLI
// `terminal create` without --focus) legitimately connect at 0×0 because
// safeFit skips fitting unmeasurable panes; they refit via the pane resize
// observer once shown, so the diagnostic must not fire while hidden.
if ((cols === 0 || rows === 0) && deps.isVisibleRef.current) {
deps.onPtyErrorRef?.current?.(pane.id, createTerminalZeroDimensionsMessage(cols, rows))
}
const reportError = (message: string): void => {

View File

@ -0,0 +1,16 @@
import { describe, expect, it } from 'vitest'
import {
createTerminalZeroDimensionsMessage,
isTerminalZeroDimensionsDiagnostic
} from './terminal-zero-dimensions-diagnostic'
describe('terminal zero-dimensions diagnostic', () => {
it('round-trips its own message through the matcher', () => {
expect(isTerminalZeroDimensionsDiagnostic(createTerminalZeroDimensionsMessage(0, 0))).toBe(true)
})
it('does not match unrelated terminal errors', () => {
expect(isTerminalZeroDimensionsDiagnostic('Paste failed.')).toBe(false)
expect(isTerminalZeroDimensionsDiagnostic('Failed to save terminal session state')).toBe(false)
})
})

View File

@ -0,0 +1,11 @@
// Why: the zero-dimensions diagnostic is emitted by the renderer's PTY connect
// path and later cleared once a hidden pane becomes visible and refits. Keeping
// the message text and its matcher together lets both sites stay in sync.
export function createTerminalZeroDimensionsMessage(cols: number, rows: number): string {
return `Terminal has zero dimensions (${cols}×${rows}). The pane container may not be visible.`
}
export function isTerminalZeroDimensionsDiagnostic(message: string): boolean {
return message.startsWith('Terminal has zero dimensions (')
}