fix(app): prevent terminal mount from stealing focus (#36576)

This commit is contained in:
Luke Parker
2026-07-14 21:49:57 +10:00
committed by GitHub
parent 729bd438e4
commit a1ed99278a
7 changed files with 245 additions and 44 deletions
@@ -28,7 +28,6 @@ import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff"
import { useSessionLayout } from "@/pages/session/session-layout"
export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
const delays = [120, 240]
const layout = useLayout()
const terminal = useTerminal()
const sdk = useSDK()
@@ -46,6 +45,8 @@ export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
let root: HTMLDivElement | undefined
let tabList: HTMLDivElement | undefined
onCleanup(() => terminal.cancelFocus())
const [store, setStore] = createStore({
autoCreated: false,
recovered: {} as Record<string, boolean>,
@@ -94,36 +95,12 @@ export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
),
)
const focus = (id: string) => {
focusTerminalById(id)
const frame = requestAnimationFrame(() => {
if (!opened()) return
if (terminal.active() !== id) return
focusTerminalById(id)
})
const timers = delays.map((ms) =>
window.setTimeout(() => {
if (!opened()) return
if (terminal.active() !== id) return
focusTerminalById(id)
}, ms),
)
return () => {
cancelAnimationFrame(frame)
for (const timer of timers) clearTimeout(timer)
}
}
createEffect(
on(
() => [opened(), terminal.active()] as const,
([next, id]) => {
if (!next || !id) return
const stop = focus(id)
onCleanup(stop)
() => [opened(), terminal.active(), terminal.focusRequested(terminal.active())] as const,
([next, id, requested]) => {
if (!next || !id || !requested) return
focusTerminalById(id)
},
),
)
@@ -304,7 +281,7 @@ export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
icon="plus-small"
variant="ghost"
iconSize="large"
onClick={terminal.new}
onClick={() => terminal.new({ focus: true })}
aria-label={language.t("command.terminal.new")}
/>
</TooltipKeybind>
@@ -326,7 +303,7 @@ export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
icon="plus-small"
variant="ghost"
iconSize="large"
onClick={terminal.new}
onClick={() => terminal.new({ focus: true })}
aria-label={language.t("command.terminal.new")}
/>
</TooltipV2>
@@ -344,7 +321,8 @@ export function TerminalPanelV2(props: { stacked?: boolean } = {}) {
<div id={`terminal-wrapper-${id}`} class="absolute inset-0">
<Terminal
pty={pty()}
autoFocus={opened()}
autoFocus={terminal.focusRequested(id)}
onAutoFocus={() => terminal.consumeFocus(id)}
class="!px-[14px]"
onConnect={() => markTerminalConnected(terminalRecoveryKey(pty()), id, ops.trim)}
onCleanup={ops.update}
@@ -38,6 +38,8 @@ export function TerminalPanel() {
const close = () => view().terminal.close()
let root: HTMLDivElement | undefined
onCleanup(() => terminal.cancelFocus())
const [store, setStore] = createStore({
autoCreated: false,
activeDraggable: undefined as string | undefined,
@@ -285,7 +287,7 @@ export function TerminalPanel() {
icon="plus-small"
variant="ghost"
iconSize="large"
onClick={terminal.new}
onClick={() => terminal.new()}
aria-label={language.t("command.terminal.new")}
/>
</TooltipKeybind>
@@ -303,6 +305,7 @@ export function TerminalPanel() {
<Terminal
pty={pty()}
autoFocus={opened()}
onAutoFocus={() => terminal.consumeFocus(id)}
onConnect={() => markTerminalConnected(terminalRecoveryKey(pty()), id, ops.trim)}
onCleanup={ops.update}
onConnectError={() => recoverTerminal(terminalRecoveryKey(pty()), id, ops.clone)}
@@ -264,7 +264,8 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
}
const openTerminal = () => {
if (terminal.all().length > 0) terminal.new()
if (terminal.all().length > 0) terminal.new({ focus: true })
if (terminal.all().length === 0) terminal.requestFocus()
view().terminal.open()
}
@@ -498,7 +499,15 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
title: language.t("command.terminal.toggle"),
keybind: "ctrl+`",
slash: "terminal",
onSelect: () => view().terminal.toggle(),
onSelect: () => {
if (view().terminal.opened()) {
terminal.cancelFocus()
view().terminal.close()
return
}
terminal.requestFocus(terminal.active())
view().terminal.open()
},
}),
viewCommand({
id: "review.toggle",