Links and images
paratext/link for one hyperlink without the registry, paratext/terminal-link for terminal-link's API and detection, and paratext/term-img for term-img's API over image bytes.
Three subpaths cover the two capabilities most programs want, without loading the registry or the schema.
paratext/link
link(text, url) over the real process, linkFor(runtime) for any other, and
supportsLink(runtime) for a renderer deciding layout — a help screen that prints a
separate Docs column only where the URL would otherwise be repeated in parentheses.
import { linkFor, supportsLink } from 'paratext/link';
const pipe = { env: {}, isTTY: { stdout: false } };
const wezterm = { env: { TERM_PROGRAM: 'WezTerm' }, isTTY: { stdout: true } };
console.log(JSON.stringify(linkFor(pipe)('Docs', 'https://x.dev')), supportsLink(pipe));
console.log(JSON.stringify(linkFor(wezterm)('Docs', 'https://x.dev')), supportsLink(wezterm));"Docs (https://x.dev)" false
"\u001b]8;;https://x.dev\u0007Docs\u001b]8;;\u0007" trueThe subpath registers nothing: it emits through the same link record the registry ships.
paratext/terminal-link
terminal-link 5's API — terminalLink(text, url, options), terminalLink.stderr,
isSupported — with terminal-link's detection, which is supports-hyperlinks'. Its fallback is
terminal-link's too: the text, a space and the URL, or your fallback function, or the text
alone with fallback: false.
import terminalLink from 'paratext/terminal-link';
console.log(terminalLink('Docs', 'https://x.dev'));
console.log(terminalLink('Docs', 'https://x.dev', { fallback: (text, url) => `${text} <${url}>` }));
console.log(terminalLink('Docs', 'https://x.dev', { fallback: false }));
console.log(terminalLink.isSupported);Docs https://x.dev
Docs <https://x.dev>
Docs
falseFORCE_HYPERLINK=1 forces a link, as it does with terminal-link:
import terminalLink from 'paratext/terminal-link';
console.log(JSON.stringify(terminalLink('Docs', 'https://x.dev')));"\u001b]8;;https://x.dev\u0007Docs\u001b]8;;\u0007"paratext/term-img
term-img 7's API for image bytes: terminalImage(bytes, { width, height, preserveAspectRatio, fallback }) and UnsupportedTerminalError. Support is term-img's own table
— iTerm2 3+, WezTerm 20220319+, Konsole 22.04+, Rio 0.1.13+ and VS Code 1.80+ — read from the
environment, as term-img reads it. An unsupported terminal calls your fallback, or throws:
import terminalImage, { UnsupportedTerminalError } from 'paratext/term-img';
const png = Buffer.from('iVBORw0KGgo=', 'base64');
console.log(terminalImage(png, { fallback: () => '[an image your terminal cannot show]' }));
try {
terminalImage(png);
} catch (error) {
console.log(error instanceof UnsupportedTerminalError, error.name);
}[an image your terminal cannot show]
true UnsupportedTerminalErrorIt takes bytes, not a path: paratext has no node:fs dependency (D-030), so the caller reads
the file — terminalImage(await readFile(path)). A path handed to a supported terminal throws a
TypeError that says so, at the point term-img would have read the file.
Like term-img, paratext/term-img reads the environment and not the terminal: a program run
inside iTerm2 with its output piped still gets the image bytes. image() from the root is the
form that projects to a caption off a terminal (The ansi-escapes surface).
The ansi-escapes surface
paratext's root is ansi-escapes 7.3.0's API: the CSI half byte-exact, the four OSC members through emit(), so a link or an image on a pipe is its text rather than raw bytes.
Why paratext
paratext against ansi-escapes, terminal-link and term-img, one capability per row, every cell linked to the test, grade or source that proves it.