@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.
Files changed (212) hide show
  1. package/README.md +499 -313
  2. package/THIRD_PARTY_NOTICES.md +70 -23
  3. package/dist/Text-DV9CuzAT.d.ts +452 -0
  4. package/dist/ansi.d.ts +217 -0
  5. package/dist/ansi.js +87 -0
  6. package/dist/capabilities.d.ts +5 -0
  7. package/dist/capabilities.js +3 -0
  8. package/dist/cell-_ZVhbfl0.js +44 -0
  9. package/dist/color-CkbalRqK.js +2 -0
  10. package/dist/color-policy-BAC9-TZX.js +592 -0
  11. package/dist/color-policy-CCxuHIdD.d.ts +22 -0
  12. package/dist/color-profile-CyeHnG1T.d.ts +97 -0
  13. package/dist/color-profile-DHhQHY55.js +36 -0
  14. package/dist/color.d.ts +21 -0
  15. package/dist/color.js +3 -0
  16. package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
  17. package/dist/detect-B3dL4Q11.js +374 -0
  18. package/dist/detect-Db6GbKOm.d.ts +195 -0
  19. package/dist/{devtools-QpCMm9JH.mjs → devtools-DbthxoD1.js} +23 -24
  20. package/dist/env-YVw64yZS.js +9 -0
  21. package/dist/escapes-CB_6CWOE.d.ts +72 -0
  22. package/dist/geometry-BxXOzJgo.d.ts +11 -0
  23. package/dist/index-D48vQhhe.d.ts +21 -0
  24. package/dist/index-DDVME65c.d.ts +919 -0
  25. package/dist/index.d.ts +1310 -0
  26. package/dist/index.js +3209 -0
  27. package/dist/osc-BFKKSqpg.js +71 -0
  28. package/dist/osc-Cn0fw77g.d.ts +23 -0
  29. package/dist/paint-Cx-zC_sX.d.ts +81 -0
  30. package/dist/query-BNc2B8GD.d.ts +152 -0
  31. package/dist/router.d.ts +392 -0
  32. package/dist/router.js +709 -0
  33. package/dist/sample-Cqw1bjUL.js +445 -0
  34. package/dist/screen-CgC2WlVM.d.ts +49 -0
  35. package/dist/screen-CiPytswf.js +342 -0
  36. package/dist/screen.d.ts +5 -0
  37. package/dist/screen.js +5 -0
  38. package/dist/semantic-text-style-DIMzC7xt.js +91 -0
  39. package/dist/serialize-BTkAZgw1.js +79 -0
  40. package/dist/session-DDQ5V300.js +723 -0
  41. package/dist/sgr-BhwaWAJB.js +246 -0
  42. package/dist/store-C1P5fOUi.d.ts +72 -0
  43. package/dist/string-width-CijQwpIk.js +69 -0
  44. package/dist/strip-BvU4toXG.js +6 -0
  45. package/dist/terminal.d.ts +121 -0
  46. package/dist/terminal.js +2 -0
  47. package/dist/tokenize-AjqbvtiT.js +1242 -0
  48. package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
  49. package/dist/truncate-D31fhU6i.js +562 -0
  50. package/dist/use-focus-CcCIdCWA.js +1337 -0
  51. package/dist/yoga-5jKhYCJC.js +3465 -0
  52. package/dist/yoga.d.ts +2 -0
  53. package/dist/yoga.js +2 -0
  54. package/package.json +70 -24
  55. package/src/ansi/chalk.ts +138 -0
  56. package/src/ansi/cursor.ts +46 -0
  57. package/src/ansi/east-asian-width.ts +259 -0
  58. package/src/ansi/escapes.ts +120 -0
  59. package/src/ansi/graphemes.ts +8 -0
  60. package/src/ansi/hyperlink.ts +44 -0
  61. package/src/ansi/index.ts +27 -0
  62. package/src/ansi/osc.ts +77 -0
  63. package/src/ansi/sgr.ts +234 -0
  64. package/src/ansi/slice.ts +43 -0
  65. package/src/ansi/string-width.ts +121 -0
  66. package/src/ansi/strip.ts +13 -0
  67. package/src/ansi/tokenize.ts +380 -0
  68. package/src/ansi/truncate.ts +196 -0
  69. package/src/ansi/wrap.ts +765 -0
  70. package/src/ansi-tokenizer.ts +510 -0
  71. package/src/capabilities/color-policy.ts +34 -0
  72. package/src/capabilities/detect.ts +608 -0
  73. package/src/capabilities/index.ts +37 -0
  74. package/src/capabilities/query.ts +679 -0
  75. package/src/capabilities/store.ts +394 -0
  76. package/src/code-excerpt.ts +39 -0
  77. package/src/color/index.ts +3 -0
  78. package/src/color/paint.ts +169 -0
  79. package/src/color/palette.ts +48 -0
  80. package/src/color/sample.ts +323 -0
  81. package/src/color.ts +1 -0
  82. package/src/components/AccessibilityContext.ts +5 -0
  83. package/src/components/AnimationContext.ts +24 -0
  84. package/src/components/AnsiText.tsx +42 -0
  85. package/src/components/App.tsx +878 -0
  86. package/src/components/AppContext.ts +111 -0
  87. package/src/components/BackgroundContext.ts +7 -0
  88. package/src/components/Box.tsx +100 -0
  89. package/src/components/CursorContext.ts +19 -0
  90. package/src/components/ErrorBoundary.tsx +39 -0
  91. package/src/components/ErrorOverview.tsx +135 -0
  92. package/src/components/FocusContext.ts +30 -0
  93. package/src/components/Hyperlink.tsx +56 -0
  94. package/src/components/Newline.tsx +16 -0
  95. package/src/components/Spacer.tsx +11 -0
  96. package/src/components/Static.tsx +60 -0
  97. package/src/components/StderrContext.ts +24 -0
  98. package/src/components/StdinContext.ts +48 -0
  99. package/src/components/StdoutContext.ts +26 -0
  100. package/src/components/TerminalOscContext.ts +25 -0
  101. package/src/components/Text.tsx +122 -0
  102. package/src/components/Transform.tsx +38 -0
  103. package/src/components/VirtualList.tsx +128 -0
  104. package/src/cursor-position.ts +103 -0
  105. package/src/devtools.ts +103 -0
  106. package/src/dom.ts +301 -0
  107. package/src/env.ts +12 -0
  108. package/src/get-max-width.ts +11 -0
  109. package/src/global.d.ts +38 -0
  110. package/src/glyphs.ts +99 -0
  111. package/src/hooks/use-animation.ts +142 -0
  112. package/src/hooks/use-app.ts +8 -0
  113. package/src/hooks/use-box-metrics.ts +134 -0
  114. package/src/hooks/use-capabilities.ts +73 -0
  115. package/src/hooks/use-cursor.ts +33 -0
  116. package/src/hooks/use-focus-manager.ts +62 -0
  117. package/src/hooks/use-focus.ts +82 -0
  118. package/src/hooks/use-input.ts +267 -0
  119. package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
  120. package/src/hooks/use-paste.ts +78 -0
  121. package/src/hooks/use-stderr.ts +8 -0
  122. package/src/hooks/use-stdin.ts +10 -0
  123. package/src/hooks/use-stdout.ts +8 -0
  124. package/src/hooks/use-terminal-osc.ts +59 -0
  125. package/src/hooks/use-virtual-scroll.ts +84 -0
  126. package/src/hooks/use-window-size.ts +37 -0
  127. package/src/index.ts +96 -0
  128. package/src/ink.tsx +1452 -0
  129. package/src/input-parser.ts +303 -0
  130. package/src/instances.ts +9 -0
  131. package/src/kitty-keyboard.ts +185 -0
  132. package/src/measure-element.ts +62 -0
  133. package/src/measure-text.ts +31 -0
  134. package/src/paint-tree.ts +220 -0
  135. package/src/parse-keypress.ts +515 -0
  136. package/src/parse-stack-line.ts +138 -0
  137. package/src/patch-console.ts +106 -0
  138. package/src/quick-lru.ts +85 -0
  139. package/src/reconciler.ts +425 -0
  140. package/src/render-background.ts +59 -0
  141. package/src/render-border.ts +167 -0
  142. package/src/render-frame.ts +83 -0
  143. package/src/render-to-string.ts +146 -0
  144. package/src/render.ts +284 -0
  145. package/src/router/components.tsx +343 -0
  146. package/src/router/context.ts +41 -0
  147. package/src/router/history.ts +194 -0
  148. package/src/router/hooks.tsx +391 -0
  149. package/src/router/index.ts +34 -0
  150. package/src/router/matcher.ts +571 -0
  151. package/src/sanitize-ansi.ts +33 -0
  152. package/src/screen/ansi.ts +184 -0
  153. package/src/screen/canvas.ts +160 -0
  154. package/src/screen/cell.ts +138 -0
  155. package/src/screen/color-profile.ts +47 -0
  156. package/src/screen/geometry.ts +9 -0
  157. package/src/screen/index.ts +6 -0
  158. package/src/screen/screen.ts +305 -0
  159. package/src/screen/serialize.ts +129 -0
  160. package/src/screen.ts +1 -0
  161. package/src/semantic-text-style.ts +118 -0
  162. package/src/signal-exit.ts +106 -0
  163. package/src/squash-text-nodes.ts +37 -0
  164. package/src/stream.ts +28 -0
  165. package/src/structured-text.ts +325 -0
  166. package/src/styles.ts +753 -0
  167. package/src/terminal/index.ts +2 -0
  168. package/src/terminal/inline-presenter.ts +120 -0
  169. package/src/terminal/input.ts +92 -0
  170. package/src/terminal/render-scheduler.ts +37 -0
  171. package/src/terminal/screen-presenter.ts +242 -0
  172. package/src/terminal/session.ts +408 -0
  173. package/src/terminal-size.ts +58 -0
  174. package/src/terminal.ts +1 -0
  175. package/src/testing/browser.ts +588 -0
  176. package/src/testing/emulators.ts +205 -0
  177. package/src/testing/explorer-app/index.html +12 -0
  178. package/src/testing/explorer-app/main.ts +381 -0
  179. package/src/testing/explorer-app/style.css +194 -0
  180. package/src/testing/explorer-app/tsconfig.json +15 -0
  181. package/src/testing/explorer-app/vite-env.d.ts +1 -0
  182. package/src/testing/index.ts +26 -0
  183. package/src/testing/keys.ts +56 -0
  184. package/src/testing/live.ts +85 -0
  185. package/src/testing/matchers.ts +70 -0
  186. package/src/testing/public.ts +94 -0
  187. package/src/testing/terminal.ts +361 -0
  188. package/src/testing/vitest.ts +157 -0
  189. package/src/throttle.ts +73 -0
  190. package/src/transform-adapter.ts +14 -0
  191. package/src/types.ts +10 -0
  192. package/src/virtual-scroll.ts +133 -0
  193. package/src/wrap-text.ts +54 -0
  194. package/src/yoga/config.ts +57 -0
  195. package/src/yoga/core/absoluteLayout.ts +626 -0
  196. package/src/yoga/core/baseline.ts +66 -0
  197. package/src/yoga/core/cache.ts +136 -0
  198. package/src/yoga/core/calculateLayout.ts +2926 -0
  199. package/src/yoga/core/config.ts +104 -0
  200. package/src/yoga/core/flexLine.ts +177 -0
  201. package/src/yoga/core/helpers.ts +293 -0
  202. package/src/yoga/core/layoutResults.ts +167 -0
  203. package/src/yoga/core/node.ts +611 -0
  204. package/src/yoga/core/numeric.ts +44 -0
  205. package/src/yoga/core/pixelGrid.ts +151 -0
  206. package/src/yoga/core/style.ts +887 -0
  207. package/src/yoga/core/types.ts +224 -0
  208. package/src/yoga/generated/YGEnums.ts +263 -0
  209. package/src/yoga/index.ts +19 -0
  210. package/src/yoga/node.ts +1140 -0
  211. package/dist/index.d.mts +0 -2379
  212. 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
+ };