diff --git a/packages/app/src/pages/home-session-open.test.ts b/packages/app/src/pages/home-session-open.test.ts index a71d58ff072..f6769d84d67 100644 --- a/packages/app/src/pages/home-session-open.test.ts +++ b/packages/app/src/pages/home-session-open.test.ts @@ -2,11 +2,30 @@ import { describe, expect, test } from "bun:test" import { shouldOpenSessionInBackground } from "./home-session-open" describe("shouldOpenSessionInBackground", () => { + test("opens middle clicks in the background", () => { + expect(shouldOpenSessionInBackground({ button: 1, mac: true, meta: false, ctrl: false, shift: false, alt: false })).toBe( + true, + ) + expect(shouldOpenSessionInBackground({ button: 2, mac: true, meta: false, ctrl: false, shift: false, alt: false })).toBe( + false, + ) + }) + test("requires only the platform primary modifier", () => { - expect(shouldOpenSessionInBackground({ mac: true, meta: true, ctrl: false, shift: false, alt: false })).toBe(true) - expect(shouldOpenSessionInBackground({ mac: false, meta: false, ctrl: true, shift: false, alt: false })).toBe(true) - expect(shouldOpenSessionInBackground({ mac: true, meta: true, ctrl: false, shift: true, alt: false })).toBe(false) - expect(shouldOpenSessionInBackground({ mac: false, meta: false, ctrl: true, shift: false, alt: true })).toBe(false) - expect(shouldOpenSessionInBackground({ mac: false, meta: true, ctrl: false, shift: false, alt: false })).toBe(false) + expect(shouldOpenSessionInBackground({ button: 0, mac: true, meta: true, ctrl: false, shift: false, alt: false })).toBe( + true, + ) + expect(shouldOpenSessionInBackground({ button: 0, mac: false, meta: false, ctrl: true, shift: false, alt: false })).toBe( + true, + ) + expect(shouldOpenSessionInBackground({ button: 0, mac: true, meta: true, ctrl: false, shift: true, alt: false })).toBe( + false, + ) + expect(shouldOpenSessionInBackground({ button: 0, mac: false, meta: false, ctrl: true, shift: false, alt: true })).toBe( + false, + ) + expect(shouldOpenSessionInBackground({ button: 0, mac: false, meta: true, ctrl: false, shift: false, alt: false })).toBe( + false, + ) }) }) diff --git a/packages/app/src/pages/home-session-open.ts b/packages/app/src/pages/home-session-open.ts index 8e32efb559d..9f117935b77 100644 --- a/packages/app/src/pages/home-session-open.ts +++ b/packages/app/src/pages/home-session-open.ts @@ -1,10 +1,13 @@ export function shouldOpenSessionInBackground(input: { + button: number mac: boolean meta: boolean ctrl: boolean shift: boolean alt: boolean }) { + if (input.button === 1) return true + if (input.button !== 0) return false if (input.shift || input.alt) return false if (input.mac) return input.meta && !input.ctrl return input.ctrl && !input.meta diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index c8a89672dfc..a60ec2977cd 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -240,10 +240,11 @@ function useHomeSessionHeaderOpacity(groups: () => HomeSessionGroup[]) { return { setViewport, setContentRef, setHeaderRef, update, titleOpacity } } -// Cmd+click on macOS (Ctrl+click elsewhere) opens a session tab in the -// background without navigating, matching browser conventions. +// Middle-click or Cmd+click on macOS (Ctrl+click elsewhere) opens a session +// tab in the background without navigating, matching browser conventions. function isBackgroundOpen(event: MouseEvent) { return shouldOpenSessionInBackground({ + button: event.button, mac: typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform), meta: event.metaKey, ctrl: event.ctrlKey, @@ -1386,7 +1387,15 @@ function HomeSessionSearchResultRow(props: { group: !!showProjectName(), }} onMouseEnter={() => props.onHighlight()} + onMouseDown={(event) => { + if (event.button === 1) event.preventDefault() + }} onClick={(event) => props.onSelect(props.record.session, { background: isBackgroundOpen(event) })} + onAuxClick={(event) => { + if (!isBackgroundOpen(event)) return + event.preventDefault() + props.onSelect(props.record.session, { background: true }) + }} > { + if (event.button === 1) event.preventDefault() + }} onClick={(event) => props.openSession(props.record.session, { background: isBackgroundOpen(event) })} + onAuxClick={(event) => { + if (!isBackgroundOpen(event)) return + event.preventDefault() + props.openSession(props.record.session, { background: true }) + }} >