@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/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.5",
|
|
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",
|
|
@@ -33,29 +33,48 @@
|
|
|
33
33
|
],
|
|
34
34
|
"type": "module",
|
|
35
35
|
"imports": {
|
|
36
|
-
"
|
|
36
|
+
"#/*": "./src/*"
|
|
37
37
|
},
|
|
38
38
|
"exports": {
|
|
39
39
|
".": {
|
|
40
|
-
"typescript": "./src/index.ts",
|
|
41
|
-
"bun": "./src/index.ts",
|
|
42
40
|
"import": "./dist/index.js",
|
|
43
41
|
"types": "./dist/index.d.ts",
|
|
44
42
|
"default": "./dist/index.js"
|
|
45
43
|
},
|
|
46
44
|
"./ansi": {
|
|
47
|
-
"typescript": "./src/ansi/index.ts",
|
|
48
|
-
"bun": "./src/ansi/index.ts",
|
|
49
45
|
"import": "./dist/ansi.js",
|
|
50
46
|
"types": "./dist/ansi.d.ts",
|
|
51
47
|
"default": "./dist/ansi.js"
|
|
52
48
|
},
|
|
53
49
|
"./yoga": {
|
|
54
|
-
"typescript": "./src/yoga/index.ts",
|
|
55
|
-
"bun": "./src/yoga/index.ts",
|
|
56
50
|
"import": "./dist/yoga.js",
|
|
57
51
|
"types": "./dist/yoga.d.ts",
|
|
58
52
|
"default": "./dist/yoga.js"
|
|
53
|
+
},
|
|
54
|
+
"./router": {
|
|
55
|
+
"import": "./dist/router.js",
|
|
56
|
+
"types": "./dist/router.d.ts",
|
|
57
|
+
"default": "./dist/router.js"
|
|
58
|
+
},
|
|
59
|
+
"./capabilities": {
|
|
60
|
+
"import": "./dist/capabilities.js",
|
|
61
|
+
"types": "./dist/capabilities.d.ts",
|
|
62
|
+
"default": "./dist/capabilities.js"
|
|
63
|
+
},
|
|
64
|
+
"./color": {
|
|
65
|
+
"import": "./dist/color.js",
|
|
66
|
+
"types": "./dist/color.d.ts",
|
|
67
|
+
"default": "./dist/color.js"
|
|
68
|
+
},
|
|
69
|
+
"./screen": {
|
|
70
|
+
"import": "./dist/screen.js",
|
|
71
|
+
"types": "./dist/screen.d.ts",
|
|
72
|
+
"default": "./dist/screen.js"
|
|
73
|
+
},
|
|
74
|
+
"./terminal": {
|
|
75
|
+
"import": "./dist/terminal.js",
|
|
76
|
+
"types": "./dist/terminal.d.ts",
|
|
77
|
+
"default": "./dist/terminal.js"
|
|
59
78
|
}
|
|
60
79
|
},
|
|
61
80
|
"publishConfig": {
|
|
@@ -66,26 +85,33 @@
|
|
|
66
85
|
"scheduler": "^0.27.0"
|
|
67
86
|
},
|
|
68
87
|
"devDependencies": {
|
|
88
|
+
"@slopus/ghostty-wasm": "^1.3.6",
|
|
69
89
|
"@types/node": "^25.5.2",
|
|
70
90
|
"@types/react": "^19.2.18",
|
|
71
91
|
"@types/react-reconciler": "^0.33.0",
|
|
72
92
|
"@types/scheduler": "^0.26.0",
|
|
73
|
-
"@
|
|
93
|
+
"@vitest/ui": "^4.1.11",
|
|
94
|
+
"@xterm/addon-clipboard": "^0.2.0",
|
|
95
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
96
|
+
"@xterm/addon-image": "^0.9.0",
|
|
97
|
+
"@xterm/addon-unicode11": "^0.9.0",
|
|
98
|
+
"@xterm/addon-web-links": "^0.12.0",
|
|
99
|
+
"@xterm/addon-webgl": "^0.19.0",
|
|
100
|
+
"@xterm/headless": "^6.0.0",
|
|
101
|
+
"@xterm/xterm": "^6.0.0",
|
|
74
102
|
"react": "^19.2.8",
|
|
75
103
|
"react-devtools-core": "^7.0.1",
|
|
76
|
-
"react-router": "^8.3.0",
|
|
77
104
|
"tsx": "^4.21.0",
|
|
78
105
|
"typescript": "^7.0.2",
|
|
106
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@^0.2.9",
|
|
79
107
|
"vite-plus": "^0.2.9",
|
|
80
108
|
"vitest": "4.1.11",
|
|
81
|
-
"ws": "^8.20.0",
|
|
82
109
|
"zigpty": "^0.2.1"
|
|
83
110
|
},
|
|
84
111
|
"peerDependencies": {
|
|
85
112
|
"@types/react": ">=19.2.0",
|
|
86
113
|
"react": ">=19.2.0",
|
|
87
|
-
"react-devtools-core": ">=6.1.2"
|
|
88
|
-
"ws": ">=8.20.0"
|
|
114
|
+
"react-devtools-core": ">=6.1.2"
|
|
89
115
|
},
|
|
90
116
|
"peerDependenciesMeta": {
|
|
91
117
|
"@types/react": {
|
|
@@ -93,9 +119,6 @@
|
|
|
93
119
|
},
|
|
94
120
|
"react-devtools-core": {
|
|
95
121
|
"optional": true
|
|
96
|
-
},
|
|
97
|
-
"ws": {
|
|
98
|
-
"optional": true
|
|
99
122
|
}
|
|
100
123
|
},
|
|
101
124
|
"engines": {
|
|
@@ -105,12 +128,15 @@
|
|
|
105
128
|
"dev": "vp pack -w",
|
|
106
129
|
"build": "vp pack",
|
|
107
130
|
"test": "vp test",
|
|
131
|
+
"e2e": "vp test test/e2e.test.ts",
|
|
132
|
+
"explorer": "node --import=tsx scripts/explorer.ts",
|
|
108
133
|
"lint": "vp lint",
|
|
109
134
|
"format": "vp fmt .",
|
|
110
135
|
"check": "vp check",
|
|
111
|
-
"typecheck": "tsc --noEmit",
|
|
136
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p test/contracts/tsconfig.json",
|
|
112
137
|
"inspect": "vpx react-devtools",
|
|
113
138
|
"build:reference": "tsx scripts/build-reference.ts",
|
|
139
|
+
"bench:terminal": "tsx benchmark/terminal-core.tsx --check",
|
|
114
140
|
"fuzz": "tsx test/yoga/fuzz/typescript.ts",
|
|
115
141
|
"fuzz:differential": "vpr build:reference && tsx test/yoga/fuzz/differential.ts",
|
|
116
142
|
"fuzz:loop": "vpr build:reference && tsx test/yoga/fuzz/loop.ts"
|
package/src/ansi/chalk.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { isatty } from "node:tty";
|
|
2
|
+
|
|
1
3
|
// Terminal string styling with a chalk-compatible API for the styles Ink uses.
|
|
2
4
|
// Ported from `chalk` (MIT, Sindre Sorhus) without the chaining builder —
|
|
3
5
|
// Ink only ever applies one style per call.
|
|
4
|
-
import { ESC } from "
|
|
6
|
+
import { ESC } from "#/ansi/escapes.ts";
|
|
5
7
|
import {
|
|
6
8
|
background,
|
|
7
9
|
backgroundColorNames,
|
|
@@ -15,18 +17,18 @@ import {
|
|
|
15
17
|
type BackgroundColorName,
|
|
16
18
|
type ForegroundColorName,
|
|
17
19
|
type ModifierName,
|
|
18
|
-
} from "
|
|
20
|
+
} from "#/ansi/sgr.ts";
|
|
19
21
|
import {
|
|
20
|
-
|
|
22
|
+
createSupportsColor,
|
|
21
23
|
type ColorInfo,
|
|
22
24
|
type ColorSupportLevel,
|
|
23
|
-
} from "
|
|
25
|
+
} from "#/capabilities/detect.ts";
|
|
24
26
|
|
|
25
27
|
export type { BackgroundColorName, ForegroundColorName, ModifierName };
|
|
26
28
|
|
|
27
29
|
export type StyleFunction = (text: string) => string;
|
|
28
30
|
|
|
29
|
-
const {
|
|
31
|
+
const stdoutColor = createSupportsColor({ isTTY: isatty(1) });
|
|
30
32
|
|
|
31
33
|
// `level` → color model for hex/rgb downsampling
|
|
32
34
|
const levelMapping = ["ansi", "ansi", "ansi256", "ansi16m"] as const;
|
|
@@ -38,50 +40,6 @@ const state = {
|
|
|
38
40
|
|
|
39
41
|
// Replace every occurrence of `substring` with `substring + replacer`,
|
|
40
42
|
// 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
43
|
const applyStyle = (open: string, close: string, text: string): string => {
|
|
86
44
|
if (state.level <= 0 || !text) {
|
|
87
45
|
return text;
|
|
@@ -92,12 +50,13 @@ const applyStyle = (open: string, close: string, text: string): string => {
|
|
|
92
50
|
if (string.includes(ESC)) {
|
|
93
51
|
// Re-open the style wherever the text already contains its close code,
|
|
94
52
|
// otherwise only the part before that code would stay styled.
|
|
95
|
-
string =
|
|
53
|
+
string = string.replaceAll(close, close + open);
|
|
96
54
|
}
|
|
97
55
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
56
|
+
if (string.includes("\n")) {
|
|
57
|
+
// Close the style before every line break and reopen it after, to prevent
|
|
58
|
+
// background bleed across lines (https://github.com/chalk/chalk/pull/92).
|
|
59
|
+
string = string.replaceAll(/\r?\n/g, `${close}$&${open}`);
|
|
101
60
|
}
|
|
102
61
|
|
|
103
62
|
return open + string + close;
|
package/src/ansi/cursor.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { cursorHide, cursorShow } from "#/ansi/escapes.ts";
|
|
1
2
|
// Derived from `cli-cursor` + `restore-cursor` (MIT, Sindre Sorhus),
|
|
2
3
|
// collapsed into one module. Restores the cursor on process exit once
|
|
3
4
|
// `hide()` has been called, like `restore-cursor` did.
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import { signalExit } from "../signal-exit.ts";
|
|
7
|
-
import { cursorHide, cursorShow } from "./escapes.ts";
|
|
5
|
+
import { signalExit } from "#/signal-exit.ts";
|
|
8
6
|
|
|
9
7
|
type CursorStream = {
|
|
10
8
|
isTTY?: boolean;
|
|
@@ -206,6 +206,50 @@ export function eastAsianWidth(
|
|
|
206
206
|
return 1;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
/**
|
|
210
|
+
Grapheme-cluster width test shared by the tokenizer (slice/clip path): the
|
|
211
|
+
base code point's East Asian Width, plus emoji presentation rules.
|
|
212
|
+
*/
|
|
213
|
+
export function isFullwidthGrapheme(grapheme: string, baseCodePoint: number): boolean {
|
|
214
|
+
if (isFullwidthCodePoint(baseCodePoint)) return true;
|
|
215
|
+
// Variation Selector 16 forces emoji presentation (2 columns wide)
|
|
216
|
+
if (grapheme.includes("\uFE0F")) return true;
|
|
217
|
+
// Regional indicator pairs form flag emoji (2 columns wide)
|
|
218
|
+
if (baseCodePoint >= 0x1f1e6 && baseCodePoint <= 0x1f1ff) return true;
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
Wide code points not covered by string-width's CJKT script regex or emoji
|
|
224
|
+
regex — the fallback bucket of its block parser. A disjoint decomposition of
|
|
225
|
+
the `wide` table above, kept alongside it so the width data lives in one
|
|
226
|
+
module.
|
|
227
|
+
*/
|
|
228
|
+
export const isWideNotCJKTNotEmoji = (x: number): boolean => {
|
|
229
|
+
return (
|
|
230
|
+
x === 0x231b ||
|
|
231
|
+
x === 0x2329 ||
|
|
232
|
+
(x >= 0x2ff0 && x <= 0x2fff) ||
|
|
233
|
+
(x >= 0x3001 && x <= 0x303e) ||
|
|
234
|
+
(x >= 0x3099 && x <= 0x30ff) ||
|
|
235
|
+
(x >= 0x3105 && x <= 0x312f) ||
|
|
236
|
+
(x >= 0x3131 && x <= 0x318e) ||
|
|
237
|
+
(x >= 0x3190 && x <= 0x31e3) ||
|
|
238
|
+
(x >= 0x31ef && x <= 0x321e) ||
|
|
239
|
+
(x >= 0x3220 && x <= 0x3247) ||
|
|
240
|
+
(x >= 0x3250 && x <= 0x4dbf) ||
|
|
241
|
+
(x >= 0xfe10 && x <= 0xfe19) ||
|
|
242
|
+
(x >= 0xfe30 && x <= 0xfe52) ||
|
|
243
|
+
(x >= 0xfe54 && x <= 0xfe66) ||
|
|
244
|
+
(x >= 0xfe68 && x <= 0xfe6b) ||
|
|
245
|
+
(x >= 0x1f200 && x <= 0x1f202) ||
|
|
246
|
+
(x >= 0x1f210 && x <= 0x1f23b) ||
|
|
247
|
+
(x >= 0x1f240 && x <= 0x1f248) ||
|
|
248
|
+
(x >= 0x20000 && x <= 0x2fffd) ||
|
|
249
|
+
(x >= 0x30000 && x <= 0x3fffd)
|
|
250
|
+
);
|
|
251
|
+
};
|
|
252
|
+
|
|
209
253
|
export function isFullwidthCodePoint(codePoint: number): boolean {
|
|
210
254
|
if (!Number.isInteger(codePoint)) {
|
|
211
255
|
return false;
|
package/src/ansi/escapes.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
1
|
// Derived from `ansi-escapes` (MIT, Sindre Sorhus), reduced to the escapes Ink uses.
|
|
3
|
-
import process from "node:process";
|
|
4
|
-
|
|
5
2
|
// Control characters and sequence introducers. This module is the single
|
|
6
3
|
// home for escape sequences: everything else imports these instead of
|
|
7
4
|
// spelling out `\u001B[...` inline.
|
|
@@ -51,35 +48,27 @@ export const eraseLines = (count: number): string => {
|
|
|
51
48
|
return clear;
|
|
52
49
|
};
|
|
53
50
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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`;
|
|
51
|
+
// 1. Erases the screen (only done in case `2` is not supported)
|
|
52
|
+
// 2. Erases the whole screen including scrollback buffer
|
|
53
|
+
// 3. Moves cursor to the top-left position
|
|
54
|
+
export const clearTerminal = `${eraseScreen}${CSI}3J${CSI}H`;
|
|
77
55
|
|
|
78
56
|
export const enterAlternativeScreen = CSI + "?1049h";
|
|
79
57
|
export const exitAlternativeScreen = CSI + "?1049l";
|
|
80
58
|
|
|
81
59
|
export const cursorShow = CSI + "?25h";
|
|
82
60
|
export const cursorHide = CSI + "?25l";
|
|
61
|
+
export type CursorShape = "block" | "underline" | "bar";
|
|
62
|
+
export const cursorShape = (shape: CursorShape, blinking = true): string => {
|
|
63
|
+
const value = {
|
|
64
|
+
block: blinking ? 1 : 2,
|
|
65
|
+
underline: blinking ? 3 : 4,
|
|
66
|
+
bar: blinking ? 5 : 6,
|
|
67
|
+
}[shape];
|
|
68
|
+
return `${CSI}${value} q`;
|
|
69
|
+
};
|
|
70
|
+
export const cursorColor = (color: string): string => `${OSC}12;${color}${BEL}`;
|
|
71
|
+
export const resetCursorColor = `${OSC}112${BEL}`;
|
|
83
72
|
|
|
84
73
|
export const enableBracketedPaste = CSI + "?2004h";
|
|
85
74
|
export const disableBracketedPaste = CSI + "?2004l";
|
|
@@ -115,6 +104,9 @@ export const ansiEscapes = {
|
|
|
115
104
|
exitAlternativeScreen,
|
|
116
105
|
cursorShow,
|
|
117
106
|
cursorHide,
|
|
107
|
+
cursorShape,
|
|
108
|
+
cursorColor,
|
|
109
|
+
resetCursorColor,
|
|
118
110
|
enableBracketedPaste,
|
|
119
111
|
disableBracketedPaste,
|
|
120
112
|
pasteStart,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
2
|
+
|
|
3
|
+
/** The canonical grapheme segmentation service for measurement and rasterization. */
|
|
4
|
+
export function* graphemes(text: string): Generator<string> {
|
|
5
|
+
for (const { segment } of segmenter.segment(text)) {
|
|
6
|
+
yield segment;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Standalone OSC 8 hyperlink builder with fallback — the string counterpart
|
|
2
|
+
// to the <Hyperlink> component, for use outside the React renderer.
|
|
3
|
+
import { link } from "#/ansi/escapes.ts";
|
|
4
|
+
import { detectHyperlinkSupport } from "#/capabilities/detect.ts";
|
|
5
|
+
|
|
6
|
+
type HyperlinkOptions = {
|
|
7
|
+
/**
|
|
8
|
+
When the terminal does not support OSC 8 hyperlinks, append the URL in
|
|
9
|
+
parentheses after the text so it stays reachable. Set to `false` to
|
|
10
|
+
return the text alone.
|
|
11
|
+
|
|
12
|
+
@default true
|
|
13
|
+
*/
|
|
14
|
+
fallback?: boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
The stream the text will be written to, for support detection.
|
|
18
|
+
|
|
19
|
+
@default process.stdout
|
|
20
|
+
*/
|
|
21
|
+
stream?: { isTTY?: boolean };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
Wraps text in a clickable OSC 8 hyperlink when the terminal supports it,
|
|
26
|
+
falling back to `text (url)`.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { hyperlink } from "@alchemy.run/sigil/ansi";
|
|
30
|
+
|
|
31
|
+
console.log(hyperlink("Documentation", "https://example.com"));
|
|
32
|
+
```
|
|
33
|
+
*/
|
|
34
|
+
export const hyperlink = (
|
|
35
|
+
text: string,
|
|
36
|
+
url: string,
|
|
37
|
+
{ fallback = true, stream = process.stdout }: HyperlinkOptions = {},
|
|
38
|
+
): string => {
|
|
39
|
+
if (detectHyperlinkSupport(stream)) {
|
|
40
|
+
return link(text, url);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return fallback ? `${text} (${url})` : text;
|
|
44
|
+
};
|
package/src/ansi/index.ts
CHANGED
|
@@ -3,25 +3,25 @@
|
|
|
3
3
|
// `@alchemy.run/sigil/ansi`.
|
|
4
4
|
|
|
5
5
|
// Styling
|
|
6
|
-
export { chalk } from "
|
|
7
|
-
export { supportsColor } from "
|
|
8
|
-
export
|
|
6
|
+
export { chalk } from "#/ansi/chalk.ts";
|
|
7
|
+
export { supportsColor } from "#/ansi/chalk.ts";
|
|
8
|
+
export { hyperlink } from "#/ansi/hyperlink.ts";
|
|
9
|
+
export * from "#/ansi/sgr.ts";
|
|
9
10
|
|
|
10
11
|
// Escape sequences (raw building blocks + named sequences)
|
|
11
|
-
export
|
|
12
|
-
export * from "
|
|
13
|
-
export { cliCursor } from "
|
|
12
|
+
export * from "#/ansi/escapes.ts";
|
|
13
|
+
export * from "#/ansi/osc.ts";
|
|
14
|
+
export { cliCursor } from "#/ansi/cursor.ts";
|
|
14
15
|
|
|
15
16
|
// Measurement
|
|
16
|
-
export { stringWidth } from "
|
|
17
|
-
export
|
|
18
|
-
export * from "./east-asian-width.ts";
|
|
17
|
+
export { stringWidth, widestLine } from "#/ansi/string-width.ts";
|
|
18
|
+
export * from "#/ansi/east-asian-width.ts";
|
|
19
19
|
|
|
20
20
|
// String manipulation
|
|
21
|
-
export { stripAnsi
|
|
22
|
-
export { sliceAnsi } from "
|
|
23
|
-
export { cliTruncate as truncateAnsi } from "
|
|
24
|
-
export { wrapAnsi } from "
|
|
21
|
+
export { stripAnsi } from "#/ansi/strip.ts";
|
|
22
|
+
export { sliceAnsi } from "#/ansi/slice.ts";
|
|
23
|
+
export { cliTruncate as truncateAnsi } from "#/ansi/truncate.ts";
|
|
24
|
+
export { wrapAnsi } from "#/ansi/wrap.ts";
|
|
25
25
|
|
|
26
26
|
// Tokenizer: style-aware parsing and minimal re-emission
|
|
27
|
-
export * from "
|
|
27
|
+
export * from "#/ansi/tokenize.ts";
|
package/src/ansi/osc.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
|
|
3
|
+
import { ESC, OSC, ST } from "#/ansi/escapes.ts";
|
|
4
|
+
|
|
5
|
+
export type ClipboardSelection = "clipboard" | "primary" | "selection";
|
|
6
|
+
export type TerminalProgressState = "inactive" | "normal" | "error" | "indeterminate" | "paused";
|
|
7
|
+
|
|
8
|
+
const clipboardCode: Record<ClipboardSelection, string> = {
|
|
9
|
+
clipboard: "c",
|
|
10
|
+
primary: "p",
|
|
11
|
+
selection: "s",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const progressCode: Record<TerminalProgressState, number> = {
|
|
15
|
+
inactive: 0,
|
|
16
|
+
normal: 1,
|
|
17
|
+
error: 2,
|
|
18
|
+
indeterminate: 3,
|
|
19
|
+
paused: 4,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const field = (value: string): string => value.replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
|
|
23
|
+
|
|
24
|
+
const command = (code: number, payload: string): string => `${OSC}${code};${payload}${ST}`;
|
|
25
|
+
|
|
26
|
+
/** Change the terminal window title (OSC 2). */
|
|
27
|
+
export const setWindowTitle = (title: string): string => command(2, field(title));
|
|
28
|
+
|
|
29
|
+
/** Announce the current directory as a file URI (OSC 7). */
|
|
30
|
+
export const setWorkingDirectory = (directory: URL | string): string => {
|
|
31
|
+
const uri =
|
|
32
|
+
directory instanceof URL
|
|
33
|
+
? directory
|
|
34
|
+
: directory.startsWith("file:")
|
|
35
|
+
? new URL(directory)
|
|
36
|
+
: pathToFileURL(directory);
|
|
37
|
+
if (uri.protocol !== "file:") throw new Error("Terminal working directory must be a file URI");
|
|
38
|
+
return command(7, uri.href);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Show a desktop notification (OSC 9). */
|
|
42
|
+
export const notify = (title: string): string => {
|
|
43
|
+
const safe = field(title);
|
|
44
|
+
return command(9, /^\d+;/.test(safe) ? ` ${safe}` : safe);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Change the terminal pointer shape (OSC 22; terminal support varies). */
|
|
48
|
+
export const setPointerShape = (shape: string): string => command(22, field(shape));
|
|
49
|
+
|
|
50
|
+
/** Write UTF-8 text to a terminal clipboard using OSC 52. */
|
|
51
|
+
export const setClipboard = (text: string, selection: ClipboardSelection = "clipboard"): string =>
|
|
52
|
+
command(52, `${clipboardCode[selection]};${Buffer.from(text).toString("base64")}`);
|
|
53
|
+
|
|
54
|
+
/** Request clipboard contents. The response is another OSC 52 sequence. */
|
|
55
|
+
export const queryClipboard = (selection: ClipboardSelection = "clipboard"): string =>
|
|
56
|
+
command(52, `${clipboardCode[selection]};?`);
|
|
57
|
+
|
|
58
|
+
/** Clear a terminal clipboard. */
|
|
59
|
+
export const clearClipboard = (selection: ClipboardSelection = "clipboard"): string =>
|
|
60
|
+
command(52, `${clipboardCode[selection]};`);
|
|
61
|
+
|
|
62
|
+
/** Report terminal-native progress using OSC 9;4. */
|
|
63
|
+
export const setTerminalProgress = (state: TerminalProgressState, value?: number): string => {
|
|
64
|
+
const code = progressCode[state];
|
|
65
|
+
if (state === "inactive" || state === "indeterminate") {
|
|
66
|
+
return command(9, `4;${code}`);
|
|
67
|
+
}
|
|
68
|
+
if (value === undefined && (state === "error" || state === "paused")) {
|
|
69
|
+
return command(9, `4;${code}`);
|
|
70
|
+
}
|
|
71
|
+
const percentage = Math.max(0, Math.min(100, Math.round(value ?? 0)));
|
|
72
|
+
return command(9, `4;${code};${percentage}`);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** Wrap a sequence for transport through tmux's DCS passthrough. */
|
|
76
|
+
export const tmuxPassthrough = (sequence: string): string =>
|
|
77
|
+
`${ESC}Ptmux;${sequence.replaceAll(ESC, `${ESC}${ESC}`)}${ST}`;
|
package/src/ansi/sgr.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ANSI SGR style tables and color-space conversions.
|
|
2
2
|
// Ported from `ansi-styles` (MIT, Sindre Sorhus).
|
|
3
|
-
import { CSI } from "
|
|
3
|
+
import { CSI } from "#/ansi/escapes.ts";
|
|
4
4
|
|
|
5
5
|
const ANSI_BACKGROUND_OFFSET = 10;
|
|
6
6
|
|
|
@@ -173,10 +173,7 @@ export const hexToRgb = (hex: string): [number, number, number] => {
|
|
|
173
173
|
let [colorString] = matches;
|
|
174
174
|
|
|
175
175
|
if (colorString.length === 3) {
|
|
176
|
-
colorString = colorString
|
|
177
|
-
.split("")
|
|
178
|
-
.map((character) => character + character)
|
|
179
|
-
.join("");
|
|
176
|
+
colorString = colorString.replaceAll(/./g, "$&$&");
|
|
180
177
|
}
|
|
181
178
|
|
|
182
179
|
const integer = Number.parseInt(colorString, 16);
|
package/src/ansi/slice.ts
CHANGED