From f599f8c867efb0e4d7bd21873d6415ad9e4f33aa Mon Sep 17 00:00:00 2001 From: Alex Straight Date: Sun, 8 Jun 2025 21:53:28 -0700 Subject: [PATCH 1/4] fix: copy_url_to_clipboard copies full OSC8 URL instead of single character OSC8 links were only detected when exact platform-specific modifiers were held (Cmd on macOS, Ctrl on Linux), but copy_url_to_clipboard should work with either. Additionally, OSC8 links were using selectionString() which gets visible text instead of the actual URI. Now we use osc8URI() for OSC8 links and fall back to selectionString() for regex-detected links. Fixes #7491 --- src/Surface.zig | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index dc7b0e3bf..7bd7a2748 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3625,7 +3625,7 @@ fn linkAtPos( const mouse_mods = self.mouseModsWithCapture(self.mouse.mods); // If we have the proper modifiers set then we can check for OSC8 links. - if (mouse_mods.equal(input.ctrlOrSuper(.{}))) hyperlink: { + if (mouse_mods.ctrl or mouse_mods.super) hyperlink: { const rac = mouse_pin.rowAndCell(); const cell = rac.cell; if (!cell.hyperlink) break :hyperlink; @@ -4416,19 +4416,32 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool const pos = try self.rt_surface.getCursorPos(); if (try self.linkAtPos(pos)) |link_info| { - // Get the URL text from selection - const url_text = (self.io.terminal.screen.selectionString(self.alloc, .{ - .sel = link_info[1], - .trim = self.config.clipboard_trim_trailing_spaces, - })) catch |err| { - log.err("error reading url string err={}", .{err}); - return false; + const url_text = switch (link_info[0]) { + ._open_osc8 => url_text: { + // For OSC8 links, get the URI directly from hyperlink data + const uri = self.osc8URI(link_info[1].start()) orelse { + log.warn("failed to get URI for OSC8 hyperlink", .{}); + return false; + }; + break :url_text try self.alloc.dupeZ(u8, uri); + }, + .open => url_text: { + // For regex links, get the text from selection + break :url_text (self.io.terminal.screen.selectionString(self.alloc, .{ + .sel = link_info[1], + .trim = self.config.clipboard_trim_trailing_spaces, + })) catch |err| { + log.err("error reading url string err={}", .{err}); + return false; + }; + }, }; + defer self.alloc.free(url_text); self.rt_surface.setClipboardString(url_text, .standard, false) catch |err| { log.err("error copying url to clipboard err={}", .{err}); - return true; + return false; }; return true; From 7d0134db1b0c1e92fb4cc25456a2e35617dc931a Mon Sep 17 00:00:00 2001 From: Alex Straight Date: Sun, 8 Jun 2025 23:08:11 -0700 Subject: [PATCH 2/4] fix: make regular URLs work with either ctrl or super modifiers --- src/Surface.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Surface.zig b/src/Surface.zig index 7bd7a2748..4a1c5d6c2 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3656,7 +3656,17 @@ fn linkAtPos( for (self.config.links) |link| { switch (link.highlight) { .always, .hover => {}, - .always_mods, .hover_mods => |v| if (!v.equal(mouse_mods)) continue, + .always_mods, .hover_mods => |v| { + // Special case: if the expected mods are "ctrl or super" (like the default URL config), + // then we should match if the user pressed either ctrl or super, just like OSC8 links. + const is_ctrl_or_super_expected = (v.ctrl and !v.super and !v.shift and !v.alt) or + (v.super and !v.ctrl and !v.shift and !v.alt); + if (is_ctrl_or_super_expected) { + if (!(mouse_mods.ctrl or mouse_mods.super)) continue; + } else { + if (!v.equal(mouse_mods)) continue; + } + }, } var it = strmap.searchIterator(link.regex); From a19f4aea0ad61664ef59a4fd7b947e8142e4550c Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jul 2025 09:29:03 -0700 Subject: [PATCH 3/4] Removed boolean logic, reverted back to ctrlOrSuper call --- src/Surface.zig | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 22f80d166..e3d813d17 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3676,17 +3676,18 @@ fn linkAtPos( for (self.config.links) |link| { switch (link.highlight) { .always, .hover => {}, - .always_mods, .hover_mods => |v| { - // Special case: if the expected mods are "ctrl or super" (like the default URL config), - // then we should match if the user pressed either ctrl or super, just like OSC8 links. - const is_ctrl_or_super_expected = (v.ctrl and !v.super and !v.shift and !v.alt) or - (v.super and !v.ctrl and !v.shift and !v.alt); - if (is_ctrl_or_super_expected) { - if (!(mouse_mods.ctrl or mouse_mods.super)) continue; - } else { - if (!v.equal(mouse_mods)) continue; - } - }, + .always_mods, .hover_mods => |v| if (!v.equal(mouse_mods)) continue, + // .always_mods, .hover_mods => |_| { + // // Special case: if the expected mods are "ctrl or super" (like the default URL config), + // // then we should match if the user pressed either ctrl or super, just like OSC8 links. + // // const is_ctrl_or_super_expected = (v.ctrl and !v.super and !v.shift and !v.alt) or + // // (v.super and !v.ctrl and !v.shift and !v.alt); + // // if (is_ctrl_or_super_expected) { + // // if (!(mouse_mods.ctrl or mouse_mods.super)) continue; + // // } else { + // // if (!v.equal(mouse_mods)) continue; + // // } + // }, } var it = strmap.searchIterator(link.regex); From fc48354b36b1e8e8012974ce2ea0b8c44054230e Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jul 2025 09:30:01 -0700 Subject: [PATCH 4/4] remove commented out block --- src/Surface.zig | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index e3d813d17..e2694b62e 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -3677,17 +3677,6 @@ fn linkAtPos( switch (link.highlight) { .always, .hover => {}, .always_mods, .hover_mods => |v| if (!v.equal(mouse_mods)) continue, - // .always_mods, .hover_mods => |_| { - // // Special case: if the expected mods are "ctrl or super" (like the default URL config), - // // then we should match if the user pressed either ctrl or super, just like OSC8 links. - // // const is_ctrl_or_super_expected = (v.ctrl and !v.super and !v.shift and !v.alt) or - // // (v.super and !v.ctrl and !v.shift and !v.alt); - // // if (is_ctrl_or_super_expected) { - // // if (!(mouse_mods.ctrl or mouse_mods.super)) continue; - // // } else { - // // if (!v.equal(mouse_mods)) continue; - // // } - // }, } var it = strmap.searchIterator(link.regex);