Wrapping
wrap() folds styled text to a column width, closing every style and hyperlink at the end of a row and reopening it on the next — wrap-ansi's API and options, graded by its suite.
wrap(text, columns, options) folds text so no row is wider than columns. It is a port of
wrap-ansi 10: the named export wrap from linegauge and linegauge/wrap, and the default
export of linegauge/wrap, which wrap-ansi's own suite grades
(Compatibility).
Styles stay whole on every row
import { wrap } from 'linegauge/wrap';
const red = (s) => `\u001B[31m${s}\u001B[39m`;
const link = (url, s) => `\u001B]8;;${url}\u0007${s}\u001B]8;;\u0007`;
console.log(JSON.stringify(wrap(red('hello world again'), 7)));
console.log(JSON.stringify(wrap(link('https://example.com', 'a link across rows'), 8)));"\u001b[31mhello\u001b[39m\n\u001b[31mworld\u001b[39m\n\u001b[31magain\u001b[39m"
"\u001b]8;;https://example.com\u0007a link\u001b]8;;\u0007\n\u001b]8;;https://example.com\u0007across\u001b]8;;\u0007\n\u001b]8;;https://example.com\u0007rows\u001b]8;;\u0007"A colour open at a break is closed at the end of the row and reopened at the start of the
next; so is an OSC 8 hyperlink. Every row therefore stands on its own: a caller can print
row 3 alone, drop the first two, or put a border character between rows, and no colour leaks
into it or goes missing. flagstaff/log-update depends on this to drop the rows that have
scrolled off the screen without tracking any escape state.
Options
import { wrap } from 'linegauge/wrap';
console.log(JSON.stringify(wrap('supercalifragilistic', 8)));
console.log(JSON.stringify(wrap('supercalifragilistic', 8, { hard: true })));
console.log(JSON.stringify(wrap(' padded ', 8)));
console.log(JSON.stringify(wrap(' padded ', 8, { trim: false })));
console.log(JSON.stringify(wrap('古池や蛙飛び込む水の音', 8, { hard: true })));"supercalifragilistic"
"supercal\nifragili\nstic"
"padded"
" \npadded \n "
"古池や蛙\n飛び込む\n水の音"| option | default | |
|---|---|---|
hard | false | Break a word longer than the row. Without it a long word overflows its row rather than being split. |
trim | true | Remove the whitespace at the start and end of each row. |
wordWrap | true | Break at spaces. false fills every row to the width and breaks wherever that lands. |
CJK text has no spaces between words, so wrapping it needs hard: true — and a hard break
lands between grapheme clusters, never inside one, measured in columns: four two-column
characters to an eight-column row.
The options type is exported as WrapOptions and, from linegauge/wrap, as Options — the
name wrap-ansi gives it — so import { type Options } from 'wrap-ansi' moves with the same
edit as the import.
Measured with the graded width
Every row is measured with the same width that passes string-width's suite, so a wrapped
row is exactly as wide as the code laying out a box around it thinks it is.
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.
Slicing and truncating
slice() cuts a column range that closes its own styles and never splits a grapheme cluster; truncate() shortens to a budget with the ellipsis counted inside it, from the end, the start or the middle.