@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.11

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 (223) hide show
  1. package/README.md +499 -313
  2. package/THIRD_PARTY_NOTICES.md +98 -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 +3211 -0
  27. package/dist/jsx-dev-runtime.d.ts +2 -0
  28. package/dist/jsx-dev-runtime.js +227 -0
  29. package/dist/jsx-runtime-XtwIVFsg.js +255 -0
  30. package/dist/jsx-runtime.d.ts +2 -0
  31. package/dist/jsx-runtime.js +8 -0
  32. package/dist/osc-BFKKSqpg.js +71 -0
  33. package/dist/osc-Cn0fw77g.d.ts +23 -0
  34. package/dist/paint-Cx-zC_sX.d.ts +81 -0
  35. package/dist/query-BNc2B8GD.d.ts +152 -0
  36. package/dist/react-CTZ_8dwh.js +1197 -0
  37. package/dist/react.d.ts +2 -0
  38. package/dist/react.js +49 -0
  39. package/dist/router.d.ts +392 -0
  40. package/dist/router.js +711 -0
  41. package/dist/sample-Cqw1bjUL.js +445 -0
  42. package/dist/screen-CgC2WlVM.d.ts +49 -0
  43. package/dist/screen-CiPytswf.js +342 -0
  44. package/dist/screen.d.ts +5 -0
  45. package/dist/screen.js +5 -0
  46. package/dist/semantic-text-style-DIMzC7xt.js +91 -0
  47. package/dist/serialize-BTkAZgw1.js +79 -0
  48. package/dist/session-DDQ5V300.js +723 -0
  49. package/dist/sgr-BhwaWAJB.js +246 -0
  50. package/dist/store-C1P5fOUi.d.ts +72 -0
  51. package/dist/string-width-CijQwpIk.js +69 -0
  52. package/dist/strip-BvU4toXG.js +6 -0
  53. package/dist/terminal.d.ts +121 -0
  54. package/dist/terminal.js +2 -0
  55. package/dist/tokenize-AjqbvtiT.js +1242 -0
  56. package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
  57. package/dist/truncate-D31fhU6i.js +562 -0
  58. package/dist/use-focus-B5npZrJU.js +18736 -0
  59. package/dist/yoga-5jKhYCJC.js +3465 -0
  60. package/dist/yoga.d.ts +2 -0
  61. package/dist/yoga.js +2 -0
  62. package/package.json +89 -29
  63. package/src/ansi/chalk.ts +138 -0
  64. package/src/ansi/cursor.ts +46 -0
  65. package/src/ansi/east-asian-width.ts +259 -0
  66. package/src/ansi/escapes.ts +120 -0
  67. package/src/ansi/graphemes.ts +8 -0
  68. package/src/ansi/hyperlink.ts +44 -0
  69. package/src/ansi/index.ts +27 -0
  70. package/src/ansi/osc.ts +77 -0
  71. package/src/ansi/sgr.ts +234 -0
  72. package/src/ansi/slice.ts +43 -0
  73. package/src/ansi/string-width.ts +121 -0
  74. package/src/ansi/strip.ts +13 -0
  75. package/src/ansi/tokenize.ts +380 -0
  76. package/src/ansi/truncate.ts +196 -0
  77. package/src/ansi/wrap.ts +765 -0
  78. package/src/ansi-tokenizer.ts +510 -0
  79. package/src/capabilities/color-policy.ts +34 -0
  80. package/src/capabilities/detect.ts +608 -0
  81. package/src/capabilities/index.ts +37 -0
  82. package/src/capabilities/query.ts +679 -0
  83. package/src/capabilities/store.ts +394 -0
  84. package/src/code-excerpt.ts +39 -0
  85. package/src/color/index.ts +3 -0
  86. package/src/color/paint.ts +169 -0
  87. package/src/color/palette.ts +48 -0
  88. package/src/color/sample.ts +323 -0
  89. package/src/color.ts +1 -0
  90. package/src/components/AccessibilityContext.ts +5 -0
  91. package/src/components/AnimationContext.ts +24 -0
  92. package/src/components/AnsiText.tsx +42 -0
  93. package/src/components/App.tsx +878 -0
  94. package/src/components/AppContext.ts +111 -0
  95. package/src/components/BackgroundContext.ts +7 -0
  96. package/src/components/Box.tsx +100 -0
  97. package/src/components/CursorContext.ts +19 -0
  98. package/src/components/ErrorBoundary.tsx +39 -0
  99. package/src/components/ErrorOverview.tsx +135 -0
  100. package/src/components/FocusContext.ts +30 -0
  101. package/src/components/Hyperlink.tsx +56 -0
  102. package/src/components/Newline.tsx +16 -0
  103. package/src/components/Spacer.tsx +11 -0
  104. package/src/components/Static.tsx +60 -0
  105. package/src/components/StderrContext.ts +24 -0
  106. package/src/components/StdinContext.ts +48 -0
  107. package/src/components/StdoutContext.ts +26 -0
  108. package/src/components/TerminalOscContext.ts +25 -0
  109. package/src/components/Text.tsx +122 -0
  110. package/src/components/Transform.tsx +38 -0
  111. package/src/components/VirtualList.tsx +128 -0
  112. package/src/cursor-position.ts +103 -0
  113. package/src/devtools.ts +103 -0
  114. package/src/dom.ts +301 -0
  115. package/src/env.ts +12 -0
  116. package/src/get-max-width.ts +11 -0
  117. package/src/global.d.ts +38 -0
  118. package/src/glyphs.ts +99 -0
  119. package/src/hooks/use-animation.ts +142 -0
  120. package/src/hooks/use-app.ts +8 -0
  121. package/src/hooks/use-box-metrics.ts +134 -0
  122. package/src/hooks/use-capabilities.ts +73 -0
  123. package/src/hooks/use-cursor.ts +33 -0
  124. package/src/hooks/use-focus-manager.ts +62 -0
  125. package/src/hooks/use-focus.ts +82 -0
  126. package/src/hooks/use-input.ts +267 -0
  127. package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
  128. package/src/hooks/use-paste.ts +78 -0
  129. package/src/hooks/use-stderr.ts +8 -0
  130. package/src/hooks/use-stdin.ts +10 -0
  131. package/src/hooks/use-stdout.ts +8 -0
  132. package/src/hooks/use-terminal-osc.ts +59 -0
  133. package/src/hooks/use-virtual-scroll.ts +84 -0
  134. package/src/hooks/use-window-size.ts +37 -0
  135. package/src/index.ts +96 -0
  136. package/src/ink.tsx +1452 -0
  137. package/src/input-parser.ts +303 -0
  138. package/src/instances.ts +9 -0
  139. package/src/jsx-dev-runtime.ts +3 -0
  140. package/src/jsx-runtime.ts +6 -0
  141. package/src/kitty-keyboard.ts +185 -0
  142. package/src/measure-element.ts +62 -0
  143. package/src/measure-text.ts +31 -0
  144. package/src/paint-tree.ts +220 -0
  145. package/src/parse-keypress.ts +515 -0
  146. package/src/parse-stack-line.ts +138 -0
  147. package/src/patch-console.ts +106 -0
  148. package/src/quick-lru.ts +85 -0
  149. package/src/react.ts +53 -0
  150. package/src/reconciler.ts +476 -0
  151. package/src/render-background.ts +59 -0
  152. package/src/render-border.ts +167 -0
  153. package/src/render-frame.ts +83 -0
  154. package/src/render-to-string.ts +146 -0
  155. package/src/render.ts +284 -0
  156. package/src/router/components.tsx +343 -0
  157. package/src/router/context.ts +41 -0
  158. package/src/router/history.ts +194 -0
  159. package/src/router/hooks.tsx +391 -0
  160. package/src/router/index.ts +34 -0
  161. package/src/router/matcher.ts +571 -0
  162. package/src/sanitize-ansi.ts +33 -0
  163. package/src/screen/ansi.ts +184 -0
  164. package/src/screen/canvas.ts +160 -0
  165. package/src/screen/cell.ts +138 -0
  166. package/src/screen/color-profile.ts +47 -0
  167. package/src/screen/geometry.ts +9 -0
  168. package/src/screen/index.ts +6 -0
  169. package/src/screen/screen.ts +305 -0
  170. package/src/screen/serialize.ts +129 -0
  171. package/src/screen.ts +1 -0
  172. package/src/semantic-text-style.ts +118 -0
  173. package/src/signal-exit.ts +106 -0
  174. package/src/squash-text-nodes.ts +37 -0
  175. package/src/stream.ts +28 -0
  176. package/src/structured-text.ts +325 -0
  177. package/src/styles.ts +753 -0
  178. package/src/terminal/index.ts +2 -0
  179. package/src/terminal/inline-presenter.ts +120 -0
  180. package/src/terminal/input.ts +92 -0
  181. package/src/terminal/render-scheduler.ts +37 -0
  182. package/src/terminal/screen-presenter.ts +242 -0
  183. package/src/terminal/session.ts +408 -0
  184. package/src/terminal-size.ts +58 -0
  185. package/src/terminal.ts +1 -0
  186. package/src/testing/browser.ts +588 -0
  187. package/src/testing/emulators.ts +205 -0
  188. package/src/testing/explorer-app/index.html +12 -0
  189. package/src/testing/explorer-app/main.ts +381 -0
  190. package/src/testing/explorer-app/style.css +194 -0
  191. package/src/testing/explorer-app/tsconfig.json +15 -0
  192. package/src/testing/explorer-app/vite-env.d.ts +1 -0
  193. package/src/testing/index.ts +26 -0
  194. package/src/testing/keys.ts +56 -0
  195. package/src/testing/live.ts +85 -0
  196. package/src/testing/matchers.ts +70 -0
  197. package/src/testing/public.ts +94 -0
  198. package/src/testing/terminal.ts +361 -0
  199. package/src/testing/vitest.ts +157 -0
  200. package/src/throttle.ts +73 -0
  201. package/src/transform-adapter.ts +14 -0
  202. package/src/types.ts +10 -0
  203. package/src/virtual-scroll.ts +133 -0
  204. package/src/wrap-text.ts +54 -0
  205. package/src/yoga/config.ts +57 -0
  206. package/src/yoga/core/absoluteLayout.ts +626 -0
  207. package/src/yoga/core/baseline.ts +66 -0
  208. package/src/yoga/core/cache.ts +136 -0
  209. package/src/yoga/core/calculateLayout.ts +2926 -0
  210. package/src/yoga/core/config.ts +104 -0
  211. package/src/yoga/core/flexLine.ts +177 -0
  212. package/src/yoga/core/helpers.ts +293 -0
  213. package/src/yoga/core/layoutResults.ts +167 -0
  214. package/src/yoga/core/node.ts +611 -0
  215. package/src/yoga/core/numeric.ts +44 -0
  216. package/src/yoga/core/pixelGrid.ts +151 -0
  217. package/src/yoga/core/style.ts +887 -0
  218. package/src/yoga/core/types.ts +224 -0
  219. package/src/yoga/generated/YGEnums.ts +263 -0
  220. package/src/yoga/index.ts +19 -0
  221. package/src/yoga/node.ts +1140 -0
  222. package/dist/index.d.mts +0 -2379
  223. package/dist/index.mjs +0 -10072
@@ -0,0 +1,510 @@
1
+ import { BEL, C1_CSI, C1_ST, ESC } from "#/ansi/escapes.ts";
2
+
3
+ const bellCharacter = BEL;
4
+ const escapeCharacter = ESC;
5
+ const stringTerminatorCharacter = C1_ST;
6
+ const csiCharacter = C1_CSI;
7
+ const oscCharacter = "\u009D";
8
+ const dcsCharacter = "\u0090";
9
+ const pmCharacter = "\u009E";
10
+ const apcCharacter = "\u009F";
11
+ const sosCharacter = "\u0098";
12
+
13
+ type ControlStringType = "osc" | "dcs" | "pm" | "apc" | "sos";
14
+
15
+ type CsiToken = {
16
+ readonly type: "csi";
17
+ readonly value: string;
18
+ readonly parameterString: string;
19
+ readonly intermediateString: string;
20
+ readonly finalCharacter: string;
21
+ };
22
+
23
+ type EscToken = {
24
+ readonly type: "esc";
25
+ readonly value: string;
26
+ readonly intermediateString: string;
27
+ readonly finalCharacter: string;
28
+ };
29
+
30
+ type ControlStringToken = {
31
+ readonly type: ControlStringType;
32
+ readonly value: string;
33
+ };
34
+
35
+ type TextToken = {
36
+ readonly type: "text";
37
+ readonly value: string;
38
+ };
39
+
40
+ type StToken = {
41
+ readonly type: "st";
42
+ readonly value: string;
43
+ };
44
+
45
+ type C1Token = {
46
+ readonly type: "c1";
47
+ readonly value: string;
48
+ };
49
+
50
+ type InvalidToken = {
51
+ readonly type: "invalid";
52
+ readonly value: string;
53
+ };
54
+
55
+ export type AnsiToken =
56
+ | TextToken
57
+ | CsiToken
58
+ | EscToken
59
+ | ControlStringToken
60
+ | StToken
61
+ | C1Token
62
+ | InvalidToken;
63
+
64
+ const isCsiParameterCharacter = (character: string): boolean => {
65
+ const codePoint = character.codePointAt(0);
66
+
67
+ return codePoint !== undefined && codePoint >= 0x30 && codePoint <= 0x3f;
68
+ };
69
+
70
+ const isCsiIntermediateCharacter = (character: string): boolean => {
71
+ const codePoint = character.codePointAt(0);
72
+
73
+ return codePoint !== undefined && codePoint >= 0x20 && codePoint <= 0x2f;
74
+ };
75
+
76
+ const isCsiFinalCharacter = (character: string): boolean => {
77
+ const codePoint = character.codePointAt(0);
78
+
79
+ return codePoint !== undefined && codePoint >= 0x40 && codePoint <= 0x7e;
80
+ };
81
+
82
+ const isEscapeIntermediateCharacter = (character: string): boolean => {
83
+ const codePoint = character.codePointAt(0);
84
+
85
+ return codePoint !== undefined && codePoint >= 0x20 && codePoint <= 0x2f;
86
+ };
87
+
88
+ const isEscapeFinalCharacter = (character: string): boolean => {
89
+ const codePoint = character.codePointAt(0);
90
+
91
+ return codePoint !== undefined && codePoint >= 0x30 && codePoint <= 0x7e;
92
+ };
93
+
94
+ const isC1ControlCharacter = (character: string): boolean => {
95
+ const codePoint = character.codePointAt(0);
96
+
97
+ return codePoint !== undefined && codePoint >= 0x80 && codePoint <= 0x9f;
98
+ };
99
+
100
+ // Standards references:
101
+ // ECMA-48 control functions and CSI byte classes: https://ecma-international.org/publications-and-standards/standards/ecma-48/
102
+ // xterm CSI parameter/intermediate/final format notes: https://invisible-island.net/xterm/ecma-48-parameter-format.html
103
+ // xterm/OSC BEL termination behavior: https://davidrg.github.io/ckwin/dev/ctlseqs.html
104
+ const readCsiSequence = (
105
+ text: string,
106
+ fromIndex: number,
107
+ ):
108
+ | {
109
+ readonly endIndex: number;
110
+ readonly parameterString: string;
111
+ readonly intermediateString: string;
112
+ readonly finalCharacter: string;
113
+ }
114
+ | undefined => {
115
+ let index = fromIndex;
116
+
117
+ while (index < text.length) {
118
+ const character = text[index]!;
119
+
120
+ if (!isCsiParameterCharacter(character)) {
121
+ break;
122
+ }
123
+
124
+ index++;
125
+ }
126
+
127
+ const parameterString = text.slice(fromIndex, index);
128
+ const intermediateStartIndex = index;
129
+
130
+ while (index < text.length) {
131
+ const character = text[index]!;
132
+
133
+ if (!isCsiIntermediateCharacter(character)) {
134
+ break;
135
+ }
136
+
137
+ index++;
138
+ }
139
+
140
+ const intermediateString = text.slice(intermediateStartIndex, index);
141
+ const finalCharacter = text[index];
142
+
143
+ if (finalCharacter === undefined || !isCsiFinalCharacter(finalCharacter)) {
144
+ return;
145
+ }
146
+
147
+ return {
148
+ endIndex: index + 1,
149
+ parameterString,
150
+ intermediateString,
151
+ finalCharacter,
152
+ };
153
+ };
154
+
155
+ const findControlStringTerminatorIndex = (
156
+ text: string,
157
+ fromIndex: number,
158
+ allowBellTerminator: boolean,
159
+ ): number | undefined => {
160
+ for (let index = fromIndex; index < text.length; index++) {
161
+ const character = text[index];
162
+
163
+ if (allowBellTerminator && character === bellCharacter) {
164
+ return index + 1;
165
+ }
166
+
167
+ if (character === stringTerminatorCharacter) {
168
+ return index + 1;
169
+ }
170
+
171
+ if (character === escapeCharacter) {
172
+ const followingCharacter = text[index + 1];
173
+
174
+ // Tmux escapes ESC bytes in payload as ESC ESC.
175
+ if (followingCharacter === escapeCharacter) {
176
+ index++;
177
+ continue;
178
+ }
179
+
180
+ if (followingCharacter === "\\") {
181
+ return index + 2;
182
+ }
183
+ }
184
+ }
185
+
186
+ return;
187
+ };
188
+
189
+ const readEscapeSequence = (
190
+ text: string,
191
+ fromIndex: number,
192
+ ):
193
+ | {
194
+ readonly endIndex: number;
195
+ readonly intermediateString: string;
196
+ readonly finalCharacter: string;
197
+ }
198
+ | undefined => {
199
+ let index = fromIndex;
200
+
201
+ while (index < text.length) {
202
+ const character = text[index]!;
203
+
204
+ if (!isEscapeIntermediateCharacter(character)) {
205
+ break;
206
+ }
207
+
208
+ index++;
209
+ }
210
+
211
+ const intermediateString = text.slice(fromIndex, index);
212
+ const finalCharacter = text[index];
213
+
214
+ if (finalCharacter === undefined || !isEscapeFinalCharacter(finalCharacter)) {
215
+ return;
216
+ }
217
+
218
+ return {
219
+ endIndex: index + 1,
220
+ intermediateString,
221
+ finalCharacter,
222
+ };
223
+ };
224
+
225
+ // Centralize control-string rules so ESC and C1 paths do not diverge.
226
+ const getControlStringFromEscapeIntroducer = (
227
+ character: string,
228
+ ):
229
+ | {
230
+ readonly type: ControlStringType;
231
+ readonly allowBellTerminator: boolean;
232
+ }
233
+ | undefined => {
234
+ switch (character) {
235
+ case "]": {
236
+ return { type: "osc", allowBellTerminator: true };
237
+ }
238
+
239
+ case "P": {
240
+ return { type: "dcs", allowBellTerminator: false };
241
+ }
242
+
243
+ case "^": {
244
+ return { type: "pm", allowBellTerminator: false };
245
+ }
246
+
247
+ case "_": {
248
+ return { type: "apc", allowBellTerminator: false };
249
+ }
250
+
251
+ case "X": {
252
+ return { type: "sos", allowBellTerminator: false };
253
+ }
254
+
255
+ default: {
256
+ return;
257
+ }
258
+ }
259
+ };
260
+
261
+ const getControlStringFromC1Introducer = (
262
+ character: string,
263
+ ):
264
+ | {
265
+ readonly type: ControlStringType;
266
+ readonly allowBellTerminator: boolean;
267
+ }
268
+ | undefined => {
269
+ switch (character) {
270
+ case oscCharacter: {
271
+ return { type: "osc", allowBellTerminator: true };
272
+ }
273
+
274
+ case dcsCharacter: {
275
+ return { type: "dcs", allowBellTerminator: false };
276
+ }
277
+
278
+ case pmCharacter: {
279
+ return { type: "pm", allowBellTerminator: false };
280
+ }
281
+
282
+ case apcCharacter: {
283
+ return { type: "apc", allowBellTerminator: false };
284
+ }
285
+
286
+ case sosCharacter: {
287
+ return { type: "sos", allowBellTerminator: false };
288
+ }
289
+
290
+ default: {
291
+ return;
292
+ }
293
+ }
294
+ };
295
+
296
+ export const hasAnsiControlCharacters = (text: string): boolean => {
297
+ if (text.includes(escapeCharacter)) {
298
+ return true;
299
+ }
300
+
301
+ for (const character of text) {
302
+ if (isC1ControlCharacter(character)) {
303
+ return true;
304
+ }
305
+ }
306
+
307
+ return false;
308
+ };
309
+
310
+ const malformedFromIndex = (
311
+ tokens: AnsiToken[],
312
+ text: string,
313
+ textStartIndex: number,
314
+ fromIndex: number,
315
+ ): AnsiToken[] => {
316
+ if (fromIndex > textStartIndex) {
317
+ tokens.push({ type: "text", value: text.slice(textStartIndex, fromIndex) });
318
+ }
319
+
320
+ // Treat the remainder as invalid so callers can drop it as one unsafe unit.
321
+ tokens.push({ type: "invalid", value: text.slice(fromIndex) });
322
+
323
+ return tokens;
324
+ };
325
+
326
+ export const tokenizeAnsi = (text: string): AnsiToken[] => {
327
+ if (!hasAnsiControlCharacters(text)) {
328
+ return [{ type: "text", value: text }];
329
+ }
330
+
331
+ const tokens: AnsiToken[] = [];
332
+ let textStartIndex = 0;
333
+
334
+ for (let index = 0; index < text.length;) {
335
+ const character = text[index];
336
+
337
+ if (character === undefined) {
338
+ break;
339
+ }
340
+
341
+ if (character === escapeCharacter) {
342
+ const followingCharacter = text[index + 1];
343
+
344
+ if (followingCharacter === undefined) {
345
+ return malformedFromIndex(tokens, text, textStartIndex, index);
346
+ }
347
+
348
+ if (followingCharacter === "[") {
349
+ const csiSequence = readCsiSequence(text, index + 2);
350
+
351
+ if (csiSequence === undefined) {
352
+ return malformedFromIndex(tokens, text, textStartIndex, index);
353
+ }
354
+
355
+ if (index > textStartIndex) {
356
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
357
+ }
358
+
359
+ tokens.push({
360
+ type: "csi",
361
+ value: text.slice(index, csiSequence.endIndex),
362
+ parameterString: csiSequence.parameterString,
363
+ intermediateString: csiSequence.intermediateString,
364
+ finalCharacter: csiSequence.finalCharacter,
365
+ });
366
+ index = csiSequence.endIndex;
367
+ textStartIndex = index;
368
+ continue;
369
+ }
370
+
371
+ const escapeControlString = getControlStringFromEscapeIntroducer(followingCharacter);
372
+
373
+ if (escapeControlString !== undefined) {
374
+ const controlStringTerminatorIndex = findControlStringTerminatorIndex(
375
+ text,
376
+ index + 2,
377
+ escapeControlString.allowBellTerminator,
378
+ );
379
+
380
+ if (controlStringTerminatorIndex === undefined) {
381
+ return malformedFromIndex(tokens, text, textStartIndex, index);
382
+ }
383
+
384
+ if (index > textStartIndex) {
385
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
386
+ }
387
+
388
+ tokens.push({
389
+ type: escapeControlString.type,
390
+ value: text.slice(index, controlStringTerminatorIndex),
391
+ });
392
+ index = controlStringTerminatorIndex;
393
+ textStartIndex = index;
394
+ continue;
395
+ }
396
+
397
+ const escapeSequence = readEscapeSequence(text, index + 1);
398
+
399
+ if (escapeSequence === undefined) {
400
+ // Incomplete escape sequences with intermediates are malformed control strings.
401
+ if (isEscapeIntermediateCharacter(followingCharacter)) {
402
+ return malformedFromIndex(tokens, text, textStartIndex, index);
403
+ }
404
+
405
+ if (index > textStartIndex) {
406
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
407
+ }
408
+
409
+ // Ignore lone ESC and continue tokenizing the rest.
410
+ index++;
411
+ textStartIndex = index;
412
+ continue;
413
+ }
414
+
415
+ if (index > textStartIndex) {
416
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
417
+ }
418
+
419
+ tokens.push({
420
+ type: "esc",
421
+ value: text.slice(index, escapeSequence.endIndex),
422
+ intermediateString: escapeSequence.intermediateString,
423
+ finalCharacter: escapeSequence.finalCharacter,
424
+ });
425
+ index = escapeSequence.endIndex;
426
+ textStartIndex = index;
427
+ continue;
428
+ }
429
+
430
+ if (character === csiCharacter) {
431
+ const csiSequence = readCsiSequence(text, index + 1);
432
+
433
+ if (csiSequence === undefined) {
434
+ return malformedFromIndex(tokens, text, textStartIndex, index);
435
+ }
436
+
437
+ if (index > textStartIndex) {
438
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
439
+ }
440
+
441
+ tokens.push({
442
+ type: "csi",
443
+ value: text.slice(index, csiSequence.endIndex),
444
+ parameterString: csiSequence.parameterString,
445
+ intermediateString: csiSequence.intermediateString,
446
+ finalCharacter: csiSequence.finalCharacter,
447
+ });
448
+ index = csiSequence.endIndex;
449
+ textStartIndex = index;
450
+ continue;
451
+ }
452
+
453
+ const c1ControlString = getControlStringFromC1Introducer(character);
454
+
455
+ if (c1ControlString !== undefined) {
456
+ const controlStringTerminatorIndex = findControlStringTerminatorIndex(
457
+ text,
458
+ index + 1,
459
+ c1ControlString.allowBellTerminator,
460
+ );
461
+
462
+ if (controlStringTerminatorIndex === undefined) {
463
+ return malformedFromIndex(tokens, text, textStartIndex, index);
464
+ }
465
+
466
+ if (index > textStartIndex) {
467
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
468
+ }
469
+
470
+ tokens.push({
471
+ type: c1ControlString.type,
472
+ value: text.slice(index, controlStringTerminatorIndex),
473
+ });
474
+ index = controlStringTerminatorIndex;
475
+ textStartIndex = index;
476
+ continue;
477
+ }
478
+
479
+ if (character === stringTerminatorCharacter) {
480
+ if (index > textStartIndex) {
481
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
482
+ }
483
+
484
+ tokens.push({ type: "st", value: character });
485
+ index++;
486
+ textStartIndex = index;
487
+ continue;
488
+ }
489
+
490
+ // Strip remaining C1 controls as standalone functions.
491
+ if (isC1ControlCharacter(character)) {
492
+ if (index > textStartIndex) {
493
+ tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
494
+ }
495
+
496
+ tokens.push({ type: "c1", value: character });
497
+ index++;
498
+ textStartIndex = index;
499
+ continue;
500
+ }
501
+
502
+ index++;
503
+ }
504
+
505
+ if (textStartIndex < text.length) {
506
+ tokens.push({ type: "text", value: text.slice(textStartIndex) });
507
+ }
508
+
509
+ return tokens;
510
+ };
@@ -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
+ }