@alchemy.run/sigil 0.0.0-alpha.3 → 0.0.0-alpha.5
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 +23 -13
- package/THIRD_PARTY_NOTICES.md +70 -23
- package/dist/Text-D5HUf3Fj.d.ts +452 -0
- package/dist/ansi.d.ts +137 -143
- package/dist/ansi.js +87 -2
- package/dist/capabilities.d.ts +5 -0
- package/dist/capabilities.js +3 -0
- package/dist/cell-_ZVhbfl0.js +44 -0
- package/dist/color-CkbalRqK.js +2 -0
- package/dist/color-policy-BMzMwV7Q.d.ts +22 -0
- package/dist/color-policy-SVj1pYTA.js +560 -0
- package/dist/color-profile-DHhQHY55.js +36 -0
- package/dist/color-profile-u0Nhe9Nv.d.ts +97 -0
- package/dist/color.d.ts +21 -0
- package/dist/color.js +3 -0
- package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
- package/dist/detect-Bh4yGP6w.d.ts +186 -0
- package/dist/detect-BuTXtY6e.js +373 -0
- package/dist/{devtools-BhYGjb7h.js → devtools-DbthxoD1.js} +22 -23
- package/dist/env-YVw64yZS.js +9 -0
- package/dist/escapes-CB_6CWOE.d.ts +72 -0
- package/dist/geometry-BxXOzJgo.d.ts +11 -0
- package/dist/index-Bmc2tRPk.d.ts +21 -0
- package/dist/index.d.ts +235 -754
- package/dist/index.js +1362 -3001
- package/dist/osc-CCH7xDoS.js +71 -0
- package/dist/osc-Cn0fw77g.d.ts +23 -0
- package/dist/paint-C19minOS.d.ts +81 -0
- package/dist/query-vaIeGOkH.d.ts +152 -0
- package/dist/router.d.ts +392 -0
- package/dist/router.js +709 -0
- package/dist/sample-Cqw1bjUL.js +445 -0
- package/dist/screen-BOh__-65.js +324 -0
- package/dist/screen-BReKIheE.d.ts +40 -0
- package/dist/screen.d.ts +5 -0
- package/dist/screen.js +5 -0
- package/dist/semantic-text-style-DIMzC7xt.js +91 -0
- package/dist/serialize-BTkAZgw1.js +79 -0
- package/dist/session-BJmzX2NJ.js +664 -0
- package/dist/sgr-BhwaWAJB.js +246 -0
- package/dist/store-CgrG9K4y.d.ts +72 -0
- package/dist/string-width-CijQwpIk.js +69 -0
- package/dist/strip-BvU4toXG.js +6 -0
- package/dist/terminal.d.ts +118 -0
- package/dist/terminal.js +2 -0
- package/dist/tokenize-AjqbvtiT.js +1242 -0
- package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
- package/dist/truncate-D31fhU6i.js +562 -0
- package/dist/use-focus-C2sciZOo.js +1335 -0
- package/package.json +43 -17
- package/src/ansi/chalk.ts +12 -53
- package/src/ansi/cursor.ts +2 -4
- package/src/ansi/east-asian-width.ts +44 -0
- package/src/ansi/escapes.ts +18 -26
- package/src/ansi/graphemes.ts +8 -0
- package/src/ansi/hyperlink.ts +44 -0
- package/src/ansi/index.ts +14 -14
- package/src/ansi/osc.ts +77 -0
- package/src/ansi/sgr.ts +2 -5
- package/src/ansi/slice.ts +1 -1
- package/src/ansi/string-width.ts +91 -206
- package/src/ansi/strip.ts +13 -33
- package/src/ansi/tokenize.ts +96 -169
- package/src/ansi/truncate.ts +5 -3
- package/src/ansi/wrap.ts +13 -14
- package/src/ansi-tokenizer.ts +1 -1
- package/src/capabilities/color-policy.ts +34 -0
- package/src/capabilities/detect.ts +594 -0
- package/src/capabilities/index.ts +37 -0
- package/src/capabilities/query.ts +657 -0
- package/src/capabilities/store.ts +379 -0
- package/src/color/index.ts +3 -0
- package/src/color/paint.ts +169 -0
- package/src/color/palette.ts +48 -0
- package/src/color/sample.ts +323 -0
- package/src/color.ts +1 -0
- package/src/components/AnsiText.tsx +42 -0
- package/src/components/App.tsx +109 -22
- package/src/components/BackgroundContext.ts +2 -3
- package/src/components/Box.tsx +43 -60
- package/src/components/CursorContext.ts +1 -1
- package/src/components/ErrorBoundary.tsx +1 -1
- package/src/components/ErrorOverview.tsx +8 -7
- package/src/components/Hyperlink.tsx +56 -0
- package/src/components/Spacer.tsx +1 -1
- package/src/components/Static.tsx +1 -1
- package/src/components/StderrContext.ts +0 -2
- package/src/components/StdinContext.ts +0 -1
- package/src/components/StdoutContext.ts +1 -3
- package/src/components/TerminalOscContext.ts +25 -0
- package/src/components/Text.tsx +23 -46
- package/src/components/Transform.tsx +2 -2
- package/src/cursor-position.ts +1 -1
- package/src/devtools.ts +84 -24
- package/src/dom.ts +11 -8
- package/src/env.ts +12 -0
- package/src/get-max-width.ts +1 -1
- package/src/global.d.ts +6 -4
- package/src/{boxes.ts → glyphs.ts} +17 -18
- package/src/hooks/use-animation.ts +1 -1
- package/src/hooks/use-app.ts +1 -1
- package/src/hooks/use-box-metrics.ts +1 -1
- package/src/hooks/use-capabilities.ts +73 -0
- package/src/hooks/use-cursor.ts +3 -3
- package/src/hooks/use-focus-manager.ts +1 -1
- package/src/hooks/use-focus.ts +5 -6
- package/src/hooks/use-input.ts +4 -4
- package/src/hooks/use-is-screen-reader-enabled.ts +1 -1
- package/src/hooks/use-paste.ts +2 -2
- package/src/hooks/use-stderr.ts +1 -1
- package/src/hooks/use-stdin.ts +1 -1
- package/src/hooks/use-stdout.ts +1 -1
- package/src/hooks/use-terminal-osc.ts +59 -0
- package/src/hooks/use-window-size.ts +2 -2
- package/src/index.ts +88 -44
- package/src/ink.tsx +620 -745
- package/src/input-parser.ts +1 -1
- package/src/instances.ts +1 -1
- package/src/kitty-keyboard.ts +128 -0
- package/src/measure-element.ts +1 -1
- package/src/measure-text.ts +2 -2
- package/src/{render-node-to-output.ts → paint-tree.ts} +78 -53
- package/src/parse-keypress.ts +4 -5
- package/src/parse-stack-line.ts +0 -1
- package/src/patch-console.ts +44 -0
- package/src/reconciler.ts +36 -62
- package/src/render-background.ts +37 -16
- package/src/render-border.ts +97 -64
- package/src/render-frame.ts +83 -0
- package/src/render-to-string.ts +24 -9
- package/src/render.ts +24 -16
- package/src/router/components.tsx +343 -0
- package/src/router/context.ts +41 -0
- package/src/router/history.ts +194 -0
- package/src/router/hooks.tsx +391 -0
- package/src/router/index.ts +34 -0
- package/src/router/matcher.ts +571 -0
- package/src/sanitize-ansi.ts +1 -1
- package/src/screen/ansi.ts +184 -0
- package/src/screen/canvas.ts +137 -0
- package/src/screen/cell.ts +138 -0
- package/src/screen/color-profile.ts +47 -0
- package/src/screen/geometry.ts +9 -0
- package/src/screen/index.ts +6 -0
- package/src/screen/screen.ts +272 -0
- package/src/screen/serialize.ts +129 -0
- package/src/screen.ts +1 -0
- package/src/semantic-text-style.ts +118 -0
- package/src/signal-exit.ts +4 -5
- package/src/squash-text-nodes.ts +4 -7
- package/src/stream.ts +2 -4
- package/src/structured-text.ts +321 -0
- package/src/styles.ts +16 -17
- package/src/terminal/index.ts +2 -0
- package/src/terminal/inline-presenter.ts +120 -0
- package/src/terminal/input.ts +86 -0
- package/src/terminal/render-scheduler.ts +37 -0
- package/src/terminal/screen-presenter.ts +188 -0
- package/src/terminal/session.ts +407 -0
- package/src/terminal-size.ts +9 -8
- package/src/terminal.ts +1 -0
- package/src/testing/browser.ts +588 -0
- package/src/testing/emulators.ts +205 -0
- package/src/testing/explorer-app/index.html +12 -0
- package/src/testing/explorer-app/main.ts +381 -0
- package/src/testing/explorer-app/style.css +194 -0
- package/src/testing/explorer-app/tsconfig.json +15 -0
- package/src/testing/explorer-app/vite-env.d.ts +1 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/keys.ts +56 -0
- package/src/testing/live.ts +85 -0
- package/src/testing/matchers.ts +70 -0
- package/src/testing/public.ts +94 -0
- package/src/testing/terminal.ts +349 -0
- package/src/testing/vitest.ts +157 -0
- package/src/transform-adapter.ts +14 -0
- package/src/types.ts +1 -6
- package/src/utils.ts +2 -2
- package/src/wrap-text.ts +4 -4
- package/src/yoga/config.ts +2 -2
- package/src/yoga/core/absoluteLayout.ts +15 -15
- package/src/yoga/core/baseline.ts +3 -3
- package/src/yoga/core/cache.ts +4 -4
- package/src/yoga/core/calculateLayout.ts +33 -27
- package/src/yoga/core/config.ts +1 -1
- package/src/yoga/core/flexLine.ts +3 -3
- package/src/yoga/core/helpers.ts +3 -3
- package/src/yoga/core/layoutResults.ts +4 -4
- package/src/yoga/core/node.ts +16 -16
- package/src/yoga/core/pixelGrid.ts +4 -4
- package/src/yoga/core/style.ts +11 -11
- package/src/yoga/core/types.ts +2 -2
- package/src/yoga/index.ts +6 -6
- package/src/yoga/node.ts +8 -8
- package/dist/sgr-CMfEpjSk.d.ts +0 -91
- package/dist/truncate-CBiyyZzw.js +0 -2156
- package/src/ansi/supports-color.ts +0 -213
- package/src/ansi/widest-line.ts +0 -12
- package/src/auto-bind.ts +0 -41
- package/src/colorize.ts +0 -60
- package/src/devtools-window-polyfill.ts +0 -73
- package/src/indent-string.ts +0 -16
- package/src/is-in-ci.ts +0 -7
- package/src/log-update.ts +0 -370
- package/src/output.ts +0 -308
- package/src/renderer.ts +0 -73
- package/src/write-synchronized.ts +0 -9
package/src/ansi/tokenize.ts
CHANGED
|
@@ -1,34 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { tokenizeAnsi } from "#/ansi-tokenizer.ts";
|
|
2
|
+
import { isFullwidthGrapheme } from "#/ansi/east-asian-width.ts";
|
|
3
|
+
// ANSI-aware style tokenizer: splits a string into grapheme clusters and
|
|
4
|
+
// ANSI codes, tracks active styles per character, and re-emits minimal
|
|
5
|
+
// escape sequences. This is the style model Ink's renderer is built on.
|
|
6
|
+
// Style semantics ported from `@alcalzone/ansi-tokenize` (MIT, AlCalzone);
|
|
7
|
+
// escape recognition delegates to the shared ECMA-48 grammar in
|
|
8
|
+
// `src/ansi-tokenizer.ts` (also behind sanitize-ansi), and grapheme widths
|
|
9
|
+
// come from `east-asian-width.ts` — one grammar, one width table.
|
|
10
|
+
import { BEL, C1_ST, ESC } from "#/ansi/escapes.ts";
|
|
11
|
+
import { graphemes } from "#/ansi/graphemes.ts";
|
|
12
|
+
import { codes as sgrCodes, foreground, background, styles } from "#/ansi/sgr.ts";
|
|
8
13
|
|
|
9
14
|
// Single-character introducer suffixes (`ESC [` = CSI, `ESC ]` = OSC).
|
|
10
15
|
const BACKSLASH = "\\";
|
|
11
16
|
const CSI = "[";
|
|
12
17
|
const OSC = "]";
|
|
13
18
|
|
|
14
|
-
// Char codes
|
|
15
|
-
const CC_BEL = BEL.charCodeAt(0);
|
|
16
|
-
const CC_ESC = ESC.charCodeAt(0);
|
|
17
|
-
const CC_BACKSLASH = BACKSLASH.charCodeAt(0);
|
|
18
|
-
const CC_CSI = CSI.charCodeAt(0);
|
|
19
|
-
const CC_OSC = OSC.charCodeAt(0);
|
|
20
|
-
const CC_C1_ST = C1_ST.charCodeAt(0);
|
|
21
|
-
const CC_0 = "0".charCodeAt(0);
|
|
22
|
-
const CC_9 = "9".charCodeAt(0);
|
|
23
|
-
const CC_SEMI = ";".charCodeAt(0);
|
|
24
|
-
const CC_M = "m".charCodeAt(0);
|
|
25
|
-
|
|
26
|
-
// Escape code points: \u001B and \u009B
|
|
27
|
-
const ESCAPES = new Set([CC_ESC, 0x9b]);
|
|
28
|
-
|
|
29
19
|
// OSC 8 hyperlink constants
|
|
30
20
|
const linkCodePrefix = `${ESC}${OSC}8;`;
|
|
31
|
-
const linkCodePrefixCharCodes = linkCodePrefix.split("").map((char) => char.charCodeAt(0));
|
|
32
21
|
const linkCodeSuffix = BEL;
|
|
33
22
|
const linkEndCode = `${ESC}${OSC}8;;${BEL}`;
|
|
34
23
|
const linkEndCodeST = `${ESC}${OSC}8;;${ESC}${BACKSLASH}`;
|
|
@@ -103,6 +92,11 @@ export function getEndCode(code: string): string {
|
|
|
103
92
|
return background.close;
|
|
104
93
|
}
|
|
105
94
|
|
|
95
|
+
// Extended underline color (T.416) closes with 59m; not in the SGR map.
|
|
96
|
+
if (code.startsWith("58")) {
|
|
97
|
+
return `${ESC}[59m`;
|
|
98
|
+
}
|
|
99
|
+
|
|
106
100
|
// Otherwise find the reset code in the SGR code map
|
|
107
101
|
const endCode = sgrCodes.get(Number.parseInt(code, 10));
|
|
108
102
|
if (endCode !== undefined) {
|
|
@@ -126,97 +120,6 @@ export function isIntensityCode(code: AnsiCode): boolean {
|
|
|
126
120
|
return code.code === styles.bold.open || code.code === styles.dim.open;
|
|
127
121
|
}
|
|
128
122
|
|
|
129
|
-
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
130
|
-
|
|
131
|
-
function isFullwidthGrapheme(grapheme: string, baseCodePoint: number): boolean {
|
|
132
|
-
if (isFullwidthCodePoint(baseCodePoint)) return true;
|
|
133
|
-
// Variation Selector 16 forces emoji presentation (2 columns wide)
|
|
134
|
-
if (grapheme.includes("\uFE0F")) return true;
|
|
135
|
-
// Regional indicator pairs form flag emoji (2 columns wide)
|
|
136
|
-
if (baseCodePoint >= 0x1f1e6 && baseCodePoint <= 0x1f1ff) return true;
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// HOT PATH: Use only basic string/char code operations for maximum performance
|
|
141
|
-
function parseLinkCode(string: string, offset: number): string | undefined {
|
|
142
|
-
string = string.slice(offset);
|
|
143
|
-
for (let index = 1; index < linkCodePrefixCharCodes.length; index++) {
|
|
144
|
-
if (string.charCodeAt(index) !== linkCodePrefixCharCodes[index]) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Find the semicolon that ends params
|
|
150
|
-
const paramsEndIndex = string.indexOf(";", linkCodePrefix.length);
|
|
151
|
-
if (paramsEndIndex === -1) return;
|
|
152
|
-
|
|
153
|
-
// This is a link code (with or without the URL part). Find the end of it.
|
|
154
|
-
const endIndex = findOscTerminatorIndex(string, paramsEndIndex + 1);
|
|
155
|
-
if (endIndex === -1) return;
|
|
156
|
-
|
|
157
|
-
return string.slice(0, endIndex + 1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// HOT PATH: Generic fallback for non-link OSC sequences (window title, etc.)
|
|
161
|
-
function parseOscSequence(string: string, offset: number): string | undefined {
|
|
162
|
-
string = string.slice(offset);
|
|
163
|
-
// Find the OSC terminator (starting after "ESC ]")
|
|
164
|
-
const endIndex = findOscTerminatorIndex(string, 2);
|
|
165
|
-
if (endIndex === -1) return;
|
|
166
|
-
return string.slice(0, endIndex + 1);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
Finds the index of the last character of the first OSC terminator at or after
|
|
171
|
-
`startIndex`. Recognizes BEL (\u0007), C1 ST (\u009C), and ESC+backslash.
|
|
172
|
-
Returns -1 if no terminator is found.
|
|
173
|
-
*/
|
|
174
|
-
function findOscTerminatorIndex(string: string, startIndex: number): number {
|
|
175
|
-
for (let index = startIndex; index < string.length; index++) {
|
|
176
|
-
const charCode = string.charCodeAt(index);
|
|
177
|
-
if (charCode === CC_BEL) return index;
|
|
178
|
-
if (charCode === CC_C1_ST) return index;
|
|
179
|
-
if (
|
|
180
|
-
charCode === CC_ESC &&
|
|
181
|
-
index + 1 < string.length &&
|
|
182
|
-
string.charCodeAt(index + 1) === CC_BACKSLASH
|
|
183
|
-
) {
|
|
184
|
-
return index + 1;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return -1;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
Scans through the given string and finds the index of the last character of an
|
|
193
|
-
SGR sequence like `\u001B[38;2;123;123;123m`. This assumes that the string has
|
|
194
|
-
been checked to start with `\u001B[`. Returns -1 if no valid SGR sequence is
|
|
195
|
-
found.
|
|
196
|
-
*/
|
|
197
|
-
function findSgrSequenceEndIndex(string: string): number {
|
|
198
|
-
for (let index = 2; index < string.length; index++) {
|
|
199
|
-
const charCode = string.charCodeAt(index);
|
|
200
|
-
// m marks the end of the SGR sequence
|
|
201
|
-
if (charCode === CC_M) return index;
|
|
202
|
-
// Digits and semicolons are valid
|
|
203
|
-
if (charCode === CC_SEMI) continue;
|
|
204
|
-
if (charCode >= CC_0 && charCode <= CC_9) continue;
|
|
205
|
-
// Everything else is invalid
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return -1;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// HOT PATH: Use only basic string/char code operations for maximum performance
|
|
213
|
-
function parseSgrSequence(string: string, offset: number): string | undefined {
|
|
214
|
-
string = string.slice(offset);
|
|
215
|
-
const endIndex = findSgrSequenceEndIndex(string);
|
|
216
|
-
if (endIndex === -1) return;
|
|
217
|
-
return string.slice(0, endIndex + 1);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
123
|
/**
|
|
221
124
|
Splits compound SGR sequences like `\u001B[1;3;31m` into individual components.
|
|
222
125
|
*/
|
|
@@ -235,7 +138,7 @@ function splitCompoundSgrSequences(code: string): string[] {
|
|
|
235
138
|
for (let index = 0; index < codeParts.length; index++) {
|
|
236
139
|
const rawCode = codeParts[index]!;
|
|
237
140
|
// Keep 8-bit and 24-bit color codes (containing multiple ";") together
|
|
238
|
-
if (rawCode === "38" || rawCode === "48") {
|
|
141
|
+
if (rawCode === "38" || rawCode === "48" || rawCode === "58") {
|
|
239
142
|
if (index + 2 < codeParts.length && codeParts[index + 1] === "5") {
|
|
240
143
|
// 8-bit color, followed by another number
|
|
241
144
|
result.push(codeParts.slice(index, index + 3).join(";"));
|
|
@@ -256,73 +159,97 @@ function splitCompoundSgrSequences(code: string): string[] {
|
|
|
256
159
|
return result.map((part) => `${ESC}[${part}m`);
|
|
257
160
|
}
|
|
258
161
|
|
|
162
|
+
// SGR parameters: digits separated by `;` or `:` (ITU T.416 colon form).
|
|
163
|
+
const SGR_PARAMETERS_RE = /^[\d;:]*$/;
|
|
164
|
+
|
|
165
|
+
const c1LinkCodePrefix = "\u009D8;";
|
|
166
|
+
|
|
259
167
|
export function tokenize(string: string, endChar = Number.POSITIVE_INFINITY): Token[] {
|
|
260
168
|
const result: Token[] = [];
|
|
261
169
|
let visible = 0;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
170
|
+
|
|
171
|
+
outer: for (const ansiToken of tokenizeAnsi(string)) {
|
|
172
|
+
if (ansiToken.type === "text") {
|
|
173
|
+
for (const segment of graphemes(ansiToken.value)) {
|
|
174
|
+
const fullWidth = isFullwidthGrapheme(segment, segment.codePointAt(0)!);
|
|
175
|
+
result.push({
|
|
176
|
+
type: "char",
|
|
177
|
+
value: segment,
|
|
178
|
+
fullWidth,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
visible += fullWidth ? 2 : 1;
|
|
182
|
+
if (visible >= endChar) {
|
|
183
|
+
break outer;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (ansiToken.type === "csi") {
|
|
191
|
+
// SGR sequences carry style state; C1 introducers are normalized to the
|
|
192
|
+
// 7-bit form so downstream comparisons see a single representation.
|
|
193
|
+
if (
|
|
194
|
+
ansiToken.finalCharacter === "m" &&
|
|
195
|
+
ansiToken.intermediateString === "" &&
|
|
196
|
+
SGR_PARAMETERS_RE.test(ansiToken.parameterString)
|
|
197
|
+
) {
|
|
198
|
+
// Split compound codes into individual tokens
|
|
199
|
+
const code = `${ESC}${CSI}${ansiToken.parameterString}m`;
|
|
200
|
+
for (const individualCode of splitCompoundSgrSequences(code)) {
|
|
278
201
|
result.push({
|
|
279
202
|
type: "ansi",
|
|
280
|
-
code,
|
|
281
|
-
endCode: getEndCode(
|
|
203
|
+
code: individualCode,
|
|
204
|
+
endCode: getEndCode(individualCode),
|
|
282
205
|
});
|
|
283
|
-
} else {
|
|
284
|
-
// Other OSC sequences (window title, etc.) are self-contained
|
|
285
|
-
// control codes with no endCode.
|
|
286
|
-
code = parseOscSequence(string, index);
|
|
287
|
-
if (code) {
|
|
288
|
-
result.push({
|
|
289
|
-
type: "control",
|
|
290
|
-
code,
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
} else if (nextCodePoint === CC_CSI) {
|
|
295
|
-
// [ = control sequence introducer, like SGR sequences [...m
|
|
296
|
-
code = parseSgrSequence(string, index);
|
|
297
|
-
if (code) {
|
|
298
|
-
// Split compound codes into individual tokens
|
|
299
|
-
for (const individualCode of splitCompoundSgrSequences(code)) {
|
|
300
|
-
result.push({
|
|
301
|
-
type: "ansi",
|
|
302
|
-
code: individualCode,
|
|
303
|
-
endCode: getEndCode(individualCode),
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
206
|
}
|
|
207
|
+
} else {
|
|
208
|
+
result.push({
|
|
209
|
+
type: "control",
|
|
210
|
+
code: ansiToken.value,
|
|
211
|
+
});
|
|
307
212
|
}
|
|
308
213
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (ansiToken.type === "osc") {
|
|
218
|
+
// OSC 8 hyperlinks are paired codes with an endCode; C1 introducers are
|
|
219
|
+
// normalized to the 7-bit form. Other OSC sequences (window title,
|
|
220
|
+
// etc.) are self-contained control codes with no endCode.
|
|
221
|
+
const { value } = ansiToken;
|
|
222
|
+
const prefix = value.startsWith(linkCodePrefix)
|
|
223
|
+
? linkCodePrefix
|
|
224
|
+
: value.startsWith(c1LinkCodePrefix)
|
|
225
|
+
? c1LinkCodePrefix
|
|
226
|
+
: undefined;
|
|
227
|
+
|
|
228
|
+
if (prefix !== undefined && value.includes(";", prefix.length)) {
|
|
229
|
+
const code =
|
|
230
|
+
prefix === linkCodePrefix ? value : `${linkCodePrefix}${value.slice(prefix.length)}`;
|
|
231
|
+
result.push({
|
|
232
|
+
type: "ansi",
|
|
233
|
+
code,
|
|
234
|
+
endCode: getEndCode(code),
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
result.push({
|
|
238
|
+
type: "control",
|
|
239
|
+
code: value,
|
|
240
|
+
});
|
|
312
241
|
}
|
|
242
|
+
|
|
243
|
+
continue;
|
|
313
244
|
}
|
|
314
245
|
|
|
315
|
-
|
|
246
|
+
// Everything else the grammar recognizes (ESC sequences, DCS/PM/APC/SOS
|
|
247
|
+
// control strings, stray ST or C1 bytes, malformed tails) is a zero-width
|
|
248
|
+
// control unit, preserved verbatim.
|
|
316
249
|
result.push({
|
|
317
|
-
type: "
|
|
318
|
-
|
|
319
|
-
fullWidth,
|
|
250
|
+
type: "control",
|
|
251
|
+
code: ansiToken.value,
|
|
320
252
|
});
|
|
321
|
-
|
|
322
|
-
visible += fullWidth ? 2 : 1;
|
|
323
|
-
if (visible >= endChar) {
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
326
253
|
}
|
|
327
254
|
|
|
328
255
|
return result;
|
|
@@ -377,7 +304,7 @@ Returns the combination of ANSI codes needed to undo the given ANSI codes.
|
|
|
377
304
|
*/
|
|
378
305
|
export function undoAnsiCodes(codes: readonly AnsiCode[]): AnsiCode[] {
|
|
379
306
|
return reduceAnsiCodes(codes)
|
|
380
|
-
.
|
|
307
|
+
.toReversed()
|
|
381
308
|
.map((code) => ({
|
|
382
309
|
...code,
|
|
383
310
|
code: code.endCode,
|
package/src/ansi/truncate.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Truncate a string to a visible width, ANSI-aware.
|
|
2
2
|
// Ported from `cli-truncate` (MIT, Sindre Sorhus) on top of the shared
|
|
3
3
|
// slice and width modules.
|
|
4
|
-
import { sliceAnsi } from "
|
|
5
|
-
import { stringWidth } from "
|
|
4
|
+
import { sliceAnsi } from "#/ansi/slice.ts";
|
|
5
|
+
import { stringWidth } from "#/ansi/string-width.ts";
|
|
6
6
|
|
|
7
7
|
export type TruncateOptions = {
|
|
8
8
|
readonly position?: "start" | "middle" | "end";
|
|
@@ -36,7 +36,9 @@ const ANSI_ESC = 27;
|
|
|
36
36
|
const ANSI_LEFT_BRACKET = 91;
|
|
37
37
|
const ANSI_LETTER_M = 109;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
// 0-9, ; or : (ITU T.416 colon-form parameters)
|
|
40
|
+
const isSgrParameter = (code: number): boolean =>
|
|
41
|
+
(code >= 48 && code <= 57) || code === 59 || code === 58;
|
|
40
42
|
|
|
41
43
|
function leadingSgrSpanEndIndex(string: string): number {
|
|
42
44
|
let index = 0;
|
package/src/ansi/wrap.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// Word-wrap a string to a column width, ANSI-aware.
|
|
2
2
|
// Ported from `wrap-ansi` (MIT, Sindre Sorhus).
|
|
3
|
-
import { BEL, C1_CSI, ESC } from "
|
|
3
|
+
import { BEL, C1_CSI, ESC } from "#/ansi/escapes.ts";
|
|
4
4
|
//
|
|
5
5
|
// Supported boundary: semicolon-delimited SGR styling, colon-delimited
|
|
6
6
|
// RGB/indexed colors, and OSC 8 hyperlinks are tracked, while ordinary CSI
|
|
7
7
|
// sequences and other complete 7-bit OSC commands are preserved as opaque
|
|
8
8
|
// zero-width units. This is intentionally not a terminal emulator. Newlines
|
|
9
9
|
// always delimit input lines before ANSI parsing.
|
|
10
|
-
import { codes as sgrCodes } from "
|
|
11
|
-
import { stringWidth } from "
|
|
10
|
+
import { codes as sgrCodes } from "#/ansi/sgr.ts";
|
|
11
|
+
import { stringWidth } from "#/ansi/string-width.ts";
|
|
12
12
|
|
|
13
13
|
export type WrapOptions = {
|
|
14
14
|
readonly trim?: boolean;
|
|
@@ -59,8 +59,7 @@ const segmenter = new Intl.Segmenter();
|
|
|
59
59
|
// Complete ANSI sequences have already been removed before measuring these
|
|
60
60
|
// strings. Avoid string-width's ANSI scan so malformed sequences are not
|
|
61
61
|
// rescanned.
|
|
62
|
-
const getStringWidth = (string: string): number =>
|
|
63
|
-
stringWidth(string, { countAnsiEscapeCodes: true });
|
|
62
|
+
const getStringWidth = (string: string): number => stringWidth(string);
|
|
64
63
|
const TAB_SIZE = 8;
|
|
65
64
|
|
|
66
65
|
// Finds the next character that could introduce a sequence, so plain text is
|
|
@@ -221,18 +220,18 @@ type ActiveStyle = {
|
|
|
221
220
|
readonly close: number;
|
|
222
221
|
};
|
|
223
222
|
|
|
223
|
+
const EXTENDED_COLOR_CODES = new Set([
|
|
224
|
+
ANSI_SGR_FOREGROUND_EXTENDED,
|
|
225
|
+
ANSI_SGR_BACKGROUND_EXTENDED,
|
|
226
|
+
ANSI_SGR_UNDERLINE_COLOR_EXTENDED,
|
|
227
|
+
]);
|
|
228
|
+
|
|
224
229
|
const getColonColorToken = (parameter: string): SgrToken | undefined => {
|
|
225
230
|
const parts = parameter.split(":");
|
|
226
231
|
const code = Number.parseInt(parts[0]!, 10);
|
|
227
232
|
const mode = Number.parseInt(parts[1]!, 10);
|
|
228
233
|
|
|
229
|
-
if (
|
|
230
|
-
![
|
|
231
|
-
ANSI_SGR_FOREGROUND_EXTENDED,
|
|
232
|
-
ANSI_SGR_BACKGROUND_EXTENDED,
|
|
233
|
-
ANSI_SGR_UNDERLINE_COLOR_EXTENDED,
|
|
234
|
-
].includes(code)
|
|
235
|
-
) {
|
|
234
|
+
if (!EXTENDED_COLOR_CODES.has(code)) {
|
|
236
235
|
return;
|
|
237
236
|
}
|
|
238
237
|
|
|
@@ -467,8 +466,8 @@ const applyLeadingSgrResets = (
|
|
|
467
466
|
};
|
|
468
467
|
|
|
469
468
|
const getClosingSgrSequence = (activeStyles: readonly ActiveStyle[]): string =>
|
|
470
|
-
|
|
471
|
-
.
|
|
469
|
+
activeStyles
|
|
470
|
+
.toReversed()
|
|
472
471
|
.map((activeStyle) => wrapAnsiCode(activeStyle.close))
|
|
473
472
|
.join("");
|
|
474
473
|
|
package/src/ansi-tokenizer.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ColorSupportLevel } from "#/capabilities/detect.ts";
|
|
2
|
+
import { colorProfileFromLevel, type ColorProfile } from "#/screen/color-profile.ts";
|
|
3
|
+
|
|
4
|
+
/** A user-selected output policy. `auto` follows the target stream. */
|
|
5
|
+
export type ColorPolicy = "auto" | ColorProfile;
|
|
6
|
+
|
|
7
|
+
/** The three distinct inputs and result of terminal color negotiation. */
|
|
8
|
+
export type ColorState = {
|
|
9
|
+
/** The stream/terminal fact after environment and query detection. */
|
|
10
|
+
readonly detected: ColorProfile;
|
|
11
|
+
/** The application's policy for this render instance. */
|
|
12
|
+
readonly policy: ColorPolicy;
|
|
13
|
+
/** The profile the serializer must actually emit. */
|
|
14
|
+
readonly effective: ColorProfile;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function resolveColorProfile(
|
|
18
|
+
detectedLevel: ColorSupportLevel,
|
|
19
|
+
policy: ColorPolicy = "auto",
|
|
20
|
+
): ColorProfile {
|
|
21
|
+
return policy === "auto" ? colorProfileFromLevel(detectedLevel) : policy;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function colorState(
|
|
25
|
+
capabilities: { readonly color: { readonly level: ColorSupportLevel } },
|
|
26
|
+
policy: ColorPolicy = "auto",
|
|
27
|
+
): ColorState {
|
|
28
|
+
const detected = colorProfileFromLevel(capabilities.color.level);
|
|
29
|
+
return {
|
|
30
|
+
detected,
|
|
31
|
+
policy,
|
|
32
|
+
effective: policy === "auto" ? detected : policy,
|
|
33
|
+
};
|
|
34
|
+
}
|