Skip to content

Suppress cmd-hover path highlighting while terminal selection is active - #2579

Merged
austinywang merged 15 commits into
mainfrom
issue-2577-cmd-hold-highlight
Apr 6, 2026
Merged

austinywang merged 15 commits into
mainfrom
issue-2577-cmd-hold-highlight

Conversation

@austinywang

@austinywang austinywang commented Apr 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • suppress command-modified hover refresh when the terminal already has an active text selection
  • disable cmux's extra bare-path hover cursor in that selected state so holding Cmd does not add visual interference
  • keep normal Cmd key handling and Cmd+click behavior unchanged when there is no selection

Testing

  • Not run locally per repo policy
  • Built tagged dev app via ./scripts/reload.sh --tag cmd-hold-highlight --launch
  • Rebuilt tagged dev app via ./scripts/reload.sh --tag cmd-hold-highlight

Fixes #2577


Summary by cubic

Suppresses Cmd-hover link/path highlighting and the hand cursor while a text selection is active; behavior without a selection is unchanged (fixes #2577). Improves Cmd-click path opening by resolving escaped/quoted/raw-space filenames with unquoting/unescaping, tilde/CWD expansion, and preferring the click-point snapshot path when quicklook or viewport offsets disagree.

  • Tests
    • Added TerminalCmdClickUITests and a harness to simulate selection and cmd-hover/click, and to capture opened paths and hover diagnostics.
    • Verifies hover suppression during selection; cmd-click opening for escaped/raw-space paths in the CWD; and mismatch cases that prefer the snapshot path (quicklook and viewport-offset).
    • Adds a logged-path cmd-click regression matrix across grid, log, and alt-screen output; strengthens ls cell coverage.

Written for commit 641f115. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added a UI-test harness and capture mechanisms to record open-paths and hover diagnostics and simulate cmd-hover/click interactions.
  • Bug Fixes

    • Cmd-hover and pointing-hand cursor are suppressed correctly during active text selection.
    • Cmd-click now resolves escaped/quoted paths (including spaces) before opening.
  • Refactor

    • Improved modifier-handling and a two-stage path-resolution flow for hover/click interactions.
  • Tests

    • New UI tests validating hover-suppression and cmd-click path-opening behavior.

@vercel

vercel Bot commented Apr 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 6, 2026 1:48am

@coderabbitai

coderabbitai Bot commented Apr 4, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Suppresses Quick Look word-hover when Command is held while a terminal selection exists; refactors modifier extraction to flags-based handling; adds internal quicklook path-resolution helpers with a test-only wrapper; introduces UI-test capture utilities and a DEBUG-gated terminal cmd-click UI-test harness plus new UI tests and Xcode project entries.

Changes

Cohort / File(s) Summary
Ghostty view behavior & modifier handling
Sources/GhosttyTerminalView.swift
Add shouldSuppressCommandPathHover(for:), thread suppressCommandPathHover through hover-mod logic, replace event→mods translation with flags-based modsFromFlags, extend cursor/hit-test logic, implement internal quicklook path-resolution helpers and expose cmuxResolveQuicklookPathForTesting(_:cwd:existingPaths:) under DEBUG.
App UI-test capture & helpers
Sources/cmuxApp.swift
Add CmuxUITestCapture helpers (appendLineIfConfigured, mutateJSONObjectIfConfigured) and update PreferredEditorSettings.open(_:) to optionally append open-paths to a configured capture file.
UI-test harness & app wiring
Sources/AppDelegate.swift
Add DEBUG-gated terminal cmd-click test harness: setup manifest writing, snapshot token geometry derivation, simulated actions (cmd-click, selection+cmd-hold), observers and polling timer, and cleanup logic.
New UI tests
cmuxUITests/TerminalCmdClickUITests.swift
Add UI test suite that launches app with capture env vars, drives harness via file-based commands, polls JSON/setup artifacts, asserts hover-suppression and resolved-open behavior, and validates captured open-paths.
Xcode project metadata
GhosttyTabs.xcodeproj/project.pbxproj
Add TerminalCmdClickUITests.swift to the cmuxUITests target (PBXFileReference, PBXBuildFile, and added to PBXSourcesBuildPhase).

Sequence Diagram(s)

sequenceDiagram
    participant TestRunner as Test Runner
    participant App as cmux App (DEBUG harness)
    participant Socket as Control Socket
    participant FS as Filesystem

    TestRunner->>App: Launch with env (socket, capture files, manifest)
    App->>Socket: Bind/listen control socket
    TestRunner->>Socket: Connect & poll for ready manifest
    TestRunner->>Socket: Send action command (e.g., cmd_click_token)
    App->>App: Read terminal snapshot, compute token geometry
    App->>App: Simulate input (CGEvents) / selection as requested
    App->>App: On Cmd-down + selection -> evaluate shouldSuppressCommandPathHover
    App->>App: Resolve candidate path via quicklook helpers
    App->>FS: If open-capture configured, append resolved path to capture log
    TestRunner->>FS: Read capture log / hover diagnostics JSON
    TestRunner-->>App: Assert expected suppression and opened-path entries
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hop where glyphs and cursors softly play,
I hush the Cmd that tries to steal your day.
Where selections lie, I keep the paths asleep,
I nudge the tests to click where tokens peep—
A carrot-coded cheer for fixes, small and spry.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main change: suppressing cmd-hover while selection is active, which is the primary objective from issue #2577.
Linked Issues check ✅ Passed The code changes implement all key requirements from #2577: selection-aware suppression logic, elimination of visual interference during selection, and preservation of normal Cmd behavior when no selection exists [#2577].
Out of Scope Changes check ✅ Passed All changes are scoped to the linked objectives: cmd-hover suppression, cmd-click path resolution improvements, and UI test infrastructure for verification. No extraneous modifications detected.
Description check ✅ Passed The pull request description covers the main changes, testing approach, and includes the required checklist sections, though some items are incomplete.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-2577-cmd-hold-highlight

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR suppresses the cmd-hover path-highlight and custom pointer-hand cursor while a terminal text selection is active. It adds two focused helpers — shouldSuppressCommandPathHover(for:) and hoverModsFromFlags(_:suppressCommandPathHover:) — and threads a suppressPathHover flag through flagsChanged, mouseMoved, and mouseEntered to prevent visual interference when Cmd is held over an existing selection.

Confidence Score: 5/5

Safe to merge — the change is tightly scoped, all event paths clean up cursor state correctly, and no P0/P1 issues were found.

The suppression logic is applied consistently across all three entry points (flagsChanged, mouseMoved, mouseEntered). The shouldSuppressCommandPathHover guard is short-circuited when Cmd is not held, so there is no extra cost on non-Cmd moves. The wordPathHoverActive cursor stack is always balanced: mouseMoved after drag completion, flagsChanged on Cmd release, mouseExited, and viewDidMoveToWindow all pop it when needed. No P0 or P1 findings.

No files require special attention.

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Adds shouldSuppressCommandPathHover, hoverModsFromFlags, and a suppressPathHover parameter to updateWordPathHover; threads suppression correctly through flagsChanged, mouseMoved, and mouseEntered.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Cmd key pressed / mouse moved / mouse entered] --> B{shouldSuppressCommandPathHover}
    B --> C{Cmd held?}
    C -- No --> D[suppressPathHover = false]
    C -- Yes --> E{ghostty_surface_has_selection?}
    E -- No --> D
    E -- Yes --> F[suppressPathHover = true]

    D --> G[hoverModsFromFlags: full modifiers]
    F --> H[hoverModsFromFlags: strip .command]

    G --> I[ghostty_surface_mouse_pos with full mods]
    H --> J[ghostty_surface_mouse_pos without .command]

    I --> K[updateWordPathHover cmdHeld suppressPathHover=false]
    J --> L[updateWordPathHover cmdHeld suppressPathHover=true]

    K --> M{cmdHeld?}
    M -- No --> N[Pop cursor if active]
    M -- Yes --> O{resolveWordUnderCursorAsPath?}
    O -- Found --> P[Push pointingHand cursor]
    O -- Not found --> N

    L --> N
Loading

Reviews (1): Last reviewed commit: "Suppress cmd hover path highlight during..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 6535-6548: The cmd-click fallback path activation code needs to
respect active text selection like the hover logic does: update the cmd-click
detection branch (the block that detects "cmd-click fallback path" where
hoverModsFromFlags is used) to first check selection state using
shouldSuppressCommandPathHover(_:) or directly via
ghostty_surface_has_selection(surface) and skip path activation when selection
is present; i.e., if shouldSuppressCommandPathHover(flags) is true, do not run
the cmd-click fallback path detection/activation and proceed with the regular
selection-aware behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0ff8bbdf-77dc-4f39-a098-ad8cba16a86d

📥 Commits

Reviewing files that changed from the base of the PR and between c1b8a5c and af9b452.

📒 Files selected for processing (1)
  • Sources/GhosttyTerminalView.swift

Comment thread Sources/GhosttyTerminalView.swift

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 1 file

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/GhosttyTerminalView.swift">

<violation number="1" location="Sources/GhosttyTerminalView.swift:6527">
P2: Command-hover suppression is computed but not applied to `ghostty_surface_mouse_pos`, so Cmd-modified hover can still be activated while a selection is active.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread Sources/GhosttyTerminalView.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
Sources/GhosttyTerminalView.swift (1)

6779-6785: ⚠️ Potential issue | 🟠 Major

Apply selection suppression before mouse button dispatch to avoid cmd-click leakage.

On Line 6792, release is sent before suppression is computed; if Ghostty clears selection during that event, the later check can miss prior active selection and still run fallback open logic. Also, mouse press/release still use full Command mods, so suppression is not consistently enforced in click paths.

🔧 Suggested patch
 override func mouseDown(with event: NSEvent) {
@@
-    if event.clickCount == 1 {
-        ghostty_surface_mouse_pos(surface, point.x, bounds.height - point.y, modsFromEvent(event))
-    }
-    _ = ghostty_surface_mouse_button(surface, GHOSTTY_MOUSE_PRESS, GHOSTTY_MOUSE_LEFT, modsFromEvent(event))
+    let suppressCommandPathHover = shouldSuppressCommandPathHover(for: event.modifierFlags)
+    let mouseMods = hoverModsFromFlags(
+        event.modifierFlags,
+        suppressCommandPathHover: suppressCommandPathHover
+    )
+    if event.clickCount == 1 {
+        ghostty_surface_mouse_pos(surface, point.x, bounds.height - point.y, mouseMods)
+    }
+    _ = ghostty_surface_mouse_button(surface, GHOSTTY_MOUSE_PRESS, GHOSTTY_MOUSE_LEFT, mouseMods)
 }
 
 override func mouseUp(with event: NSEvent) {
@@
-    let consumed = ghostty_surface_mouse_button(surface, GHOSTTY_MOUSE_RELEASE, GHOSTTY_MOUSE_LEFT, modsFromEvent(event))
+    let suppressCommandPathHover = shouldSuppressCommandPathHover(for: event.modifierFlags)
+    let mouseMods = hoverModsFromFlags(
+        event.modifierFlags,
+        suppressCommandPathHover: suppressCommandPathHover
+    )
+    let consumed = ghostty_surface_mouse_button(surface, GHOSTTY_MOUSE_RELEASE, GHOSTTY_MOUSE_LEFT, mouseMods)
@@
-    let suppressCommandPathHover = shouldSuppressCommandPathHover(for: event.modifierFlags)
     if !consumed,
        event.modifierFlags.contains(.command),
        !suppressCommandPathHover {
@@
-        ghostty_surface_mouse_pos(surface, point.x, bounds.height - point.y, modsFromEvent(event))
+        ghostty_surface_mouse_pos(surface, point.x, bounds.height - point.y, mouseMods)
         tryOpenWordAsPath()
     }
 }

Also applies to: 6792-6805

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/GhosttyTerminalView.swift` around lines 6779 - 6785, Compute and
apply the selection-suppression-modifiers before calling any
ghostty_surface_mouse_button/ghostty_surface_mouse_pos so the suppressed
modifier set is used for both press and release and the selection state check
sees the correct suppressed mods; specifically, derive a suppressedMods =
suppressedModsFromEvent(event) (or call the existing suppression helper)
immediately after computing modsFromEvent(event), use suppressedMods for the
ghostty_surface_mouse_pos and both ghostty_surface_mouse_button calls
(GHOSTTY_MOUSE_PRESS/GHOSTTY_MOUSE_LEFT and the corresponding release), and
ensure suppression logic runs before dispatching the release so clearing
selection cannot race with your fallback open logic (update the click-handling
paths that call ghostty_surface_mouse_button and modsFromEvent(event)
accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 6779-6785: Compute and apply the selection-suppression-modifiers
before calling any ghostty_surface_mouse_button/ghostty_surface_mouse_pos so the
suppressed modifier set is used for both press and release and the selection
state check sees the correct suppressed mods; specifically, derive a
suppressedMods = suppressedModsFromEvent(event) (or call the existing
suppression helper) immediately after computing modsFromEvent(event), use
suppressedMods for the ghostty_surface_mouse_pos and both
ghostty_surface_mouse_button calls (GHOSTTY_MOUSE_PRESS/GHOSTTY_MOUSE_LEFT and
the corresponding release), and ensure suppression logic runs before dispatching
the release so clearing selection cannot race with your fallback open logic
(update the click-handling paths that call ghostty_surface_mouse_button and
modsFromEvent(event) accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2bf1637c-60d6-409d-915b-78e312ebbe98

📥 Commits

Reviewing files that changed from the base of the PR and between f91bcf2 and ea102bc.

📒 Files selected for processing (1)
  • Sources/GhosttyTerminalView.swift

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/GhosttyTerminalView.swift">

<violation number="1" location="Sources/GhosttyTerminalView.swift:6666">
P3: This nested debug condition is unreachable: `effectiveFlags` has already had `.command` removed, so `effectiveFlags.contains(.command)` is always false.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread Sources/GhosttyTerminalView.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/TerminalCmdClickUITests.swift`:
- Around line 159-166: The XCTFail message currently calls socketJSON(method:
"surface.send_text", ...) again, which re-sends the mutation; instead call
socketJSON once and store its raw response (e.g., responseEnvelope or
responseRaw) before the guard, use that stored value to derive 'envelope' and
'ok', and interpolate the stored response in XCTFail—this ensures socketJSON is
invoked only once (refer to socketJSON, envelope, XCTFail, and the
"surface.send_text" call).
- Around line 320-331: The code mixes AppKit and Quartz coordinates by using
NSEvent.mouseLocation (AppKit Y-down) with quartzEnd (Quartz Y-up) when
interpolating drag points; convert the currentLocation to Quartz coordinates
before the interpolation (e.g., use the existing
quartzPoint(fromAccessibilityPoint:) or add a helper to transform
NSEvent.mouseLocation into Quartz space) so the loop that computes point for
postMouseEvent(type:.leftMouseDragged, at:point, ...) uses both endpoints in the
same coordinate system; keep the same clampedSteps and dragDuration logic but
perform the coordinate conversion once before the for loop.
- Around line 509-513: The current send loop treats any non-negative writeResult
as success and can leave part of outbound unwritten; change the logic around
outbound.withUnsafeBytes { ... send(fd, ...) } so that you loop until all bytes
are sent or an error occurs: keep track of bytesSent and bytesRemaining, call
send repeatedly using bytes.baseAddress.advanced(by: bytesSent) and
bytesRemaining as the length, update bytesSent by the positive return value, and
handle negative returns (errno) by returning nil; update the code that uses
outbound, send, writeResult and fd to implement this retrying write loop to
ensure the full request is written.

In `@Sources/cmuxApp.swift`:
- Around line 3909-3925: appendLine currently falls back to
payload.write(to:options:) inside the catch, which overwrites the whole file;
change the catch in appendLine(_ line:String, to url:URL) so it preserves
existing contents by reading existing data (if any) and writing existingData +
payload atomically, or by retrying an append via FileHandle seeking to end
before writing, instead of replacing the file. Ensure you still call
ensureParentDirectory(for:) and keep the original append path
(FileHandle(forWritingTo:)) as primary and only use the combined-data atomic
write as a safe fallback to maintain append-only semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c4e96001-9347-462f-a76e-17c3f6edb82f

📥 Commits

Reviewing files that changed from the base of the PR and between ea102bc and b96f319.

📒 Files selected for processing (4)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/GhosttyTerminalView.swift
  • Sources/cmuxApp.swift
  • cmuxUITests/TerminalCmdClickUITests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/GhosttyTerminalView.swift

Comment thread cmuxUITests/TerminalCmdClickUITests.swift Outdated
Comment thread cmuxUITests/TerminalCmdClickUITests.swift Outdated
Comment thread cmuxUITests/TerminalCmdClickUITests.swift Outdated
Comment thread Sources/cmuxApp.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
cmuxUITests/TerminalCmdClickUITests.swift (1)

42-47: Decouple hover-test readiness from cmd-click fixture setup.

testHoldingCommandAfterSelectionSuppresssCommandHoverDispatch only needs ready == "1", but waitForReadySetup() also blocks on expectedPath. That means a cmd-click fixture/setup regression can fail the hover test before it ever reaches select_token_and_hold_command.

♻️ Suggested refactor
     func testHoldingCommandAfterSelectionSuppresssCommandHoverDispatch() throws {
         let app = launchApp(captureOpenPaths: false, captureHoverDiagnostics: true)
         defer { app.terminate() }
 
-        _ = try waitForReadySetup()
+        try waitForHarnessReady()
         let result = try runCommand(action: "select_token_and_hold_command")
@@
+    private func waitForHarnessReady(timeout: TimeInterval = 15.0) throws {
+        let matched = waitForCondition(timeout: timeout) {
+            self.loadSetupData()?["ready"] as? String == "1"
+        }
+
+        guard matched else {
+            throw NSError(domain: "TerminalCmdClickUITests", code: 1, userInfo: [
+                NSLocalizedDescriptionKey: "Expected terminal cmd-click setup data. payload=\(loadSetupData() ?? [:])"
+            ])
+        }
+    }
+
     private func waitForReadySetup(timeout: TimeInterval = 15.0) throws -> SetupData {
         var setup: SetupData?
         let matched = waitForCondition(timeout: timeout) {
             guard let payload = self.loadSetupData(),
                   payload["ready"] as? String == "1",

Also applies to: 168-176

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/TerminalCmdClickUITests.swift` around lines 42 - 47, The test
testHoldingCommandAfterSelectionSuppresssCommandHoverDispatch should not call
waitForReadySetup() because that blocks on expectedPath; instead wait only for
hover readiness (ready == "1") before running the fixture action. Replace the
call to waitForReadySetup() in
testHoldingCommandAfterSelectionSuppresssCommandHoverDispatch (and the other
affected test at lines ~168-176) with a call to a readiness-only helper (e.g.,
waitForReady(ready: "1") or a new waitForReadyOnly function) so the test waits
just for ready == "1" and then proceeds to runCommand(action:
"select_token_and_hold_command"); keep references to waitForReadySetup,
runCommand, and the test method names to locate the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/TerminalCmdClickUITests.swift`:
- Around line 117-135: The launch path can return before the UI is actually
foregrounded; update launchAndEnsureForeground to use its timeout parameter,
treat only .runningForeground as success (not .runningBackground), and block
until the app's state is .runningForeground (or until key window/activation
evidence is present) before returning; then in launchApp call
launchAndEnsureForeground and only call/return after that confirmation so
app.activate() (if still needed) happens after the foreground check—refer to the
functions launchAndEnsureForeground and launchApp when making these changes.

In `@Sources/AppDelegate.swift`:
- Around line 3376-3379: The current logic only sets tokenPointPayload once when
hasRenderedToken is true, so if that payload contains tokenLayoutMatch = "0" we
never recompute and never detect the later "1"; modify the condition around
tokenPointPayload assignment in AppDelegate (the hasRenderedToken /
tokenPointPayload block) to recompute tokenPointPayload by calling
tokenPoints(in:terminalPanel, visibleText:visibleText) whenever hasRenderedToken
is true AND (tokenPointPayload is nil OR (tokenPointPayload?["tokenLayoutMatch"]
as? String) != "1"), or alternatively clear tokenPointPayload when
tokenLayoutMatch == "0" so subsequent polls invoke tokenPoints again; reference
tokenPointPayload, hasRenderedToken, tokenPoints(in:visibleText:), and
tokenLayoutMatch in your change.
- Around line 3302-3309: The test captures currentTabManager but later code
(evaluate()/timeout path) uses self.tabManager and looks up the first cmux.main*
window, causing a race; update the terminal snapshot and window lookup to use
the captured currentTabManager (and its selectedWorkspace/focusedTerminalPanel)
throughout the evaluate()/timeout paths and in writeState calls so the same
pinned manager/window pair is used for both reading textSnapshot and for the
simulated selection; specifically, replace references that call self.tabManager
or NSApp.windows.first... with lookups bound to currentTabManager and a window
resolved once from that captured context before passing into
writeState/evaluate/timeout routines.

---

Nitpick comments:
In `@cmuxUITests/TerminalCmdClickUITests.swift`:
- Around line 42-47: The test
testHoldingCommandAfterSelectionSuppresssCommandHoverDispatch should not call
waitForReadySetup() because that blocks on expectedPath; instead wait only for
hover readiness (ready == "1") before running the fixture action. Replace the
call to waitForReadySetup() in
testHoldingCommandAfterSelectionSuppresssCommandHoverDispatch (and the other
affected test at lines ~168-176) with a call to a readiness-only helper (e.g.,
waitForReady(ready: "1") or a new waitForReadyOnly function) so the test waits
just for ready == "1" and then proceeds to runCommand(action:
"select_token_and_hold_command"); keep references to waitForReadySetup,
runCommand, and the test method names to locate the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7bccfa5f-fa80-424d-b9b6-5354bf45150f

📥 Commits

Reviewing files that changed from the base of the PR and between b96f319 and 8d73be9.

📒 Files selected for processing (3)
  • Sources/AppDelegate.swift
  • Sources/GhosttyTerminalView.swift
  • cmuxUITests/TerminalCmdClickUITests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/GhosttyTerminalView.swift

Comment thread cmuxUITests/TerminalCmdClickUITests.swift Outdated
Comment thread Sources/AppDelegate.swift Outdated
Comment thread Sources/AppDelegate.swift Outdated

This branch was successfully deployed

1 active deployment
Preview — 641f1159 Deployed Apr 6, 2026 by vercel[bot]
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.

Holding Cmd after selecting text triggers unwanted highlighting (cmd+click path detection)

2 participants