Skip to content

fix(web): Ctrl+B reaches a focused terminal again - #34588

Open
sti0 wants to merge 2 commits into
pingdotgg:mainfrom
sti0:fix/terminal-ctrl-b
Open

sti0 wants to merge 2 commits into
pingdotgg:mainfrom
sti0:fix/terminal-ctrl-b

Conversation

@sti0

@sti0 sti0 commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On Linux and Windows, pressing Ctrl+B in a focused terminal toggles the sidebar and never reaches the shell, so tmux with its default prefix is unusable. The sidebar toggle (mod+b) is handled by a capture-phase window listener in AppSidebarLayout.tsx. Since #29612 that listener passes terminalFocus, which helps only users who add when: "!terminalFocus" to the binding themselves. The default mod+b has no when, so the listener still claims Ctrl+B before xterm sees it.

Reproduce: open a terminal, run tmux, press Ctrl+B then c. The sidebar toggles and no tmux window is created.

Change

The listener keeps the focus and panel context from #29612 and now goes through a new isSidebarToggleShortcut helper next to the existing is*Shortcut helpers in keybindings.ts. The helper skips Ctrl chords while a terminal has focus. Cmd never reaches the terminal, so on macOS Cmd+B still toggles the sidebar from a focused terminal, the same as today. The user keybinding docs gain one sentence about this.

I did not add when: "!terminalFocus" to the default mod+b binding, which is the pattern many other defaults use. On macOS it would turn off Cmd+B in the terminal, which is a working shortcut that does not conflict with anything there.

Scope and approval

This is a small fix for an obvious bug, so there is no prior issue. A default binding blocks a standard terminal control character on two of three platforms. Most other app-level defaults in packages/shared/src/keybindings.ts already keep out of the focused terminal, and sidebar.toggle was the gap. It is one problem in one listener. The desktop menu action and the shortcuts forwarded from browser previews are untouched, and macOS behavior is unchanged.

Reversible: yes

Blast radius: The sidebar toggle shortcut in the web and desktop clients. Mobile does not use this listener.

Before / after

Linux, web client: run cat -v in the focused terminal, then press Ctrl+B. On main the sidebar collapses and the shell receives nothing. With the fix the sidebar stays open and the shell echoes ^B. The key and caption overlays were added for the recording and are not part of the UI.

Before After
tmux_before.webm
tmux_after.webm

Verification

  • In the real web client (Linux, Chromium), vp run scenarios:prove fails on base a8ccf14d06 and passes on head. The scenario opens the terminal drawer, runs read -r key | od -c, and presses Ctrl+B. On base the sidebar collapses (expected "collapsed" to be "expanded") and the shell gets nothing. On head the sidebar stays open and the shell receives ^B (002).
  • vp test run apps/web/src/keybindings.test.ts: the new case "leaves Ctrl+B to a focused terminal" fails against main's logic (expected true to be false) and passes with the fix. The macOS Cmd+B case passes before and after.
  • vp run check:changed: all checks pass.
  • Not checked: macOS, the desktop app, and Windows. On macOS the tests cover Cmd+B, and the desktop app goes through the same web listener.
Scenario used for the proof (not part of this PR)
import { readFile } from "node:fs/promises";
import { join } from "node:path";

import { expect } from "e2e";

import { test, WEB } from "./t3.ts";

const sidebarState = `document.querySelector('[data-slot="sidebar-wrapper"]')?.getAttribute("data-sidebar-state")`;
const terminalFocused = `document.activeElement?.closest("[data-terminal-owner]") !== null`;

test("Ctrl+B in a focused terminal reaches the shell", WEB, async ({ t3, app, screen, browser }) => {
  const project = await t3.project("demo");
  await t3.open();
  await expect(screen.getByRole("textbox")).toBeVisible();

  await screen.getByRole("button", "Toggle terminal drawer").tap();
  await expect.poll(() => browser.evaluate(terminalFocused)).toBe(true);
  const sidebarBefore = await browser.evaluate(sidebarState);

  // The terminal renders to a canvas, so a file shows when the shell takes input.
  await expect
    .poll(async () => {
      await browser.keyboard.type("touch ready.txt");
      await browser.keyboard.press("Enter");
      return readFile(join(project, "ready.txt")).then(() => true, () => false);
    })
    .toBe(true);
  await browser.keyboard.type(`read -r key && printf %s "$key" | od -An -c > ctrl-b.txt`);
  await browser.keyboard.press("Enter");
  await browser.keyboard.press("Control+b");
  await browser.keyboard.press("Enter");
  await app.screenshot("after-ctrl-b");

  expect(await browser.evaluate(sidebarState)).toBe(sidebarBefore);
  await expect
    .poll(() => readFile(join(project, "ctrl-b.txt"), "utf8").catch(() => ""))
    .toContain("002");
});

Written by Claude Opus 5.5 in T3 Code.

@github-actions github-actions Bot added vouch:unvouched Author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 11, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 3a37691

Macroscope's review found this PR approvable — The sidebar stops intercepting Ctrl shortcuts while a terminal is focused, allowing tmux's Ctrl+B prefix through. The change is confined to keyboard routing; Cmd shortcuts and menu actions remain available.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The sidebar toggle now uses terminal-focus context when matching its shortcut. While the terminal is focused, Ctrl-modified events do not toggle the sidebar. On macOS, Cmd+B still toggles it.

Changes: Sidebar toggle shortcut

Layer / File(s) Summary
Terminal-aware shortcut matching
apps/web/src/keybindings.ts, apps/web/src/components/AppSidebarLayout.tsx, apps/web/src/keybindings.test.ts, docs/user/keybindings.md
isSidebarToggleShortcut excludes Ctrl-modified events while the terminal is focused. The sidebar handler passes terminal-focus and Usage-page context. Tests cover Linux Ctrl+B and macOS Cmd+B. The documentation describes the behavior.

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix

Suggested reviewers: saphid, juliusmarminge

Merge Risk: Minimal · up to 9a5a2

Ctrl+B now reaches a focused terminal, so tmux and shell prefixes work again, while Cmd+B on macOS still toggles the sidebar. No actionable merge-blocking risk was found.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the main change: Ctrl+B is no longer captured by the sidebar toggle when a terminal is focused, so it reaches the terminal. The raw summary confirms the new isSidebarToggleShortcut hel…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description follows the repository template closely. It has a Problem section that explains the Ctrl+B conflict with tmux on Linux and Windows, with reproduction steps. It has a Change section tha…
Full details: Title check

Explanation

The title names the main change: Ctrl+B is no longer captured by the sidebar toggle when a terminal is focused, so it reaches the terminal. The raw summary confirms the new isSidebarToggleShortcut helper skips Ctrl chords while the terminal is focused. The title uses the conventional commit form with a plain-language description.

Full details: Description check

Explanation

The description follows the repository template closely. It has a Problem section that explains the Ctrl+B conflict with tmux on Linux and Windows, with reproduction steps. It has a Change section that names the new isSidebarToggleShortcut helper and explains why the default binding was not changed. It has a Scope section that states the fix is small and gives a reason for having no linked issue. It has a Verification section with focused test results, a scenario run on base and head, and a list of unchecked platforms. It has before/after recordings, and the author names the model in the closing line. The author's own verification and visual evidence are substantive. One gap is that the template asks for a conventional commit title, and the description does not show one. The description is otherwise mostly complete.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between e457a7a and 9a5a2b1.

Files selected for processing: 2

  • apps/web/src/components/AppSidebarLayout.tsx
  • apps/web/src/keybindings.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@sti0
sti0 force-pushed the fix/terminal-ctrl-b branch from 3a37691 to e457a7a Compare November 14, 2026 07:24
@maria-rcks

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

verified unchanged head e457a7a57b223edbe302f206cb0865936fb25f0d on linux using the real dev web app, repo Playwright, and /usr/bin/google-chrome.

created the same real git project through the app on both github/main (bee9eb5939) and the pr. selected codex gpt-6.1-sol, sent a request to read README.md through a shell command, and observed both turns complete. no mocks, fixtures, or demo data.

  • main reproduces the bug: ctrl+b collapses the sidebar from a focused terminal. ctrl+b then c leaves tmux at one window and types c at the shell prompt.
  • the pr passes: ctrl+b reaches both drawer and right-panel terminals as byte 2, without changing the sidebar. ctrl+b then c creates tmux window 1 alongside window 0.
  • nearby behavior passes: ctrl+b toggles the sidebar from an empty composer, bolds selected composer text, and ctrl+c still interrupts a shell command and allows the next command to run.
  • vp test run apps/web/src/keybindings.test.ts passed all 47 tests through blacksmith after vp i. head ci has no failing or pending checks. no source changes or pushes were needed.

main after ctrl+b then c:

main: sidebar collapsed and tmux still has one window

pr after the same keys:

pr: sidebar stays open and tmux has windows 0 and 1

command transcript

Not verified

native electron, macos cmd+b, windows ctrl+b, ios/android, remote/relay/tunnel connections, custom keybinding configurations, and other provider runtimes were not exercised on this linux vm. the existing macos shortcut assertion passed in the focused suite.

sti0 and others added 2 commits November 15, 2026 09:30
The sidebar toggle listens for mod+b in the capture phase and ignored
terminal focus, so on Linux and Windows it swallowed Ctrl+B before the
terminal saw it. tmux with its default prefix was unusable.

The toggle now skips Ctrl chords while a terminal has focus. Cmd never
reaches the terminal, so Cmd+B still toggles the sidebar from a focused
terminal on macOS.

Written by Claude Opus 5.5 in T3 Code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…debar toggle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sti0
sti0 force-pushed the fix/terminal-ctrl-b branch from e457a7a to 9a5a2b1 Compare November 15, 2026 08:30
@sti0

sti0 commented Nov 15, 2026

Copy link
Copy Markdown
Contributor Author

Note

🤖 Claude Opus 5.5 replying on behalf of Timo

@maria-rcks thanks for verifying e457a7a. Since then I rebased the branch onto main (f2c9449) and verified it again.

The rebase picked up #29612, which touched the same sidebar listener. It now passes terminalFocus, previewFocus and the panel state to the resolver. I kept that context and applied this PR's guard on top. #29612 alone does not fix the bug: it only helps when a user adds when: "!terminalFocus" to the binding themselves. The default mod+b has no when, so on main Ctrl+B still collapses the sidebar from a focused terminal and never reaches the shell.

Verified on the new head 9a5a2b1:

  • vp run scenarios:prove in the real web client fails on base f2c9449 (expected "collapsed" to be "expanded") and passes on head, where the shell receives ^B.
  • vp test run apps/web/src/keybindings.test.ts apps/web/src/components/AppSidebarLayout.browser.test.tsx passes 48/48, including the browser test fix(web): the sidebar shortcut honors terminal and preview focus conditions #29612 added.
  • vp run check:changed f2c9449: format, lint, knip, typecheck, tests and react compiler all pass.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants