@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.2
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 +299 -299
- package/dist/ansi.d.ts +223 -0
- package/dist/ansi.js +2 -0
- package/dist/{devtools-QpCMm9JH.mjs → devtools-BhYGjb7h.js} +1 -1
- package/dist/index-DDVME65c.d.ts +919 -0
- package/dist/index.d.ts +1657 -0
- package/dist/index.js +4643 -0
- package/dist/sgr-CMfEpjSk.d.ts +91 -0
- package/dist/truncate-CBiyyZzw.js +2156 -0
- package/dist/yoga-5jKhYCJC.js +3465 -0
- package/dist/yoga.d.ts +2 -0
- package/dist/yoga.js +2 -0
- package/package.json +37 -17
- package/src/ansi/chalk.ts +179 -0
- package/src/ansi/cursor.ts +48 -0
- package/src/ansi/east-asian-width.ts +215 -0
- package/src/ansi/escapes.ts +128 -0
- package/src/ansi/index.ts +27 -0
- package/src/ansi/sgr.ts +237 -0
- package/src/ansi/slice.ts +43 -0
- package/src/ansi/string-width.ts +236 -0
- package/src/ansi/strip.ts +33 -0
- package/src/ansi/supports-color.ts +213 -0
- package/src/ansi/tokenize.ts +453 -0
- package/src/ansi/truncate.ts +194 -0
- package/src/ansi/widest-line.ts +12 -0
- package/src/ansi/wrap.ts +766 -0
- package/src/ansi-tokenizer.ts +510 -0
- package/src/auto-bind.ts +41 -0
- package/src/boxes.ts +100 -0
- package/src/code-excerpt.ts +39 -0
- package/src/colorize.ts +60 -0
- package/src/components/AccessibilityContext.ts +5 -0
- package/src/components/AnimationContext.ts +24 -0
- package/src/components/App.tsx +781 -0
- package/src/components/AppContext.ts +111 -0
- package/src/components/BackgroundContext.ts +8 -0
- package/src/components/Box.tsx +116 -0
- package/src/components/CursorContext.ts +19 -0
- package/src/components/ErrorBoundary.tsx +38 -0
- package/src/components/ErrorOverview.tsx +133 -0
- package/src/components/FocusContext.ts +30 -0
- package/src/components/Newline.tsx +15 -0
- package/src/components/Spacer.tsx +10 -0
- package/src/components/Static.tsx +59 -0
- package/src/components/StderrContext.ts +26 -0
- package/src/components/StdinContext.ts +49 -0
- package/src/components/StdoutContext.ts +28 -0
- package/src/components/Text.tsx +144 -0
- package/src/components/Transform.tsx +37 -0
- package/src/cursor-position.ts +103 -0
- package/src/devtools-window-polyfill.ts +73 -0
- package/src/devtools.ts +43 -0
- package/src/dom.ts +292 -0
- package/src/get-max-width.ts +11 -0
- package/src/global.d.ts +36 -0
- package/src/hooks/use-animation.ts +142 -0
- package/src/hooks/use-app.ts +8 -0
- package/src/hooks/use-box-metrics.ts +134 -0
- package/src/hooks/use-cursor.ts +33 -0
- package/src/hooks/use-focus-manager.ts +62 -0
- package/src/hooks/use-focus.ts +83 -0
- package/src/hooks/use-input.ts +267 -0
- package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
- package/src/hooks/use-paste.ts +78 -0
- package/src/hooks/use-stderr.ts +8 -0
- package/src/hooks/use-stdin.ts +10 -0
- package/src/hooks/use-stdout.ts +8 -0
- package/src/hooks/use-window-size.ts +41 -0
- package/src/indent-string.ts +16 -0
- package/src/index.ts +44 -0
- package/src/ink.tsx +1506 -0
- package/src/input-parser.ts +283 -0
- package/src/instances.ts +9 -0
- package/src/is-in-ci.ts +7 -0
- package/src/kitty-keyboard.ts +57 -0
- package/src/log-update.ts +370 -0
- package/src/measure-element.ts +62 -0
- package/src/measure-text.ts +31 -0
- package/src/output.ts +308 -0
- package/src/parse-keypress.ts +516 -0
- package/src/parse-stack-line.ts +139 -0
- package/src/patch-console.ts +62 -0
- package/src/quick-lru.ts +85 -0
- package/src/reconciler.ts +451 -0
- package/src/render-background.ts +38 -0
- package/src/render-border.ts +134 -0
- package/src/render-node-to-output.ts +191 -0
- package/src/render-to-string.ts +131 -0
- package/src/render.ts +276 -0
- package/src/renderer.ts +73 -0
- package/src/sanitize-ansi.ts +33 -0
- package/src/signal-exit.ts +107 -0
- package/src/squash-text-nodes.ts +40 -0
- package/src/stream.ts +30 -0
- package/src/styles.ts +748 -0
- package/src/terminal-size.ts +57 -0
- package/src/throttle.ts +73 -0
- package/src/types.ts +15 -0
- package/src/utils.ts +40 -0
- package/src/wrap-text.ts +50 -0
- package/src/write-synchronized.ts +9 -0
- package/src/yoga/config.ts +57 -0
- package/src/yoga/core/absoluteLayout.ts +626 -0
- package/src/yoga/core/baseline.ts +66 -0
- package/src/yoga/core/cache.ts +136 -0
- package/src/yoga/core/calculateLayout.ts +2920 -0
- package/src/yoga/core/config.ts +104 -0
- package/src/yoga/core/flexLine.ts +177 -0
- package/src/yoga/core/helpers.ts +293 -0
- package/src/yoga/core/layoutResults.ts +167 -0
- package/src/yoga/core/node.ts +611 -0
- package/src/yoga/core/numeric.ts +44 -0
- package/src/yoga/core/pixelGrid.ts +151 -0
- package/src/yoga/core/style.ts +887 -0
- package/src/yoga/core/types.ts +224 -0
- package/src/yoga/generated/YGEnums.ts +263 -0
- package/src/yoga/index.ts +19 -0
- package/src/yoga/node.ts +1140 -0
- package/dist/index.d.mts +0 -2379
- package/dist/index.mjs +0 -10072
package/dist/yoga.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as Unit, S as PositionType, T as constants, _ as Justify, a as Config, b as NodeType, c as Dimension, d as Edge, f as Errata, g as Gutter, h as GridTrackType, i as Node, l as Direction, m as FlexDirection, n as DirtiedFunction, o as Align, p as ExperimentalFeature, r as MeasureFunction, s as BoxSizing, t as Yoga, u as Display, v as LogLevel, w as Wrap, x as Overflow, y as MeasureMode } from "./index-DDVME65c.js";
|
|
2
|
+
export { Align, BoxSizing, type Config, Dimension, Direction, type DirtiedFunction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, GridTrackType, Gutter, Justify, LogLevel, type MeasureFunction, MeasureMode, type Node, NodeType, Overflow, PositionType, Unit, Wrap, Yoga, constants };
|
package/dist/yoga.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as Overflow, a as Direction, b as Wrap, c as Errata, d as GridTrackType, f as Gutter, g as NodeType, h as MeasureMode, i as Dimension, l as ExperimentalFeature, m as LogLevel, n as Align, o as Display, p as Justify, r as BoxSizing, s as Edge, t as Yoga, u as FlexDirection, v as PositionType, x as constants, y as Unit } from "./yoga-5jKhYCJC.js";
|
|
2
|
+
export { Align, BoxSizing, Dimension, Direction, Display, Edge, Errata, ExperimentalFeature, FlexDirection, GridTrackType, Gutter, Justify, LogLevel, MeasureMode, NodeType, Overflow, PositionType, Unit, Wrap, Yoga, constants };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alchemy.run/sigil",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.2",
|
|
4
4
|
"description": "React for CLIs. A self-contained fork of Ink with an integrated TypeScript Yoga layout engine.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -27,13 +27,36 @@
|
|
|
27
27
|
"url": "alchemy-run/sigil"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
+
"src",
|
|
30
31
|
"dist",
|
|
31
32
|
"THIRD_PARTY_NOTICES.md"
|
|
32
33
|
],
|
|
33
34
|
"type": "module",
|
|
35
|
+
"imports": {
|
|
36
|
+
"#/": "./src"
|
|
37
|
+
},
|
|
34
38
|
"exports": {
|
|
35
|
-
"
|
|
36
|
-
|
|
39
|
+
".": {
|
|
40
|
+
"typescript": "./src/index.ts",
|
|
41
|
+
"bun": "./src/index.ts",
|
|
42
|
+
"import": "./dist/index.js",
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"default": "./dist/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./ansi": {
|
|
47
|
+
"typescript": "./src/ansi/index.ts",
|
|
48
|
+
"bun": "./src/ansi/index.ts",
|
|
49
|
+
"import": "./dist/ansi.js",
|
|
50
|
+
"types": "./dist/ansi.d.ts",
|
|
51
|
+
"default": "./dist/ansi.js"
|
|
52
|
+
},
|
|
53
|
+
"./yoga": {
|
|
54
|
+
"typescript": "./src/yoga/index.ts",
|
|
55
|
+
"bun": "./src/yoga/index.ts",
|
|
56
|
+
"import": "./dist/yoga.js",
|
|
57
|
+
"types": "./dist/yoga.d.ts",
|
|
58
|
+
"default": "./dist/yoga.js"
|
|
59
|
+
}
|
|
37
60
|
},
|
|
38
61
|
"publishConfig": {
|
|
39
62
|
"access": "public"
|
|
@@ -44,20 +67,19 @@
|
|
|
44
67
|
},
|
|
45
68
|
"devDependencies": {
|
|
46
69
|
"@types/node": "^25.5.2",
|
|
47
|
-
"@types/react": "^19.2.
|
|
70
|
+
"@types/react": "^19.2.18",
|
|
48
71
|
"@types/react-reconciler": "^0.33.0",
|
|
49
72
|
"@types/scheduler": "^0.26.0",
|
|
50
73
|
"@types/ws": "^8.18.1",
|
|
51
|
-
"
|
|
52
|
-
"node-pty": "^1.2.0-beta.12",
|
|
53
|
-
"react": "^19.2.4",
|
|
74
|
+
"react": "^19.2.8",
|
|
54
75
|
"react-devtools-core": "^7.0.1",
|
|
55
|
-
"react-router": "^
|
|
76
|
+
"react-router": "^8.3.0",
|
|
56
77
|
"tsx": "^4.21.0",
|
|
57
78
|
"typescript": "^7.0.2",
|
|
58
79
|
"vite-plus": "^0.2.9",
|
|
59
|
-
"vitest": "4.1.
|
|
60
|
-
"ws": "^8.20.0"
|
|
80
|
+
"vitest": "4.1.11",
|
|
81
|
+
"ws": "^8.20.0",
|
|
82
|
+
"zigpty": "^0.2.1"
|
|
61
83
|
},
|
|
62
84
|
"peerDependencies": {
|
|
63
85
|
"@types/react": ">=19.2.0",
|
|
@@ -87,12 +109,10 @@
|
|
|
87
109
|
"format": "vp fmt .",
|
|
88
110
|
"check": "vp check",
|
|
89
111
|
"typecheck": "tsc --noEmit",
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"fuzz": "
|
|
95
|
-
"fuzz:differential": "vp run build:reference && NODE_NO_WARNINGS=1 node --import=tsx test/yoga/fuzz/differential.ts",
|
|
96
|
-
"fuzz:loop": "vp run build:reference && NODE_NO_WARNINGS=1 node --import=tsx test/yoga/fuzz/loop.ts"
|
|
112
|
+
"inspect": "vpx react-devtools",
|
|
113
|
+
"build:reference": "tsx scripts/build-reference.ts",
|
|
114
|
+
"fuzz": "tsx test/yoga/fuzz/typescript.ts",
|
|
115
|
+
"fuzz:differential": "vpr build:reference && tsx test/yoga/fuzz/differential.ts",
|
|
116
|
+
"fuzz:loop": "vpr build:reference && tsx test/yoga/fuzz/loop.ts"
|
|
97
117
|
}
|
|
98
118
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Terminal string styling with a chalk-compatible API for the styles Ink uses.
|
|
2
|
+
// Ported from `chalk` (MIT, Sindre Sorhus) without the chaining builder —
|
|
3
|
+
// Ink only ever applies one style per call.
|
|
4
|
+
import { ESC } from "./escapes.ts";
|
|
5
|
+
import {
|
|
6
|
+
background,
|
|
7
|
+
backgroundColorNames,
|
|
8
|
+
foreground,
|
|
9
|
+
foregroundColorNames,
|
|
10
|
+
hexToRgb,
|
|
11
|
+
modifierNames,
|
|
12
|
+
rgbToAnsi,
|
|
13
|
+
rgbToAnsi256,
|
|
14
|
+
styles,
|
|
15
|
+
type BackgroundColorName,
|
|
16
|
+
type ForegroundColorName,
|
|
17
|
+
type ModifierName,
|
|
18
|
+
} from "./sgr.ts";
|
|
19
|
+
import {
|
|
20
|
+
supportsColor as supportsColorDetection,
|
|
21
|
+
type ColorInfo,
|
|
22
|
+
type ColorSupportLevel,
|
|
23
|
+
} from "./supports-color.ts";
|
|
24
|
+
|
|
25
|
+
export type { BackgroundColorName, ForegroundColorName, ModifierName };
|
|
26
|
+
|
|
27
|
+
export type StyleFunction = (text: string) => string;
|
|
28
|
+
|
|
29
|
+
const { stdout: stdoutColor } = supportsColorDetection;
|
|
30
|
+
|
|
31
|
+
// `level` → color model for hex/rgb downsampling
|
|
32
|
+
const levelMapping = ["ansi", "ansi", "ansi256", "ansi16m"] as const;
|
|
33
|
+
type ColorModel = (typeof levelMapping)[number];
|
|
34
|
+
|
|
35
|
+
const state = {
|
|
36
|
+
level: stdoutColor ? stdoutColor.level : 0,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Replace every occurrence of `substring` with `substring + replacer`,
|
|
40
|
+
// re-opening a style after a nested close code would have ended it.
|
|
41
|
+
const stringReplaceAll = (string: string, substring: string, replacer: string): string => {
|
|
42
|
+
let index = string.indexOf(substring);
|
|
43
|
+
if (index === -1) {
|
|
44
|
+
return string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const substringLength = substring.length;
|
|
48
|
+
let endIndex = 0;
|
|
49
|
+
let returnValue = "";
|
|
50
|
+
do {
|
|
51
|
+
returnValue += string.slice(endIndex, index) + substring + replacer;
|
|
52
|
+
endIndex = index + substringLength;
|
|
53
|
+
index = string.indexOf(substring, endIndex);
|
|
54
|
+
} while (index !== -1);
|
|
55
|
+
|
|
56
|
+
returnValue += string.slice(endIndex);
|
|
57
|
+
return returnValue;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Close the style before every line break and reopen it after, to prevent
|
|
61
|
+
// background bleed across lines (https://github.com/chalk/chalk/pull/92).
|
|
62
|
+
const stringEncaseCRLFWithFirstIndex = (
|
|
63
|
+
string: string,
|
|
64
|
+
prefix: string,
|
|
65
|
+
postfix: string,
|
|
66
|
+
index: number,
|
|
67
|
+
): string => {
|
|
68
|
+
let endIndex = 0;
|
|
69
|
+
let returnValue = "";
|
|
70
|
+
do {
|
|
71
|
+
const gotCR = string[index - 1] === "\r";
|
|
72
|
+
returnValue +=
|
|
73
|
+
string.slice(endIndex, gotCR ? index - 1 : index) +
|
|
74
|
+
prefix +
|
|
75
|
+
(gotCR ? "\r\n" : "\n") +
|
|
76
|
+
postfix;
|
|
77
|
+
endIndex = index + 1;
|
|
78
|
+
index = string.indexOf("\n", endIndex);
|
|
79
|
+
} while (index !== -1);
|
|
80
|
+
|
|
81
|
+
returnValue += string.slice(endIndex);
|
|
82
|
+
return returnValue;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const applyStyle = (open: string, close: string, text: string): string => {
|
|
86
|
+
if (state.level <= 0 || !text) {
|
|
87
|
+
return text;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let string = text;
|
|
91
|
+
|
|
92
|
+
if (string.includes(ESC)) {
|
|
93
|
+
// Re-open the style wherever the text already contains its close code,
|
|
94
|
+
// otherwise only the part before that code would stay styled.
|
|
95
|
+
string = stringReplaceAll(string, close, open);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const lfIndex = string.indexOf("\n");
|
|
99
|
+
if (lfIndex !== -1) {
|
|
100
|
+
string = stringEncaseCRLFWithFirstIndex(string, close, open, lfIndex);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return open + string + close;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const styleFunction = (open: string, close: string): StyleFunction => {
|
|
107
|
+
return (text) => applyStyle(open, close, text);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const foregroundOpen = (model: ColorModel, args: number[] | [string]): string => {
|
|
111
|
+
return colorOpen(model, args, foreground, rgbToAnsi256, rgbToAnsi);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const backgroundOpen = (model: ColorModel, args: number[] | [string]): string => {
|
|
115
|
+
return colorOpen(model, args, background, rgbToAnsi256, rgbToAnsi);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const colorOpen = (
|
|
119
|
+
model: ColorModel,
|
|
120
|
+
args: number[] | [string],
|
|
121
|
+
space: typeof foreground,
|
|
122
|
+
toAnsi256: typeof rgbToAnsi256,
|
|
123
|
+
toAnsi: typeof rgbToAnsi,
|
|
124
|
+
): string => {
|
|
125
|
+
const rgb = (typeof args[0] === "string" ? hexToRgb(args[0]) : args) as [number, number, number];
|
|
126
|
+
|
|
127
|
+
if (model === "ansi16m") {
|
|
128
|
+
return space.ansi16m(...rgb);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (model === "ansi256") {
|
|
132
|
+
return space.ansi256(toAnsi256(...rgb));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return space.ansi(toAnsi(...rgb));
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
type NamedStyles = Record<ModifierName | ForegroundColorName | BackgroundColorName, StyleFunction>;
|
|
139
|
+
|
|
140
|
+
export type Chalk = NamedStyles & {
|
|
141
|
+
level: ColorSupportLevel;
|
|
142
|
+
hex: (color: string) => StyleFunction;
|
|
143
|
+
bgHex: (color: string) => StyleFunction;
|
|
144
|
+
rgb: (red: number, green: number, blue: number) => StyleFunction;
|
|
145
|
+
bgRgb: (red: number, green: number, blue: number) => StyleFunction;
|
|
146
|
+
ansi256: (code: number) => StyleFunction;
|
|
147
|
+
bgAnsi256: (code: number) => StyleFunction;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const named = {} as NamedStyles;
|
|
151
|
+
|
|
152
|
+
for (const name of [...modifierNames, ...foregroundColorNames, ...backgroundColorNames]) {
|
|
153
|
+
named[name] = styleFunction(styles[name].open, styles[name].close);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const chalk: Chalk = {
|
|
157
|
+
...named,
|
|
158
|
+
|
|
159
|
+
get level(): ColorSupportLevel {
|
|
160
|
+
return state.level;
|
|
161
|
+
},
|
|
162
|
+
set level(level: ColorSupportLevel) {
|
|
163
|
+
state.level = level;
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
hex: (color) =>
|
|
167
|
+
styleFunction(foregroundOpen(levelMapping[state.level], [color]), foreground.close),
|
|
168
|
+
bgHex: (color) =>
|
|
169
|
+
styleFunction(backgroundOpen(levelMapping[state.level], [color]), background.close),
|
|
170
|
+
rgb: (red, green, blue) =>
|
|
171
|
+
styleFunction(foregroundOpen(levelMapping[state.level], [red, green, blue]), foreground.close),
|
|
172
|
+
bgRgb: (red, green, blue) =>
|
|
173
|
+
styleFunction(backgroundOpen(levelMapping[state.level], [red, green, blue]), background.close),
|
|
174
|
+
// Like chalk, 256-color styles always emit 8-bit codes (level only gates level 0).
|
|
175
|
+
ansi256: (code) => styleFunction(foreground.ansi256(code), foreground.close),
|
|
176
|
+
bgAnsi256: (code) => styleFunction(background.ansi256(code), background.close),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const supportsColor: ColorInfo = stdoutColor;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Derived from `cli-cursor` + `restore-cursor` (MIT, Sindre Sorhus),
|
|
2
|
+
// collapsed into one module. Restores the cursor on process exit once
|
|
3
|
+
// `hide()` has been called, like `restore-cursor` did.
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
import { signalExit } from "../signal-exit.ts";
|
|
7
|
+
import { cursorHide, cursorShow } from "./escapes.ts";
|
|
8
|
+
|
|
9
|
+
type CursorStream = {
|
|
10
|
+
isTTY?: boolean;
|
|
11
|
+
write: (data: string) => unknown;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let restoreRegistered = false;
|
|
15
|
+
|
|
16
|
+
const registerRestore = (): void => {
|
|
17
|
+
if (restoreRegistered) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
restoreRegistered = true;
|
|
22
|
+
|
|
23
|
+
signalExit(
|
|
24
|
+
() => {
|
|
25
|
+
process.stderr.write(cursorShow);
|
|
26
|
+
},
|
|
27
|
+
{ alwaysLast: true },
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const cliCursor = {
|
|
32
|
+
show(writableStream: CursorStream = process.stderr): void {
|
|
33
|
+
if (!writableStream.isTTY) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
writableStream.write(cursorShow);
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
hide(writableStream: CursorStream = process.stderr): void {
|
|
41
|
+
if (!writableStream.isTTY) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
registerRestore();
|
|
46
|
+
writableStream.write(cursorHide);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// East Asian Width lookup, plus string-width's full-width test.
|
|
2
|
+
// Ported from `get-east-asian-width` and `is-fullwidth-code-point`
|
|
3
|
+
// (MIT, Sindre Sorhus). The range tables are generated from Unicode data.
|
|
4
|
+
|
|
5
|
+
export type EastAsianWidthType =
|
|
6
|
+
| "ambiguous"
|
|
7
|
+
| "fullwidth"
|
|
8
|
+
| "halfwidth"
|
|
9
|
+
| "narrow"
|
|
10
|
+
| "neutral"
|
|
11
|
+
| "wide";
|
|
12
|
+
|
|
13
|
+
// Binary search on a sorted flat array of inclusive [start, end] pairs.
|
|
14
|
+
const isInRange = (ranges: readonly number[], codePoint: number): boolean => {
|
|
15
|
+
let low = 0;
|
|
16
|
+
let high = Math.floor(ranges.length / 2) - 1;
|
|
17
|
+
while (low <= high) {
|
|
18
|
+
const mid = Math.floor((low + high) / 2);
|
|
19
|
+
const i = mid * 2;
|
|
20
|
+
if (codePoint < ranges[i]!) {
|
|
21
|
+
high = mid - 1;
|
|
22
|
+
} else if (codePoint > ranges[i + 1]!) {
|
|
23
|
+
low = mid + 1;
|
|
24
|
+
} else {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const ambiguousMinimalCodePoint = 161;
|
|
33
|
+
export const ambiguousMaximumCodePoint = 1114109;
|
|
34
|
+
export const ambiguousRanges: readonly number[] = [
|
|
35
|
+
161, 161, 164, 164, 167, 168, 170, 170, 173, 174, 176, 180, 182, 186, 188, 191, 198, 198, 208,
|
|
36
|
+
208, 215, 216, 222, 225, 230, 230, 232, 234, 236, 237, 240, 240, 242, 243, 247, 250, 252, 252,
|
|
37
|
+
254, 254, 257, 257, 273, 273, 275, 275, 283, 283, 294, 295, 299, 299, 305, 307, 312, 312, 319,
|
|
38
|
+
322, 324, 324, 328, 331, 333, 333, 338, 339, 358, 359, 363, 363, 462, 462, 464, 464, 466, 466,
|
|
39
|
+
468, 468, 470, 470, 472, 472, 474, 474, 476, 476, 593, 593, 609, 609, 708, 708, 711, 711, 713,
|
|
40
|
+
715, 717, 717, 720, 720, 728, 731, 733, 733, 735, 735, 768, 879, 913, 929, 931, 937, 945, 961,
|
|
41
|
+
963, 969, 1025, 1025, 1040, 1103, 1105, 1105, 8208, 8208, 8211, 8214, 8216, 8217, 8220, 8221,
|
|
42
|
+
8224, 8226, 8228, 8231, 8240, 8240, 8242, 8243, 8245, 8245, 8251, 8251, 8254, 8254, 8308, 8308,
|
|
43
|
+
8319, 8319, 8321, 8324, 8364, 8364, 8451, 8451, 8453, 8453, 8457, 8457, 8467, 8467, 8470, 8470,
|
|
44
|
+
8481, 8482, 8486, 8486, 8491, 8491, 8531, 8532, 8539, 8542, 8544, 8555, 8560, 8569, 8585, 8585,
|
|
45
|
+
8592, 8601, 8632, 8633, 8658, 8658, 8660, 8660, 8679, 8679, 8704, 8704, 8706, 8707, 8711, 8712,
|
|
46
|
+
8715, 8715, 8719, 8719, 8721, 8721, 8725, 8725, 8730, 8730, 8733, 8736, 8739, 8739, 8741, 8741,
|
|
47
|
+
8743, 8748, 8750, 8750, 8756, 8759, 8764, 8765, 8776, 8776, 8780, 8780, 8786, 8786, 8800, 8801,
|
|
48
|
+
8804, 8807, 8810, 8811, 8814, 8815, 8834, 8835, 8838, 8839, 8853, 8853, 8857, 8857, 8869, 8869,
|
|
49
|
+
8895, 8895, 8978, 8978, 9312, 9449, 9451, 9547, 9552, 9587, 9600, 9615, 9618, 9621, 9632, 9633,
|
|
50
|
+
9635, 9641, 9650, 9651, 9654, 9655, 9660, 9661, 9664, 9665, 9670, 9672, 9675, 9675, 9678, 9681,
|
|
51
|
+
9698, 9701, 9711, 9711, 9733, 9734, 9737, 9737, 9742, 9743, 9756, 9756, 9758, 9758, 9792, 9792,
|
|
52
|
+
9794, 9794, 9824, 9825, 9827, 9829, 9831, 9834, 9836, 9837, 9839, 9839, 9886, 9887, 9919, 9919,
|
|
53
|
+
9926, 9933, 9935, 9939, 9941, 9953, 9955, 9955, 9960, 9961, 9963, 9969, 9972, 9972, 9974, 9977,
|
|
54
|
+
9979, 9980, 9982, 9983, 10045, 10045, 10102, 10111, 11094, 11097, 12872, 12879, 57344, 63743,
|
|
55
|
+
65024, 65039, 65533, 65533, 127232, 127242, 127248, 127277, 127280, 127337, 127344, 127373,
|
|
56
|
+
127375, 127376, 127387, 127404, 917760, 917999, 983040, 1048573, 1048576, 1114109,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export const fullwidthMinimalCodePoint = 12288;
|
|
60
|
+
export const fullwidthMaximumCodePoint = 65510;
|
|
61
|
+
export const fullwidthRanges: readonly number[] = [12288, 12288, 65281, 65376, 65504, 65510];
|
|
62
|
+
|
|
63
|
+
export const halfwidthMinimalCodePoint = 8361;
|
|
64
|
+
export const halfwidthMaximumCodePoint = 65518;
|
|
65
|
+
export const halfwidthRanges: readonly number[] = [
|
|
66
|
+
8361, 8361, 65377, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65512, 65518,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
export const narrowMinimalCodePoint = 32;
|
|
70
|
+
export const narrowMaximumCodePoint = 10630;
|
|
71
|
+
export const narrowRanges: readonly number[] = [
|
|
72
|
+
32, 126, 162, 163, 165, 166, 172, 172, 175, 175, 10214, 10221, 10629, 10630,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
export const wideMinimalCodePoint = 4352;
|
|
76
|
+
export const wideMaximumCodePoint = 262141;
|
|
77
|
+
export const wideRanges: readonly number[] = [
|
|
78
|
+
4352, 4447, 8986, 8987, 9001, 9002, 9193, 9196, 9200, 9200, 9203, 9203, 9725, 9726, 9748, 9749,
|
|
79
|
+
9776, 9783, 9800, 9811, 9855, 9855, 9866, 9871, 9875, 9875, 9889, 9889, 9898, 9899, 9917, 9918,
|
|
80
|
+
9924, 9925, 9934, 9934, 9940, 9940, 9962, 9962, 9970, 9971, 9973, 9973, 9978, 9978, 9981, 9981,
|
|
81
|
+
9989, 9989, 9994, 9995, 10024, 10024, 10060, 10060, 10062, 10062, 10067, 10069, 10071, 10071,
|
|
82
|
+
10133, 10135, 10160, 10160, 10175, 10175, 11035, 11036, 11088, 11088, 11093, 11093, 11904, 11929,
|
|
83
|
+
11931, 12019, 12032, 12245, 12272, 12287, 12289, 12350, 12353, 12438, 12441, 12543, 12549, 12591,
|
|
84
|
+
12593, 12686, 12688, 12773, 12783, 12830, 12832, 12871, 12880, 42124, 42128, 42182, 43360, 43388,
|
|
85
|
+
44032, 55203, 63744, 64255, 65040, 65049, 65072, 65106, 65108, 65126, 65128, 65131, 94176, 94180,
|
|
86
|
+
94192, 94198, 94208, 101589, 101631, 101662, 101760, 101874, 110576, 110579, 110581, 110587,
|
|
87
|
+
110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951,
|
|
88
|
+
110960, 111355, 119552, 119638, 119648, 119670, 126980, 126980, 127183, 127183, 127374, 127374,
|
|
89
|
+
127377, 127386, 127488, 127490, 127504, 127547, 127552, 127560, 127568, 127569, 127584, 127589,
|
|
90
|
+
127744, 127776, 127789, 127797, 127799, 127868, 127870, 127891, 127904, 127946, 127951, 127955,
|
|
91
|
+
127968, 127984, 127988, 127988, 127992, 128062, 128064, 128064, 128066, 128252, 128255, 128317,
|
|
92
|
+
128331, 128334, 128336, 128359, 128378, 128378, 128405, 128406, 128420, 128420, 128507, 128591,
|
|
93
|
+
128640, 128709, 128716, 128716, 128720, 128722, 128725, 128728, 128732, 128735, 128747, 128748,
|
|
94
|
+
128756, 128764, 128992, 129003, 129008, 129008, 129292, 129338, 129340, 129349, 129351, 129535,
|
|
95
|
+
129648, 129660, 129664, 129674, 129678, 129734, 129736, 129736, 129741, 129756, 129759, 129770,
|
|
96
|
+
129775, 129784, 131072, 196605, 196608, 262141,
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const commonCjkCodePoint = 0x4e_00;
|
|
100
|
+
const [wideFastPathStart, wideFastPathEnd] = /* #__PURE__ */ findWideFastPathRange(wideRanges);
|
|
101
|
+
|
|
102
|
+
// Use a hot-path range so common `isWide` calls can skip binary search.
|
|
103
|
+
// The range containing U+4E00 covers common CJK ideographs;
|
|
104
|
+
// fallback to the largest range for resilience to Unicode table changes.
|
|
105
|
+
function findWideFastPathRange(ranges: readonly number[]): [number, number] {
|
|
106
|
+
let fastPathStart = ranges[0]!;
|
|
107
|
+
let fastPathEnd = ranges[1]!;
|
|
108
|
+
|
|
109
|
+
for (let index = 0; index < ranges.length; index += 2) {
|
|
110
|
+
const start = ranges[index]!;
|
|
111
|
+
const end = ranges[index + 1]!;
|
|
112
|
+
|
|
113
|
+
if (commonCjkCodePoint >= start && commonCjkCodePoint <= end) {
|
|
114
|
+
return [start, end];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (end - start > fastPathEnd - fastPathStart) {
|
|
118
|
+
fastPathStart = start;
|
|
119
|
+
fastPathEnd = end;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return [fastPathStart, fastPathEnd];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const isAmbiguous = (codePoint: number): boolean => {
|
|
127
|
+
if (codePoint < ambiguousMinimalCodePoint || codePoint > ambiguousMaximumCodePoint) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return isInRange(ambiguousRanges, codePoint);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const isFullWidth = (codePoint: number): boolean => {
|
|
135
|
+
if (codePoint < fullwidthMinimalCodePoint || codePoint > fullwidthMaximumCodePoint) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return isInRange(fullwidthRanges, codePoint);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const isHalfWidth = (codePoint: number): boolean => {
|
|
143
|
+
if (codePoint < halfwidthMinimalCodePoint || codePoint > halfwidthMaximumCodePoint) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return isInRange(halfwidthRanges, codePoint);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const isNarrow = (codePoint: number): boolean => {
|
|
151
|
+
if (codePoint < narrowMinimalCodePoint || codePoint > narrowMaximumCodePoint) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return isInRange(narrowRanges, codePoint);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const isWide = (codePoint: number): boolean => {
|
|
159
|
+
if (codePoint >= wideFastPathStart && codePoint <= wideFastPathEnd) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (codePoint < wideMinimalCodePoint || codePoint > wideMaximumCodePoint) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return isInRange(wideRanges, codePoint);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export function getCategory(codePoint: number): EastAsianWidthType {
|
|
171
|
+
if (isAmbiguous(codePoint)) {
|
|
172
|
+
return "ambiguous";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (isFullWidth(codePoint)) {
|
|
176
|
+
return "fullwidth";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (isHalfWidth(codePoint)) {
|
|
180
|
+
return "halfwidth";
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isNarrow(codePoint)) {
|
|
184
|
+
return "narrow";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isWide(codePoint)) {
|
|
188
|
+
return "wide";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return "neutral";
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function eastAsianWidthType(codePoint: number): EastAsianWidthType {
|
|
195
|
+
return getCategory(codePoint);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function eastAsianWidth(
|
|
199
|
+
codePoint: number,
|
|
200
|
+
{ ambiguousAsWide = false }: { ambiguousAsWide?: boolean } = {},
|
|
201
|
+
): 1 | 2 {
|
|
202
|
+
if (isFullWidth(codePoint) || isWide(codePoint) || (ambiguousAsWide && isAmbiguous(codePoint))) {
|
|
203
|
+
return 2;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return 1;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function isFullwidthCodePoint(codePoint: number): boolean {
|
|
210
|
+
if (!Number.isInteger(codePoint)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return isFullWidth(codePoint) || isWide(codePoint);
|
|
215
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
// Derived from `ansi-escapes` (MIT, Sindre Sorhus), reduced to the escapes Ink uses.
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
|
|
5
|
+
// Control characters and sequence introducers. This module is the single
|
|
6
|
+
// home for escape sequences: everything else imports these instead of
|
|
7
|
+
// spelling out `\u001B[...` inline.
|
|
8
|
+
export const ESC = "\u001B";
|
|
9
|
+
export const BEL = "\u0007";
|
|
10
|
+
export const DEL = "\u007F";
|
|
11
|
+
/** Control Sequence Introducer. */
|
|
12
|
+
export const CSI = `${ESC}[`;
|
|
13
|
+
/** Operating System Command. */
|
|
14
|
+
export const OSC = `${ESC}]`;
|
|
15
|
+
/** String Terminator. */
|
|
16
|
+
export const ST = `${ESC}\\`;
|
|
17
|
+
/** Single-byte C1 forms of CSI and ST. */
|
|
18
|
+
export const C1_CSI = "\u009B";
|
|
19
|
+
export const C1_ST = "\u009C";
|
|
20
|
+
|
|
21
|
+
const sep = ";";
|
|
22
|
+
|
|
23
|
+
export const cursorTo = (x: number, y?: number): string => {
|
|
24
|
+
if (typeof y !== "number") {
|
|
25
|
+
return CSI + (x + 1) + "G";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return CSI + (y + 1) + sep + (x + 1) + "H";
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const cursorUp = (count = 1): string => CSI + count + "A";
|
|
32
|
+
export const cursorDown = (count = 1): string => CSI + count + "B";
|
|
33
|
+
export const cursorLeft = CSI + "G";
|
|
34
|
+
export const cursorNextLine = CSI + "E";
|
|
35
|
+
|
|
36
|
+
export const eraseEndLine = CSI + "K";
|
|
37
|
+
export const eraseLine = CSI + "2K";
|
|
38
|
+
export const eraseScreen = CSI + "2J";
|
|
39
|
+
|
|
40
|
+
export const eraseLines = (count: number): string => {
|
|
41
|
+
let clear = "";
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < count; i++) {
|
|
44
|
+
clear += eraseLine + (i < count - 1 ? cursorUp() : "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (count) {
|
|
48
|
+
clear += cursorLeft;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return clear;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Windows 10 builds before 10586 don't support the `3J` escape.
|
|
55
|
+
const isOldWindows = (): boolean => {
|
|
56
|
+
if (process.platform !== "win32") {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parts = os.release().split(".");
|
|
61
|
+
const major = Number(parts[0]);
|
|
62
|
+
const build = Number(parts[2] ?? 0);
|
|
63
|
+
|
|
64
|
+
if (major < 10) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return major === 10 && build < 10_586;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const clearTerminal = isOldWindows()
|
|
72
|
+
? `${eraseScreen}${CSI}0f`
|
|
73
|
+
: // 1. Erases the screen (only done in case `2` is not supported)
|
|
74
|
+
// 2. Erases the whole screen including scrollback buffer
|
|
75
|
+
// 3. Moves cursor to the top-left position
|
|
76
|
+
`${eraseScreen}${CSI}3J${CSI}H`;
|
|
77
|
+
|
|
78
|
+
export const enterAlternativeScreen = CSI + "?1049h";
|
|
79
|
+
export const exitAlternativeScreen = CSI + "?1049l";
|
|
80
|
+
|
|
81
|
+
export const cursorShow = CSI + "?25h";
|
|
82
|
+
export const cursorHide = CSI + "?25l";
|
|
83
|
+
|
|
84
|
+
export const enableBracketedPaste = CSI + "?2004h";
|
|
85
|
+
export const disableBracketedPaste = CSI + "?2004l";
|
|
86
|
+
|
|
87
|
+
// Markers the terminal wraps around pasted input while bracketed paste is on.
|
|
88
|
+
export const pasteStart = CSI + "200~";
|
|
89
|
+
export const pasteEnd = CSI + "201~";
|
|
90
|
+
|
|
91
|
+
// Synchronized update mode (DEC mode 2026): begin / end synchronized update.
|
|
92
|
+
export const bsu = CSI + "?2026h";
|
|
93
|
+
export const esu = CSI + "?2026l";
|
|
94
|
+
|
|
95
|
+
// Kitty keyboard protocol: query support, push a flag set, pop it off.
|
|
96
|
+
export const kittyQuery = CSI + "?u";
|
|
97
|
+
export const pushKittyKeyboard = (flags: number): string => `${CSI}>${flags}u`;
|
|
98
|
+
export const popKittyKeyboard = CSI + "<u";
|
|
99
|
+
|
|
100
|
+
export const link = (text: string, url: string): string =>
|
|
101
|
+
[OSC, "8", sep, sep, url, BEL, text, OSC, "8", sep, sep, BEL].join("");
|
|
102
|
+
|
|
103
|
+
export const ansiEscapes = {
|
|
104
|
+
cursorTo,
|
|
105
|
+
cursorUp,
|
|
106
|
+
cursorDown,
|
|
107
|
+
cursorLeft,
|
|
108
|
+
cursorNextLine,
|
|
109
|
+
eraseEndLine,
|
|
110
|
+
eraseLine,
|
|
111
|
+
eraseScreen,
|
|
112
|
+
eraseLines,
|
|
113
|
+
clearTerminal,
|
|
114
|
+
enterAlternativeScreen,
|
|
115
|
+
exitAlternativeScreen,
|
|
116
|
+
cursorShow,
|
|
117
|
+
cursorHide,
|
|
118
|
+
enableBracketedPaste,
|
|
119
|
+
disableBracketedPaste,
|
|
120
|
+
pasteStart,
|
|
121
|
+
pasteEnd,
|
|
122
|
+
bsu,
|
|
123
|
+
esu,
|
|
124
|
+
kittyQuery,
|
|
125
|
+
pushKittyKeyboard,
|
|
126
|
+
popKittyKeyboard,
|
|
127
|
+
link,
|
|
128
|
+
};
|