Fix split terminal tab focus target (#6375)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Brennan Benson 2026-06-25 13:33:16 -07:00 committed by GitHub
parent d16082015a
commit bf9fde16d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 5 deletions

View File

@ -133,6 +133,7 @@ function resetStore(): void {
reconcileWorktreeTabModel: vi.fn(() => ({ renderableTabCount: 0 })),
settings: { activeRuntimeEnvironmentId: null },
tabsByWorktree: { 'wt-1': [terminalTab] },
terminalLayoutsByTabId: {},
unifiedTabsByWorktree: { 'wt-1': [unifiedTab] },
activateTab: mocks.activateTab,
closeBrowserTab: mocks.closeBrowserTab,
@ -186,7 +187,35 @@ describe('useTabGroupWorkspaceModel terminal activation focus', () => {
expect(mocks.activateTab).toHaveBeenCalledWith('unified-terminal-1')
expect(mocks.setActiveTab).toHaveBeenCalledWith('terminal-1')
expect(mocks.setActiveTabType).toHaveBeenCalledWith('terminal')
expect(mocks.focusTerminalTabSurface).toHaveBeenCalledWith('terminal-1')
expect(mocks.focusTerminalTabSurface).toHaveBeenCalledWith('terminal-1', null)
})
it('returns keyboard focus to the active split pane leaf when a terminal tab is activated', async () => {
storeBox.state = {
...storeBox.state,
terminalLayoutsByTabId: {
'terminal-1': {
activeLeafId: 'right-leaf',
ptyIdsByLeafId: {
'left-leaf': 'pty-left',
'right-leaf': 'pty-right'
},
root: {
type: 'split',
direction: 'horizontal',
first: { type: 'leaf', leafId: 'left-leaf' },
second: { type: 'leaf', leafId: 'right-leaf' }
},
expandedLeafId: null
}
}
}
const { useTabGroupWorkspaceModel } = await import('./useTabGroupWorkspaceModel')
const model = useTabGroupWorkspaceModel({ groupId: 'group-1', worktreeId: 'wt-1' })
model.commands.activateTerminal('terminal-1')
expect(mocks.focusTerminalTabSurface).toHaveBeenCalledWith('terminal-1', 'right-leaf')
})
it('toggles pane expansion from the split-group tab bar collapse button', async () => {

View File

@ -44,6 +44,9 @@ const EMPTY_GROUPS: readonly TabGroup[] = []
const EMPTY_UNIFIED_TABS: readonly Tab[] = []
const EMPTY_BROWSER_TABS: readonly BrowserTabState[] = []
const EMPTY_TERMINAL_TABS: readonly TerminalTab[] = []
const EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID: NonNullable<
ReturnType<typeof useAppStore.getState>['terminalLayoutsByTabId']
> = {}
type TerminalTabItem = TerminalTab & { unifiedTabId: string }
@ -67,6 +70,7 @@ export function useTabGroupWorkspaceModel({
openFiles: state.openFiles,
browserTabs: state.browserTabsByWorktree[worktreeId] ?? EMPTY_BROWSER_TABS,
expandedPaneByTabId: state.expandedPaneByTabId,
terminalLayoutsByTabId: state.terminalLayoutsByTabId ?? EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID,
generatedTabTitlesEnabled: state.settings?.tabAutoGenerateTitle === true,
mobileEmulatorEnabled: state.settings?.mobileEmulatorEnabled !== false
}))
@ -376,11 +380,21 @@ export function useTabGroupWorkspaceModel({
}
setActiveTab(terminalId)
setActiveTabType('terminal')
// Why: clicking the tab button gives the browser focus to the tab strip
// after pointerdown; explicitly return it to xterm on the next frames.
focusTerminalTabSurface(terminalId)
const activeLeafId = worktreeState.terminalLayoutsByTabId[terminalId]?.activeLeafId ?? null
// Why: split terminal tab activation must restore xterm focus to the
// store-active leaf so keyboard input cannot drift to a sibling pane.
focusTerminalTabSurface(terminalId, activeLeafId)
},
[activateTab, focusGroup, groupId, groupTabs, setActiveTab, setActiveTabType, worktreeId]
[
activateTab,
focusGroup,
groupId,
groupTabs,
setActiveTab,
setActiveTabType,
worktreeState.terminalLayoutsByTabId,
worktreeId
]
)
const toggleTerminalPaneExpand = useCallback(