diff --git a/src/renderer/src/components/tab-bar/TabBar.tsx b/src/renderer/src/components/tab-bar/TabBar.tsx index 93dd7662002..60683920d3f 100644 --- a/src/renderer/src/components/tab-bar/TabBar.tsx +++ b/src/renderer/src/components/tab-bar/TabBar.tsx @@ -162,6 +162,27 @@ export default function TabBar({ [sortableIds, worktreeId, onReorder] ) + const focusTerminalTabSurface = useCallback((tabId: string) => { + // Why: creating a terminal from the "+" menu is a two-step focus race: + // React must first mount the new TerminalPane/xterm, then Radix closes the + // menu. Even after suppressing trigger focus restore, the terminal's hidden + // textarea may not exist until the next paint. Double-rAF waits for that + // commit so the new tab, not the "+" button, ends up owning keyboard focus. + requestAnimationFrame(() => { + requestAnimationFrame(() => { + const scoped = document.querySelector( + `[data-terminal-tab-id="${tabId}"] .xterm-helper-textarea` + ) as HTMLElement | null + if (scoped) { + scoped.focus() + return + } + const fallback = document.querySelector('.xterm-helper-textarea') as HTMLElement | null + fallback?.focus() + }) + }) + }, []) + // Horizontal wheel scrolling for the tab strip const tabStripRef = useRef(null) useEffect(() => { @@ -250,7 +271,7 @@ export default function TabBar({ - +