Skip to content

feat(web): add terminal.focus and chat.focusComposer keybindings - #5961

Open
achtan wants to merge 3 commits into
pingdotgg:mainfrom
achtan:t3code/terminal-focus-and-chat-composer-focus-actions
Open

feat(web): add terminal.focus and chat.focusComposer keybindings#5961
achtan wants to merge 3 commits into
pingdotgg:mainfrom
achtan:t3code/terminal-focus-and-chat-composer-focus-actions

Conversation

@achtan

@achtan achtan commented Aug 10, 2026

Copy link
Copy Markdown

Part of #4641 (items 4 and 5).

Problem

Focus is coupled to panel visibility. Opening the terminal drawer requests terminal focus and closing it returns focus to the composer, but there is no keyboard route between the two while the drawer stays open: type-to-focus bails when focus sits in any input or textarea, and the terminal renders a textarea, so typing in the terminal can never bring you back. The workaround today is toggling the drawer twice with mod+j.

What changed

  • packages/contractsterminal.focus and chat.focusComposer join STATIC_KEYBINDING_COMMANDS. Older clients drop unknown commands through the existing ForwardCompatibleArray decoding, so no compatibility work was needed.

  • packages/shared — both ship as one default key with mutually exclusive when clauses, so a single key hops focus in either direction without touching drawer visibility:

    { "key": "ctrl+`", "command": "terminal.focus", "when": "!terminalFocus" },
    { "key": "ctrl+`", "command": "chat.focusComposer", "when": "terminalFocus" }

    ctrl+` follows VS Code's focus-terminal binding and is unclaimed by the existing defaults on every platform. Each command is still independently rebindable; the startup default sync backfills both rules into existing configs.

  • apps/web (ChatView) — dispatch in the existing global shortcut handler.

    • terminal.focus picks one target via a pure resolveTerminalFocusTarget decision (unit-tested): the right-panel terminal when it is the only terminal showing (drawer closed, or right panel maximized), otherwise the drawer — opening it when closed, where the closed→open transition already requests focus and creates the first session. The right-panel terminal now consumes its own panelTerminalFocusRequestId — bumped by the panel's own lifecycle actions (add, split, activate, close, surface activation) and by terminal.focus when the panel is the target — so a focus request lands on exactly one surface instead of broadcasting to both terminal hosts.
    • chat.focusComposer calls the existing focusComposer() and leaves the terminal open.
    • Both commands first un-maximize the right panel when their target lives in the chat column, which a maximized panel collapses to zero width — without this, focus would land on an invisible element.
  • Settings → Keybindings — no changes needed: labels derive from the command IDs ("Terminal: Focus", "Chat: Focus Composer") and both commands appear in the add-binding dropdown because they ship as defaults.

  • docs/user/keybindings.md — short note on the pair and how it differs from terminal.toggle.

Verification

  • vp test run on the touched suites: web keybindings 46 passed (4 new: the ctrl+` pair resolves per terminalFocus context on macOS and Windows, against the real shipped defaults so the rules are proven to parse), ChatView logic 43 passed (5 new covering every resolveTerminalFocusTarget branch), keybindings settings logic 9 passed (label + dropdown coverage), server keybindings 22 passed (default backfill is generic over DEFAULT_KEYBINDINGS).

  • tsgo --noEmit clean for contracts, shared, and web; vp lint clean on touched files.

  • Demo video (25s): ctrl-backtick-focus-demo.webm — recorded in a real browser against a dev build of this branch. It shows: typing in the composer → ctrl+` opens the drawer and focuses the terminal (an echo typed immediately after the keypress lands at the shell prompt) → ctrl+` hops back to the composer with the drawer still open → a third press hops into the already-open terminal (pwd) → a final press returns to the composer. No mouse after the first click.

    Final state:

    final state: composer text typed across two hops, terminal transcript with echo and pwd

Built with Claude Fable 5 (claude-fable-5) via Claude Code.


Note

Low Risk
UI focus and keybinding routing only; no auth, data, or API changes. Logic is isolated and covered by new unit tests.

Overview
Adds terminal.focus and chat.focusComposer so users can hop between the message composer and terminal without toggling the drawer (addresses the gap where typing cannot return focus from the terminal textarea).

Defaults: Both bind to ctrl+` with opposite when: terminalFocus clauses in shared keybindings; contracts register the new static commands.

Behavior: ChatView handles the commands in the global shortcut handler. resolveTerminalFocusTarget (pure, unit-tested) chooses the right-panel terminal vs drawer vs open-drawer, including when the right panel is maximized. Right-panel terminal actions bump a separate panelTerminalFocusRequestId so focus requests hit one surface instead of drawer + panel together. Either command un-maximizes the right panel when focus must land in the chat column.

Docs and settings tests cover labels and the new commands in the keybindings UI.

Reviewed by Cursor Bugbot for commit 488b424. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add terminal.focus and chat.focusComposer keybindings mapped to Ctrl+`

  • Pressing Ctrl+` outside the terminal focuses the terminal: targeting the panel terminal if active, or opening/focusing the drawer otherwise.
  • Pressing Ctrl+` while inside the terminal focuses the message composer and collapses a maximized right panel if needed.
  • Adds resolveTerminalFocusTarget in ChatView.logic.ts to determine which terminal surface (panel, drawer, or open-drawer) to target.
  • Introduces a dedicated panelTerminalFocusRequestId state separate from terminalFocusRequestId so panel terminal actions no longer interfere with drawer focus requests.
  • Behavioral Change: panel terminal actions (addTerminalSurface, splitPanelTerminal, etc.) now increment panelTerminalFocusRequestId instead of terminalFocusRequestId.

Macroscope summarized 488b424.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 788c1747-a060-4715-b0b6-9df2041b6711

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 10, 2026
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/ChatView.tsx
@macroscopeapp

macroscopeapp Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds new user-facing keybindings (terminal.focus and chat.focusComposer) that modify focus management behavior. While well-structured and tested, new capabilities from a first-time contributor warrant human review.

You can customize Macroscope's approvability policy. Learn more.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e3adb5b. Configure here.

Comment thread apps/web/src/components/ChatView.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant