# How Nodeterm Handles Mouse Interactions in tmux Panes: A Technical Deep Dive

> Discover how Nodeterm expertly handles mouse interactions in tmux panes by integrating with tmux's native mouse support for seamless scrolling selection and clipboard functionality.

- Repository: [eneskirca/nodeterm](https://github.com/eneskirca/nodeterm)
- Tags: deep-dive
- Published: 2026-08-25

---

**Nodeterm delegates mouse event handling to tmux by disabling xterm.js mouse reporting while enabling tmux's native mouse support, allowing seamless scrolling, selection, and clipboard integration within terminal nodes.**

The open-source Nodeterm project (eneskirca/nodeterm) provides a node-based terminal interface that bridges xterm.js with tmux sessions. Understanding **mouse interactions within tmux panes** requires examining the deliberate architectural coordination between the frontend terminal emulator and the backend multiplexer.

## Enabling Native tmux Mouse Support

Nodeterm ensures every tmux session launches with mouse capabilities activated. In [`src/shared/ssh.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/ssh.ts) around line 277, the application generates a tmux configuration string that explicitly enables mouse mode for both local and remote sessions:

```typescript
const tmuxConf = `set -g mouse on\n`;

```

This configuration allows tmux to intercept mouse wheel events for scrollback history, handle drag selections via copy-mode, and capture clicks for URL activation. By delegating these responsibilities to tmux, Nodeterm maintains consistent behavior across all terminal nodes regardless of the underlying shell.

## Disabling xterm.js Mouse Reporting

To prevent conflicts with tmux's mouse handling, Nodeterm explicitly instructs xterm.js to stop emitting mouse events. The [`src/renderer/terminal/terminal-config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/terminal-config.ts) file writes a specific escape sequence to disable client-side tracking (lines 511-516):

```typescript
term.write(CO_ATTACH_MOUSE_SEQ);

```

This sequence sets `modes.mouseTrackingMode` to `none`, ensuring the terminal forwards wheel events to tmux rather than buffering them locally. The implementation comments explain that this delegation is essential for tmux to receive mouse wheel input for history scrolling instead of xterm.js triggering its own scrollback buffer.

## Clipboard Integration via OSC 52

When users select text with the mouse in tmux copy-mode, Nodeterm synchronizes the selection to the system clipboard using the OSC 52 escape sequence. The [`src/renderer/terminal/copy-feedback.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/copy-feedback.ts) module listens for these sequences and writes the data to the host clipboard.

The implementation defines a grace period constant to handle timing between selection and clipboard events:

```typescript
// Lines 17-19
const COPY_FEEDBACK_TIMEOUT_MS = 100;

```

This timeout ensures that clipboard events occurring shortly after a mouse-up event are handled as part of the same drag operation, preventing race conditions between tmux's buffer copying and the system's clipboard API.

## Blocking Middle-Click Interference

Nodeterm prevents middle-mouse button events from reaching the xterm.js canvas to avoid generating spurious mouse reports that tmux would misinterpret. The [`src/renderer/terminal/middle-click.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/middle-click.ts) module registers capture-phase listeners on the terminal host element:

```typescript
host.addEventListener('mousedown', onEvent, true);
host.addEventListener('mouseup', onEvent, true);
host.addEventListener('auxclick', onEvent, true);

```

These listeners swallow `mousedown`, `mouseup`, and `auxclick` events during the capture phase, effectively suppressing middle-click paste operations that would otherwise conflict with tmux's internal mouse handling workflow.

## File and URL Link Handling

For clickable file paths and URLs, Nodeterm implements custom hit-testing logic in [`src/renderer/terminal/file-links.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/file-links.ts) (lines 143-571). When processing a click event, the system first checks whether the target tmux pane has active mouse tracking:

```typescript
if (term.modes.mouseTrackingMode !== 'none') {
  // tmux receives the click, skip custom handling
  return;
}
handleUrlOrPathClick(event);

```

This conditional ensures that clicks in applications with their own mouse handling (such as Vim or Emacs) pass through to tmux unmodified, while clicks in standard shell panes trigger Nodeterm's link detection and external application launching.

## Correcting Scale-Induced Coordinate Drift

Because Nodeterm renders terminals inside CSS-scaled containers, raw mouse coordinates do not align with the visual character grid. The [`src/renderer/terminal/scale-fix.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/scale-fix.ts) module patches xterm.js's internal mouse service to compensate for CSS transform scaling factors, ensuring that mouse selections accurately correspond to the text being targeted.

This correction prevents selection misalignment when users zoom the interface or when nodes are displayed at non-1:1 scale ratios, maintaining precision during drag-to-select operations within tmux panes.

## Summary

- **tmux mouse mode** is force-enabled in [`src/shared/ssh.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/ssh.ts) to handle scrolling, selection, and URL clicks natively.
- **xterm.js mouse reporting** is disabled via `CO_ATTACH_MOUSE_SEQ` in [`src/renderer/terminal/terminal-config.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/terminal-config.ts) to prevent event conflicts.
- **Clipboard synchronization** relies on OSC 52 sequences captured in [`src/renderer/terminal/copy-feedback.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/copy-feedback.ts) with defined timeout handling.
- **Middle-click events** are intercepted and suppressed in [`src/renderer/terminal/middle-click.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/middle-click.ts) to avoid spurious tmux reports.
- **Link handling** checks `mouseTrackingMode` in [`src/renderer/terminal/file-links.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/file-links.ts) to distinguish between tmux-managed clicks and custom URL opening.
- **Coordinate accuracy** is maintained through CSS scale compensation in [`src/renderer/terminal/scale-fix.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/scale-fix.ts).

## Frequently Asked Questions

### How does Nodeterm prevent double-handling of mouse wheel events?

Nodeterm writes the `CO_ATTACH_MOUSE_SEQ` escape sequence to xterm.js, setting `modes.mouseTrackingMode` to `none`. This prevents the terminal emulator from buffering wheel events locally, forcing them to pass through to tmux where `set -g mouse on` handles scrollback history instead of the xterm.js buffer.

### Why does middle-click not work for pasting in Nodeterm terminals?

The application intentionally captures and swallows middle-mouse button events in the capture phase via listeners in [`src/renderer/terminal/middle-click.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/middle-click.ts). This prevents xterm.js from generating mouse reports that tmux would interpret as commands, avoiding conflicts with tmux's native copy-paste workflow.

### Can I use mouse interactions in Vim or Emacs within Nodeterm?

Yes. When applications enable their own mouse tracking, `term.modes.mouseTrackingMode` changes from `none`, and the logic in [`src/renderer/terminal/file-links.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/file-links.ts) allows those events to pass through to tmux unmodified. This enables full mouse support in terminal applications like Vim while preserving Nodeterm's link-clicking functionality in standard shells.

### How does text selection get copied to the system clipboard?

When you select text in tmux copy-mode, tmux emits an OSC 52 escape sequence containing the selected data. The [`src/renderer/terminal/copy-feedback.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/renderer/terminal/copy-feedback.ts) module detects this sequence and writes the content to the system clipboard, utilizing `COPY_FEEDBACK_TIMEOUT_MS` to handle the timing between mouse-up and the sequence arrival.