#3413 · Palette keyboard handling consumes IME confirmation
Bug · Medium priority · Low effort · ui · 2026-09-10
Issue · Base 8ac123f3551e7502a91c93e87329797f34ee626b
Verdict: REPRODUCED · Root-cause confidence: high
TL;DR
The palette treats an IME confirmation key as a command activation key. Its input keydown handler checks the key but never checks composition state. A test on the real component fails on the unchanged main branch in two clean checkouts. A one-line composition guard makes the test and all 55 command subsystem tests pass.
Claims vs findings
| Claim | Finding |
|---|---|
| Composing Enter is consumed by palette handling | Verified twice: defaultPrevented is true, expected false. |
| Composition guard resolves the behavior | Verified: regression checks unconsumed event, retained combobox, no action, and subsequent normal Enter activation. |
| Native macOS Pinyin candidate confirmation | Unverified: tests use jsdom KeyboardEvent, not an OS candidate window. |
Environment
macOS arm64; Node 22.22.3; pnpm 9.15.0; Vitest 4.1.1. Source at the full base commit above. Frozen installs completed separately in both checkouts. The normal pnpm launcher was broken, so commands used npx --yes pnpm@9.15.0. No dependencies were added. No app instance, providers, database, ports, or runtime data were used.
Minimal reproduction
- Check out the base commit in a clean get-bb/bb checkout.
- Apply the regression-only patch with git apply.
- Run:
pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run test --filter=@bb/app -- src/components/commands/CommandPalette.test.tsx -t 'keeps composition confirmation'
Expected: confirmation is not prevented; palette stays open and no command runs. Actual on main:
AssertionError: expected true to be false // Object.is equality expect(confirmation.defaultPrevented).toBe(false); Test Files 1 failed (1) Tests 1 failed | 23 skipped (24)
The test stops on the consumed-event assertion. The source path explains selection and closure; the passing fixed run exercises every later assertion.
it("keeps composition confirmation separate from command activation", async () => {
renderPalette();
openPalette();
await waitFor(() => expect(searchField()).toBeTruthy());
fireEvent.change(searchField(), { target: { value: ">toggle panel" } });
await waitFor(() =>
expect(selectedOption()?.textContent).toContain("Toggle panel"),
);
const input = searchField();
const confirmation = new KeyboardEvent("keydown", {
key: "Enter",
isComposing: true,
bubbles: true,
cancelable: true,
});
fireEvent(input, confirmation);
expect(confirmation.defaultPrevented).toBe(false);
expect(screen.queryByRole("combobox")).toBe(input);
expect(testState.calls).toEqual([]);
fireEvent.keyDown(input, { key: "Enter" });
await waitFor(() => expect(testState.calls).toEqual(["panel.toggle"]));
expect(screen.queryByRole("combobox")).toBeNull();
});
Root cause
handleKeyDown handles Enter by calling preventDefault and selecting the active command or thread. It has no composition guard. The combobox directly installs that handler. Thus an Enter emitted during composition follows the same selection path as normal activation. This is local UI event policy; no server or protocol change is needed.
Proposed fix
Return immediately when event.nativeEvent.isComposing is true, before handling palette navigation or activation keys. Keep normal Enter behavior. The implementation adds one production line and 27 test lines in two existing command subsystem files.
Verification
The same agent created a second detached temporary Git worktree at the exact base commit, installed with the frozen lockfile, applied only the authored test, and reran the command above. It failed on the same assertion. No correction to the root cause was needed. This is repeated verification by the same agent, not independent review.
After the guard, pnpm exec turbo run test --filter=@bb/app -- src/components/commands passed: 4 files, 55 tests. Native IME/browser-specific event sequencing remains untested.
Related issues and PRs
No linked open PR appeared in the issue timeline metadata at investigation time. No other issue was investigated.
Appendix
First failure · Second clean failure · Passing command suite. Issue content was treated only as untrusted claims; reproduction code was authored from repository evidence.