From 8d5987cdd4fc850d2c37035d596e56c7319edcaa Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Thu, 21 May 2026 23:48:10 -0700 Subject: [PATCH] =?UTF-8?q?feat(terminal):=20focus=20management=20?= =?UTF-8?q?=E2=80=94=20terminal=20on=20open,=20composer=20on=20close?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Opening terminal: xterm.focus() called after fit in the visibility effect - Closing terminal: ProseMirror editor focused via querySelector after rAF - Works for all paths: pill click, ⌘+J toggle, trash icon kill --- desktop/src/features/terminal/TerminalContext.tsx | 15 +++++++++++++++ .../src/features/terminal/TerminalInstance.tsx | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/desktop/src/features/terminal/TerminalContext.tsx b/desktop/src/features/terminal/TerminalContext.tsx index 99f6aed007..2b103c8968 100644 --- a/desktop/src/features/terminal/TerminalContext.tsx +++ b/desktop/src/features/terminal/TerminalContext.tsx @@ -35,6 +35,21 @@ export function TerminalProvider({ children }: { children: React.ReactNode }) { }; }, []); + // Focus management: return focus to composer when terminal closes. + const prevOpenRef = React.useRef(isOpen); + React.useEffect(() => { + if (prevOpenRef.current && !isOpen) { + // Terminal just closed — focus the composer. + requestAnimationFrame(() => { + const editor = document.querySelector( + '[data-testid="message-composer"] .ProseMirror', + ); + editor?.focus(); + }); + } + prevOpenRef.current = isOpen; + }, [isOpen]); + const value = React.useMemo( () => ({ isOpen, toggle, close }), [isOpen, toggle, close], diff --git a/desktop/src/features/terminal/TerminalInstance.tsx b/desktop/src/features/terminal/TerminalInstance.tsx index 28bab7c878..37248c492a 100644 --- a/desktop/src/features/terminal/TerminalInstance.tsx +++ b/desktop/src/features/terminal/TerminalInstance.tsx @@ -212,11 +212,12 @@ export function TerminalInstance({ }; }, [channelId, isVisible]); - // Re-fit when visibility changes. + // Re-fit and focus when visibility changes. React.useEffect(() => { if (isVisible && fitAddonRef.current) { requestAnimationFrame(() => { fitAddonRef.current?.fit(); + termRef.current?.focus(); if (sessionIdRef.current && termRef.current) { invoke("terminal_resize", { input: {