feat(web): setting to keep the terminal from taking focus automatically - #8372
feat(web): setting to keep the terminal from taking focus automatically#8372darahaas15 wants to merge 4 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
There was a problem hiding this comment.
One finding: gating the focusRequestId effect on the new setting also suppresses focus for explicit user actions (new/split/close terminal, terminal tab activation), which goes beyond the setting's documented scope and can leave keyboard focus on nothing. Details inline.
Posted via Macroscope — UI Consistency
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This adds a persisted user-facing setting and changes production terminal focus behavior across ChatView and the terminal lifecycle, including asynchronous mount and visibility transitions. The implementation is broader than a simple settings-only change and lacks new tests for the runtime focus paths. You can add or adjust custom eligibility rules. Learn more. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5823e3e. Configure here.
There was a problem hiding this comment.
One finding on the new focus-gating logic in TerminalViewport: the mount-time focus grant (mountFocusPending) is handed to every viewport mounted on a request render, including split panes that are not the request target, and it never expires — so it can be redeemed by a later automatic reveal even when terminalAutoFocus is off. Details inline.
Separately, the focus decision is now a four-signal protocol (focusRequestId, autoFocus, visible, mountFocusPending) plus three refs, and none of it is covered by a test. This file already follows the pattern of exporting pure predicates (shouldHandleTerminalExit, shouldHandleTerminalSelectionMouseUp) and unit-testing them in ThreadTerminalDrawer.test.ts; extracting the permission rule (shouldAutoFocus || (autoFocus && mountFocusPending)) and the reveal/request-change transitions into such a predicate would make the new state transitions verifiable without changing behavior.
Posted via Macroscope — UI Consistency

What Changed
A new client setting: Settings → General → "Terminal auto-focus" (
terminalAutoFocus, default on, so behavior is unchanged for existing users). When off, the embedded terminal never takes keyboard focus on its own: not when you switch to a thread whose terminal is open, and not when the drawer opens. Direct interaction still focuses it: clicking into the terminal, and terminal actions such as creating, splitting, closing, or activating one.The gating splits automatic focus from explicit focus requests (refined per the review feedback below). Automatic paths honor the setting: the surface-ready and first-output focus in
TerminalViewport, its reveal effect on the hidden-to-visible transition (thread activation, drawer open), and the drawer-open request bump inChatView. The focus-request id carries only explicit actions - encoding visibility or the setting into it would turn their transitions, including async settings hydration, into spoofed requests. Action-driven focus requests (terminal create/split/close/tab activation) always focus, so terminal actions cannot drop keyboard focus ondocument.body. Two supporting changes make the split sound: the focus-request id is now passed to persistent drawers unconditionally (the previous swap-to-0 made thread activation itself look like a request), and a viewport mounted by the same render as a request (the single-terminal view remounts on create/close/activate) carries it across the remount via a mount-captured flag. Focus permission is checked at frame time through an effect event, so a setting that hydrates or toggles after scheduling wins over the captured render. Both terminal surfaces (bottom drawer and right-panel terminal) render throughTerminalViewport, so both respect the setting. The row is searchable, has a reset button, and participates in restore-defaults.Why
Switching threads with
thread.next/thread.previous(⌘⇧] / ⌘⇧[) orthread.jump.Ndrops focus into the terminal whenever the target thread has one open. Once the Ghostty surface has focus it owns keystrokes:keybindings.tsalready documents that the jump commands cannot run there regardless ofwhenclauses, and the jump hints are deliberately hidden whileterminalFocusfor the same reason. So keyboard-driven thread navigation dead-ends on the first thread with an open terminal, and every jump costs an extra ⌘J or a click to get focus back.This is a setting rather than a behavior change because auto-focus is the right default for people who open a terminal to type into it. Keyboard-heavy users who mostly watch agent terminals can opt out and keep focus in the composer.
UI Changes
One standard
SettingsRow+Switchin Settings → General, placed with the other interaction-behavior rows and reusing the archive/delete-confirmation row pattern verbatim. Happy to attach before/after screenshots if wanted.Checklist
Note
Medium Risk
Terminal focus is intricate keyboard UX; regressions could break thread navigation or terminal shortcuts, though the default preserves prior auto-focus behavior.
Overview
Adds a Terminal auto-focus client setting (
terminalAutoFocus, default on) so existing behavior stays the same until users turn it off in Settings → General.When off, the embedded terminal no longer steals keyboard focus on automatic reveals—opening the drawer, switching to a thread with an open terminal, surface-ready mount, or first output. Explicit terminal actions (create, split, close, tab activate) still focus the terminal so focus does not fall to
document.bodywhen a viewport remounts.The focus model is split so
focusRequestIdcarries only action-driven requests (visibility and settings are no longer mixed into that counter).TerminalViewportgates automatic paths via frame-time permission checks and a one-shotmountFocusPendingflag for remounts tied to explicit requests;ChatViewonly bumps the focus request when the drawer opens if auto-focus is enabled.Reviewed by Cursor Bugbot for commit 8f49ff3. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add
terminalAutoFocussetting to control terminal drawer auto-focusterminalAutoFocusfield (defaulttrue) toClientSettingsSchemaand the patch schema in settings.tsautoFocusis truefocusRequestIdno longer zeroes for inactive threads and no longer changes on visibility transitions, preventing spurious focus on activationfocusRequestIdis now always the currentterminalFocusRequestIdinstead of zeroed for inactive threads; hidden drawers ignore requests. Reviewers should verify inactive-thread terminal activation still behaves correctly.Macroscope summarized 8f49ff3.