Getting started
Install linegauge, measure a string in terminal columns, and wrap, truncate and slice styled text without cutting a grapheme cluster or leaking a colour.
linegauge answers one question five ways: how many terminal columns a piece of styled text
occupies, and where it can be cut. width measures, wrap folds, truncate shortens,
slice cuts out a column range, and widest measures many lines at once. Each one knows
where the escape sequences are and where the grapheme clusters begin and end, so none of them
splits an emoji or leaves a colour open.
Install
npm install linegaugeIt is ESM with a default condition, so require('linegauge') also works from CommonJS on
Node 20.19+ and 22.13+. It depends on nothing
(package-shape-lock.test.ts
holds it at zero).
A first program
import { slice, truncate, widest, width, wrap } from 'linegauge';
const red = (s) => `\u001B[31m${s}\u001B[39m`;
console.log(width('hello'));
console.log(width('古池や'));
console.log(width('👨👩👧👦'));
console.log(width(red('hello')));
console.log(JSON.stringify(wrap('the quick brown fox jumps', 10)));
console.log(truncate('the quick brown fox', 10));
console.log(JSON.stringify(slice(red('hello world'), 0, 5)));
console.log(widest(['a', '古池や', 'bb']));5
6
2
5
"the quick\nbrown fox\njumps"
the quick…
"\u001b[31mhello\u001b[39m"
6Line by line: ASCII is one column a character; each of the three CJK characters is two; the
family emoji is four people and three joiners, one grapheme cluster, two columns; the colour
codes take no columns at all. wrap breaks between words at ten columns. truncate spends one
of its ten columns on the ellipsis, so the result is ten wide, not eleven. slice takes columns
0 to 5 of a red string and closes the red it reopened. widest is the width of the widest line.
Every output block on this site is checked: tests/examples.test.ts writes each titled file,
runs the command in the block's title, and compares.
The five answers
| function | answers | guide |
|---|---|---|
width(text, options) | the columns the text occupies | Measuring |
wrap(text, columns, options) | the text folded to a width, styles carried across rows | Wrapping |
truncate(text, columns, options) | the text shortened, ellipsis inside the budget | Slicing and truncating |
slice(text, start, end) | the columns [start, end), self-contained | Slicing and truncating |
widest(lines) | the width of the widest line of any iterable | Measuring |
strip(text) removes the escape sequences, and is the fifth drop-in's
(Stripping escapes).
Pure, on purpose
Nothing in linegauge reads process. It does not know how wide your terminal is or whether
there is one, so the width to wrap or truncate at is always yours to pass, and the same input
gives the same answer on a terminal, in a pipe, under --json and in a test. That is why
burgee, caique and flagstaff measure their own output with it.
Where next
- Guides: measuring, wrapping, slicing and truncating, stripping, and width plugins.
- Why linegauge: what it does that string-width, wrap-ansi, strip-ansi and slice-ansi do not, cell by cell, with the evidence.
- Coming from string-width and the other three: change one import.
- API reference: every export of every entry point.
linegauge
A printer's line gauge — the steel rule marked in picas and points. Measuring, wrapping, truncating and slicing styled terminal text without the edge fraying — grapheme-correct over Intl.Segmenter. Drop-in paths for string-width, wrap-ansi, strip-ansi and slice-ansi. Zero dependencies.
Measuring
width() counts terminal columns by grapheme cluster and East Asian Width, with string-width's two options; lineCount, measure and widest answer the neighbouring questions.