Skip to content

fix(terminal): paste into the terminal that got the key, not the mount's last session - #172

Merged
kipavy merged 1 commit into
devfrom
fix/terminal-clipboard-routing
Aug 24, 2026
Merged

fix(terminal): paste into the terminal that got the key, not the mount's last session#172
kipavy merged 1 commit into
devfrom
fix/terminal-clipboard-routing

Conversation

@kipavy

@kipavy kipavy commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Bug

With 5 tabs — 1: Host A, 2: Host B, 3+4 grouped in a split, 5: Host B — pasting into tab 2 landed in tab 3.

Root cause

The clipboard handle from attachTerminalClipboard (Ctrl+V, Ctrl+Shift+V, right-click paste) was stored in clipRef, a ref owned by the useTerminal hook instance. The xterm handler that reads it, term.attachCustomKeyEventHandler, is registered once when the terminal is created and lives as long as the cached terminal — cached terminals outlive their mounts by design.

A mount that switches session — a pane re-targeted, a tab dragged in or out of a split, the path useTerminal.reattach.test.tsx already covers — rebinds that ref to the new session's terminal. From then on the old terminal's Ctrl+V did readClipboard() and term.paste() on the new session's terminal.

Instrumented run before the fix, showing terminal 1's key handler firing terminal 2's clipboard handle:

DIAG { clips: [ [ 'term-1', 0 ], [ 'term-2', 1 ], [ 'term-1', 0 ] ] }

Fix

The handle moves onto the cache entry as CacheEntry.clip, next to the other per-terminal mirrors (inputGateRef, onClosedRef, onResizeRef), and bindContainer now takes the entry it is binding. A terminal's key handler can only reach its own clipboard.

Verification

  • New regression test src/hooks/useTerminal.clipboard.test.tsx — fails on dev, passes here.
  • vitest run src/hooks src/components/terminal: 41 files, 266 tests passed.
  • tsc --noEmit: clean.

Not yet exercised by hand in a live build.

…t's last session

The Ctrl+V/Ctrl+Shift+V/right-click clipboard handle was stored in a ref owned
by the useTerminal hook instance, while the xterm custom key handler that reads
it is registered once, when the terminal is created, and lives as long as the
cached terminal. A mount that switches session (a pane re-targeted, a tab
dragged in or out of a split) rebinds that ref to the new session's terminal,
so the old terminal's Ctrl+V pasted into the new one.

Store the handle on the cache entry instead, next to the other per-terminal
mirrors, so a terminal's key handler can only reach its own clipboard.
@kipavy

kipavy commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Live-verified in the headless dev build (compose.headless.yml, debug app + tauri-driver), same steps before and after:

  1. Duplicate a session into a split pane with Ctrl+Alt+D (real keystroke via xdotool) — the new session's terminal is created by the pane mount, since MainPanel skips a session that is already in the layout.
  2. Swap the two panes (movePane, the call the drag controller makes), so the mount that created the duplicate's terminal now renders the other session.
  3. Click into the left pane, put PROBE_* on the X clipboard, press Ctrl+V (real keystroke).

Before the fix (dev @ c881568): focus and cursor in the left pane, PROBE_ALPHA landed in the right pane — the reported bug.

After the fix: PROBE_BETA landed in the left pane, the one that had focus.

@kipavy
kipavy merged commit 7f6faf8 into dev Aug 24, 2026
4 checks passed
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.

1 participant