From eafaf83fb9bdb2a3a20aed485c944fe055d56771 Mon Sep 17 00:00:00 2001 From: Eric Lee Date: Sun, 12 Jul 2026 15:46:22 -0700 Subject: [PATCH] feat(ui-tui): say where the link hover indicator lives in help rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ui-tui/src/lib/linkAffordance.test.ts | 12 ++++++++---- ui-tui/src/lib/linkAffordance.ts | 13 ++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ui-tui/src/lib/linkAffordance.test.ts b/ui-tui/src/lib/linkAffordance.test.ts index 428f8d38..09c390e9 100644 --- a/ui-tui/src/lib/linkAffordance.test.ts +++ b/ui-tui/src/lib/linkAffordance.test.ts @@ -9,9 +9,11 @@ const XTERM = { TERM_PROGRAM: undefined } as NodeJS.ProcessEnv describe('linkOpenHotkey', () => { it('advertises the platform click gesture when the terminal supports OSC 8', () => { + const mod = isMac ? 'Cmd' : 'Ctrl' + expect(linkOpenHotkey({ TERM_PROGRAM: 'iTerm.app' } as NodeJS.ProcessEnv, true)).toEqual([ - `${isMac ? 'Cmd' : 'Ctrl'}+click link`, - 'open link in browser' + `${mod}+click link`, + `open link in browser (${mod}+hover underlines it)` ]) }) @@ -24,8 +26,10 @@ describe('linkOpenHotkey', () => { }) it('advertises a plain click in fullscreen mode, where the in-process opener handles every terminal', () => { - expect(linkOpenHotkey(APPLE, false, false)).toEqual(['click link', 'open link in browser']) - expect(linkOpenHotkey(XTERM, false, false)).toEqual(['click link', 'open link in browser']) + const row = ['click link', 'open link in browser (highlights on hover)'] + + expect(linkOpenHotkey(APPLE, false, false)).toEqual(row) + expect(linkOpenHotkey(XTERM, false, false)).toEqual(row) }) }) diff --git a/ui-tui/src/lib/linkAffordance.ts b/ui-tui/src/lib/linkAffordance.ts index 072bb6b4..59b5f874 100644 --- a/ui-tui/src/lib/linkAffordance.ts +++ b/ui-tui/src/lib/linkAffordance.ts @@ -42,15 +42,22 @@ export function linkOpenHotkey( // OSC 8 terminals open our hyperlink metadata on Cmd/Ctrl+click in both // modes (xterm.js and iTerm2 handle it themselves even with mouse // tracking armed — the double-open dance the renderer's click dispatcher - // defers to in @clawcodex/ink's components/App.tsx). - return [`${isMac ? 'Cmd' : 'Ctrl'}+click link`, 'open link in browser'] + // defers to in @clawcodex/ink's components/App.tsx). They also render + // their own hover affordance (underline/tooltip while the modifier is + // held), so tell the user that's how to spot a clickable link. + const mod = isMac ? 'Cmd' : 'Ctrl' + + return [`${mod}+click link`, `open link in browser (${mod}+hover underlines it)`] } if (!inline) { // Fullscreen arms mouse tracking, so clicks are handled in-process // (onHyperlinkClick) in every terminal — including Apple Terminal, // where the captured mouse suppresses the native Cmd+double-click. - return ['click link', 'open link in browser'] + // The renderer's hover overlay (applyHyperlinkHoverHighlight) inverts + // a link's cells while the pointer is over it — no modifier, since + // terminals don't put Cmd in mouse reports. + return ['click link', 'open link in browser (highlights on hover)'] } if (isAppleTerminal(env)) {