@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.10
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 +499 -313
- package/THIRD_PARTY_NOTICES.md +70 -23
- package/dist/Text-DV9CuzAT.d.ts +452 -0
- package/dist/ansi.d.ts +217 -0
- package/dist/ansi.js +87 -0
- 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-BAC9-TZX.js +592 -0
- package/dist/color-policy-CCxuHIdD.d.ts +22 -0
- package/dist/color-profile-CyeHnG1T.d.ts +97 -0
- package/dist/color-profile-DHhQHY55.js +36 -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-B3dL4Q11.js +374 -0
- package/dist/detect-Db6GbKOm.d.ts +195 -0
- package/dist/{devtools-QpCMm9JH.mjs → devtools-DbthxoD1.js} +23 -24
- 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-D48vQhhe.d.ts +21 -0
- package/dist/index-DDVME65c.d.ts +919 -0
- package/dist/index.d.ts +1310 -0
- package/dist/index.js +3209 -0
- package/dist/osc-BFKKSqpg.js +71 -0
- package/dist/osc-Cn0fw77g.d.ts +23 -0
- package/dist/paint-Cx-zC_sX.d.ts +81 -0
- package/dist/query-BNc2B8GD.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-CgC2WlVM.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-DDQ5V300.js +723 -0
- package/dist/sgr-BhwaWAJB.js +246 -0
- package/dist/store-C1P5fOUi.d.ts +72 -0
- package/dist/string-width-CijQwpIk.js +69 -0
- package/dist/strip-BvU4toXG.js +6 -0
- package/dist/terminal.d.ts +121 -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-CcCIdCWA.js +1337 -0
- package/dist/yoga-5jKhYCJC.js +3465 -0
- package/dist/yoga.d.ts +2 -0
- package/dist/yoga.js +2 -0
- package/package.json +70 -24
- package/src/ansi/chalk.ts +138 -0
- package/src/ansi/cursor.ts +46 -0
- package/src/ansi/east-asian-width.ts +259 -0
- package/src/ansi/escapes.ts +120 -0
- package/src/ansi/graphemes.ts +8 -0
- package/src/ansi/hyperlink.ts +44 -0
- package/src/ansi/index.ts +27 -0
- package/src/ansi/osc.ts +77 -0
- package/src/ansi/sgr.ts +234 -0
- package/src/ansi/slice.ts +43 -0
- package/src/ansi/string-width.ts +121 -0
- package/src/ansi/strip.ts +13 -0
- package/src/ansi/tokenize.ts +380 -0
- package/src/ansi/truncate.ts +196 -0
- package/src/ansi/wrap.ts +765 -0
- package/src/ansi-tokenizer.ts +510 -0
- package/src/capabilities/color-policy.ts +34 -0
- package/src/capabilities/detect.ts +608 -0
- package/src/capabilities/index.ts +37 -0
- package/src/capabilities/query.ts +679 -0
- package/src/capabilities/store.ts +394 -0
- package/src/code-excerpt.ts +39 -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/AccessibilityContext.ts +5 -0
- package/src/components/AnimationContext.ts +24 -0
- package/src/components/AnsiText.tsx +42 -0
- package/src/components/App.tsx +878 -0
- package/src/components/AppContext.ts +111 -0
- package/src/components/BackgroundContext.ts +7 -0
- package/src/components/Box.tsx +100 -0
- package/src/components/CursorContext.ts +19 -0
- package/src/components/ErrorBoundary.tsx +39 -0
- package/src/components/ErrorOverview.tsx +135 -0
- package/src/components/FocusContext.ts +30 -0
- package/src/components/Hyperlink.tsx +56 -0
- package/src/components/Newline.tsx +16 -0
- package/src/components/Spacer.tsx +11 -0
- package/src/components/Static.tsx +60 -0
- package/src/components/StderrContext.ts +24 -0
- package/src/components/StdinContext.ts +48 -0
- package/src/components/StdoutContext.ts +26 -0
- package/src/components/TerminalOscContext.ts +25 -0
- package/src/components/Text.tsx +122 -0
- package/src/components/Transform.tsx +38 -0
- package/src/components/VirtualList.tsx +128 -0
- package/src/cursor-position.ts +103 -0
- package/src/devtools.ts +103 -0
- package/src/dom.ts +301 -0
- package/src/env.ts +12 -0
- package/src/get-max-width.ts +11 -0
- package/src/global.d.ts +38 -0
- package/src/glyphs.ts +99 -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-capabilities.ts +73 -0
- package/src/hooks/use-cursor.ts +33 -0
- package/src/hooks/use-focus-manager.ts +62 -0
- package/src/hooks/use-focus.ts +82 -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-terminal-osc.ts +59 -0
- package/src/hooks/use-virtual-scroll.ts +84 -0
- package/src/hooks/use-window-size.ts +37 -0
- package/src/index.ts +96 -0
- package/src/ink.tsx +1452 -0
- package/src/input-parser.ts +303 -0
- package/src/instances.ts +9 -0
- package/src/kitty-keyboard.ts +185 -0
- package/src/measure-element.ts +62 -0
- package/src/measure-text.ts +31 -0
- package/src/paint-tree.ts +220 -0
- package/src/parse-keypress.ts +515 -0
- package/src/parse-stack-line.ts +138 -0
- package/src/patch-console.ts +106 -0
- package/src/quick-lru.ts +85 -0
- package/src/reconciler.ts +425 -0
- package/src/render-background.ts +59 -0
- package/src/render-border.ts +167 -0
- package/src/render-frame.ts +83 -0
- package/src/render-to-string.ts +146 -0
- package/src/render.ts +284 -0
- 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 +33 -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/signal-exit.ts +106 -0
- package/src/squash-text-nodes.ts +37 -0
- package/src/stream.ts +28 -0
- package/src/structured-text.ts +325 -0
- package/src/styles.ts +753 -0
- package/src/terminal/index.ts +2 -0
- package/src/terminal/inline-presenter.ts +120 -0
- package/src/terminal/input.ts +92 -0
- package/src/terminal/render-scheduler.ts +37 -0
- package/src/terminal/screen-presenter.ts +242 -0
- package/src/terminal/session.ts +408 -0
- package/src/terminal-size.ts +58 -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 +361 -0
- package/src/testing/vitest.ts +157 -0
- package/src/throttle.ts +73 -0
- package/src/transform-adapter.ts +14 -0
- package/src/types.ts +10 -0
- package/src/virtual-scroll.ts +133 -0
- package/src/wrap-text.ts +54 -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 +2926 -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
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// Copied from https://github.com/enquirer/enquirer/blob/36785f3399a41cd61e9d28d1eb9c2fcd73d69b4c/lib/keypress.js
|
|
2
|
+
import { DEL, ESC } from "#/ansi/escapes.ts";
|
|
3
|
+
import { kittyModifiers } from "#/kitty-keyboard.ts";
|
|
4
|
+
|
|
5
|
+
const textDecoder = new TextDecoder();
|
|
6
|
+
|
|
7
|
+
const metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
|
|
8
|
+
|
|
9
|
+
const fnKeyRe = /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
|
|
10
|
+
|
|
11
|
+
const keyName: Record<string, string> = {
|
|
12
|
+
/* xterm/gnome ESC O letter */
|
|
13
|
+
OP: "f1",
|
|
14
|
+
OQ: "f2",
|
|
15
|
+
OR: "f3",
|
|
16
|
+
OS: "f4",
|
|
17
|
+
/* vt220-style ESC [ letter (e.g. Ctrl+F1 sends ESC [ 1 ; 5 P) */
|
|
18
|
+
"[P": "f1",
|
|
19
|
+
"[Q": "f2",
|
|
20
|
+
"[R": "f3",
|
|
21
|
+
"[S": "f4",
|
|
22
|
+
/* xterm/rxvt ESC [ number ~ */
|
|
23
|
+
"[11~": "f1",
|
|
24
|
+
"[12~": "f2",
|
|
25
|
+
"[13~": "f3",
|
|
26
|
+
"[14~": "f4",
|
|
27
|
+
/* from Cygwin and used in libuv */
|
|
28
|
+
"[[A": "f1",
|
|
29
|
+
"[[B": "f2",
|
|
30
|
+
"[[C": "f3",
|
|
31
|
+
"[[D": "f4",
|
|
32
|
+
"[[E": "f5",
|
|
33
|
+
/* common */
|
|
34
|
+
"[15~": "f5",
|
|
35
|
+
"[17~": "f6",
|
|
36
|
+
"[18~": "f7",
|
|
37
|
+
"[19~": "f8",
|
|
38
|
+
"[20~": "f9",
|
|
39
|
+
"[21~": "f10",
|
|
40
|
+
"[23~": "f11",
|
|
41
|
+
"[24~": "f12",
|
|
42
|
+
/* xterm ESC [ letter */
|
|
43
|
+
"[A": "up",
|
|
44
|
+
"[B": "down",
|
|
45
|
+
"[C": "right",
|
|
46
|
+
"[D": "left",
|
|
47
|
+
"[E": "clear",
|
|
48
|
+
"[F": "end",
|
|
49
|
+
"[H": "home",
|
|
50
|
+
/* xterm/gnome ESC O letter */
|
|
51
|
+
OA: "up",
|
|
52
|
+
OB: "down",
|
|
53
|
+
OC: "right",
|
|
54
|
+
OD: "left",
|
|
55
|
+
OE: "clear",
|
|
56
|
+
OF: "end",
|
|
57
|
+
OH: "home",
|
|
58
|
+
/* xterm/rxvt ESC [ number ~ */
|
|
59
|
+
"[1~": "home",
|
|
60
|
+
"[2~": "insert",
|
|
61
|
+
"[3~": "delete",
|
|
62
|
+
"[4~": "end",
|
|
63
|
+
"[5~": "pageup",
|
|
64
|
+
"[6~": "pagedown",
|
|
65
|
+
/* putty */
|
|
66
|
+
"[[5~": "pageup",
|
|
67
|
+
"[[6~": "pagedown",
|
|
68
|
+
/* rxvt */
|
|
69
|
+
"[7~": "home",
|
|
70
|
+
"[8~": "end",
|
|
71
|
+
/* rxvt keys with modifiers */
|
|
72
|
+
"[a": "up",
|
|
73
|
+
"[b": "down",
|
|
74
|
+
"[c": "right",
|
|
75
|
+
"[d": "left",
|
|
76
|
+
"[e": "clear",
|
|
77
|
+
|
|
78
|
+
"[2$": "insert",
|
|
79
|
+
"[3$": "delete",
|
|
80
|
+
"[5$": "pageup",
|
|
81
|
+
"[6$": "pagedown",
|
|
82
|
+
"[7$": "home",
|
|
83
|
+
"[8$": "end",
|
|
84
|
+
|
|
85
|
+
Oa: "up",
|
|
86
|
+
Ob: "down",
|
|
87
|
+
Oc: "right",
|
|
88
|
+
Od: "left",
|
|
89
|
+
Oe: "clear",
|
|
90
|
+
|
|
91
|
+
"[2^": "insert",
|
|
92
|
+
"[3^": "delete",
|
|
93
|
+
"[5^": "pageup",
|
|
94
|
+
"[6^": "pagedown",
|
|
95
|
+
"[7^": "home",
|
|
96
|
+
"[8^": "end",
|
|
97
|
+
/* misc. */
|
|
98
|
+
"[Z": "tab",
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const nonAlphanumericKeys = [...Object.values(keyName), "backspace"];
|
|
102
|
+
|
|
103
|
+
const isShiftKey = (code: string) => {
|
|
104
|
+
return ["[a", "[b", "[c", "[d", "[e", "[2$", "[3$", "[5$", "[6$", "[7$", "[8$", "[Z"].includes(
|
|
105
|
+
code,
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const isCtrlKey = (code: string) => {
|
|
110
|
+
return ["Oa", "Ob", "Oc", "Od", "Oe", "[2^", "[3^", "[5^", "[6^", "[7^", "[8^"].includes(code);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
type ParsedKey = {
|
|
114
|
+
name: string;
|
|
115
|
+
ctrl: boolean;
|
|
116
|
+
meta: boolean;
|
|
117
|
+
shift: boolean;
|
|
118
|
+
sequence: string;
|
|
119
|
+
raw: string | undefined;
|
|
120
|
+
code?: string;
|
|
121
|
+
super?: boolean;
|
|
122
|
+
hyper?: boolean;
|
|
123
|
+
capsLock?: boolean;
|
|
124
|
+
numLock?: boolean;
|
|
125
|
+
eventType?: "press" | "repeat" | "release";
|
|
126
|
+
isKittyProtocol?: boolean;
|
|
127
|
+
text?: string;
|
|
128
|
+
// Whether this key represents printable text input.
|
|
129
|
+
// When false, the key is a control/function/modifier key that should not
|
|
130
|
+
// produce text input (e.g., arrows, function keys, capslock, media keys).
|
|
131
|
+
// Only set by the kitty protocol parser.
|
|
132
|
+
isPrintable?: boolean;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Kitty keyboard protocol: CSI codepoint ; modifiers [: eventType] [; text-as-codepoints] u
|
|
136
|
+
const kittyKeyRe = /^\x1b\[(\d+)(?:;(\d+)(?::(\d+))?(?:;([\d:]+))?)?u$/;
|
|
137
|
+
|
|
138
|
+
// Kitty-enhanced special keys: CSI number ; modifiers : eventType {letter|~}
|
|
139
|
+
// These are legacy CSI sequences enhanced with the :eventType field.
|
|
140
|
+
// Examples: \x1b[1;1:1A (up arrow press), \x1b[3;1:3~ (delete release)
|
|
141
|
+
const kittySpecialKeyRe = /^\x1b\[(\d+);(\d+):(\d+)([A-Za-z~])$/;
|
|
142
|
+
|
|
143
|
+
// Letter-terminated special key names (CSI 1 ; mods letter)
|
|
144
|
+
const kittySpecialLetterKeys: Record<string, string> = {
|
|
145
|
+
A: "up",
|
|
146
|
+
B: "down",
|
|
147
|
+
C: "right",
|
|
148
|
+
D: "left",
|
|
149
|
+
E: "clear",
|
|
150
|
+
F: "end",
|
|
151
|
+
H: "home",
|
|
152
|
+
P: "f1",
|
|
153
|
+
Q: "f2",
|
|
154
|
+
R: "f3",
|
|
155
|
+
S: "f4",
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Number-terminated special key names (CSI number ; mods ~)
|
|
159
|
+
const kittySpecialNumberKeys: Record<number, string> = {
|
|
160
|
+
2: "insert",
|
|
161
|
+
3: "delete",
|
|
162
|
+
5: "pageup",
|
|
163
|
+
6: "pagedown",
|
|
164
|
+
7: "home",
|
|
165
|
+
8: "end",
|
|
166
|
+
11: "f1",
|
|
167
|
+
12: "f2",
|
|
168
|
+
13: "f3",
|
|
169
|
+
14: "f4",
|
|
170
|
+
15: "f5",
|
|
171
|
+
17: "f6",
|
|
172
|
+
18: "f7",
|
|
173
|
+
19: "f8",
|
|
174
|
+
20: "f9",
|
|
175
|
+
21: "f10",
|
|
176
|
+
23: "f11",
|
|
177
|
+
24: "f12",
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Map of special codepoints to key names in kitty protocol
|
|
181
|
+
const kittyCodepointNames: Record<number, string> = {
|
|
182
|
+
27: "escape",
|
|
183
|
+
// 13 (return) and 32 (space) are handled before this lookup
|
|
184
|
+
// in parseKittyKeypress so they can be marked as printable.
|
|
185
|
+
9: "tab",
|
|
186
|
+
127: "backspace",
|
|
187
|
+
8: "backspace",
|
|
188
|
+
57358: "capslock",
|
|
189
|
+
57359: "scrolllock",
|
|
190
|
+
57360: "numlock",
|
|
191
|
+
57361: "printscreen",
|
|
192
|
+
57362: "pause",
|
|
193
|
+
57363: "menu",
|
|
194
|
+
57376: "f13",
|
|
195
|
+
57377: "f14",
|
|
196
|
+
57378: "f15",
|
|
197
|
+
57379: "f16",
|
|
198
|
+
57380: "f17",
|
|
199
|
+
57381: "f18",
|
|
200
|
+
57382: "f19",
|
|
201
|
+
57383: "f20",
|
|
202
|
+
57384: "f21",
|
|
203
|
+
57385: "f22",
|
|
204
|
+
57386: "f23",
|
|
205
|
+
57387: "f24",
|
|
206
|
+
57388: "f25",
|
|
207
|
+
57389: "f26",
|
|
208
|
+
57390: "f27",
|
|
209
|
+
57391: "f28",
|
|
210
|
+
57392: "f29",
|
|
211
|
+
57393: "f30",
|
|
212
|
+
57394: "f31",
|
|
213
|
+
57395: "f32",
|
|
214
|
+
57396: "f33",
|
|
215
|
+
57397: "f34",
|
|
216
|
+
57398: "f35",
|
|
217
|
+
57399: "kp0",
|
|
218
|
+
57400: "kp1",
|
|
219
|
+
57401: "kp2",
|
|
220
|
+
57402: "kp3",
|
|
221
|
+
57403: "kp4",
|
|
222
|
+
57404: "kp5",
|
|
223
|
+
57405: "kp6",
|
|
224
|
+
57406: "kp7",
|
|
225
|
+
57407: "kp8",
|
|
226
|
+
57408: "kp9",
|
|
227
|
+
57409: "kpdecimal",
|
|
228
|
+
57410: "kpdivide",
|
|
229
|
+
57411: "kpmultiply",
|
|
230
|
+
57412: "kpsubtract",
|
|
231
|
+
57413: "kpadd",
|
|
232
|
+
57414: "kpenter",
|
|
233
|
+
57415: "kpequal",
|
|
234
|
+
57416: "kpseparator",
|
|
235
|
+
57417: "kpleft",
|
|
236
|
+
57418: "kpright",
|
|
237
|
+
57419: "kpup",
|
|
238
|
+
57420: "kpdown",
|
|
239
|
+
57421: "kppageup",
|
|
240
|
+
57422: "kppagedown",
|
|
241
|
+
57423: "kphome",
|
|
242
|
+
57424: "kpend",
|
|
243
|
+
57425: "kpinsert",
|
|
244
|
+
57426: "kpdelete",
|
|
245
|
+
57427: "kpbegin",
|
|
246
|
+
57428: "mediaplay",
|
|
247
|
+
57429: "mediapause",
|
|
248
|
+
57430: "mediaplaypause",
|
|
249
|
+
57431: "mediareverse",
|
|
250
|
+
57432: "mediastop",
|
|
251
|
+
57433: "mediafastforward",
|
|
252
|
+
57434: "mediarewind",
|
|
253
|
+
57435: "mediatracknext",
|
|
254
|
+
57436: "mediatrackprevious",
|
|
255
|
+
57437: "mediarecord",
|
|
256
|
+
57438: "lowervolume",
|
|
257
|
+
57439: "raisevolume",
|
|
258
|
+
57440: "mutevolume",
|
|
259
|
+
57441: "leftshift",
|
|
260
|
+
57442: "leftcontrol",
|
|
261
|
+
57443: "leftalt",
|
|
262
|
+
57444: "leftsuper",
|
|
263
|
+
57445: "lefthyper",
|
|
264
|
+
57446: "leftmeta",
|
|
265
|
+
57447: "rightshift",
|
|
266
|
+
57448: "rightcontrol",
|
|
267
|
+
57449: "rightalt",
|
|
268
|
+
57450: "rightsuper",
|
|
269
|
+
57451: "righthyper",
|
|
270
|
+
57452: "rightmeta",
|
|
271
|
+
57453: "isoLevel3Shift",
|
|
272
|
+
57454: "isoLevel5Shift",
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Valid Unicode codepoint range, excluding surrogates
|
|
276
|
+
const isValidCodepoint = (cp: number): boolean =>
|
|
277
|
+
cp >= 0 && cp <= 0x10_ffff && !(cp >= 0xd8_00 && cp <= 0xdf_ff);
|
|
278
|
+
|
|
279
|
+
const safeFromCodePoint = (cp: number): string =>
|
|
280
|
+
isValidCodepoint(cp) ? String.fromCodePoint(cp) : "?";
|
|
281
|
+
|
|
282
|
+
type EventType = "press" | "repeat" | "release";
|
|
283
|
+
|
|
284
|
+
function resolveEventType(value: number): EventType {
|
|
285
|
+
if (value === 3) return "release";
|
|
286
|
+
if (value === 2) return "repeat";
|
|
287
|
+
return "press";
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function parseKittyModifiers(
|
|
291
|
+
modifiers: number,
|
|
292
|
+
): Pick<ParsedKey, "ctrl" | "shift" | "meta" | "super" | "hyper" | "capsLock" | "numLock"> {
|
|
293
|
+
return {
|
|
294
|
+
ctrl: !!(modifiers & kittyModifiers.ctrl),
|
|
295
|
+
shift: !!(modifiers & kittyModifiers.shift),
|
|
296
|
+
meta: !!(modifiers & (kittyModifiers.meta | kittyModifiers.alt)),
|
|
297
|
+
super: !!(modifiers & kittyModifiers.super),
|
|
298
|
+
hyper: !!(modifiers & kittyModifiers.hyper),
|
|
299
|
+
capsLock: !!(modifiers & kittyModifiers.capsLock),
|
|
300
|
+
numLock: !!(modifiers & kittyModifiers.numLock),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const parseKittyKeypress = (s: string): ParsedKey | null => {
|
|
305
|
+
const match = kittyKeyRe.exec(s);
|
|
306
|
+
if (!match) return null;
|
|
307
|
+
|
|
308
|
+
const codepoint = parseInt(match[1]!, 10);
|
|
309
|
+
const modifiers = match[2] ? Math.max(0, parseInt(match[2], 10) - 1) : 0;
|
|
310
|
+
const eventType = match[3] ? parseInt(match[3], 10) : 1;
|
|
311
|
+
const textField = match[4];
|
|
312
|
+
|
|
313
|
+
// Bail on invalid primary codepoint
|
|
314
|
+
if (!isValidCodepoint(codepoint)) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Parse text-as-codepoints field (colon-separated Unicode codepoints)
|
|
319
|
+
let text: string | undefined;
|
|
320
|
+
if (textField) {
|
|
321
|
+
text = textField
|
|
322
|
+
.split(":")
|
|
323
|
+
.map((cp) => safeFromCodePoint(parseInt(cp, 10)))
|
|
324
|
+
.join("");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Determine key name from codepoint
|
|
328
|
+
let name: string;
|
|
329
|
+
let isPrintable: boolean;
|
|
330
|
+
if (codepoint === 32) {
|
|
331
|
+
name = "space";
|
|
332
|
+
isPrintable = true;
|
|
333
|
+
} else if (codepoint === 13) {
|
|
334
|
+
name = "return";
|
|
335
|
+
isPrintable = true;
|
|
336
|
+
} else if (kittyCodepointNames[codepoint]) {
|
|
337
|
+
name = kittyCodepointNames[codepoint]!;
|
|
338
|
+
isPrintable = false;
|
|
339
|
+
} else if (codepoint >= 1 && codepoint <= 26) {
|
|
340
|
+
// Ctrl+letter comes as codepoint 1-26
|
|
341
|
+
name = String.fromCodePoint(codepoint + 96); // 'a' is 97
|
|
342
|
+
isPrintable = false;
|
|
343
|
+
} else {
|
|
344
|
+
name = safeFromCodePoint(codepoint).toLowerCase();
|
|
345
|
+
isPrintable = true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Default text to the character from the codepoint when not explicitly
|
|
349
|
+
// provided by the protocol, so keys like space and return produce their
|
|
350
|
+
// expected text input (' ' and '\r' respectively).
|
|
351
|
+
if (isPrintable && !text) {
|
|
352
|
+
text = safeFromCodePoint(codepoint);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
name,
|
|
357
|
+
...parseKittyModifiers(modifiers),
|
|
358
|
+
eventType: resolveEventType(eventType),
|
|
359
|
+
sequence: s,
|
|
360
|
+
raw: s,
|
|
361
|
+
isKittyProtocol: true,
|
|
362
|
+
isPrintable,
|
|
363
|
+
text,
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// Parse kitty-enhanced special key sequences (arrow keys, function keys, etc.)
|
|
368
|
+
// These use the legacy CSI format but with an added :eventType field.
|
|
369
|
+
const parseKittySpecialKey = (s: string): ParsedKey | null => {
|
|
370
|
+
const match = kittySpecialKeyRe.exec(s);
|
|
371
|
+
if (!match) return null;
|
|
372
|
+
|
|
373
|
+
const number = parseInt(match[1]!, 10);
|
|
374
|
+
const modifiers = Math.max(0, parseInt(match[2]!, 10) - 1);
|
|
375
|
+
const eventType = parseInt(match[3]!, 10);
|
|
376
|
+
const terminator = match[4]!;
|
|
377
|
+
|
|
378
|
+
const name =
|
|
379
|
+
terminator === "~" ? kittySpecialNumberKeys[number] : kittySpecialLetterKeys[terminator];
|
|
380
|
+
|
|
381
|
+
if (!name) return null;
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
name,
|
|
385
|
+
...parseKittyModifiers(modifiers),
|
|
386
|
+
eventType: resolveEventType(eventType),
|
|
387
|
+
sequence: s,
|
|
388
|
+
raw: s,
|
|
389
|
+
isKittyProtocol: true,
|
|
390
|
+
isPrintable: false,
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
export const parseKeypress = (s: Uint8Array | string = ""): ParsedKey => {
|
|
395
|
+
let parts;
|
|
396
|
+
|
|
397
|
+
if (s instanceof Uint8Array) {
|
|
398
|
+
if (s[0]! > 127 && s[1] === undefined) {
|
|
399
|
+
s = ESC + textDecoder.decode(Uint8Array.of(s[0]! - 128));
|
|
400
|
+
} else {
|
|
401
|
+
s = textDecoder.decode(s);
|
|
402
|
+
}
|
|
403
|
+
} else if (s !== undefined && typeof s !== "string") {
|
|
404
|
+
s = String(s);
|
|
405
|
+
} else if (!s) {
|
|
406
|
+
s = "";
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Try kitty keyboard protocol parsers first
|
|
410
|
+
const kittyResult = parseKittyKeypress(s);
|
|
411
|
+
if (kittyResult) return kittyResult;
|
|
412
|
+
|
|
413
|
+
const kittySpecialResult = parseKittySpecialKey(s);
|
|
414
|
+
if (kittySpecialResult) return kittySpecialResult;
|
|
415
|
+
|
|
416
|
+
// If the input matched the kitty CSI-u pattern but was rejected (e.g.,
|
|
417
|
+
// invalid codepoint), return a safe empty keypress instead of falling
|
|
418
|
+
// through to legacy parsing which can produce unsafe states (undefined name)
|
|
419
|
+
if (kittyKeyRe.test(s)) {
|
|
420
|
+
return {
|
|
421
|
+
name: "",
|
|
422
|
+
ctrl: false,
|
|
423
|
+
meta: false,
|
|
424
|
+
shift: false,
|
|
425
|
+
sequence: s,
|
|
426
|
+
raw: s,
|
|
427
|
+
isKittyProtocol: true,
|
|
428
|
+
isPrintable: false,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const key: ParsedKey = {
|
|
433
|
+
name: "",
|
|
434
|
+
ctrl: false,
|
|
435
|
+
meta: false,
|
|
436
|
+
shift: false,
|
|
437
|
+
sequence: s,
|
|
438
|
+
raw: s,
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
key.sequence = key.sequence || s || key.name;
|
|
442
|
+
|
|
443
|
+
if (s === "\r" || s === ESC + "\r") {
|
|
444
|
+
// carriage return (or meta+return on macOS)
|
|
445
|
+
key.raw = undefined;
|
|
446
|
+
key.name = "return";
|
|
447
|
+
key.meta = s.length === 2;
|
|
448
|
+
} else if (s === "\n") {
|
|
449
|
+
// enter, should have been called linefeed
|
|
450
|
+
key.name = "enter";
|
|
451
|
+
} else if (s === "\t") {
|
|
452
|
+
// tab
|
|
453
|
+
key.name = "tab";
|
|
454
|
+
} else if (s === "\b" || s === ESC + "\b") {
|
|
455
|
+
// backspace or ctrl+h
|
|
456
|
+
key.name = "backspace";
|
|
457
|
+
key.meta = s.charAt(0) === ESC;
|
|
458
|
+
} else if (s === DEL || s === ESC + DEL) {
|
|
459
|
+
// backspace
|
|
460
|
+
key.name = "backspace";
|
|
461
|
+
key.meta = s.charAt(0) === ESC;
|
|
462
|
+
} else if (s === ESC || s === ESC + ESC) {
|
|
463
|
+
// escape key
|
|
464
|
+
key.name = "escape";
|
|
465
|
+
key.meta = s.length === 2;
|
|
466
|
+
} else if (s === " " || s === ESC + " ") {
|
|
467
|
+
key.name = "space";
|
|
468
|
+
key.meta = s.length === 2;
|
|
469
|
+
} else if (s.length === 1 && s <= "\x1a") {
|
|
470
|
+
// ctrl+letter
|
|
471
|
+
key.name = String.fromCharCode(s.charCodeAt(0) + "a".charCodeAt(0) - 1);
|
|
472
|
+
key.ctrl = true;
|
|
473
|
+
} else if (s.length === 1 && s >= "0" && s <= "9") {
|
|
474
|
+
// number
|
|
475
|
+
key.name = "number";
|
|
476
|
+
} else if (s.length === 1 && s >= "a" && s <= "z") {
|
|
477
|
+
// lowercase letter
|
|
478
|
+
key.name = s;
|
|
479
|
+
} else if (s.length === 1 && s >= "A" && s <= "Z") {
|
|
480
|
+
// shift+letter
|
|
481
|
+
key.name = s.toLowerCase();
|
|
482
|
+
key.shift = true;
|
|
483
|
+
} else if ((parts = metaKeyCodeRe.exec(s))) {
|
|
484
|
+
// meta+character key
|
|
485
|
+
key.name = parts[1]!.toLowerCase();
|
|
486
|
+
key.meta = true;
|
|
487
|
+
key.shift = /^[A-Z]$/.test(parts[1]!);
|
|
488
|
+
} else if ((parts = fnKeyRe.exec(s))) {
|
|
489
|
+
// oxlint-disable-next-line typescript/no-misused-spread
|
|
490
|
+
const segs = [...s];
|
|
491
|
+
|
|
492
|
+
if (segs[0] === ESC && segs[1] === ESC) {
|
|
493
|
+
key.meta = true;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ansi escape sequence
|
|
497
|
+
// reassemble the key code leaving out leading \x1b's,
|
|
498
|
+
// the modifier key bitflag and any meaningless "1;" sequence
|
|
499
|
+
const code = [parts[1], parts[2], parts[4], parts[6]].filter(Boolean).join("");
|
|
500
|
+
|
|
501
|
+
const modifier = Number(parts[3] || parts[5] || 1) - 1;
|
|
502
|
+
|
|
503
|
+
// Parse the key modifier
|
|
504
|
+
key.ctrl = !!(modifier & 4);
|
|
505
|
+
key.meta = key.meta || !!(modifier & 10);
|
|
506
|
+
key.shift = !!(modifier & 1);
|
|
507
|
+
key.code = code;
|
|
508
|
+
|
|
509
|
+
key.name = keyName[code] ?? "";
|
|
510
|
+
key.shift = isShiftKey(code) || key.shift;
|
|
511
|
+
key.ctrl = isCtrlKey(code) || key.ctrl;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return key;
|
|
515
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Derived from `stack-utils` (MIT, Isaac Z. Schlueter, James Talmage),
|
|
2
|
+
// reduced to the `parseLine()` surface Ink's error overview uses.
|
|
3
|
+
|
|
4
|
+
export type ParsedStackLine = {
|
|
5
|
+
line?: number;
|
|
6
|
+
column?: number;
|
|
7
|
+
file?: string;
|
|
8
|
+
function?: string;
|
|
9
|
+
method?: string;
|
|
10
|
+
native?: boolean;
|
|
11
|
+
evalOrigin?: string;
|
|
12
|
+
evalFile?: string;
|
|
13
|
+
evalLine?: number;
|
|
14
|
+
evalColumn?: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const lineRegExp = new RegExp(
|
|
18
|
+
"^" +
|
|
19
|
+
// Sometimes we strip out the ' at' because it's noisy
|
|
20
|
+
String.raw`(?:\s*at )?` +
|
|
21
|
+
// $1 = ctor if 'new'
|
|
22
|
+
"(?:(new) )?" +
|
|
23
|
+
// $2 = function name (can be literally anything). May contain method at
|
|
24
|
+
// the end as [as xyz]
|
|
25
|
+
String.raw`(?:(.*?) \()?` +
|
|
26
|
+
// (eval at <anonymous> (file.js:1:1),
|
|
27
|
+
// $3 = eval origin; $4:$5:$6 are eval file/line/col
|
|
28
|
+
String.raw`(?:eval at ([^ ]+) \((.+?):(\d+):(\d+)\), )?` +
|
|
29
|
+
// File:line:col: $7:$8:$9; $10 = 'native' if native
|
|
30
|
+
String.raw`(?:(.+?):(\d+):(\d+)|(native))` +
|
|
31
|
+
// Maybe close the paren, then end. If $11 is ), then we only allow
|
|
32
|
+
// balanced parens in the filename; any imbalance is placed on the fname.
|
|
33
|
+
String.raw`(\)?)$`,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const methodRegExp = /^(.*?) \[as (.*?)]$/;
|
|
37
|
+
|
|
38
|
+
const cwd = process.cwd().replaceAll("\\", "/");
|
|
39
|
+
|
|
40
|
+
const setFile = (result: ParsedStackLine, filename: string): void => {
|
|
41
|
+
if (filename) {
|
|
42
|
+
filename = filename.replaceAll("\\", "/");
|
|
43
|
+
|
|
44
|
+
if (filename.startsWith(`${cwd}/`)) {
|
|
45
|
+
filename = filename.slice(cwd.length + 1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
result.file = filename;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const parseStackLine = (line: string): ParsedStackLine | undefined => {
|
|
53
|
+
const match = lineRegExp.exec(line);
|
|
54
|
+
|
|
55
|
+
if (!match) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let functionName = match[2];
|
|
60
|
+
const evalOrigin = match[3];
|
|
61
|
+
const evalFile = match[4];
|
|
62
|
+
const evalLine = Number(match[5]);
|
|
63
|
+
const evalColumn = Number(match[6]);
|
|
64
|
+
let file = match[7];
|
|
65
|
+
const lineNumber = match[8];
|
|
66
|
+
const columnNumber = match[9];
|
|
67
|
+
const native = match[10] === "native";
|
|
68
|
+
const closeParen = match[11] === ")";
|
|
69
|
+
let method;
|
|
70
|
+
|
|
71
|
+
const result: ParsedStackLine = {};
|
|
72
|
+
|
|
73
|
+
if (lineNumber) {
|
|
74
|
+
result.line = Number(lineNumber);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (columnNumber) {
|
|
78
|
+
result.column = Number(columnNumber);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (closeParen && file) {
|
|
82
|
+
// Make sure parens are balanced. If we have a file like
|
|
83
|
+
// "asdf) [as foo] (xyz.js", then odds are that the fname should be
|
|
84
|
+
// += " (asdf) [as foo]" and the file should be just "xyz.js". Walk
|
|
85
|
+
// backwards from the end to find the last unbalanced (.
|
|
86
|
+
let closes = 0;
|
|
87
|
+
|
|
88
|
+
for (let index = file.length - 1; index > 0; index--) {
|
|
89
|
+
if (file.charAt(index) === ")") {
|
|
90
|
+
closes++;
|
|
91
|
+
} else if (file.charAt(index) === "(" && file.charAt(index - 1) === " ") {
|
|
92
|
+
closes--;
|
|
93
|
+
|
|
94
|
+
if (closes === -1 && file.charAt(index - 1) === " ") {
|
|
95
|
+
const before = file.slice(0, index - 1);
|
|
96
|
+
const after = file.slice(index + 1);
|
|
97
|
+
file = after;
|
|
98
|
+
functionName += ` (${before}`;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (functionName) {
|
|
106
|
+
const methodMatch = methodRegExp.exec(functionName);
|
|
107
|
+
|
|
108
|
+
if (methodMatch) {
|
|
109
|
+
functionName = methodMatch[1];
|
|
110
|
+
method = methodMatch[2];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (file) {
|
|
115
|
+
setFile(result, file);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (evalOrigin) {
|
|
119
|
+
result.evalOrigin = evalOrigin;
|
|
120
|
+
result.evalLine = evalLine;
|
|
121
|
+
result.evalColumn = evalColumn;
|
|
122
|
+
result.evalFile = evalFile?.replaceAll("\\", "/");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (native) {
|
|
126
|
+
result.native = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (functionName) {
|
|
130
|
+
result.function = functionName;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (method && functionName !== method) {
|
|
134
|
+
result.method = method;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
};
|