feat(tui): use OSC 8 hyperlinks in Markdown when terminal supports them (#3248)
TerminalCapabilities already tracks hyperlinks: boolean and returns true for Ghostty, Kitty, WezTerm, and iTerm2, but nothing generated OSC 8 sequences. This completes that stub. Changes to packages/tui: - terminal-image.ts: add hyperlink(text, url) and setCapabilities() - index.ts: export hyperlink and setCapabilities - utils.ts: extend AnsiCodeTracker to track active OSC 8 URLs - process() now handles OSC 8 open/close sequences - getActiveCodes() re-emits the OSC 8 open at each line start - getLineEndReset() closes the OSC 8 hyperlink before each line break This ensures hyperlinks wrap correctly across multiple lines. - components/markdown.ts: link renderer uses hyperlink() when getCapabilities().hyperlinks is true; falls back to (url) text - Tests: new wrap-ansi tests for OSC 8 line-wrapping; terminal-image tests for hyperlink(); markdown tests covering both code paths; table-cell width test pinned to hyperlinks:false (checks raw columns) closes #3239 Co-authored-by: AI (Pi/Claude Sonnet 4.6) <noreply@pi.dev> Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
This commit is contained in:
@@ -81,6 +81,11 @@ export function resetCapabilitiesCache(): void {
|
||||
cachedCapabilities = null;
|
||||
}
|
||||
|
||||
/** Override the cached capabilities. Useful in tests to exercise both code paths. */
|
||||
export function setCapabilities(caps: TerminalCapabilities): void {
|
||||
cachedCapabilities = caps;
|
||||
}
|
||||
|
||||
const KITTY_PREFIX = "\x1b_G";
|
||||
const ITERM2_PREFIX = "\x1b]1337;File=";
|
||||
|
||||
@@ -372,6 +377,20 @@ export function renderImage(
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap text in an OSC 8 hyperlink sequence.
|
||||
* The text is rendered as a clickable hyperlink in terminals that support OSC 8
|
||||
* (Ghostty, Kitty, WezTerm, iTerm2, VSCode, and others).
|
||||
* In terminals that do not support OSC 8, the escape sequences are ignored
|
||||
* and only the plain text is displayed.
|
||||
*
|
||||
* @param text - The visible text to display
|
||||
* @param url - The URL to link to
|
||||
*/
|
||||
export function hyperlink(text: string, url: string): string {
|
||||
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
||||
}
|
||||
|
||||
export function imageFallback(mimeType: string, dimensions?: ImageDimensions, filename?: string): string {
|
||||
const parts: string[] = [];
|
||||
if (filename) parts.push(filename);
|
||||
|
||||
Reference in New Issue
Block a user