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.
paratext's root exports ansi-escapes' surface — the default export with every member on it, and
each member by name — so import ansiEscapes from 'paratext' is the migration. ansi-escapes'
own suite grades it, 4 of 4 (Compatibility).
ansi-escapes is one module with two unrelated jobs, and paratext treats them differently.
The CSI half: byte-exact, never degraded
Cursor movement, erasing, scrolling, the alternate screen and synchronized output are the incumbent's bytes exactly:
import ansiEscapes, { cursorTo, eraseLines, synchronizedOutput } from 'paratext';
console.log(JSON.stringify(cursorTo(2, 2)));
console.log(JSON.stringify(eraseLines(2)));
console.log(JSON.stringify(synchronizedOutput('frame')));
console.log(ansiEscapes.cursorTo === cursorTo);"\u001b[3;3H"
"\u001b[2K\u001b[1A\u001b[2K\u001b[G"
"\u001b[?2026hframe\u001b[?2026l"
trueThey do not degrade on a pipe, because the incumbent's do not, and a cursor move silently
dropped would corrupt the screen of a program that relied on it. A program that should not move
the cursor on a pipe decides that itself — roundel's outputMode
is the question to ask.
The CSI half is also published on its own, as paratext/csi: it registers none of the
built-ins the root does, and it is where flagstaff and caique take their cursor moves from.
import { cursorUp, eraseLines } from 'paratext/csi';
console.log(JSON.stringify(cursorUp(2) + eraseLines(1)));"\u001b[2A\u001b[2K\u001b[G"The OSC half: the incumbent's bytes, or the projection
link, image, setCwd and beep go through emit() over the real process, so on a terminal
believed to understand them they are ansi-escapes' exact bytes, and everywhere else the static
projection:
import { beep, image, link, setCwd } from 'paratext';
console.log(JSON.stringify(link('Docs', 'https://x.dev')));
console.log(JSON.stringify(image(Buffer.from('iVBORw0KGgo=', 'base64'), { caption: '[logo]' })));
console.log(JSON.stringify(setCwd('/tmp')));
console.log(JSON.stringify(beep));"Docs (https://x.dev)"
"[logo]"
""
"\u0007"That is the one deliberate difference from ansi-escapes, and the reason to prefer this: through
ansi-escapes the first three would be escape bytes in the pipe. image takes a caption
option on top of ansi-escapes' own, which is what it projects to. beep is a value, as it is
upstream.
For a runtime other than the process — a test, or rendering for somebody else's terminal —
ansiEscapesFor(runtime) returns the same four bound to it.
What is declared and empty
iTerm and ConEmu — iTerm.annotation, ConEmu's progress bar — are capabilities paratext
does not ship yet. They are exported as undefined, so a program that imports them by name still
loads, and typed so that calling one is a compile error rather than a surprise at run time.
Capabilities as data
Register a capability as a plain object — encode, fallback and when — validate it against the family schema with check(), ship it as a plugin, and check the plugin with npx paratext check.
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.