Skip to content

feat(ui-tui): make link-opening discoverable per terminal#701

Merged
ericleepi314 merged 1 commit into
mainfrom
worktree-apple-terminal-link-affordance
Jul 12, 2026
Merged

feat(ui-tui): make link-opening discoverable per terminal#701
ericleepi314 merged 1 commit into
mainfrom
worktree-apple-terminal-link-affordance

Conversation

@ericleepi314

Copy link
Copy Markdown
Collaborator

Problem

Links the agent prints looked broken in Apple Terminal: "I saw these links but I cannot click on them to open the link."

Diagnosis (verified empirically via raw PTY byte capture + terminal-support research):

  • Our OSC 8 emission is correct — balanced regions with id= grouping wrapping label (url) — so Cmd+click already works in VS Code/iTerm2/kitty/Ghostty/WezTerm.
  • Apple Terminal has no OSC 8 support (still true on macOS 26.5: no adoption-list entry, zero hyperlink handling in the Terminal binary).
  • Inline mode (the default) deliberately never arms mouse tracking — native scrollback/selection is the point, matching original Claude Code — so the in-process click opener can't fire there.
  • Therefore the only way to open a link in Apple Terminal is its native URL detection over the visible text: hold ⌘ and double-click the URL (or right-click → Open URL). That gesture was completely undiscoverable, reading as "links are broken".

Change

  • One-time dim transcript tip under the first assistant message that contains a URL, only in Apple Terminal + inline mode: tip: to open links in Apple Terminal, hold ⌘ and double-click the URL. Lands via sys() directly under the links it explains; once per session.
  • ? quick help + /help now list the link gesture per terminal: Cmd+click link in OSC 8 terminals, Cmd+double-click URL in Apple Terminal (inline), click link in fullscreen mode (mouse tracking armed → in-process opener works everywhere), omitted when unknown. Preview widened to 9 rows so Tab-completion stays visible.
  • supportsHyperlinks now recognizes TERM_PROGRAM=vscode (VS Code since 1.72, Cursor, Windsurf — xterm.js handles OSC 8 Cmd+click). It had no functional consumers in the renderer (links are emitted unconditionally), so this only affects gesture advertising. Exported from @clawcodex/ink.

Verification

  • Unit + integration tests (15 new): gesture truth table incl. fullscreen, once-only tip semantics (incl. non-consumption when suppressed), tip ordering after the message, env-hermetic under simulated iTerm2 leak (LC_TERMINAL=iTerm2 TERM=xterm-kitty).
  • Live PTY+pyte e2e, three configs: Apple_Terminal+inline → tip + Cmd+double-click URL row; vscode+inline → no tip + Cmd+click link row; Apple_Terminal+fullscreen → no tip + click link row. Raw OSC 8 framing re-verified balanced in all captures.
  • Full ui-tui suite 1425 passed; the 8 failures are pre-existing on origin/main (inline_diff ×2, statusRule ×2, useConfigSync ×2, prompt-width, cursorDrift) in files/tests this change doesn't touch. Typecheck clean; touched files eslint-clean.
  • Critic-reviewed: REVISE round (fullscreen gating + test hermeticity) applied → APPROVE.

🤖 Generated with Claude Code

Agent-printed links looked broken in Apple Terminal: it has no OSC 8
hyperlink support (still true on macOS 26), and inline mode deliberately
never captures the mouse, so the only working affordance is Apple
Terminal's native URL detection — hold Cmd and double-click the URL.
That gesture was undocumented, so users clicked and got nothing.

- One-time dim transcript tip under the first assistant message that
  contains a URL, only in Apple Terminal (linkTipFor, wired at
  message.complete after the message it explains).
- ? quick help + /help now list the terminal-appropriate gesture:
  "Cmd+click link" in OSC 8 terminals, "Cmd+double-click URL" in Apple
  Terminal, omitted when the terminal's gesture is unknown.
- supportsHyperlinks now recognizes TERM_PROGRAM=vscode (VS Code,
  Cursor, Windsurf): xterm.js has handled OSC 8 Cmd+click since 2022.
  Exported from @clawcodex/ink for app-level use.

Verified live via PTY+pyte in both terminal identities, including raw
OSC 8 byte framing (balanced regions, id= grouping, label and URL in
one region).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ericleepi314 ericleepi314 merged commit 3861a63 into main Jul 12, 2026
2 checks passed
ericleepi314 added a commit that referenced this pull request Jul 12, 2026
Follow-up to #701 from user feedback ("the user needs to know the link
is clickable"): the hover affordance exists, but nothing said so.

- OSC 8 terminals row: "(Cmd/Ctrl+hover underlines it)" — the terminal's
  own native hover indicator (VS Code/iTerm2/kitty...).
- Fullscreen row: "(highlights on hover)" — the renderer's own hover
  overlay (applyHyperlinkHoverHighlight inverts the link's cells),
  verified live by injecting SGR mouse-motion events in a PTY e2e.

No modifier-gated variant is possible: terminals don't encode the Cmd
key in SGR mouse reports, and inline mode receives no mouse events at
all (Apple Terminal inline can't have a hover indicator, full stop).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ericleepi314 added a commit that referenced this pull request Jul 12, 2026
…704)

Follow-up to #701 from user feedback ("the user needs to know the link
is clickable"): the hover affordance exists, but nothing said so.

- OSC 8 terminals row: "(Cmd/Ctrl+hover underlines it)" — the terminal's
  own native hover indicator (VS Code/iTerm2/kitty...).
- Fullscreen row: "(highlights on hover)" — the renderer's own hover
  overlay (applyHyperlinkHoverHighlight inverts the link's cells),
  verified live by injecting SGR mouse-motion events in a PTY e2e.

No modifier-gated variant is possible: terminals don't encode the Cmd
key in SGR mouse reports, and inline mode receives no mouse events at
all (Apple Terminal inline can't have a hover indicator, full stop).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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