diff --git a/desktop/src/features/terminal/TerminalSubstrate.tsx b/desktop/src/features/terminal/TerminalSubstrate.tsx index 83dd1e04b3..081b121a8a 100644 --- a/desktop/src/features/terminal/TerminalSubstrate.tsx +++ b/desktop/src/features/terminal/TerminalSubstrate.tsx @@ -517,7 +517,17 @@ export function TerminalSubstrate({ {shortcutLabel} BUZZ -
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: the hidden textarea owns keyboard semantics; this only preserves its focus across canvas clicks. */} +
{ + if (owner !== "terminal") return; + // Preventing the canvas mousedown also suppresses selection. Revisit + // this when the terminal gains mouse selection support. + event.preventDefault(); + textareaRef.current?.focus({ preventScroll: true }); + }} + > {welcomeVisible && banner ? ( diff --git a/desktop/tests/e2e/terminal-wheel.spec.ts b/desktop/tests/e2e/terminal-wheel.spec.ts index bd3d742e1a..d90fb9da85 100644 --- a/desktop/tests/e2e/terminal-wheel.spec.ts +++ b/desktop/tests/e2e/terminal-wheel.spec.ts @@ -187,3 +187,46 @@ test("scrollback: wheel over Buzz Term reaches terminal_scroll", async ({ console.log("SCROLLS", JSON.stringify(scrolls)); expect(scrolls).toEqual([-5, 2]); }); + +test("terminal viewport click retains keyboard input ownership", async ({ + page, +}) => { + await reveal(page); + const input = page.getByLabel("Terminal input"); + await expect(input).toBeFocused(); + + await page + .locator(".buzz-terminal-viewport") + .click({ position: { x: 40, y: 40 } }); + await expect(input).toBeFocused(); + + await page.keyboard.type("FOCUS_KEYSTROKE"); + await expect + .poll(async () => + page.evaluate(() => + ( + window as typeof window & { __SAMI_TERM__: { inputs: string[] } } + ).__SAMI_TERM__.inputs.join(""), + ), + ) + .toContain("FOCUS_KEYSTROKE"); +}); + +test("concealed terminal viewport does not steal Buzz focus", async ({ + page, +}) => { + await reveal(page); + await page.keyboard.press("Meta+j"); + await expect(page.locator(TERM)).toHaveAttribute( + "data-terminal-owner", + "buzz", + ); + + const input = page.getByLabel("Terminal input"); + await expect(input).not.toBeFocused(); + await page.locator(".buzz-terminal-viewport").click({ + force: true, + position: { x: 40, y: 40 }, + }); + await expect(input).not.toBeFocused(); +});