diff --git a/src/Surface.zig b/src/Surface.zig index 50e55e722a..a341568f82 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1542,6 +1542,10 @@ fn searchCallback_( fn modsChanged(self: *Surface, mods: input.Mods) void { // The only place we keep track of mods currently is on the mouse. if (!self.mouse.mods.equal(mods)) { + // Invalidate link point cache so that mouseRefreshLinks + // re-evaluates on the next cursor callback with new mods. + self.mouse.link_point = null; + // The mouse mods only contain binding modifiers since we don't // want caps/num lock or sided modifiers to affect the mouse. self.mouse.mods = mods.binding(); @@ -4492,8 +4496,14 @@ fn linkAtPos( // Get our comparison mods const mouse_mods = self.mouseModsWithCapture(self.mouse.mods); + // For link detection, ignore shift so that Cmd+Shift+Click can also + // activate links (the apprt reads the actual shift state separately + // to choose between built-in and external browser). + var link_mods = mouse_mods; + link_mods.shift = false; + // If we have the proper modifiers set then we can check for OSC8 links. - if (mouse_mods.equal(input.ctrlOrSuper(.{}))) hyperlink: { + if (link_mods.equal(input.ctrlOrSuper(.{}))) hyperlink: { const rac = mouse_pin.rowAndCell(); const cell = rac.cell; if (!cell.hyperlink) break :hyperlink; @@ -4502,7 +4512,7 @@ fn linkAtPos( } // Fall back to configured links - return try self.linkAtPin(mouse_pin, mouse_mods); + return try self.linkAtPin(mouse_pin, link_mods); } /// Detects if a link is present at the given pin. diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index e0d8a4dd67..8458d414ec 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -1259,6 +1259,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type { ); } + // Strip shift for link highlighting — shift toggles browser + // destination in the apprt, not link detection. + var link_mods = state.mouse.mods; + link_mods.shift = false; + // Get our OSC8 links we're hovering if we have a mouse. // This requires terminal state because of URLs. const links: terminal.RenderState.CellSet = osc8: { @@ -1266,7 +1271,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { const vp = state.mouse.point orelse break :osc8 .empty; // If the right mods aren't pressed, then we can't match. - if (!state.mouse.mods.equal(inputpkg.ctrlOrSuper(.{}))) + if (!link_mods.equal(inputpkg.ctrlOrSuper(.{}))) break :osc8 .empty; break :osc8 self.terminal_state.linkCells( @@ -1296,13 +1301,17 @@ pub fn Renderer(comptime GraphicsAPI: type) type { }; // Outside the critical area we can update our links to contain - // our regex results. + // our regex results. Strip shift — it toggles browser destination, + // not link detection. + var render_link_mods = state.mouse.mods; + render_link_mods.shift = false; + self.config.links.renderCellMap( arena_alloc, &critical.links, &self.terminal_state, state.mouse.point, - state.mouse.mods, + render_link_mods, ) catch |err| { log.warn("error searching for regex links err={}", .{err}); };