mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 00:03:15 +00:00
fix(chat): close covered terminal focus fallbacks
This commit is contained in:
@@ -48,6 +48,7 @@ export function TerminalPaneSurface({
|
||||
dismissTerminalError,
|
||||
expectedLayoutLeafIdsAttr,
|
||||
expandedPaneId,
|
||||
effectiveChatViewMode,
|
||||
handleCancelClose,
|
||||
handleConfirmClose,
|
||||
handleContextMenuToggleNativeChat,
|
||||
@@ -117,7 +118,7 @@ export function TerminalPaneSurface({
|
||||
className="absolute inset-0 min-h-0 min-w-0"
|
||||
data-native-file-drop-target="terminal"
|
||||
data-terminal-tab-id={tabId}
|
||||
data-terminal-chat-view={activePaneIsChatLeaf ? 'true' : undefined}
|
||||
data-terminal-chat-view={effectiveChatViewMode && activePaneIsChatLeaf ? 'true' : undefined}
|
||||
data-terminal-layout-leaf-ids={expectedLayoutLeafIdsAttr}
|
||||
data-pane-title-surface={titleUsesLightSurface ? 'light' : 'dark'}
|
||||
style={terminalContainerStyle}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { PaneManager } from '@/lib/pane-manager/pane-manager'
|
||||
|
||||
const NATIVE_CHAT_ROOT_SELECTOR = '[data-native-chat-root]'
|
||||
const NATIVE_CHAT_COVER_SELECTOR = '.native-chat-pane-shell'
|
||||
|
||||
/**
|
||||
* Leaf container selector that excludes panes whose xterm sits under the native
|
||||
* chat portal. Chat mode is a tab flag, but only the chat leaf's xterm is
|
||||
* covered — a split terminal leaf in the same tab must still take focus.
|
||||
*/
|
||||
export const UNCOVERED_TERMINAL_LEAF_SELECTOR = `[data-leaf-id]:not(:has(${NATIVE_CHAT_ROOT_SELECTOR}))`
|
||||
export const UNCOVERED_TERMINAL_LEAF_SELECTOR = `[data-leaf-id]:not(:has(${NATIVE_CHAT_COVER_SELECTOR}))`
|
||||
|
||||
export function paneIsCoveredByNativeChat(
|
||||
pane: { container: Pick<Element, 'querySelector'> } | null | undefined
|
||||
): boolean {
|
||||
return pane?.container.querySelector(NATIVE_CHAT_ROOT_SELECTOR) != null
|
||||
return pane?.container.querySelector(NATIVE_CHAT_COVER_SELECTOR) != null
|
||||
}
|
||||
|
||||
/** Mirrors focusActivePane's target so the guard tracks exactly the pane that would take focus. */
|
||||
|
||||
+1
-1
@@ -356,7 +356,7 @@ describe('useTerminalPaneGlobalEffects', () => {
|
||||
isChatViewMode: true
|
||||
})
|
||||
|
||||
expect(pane.container.querySelector).toHaveBeenCalledWith('[data-native-chat-root]')
|
||||
expect(pane.container.querySelector).toHaveBeenCalledWith('.native-chat-pane-shell')
|
||||
if (covered) {
|
||||
expect(mocks.focusActivePane).not.toHaveBeenCalled()
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,9 @@ vi.mock('@/components/terminal-pane/terminal-ime-input-context-refresh', () => (
|
||||
|
||||
// Why: tab-wide queries skip leaves whose xterm sits under the native chat portal.
|
||||
const TAB_HELPER_SELECTOR =
|
||||
'[data-terminal-tab-id="tab-1"] [data-leaf-id]:not(:has([data-native-chat-root])) .xterm-helper-textarea'
|
||||
'[data-terminal-tab-id="tab-1"] [data-leaf-id]:not(:has(.native-chat-pane-shell)) .xterm-helper-textarea'
|
||||
const GLOBAL_HELPER_SELECTOR =
|
||||
'[data-leaf-id]:not(:has(.native-chat-pane-shell)) .xterm-helper-textarea'
|
||||
|
||||
describe('focusTerminalTabSurface', () => {
|
||||
afterEach(() => {
|
||||
@@ -145,6 +147,34 @@ describe('focusTerminalTabSurface', () => {
|
||||
expect(coveredTextarea.focus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not use a covered chat helper as the global mount-race fallback', () => {
|
||||
flushAnimationFrames()
|
||||
const coveredTextarea = { focus: vi.fn() }
|
||||
vi.stubGlobal('document', {
|
||||
querySelector: vi.fn((selector: string) =>
|
||||
selector === '.xterm-helper-textarea' ? coveredTextarea : null
|
||||
)
|
||||
})
|
||||
|
||||
focusTerminalTabSurface('tab-1')
|
||||
|
||||
expect(coveredTextarea.focus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps the global mount-race fallback for an uncovered terminal helper', () => {
|
||||
flushAnimationFrames()
|
||||
const textarea = { focus: vi.fn() }
|
||||
vi.stubGlobal('document', {
|
||||
querySelector: vi.fn((selector: string) =>
|
||||
selector === GLOBAL_HELPER_SELECTOR ? textarea : null
|
||||
)
|
||||
})
|
||||
|
||||
focusTerminalTabSurface('tab-1')
|
||||
|
||||
expect(textarea.focus).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('falls back to the single tab helper when an old leaf id was reminted', () => {
|
||||
flushAnimationFrames()
|
||||
const textarea = { focus: vi.fn() }
|
||||
|
||||
@@ -105,7 +105,9 @@ export function focusTerminalTabSurface(
|
||||
}
|
||||
return
|
||||
}
|
||||
const fallback = document.querySelector('.xterm-helper-textarea') as HTMLElement | null
|
||||
const fallback = document.querySelector(
|
||||
`${UNCOVERED_TERMINAL_LEAF_SELECTOR} .xterm-helper-textarea`
|
||||
) as HTMLElement | null
|
||||
if (fallback) {
|
||||
focusTerminalHelper(fallback, options)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user