Skip to content

fix(render): wrap URLs in OSC 8 hyperlinks for clickable terminal links#135

Merged
shayne-snap merged 1 commit into
mainfrom
fix/markdown-hyperlinks
May 25, 2026
Merged

fix(render): wrap URLs in OSC 8 hyperlinks for clickable terminal links#135
shayne-snap merged 1 commit into
mainfrom
fix/markdown-hyperlinks

Conversation

@shayne-snap
Copy link
Copy Markdown
Contributor

Summary

Glamour's word-wrap breaks long URLs at hyphens, inserting newlines and padding that truncate the URL from the terminal's autodetection. This PR adds a post-processing step that wraps http(s) URLs in OSC 8 escape sequences so terminal clicks resolve to the full URL.

Changes

internal/tui/render/markdown.go

  • injectHyperlinks — post-processes glamour's ANSI output to reconstruct URLs broken across wrap gaps ([spaces]\n[spaces]) and wraps them in OSC 8 hyperlinks
  • markCodeURLs — marks http(s):// occurrences inside fenced/inline code blocks with a zero-width marker so they remain literal (no clickable link for code URLs)
  • urlSkipMark (0x1c, FS control byte) — preserved through glamour as zero-width, stripped in final output
  • scanWrappedURL / shouldJoinWrappedURL — heuristic to join glamour-wrapped URL fragments across lines
  • shouldTrimURLTail — trims trailing punctuation (.,;:!?) and unbalanced closers from click targets
  • Error fallback path now also runs injectHyperlinks on raw input

internal/tui/render/chat_test.go

  • Updated URL duplicate-counting assertions to use xansi.Strip(got) so ANSI escape sequences from OSC 8 wrapping don't cause false negatives

internal/tui/render/markdown_hyperlinks_test.go (new)

  • Tests for injectHyperlinks: wrap across lines, no over-join, punctuation trimming, balanced delimiters, no-op passthrough, multiple URLs

Testing

  • Existing tests pass with the xansi.Strip adjustments
  • New tests cover the core scenarios

Glamour's word-wrap breaks long URLs at hyphens, inserting newlines and
padding that truncate the URL from the terminal's autodetection. This
adds a post-processing step that wraps http(s) URLs in OSC 8 escape
sequences so terminal clicks resolve to the full URL.

- injectHyperlinks reconstructs URLs broken across glamour wrap gaps
  and wraps them in OSC 8 hyperlinks
- markCodeURLs marks URLs inside fenced/inline code regions with
  a zero-width marker (FS, 0x1c) so they stay literal and unlinked
- urlSkipMark (0x1c) is preserved through glamour as zero-width and
  stripped from final output
- Test assertions in chat_test.go use xansi.Strip to account for
  the new ANSI escape sequences in URL counts
@shayne-snap shayne-snap merged commit 2fc0aaf into main May 25, 2026
2 checks passed
@shayne-snap shayne-snap deleted the fix/markdown-hyperlinks branch May 25, 2026 10:35
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.

1 participant