From 7ed49f5222a842ff6e088a11d8a8b6607b028f40 Mon Sep 17 00:00:00 2001 From: Grimmer Kang Date: Thu, 22 Oct 2026 19:29:12 +0800 Subject: [PATCH] fix(settings): refocus the search box when Settings closes The Popup's focus trap holds focus in the panel while it is open. Closing it with the Settings button left focus on that button, and the switcher's click-to-refocus skips it because the button is marked as part of the panel, so typing went nowhere until another click. The panel now calls onClosed after it closes, deferred past the trap's own deferred focus return. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 1 + src/popup.tsx | 17 +++++++++++++++++ src/switcher-ui.tsx | 1 + 3 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8727171..bfab844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Feat: **Ghostty switches to the right tab when several sessions share a directory** ([#63](https://github.com/grimmerk/codev/issues/63)). Ghostty's tip builds expose each terminal's `tty` (ghostty-org/ghostty#11922), so switching now works the way it does in iTerm2 and Terminal.app: by tty and by `/rename` title, in the order the session's process provenance calls for, and only then by working directory. Before, a session without a custom title went to the first tab in its directory — measured on one machine with 16 sessions sharing `~/git`, every one of them landed on the same tab. Detection's fallback cross-reference uses the tty the same way. **Stable Ghostty (1.3.1 and earlier) has no `tty`**, so there CodeV behaves exactly as before — title, then directory — until a release ships it - Fix: detection's fallback cross-reference no longer gives a running process to the wrong session when one `/rename` title contains another (`Fix` inside `Fix backend`). Every terminal (iTerm2, cmux, Terminal.app, Ghostty) took the first title its tab matched, from every transcript in the directory; it now takes the longest match among the sessions actually queued for that process. One function for all four, where there used to be four copies +- Fix: **closing Settings puts the cursor back in the search box.** Opening Settings moves focus into its panel, as before; but closing it with the same button left focus on that button, so typing searched nothing until you clicked a row or switched apps and back. The Settings button counts as part of the panel, which the switcher deliberately never steals focus from, so its click-to-refocus skipped it. Closing the panel now hands focus back to the current tab's search box ## 1.0.93 diff --git a/src/popup.tsx b/src/popup.tsx index d871811..7da38ec 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -59,6 +59,7 @@ const PopupDefaultExample = ({ switcherMode, openToTab, onOpenToTabConsumed, + onClosed, }: { workingFolderPath?: string; saveCallback?: (key: string, value: string) => void; @@ -66,6 +67,7 @@ const PopupDefaultExample = ({ switcherMode?: string; openToTab?: 'general' | 'sessions' | 'accounts' | 'shortcuts' | null; onOpenToTabConsumed?: () => void; + onClosed?: () => void; }) => { const [isOpen, setIsOpen] = useState(false); const [launchAtLogin, setLaunchAtLogin] = useState(false); @@ -505,6 +507,21 @@ const PopupDefaultExample = ({ } }, [openToTab]); + // Hand focus back to the parent when the panel closes. While open, the + // Popup's focus trap holds focus inside the panel; the Settings button that + // closes it keeps focus afterwards, and the switcher's click-to-refocus + // skips it (it is part of the panel), so typing went nowhere until the user + // clicked elsewhere. The timeout runs after any focus the trap returns on + // deactivate, which it also defers with a 0ms timeout. + const wasOpenRef = useRef(false); + useEffect(() => { + const wasOpen = wasOpenRef.current; + wasOpenRef.current = isOpen; + if (!wasOpen || isOpen || !onClosed) return; + const timer = setTimeout(onClosed, 0); + return () => clearTimeout(timer); + }, [isOpen]); + const handleLaunchAtLoginChange = (checked: boolean) => { setLaunchAtLogin(checked); window.electronAPI.setLoginItemSettings(checked); diff --git a/src/switcher-ui.tsx b/src/switcher-ui.tsx index 6d2c0f1..7c544d6 100644 --- a/src/switcher-ui.tsx +++ b/src/switcher-ui.tsx @@ -2635,6 +2635,7 @@ function SwitcherApp() { switcherMode={mode} openToTab={settingsOpenToTab} onOpenToTabConsumed={() => setSettingsOpenToTab(null)} + onClosed={forceFocusOnInput} saveCallback={(key: string, value: string) => { if (key === 'sessionDisplayMode') { setSessionDisplayMode(value);