Skip to content

feat/tmux mouse selection#94

Merged
Kitenite merged 8 commits intomainfrom
feat/tmux-mouse-selection
Nov 17, 2025
Merged

feat/tmux mouse selection#94
Kitenite merged 8 commits intomainfrom
feat/tmux-mouse-selection

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Nov 17, 2025

  • fix scroll
  • tmux: native-feeling mouse selection\n\n- Keep tmux invisible with minimal settings (unchanged core) - Add mouse bindings: drag to select, release to copy to clipboard and exit copy-mode - Leave unrelated settings untouched to keep diff minimal - No renderer/xterm option changes

Summary by CodeRabbit

  • New Features
    • Mouse wheel now scrolls terminal history (with bounded/accelerated behavior) and auto-exits history mode after brief inactivity.
    • Improved selection and copy behavior on macOS for more native-like click interactions; right-click selects word.
  • Bug Fixes
    • Terminal input now properly resumes from the current view (finishes scroll/exit-history) before sending typed input.

…nimal settings (unchanged core)

- Add mouse bindings: drag to select, release to copy to clipboard and exit copy-mode
- Leave unrelated settings untouched to keep diff minimal
- No renderer/xterm option changes
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 17, 2025

Walkthrough

Adds renderer wheel-driven tmux history scrolling via a new IPC channel, implements tmux-manager methods to enter/exit copy-mode and bound scrolling, moves server-level history-limit application earlier, and sanitizes mouse-report sequences in emitted data.

Changes

Cohort / File(s) Summary
IPC Channel Definitions
apps/desktop/src/shared/ipc-channels/terminal.ts
Adds terminal-scroll-lines channel with request { id: string; amount: number } (NoResponse).
IPC Handlers
apps/desktop/src/main/lib/terminal-ipcs.ts
Registers handler for terminal-scroll-lines that calls tmuxManager.scrollLines; updates terminal-input handling to call tmuxManager.scrollFinish(message.id) before writing input.
Tmux Manager
apps/desktop/src/main/lib/tmux-manager.ts
Calls applyServerSettings() during init; adds applyServerSettings(); sanitizes mouse-report sequences in emitMessage; adds public scrollLines(sid, amount) and scrollFinish(sid) to enter/exit copy-mode and send bounded scroll keys.
Renderer — Terminal UI
apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx
Sets xterm options macOptionClickForcesSelection and rightClickSelectsWord; attaches custom wheel handler that computes line amount and sends terminal-scroll-lines IPC when appropriate (respects alt buffer and drag selection).

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Mouse Wheel / Keyboard)
    participant Renderer as Renderer (Terminal.tsx)
    participant IPC as Main Process IPC
    participant TmuxMgr as Tmux Manager

    User->>Renderer: wheel event
    Renderer->>Renderer: check alt-buffer & drag-selection
    alt eligible for history scroll
        Renderer->>IPC: terminal-scroll-lines { id, amount }
        IPC->>TmuxMgr: scrollLines(sid, amount)
        TmuxMgr->>TmuxMgr: enter copy-mode & send-keys (bounded)
        TmuxMgr-->>Renderer: (emits data/events as usual)
    else not eligible
        Renderer-->>User: default scroll handling
    end

    %% input arriving should finish scroll/copy-mode before being written
    User->>Renderer: keyboard input
    Renderer->>IPC: terminal-input { id, ... }
    IPC->>TmuxMgr: scrollFinish(sid)    %% called before writing input
    TmuxMgr->>TmuxMgr: exit copy-mode
    IPC->>TmuxMgr: write input to tmux
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • Correctness and bounds in scrollLines (copy-mode entry, send-keys sequences).
    • scrollFinish ordering relative to input handling.
    • emitMessage sanitization for mouse-report sequences (ensure no false positives).
    • Renderer wheel-to-line conversion and alt-buffer/drag selection checks.
    • IPC type alignment between shared channel and handlers.

Possibly related PRs

  • initial ws #77 — Related to TerminalChannels/interface changes that this PR extends by adding terminal-scroll-lines.

Poem

🐇 I twitch my whiskers, wheel a gentle scroll,
Lines hop into view where history takes hold.
I nudge copy-mode, then hush — I let it cease,
A tidy hop, a quiet stop, the terminal finds peace. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete against the template. While it mentions the feature goal, it lacks proper structuring, related issues, type of change checkbox, testing information, and additional context required by the template. Restructure the description to follow the template: add a clear Description section, mark 'New feature' checkbox, include Related Issues, provide Testing steps, and add any Additional Notes. Ensure the description follows the repository's standard format.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat/tmux mouse selection' accurately summarizes the main change—adding mouse-based selection functionality to tmux terminal interactions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/tmux-mouse-selection

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac1bd08 and 2e962f0.

📒 Files selected for processing (3)
  • apps/desktop/src/main/lib/terminal-ipcs.ts (3 hunks)
  • apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx (2 hunks)
  • apps/desktop/src/shared/ipc-channels/terminal.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/desktop/src/shared/ipc-channels/terminal.ts
  • apps/desktop/src/main/lib/terminal-ipcs.ts
  • apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build
  • GitHub Check: Typecheck
  • GitHub Check: Unit Test
  • GitHub Check: Lint & Format

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/desktop/src/main/lib/tmux-manager.ts (1)

183-228: Consider aligning session and server history limits.

The session sets history-limit to 200000 (line 192), but applyServerSettings() sets both server and global limits to 1000000. Since tmux's hierarchy means session settings take precedence for that session, the server limit of 1M won't be effective if sessions cap at 200k. Consider either:

  • Increasing the session limit to match the server (1000000), or
  • Documenting why different limits are intentional
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 397d9ad and 6af361d.

📒 Files selected for processing (4)
  • apps/desktop/src/main/lib/terminal-ipcs.ts (2 hunks)
  • apps/desktop/src/main/lib/tmux-manager.ts (3 hunks)
  • apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx (2 hunks)
  • apps/desktop/src/shared/ipc-channels/terminal.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/desktop/src/shared/ipc-channels/terminal.ts (2)
apps/desktop/src/shared/ipc-channels/types.ts (1)
  • NoResponse (22-22)
apps/desktop/src/shared/ipc-channels/index.ts (1)
  • NoResponse (21-21)
🔇 Additional comments (7)
apps/desktop/src/shared/ipc-channels/terminal.ts (1)

43-47: LGTM!

The new IPC channel follows the established pattern and the type signature matches the implementation in both the IPC handler and the frontend caller.

apps/desktop/src/main/lib/terminal-ipcs.ts (1)

87-98: LGTM!

The handler correctly invokes the tmuxManager.scrollLines() method with the provided terminal ID and scroll amount.

apps/desktop/src/main/lib/tmux-manager.ts (4)

96-98: LGTM!

Applying server-wide settings before loading sessions ensures global configuration is in place before any session-specific operations.


230-261: LGTM!

The server settings are applied correctly with appropriate error handling. The try-catch blocks ensure that failures in setting global configuration don't crash the initialization process.


648-677: LGTM!

The mouse sequence sanitization correctly filters out X11 mouse reporting enable codes while preserving other terminal sequences. The try-catch ensures robustness if the regex processing fails.


679-701: LGTM!

The scroll implementation correctly:

  • Bounds the scroll amount to a reasonable range (1-100 lines)
  • Uses tmux's copy-mode for history navigation
  • Efficiently repeats the scroll command with the -N flag
  • Handles errors gracefully without crashing
apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx (1)

164-166: No issues found — xterm.js options verified as valid in v5.5.0.

Both macOptionClickForcesSelection and rightClickSelectsWord are valid ITerminalOptions in @xterm/xterm 5.5.0. The options are correctly implemented to provide native macOS-like terminal selection behavior.

export function registerTerminalIPCs(window: BrowserWindowType) {
// Initialize tmux manager (restore sessions) only once
if (!ipcHandlersRegistered) {
if (!ipcHandlersRegistered) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix indentation to use tabs consistently.

The indentation uses spaces instead of tabs, inconsistent with the rest of the file.

Apply this diff:

-  if (!ipcHandlersRegistered) {
+	if (!ipcHandlersRegistered) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!ipcHandlersRegistered) {
if (!ipcHandlersRegistered) {
🤖 Prompt for AI Agents
In apps/desktop/src/main/lib/terminal-ipcs.ts around line 13, the indentation
for the "if (!ipcHandlersRegistered) {" line uses spaces instead of the file's
expected tabs; change the leading spaces to a single tab (or match the project's
tab-level) so the line is indented with tabs consistently with the rest of the
file.

Comment on lines +260 to +283
// Attach custom wheel handler to scroll tmux history when in normal buffer
try {
term.attachCustomWheelEventHandler((ev: WheelEvent) => {
// If alternate buffer (full-screen app), let xterm transform wheel to keys
const isAlternate = (term as any)?.buffer?.active?.type === "alternate";
if (isAlternate) return true; // allow default handling

// If user is selecting (mouse button down), don't hijack
if ((ev.buttons & 1) === 1) return true;

// Convert delta to line count (heuristic)
const lines = Math.max(1, Math.abs(Math.round(ev.deltaY / 40)));
const amount = ev.deltaY > 0 ? lines : -lines;
if (terminalIdRef.current && amount !== 0) {
window.ipcRenderer.send("terminal-scroll-lines", {
id: terminalIdRef.current,
amount,
});
// Prevent default so viewport/xterm doesn't fight with tmux copy-mode
return false;
}
return true;
});
} catch {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify behavior across platforms and document internal API usage.

The wheel handler implementation looks correct, but note:

  1. Internal API access (line 264): Accessing (term as any)?.buffer?.active?.type relies on xterm.js internals that may change between versions. While the optional chaining provides safety, consider documenting this dependency or exploring public APIs like term.modes if available.

  2. Delta-to-line conversion (line 271): The heuristic of 40 pixels per line may vary across different platforms, mice, and trackpads. Consider testing on Windows and Linux to ensure consistent scrolling behavior.

Run the following to check if xterm.js 5.5.0 provides a public API for buffer type detection:


🏁 Script executed:

#!/bin/bash
# Search for public buffer-related APIs in xterm typings
rg -nP --type=ts -C3 'buffer.*alternate|modes.*application' node_modules/@xterm/xterm/

Length of output: 66



Then I'll search the web for xterm.js documentation to verify the API concerns.




🏁 Script executed:

#!/bin/bash
# Find xterm version in package.json
fd -n "package.json" --exec grep -l "xterm" {} \; | head -5 | xargs cat | grep -A2 -B2 "@xterm/xterm"

Length of output: 230


🌐 Web query:

xterm.js 5.x public API alternate buffer detection

💡 Result:

Yes — in xterm.js 5.x you can detect the alternate buffer via the public Terminal.buffers API:

  • Check current buffer: terminal.buffers.active.type === 'alternate'
  • Listen for changes: terminal.buffers.onBufferChange(listener) and inspect the passed buffer.type

Minimal examples:

  • synchronous check:
    const isAlt = terminal.buffers.active.type === 'alternate';

  • subscribe to changes:
    const disp = terminal.buffers.onBufferChange(buf => {
    const isAlt = buf.type === 'alternate';
    // ...
    });

Docs: IBufferNamespace / IBuffer (active, alternate, type, onBufferChange). [1][2]

Sources
[1] xterm.js API — IBufferNamespace (active, alternate, onBufferChange).
[2] xterm.js API — IBuffer (type: "normal" | "alternate").


Use xterm.js public API instead of internal buffer access.

The concern about internal API usage is valid. xterm.js 5.x provides a public Terminal.buffers API for detecting the alternate buffer, accessible via terminal.buffers.active.type, eliminating the need for type casting.

Update line 264:

const isAlternate = term.buffers.active?.type === "alternate";

This replaces the internal (term as any)?.buffer?.active?.type access with the public API.

Delta-to-line heuristic (line 271): The 40 pixels-per-line conversion needs platform-specific testing on Windows and Linux to validate consistency across different input devices—this cannot be verified in the sandbox environment.

🤖 Prompt for AI Agents
In apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx
around lines 260 to 283, replace the internal buffer access `(term as
any)?.buffer?.active?.type` with the xterm.js public API
`term.buffers.active?.type` to detect the alternate buffer; update the condition
at line ~264 to use that public property and keep the rest of the wheel handler
logic intact; do not introduce any new internal casts and ensure TypeScript
types align with xterm's Terminal type (add a narrow type assertion only if
absolutely required).

…ative xterm selection\n\n- mouse off in tmux; strip mouse-enable seqs to prevent xterm mouse mode\n- display-time 0 to suppress position overlay\n- add terminal-scroll-finish IPC; debounce exit from copy-mode\n- preserve deep scrollback via server/global settings
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/main/lib/tmux-manager.ts (1)

639-645: Sanitize mouse-enable sequences for cached history as well, not just live data — This issue is valid and confirmed.

The code shows the gap:

  • addTerminalMessage stores raw data directly in session.outputHistory
  • emitMessage sanitizes with sanitizeMouseSeq only for live emission to windows
  • getHistory returns unsanitized outputHistory

On reattach after restart, any replayed history containing escape sequences like \x1b[?1000h will reach xterm without filtering, potentially enabling mouse mode. The suggested fix—extracting sanitizeMouseSeq to a class-level helper and sanitizing before storing in history—correctly addresses the root cause.

♻️ Duplicate comments (1)
apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx (1)

260-294: Avoid internal xterm buffer access in the wheel handler

The wheel-to-tmux scrolling flow looks solid, but:

  • const isAlternate = (term as any)?.buffer?.active?.type === "alternate"; depends on xterm internals and any. xterm 5.x exposes a public buffers API that you can use instead.
  • The empty catch {} will silently hide any failure to attach the wheel handler, which could make debugging harder.

You can rely on the public API and keep the logic otherwise unchanged:

-      term.attachCustomWheelEventHandler((ev: WheelEvent) => {
-        // If alternate buffer (full-screen app), let xterm transform wheel to keys
-        const isAlternate = (term as any)?.buffer?.active?.type === "alternate";
+      term.attachCustomWheelEventHandler((ev: WheelEvent) => {
+        // If alternate buffer (full-screen app), let xterm transform wheel to keys
+        const isAlternate = term.buffers?.active?.type === "alternate";
         if (isAlternate) return true; // allow default handling
@@
-      });
-    } catch {}
+      });
+    } catch (e) {
+      console.warn("Failed to attach wheel handler:", e);
+    }

This removes the internal access and gives you at least a lightweight log if the handler cannot be attached.

🧹 Nitpick comments (2)
apps/desktop/src/main/lib/tmux-manager.ts (2)

183-231: Per-session history-limit likely overrides the new server/global limit

In applySessionSettings you still set:

["history-limit", "200000"],

while applyServerSettings sets both server and global history-limit to 1000000. In tmux, a session-specific history-limit will override the global setting, so effective history will remain at 200k for these sessions.

If your goal is “maximized scrollback via server/global limit”, consider either:

  • Dropping the per-session history-limit entirely, or
  • Bumping it to match the server/global cap and adjusting the comment so it’s clear which one is authoritative.

For example, to delegate entirely to server/global:

-      ["history-limit", "200000"],

This keeps the per-session config focused on invisibility (status, titles, mouse, display-time, etc.) while letting the higher history limit do its job.


681-712: Scroll/cancel implementation looks solid; just keep an eye on spawnSync frequency

The scrollLines / scrollFinish pair is nicely constrained:

  • amount is clamped to 1–100 lines and truncated, which prevents pathological scroll values.
  • Positive vs negative mapping (scroll-down / scroll-up) matches how you derive amount from the wheel event.
  • copy-mode -e is idempotent and cancel is safe to send even when not in copy-mode.

The only thing to watch is that each wheel event triggers two spawnSync("tmux", …) calls from the main process. If you notice UI stutter under heavy wheel usage, a future optimization could be to batch/coalesce scroll amounts or switch to an async spawn-based flow, but this is reasonable as a first implementation.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6af361d and ac1bd08.

📒 Files selected for processing (4)
  • apps/desktop/src/main/lib/terminal-ipcs.ts (2 hunks)
  • apps/desktop/src/main/lib/tmux-manager.ts (3 hunks)
  • apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx (2 hunks)
  • apps/desktop/src/shared/ipc-channels/terminal.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/main/lib/terminal-ipcs.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/desktop/src/shared/ipc-channels/terminal.ts (2)
apps/desktop/src/shared/ipc-channels/index.ts (1)
  • NoResponse (21-21)
apps/desktop/src/shared/ipc-channels/types.ts (1)
  • NoResponse (22-22)
🔇 Additional comments (2)
apps/desktop/src/renderer/screens/main/components/MainContent/Terminal.tsx (1)

164-172: Xterm selection options match the “native-feeling” goal

macOptionClickForcesSelection: true and rightClickSelectsWord: true are a good fit for macOS-style selection and Cmd+C copy. Just be aware that rightClickSelectsWord means xterm will consume right-click instead of a context menu, so double-check that’s the intended UX in this terminal view.

apps/desktop/src/shared/ipc-channels/terminal.ts (1)

43-53: Scroll IPC channel contracts look consistent

The terminal-scroll-lines and terminal-scroll-finish channel shapes match the tmux-manager methods (id + amount / id-only) and are consistent with existing string-based requests like terminal-detach. No issues from a typing or API-shape perspective.

Comment on lines +96 to 101
async initialize(): Promise<void> {
// Ensure server-level settings (affect all sessions/panes)
this.applyServerSettings();

const savedSessions = this.loadSessionsFromDisk();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Make initialize synchronous again and ensure server settings apply with minimal config

Two related points here:

  1. initialize is async but never awaits
    The body is entirely synchronous. Marking it async changes the public type to Promise<void> without a behavioral need and may force call sites to be updated. Unless you plan to add await here, it’s safer to keep this method synchronous:
-  async initialize(): Promise<void> {
+  initialize(): void {
     // Ensure server-level settings (affect all sessions/panes)
     this.applyServerSettings();
@@
-    const savedSessions = this.loadSessionsFromDisk();
+    const savedSessions = this.loadSessionsFromDisk();
  1. applyServerSettings may not do what you expect on a fresh server and may break “tmux invisibility”
    • If no server is running on TMUX_SOCKET, tmux set … history-limit can fail with “no server running” (non‑zero exit) and be silently ignored since spawnSync doesn’t throw.
    • If set does start a server, it will use the default config (user’s ~/.tmux.conf), whereas everywhere else you intentionally use -f /dev/null to keep tmux invisible and unconfigured.

Consider explicitly starting the server with your minimal config and checking exit codes:

-  private applyServerSettings(): void {
-    try {
-      // Server-level hard cap for history size
-      spawnSync("tmux", [
-        "-L",
-        this.TMUX_SOCKET,
-        "set",
-        "-s",
-        "history-limit",
-        "1000000",
-      ]);
-    } catch (e) {
-      console.warn("[TmuxManager] Failed to set server history-limit", e);
-    }
-
-    try {
-      // Global default for future windows/panes
-      spawnSync("tmux", [
-        "-L",
-        this.TMUX_SOCKET,
-        "set",
-        "-g",
-        "history-limit",
-        "1000000",
-      ]);
-    } catch (e) {
-      console.warn("[TmuxManager] Failed to set global history-limit", e);
-    }
-  }
+  private applyServerSettings(): void {
+    // Ensure server exists and uses our minimal config
+    const start = spawnSync("tmux", [
+      "-f",
+      "/dev/null",
+      "-L",
+      this.TMUX_SOCKET,
+      "start-server",
+    ]);
+    if (start.status !== 0) {
+      console.warn(
+        "[TmuxManager] Failed to start tmux server:",
+        start.stderr?.toString?.() ?? start.status,
+      );
+      return;
+    }
+
+    const server = spawnSync("tmux", [
+      "-L",
+      this.TMUX_SOCKET,
+      "set",
+      "-s",
+      "history-limit",
+      "1000000",
+    ]);
+    if (server.status !== 0) {
+      console.warn(
+        "[TmuxManager] Failed to set server history-limit:",
+        server.stderr?.toString?.() ?? server.status,
+      );
+    }
+
+    const globalOpt = spawnSync("tmux", [
+      "-L",
+      this.TMUX_SOCKET,
+      "set",
+      "-g",
+      "history-limit",
+      "1000000",
+    ]);
+    if (globalOpt.status !== 0) {
+      console.warn(
+        "[TmuxManager] Failed to set global history-limit:",
+        globalOpt.stderr?.toString?.() ?? globalOpt.status,
+      );
+    }
+  }

This keeps tmux on the custom socket using /dev/null config and guarantees the history limits are actually applied.

Also applies to: 232-263


I'll verify the review comment by examining the actual code. Let me gather the necessary information.


🏁 Script executed:

#!/bin/bash
# Examine the tmux-manager.ts file to verify the concerns

# 1. Show the initialize method around lines 96-101
echo "=== initialize() method (lines 96-101) ==="
sed -n '90,110p' apps/desktop/src/main/lib/tmux-manager.ts

echo ""
echo "=== applyServerSettings() method (lines 232-263) ==="
sed -n '225,270p' apps/desktop/src/main/lib/tmux-manager.ts

echo ""
echo "=== Check for initialize method signature ==="
grep -n "initialize\s*(" apps/desktop/src/main/lib/tmux-manager.ts | head -5

echo ""
echo "=== Search for await statements in initialize ==="
awk '/^\s*(async\s+)?initialize\s*\(/,/^\s*\}/' apps/desktop/src/main/lib/tmux-manager.ts | grep -i await

Length of output: 1740


🏁 Script executed:

#!/bin/bash
# Find all call sites of initialize() method

echo "=== Call sites of .initialize() ==="
rg -n "\.initialize\s*\(" apps/desktop/src/ --type ts --type tsx -C 2

echo ""
echo "=== Check for async/await usage with initialize ==="
rg -n "await.*\.initialize|initialize\s*\(\)" apps/desktop/src/ --type ts --type tsx -C 1

Length of output: 144


🏁 Script executed:

#!/bin/bash
# Find call sites of initialize() using simpler pattern

echo "=== Call sites of .initialize() ==="
rg -n "\.initialize\s*\(" apps/desktop/src/ --type ts -C 2

echo ""
echo "=== Check if initialize is awaited ==="
rg -n "await.*\.initialize" apps/desktop/src/ --type ts

echo ""
echo "=== Look for tmuxManager instantiation and usage ==="
rg -n "new.*TmuxManager|tmuxManager\s*=" apps/desktop/src/ --type ts -C 3

Length of output: 4014


🏁 Script executed:

#!/bin/bash
# Check the full initialize method for any await calls

echo "=== Full initialize method body ==="
sed -n '96,130p' apps/desktop/src/main/lib/tmux-manager.ts

Length of output: 847


Remove async from initialize() and fix applyServerSettings() to check exit codes and use proper config

Two confirmed issues:

  1. initialize() marked async but contains no await statements
    The method body is entirely synchronous. The call site at apps/desktop/src/main/lib/terminal-ipcs.ts:14 uses .catch() but does not await, leaving the promise unhandled. Remove the async/Promise wrapper:
-  async initialize(): Promise<void> {
+  initialize(): void {
     // Ensure server-level settings (affect all sessions/panes)
     this.applyServerSettings();

     const savedSessions = this.loadSessionsFromDisk();
  1. applyServerSettings() silently ignores spawnSync exit codes and lacks config isolation
    The try/catch only catches exceptions, not command failures. spawnSync returns its status in the .status field, which is never checked. Additionally, without -f /dev/null, a missing server will load user config (~/.tmux.conf). Update to explicitly start the server with minimal config and validate all exit codes:
   private applyServerSettings(): void {
-    try {
-      // Server-level hard cap for history size
-      spawnSync("tmux", [
-        "-L",
-        this.TMUX_SOCKET,
-        "set",
-        "-s",
-        "history-limit",
-        "1000000",
-      ]);
-    } catch (e) {
-      console.warn("[TmuxManager] Failed to set server history-limit", e);
-    }
-
-    try {
-      // Global default for future windows/panes
-      spawnSync("tmux", [
-        "-L",
-        this.TMUX_SOCKET,
-        "set",
-        "-g",
-        "history-limit",
-        "1000000",
-      ]);
-    } catch (e) {
-      console.warn("[TmuxManager] Failed to set global history-limit", e);
-    }
+    // Ensure server exists with minimal config
+    const start = spawnSync("tmux", [
+      "-f",
+      "/dev/null",
+      "-L",
+      this.TMUX_SOCKET,
+      "start-server",
+    ]);
+    if (start.status !== 0) {
+      console.warn(
+        "[TmuxManager] Failed to start tmux server:",
+        start.stderr?.toString?.() ?? start.status,
+      );
+      return;
+    }
+
+    const server = spawnSync("tmux", [
+      "-L",
+      this.TMUX_SOCKET,
+      "set",
+      "-s",
+      "history-limit",
+      "1000000",
+    ]);
+    if (server.status !== 0) {
+      console.warn(
+        "[TmuxManager] Failed to set server history-limit:",
+        server.stderr?.toString?.() ?? server.status,
+      );
+    }
+
+    const globalOpt = spawnSync("tmux", [
+      "-L",
+      this.TMUX_SOCKET,
+      "set",
+      "-g",
+      "history-limit",
+      "1000000",
+    ]);
+    if (globalOpt.status !== 0) {
+      console.warn(
+        "[TmuxManager] Failed to set global history-limit:",
+        globalOpt.stderr?.toString?.() ?? globalOpt.status,
+      );
+    }
   }

Committable suggestion skipped: line range outside the PR's diff.

@Kitenite Kitenite force-pushed the feat/tmux-mouse-selection branch 3 times, most recently from 4f9f3c3 to 2e962f0 Compare November 17, 2025 00:48
@Kitenite Kitenite merged commit f59d7e8 into main Nov 17, 2025
3 of 13 checks passed
@Kitenite Kitenite deleted the feat/tmux-mouse-selection branch November 19, 2025 00:33
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