Why linegauge
linegauge against string-width, wrap-ansi, strip-ansi and slice-ansi, one capability per row, every cell linked to the test, grade or source that proves it.
string-width, wrap-ansi, strip-ansi and slice-ansi are good at what they do, and linegauge matches them where they are right: each of its four drop-in paths is graded by the incumbent's own test suite, 100% on all four. What it adds is what four separate packages cannot share — one package with no dependencies, the pieces that live in yet more packages (truncation with the ellipsis inside the budget, the widest of many lines), and a way to tell it that your terminal disagrees with Unicode.
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.
Measuring text
Unicode 17 East Asian Width
A code point Unicode 17 made wide, such as U+18D80, measures two columns, so a layout built on the width does not drift on new text.
linegauge- linegauge: yes
A ZWJ emoji sequence is one two-column cluster
Text is measured by grapheme cluster over
Intl.Segmenter, so a family emoji built from four people and three joiners is two columns, not eight.linegauge- linegauge: yes
string-width- string-width: yes
wrap-ansi- wrap-ansi: yes
slice-ansi- slice-ansi: yes
Cutting styled text
A slice closes the styles it opened
A column range re-emits the styles active at its start and closes them at its end, so the fragment neither loses its colour nor leaks it into what follows.
linegauge- linegauge: yes
slice-ansi- slice-ansi: yes
Truncation with the ellipsis inside the budget
truncate(text, 10)occupies ten columns or fewer with its ellipsis counted, cutting from the end, the start or the middle, which is what a table column depends on.linegauge- linegauge: yes
The widest of many lines, from any iterable
widest(lines)measures an array, aSetor a generator in one pass, so the lines never have to be spread into an argument list.linegauge- linegauge: yes
wrap-ansi- wrap-ansi: nofolds one string
slice-ansi- slice-ansi: nocuts one string
Terminals that disagree with Unicode
Width overrides through a plugin, with provenance
A plugin registers
{ ranges, columns, why }for a font that draws Private Use icons two columns wide, and a width table without awhyis refused.linegauge- linegauge: yes
A checker for width plugins before they ship
npx linegauge check ./widths.mjsvalidates a plugin against the family schema and exits 1 with a code and a fix when it is refused.linegauge- linegauge: yes
Weight
No runtime dependencies
Measuring, wrapping, slicing, stripping and truncating install as one package that depends on nothing.
linegauge- linegauge: yes
strip-ansi- strip-ansi: noansi-regex
Compatibility
Passes string-width's own test suite
linegauge's default export is graded by string-width 8.3.0's own tests, unedited, so changing the import keeps string-width's answers.
string-width- string-width: yesits own suite, the control run
Passes wrap-ansi's own test suite
linegauge/wrapis graded by wrap-ansi 10.0.2's own tests, unedited.string-width- string-width: does not applya different API
Passes strip-ansi's own test suite
linegauge/stripis graded by strip-ansi 7.2.0's own tests, unedited.linegauge- linegauge: yes8 / 8 of its own tests
string-width- string-width: does not applya different API
Passes slice-ansi's own test suite
linegauge/sliceis graded by slice-ansi 9.0.1's own tests, unedited.string-width- string-width: does not applya different API
Reading it
- Parity rows are here on purpose. All three measuring incumbents are at Unicode 17 and measure a ZWJ sequence as one cluster, and slice-ansi 9 closes the styles it opens. A reader would otherwise have to go and check; the cells say they match.
- — does not apply is not a soft ✗. strip-ansi removes escape sequences and measures nothing, so a measuring row does not apply to it; the cell says why.
- Truncation and
widestare not in the four incumbents — they are cli-truncate and widest-line, two more packages. The row shows what you would install on top of the four to get them.
What is not in the table
A row goes in only when every cell of it can be proved. These were left out:
OSC 8hyperlinks.wrapandslicecarry a hyperlink across a break and close it at a cut, and their differential tests include hyperlink cases — but wrap-ansi and slice-ansi do too, and no test of ours names the case on its own.- Linear time on adversarial input. There is no regression test for it in this repository yet, so there is no row.
- Ambiguous width.
width(text, { ambiguousIsNarrow: false })counts ambiguous characters wide, exactly as string-width's option does.wrapandslicedo not take the option, and neither do wrap-ansi and slice-ansi, so the row would say nothing either way (Measuring). - Weight in bytes. The per-entry figures are asserted by
weight.test.tsand published on Benchmarks, measured the same way on both sides. They are a number, not a yes-or-no capability.
Width plugins
When a terminal or font disagrees with Unicode about a code point, a plugin says so as data — ranges, columns and why — and npx linegauge check validates it before it ships.
Compatibility
How linegauge's four drop-ins are graded — each incumbent's own test suite, unedited — the current grades, and the differences that remain.