linegauge 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,28 +1,104 @@
1
1
  # linegauge
2
2
 
3
- **Not yet released.** This version reserves the name. The layer is planned as wave **F1** of the
4
- foundation tier: its intent and design are at
5
- [`.sdlc/intents/linegauge/`](https://github.com/ofri-peretz/burgee/blob/main/.sdlc/intents/linegauge/),
6
- under the umbrella
7
- [`cli-foundation-stack`](https://github.com/ofri-peretz/burgee/blob/main/.sdlc/intents/cli-foundation-stack/),
8
- and the measurements behind it are in
9
- [`candidate-layers.md`](https://github.com/ofri-peretz/burgee/blob/main/.sdlc/research/candidate-layers.md)
10
- and
11
- [`replacement-map.md`](https://github.com/ofri-peretz/burgee/blob/main/.sdlc/research/replacement-map.md).
12
- Both artifacts are `draft`; the human gate has not run, and no working release ships before
13
- burgee's compatibility scoreboard is public.
14
-
15
- A **line gauge** is the printer's ruler — the steel rule marked in picas and points that a compositor uses to measure a line of type and check it fits the measure it was set to.
16
-
17
- That is this package's whole job. Width, wrap, truncate and slice are one problem wearing four names: **cutting styled text without letting the edge come apart** — no dangling escape sequence, no half a grapheme, no severed emoji cluster.
18
-
19
- ## What it will be
20
-
21
- - **One package, not twelve.** `width` · `wrap` · `truncate` · `slice` · `strip` · `widest`. The incumbents split this across `strip-ansi`, `string-width`, `ansi-regex`, `wrap-ansi`, `emoji-regex`, `slice-ansi`, `get-east-asian-width`, `eastasianwidth`, `string-length`, `wcwidth`, `cli-truncate` and `widest-line` — **2.16 B weekly downloads** between them.
22
- - **Grapheme-correct by construction.** ZWJ families, regional-indicator flags, skin-tone modifiers, keycaps, combining marks and East Asian wide characters, over the platform's own `Intl.Segmenter`.
23
- - **Fast path for ASCII.** A byte scan when the string has no non-ASCII code unit; the segmenter only when it earns its cost.
24
- - **Drop-in paths** for `string-width`, `wrap-ansi`, `strip-ansi` and `slice-ansi`, graded by their own suites.
25
- - **Zero external dependencies.**
3
+ **Measuring, wrapping, truncating and slicing styled terminal text — without the edge
4
+ fraying.**
5
+
6
+ A printer's line gauge is the steel rule marked in picas and points: a compositor holds it
7
+ against a line of type and checks it fits the measure it was set to.
8
+
9
+ Zero dependencies. Grapheme-correct over the platform's own `Intl.Segmenter`.
10
+
11
+ ```bash
12
+ npm i linegauge
13
+ ```
14
+
15
+ ## One problem wearing five names
16
+
17
+ `width` · `wrap` · `truncate` · `slice` · `widest`
18
+
19
+ They look like five utilities. They are one: **cutting styled text without letting the edge
20
+ come apart** — no dangling escape sequence, no half a grapheme, no severed emoji cluster.
21
+ Each has to know where the ANSI is and where the cluster boundaries are, and once you know
22
+ that, you may as well answer all five.
23
+
24
+ The ecosystem splits it across twelve packages — `strip-ansi`, `string-width`,
25
+ `ansi-regex`, `wrap-ansi`, `emoji-regex`, `slice-ansi`, `get-east-asian-width`,
26
+ `eastasianwidth`, `string-length`, `wcwidth`, `cli-truncate` and `widest-line` — which
27
+ between them sit under most of the terminal ecosystem.
28
+
29
+ ## Use
30
+
31
+ ```js
32
+ import { width, wrap, truncate, slice, widest } from 'linegauge';
33
+
34
+ width('古代'); // 4 — East Asian wide, two columns each
35
+ width('👨‍👩‍👧‍👦'); // 2 — one cluster, not four people
36
+
37
+ wrap('a long sentence that needs folding', 12);
38
+ truncate('the quick brown fox', 10); // 'the quick…'
39
+ slice(styled, 2, 4); // columns 2 and 3, styles intact
40
+ widest(['a', 'bbb', 'cc']); // 3
41
+ ```
42
+
43
+ ### The default export is `string-width`
44
+
45
+ Byte-for-byte call-compatible, so this resolves without a code change:
46
+
47
+ ```json
48
+ { "overrides": { "string-width": "npm:linegauge@^0.2" } }
49
+ ```
50
+
51
+ ## What "without the edge fraying" means
52
+
53
+ **A cluster is atomic.** A cut that would land inside a grapheme drops the whole cluster
54
+ rather than half of it. Half an emoji is not a narrower emoji, it is mojibake, and a flag
55
+ cut down the middle is two unrelated regional-indicator letters.
56
+
57
+ **A style that was open stays open — and gets closed.** A cut re-emits the styles active at
58
+ its start and closes them at its end, so the result is self-contained: paste it anywhere and
59
+ it neither loses its colour nor leaks it into what follows.
60
+
61
+ **The ellipsis is inside the budget, not on top of it.** `truncate(text, 10)` occupies ten
62
+ columns or fewer, never eleven. That is the property a table column depends on, and getting
63
+ it wrong is how a layout gains a phantom column under one input.
64
+
65
+ **`widest` takes lines, not a blob.** It accepts any iterable of strings, so the caller says
66
+ where the boundaries are rather than having a newline convention assumed for them.
67
+
68
+ ## Graded by the packages it replaces
69
+
70
+ The incumbent is the specification. `width` runs against `string-width`, `wrap` against
71
+ `wrap-ansi`, `slice` against `slice-ansi` and `truncate` against `cli-truncate`.
72
+
73
+ ## API
74
+
75
+ | | |
76
+ | :-- | :-- |
77
+ | `width(text, { countAnsiEscapeCodes })` | terminal columns the text occupies |
78
+ | `wrap(text, columns, options)` | fold to a width, styles preserved across rows |
79
+ | `truncate(text, columns, { position, ellipsis })` | cut to a budget, ellipsis counted inside it |
80
+ | `slice(text, start, end)` | the columns `[start, end)`, self-contained |
81
+ | `widest(lines)` | the width of the widest line of any iterable |
82
+ | `lineCount(text, columns)` | rows the text occupies at that width |
83
+ | `measure(text)` | columns of plain text, no escape scan |
84
+
85
+ Non-strings answer `0` rather than throwing, because a width function is usually reached
86
+ with whatever a template produced.
87
+
88
+ ## Design notes
89
+
90
+ **Ambiguous-width characters count narrow**, which is what a terminal does unless told it is
91
+ rendering an East Asian locale. `string-width` makes that an option; nothing above this has
92
+ ever needed the other answer, so it is not one here.
93
+
94
+ **Not a terminal emulator.** Semicolon-delimited SGR, colon-delimited extended colour and
95
+ OSC 8 hyperlinks are understood. Every other complete CSI or OSC command is carried through
96
+ as an opaque zero-width unit, and anything that only looks like an introducer stays plain
97
+ text.
98
+
99
+ **Still at the Design→Build gate:** an exported `strip`, and the ASCII fast path — a byte
100
+ scan when the string has no non-ASCII code unit, so the segmenter is reached only when it
101
+ earns its cost.
26
102
 
27
103
  ## Licence
28
104
 
package/dist/index.d.ts CHANGED
@@ -15,10 +15,19 @@
15
15
  * The default export is `width`, byte-for-byte call-compatible with `string-width`'s
16
16
  * default (R8), so `overrides: { "string-width": "npm:linegauge@^1" }` resolves.
17
17
  *
18
- * Not here yet, and each still at the Design→Build gate: `slice`, `truncate`, `widest`,
19
- * the R2 ASCII fast path, and an exported `strip`. This release is the move, so that a
20
- * green `flagstaff` across the deletion proves the consolidation is real before anything
21
- * new is written on top of it.
18
+ * `slice`, `truncate` and `widest` came next, built on the style stack `wrap` already
19
+ * carried — which is the consolidation the design is named for: `slice-ansi`, `wrap-ansi`
20
+ * and `cli-truncate` each keep their own copy of it, and they disagree at the edges.
21
+ *
22
+ * `strip` (R3) followed, and it is where the measured divergence from Node's own
23
+ * `stripVTControlCharacters` is recorded — one shape in sixteen, and it was a live bug in
24
+ * `width()`.
25
+ *
26
+ * Still at the Design→Build gate: the R2 ASCII fast path.
22
27
  */
23
- export { lineCount, measure, width, width as default } from './width.js';
28
+ export { lineCount, measure, width, width as default, type WidthOptions } from './width.js';
29
+ export { slice } from './slice.js';
30
+ export { strip } from './strip.js';
31
+ export { truncate, type TruncateOptions } from './truncate.js';
32
+ export { widest } from './widest.js';
24
33
  export { wrap, type WrapOptions } from './wrap.js';
package/dist/index.js CHANGED
@@ -15,11 +15,20 @@
15
15
  * The default export is `width`, byte-for-byte call-compatible with `string-width`'s
16
16
  * default (R8), so `overrides: { "string-width": "npm:linegauge@^1" }` resolves.
17
17
  *
18
- * Not here yet, and each still at the Design→Build gate: `slice`, `truncate`, `widest`,
19
- * the R2 ASCII fast path, and an exported `strip`. This release is the move, so that a
20
- * green `flagstaff` across the deletion proves the consolidation is real before anything
21
- * new is written on top of it.
18
+ * `slice`, `truncate` and `widest` came next, built on the style stack `wrap` already
19
+ * carried — which is the consolidation the design is named for: `slice-ansi`, `wrap-ansi`
20
+ * and `cli-truncate` each keep their own copy of it, and they disagree at the edges.
21
+ *
22
+ * `strip` (R3) followed, and it is where the measured divergence from Node's own
23
+ * `stripVTControlCharacters` is recorded — one shape in sixteen, and it was a live bug in
24
+ * `width()`.
25
+ *
26
+ * Still at the Design→Build gate: the R2 ASCII fast path.
22
27
  */
23
28
  export { lineCount, measure, width, width as default } from './width.js';
29
+ export { slice } from './slice.js';
30
+ export { strip } from './strip.js';
31
+ export { truncate } from './truncate.js';
32
+ export { widest } from './widest.js';
24
33
  export { wrap } from './wrap.js';
25
34
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) 2026 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * `[start, end)` in display columns. A negative or reversed range is empty rather than an
8
+ * error, matching `String.prototype.slice`'s temperament if not its units.
9
+ */
10
+ export declare function slice(string: string, start?: number, end?: number): string;
package/dist/slice.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Copyright (c) 2026 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * R4 — cut a styled string in **display columns**, never in code units.
8
+ *
9
+ * `"\u001B[31mred\u001B[39m".slice(0, 3)` returns three characters of an escape sequence
10
+ * and no red at all; that is the bug this exists to remove, and it is the same bug in every
11
+ * hand-rolled column-cutter. Three rules, none of which a code-unit slice can honour:
12
+ *
13
+ * 1. **Never split a grapheme cluster.** A cluster that straddles a boundary is included
14
+ * whole — the cut rounds *outward*, never inward, so a slice can be one column wider
15
+ * than asked but never returns half a family emoji.
16
+ * 2. **Close what is open at the cut, and reopen it at the start.** A style opened before
17
+ * `start` is re-emitted at the front of the result, because the caller is going to
18
+ * print this fragment somewhere the opener never reached.
19
+ * 3. **A hyperlink is a style too** (`OSC 8`), closed and reopened the same way.
20
+ *
21
+ * Built on the same stack `wrap` uses rather than a second copy of it, which is the whole
22
+ * consolidation: `slice-ansi` and `wrap-ansi` each carry their own, and they disagree.
23
+ */
24
+ import { applyParameters, closingSequence, hyperlink, matchEscape, openingSequence, segmenter } from './style.js';
25
+ import { measure } from './width.js';
26
+ /**
27
+ * Rule 2. Deliberately without `wrap`'s `applyLeadingResets`: that optimisation drops a
28
+ * style whose reset immediately follows the cut, which is right for a row boundary and
29
+ * wrong here — by the time the first kept cluster is known the walk has already run past
30
+ * the whole plain-text segment, so the "next" sequence it would inspect belongs to text
31
+ * this slice still contains. Reopening a style that is about to be reset costs bytes;
32
+ * dropping one that is not costs the colour.
33
+ */
34
+ function open(cut) {
35
+ if (cut.started)
36
+ return;
37
+ cut.started = true;
38
+ cut.body += openingSequence(cut.active);
39
+ if (cut.link !== undefined)
40
+ cut.body += hyperlink(cut.link.uri, cut.link.parameters);
41
+ }
42
+ /**
43
+ * An escape sequence: it always moves the stack, and is copied through only when the cut
44
+ * has started and has not finished. The stack is what gets re-emitted at `start`, which is
45
+ * why a sequence outside the range is still read.
46
+ */
47
+ function takeEscape(cut, escape, end) {
48
+ const groups = escape.groups ?? {};
49
+ if (groups['sgr'] !== undefined)
50
+ applyParameters(groups['sgr'], cut.active);
51
+ else if (groups['uri'] !== undefined)
52
+ cut.link = groups['uri'].length === 0 ? undefined : { parameters: groups['parameters'] ?? '', uri: groups['uri'] };
53
+ if (cut.started && cut.column < end)
54
+ cut.body += escape[0];
55
+ }
56
+ /** One run of plain text, cluster by cluster. Returns true when the range has been filled. */
57
+ function takeText(cut, run, start, end) {
58
+ for (const { segment } of segmenter.segment(run)) {
59
+ const columns = measure(segment);
60
+ // Rule 1: a cluster is in when any column it occupies is in, so a zero-width mark rides
61
+ // with the cluster it follows rather than falling off the front of a slice.
62
+ if (cut.column + Math.max(columns, 1) > start && cut.column < end) {
63
+ open(cut);
64
+ cut.body += segment;
65
+ }
66
+ cut.column += columns;
67
+ if (cut.column >= end && columns > 0)
68
+ return true;
69
+ }
70
+ return cut.column >= end;
71
+ }
72
+ /**
73
+ * `[start, end)` in display columns. A negative or reversed range is empty rather than an
74
+ * error, matching `String.prototype.slice`'s temperament if not its units.
75
+ */
76
+ export function slice(string, start = 0, end = Number.POSITIVE_INFINITY) {
77
+ if (end <= start || string.length === 0)
78
+ return '';
79
+ const cut = { active: [], link: undefined, column: 0, body: '', started: false };
80
+ let index = 0;
81
+ while (index < string.length) {
82
+ const escape = matchEscape(string, index);
83
+ if (escape !== undefined) {
84
+ takeEscape(cut, escape, end);
85
+ index += escape[0].length;
86
+ continue;
87
+ }
88
+ let run = '';
89
+ while (index < string.length && matchEscape(string, index) === undefined) {
90
+ run += string[index];
91
+ index += 1;
92
+ }
93
+ if (takeText(cut, run, start, end))
94
+ break;
95
+ }
96
+ if (!cut.started)
97
+ return '';
98
+ return cut.body + (cut.link === undefined ? '' : hyperlink('')) + closingSequence(cut.active);
99
+ }
100
+ //# sourceMappingURL=slice.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (c) 2026 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * Everything a terminal would print, with the escape sequences removed: CSI (SGR and the
8
+ * cursor and erase forms), OSC including `OSC 8` hyperlinks under both terminators, DCS, the
9
+ * charset selections, and the single-character escapes. A lone `ESC` with nothing that parses
10
+ * after it is text and is kept — the same answer `strip-ansi` gives.
11
+ *
12
+ * Two passes, which is the design's prescription taken literally: *"using
13
+ * `util.stripVTControlCharacters` where it is exact and a local scan where it is not."*
14
+ *
15
+ * 1. The local scan, over `style.ts`'s `ANSI_ESCAPE`. It removes CSI and OSC **including
16
+ * the colon form** of an extended colour, which is the one shape Node gets wrong.
17
+ * 2. Node's stripper on what is left — the single-character escapes (`ESC c`), the charset
18
+ * selections (`ESC ( B`) and a truncated sequence at the end of a string. `ANSI_ESCAPE`
19
+ * matches none of those, because `wrap` never needed them: it was built to find the
20
+ * sequences it has to *reopen*, and a charset selection is not one.
21
+ *
22
+ * Order is load-bearing. Ours runs first so the colon form is already gone by the time Node
23
+ * sees the string; reversed, pass 2 would leave `:2::255:0:0m` behind as text and pass 1
24
+ * would have nothing left to match.
25
+ */
26
+ export declare function strip(string: string): string;
package/dist/strip.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Copyright (c) 2026 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * R3 — remove the sequences a terminal consumes, and leave the text it prints.
8
+ *
9
+ * The design says to use `util.stripVTControlCharacters` "where it is exact and a local scan
10
+ * where it is not — measured, and the divergence recorded rather than assumed". Measured, on
11
+ * Node 24 over sixteen sequence shapes: **fifteen agree with `strip-ansi` and one does not.**
12
+ *
13
+ * ESC[38:2::255:0:0m node -> ":2::255:0:0m" strip-ansi -> ""
14
+ *
15
+ * That is the colon form of an extended colour (ITU T.416, which `chalk`, `wrap-ansi` and
16
+ * every 24-bit-colour library emit): Node's scanner stops at the first `:` and leaves the
17
+ * rest of the sequence in the output as text. One shape, and the one that matters most,
18
+ * because it is **not** a rare dialect — it is how a truecolor SGR is written when the
19
+ * sub-parameter form is used.
20
+ *
21
+ * It was a live bug here, not a theoretical one. `width.ts` called
22
+ * `stripVTControlCharacters`, so `width("ESC[38:2::255:0:0mred ESC[39m")` answered **15**
23
+ * where `string-width` answers **3** — and every caller that measures went with it: `wrap`,
24
+ * `slice`, `truncate`, `widest`, and in `flagstaff` the box, the table and the spinner.
25
+ *
26
+ * So the local scan is the whole implementation, over `style.ts`'s `ANSI_ESCAPE` — which has
27
+ * always handled the colon form, because `wrap` needs to reopen those colours across a row.
28
+ * The package understood the syntax in one module and mis-stripped it in another, which is
29
+ * precisely the duplication the consolidation exists to remove.
30
+ */
31
+ import { stripVTControlCharacters as nodeStrip } from 'node:util';
32
+ import { forEachSegment } from './style.js';
33
+ /**
34
+ * Everything a terminal would print, with the escape sequences removed: CSI (SGR and the
35
+ * cursor and erase forms), OSC including `OSC 8` hyperlinks under both terminators, DCS, the
36
+ * charset selections, and the single-character escapes. A lone `ESC` with nothing that parses
37
+ * after it is text and is kept — the same answer `strip-ansi` gives.
38
+ *
39
+ * Two passes, which is the design's prescription taken literally: *"using
40
+ * `util.stripVTControlCharacters` where it is exact and a local scan where it is not."*
41
+ *
42
+ * 1. The local scan, over `style.ts`'s `ANSI_ESCAPE`. It removes CSI and OSC **including
43
+ * the colon form** of an extended colour, which is the one shape Node gets wrong.
44
+ * 2. Node's stripper on what is left — the single-character escapes (`ESC c`), the charset
45
+ * selections (`ESC ( B`) and a truncated sequence at the end of a string. `ANSI_ESCAPE`
46
+ * matches none of those, because `wrap` never needed them: it was built to find the
47
+ * sequences it has to *reopen*, and a charset selection is not one.
48
+ *
49
+ * Order is load-bearing. Ours runs first so the colon form is already gone by the time Node
50
+ * sees the string; reversed, pass 2 would leave `:2::255:0:0m` behind as text and pass 1
51
+ * would have nothing left to match.
52
+ */
53
+ export function strip(string) {
54
+ if (string === '')
55
+ return '';
56
+ let out = '';
57
+ forEachSegment(string, (text) => {
58
+ out += text;
59
+ });
60
+ return nodeStrip(out);
61
+ }
62
+ //# sourceMappingURL=strip.js.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Copyright (c) 2026 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * The style stack — the part of this package that every cutting operation shares.
8
+ *
9
+ * `wrap`, `slice` and `truncate` are one algorithm wearing three names: walk the string,
10
+ * keep a stack of the SGR parameters a terminal currently has open, emit graphemes while
11
+ * inside the range you were asked for, and at every cut emit the closer for whatever is
12
+ * open and re-emit the openers on resume. The twelve incumbents write that three times
13
+ * between them — `wrap-ansi`, `slice-ansi` and `cli-truncate` each carry their own copy —
14
+ * and disagree at the edges, which is most of the reason this package exists.
15
+ *
16
+ * Extracted from `wrap.ts` when `slice` arrived, with wrap-ansi's own suite as the check:
17
+ * this module is a move, and `wrap.test.ts` grades every case against the real `wrap-ansi`,
18
+ * so a mistake in the extraction is a red suite rather than a subtle divergence.
19
+ *
20
+ * It is not a published subpath. `linegauge/wrap` and `linegauge/slice` each reach it, and
21
+ * R8 keeps them from reaching each other.
22
+ */
23
+ export declare const ESC = "\u001B";
24
+ export declare const BELL = "\u0007";
25
+ export declare const CSI = "[";
26
+ export declare const OSC = "]";
27
+ export declare const SGR_TERMINATOR = "m";
28
+ /** How many columns a tab advances to the next stop. */
29
+ export declare const TAB_SIZE = 8;
30
+ export declare const ESCAPES: Set<string>;
31
+ export declare const ANSI_ESCAPE: RegExp;
32
+ export declare const ROW_BOUNDARY: RegExp;
33
+ /** Every printable ASCII character is its own cluster of width one — skip the segmenter. */
34
+ export declare const ASCII_PRINTABLE: RegExp;
35
+ /**
36
+ * Which SGR code closes which modifier. This is ECMA-48, not any library's table — bold
37
+ * opens with 1 and closes with 22 wherever you read it — so it lives here rather than
38
+ * being imported, which keeps `wrap()` free of `roundel/chalk` and takes 18 KB off every
39
+ * subpath that wraps. The colour families close with 39, 49 and 59 and are handled by name
40
+ * above, before this map is consulted.
41
+ */
42
+ export declare const MODIFIER_CLOSE: Map<number, number>;
43
+ export declare const MODIFIER_CLOSE_CODES: Set<number>;
44
+ export declare const segmenter: Intl.Segmenter;
45
+ export declare const sgr: (code: number | string) => string;
46
+ export declare const hyperlink: (url: string, parameters?: string) => string;
47
+ /** The complete escape sequence starting at `index`, or nothing when none starts there. */
48
+ export declare function matchEscape(string: string, index: number): RegExpExecArray | undefined;
49
+ /**
50
+ * Walk a string as alternating runs of plain text and complete escape sequences. A
51
+ * character that looks like an introducer but starts no valid sequence stays plain text.
52
+ */
53
+ export declare function forEachSegment(string: string, onPlainText: (text: string) => void, onEscape?: (escape: string) => void): void;
54
+ export interface SgrToken {
55
+ code: number;
56
+ open: string;
57
+ hasArguments: boolean;
58
+ }
59
+ export interface ActiveStyle {
60
+ /** One slot per thing a terminal tracks separately, so a second red replaces the first. */
61
+ family: string;
62
+ open: string;
63
+ close: number;
64
+ }
65
+ export declare function sgrTokens(parameters: string): SgrToken[];
66
+ export declare function applyToken(token: SgrToken, active: ActiveStyle[]): void;
67
+ export declare const applyParameters: (parameters: string, active: ActiveStyle[]) => void;
68
+ /** A row that opens with its own resets should not have them undone by the reopening. */
69
+ export declare function applyLeadingResets(string: string, startIndex: number, active: ActiveStyle[]): void;
70
+ export declare const closingSequence: (active: ActiveStyle[]) => string;
71
+ export declare const openingSequence: (active: ActiveStyle[]) => string;