linegauge

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 linegauge

It 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

measure.mjs
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']));
node measure.mjs
5
6
2
5
"the quick\nbrown fox\njumps"
the quick…
"\u001b[31mhello\u001b[39m"
6

Line 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

functionanswersguide
width(text, options)the columns the text occupiesMeasuring
wrap(text, columns, options)the text folded to a width, styles carried across rowsWrapping
truncate(text, columns, options)the text shortened, ellipsis inside the budgetSlicing and truncating
slice(text, start, end)the columns [start, end), self-containedSlicing and truncating
widest(lines)the width of the widest line of any iterableMeasuring

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.

On this page