Skip to content

feat: make hook hint deep links clickable using OSC 8 terminal hyperlinks#13734

Open
glours wants to merge 1 commit intomainfrom
os8link-for-hint
Open

feat: make hook hint deep links clickable using OSC 8 terminal hyperlinks#13734
glours wants to merge 1 commit intomainfrom
os8link-for-hint

Conversation

@glours
Copy link
Copy Markdown
Contributor

@glours glours commented Apr 14, 2026

What I did

Wrap the docker-desktop://dashboard/logs URL in OSC 8 escape sequences with underline styling so it appears as a clickable link in supported terminals. Respects NO_COLOR and COMPOSE_ANSI=never to suppress escapes.

Related issue
N/A

(not mandatory) A picture of a cute animal, if possible in relation to what you did
image

Copilot AI review requested due to automatic review settings April 14, 2026 14:25
@glours glours requested a review from a team as a code owner April 14, 2026 14:25
@glours glours requested a review from ndeloof April 14, 2026 14:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Compose hook “next steps” hint to render the Docker Desktop logs deep link as a clickable OSC 8 terminal hyperlink (with underline styling), while suppressing escape sequences when ANSI output is disabled via NO_COLOR or COMPOSE_ANSI=never.

Changes:

  • Added Osc8Link helper to generate OSC 8 hyperlinks when ANSI is enabled.
  • Updated hook hint templates to use hyperlink-wrapped deep links, gated by env-based ANSI suppression.
  • Added unit tests for OSC 8 link formatting and hook output under ANSI-enabled/disabled conditions.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
cmd/formatter/ansi.go Adds OSC 8 hyperlink helper and improves lenAnsi documentation.
cmd/formatter/ansi_test.go Adds tests validating OSC 8 hyperlink output and ANSI-disabled behavior.
cmd/compose/hooks.go Converts hint templates to functions and wraps the deep link using OSC 8 when allowed.
cmd/compose/hooks_test.go Updates existing expectations and adds tests for OSC 8 presence/absence based on env.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +98 to +106
// Osc8Link wraps text in an OSC 8 terminal hyperlink escape sequence with
// underline styling, making it clickable in supported terminal emulators.
// When ANSI output is disabled, returns the plain text without escape sequences.
func Osc8Link(url, text string) string {
if disableAnsi {
return text
}
return "\033]8;;" + url + "\033\\\033[4m" + text + "\033[24m\033]8;;\033\\"
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported function name Osc8Link doesn’t follow the repository’s initialism convention (e.g., SetANSIMode, URL). Consider renaming it to OSC8Link to match Go initialism style, and update call sites/tests accordingly.

Copilot uses AI. Check for mistakes.
if shouldDisableAnsi() {
return url
}
return formatter.Osc8Link(url, url)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call site uses formatter.Osc8Link; if the helper is renamed to follow initialism conventions (e.g., OSC8Link), update this reference as well so the hook continues to compile.

Suggested change
return formatter.Osc8Link(url, url)
return formatter.OSC8Link(url, url)

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +139
func TestHandleHook_HintContainsOsc8Link(t *testing.T) {
data := marshalHookData(t, hooks.Request{
RootCmd: "compose logs",
})
var buf bytes.Buffer
err := handleHook([]string{data}, &buf)
assert.NilError(t, err)

msg := unmarshalResponse(t, buf.Bytes())
// Verify the template contains the OSC 8 hyperlink sequence
wantLink := formatter.Osc8Link(deepLink, deepLink)
assert.Assert(t, len(wantLink) > len(deepLink), "Osc8Link should wrap the URL with escape sequences")
assert.Assert(t, strings.Contains(msg.Template, wantLink), "hint should contain OSC 8 hyperlink")
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestHandleHook_HintContainsOsc8Link assumes OSC 8 is enabled, but it doesn’t control NO_COLOR/COMPOSE_ANSI. If either env var is set in the test runner environment, shouldDisableAnsi() will return true and this test will fail/flap. Make the test deterministic by explicitly clearing or setting these env vars to values that allow ANSI before calling handleHook.

Copilot uses AI. Check for mistakes.
…inks

Wrap the docker-desktop://dashboard/logs URL in OSC 8 escape sequences
with underline styling so it appears as a clickable link in supported
terminals. Respects NO_COLOR and COMPOSE_ANSI=never to suppress escapes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
@glours glours force-pushed the os8link-for-hint branch from 0249bfa to 6734d5e Compare April 14, 2026 16:36
@afurm
Copy link
Copy Markdown

afurm commented Apr 14, 2026

The template string field on hookHint was changed to template func() string, and callers switched to hint.template() — this shifts from a static string to a lazy evaluation. The original design appeared to call enc.Encode once per hook invocation, so lazy evaluation should be fine here. However, if hookHint is ever used in a way where the template is inspected rather than rendered (e.g., log output, JSON marshaling of the struct itself), the function would not serialize correctly. Does any code path marshal hookHint directly to JSON?

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.

3 participants