@alchemy.run/sigil 0.0.0-alpha.4 → 0.0.0-alpha.6
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 +21 -9
- package/THIRD_PARTY_NOTICES.md +39 -1
- package/dist/Text-BobFKi74.d.ts +452 -0
- package/dist/ansi.d.ts +122 -131
- package/dist/ansi.js +86 -6
- 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/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-DZ88EXJv.d.ts +21 -0
- package/dist/index.d.ts +143 -498
- package/dist/index.js +1026 -2244
- 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-BOSLQ8fF.d.ts +49 -0
- package/dist/screen-CiPytswf.js +342 -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-aZr9O8h3.js +665 -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-BzqAJi0n.js +1337 -0
- package/package.json +41 -9
- package/src/ansi/chalk.ts +5 -3
- package/src/ansi/escapes.ts +14 -0
- package/src/ansi/graphemes.ts +8 -0
- package/src/ansi/hyperlink.ts +44 -0
- package/src/ansi/index.ts +3 -1
- package/src/ansi/osc.ts +77 -0
- package/src/ansi/tokenize.ts +3 -4
- 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 +98 -10
- package/src/components/BackgroundContext.ts +2 -3
- package/src/components/Box.tsx +0 -8
- package/src/components/CursorContext.ts +1 -1
- package/src/components/Hyperlink.tsx +56 -0
- package/src/components/TerminalOscContext.ts +25 -0
- package/src/components/Text.tsx +22 -45
- package/src/components/Transform.tsx +1 -1
- package/src/dom.ts +11 -2
- package/src/global.d.ts +3 -0
- package/src/hooks/use-capabilities.ts +73 -0
- package/src/hooks/use-cursor.ts +2 -2
- package/src/hooks/use-terminal-osc.ts +59 -0
- package/src/index.ts +45 -1
- package/src/ink.tsx +213 -153
- package/src/{render-node-to-output.ts → paint-tree.ts} +75 -48
- package/src/reconciler.ts +24 -3
- package/src/render-background.ts +36 -15
- package/src/render-border.ts +94 -61
- package/src/render-frame.ts +83 -0
- package/src/render-to-string.ts +21 -6
- package/src/render.ts +15 -6
- 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/screen/ansi.ts +184 -0
- package/src/screen/canvas.ts +160 -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 +305 -0
- package/src/screen/serialize.ts +129 -0
- package/src/screen.ts +1 -0
- package/src/semantic-text-style.ts +118 -0
- package/src/squash-text-nodes.ts +2 -5
- package/src/structured-text.ts +325 -0
- package/src/styles.ts +19 -14
- 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.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/wrap-text.ts +4 -0
- package/dist/sgr-CMfEpjSk.d.ts +0 -91
- package/dist/truncate-Cr6xVFMa.js +0 -2330
- package/src/ansi/supports-color.ts +0 -207
- package/src/colorize.ts +0 -60
- package/src/log-update.ts +0 -370
- package/src/output.ts +0 -308
- package/src/renderer.ts +0 -73
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.6",
|
|
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",
|
|
@@ -37,25 +37,44 @@
|
|
|
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,15 +85,25 @@
|
|
|
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",
|
|
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",
|
|
73
102
|
"react": "^19.2.8",
|
|
74
103
|
"react-devtools-core": "^7.0.1",
|
|
75
|
-
"react-router": "^8.3.0",
|
|
76
104
|
"tsx": "^4.21.0",
|
|
77
105
|
"typescript": "^7.0.2",
|
|
106
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@^0.2.9",
|
|
78
107
|
"vite-plus": "^0.2.9",
|
|
79
108
|
"vitest": "4.1.11",
|
|
80
109
|
"zigpty": "^0.2.1"
|
|
@@ -99,12 +128,15 @@
|
|
|
99
128
|
"dev": "vp pack -w",
|
|
100
129
|
"build": "vp pack",
|
|
101
130
|
"test": "vp test",
|
|
131
|
+
"e2e": "vp test test/e2e.test.ts",
|
|
132
|
+
"explorer": "node --import=tsx scripts/explorer.ts",
|
|
102
133
|
"lint": "vp lint",
|
|
103
134
|
"format": "vp fmt .",
|
|
104
135
|
"check": "vp check",
|
|
105
|
-
"typecheck": "tsc --noEmit",
|
|
136
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p test/contracts/tsconfig.json",
|
|
106
137
|
"inspect": "vpx react-devtools",
|
|
107
138
|
"build:reference": "tsx scripts/build-reference.ts",
|
|
139
|
+
"bench:terminal": "tsx benchmark/terminal-core.tsx --check",
|
|
108
140
|
"fuzz": "tsx test/yoga/fuzz/typescript.ts",
|
|
109
141
|
"fuzz:differential": "vpr build:reference && tsx test/yoga/fuzz/differential.ts",
|
|
110
142
|
"fuzz:loop": "vpr build:reference && tsx test/yoga/fuzz/loop.ts"
|
package/src/ansi/chalk.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
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.
|
|
@@ -17,16 +19,16 @@ import {
|
|
|
17
19
|
type ModifierName,
|
|
18
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;
|
package/src/ansi/escapes.ts
CHANGED
|
@@ -58,6 +58,17 @@ export const exitAlternativeScreen = CSI + "?1049l";
|
|
|
58
58
|
|
|
59
59
|
export const cursorShow = CSI + "?25h";
|
|
60
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}`;
|
|
61
72
|
|
|
62
73
|
export const enableBracketedPaste = CSI + "?2004h";
|
|
63
74
|
export const disableBracketedPaste = CSI + "?2004l";
|
|
@@ -93,6 +104,9 @@ export const ansiEscapes = {
|
|
|
93
104
|
exitAlternativeScreen,
|
|
94
105
|
cursorShow,
|
|
95
106
|
cursorHide,
|
|
107
|
+
cursorShape,
|
|
108
|
+
cursorColor,
|
|
109
|
+
resetCursorColor,
|
|
96
110
|
enableBracketedPaste,
|
|
97
111
|
disableBracketedPaste,
|
|
98
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
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
// Styling
|
|
6
6
|
export { chalk } from "#/ansi/chalk.ts";
|
|
7
|
-
export { supportsColor } from "#/ansi/
|
|
7
|
+
export { supportsColor } from "#/ansi/chalk.ts";
|
|
8
|
+
export { hyperlink } from "#/ansi/hyperlink.ts";
|
|
8
9
|
export * from "#/ansi/sgr.ts";
|
|
9
10
|
|
|
10
11
|
// Escape sequences (raw building blocks + named sequences)
|
|
11
12
|
export * from "#/ansi/escapes.ts";
|
|
13
|
+
export * from "#/ansi/osc.ts";
|
|
12
14
|
export { cliCursor } from "#/ansi/cursor.ts";
|
|
13
15
|
|
|
14
16
|
// Measurement
|
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/tokenize.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { isFullwidthGrapheme } from "#/ansi/east-asian-width.ts";
|
|
|
8
8
|
// `src/ansi-tokenizer.ts` (also behind sanitize-ansi), and grapheme widths
|
|
9
9
|
// come from `east-asian-width.ts` — one grammar, one width table.
|
|
10
10
|
import { BEL, C1_ST, ESC } from "#/ansi/escapes.ts";
|
|
11
|
+
import { graphemes } from "#/ansi/graphemes.ts";
|
|
11
12
|
import { codes as sgrCodes, foreground, background, styles } from "#/ansi/sgr.ts";
|
|
12
13
|
|
|
13
14
|
// Single-character introducer suffixes (`ESC [` = CSI, `ESC ]` = OSC).
|
|
@@ -119,8 +120,6 @@ export function isIntensityCode(code: AnsiCode): boolean {
|
|
|
119
120
|
return code.code === styles.bold.open || code.code === styles.dim.open;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
123
|
-
|
|
124
123
|
/**
|
|
125
124
|
Splits compound SGR sequences like `\u001B[1;3;31m` into individual components.
|
|
126
125
|
*/
|
|
@@ -139,7 +138,7 @@ function splitCompoundSgrSequences(code: string): string[] {
|
|
|
139
138
|
for (let index = 0; index < codeParts.length; index++) {
|
|
140
139
|
const rawCode = codeParts[index]!;
|
|
141
140
|
// Keep 8-bit and 24-bit color codes (containing multiple ";") together
|
|
142
|
-
if (rawCode === "38" || rawCode === "48") {
|
|
141
|
+
if (rawCode === "38" || rawCode === "48" || rawCode === "58") {
|
|
143
142
|
if (index + 2 < codeParts.length && codeParts[index + 1] === "5") {
|
|
144
143
|
// 8-bit color, followed by another number
|
|
145
144
|
result.push(codeParts.slice(index, index + 3).join(";"));
|
|
@@ -171,7 +170,7 @@ export function tokenize(string: string, endChar = Number.POSITIVE_INFINITY): To
|
|
|
171
170
|
|
|
172
171
|
outer: for (const ansiToken of tokenizeAnsi(string)) {
|
|
173
172
|
if (ansiToken.type === "text") {
|
|
174
|
-
for (const
|
|
173
|
+
for (const segment of graphemes(ansiToken.value)) {
|
|
175
174
|
const fullWidth = isFullwidthGrapheme(segment, segment.codePointAt(0)!);
|
|
176
175
|
result.push({
|
|
177
176
|
type: "char",
|
|
@@ -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
|
+
}
|