#3184 · Browser-focused host shortcuts are filtered out
Verdict: PARTIALLY REPRODUCED · Root-cause confidence: high
1. TL;DR
When an embedded page owns focus, Electron sends the keystroke to that page’s separate webContents. BB already intercepts that event, but its resolver only accepts bindings whose context explicitly includes browserFocus. The desktop numbered-thread bindings include mainSurface but not browserFocus, so the resolver returns null and the host command is never dispatched. This was reproduced twice at the exact trusted main commit. The address-field and named third-party-plugin subclaims were not directly executed, so the overall verdict is partial.
2. Claims vs findings
| Claim | Status | Evidence |
|---|---|---|
| A numbered thread shortcut is lost while embedded page content owns focus. | Verified | The focused regression test expected thread.jump.1; the production resolver returned null in both clean runs. |
| The same behavior occurs in the browser address field. | Unverified | The address input remains inside the host renderer, under data-app-browser, and therefore takes a different event path than embedded page content. No native desktop interaction was used in this unit-level investigation. |
| A plugin’s page-level listener cannot receive keys focused in embedded page content. | Verified statically | Plugin content scripts run in the BB window, whose shortcut listener is a host-window keydown listener. Embedded content is intercepted from a separate Electron webContents. The public plugin surface has no host-owned shortcut registration contract. |
| Unmatched embedded-page shortcuts are left alone. | Verified | The interceptor returns without calling preventDefault() when the resolver returns null; the existing resolver/manager tests passed 63/63. |
3. Environment
- Trusted repository:
get-bb/bbat6cdb4ba6125514b7660332cf311037bc09c82b0e, confirmed as targetmainimmediately before both runs. - macOS 26.6.1 arm64; Node 22.22.3; pnpm 9.15.0.
- Both checkouts completed
pnpm install --frozen-lockfile --prefer-offlineandpnpm exec turbo run build. - No provider, server, port, user data directory, or live BB instance was used.
4. Minimal reproduction
- Check out trusted commit
6cdb4ba6125514b7660332cf311037bc09c82b0e. - Save the regression test shown below as
apps/desktop/test/issue-3184-browser-shortcut-repro.test.ts. - From the repository root, run:
pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build pnpm exec turbo run test --filter=@bb/desktop -- --run test/issue-3184-browser-shortcut-repro.test.ts
Expected: the native resolver returns thread.jump.1, allowing the owning app renderer to dispatch the command.
Actual in both clean runs:
FAIL test/issue-3184-browser-shortcut-repro.test.ts AssertionError: expected null to be 'thread.jump.1' - Expected: "thread.jump.1" + Received: null Test Files 1 failed (1) Tests 1 failed (1)
Regression test
import type { AppKeybindings } from "@bb/domain";
import { describe, expect, it } from "vitest";
import { resolveDesktopBrowserAppCommand } from "../src/desktop-browser-shortcuts.js";
const keybindings: AppKeybindings = [
{
command: "thread.jump.1",
desktopOnly: true,
shortcut: {
key: "1",
mod: true,
meta: false,
control: false,
alt: false,
shift: false,
},
when: { all: ["mainSurface"], none: ["modalOpen"] },
},
];
describe("embedded browser app shortcuts", () => {
it("resolves a numbered thread command while browser content owns focus", () => {
expect(
resolveDesktopBrowserAppCommand({
input: {
altKey: false,
code: "Digit1",
ctrlKey: false,
key: "1",
metaKey: true,
shiftKey: false,
},
isMac: true,
keybindings,
}),
).toBe("thread.jump.1");
});
});
5. Root cause
The embedded browser interceptor correctly receives native key input and asks the configured resolver for a command. It only prevents the page default after a non-null resolution, then sends the command to the owning renderer. See desktop-browser-view.ts lines 618–639 and main.ts lines 2195–2223.
The resolver has a hard gate: it skips any binding whose when.all does not contain browserFocus. See desktop-browser-shortcuts.ts lines 14–27. Numbered desktop thread bindings are generated with mainSurface and desktopOnly, not browserFocus. See app-keybindings.ts lines 73–105 and its numbered-command registration. The observed null follows directly from that mismatch.
The deeper limitation is that main-process resolution knows configured built-in bindings but not whether the renderer currently has a handler that will accept a command. The renderer normally prevents defaults only after a handler returns true; see AppCommandProvider.tsx lines 302–327. The desktop bridge instead sends a closed AppCommandId and does not receive a handled result. Plugin content scripts are documented as app-window code that may add a keyboard listener, but no public host-owned shortcut registration exists; see the Plugin Guide surface map and the closed built-in command list.
6. Proposed fix (first principles)
Add a host-owned registration channel that keeps Electron main synchronized with the owning renderer’s currently handleable shortcut chords, including opaque plugin registrations. The embedded before-input-event path should match only that active registry, dispatch the opaque registration without focusing either web contents, and call preventDefault() only for an active match. Built-in commands can use the same registry. This requires a product decision for collision precedence, lifecycle, and plugin API shape, plus changes to the desktop contract and Plugin Guide; a resolver-only exception would not meet the handled-only or plugin requirements.
7. Related issues
- #1162 covered sidebar shortcuts while an app-renderer input had focus.
- #1812 covered app shortcuts that were scoped too narrowly to the composer.
8. Appendix
Verification
The same agent created a second detached, clean checkout at the recorded base commit, installed with the frozen lockfile, built the monorepo, copied only the reproduction test, and ran the same Turbo command. It failed with the same expected/received values. No report claim was corrected after the second run.
Additional checks
gh api repos/get-bb/bb/commits/main --jq .sha 6cdb4ba6125514b7660332cf311037bc09c82b0e pnpm exec turbo run test --filter=@bb/desktop -- --run test/desktop-browser-shortcuts.test.ts test/desktop-browser-view-manager.test.ts Test Files 2 passed (2) Tests 63 passed (63) git log 6cdb4ba6125514b7660332cf311037bc09c82b0e..origin/main --oneline -- <affected paths> [no output]
The issue text was treated as untrusted input. No commands, links, patches, or code from it were executed.