Skip to content

Commit fd84e53

Browse files
authored
fix(hyperlinks): check if OSC-8 is supported, fallback to just the link (#92)
1 parent 5772948 commit fd84e53

1 file changed

Lines changed: 71 additions & 4 deletions

File tree

view/html.go

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,76 @@ func getCellHeightFromFd(fd int) int {
7878
return 0
7979
}
8080

81-
// hyperlink formats a string as a terminal-clickable hyperlink.
81+
// hyperlinkSupported checks if the terminal supports OSC 8 hyperlinks.
82+
func hyperlinkSupported() bool {
83+
term := strings.ToLower(os.Getenv("TERM"))
84+
85+
// Terminals known to support OSC 8 hyperlinks
86+
supportedTerms := []string{
87+
"kitty",
88+
"ghostty",
89+
"wezterm",
90+
"alacritty",
91+
"foot",
92+
"tmux",
93+
"screen",
94+
}
95+
96+
for _, supported := range supportedTerms {
97+
if strings.Contains(term, supported) {
98+
return true
99+
}
100+
}
101+
102+
// Check for specific terminal programs
103+
termProgram := strings.ToLower(os.Getenv("TERM_PROGRAM"))
104+
supportedPrograms := []string{
105+
"iterm.app",
106+
"hyper",
107+
"vscode",
108+
"ghostty",
109+
"wezterm",
110+
}
111+
112+
for _, supported := range supportedPrograms {
113+
if strings.Contains(termProgram, supported) {
114+
return true
115+
}
116+
}
117+
118+
// Check for VTE-based terminals (GNOME Terminal, etc.)
119+
if os.Getenv("VTE_VERSION") != "" {
120+
return true
121+
}
122+
123+
// Check for specific environment variables that indicate hyperlink support
124+
if os.Getenv("KITTY_WINDOW_ID") != "" ||
125+
os.Getenv("GHOSTTY_RESOURCES_DIR") != "" ||
126+
os.Getenv("WEZTERM_EXECUTABLE") != "" {
127+
return true
128+
}
129+
130+
return false
131+
}
132+
133+
// hyperlink formats a string as either a terminal-clickable hyperlink or plain text with URL.
82134
func hyperlink(url, text string) string {
83135
if text == "" {
84136
text = url
85137
}
86-
return fmt.Sprintf("\x1b]8;;%s\x07%s\x1b]8;;\x07", url, text)
138+
139+
supported := hyperlinkSupported()
140+
141+
if supported {
142+
// Use OSC 8 hyperlink sequence for supported terminals
143+
return fmt.Sprintf("\x1b]8;;%s\x07%s\x1b]8;;\x07", url, text)
144+
} else {
145+
// Fallback to plain text format for unsupported terminals
146+
if text == url {
147+
return fmt.Sprintf("<%s>", url)
148+
}
149+
return fmt.Sprintf("%s <%s>", text, url)
150+
}
87151
}
88152

89153
func decodeQuotedPrintable(s string) (string, error) {
@@ -362,8 +426,11 @@ func processBody(rawBody string, inline map[string]string, h1Style, h2Style, bod
362426
} else {
363427
debugKitty("kitty not detected for src=%s", src)
364428
}
365-
366-
s.ReplaceWithHtml(hyperlink(src, fmt.Sprintf("\n [Click here to view image: %s] \n", alt)))
429+
if hyperlinkSupported() {
430+
s.ReplaceWithHtml(hyperlink(src, fmt.Sprintf("\n [Click here to view image: %s] \n", alt)))
431+
} else {
432+
s.ReplaceWithHtml(fmt.Sprintf("\n [Image: %s, %s] \n", alt, src))
433+
}
367434
})
368435

369436
text := doc.Text()

0 commit comments

Comments
 (0)