@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
package/dist/index.js ADDED
@@ -0,0 +1,3211 @@
1
+ import { r as __toESM, t as require_react } from "./react-CTZ_8dwh.js";
2
+ import { _ as squashTextNodes, a as reconciler, b as require_react_reconciler, c as kittyModifiers, d as FocusContext, f as Text, g as transformAnsiLine, h as emitLayoutListeners, i as useStdinContext, l as resolveFlags, m as createNode, n as useInput, o as detectKittySupport, p as addLayoutListener, r as useStdin, s as kittyFlags, t as useFocus, u as StdinContext, v as accessibilityContext, y as require_constants } from "./use-focus-B5npZrJU.js";
3
+ import { C as bsu, S as ansiEscapes, V as esu, W as link, _ as CSI } from "./sgr-BhwaWAJB.js";
4
+ import { h as tokenizeAnsi, p as graphemes } from "./tokenize-AjqbvtiT.js";
5
+ import { t as stringWidth } from "./string-width-CijQwpIk.js";
6
+ import { n as sliceAnsi, r as wrapAnsi } from "./truncate-D31fhU6i.js";
7
+ import { a as isTty, i as isSigilDev, o as isWindows, r as isScreenReader, t as isInCi } from "./env-YVw64yZS.js";
8
+ import { a as detectTerminal, i as detectHyperlinkSupport, n as detectCapabilities, o as detectUnicodeSupport, r as detectColorLevel, s as signalExit, t as createSupportsColor } from "./detect-B3dL4Q11.js";
9
+ import { u as cliCursor } from "./osc-BFKKSqpg.js";
10
+ import { a as registerTerminalIntegration, c as ensureTerminalQuery, f as queryTerminal, i as getCapabilities, l as getTerminalQuery, o as getRawModeStream, p as refreshTerminalQuery, r as capabilities, s as applyTerminalQuery, u as getTerminalQueryPromise } from "./color-policy-BAC9-TZX.js";
11
+ import { a as buildCursorSuffix, c as hideCursorEscape, i as buildCursorOnlySequence, o as buildReturnToBottomPrefix, s as cursorPositionChanged, t as TerminalSession } from "./session-DDQ5V300.js";
12
+ import { t as Yoga } from "./yoga-5jKhYCJC.js";
13
+ import { t as require_jsx_runtime } from "./jsx-runtime-XtwIVFsg.js";
14
+ import { r as createCell, t as cellAttributes } from "./cell-_ZVhbfl0.js";
15
+ import { i as semanticTextStyleToCellStyle, n as mergeSemanticTextStyles, t as emptySemanticTextStyle } from "./semantic-text-style-DIMzC7xt.js";
16
+ import { a as samplePaint } from "./sample-Cqw1bjUL.js";
17
+ import { n as cellFromStyledChar, r as cellsFromAnsi, t as Screen } from "./screen-CiPytswf.js";
18
+ import { t as colorProfileFromLevel } from "./color-profile-DHhQHY55.js";
19
+ import { n as serializeScreen, t as serializeLine } from "./serialize-BTkAZgw1.js";
20
+ import { t as stripAnsi } from "./strip-BvU4toXG.js";
21
+ import "./color-CkbalRqK.js";
22
+ import { PassThrough, Stream } from "node:stream";
23
+ import { setImmediate as setImmediate$1 } from "node:timers/promises";
24
+ import { isNativeError } from "node:util/types";
25
+ import { EventEmitter } from "node:events";
26
+ import { existsSync, readFileSync } from "node:fs";
27
+ import { cwd } from "node:process";
28
+ import { Console } from "node:console";
29
+ require_react_reconciler();
30
+ var import_constants = require_constants();
31
+ var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
32
+ const animationContext = (0, import_react.createContext)({
33
+ renderThrottleMs: 0,
34
+ subscribe() {
35
+ return {
36
+ startTime: 0,
37
+ unsubscribe() {}
38
+ };
39
+ }
40
+ });
41
+ animationContext.displayName = "InternalAnimationContext";
42
+ //#endregion
43
+ //#region src/components/AppContext.ts
44
+ /**
45
+ `AppContext` is a React context that exposes lifecycle methods for the app.
46
+ */
47
+ const noopSuspension = {
48
+ async resume() {},
49
+ async [Symbol.asyncDispose]() {}
50
+ };
51
+ const AppContext = (0, import_react.createContext)({
52
+ exit(_errorOrResult) {},
53
+ async waitUntilRenderFlush() {},
54
+ suspendTerminal: (async (callback) => {
55
+ if (callback) {
56
+ await callback();
57
+ return;
58
+ }
59
+ return noopSuspension;
60
+ })
61
+ });
62
+ AppContext.displayName = "InternalAppContext";
63
+ //#endregion
64
+ //#region src/components/CursorContext.ts
65
+ const CursorContext = (0, import_react.createContext)({ setCursorPosition() {} });
66
+ CursorContext.displayName = "InternalCursorContext";
67
+ //#endregion
68
+ //#region src/code-excerpt.ts
69
+ const tabsToSpaces = (input, spaces = 2) => input.replaceAll(/^\t+/gm, (tabs) => " ".repeat(tabs.length * spaces));
70
+ const generateLineNumbers = (line, around) => {
71
+ const lineNumbers = [];
72
+ for (let lineNumber = line - around; lineNumber <= line + around; lineNumber++) lineNumbers.push(lineNumber);
73
+ return lineNumbers;
74
+ };
75
+ const codeExcerpt = (source, line, options = {}) => {
76
+ if (!line || line < 1) throw new TypeError("Line number must start from `1`.");
77
+ const lines = tabsToSpaces(source).split(/\r?\n/);
78
+ if (line > lines.length) return;
79
+ return generateLineNumbers(line, options.around ?? 3).filter((lineNumber) => lines[lineNumber - 1] !== void 0).map((lineNumber) => ({
80
+ line: lineNumber,
81
+ value: lines[lineNumber - 1]
82
+ }));
83
+ };
84
+ //#endregion
85
+ //#region src/glyphs.ts
86
+ const BOXES = {
87
+ single: {
88
+ topLeft: "┌",
89
+ top: "─",
90
+ topRight: "┐",
91
+ right: "│",
92
+ bottomRight: "┘",
93
+ bottom: "─",
94
+ bottomLeft: "└",
95
+ left: "│"
96
+ },
97
+ double: {
98
+ topLeft: "╔",
99
+ top: "═",
100
+ topRight: "╗",
101
+ right: "║",
102
+ bottomRight: "╝",
103
+ bottom: "═",
104
+ bottomLeft: "╚",
105
+ left: "║"
106
+ },
107
+ round: {
108
+ topLeft: "╭",
109
+ top: "─",
110
+ topRight: "╮",
111
+ right: "│",
112
+ bottomRight: "╯",
113
+ bottom: "─",
114
+ bottomLeft: "╰",
115
+ left: "│"
116
+ },
117
+ bold: {
118
+ topLeft: "┏",
119
+ top: "━",
120
+ topRight: "┓",
121
+ right: "┃",
122
+ bottomRight: "┛",
123
+ bottom: "━",
124
+ bottomLeft: "┗",
125
+ left: "┃"
126
+ },
127
+ singleDouble: {
128
+ topLeft: "╓",
129
+ top: "─",
130
+ topRight: "╖",
131
+ right: "║",
132
+ bottomRight: "╜",
133
+ bottom: "─",
134
+ bottomLeft: "╙",
135
+ left: "║"
136
+ },
137
+ doubleSingle: {
138
+ topLeft: "╒",
139
+ top: "═",
140
+ topRight: "╕",
141
+ right: "│",
142
+ bottomRight: "╛",
143
+ bottom: "═",
144
+ bottomLeft: "╘",
145
+ left: "│"
146
+ },
147
+ classic: {
148
+ topLeft: "+",
149
+ top: "-",
150
+ topRight: "+",
151
+ right: "|",
152
+ bottomRight: "+",
153
+ bottom: "-",
154
+ bottomLeft: "+",
155
+ left: "|"
156
+ },
157
+ arrow: {
158
+ topLeft: "↘",
159
+ top: "↓",
160
+ topRight: "↙",
161
+ right: "←",
162
+ bottomRight: "↖",
163
+ bottom: "↑",
164
+ bottomLeft: "↗",
165
+ left: "→"
166
+ }
167
+ };
168
+ //#endregion
169
+ //#region src/components/Box.tsx
170
+ /** @jsxImportSource react */
171
+ var import_jsx_runtime = require_jsx_runtime();
172
+ /**
173
+ `<Box>` is an essential Ink component to build your layout. It's like `<div style="display: flex">` in the browser.
174
+ */
175
+ function Box({ children, ref, backgroundColor, "aria-label": ariaLabel, "aria-hidden": ariaHidden, "aria-role": role, "aria-state": ariaState, ...style }) {
176
+ const { isScreenReaderEnabled } = (0, import_react.useContext)(accessibilityContext);
177
+ const label = ariaLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ink-text", { children: ariaLabel }) : void 0;
178
+ if (isScreenReaderEnabled && ariaHidden) return null;
179
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ink-box", {
180
+ ref,
181
+ style: {
182
+ flexWrap: "nowrap",
183
+ flexDirection: "row",
184
+ flexGrow: 0,
185
+ flexShrink: 1,
186
+ ...style,
187
+ backgroundColor,
188
+ overflowX: style.overflowX ?? style.overflow ?? "visible",
189
+ overflowY: style.overflowY ?? style.overflow ?? "visible"
190
+ },
191
+ internal_accessibility: {
192
+ role,
193
+ state: ariaState
194
+ },
195
+ children: isScreenReaderEnabled && label ? label : children
196
+ });
197
+ }
198
+ //#endregion
199
+ //#region src/parse-stack-line.ts
200
+ const lineRegExp = new RegExp("^" + String.raw`(?:\s*at )?` + "(?:(new) )?" + String.raw`(?:(.*?) \()?` + String.raw`(?:eval at ([^ ]+) \((.+?):(\d+):(\d+)\), )?` + String.raw`(?:(.+?):(\d+):(\d+)|(native))` + String.raw`(\)?)$`);
201
+ const methodRegExp = /^(.*?) \[as (.*?)]$/;
202
+ const cwd$1 = process.cwd().replaceAll("\\", "/");
203
+ const setFile = (result, filename) => {
204
+ if (filename) {
205
+ filename = filename.replaceAll("\\", "/");
206
+ if (filename.startsWith(`${cwd$1}/`)) filename = filename.slice(cwd$1.length + 1);
207
+ result.file = filename;
208
+ }
209
+ };
210
+ const parseStackLine = (line) => {
211
+ const match = lineRegExp.exec(line);
212
+ if (!match) return;
213
+ let functionName = match[2];
214
+ const evalOrigin = match[3];
215
+ const evalFile = match[4];
216
+ const evalLine = Number(match[5]);
217
+ const evalColumn = Number(match[6]);
218
+ let file = match[7];
219
+ const lineNumber = match[8];
220
+ const columnNumber = match[9];
221
+ const native = match[10] === "native";
222
+ const closeParen = match[11] === ")";
223
+ let method;
224
+ const result = {};
225
+ if (lineNumber) result.line = Number(lineNumber);
226
+ if (columnNumber) result.column = Number(columnNumber);
227
+ if (closeParen && file) {
228
+ let closes = 0;
229
+ for (let index = file.length - 1; index > 0; index--) if (file.charAt(index) === ")") closes++;
230
+ else if (file.charAt(index) === "(" && file.charAt(index - 1) === " ") {
231
+ closes--;
232
+ if (closes === -1 && file.charAt(index - 1) === " ") {
233
+ const before = file.slice(0, index - 1);
234
+ file = file.slice(index + 1);
235
+ functionName += ` (${before}`;
236
+ break;
237
+ }
238
+ }
239
+ }
240
+ if (functionName) {
241
+ const methodMatch = methodRegExp.exec(functionName);
242
+ if (methodMatch) {
243
+ functionName = methodMatch[1];
244
+ method = methodMatch[2];
245
+ }
246
+ }
247
+ if (file) setFile(result, file);
248
+ if (evalOrigin) {
249
+ result.evalOrigin = evalOrigin;
250
+ result.evalLine = evalLine;
251
+ result.evalColumn = evalColumn;
252
+ result.evalFile = evalFile?.replaceAll("\\", "/");
253
+ }
254
+ if (native) result.native = true;
255
+ if (functionName) result.function = functionName;
256
+ if (method && functionName !== method) result.method = method;
257
+ return result;
258
+ };
259
+ //#endregion
260
+ //#region src/components/ErrorOverview.tsx
261
+ /** @jsxImportSource react */
262
+ const cleanupPath = (path) => {
263
+ return path?.replace(`file://${cwd()}/`, "");
264
+ };
265
+ function ErrorOverview({ error }) {
266
+ const stack = error.stack ? error.stack.split("\n").slice(1) : void 0;
267
+ const origin = stack ? parseStackLine(stack[0]) : void 0;
268
+ const filePath = cleanupPath(origin?.file);
269
+ let excerpt;
270
+ let lineWidth = 0;
271
+ const stackLineCounts = /* @__PURE__ */ new Map();
272
+ if (filePath && origin?.line && existsSync(filePath)) {
273
+ const sourceCode = readFileSync(filePath, "utf8");
274
+ excerpt = codeExcerpt(sourceCode, origin.line);
275
+ if (excerpt) for (const { line } of excerpt) lineWidth = Math.max(lineWidth, String(line).length);
276
+ }
277
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
278
+ flexDirection: "column",
279
+ padding: 1,
280
+ children: [
281
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
282
+ backgroundColor: "red",
283
+ color: "white",
284
+ children: [
285
+ " ",
286
+ "ERROR",
287
+ " "
288
+ ]
289
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" ", error.message] })] }),
290
+ origin && filePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
291
+ marginTop: 1,
292
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
293
+ dimColor: true,
294
+ children: [
295
+ filePath,
296
+ ":",
297
+ origin.line,
298
+ ":",
299
+ origin.column
300
+ ]
301
+ })
302
+ }) : null,
303
+ origin && excerpt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
304
+ marginTop: 1,
305
+ flexDirection: "column",
306
+ children: excerpt.map(({ line, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
307
+ width: lineWidth + 1,
308
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
309
+ dimColor: line !== origin.line,
310
+ backgroundColor: line === origin.line ? "red" : void 0,
311
+ color: line === origin.line ? "white" : void 0,
312
+ "aria-label": line === origin.line ? `Line ${line}, error` : `Line ${line}`,
313
+ children: [String(line).padStart(lineWidth, " "), ":"]
314
+ })
315
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
316
+ backgroundColor: line === origin.line ? "red" : void 0,
317
+ color: line === origin.line ? "white" : void 0,
318
+ children: " " + value
319
+ }, line)] }, line))
320
+ }) : null,
321
+ error.stack ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
322
+ marginTop: 1,
323
+ flexDirection: "column",
324
+ children: error.stack.split("\n").slice(1).map((line) => {
325
+ const parsedLine = parseStackLine(line);
326
+ const lineCount = stackLineCounts.get(line) ?? 0;
327
+ stackLineCounts.set(line, lineCount + 1);
328
+ const key = `${line}-${lineCount}`;
329
+ if (!parsedLine?.file || !parsedLine.line || !parsedLine.column) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
330
+ dimColor: true,
331
+ children: "- "
332
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
333
+ dimColor: true,
334
+ bold: true,
335
+ children: [
336
+ line,
337
+ "\\t",
338
+ " "
339
+ ]
340
+ })] }, key);
341
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
343
+ dimColor: true,
344
+ children: "- "
345
+ }),
346
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
347
+ dimColor: true,
348
+ bold: true,
349
+ children: parsedLine.function
350
+ }),
351
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
352
+ dimColor: true,
353
+ color: "gray",
354
+ "aria-label": `at ${cleanupPath(parsedLine.file) ?? ""} line ${parsedLine.line} column ${parsedLine.column}`,
355
+ children: [
356
+ " ",
357
+ "(",
358
+ cleanupPath(parsedLine.file) ?? "",
359
+ ":",
360
+ parsedLine.line,
361
+ ":",
362
+ parsedLine.column,
363
+ ")"
364
+ ]
365
+ })
366
+ ] }, key);
367
+ })
368
+ }) : null
369
+ ]
370
+ });
371
+ }
372
+ //#endregion
373
+ //#region src/components/ErrorBoundary.tsx
374
+ /** @jsxImportSource react */
375
+ var ErrorBoundary = class extends import_react.PureComponent {
376
+ static displayName = "InternalErrorBoundary";
377
+ static getDerivedStateFromError(error) {
378
+ return { error };
379
+ }
380
+ state = { error: void 0 };
381
+ componentDidCatch(error) {
382
+ this.props.onError(error);
383
+ }
384
+ render() {
385
+ if (this.state.error) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorOverview, { error: this.state.error });
386
+ return this.props.children;
387
+ }
388
+ };
389
+ //#endregion
390
+ //#region src/components/StderrContext.ts
391
+ /**
392
+ `StderrContext` is a React context that exposes the stderr stream.
393
+ */
394
+ const StderrContext = (0, import_react.createContext)({
395
+ stderr: process.stderr,
396
+ write() {}
397
+ });
398
+ StderrContext.displayName = "InternalStderrContext";
399
+ //#endregion
400
+ //#region src/components/StdoutContext.ts
401
+ /**
402
+ `StdoutContext` is a React context that exposes the stdout stream where Ink renders your app.
403
+ */
404
+ const StdoutContext = (0, import_react.createContext)({
405
+ stdout: process.stdout,
406
+ write() {}
407
+ });
408
+ StdoutContext.displayName = "InternalStdoutContext";
409
+ //#endregion
410
+ //#region src/components/App.tsx
411
+ /** @jsxImportSource react */
412
+ const tab = " ";
413
+ const shiftTab = `${CSI}Z`;
414
+ const escape = "\x1B";
415
+ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, exitOnCtrlC, onExit, onWaitUntilRenderFlush, onSuspendTerminal, onRegisterInputControl, setCursorPosition, interactive, renderThrottleMs, terminalInput }) {
416
+ const [isFocusEnabled, setIsFocusEnabled] = (0, import_react.useState)(true);
417
+ const [activeFocusId, setActiveFocusId] = (0, import_react.useState)(void 0);
418
+ const [, setFocusables] = (0, import_react.useState)([]);
419
+ const focusablesCountRef = (0, import_react.useRef)(0);
420
+ const animationSubscribersRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
421
+ const animationTimerRef = (0, import_react.useRef)(void 0);
422
+ const rawModeEnabledCount = (0, import_react.useRef)(0);
423
+ const pendingDisableRawModeRef = (0, import_react.useRef)(false);
424
+ const bracketedPasteModeEnabledCount = (0, import_react.useRef)(0);
425
+ const internal_eventEmitter = (0, import_react.useRef)(new EventEmitter());
426
+ internal_eventEmitter.current.setMaxListeners(Infinity);
427
+ const readableListenerRef = (0, import_react.useRef)(void 0);
428
+ const isMountedRef = (0, import_react.useRef)(true);
429
+ const pendingInputFlushRef = (0, import_react.useRef)(void 0);
430
+ const pendingInputFlushDelayMilliseconds = 20;
431
+ const clearPendingInputFlush = (0, import_react.useCallback)(() => {
432
+ if (!pendingInputFlushRef.current) return;
433
+ clearTimeout(pendingInputFlushRef.current);
434
+ pendingInputFlushRef.current = void 0;
435
+ }, []);
436
+ const clearAnimationTimer = (0, import_react.useCallback)(() => {
437
+ if (!animationTimerRef.current) return;
438
+ clearTimeout(animationTimerRef.current);
439
+ animationTimerRef.current = void 0;
440
+ }, []);
441
+ const scheduleAnimationTick = (0, import_react.useCallback)(() => {
442
+ clearAnimationTimer();
443
+ if (animationSubscribersRef.current.size === 0) return;
444
+ let nextDueTime = Number.POSITIVE_INFINITY;
445
+ for (const subscriber of animationSubscribersRef.current.values()) nextDueTime = Math.min(nextDueTime, subscriber.nextDueTime);
446
+ const delay = Math.max(0, nextDueTime - performance.now());
447
+ animationTimerRef.current = setTimeout(() => {
448
+ animationTimerRef.current = void 0;
449
+ const currentTime = performance.now();
450
+ for (const subscriber of animationSubscribersRef.current.values()) {
451
+ if (currentTime < subscriber.nextDueTime) continue;
452
+ subscriber.callback(currentTime);
453
+ const elapsedTime = currentTime - subscriber.startTime;
454
+ const elapsedFrames = Math.floor(elapsedTime / subscriber.interval) + 1;
455
+ subscriber.nextDueTime = subscriber.startTime + elapsedFrames * subscriber.interval;
456
+ }
457
+ scheduleAnimationTick();
458
+ }, delay);
459
+ }, [clearAnimationTimer]);
460
+ const animationSubscribe = (0, import_react.useCallback)((callback, interval) => {
461
+ const startTime = performance.now();
462
+ animationSubscribersRef.current.set(callback, {
463
+ callback,
464
+ interval,
465
+ startTime,
466
+ nextDueTime: startTime + interval
467
+ });
468
+ scheduleAnimationTick();
469
+ return {
470
+ startTime,
471
+ unsubscribe() {
472
+ animationSubscribersRef.current.delete(callback);
473
+ if (animationSubscribersRef.current.size === 0) {
474
+ clearAnimationTimer();
475
+ return;
476
+ }
477
+ scheduleAnimationTick();
478
+ }
479
+ };
480
+ }, [clearAnimationTimer, scheduleAnimationTick]);
481
+ (0, import_react.useEffect)(() => {
482
+ return () => {
483
+ clearAnimationTimer();
484
+ };
485
+ }, [clearAnimationTimer]);
486
+ const rawModeStdin = getRawModeStream(stdin);
487
+ const isRawModeSupported = rawModeStdin !== void 0;
488
+ const detachReadableListener = (0, import_react.useCallback)(() => {
489
+ if (!readableListenerRef.current) return;
490
+ stdin.removeListener("readable", readableListenerRef.current);
491
+ readableListenerRef.current = void 0;
492
+ }, [stdin]);
493
+ const clearInputState = (0, import_react.useCallback)(() => {
494
+ terminalInput.reset();
495
+ clearPendingInputFlush();
496
+ detachReadableListener();
497
+ }, [
498
+ clearPendingInputFlush,
499
+ detachReadableListener,
500
+ terminalInput
501
+ ]);
502
+ const disableRawMode = (0, import_react.useCallback)(() => {
503
+ if (!rawModeStdin) return;
504
+ pendingDisableRawModeRef.current = false;
505
+ rawModeStdin.setRawMode(false);
506
+ rawModeStdin.unref?.();
507
+ rawModeEnabledCount.current = 0;
508
+ clearInputState();
509
+ }, [rawModeStdin, clearInputState]);
510
+ const handleExit = (0, import_react.useCallback)((errorOrResult) => {
511
+ if (isRawModeSupported && (rawModeEnabledCount.current > 0 || pendingDisableRawModeRef.current)) disableRawMode();
512
+ onExit(errorOrResult);
513
+ }, [
514
+ isRawModeSupported,
515
+ disableRawMode,
516
+ onExit
517
+ ]);
518
+ const handleInput = (0, import_react.useCallback)((input) => {
519
+ if (input === "" && exitOnCtrlC) {
520
+ handleExit();
521
+ return;
522
+ }
523
+ if (input === escape && isFocusEnabled) setActiveFocusId(void 0);
524
+ }, [
525
+ exitOnCtrlC,
526
+ handleExit,
527
+ isFocusEnabled
528
+ ]);
529
+ const emitInput = (0, import_react.useCallback)((input) => {
530
+ handleInput(input);
531
+ internal_eventEmitter.current.emit("input", input);
532
+ }, [handleInput]);
533
+ const schedulePendingInputFlush = (0, import_react.useCallback)(() => {
534
+ clearPendingInputFlush();
535
+ pendingInputFlushRef.current = setTimeout(() => {
536
+ pendingInputFlushRef.current = void 0;
537
+ const pendingEscape = terminalInput.flushPendingEscape();
538
+ if (!pendingEscape) return;
539
+ emitInput(pendingEscape);
540
+ }, pendingInputFlushDelayMilliseconds);
541
+ }, [
542
+ clearPendingInputFlush,
543
+ emitInput,
544
+ terminalInput
545
+ ]);
546
+ const handleReadable = (0, import_react.useCallback)(() => {
547
+ clearPendingInputFlush();
548
+ let chunk;
549
+ while ((chunk = stdin.read()) !== null) {
550
+ const inputEvents = terminalInput.push(chunk);
551
+ for (const event of inputEvents) if (typeof event === "string") emitInput(event);
552
+ else {
553
+ if (internal_eventEmitter.current.listenerCount("paste") === 0) {
554
+ emitInput(event.paste);
555
+ continue;
556
+ }
557
+ internal_eventEmitter.current.emit("paste", event.paste);
558
+ }
559
+ }
560
+ if (terminalInput.hasPendingEscape()) schedulePendingInputFlush();
561
+ }, [
562
+ stdin,
563
+ emitInput,
564
+ clearPendingInputFlush,
565
+ schedulePendingInputFlush,
566
+ terminalInput
567
+ ]);
568
+ const attachReadableListener = (0, import_react.useCallback)(() => {
569
+ if (readableListenerRef.current) return;
570
+ readableListenerRef.current = handleReadable;
571
+ stdin.addListener("readable", handleReadable);
572
+ }, [stdin, handleReadable]);
573
+ const handleSetRawMode = (0, import_react.useCallback)((isEnabled) => {
574
+ if (!rawModeStdin) {
575
+ if (stdin === process.stdin) throw new Error("Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported");
576
+ else throw new Error("Raw mode is not supported on the stdin provided to Ink.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported");
577
+ }
578
+ rawModeStdin.setEncoding("utf8");
579
+ if (isEnabled) {
580
+ if (rawModeEnabledCount.current === 0) {
581
+ const isRawModeAlreadyEnabled = pendingDisableRawModeRef.current;
582
+ pendingDisableRawModeRef.current = false;
583
+ if (!isRawModeAlreadyEnabled) {
584
+ rawModeStdin.ref?.();
585
+ rawModeStdin.setRawMode(true);
586
+ }
587
+ attachReadableListener();
588
+ }
589
+ rawModeEnabledCount.current++;
590
+ return;
591
+ }
592
+ if (rawModeEnabledCount.current === 0) return;
593
+ if (--rawModeEnabledCount.current === 0) {
594
+ clearInputState();
595
+ pendingDisableRawModeRef.current = true;
596
+ queueMicrotask(() => {
597
+ if (!pendingDisableRawModeRef.current) return;
598
+ disableRawMode();
599
+ });
600
+ }
601
+ }, [
602
+ rawModeStdin,
603
+ stdin,
604
+ attachReadableListener,
605
+ clearInputState,
606
+ disableRawMode
607
+ ]);
608
+ const handleQueryTerminal = (0, import_react.useCallback)(async ({ refresh = false } = {}) => {
609
+ if (!interactive || !isRawModeSupported || !stdout.isTTY) return;
610
+ if (!refresh) {
611
+ const existing = getTerminalQueryPromise(stdout);
612
+ if (existing) return existing;
613
+ }
614
+ handleSetRawMode(true);
615
+ detachReadableListener();
616
+ try {
617
+ return await (refresh ? refreshTerminalQuery(stdin, stdout) : ensureTerminalQuery(stdin, stdout));
618
+ } finally {
619
+ if (isMountedRef.current) attachReadableListener();
620
+ handleSetRawMode(false);
621
+ }
622
+ }, [
623
+ interactive,
624
+ isRawModeSupported,
625
+ stdin,
626
+ stdout,
627
+ handleSetRawMode,
628
+ detachReadableListener,
629
+ attachReadableListener
630
+ ]);
631
+ const reportFeedActiveRef = (0, import_react.useRef)(false);
632
+ const handleSetReportFeed = (0, import_react.useCallback)((active) => {
633
+ if (active === reportFeedActiveRef.current || !isRawModeSupported) return;
634
+ reportFeedActiveRef.current = active;
635
+ handleSetRawMode(active);
636
+ }, [isRawModeSupported, handleSetRawMode]);
637
+ (0, import_react.useInsertionEffect)(() => {
638
+ const unregister = registerTerminalIntegration(stdout, {
639
+ runQuery: handleQueryTerminal,
640
+ setReportFeed: handleSetReportFeed
641
+ });
642
+ return () => {
643
+ unregister();
644
+ handleSetReportFeed(false);
645
+ };
646
+ }, [
647
+ stdout,
648
+ handleQueryTerminal,
649
+ handleSetReportFeed
650
+ ]);
651
+ const handleSetBracketedPasteMode = (0, import_react.useCallback)((isEnabled) => {
652
+ if (!stdout.isTTY) return;
653
+ if (isEnabled) {
654
+ if (bracketedPasteModeEnabledCount.current === 0) stdout.write(ansiEscapes.enableBracketedPaste);
655
+ bracketedPasteModeEnabledCount.current++;
656
+ return;
657
+ }
658
+ if (bracketedPasteModeEnabledCount.current === 0) return;
659
+ if (--bracketedPasteModeEnabledCount.current === 0) stdout.write(ansiEscapes.disableBracketedPaste);
660
+ }, [stdout]);
661
+ const suspendedInputStateRef = (0, import_react.useRef)({
662
+ rawMode: false,
663
+ bracketedPaste: false
664
+ });
665
+ const pauseInput = (0, import_react.useCallback)(() => {
666
+ const wasRawMode = isRawModeSupported && rawModeEnabledCount.current > 0;
667
+ const wasBracketedPaste = bracketedPasteModeEnabledCount.current > 0;
668
+ suspendedInputStateRef.current = {
669
+ rawMode: wasRawMode,
670
+ bracketedPaste: wasBracketedPaste
671
+ };
672
+ if (wasBracketedPaste && stdout.isTTY) try {
673
+ stdout.write(ansiEscapes.disableBracketedPaste);
674
+ } catch {}
675
+ if (wasRawMode) {
676
+ rawModeStdin?.setRawMode(false);
677
+ rawModeStdin?.unref?.();
678
+ clearInputState();
679
+ }
680
+ }, [
681
+ isRawModeSupported,
682
+ rawModeStdin,
683
+ stdout,
684
+ clearInputState
685
+ ]);
686
+ const resumeInput = (0, import_react.useCallback)(() => {
687
+ const { rawMode, bracketedPaste } = suspendedInputStateRef.current;
688
+ if (rawMode) {
689
+ rawModeStdin?.setEncoding("utf8");
690
+ rawModeStdin?.ref?.();
691
+ rawModeStdin?.setRawMode(true);
692
+ attachReadableListener();
693
+ }
694
+ if (bracketedPaste && stdout.isTTY) try {
695
+ stdout.write(ansiEscapes.enableBracketedPaste);
696
+ } catch {}
697
+ }, [
698
+ rawModeStdin,
699
+ stdout,
700
+ attachReadableListener
701
+ ]);
702
+ (0, import_react.useInsertionEffect)(() => {
703
+ onRegisterInputControl(pauseInput, resumeInput);
704
+ }, [
705
+ onRegisterInputControl,
706
+ pauseInput,
707
+ resumeInput
708
+ ]);
709
+ const findNextFocusable = (0, import_react.useCallback)((currentFocusables, currentActiveFocusId) => {
710
+ const activeIndex = currentFocusables.findIndex((focusable) => {
711
+ return focusable.id === currentActiveFocusId;
712
+ });
713
+ for (let index = activeIndex + 1; index < currentFocusables.length; index++) {
714
+ const focusable = currentFocusables[index];
715
+ if (focusable?.isActive) return focusable.id;
716
+ }
717
+ }, []);
718
+ const findPreviousFocusable = (0, import_react.useCallback)((currentFocusables, currentActiveFocusId) => {
719
+ const activeIndex = currentFocusables.findIndex((focusable) => {
720
+ return focusable.id === currentActiveFocusId;
721
+ });
722
+ for (let index = activeIndex - 1; index >= 0; index--) {
723
+ const focusable = currentFocusables[index];
724
+ if (focusable?.isActive) return focusable.id;
725
+ }
726
+ }, []);
727
+ const focusNext = (0, import_react.useCallback)(() => {
728
+ setFocusables((currentFocusables) => {
729
+ setActiveFocusId((currentActiveFocusId) => {
730
+ const firstFocusableId = currentFocusables.find((focusable) => focusable.isActive)?.id;
731
+ return findNextFocusable(currentFocusables, currentActiveFocusId) ?? firstFocusableId;
732
+ });
733
+ return currentFocusables;
734
+ });
735
+ }, [findNextFocusable]);
736
+ const focusPrevious = (0, import_react.useCallback)(() => {
737
+ setFocusables((currentFocusables) => {
738
+ setActiveFocusId((currentActiveFocusId) => {
739
+ const lastFocusableId = currentFocusables.findLast((focusable) => focusable.isActive)?.id;
740
+ return findPreviousFocusable(currentFocusables, currentActiveFocusId) ?? lastFocusableId;
741
+ });
742
+ return currentFocusables;
743
+ });
744
+ }, [findPreviousFocusable]);
745
+ (0, import_react.useEffect)(() => {
746
+ const handleTabNavigation = (input) => {
747
+ if (!isFocusEnabled || focusablesCountRef.current === 0) return;
748
+ if (input === tab) focusNext();
749
+ if (input === shiftTab) focusPrevious();
750
+ };
751
+ internal_eventEmitter.current.on("input", handleTabNavigation);
752
+ const emitter = internal_eventEmitter.current;
753
+ return () => {
754
+ emitter.off("input", handleTabNavigation);
755
+ };
756
+ }, [
757
+ isFocusEnabled,
758
+ focusNext,
759
+ focusPrevious
760
+ ]);
761
+ const enableFocus = (0, import_react.useCallback)(() => {
762
+ setIsFocusEnabled(true);
763
+ }, []);
764
+ const disableFocus = (0, import_react.useCallback)(() => {
765
+ setIsFocusEnabled(false);
766
+ }, []);
767
+ const focus = (0, import_react.useCallback)((id) => {
768
+ setFocusables((currentFocusables) => {
769
+ if (currentFocusables.some((focusable) => focusable?.id === id)) setActiveFocusId(id);
770
+ return currentFocusables;
771
+ });
772
+ }, []);
773
+ const addFocusable = (0, import_react.useCallback)((id, { autoFocus }) => {
774
+ setFocusables((currentFocusables) => {
775
+ focusablesCountRef.current = currentFocusables.length + 1;
776
+ return [...currentFocusables, {
777
+ id,
778
+ isActive: true
779
+ }];
780
+ });
781
+ if (autoFocus) setActiveFocusId((currentActiveFocusId) => {
782
+ if (!currentActiveFocusId) return id;
783
+ return currentActiveFocusId;
784
+ });
785
+ }, []);
786
+ const removeFocusable = (0, import_react.useCallback)((id) => {
787
+ setActiveFocusId((currentActiveFocusId) => {
788
+ if (currentActiveFocusId === id) return;
789
+ return currentActiveFocusId;
790
+ });
791
+ setFocusables((currentFocusables) => {
792
+ const filtered = currentFocusables.filter((focusable) => {
793
+ return focusable.id !== id;
794
+ });
795
+ focusablesCountRef.current = filtered.length;
796
+ return filtered;
797
+ });
798
+ }, []);
799
+ const activateFocusable = (0, import_react.useCallback)((id) => {
800
+ setFocusables((currentFocusables) => currentFocusables.map((focusable) => {
801
+ if (focusable.id !== id) return focusable;
802
+ return {
803
+ id,
804
+ isActive: true
805
+ };
806
+ }));
807
+ }, []);
808
+ const deactivateFocusable = (0, import_react.useCallback)((id) => {
809
+ setActiveFocusId((currentActiveFocusId) => {
810
+ if (currentActiveFocusId === id) return;
811
+ return currentActiveFocusId;
812
+ });
813
+ setFocusables((currentFocusables) => currentFocusables.map((focusable) => {
814
+ if (focusable.id !== id) return focusable;
815
+ return {
816
+ id,
817
+ isActive: false
818
+ };
819
+ }));
820
+ }, []);
821
+ (0, import_react.useEffect)(() => {
822
+ isMountedRef.current = true;
823
+ return () => {
824
+ isMountedRef.current = false;
825
+ const canWriteToStdout = !stdout.destroyed && !stdout.writableEnded;
826
+ if (interactive && canWriteToStdout) cliCursor.show(stdout);
827
+ if (isRawModeSupported && (rawModeEnabledCount.current > 0 || pendingDisableRawModeRef.current)) disableRawMode();
828
+ if (bracketedPasteModeEnabledCount.current > 0) {
829
+ if (stdout.isTTY && canWriteToStdout) stdout.write(ansiEscapes.disableBracketedPaste);
830
+ bracketedPasteModeEnabledCount.current = 0;
831
+ }
832
+ };
833
+ }, [
834
+ stdout,
835
+ isRawModeSupported,
836
+ disableRawMode,
837
+ interactive
838
+ ]);
839
+ const appContextValue = (0, import_react.useMemo)(() => ({
840
+ exit: handleExit,
841
+ waitUntilRenderFlush: onWaitUntilRenderFlush,
842
+ suspendTerminal: onSuspendTerminal
843
+ }), [
844
+ handleExit,
845
+ onWaitUntilRenderFlush,
846
+ onSuspendTerminal
847
+ ]);
848
+ const stdinContextValue = (0, import_react.useMemo)(() => ({
849
+ stdin,
850
+ setRawMode: handleSetRawMode,
851
+ setBracketedPasteMode: handleSetBracketedPasteMode,
852
+ isRawModeSupported,
853
+ internal_exitOnCtrlC: exitOnCtrlC,
854
+ internal_eventEmitter: internal_eventEmitter.current
855
+ }), [
856
+ stdin,
857
+ handleSetRawMode,
858
+ handleSetBracketedPasteMode,
859
+ isRawModeSupported,
860
+ exitOnCtrlC
861
+ ]);
862
+ const stdoutContextValue = (0, import_react.useMemo)(() => ({
863
+ stdout,
864
+ write: writeToStdout
865
+ }), [stdout, writeToStdout]);
866
+ const stderrContextValue = (0, import_react.useMemo)(() => ({
867
+ stderr,
868
+ write: writeToStderr
869
+ }), [stderr, writeToStderr]);
870
+ const cursorContextValue = (0, import_react.useMemo)(() => ({ setCursorPosition }), [setCursorPosition]);
871
+ const focusContextValue = (0, import_react.useMemo)(() => ({
872
+ activeId: activeFocusId,
873
+ add: addFocusable,
874
+ remove: removeFocusable,
875
+ activate: activateFocusable,
876
+ deactivate: deactivateFocusable,
877
+ enableFocus,
878
+ disableFocus,
879
+ focusNext,
880
+ focusPrevious,
881
+ focus
882
+ }), [
883
+ activeFocusId,
884
+ addFocusable,
885
+ removeFocusable,
886
+ activateFocusable,
887
+ deactivateFocusable,
888
+ enableFocus,
889
+ disableFocus,
890
+ focusNext,
891
+ focusPrevious,
892
+ focus
893
+ ]);
894
+ const animationContextValue = (0, import_react.useMemo)(() => ({
895
+ renderThrottleMs,
896
+ subscribe: animationSubscribe
897
+ }), [animationSubscribe, renderThrottleMs]);
898
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppContext.Provider, {
899
+ value: appContextValue,
900
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StdinContext.Provider, {
901
+ value: stdinContextValue,
902
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StdoutContext.Provider, {
903
+ value: stdoutContextValue,
904
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StderrContext.Provider, {
905
+ value: stderrContextValue,
906
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FocusContext.Provider, {
907
+ value: focusContextValue,
908
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(animationContext.Provider, {
909
+ value: animationContextValue,
910
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CursorContext.Provider, {
911
+ value: cursorContextValue,
912
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBoundary, {
913
+ onError: handleExit,
914
+ children
915
+ })
916
+ })
917
+ })
918
+ })
919
+ })
920
+ })
921
+ })
922
+ });
923
+ }
924
+ App.displayName = "InternalApp";
925
+ //#endregion
926
+ //#region src/components/TerminalOscContext.ts
927
+ const noop$1 = () => {};
928
+ const TerminalOscContext = (0, import_react.createContext)({
929
+ publishProgress: noop$1,
930
+ copyToClipboard: noop$1,
931
+ publishTitle: noop$1,
932
+ setWorkingDirectory: noop$1,
933
+ notify: noop$1,
934
+ setPointerShape: noop$1
935
+ });
936
+ TerminalOscContext.displayName = "InternalTerminalOscContext";
937
+ //#endregion
938
+ //#region src/instances.ts
939
+ const instances = /* @__PURE__ */ new WeakMap();
940
+ //#endregion
941
+ //#region src/patch-console.ts
942
+ const consoleMethods = [
943
+ "assert",
944
+ "count",
945
+ "countReset",
946
+ "debug",
947
+ "dir",
948
+ "dirxml",
949
+ "error",
950
+ "group",
951
+ "groupCollapsed",
952
+ "groupEnd",
953
+ "info",
954
+ "log",
955
+ "table",
956
+ "time",
957
+ "timeEnd",
958
+ "timeLog",
959
+ "trace",
960
+ "warn"
961
+ ];
962
+ const patchConsole = (callback) => {
963
+ const stdout = new PassThrough();
964
+ const stderr = new PassThrough();
965
+ stdout.write = (data) => {
966
+ callback("stdout", String(data));
967
+ return true;
968
+ };
969
+ stderr.write = (data) => {
970
+ callback("stderr", String(data));
971
+ return true;
972
+ };
973
+ const internalConsole = new Console(stdout, stderr);
974
+ const originalMethods = /* @__PURE__ */ new Map();
975
+ for (const method of consoleMethods) {
976
+ originalMethods.set(method, console[method]);
977
+ console[method] = internalConsole[method];
978
+ }
979
+ return () => {
980
+ for (const method of consoleMethods) console[method] = originalMethods.get(method);
981
+ originalMethods.clear();
982
+ };
983
+ };
984
+ const patchStreamWrite = (stream, onData) => {
985
+ const originalWrite = stream.write;
986
+ const patchedWrite = (chunk, encodingOrCallback, callback) => {
987
+ onData(typeof chunk === "string" ? chunk : chunk instanceof Uint8Array ? Buffer.from(chunk).toString() : String(chunk));
988
+ (typeof encodingOrCallback === "function" ? encodingOrCallback : typeof callback === "function" ? callback : void 0)?.();
989
+ return true;
990
+ };
991
+ stream.write = patchedWrite;
992
+ return () => {
993
+ stream.write = originalWrite;
994
+ };
995
+ };
996
+ //#endregion
997
+ //#region src/get-max-width.ts
998
+ const getMaxWidth = (yogaNode) => {
999
+ return yogaNode.getComputedWidth() - yogaNode.getComputedPadding(Yoga.EDGE_LEFT) - yogaNode.getComputedPadding(Yoga.EDGE_RIGHT) - yogaNode.getComputedBorder(Yoga.EDGE_LEFT) - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT);
1000
+ };
1001
+ //#endregion
1002
+ //#region src/render-background.ts
1003
+ const renderBackground = (x, y, node, output) => {
1004
+ if (node.style.backgroundColor === void 0) return;
1005
+ const width = node.yogaNode.getComputedWidth();
1006
+ const height = node.yogaNode.getComputedHeight();
1007
+ const leftBorderWidth = node.style.borderStyle && node.style.borderLeft !== false ? 1 : 0;
1008
+ const rightBorderWidth = node.style.borderStyle && node.style.borderRight !== false ? 1 : 0;
1009
+ const topBorderHeight = node.style.borderStyle && node.style.borderTop !== false ? 1 : 0;
1010
+ const bottomBorderHeight = node.style.borderStyle && node.style.borderBottom !== false ? 1 : 0;
1011
+ const contentWidth = width - leftBorderWidth - rightBorderWidth;
1012
+ const contentHeight = height - topBorderHeight - bottomBorderHeight;
1013
+ if (!(contentWidth > 0 && contentHeight > 0)) return;
1014
+ const bounds = {
1015
+ x: x + leftBorderWidth,
1016
+ y: y + topBorderHeight,
1017
+ width: contentWidth,
1018
+ height: contentHeight
1019
+ };
1020
+ const lines = Array.from({ length: contentHeight }, (_unused, row) => Array.from({ length: contentWidth }, (_empty, column) => {
1021
+ const resetBackground = node.style.backgroundColor === "";
1022
+ const background = resetBackground ? void 0 : samplePaint(node.style.backgroundColor, bounds.x + column, bounds.y + row, bounds, output.paintContext);
1023
+ return createCell(" ", 1, {
1024
+ ...background ? { background } : {},
1025
+ underline: "none",
1026
+ attributes: cellAttributes.none
1027
+ }, void 0, resetBackground ? { background: true } : void 0);
1028
+ }));
1029
+ output.writeCells(bounds.x, bounds.y, lines);
1030
+ };
1031
+ //#endregion
1032
+ //#region src/render-border.ts
1033
+ const stylePiece = (segment, x, y, bounds, context, fg, bg, dim) => {
1034
+ let column = x;
1035
+ return [...graphemes(segment)].map((grapheme) => {
1036
+ const foreground = fg ? samplePaint(fg, column, y, bounds, context) : void 0;
1037
+ const background = bg ? samplePaint(bg, column, y, bounds, context) : void 0;
1038
+ const width = Math.max(1, stringWidth(grapheme));
1039
+ column += width;
1040
+ return createCell(grapheme, width, {
1041
+ ...foreground ? { foreground } : {},
1042
+ ...background ? { background } : {},
1043
+ underline: "none",
1044
+ attributes: dim ? cellAttributes.faint : cellAttributes.none
1045
+ });
1046
+ });
1047
+ };
1048
+ const renderBorder = (x, y, node, output) => {
1049
+ if (node.style.borderStyle) {
1050
+ const width = node.yogaNode.getComputedWidth();
1051
+ const height = node.yogaNode.getComputedHeight();
1052
+ const bounds = {
1053
+ x,
1054
+ y,
1055
+ width,
1056
+ height
1057
+ };
1058
+ const box = typeof node.style.borderStyle === "string" ? BOXES[node.style.borderStyle] : node.style.borderStyle;
1059
+ const topBorderColor = node.style.borderTopColor ?? node.style.borderColor;
1060
+ const bottomBorderColor = node.style.borderBottomColor ?? node.style.borderColor;
1061
+ const leftBorderColor = node.style.borderLeftColor ?? node.style.borderColor;
1062
+ const rightBorderColor = node.style.borderRightColor ?? node.style.borderColor;
1063
+ const topBorderBackgroundColor = node.style.borderTopBackgroundColor ?? node.style.borderBackgroundColor;
1064
+ const bottomBorderBackgroundColor = node.style.borderBottomBackgroundColor ?? node.style.borderBackgroundColor;
1065
+ const leftBorderBackgroundColor = node.style.borderLeftBackgroundColor ?? node.style.borderBackgroundColor;
1066
+ const rightBorderBackgroundColor = node.style.borderRightBackgroundColor ?? node.style.borderBackgroundColor;
1067
+ const dimTopBorderColor = node.style.borderTopDimColor ?? node.style.borderDimColor;
1068
+ const dimBottomBorderColor = node.style.borderBottomDimColor ?? node.style.borderDimColor;
1069
+ const dimLeftBorderColor = node.style.borderLeftDimColor ?? node.style.borderDimColor;
1070
+ const dimRightBorderColor = node.style.borderRightDimColor ?? node.style.borderDimColor;
1071
+ const showTopBorder = node.style.borderTop !== false;
1072
+ const showBottomBorder = node.style.borderBottom !== false;
1073
+ const showLeftBorder = node.style.borderLeft !== false;
1074
+ const showRightBorder = node.style.borderRight !== false;
1075
+ const contentWidth = width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0);
1076
+ const topBorder = showTopBorder ? (showLeftBorder ? box.topLeft : "") + box.top.repeat(contentWidth) + (showRightBorder ? box.topRight : "") : void 0;
1077
+ let verticalBorderHeight = height;
1078
+ if (showTopBorder) verticalBorderHeight -= 1;
1079
+ if (showBottomBorder) verticalBorderHeight -= 1;
1080
+ const offsetY = showTopBorder ? 1 : 0;
1081
+ const bottomBorder = showBottomBorder ? (showLeftBorder ? box.bottomLeft : "") + box.bottom.repeat(contentWidth) + (showRightBorder ? box.bottomRight : "") : void 0;
1082
+ if (topBorder) output.writeCells(x, y, [stylePiece(topBorder, x, y, bounds, output.paintContext, topBorderColor, topBorderBackgroundColor, dimTopBorderColor)]);
1083
+ if (showLeftBorder) output.writeCells(x, y + offsetY, Array.from({ length: verticalBorderHeight }, (_, row) => stylePiece(box.left, x, y + offsetY + row, bounds, output.paintContext, leftBorderColor, leftBorderBackgroundColor, dimLeftBorderColor)));
1084
+ if (showRightBorder) output.writeCells(x + width - 1, y + offsetY, Array.from({ length: verticalBorderHeight }, (_, row) => stylePiece(box.right, x + width - 1, y + offsetY + row, bounds, output.paintContext, rightBorderColor, rightBorderBackgroundColor, dimRightBorderColor)));
1085
+ if (bottomBorder) output.writeCells(x, y + height - 1, [stylePiece(bottomBorder, x, y + height - 1, bounds, output.paintContext, bottomBorderColor, bottomBorderBackgroundColor, dimBottomBorderColor)]);
1086
+ }
1087
+ };
1088
+ //#endregion
1089
+ //#region src/structured-text.ts
1090
+ /** Whether a text subtree must retain the ANSI compatibility pipeline. */
1091
+ function hasCompatibilityText(node) {
1092
+ if (node.internal_transform && !node.internal_textStyle) return true;
1093
+ return node.childNodes.some((child) => child.nodeName !== "#text" && hasCompatibilityText(child));
1094
+ }
1095
+ /** Rasterizes an unwrapped native Text subtree into semantic cell lines. */
1096
+ function structuredTextLines(node) {
1097
+ const lines = [[]];
1098
+ appendNode(node, emptySemanticTextStyle, lines, false);
1099
+ return lines;
1100
+ }
1101
+ /** Samples semantic paints only after wrapping and final layout are known. */
1102
+ function sampleStructuredText(lines, bounds, context) {
1103
+ return lines.map((line, row) => {
1104
+ let column = 0;
1105
+ return line.map((cell) => {
1106
+ const foreground = cell.foregroundPaint ? samplePaint(cell.foregroundPaint, bounds.x + column, bounds.y + row, bounds, context) : cell.style.foreground;
1107
+ const background = cell.backgroundPaint ? samplePaint(cell.backgroundPaint, bounds.x + column, bounds.y + row, bounds, context) : cell.style.background;
1108
+ column += cell.width;
1109
+ return createCell(cell.grapheme, cell.width, {
1110
+ ...cell.style,
1111
+ ...foreground ? { foreground } : {},
1112
+ ...background ? { background } : {}
1113
+ }, cell.hyperlink, cell.reset);
1114
+ });
1115
+ });
1116
+ }
1117
+ function structuredTextBaseStyle(node) {
1118
+ return semanticTextStyleToCellStyle(mergeSemanticTextStyles(emptySemanticTextStyle, node.internal_textStyle));
1119
+ }
1120
+ /** Wraps or truncates semantic cells without converting them through ANSI. */
1121
+ function wrapStructuredText(input, maxWidth, wrap, baseStyle) {
1122
+ if (wrap === "none") return input.map((line) => [...line]);
1123
+ if (maxWidth < 1) return [[]];
1124
+ return input.flatMap((line) => {
1125
+ if (lineWidth(line) <= maxWidth) return [[...line]];
1126
+ if (wrap === "hard") return hardWrap(line, maxWidth);
1127
+ if (wrap === "wrap") return wordWrap(line, maxWidth);
1128
+ return [truncate(line, maxWidth, wrap, baseStyle)];
1129
+ });
1130
+ }
1131
+ function wordWrap(line, maxWidth) {
1132
+ const words = [[]];
1133
+ const spaces = [];
1134
+ for (const cell of line) if (cell.grapheme === " ") {
1135
+ spaces.push(cell);
1136
+ words.push([]);
1137
+ } else words.at(-1).push(cell);
1138
+ const output = [[]];
1139
+ appendHard(output, words[0], maxWidth);
1140
+ for (let index = 1; index < words.length; index++) {
1141
+ appendHard(output, [spaces[index - 1]], maxWidth);
1142
+ const word = words[index];
1143
+ const current = output.at(-1);
1144
+ const wordWidth = lineWidth(word);
1145
+ const currentWidth = lineWidth(current);
1146
+ if (wordWidth <= maxWidth && currentWidth + wordWidth > maxWidth) output.push([]);
1147
+ else if (wordWidth > maxWidth && currentWidth > 0) {
1148
+ const remaining = maxWidth - currentWidth;
1149
+ const breaksHere = 1 + Math.floor((wordWidth - remaining - 1) / maxWidth);
1150
+ if (Math.floor((wordWidth - 1) / maxWidth) < breaksHere) output.push([]);
1151
+ }
1152
+ appendHard(output, word, maxWidth);
1153
+ }
1154
+ return output;
1155
+ }
1156
+ function hardWrap(line, maxWidth) {
1157
+ const output = [[]];
1158
+ appendHard(output, line, maxWidth);
1159
+ return output;
1160
+ }
1161
+ function appendHard(output, cells, maxWidth) {
1162
+ for (const cell of cells) {
1163
+ const current = output.at(-1);
1164
+ if (current.length > 0 && lineWidth(current) + cell.width > maxWidth) output.push([]);
1165
+ output.at(-1).push(cell);
1166
+ }
1167
+ }
1168
+ function truncate(line, maxWidth, wrap, baseStyle) {
1169
+ if (maxWidth === 1) return [ellipsis(baseStyle ?? cellStyleIntersection(line.at(0), line.at(-1)))];
1170
+ if (wrap === "truncate-start") {
1171
+ const right = sliceCells(line, lineWidth(line) - maxWidth + 1, lineWidth(line));
1172
+ return [ellipsis(right[0]?.style), ...right];
1173
+ }
1174
+ if (wrap === "truncate-middle") {
1175
+ const half = Math.min(Math.floor(maxWidth / 2), maxWidth - 1);
1176
+ const left = sliceCells(line, 0, half);
1177
+ const rightWidth = maxWidth - half - 1;
1178
+ const right = sliceCells(line, lineWidth(line) - rightWidth, lineWidth(line));
1179
+ return [
1180
+ ...left,
1181
+ ellipsis(baseStyle ?? cellStyleIntersection(left.at(-1), right.at(0))),
1182
+ ...right
1183
+ ];
1184
+ }
1185
+ const left = sliceCells(line, 0, maxWidth - 1);
1186
+ return [...left, ellipsis(left.at(-1)?.style)];
1187
+ }
1188
+ function sliceCells(line, start, end) {
1189
+ const output = [];
1190
+ let column = 0;
1191
+ for (const cell of line) {
1192
+ const nextColumn = column + cell.width;
1193
+ if (column >= start && nextColumn <= end) output.push(cell);
1194
+ column = nextColumn;
1195
+ }
1196
+ return output;
1197
+ }
1198
+ function ellipsis(style = semanticTextStyleToCellStyle(emptySemanticTextStyle)) {
1199
+ return createCell("…", 1, style);
1200
+ }
1201
+ function cellStyleIntersection(left, right) {
1202
+ if (!left) return right?.style ?? semanticTextStyleToCellStyle(emptySemanticTextStyle);
1203
+ if (!right) return left.style;
1204
+ return {
1205
+ ...sameColor(left.style.foreground, right.style.foreground) ? { foreground: left.style.foreground } : {},
1206
+ ...sameColor(left.style.background, right.style.background) ? { background: left.style.background } : {},
1207
+ ...sameColor(left.style.underlineColor, right.style.underlineColor) ? { underlineColor: left.style.underlineColor } : {},
1208
+ underline: left.style.underline === right.style.underline ? left.style.underline : "none",
1209
+ attributes: left.style.attributes & right.style.attributes
1210
+ };
1211
+ }
1212
+ function sameColor(left, right) {
1213
+ return JSON.stringify(left) === JSON.stringify(right);
1214
+ }
1215
+ function lineWidth(line) {
1216
+ return line.reduce((width, cell) => width + cell.width, 0);
1217
+ }
1218
+ function appendNode(node, inherited, lines, ansi) {
1219
+ if (node.nodeName === "#text") {
1220
+ if (ansi) appendAnsiText(node.nodeValue, inherited, lines);
1221
+ else appendText(node.nodeValue, inherited, lines);
1222
+ return;
1223
+ }
1224
+ const style = mergeSemanticTextStyles(inherited, node.internal_textStyle);
1225
+ for (const child of node.childNodes) appendNode(child, style, lines, ansi || node.internal_ansi === true);
1226
+ }
1227
+ function appendAnsiText(text, semantic, lines) {
1228
+ const base = semanticTextStyleToCellStyle(semantic);
1229
+ for (const cell of cellsFromAnsi(text)) {
1230
+ if (cell.grapheme === "\n" || cell.grapheme === "\r\n") {
1231
+ lines.push([]);
1232
+ continue;
1233
+ }
1234
+ const hasForeground = cell.style.foreground !== void 0;
1235
+ const hasBackground = cell.style.background !== void 0;
1236
+ lines.at(-1).push({
1237
+ ...cell,
1238
+ style: {
1239
+ ...cell.style.foreground ?? base.foreground ? { foreground: cell.style.foreground ?? base.foreground } : {},
1240
+ ...cell.style.background ?? base.background ? { background: cell.style.background ?? base.background } : {},
1241
+ ...cell.style.underlineColor ? { underlineColor: cell.style.underlineColor } : {},
1242
+ underline: cell.style.underline === "none" ? base.underline : cell.style.underline,
1243
+ attributes: base.attributes | cell.style.attributes
1244
+ },
1245
+ ...!hasForeground && semantic.foreground ? { foregroundPaint: semantic.foreground } : {},
1246
+ ...!hasBackground && semantic.background ? { backgroundPaint: semantic.background } : {}
1247
+ });
1248
+ }
1249
+ }
1250
+ function appendText(text, style, lines) {
1251
+ const cellStyle = semanticTextStyleToCellStyle(style);
1252
+ const plainText = tokenizeAnsi(text).filter((token) => token.type === "text").map((token) => token.value).join("");
1253
+ for (const segment of graphemes(stripAnsi(plainText))) {
1254
+ if (segment === "\n" || segment === "\r\n") {
1255
+ lines.push([]);
1256
+ continue;
1257
+ }
1258
+ lines.at(-1).push({
1259
+ ...createCell(segment, Math.max(1, stringWidth(segment)), cellStyle, void 0, style.resetForeground || style.resetBackground ? {
1260
+ foreground: style.resetForeground,
1261
+ background: style.resetBackground
1262
+ } : void 0),
1263
+ ...style.foreground ? { foregroundPaint: style.foreground } : {},
1264
+ ...style.background ? { backgroundPaint: style.background } : {}
1265
+ });
1266
+ }
1267
+ }
1268
+ //#endregion
1269
+ //#region src/paint-tree.ts
1270
+ const renderAccessibleText = (node, options = {}) => {
1271
+ if (options.skipStaticElements && node.internal_static) return "";
1272
+ if (node.yogaNode?.getDisplay() === Yoga.DISPLAY_NONE) return "";
1273
+ let output = "";
1274
+ if (node.nodeName === "ink-text") output = squashTextNodes(node);
1275
+ else if (node.nodeName === "ink-box" || node.nodeName === "ink-root") {
1276
+ const separator = node.style.flexDirection === "row" || node.style.flexDirection === "row-reverse" ? " " : "\n";
1277
+ output = (node.style.flexDirection === "row-reverse" || node.style.flexDirection === "column-reverse" ? [...node.childNodes].reverse() : [...node.childNodes]).map((childNode) => {
1278
+ return renderAccessibleText(childNode, {
1279
+ parentRole: node.internal_accessibility?.role,
1280
+ skipStaticElements: options.skipStaticElements
1281
+ });
1282
+ }).filter(Boolean).join(separator);
1283
+ }
1284
+ if (node.internal_accessibility) {
1285
+ const { role, state } = node.internal_accessibility;
1286
+ if (state) {
1287
+ const stateDescription = Object.keys(state).filter((key) => state[key]).join(", ");
1288
+ if (stateDescription) output = `(${stateDescription}) ${output}`;
1289
+ }
1290
+ if (role && role !== options.parentRole) output = `${role}: ${output}`;
1291
+ }
1292
+ return output;
1293
+ };
1294
+ const paintTree = (node, output, options) => {
1295
+ const { offsetX = 0, offsetY = 0, transformers = [], skipStaticElements } = options;
1296
+ if (skipStaticElements && node.internal_static) return;
1297
+ const { yogaNode } = node;
1298
+ if (yogaNode) {
1299
+ if (yogaNode.getDisplay() === Yoga.DISPLAY_NONE) return;
1300
+ const x = offsetX + yogaNode.getComputedLeft();
1301
+ const y = offsetY + yogaNode.getComputedTop();
1302
+ const newTransformers = node.internal_transform ? [node.internal_transform, ...transformers] : transformers;
1303
+ if (node.nodeName === "ink-text") {
1304
+ if (squashTextNodes(node).length > 0) {
1305
+ const maxWidth = getMaxWidth(yogaNode);
1306
+ const firstChildYoga = node.childNodes[0]?.yogaNode;
1307
+ const paddingX = firstChildYoga?.getComputedLeft() ?? 0;
1308
+ const paddingY = firstChildYoga?.getComputedTop() ?? 0;
1309
+ const textWrap = node.style.textWrap ?? "wrap";
1310
+ const overflow = textWrap === "none";
1311
+ const lines = wrapStructuredText(structuredTextLines(node), maxWidth, textWrap, structuredTextBaseStyle(node));
1312
+ const paintBounds = {
1313
+ x: x + paddingX,
1314
+ y: y + paddingY,
1315
+ width: Math.max(1, ...lines.map((line) => line.reduce((width, cell) => width + cell.width, 0))),
1316
+ height: lines.length
1317
+ };
1318
+ const sampled = sampleStructuredText(lines, paintBounds, output.paintContext);
1319
+ if (hasCompatibilityText(node)) {
1320
+ const textTransformers = collectTransformers(node, transformers);
1321
+ output.writeAnsi(paintBounds.x, paintBounds.y, sampled.map((line) => serializeLine(line, {
1322
+ colorProfile: output.paintContext.profile ?? "truecolor",
1323
+ trimEnd: false
1324
+ })).join("\n"), {
1325
+ transformers: textTransformers,
1326
+ overflow
1327
+ });
1328
+ } else output.writeCells(paintBounds.x, paintBounds.y, sampled, { overflow });
1329
+ }
1330
+ return;
1331
+ }
1332
+ let clipped = false;
1333
+ if (node.nodeName === "ink-box") {
1334
+ renderBackground(x, y, node, output);
1335
+ renderBorder(x, y, node, output);
1336
+ const clipHorizontally = node.style.overflowX === "hidden" || node.style.overflow === "hidden";
1337
+ const clipVertically = node.style.overflowY === "hidden" || node.style.overflow === "hidden";
1338
+ if (clipHorizontally || clipVertically) {
1339
+ const x1 = clipHorizontally ? x + yogaNode.getComputedBorder(Yoga.EDGE_LEFT) : void 0;
1340
+ const x2 = clipHorizontally ? x + yogaNode.getComputedWidth() - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT) : void 0;
1341
+ const y1 = clipVertically ? y + yogaNode.getComputedBorder(Yoga.EDGE_TOP) : void 0;
1342
+ const y2 = clipVertically ? y + yogaNode.getComputedHeight() - yogaNode.getComputedBorder(Yoga.EDGE_BOTTOM) : void 0;
1343
+ output.clip({
1344
+ x1,
1345
+ x2,
1346
+ y1,
1347
+ y2
1348
+ });
1349
+ clipped = true;
1350
+ }
1351
+ }
1352
+ if (node.nodeName === "ink-root" || node.nodeName === "ink-box") {
1353
+ for (const childNode of node.childNodes) paintTree(childNode, output, {
1354
+ offsetX: x,
1355
+ offsetY: y,
1356
+ transformers: newTransformers,
1357
+ skipStaticElements
1358
+ });
1359
+ if (clipped) output.unclip();
1360
+ }
1361
+ }
1362
+ };
1363
+ function collectTransformers(node, inherited) {
1364
+ return [
1365
+ ...node.childNodes.flatMap((child) => child.nodeName === "#text" ? [] : collectTransformers(child, [])),
1366
+ ...node.internal_transform ? [node.internal_transform] : [],
1367
+ ...inherited
1368
+ ];
1369
+ }
1370
+ //#endregion
1371
+ //#region src/screen/canvas.ts
1372
+ /** Immediate structured drawing canvas with one explicit ANSI compatibility adapter. */
1373
+ var Canvas = class {
1374
+ width;
1375
+ height;
1376
+ paintContext;
1377
+ #screen;
1378
+ #clips = [];
1379
+ constructor(options) {
1380
+ this.width = options.width;
1381
+ this.height = options.height;
1382
+ this.paintContext = {
1383
+ profile: options.colorProfile ?? "truecolor",
1384
+ ...options.paintContext
1385
+ };
1386
+ this.#screen = new Screen(this.width, this.height);
1387
+ }
1388
+ writeCells(x, y, lines, options) {
1389
+ const clip = this.#clips.at(-1);
1390
+ const overflow = options?.overflow === true;
1391
+ for (const [rowOffset, line] of lines.entries()) {
1392
+ const currentY = y + rowOffset;
1393
+ if (!this.#insideY(currentY, clip)) continue;
1394
+ let currentX = x;
1395
+ for (const cell of line) {
1396
+ const endX = currentX + cell.width;
1397
+ if (!(clip?.x1 !== void 0 && currentX < clip.x1 || clip?.x2 !== void 0 && endX > clip.x2)) this.#composeNativeCell(currentX, currentY, cell, overflow);
1398
+ currentX = endX;
1399
+ }
1400
+ }
1401
+ }
1402
+ writeAnsi(x, y, text, options) {
1403
+ if (!text) return;
1404
+ let lines = text.split("\n");
1405
+ const clip = this.#clips.at(-1);
1406
+ const overflow = options.overflow === true;
1407
+ if (clip?.y1 !== void 0 && y < clip.y1) {
1408
+ lines = lines.slice(clip.y1 - y);
1409
+ y = clip.y1;
1410
+ }
1411
+ if (clip?.y2 !== void 0) lines = lines.slice(0, Math.max(0, clip.y2 - y));
1412
+ for (const [lineIndex, original] of lines.entries()) {
1413
+ const currentY = y + lineIndex;
1414
+ if (!this.#insideY(currentY, clip)) continue;
1415
+ let line = original;
1416
+ let currentX = x;
1417
+ if (clip?.x1 !== void 0 && currentX < clip.x1) {
1418
+ line = sliceAnsi(line, clip.x1 - currentX);
1419
+ currentX = clip.x1;
1420
+ }
1421
+ if (clip?.x2 !== void 0) line = sliceAnsi(line, 0, Math.max(0, clip.x2 - currentX));
1422
+ for (const character of transformAnsiLine(line, lineIndex, options.transformers)) {
1423
+ const width = Math.max(1, stringWidth(character.value));
1424
+ if (clip?.x2 !== void 0 && currentX + width > clip.x2) break;
1425
+ this.#writeCompatibilityCell(currentX, currentY, character, width, overflow);
1426
+ currentX += width;
1427
+ }
1428
+ }
1429
+ }
1430
+ clip(clip) {
1431
+ this.#clips.push(clip);
1432
+ }
1433
+ unclip() {
1434
+ this.#clips.pop();
1435
+ }
1436
+ finish() {
1437
+ return this.#screen;
1438
+ }
1439
+ #insideY(y, clip) {
1440
+ return y >= 0 && y < this.#screen.height && (clip?.y1 === void 0 || y >= clip.y1) && (clip?.y2 === void 0 || y < clip.y2);
1441
+ }
1442
+ #composeNativeCell(x, y, cell, overflow = false) {
1443
+ if (!overflow && x >= this.#screen.width) return;
1444
+ this.#screen.composeCell(x, y, {
1445
+ content: {
1446
+ grapheme: cell.grapheme,
1447
+ width: cell.width
1448
+ },
1449
+ foreground: cell.reset?.foreground ? null : cell.style.foreground ?? null,
1450
+ background: cell.reset?.background ? null : cell.style.background,
1451
+ underlineColor: cell.style.underlineColor ?? null,
1452
+ underline: cell.style.underline,
1453
+ attributes: cell.style.attributes,
1454
+ hyperlink: cell.hyperlink ?? null
1455
+ }, { overflow });
1456
+ }
1457
+ #writeCompatibilityCell(x, y, character, width, overflow = false) {
1458
+ if (!overflow && x >= this.#screen.width) return;
1459
+ const cell = cellFromStyledChar(character, width);
1460
+ this.#screen.composeCell(x, y, {
1461
+ content: {
1462
+ grapheme: cell.grapheme,
1463
+ width: cell.width
1464
+ },
1465
+ foreground: cell.style.foreground ?? null,
1466
+ background: cell.style.background,
1467
+ underlineColor: cell.style.underlineColor ?? null,
1468
+ underline: cell.style.underline,
1469
+ attributes: cell.style.attributes,
1470
+ hyperlink: cell.hyperlink ?? null
1471
+ }, { overflow });
1472
+ }
1473
+ };
1474
+ //#endregion
1475
+ //#region src/render-frame.ts
1476
+ const renderDimension = (value) => Number.isFinite(value) ? Math.max(0, Math.ceil(value)) : 0;
1477
+ const renderFrame = (node, isScreenReaderEnabled, options = {}) => {
1478
+ if (node.yogaNode) {
1479
+ if (isScreenReaderEnabled) {
1480
+ const output = renderAccessibleText(node, { skipStaticElements: true });
1481
+ let staticOutput = "";
1482
+ if (node.staticNode) staticOutput = renderAccessibleText(node.staticNode, { skipStaticElements: false });
1483
+ return {
1484
+ accessibleText: output,
1485
+ staticAccessibleText: staticOutput
1486
+ };
1487
+ }
1488
+ const output = new Canvas({
1489
+ width: renderDimension(node.yogaNode.getComputedWidth()),
1490
+ height: renderDimension(node.yogaNode.getComputedHeight()),
1491
+ colorProfile: options.colorProfile,
1492
+ paintContext: options.paintContext
1493
+ });
1494
+ paintTree(node, output, { skipStaticElements: true });
1495
+ let staticOutput;
1496
+ if (node.staticNode?.yogaNode) {
1497
+ staticOutput = new Canvas({
1498
+ width: renderDimension(node.staticNode.yogaNode.getComputedWidth()),
1499
+ height: renderDimension(node.staticNode.yogaNode.getComputedHeight()),
1500
+ colorProfile: options.colorProfile,
1501
+ paintContext: options.paintContext
1502
+ });
1503
+ paintTree(node.staticNode, staticOutput, { skipStaticElements: false });
1504
+ }
1505
+ return {
1506
+ screen: output.finish(),
1507
+ ...staticOutput ? { staticScreen: staticOutput.finish() } : {}
1508
+ };
1509
+ }
1510
+ return { accessibleText: "" };
1511
+ };
1512
+ //#endregion
1513
+ //#region src/terminal/inline-presenter.ts
1514
+ /** Full-rewrite fallback and cursor state for the structured inline renderer. */
1515
+ function createInlinePresenter(stream, options = {}) {
1516
+ let previousLineCount = 0;
1517
+ let previousOutput = "";
1518
+ let hasHiddenCursor = false;
1519
+ let cursorPosition;
1520
+ let cursorDirty = false;
1521
+ let previousCursorPosition;
1522
+ let cursorWasShown = false;
1523
+ const showCursor = options.showCursor ?? false;
1524
+ const activeCursor = () => cursorDirty ? cursorPosition : void 0;
1525
+ const hasChanges = (output, cursor) => output !== previousOutput || cursorPositionChanged(cursor, previousCursorPosition);
1526
+ const present = (output) => {
1527
+ if (!showCursor && !hasHiddenCursor) {
1528
+ cliCursor.hide(stream);
1529
+ hasHiddenCursor = true;
1530
+ }
1531
+ const cursor = activeCursor();
1532
+ cursorDirty = false;
1533
+ const cursorChanged = cursorPositionChanged(cursor, previousCursorPosition);
1534
+ if (!hasChanges(output, cursor)) return false;
1535
+ const lines = output.split("\n");
1536
+ const suffix = buildCursorSuffix(lines.length - 1, cursor);
1537
+ if (output === previousOutput && cursorChanged) stream.write(buildCursorOnlySequence({
1538
+ cursorWasShown,
1539
+ previousLineCount,
1540
+ previousCursorPosition,
1541
+ cursorPosition: cursor
1542
+ }));
1543
+ else {
1544
+ previousOutput = output;
1545
+ stream.write(buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition) + ansiEscapes.eraseLines(previousLineCount) + output + suffix);
1546
+ previousLineCount = lines.length;
1547
+ }
1548
+ previousCursorPosition = cursor ? { ...cursor } : void 0;
1549
+ cursorWasShown = cursor !== void 0;
1550
+ return true;
1551
+ };
1552
+ present.clear = () => {
1553
+ stream.write(buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition) + ansiEscapes.eraseLines(previousLineCount));
1554
+ previousOutput = "";
1555
+ previousLineCount = 0;
1556
+ previousCursorPosition = void 0;
1557
+ cursorWasShown = false;
1558
+ };
1559
+ present.done = () => {
1560
+ previousOutput = "";
1561
+ previousLineCount = 0;
1562
+ previousCursorPosition = void 0;
1563
+ cursorWasShown = false;
1564
+ if (!showCursor) {
1565
+ cliCursor.show(stream);
1566
+ hasHiddenCursor = false;
1567
+ }
1568
+ };
1569
+ present.reset = () => {
1570
+ previousOutput = "";
1571
+ previousLineCount = 0;
1572
+ previousCursorPosition = void 0;
1573
+ cursorWasShown = false;
1574
+ };
1575
+ present.sync = (output) => {
1576
+ const cursor = activeCursor();
1577
+ cursorDirty = false;
1578
+ const lines = output.split("\n");
1579
+ previousOutput = output;
1580
+ previousLineCount = lines.length;
1581
+ if (!cursor && cursorWasShown) stream.write(hideCursorEscape);
1582
+ if (cursor) stream.write(buildCursorSuffix(lines.length - 1, cursor));
1583
+ previousCursorPosition = cursor ? { ...cursor } : void 0;
1584
+ cursorWasShown = cursor !== void 0;
1585
+ };
1586
+ present.setCursorPosition = (position) => {
1587
+ cursorPosition = position;
1588
+ cursorDirty = true;
1589
+ };
1590
+ present.isCursorDirty = () => cursorDirty;
1591
+ present.willRender = (output) => hasChanges(output, activeCursor());
1592
+ return present;
1593
+ }
1594
+ //#endregion
1595
+ //#region src/throttle.ts
1596
+ /**
1597
+ Invokes `fn` at most once per `wait` milliseconds.
1598
+
1599
+ The first call in a window fires immediately (leading edge). Calls made while
1600
+ the window is open are coalesced into a single trailing invocation with the
1601
+ latest arguments. Matches the lodash semantics Ink relied on: a single call
1602
+ produces only a leading invocation, no trailing one.
1603
+ */
1604
+ const throttle = (fn, wait = 0) => {
1605
+ let timer;
1606
+ let pendingArgs;
1607
+ const invokePending = () => {
1608
+ const args = pendingArgs;
1609
+ pendingArgs = void 0;
1610
+ fn(...args);
1611
+ };
1612
+ const onTimer = () => {
1613
+ if (pendingArgs) {
1614
+ invokePending();
1615
+ timer = setTimeout(onTimer, wait);
1616
+ } else timer = void 0;
1617
+ };
1618
+ const throttled = (...args) => {
1619
+ if (timer) {
1620
+ pendingArgs = args;
1621
+ return;
1622
+ }
1623
+ fn(...args);
1624
+ timer = setTimeout(onTimer, wait);
1625
+ };
1626
+ throttled.cancel = () => {
1627
+ if (timer) {
1628
+ clearTimeout(timer);
1629
+ timer = void 0;
1630
+ }
1631
+ pendingArgs = void 0;
1632
+ };
1633
+ throttled.flush = () => {
1634
+ if (!pendingArgs) return;
1635
+ if (timer) {
1636
+ clearTimeout(timer);
1637
+ timer = void 0;
1638
+ }
1639
+ invokePending();
1640
+ };
1641
+ return throttled;
1642
+ };
1643
+ //#endregion
1644
+ //#region src/terminal/render-scheduler.ts
1645
+ /** Owns frame cadence independently of React reconciliation and terminal presentation. */
1646
+ function createRenderScheduler(render, options) {
1647
+ const frameInterval = options.maxFps > 0 ? Math.max(1, Math.ceil(1e3 / options.maxFps)) : 0;
1648
+ let pending = false;
1649
+ const throttled = options.unthrottled ? void 0 : throttle(render, frameInterval);
1650
+ return {
1651
+ intervalMs: options.unthrottled ? 0 : frameInterval,
1652
+ throttled,
1653
+ get pending() {
1654
+ return pending;
1655
+ },
1656
+ schedule: options.unthrottled ? render : () => {
1657
+ pending = true;
1658
+ throttled();
1659
+ },
1660
+ immediate: render,
1661
+ markRendered() {
1662
+ pending = false;
1663
+ }
1664
+ };
1665
+ }
1666
+ //#endregion
1667
+ //#region src/ink.tsx
1668
+ /** @jsxImportSource react */
1669
+ const noop = () => {};
1670
+ const beforeExitCallbacks = /* @__PURE__ */ new Set();
1671
+ const runBeforeExitCallbacks = () => {
1672
+ for (const callback of beforeExitCallbacks) callback();
1673
+ };
1674
+ function registerBeforeExit(callback) {
1675
+ if (beforeExitCallbacks.size === 0) process.on("beforeExit", runBeforeExitCallbacks);
1676
+ beforeExitCallbacks.add(callback);
1677
+ return () => {
1678
+ beforeExitCallbacks.delete(callback);
1679
+ if (beforeExitCallbacks.size === 0) process.off("beforeExit", runBeforeExitCallbacks);
1680
+ };
1681
+ }
1682
+ function bottomRows(screen, height) {
1683
+ if (screen.height <= height) return screen;
1684
+ const cropped = new Screen(screen.width, height);
1685
+ const offset = screen.height - height;
1686
+ for (let y = 0; y < height; y++) for (let x = 0; x < screen.width; x++) {
1687
+ const cell = screen.cellAt(x, y + offset);
1688
+ if (cell && cell.width > 0) cropped.setCell(x, y, cell);
1689
+ }
1690
+ return cropped;
1691
+ }
1692
+ const RESIZE_SETTLE_MS = 50;
1693
+ const shouldClearTerminalForFrame = ({ isTTY, viewportRows, previousOutputHeight, nextOutputHeight, isUnmounting }) => {
1694
+ if (!isTTY) return false;
1695
+ const hadPreviousFrame = previousOutputHeight > 0;
1696
+ const wasFullscreen = previousOutputHeight >= viewportRows;
1697
+ const wasOverflowing = previousOutputHeight > viewportRows;
1698
+ const isOverflowing = nextOutputHeight > viewportRows;
1699
+ const isFullscreen = nextOutputHeight >= viewportRows;
1700
+ const isLeavingFullscreen = wasOverflowing && nextOutputHeight < viewportRows;
1701
+ const shouldClearOnUnmount = isUnmounting && wasFullscreen;
1702
+ if (isWindows && (wasFullscreen || isFullscreen)) return true;
1703
+ return wasOverflowing || isOverflowing && hadPreviousFrame || isLeavingFullscreen || shouldClearOnUnmount;
1704
+ };
1705
+ const isErrorInput = (value) => {
1706
+ return value instanceof Error || isNativeError(value);
1707
+ };
1708
+ const getWritableStreamState = (stdout) => {
1709
+ return { canWriteToStdout: !stdout.destroyed && !stdout.writableEnded && (stdout.writable ?? true) };
1710
+ };
1711
+ const settleThrottle = (throttled, canWriteToStdout) => {
1712
+ if (!throttled) return;
1713
+ if (canWriteToStdout) throttled.flush();
1714
+ else throttled.cancel();
1715
+ };
1716
+ const writeBestEffort = (stream, data) => {
1717
+ try {
1718
+ stream.write(data);
1719
+ } catch {}
1720
+ };
1721
+ const createRenderPassthrough = (stream) => {
1722
+ const passthrough = Object.create(stream);
1723
+ passthrough.write = stream.write.bind(stream);
1724
+ passthrough.on = stream.on.bind(stream);
1725
+ passthrough.off = stream.off.bind(stream);
1726
+ passthrough.once = stream.once.bind(stream);
1727
+ passthrough.addListener = stream.addListener.bind(stream);
1728
+ passthrough.removeListener = stream.removeListener.bind(stream);
1729
+ passthrough.emit = stream.emit.bind(stream);
1730
+ return passthrough;
1731
+ };
1732
+ const createInk = (options) => {
1733
+ let captureTargets;
1734
+ if (options.patchConsole === "stdio") {
1735
+ captureTargets = {
1736
+ stdout: options.stdout,
1737
+ stderr: options.stderr
1738
+ };
1739
+ options = {
1740
+ ...options,
1741
+ stdout: createRenderPassthrough(options.stdout),
1742
+ stderr: createRenderPassthrough(options.stderr)
1743
+ };
1744
+ }
1745
+ const rootNode = createNode("ink-root");
1746
+ rootNode.onComputeLayout = calculateLayout;
1747
+ const isScreenReaderEnabled = options.isScreenReaderEnabled ?? isScreenReader;
1748
+ const interactive = options.interactive ?? (!isInCi && Boolean(options.stdout.isTTY));
1749
+ const terminal = new TerminalSession({
1750
+ stdin: options.stdin,
1751
+ stdout: options.stdout,
1752
+ stderr: options.stderr,
1753
+ colorPolicy: options.colorProfile ?? "auto",
1754
+ onCapabilitiesChange: () => rootNode.onRender?.()
1755
+ });
1756
+ const terminalOsc = {
1757
+ publishProgress: (owner, state, value) => {
1758
+ terminal.publishProgress(owner, state, value);
1759
+ },
1760
+ copyToClipboard: (text, selection) => {
1761
+ terminal.copyToClipboard(text, selection);
1762
+ },
1763
+ publishTitle: (owner, title) => terminal.publishTitle(owner, title),
1764
+ setWorkingDirectory: (directory) => terminal.setWorkingDirectory(directory),
1765
+ notify: (title) => terminal.notify(title),
1766
+ setPointerShape: (shape) => terminal.setPointerShape(shape)
1767
+ };
1768
+ const capabilitiesStore = terminal.capabilities;
1769
+ const renderScheduler = createRenderScheduler(onRender, {
1770
+ unthrottled: options.debug || isScreenReaderEnabled,
1771
+ maxFps: options.maxFps ?? 30
1772
+ });
1773
+ rootNode.onRender = renderScheduler.schedule;
1774
+ rootNode.onImmediateRender = renderScheduler.immediate;
1775
+ rootNode.onStaticChange = handleStaticChange;
1776
+ const accessiblePresenter = isScreenReaderEnabled ? createInlinePresenter(options.stdout, { showCursor: true }) : void 0;
1777
+ let isUnmounted = false;
1778
+ let isUnmounting = false;
1779
+ const isConcurrent = options.concurrent ?? false;
1780
+ let lastOutput = "";
1781
+ let lastOutputToRender = "";
1782
+ let lastOutputHeight = 0;
1783
+ let lastScreen;
1784
+ const windowSize = () => capabilitiesStore.current.size;
1785
+ let lastTerminalWidth = windowSize().columns;
1786
+ let lastTerminalHeight = windowSize().rows;
1787
+ let resizeSettle;
1788
+ let fullStaticOutput = "";
1789
+ let exitResult;
1790
+ let unsubscribeBeforeExit;
1791
+ let restoreConsole;
1792
+ const capturedStdioTails = {
1793
+ stdout: "",
1794
+ stderr: ""
1795
+ };
1796
+ let unsubscribeResize;
1797
+ let kittyProtocolEnabled = false;
1798
+ let kittyFlags;
1799
+ let cancelKittyDetection;
1800
+ let nextRenderCommit;
1801
+ let pauseInput;
1802
+ let resumeInput;
1803
+ const rootTag = isConcurrent ? import_constants.ConcurrentRoot : import_constants.LegacyRoot;
1804
+ const container = reconciler.createContainer(rootNode, rootTag, null, false, null, "id", () => {}, () => {}, () => {}, () => {});
1805
+ const unsubscribeExit = signalExit(unmount, { alwaysLast: false });
1806
+ setAlternateScreen(Boolean(options.alternateScreen));
1807
+ if (isSigilDev) reconciler.injectIntoDevTools();
1808
+ if (options.patchConsole) installConsolePatch();
1809
+ if (interactive) {
1810
+ let seenColumns = lastTerminalWidth;
1811
+ let seenRows = lastTerminalHeight;
1812
+ unsubscribeResize = capabilitiesStore.subscribe(({ size }) => {
1813
+ if (size.columns === seenColumns && size.rows === seenRows) return;
1814
+ seenColumns = size.columns;
1815
+ seenRows = size.rows;
1816
+ resized(size.source);
1817
+ });
1818
+ }
1819
+ initKittyKeyboard();
1820
+ const { promise: exitPromise, resolve: resolveExitPromise, reject: rejectExitPromise } = Promise.withResolvers();
1821
+ exitPromise.catch(noop);
1822
+ function resized(source) {
1823
+ if (resizeSettle !== void 0) {
1824
+ clearTimeout(resizeSettle);
1825
+ resizeSettle = void 0;
1826
+ }
1827
+ if (source === "terminal") {
1828
+ settleResize();
1829
+ return;
1830
+ }
1831
+ resizeSettle = setTimeout(settleResize, RESIZE_SETTLE_MS);
1832
+ }
1833
+ function settleResize() {
1834
+ resizeSettle = void 0;
1835
+ if (isUnmounted || isUnmounting) return;
1836
+ const { columns: currentWidth, rows: currentHeight } = windowSize();
1837
+ if (currentWidth < lastTerminalWidth || currentHeight !== lastTerminalHeight) {
1838
+ clearLiveOutput(rewrapsOnResize() ? currentWidth : void 0);
1839
+ resetLiveOutput();
1840
+ lastOutput = "";
1841
+ lastOutputToRender = "";
1842
+ lastOutputHeight = 0;
1843
+ }
1844
+ lastTerminalWidth = currentWidth;
1845
+ lastTerminalHeight = currentHeight;
1846
+ calculateLayout();
1847
+ emitLayoutListeners(rootNode);
1848
+ onRender();
1849
+ }
1850
+ function rewrapsOnResize() {
1851
+ return !isWindows && capabilitiesStore.current.terminal.name !== "apple-terminal";
1852
+ }
1853
+ function handleAppExit(errorOrResult) {
1854
+ if (isUnmounted || isUnmounting) return;
1855
+ if (isErrorInput(errorOrResult)) {
1856
+ unmount(errorOrResult);
1857
+ return;
1858
+ }
1859
+ exitResult = errorOrResult;
1860
+ unmount();
1861
+ }
1862
+ function setCursorPosition(position) {
1863
+ terminal.setCursor(position);
1864
+ accessiblePresenter?.setCursorPosition(position);
1865
+ }
1866
+ function restoreLastOutput() {
1867
+ if (!interactive) return;
1868
+ if (isScreenReaderEnabled) {
1869
+ accessiblePresenter.setCursorPosition(terminal.cursor.position);
1870
+ accessiblePresenter(lastOutputToRender || lastOutput + "\n");
1871
+ } else if (lastScreen) terminal.present(lastScreen, {
1872
+ fullscreen: lastOutputToRender === lastOutput,
1873
+ forceRewrite: true
1874
+ });
1875
+ }
1876
+ function clearLiveOutput(columns) {
1877
+ if (isScreenReaderEnabled) accessiblePresenter.clear();
1878
+ else terminal.clearFrame({ columns });
1879
+ }
1880
+ function finishLiveOutput() {
1881
+ if (isScreenReaderEnabled) accessiblePresenter.done();
1882
+ else terminal.finishFrame();
1883
+ }
1884
+ function resetLiveOutput() {
1885
+ if (isScreenReaderEnabled) accessiblePresenter.reset();
1886
+ else terminal.resetFrame();
1887
+ }
1888
+ function calculateLayout() {
1889
+ const terminalWidth = windowSize().columns;
1890
+ rootNode.yogaNode.setWidth(terminalWidth);
1891
+ rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
1892
+ }
1893
+ function handleStaticChange() {
1894
+ fullStaticOutput = "";
1895
+ }
1896
+ function onRender() {
1897
+ renderScheduler.markRendered();
1898
+ if (isUnmounted) return;
1899
+ if (terminal.suspended) {
1900
+ if (nextRenderCommit) {
1901
+ nextRenderCommit.resolve();
1902
+ nextRenderCommit = void 0;
1903
+ }
1904
+ return;
1905
+ }
1906
+ if (resizeSettle !== void 0 && !isUnmounting) {
1907
+ if (nextRenderCommit) {
1908
+ nextRenderCommit.resolve();
1909
+ nextRenderCommit = void 0;
1910
+ }
1911
+ return;
1912
+ }
1913
+ if (nextRenderCommit) {
1914
+ nextRenderCommit.resolve();
1915
+ nextRenderCommit = void 0;
1916
+ }
1917
+ const startTime = performance.now();
1918
+ const rendered = renderFrame(rootNode, isScreenReaderEnabled, {
1919
+ colorProfile: terminal.colorProfile,
1920
+ paintContext: {
1921
+ appearance: capabilitiesStore.current.theme.appearance,
1922
+ palette: capabilitiesStore.current.theme.palette
1923
+ }
1924
+ });
1925
+ const screen = rendered.screen;
1926
+ const output = rendered.accessibleText ?? (screen ? terminal.encode(screen) : "");
1927
+ const outputHeight = rendered.accessibleText === void 0 ? screen?.height ?? 0 : rendered.accessibleText === "" ? 0 : rendered.accessibleText.split("\n").length;
1928
+ const staticBody = rendered.staticAccessibleText ?? (rendered.staticScreen ? terminal.encode(rendered.staticScreen) : "");
1929
+ const staticOutput = staticBody ? `${staticBody}\n` : "";
1930
+ options.onRender?.({ renderTime: performance.now() - startTime });
1931
+ const hasStaticOutput = staticOutput && staticOutput !== "\n";
1932
+ if (options.debug) {
1933
+ if (hasStaticOutput) fullStaticOutput += staticOutput;
1934
+ lastOutput = output;
1935
+ lastOutputToRender = output;
1936
+ lastOutputHeight = outputHeight;
1937
+ options.stdout.write(fullStaticOutput + output);
1938
+ return;
1939
+ }
1940
+ if (!interactive) {
1941
+ if (hasStaticOutput) options.stdout.write(staticOutput);
1942
+ lastOutput = output;
1943
+ lastOutputToRender = output + "\n";
1944
+ lastOutputHeight = outputHeight;
1945
+ return;
1946
+ }
1947
+ if (isScreenReaderEnabled) {
1948
+ const sync = shouldSync();
1949
+ if (sync) options.stdout.write(bsu);
1950
+ if (hasStaticOutput) {
1951
+ const erase = lastOutputHeight > 0 ? ansiEscapes.eraseLines(lastOutputHeight) : "";
1952
+ options.stdout.write(erase + staticOutput);
1953
+ lastOutputHeight = 0;
1954
+ }
1955
+ if (output === lastOutput && !hasStaticOutput) {
1956
+ if (sync) options.stdout.write(esu);
1957
+ return;
1958
+ }
1959
+ const terminalWidth = windowSize().columns;
1960
+ const wrappedOutput = wrapAnsi(output, terminalWidth, {
1961
+ trim: false,
1962
+ hard: true
1963
+ });
1964
+ if (hasStaticOutput) options.stdout.write(wrappedOutput);
1965
+ else {
1966
+ const erase = lastOutputHeight > 0 ? ansiEscapes.eraseLines(lastOutputHeight) : "";
1967
+ options.stdout.write(erase + wrappedOutput);
1968
+ }
1969
+ lastOutput = output;
1970
+ lastOutputToRender = wrappedOutput;
1971
+ lastOutputHeight = wrappedOutput === "" ? 0 : wrappedOutput.split("\n").length;
1972
+ if (sync) options.stdout.write(esu);
1973
+ return;
1974
+ }
1975
+ if (hasStaticOutput) fullStaticOutput += staticOutput;
1976
+ renderInteractiveFrame(output, outputHeight, hasStaticOutput ? staticOutput : "", screen, false);
1977
+ }
1978
+ function render(node) {
1979
+ const tree = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(accessibilityContext.Provider, {
1980
+ value: { isScreenReaderEnabled },
1981
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalOscContext.Provider, {
1982
+ value: terminalOsc,
1983
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(App, {
1984
+ stdin: options.stdin,
1985
+ stdout: options.stdout,
1986
+ stderr: options.stderr,
1987
+ exitOnCtrlC: options.exitOnCtrlC,
1988
+ interactive,
1989
+ renderThrottleMs: renderScheduler.intervalMs,
1990
+ terminalInput: terminal.input,
1991
+ writeToStdout,
1992
+ writeToStderr,
1993
+ setCursorPosition,
1994
+ onExit: handleAppExit,
1995
+ onWaitUntilRenderFlush: waitUntilRenderFlush,
1996
+ onSuspendTerminal: suspendTerminal,
1997
+ onRegisterInputControl: registerInputControl,
1998
+ children: node
1999
+ })
2000
+ })
2001
+ });
2002
+ if (isConcurrent) reconciler.updateContainer(tree, container, null, noop);
2003
+ else {
2004
+ reconciler.updateContainerSync(tree, container, null, noop);
2005
+ reconciler.flushSyncWork();
2006
+ }
2007
+ }
2008
+ function writeToStdout(data) {
2009
+ if (isUnmounted) return;
2010
+ if (terminal.suspended) return;
2011
+ if (options.debug) {
2012
+ options.stdout.write(data + fullStaticOutput + lastOutput);
2013
+ return;
2014
+ }
2015
+ if (!interactive) {
2016
+ options.stdout.write(data);
2017
+ return;
2018
+ }
2019
+ const sync = shouldSync();
2020
+ if (sync) options.stdout.write(bsu);
2021
+ clearLiveOutput();
2022
+ options.stdout.write(data);
2023
+ restoreLastOutput();
2024
+ if (sync) options.stdout.write(esu);
2025
+ }
2026
+ function writeToStderr(data) {
2027
+ if (isUnmounted) return;
2028
+ if (terminal.suspended) return;
2029
+ if (options.debug) {
2030
+ options.stderr.write(data);
2031
+ options.stdout.write(fullStaticOutput + lastOutput);
2032
+ return;
2033
+ }
2034
+ if (!interactive) {
2035
+ options.stderr.write(data);
2036
+ return;
2037
+ }
2038
+ const sync = shouldSync();
2039
+ if (sync) options.stdout.write(bsu);
2040
+ clearLiveOutput();
2041
+ options.stderr.write(data);
2042
+ restoreLastOutput();
2043
+ if (sync) options.stdout.write(esu);
2044
+ }
2045
+ function unmount(error) {
2046
+ if (isUnmounted || isUnmounting) return;
2047
+ isUnmounting = true;
2048
+ if (resizeSettle !== void 0) {
2049
+ clearTimeout(resizeSettle);
2050
+ resizeSettle = void 0;
2051
+ }
2052
+ unsubscribeBeforeExit?.();
2053
+ unsubscribeBeforeExit = void 0;
2054
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2055
+ if (canWriteToStdout) flushCapturedStdio();
2056
+ settleThrottle(renderScheduler.throttled, canWriteToStdout);
2057
+ if (canWriteToStdout) {
2058
+ if (!renderScheduler.throttled || !renderScheduler.pending && fullStaticOutput === "") {
2059
+ calculateLayout();
2060
+ onRender();
2061
+ }
2062
+ }
2063
+ isUnmounted = true;
2064
+ unsubscribeExit();
2065
+ terminal.cleanup();
2066
+ if (typeof restoreConsole === "function") restoreConsole();
2067
+ const finishUnmount = () => {
2068
+ if (typeof unsubscribeResize === "function") unsubscribeResize();
2069
+ if (cancelKittyDetection) cancelKittyDetection();
2070
+ if (canWriteToStdout) {
2071
+ if (kittyProtocolEnabled) writeBestEffort(options.stdout, ansiEscapes.popKittyKeyboard);
2072
+ if (terminal.alternateScreen) {
2073
+ terminal.setAlternateScreen(false);
2074
+ terminal.setCursorAppearance({ visible: true });
2075
+ }
2076
+ if (!interactive) options.stdout.write(options.debug ? "\n" : lastOutput + "\n");
2077
+ else if (!options.debug) finishLiveOutput();
2078
+ }
2079
+ kittyProtocolEnabled = false;
2080
+ instances.delete(captureTargets?.stdout ?? options.stdout);
2081
+ const finalExitResult = exitResult;
2082
+ const resolveOrReject = () => {
2083
+ if (isErrorInput(error)) rejectExitPromise(error);
2084
+ else resolveExitPromise(finalExitResult);
2085
+ };
2086
+ if (error !== void 0 && !isErrorInput(error)) resolveOrReject();
2087
+ else if (canWriteToStdout) options.stdout.write("", resolveOrReject);
2088
+ else setImmediate(resolveOrReject);
2089
+ };
2090
+ const concurrentReconciler = reconciler;
2091
+ if (isConcurrent) {
2092
+ reconciler.updateContainerSync(null, container, null, noop);
2093
+ reconciler.flushSyncWork();
2094
+ concurrentReconciler.flushPassiveEffects?.();
2095
+ finishUnmount();
2096
+ } else {
2097
+ reconciler.updateContainerSync(null, container, null, noop);
2098
+ reconciler.flushSyncWork();
2099
+ finishUnmount();
2100
+ }
2101
+ }
2102
+ async function waitUntilExit() {
2103
+ if (!unsubscribeBeforeExit) unsubscribeBeforeExit = registerBeforeExit(() => {
2104
+ unmount();
2105
+ });
2106
+ return exitPromise;
2107
+ }
2108
+ async function waitUntilRenderFlush() {
2109
+ if (isUnmounted || isUnmounting) {
2110
+ await awaitExit();
2111
+ return;
2112
+ }
2113
+ await setImmediate$1();
2114
+ if (isUnmounted || isUnmounting) {
2115
+ await awaitExit();
2116
+ return;
2117
+ }
2118
+ if (isConcurrent && hasPendingConcurrentWork()) {
2119
+ await Promise.race([awaitNextRender(), awaitExit()]);
2120
+ if (isUnmounted || isUnmounting) {
2121
+ nextRenderCommit = void 0;
2122
+ await awaitExit();
2123
+ return;
2124
+ }
2125
+ }
2126
+ reconciler.flushSyncWork();
2127
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2128
+ settleThrottle(renderScheduler.throttled, canWriteToStdout);
2129
+ if (canWriteToStdout) {
2130
+ await new Promise((resolve) => {
2131
+ options.stdout.write("", () => {
2132
+ resolve();
2133
+ });
2134
+ });
2135
+ return;
2136
+ }
2137
+ await setImmediate$1();
2138
+ }
2139
+ function clear() {
2140
+ if (interactive && !options.debug) {
2141
+ clearLiveOutput();
2142
+ if (isScreenReaderEnabled) accessiblePresenter.sync(lastOutputToRender || lastOutput + "\n");
2143
+ }
2144
+ }
2145
+ function installConsolePatch() {
2146
+ if (options.debug) return;
2147
+ const restoreConsoleMethods = patchConsole((stream, data) => {
2148
+ if (options.onCapturedOutput?.(stream, data, "console") === true) return;
2149
+ if (stream === "stdout") writeToStdout(data);
2150
+ if (stream === "stderr") {
2151
+ if (!data.startsWith("The above error occurred")) writeToStderr(data);
2152
+ }
2153
+ });
2154
+ const restoreDirectStdio = patchDirectStdio();
2155
+ restoreConsole = () => {
2156
+ restoreConsoleMethods();
2157
+ restoreDirectStdio?.();
2158
+ };
2159
+ }
2160
+ function patchDirectStdio() {
2161
+ if (!captureTargets) return;
2162
+ const restoreStdout = patchStreamWrite(captureTargets.stdout, (data) => {
2163
+ handleCapturedStdio("stdout", data);
2164
+ });
2165
+ const restoreStderr = patchStreamWrite(captureTargets.stderr, (data) => {
2166
+ handleCapturedStdio("stderr", data);
2167
+ });
2168
+ return () => {
2169
+ restoreStdout();
2170
+ restoreStderr();
2171
+ };
2172
+ }
2173
+ function handleCapturedStdio(stream, data) {
2174
+ if (options.onCapturedOutput?.(stream, data, "stdio") === true) return;
2175
+ const parts = (capturedStdioTails[stream] + data).split(/\r?\n/);
2176
+ capturedStdioTails[stream] = parts.pop() ?? "";
2177
+ if (parts.length === 0) return;
2178
+ const payload = parts.join("\n") + "\n";
2179
+ if (stream === "stdout") writeToStdout(payload);
2180
+ else writeToStderr(payload);
2181
+ }
2182
+ function flushCapturedStdio() {
2183
+ for (const stream of ["stdout", "stderr"]) {
2184
+ const tail = capturedStdioTails[stream];
2185
+ if (tail === "") continue;
2186
+ capturedStdioTails[stream] = "";
2187
+ if (stream === "stdout") writeToStdout(tail + "\n");
2188
+ else writeToStderr(tail + "\n");
2189
+ }
2190
+ }
2191
+ function registerInputControl(pause, resume) {
2192
+ pauseInput = pause;
2193
+ resumeInput = resume;
2194
+ }
2195
+ async function suspendTerminal(callback) {
2196
+ beginSuspend();
2197
+ if (callback) {
2198
+ try {
2199
+ await callback();
2200
+ } finally {
2201
+ await endSuspend();
2202
+ }
2203
+ return;
2204
+ }
2205
+ const resume = async () => {
2206
+ await endSuspend();
2207
+ };
2208
+ return {
2209
+ resume,
2210
+ [Symbol.asyncDispose]: resume
2211
+ };
2212
+ }
2213
+ function setAlternateScreen(enabled) {
2214
+ terminal.setAlternateScreen(enabled && interactive && Boolean(options.stdout.isTTY), { hideCursor: true });
2215
+ }
2216
+ function shouldSync() {
2217
+ return Boolean(options.stdout.isTTY) && interactive;
2218
+ }
2219
+ async function awaitExit() {
2220
+ try {
2221
+ await exitPromise;
2222
+ } catch {}
2223
+ }
2224
+ function hasPendingConcurrentWork() {
2225
+ const concurrentContainer = container;
2226
+ return (concurrentContainer.pendingLanes ?? 0) !== 0 && concurrentContainer.callbackNode !== void 0 && concurrentContainer.callbackNode !== null;
2227
+ }
2228
+ async function awaitNextRender() {
2229
+ nextRenderCommit ??= Promise.withResolvers();
2230
+ return nextRenderCommit.promise;
2231
+ }
2232
+ function renderInteractiveFrame(output, outputHeight, staticOutput, screen, hasOverflow) {
2233
+ if (!screen) return;
2234
+ const hasStaticOutput = staticOutput !== "";
2235
+ const isTTY = Boolean(options.stdout.isTTY);
2236
+ const viewportRows = isTTY ? windowSize().rows : 24;
2237
+ if (isTTY && outputHeight > viewportRows) {
2238
+ const lines = output.split("\n");
2239
+ output = lines.slice(lines.length - viewportRows).join("\n");
2240
+ outputHeight = viewportRows;
2241
+ screen = bottomRows(screen, viewportRows);
2242
+ }
2243
+ const isFullscreen = isTTY && outputHeight >= viewportRows;
2244
+ const outputToRender = isFullscreen ? output : output + "\n";
2245
+ if (shouldClearTerminalForFrame({
2246
+ isTTY,
2247
+ viewportRows,
2248
+ previousOutputHeight: lastOutputHeight,
2249
+ nextOutputHeight: outputHeight,
2250
+ isUnmounting
2251
+ })) {
2252
+ const sync = shouldSync();
2253
+ if (sync) options.stdout.write(bsu);
2254
+ options.stdout.write(ansiEscapes.clearTerminal + fullStaticOutput + outputToRender);
2255
+ lastOutput = output;
2256
+ lastOutputToRender = outputToRender;
2257
+ lastOutputHeight = outputHeight;
2258
+ lastScreen = screen;
2259
+ terminal.resetFrame();
2260
+ if (sync) options.stdout.write(esu);
2261
+ return;
2262
+ }
2263
+ const willPresent = terminal.willPresent(screen, {
2264
+ fullscreen: isFullscreen,
2265
+ forceRewrite: hasStaticOutput || hasOverflow
2266
+ });
2267
+ const sync = shouldSync() && willPresent;
2268
+ if (sync) terminal.write(bsu);
2269
+ if (hasStaticOutput) {
2270
+ terminal.clearFrame();
2271
+ terminal.write(staticOutput);
2272
+ }
2273
+ terminal.present(screen, {
2274
+ fullscreen: isFullscreen,
2275
+ forceRewrite: hasStaticOutput || hasOverflow
2276
+ });
2277
+ if (sync) terminal.write(esu);
2278
+ lastOutput = output;
2279
+ lastOutputToRender = outputToRender;
2280
+ lastOutputHeight = outputHeight;
2281
+ lastScreen = screen;
2282
+ }
2283
+ function initKittyKeyboard() {
2284
+ if (!options.kittyKeyboard) return;
2285
+ const opts = options.kittyKeyboard;
2286
+ const mode = opts.mode ?? "auto";
2287
+ if (mode === "disabled") return;
2288
+ const flags = opts.flags ?? ["disambiguateEscapeCodes"];
2289
+ if (mode === "enabled") {
2290
+ if (isTty(options.stdin) && options.stdout.isTTY) enableKittyProtocol(flags);
2291
+ return;
2292
+ }
2293
+ if (!interactive || !isTty(options.stdin) || !options.stdout.isTTY) return;
2294
+ cancelKittyDetection = detectKittySupport(options.stdin, options.stdout, () => {
2295
+ cancelKittyDetection = void 0;
2296
+ if (!isUnmounted) enableKittyProtocol(flags);
2297
+ });
2298
+ }
2299
+ function enableKittyProtocol(flags) {
2300
+ options.stdout.write(ansiEscapes.pushKittyKeyboard(resolveFlags(flags)));
2301
+ kittyProtocolEnabled = true;
2302
+ kittyFlags = flags;
2303
+ }
2304
+ function beginSuspend() {
2305
+ terminal.beginSuspension();
2306
+ if (!interactive || isUnmounted || isUnmounting) return;
2307
+ try {
2308
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2309
+ settleThrottle(renderScheduler.throttled, canWriteToStdout);
2310
+ if (canWriteToStdout) flushCapturedStdio();
2311
+ if (canWriteToStdout) {
2312
+ clearLiveOutput();
2313
+ finishLiveOutput();
2314
+ if (kittyProtocolEnabled) writeBestEffort(options.stdout, ansiEscapes.popKittyKeyboard);
2315
+ if (terminal.alternateScreen) writeBestEffort(options.stdout, ansiEscapes.exitAlternativeScreen);
2316
+ }
2317
+ pauseInput?.();
2318
+ } catch (error) {
2319
+ terminal.resume();
2320
+ try {
2321
+ resumeInput?.();
2322
+ } catch {}
2323
+ throw error;
2324
+ }
2325
+ }
2326
+ async function endSuspend() {
2327
+ if (!terminal.suspended) return;
2328
+ terminal.resume();
2329
+ resumeInput?.();
2330
+ if (!interactive || isUnmounted || isUnmounting) return;
2331
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2332
+ if (canWriteToStdout) {
2333
+ if (terminal.alternateScreen) writeBestEffort(options.stdout, ansiEscapes.enterAlternativeScreen);
2334
+ if (kittyProtocolEnabled && kittyFlags) writeBestEffort(options.stdout, ansiEscapes.pushKittyKeyboard(resolveFlags(kittyFlags)));
2335
+ }
2336
+ lastOutput = "";
2337
+ lastOutputToRender = "";
2338
+ lastOutputHeight = 0;
2339
+ resetLiveOutput();
2340
+ try {
2341
+ calculateLayout();
2342
+ onRender();
2343
+ await waitUntilRenderFlush();
2344
+ } catch {}
2345
+ }
2346
+ return {
2347
+ render,
2348
+ unmount,
2349
+ waitUntilExit,
2350
+ waitUntilRenderFlush,
2351
+ clear,
2352
+ copyToClipboard: (text, selection) => terminal.copyToClipboard(text, selection),
2353
+ setProgress: (state, value) => terminal.setProgress(state, value)
2354
+ };
2355
+ };
2356
+ //#endregion
2357
+ //#region src/render.ts
2358
+ /**
2359
+ Mount a component and render the output.
2360
+ */
2361
+ const render = (node, options) => {
2362
+ const inkOptions = {
2363
+ stdout: process.stdout,
2364
+ stdin: process.stdin,
2365
+ stderr: process.stderr,
2366
+ debug: false,
2367
+ exitOnCtrlC: true,
2368
+ patchConsole: true,
2369
+ maxFps: 30,
2370
+ concurrent: false,
2371
+ alternateScreen: false,
2372
+ ...getOptions(options)
2373
+ };
2374
+ const instance = getInstance(inkOptions.stdout, () => createInk(inkOptions));
2375
+ instance.render(node);
2376
+ return {
2377
+ rerender: instance.render,
2378
+ unmount() {
2379
+ instance.unmount();
2380
+ },
2381
+ waitUntilExit: instance.waitUntilExit,
2382
+ waitUntilRenderFlush: instance.waitUntilRenderFlush,
2383
+ cleanup() {
2384
+ instance.unmount();
2385
+ },
2386
+ clear: instance.clear,
2387
+ copyToClipboard: instance.copyToClipboard,
2388
+ setProgress: instance.setProgress
2389
+ };
2390
+ };
2391
+ const getOptions = (stdout = {}) => {
2392
+ if (stdout instanceof Stream) return {
2393
+ stdout,
2394
+ stdin: process.stdin
2395
+ };
2396
+ return stdout;
2397
+ };
2398
+ const getInstance = (stdout, createInstance) => {
2399
+ const instance = instances.get(stdout);
2400
+ if (instance === void 0) {
2401
+ const newInstance = createInstance();
2402
+ instances.set(stdout, newInstance);
2403
+ return newInstance;
2404
+ }
2405
+ process.stderr.write("Warning: render() was called again for the same stdout before the previous Ink instance was unmounted. Reusing stdout across multiple render() calls is unsupported. Call unmount() first.\n");
2406
+ return instance;
2407
+ };
2408
+ //#endregion
2409
+ //#region src/render-to-string.ts
2410
+ /**
2411
+ Render a React element to a string synchronously. Unlike `render()`, this function does not write to stdout, does not set up any terminal event listeners, and returns the rendered output as a string.
2412
+
2413
+ Useful for generating documentation, writing output to files, testing, or any scenario where you need the rendered output as a string without starting a persistent terminal application.
2414
+
2415
+ **Notes:**
2416
+
2417
+ - Terminal-specific hooks (`useInput`, `useStdin`, `useStdout`, `useStderr`, `useApp`, `useFocus`, `useFocusManager`) return default no-op values since there is no terminal session. They will not throw, but they will not function as in a live terminal.
2418
+ - `useEffect` callbacks will execute during rendering (due to synchronous rendering mode), but state updates they trigger will not affect the returned output, which reflects the initial render.
2419
+ - `useLayoutEffect` callbacks fire synchronously during commit, so state updates they trigger **will** be reflected in the output.
2420
+ - The `<Static>` component is supported — its output is prepended to the dynamic output.
2421
+ - If a component throws during rendering, the error is propagated to the caller after cleanup.
2422
+
2423
+ @example
2424
+ ```
2425
+ import {renderToString, Text, Box} from 'ink';
2426
+
2427
+ const output = renderToString(
2428
+ <Box padding={1}>
2429
+ <Text color="green">Hello World</Text>
2430
+ </Box>,
2431
+ {columns: 40}
2432
+ );
2433
+
2434
+ console.log(output);
2435
+ ```
2436
+ */
2437
+ const renderToString = (node, options) => {
2438
+ const columns = options?.columns ?? 80;
2439
+ const colorProfile = options?.colorProfile ?? colorProfileFromLevel(detectColorLevel());
2440
+ const rootNode = createNode("ink-root");
2441
+ let capturedStaticOutput = "";
2442
+ rootNode.onComputeLayout = () => {
2443
+ rootNode.yogaNode.setWidth(columns);
2444
+ rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
2445
+ };
2446
+ rootNode.onImmediateRender = () => {
2447
+ const { staticScreen } = renderFrame(rootNode, false, { colorProfile });
2448
+ const staticOutput = staticScreen ? `${serializeScreen(staticScreen, {
2449
+ colorProfile,
2450
+ styles: colorProfile !== "none"
2451
+ })}\n` : "";
2452
+ if (staticOutput && staticOutput !== "\n") capturedStaticOutput += staticOutput;
2453
+ };
2454
+ let uncaughtError;
2455
+ const container = reconciler.createContainer(rootNode, import_constants.LegacyRoot, null, false, null, "render-to-string", (error) => {
2456
+ uncaughtError ??= error;
2457
+ }, () => {}, () => {}, () => {});
2458
+ reconciler.updateContainerSync(node, container, null, () => {});
2459
+ reconciler.flushSyncWork();
2460
+ const { screen } = renderFrame(rootNode, false, { colorProfile });
2461
+ const output = screen ? serializeScreen(screen, {
2462
+ colorProfile,
2463
+ styles: colorProfile !== "none"
2464
+ }) : "";
2465
+ reconciler.updateContainerSync(null, container, null, () => {});
2466
+ reconciler.flushSyncWork();
2467
+ if (uncaughtError !== void 0) throw uncaughtError instanceof Error ? uncaughtError : new Error(String(uncaughtError));
2468
+ const normalizedStaticOutput = capturedStaticOutput.endsWith("\n") ? capturedStaticOutput.slice(0, -1) : capturedStaticOutput;
2469
+ if (normalizedStaticOutput && output) return normalizedStaticOutput + "\n" + output;
2470
+ return normalizedStaticOutput || output;
2471
+ };
2472
+ //#endregion
2473
+ //#region src/components/AnsiText.tsx
2474
+ /** @jsxImportSource react */
2475
+ /**
2476
+ * Renders explicitly trusted ANSI-styled output as structured terminal cells.
2477
+ * Ordinary `Text` continues to strip terminal control sequences.
2478
+ */
2479
+ function AnsiText({ children, wrap = "wrap", "aria-label": ariaLabel, "aria-hidden": ariaHidden = false }) {
2480
+ const { isScreenReaderEnabled } = (0, import_react.useContext)(accessibilityContext);
2481
+ if (isScreenReaderEnabled) {
2482
+ if (ariaHidden) return null;
2483
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: ariaLabel ?? stripAnsi(children) });
2484
+ }
2485
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ink-text", {
2486
+ style: {
2487
+ flexGrow: 0,
2488
+ flexShrink: 1,
2489
+ flexDirection: "row",
2490
+ textWrap: wrap
2491
+ },
2492
+ internal_ansi: true,
2493
+ children
2494
+ });
2495
+ }
2496
+ //#endregion
2497
+ //#region src/components/Static.tsx
2498
+ /** @jsxImportSource react */
2499
+ /**
2500
+ `<Static>` component permanently renders its output above everything else. It's useful for displaying activity like completed tasks or logs—things that don't change after they're rendered (hence the name "Static").
2501
+
2502
+ It's preferred to use `<Static>` for use cases like these when you can't know or control the number of items that need to be rendered.
2503
+
2504
+ For example, [Tap](https://github.com/tapjs/node-tap) uses `<Static>` to display a list of completed tests. [Gatsby](https://github.com/gatsbyjs/gatsby) uses it to display a list of generated pages while still displaying a live progress bar.
2505
+ */
2506
+ function Static(props) {
2507
+ const { items, children: render, style: customStyle } = props;
2508
+ const [index, setIndex] = (0, import_react.useState)(0);
2509
+ const itemsToRender = (0, import_react.useMemo)(() => {
2510
+ return items.slice(index);
2511
+ }, [items, index]);
2512
+ (0, import_react.useLayoutEffect)(() => {
2513
+ setIndex(items.length);
2514
+ }, [items.length]);
2515
+ const children = itemsToRender.map((item, itemIndex) => {
2516
+ return render(item, index + itemIndex);
2517
+ });
2518
+ const style = (0, import_react.useMemo)(() => ({
2519
+ position: "absolute",
2520
+ flexDirection: "column",
2521
+ ...customStyle
2522
+ }), [customStyle]);
2523
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ink-box", {
2524
+ internal_static: true,
2525
+ style,
2526
+ children
2527
+ });
2528
+ }
2529
+ //#endregion
2530
+ //#region src/components/Transform.tsx
2531
+ /** @jsxImportSource react */
2532
+ /**
2533
+ Transform a string representation of React components before they're written to output. For example, you might want to apply a gradient to text, add a clickable link, or create some text effects. These use cases can't accept React nodes as input; they expect a string. That's what the <Transform> component does: it gives you an output string of its child components and lets you transform it in any way.
2534
+ */
2535
+ function Transform({ children, transform, accessibilityLabel }) {
2536
+ const { isScreenReaderEnabled } = (0, import_react.useContext)(accessibilityContext);
2537
+ if (children === void 0 || children === null) return null;
2538
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ink-text", {
2539
+ style: {
2540
+ flexGrow: 0,
2541
+ flexShrink: 1,
2542
+ flexDirection: "row"
2543
+ },
2544
+ internal_transform: transform,
2545
+ children: isScreenReaderEnabled && accessibilityLabel ? accessibilityLabel : children
2546
+ });
2547
+ }
2548
+ //#endregion
2549
+ //#region src/hooks/use-stdout.ts
2550
+ /**
2551
+ A React hook that returns the stdout stream where Ink renders your app.
2552
+ */
2553
+ const useStdout = () => (0, import_react.useContext)(StdoutContext);
2554
+ //#endregion
2555
+ //#region src/components/Hyperlink.tsx
2556
+ /** @jsxImportSource react */
2557
+ /**
2558
+ A clickable OSC 8 hyperlink — the counterpart to the router's `<Link>`, which
2559
+ navigates between screens. On terminals without hyperlink support it falls
2560
+ back to `text (url)`.
2561
+
2562
+ ```tsx
2563
+ <Hyperlink url="https://example.com">Documentation</Hyperlink>
2564
+ ```
2565
+ */
2566
+ function Hyperlink({ url, fallback = true, children, ...textProps }) {
2567
+ const { stdout } = useStdout();
2568
+ if (!detectHyperlinkSupport(stdout)) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
2569
+ ...textProps,
2570
+ children: [children, fallback ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
2571
+ dimColor: true,
2572
+ children: [
2573
+ " (",
2574
+ url,
2575
+ ")"
2576
+ ]
2577
+ }) : null]
2578
+ });
2579
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Transform, {
2580
+ transform: (text) => link(text, url),
2581
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
2582
+ ...textProps,
2583
+ children
2584
+ })
2585
+ });
2586
+ }
2587
+ //#endregion
2588
+ //#region src/components/Newline.tsx
2589
+ /**
2590
+ Adds one or more newline (`\n`) characters. Must be used within `<Text>` components.
2591
+ */
2592
+ function Newline({ count = 1 }) {
2593
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ink-text", { children: "\n".repeat(count) });
2594
+ }
2595
+ //#endregion
2596
+ //#region src/components/Spacer.tsx
2597
+ /** @jsxImportSource react */
2598
+ /**
2599
+ A flexible space that expands along the major axis of its containing layout.
2600
+
2601
+ It's useful as a shortcut for filling all the available space between elements.
2602
+ */
2603
+ function Spacer() {
2604
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { flexGrow: 1 });
2605
+ }
2606
+ //#endregion
2607
+ //#region src/virtual-scroll.ts
2608
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
2609
+ const wholeRows = (value) => Math.max(0, Math.floor(value)) || 0;
2610
+ /**
2611
+ Compute which items intersect a viewport over a vertical stack of items.
2612
+ */
2613
+ const virtualScrollWindow = (options) => {
2614
+ const count = wholeRows(options.count);
2615
+ const viewportHeight = wholeRows(options.viewportHeight);
2616
+ const tops = [];
2617
+ let totalHeight = 0;
2618
+ for (let index = 0; index < count; index++) {
2619
+ tops.push(totalHeight);
2620
+ totalHeight += wholeRows(options.itemHeight(index));
2621
+ }
2622
+ const bottomOf = (index) => index + 1 < count ? tops[index + 1] : totalHeight;
2623
+ const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
2624
+ let scrollTop = clamp(Math.floor(options.scrollTop) || 0, 0, maxScrollTop);
2625
+ const focused = options.focusedIndex;
2626
+ if (focused !== void 0 && Number.isInteger(focused) && focused >= 0 && focused < count) {
2627
+ const top = tops[focused];
2628
+ const bottom = bottomOf(focused);
2629
+ if (top < scrollTop) scrollTop = top;
2630
+ else if (bottom > scrollTop + viewportHeight) scrollTop = Math.min(top, bottom - viewportHeight);
2631
+ scrollTop = clamp(scrollTop, 0, maxScrollTop);
2632
+ }
2633
+ const viewportBottom = scrollTop + viewportHeight;
2634
+ let start = 0;
2635
+ while (start < count && bottomOf(start) <= scrollTop) start++;
2636
+ let end = start;
2637
+ while (end < count && tops[end] < viewportBottom) end++;
2638
+ return {
2639
+ start,
2640
+ end,
2641
+ scrollTop,
2642
+ maxScrollTop,
2643
+ totalHeight,
2644
+ offset: start < end ? tops[start] - scrollTop : 0,
2645
+ hiddenAbove: scrollTop,
2646
+ hiddenBelow: Math.max(0, totalHeight - viewportBottom)
2647
+ };
2648
+ };
2649
+ //#endregion
2650
+ //#region src/hooks/use-virtual-scroll.ts
2651
+ /**
2652
+ A React hook that owns the scroll position of a windowed list and returns which
2653
+ items to render for it. The position is clamped to the scrollable range and,
2654
+ while `focusedIndex` is set, moved as little as necessary to keep that item
2655
+ fully visible. Render the items in `[start, end)` inside an `overflowY="hidden"`
2656
+ box of `viewportHeight` rows, shifted up by `offset` rows.
2657
+
2658
+ `<VirtualList>` wraps this hook; use it directly to draw your own chrome such
2659
+ as overflow markers or a scrollbar around the window.
2660
+
2661
+ @example
2662
+ ```tsx
2663
+ import { Box, Text, useVirtualScroll } from "@alchemy.run/sigil";
2664
+
2665
+ const Example = ({ lines, cursor }: { lines: string[]; cursor: number }) => {
2666
+ const { start, end, offset, hiddenAbove, hiddenBelow } = useVirtualScroll({
2667
+ count: lines.length,
2668
+ itemHeight: () => 1,
2669
+ viewportHeight: 10,
2670
+ focusedIndex: cursor,
2671
+ });
2672
+
2673
+ return (
2674
+ <Box flexDirection="column">
2675
+ <Text dimColor>{hiddenAbove > 0 ? `↑ ${hiddenAbove} more` : ""}</Text>
2676
+ <Box flexDirection="column" height={10} overflowY="hidden">
2677
+ <Box flexDirection="column" flexShrink={0} marginTop={offset}>
2678
+ {lines.slice(start, end).map((line, index) => (
2679
+ <Text key={start + index} inverse={start + index === cursor}>
2680
+ {line}
2681
+ </Text>
2682
+ ))}
2683
+ </Box>
2684
+ </Box>
2685
+ <Text dimColor>{hiddenBelow > 0 ? `↓ ${hiddenBelow} more` : ""}</Text>
2686
+ </Box>
2687
+ );
2688
+ };
2689
+ ```
2690
+ */
2691
+ const useVirtualScroll = (options) => {
2692
+ const [position, setPosition] = (0, import_react.useState)(0);
2693
+ const window = virtualScrollWindow({
2694
+ ...options,
2695
+ scrollTop: position
2696
+ });
2697
+ (0, import_react.useEffect)(() => {
2698
+ if (window.scrollTop !== position) setPosition(window.scrollTop);
2699
+ }, [window.scrollTop, position]);
2700
+ const scrollTo = (0, import_react.useCallback)((top) => {
2701
+ setPosition(top);
2702
+ }, []);
2703
+ const scrollBy = (0, import_react.useCallback)((delta) => {
2704
+ setPosition((current) => current + delta);
2705
+ }, []);
2706
+ return {
2707
+ ...window,
2708
+ scrollTo,
2709
+ scrollBy
2710
+ };
2711
+ };
2712
+ //#endregion
2713
+ //#region src/components/VirtualList.tsx
2714
+ /** @jsxImportSource react */
2715
+ const rootOf = (node) => {
2716
+ let current = node;
2717
+ while (current?.parentNode) current = current.parentNode;
2718
+ return current?.nodeName === "ink-root" ? current : void 0;
2719
+ };
2720
+ /**
2721
+ A vertically windowed list: only the items intersecting the viewport are
2722
+ rendered, inside a clipped box that scrolls by whole rows. Items may have
2723
+ different heights, and the item at the top edge may be partially visible.
2724
+
2725
+ Until the first layout pass has measured the viewport, every item is rendered
2726
+ inside the clipped box so the first frame already looks right.
2727
+ */
2728
+ function VirtualList({ items, itemHeight, renderItem, getKey, focusedIndex, height }) {
2729
+ const ref = (0, import_react.useRef)(null);
2730
+ const [measured, setMeasured] = (0, import_react.useState)();
2731
+ const measure = () => {
2732
+ const next = ref.current?.yogaNode?.getComputedHeight();
2733
+ if (next !== void 0) setMeasured((previous) => previous === next ? previous : next);
2734
+ };
2735
+ (0, import_react.useLayoutEffect)(() => {
2736
+ if (height === void 0) measure();
2737
+ });
2738
+ (0, import_react.useEffect)(() => {
2739
+ if (height !== void 0) return;
2740
+ const root = rootOf(ref.current);
2741
+ return root ? addLayoutListener(root, measure) : void 0;
2742
+ });
2743
+ const viewportHeight = height ?? measured;
2744
+ const windowed = viewportHeight !== void 0;
2745
+ const window = useVirtualScroll({
2746
+ count: items.length,
2747
+ itemHeight: (index) => itemHeight(items[index], index),
2748
+ viewportHeight: viewportHeight ?? 0,
2749
+ focusedIndex: windowed ? focusedIndex : void 0
2750
+ });
2751
+ const start = windowed ? window.start : 0;
2752
+ const end = windowed ? window.end : items.length;
2753
+ const offset = windowed ? window.offset : 0;
2754
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
2755
+ ref,
2756
+ flexDirection: "column",
2757
+ overflowY: "hidden",
2758
+ ...height === void 0 ? {
2759
+ height: window.totalHeight,
2760
+ minHeight: 0,
2761
+ flexShrink: 1
2762
+ } : {
2763
+ height,
2764
+ flexShrink: 0
2765
+ },
2766
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
2767
+ flexDirection: "column",
2768
+ flexShrink: 0,
2769
+ marginTop: offset,
2770
+ children: items.slice(start, end).map((item, sliceIndex) => {
2771
+ const index = start + sliceIndex;
2772
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
2773
+ flexDirection: "column",
2774
+ flexShrink: 0,
2775
+ children: renderItem(item, index)
2776
+ }, getKey ? getKey(item, index) : index);
2777
+ })
2778
+ })
2779
+ });
2780
+ }
2781
+ //#endregion
2782
+ //#region src/hooks/use-capabilities.ts
2783
+ /**
2784
+ Returns everything knowable about the terminal: size, identity, platform,
2785
+ color depth, theme, and feature support.
2786
+
2787
+ A thin wrapper over the framework-free capabilities store (`getCapabilities`):
2788
+ environment-derived facts are available immediately; facts only the terminal
2789
+ itself can answer fill in after a lazy one-time query, and re-mounting
2790
+ consumers refreshes the dynamic facts (theme colors, pixel geometry).
2791
+ Re-renders on terminal resize and whenever query answers arrive.
2792
+ */
2793
+ const useCapabilities = () => {
2794
+ const { stdout } = useStdout();
2795
+ const { stdin } = useStdinContext();
2796
+ const store = getCapabilities(stdin, stdout);
2797
+ const capabilities = (0, import_react.useSyncExternalStore)(store.subscribe, () => store.current);
2798
+ (0, import_react.useEffect)(() => {
2799
+ store.query();
2800
+ }, [store]);
2801
+ return capabilities;
2802
+ };
2803
+ /**
2804
+ Calls `onChange` whenever the terminal changes: resizes (including in-band
2805
+ pixel geometry), color scheme switches, window focus, and query answers
2806
+ arriving. The React wrapper over `capabilities.subscribe()` for side effects —
2807
+ for rendering, use `useCapabilities` instead.
2808
+
2809
+ The callback always sees the latest render's closure and changing it does not
2810
+ resubscribe. Both the new and previous snapshot are passed, so handlers can
2811
+ react to the specific change:
2812
+
2813
+ ```tsx
2814
+ useCapabilitiesChange((next, previous) => {
2815
+ if (next.theme.appearance !== previous.theme.appearance) {
2816
+ // re-theme
2817
+ }
2818
+ });
2819
+ ```
2820
+ */
2821
+ const useCapabilitiesChange = (onChange) => {
2822
+ const { stdout } = useStdout();
2823
+ const { stdin } = useStdinContext();
2824
+ const store = getCapabilities(stdin, stdout);
2825
+ const handleChange = (0, import_react.useEffectEvent)(onChange);
2826
+ (0, import_react.useEffect)(() => {
2827
+ let previous = store.current;
2828
+ const unsubscribe = store.subscribe((next) => {
2829
+ const before = previous;
2830
+ previous = next;
2831
+ handleChange(next, before);
2832
+ });
2833
+ store.query();
2834
+ return unsubscribe;
2835
+ }, [store]);
2836
+ };
2837
+ //#endregion
2838
+ //#region src/hooks/use-paste.ts
2839
+ /**
2840
+ A React hook that calls `handler` whenever the user pastes text in the terminal. Bracketed paste mode (`\x1b[?2004h`) is automatically enabled while the hook is active, so pasted text arrives as a single string rather than being misinterpreted as individual key presses.
2841
+
2842
+ `usePaste` and `useInput` can be used together in the same component. They operate on separate event channels, so paste content is never forwarded to `useInput` handlers when `usePaste` is active.
2843
+
2844
+ ```
2845
+ import {useInput, usePaste} from 'ink';
2846
+
2847
+ const MyInput = () => {
2848
+ useInput((input, key) => {
2849
+ // Only receives typed characters and key events, not pasted text.
2850
+ if (key.return) {
2851
+ // Submit
2852
+ }
2853
+ });
2854
+
2855
+ usePaste((text) => {
2856
+ // Receives the full pasted string, including newlines.
2857
+ console.log('Pasted:', text);
2858
+ });
2859
+
2860
+ return …
2861
+ };
2862
+ ```
2863
+ */
2864
+ const usePaste = (handler, options = {}) => {
2865
+ const { setRawMode, setBracketedPasteMode, internal_eventEmitter } = useStdinContext();
2866
+ (0, import_react.useEffect)(() => {
2867
+ if (options.isActive === false) return;
2868
+ setRawMode(true);
2869
+ setBracketedPasteMode(true);
2870
+ return () => {
2871
+ setRawMode(false);
2872
+ setBracketedPasteMode(false);
2873
+ };
2874
+ }, [
2875
+ options.isActive,
2876
+ setRawMode,
2877
+ setBracketedPasteMode
2878
+ ]);
2879
+ const handlePaste = (0, import_react.useEffectEvent)((text) => {
2880
+ reconciler.discreteUpdates(() => {
2881
+ handler(text);
2882
+ });
2883
+ });
2884
+ (0, import_react.useEffect)(() => {
2885
+ if (options.isActive === false) return;
2886
+ internal_eventEmitter.on("paste", handlePaste);
2887
+ return () => {
2888
+ internal_eventEmitter.removeListener("paste", handlePaste);
2889
+ };
2890
+ }, [options.isActive, internal_eventEmitter]);
2891
+ };
2892
+ //#endregion
2893
+ //#region src/hooks/use-app.ts
2894
+ /**
2895
+ A React hook that returns app lifecycle methods like `exit()` and `waitUntilRenderFlush()`.
2896
+ */
2897
+ const useApp = () => (0, import_react.useContext)(AppContext);
2898
+ //#endregion
2899
+ //#region src/hooks/use-stderr.ts
2900
+ /**
2901
+ A React hook that returns the stderr stream.
2902
+ */
2903
+ const useStderr = () => (0, import_react.useContext)(StderrContext);
2904
+ //#endregion
2905
+ //#region src/hooks/use-focus-manager.ts
2906
+ /**
2907
+ A React hook that returns methods to enable or disable focus management for all components or manually switch focus to the next or previous components.
2908
+ */
2909
+ const useFocusManager = () => {
2910
+ const focusContext = (0, import_react.useContext)(FocusContext);
2911
+ return {
2912
+ enableFocus: focusContext.enableFocus,
2913
+ disableFocus: focusContext.disableFocus,
2914
+ focusNext: focusContext.focusNext,
2915
+ focusPrevious: focusContext.focusPrevious,
2916
+ focus: focusContext.focus,
2917
+ activeId: focusContext.activeId
2918
+ };
2919
+ };
2920
+ //#endregion
2921
+ //#region src/hooks/use-is-screen-reader-enabled.ts
2922
+ /**
2923
+ A React hook that returns whether a screen reader is enabled.
2924
+ This is useful when you want to render different output for screen readers.
2925
+ */
2926
+ const useIsScreenReaderEnabled = () => {
2927
+ const { isScreenReaderEnabled } = (0, import_react.useContext)(accessibilityContext);
2928
+ return isScreenReaderEnabled;
2929
+ };
2930
+ //#endregion
2931
+ //#region src/hooks/use-cursor.ts
2932
+ /**
2933
+ A React hook that returns methods to control the terminal cursor position.
2934
+
2935
+ Setting a cursor position makes the cursor visible at the specified coordinates (relative to the Ink output origin). This is useful for IME (Input Method Editor) support, where the composing character is displayed at the cursor location.
2936
+
2937
+ Pass `undefined` to hide the cursor.
2938
+ */
2939
+ const useCursor = () => {
2940
+ const context = (0, import_react.useContext)(CursorContext);
2941
+ const positionRef = (0, import_react.useRef)(void 0);
2942
+ const setCursorPosition = (0, import_react.useCallback)((position) => {
2943
+ positionRef.current = position;
2944
+ }, []);
2945
+ (0, import_react.useInsertionEffect)(() => {
2946
+ context.setCursorPosition(positionRef.current);
2947
+ return () => {
2948
+ context.setCursorPosition(void 0);
2949
+ };
2950
+ });
2951
+ return { setCursorPosition };
2952
+ };
2953
+ //#endregion
2954
+ //#region src/hooks/use-animation.ts
2955
+ const defaultAnimationInterval = 100;
2956
+ const maximumTimerInterval = 2147483647;
2957
+ const zeroAnimState = {
2958
+ frame: 0,
2959
+ time: 0,
2960
+ delta: 0
2961
+ };
2962
+ /**
2963
+ A React hook that drives animations. Returns a frame counter, elapsed time, frame delta, and a reset function. All animations share a single timer internally, so multiple animated components consolidate into one render cycle.
2964
+
2965
+ @example
2966
+ ```
2967
+ import {Text, useAnimation} from 'ink';
2968
+
2969
+ const Spinner = () => {
2970
+ const {frame} = useAnimation({interval: 80});
2971
+ const characters = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
2972
+
2973
+ return <Text>{characters[frame % characters.length]}</Text>;
2974
+ };
2975
+ ```
2976
+ */
2977
+ function useAnimation(options) {
2978
+ const { interval = defaultAnimationInterval, isActive = true } = options ?? {};
2979
+ const safeInterval = normalizeAnimationInterval(interval);
2980
+ const { subscribe, renderThrottleMs } = (0, import_react.useContext)(animationContext);
2981
+ const [resetKey, setResetKey] = (0, import_react.useState)(0);
2982
+ const [animState, setAnimState] = (0, import_react.useState)(zeroAnimState);
2983
+ const nextRenderTimeRef = (0, import_react.useRef)(0);
2984
+ const lastRenderTimeRef = (0, import_react.useRef)(0);
2985
+ const previousOptionsRef = (0, import_react.useRef)({
2986
+ isActive,
2987
+ safeInterval,
2988
+ resetKey
2989
+ });
2990
+ const previousOptions = previousOptionsRef.current;
2991
+ const shouldReset = isActive && (safeInterval !== previousOptions.safeInterval || !previousOptions.isActive || resetKey !== previousOptions.resetKey);
2992
+ const reset = (0, import_react.useCallback)(() => {
2993
+ setResetKey((k) => k + 1);
2994
+ }, []);
2995
+ (0, import_react.useLayoutEffect)(() => {
2996
+ if (!isActive) return;
2997
+ setAnimState(zeroAnimState);
2998
+ let startTime = 0;
2999
+ const { startTime: subscriberStartTime, unsubscribe } = subscribe((currentTime) => {
3000
+ if (renderThrottleMs > 0 && currentTime < nextRenderTimeRef.current) return;
3001
+ const elapsed = currentTime - startTime;
3002
+ const nextDelta = currentTime - lastRenderTimeRef.current;
3003
+ lastRenderTimeRef.current = currentTime;
3004
+ nextRenderTimeRef.current = currentTime + renderThrottleMs;
3005
+ setAnimState({
3006
+ frame: Math.floor(elapsed / safeInterval),
3007
+ time: elapsed,
3008
+ delta: nextDelta
3009
+ });
3010
+ }, safeInterval);
3011
+ startTime = subscriberStartTime;
3012
+ lastRenderTimeRef.current = subscriberStartTime;
3013
+ nextRenderTimeRef.current = startTime + renderThrottleMs;
3014
+ return unsubscribe;
3015
+ }, [
3016
+ safeInterval,
3017
+ isActive,
3018
+ subscribe,
3019
+ renderThrottleMs,
3020
+ resetKey
3021
+ ]);
3022
+ (0, import_react.useLayoutEffect)(() => {
3023
+ previousOptionsRef.current = {
3024
+ isActive,
3025
+ safeInterval,
3026
+ resetKey
3027
+ };
3028
+ }, [
3029
+ isActive,
3030
+ safeInterval,
3031
+ resetKey
3032
+ ]);
3033
+ if (shouldReset) return {
3034
+ ...zeroAnimState,
3035
+ reset
3036
+ };
3037
+ return {
3038
+ ...animState,
3039
+ reset
3040
+ };
3041
+ }
3042
+ function normalizeAnimationInterval(interval) {
3043
+ if (!Number.isFinite(interval)) return defaultAnimationInterval;
3044
+ return Math.min(maximumTimerInterval, Math.max(1, interval));
3045
+ }
3046
+ //#endregion
3047
+ //#region src/hooks/use-terminal-osc.ts
3048
+ const useProgress = ({ state, value }) => {
3049
+ const terminal = (0, import_react.useContext)(TerminalOscContext);
3050
+ const owner = (0, import_react.useRef)(void 0);
3051
+ owner.current ??= Symbol("terminal-progress");
3052
+ (0, import_react.useEffect)(() => {
3053
+ terminal.publishProgress(owner.current, state, value);
3054
+ }, [
3055
+ state,
3056
+ terminal,
3057
+ value
3058
+ ]);
3059
+ (0, import_react.useEffect)(() => {
3060
+ return () => {
3061
+ terminal.publishProgress(owner.current, "inactive");
3062
+ };
3063
+ }, [terminal]);
3064
+ };
3065
+ const useClipboard = () => {
3066
+ const terminal = (0, import_react.useContext)(TerminalOscContext);
3067
+ return (0, import_react.useCallback)((text, selection) => terminal.copyToClipboard(text, selection), [terminal]);
3068
+ };
3069
+ const useTitle = (title) => {
3070
+ const terminal = (0, import_react.useContext)(TerminalOscContext);
3071
+ const owner = (0, import_react.useRef)(void 0);
3072
+ owner.current ??= Symbol("terminal-title");
3073
+ (0, import_react.useEffect)(() => {
3074
+ terminal.publishTitle(owner.current, title);
3075
+ }, [terminal, title]);
3076
+ (0, import_react.useEffect)(() => () => terminal.publishTitle(owner.current, void 0), [terminal]);
3077
+ };
3078
+ const useWorkingDirectory = (directory) => {
3079
+ const terminal = (0, import_react.useContext)(TerminalOscContext);
3080
+ (0, import_react.useEffect)(() => {
3081
+ terminal.setWorkingDirectory(directory);
3082
+ }, [directory, terminal]);
3083
+ };
3084
+ const useNotification = () => {
3085
+ const terminal = (0, import_react.useContext)(TerminalOscContext);
3086
+ return (0, import_react.useCallback)((title) => terminal.notify(title), [terminal]);
3087
+ };
3088
+ const usePointerShape = (shape) => {
3089
+ const terminal = (0, import_react.useContext)(TerminalOscContext);
3090
+ (0, import_react.useEffect)(() => {
3091
+ terminal.setPointerShape(shape);
3092
+ }, [shape, terminal]);
3093
+ };
3094
+ //#endregion
3095
+ //#region src/hooks/use-window-size.ts
3096
+ /**
3097
+ A React hook that returns the current terminal window dimensions and re-renders the component whenever the terminal is resized.
3098
+
3099
+ Reads the capabilities store, so on terminals that send in-band size reports
3100
+ (mode 2048) the dimensions are the emulator's own, arriving after it has
3101
+ rewrapped its screen; elsewhere they are the stream's `columns`/`rows`.
3102
+ Subscribing to dimensions does not initiate capability queries. In-band reports
3103
+ are used when another consumer has explicitly enabled capability discovery.
3104
+ */
3105
+ const useWindowSize = () => {
3106
+ const { stdin } = useStdinContext();
3107
+ const { stdout } = useStdout();
3108
+ const store = getCapabilities(stdin, stdout);
3109
+ const { size } = (0, import_react.useSyncExternalStore)(store.subscribe, () => store.current);
3110
+ return (0, import_react.useMemo)(() => ({
3111
+ columns: size.columns,
3112
+ rows: size.rows
3113
+ }), [size.columns, size.rows]);
3114
+ };
3115
+ //#endregion
3116
+ //#region src/hooks/use-box-metrics.ts
3117
+ const emptyMetrics = {
3118
+ width: 0,
3119
+ height: 0,
3120
+ left: 0,
3121
+ top: 0
3122
+ };
3123
+ const findRootNode = (node) => {
3124
+ if (!node) return;
3125
+ if (!node.parentNode) return node.nodeName === "ink-root" ? node : void 0;
3126
+ return findRootNode(node.parentNode);
3127
+ };
3128
+ /**
3129
+ A React hook that returns the current layout metrics for a tracked box element.
3130
+ It updates when layout changes (for example terminal resize, sibling/content changes, or position changes).
3131
+
3132
+ The hook returns `{width: 0, height: 0, left: 0, top: 0}` until the first layout pass completes. It also returns zeros when the tracked ref is detached.
3133
+
3134
+ Use `hasMeasured` to detect when the currently tracked element has been measured.
3135
+
3136
+ @example
3137
+ ```tsx
3138
+ import {useRef} from 'react';
3139
+ import {Box, Text, useBoxMetrics} from 'ink';
3140
+
3141
+ const Example = () => {
3142
+ const ref = useRef(null);
3143
+ const {width, height, left, top, hasMeasured} = useBoxMetrics(ref);
3144
+ return (
3145
+ <Box ref={ref}>
3146
+ <Text>
3147
+ {hasMeasured ? `${width}x${height} at ${left},${top}` : 'Measuring...'}
3148
+ </Text>
3149
+ </Box>
3150
+ );
3151
+ };
3152
+ ```
3153
+ */
3154
+ const useBoxMetrics = (ref) => {
3155
+ const [metrics, setMetrics] = (0, import_react.useState)(emptyMetrics);
3156
+ const [hasMeasured, setHasMeasured] = (0, import_react.useState)(false);
3157
+ const updateMetrics = (0, import_react.useCallback)(() => {
3158
+ const layout = ref.current?.yogaNode?.getComputedLayout() ?? emptyMetrics;
3159
+ setMetrics((previousMetrics) => {
3160
+ return previousMetrics.width !== layout.width || previousMetrics.height !== layout.height || previousMetrics.left !== layout.left || previousMetrics.top !== layout.top ? layout : previousMetrics;
3161
+ });
3162
+ setHasMeasured(Boolean(ref.current));
3163
+ }, [ref]);
3164
+ (0, import_react.useEffect)(updateMetrics);
3165
+ (0, import_react.useEffect)(() => {
3166
+ const rootNode = findRootNode(ref.current);
3167
+ if (!rootNode) return;
3168
+ return addLayoutListener(rootNode, updateMetrics);
3169
+ });
3170
+ return (0, import_react.useMemo)(() => ({
3171
+ ...metrics,
3172
+ hasMeasured
3173
+ }), [metrics, hasMeasured]);
3174
+ };
3175
+ //#endregion
3176
+ //#region src/measure-element.ts
3177
+ /**
3178
+ Measure the layout metrics of a particular `<Box>` element.
3179
+ Returns an object with `x`, `y`, `width`, and `height` properties.
3180
+
3181
+ `x` and `y` are the element's position within the live layout region, computed by walking up the layout tree and accumulating each ancestor's offset. These are layout-tree coordinates, not terminal viewport coordinates. To compare them with mouse events, convert the event coordinates using the live region's viewport position. This is necessary even in alternate-screen mode when output, such as `<Static>` content, appears above the live region.
3182
+
3183
+ Note: `measureElement()` returns `{x: 0, y: 0, width: 0, height: 0}` when called during render (before layout is calculated). Call it from post-render code, such as `useEffect`, `useLayoutEffect`, input handlers, or timer callbacks. When content changes, pass the relevant dependency to your effect so it re-measures after each update.
3184
+ */
3185
+ const measureElement = (node) => {
3186
+ const { yogaNode } = node;
3187
+ if (!yogaNode) return {
3188
+ x: 0,
3189
+ y: 0,
3190
+ width: 0,
3191
+ height: 0
3192
+ };
3193
+ let x = yogaNode.getComputedLeft();
3194
+ let y = yogaNode.getComputedTop();
3195
+ let current = node.parentNode;
3196
+ while (current) {
3197
+ if (current.yogaNode) {
3198
+ x += current.yogaNode.getComputedLeft();
3199
+ y += current.yogaNode.getComputedTop();
3200
+ }
3201
+ current = current.parentNode;
3202
+ }
3203
+ return {
3204
+ x,
3205
+ y,
3206
+ width: yogaNode.getComputedWidth(),
3207
+ height: yogaNode.getComputedHeight()
3208
+ };
3209
+ };
3210
+ //#endregion
3211
+ export { AnsiText, Box, Hyperlink, Newline, Spacer, Static, Text, Transform, VirtualList, applyTerminalQuery, capabilities, createSupportsColor, detectCapabilities, detectColorLevel, detectHyperlinkSupport, detectTerminal, detectUnicodeSupport, getCapabilities, getTerminalQuery, kittyFlags, kittyModifiers, measureElement, queryTerminal, refreshTerminalQuery, render, renderToString, useAnimation, useApp, useBoxMetrics, useCapabilities, useCapabilitiesChange, useClipboard, useCursor, useFocus, useFocusManager, useInput, useIsScreenReaderEnabled, useNotification, usePaste, usePointerShape, useProgress, useStderr, useStdin, useStdout, useTitle, useVirtualScroll, useWindowSize, useWorkingDirectory };