feat(style): hyperlink PR numbers in submit output (#105)#107
feat(style): hyperlink PR numbers in submit output (#105)#107boneskull merged 2 commits intoboneskull:mainfrom
Conversation
submit prints "Updating PR #N for branch (base: parent)..." with no URL. Per the issue, the PR number should be the linked text when the terminal supports OSC 8 hyperlinks; otherwise fall back to "text (URL)" so the URL remains visible. Added Hyperlink(text, url) to internal/style/style.go: - Color/TTY enabled: emits OSC 8 escape sequence (\x1b]8;;URL\x1b\\TEXT\x1b]8;;\x1b\\), which renders as a clickable link in iTerm2, kitty, recent macOS Terminal, GNOME Terminal, etc. - Color/TTY disabled (NO_COLOR, piped output, dumb terminal): falls back to "text (url)". - Empty URL: returns text alone, no parens. Updated cmd/submit.go:465 to wrap "PR #N" with Hyperlink() and ghClient.PRURL(d.prNum). The (base: parent) suffix and trailing "... ok" / "failed" are unchanged. Added internal/style/style_test.go covering the three branches (enabled, disabled, empty URL). Closes boneskull#105
There was a problem hiding this comment.
Pull request overview
Adds terminal hyperlink support (OSC 8) for the PR #N token in submit’s “Updating …” progress output, with a non-TTY / NO_COLOR fallback that keeps the PR URL visible.
Changes:
- Introduces
(*style.Style).Hyperlink(text, url)to render OSC 8 hyperlinks when styling is enabled, otherwisetext (url)(ortextwhen URL is empty). - Updates
cmd/submit.goto render the PR number in the update progress line usingHyperlink(...). - Adds unit tests covering the enabled/disabled/empty-URL branches.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| internal/style/style.go | Adds Hyperlink helper for OSC 8 hyperlinks with fallback formatting. |
| internal/style/style_test.go | Adds tests for hyperlink rendering across enabled/disabled/empty URL cases. |
| cmd/submit.go | Uses Style.Hyperlink when printing “Updating PR …” progress output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if !s.enabled || url == "" { | ||
| if url == "" { | ||
| return text | ||
| } |
There was a problem hiding this comment.
@mvanhorn This seems valid, but also I'm not too terribly concerned about piped use-cases.
| if !strings.Contains(got, "https://github.com/owner/repo/pull/42") { | ||
| t.Errorf("Hyperlink should contain URL; got %q", got) | ||
| } | ||
| if !strings.HasPrefix(got, "\x1b]8;;") { | ||
| t.Errorf("Hyperlink should start with OSC 8 sequence; got %q", got) |
Address review feedback from copilot-pull-request-reviewer[bot]: - Hyperlink now requires both s.enabled and s.isTTY before emitting OSC 8, so CLICOLOR_FORCE / GH_FORCE_TTY no longer leaks escape sequences into piped output. NewWithColor(enabled) sets isTTY to match enabled so existing test behavior is preserved. - TestHyperlinkColorsEnabled now asserts the full OSC 8 sequence including the closing terminator, catching malformed or truncated output that the prior substring checks would have missed.
|
@boneskull addressed both in 05b7f66:
Verified locally: |
|
@mvanhorn TYVM |
|
@all-contributors please add @mvanhorn for code |
|
I've put up a pull request to add @mvanhorn! 🎉 |
Closes #105.
What changed
`submit` now wraps the `PR #N` token in its progress line with an OSC 8 terminal hyperlink, so iTerm2 / kitty / recent macOS Terminal / GNOME Terminal users can click the PR number to jump to the URL. Terminals without hyperlink support (or sessions where output is piped) get a plain `text (URL)` fallback so the URL is still visible.
Before:
```
Updating PR #3150 for boneskull/additional-locations (base: master)... ok
```
After (TTY with OSC 8): the `PR #3150` is a clickable link to the PR URL.
After (no TTY / NO_COLOR):
```
Updating PR #3150 (https://github.com/boneskull/gh-stack/pull/3150) for boneskull/additional-locations (base: master)... ok
```
How
Why this matches the issue
Quoted from the issue (#105):
That's the OSC 8 path. The fallback is what the issue showed as the alternate render.
Verification