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.
ansi-escapes, terminal-link and term-img each write the bytes a terminal needs, and paratext writes the same bytes — its three drop-in paths are graded by each one's own test suite. What none of them has is the other half: a text form for every capability, used whenever the terminal cannot be shown to understand the sequence, so a pipe, a log and an agent read text instead of escape bytes. terminal-link comes closest, for links alone.
The table below is the whole comparison. Every mark links to its evidence: a test in this
repository for ours, and for theirs the source file of the exact version compat-oracle grades,
or that package's own test suite. scripts/capabilities-lock.test.ts fails the build when a
cited test no longer contains the title it is cited for, when a source no longer contains the
line it is quoted for, or when a source we say lacks something has gained it.
✓ yes · ◐ partial, with what is missing · ✗ no · — does not apply. Every mark links to its evidence: our test or grade, or the incumbent’s source at the version compat-oracle grades.
| Capability | paratext | ansi-escapes | terminal-link | term-img |
|---|---|---|---|---|
| Off a terminal | ||||
A hyperlink on a pipe is its text and its URLWhere OSC 8 is not understood, a link is written as Docs (https://x.dev), so a log, an agent or a redirected file reads the address instead of escape bytes. | paratext: yes | ansi-escapes: nowrites the OSC 8 bytes on every stream; it never checks | terminal-link: yesthe text, a space and the URL | term-img: does not applydraws images, not links |
| Every OSC capability has a text formAn image becomes its caption, a notification a printed line and a link its text and URL whenever support is absent or unknown, so no OSC byte reaches a pipe. | paratext: yes | ansi-escapes: noevery OSC helper returns its bytes, whatever the stream | terminal-link: partialpartiala text form for links only; it has no other capability | term-img: partialpartialan unsupported terminal throws UnsupportedTerminalError unless the caller passes a fallback function |
| Knowing the terminal | ||||
Hyperlink support detected as supports-hyperlinks detects itparatext/terminal-link links on exactly the terminals supports-hyperlinks 4.5.0 says understand OSC 8, and asks about stderr when the link is bound for stderr. | paratext: yes | ansi-escapes: nodetects nothing; the caller decides | terminal-link: yes | term-img: does not applydraws images, not links |
Inline-image support detected by terminalparatext/term-img draws on iTerm2, WezTerm, Konsole, Rio and VS Code at the versions term-img names, read from the environment alone. | paratext: yes | ansi-escapes: noencodes the image for any terminal it is given | terminal-link: does not applylinks only | term-img: yesthe same table |
| Capabilities as data | ||||
| A registry of capabilities, extensible by plain objectsA capability the package never heard of — a Kitty image, a WezTerm user variable — is registered as an object with an encoding and a fallback, survives a round trip through JSON, and emits through the same call as the built-ins. | paratext: yes | ansi-escapes: noa fixed set of functions | terminal-link: noone function | term-img: noone function |
| A capability without a text form is refusedA capability with no static projection is refused at registration, with the family's code and a fix, so nothing registered can put raw bytes into a pipe. | paratext: yes | ansi-escapes: nono helper has a text form to require | terminal-link: partialpartialits one link has a text form, and a caller can switch it off | term-img: nothe fallback is optional, and the default is to throw |
A checker for capability plugins before they shipnpx paratext check ./kitty.mjs validates a plugin against the family schema without running its author's code, and exits 1 with a code and a fix when it is refused. | paratext: yes | ansi-escapes: does not applytakes no plugins, so has nothing to check | terminal-link: does not applytakes no plugins, so has nothing to check | term-img: does not applytakes no plugins, so has nothing to check |
| Weight | ||||
| No runtime dependenciesThe escape sequences, the hyperlink detection, the image table and the registry install as one package that depends on nothing. | paratext: yes | ansi-escapes: noenvironment | terminal-link: noansi-escapes and supports-hyperlinks | term-img: noansi-escapes and iterm2-version |
| Compatibility | ||||
| Passes ansi-escapes' own test suiteparatext's root is graded by ansi-escapes 7.3.0's own tests, unedited, and its CSI half is byte-exact with it. | paratext: yes4 / 4 of its own tests | ansi-escapes: yesits own suite, the control run | terminal-link: does not applya different API | term-img: does not applya different API |
Passes terminal-link's own test suiteparatext/terminal-link is graded by terminal-link 5.0.0's own tests, unedited. | paratext: yes8 / 8 of its own tests | ansi-escapes: does not applya different API | terminal-link: yesits own suite, the control run | term-img: does not applya different API |
Passes term-img's own test suiteparatext/term-img is graded by term-img 7.1.0's own tests, unedited, and passes every case that hands it image bytes. | paratext: partialpartial12 / 18 of its own tests | ansi-escapes: does not applya different API | terminal-link: does not applya different API | term-img: yesits own suite, the control run |
Off a terminal
A hyperlink on a pipe is its text and its URL
Where OSC 8 is not understood, a link is written as
Docs (https://x.dev), so a log, an agent or a redirected file reads the address instead of escape bytes.paratext- paratext: yes
terminal-link- terminal-link: yesthe text, a space and the URL
Every OSC capability has a text form
An image becomes its caption, a notification a printed line and a link its text and URL whenever support is absent or unknown, so no OSC byte reaches a pipe.
paratext- paratext: yes
Knowing the terminal
Hyperlink support detected as supports-hyperlinks detects it
paratext/terminal-linklinks on exactly the terminals supports-hyperlinks 4.5.0 says understand OSC 8, and asks about stderr when the link is bound for stderr.paratext- paratext: yes
terminal-link- terminal-link: yes
Inline-image support detected by terminal
paratext/term-imgdraws on iTerm2, WezTerm, Konsole, Rio and VS Code at the versions term-img names, read from the environment alone.paratext- paratext: yes
terminal-link- terminal-link: does not applylinks only
term-img- term-img: yesthe same table
Capabilities as data
A registry of capabilities, extensible by plain objects
A capability the package never heard of — a Kitty image, a WezTerm user variable — is registered as an object with an encoding and a fallback, survives a round trip through JSON, and emits through the same call as the built-ins.
paratext- paratext: yes
ansi-escapes- ansi-escapes: noa fixed set of functions
terminal-link- terminal-link: noone function
term-img- term-img: noone function
A capability without a text form is refused
A capability with no static projection is refused at registration, with the family's code and a fix, so nothing registered can put raw bytes into a pipe.
A checker for capability plugins before they ship
npx paratext check ./kitty.mjsvalidates a plugin against the family schema without running its author's code, and exits 1 with a code and a fix when it is refused.paratext- paratext: yes
Weight
No runtime dependencies
The escape sequences, the hyperlink detection, the image table and the registry install as one package that depends on nothing.
paratext- paratext: yes
ansi-escapes- ansi-escapes: noenvironment
Compatibility
Passes ansi-escapes' own test suite
paratext's root is graded by ansi-escapes 7.3.0's own tests, unedited, and its CSI half is byte-exact with it.
ansi-escapes- ansi-escapes: yesits own suite, the control run
terminal-link- terminal-link: does not applya different API
Passes terminal-link's own test suite
paratext/terminal-linkis graded by terminal-link 5.0.0's own tests, unedited.ansi-escapes- ansi-escapes: does not applya different API
terminal-link- terminal-link: yesits own suite, the control run
Passes term-img's own test suite
paratext/term-imgis graded by term-img 7.1.0's own tests, unedited, and passes every case that hands it image bytes.ansi-escapes- ansi-escapes: does not applya different API
terminal-link- terminal-link: does not applya different API
Reading it
- terminal-link already projects a link, and its row says so. paratext's
text (url)and terminal-link'stext urlare two spellings of the same idea;paratext/terminal-linkkeeps terminal-link's. - term-img's cells link to term-img 7.1.0 on jsDelivr: term-img is not installed in this repository, so there is no local copy for the lock to read. Each URL cell was checked against that file by hand; the compatibility row, whose source is term-img's vendored suite, is checked on every run.
- term-img is partial on purpose. Six of its eighteen cases hand
paratext/term-imga file path, and paratext takes bytes so that it never touches the filesystem (D-030). The fix for a caller is onereadFile(Coming from term-img).
What is not in the table
A row goes in only when every cell of it can be proved. These were left out:
- Clipboard, notifications, the window title and the working directory. paratext ships
them; ansi-escapes 7.3.0 has
setCwdand none of the other three, and terminal-link and term-img have none. They are in the text-form row rather than a row each. - Detection as a pure function of a runtime.
supports()andemit()read only the runtime they are handed; each incumbent readsprocessitself. It is how every example on this site runs, but no single test states the comparison. - Weight in bytes. The per-subpath figures are asserted by
weight.test.tsand published on Benchmarks.
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.
Compatibility
How paratext's three drop-ins are graded — each incumbent's own test suite, unedited — the current grades, and the differences that remain, term-img's six path cases included.