@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.
Files changed (138) hide show
  1. package/README.md +21 -9
  2. package/THIRD_PARTY_NOTICES.md +39 -1
  3. package/dist/Text-BobFKi74.d.ts +452 -0
  4. package/dist/ansi.d.ts +122 -131
  5. package/dist/ansi.js +86 -6
  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-BMzMwV7Q.d.ts +22 -0
  11. package/dist/color-policy-SVj1pYTA.js +560 -0
  12. package/dist/color-profile-DHhQHY55.js +36 -0
  13. package/dist/color-profile-u0Nhe9Nv.d.ts +97 -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-Bh4yGP6w.d.ts +186 -0
  18. package/dist/detect-BuTXtY6e.js +373 -0
  19. package/dist/env-YVw64yZS.js +9 -0
  20. package/dist/escapes-CB_6CWOE.d.ts +72 -0
  21. package/dist/geometry-BxXOzJgo.d.ts +11 -0
  22. package/dist/index-DZ88EXJv.d.ts +21 -0
  23. package/dist/index.d.ts +143 -498
  24. package/dist/index.js +1026 -2244
  25. package/dist/osc-CCH7xDoS.js +71 -0
  26. package/dist/osc-Cn0fw77g.d.ts +23 -0
  27. package/dist/paint-C19minOS.d.ts +81 -0
  28. package/dist/query-vaIeGOkH.d.ts +152 -0
  29. package/dist/router.d.ts +392 -0
  30. package/dist/router.js +709 -0
  31. package/dist/sample-Cqw1bjUL.js +445 -0
  32. package/dist/screen-BOSLQ8fF.d.ts +49 -0
  33. package/dist/screen-CiPytswf.js +342 -0
  34. package/dist/screen.d.ts +5 -0
  35. package/dist/screen.js +5 -0
  36. package/dist/semantic-text-style-DIMzC7xt.js +91 -0
  37. package/dist/serialize-BTkAZgw1.js +79 -0
  38. package/dist/session-aZr9O8h3.js +665 -0
  39. package/dist/sgr-BhwaWAJB.js +246 -0
  40. package/dist/store-CgrG9K4y.d.ts +72 -0
  41. package/dist/string-width-CijQwpIk.js +69 -0
  42. package/dist/strip-BvU4toXG.js +6 -0
  43. package/dist/terminal.d.ts +118 -0
  44. package/dist/terminal.js +2 -0
  45. package/dist/tokenize-AjqbvtiT.js +1242 -0
  46. package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
  47. package/dist/truncate-D31fhU6i.js +562 -0
  48. package/dist/use-focus-BzqAJi0n.js +1337 -0
  49. package/package.json +41 -9
  50. package/src/ansi/chalk.ts +5 -3
  51. package/src/ansi/escapes.ts +14 -0
  52. package/src/ansi/graphemes.ts +8 -0
  53. package/src/ansi/hyperlink.ts +44 -0
  54. package/src/ansi/index.ts +3 -1
  55. package/src/ansi/osc.ts +77 -0
  56. package/src/ansi/tokenize.ts +3 -4
  57. package/src/capabilities/color-policy.ts +34 -0
  58. package/src/capabilities/detect.ts +594 -0
  59. package/src/capabilities/index.ts +37 -0
  60. package/src/capabilities/query.ts +657 -0
  61. package/src/capabilities/store.ts +379 -0
  62. package/src/color/index.ts +3 -0
  63. package/src/color/paint.ts +169 -0
  64. package/src/color/palette.ts +48 -0
  65. package/src/color/sample.ts +323 -0
  66. package/src/color.ts +1 -0
  67. package/src/components/AnsiText.tsx +42 -0
  68. package/src/components/App.tsx +98 -10
  69. package/src/components/BackgroundContext.ts +2 -3
  70. package/src/components/Box.tsx +0 -8
  71. package/src/components/CursorContext.ts +1 -1
  72. package/src/components/Hyperlink.tsx +56 -0
  73. package/src/components/TerminalOscContext.ts +25 -0
  74. package/src/components/Text.tsx +22 -45
  75. package/src/components/Transform.tsx +1 -1
  76. package/src/dom.ts +11 -2
  77. package/src/global.d.ts +3 -0
  78. package/src/hooks/use-capabilities.ts +73 -0
  79. package/src/hooks/use-cursor.ts +2 -2
  80. package/src/hooks/use-terminal-osc.ts +59 -0
  81. package/src/index.ts +45 -1
  82. package/src/ink.tsx +213 -153
  83. package/src/{render-node-to-output.ts → paint-tree.ts} +75 -48
  84. package/src/reconciler.ts +24 -3
  85. package/src/render-background.ts +36 -15
  86. package/src/render-border.ts +94 -61
  87. package/src/render-frame.ts +83 -0
  88. package/src/render-to-string.ts +21 -6
  89. package/src/render.ts +15 -6
  90. package/src/router/components.tsx +343 -0
  91. package/src/router/context.ts +41 -0
  92. package/src/router/history.ts +194 -0
  93. package/src/router/hooks.tsx +391 -0
  94. package/src/router/index.ts +34 -0
  95. package/src/router/matcher.ts +571 -0
  96. package/src/screen/ansi.ts +184 -0
  97. package/src/screen/canvas.ts +160 -0
  98. package/src/screen/cell.ts +138 -0
  99. package/src/screen/color-profile.ts +47 -0
  100. package/src/screen/geometry.ts +9 -0
  101. package/src/screen/index.ts +6 -0
  102. package/src/screen/screen.ts +305 -0
  103. package/src/screen/serialize.ts +129 -0
  104. package/src/screen.ts +1 -0
  105. package/src/semantic-text-style.ts +118 -0
  106. package/src/squash-text-nodes.ts +2 -5
  107. package/src/structured-text.ts +325 -0
  108. package/src/styles.ts +19 -14
  109. package/src/terminal/index.ts +2 -0
  110. package/src/terminal/inline-presenter.ts +120 -0
  111. package/src/terminal/input.ts +86 -0
  112. package/src/terminal/render-scheduler.ts +37 -0
  113. package/src/terminal/screen-presenter.ts +188 -0
  114. package/src/terminal/session.ts +407 -0
  115. package/src/terminal.ts +1 -0
  116. package/src/testing/browser.ts +588 -0
  117. package/src/testing/emulators.ts +205 -0
  118. package/src/testing/explorer-app/index.html +12 -0
  119. package/src/testing/explorer-app/main.ts +381 -0
  120. package/src/testing/explorer-app/style.css +194 -0
  121. package/src/testing/explorer-app/tsconfig.json +15 -0
  122. package/src/testing/explorer-app/vite-env.d.ts +1 -0
  123. package/src/testing/index.ts +26 -0
  124. package/src/testing/keys.ts +56 -0
  125. package/src/testing/live.ts +85 -0
  126. package/src/testing/matchers.ts +70 -0
  127. package/src/testing/public.ts +94 -0
  128. package/src/testing/terminal.ts +349 -0
  129. package/src/testing/vitest.ts +157 -0
  130. package/src/transform-adapter.ts +14 -0
  131. package/src/wrap-text.ts +4 -0
  132. package/dist/sgr-CMfEpjSk.d.ts +0 -91
  133. package/dist/truncate-Cr6xVFMa.js +0 -2330
  134. package/src/ansi/supports-color.ts +0 -207
  135. package/src/colorize.ts +0 -60
  136. package/src/log-update.ts +0 -370
  137. package/src/output.ts +0 -308
  138. package/src/renderer.ts +0 -73
@@ -0,0 +1,560 @@
1
+ import { _ as CSI, b as OSC } from "./sgr-BhwaWAJB.js";
2
+ import { a as isTty } from "./env-YVw64yZS.js";
3
+ import { c as signalExit, n as detectCapabilities } from "./detect-BuTXtY6e.js";
4
+ import { t as colorProfileFromLevel } from "./color-profile-DHhQHY55.js";
5
+ //#region src/capabilities/query.ts
6
+ /**
7
+ The DEC private modes queried via DECRQM, by name.
8
+ */
9
+ const queriedModes = {
10
+ focusEvents: 1004,
11
+ sgrMouse: 1006,
12
+ sgrPixelMouse: 1016,
13
+ bracketedPaste: 2004,
14
+ synchronizedOutput: 2026,
15
+ graphemeClustering: 2027,
16
+ colorSchemeUpdates: 2031,
17
+ inBandResize: 2048
18
+ };
19
+ const paletteSize = 16;
20
+ const parseColorValue = (value) => {
21
+ if (value.startsWith("rgb:")) {
22
+ const parts = value.slice(4).split("/");
23
+ if (parts.length !== 3) return;
24
+ const channels = parts.map((part) => {
25
+ if (!/^[0-9a-fA-F]{1,4}$/.test(part)) return;
26
+ return Math.round(Number.parseInt(part, 16) / (16 ** part.length - 1) * 255);
27
+ });
28
+ if (channels.some((channel) => channel === void 0)) return;
29
+ return {
30
+ r: channels[0],
31
+ g: channels[1],
32
+ b: channels[2]
33
+ };
34
+ }
35
+ if (/^#[0-9a-fA-F]{6}$/.test(value)) return {
36
+ r: Number.parseInt(value.slice(1, 3), 16),
37
+ g: Number.parseInt(value.slice(3, 5), 16),
38
+ b: Number.parseInt(value.slice(5, 7), 16)
39
+ };
40
+ };
41
+ const appearanceOf = (background) => {
42
+ if (!background) return;
43
+ return .2126 * background.r + .7152 * background.g + .0722 * background.b > 127.5 ? "light" : "dark";
44
+ };
45
+ const parseXtversion = (raw) => {
46
+ const match = raw.match(/^(.+?)[(\s]v?(\d[\w.-]*)\)?$/);
47
+ if (!match) return {
48
+ raw,
49
+ name: raw.toLowerCase() || void 0,
50
+ version: void 0
51
+ };
52
+ return {
53
+ raw,
54
+ name: match[1].trim().toLowerCase(),
55
+ version: match[2]
56
+ };
57
+ };
58
+ const xtgettcapRgb = "524742";
59
+ const kittyGraphicsProbe = `_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\\`;
60
+ const oscColorResponse = new RegExp(`\\](\\d+);(?:(\\d+);)?([^]*)(?:|\\\\)`);
61
+ const kittyKeyboardResponse = new RegExp(`\\[\\?(\\d+)u`);
62
+ const decrqmResponse = new RegExp(`\\[\\?(\\d+);(\\d+)\\$y`);
63
+ const xtversionResponse = new RegExp(`P>\\|([^]*)\\\\`);
64
+ const xtgettcapResponse = new RegExp(`P([01])\\+r([^]*)\\\\`);
65
+ const kittyGraphicsResponse = new RegExp(`_G([^]*)\\\\`);
66
+ const winopsResponse = new RegExp(`\\[(4|6);(\\d+);(\\d+)t`);
67
+ const colorSchemeResponse = new RegExp(`\\[\\?997;(\\d+)n`);
68
+ const da1Response = new RegExp(`\\[\\?([\\d;]*)c`);
69
+ const buildQuery = (palette, scope) => {
70
+ const queries = [
71
+ `${OSC}10;?`,
72
+ `${OSC}11;?`,
73
+ `${OSC}12;?`
74
+ ];
75
+ if (palette) for (let index = 0; index < paletteSize; index++) queries.push(`${OSC}4;${index};?`);
76
+ if (scope === "full") {
77
+ for (const mode of Object.values(queriedModes)) queries.push(`${CSI}?${mode}$p`);
78
+ queries.push(`${CSI}?u`, `P+q${xtgettcapRgb}\\`, kittyGraphicsProbe, `${CSI}>0q`);
79
+ }
80
+ queries.push(`${CSI}14t`, `${CSI}16t`, `${CSI}?996n`, `${CSI}c`);
81
+ return queries.join("");
82
+ };
83
+ /**
84
+ Sends a batch of terminal queries and collects the responses. Resolves when
85
+ the terminal answers the DA1 sentinel, or after `timeout` with whatever was
86
+ gathered. Stdin bytes that are not query responses are pushed back into the
87
+ stream.
88
+
89
+ The query is lazy: nothing is sent until something asks. Inside an Ink app,
90
+ use `useCapabilities` — it triggers the query through Ink's input pipeline so
91
+ responses can't collide with key handling. Call this directly only outside of
92
+ Ink, before any other stdin consumer is attached.
93
+ */
94
+ const queryTerminal = async (stdin, stdout, { timeout = 500, palette = true, scope = "full" } = {}) => new Promise((resolve) => {
95
+ const result = {
96
+ foreground: void 0,
97
+ background: void 0,
98
+ cursorColor: void 0,
99
+ palette: void 0,
100
+ appearance: void 0,
101
+ systemAppearance: void 0,
102
+ kittyKeyboard: false,
103
+ kittyGraphics: false,
104
+ sixel: false,
105
+ deviceAttributes: void 0,
106
+ trueColor: false,
107
+ focusEvents: false,
108
+ sgrMouse: false,
109
+ sgrPixelMouse: false,
110
+ bracketedPaste: false,
111
+ synchronizedOutput: false,
112
+ graphemeClustering: false,
113
+ colorSchemeUpdates: false,
114
+ inBandResize: false,
115
+ textAreaPixels: void 0,
116
+ cellPixels: void 0,
117
+ terminal: void 0
118
+ };
119
+ const paletteColors = /* @__PURE__ */ new Map();
120
+ let reportedAppearance;
121
+ let buffer = "";
122
+ let receivedStrings = false;
123
+ let done = false;
124
+ const finish = () => {
125
+ if (done) return;
126
+ done = true;
127
+ clearTimeout(timer);
128
+ stdin.removeListener("data", onData);
129
+ if (paletteColors.size === paletteSize) result.palette = Array.from({ length: paletteSize }, (_, index) => paletteColors.get(index));
130
+ result.systemAppearance = reportedAppearance;
131
+ result.appearance = appearanceOf(result.background) ?? reportedAppearance;
132
+ if (buffer.length > 0) {
133
+ stdin.unshift(receivedStrings ? buffer : Buffer.from(buffer, "latin1"));
134
+ buffer = "";
135
+ }
136
+ resolve(result);
137
+ };
138
+ const consumeMatch = (pattern) => {
139
+ const match = buffer.match(pattern);
140
+ if (match) buffer = buffer.replace(pattern, "");
141
+ return match ?? void 0;
142
+ };
143
+ const consumeResponse = () => {
144
+ const oscMatch = consumeMatch(oscColorResponse);
145
+ if (oscMatch) {
146
+ const [, code, index, value] = oscMatch;
147
+ const color = parseColorValue(value);
148
+ if (color) {
149
+ if (code === "10") result.foreground = color;
150
+ else if (code === "11") result.background = color;
151
+ else if (code === "12") result.cursorColor = color;
152
+ else if (code === "4" && index !== void 0) paletteColors.set(Number.parseInt(index, 10), color);
153
+ }
154
+ return true;
155
+ }
156
+ const xtversionMatch = consumeMatch(xtversionResponse);
157
+ if (xtversionMatch) {
158
+ const raw = xtversionMatch[1].trim();
159
+ if (raw.length > 0) result.terminal = parseXtversion(raw);
160
+ return true;
161
+ }
162
+ const xtgettcapMatch = consumeMatch(xtgettcapResponse);
163
+ if (xtgettcapMatch) {
164
+ if (xtgettcapMatch[1] === "1" && xtgettcapMatch[2].includes(xtgettcapRgb)) result.trueColor = true;
165
+ return true;
166
+ }
167
+ const graphicsMatch = consumeMatch(kittyGraphicsResponse);
168
+ if (graphicsMatch) {
169
+ if (graphicsMatch[1].includes("OK")) result.kittyGraphics = true;
170
+ return true;
171
+ }
172
+ const decrqmMatch = consumeMatch(decrqmResponse);
173
+ if (decrqmMatch) {
174
+ const mode = Number.parseInt(decrqmMatch[1], 10);
175
+ const recognized = decrqmMatch[2] !== "0";
176
+ for (const [name, number] of Object.entries(queriedModes)) if (number === mode) result[name] = recognized;
177
+ return true;
178
+ }
179
+ const winopsMatch = consumeMatch(winopsResponse);
180
+ if (winopsMatch) {
181
+ const size = {
182
+ height: Number.parseInt(winopsMatch[2], 10),
183
+ width: Number.parseInt(winopsMatch[3], 10)
184
+ };
185
+ if (winopsMatch[1] === "4") result.textAreaPixels = size;
186
+ else result.cellPixels = size;
187
+ return true;
188
+ }
189
+ const colorSchemeMatch = consumeMatch(colorSchemeResponse);
190
+ if (colorSchemeMatch) {
191
+ if (colorSchemeMatch[1] === "1") reportedAppearance = "dark";
192
+ else if (colorSchemeMatch[1] === "2") reportedAppearance = "light";
193
+ return true;
194
+ }
195
+ if (consumeMatch(kittyKeyboardResponse)) {
196
+ result.kittyKeyboard = true;
197
+ return true;
198
+ }
199
+ const da1Match = consumeMatch(da1Response);
200
+ if (da1Match) {
201
+ const attributes = da1Match[1].split(";").filter((part) => part.length > 0).map((part) => Number.parseInt(part, 10));
202
+ result.deviceAttributes = attributes;
203
+ result.sixel = attributes.slice(1).includes(4);
204
+ finish();
205
+ return false;
206
+ }
207
+ return false;
208
+ };
209
+ const onData = (data) => {
210
+ receivedStrings = typeof data === "string";
211
+ buffer += typeof data === "string" ? data : Buffer.from(data).toString("latin1");
212
+ while (consumeResponse());
213
+ };
214
+ stdin.on("data", onData);
215
+ const timer = setTimeout(finish, timeout);
216
+ stdout.write(buildQuery(palette, scope));
217
+ });
218
+ const queryPromises = /* @__PURE__ */ new WeakMap();
219
+ const queryResults = /* @__PURE__ */ new WeakMap();
220
+ /**
221
+ Starts (or joins) the terminal query for a stdout stream. The result is
222
+ cached per stream — the terminal is only ever asked once.
223
+ */
224
+ const ensureTerminalQuery = (stdin, stdout, options) => {
225
+ let promise = queryPromises.get(stdout);
226
+ if (!promise) {
227
+ promise = queryTerminal(stdin, stdout, options);
228
+ promise.then((result) => queryResults.set(stdout, result));
229
+ queryPromises.set(stdout, promise);
230
+ }
231
+ return promise;
232
+ };
233
+ /**
234
+ The completed query result for a stdout stream, if the query has finished.
235
+ */
236
+ const getTerminalQuery = (stdout) => queryResults.get(stdout);
237
+ const getTerminalQueryPromise = (stdout) => queryPromises.get(stdout);
238
+ /**
239
+ Merges a partial update (from an unsolicited terminal report) into the
240
+ cached query result. Returns the merged result, or `undefined` when no query
241
+ has completed yet.
242
+ */
243
+ const patchTerminalQuery = (stdout, patch) => {
244
+ const previous = queryResults.get(stdout);
245
+ if (!previous) return;
246
+ const merged = {
247
+ ...previous,
248
+ ...patch
249
+ };
250
+ queryResults.set(stdout, merged);
251
+ queryPromises.set(stdout, Promise.resolve(merged));
252
+ return merged;
253
+ };
254
+ const refreshPromises = /* @__PURE__ */ new WeakMap();
255
+ /**
256
+ Re-asks the terminal only the dynamic questions — colors/theme and pixel
257
+ geometry, which change when the user switches themes or resizes — and merges
258
+ the answers into the cached result. Static facts (mode and protocol support,
259
+ identity) are kept from the original query. Falls back to a full query when
260
+ none has completed yet; concurrent refreshes share one round-trip.
261
+ */
262
+ const refreshTerminalQuery = (stdin, stdout, options) => {
263
+ const previous = queryResults.get(stdout);
264
+ if (!previous) return ensureTerminalQuery(stdin, stdout, options);
265
+ const inFlight = refreshPromises.get(stdout);
266
+ if (inFlight) return inFlight;
267
+ const promise = queryTerminal(stdin, stdout, {
268
+ ...options,
269
+ scope: "dynamic"
270
+ }).then((fresh) => {
271
+ const merged = {
272
+ ...previous,
273
+ foreground: fresh.foreground ?? previous.foreground,
274
+ background: fresh.background ?? previous.background,
275
+ cursorColor: fresh.cursorColor ?? previous.cursorColor,
276
+ palette: fresh.palette ?? previous.palette,
277
+ appearance: fresh.appearance ?? previous.appearance,
278
+ systemAppearance: fresh.systemAppearance ?? previous.systemAppearance,
279
+ textAreaPixels: fresh.textAreaPixels ?? previous.textAreaPixels,
280
+ cellPixels: fresh.cellPixels ?? previous.cellPixels
281
+ };
282
+ queryResults.set(stdout, merged);
283
+ queryPromises.set(stdout, Promise.resolve(merged));
284
+ refreshPromises.delete(stdout);
285
+ return merged;
286
+ });
287
+ refreshPromises.set(stdout, promise);
288
+ return promise;
289
+ };
290
+ /**
291
+ Merges an async query result into a synchronous capabilities snapshot,
292
+ producing the complete picture. The query is authoritative where it answered:
293
+ a terminal that confirms truecolor via XTGETTCAP upgrades the sniffed color
294
+ level.
295
+ */
296
+ const applyTerminalQuery = (capabilities, query) => ({
297
+ ...capabilities,
298
+ size: {
299
+ ...capabilities.size,
300
+ pixels: query.textAreaPixels || query.cellPixels ? {
301
+ textArea: query.textAreaPixels,
302
+ cell: query.cellPixels
303
+ } : void 0
304
+ },
305
+ terminal: {
306
+ ...capabilities.terminal,
307
+ name: query.terminal?.name ?? capabilities.terminal.name,
308
+ version: query.terminal?.version ?? capabilities.terminal.version
309
+ },
310
+ color: query.trueColor ? {
311
+ level: 3,
312
+ depth: 24,
313
+ trueColor: true
314
+ } : capabilities.color,
315
+ theme: {
316
+ appearance: query.appearance ?? capabilities.theme.appearance,
317
+ systemAppearance: query.systemAppearance,
318
+ foreground: query.foreground,
319
+ background: query.background,
320
+ cursor: query.cursorColor,
321
+ palette: query.palette
322
+ },
323
+ supports: {
324
+ ...capabilities.supports,
325
+ color: capabilities.supports.color || query.trueColor,
326
+ kittyKeyboard: query.kittyKeyboard,
327
+ kittyGraphics: query.kittyGraphics,
328
+ sixel: query.sixel,
329
+ focusEvents: query.focusEvents,
330
+ sgrMouse: query.sgrMouse,
331
+ sgrPixelMouse: query.sgrPixelMouse,
332
+ bracketedPaste: query.bracketedPaste,
333
+ synchronizedOutput: query.synchronizedOutput,
334
+ graphemeClustering: query.graphemeClustering,
335
+ colorSchemeUpdates: query.colorSchemeUpdates,
336
+ inBandResize: query.inBandResize
337
+ }
338
+ });
339
+ //#endregion
340
+ //#region src/stream.ts
341
+ const isRawModeStream = (stdin) => {
342
+ return isTty(stdin) && "setRawMode" in stdin && typeof stdin.setRawMode === "function";
343
+ };
344
+ const getRawModeStream = (stdin) => {
345
+ if (!isRawModeStream(stdin)) return;
346
+ return stdin;
347
+ };
348
+ //#endregion
349
+ //#region src/capabilities/store.ts
350
+ const integrations = /* @__PURE__ */ new WeakMap();
351
+ const reportingUpdaters = /* @__PURE__ */ new WeakMap();
352
+ const registerTerminalIntegration = (stdout, integration) => {
353
+ integrations.set(stdout, integration);
354
+ reportingUpdaters.get(stdout)?.();
355
+ return () => {
356
+ if (integrations.get(stdout) === integration) {
357
+ integrations.delete(stdout);
358
+ reportingUpdaters.get(stdout)?.();
359
+ }
360
+ };
361
+ };
362
+ const focusInReport = `${CSI}I`;
363
+ const focusOutReport = `${CSI}O`;
364
+ const colorSchemeReport = new RegExp(`^\\[\\?997;(\\d+)n$`);
365
+ const inBandResizeReport = new RegExp(`^\\[48;(\\d+);(\\d+);(\\d+);(\\d+)t$`);
366
+ const reportModes = [
367
+ [1004, (result) => result.focusEvents],
368
+ [2031, (result) => result.colorSchemeUpdates],
369
+ [2048, (result) => result.inBandResize]
370
+ ];
371
+ const stores = /* @__PURE__ */ new WeakMap();
372
+ const createStore = (stdin, stdout) => {
373
+ const listeners = /* @__PURE__ */ new Set();
374
+ let resizeSubscribers = 0;
375
+ let focused;
376
+ let snapshot;
377
+ let snapshotQuery;
378
+ let snapshotColumns;
379
+ let snapshotRows;
380
+ let snapshotFocused;
381
+ const current = () => {
382
+ const query = getTerminalQuery(stdout);
383
+ const { columns, rows } = stdout;
384
+ if (!snapshot || query !== snapshotQuery || columns !== snapshotColumns || rows !== snapshotRows || focused !== snapshotFocused) {
385
+ const detected = detectCapabilities({ stdout });
386
+ const applied = query ? applyTerminalQuery(detected, query) : detected;
387
+ snapshot = focused === void 0 ? applied : {
388
+ ...applied,
389
+ focused
390
+ };
391
+ snapshotQuery = query;
392
+ snapshotColumns = columns;
393
+ snapshotRows = rows;
394
+ snapshotFocused = focused;
395
+ }
396
+ return snapshot;
397
+ };
398
+ let lastNotified;
399
+ const notify = () => {
400
+ const capabilities = current();
401
+ if (capabilities === lastNotified) return;
402
+ lastNotified = capabilities;
403
+ for (const listener of listeners) listener(capabilities);
404
+ };
405
+ let reportingEnabled = false;
406
+ let removeExitHandler;
407
+ const enabledModes = () => {
408
+ const result = getTerminalQuery(stdout);
409
+ if (!result) return [];
410
+ return reportModes.filter(([, supported]) => supported(result)).map(([mode]) => mode);
411
+ };
412
+ const writeModes = (suffix) => {
413
+ const sequence = enabledModes().map((mode) => `${CSI}?${mode}${suffix}`).join("");
414
+ if (sequence.length > 0) try {
415
+ stdout.write(sequence);
416
+ } catch {}
417
+ };
418
+ const updateReporting = () => {
419
+ const integration = integrations.get(stdout);
420
+ const shouldEnable = listeners.size > 0 && integration !== void 0 && stdout.isTTY === true;
421
+ if (shouldEnable && !reportingEnabled && enabledModes().length > 0) {
422
+ reportingEnabled = true;
423
+ integration?.setReportFeed?.(true);
424
+ writeModes("h");
425
+ removeExitHandler = signalExit(() => {
426
+ writeModes("l");
427
+ });
428
+ } else if (!shouldEnable && reportingEnabled) {
429
+ reportingEnabled = false;
430
+ writeModes("l");
431
+ integration?.setReportFeed?.(false);
432
+ removeExitHandler?.();
433
+ removeExitHandler = void 0;
434
+ }
435
+ };
436
+ reportingUpdaters.set(stdout, updateReporting);
437
+ const ingest = (sequence) => {
438
+ if (sequence === focusInReport || sequence === focusOutReport) {
439
+ focused = sequence === focusInReport;
440
+ notify();
441
+ return true;
442
+ }
443
+ const colorScheme = sequence.match(colorSchemeReport);
444
+ if (colorScheme) {
445
+ const reported = colorScheme[1] === "1" ? "dark" : colorScheme[1] === "2" ? "light" : void 0;
446
+ if (reported) {
447
+ const cached = getTerminalQuery(stdout);
448
+ patchTerminalQuery(stdout, {
449
+ systemAppearance: reported,
450
+ ...cached?.background === void 0 ? { appearance: reported } : {}
451
+ });
452
+ notify();
453
+ if (integrations.has(stdout)) query().catch(() => {});
454
+ }
455
+ return true;
456
+ }
457
+ const resize = sequence.match(inBandResizeReport);
458
+ if (resize) {
459
+ const [, rows, columns, pixelHeight, pixelWidth] = resize.map(Number);
460
+ if (columns && rows) patchTerminalQuery(stdout, {
461
+ textAreaPixels: pixelWidth && pixelHeight ? {
462
+ width: pixelWidth,
463
+ height: pixelHeight
464
+ } : void 0,
465
+ cellPixels: pixelWidth && pixelHeight ? {
466
+ width: Math.round(pixelWidth / columns),
467
+ height: Math.round(pixelHeight / rows)
468
+ } : void 0
469
+ });
470
+ notify();
471
+ return true;
472
+ }
473
+ return false;
474
+ };
475
+ const runStandaloneQuery = async () => {
476
+ if (!stdout.isTTY || !stdin.isTTY) return;
477
+ const wasRaw = stdin.isRaw ?? false;
478
+ if (!wasRaw) stdin.setRawMode?.(true);
479
+ try {
480
+ await (getTerminalQuery(stdout) !== void 0 ? refreshTerminalQuery(stdin, stdout) : ensureTerminalQuery(stdin, stdout));
481
+ } finally {
482
+ if (!wasRaw) stdin.setRawMode?.(false);
483
+ }
484
+ };
485
+ const query = async () => {
486
+ const integration = integrations.get(stdout);
487
+ if (integration) await integration.runQuery({ refresh: getTerminalQuery(stdout) !== void 0 });
488
+ else await runStandaloneQuery();
489
+ notify();
490
+ updateReporting();
491
+ return current();
492
+ };
493
+ const onResize = () => {
494
+ notify();
495
+ };
496
+ const subscribe = (listener, options = {}) => {
497
+ const resizes = options.resizes ?? true;
498
+ if (resizes && resizeSubscribers++ === 0) stdout.on("resize", onResize);
499
+ listeners.add(listener);
500
+ updateReporting();
501
+ let subscribed = true;
502
+ return () => {
503
+ if (!subscribed) return;
504
+ subscribed = false;
505
+ listeners.delete(listener);
506
+ if (resizes && --resizeSubscribers === 0) stdout.removeListener("resize", onResize);
507
+ updateReporting();
508
+ };
509
+ };
510
+ return {
511
+ get current() {
512
+ return current();
513
+ },
514
+ query,
515
+ subscribe,
516
+ ingest
517
+ };
518
+ };
519
+ /**
520
+ The capabilities store for a stream pair. Stores are cached per stdout, so
521
+ standalone code and Ink components observing the same terminal share one
522
+ snapshot and one set of query answers.
523
+ */
524
+ const getCapabilities = (stdin = process.stdin, stdout = process.stdout) => {
525
+ let store = stores.get(stdout);
526
+ if (!store) {
527
+ store = createStore(stdin, stdout);
528
+ stores.set(stdout, store);
529
+ }
530
+ return store;
531
+ };
532
+ /**
533
+ The process's own terminal — the store for `process.stdin`/`process.stdout`.
534
+
535
+ ```ts
536
+ import { capabilities } from "@alchemy.run/sigil/capabilities";
537
+
538
+ capabilities.current.supports.hyperlinks;
539
+ const fresh = await capabilities.query();
540
+ const unsubscribe = capabilities.subscribe((caps) => {
541
+ console.log(caps.size, caps.theme.appearance);
542
+ });
543
+ ```
544
+ */
545
+ const capabilities = getCapabilities();
546
+ //#endregion
547
+ //#region src/capabilities/color-policy.ts
548
+ function resolveColorProfile(detectedLevel, policy = "auto") {
549
+ return policy === "auto" ? colorProfileFromLevel(detectedLevel) : policy;
550
+ }
551
+ function colorState(capabilities, policy = "auto") {
552
+ const detected = colorProfileFromLevel(capabilities.color.level);
553
+ return {
554
+ detected,
555
+ policy,
556
+ effective: policy === "auto" ? detected : policy
557
+ };
558
+ }
559
+ //#endregion
560
+ export { registerTerminalIntegration as a, ensureTerminalQuery as c, queryTerminal as d, refreshTerminalQuery as f, getCapabilities as i, getTerminalQuery as l, resolveColorProfile as n, getRawModeStream as o, capabilities as r, applyTerminalQuery as s, colorState as t, getTerminalQueryPromise as u };
@@ -0,0 +1,36 @@
1
+ import { d as rgbToAnsi, f as rgbToAnsi256, t as ansi256ToAnsi } from "./sgr-BhwaWAJB.js";
2
+ //#region src/screen/color-profile.ts
3
+ function colorProfileFromLevel(level) {
4
+ return [
5
+ "none",
6
+ "ansi16",
7
+ "ansi256",
8
+ "truecolor"
9
+ ][level];
10
+ }
11
+ /**
12
+ Quantizes a semantic color for an output profile. `undefined` means the
13
+ terminal default and is also the result for the no-color profile.
14
+ */
15
+ function quantizeColor(color, profile) {
16
+ if (!color || profile === "none") return;
17
+ if (profile === "truecolor") return color;
18
+ if (profile === "ansi256") {
19
+ if (color.model === "indexed") return color;
20
+ return {
21
+ model: "indexed",
22
+ index: rgbToAnsi256(color.red, color.green, color.blue),
23
+ encoding: "ansi256"
24
+ };
25
+ }
26
+ return {
27
+ model: "indexed",
28
+ index: ansiCodeToPaletteIndex(color.model === "indexed" ? ansi256ToAnsi(color.index) : rgbToAnsi(color.red, color.green, color.blue)),
29
+ encoding: "ansi16"
30
+ };
31
+ }
32
+ function ansiCodeToPaletteIndex(code) {
33
+ return code >= 90 ? code - 90 + 8 : code - 30;
34
+ }
35
+ //#endregion
36
+ export { quantizeColor as n, colorProfileFromLevel as t };
@@ -0,0 +1,97 @@
1
+ import { i as ColorSupportLevel } from "./detect-Bh4yGP6w.js";
2
+ //#region src/screen/cell.d.ts
3
+ /**
4
+ A terminal color before it is converted to an output profile.
5
+ */
6
+ type Color = RgbColor | IndexedColor;
7
+ type RgbColor = {
8
+ readonly model: "rgb";
9
+ readonly red: number;
10
+ readonly green: number;
11
+ readonly blue: number;
12
+ readonly alpha: number;
13
+ };
14
+ type IndexedColor = {
15
+ readonly model: "indexed";
16
+ readonly index: number;
17
+ /** Preserve an explicit palette encoding when compatibility requires it. */
18
+ readonly encoding?: "ansi16" | "ansi256";
19
+ };
20
+ /**
21
+ Bit flags for terminal text attributes. Multiple attributes may be combined.
22
+ */
23
+ declare const cellAttributes: {
24
+ readonly none: 0;
25
+ readonly bold: number;
26
+ readonly faint: number;
27
+ readonly italic: number;
28
+ readonly blink: number;
29
+ readonly rapidBlink: number;
30
+ readonly inverse: number;
31
+ readonly hidden: number;
32
+ readonly strikethrough: number;
33
+ };
34
+ type UnderlineStyle = "none" | "single" | "double" | "curly" | "dotted" | "dashed";
35
+ type CellStyle = {
36
+ readonly foreground?: Color;
37
+ readonly background?: Color;
38
+ readonly underlineColor?: Color;
39
+ readonly underline: UnderlineStyle;
40
+ readonly attributes: number;
41
+ };
42
+ type Hyperlink = {
43
+ readonly url: string;
44
+ readonly parameters?: string;
45
+ };
46
+ type CellContent = {
47
+ readonly grapheme: string;
48
+ readonly width: number;
49
+ };
50
+ /**
51
+ A compositing operation for one screen position.
52
+
53
+ An absent field is transparent and preserves the destination channel. `null`
54
+ explicitly resets a color or hyperlink to the terminal default. Supplying
55
+ `content` paints a grapheme; `{grapheme: " ", width: 1}` is therefore an
56
+ explicit blank, while an absent patch paints nothing at all.
57
+ */
58
+ type CellPatch = {
59
+ readonly content?: CellContent;
60
+ readonly foreground?: Color | null;
61
+ readonly background?: Color | null;
62
+ readonly underlineColor?: Color | null;
63
+ readonly underline?: UnderlineStyle;
64
+ readonly attributes?: number;
65
+ readonly hyperlink?: Hyperlink | null;
66
+ };
67
+ /**
68
+ One grapheme cluster in a terminal screen. A width of zero is reserved for the
69
+ continuation columns of a wide grapheme and is created only by `Screen`.
70
+ */
71
+ type Cell = {
72
+ readonly grapheme: string;
73
+ readonly width: number;
74
+ readonly style: CellStyle;
75
+ readonly hyperlink?: Hyperlink;
76
+ /** Composition intent consumed when this cell is drawn over another surface. */
77
+ readonly reset?: {
78
+ readonly foreground?: boolean;
79
+ readonly background?: boolean;
80
+ };
81
+ };
82
+ declare const emptyCellStyle: CellStyle;
83
+ declare const emptyCell: Cell;
84
+ declare const createCell: (grapheme: string, width: number, style?: CellStyle, hyperlink?: Hyperlink, reset?: Cell["reset"]) => Cell;
85
+ declare const cellsEqual: (left: Cell | undefined, right: Cell | undefined) => boolean;
86
+ //#endregion
87
+ //#region src/screen/color-profile.d.ts
88
+ /** The color model emitted by a terminal serializer. */
89
+ type ColorProfile = "none" | "ansi16" | "ansi256" | "truecolor";
90
+ declare function colorProfileFromLevel(level: ColorSupportLevel): ColorProfile;
91
+ /**
92
+ Quantizes a semantic color for an output profile. `undefined` means the
93
+ terminal default and is also the result for the no-color profile.
94
+ */
95
+ declare function quantizeColor(color: Color | undefined, profile: ColorProfile): Color | undefined;
96
+ //#endregion
97
+ export { emptyCellStyle as _, CellContent as a, Color as c, RgbColor as d, UnderlineStyle as f, emptyCell as g, createCell as h, Cell as i, Hyperlink as l, cellsEqual as m, colorProfileFromLevel as n, CellPatch as o, cellAttributes as p, quantizeColor as r, CellStyle as s, ColorProfile as t, IndexedColor as u };