Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Cancel native middle-click paste on tab mouseup#2518

Merged
nwparker merged 1 commit into
stablyai:mainfrom
BorjaLL:b.llanderas/fix-middle-click-tab-primary-selection-paste
May 22, 2026
Merged

Cancel native middle-click paste on tab mouseup#2518
nwparker merged 1 commit into
stablyai:mainfrom
BorjaLL:b.llanderas/fix-middle-click-tab-primary-selection-paste

Conversation

@BorjaLL
Copy link
Copy Markdown
Contributor

@BorjaLL BorjaLL commented May 21, 2026

Summary

Fixes #2346.

On Linux with primary-selection paste enabled (the default), middle-clicking a tab to close it produced an unwanted primary-selection paste into whichever terminal became active after the close. Repro is in the linked issue.

The existing `onMouseDown` `preventDefault` on each tab blocks Chromium's middle-button auto-scroll but is not sufficient to block the native paste pipeline — that one is gated on `mouseup`. After the tab unmounts via `onAuxClick` → `onClose`, focus moves to xterm's hidden `<textarea>` on the newly active terminal, and the native paste lands there.

This applies on both X11 and Wayland sessions (the issue author confirmed Hyprland with `wp_primary_selection_unstable_v1`).

Fix

Adopt the same option-(a) approach from the issue's analysis:

  • Add a small named helper `preventMiddleButtonDefault` in `src/renderer/src/components/tab-bar/middle-button-default-guard.ts`.
  • Wire it as `onMouseUp` on `SortableTab`, `BrowserTab`, and `EditorFileTab` — the three tab surfaces that close on middle-click.
  • Mirrors the cancel already performed by `usePrimarySelectionPaste`'s `enabled=false` branch for non-tab surfaces.

The change is tab-local and surgical: only the three components that already close on `auxclick` get the new `onMouseUp`. No global event listeners are added or modified.

No platform branching

`event.preventDefault()` on a `mouseup` event is harmless on macOS and Windows — there is no native primary-selection paste pipeline to cancel there. Avoiding a platform check keeps the surfaces consistent and follows the project's cross-platform guidance (no `navigator.userAgent` sniffing for a behavior that's a no-op off Linux).

Tests

`src/renderer/src/components/tab-bar/middle-button-default-guard.test.ts` pins the contract:

  • Middle button (`button === 1`) → `preventDefault` is called.
  • Left button (`button === 0`) → `preventDefault` is not called.
  • Right button (`button === 2`) → `preventDefault` is not called.

(Project convention is to extract logic into named functions and unit-test those — no React rendering in tests. See `QuickLaunchButton.test.ts` for the same pattern.)

No visual change

This is pure event-handler behavior; the rendered tab markup is unchanged.

Manual verification

Manual repro requires a Linux session with primary-selection paste enabled, which I do not have available locally. The fix follows the issue author's analysis precisely and matches the working pattern in the existing `enabled=false` branch of `usePrimarySelectionPaste`. Linux verification by a reviewer / the issue author would be appreciated before merge.

Cross-platform / security review

  • Cross-platform: `preventDefault` on `mouseup` for `button === 1` is a no-op on macOS/Windows (no native primary-selection paste pipeline). Behavior is unchanged there.
  • SSH use case: Tab handlers run in the renderer regardless of where the underlying PTY lives, so the fix applies uniformly.
  • Security: Strictly stops a spurious paste of the user's own primary-selection text into the wrong terminal. No new surfaces exposed, no new code paths in privileged processes.

CI checks run locally

  • `pnpm lint`
  • `pnpm typecheck`
  • `pnpm test` — 8910 passed, 10 skipped. One pre-existing macOS-only flake in `src/main/startup/run-electron-vite-dev.test.ts` ("preserves relative Electron framework symlinks in the copied mac dev app") timed out at the 5000ms limit; unrelated to this renderer-only change and the test passes intermittently.
  • `pnpm build`

On Linux with primary-selection paste enabled, middle-clicking a tab to
close it triggered an unwanted primary-selection paste into whichever
terminal became active after the close. The existing onMouseDown
preventDefault on each tab blocks Chromium's middle-button auto-scroll
but does not block the native paste pipeline, which is gated on
mouseup. After the tab unmounts via onAuxClick → onClose, focus moves
to xterm's hidden textarea on the newly active terminal, and the
primary selection lands there.

Add a small named helper preventMiddleButtonDefault and wire it as
onMouseUp on SortableTab, BrowserTab, and EditorFileTab. Mirrors the
preventDefault that usePrimarySelectionPaste's enabled=false branch
already performs for non-tab surfaces. No platform branching: the
mouseup cancel is a no-op on macOS and Windows because there is no
native primary-selection paste pipeline to cancel.

Fixes stablyai#2346
@nwparker nwparker force-pushed the b.llanderas/fix-middle-click-tab-primary-selection-paste branch from f0e0831 to 8984f96 Compare May 22, 2026 07:12
Copy link
Copy Markdown
Contributor

@nwparker nwparker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed and took ownership of the final validation. Rebased onto current main, tightened the helper comment to match the repo comment rules, and re-ran verification locally: targeted Vitest for the new guard + primary-selection coverage, full pnpm typecheck, pnpm lint, git diff --check, and full pnpm test (9360 passed, 10 skipped). The fix is intentionally tab-local and matches the Linux mouseup cancellation path described in #2346.

@nwparker nwparker merged commit 8ae292e into stablyai:main May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: middle-click on a tab closes it AND pastes the primary selection into the next-active terminal (Linux)

2 participants