Repository navigation
fix: open terminal links on cmd-click even when mouse reporting is active - #71
Conversation
…tive When a fullscreen/alternate-screen TUI (e.g. Claude Code) enables mouse reporting, holding the ctrl/super link modifier did not let link hover/click be processed locally. The gates in keyCallback and cursorPosCallback refreshed link state only when mouse reporting was off or shift was releasing capture, so a cmd-click was reported to the program instead of opening the link. Embedders like cmux that route link opens to a built-in browser therefore fell back to the OS default browser whenever a mouse-grabbing TUI was active. Allow local link handling when the ctrl/super link-activation modifier is held, even under mouse reporting (matching iTerm2 and macOS Terminal), via a shared mouseLinkRefreshAllowed helper. The click is then swallowed by processLinks before the mouse-report path, so the link opens consistently. Adds a unit test for the pure decision logic. Fixes manaflow-ai/cmux#5128 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughSurface.zig consolidates link-hover evaluation logic to consistently handle the ctrl/super link-activation modifier across keyboard and cursor events. New helper functions encode the eligibility rules and are applied uniformly to both input paths, with unit test coverage for the testable pure function. ChangesLink-hover evaluation and modifier consistency
Possibly related PRs
Poem
🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/Surface.zig`:
- Around line 3824-3828: The gate is using only terminal mouse_event flag;
change it to use the effective mouse-reporting state (respecting
config.mouse_reporting and runtime toggles) when calling
mouseLinkRefreshAllowedState. Replace the first argument expression
self.io.terminal.flags.mouse_event != .none with a call/reference to the
effective mouse-reporting boolean (e.g., a helper like
self.effectiveMouseReporting() or the evaluated expression that combines
self.config.mouse_reporting and terminal flags), keeping the other args
(self.mouseShiftCapture(false), self.mouse.mods) unchanged and ensuring the
effective state reflects runtime toggles.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Greptile SummaryFixes a bug where Cmd/Ctrl-click on a terminal link was silently swallowed by mouse reporting when a fullscreen TUI (e.g. an alt-screen app) had grabbed the mouse, causing link opens to fall back to the OS browser. The fix introduces a
Confidence Score: 4/5Safe to merge; the bypass fires only on the exact ctrl/super chord that link activation already requires, keeping the gate and the activation condition in lock-step. The fix correctly mirrors the activation condition used inside linkAtPos in the new bypass gate. Custom hover_mods configurations won't benefit under mouse reporting, but that is a pre-existing design constraint acknowledged by the PR. One test matrix cell is absent but the behavior there is correct. src/Surface.zig around mouseLinkRefreshAllowedState if custom link modifier support is added later. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Mouse event: key change or cursor move] --> B{mods changed?}
B -- No --> Z[No action]
B -- Yes --> C[modsChanged: update self.mouse.mods]
C --> D[mouseLinkRefreshAllowed?]
D --> E{mouse_event == .none?}
E -- Yes --> F[return true: evaluate links locally]
E -- No --> G{mods.shift AND NOT shift_capture?}
G -- Yes --> F
G -- No --> H{mods == ctrlOrSuper exactly?}
H -- Yes --> F
H -- No --> I[return false: deliver to program]
F --> J[mouseRefreshLinks]
J --> K[linkAtPos]
K --> L{link found?}
L -- Yes --> M[over_link = true]
L -- No --> N[over_link = false]
M --> O[mouseButtonCallback: processLinks, open URL]
I --> P[Mouse event reported to running program]
Reviews (1): Last reviewed commit: "fix: open terminal links on cmd-click ev..." | Re-trigger Greptile |
| ) bool { | ||
| if (!mouse_event_active) return true; | ||
| if (mods.shift and !shift_capture) return true; | ||
| return mods.equal(input.ctrlOrSuper(.{})); |
There was a problem hiding this comment.
Bypass condition doesn't cover custom link modifiers
mouseLinkRefreshAllowedState bypasses mouse reporting only for the exact ctrlOrSuper(.{}) chord. For OSC 8 links that's the right check (it mirrors linkAtPos line 4413), but for regex links the activation mod comes from Config.link.highlight.hover_mods, which users can configure to something else (e.g. ctrl+alt). A user who configured a non-default hover_mods will see the bypass condition silently fail under mouse reporting because the exact-match against the default chord won't fire for their custom mod.
This is a design limitation that could be addressed by threading the configured hover_mods into this check, but it's out of scope for the current fix and the PR description acknowledges the default modifier scope.
| // Mouse reporting off: links are always evaluated, regardless of mods. | ||
| try std.testing.expect(mouseLinkRefreshAllowedState(false, false, .{})); | ||
| try std.testing.expect(mouseLinkRefreshAllowedState(false, false, ctrl_or_super)); | ||
|
|
||
| // Mouse reporting on, no relevant mods: the event is reported to the app, | ||
| // links are not evaluated locally. | ||
| try std.testing.expect(!mouseLinkRefreshAllowedState(true, false, .{})); | ||
|
|
||
| // Mouse reporting on, ctrl/super link modifier held: links are evaluated | ||
| // so Cmd-click (macOS) / Ctrl-click opens a link even while a | ||
| // fullscreen/alternate-screen TUI has grabbed the mouse. This is the | ||
| // behavior that was missing in cmux issue #5128. | ||
| try std.testing.expect(mouseLinkRefreshAllowedState(true, false, ctrl_or_super)); | ||
|
|
||
| // Mouse reporting on, shift held and shift-capture disallowed: evaluated | ||
| // (pre-existing shift-release-from-capture behavior, unchanged). | ||
| try std.testing.expect(mouseLinkRefreshAllowedState(true, false, .{ .shift = true })); | ||
|
|
||
| // Mouse reporting on, shift held but shift-capture allowed: reported. | ||
| try std.testing.expect(!mouseLinkRefreshAllowedState(true, true, .{ .shift = true })); | ||
|
|
||
| // Mouse reporting on, ctrl/super plus a non-shift modifier: not an exact | ||
| // link-activation chord, so the event is reported to the app. | ||
| try std.testing.expect(!mouseLinkRefreshAllowedState(true, false, input.ctrlOrSuper(.{ .alt = true }))); |
There was a problem hiding this comment.
Test matrix is missing one cell
The truth table covers (mouse_event_active=true, shift_capture=false, ctrl_or_super) but not (true, shift_capture=true, ctrl_or_super). When shift capture is enabled and ctrl/super is the only mod, the function should still return true because mods.shift is false and we fall through to the mods.equal(ctrlOrSuper) check. Adding that assertion would prevent a future refactor from inadvertently gating the ctrl/super path on shift_capture.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
1 issue found across 1 file
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Addresses review feedback on the PR. Pass isMouseReporting() (config + terminal mode) into mouseLinkRefreshAllowedState instead of the raw terminal mouse_event flag, mirroring the sibling mouse-report gate in mouseButtonCallback/cursorPosCallback. This also handles links locally when mouse reporting is disabled via config even though a program set the mode (the program receives no mouse events in that case). Renames the parameter to mouse_reporting and adds the shift-capture + ctrl/super test cell so the ctrl/super path is never inadvertently gated on shift_capture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed the bot review in 55d154a:
AI disclosure: review triage and the above change were done with Claude Code (Claude Opus 4.8); reviewed by me. |
…ostty-org#5128 follow-up) Follow-up to #71: keeps Cmd-click link-open from leaking a half-click to mouse-grabbing alt-screen TUIs. Merge commit keeps d1dbbec an ancestor of fork main for the cmux submodule pin.
…mux ghostty-org#5128) Final follow-up to #71/#74: keeps a Cmd-held drag from leaking button-motion to mouse-grabbing TUIs. Merge commit keeps 76ead3e an ancestor of fork main for the cmux submodule pin.
…mux ghostty-org#5128) Final review follow-up to #71/#74/#75: scopes the link-chord click/drag suppression to the left button and clears stale link hover when the chord drops via cursor mods. Merge commit keeps f241952 an ancestor of fork main.
Follow-up to manaflow-ai#71. When the ctrl/super link-activation modifier is held over a link in a mouse-reporting alt-screen app, mouseButtonCallback previously still reported the left-button *press* to the program and only swallowed the later *release* via processLinks (which runs only on release). That leaked a half-click — a press with no matching release — to mouse-grabbing TUIs like Claude Code and Codex, exactly the scenario the link fix targets. Break out of the mouse-report path when over a link with the ctrl/super chord held, suppressing both press and release from the program, mirroring the existing shift-release-from-capture suppression. The release still opens the link locally. Fixes manaflow-ai/cmux#5128. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ex) (#5406) * Bump ghostty to 55d154a: open links on cmd-click under mouse reporting Fixes #5128. Clicking a link inside a fullscreen alternate-screen TUI (Claude Code, Codex) opened the OS default browser instead of honoring the configured cmux link-open target. cmux's GHOSTTY_ACTION_OPEN_URL handler is already mode-independent (resolveTerminalOpenURLTarget routes per BrowserAvailabilitySettings, no mouse-mode branch); the gap was in ghostty core, where link hover state was refreshed only when mouse reporting was off or shift released capture, so a Cmd-click under a mouse-grabbing TUI never fired open_url. Bumps the ghostty submodule to 55d154a (previous pin 176bd55 + the two link-fix commits from manaflow-ai/ghostty#71, merged into fork main). The fix also evaluates links locally when the ctrl/super link modifier is held, using the effective mouse-reporting state, matching iTerm2 and macOS Terminal. Publishes and pins the matching GhosttyKit xcframework (xcframework-55d154a...-crashsubdir-cmux-crash-v1) and updates docs/ghostty-fork.md. No cmux Swift change is required (no cmux-only gap), so there are no new user-facing strings to localize. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to d1dbbec: suppress half-click leak on cmd-clicked links Addresses codex/greptile review of the link fix: under mouse reporting a Cmd-click over a link still leaked a half-click to the program because mouseButtonCallback reported the press (link-open runs only on release). The ghostty follow-up (manaflow-ai/ghostty#74) suppresses the whole click — press and release — whenever the ctrl/super link chord is held, keyed on the modifier like the existing shift-release path so cursor jitter can't leak a press or a release. Eliminates the half-click in both directions. Re-pins the ghostty submodule from 55d154a to d1dbbec, repoints the prebuilt GhosttyKit release/checksum, and updates docs/ghostty-fork.md. d1dbbec is an ancestor of manaflow-ai/ghostty main (PR #71 and #74 merged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to 76ead3e: full click+drag suppression for cmd-clicked links Final review iteration (codex P2). After #74 suppressed the press/release of a ctrl/super-chord link click, cursorPosCallback could still emit .motion reports during a Cmd-held drag (click_state == .press), leaking button-motion to a mouse-grabbing program. ghostty #75 mirrors the shift "grab override" for the ctrl/super chord in the motion path, so the link chord now suppresses the whole click+drag — press, release, and motion — consistently. Re-pins the ghostty submodule from d1dbbec to 76ead3e and repoints the prebuilt GhosttyKit release/checksum + docs. 76ead3e is an ancestor of manaflow-ai/ghostty main (PR #71, #74, #75 merged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to f241952: scope link suppression + clear stale hover Final review iteration (codex P2 x2). After #74/#75 suppressed the whole click+ drag for the ctrl/super link chord, two edges remained: the suppression fired for any button (swallowing ctrl/super right/middle clicks instead of delivering them to the program), and a stale link highlight/cursor could persist when the chord was released through cursorPosCallback's mods. ghostty #76 scopes the suppression to the left button and clears the hover by refreshing when over_link is set (mirroring keyCallback's existing reset branch). Re-pins the ghostty submodule from 76ead3e to f241952 and repoints the prebuilt GhosttyKit release/checksum + docs. f241952 is an ancestor of manaflow-ai/ghostty main (PR #71, #74, #75, #76 merged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to 59fb750: clear link-click latch unconditionally Addresses the final review round on the link-under-mouse-reporting fix: - codex P2: the suppression re-checked live modifiers, so releasing ctrl/super before the mouse button could leak the release. ghostty #77 latches the decision at left-button press (mouse.link_click_active) and applies it through the release. - greptile P2: the latch was cleared only inside the mouse-reporting block, so toggling reporting off mid-click could leave it stale; it is now cleared unconditionally on left release. Re-pins the ghostty submodule from f241952 to 59fb750 and repoints the prebuilt GhosttyKit release/checksum + docs. 59fb750 is an ancestor of manaflow-ai/ghostty main once #77 merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to 9f014e9: unify link-click suppression and open Final review round on the link-under-mouse-reporting fix (codex P2/P3): the report-suppression latched at press but the link-open path re-derived from live modifiers, so releasing ctrl/super before the button swallowed the click, and the latch reset wasn't reached on the link-open early return. ghostty #78 makes linkAtPos use the latched chord and attempts processLinks whenever the click is latched, and clears the latch via a function-level defer. One press-time decision now drives press/drag/release suppression and link opening. Re-pins the ghostty submodule from 59fb750 to 9f014e9 and repoints the prebuilt GhosttyKit release/checksum + docs. 9f014e9 is an ancestor of manaflow-ai/ghostty main once #78 merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to df789cd: only open latched link clicks that started on a link Final review round (codex): the latched link-open path opened the link under the release cursor for any in-flight ctrl/super click, so a chord drag that started off a link and released over one opened that link (its press was suppressed). ghostty #79 captures whether the press was over a link (link_press_over_link) and only opens via the latched path when it was, so an off-link-started drag is swallowed rather than opening a link it merely released over. Re-pins the ghostty submodule from 9f014e9 to df789cd and repoints the prebuilt GhosttyKit release/checksum + docs. df789cd is an ancestor of manaflow-ai/ghostty main once #79 merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: pin merged GhosttyKit artifact * Pin GhosttyKit checksum for merged ghostty 34cbf18 (#5128 + #5458) Pins the SHA256 of the published prebuilt xcframework-34cbf180d8917b802d61d9929cfb493594f2ab52-crashsubdir-cmux-crash-v1 that merges the surface registry serialization (#5458) into the alt-screen Cmd-click link fix (#5128). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Refresh Swift file-length budget for ContentView/SessionIndexView The origin/main merge (sidebar scroll + macOS 27 crash fixes, PR #5670) grew Sources/ContentView.swift to 19161 and Sources/SessionIndexView.swift to 2877, which exceed the inherited budget. Bump only those two entries to match; this is upstream main debt surfaced by the merge, not a change from the ghostty bump. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ex) (#5406) * Bump ghostty to 55d154a: open links on cmd-click under mouse reporting Fixes #5128. Clicking a link inside a fullscreen alternate-screen TUI (Claude Code, Codex) opened the OS default browser instead of honoring the configured cmux link-open target. cmux's GHOSTTY_ACTION_OPEN_URL handler is already mode-independent (resolveTerminalOpenURLTarget routes per BrowserAvailabilitySettings, no mouse-mode branch); the gap was in ghostty core, where link hover state was refreshed only when mouse reporting was off or shift released capture, so a Cmd-click under a mouse-grabbing TUI never fired open_url. Bumps the ghostty submodule to 55d154a (previous pin 176bd550f + the two link-fix commits from manaflow-ai/ghostty#71, merged into fork main). The fix also evaluates links locally when the ctrl/super link modifier is held, using the effective mouse-reporting state, matching iTerm2 and macOS Terminal. Publishes and pins the matching GhosttyKit xcframework (xcframework-55d154a...-crashsubdir-cmux-crash-v1) and updates docs/ghostty-fork.md. No cmux Swift change is required (no cmux-only gap), so there are no new user-facing strings to localize. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to d1dbbec9b: suppress half-click leak on cmd-clicked links Addresses codex/greptile review of the link fix: under mouse reporting a Cmd-click over a link still leaked a half-click to the program because mouseButtonCallback reported the press (link-open runs only on release). The ghostty follow-up (manaflow-ai/ghostty#74) suppresses the whole click — press and release — whenever the ctrl/super link chord is held, keyed on the modifier like the existing shift-release path so cursor jitter can't leak a press or a release. Eliminates the half-click in both directions. Re-pins the ghostty submodule from 55d154a to d1dbbec9b, repoints the prebuilt GhosttyKit release/checksum, and updates docs/ghostty-fork.md. d1dbbec9b is an ancestor of manaflow-ai/ghostty main (PR #71 and #74 merged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to 76ead3eae: full click+drag suppression for cmd-clicked links Final review iteration (codex P2). After #74 suppressed the press/release of a ctrl/super-chord link click, cursorPosCallback could still emit .motion reports during a Cmd-held drag (click_state == .press), leaking button-motion to a mouse-grabbing program. ghostty #75 mirrors the shift "grab override" for the ctrl/super chord in the motion path, so the link chord now suppresses the whole click+drag — press, release, and motion — consistently. Re-pins the ghostty submodule from d1dbbec9b to 76ead3eae and repoints the prebuilt GhosttyKit release/checksum + docs. 76ead3eae is an ancestor of manaflow-ai/ghostty main (PR #71, #74, #75 merged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to f24195271: scope link suppression + clear stale hover Final review iteration (codex P2 x2). After #74/#75 suppressed the whole click+ drag for the ctrl/super link chord, two edges remained: the suppression fired for any button (swallowing ctrl/super right/middle clicks instead of delivering them to the program), and a stale link highlight/cursor could persist when the chord was released through cursorPosCallback's mods. ghostty #76 scopes the suppression to the left button and clears the hover by refreshing when over_link is set (mirroring keyCallback's existing reset branch). Re-pins the ghostty submodule from 76ead3eae to f24195271 and repoints the prebuilt GhosttyKit release/checksum + docs. f24195271 is an ancestor of manaflow-ai/ghostty main (PR #71, #74, #75, #76 merged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to 59fb750c0: clear link-click latch unconditionally Addresses the final review round on the link-under-mouse-reporting fix: - codex P2: the suppression re-checked live modifiers, so releasing ctrl/super before the mouse button could leak the release. ghostty #77 latches the decision at left-button press (mouse.link_click_active) and applies it through the release. - greptile P2: the latch was cleared only inside the mouse-reporting block, so toggling reporting off mid-click could leave it stale; it is now cleared unconditionally on left release. Re-pins the ghostty submodule from f24195271 to 59fb750c0 and repoints the prebuilt GhosttyKit release/checksum + docs. 59fb750c0 is an ancestor of manaflow-ai/ghostty main once #77 merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to 9f014e98b: unify link-click suppression and open Final review round on the link-under-mouse-reporting fix (codex P2/P3): the report-suppression latched at press but the link-open path re-derived from live modifiers, so releasing ctrl/super before the button swallowed the click, and the latch reset wasn't reached on the link-open early return. ghostty #78 makes linkAtPos use the latched chord and attempts processLinks whenever the click is latched, and clears the latch via a function-level defer. One press-time decision now drives press/drag/release suppression and link opening. Re-pins the ghostty submodule from 59fb750c0 to 9f014e98b and repoints the prebuilt GhosttyKit release/checksum + docs. 9f014e98b is an ancestor of manaflow-ai/ghostty main once #78 merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Re-pin ghostty to df789cd4b: only open latched link clicks that started on a link Final review round (codex): the latched link-open path opened the link under the release cursor for any in-flight ctrl/super click, so a chord drag that started off a link and released over one opened that link (its press was suppressed). ghostty #79 captures whether the press was over a link (link_press_over_link) and only opens via the latched path when it was, so an off-link-started drag is swallowed rather than opening a link it merely released over. Re-pins the ghostty submodule from 9f014e98b to df789cd4b and repoints the prebuilt GhosttyKit release/checksum + docs. df789cd4b is an ancestor of manaflow-ai/ghostty main once #79 merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: pin merged GhosttyKit artifact * Pin GhosttyKit checksum for merged ghostty 34cbf180d (#5128 + #5458) Pins the SHA256 of the published prebuilt xcframework-34cbf180d8917b802d61d9929cfb493594f2ab52-crashsubdir-cmux-crash-v1 that merges the surface registry serialization (#5458) into the alt-screen Cmd-click link fix (#5128). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Refresh Swift file-length budget for ContentView/SessionIndexView The origin/main merge (sidebar scroll + macOS 27 crash fixes, PR #5670) grew Sources/ContentView.swift to 19161 and Sources/SessionIndexView.swift to 2877, which exceed the inherited budget. Bump only those two entries to match; this is upstream main debt surfaced by the merge, not a change from the ghostty bump. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Fixes manaflow-ai/cmux#5128 — terminal links open in the external browser instead of the embedded one, but only while a fullscreen / alternate-screen TUI (which enables mouse reporting) is active.
Cause
Link hover state is refreshed (
keyCallback,cursorPosCallback) only when mouse reporting is off, or shift is releasing the mouse from capture. Holding the ctrl/super link modifier isn't considered, so under a mouse-grabbing TUIover_linkstaysfalse, the link-click branch inmouseButtonCallbackis skipped, and the click is reported to the program instead of opening the link.Fix
Also refresh links when the ctrl/super link-activation modifier is held, even under mouse reporting — so cmd-click (macOS) / ctrl-click opens a link regardless of mouse capture, matching iTerm2 and macOS Terminal. The click is then swallowed by
processLinksbefore the mouse-report path. The shared condition lives inmouseLinkRefreshAllowed(used by both gates); the pure logic is split intomouseLinkRefreshAllowedStatefor testing. That modifier is exactly what both link kinds already require to activate: OSC 8 (linkAtPos) and the default url link (hover_mods = ctrlOrSuper).Tests
mouseLinkRefreshAllowedStateunit test covers the truth table, including the regression case (mouse reporting on + ctrl/super held ⇒ links handled locally).zig fmt/ast-checkclean;zig build testcompiles, but the test binary can't linklibSystemunder zig 0.15.2 on macOS 26 in my environment, so I couldn't execute it locally — CI should.AI disclosure (per AI_POLICY.md): developed with Claude Code (Claude Opus 4.8). Investigation, patch, and test were AI-assisted; I have reviewed the change and can explain it.