@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,592 @@
1
+ import { _ as CSI, b as OSC } from "./sgr-BhwaWAJB.js";
2
+ import { a as isTty } from "./env-YVw64yZS.js";
3
+ import { n as detectCapabilities, s as signalExit } from "./detect-B3dL4Q11.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 terminalResponses = [
70
+ oscColorResponse,
71
+ kittyKeyboardResponse,
72
+ decrqmResponse,
73
+ xtversionResponse,
74
+ xtgettcapResponse,
75
+ kittyGraphicsResponse,
76
+ winopsResponse,
77
+ colorSchemeResponse,
78
+ da1Response
79
+ ].map((pattern) => new RegExp(`^(?:${pattern.source})$`));
80
+ const isTerminalQueryResponse = (sequence) => terminalResponses.some((pattern) => pattern.test(sequence));
81
+ const buildQuery = (palette, scope) => {
82
+ const queries = [
83
+ `${OSC}10;?`,
84
+ `${OSC}11;?`,
85
+ `${OSC}12;?`
86
+ ];
87
+ if (palette) for (let index = 0; index < paletteSize; index++) queries.push(`${OSC}4;${index};?`);
88
+ if (scope === "full") {
89
+ for (const mode of Object.values(queriedModes)) queries.push(`${CSI}?${mode}$p`);
90
+ queries.push(`${CSI}?u`, `P+q${xtgettcapRgb}\\`, kittyGraphicsProbe, `${CSI}>0q`);
91
+ }
92
+ queries.push(`${CSI}14t`, `${CSI}16t`, `${CSI}?996n`, `${CSI}c`);
93
+ return queries.join("");
94
+ };
95
+ /**
96
+ Sends a batch of terminal queries and collects the responses. Resolves when
97
+ the terminal answers the DA1 sentinel, or after `timeout` with whatever was
98
+ gathered. Stdin bytes that are not query responses are pushed back into the
99
+ stream.
100
+
101
+ The query is lazy: nothing is sent until something asks. Inside an Ink app,
102
+ use `useCapabilities` — it triggers the query through Ink's input pipeline so
103
+ responses can't collide with key handling. Call this directly only outside of
104
+ Ink, before any other stdin consumer is attached.
105
+ */
106
+ const queryTerminal = async (stdin, stdout, { timeout = 500, palette = true, scope = "full" } = {}) => new Promise((resolve) => {
107
+ const result = {
108
+ foreground: void 0,
109
+ background: void 0,
110
+ cursorColor: void 0,
111
+ palette: void 0,
112
+ appearance: void 0,
113
+ systemAppearance: void 0,
114
+ kittyKeyboard: false,
115
+ kittyGraphics: false,
116
+ sixel: false,
117
+ deviceAttributes: void 0,
118
+ trueColor: false,
119
+ focusEvents: false,
120
+ sgrMouse: false,
121
+ sgrPixelMouse: false,
122
+ bracketedPaste: false,
123
+ synchronizedOutput: false,
124
+ graphemeClustering: false,
125
+ colorSchemeUpdates: false,
126
+ inBandResize: false,
127
+ textAreaPixels: void 0,
128
+ cellPixels: void 0,
129
+ terminal: void 0
130
+ };
131
+ const paletteColors = /* @__PURE__ */ new Map();
132
+ let reportedAppearance;
133
+ let buffer = "";
134
+ let receivedStrings = false;
135
+ let done = false;
136
+ const finish = () => {
137
+ if (done) return;
138
+ done = true;
139
+ clearTimeout(timer);
140
+ stdin.pause();
141
+ stdin.removeListener("data", onData);
142
+ if (paletteColors.size === paletteSize) result.palette = Array.from({ length: paletteSize }, (_, index) => paletteColors.get(index));
143
+ result.systemAppearance = reportedAppearance;
144
+ result.appearance = appearanceOf(result.background) ?? reportedAppearance;
145
+ if (buffer.length > 0) {
146
+ stdin.unshift(receivedStrings ? buffer : Buffer.from(buffer, "latin1"));
147
+ buffer = "";
148
+ }
149
+ resolve(result);
150
+ };
151
+ const consumeMatch = (pattern) => {
152
+ const match = buffer.match(pattern);
153
+ if (match) buffer = buffer.replace(pattern, "");
154
+ return match ?? void 0;
155
+ };
156
+ const consumeResponse = () => {
157
+ const oscMatch = consumeMatch(oscColorResponse);
158
+ if (oscMatch) {
159
+ const [, code, index, value] = oscMatch;
160
+ const color = parseColorValue(value);
161
+ if (color) {
162
+ if (code === "10") result.foreground = color;
163
+ else if (code === "11") result.background = color;
164
+ else if (code === "12") result.cursorColor = color;
165
+ else if (code === "4" && index !== void 0) paletteColors.set(Number.parseInt(index, 10), color);
166
+ }
167
+ return true;
168
+ }
169
+ const xtversionMatch = consumeMatch(xtversionResponse);
170
+ if (xtversionMatch) {
171
+ const raw = xtversionMatch[1].trim();
172
+ if (raw.length > 0) result.terminal = parseXtversion(raw);
173
+ return true;
174
+ }
175
+ const xtgettcapMatch = consumeMatch(xtgettcapResponse);
176
+ if (xtgettcapMatch) {
177
+ if (xtgettcapMatch[1] === "1" && xtgettcapMatch[2].includes(xtgettcapRgb)) result.trueColor = true;
178
+ return true;
179
+ }
180
+ const graphicsMatch = consumeMatch(kittyGraphicsResponse);
181
+ if (graphicsMatch) {
182
+ if (graphicsMatch[1].includes("OK")) result.kittyGraphics = true;
183
+ return true;
184
+ }
185
+ const decrqmMatch = consumeMatch(decrqmResponse);
186
+ if (decrqmMatch) {
187
+ const mode = Number.parseInt(decrqmMatch[1], 10);
188
+ const recognized = decrqmMatch[2] !== "0";
189
+ for (const [name, number] of Object.entries(queriedModes)) if (number === mode) result[name] = recognized;
190
+ return true;
191
+ }
192
+ const winopsMatch = consumeMatch(winopsResponse);
193
+ if (winopsMatch) {
194
+ const size = {
195
+ height: Number.parseInt(winopsMatch[2], 10),
196
+ width: Number.parseInt(winopsMatch[3], 10)
197
+ };
198
+ if (winopsMatch[1] === "4") result.textAreaPixels = size;
199
+ else result.cellPixels = size;
200
+ return true;
201
+ }
202
+ const colorSchemeMatch = consumeMatch(colorSchemeResponse);
203
+ if (colorSchemeMatch) {
204
+ if (colorSchemeMatch[1] === "1") reportedAppearance = "dark";
205
+ else if (colorSchemeMatch[1] === "2") reportedAppearance = "light";
206
+ return true;
207
+ }
208
+ if (consumeMatch(kittyKeyboardResponse)) {
209
+ result.kittyKeyboard = true;
210
+ return true;
211
+ }
212
+ const da1Match = consumeMatch(da1Response);
213
+ if (da1Match) {
214
+ const attributes = da1Match[1].split(";").filter((part) => part.length > 0).map((part) => Number.parseInt(part, 10));
215
+ result.deviceAttributes = attributes;
216
+ result.sixel = attributes.slice(1).includes(4);
217
+ finish();
218
+ return false;
219
+ }
220
+ return false;
221
+ };
222
+ const onData = (data) => {
223
+ receivedStrings = typeof data === "string";
224
+ buffer += typeof data === "string" ? data : Buffer.from(data).toString("latin1");
225
+ while (consumeResponse());
226
+ };
227
+ stdin.on("data", onData);
228
+ const timer = setTimeout(finish, timeout);
229
+ stdin.resume();
230
+ stdout.write(buildQuery(palette, scope));
231
+ });
232
+ const queryPromises = /* @__PURE__ */ new WeakMap();
233
+ const queryResults = /* @__PURE__ */ new WeakMap();
234
+ /**
235
+ Starts (or joins) the terminal query for a stdout stream. The result is
236
+ cached per stream — the terminal is only ever asked once.
237
+ */
238
+ const ensureTerminalQuery = (stdin, stdout, options) => {
239
+ let promise = queryPromises.get(stdout);
240
+ if (!promise) {
241
+ promise = queryTerminal(stdin, stdout, options);
242
+ promise.then((result) => queryResults.set(stdout, result));
243
+ queryPromises.set(stdout, promise);
244
+ }
245
+ return promise;
246
+ };
247
+ /**
248
+ The completed query result for a stdout stream, if the query has finished.
249
+ */
250
+ const getTerminalQuery = (stdout) => queryResults.get(stdout);
251
+ const getTerminalQueryPromise = (stdout) => queryPromises.get(stdout);
252
+ /**
253
+ Merges a partial update (from an unsolicited terminal report) into the
254
+ cached query result. Returns the merged result, or `undefined` when no query
255
+ has completed yet.
256
+ */
257
+ const patchTerminalQuery = (stdout, patch) => {
258
+ const previous = queryResults.get(stdout);
259
+ if (!previous) return;
260
+ const merged = {
261
+ ...previous,
262
+ ...patch
263
+ };
264
+ queryResults.set(stdout, merged);
265
+ queryPromises.set(stdout, Promise.resolve(merged));
266
+ return merged;
267
+ };
268
+ const refreshPromises = /* @__PURE__ */ new WeakMap();
269
+ /**
270
+ Re-asks the terminal only the dynamic questions — colors/theme and pixel
271
+ geometry, which change when the user switches themes or resizes — and merges
272
+ the answers into the cached result. Static facts (mode and protocol support,
273
+ identity) are kept from the original query. Falls back to a full query when
274
+ none has completed yet; concurrent refreshes share one round-trip.
275
+ */
276
+ const refreshTerminalQuery = (stdin, stdout, options) => {
277
+ const previous = queryResults.get(stdout);
278
+ if (!previous) return ensureTerminalQuery(stdin, stdout, options);
279
+ const inFlight = refreshPromises.get(stdout);
280
+ if (inFlight) return inFlight;
281
+ const promise = queryTerminal(stdin, stdout, {
282
+ ...options,
283
+ scope: "dynamic"
284
+ }).then((fresh) => {
285
+ const merged = {
286
+ ...previous,
287
+ foreground: fresh.foreground ?? previous.foreground,
288
+ background: fresh.background ?? previous.background,
289
+ cursorColor: fresh.cursorColor ?? previous.cursorColor,
290
+ palette: fresh.palette ?? previous.palette,
291
+ appearance: fresh.appearance ?? previous.appearance,
292
+ systemAppearance: fresh.systemAppearance ?? previous.systemAppearance,
293
+ textAreaPixels: fresh.textAreaPixels ?? previous.textAreaPixels,
294
+ cellPixels: fresh.cellPixels ?? previous.cellPixels
295
+ };
296
+ queryResults.set(stdout, merged);
297
+ queryPromises.set(stdout, Promise.resolve(merged));
298
+ refreshPromises.delete(stdout);
299
+ return merged;
300
+ });
301
+ refreshPromises.set(stdout, promise);
302
+ return promise;
303
+ };
304
+ /**
305
+ Merges an async query result into a synchronous capabilities snapshot,
306
+ producing the complete picture. The query is authoritative where it answered:
307
+ a terminal that confirms truecolor via XTGETTCAP upgrades the sniffed color
308
+ level.
309
+ */
310
+ const applyTerminalQuery = (capabilities, query) => ({
311
+ ...capabilities,
312
+ size: {
313
+ ...capabilities.size,
314
+ pixels: query.textAreaPixels || query.cellPixels ? {
315
+ textArea: query.textAreaPixels,
316
+ cell: query.cellPixels
317
+ } : void 0
318
+ },
319
+ terminal: {
320
+ ...capabilities.terminal,
321
+ name: query.terminal?.name ?? capabilities.terminal.name,
322
+ version: query.terminal?.version ?? capabilities.terminal.version
323
+ },
324
+ color: query.trueColor ? {
325
+ level: 3,
326
+ depth: 24,
327
+ trueColor: true
328
+ } : capabilities.color,
329
+ theme: {
330
+ appearance: query.appearance ?? capabilities.theme.appearance,
331
+ systemAppearance: query.systemAppearance,
332
+ foreground: query.foreground,
333
+ background: query.background,
334
+ cursor: query.cursorColor,
335
+ palette: query.palette
336
+ },
337
+ supports: {
338
+ ...capabilities.supports,
339
+ color: capabilities.supports.color || query.trueColor,
340
+ kittyKeyboard: query.kittyKeyboard,
341
+ kittyGraphics: query.kittyGraphics,
342
+ sixel: query.sixel,
343
+ focusEvents: query.focusEvents,
344
+ sgrMouse: query.sgrMouse,
345
+ sgrPixelMouse: query.sgrPixelMouse,
346
+ bracketedPaste: query.bracketedPaste,
347
+ synchronizedOutput: query.synchronizedOutput,
348
+ graphemeClustering: query.graphemeClustering,
349
+ colorSchemeUpdates: query.colorSchemeUpdates,
350
+ inBandResize: query.inBandResize
351
+ }
352
+ });
353
+ //#endregion
354
+ //#region src/stream.ts
355
+ const isRawModeStream = (stdin) => {
356
+ return isTty(stdin) && "setRawMode" in stdin && typeof stdin.setRawMode === "function";
357
+ };
358
+ const getRawModeStream = (stdin) => {
359
+ if (!isRawModeStream(stdin)) return;
360
+ return stdin;
361
+ };
362
+ //#endregion
363
+ //#region src/capabilities/store.ts
364
+ const integrations = /* @__PURE__ */ new WeakMap();
365
+ const reportingUpdaters = /* @__PURE__ */ new WeakMap();
366
+ const registerTerminalIntegration = (stdout, integration) => {
367
+ integrations.set(stdout, integration);
368
+ reportingUpdaters.get(stdout)?.();
369
+ return () => {
370
+ if (integrations.get(stdout) === integration) {
371
+ integrations.delete(stdout);
372
+ reportingUpdaters.get(stdout)?.();
373
+ }
374
+ };
375
+ };
376
+ const focusInReport = `${CSI}I`;
377
+ const focusOutReport = `${CSI}O`;
378
+ const colorSchemeReport = new RegExp(`^\\[\\?997;(\\d+)n$`);
379
+ const inBandResizeReport = new RegExp(`^\\[48;(\\d+);(\\d+);(\\d+);(\\d+)t$`);
380
+ const reportModes = [
381
+ [1004, (result) => result.focusEvents],
382
+ [2031, (result) => result.colorSchemeUpdates],
383
+ [2048, (result) => result.inBandResize]
384
+ ];
385
+ const stores = /* @__PURE__ */ new WeakMap();
386
+ const createStore = (stdin, stdout) => {
387
+ const listeners = /* @__PURE__ */ new Set();
388
+ let resizeSubscribers = 0;
389
+ let focused;
390
+ let reportedSize;
391
+ let snapshot;
392
+ let snapshotQuery;
393
+ let snapshotColumns;
394
+ let snapshotRows;
395
+ let snapshotFocused;
396
+ let snapshotReportedSize;
397
+ const current = () => {
398
+ const query = getTerminalQuery(stdout);
399
+ const { columns, rows } = stdout;
400
+ if (!snapshot || query !== snapshotQuery || columns !== snapshotColumns || rows !== snapshotRows || focused !== snapshotFocused || reportedSize !== snapshotReportedSize) {
401
+ const detected = detectCapabilities({ stdout });
402
+ const applied = query ? applyTerminalQuery(detected, query) : detected;
403
+ const sized = reportedSize ? {
404
+ ...applied,
405
+ size: {
406
+ ...applied.size,
407
+ ...reportedSize,
408
+ source: "terminal"
409
+ }
410
+ } : applied;
411
+ snapshot = focused === void 0 ? sized : {
412
+ ...sized,
413
+ focused
414
+ };
415
+ snapshotQuery = query;
416
+ snapshotColumns = columns;
417
+ snapshotRows = rows;
418
+ snapshotFocused = focused;
419
+ snapshotReportedSize = reportedSize;
420
+ }
421
+ return snapshot;
422
+ };
423
+ let lastNotified;
424
+ const notify = () => {
425
+ const capabilities = current();
426
+ if (capabilities === lastNotified) return;
427
+ lastNotified = capabilities;
428
+ for (const listener of listeners) listener(capabilities);
429
+ };
430
+ let reportingEnabled = false;
431
+ let removeExitHandler;
432
+ const enabledModes = () => {
433
+ const result = getTerminalQuery(stdout);
434
+ if (!result) return [];
435
+ return reportModes.filter(([, supported]) => supported(result)).map(([mode]) => mode);
436
+ };
437
+ const writeModes = (suffix) => {
438
+ const sequence = enabledModes().map((mode) => `${CSI}?${mode}${suffix}`).join("");
439
+ if (sequence.length > 0) try {
440
+ stdout.write(sequence);
441
+ } catch {}
442
+ };
443
+ const updateReporting = () => {
444
+ const integration = integrations.get(stdout);
445
+ const shouldEnable = listeners.size > 0 && integration !== void 0 && stdout.isTTY === true;
446
+ if (shouldEnable && !reportingEnabled && enabledModes().length > 0) {
447
+ reportingEnabled = true;
448
+ integration?.setReportFeed?.(true);
449
+ writeModes("h");
450
+ removeExitHandler = signalExit(() => {
451
+ writeModes("l");
452
+ });
453
+ } else if (!shouldEnable && reportingEnabled) {
454
+ reportingEnabled = false;
455
+ writeModes("l");
456
+ integration?.setReportFeed?.(false);
457
+ removeExitHandler?.();
458
+ removeExitHandler = void 0;
459
+ reportedSize = void 0;
460
+ }
461
+ };
462
+ reportingUpdaters.set(stdout, updateReporting);
463
+ const ingest = (sequence) => {
464
+ if (sequence === focusInReport || sequence === focusOutReport) {
465
+ focused = sequence === focusInReport;
466
+ notify();
467
+ return true;
468
+ }
469
+ const colorScheme = sequence.match(colorSchemeReport);
470
+ if (colorScheme) {
471
+ const reported = colorScheme[1] === "1" ? "dark" : colorScheme[1] === "2" ? "light" : void 0;
472
+ if (reported) {
473
+ const cached = getTerminalQuery(stdout);
474
+ patchTerminalQuery(stdout, {
475
+ systemAppearance: reported,
476
+ ...cached?.background === void 0 ? { appearance: reported } : {}
477
+ });
478
+ notify();
479
+ if (integrations.has(stdout)) query().catch(() => {});
480
+ }
481
+ return true;
482
+ }
483
+ const resize = sequence.match(inBandResizeReport);
484
+ if (resize) {
485
+ const [, rows, columns, pixelHeight, pixelWidth] = resize.map(Number);
486
+ if (columns && rows) {
487
+ reportedSize = {
488
+ columns,
489
+ rows
490
+ };
491
+ patchTerminalQuery(stdout, {
492
+ textAreaPixels: pixelWidth && pixelHeight ? {
493
+ width: pixelWidth,
494
+ height: pixelHeight
495
+ } : void 0,
496
+ cellPixels: pixelWidth && pixelHeight ? {
497
+ width: Math.round(pixelWidth / columns),
498
+ height: Math.round(pixelHeight / rows)
499
+ } : void 0
500
+ });
501
+ }
502
+ notify();
503
+ return true;
504
+ }
505
+ return false;
506
+ };
507
+ const runStandaloneQuery = async () => {
508
+ if (!stdout.isTTY || !stdin.isTTY) return;
509
+ const wasRaw = stdin.isRaw ?? false;
510
+ if (!wasRaw) stdin.setRawMode?.(true);
511
+ try {
512
+ await (getTerminalQuery(stdout) !== void 0 ? refreshTerminalQuery(stdin, stdout) : ensureTerminalQuery(stdin, stdout));
513
+ } finally {
514
+ if (!wasRaw) stdin.setRawMode?.(false);
515
+ }
516
+ };
517
+ const query = async () => {
518
+ const integration = integrations.get(stdout);
519
+ if (integration) await integration.runQuery({ refresh: getTerminalQuery(stdout) !== void 0 });
520
+ else await runStandaloneQuery();
521
+ notify();
522
+ updateReporting();
523
+ return current();
524
+ };
525
+ const onResize = () => {
526
+ notify();
527
+ };
528
+ const subscribe = (listener, options = {}) => {
529
+ const resizes = options.resizes ?? true;
530
+ if (resizes && resizeSubscribers++ === 0) stdout.on("resize", onResize);
531
+ listeners.add(listener);
532
+ updateReporting();
533
+ let subscribed = true;
534
+ return () => {
535
+ if (!subscribed) return;
536
+ subscribed = false;
537
+ listeners.delete(listener);
538
+ if (resizes && --resizeSubscribers === 0) stdout.removeListener("resize", onResize);
539
+ updateReporting();
540
+ };
541
+ };
542
+ return {
543
+ get current() {
544
+ return current();
545
+ },
546
+ query,
547
+ subscribe,
548
+ ingest
549
+ };
550
+ };
551
+ /**
552
+ The capabilities store for a stream pair. Stores are cached per stdout, so
553
+ standalone code and Ink components observing the same terminal share one
554
+ snapshot and one set of query answers.
555
+ */
556
+ const getCapabilities = (stdin = process.stdin, stdout = process.stdout) => {
557
+ let store = stores.get(stdout);
558
+ if (!store) {
559
+ store = createStore(stdin, stdout);
560
+ stores.set(stdout, store);
561
+ }
562
+ return store;
563
+ };
564
+ /**
565
+ The process's own terminal — the store for `process.stdin`/`process.stdout`.
566
+
567
+ ```ts
568
+ import { capabilities } from "@alchemy.run/sigil/capabilities";
569
+
570
+ capabilities.current.supports.hyperlinks;
571
+ const fresh = await capabilities.query();
572
+ const unsubscribe = capabilities.subscribe((caps) => {
573
+ console.log(caps.size, caps.theme.appearance);
574
+ });
575
+ ```
576
+ */
577
+ const capabilities = getCapabilities();
578
+ //#endregion
579
+ //#region src/capabilities/color-policy.ts
580
+ function resolveColorProfile(detectedLevel, policy = "auto") {
581
+ return policy === "auto" ? colorProfileFromLevel(detectedLevel) : policy;
582
+ }
583
+ function colorState(capabilities, policy = "auto") {
584
+ const detected = colorProfileFromLevel(capabilities.color.level);
585
+ return {
586
+ detected,
587
+ policy,
588
+ effective: policy === "auto" ? detected : policy
589
+ };
590
+ }
591
+ //#endregion
592
+ export { registerTerminalIntegration as a, ensureTerminalQuery as c, isTerminalQueryResponse as d, queryTerminal as f, getCapabilities as i, getTerminalQuery as l, resolveColorProfile as n, getRawModeStream as o, refreshTerminalQuery as p, capabilities as r, applyTerminalQuery as s, colorState as t, getTerminalQueryPromise as u };
@@ -0,0 +1,22 @@
1
+ import { i as ColorSupportLevel } from "./detect-Db6GbKOm.js";
2
+ import { t as ColorProfile } from "./color-profile-CyeHnG1T.js";
3
+ //#region src/capabilities/color-policy.d.ts
4
+ /** A user-selected output policy. `auto` follows the target stream. */
5
+ type ColorPolicy = "auto" | ColorProfile;
6
+ /** The three distinct inputs and result of terminal color negotiation. */
7
+ type ColorState = {
8
+ /** The stream/terminal fact after environment and query detection. */
9
+ readonly detected: ColorProfile;
10
+ /** The application's policy for this render instance. */
11
+ readonly policy: ColorPolicy;
12
+ /** The profile the serializer must actually emit. */
13
+ readonly effective: ColorProfile;
14
+ };
15
+ declare function resolveColorProfile(detectedLevel: ColorSupportLevel, policy?: ColorPolicy): ColorProfile;
16
+ declare function colorState(capabilities: {
17
+ readonly color: {
18
+ readonly level: ColorSupportLevel;
19
+ };
20
+ }, policy?: ColorPolicy): ColorState;
21
+ //#endregion
22
+ export { resolveColorProfile as i, ColorState as n, colorState as r, ColorPolicy as t };
@@ -0,0 +1,97 @@
1
+ import { i as ColorSupportLevel } from "./detect-Db6GbKOm.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 };