From 6a6f9cd3f02dc58899517ce4a20c41f9f6a02260 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Wed, 14 Oct 2026 17:05:24 -0700 Subject: [PATCH] feat(terminal): close tabs with middle click Co-authored-by: Carl Signed-off-by: Taylor Ho --- .../terminal/TerminalSubstrate.test.mjs | 34 +++++++++++++++++++ .../features/terminal/TerminalSubstrate.tsx | 6 ++++ 2 files changed, 40 insertions(+) diff --git a/desktop/src/features/terminal/TerminalSubstrate.test.mjs b/desktop/src/features/terminal/TerminalSubstrate.test.mjs index d6113cbf08..6d994f7f28 100644 --- a/desktop/src/features/terminal/TerminalSubstrate.test.mjs +++ b/desktop/src/features/terminal/TerminalSubstrate.test.mjs @@ -233,6 +233,40 @@ test("tab actions restore terminal input focus", async () => { } }); +test("middle-click closes an open terminal tab only", async () => { + const closed = []; + const { view } = fixture({ + onCloseSession(id) { + closed.push(id); + }, + sessions: [ + { active: true, closing: false, id: "one", title: "SHELL" }, + { active: false, closing: true, id: "two", title: "LOG" }, + ], + }); + await ready(view); + + const openTab = view.getByRole("tab", { name: "Terminal 1" }).parentElement; + const closingTab = view.getByRole("tab", { + name: "Terminal 2, closing", + }).parentElement; + const auxClick = (target, button) => { + const event = new dom.window.MouseEvent("auxclick", { + bubbles: true, + button, + cancelable: true, + }); + fireEvent(target, event); + return event; + }; + const middleClick = auxClick(openTab, 1); + auxClick(openTab, 2); + auxClick(closingTab, 1); + + assert.deepEqual(closed, ["one"]); + assert.equal(middleClick.defaultPrevented, true); +}); + test("drag resize batches visual updates and commits state only on release", async () => { const { view } = fixture({ mode: "docked" }); await ready(view); diff --git a/desktop/src/features/terminal/TerminalSubstrate.tsx b/desktop/src/features/terminal/TerminalSubstrate.tsx index d8aaee2e2d..41932a22b3 100644 --- a/desktop/src/features/terminal/TerminalSubstrate.tsx +++ b/desktop/src/features/terminal/TerminalSubstrate.tsx @@ -609,6 +609,12 @@ export function TerminalSubstrate({ session.active && "buzz-terminal-tab-active", )} key={session.id} + onAuxClick={(event) => { + if (event.button !== 1) return; + event.preventDefault(); + if (session.closing) return; + runTabAction(() => onCloseSession(session.id)); + }} role="presentation" >