@alchemy.run/sigil 0.0.0-alpha.3 → 0.0.0-alpha.5

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 (207) hide show
  1. package/README.md +23 -13
  2. package/THIRD_PARTY_NOTICES.md +70 -23
  3. package/dist/Text-D5HUf3Fj.d.ts +452 -0
  4. package/dist/ansi.d.ts +137 -143
  5. package/dist/ansi.js +87 -2
  6. package/dist/capabilities.d.ts +5 -0
  7. package/dist/capabilities.js +3 -0
  8. package/dist/cell-_ZVhbfl0.js +44 -0
  9. package/dist/color-CkbalRqK.js +2 -0
  10. package/dist/color-policy-BMzMwV7Q.d.ts +22 -0
  11. package/dist/color-policy-SVj1pYTA.js +560 -0
  12. package/dist/color-profile-DHhQHY55.js +36 -0
  13. package/dist/color-profile-u0Nhe9Nv.d.ts +97 -0
  14. package/dist/color.d.ts +21 -0
  15. package/dist/color.js +3 -0
  16. package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
  17. package/dist/detect-Bh4yGP6w.d.ts +186 -0
  18. package/dist/detect-BuTXtY6e.js +373 -0
  19. package/dist/{devtools-BhYGjb7h.js → devtools-DbthxoD1.js} +22 -23
  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-Bmc2tRPk.d.ts +21 -0
  24. package/dist/index.d.ts +235 -754
  25. package/dist/index.js +1362 -3001
  26. package/dist/osc-CCH7xDoS.js +71 -0
  27. package/dist/osc-Cn0fw77g.d.ts +23 -0
  28. package/dist/paint-C19minOS.d.ts +81 -0
  29. package/dist/query-vaIeGOkH.d.ts +152 -0
  30. package/dist/router.d.ts +392 -0
  31. package/dist/router.js +709 -0
  32. package/dist/sample-Cqw1bjUL.js +445 -0
  33. package/dist/screen-BOh__-65.js +324 -0
  34. package/dist/screen-BReKIheE.d.ts +40 -0
  35. package/dist/screen.d.ts +5 -0
  36. package/dist/screen.js +5 -0
  37. package/dist/semantic-text-style-DIMzC7xt.js +91 -0
  38. package/dist/serialize-BTkAZgw1.js +79 -0
  39. package/dist/session-BJmzX2NJ.js +664 -0
  40. package/dist/sgr-BhwaWAJB.js +246 -0
  41. package/dist/store-CgrG9K4y.d.ts +72 -0
  42. package/dist/string-width-CijQwpIk.js +69 -0
  43. package/dist/strip-BvU4toXG.js +6 -0
  44. package/dist/terminal.d.ts +118 -0
  45. package/dist/terminal.js +2 -0
  46. package/dist/tokenize-AjqbvtiT.js +1242 -0
  47. package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
  48. package/dist/truncate-D31fhU6i.js +562 -0
  49. package/dist/use-focus-C2sciZOo.js +1335 -0
  50. package/package.json +43 -17
  51. package/src/ansi/chalk.ts +12 -53
  52. package/src/ansi/cursor.ts +2 -4
  53. package/src/ansi/east-asian-width.ts +44 -0
  54. package/src/ansi/escapes.ts +18 -26
  55. package/src/ansi/graphemes.ts +8 -0
  56. package/src/ansi/hyperlink.ts +44 -0
  57. package/src/ansi/index.ts +14 -14
  58. package/src/ansi/osc.ts +77 -0
  59. package/src/ansi/sgr.ts +2 -5
  60. package/src/ansi/slice.ts +1 -1
  61. package/src/ansi/string-width.ts +91 -206
  62. package/src/ansi/strip.ts +13 -33
  63. package/src/ansi/tokenize.ts +96 -169
  64. package/src/ansi/truncate.ts +5 -3
  65. package/src/ansi/wrap.ts +13 -14
  66. package/src/ansi-tokenizer.ts +1 -1
  67. package/src/capabilities/color-policy.ts +34 -0
  68. package/src/capabilities/detect.ts +594 -0
  69. package/src/capabilities/index.ts +37 -0
  70. package/src/capabilities/query.ts +657 -0
  71. package/src/capabilities/store.ts +379 -0
  72. package/src/color/index.ts +3 -0
  73. package/src/color/paint.ts +169 -0
  74. package/src/color/palette.ts +48 -0
  75. package/src/color/sample.ts +323 -0
  76. package/src/color.ts +1 -0
  77. package/src/components/AnsiText.tsx +42 -0
  78. package/src/components/App.tsx +109 -22
  79. package/src/components/BackgroundContext.ts +2 -3
  80. package/src/components/Box.tsx +43 -60
  81. package/src/components/CursorContext.ts +1 -1
  82. package/src/components/ErrorBoundary.tsx +1 -1
  83. package/src/components/ErrorOverview.tsx +8 -7
  84. package/src/components/Hyperlink.tsx +56 -0
  85. package/src/components/Spacer.tsx +1 -1
  86. package/src/components/Static.tsx +1 -1
  87. package/src/components/StderrContext.ts +0 -2
  88. package/src/components/StdinContext.ts +0 -1
  89. package/src/components/StdoutContext.ts +1 -3
  90. package/src/components/TerminalOscContext.ts +25 -0
  91. package/src/components/Text.tsx +23 -46
  92. package/src/components/Transform.tsx +2 -2
  93. package/src/cursor-position.ts +1 -1
  94. package/src/devtools.ts +84 -24
  95. package/src/dom.ts +11 -8
  96. package/src/env.ts +12 -0
  97. package/src/get-max-width.ts +1 -1
  98. package/src/global.d.ts +6 -4
  99. package/src/{boxes.ts → glyphs.ts} +17 -18
  100. package/src/hooks/use-animation.ts +1 -1
  101. package/src/hooks/use-app.ts +1 -1
  102. package/src/hooks/use-box-metrics.ts +1 -1
  103. package/src/hooks/use-capabilities.ts +73 -0
  104. package/src/hooks/use-cursor.ts +3 -3
  105. package/src/hooks/use-focus-manager.ts +1 -1
  106. package/src/hooks/use-focus.ts +5 -6
  107. package/src/hooks/use-input.ts +4 -4
  108. package/src/hooks/use-is-screen-reader-enabled.ts +1 -1
  109. package/src/hooks/use-paste.ts +2 -2
  110. package/src/hooks/use-stderr.ts +1 -1
  111. package/src/hooks/use-stdin.ts +1 -1
  112. package/src/hooks/use-stdout.ts +1 -1
  113. package/src/hooks/use-terminal-osc.ts +59 -0
  114. package/src/hooks/use-window-size.ts +2 -2
  115. package/src/index.ts +88 -44
  116. package/src/ink.tsx +620 -745
  117. package/src/input-parser.ts +1 -1
  118. package/src/instances.ts +1 -1
  119. package/src/kitty-keyboard.ts +128 -0
  120. package/src/measure-element.ts +1 -1
  121. package/src/measure-text.ts +2 -2
  122. package/src/{render-node-to-output.ts → paint-tree.ts} +78 -53
  123. package/src/parse-keypress.ts +4 -5
  124. package/src/parse-stack-line.ts +0 -1
  125. package/src/patch-console.ts +44 -0
  126. package/src/reconciler.ts +36 -62
  127. package/src/render-background.ts +37 -16
  128. package/src/render-border.ts +97 -64
  129. package/src/render-frame.ts +83 -0
  130. package/src/render-to-string.ts +24 -9
  131. package/src/render.ts +24 -16
  132. package/src/router/components.tsx +343 -0
  133. package/src/router/context.ts +41 -0
  134. package/src/router/history.ts +194 -0
  135. package/src/router/hooks.tsx +391 -0
  136. package/src/router/index.ts +34 -0
  137. package/src/router/matcher.ts +571 -0
  138. package/src/sanitize-ansi.ts +1 -1
  139. package/src/screen/ansi.ts +184 -0
  140. package/src/screen/canvas.ts +137 -0
  141. package/src/screen/cell.ts +138 -0
  142. package/src/screen/color-profile.ts +47 -0
  143. package/src/screen/geometry.ts +9 -0
  144. package/src/screen/index.ts +6 -0
  145. package/src/screen/screen.ts +272 -0
  146. package/src/screen/serialize.ts +129 -0
  147. package/src/screen.ts +1 -0
  148. package/src/semantic-text-style.ts +118 -0
  149. package/src/signal-exit.ts +4 -5
  150. package/src/squash-text-nodes.ts +4 -7
  151. package/src/stream.ts +2 -4
  152. package/src/structured-text.ts +321 -0
  153. package/src/styles.ts +16 -17
  154. package/src/terminal/index.ts +2 -0
  155. package/src/terminal/inline-presenter.ts +120 -0
  156. package/src/terminal/input.ts +86 -0
  157. package/src/terminal/render-scheduler.ts +37 -0
  158. package/src/terminal/screen-presenter.ts +188 -0
  159. package/src/terminal/session.ts +407 -0
  160. package/src/terminal-size.ts +9 -8
  161. package/src/terminal.ts +1 -0
  162. package/src/testing/browser.ts +588 -0
  163. package/src/testing/emulators.ts +205 -0
  164. package/src/testing/explorer-app/index.html +12 -0
  165. package/src/testing/explorer-app/main.ts +381 -0
  166. package/src/testing/explorer-app/style.css +194 -0
  167. package/src/testing/explorer-app/tsconfig.json +15 -0
  168. package/src/testing/explorer-app/vite-env.d.ts +1 -0
  169. package/src/testing/index.ts +26 -0
  170. package/src/testing/keys.ts +56 -0
  171. package/src/testing/live.ts +85 -0
  172. package/src/testing/matchers.ts +70 -0
  173. package/src/testing/public.ts +94 -0
  174. package/src/testing/terminal.ts +349 -0
  175. package/src/testing/vitest.ts +157 -0
  176. package/src/transform-adapter.ts +14 -0
  177. package/src/types.ts +1 -6
  178. package/src/utils.ts +2 -2
  179. package/src/wrap-text.ts +4 -4
  180. package/src/yoga/config.ts +2 -2
  181. package/src/yoga/core/absoluteLayout.ts +15 -15
  182. package/src/yoga/core/baseline.ts +3 -3
  183. package/src/yoga/core/cache.ts +4 -4
  184. package/src/yoga/core/calculateLayout.ts +33 -27
  185. package/src/yoga/core/config.ts +1 -1
  186. package/src/yoga/core/flexLine.ts +3 -3
  187. package/src/yoga/core/helpers.ts +3 -3
  188. package/src/yoga/core/layoutResults.ts +4 -4
  189. package/src/yoga/core/node.ts +16 -16
  190. package/src/yoga/core/pixelGrid.ts +4 -4
  191. package/src/yoga/core/style.ts +11 -11
  192. package/src/yoga/core/types.ts +2 -2
  193. package/src/yoga/index.ts +6 -6
  194. package/src/yoga/node.ts +8 -8
  195. package/dist/sgr-CMfEpjSk.d.ts +0 -91
  196. package/dist/truncate-CBiyyZzw.js +0 -2156
  197. package/src/ansi/supports-color.ts +0 -213
  198. package/src/ansi/widest-line.ts +0 -12
  199. package/src/auto-bind.ts +0 -41
  200. package/src/colorize.ts +0 -60
  201. package/src/devtools-window-polyfill.ts +0 -73
  202. package/src/indent-string.ts +0 -16
  203. package/src/is-in-ci.ts +0 -7
  204. package/src/log-update.ts +0 -370
  205. package/src/output.ts +0 -308
  206. package/src/renderer.ts +0 -73
  207. package/src/write-synchronized.ts +0 -9
package/dist/index.js CHANGED
@@ -1,198 +1,33 @@
1
- import { At as esu, Ft as pasteStart, Pt as pasteEnd, b as wrapAnsi, ct as CSI, d as styledCharsFromTokens, f as styledCharsToString, g as chalk, h as widestLine, mt as bsu, n as sliceAnsi, p as tokenize, pt as ansiEscapes, t as cliTruncate, v as cliCursor, x as stringWidth, y as signalExit } from "./truncate-CBiyyZzw.js";
1
+ import { C as bsu, S as ansiEscapes, V as esu, W as link, _ as CSI } from "./sgr-BhwaWAJB.js";
2
+ import { h as tokenizeAnsi, p as graphemes } from "./tokenize-AjqbvtiT.js";
3
+ import { t as stringWidth } from "./string-width-CijQwpIk.js";
4
+ import { n as sliceAnsi, r as wrapAnsi } from "./truncate-D31fhU6i.js";
5
+ import { _ as squashTextNodes, a as 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 } from "./use-focus-C2sciZOo.js";
6
+ import { a as isTty, i as isSigilDev, o as isWindows, r as isScreenReader, t as isInCi } from "./env-YVw64yZS.js";
7
+ import { a as detectTerminal, c as signalExit, i as detectHyperlinkSupport, n as detectCapabilities, o as detectUnicodeSupport, r as detectColorLevel, s as terminalSize, t as createSupportsColor } from "./detect-BuTXtY6e.js";
8
+ import { u as cliCursor } from "./osc-CCH7xDoS.js";
9
+ import { a as registerTerminalIntegration, c as ensureTerminalQuery, d as queryTerminal, f as refreshTerminalQuery, i as getCapabilities, l as getTerminalQuery, o as getRawModeStream, r as capabilities, s as applyTerminalQuery, u as getTerminalQueryPromise } from "./color-policy-SVj1pYTA.js";
10
+ import { a as buildCursorSuffix, c as hideCursorEscape, i as buildCursorOnlySequence, o as buildReturnToBottomPrefix, s as cursorPositionChanged, t as TerminalSession } from "./session-BJmzX2NJ.js";
2
11
  import { t as Yoga } from "./yoga-5jKhYCJC.js";
3
- import process, { cwd } from "node:process";
12
+ import { r as createCell, t as cellAttributes } from "./cell-_ZVhbfl0.js";
13
+ import { i as semanticTextStyleToCellStyle, n as mergeSemanticTextStyles, t as emptySemanticTextStyle } from "./semantic-text-style-DIMzC7xt.js";
14
+ import { a as samplePaint } from "./sample-Cqw1bjUL.js";
15
+ import { n as cellFromStyledChar, r as cellsFromAnsi, t as Screen } from "./screen-BOh__-65.js";
16
+ import { t as colorProfileFromLevel } from "./color-profile-DHhQHY55.js";
17
+ import { n as serializeScreen, t as serializeLine } from "./serialize-BTkAZgw1.js";
18
+ import { t as stripAnsi } from "./strip-BvU4toXG.js";
19
+ import "./color-CkbalRqK.js";
4
20
  import { PassThrough, Stream } from "node:stream";
5
- import { ConcurrentRoot, DefaultEventPriority, LegacyRoot, NoEventPriority } from "react-reconciler/constants.js";
6
- import { PureComponent, createContext, forwardRef, useCallback, useContext, useEffect, useEffectEvent, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState, version } from "react";
21
+ import { setImmediate as setImmediate$1 } from "node:timers/promises";
22
+ import { isNativeError } from "node:util/types";
23
+ import { PureComponent, createContext, useCallback, useContext, useEffect, useEffectEvent, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
24
+ import "react-reconciler";
25
+ import { ConcurrentRoot, LegacyRoot } from "react-reconciler/constants.js";
7
26
  import { EventEmitter } from "node:events";
8
- import * as fs$1 from "node:fs";
9
- import fs from "node:fs";
27
+ import { existsSync, readFileSync } from "node:fs";
28
+ import { cwd } from "node:process";
10
29
  import { jsx, jsxs } from "react/jsx-runtime";
11
- import tty from "node:tty";
12
30
  import { Console } from "node:console";
13
- import createReconciler from "react-reconciler";
14
- import * as Scheduler from "scheduler";
15
- //#region src/auto-bind.ts
16
- const getAllProperties = (object) => {
17
- const properties = [];
18
- let current = object;
19
- do {
20
- for (const key of Reflect.ownKeys(current)) properties.push([current, key]);
21
- current = Reflect.getPrototypeOf(current);
22
- } while (current && current !== Object.prototype);
23
- return properties;
24
- };
25
- const autoBind = (self) => {
26
- for (const [object, key] of getAllProperties(self.constructor.prototype)) {
27
- if (key === "constructor") continue;
28
- const descriptor = Reflect.getOwnPropertyDescriptor(object, key);
29
- if (descriptor && typeof descriptor.value === "function") {
30
- const value = self[key];
31
- if (typeof value === "function") self[key] = value.bind(self);
32
- }
33
- }
34
- return self;
35
- };
36
- //#endregion
37
- //#region src/components/AccessibilityContext.ts
38
- const accessibilityContext = createContext({ isScreenReaderEnabled: false });
39
- //#endregion
40
- //#region src/input-parser.ts
41
- const escape$1 = "\x1B";
42
- const isCsiParameterByte = (byte) => {
43
- return byte >= 48 && byte <= 63;
44
- };
45
- const isCsiIntermediateByte = (byte) => {
46
- return byte >= 32 && byte <= 47;
47
- };
48
- const isCsiFinalByte = (byte) => {
49
- return byte >= 64 && byte <= 126;
50
- };
51
- const parseCsiSequence = (input, startIndex, prefixLength) => {
52
- const csiPayloadStart = startIndex + prefixLength + 1;
53
- let index = csiPayloadStart;
54
- for (; index < input.length; index++) {
55
- const byte = input.codePointAt(index);
56
- if (byte === void 0) return "pending";
57
- if (isCsiParameterByte(byte) || isCsiIntermediateByte(byte)) continue;
58
- if (byte === 91 && index === csiPayloadStart) continue;
59
- if (isCsiFinalByte(byte)) return {
60
- sequence: input.slice(startIndex, index + 1),
61
- nextIndex: index + 1
62
- };
63
- return;
64
- }
65
- return "pending";
66
- };
67
- const parseSs3Sequence = (input, startIndex, prefixLength) => {
68
- const nextIndex = startIndex + prefixLength + 2;
69
- if (nextIndex > input.length) return "pending";
70
- const finalByte = input.codePointAt(nextIndex - 1);
71
- if (finalByte === void 0 || !isCsiFinalByte(finalByte)) return;
72
- return {
73
- sequence: input.slice(startIndex, nextIndex),
74
- nextIndex
75
- };
76
- };
77
- const parseControlSequence = (input, startIndex, prefixLength) => {
78
- const sequenceType = input[startIndex + prefixLength];
79
- if (sequenceType === void 0) return "pending";
80
- if (sequenceType === "[") return parseCsiSequence(input, startIndex, prefixLength);
81
- if (sequenceType === "O") return parseSs3Sequence(input, startIndex, prefixLength);
82
- };
83
- const parseEscapedCodePoint = (input, escapeIndex) => {
84
- const nextCodePoint = input.codePointAt(escapeIndex + 1);
85
- const nextCodePointLength = nextCodePoint !== void 0 && nextCodePoint > 65535 ? 2 : 1;
86
- const nextIndex = escapeIndex + 1 + nextCodePointLength;
87
- return {
88
- sequence: input.slice(escapeIndex, nextIndex),
89
- nextIndex
90
- };
91
- };
92
- const parseEscapeSequence = (input, escapeIndex) => {
93
- if (escapeIndex === input.length - 1) return "pending";
94
- if (input[escapeIndex + 1] === escape$1) {
95
- if (escapeIndex + 2 >= input.length) return "pending";
96
- const doubleEscapeSequence = parseControlSequence(input, escapeIndex, 2);
97
- if (doubleEscapeSequence === "pending") return "pending";
98
- if (doubleEscapeSequence) return doubleEscapeSequence;
99
- return {
100
- sequence: input.slice(escapeIndex, escapeIndex + 2),
101
- nextIndex: escapeIndex + 2
102
- };
103
- }
104
- const controlSequence = parseControlSequence(input, escapeIndex, 1);
105
- if (controlSequence === "pending") return "pending";
106
- if (controlSequence) return controlSequence;
107
- return parseEscapedCodePoint(input, escapeIndex);
108
- };
109
- /**
110
- Split a chunk of non-escape text so that backspace bytes (`0x7F` and `0x08`) become individual events. When a user holds the backspace key, the terminal sends repeated bytes in a single stdin chunk. Without splitting, `parseKeypress` receives the multi-byte string and fails to recognize it as a key event, corrupting the input state.
111
-
112
- Other control characters like `\r` and `\t` are NOT split because they can legitimately appear inside pasted text.
113
- */
114
- const splitBackspaceBytes = (text, events) => {
115
- let textSegmentStart = 0;
116
- for (let index = 0; index < text.length; index++) {
117
- const character = text[index];
118
- if (character === "" || character === "\b") {
119
- if (index > textSegmentStart) events.push(text.slice(textSegmentStart, index));
120
- events.push(character);
121
- textSegmentStart = index + 1;
122
- }
123
- }
124
- if (textSegmentStart < text.length) events.push(text.slice(textSegmentStart));
125
- };
126
- const parseKeypresses = (input) => {
127
- const events = [];
128
- let index = 0;
129
- const pendingFrom = (pendingStartIndex) => ({
130
- events,
131
- pending: input.slice(pendingStartIndex)
132
- });
133
- while (index < input.length) {
134
- const escapeIndex = input.indexOf(escape$1, index);
135
- if (escapeIndex === -1) {
136
- splitBackspaceBytes(input.slice(index), events);
137
- return {
138
- events,
139
- pending: ""
140
- };
141
- }
142
- if (escapeIndex > index) splitBackspaceBytes(input.slice(index, escapeIndex), events);
143
- const parsedEscapeSequence = parseEscapeSequence(input, escapeIndex);
144
- if (parsedEscapeSequence === "pending") return pendingFrom(escapeIndex);
145
- if (parsedEscapeSequence.sequence === pasteStart) {
146
- const afterStart = parsedEscapeSequence.nextIndex;
147
- const endIndex = input.indexOf(pasteEnd, afterStart);
148
- if (endIndex === -1) return pendingFrom(escapeIndex);
149
- events.push({ paste: input.slice(afterStart, endIndex) });
150
- index = endIndex + pasteEnd.length;
151
- continue;
152
- }
153
- events.push(parsedEscapeSequence.sequence);
154
- index = parsedEscapeSequence.nextIndex;
155
- }
156
- return {
157
- events,
158
- pending: ""
159
- };
160
- };
161
- const createInputParser = () => {
162
- let pending = "";
163
- return {
164
- push(chunk) {
165
- const parsedInput = parseKeypresses(pending + chunk);
166
- pending = parsedInput.pending;
167
- return parsedInput.events;
168
- },
169
- hasPendingEscape() {
170
- return pending.startsWith(escape$1) && !pending.startsWith(pasteStart) && pending !== pasteStart.slice(0, -1);
171
- },
172
- flushPendingEscape() {
173
- if (!pending.startsWith(escape$1)) return;
174
- const pendingEscape = pending;
175
- pending = "";
176
- return pendingEscape;
177
- },
178
- reset() {
179
- pending = "";
180
- }
181
- };
182
- };
183
- //#endregion
184
- //#region src/stream.ts
185
- const isTty = (stream) => {
186
- return "isTTY" in stream && stream.isTTY === true;
187
- };
188
- const isRawModeStream = (stdin) => {
189
- return isTty(stdin) && "setRawMode" in stdin && typeof stdin.setRawMode === "function";
190
- };
191
- const getRawModeStream = (stdin) => {
192
- if (!isRawModeStream(stdin)) return;
193
- return stdin;
194
- };
195
- //#endregion
196
31
  //#region src/components/AnimationContext.ts
197
32
  const animationContext = createContext({
198
33
  renderThrottleMs: 0,
@@ -247,6 +82,119 @@ const codeExcerpt = (source, line, options = {}) => {
247
82
  }));
248
83
  };
249
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
+ /**
172
+ `<Box>` is an essential Ink component to build your layout. It's like `<div style="display: flex">` in the browser.
173
+ */
174
+ function Box({ children, ref, backgroundColor, "aria-label": ariaLabel, "aria-hidden": ariaHidden, "aria-role": role, "aria-state": ariaState, ...style }) {
175
+ const { isScreenReaderEnabled } = useContext(accessibilityContext);
176
+ const label = ariaLabel ? /* @__PURE__ */ jsx("ink-text", { children: ariaLabel }) : void 0;
177
+ if (isScreenReaderEnabled && ariaHidden) return null;
178
+ return /* @__PURE__ */ jsx("ink-box", {
179
+ ref,
180
+ style: {
181
+ flexWrap: "nowrap",
182
+ flexDirection: "row",
183
+ flexGrow: 0,
184
+ flexShrink: 1,
185
+ ...style,
186
+ backgroundColor,
187
+ overflowX: style.overflowX ?? style.overflow ?? "visible",
188
+ overflowY: style.overflowY ?? style.overflow ?? "visible"
189
+ },
190
+ internal_accessibility: {
191
+ role,
192
+ state: ariaState
193
+ },
194
+ children: isScreenReaderEnabled && label ? label : children
195
+ });
196
+ }
197
+ //#endregion
250
198
  //#region src/parse-stack-line.ts
251
199
  const lineRegExp = new RegExp("^" + String.raw`(?:\s*at )?` + "(?:(new) )?" + String.raw`(?:(.*?) \()?` + String.raw`(?:eval at ([^ ]+) \((.+?):(\d+):(\d+)\), )?` + String.raw`(?:(.+?):(\d+):(\d+)|(native))` + String.raw`(\)?)$`);
252
200
  const methodRegExp = /^(.*?) \[as (.*?)]$/;
@@ -308,110 +256,6 @@ const parseStackLine = (line) => {
308
256
  return result;
309
257
  };
310
258
  //#endregion
311
- //#region src/components/BackgroundContext.ts
312
- const backgroundContext = createContext(void 0);
313
- //#endregion
314
- //#region src/components/Box.tsx
315
- /** @jsxImportSource react */
316
- /**
317
- `<Box>` is an essential Ink component to build your layout. It's like `<div style="display: flex">` in the browser.
318
- */
319
- const Box = forwardRef(({ children, backgroundColor, "aria-label": ariaLabel, "aria-hidden": ariaHidden, "aria-role": role, "aria-state": ariaState, ...style }, ref) => {
320
- const { isScreenReaderEnabled } = useContext(accessibilityContext);
321
- const label = ariaLabel ? /* @__PURE__ */ jsx("ink-text", { children: ariaLabel }) : void 0;
322
- if (isScreenReaderEnabled && ariaHidden) return null;
323
- const boxElement = /* @__PURE__ */ jsx("ink-box", {
324
- ref,
325
- style: {
326
- flexWrap: "nowrap",
327
- flexDirection: "row",
328
- flexGrow: 0,
329
- flexShrink: 1,
330
- ...style,
331
- backgroundColor,
332
- overflowX: style.overflowX ?? style.overflow ?? "visible",
333
- overflowY: style.overflowY ?? style.overflow ?? "visible"
334
- },
335
- internal_accessibility: {
336
- role,
337
- state: ariaState
338
- },
339
- children: isScreenReaderEnabled && label ? label : children
340
- });
341
- if (backgroundColor) return /* @__PURE__ */ jsx(backgroundContext.Provider, {
342
- value: backgroundColor,
343
- children: boxElement
344
- });
345
- return boxElement;
346
- });
347
- Box.displayName = "Box";
348
- //#endregion
349
- //#region src/colorize.ts
350
- const rgbRegex = /^rgb\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/;
351
- const ansiRegex = /^ansi256\(\s?(\d+)\s?\)$/;
352
- const isNamedColor = (color) => {
353
- return color in chalk;
354
- };
355
- const colorize = (str, color, type) => {
356
- if (!color) return str;
357
- if (isNamedColor(color)) {
358
- if (type === "foreground") return chalk[color](str);
359
- const methodName = `bg${color[0].toUpperCase() + color.slice(1)}`;
360
- return chalk[methodName](str);
361
- }
362
- if (color.startsWith("#")) return type === "foreground" ? chalk.hex(color)(str) : chalk.bgHex(color)(str);
363
- if (color.startsWith("ansi256")) {
364
- const matches = ansiRegex.exec(color);
365
- if (!matches) return str;
366
- const value = Number(matches[1]);
367
- return type === "foreground" ? chalk.ansi256(value)(str) : chalk.bgAnsi256(value)(str);
368
- }
369
- if (color.startsWith("rgb")) {
370
- const matches = rgbRegex.exec(color);
371
- if (!matches) return str;
372
- const firstValue = Number(matches[1]);
373
- const secondValue = Number(matches[2]);
374
- const thirdValue = Number(matches[3]);
375
- return type === "foreground" ? chalk.rgb(firstValue, secondValue, thirdValue)(str) : chalk.bgRgb(firstValue, secondValue, thirdValue)(str);
376
- }
377
- return str;
378
- };
379
- //#endregion
380
- //#region src/components/Text.tsx
381
- /** @jsxImportSource react */
382
- /**
383
- This component can display text and change its style to make it bold, underlined, italic, or strikethrough.
384
- */
385
- function Text({ color, backgroundColor, dimColor = false, bold = false, italic = false, underline = false, strikethrough = false, inverse = false, wrap = "wrap", children, "aria-label": ariaLabel, "aria-hidden": ariaHidden = false }) {
386
- const { isScreenReaderEnabled } = useContext(accessibilityContext);
387
- const inheritedBackgroundColor = useContext(backgroundContext);
388
- const childrenOrAriaLabel = isScreenReaderEnabled && ariaLabel ? ariaLabel : children;
389
- if (childrenOrAriaLabel === void 0 || childrenOrAriaLabel === null) return null;
390
- const transform = (text) => {
391
- if (dimColor) text = chalk.dim(text);
392
- if (color) text = colorize(text, color, "foreground");
393
- const effectiveBackgroundColor = backgroundColor ?? inheritedBackgroundColor;
394
- if (effectiveBackgroundColor) text = colorize(text, effectiveBackgroundColor, "background");
395
- if (bold) text = chalk.bold(text);
396
- if (italic) text = chalk.italic(text);
397
- if (underline) text = chalk.underline(text);
398
- if (strikethrough) text = chalk.strikethrough(text);
399
- if (inverse) text = chalk.inverse(text);
400
- return text;
401
- };
402
- if (isScreenReaderEnabled && ariaHidden) return null;
403
- return /* @__PURE__ */ jsx("ink-text", {
404
- style: {
405
- flexGrow: 0,
406
- flexShrink: 1,
407
- flexDirection: "row",
408
- textWrap: wrap
409
- },
410
- internal_transform: transform,
411
- children: childrenOrAriaLabel
412
- });
413
- }
414
- //#endregion
415
259
  //#region src/components/ErrorOverview.tsx
416
260
  /** @jsxImportSource react */
417
261
  const cleanupPath = (path) => {
@@ -424,8 +268,8 @@ function ErrorOverview({ error }) {
424
268
  let excerpt;
425
269
  let lineWidth = 0;
426
270
  const stackLineCounts = /* @__PURE__ */ new Map();
427
- if (filePath && origin?.line && fs$1.existsSync(filePath)) {
428
- const sourceCode = fs$1.readFileSync(filePath, "utf8");
271
+ if (filePath && origin?.line && existsSync(filePath)) {
272
+ const sourceCode = readFileSync(filePath, "utf8");
429
273
  excerpt = codeExcerpt(sourceCode, origin.line);
430
274
  if (excerpt) for (const { line } of excerpt) lineWidth = Math.max(lineWidth, String(line).length);
431
275
  }
@@ -542,21 +386,6 @@ var ErrorBoundary = class extends PureComponent {
542
386
  }
543
387
  };
544
388
  //#endregion
545
- //#region src/components/FocusContext.ts
546
- const FocusContext = createContext({
547
- activeId: void 0,
548
- add() {},
549
- remove() {},
550
- activate() {},
551
- deactivate() {},
552
- enableFocus() {},
553
- disableFocus() {},
554
- focusNext() {},
555
- focusPrevious() {},
556
- focus() {}
557
- });
558
- FocusContext.displayName = "InternalFocusContext";
559
- //#endregion
560
389
  //#region src/components/StderrContext.ts
561
390
  /**
562
391
  `StderrContext` is a React context that exposes the stderr stream.
@@ -567,20 +396,6 @@ const StderrContext = createContext({
567
396
  });
568
397
  StderrContext.displayName = "InternalStderrContext";
569
398
  //#endregion
570
- //#region src/components/StdinContext.ts
571
- /**
572
- `StdinContext` is a React context that exposes the input stream.
573
- */
574
- const StdinContext = createContext({
575
- stdin: process.stdin,
576
- internal_eventEmitter: new EventEmitter(),
577
- setRawMode() {},
578
- setBracketedPasteMode() {},
579
- isRawModeSupported: false,
580
- internal_exitOnCtrlC: true
581
- });
582
- StdinContext.displayName = "InternalStdinContext";
583
- //#endregion
584
399
  //#region src/components/StdoutContext.ts
585
400
  /**
586
401
  `StdoutContext` is a React context that exposes the stdout stream where Ink renders your app.
@@ -596,7 +411,7 @@ StdoutContext.displayName = "InternalStdoutContext";
596
411
  const tab = " ";
597
412
  const shiftTab = `${CSI}Z`;
598
413
  const escape = "\x1B";
599
- function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, exitOnCtrlC, onExit, onWaitUntilRenderFlush, onSuspendTerminal, onRegisterInputControl, setCursorPosition, interactive, renderThrottleMs }) {
414
+ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, exitOnCtrlC, onExit, onWaitUntilRenderFlush, onSuspendTerminal, onRegisterInputControl, setCursorPosition, interactive, renderThrottleMs, terminalInput }) {
600
415
  const [isFocusEnabled, setIsFocusEnabled] = useState(true);
601
416
  const [activeFocusId, setActiveFocusId] = useState(void 0);
602
417
  const [, setFocusables] = useState([]);
@@ -609,7 +424,6 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
609
424
  const internal_eventEmitter = useRef(new EventEmitter());
610
425
  internal_eventEmitter.current.setMaxListeners(Infinity);
611
426
  const readableListenerRef = useRef(void 0);
612
- const inputParserRef = useRef(createInputParser());
613
427
  const pendingInputFlushRef = useRef(void 0);
614
428
  const pendingInputFlushDelayMilliseconds = 20;
615
429
  const clearPendingInputFlush = useCallback(() => {
@@ -675,10 +489,14 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
675
489
  readableListenerRef.current = void 0;
676
490
  }, [stdin]);
677
491
  const clearInputState = useCallback(() => {
678
- inputParserRef.current.reset();
492
+ terminalInput.reset();
679
493
  clearPendingInputFlush();
680
494
  detachReadableListener();
681
- }, [clearPendingInputFlush, detachReadableListener]);
495
+ }, [
496
+ clearPendingInputFlush,
497
+ detachReadableListener,
498
+ terminalInput
499
+ ]);
682
500
  const disableRawMode = useCallback(() => {
683
501
  if (!rawModeStdin) return;
684
502
  pendingDisableRawModeRef.current = false;
@@ -714,16 +532,20 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
714
532
  clearPendingInputFlush();
715
533
  pendingInputFlushRef.current = setTimeout(() => {
716
534
  pendingInputFlushRef.current = void 0;
717
- const pendingEscape = inputParserRef.current.flushPendingEscape();
535
+ const pendingEscape = terminalInput.flushPendingEscape();
718
536
  if (!pendingEscape) return;
719
537
  emitInput(pendingEscape);
720
538
  }, pendingInputFlushDelayMilliseconds);
721
- }, [clearPendingInputFlush, emitInput]);
539
+ }, [
540
+ clearPendingInputFlush,
541
+ emitInput,
542
+ terminalInput
543
+ ]);
722
544
  const handleReadable = useCallback(() => {
723
545
  clearPendingInputFlush();
724
546
  let chunk;
725
547
  while ((chunk = stdin.read()) !== null) {
726
- const inputEvents = inputParserRef.current.push(chunk);
548
+ const inputEvents = terminalInput.push(chunk);
727
549
  for (const event of inputEvents) if (typeof event === "string") emitInput(event);
728
550
  else {
729
551
  if (internal_eventEmitter.current.listenerCount("paste") === 0) {
@@ -733,12 +555,13 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
733
555
  internal_eventEmitter.current.emit("paste", event.paste);
734
556
  }
735
557
  }
736
- if (inputParserRef.current.hasPendingEscape()) schedulePendingInputFlush();
558
+ if (terminalInput.hasPendingEscape()) schedulePendingInputFlush();
737
559
  }, [
738
560
  stdin,
739
561
  emitInput,
740
562
  clearPendingInputFlush,
741
- schedulePendingInputFlush
563
+ schedulePendingInputFlush,
564
+ terminalInput
742
565
  ]);
743
566
  const attachReadableListener = useCallback(() => {
744
567
  if (readableListenerRef.current) return;
@@ -780,6 +603,49 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
780
603
  clearInputState,
781
604
  disableRawMode
782
605
  ]);
606
+ const handleQueryTerminal = useCallback(async ({ refresh = false } = {}) => {
607
+ if (!interactive || !isRawModeSupported || !stdout.isTTY) return;
608
+ if (!refresh) {
609
+ const existing = getTerminalQueryPromise(stdout);
610
+ if (existing) return existing;
611
+ }
612
+ handleSetRawMode(true);
613
+ detachReadableListener();
614
+ try {
615
+ return await (refresh ? refreshTerminalQuery(stdin, stdout) : ensureTerminalQuery(stdin, stdout));
616
+ } finally {
617
+ attachReadableListener();
618
+ handleSetRawMode(false);
619
+ }
620
+ }, [
621
+ interactive,
622
+ isRawModeSupported,
623
+ stdin,
624
+ stdout,
625
+ handleSetRawMode,
626
+ detachReadableListener,
627
+ attachReadableListener
628
+ ]);
629
+ const reportFeedActiveRef = useRef(false);
630
+ const handleSetReportFeed = useCallback((active) => {
631
+ if (active === reportFeedActiveRef.current || !isRawModeSupported) return;
632
+ reportFeedActiveRef.current = active;
633
+ handleSetRawMode(active);
634
+ }, [isRawModeSupported, handleSetRawMode]);
635
+ useInsertionEffect(() => {
636
+ const unregister = registerTerminalIntegration(stdout, {
637
+ runQuery: handleQueryTerminal,
638
+ setReportFeed: handleSetReportFeed
639
+ });
640
+ return () => {
641
+ unregister();
642
+ handleSetReportFeed(false);
643
+ };
644
+ }, [
645
+ stdout,
646
+ handleQueryTerminal,
647
+ handleSetReportFeed
648
+ ]);
783
649
  const handleSetBracketedPasteMode = useCallback((isEnabled) => {
784
650
  if (!stdout.isTTY) return;
785
651
  if (isEnabled) {
@@ -1053,844 +919,21 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
1053
919
  }
1054
920
  App.displayName = "InternalApp";
1055
921
  //#endregion
1056
- //#region src/cursor-position.ts
1057
- const showCursorEscape = ansiEscapes.cursorShow;
1058
- const hideCursorEscape = ansiEscapes.cursorHide;
1059
- /**
1060
- Compare two cursor positions. Returns true if they differ.
1061
- */
1062
- const cursorPositionChanged = (a, b) => a?.x !== b?.x || a?.y !== b?.y;
1063
- /**
1064
- Build escape sequence to move cursor from the bottom of the output to the target position and show it.
1065
-
1066
- `bottomLine` is the row the renderer left the cursor on, counted from the top of the output.
1067
- That is always `lines.length - 1` for `lines = str.split('\n')`, whether or not the output ends
1068
- with a newline:
1069
-
1070
- - With a trailing newline, `split` yields one extra empty element and the renderer stops just
1071
- past the last visible line — which is `lines.length - 1`.
1072
- - Without one, there is no extra element and the renderer deliberately stops on the last visible
1073
- line instead of moving past it — which is also `lines.length - 1`.
1074
-
1075
- This is the same row basis `buildReturnToBottom` measures from, so the two stay in step.
1076
- */
1077
- const buildCursorSuffix = (bottomLine, cursorPosition) => {
1078
- if (!cursorPosition) return "";
1079
- const moveUp = bottomLine - cursorPosition.y;
1080
- return (moveUp > 0 ? ansiEscapes.cursorUp(moveUp) : "") + ansiEscapes.cursorTo(cursorPosition.x) + showCursorEscape;
1081
- };
1082
- /**
1083
- Build escape sequence to move cursor from previousCursorPosition back to the bottom of output.
1084
- This must be done before eraseLines or any operation that assumes cursor is at the bottom.
1085
- */
1086
- const buildReturnToBottom = (previousLineCount, previousCursorPosition) => {
1087
- if (!previousCursorPosition) return "";
1088
- const down = previousLineCount - 1 - previousCursorPosition.y;
1089
- return (down > 0 ? ansiEscapes.cursorDown(down) : "") + ansiEscapes.cursorTo(0);
1090
- };
1091
- /**
1092
- Build the escape sequence for cursor-only updates (output unchanged, cursor moved).
1093
- Hides cursor if it was previously shown, returns to bottom, then repositions.
1094
-
1095
- `buildReturnToBottom` has just placed the cursor on row `previousLineCount - 1`, so the
1096
- suffix measures from there rather than recomputing the row from the output.
1097
- */
1098
- const buildCursorOnlySequence = (input) => {
1099
- const hidePrefix = input.cursorWasShown ? hideCursorEscape : "";
1100
- const returnToBottom = buildReturnToBottom(input.previousLineCount, input.previousCursorPosition);
1101
- const cursorSuffix = buildCursorSuffix(input.previousLineCount - 1, input.cursorPosition);
1102
- return hidePrefix + returnToBottom + cursorSuffix;
1103
- };
1104
- /**
1105
- Build the prefix that hides cursor and returns to bottom before erasing or rewriting.
1106
- Returns empty string if cursor was not shown.
1107
- */
1108
- const buildReturnToBottomPrefix = (cursorWasShown, previousLineCount, previousCursorPosition) => {
1109
- if (!cursorWasShown) return "";
1110
- return hideCursorEscape + buildReturnToBottom(previousLineCount, previousCursorPosition);
1111
- };
1112
- //#endregion
1113
- //#region src/quick-lru.ts
1114
- var QuickLru = class {
1115
- #size = 0;
1116
- #cache = /* @__PURE__ */ new Map();
1117
- #oldCache = /* @__PURE__ */ new Map();
1118
- #maxSize;
1119
- constructor({ maxSize }) {
1120
- if (!(maxSize && maxSize > 0)) throw new TypeError("`maxSize` must be a number greater than 0");
1121
- this.#maxSize = maxSize;
1122
- }
1123
- get size() {
1124
- let oldCacheSize = 0;
1125
- for (const key of this.#oldCache.keys()) if (!this.#cache.has(key)) oldCacheSize++;
1126
- return Math.min(this.#size + oldCacheSize, this.#maxSize);
1127
- }
1128
- get(key) {
1129
- if (this.#cache.has(key)) return this.#cache.get(key);
1130
- if (this.#oldCache.has(key)) {
1131
- const value = this.#oldCache.get(key);
1132
- this.#oldCache.delete(key);
1133
- this.#set(key, value);
1134
- return value;
1135
- }
1136
- }
1137
- set(key, value) {
1138
- if (this.#cache.has(key)) this.#cache.set(key, value);
1139
- else this.#set(key, value);
1140
- return this;
1141
- }
1142
- has(key) {
1143
- return this.#cache.has(key) || this.#oldCache.has(key);
1144
- }
1145
- delete(key) {
1146
- const deleted = this.#cache.delete(key);
1147
- if (deleted) this.#size--;
1148
- return this.#oldCache.delete(key) || deleted;
1149
- }
1150
- clear() {
1151
- this.#cache.clear();
1152
- this.#oldCache.clear();
1153
- this.#size = 0;
1154
- }
1155
- #set(key, value) {
1156
- this.#cache.set(key, value);
1157
- this.#size++;
1158
- if (this.#size >= this.#maxSize) {
1159
- this.#size = 0;
1160
- this.#oldCache = this.#cache;
1161
- this.#cache = /* @__PURE__ */ new Map();
1162
- }
1163
- }
1164
- };
1165
- //#endregion
1166
- //#region src/measure-text.ts
1167
- const cache = new QuickLru({ maxSize: 4096 });
1168
- const measureText = (text) => {
1169
- if (text.length === 0) return {
1170
- width: 0,
1171
- height: 0
1172
- };
1173
- const cachedDimensions = cache.get(text);
1174
- if (cachedDimensions) return cachedDimensions;
1175
- const dimensions = {
1176
- width: widestLine(text),
1177
- height: text.split("\n").length
1178
- };
1179
- cache.set(text, dimensions);
1180
- return dimensions;
1181
- };
1182
- //#endregion
1183
- //#region src/ansi-tokenizer.ts
1184
- const bellCharacter = "\x07";
1185
- const escapeCharacter = "\x1B";
1186
- const stringTerminatorCharacter = "œ";
1187
- const csiCharacter = "›";
1188
- const oscCharacter = "";
1189
- const dcsCharacter = "";
1190
- const pmCharacter = "ž";
1191
- const apcCharacter = "Ÿ";
1192
- const sosCharacter = "˜";
1193
- const isCsiParameterCharacter = (character) => {
1194
- const codePoint = character.codePointAt(0);
1195
- return codePoint !== void 0 && codePoint >= 48 && codePoint <= 63;
1196
- };
1197
- const isCsiIntermediateCharacter = (character) => {
1198
- const codePoint = character.codePointAt(0);
1199
- return codePoint !== void 0 && codePoint >= 32 && codePoint <= 47;
1200
- };
1201
- const isCsiFinalCharacter = (character) => {
1202
- const codePoint = character.codePointAt(0);
1203
- return codePoint !== void 0 && codePoint >= 64 && codePoint <= 126;
1204
- };
1205
- const isEscapeIntermediateCharacter = (character) => {
1206
- const codePoint = character.codePointAt(0);
1207
- return codePoint !== void 0 && codePoint >= 32 && codePoint <= 47;
1208
- };
1209
- const isEscapeFinalCharacter = (character) => {
1210
- const codePoint = character.codePointAt(0);
1211
- return codePoint !== void 0 && codePoint >= 48 && codePoint <= 126;
1212
- };
1213
- const isC1ControlCharacter = (character) => {
1214
- const codePoint = character.codePointAt(0);
1215
- return codePoint !== void 0 && codePoint >= 128 && codePoint <= 159;
1216
- };
1217
- const readCsiSequence = (text, fromIndex) => {
1218
- let index = fromIndex;
1219
- while (index < text.length) {
1220
- const character = text[index];
1221
- if (!isCsiParameterCharacter(character)) break;
1222
- index++;
1223
- }
1224
- const parameterString = text.slice(fromIndex, index);
1225
- const intermediateStartIndex = index;
1226
- while (index < text.length) {
1227
- const character = text[index];
1228
- if (!isCsiIntermediateCharacter(character)) break;
1229
- index++;
1230
- }
1231
- const intermediateString = text.slice(intermediateStartIndex, index);
1232
- const finalCharacter = text[index];
1233
- if (finalCharacter === void 0 || !isCsiFinalCharacter(finalCharacter)) return;
1234
- return {
1235
- endIndex: index + 1,
1236
- parameterString,
1237
- intermediateString,
1238
- finalCharacter
1239
- };
1240
- };
1241
- const findControlStringTerminatorIndex = (text, fromIndex, allowBellTerminator) => {
1242
- for (let index = fromIndex; index < text.length; index++) {
1243
- const character = text[index];
1244
- if (allowBellTerminator && character === bellCharacter) return index + 1;
1245
- if (character === stringTerminatorCharacter) return index + 1;
1246
- if (character === escapeCharacter) {
1247
- const followingCharacter = text[index + 1];
1248
- if (followingCharacter === escapeCharacter) {
1249
- index++;
1250
- continue;
1251
- }
1252
- if (followingCharacter === "\\") return index + 2;
1253
- }
1254
- }
1255
- };
1256
- const readEscapeSequence = (text, fromIndex) => {
1257
- let index = fromIndex;
1258
- while (index < text.length) {
1259
- const character = text[index];
1260
- if (!isEscapeIntermediateCharacter(character)) break;
1261
- index++;
1262
- }
1263
- const intermediateString = text.slice(fromIndex, index);
1264
- const finalCharacter = text[index];
1265
- if (finalCharacter === void 0 || !isEscapeFinalCharacter(finalCharacter)) return;
1266
- return {
1267
- endIndex: index + 1,
1268
- intermediateString,
1269
- finalCharacter
1270
- };
1271
- };
1272
- const getControlStringFromEscapeIntroducer = (character) => {
1273
- switch (character) {
1274
- case "]": return {
1275
- type: "osc",
1276
- allowBellTerminator: true
1277
- };
1278
- case "P": return {
1279
- type: "dcs",
1280
- allowBellTerminator: false
1281
- };
1282
- case "^": return {
1283
- type: "pm",
1284
- allowBellTerminator: false
1285
- };
1286
- case "_": return {
1287
- type: "apc",
1288
- allowBellTerminator: false
1289
- };
1290
- case "X": return {
1291
- type: "sos",
1292
- allowBellTerminator: false
1293
- };
1294
- default: return;
1295
- }
1296
- };
1297
- const getControlStringFromC1Introducer = (character) => {
1298
- switch (character) {
1299
- case oscCharacter: return {
1300
- type: "osc",
1301
- allowBellTerminator: true
1302
- };
1303
- case dcsCharacter: return {
1304
- type: "dcs",
1305
- allowBellTerminator: false
1306
- };
1307
- case pmCharacter: return {
1308
- type: "pm",
1309
- allowBellTerminator: false
1310
- };
1311
- case apcCharacter: return {
1312
- type: "apc",
1313
- allowBellTerminator: false
1314
- };
1315
- case sosCharacter: return {
1316
- type: "sos",
1317
- allowBellTerminator: false
1318
- };
1319
- default: return;
1320
- }
1321
- };
1322
- const hasAnsiControlCharacters = (text) => {
1323
- if (text.includes(escapeCharacter)) return true;
1324
- for (const character of text) if (isC1ControlCharacter(character)) return true;
1325
- return false;
1326
- };
1327
- const malformedFromIndex = (tokens, text, textStartIndex, fromIndex) => {
1328
- if (fromIndex > textStartIndex) tokens.push({
1329
- type: "text",
1330
- value: text.slice(textStartIndex, fromIndex)
1331
- });
1332
- tokens.push({
1333
- type: "invalid",
1334
- value: text.slice(fromIndex)
1335
- });
1336
- return tokens;
1337
- };
1338
- const tokenizeAnsi = (text) => {
1339
- if (!hasAnsiControlCharacters(text)) return [{
1340
- type: "text",
1341
- value: text
1342
- }];
1343
- const tokens = [];
1344
- let textStartIndex = 0;
1345
- for (let index = 0; index < text.length;) {
1346
- const character = text[index];
1347
- if (character === void 0) break;
1348
- if (character === escapeCharacter) {
1349
- const followingCharacter = text[index + 1];
1350
- if (followingCharacter === void 0) return malformedFromIndex(tokens, text, textStartIndex, index);
1351
- if (followingCharacter === "[") {
1352
- const csiSequence = readCsiSequence(text, index + 2);
1353
- if (csiSequence === void 0) return malformedFromIndex(tokens, text, textStartIndex, index);
1354
- if (index > textStartIndex) tokens.push({
1355
- type: "text",
1356
- value: text.slice(textStartIndex, index)
1357
- });
1358
- tokens.push({
1359
- type: "csi",
1360
- value: text.slice(index, csiSequence.endIndex),
1361
- parameterString: csiSequence.parameterString,
1362
- intermediateString: csiSequence.intermediateString,
1363
- finalCharacter: csiSequence.finalCharacter
1364
- });
1365
- index = csiSequence.endIndex;
1366
- textStartIndex = index;
1367
- continue;
1368
- }
1369
- const escapeControlString = getControlStringFromEscapeIntroducer(followingCharacter);
1370
- if (escapeControlString !== void 0) {
1371
- const controlStringTerminatorIndex = findControlStringTerminatorIndex(text, index + 2, escapeControlString.allowBellTerminator);
1372
- if (controlStringTerminatorIndex === void 0) return malformedFromIndex(tokens, text, textStartIndex, index);
1373
- if (index > textStartIndex) tokens.push({
1374
- type: "text",
1375
- value: text.slice(textStartIndex, index)
1376
- });
1377
- tokens.push({
1378
- type: escapeControlString.type,
1379
- value: text.slice(index, controlStringTerminatorIndex)
1380
- });
1381
- index = controlStringTerminatorIndex;
1382
- textStartIndex = index;
1383
- continue;
1384
- }
1385
- const escapeSequence = readEscapeSequence(text, index + 1);
1386
- if (escapeSequence === void 0) {
1387
- if (isEscapeIntermediateCharacter(followingCharacter)) return malformedFromIndex(tokens, text, textStartIndex, index);
1388
- if (index > textStartIndex) tokens.push({
1389
- type: "text",
1390
- value: text.slice(textStartIndex, index)
1391
- });
1392
- index++;
1393
- textStartIndex = index;
1394
- continue;
1395
- }
1396
- if (index > textStartIndex) tokens.push({
1397
- type: "text",
1398
- value: text.slice(textStartIndex, index)
1399
- });
1400
- tokens.push({
1401
- type: "esc",
1402
- value: text.slice(index, escapeSequence.endIndex),
1403
- intermediateString: escapeSequence.intermediateString,
1404
- finalCharacter: escapeSequence.finalCharacter
1405
- });
1406
- index = escapeSequence.endIndex;
1407
- textStartIndex = index;
1408
- continue;
1409
- }
1410
- if (character === csiCharacter) {
1411
- const csiSequence = readCsiSequence(text, index + 1);
1412
- if (csiSequence === void 0) return malformedFromIndex(tokens, text, textStartIndex, index);
1413
- if (index > textStartIndex) tokens.push({
1414
- type: "text",
1415
- value: text.slice(textStartIndex, index)
1416
- });
1417
- tokens.push({
1418
- type: "csi",
1419
- value: text.slice(index, csiSequence.endIndex),
1420
- parameterString: csiSequence.parameterString,
1421
- intermediateString: csiSequence.intermediateString,
1422
- finalCharacter: csiSequence.finalCharacter
1423
- });
1424
- index = csiSequence.endIndex;
1425
- textStartIndex = index;
1426
- continue;
1427
- }
1428
- const c1ControlString = getControlStringFromC1Introducer(character);
1429
- if (c1ControlString !== void 0) {
1430
- const controlStringTerminatorIndex = findControlStringTerminatorIndex(text, index + 1, c1ControlString.allowBellTerminator);
1431
- if (controlStringTerminatorIndex === void 0) return malformedFromIndex(tokens, text, textStartIndex, index);
1432
- if (index > textStartIndex) tokens.push({
1433
- type: "text",
1434
- value: text.slice(textStartIndex, index)
1435
- });
1436
- tokens.push({
1437
- type: c1ControlString.type,
1438
- value: text.slice(index, controlStringTerminatorIndex)
1439
- });
1440
- index = controlStringTerminatorIndex;
1441
- textStartIndex = index;
1442
- continue;
1443
- }
1444
- if (character === stringTerminatorCharacter) {
1445
- if (index > textStartIndex) tokens.push({
1446
- type: "text",
1447
- value: text.slice(textStartIndex, index)
1448
- });
1449
- tokens.push({
1450
- type: "st",
1451
- value: character
1452
- });
1453
- index++;
1454
- textStartIndex = index;
1455
- continue;
1456
- }
1457
- if (isC1ControlCharacter(character)) {
1458
- if (index > textStartIndex) tokens.push({
1459
- type: "text",
1460
- value: text.slice(textStartIndex, index)
1461
- });
1462
- tokens.push({
1463
- type: "c1",
1464
- value: character
1465
- });
1466
- index++;
1467
- textStartIndex = index;
1468
- continue;
1469
- }
1470
- index++;
1471
- }
1472
- if (textStartIndex < text.length) tokens.push({
1473
- type: "text",
1474
- value: text.slice(textStartIndex)
1475
- });
1476
- return tokens;
1477
- };
1478
- //#endregion
1479
- //#region src/sanitize-ansi.ts
1480
- const sgrParametersRegex = /^[\d:;]*$/;
1481
- const sanitizeAnsi = (text) => {
1482
- if (!hasAnsiControlCharacters(text)) return text;
1483
- let output = "";
1484
- for (const token of tokenizeAnsi(text)) {
1485
- if (token.type === "text" || token.type === "osc") {
1486
- output += token.value;
1487
- continue;
1488
- }
1489
- if (token.type === "csi" && token.finalCharacter === "m" && token.intermediateString === "" && sgrParametersRegex.test(token.parameterString)) output += token.value;
1490
- }
1491
- return output;
1492
- };
1493
- //#endregion
1494
- //#region src/squash-text-nodes.ts
1495
- const squashTextNodes = (node) => {
1496
- let text = "";
1497
- for (let index = 0; index < node.childNodes.length; index++) {
1498
- const childNode = node.childNodes[index];
1499
- if (childNode === void 0) continue;
1500
- let nodeText = "";
1501
- if (childNode.nodeName === "#text") nodeText = childNode.nodeValue;
1502
- else {
1503
- if (childNode.nodeName === "ink-text" || childNode.nodeName === "ink-virtual-text") nodeText = squashTextNodes(childNode);
1504
- if (nodeText.length > 0 && typeof childNode.internal_transform === "function") nodeText = childNode.internal_transform(nodeText, index);
1505
- }
1506
- text += nodeText;
1507
- }
1508
- return sanitizeAnsi(text);
1509
- };
1510
- //#endregion
1511
- //#region src/wrap-text.ts
1512
- const wrapTextCache = new QuickLru({ maxSize: 4096 });
1513
- const wrapText = (text, maxWidth, wrapType) => {
1514
- const cacheKey = text + String(maxWidth) + String(wrapType);
1515
- const cachedText = wrapTextCache.get(cacheKey);
1516
- if (cachedText !== void 0) return cachedText;
1517
- let wrappedText = text;
1518
- if (wrapType === "wrap") wrappedText = wrapAnsi(text, maxWidth, {
1519
- trim: false,
1520
- hard: true
1521
- });
1522
- if (wrapType === "hard") wrappedText = wrapAnsi(text, maxWidth, {
1523
- trim: false,
1524
- hard: true,
1525
- wordWrap: false
1526
- });
1527
- if (wrapType.startsWith("truncate")) {
1528
- let position = "end";
1529
- if (wrapType === "truncate-middle") position = "middle";
1530
- if (wrapType === "truncate-start") position = "start";
1531
- wrappedText = cliTruncate(text, maxWidth, { position });
1532
- }
1533
- wrapTextCache.set(cacheKey, wrappedText);
1534
- return wrappedText;
1535
- };
1536
- //#endregion
1537
- //#region src/dom.ts
1538
- const createNode = (nodeName) => {
1539
- const node = {
1540
- nodeName,
1541
- style: {},
1542
- attributes: {},
1543
- childNodes: [],
1544
- parentNode: void 0,
1545
- yogaNode: nodeName === "ink-virtual-text" ? void 0 : Yoga.Node.create(),
1546
- internal_accessibility: {}
1547
- };
1548
- if (nodeName === "ink-text") node.yogaNode?.setMeasureFunc(measureTextNode.bind(null, node));
1549
- return node;
1550
- };
1551
- const appendChildNode = (node, childNode) => {
1552
- if (childNode.parentNode) removeChildNode(childNode.parentNode, childNode);
1553
- childNode.parentNode = node;
1554
- node.childNodes.push(childNode);
1555
- if (childNode.yogaNode) node.yogaNode?.insertChild(childNode.yogaNode, node.yogaNode.getChildCount());
1556
- if (node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text") markNodeAsDirty(node);
1557
- };
1558
- const insertBeforeNode = (node, newChildNode, beforeChildNode) => {
1559
- if (newChildNode.parentNode) removeChildNode(newChildNode.parentNode, newChildNode);
1560
- newChildNode.parentNode = node;
1561
- const index = node.childNodes.indexOf(beforeChildNode);
1562
- if (index >= 0) {
1563
- node.childNodes.splice(index, 0, newChildNode);
1564
- if (newChildNode.yogaNode) node.yogaNode?.insertChild(newChildNode.yogaNode, index);
1565
- } else {
1566
- node.childNodes.push(newChildNode);
1567
- if (newChildNode.yogaNode) node.yogaNode?.insertChild(newChildNode.yogaNode, node.yogaNode.getChildCount());
1568
- }
1569
- if (node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text") markNodeAsDirty(node);
1570
- };
1571
- const removeChildNode = (node, removeNode) => {
1572
- if (removeNode.yogaNode) removeNode.parentNode?.yogaNode?.removeChild(removeNode.yogaNode);
1573
- removeNode.parentNode = void 0;
1574
- const index = node.childNodes.indexOf(removeNode);
1575
- if (index >= 0) node.childNodes.splice(index, 1);
1576
- if (node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text") markNodeAsDirty(node);
1577
- };
1578
- const nullifyYogaNodes = (node) => {
1579
- node.yogaNode = void 0;
1580
- if (node.nodeName !== "#text") for (const childNode of node.childNodes) nullifyYogaNodes(childNode);
1581
- };
1582
- /**
1583
- Detach a removed subtree from the layout engine: drop the measure callback
1584
- and null the `yogaNode` reference on every DOM node within it.
1585
-
1586
- Nulling the references makes every `?.yogaNode` guard in the codebase
1587
- effective for removed nodes, turns lingering access into a safe no-op (see
1588
- QwenLM/qwen-code#6820), and lets the garbage collector reclaim the Yoga
1589
- tree along with its closures.
1590
- */
1591
- const detachYogaSubtree = (removeNode) => {
1592
- removeNode.yogaNode?.unsetMeasureFunc();
1593
- nullifyYogaNodes(removeNode);
1594
- };
1595
- const setAttribute = (node, key, value) => {
1596
- if (key === "internal_accessibility") {
1597
- node.internal_accessibility = value;
1598
- return;
1599
- }
1600
- node.attributes[key] = value;
1601
- };
1602
- const setStyle = (node, style) => {
1603
- node.style = style ?? {};
1604
- };
1605
- const createTextNode = (text) => {
1606
- const node = {
1607
- nodeName: "#text",
1608
- nodeValue: text,
1609
- yogaNode: void 0,
1610
- parentNode: void 0,
1611
- style: {}
1612
- };
1613
- setTextNodeValue(node, text);
1614
- return node;
1615
- };
1616
- const measureTextNode = function(node, width) {
1617
- const text = node.nodeName === "#text" ? node.nodeValue : squashTextNodes(node);
1618
- const dimensions = measureText(text);
1619
- if (dimensions.width <= width) return dimensions;
1620
- if (dimensions.width >= 1 && width > 0 && width < 1) return dimensions;
1621
- const textWrap = node.style?.textWrap ?? "wrap";
1622
- const wrappedText = wrapText(text, width, textWrap);
1623
- return measureText(wrappedText);
1624
- };
1625
- const findClosestYogaNode = (node) => {
1626
- if (!node?.parentNode) return;
1627
- return node.yogaNode ?? findClosestYogaNode(node.parentNode);
1628
- };
1629
- const markNodeAsDirty = (node) => {
1630
- findClosestYogaNode(node)?.markDirty();
1631
- };
1632
- const setTextNodeValue = (node, text) => {
1633
- if (typeof text !== "string") text = String(text);
1634
- node.nodeValue = text;
1635
- markNodeAsDirty(node);
1636
- };
1637
- const addLayoutListener = (rootNode, listener) => {
1638
- if (rootNode.nodeName !== "ink-root") return () => {};
1639
- rootNode.internal_layoutListeners ??= /* @__PURE__ */ new Set();
1640
- rootNode.internal_layoutListeners.add(listener);
1641
- return () => {
1642
- rootNode.internal_layoutListeners?.delete(listener);
1643
- };
1644
- };
1645
- const emitLayoutListeners = (rootNode) => {
1646
- if (rootNode.nodeName !== "ink-root" || !rootNode.internal_layoutListeners) return;
1647
- for (const listener of rootNode.internal_layoutListeners) listener();
1648
- };
922
+ //#region src/components/TerminalOscContext.ts
923
+ const noop$1 = () => {};
924
+ const TerminalOscContext = createContext({
925
+ publishProgress: noop$1,
926
+ copyToClipboard: noop$1,
927
+ publishTitle: noop$1,
928
+ setWorkingDirectory: noop$1,
929
+ notify: noop$1,
930
+ setPointerShape: noop$1
931
+ });
932
+ TerminalOscContext.displayName = "InternalTerminalOscContext";
1649
933
  //#endregion
1650
934
  //#region src/instances.ts
1651
935
  const instances = /* @__PURE__ */ new WeakMap();
1652
936
  //#endregion
1653
- //#region src/is-in-ci.ts
1654
- const check = (key) => key in process.env && process.env[key] !== "0" && process.env[key] !== "false";
1655
- const isInCi = check("CI") || check("CONTINUOUS_INTEGRATION");
1656
- //#endregion
1657
- //#region src/kitty-keyboard.ts
1658
- const kittyFlags = {
1659
- disambiguateEscapeCodes: 1,
1660
- reportEventTypes: 2,
1661
- reportAlternateKeys: 4,
1662
- reportAllKeysAsEscapeCodes: 8,
1663
- reportAssociatedText: 16
1664
- };
1665
- function resolveFlags(flags) {
1666
- let result = 0;
1667
- for (const flag of flags) result |= kittyFlags[flag];
1668
- return result;
1669
- }
1670
- const kittyModifiers = {
1671
- shift: 1,
1672
- alt: 2,
1673
- ctrl: 4,
1674
- super: 8,
1675
- hyper: 16,
1676
- meta: 32,
1677
- capsLock: 64,
1678
- numLock: 128
1679
- };
1680
- //#endregion
1681
- //#region src/log-update.ts
1682
- const visibleLineCount = (lines, str) => str.endsWith("\n") ? lines.length - 1 : lines.length;
1683
- const createStandard = (stream, { showCursor = false } = {}) => {
1684
- let previousLineCount = 0;
1685
- let previousOutput = "";
1686
- let hasHiddenCursor = false;
1687
- let cursorPosition;
1688
- let cursorDirty = false;
1689
- let previousCursorPosition;
1690
- let cursorWasShown = false;
1691
- const getActiveCursor = () => cursorDirty ? cursorPosition : void 0;
1692
- const hasChanges = (str, activeCursor) => {
1693
- const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
1694
- return str !== previousOutput || cursorChanged;
1695
- };
1696
- const render = (str) => {
1697
- if (!showCursor && !hasHiddenCursor) {
1698
- cliCursor.hide(stream);
1699
- hasHiddenCursor = true;
1700
- }
1701
- const activeCursor = getActiveCursor();
1702
- cursorDirty = false;
1703
- const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
1704
- if (!hasChanges(str, activeCursor)) return false;
1705
- const lines = str.split("\n");
1706
- const cursorSuffix = buildCursorSuffix(lines.length - 1, activeCursor);
1707
- if (str === previousOutput && cursorChanged) stream.write(buildCursorOnlySequence({
1708
- cursorWasShown,
1709
- previousLineCount,
1710
- previousCursorPosition,
1711
- cursorPosition: activeCursor
1712
- }));
1713
- else {
1714
- previousOutput = str;
1715
- const returnPrefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
1716
- stream.write(returnPrefix + ansiEscapes.eraseLines(previousLineCount) + str + cursorSuffix);
1717
- previousLineCount = lines.length;
1718
- }
1719
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1720
- cursorWasShown = activeCursor !== void 0;
1721
- return true;
1722
- };
1723
- render.clear = () => {
1724
- const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
1725
- stream.write(prefix + ansiEscapes.eraseLines(previousLineCount));
1726
- previousOutput = "";
1727
- previousLineCount = 0;
1728
- previousCursorPosition = void 0;
1729
- cursorWasShown = false;
1730
- };
1731
- render.done = () => {
1732
- previousOutput = "";
1733
- previousLineCount = 0;
1734
- previousCursorPosition = void 0;
1735
- cursorWasShown = false;
1736
- if (!showCursor) {
1737
- cliCursor.show(stream);
1738
- hasHiddenCursor = false;
1739
- }
1740
- };
1741
- render.reset = () => {
1742
- previousOutput = "";
1743
- previousLineCount = 0;
1744
- previousCursorPosition = void 0;
1745
- cursorWasShown = false;
1746
- };
1747
- render.sync = (str) => {
1748
- const activeCursor = cursorDirty ? cursorPosition : void 0;
1749
- cursorDirty = false;
1750
- const lines = str.split("\n");
1751
- previousOutput = str;
1752
- previousLineCount = lines.length;
1753
- if (!activeCursor && cursorWasShown) stream.write(hideCursorEscape);
1754
- if (activeCursor) stream.write(buildCursorSuffix(lines.length - 1, activeCursor));
1755
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1756
- cursorWasShown = activeCursor !== void 0;
1757
- };
1758
- render.setCursorPosition = (position) => {
1759
- cursorPosition = position;
1760
- cursorDirty = true;
1761
- };
1762
- render.isCursorDirty = () => cursorDirty;
1763
- render.willRender = (str) => hasChanges(str, getActiveCursor());
1764
- return render;
1765
- };
1766
- const createIncremental = (stream, { showCursor = false } = {}) => {
1767
- let previousLines = [];
1768
- let previousOutput = "";
1769
- let hasHiddenCursor = false;
1770
- let cursorPosition;
1771
- let cursorDirty = false;
1772
- let previousCursorPosition;
1773
- let cursorWasShown = false;
1774
- const getActiveCursor = () => cursorDirty ? cursorPosition : void 0;
1775
- const hasChanges = (str, activeCursor) => {
1776
- const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
1777
- return str !== previousOutput || cursorChanged;
1778
- };
1779
- const render = (str) => {
1780
- if (!showCursor && !hasHiddenCursor) {
1781
- cliCursor.hide(stream);
1782
- hasHiddenCursor = true;
1783
- }
1784
- const activeCursor = getActiveCursor();
1785
- cursorDirty = false;
1786
- const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
1787
- if (!hasChanges(str, activeCursor)) return false;
1788
- const nextLines = str.split("\n");
1789
- const visibleCount = visibleLineCount(nextLines, str);
1790
- const previousVisible = visibleLineCount(previousLines, previousOutput);
1791
- if (str === previousOutput && cursorChanged) {
1792
- stream.write(buildCursorOnlySequence({
1793
- cursorWasShown,
1794
- previousLineCount: previousLines.length,
1795
- previousCursorPosition,
1796
- cursorPosition: activeCursor
1797
- }));
1798
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1799
- cursorWasShown = activeCursor !== void 0;
1800
- return true;
1801
- }
1802
- const returnPrefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
1803
- if (str === "\n" || previousOutput.length === 0) {
1804
- const cursorSuffix = buildCursorSuffix(nextLines.length - 1, activeCursor);
1805
- stream.write(returnPrefix + ansiEscapes.eraseLines(previousLines.length) + str + cursorSuffix);
1806
- cursorWasShown = activeCursor !== void 0;
1807
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1808
- previousOutput = str;
1809
- previousLines = nextLines;
1810
- return true;
1811
- }
1812
- if (visibleCount > previousVisible) {
1813
- const cursorSuffix = buildCursorSuffix(visibleCount, activeCursor);
1814
- stream.write(returnPrefix + ansiEscapes.eraseLines(previousLines.length) + str + cursorSuffix);
1815
- cursorWasShown = activeCursor !== void 0;
1816
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1817
- previousOutput = str;
1818
- previousLines = nextLines;
1819
- return true;
1820
- }
1821
- const hasTrailingNewline = str.endsWith("\n");
1822
- const buffer = [];
1823
- buffer.push(returnPrefix);
1824
- if (visibleCount < previousVisible) {
1825
- const extraSlot = previousOutput.endsWith("\n") ? 1 : 0;
1826
- buffer.push(ansiEscapes.eraseLines(previousVisible - visibleCount + extraSlot), ansiEscapes.cursorUp(visibleCount));
1827
- } else buffer.push(ansiEscapes.cursorUp(previousLines.length - 1));
1828
- for (let i = 0; i < visibleCount; i++) {
1829
- const isLastLine = i === visibleCount - 1;
1830
- if (nextLines[i] === previousLines[i]) {
1831
- if (!isLastLine || hasTrailingNewline) buffer.push(ansiEscapes.cursorNextLine);
1832
- continue;
1833
- }
1834
- buffer.push(ansiEscapes.cursorTo(0) + nextLines[i] + ansiEscapes.eraseEndLine + (isLastLine && !hasTrailingNewline ? "" : "\n"));
1835
- }
1836
- const cursorSuffix = buildCursorSuffix(nextLines.length - 1, activeCursor);
1837
- buffer.push(cursorSuffix);
1838
- stream.write(buffer.join(""));
1839
- cursorWasShown = activeCursor !== void 0;
1840
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1841
- previousOutput = str;
1842
- previousLines = nextLines;
1843
- return true;
1844
- };
1845
- render.clear = () => {
1846
- const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
1847
- stream.write(prefix + ansiEscapes.eraseLines(previousLines.length));
1848
- previousOutput = "";
1849
- previousLines = [];
1850
- previousCursorPosition = void 0;
1851
- cursorWasShown = false;
1852
- };
1853
- render.done = () => {
1854
- previousOutput = "";
1855
- previousLines = [];
1856
- previousCursorPosition = void 0;
1857
- cursorWasShown = false;
1858
- if (!showCursor) {
1859
- cliCursor.show(stream);
1860
- hasHiddenCursor = false;
1861
- }
1862
- };
1863
- render.reset = () => {
1864
- previousOutput = "";
1865
- previousLines = [];
1866
- previousCursorPosition = void 0;
1867
- cursorWasShown = false;
1868
- };
1869
- render.sync = (str) => {
1870
- const activeCursor = cursorDirty ? cursorPosition : void 0;
1871
- cursorDirty = false;
1872
- const lines = str.split("\n");
1873
- previousOutput = str;
1874
- previousLines = lines;
1875
- if (!activeCursor && cursorWasShown) stream.write(hideCursorEscape);
1876
- if (activeCursor) stream.write(buildCursorSuffix(lines.length - 1, activeCursor));
1877
- previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
1878
- cursorWasShown = activeCursor !== void 0;
1879
- };
1880
- render.setCursorPosition = (position) => {
1881
- cursorPosition = position;
1882
- cursorDirty = true;
1883
- };
1884
- render.isCursorDirty = () => cursorDirty;
1885
- render.willRender = (str) => hasChanges(str, getActiveCursor());
1886
- return render;
1887
- };
1888
- const create$1 = (stream, { showCursor = false, incremental = false } = {}) => {
1889
- if (incremental) return createIncremental(stream, { showCursor });
1890
- return createStandard(stream, { showCursor });
1891
- };
1892
- const logUpdate = { create: create$1 };
1893
- //#endregion
1894
937
  //#region src/patch-console.ts
1895
938
  const consoleMethods = [
1896
939
  "assert",
@@ -1905,590 +948,46 @@ const consoleMethods = [
1905
948
  "groupEnd",
1906
949
  "info",
1907
950
  "log",
1908
- "table",
1909
- "time",
1910
- "timeEnd",
1911
- "timeLog",
1912
- "trace",
1913
- "warn"
1914
- ];
1915
- const patchConsole = (callback) => {
1916
- const stdout = new PassThrough();
1917
- const stderr = new PassThrough();
1918
- stdout.write = (data) => {
1919
- callback("stdout", String(data));
1920
- return true;
1921
- };
1922
- stderr.write = (data) => {
1923
- callback("stderr", String(data));
1924
- return true;
1925
- };
1926
- const internalConsole = new Console(stdout, stderr);
1927
- const originalMethods = /* @__PURE__ */ new Map();
1928
- for (const method of consoleMethods) {
1929
- originalMethods.set(method, console[method]);
1930
- console[method] = internalConsole[method];
1931
- }
1932
- return () => {
1933
- for (const method of consoleMethods) console[method] = originalMethods.get(method);
1934
- originalMethods.clear();
1935
- };
1936
- };
1937
- //#endregion
1938
- //#region src/styles.ts
1939
- const positionEdges = [
1940
- ["top", Yoga.EDGE_TOP],
1941
- ["right", Yoga.EDGE_RIGHT],
1942
- ["bottom", Yoga.EDGE_BOTTOM],
1943
- ["left", Yoga.EDGE_LEFT]
1944
- ];
1945
- const applyPositionStyles = (node, style) => {
1946
- if ("position" in style) {
1947
- let positionType = Yoga.POSITION_TYPE_RELATIVE;
1948
- if (style.position === "absolute") positionType = Yoga.POSITION_TYPE_ABSOLUTE;
1949
- else if (style.position === "static") positionType = Yoga.POSITION_TYPE_STATIC;
1950
- node.setPositionType(positionType);
1951
- }
1952
- for (const [property, edge] of positionEdges) {
1953
- if (!(property in style)) continue;
1954
- const value = style[property];
1955
- if (typeof value === "string") {
1956
- node.setPositionPercent(edge, Number.parseFloat(value));
1957
- continue;
1958
- }
1959
- node.setPosition(edge, value);
1960
- }
1961
- };
1962
- const applyMarginStyles = (node, style) => {
1963
- if ("margin" in style) node.setMargin(Yoga.EDGE_ALL, style.margin ?? 0);
1964
- if ("marginX" in style) node.setMargin(Yoga.EDGE_HORIZONTAL, style.marginX ?? 0);
1965
- if ("marginY" in style) node.setMargin(Yoga.EDGE_VERTICAL, style.marginY ?? 0);
1966
- if ("marginLeft" in style) node.setMargin(Yoga.EDGE_START, style.marginLeft ?? 0);
1967
- if ("marginRight" in style) node.setMargin(Yoga.EDGE_END, style.marginRight ?? 0);
1968
- if ("marginTop" in style) node.setMargin(Yoga.EDGE_TOP, style.marginTop ?? 0);
1969
- if ("marginBottom" in style) node.setMargin(Yoga.EDGE_BOTTOM, style.marginBottom ?? 0);
1970
- };
1971
- const applyPaddingStyles = (node, style) => {
1972
- if ("padding" in style) node.setPadding(Yoga.EDGE_ALL, style.padding ?? 0);
1973
- if ("paddingX" in style) node.setPadding(Yoga.EDGE_HORIZONTAL, style.paddingX ?? 0);
1974
- if ("paddingY" in style) node.setPadding(Yoga.EDGE_VERTICAL, style.paddingY ?? 0);
1975
- if ("paddingLeft" in style) node.setPadding(Yoga.EDGE_LEFT, style.paddingLeft ?? 0);
1976
- if ("paddingRight" in style) node.setPadding(Yoga.EDGE_RIGHT, style.paddingRight ?? 0);
1977
- if ("paddingTop" in style) node.setPadding(Yoga.EDGE_TOP, style.paddingTop ?? 0);
1978
- if ("paddingBottom" in style) node.setPadding(Yoga.EDGE_BOTTOM, style.paddingBottom ?? 0);
1979
- };
1980
- const applyFlexStyles = (node, style) => {
1981
- if ("flexGrow" in style) node.setFlexGrow(style.flexGrow ?? 0);
1982
- if ("flexShrink" in style) node.setFlexShrink(typeof style.flexShrink === "number" ? style.flexShrink : 1);
1983
- if ("flexWrap" in style) {
1984
- if (style.flexWrap === "nowrap") node.setFlexWrap(Yoga.WRAP_NO_WRAP);
1985
- if (style.flexWrap === "wrap") node.setFlexWrap(Yoga.WRAP_WRAP);
1986
- if (style.flexWrap === "wrap-reverse") node.setFlexWrap(Yoga.WRAP_WRAP_REVERSE);
1987
- }
1988
- if ("flexDirection" in style) {
1989
- if (style.flexDirection === "row") node.setFlexDirection(Yoga.FLEX_DIRECTION_ROW);
1990
- if (style.flexDirection === "row-reverse") node.setFlexDirection(Yoga.FLEX_DIRECTION_ROW_REVERSE);
1991
- if (style.flexDirection === "column") node.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN);
1992
- if (style.flexDirection === "column-reverse") node.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN_REVERSE);
1993
- }
1994
- if ("flexBasis" in style) {
1995
- if (typeof style.flexBasis === "number") node.setFlexBasis(style.flexBasis);
1996
- else if (typeof style.flexBasis === "string") node.setFlexBasisPercent(Number.parseInt(style.flexBasis, 10));
1997
- else node.setFlexBasisAuto();
1998
- }
1999
- if ("alignItems" in style) {
2000
- if (style.alignItems === "stretch" || !style.alignItems) node.setAlignItems(Yoga.ALIGN_STRETCH);
2001
- if (style.alignItems === "flex-start") node.setAlignItems(Yoga.ALIGN_FLEX_START);
2002
- if (style.alignItems === "center") node.setAlignItems(Yoga.ALIGN_CENTER);
2003
- if (style.alignItems === "flex-end") node.setAlignItems(Yoga.ALIGN_FLEX_END);
2004
- if (style.alignItems === "baseline") node.setAlignItems(Yoga.ALIGN_BASELINE);
2005
- }
2006
- if ("alignSelf" in style) {
2007
- if (style.alignSelf === "auto" || !style.alignSelf) node.setAlignSelf(Yoga.ALIGN_AUTO);
2008
- if (style.alignSelf === "flex-start") node.setAlignSelf(Yoga.ALIGN_FLEX_START);
2009
- if (style.alignSelf === "center") node.setAlignSelf(Yoga.ALIGN_CENTER);
2010
- if (style.alignSelf === "flex-end") node.setAlignSelf(Yoga.ALIGN_FLEX_END);
2011
- if (style.alignSelf === "stretch") node.setAlignSelf(Yoga.ALIGN_STRETCH);
2012
- if (style.alignSelf === "baseline") node.setAlignSelf(Yoga.ALIGN_BASELINE);
2013
- }
2014
- if ("alignContent" in style) {
2015
- if (style.alignContent === "flex-start" || !style.alignContent) node.setAlignContent(Yoga.ALIGN_FLEX_START);
2016
- if (style.alignContent === "center") node.setAlignContent(Yoga.ALIGN_CENTER);
2017
- if (style.alignContent === "flex-end") node.setAlignContent(Yoga.ALIGN_FLEX_END);
2018
- if (style.alignContent === "space-between") node.setAlignContent(Yoga.ALIGN_SPACE_BETWEEN);
2019
- if (style.alignContent === "space-around") node.setAlignContent(Yoga.ALIGN_SPACE_AROUND);
2020
- if (style.alignContent === "space-evenly") node.setAlignContent(Yoga.ALIGN_SPACE_EVENLY);
2021
- if (style.alignContent === "stretch") node.setAlignContent(Yoga.ALIGN_STRETCH);
2022
- }
2023
- if ("justifyContent" in style) {
2024
- if (style.justifyContent === "flex-start" || !style.justifyContent) node.setJustifyContent(Yoga.JUSTIFY_FLEX_START);
2025
- if (style.justifyContent === "center") node.setJustifyContent(Yoga.JUSTIFY_CENTER);
2026
- if (style.justifyContent === "flex-end") node.setJustifyContent(Yoga.JUSTIFY_FLEX_END);
2027
- if (style.justifyContent === "space-between") node.setJustifyContent(Yoga.JUSTIFY_SPACE_BETWEEN);
2028
- if (style.justifyContent === "space-around") node.setJustifyContent(Yoga.JUSTIFY_SPACE_AROUND);
2029
- if (style.justifyContent === "space-evenly") node.setJustifyContent(Yoga.JUSTIFY_SPACE_EVENLY);
2030
- }
2031
- };
2032
- const applyDimensionStyles = (node, style) => {
2033
- if ("width" in style) {
2034
- if (typeof style.width === "number") node.setWidth(style.width);
2035
- else if (typeof style.width === "string") node.setWidthPercent(Number.parseInt(style.width, 10));
2036
- else node.setWidthAuto();
2037
- }
2038
- if ("height" in style) {
2039
- if (typeof style.height === "number") node.setHeight(style.height);
2040
- else if (typeof style.height === "string") node.setHeightPercent(Number.parseInt(style.height, 10));
2041
- else node.setHeightAuto();
2042
- }
2043
- if ("minWidth" in style) {
2044
- if (typeof style.minWidth === "string") node.setMinWidthPercent(Number.parseInt(style.minWidth, 10));
2045
- else node.setMinWidth(style.minWidth ?? 0);
2046
- }
2047
- if ("minHeight" in style) {
2048
- if (typeof style.minHeight === "string") node.setMinHeightPercent(Number.parseInt(style.minHeight, 10));
2049
- else node.setMinHeight(style.minHeight ?? 0);
2050
- }
2051
- if ("maxWidth" in style) {
2052
- if (typeof style.maxWidth === "string") node.setMaxWidthPercent(Number.parseInt(style.maxWidth, 10));
2053
- else node.setMaxWidth(style.maxWidth);
2054
- }
2055
- if ("maxHeight" in style) {
2056
- if (typeof style.maxHeight === "string") node.setMaxHeightPercent(Number.parseInt(style.maxHeight, 10));
2057
- else node.setMaxHeight(style.maxHeight);
2058
- }
2059
- if ("aspectRatio" in style) node.setAspectRatio(style.aspectRatio);
2060
- };
2061
- const applyDisplayStyles = (node, style) => {
2062
- if ("display" in style) node.setDisplay(style.display === "flex" ? Yoga.DISPLAY_FLEX : Yoga.DISPLAY_NONE);
2063
- };
2064
- const applyBorderStyles = (node, style, currentStyle) => {
2065
- if (!("borderStyle" in style || "borderTop" in style || "borderBottom" in style || "borderLeft" in style || "borderRight" in style)) return;
2066
- const borderWidth = currentStyle.borderStyle ? 1 : 0;
2067
- node.setBorder(Yoga.EDGE_TOP, currentStyle.borderTop === false ? 0 : borderWidth);
2068
- node.setBorder(Yoga.EDGE_BOTTOM, currentStyle.borderBottom === false ? 0 : borderWidth);
2069
- node.setBorder(Yoga.EDGE_LEFT, currentStyle.borderLeft === false ? 0 : borderWidth);
2070
- node.setBorder(Yoga.EDGE_RIGHT, currentStyle.borderRight === false ? 0 : borderWidth);
2071
- };
2072
- const applyGapStyles = (node, style) => {
2073
- if ("gap" in style) node.setGap(Yoga.GUTTER_ALL, style.gap ?? 0);
2074
- if ("columnGap" in style) node.setGap(Yoga.GUTTER_COLUMN, style.columnGap ?? 0);
2075
- if ("rowGap" in style) node.setGap(Yoga.GUTTER_ROW, style.rowGap ?? 0);
2076
- };
2077
- const styles = (node, style = {}, currentStyle = style) => {
2078
- applyPositionStyles(node, style);
2079
- applyMarginStyles(node, style);
2080
- applyPaddingStyles(node, style);
2081
- applyFlexStyles(node, style);
2082
- applyDimensionStyles(node, style);
2083
- applyDisplayStyles(node, style);
2084
- applyBorderStyles(node, style, currentStyle);
2085
- applyGapStyles(node, style);
2086
- };
2087
- //#endregion
2088
- //#region src/reconciler.ts
2089
- if (process.env["SIGIL_DEV"] === "true") {
2090
- let isDevtoolsInstalled = false;
2091
- try {
2092
- import.meta.resolve("react-devtools-core");
2093
- isDevtoolsInstalled = true;
2094
- } catch {}
2095
- if (isDevtoolsInstalled) await import("./devtools-BhYGjb7h.js");
2096
- }
2097
- const diff = (before, after) => {
2098
- if (before === after) return;
2099
- if (!before) return after;
2100
- const changed = {};
2101
- let isChanged = false;
2102
- for (const key of Object.keys(before)) if (after ? !Object.hasOwn(after, key) : true) {
2103
- changed[key] = void 0;
2104
- isChanged = true;
2105
- }
2106
- if (after) {
2107
- for (const key of Object.keys(after)) if (after[key] !== before[key]) {
2108
- changed[key] = after[key];
2109
- isChanged = true;
2110
- }
2111
- }
2112
- return isChanged ? changed : void 0;
2113
- };
2114
- const findRootNode$1 = (node) => {
2115
- let current = node;
2116
- while (current) {
2117
- if (current.nodeName === "ink-root") return current;
2118
- current = current.parentNode;
2119
- }
2120
- };
2121
- /**
2122
- * Clear the root's cached `staticNode` when the node it points at is being
2123
- * removed as part of a larger subtree.
2124
- *
2125
- * The previous identity check (`staticNode === removeNode`) only caught direct
2126
- * removal of the `<Static>` element. When an *ancestor* of `<Static>` is
2127
- * removed, the stale `staticNode` reference survives and the next render would
2128
- * replay stale static output (and, before `detachYogaSubtree`, trap on detached
2129
- * WASM memory — see QwenLM/qwen-code#6820).
2130
- *
2131
- * The owning root is derived from the host parent passed to the removal hook,
2132
- * not a module-level global, so instances with separate stdout streams don't
2133
- * clobber each other's pointers.
2134
- */
2135
- const clearStaticNodeIfContained = (rootNode, removeNode) => {
2136
- if (!rootNode?.staticNode) return;
2137
- let current = rootNode.staticNode;
2138
- while (current) {
2139
- if (current === removeNode) {
2140
- rootNode.staticNode = void 0;
2141
- return;
2142
- }
2143
- current = current.parentNode;
2144
- }
2145
- };
2146
- let currentUpdatePriority = NoEventPriority;
2147
- async function loadPackageJson() {
2148
- const content = (await import("node:fs")).readFileSync(new URL("../package.json", import.meta.url), "utf8");
2149
- const parsedContent = JSON.parse(content);
2150
- return {
2151
- name: parsedContent?.name,
2152
- version: parsedContent?.version
2153
- };
2154
- }
2155
- let packageInfo = {
2156
- name: "ink",
2157
- version
2158
- };
2159
- if (process.env["DEV"] === "true") try {
2160
- const loaded = await loadPackageJson();
2161
- packageInfo = {
2162
- name: loaded.name || packageInfo.name,
2163
- version: loaded.version || packageInfo.version
2164
- };
2165
- } catch (error) {
2166
- console.warn("Failed to load package.json in development mode. Falling back to default renderer metadata.", error);
2167
- }
2168
- const reconciler = createReconciler({
2169
- getRootHostContext: () => ({ isInsideText: false }),
2170
- prepareForCommit: () => null,
2171
- preparePortalMount: () => null,
2172
- clearContainer: () => false,
2173
- resetAfterCommit(rootNode) {
2174
- if (typeof rootNode.onComputeLayout === "function") rootNode.onComputeLayout();
2175
- emitLayoutListeners(rootNode);
2176
- if (rootNode.staticNode !== rootNode.previousStaticNode) {
2177
- rootNode.previousStaticNode = rootNode.staticNode;
2178
- if (typeof rootNode.onStaticChange === "function") rootNode.onStaticChange();
2179
- }
2180
- if (rootNode.isStaticDirty) {
2181
- rootNode.isStaticDirty = false;
2182
- if (typeof rootNode.onImmediateRender === "function") rootNode.onImmediateRender();
2183
- return;
2184
- }
2185
- if (typeof rootNode.onRender === "function") rootNode.onRender();
2186
- },
2187
- getChildHostContext(parentHostContext, type) {
2188
- const previousIsInsideText = parentHostContext.isInsideText;
2189
- const isInsideText = type === "ink-text" || type === "ink-virtual-text";
2190
- if (previousIsInsideText === isInsideText) return parentHostContext;
2191
- return { isInsideText };
2192
- },
2193
- shouldSetTextContent: () => false,
2194
- createInstance(originalType, newProps, rootNode, hostContext) {
2195
- if (hostContext.isInsideText && originalType === "ink-box") throw new Error(`<Box> can’t be nested inside <Text> component`);
2196
- const type = originalType === "ink-text" && hostContext.isInsideText ? "ink-virtual-text" : originalType;
2197
- const node = createNode(type);
2198
- for (const [key, value] of Object.entries(newProps)) {
2199
- if (key === "children") continue;
2200
- if (key === "style") {
2201
- setStyle(node, value);
2202
- if (node.yogaNode) styles(node.yogaNode, value);
2203
- continue;
2204
- }
2205
- if (key === "internal_transform") {
2206
- node.internal_transform = value;
2207
- continue;
2208
- }
2209
- if (key === "internal_static") {
2210
- node.internal_static = true;
2211
- rootNode.isStaticDirty = true;
2212
- rootNode.staticNode = node;
2213
- continue;
2214
- }
2215
- setAttribute(node, key, value);
2216
- }
2217
- return node;
2218
- },
2219
- createTextInstance(text, _root, hostContext) {
2220
- if (!hostContext.isInsideText) throw new Error(`Text string "${text}" must be rendered inside <Text> component`);
2221
- return createTextNode(text);
2222
- },
2223
- resetTextContent() {},
2224
- hideTextInstance(node) {
2225
- setTextNodeValue(node, "");
2226
- },
2227
- unhideTextInstance(node, text) {
2228
- setTextNodeValue(node, text);
2229
- },
2230
- getPublicInstance: (instance) => instance,
2231
- hideInstance(node) {
2232
- node.yogaNode?.setDisplay(Yoga.DISPLAY_NONE);
2233
- },
2234
- unhideInstance(node) {
2235
- node.yogaNode?.setDisplay(Yoga.DISPLAY_FLEX);
2236
- },
2237
- appendInitialChild: appendChildNode,
2238
- appendChild: appendChildNode,
2239
- insertBefore: insertBeforeNode,
2240
- finalizeInitialChildren() {
2241
- return false;
2242
- },
2243
- isPrimaryRenderer: true,
2244
- supportsMutation: true,
2245
- supportsPersistence: false,
2246
- supportsHydration: false,
2247
- supportsMicrotasks: true,
2248
- scheduleMicrotask: queueMicrotask,
2249
- scheduleCallback: Scheduler.unstable_scheduleCallback,
2250
- cancelCallback: Scheduler.unstable_cancelCallback,
2251
- shouldYield: Scheduler.unstable_shouldYield,
2252
- now: Scheduler.unstable_now,
2253
- scheduleTimeout: setTimeout,
2254
- cancelTimeout: clearTimeout,
2255
- noTimeout: -1,
2256
- beforeActiveInstanceBlur() {},
2257
- afterActiveInstanceBlur() {},
2258
- detachDeletedInstance() {},
2259
- getInstanceFromNode: () => null,
2260
- prepareScopeUpdate() {},
2261
- getInstanceFromScope: () => null,
2262
- appendChildToContainer: appendChildNode,
2263
- insertInContainerBefore: insertBeforeNode,
2264
- removeChildFromContainer(node, removeNode) {
2265
- clearStaticNodeIfContained(findRootNode$1(node), removeNode);
2266
- removeChildNode(node, removeNode);
2267
- detachYogaSubtree(removeNode);
2268
- },
2269
- commitUpdate(node, _type, oldProps, newProps) {
2270
- if (node.internal_static) {
2271
- const rootNode = findRootNode$1(node);
2272
- if (rootNode) rootNode.isStaticDirty = true;
2273
- }
2274
- const props = diff(oldProps, newProps);
2275
- const style = diff(oldProps["style"], newProps["style"]);
2276
- if (!props && !style) return;
2277
- if (props) for (const [key, value] of Object.entries(props)) {
2278
- if (key === "style") {
2279
- setStyle(node, value);
2280
- continue;
2281
- }
2282
- if (key === "internal_transform") {
2283
- node.internal_transform = value;
2284
- continue;
2285
- }
2286
- if (key === "internal_static") {
2287
- node.internal_static = true;
2288
- continue;
2289
- }
2290
- setAttribute(node, key, value);
2291
- }
2292
- if (style && node.yogaNode) styles(node.yogaNode, style, newProps["style"] ?? {});
2293
- },
2294
- commitTextUpdate(node, _oldText, newText) {
2295
- setTextNodeValue(node, newText);
2296
- },
2297
- removeChild(node, removeNode) {
2298
- clearStaticNodeIfContained(findRootNode$1(node), removeNode);
2299
- removeChildNode(node, removeNode);
2300
- detachYogaSubtree(removeNode);
2301
- },
2302
- setCurrentUpdatePriority(newPriority) {
2303
- currentUpdatePriority = newPriority;
2304
- },
2305
- getCurrentUpdatePriority: () => currentUpdatePriority,
2306
- resolveUpdatePriority() {
2307
- if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
2308
- return DefaultEventPriority;
2309
- },
2310
- maySuspendCommit() {
951
+ "table",
952
+ "time",
953
+ "timeEnd",
954
+ "timeLog",
955
+ "trace",
956
+ "warn"
957
+ ];
958
+ const patchConsole = (callback) => {
959
+ const stdout = new PassThrough();
960
+ const stderr = new PassThrough();
961
+ stdout.write = (data) => {
962
+ callback("stdout", String(data));
2311
963
  return true;
2312
- },
2313
- NotPendingTransition: void 0,
2314
- HostTransitionContext: createContext(null),
2315
- resetFormInstance() {},
2316
- requestPostPaintCallback() {},
2317
- shouldAttemptEagerTransition() {
2318
- return false;
2319
- },
2320
- trackSchedulerEvent() {},
2321
- resolveEventType() {
2322
- return null;
2323
- },
2324
- resolveEventTimeStamp() {
2325
- return -1.1;
2326
- },
2327
- preloadInstance() {
964
+ };
965
+ stderr.write = (data) => {
966
+ callback("stderr", String(data));
2328
967
  return true;
2329
- },
2330
- startSuspendingCommit() {},
2331
- suspendInstance() {},
2332
- waitForCommitToBeReady() {
2333
- return null;
2334
- },
2335
- rendererPackageName: packageInfo.name,
2336
- rendererVersion: packageInfo.version
2337
- });
2338
- //#endregion
2339
- //#region src/output.ts
2340
- var OutputCaches = class {
2341
- widths = /* @__PURE__ */ new Map();
2342
- blockWidths = /* @__PURE__ */ new Map();
2343
- styledChars = /* @__PURE__ */ new Map();
2344
- getStyledChars(line) {
2345
- let cached = this.styledChars.get(line);
2346
- if (cached === void 0) {
2347
- cached = styledCharsFromTokens(tokenize(line));
2348
- this.styledChars.set(line, cached);
2349
- }
2350
- return cached;
2351
- }
2352
- getStringWidth(text) {
2353
- let cached = this.widths.get(text);
2354
- if (cached === void 0) {
2355
- cached = stringWidth(text);
2356
- this.widths.set(text, cached);
2357
- }
2358
- return cached;
2359
- }
2360
- getWidestLine(text) {
2361
- let cached = this.blockWidths.get(text);
2362
- if (cached === void 0) {
2363
- let lineWidth = 0;
2364
- for (const line of text.split("\n")) lineWidth = Math.max(lineWidth, this.getStringWidth(line));
2365
- cached = lineWidth;
2366
- this.blockWidths.set(text, cached);
2367
- }
2368
- return cached;
968
+ };
969
+ const internalConsole = new Console(stdout, stderr);
970
+ const originalMethods = /* @__PURE__ */ new Map();
971
+ for (const method of consoleMethods) {
972
+ originalMethods.set(method, console[method]);
973
+ console[method] = internalConsole[method];
2369
974
  }
975
+ return () => {
976
+ for (const method of consoleMethods) console[method] = originalMethods.get(method);
977
+ originalMethods.clear();
978
+ };
2370
979
  };
2371
- var Output = class {
2372
- width;
2373
- height;
2374
- operations = [];
2375
- caches = new OutputCaches();
2376
- constructor(options) {
2377
- const { width, height } = options;
2378
- this.width = width;
2379
- this.height = height;
2380
- }
2381
- write(x, y, text, options) {
2382
- const { transformers } = options;
2383
- if (!text) return;
2384
- this.operations.push({
2385
- type: "write",
2386
- x,
2387
- y,
2388
- text,
2389
- transformers
2390
- });
2391
- }
2392
- clip(clip) {
2393
- this.operations.push({
2394
- type: "clip",
2395
- clip
2396
- });
2397
- }
2398
- unclip() {
2399
- this.operations.push({ type: "unclip" });
2400
- }
2401
- get() {
2402
- const output = [];
2403
- for (let y = 0; y < this.height; y++) {
2404
- const row = [];
2405
- for (let x = 0; x < this.width; x++) row.push({
2406
- type: "char",
2407
- value: " ",
2408
- fullWidth: false,
2409
- styles: []
2410
- });
2411
- output.push(row);
2412
- }
2413
- const clips = [];
2414
- for (const operation of this.operations) {
2415
- if (operation.type === "clip") clips.push(operation.clip);
2416
- if (operation.type === "unclip") clips.pop();
2417
- if (operation.type === "write") {
2418
- const { text, transformers } = operation;
2419
- let { x, y } = operation;
2420
- let lines = text.split("\n");
2421
- const clip = clips.at(-1);
2422
- if (clip) {
2423
- const clipHorizontally = typeof clip?.x1 === "number" && typeof clip?.x2 === "number";
2424
- const clipVertically = typeof clip?.y1 === "number" && typeof clip?.y2 === "number";
2425
- if (clipHorizontally) {
2426
- const width = this.caches.getWidestLine(text);
2427
- if (x + width < clip.x1 || x > clip.x2) continue;
2428
- }
2429
- if (clipVertically) {
2430
- const height = lines.length;
2431
- if (y + height < clip.y1 || y > clip.y2) continue;
2432
- }
2433
- if (clipHorizontally) {
2434
- lines = lines.map((line) => {
2435
- const from = x < clip.x1 ? clip.x1 - x : 0;
2436
- const width = this.caches.getStringWidth(line);
2437
- const to = x + width > clip.x2 ? clip.x2 - x : width;
2438
- return sliceAnsi(line, from, to);
2439
- });
2440
- if (x < clip.x1) x = clip.x1;
2441
- }
2442
- if (clipVertically) {
2443
- const from = y < clip.y1 ? clip.y1 - y : 0;
2444
- const height = lines.length;
2445
- const to = y + height > clip.y2 ? clip.y2 - y : height;
2446
- lines = lines.slice(from, to);
2447
- if (y < clip.y1) y = clip.y1;
2448
- }
2449
- }
2450
- let offsetY = 0;
2451
- for (let [index, line] of lines.entries()) {
2452
- const currentLine = output[y + offsetY];
2453
- if (!currentLine) continue;
2454
- for (const transformer of transformers) line = transformer(line, index);
2455
- const characters = this.caches.getStyledChars(line);
2456
- let offsetX = x;
2457
- if (characters.length === 0) {
2458
- offsetY++;
2459
- continue;
2460
- }
2461
- const spaceCell = {
2462
- type: "char",
2463
- value: " ",
2464
- fullWidth: false,
2465
- styles: []
2466
- };
2467
- if (currentLine[offsetX]?.value === "" && offsetX > 0 && this.caches.getStringWidth(currentLine[offsetX - 1]?.value ?? "") > 1) currentLine[offsetX - 1] = spaceCell;
2468
- for (const character of characters) {
2469
- currentLine[offsetX] = character;
2470
- const characterWidth = Math.max(1, this.caches.getStringWidth(character.value));
2471
- if (characterWidth > 1) for (let columnOffset = 1; columnOffset < characterWidth; columnOffset++) currentLine[offsetX + columnOffset] = {
2472
- type: "char",
2473
- value: "",
2474
- fullWidth: false,
2475
- styles: character.styles
2476
- };
2477
- offsetX += characterWidth;
2478
- }
2479
- if (currentLine[offsetX]?.value === "") currentLine[offsetX] = spaceCell;
2480
- offsetY++;
2481
- }
2482
- }
2483
- }
2484
- return {
2485
- output: output.map((line) => {
2486
- const lineWithoutEmptyItems = line.filter((item) => item !== void 0);
2487
- return styledCharsToString(lineWithoutEmptyItems).trimEnd();
2488
- }).join("\n"),
2489
- height: output.length
2490
- };
2491
- }
980
+ const patchStreamWrite = (stream, onData) => {
981
+ const originalWrite = stream.write;
982
+ const patchedWrite = (chunk, encodingOrCallback, callback) => {
983
+ onData(typeof chunk === "string" ? chunk : chunk instanceof Uint8Array ? Buffer.from(chunk).toString() : String(chunk));
984
+ (typeof encodingOrCallback === "function" ? encodingOrCallback : typeof callback === "function" ? callback : void 0)?.();
985
+ return true;
986
+ };
987
+ stream.write = patchedWrite;
988
+ return () => {
989
+ stream.write = originalWrite;
990
+ };
2492
991
  };
2493
992
  //#endregion
2494
993
  //#region src/get-max-width.ts
@@ -2496,17 +995,9 @@ const getMaxWidth = (yogaNode) => {
2496
995
  return yogaNode.getComputedWidth() - yogaNode.getComputedPadding(Yoga.EDGE_LEFT) - yogaNode.getComputedPadding(Yoga.EDGE_RIGHT) - yogaNode.getComputedBorder(Yoga.EDGE_LEFT) - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT);
2497
996
  };
2498
997
  //#endregion
2499
- //#region src/indent-string.ts
2500
- const indentString = (string, count = 1, options = {}) => {
2501
- const { indent = " ", includeEmptyLines = false } = options;
2502
- if (count === 0) return string;
2503
- const regex = includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
2504
- return string.replace(regex, indent.repeat(count));
2505
- };
2506
- //#endregion
2507
998
  //#region src/render-background.ts
2508
999
  const renderBackground = (x, y, node, output) => {
2509
- if (!node.style.backgroundColor) return;
1000
+ if (node.style.backgroundColor === void 0) return;
2510
1001
  const width = node.yogaNode.getComputedWidth();
2511
1002
  const height = node.yogaNode.getComputedHeight();
2512
1003
  const leftBorderWidth = node.style.borderStyle && node.style.borderLeft !== false ? 1 : 0;
@@ -2516,106 +1007,51 @@ const renderBackground = (x, y, node, output) => {
2516
1007
  const contentWidth = width - leftBorderWidth - rightBorderWidth;
2517
1008
  const contentHeight = height - topBorderHeight - bottomBorderHeight;
2518
1009
  if (!(contentWidth > 0 && contentHeight > 0)) return;
2519
- const backgroundLine = colorize(" ".repeat(contentWidth), node.style.backgroundColor, "background");
2520
- for (let row = 0; row < contentHeight; row++) output.write(x + leftBorderWidth, y + topBorderHeight + row, backgroundLine, { transformers: [] });
2521
- };
2522
- //#endregion
2523
- //#region src/boxes.ts
2524
- const boxes = {
2525
- single: {
2526
- topLeft: "",
2527
- top: "─",
2528
- topRight: "",
2529
- right: "│",
2530
- bottomRight: "",
2531
- bottom: "─",
2532
- bottomLeft: "└",
2533
- left: "│"
2534
- },
2535
- double: {
2536
- topLeft: "╔",
2537
- top: "═",
2538
- topRight: "╗",
2539
- right: "║",
2540
- bottomRight: "╝",
2541
- bottom: "═",
2542
- bottomLeft: "╚",
2543
- left: "║"
2544
- },
2545
- round: {
2546
- topLeft: "╭",
2547
- top: "─",
2548
- topRight: "╮",
2549
- right: "│",
2550
- bottomRight: "╯",
2551
- bottom: "─",
2552
- bottomLeft: "╰",
2553
- left: "│"
2554
- },
2555
- bold: {
2556
- topLeft: "┏",
2557
- top: "━",
2558
- topRight: "┓",
2559
- right: "┃",
2560
- bottomRight: "┛",
2561
- bottom: "━",
2562
- bottomLeft: "┗",
2563
- left: "┃"
2564
- },
2565
- singleDouble: {
2566
- topLeft: "╓",
2567
- top: "─",
2568
- topRight: "╖",
2569
- right: "║",
2570
- bottomRight: "╜",
2571
- bottom: "─",
2572
- bottomLeft: "╙",
2573
- left: "║"
2574
- },
2575
- doubleSingle: {
2576
- topLeft: "╒",
2577
- top: "═",
2578
- topRight: "╕",
2579
- right: "│",
2580
- bottomRight: "╛",
2581
- bottom: "═",
2582
- bottomLeft: "╘",
2583
- left: "│"
2584
- },
2585
- classic: {
2586
- topLeft: "+",
2587
- top: "-",
2588
- topRight: "+",
2589
- right: "|",
2590
- bottomRight: "+",
2591
- bottom: "-",
2592
- bottomLeft: "+",
2593
- left: "|"
2594
- },
2595
- arrow: {
2596
- topLeft: "↘",
2597
- top: "↓",
2598
- topRight: "↙",
2599
- right: "←",
2600
- bottomRight: "↖",
2601
- bottom: "↑",
2602
- bottomLeft: "↗",
2603
- left: "→"
2604
- }
1010
+ const bounds = {
1011
+ x: x + leftBorderWidth,
1012
+ y: y + topBorderHeight,
1013
+ width: contentWidth,
1014
+ height: contentHeight
1015
+ };
1016
+ const lines = Array.from({ length: contentHeight }, (_unused, row) => Array.from({ length: contentWidth }, (_empty, column) => {
1017
+ const resetBackground = node.style.backgroundColor === "";
1018
+ const background = resetBackground ? void 0 : samplePaint(node.style.backgroundColor, bounds.x + column, bounds.y + row, bounds, output.paintContext);
1019
+ return createCell(" ", 1, {
1020
+ ...background ? { background } : {},
1021
+ underline: "none",
1022
+ attributes: cellAttributes.none
1023
+ }, void 0, resetBackground ? { background: true } : void 0);
1024
+ }));
1025
+ output.writeCells(bounds.x, bounds.y, lines);
2605
1026
  };
2606
1027
  //#endregion
2607
1028
  //#region src/render-border.ts
2608
- const stylePiece = (segment, fg, bg, dim) => {
2609
- let styled = colorize(segment, fg, "foreground");
2610
- styled = colorize(styled, bg, "background");
2611
- if (dim) styled = chalk.dim(styled);
2612
- return styled;
1029
+ const stylePiece = (segment, x, y, bounds, context, fg, bg, dim) => {
1030
+ let column = x;
1031
+ return [...graphemes(segment)].map((grapheme) => {
1032
+ const foreground = fg ? samplePaint(fg, column, y, bounds, context) : void 0;
1033
+ const background = bg ? samplePaint(bg, column, y, bounds, context) : void 0;
1034
+ const width = Math.max(1, stringWidth(grapheme));
1035
+ column += width;
1036
+ return createCell(grapheme, width, {
1037
+ ...foreground ? { foreground } : {},
1038
+ ...background ? { background } : {},
1039
+ underline: "none",
1040
+ attributes: dim ? cellAttributes.faint : cellAttributes.none
1041
+ });
1042
+ });
2613
1043
  };
2614
1044
  const renderBorder = (x, y, node, output) => {
2615
1045
  if (node.style.borderStyle) {
2616
1046
  const width = node.yogaNode.getComputedWidth();
2617
1047
  const height = node.yogaNode.getComputedHeight();
2618
- const box = typeof node.style.borderStyle === "string" ? boxes[node.style.borderStyle] : node.style.borderStyle;
1048
+ const bounds = {
1049
+ x,
1050
+ y,
1051
+ width,
1052
+ height
1053
+ };
1054
+ const box = typeof node.style.borderStyle === "string" ? BOXES[node.style.borderStyle] : node.style.borderStyle;
2619
1055
  const topBorderColor = node.style.borderTopColor ?? node.style.borderColor;
2620
1056
  const bottomBorderColor = node.style.borderBottomColor ?? node.style.borderColor;
2621
1057
  const leftBorderColor = node.style.borderLeftColor ?? node.style.borderColor;
@@ -2633,36 +1069,200 @@ const renderBorder = (x, y, node, output) => {
2633
1069
  const showLeftBorder = node.style.borderLeft !== false;
2634
1070
  const showRightBorder = node.style.borderRight !== false;
2635
1071
  const contentWidth = width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0);
2636
- let topBorder = showTopBorder ? (showLeftBorder ? box.topLeft : "") + box.top.repeat(contentWidth) + (showRightBorder ? box.topRight : "") : void 0;
2637
- topBorder &&= stylePiece(topBorder, topBorderColor, topBorderBackgroundColor, dimTopBorderColor);
1072
+ const topBorder = showTopBorder ? (showLeftBorder ? box.topLeft : "") + box.top.repeat(contentWidth) + (showRightBorder ? box.topRight : "") : void 0;
2638
1073
  let verticalBorderHeight = height;
2639
1074
  if (showTopBorder) verticalBorderHeight -= 1;
2640
1075
  if (showBottomBorder) verticalBorderHeight -= 1;
2641
- let leftBorder = "";
2642
- if (showLeftBorder) leftBorder = (stylePiece(box.left, leftBorderColor, leftBorderBackgroundColor, dimLeftBorderColor) + "\n").repeat(verticalBorderHeight);
2643
- let rightBorder = "";
2644
- if (showRightBorder) rightBorder = (stylePiece(box.right, rightBorderColor, rightBorderBackgroundColor, dimRightBorderColor) + "\n").repeat(verticalBorderHeight);
2645
- let bottomBorder = showBottomBorder ? (showLeftBorder ? box.bottomLeft : "") + box.bottom.repeat(contentWidth) + (showRightBorder ? box.bottomRight : "") : void 0;
2646
- bottomBorder &&= stylePiece(bottomBorder, bottomBorderColor, bottomBorderBackgroundColor, dimBottomBorderColor);
2647
1076
  const offsetY = showTopBorder ? 1 : 0;
2648
- if (topBorder) output.write(x, y, topBorder, { transformers: [] });
2649
- if (leftBorder) output.write(x, y + offsetY, leftBorder, { transformers: [] });
2650
- if (rightBorder) output.write(x + width - 1, y + offsetY, rightBorder, { transformers: [] });
2651
- if (bottomBorder) output.write(x, y + height - 1, bottomBorder, { transformers: [] });
1077
+ const bottomBorder = showBottomBorder ? (showLeftBorder ? box.bottomLeft : "") + box.bottom.repeat(contentWidth) + (showRightBorder ? box.bottomRight : "") : void 0;
1078
+ if (topBorder) output.writeCells(x, y, [stylePiece(topBorder, x, y, bounds, output.paintContext, topBorderColor, topBorderBackgroundColor, dimTopBorderColor)]);
1079
+ 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)));
1080
+ 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)));
1081
+ if (bottomBorder) output.writeCells(x, y + height - 1, [stylePiece(bottomBorder, x, y + height - 1, bounds, output.paintContext, bottomBorderColor, bottomBorderBackgroundColor, dimBottomBorderColor)]);
2652
1082
  }
2653
1083
  };
2654
1084
  //#endregion
2655
- //#region src/render-node-to-output.ts
2656
- const applyPaddingToText = (node, text) => {
2657
- const yogaNode = node.childNodes[0]?.yogaNode;
2658
- if (yogaNode) {
2659
- const offsetX = yogaNode.getComputedLeft();
2660
- const offsetY = yogaNode.getComputedTop();
2661
- text = "\n".repeat(offsetY) + indentString(text, offsetX);
1085
+ //#region src/structured-text.ts
1086
+ /** Whether a text subtree must retain the ANSI compatibility pipeline. */
1087
+ function hasCompatibilityText(node) {
1088
+ if (node.internal_transform && !node.internal_textStyle) return true;
1089
+ return node.childNodes.some((child) => child.nodeName !== "#text" && hasCompatibilityText(child));
1090
+ }
1091
+ /** Rasterizes an unwrapped native Text subtree into semantic cell lines. */
1092
+ function structuredTextLines(node) {
1093
+ const lines = [[]];
1094
+ appendNode(node, emptySemanticTextStyle, lines, false);
1095
+ return lines;
1096
+ }
1097
+ /** Samples semantic paints only after wrapping and final layout are known. */
1098
+ function sampleStructuredText(lines, bounds, context) {
1099
+ return lines.map((line, row) => {
1100
+ let column = 0;
1101
+ return line.map((cell) => {
1102
+ const foreground = cell.foregroundPaint ? samplePaint(cell.foregroundPaint, bounds.x + column, bounds.y + row, bounds, context) : cell.style.foreground;
1103
+ const background = cell.backgroundPaint ? samplePaint(cell.backgroundPaint, bounds.x + column, bounds.y + row, bounds, context) : cell.style.background;
1104
+ column += cell.width;
1105
+ return createCell(cell.grapheme, cell.width, {
1106
+ ...cell.style,
1107
+ ...foreground ? { foreground } : {},
1108
+ ...background ? { background } : {}
1109
+ }, cell.hyperlink, cell.reset);
1110
+ });
1111
+ });
1112
+ }
1113
+ function structuredTextBaseStyle(node) {
1114
+ return semanticTextStyleToCellStyle(mergeSemanticTextStyles(emptySemanticTextStyle, node.internal_textStyle));
1115
+ }
1116
+ /** Wraps or truncates semantic cells without converting them through ANSI. */
1117
+ function wrapStructuredText(input, maxWidth, wrap, baseStyle) {
1118
+ if (maxWidth < 1) return [[]];
1119
+ return input.flatMap((line) => {
1120
+ if (lineWidth(line) <= maxWidth) return [[...line]];
1121
+ if (wrap === "hard") return hardWrap(line, maxWidth);
1122
+ if (wrap === "wrap") return wordWrap(line, maxWidth);
1123
+ return [truncate(line, maxWidth, wrap, baseStyle)];
1124
+ });
1125
+ }
1126
+ function wordWrap(line, maxWidth) {
1127
+ const words = [[]];
1128
+ const spaces = [];
1129
+ for (const cell of line) if (cell.grapheme === " ") {
1130
+ spaces.push(cell);
1131
+ words.push([]);
1132
+ } else words.at(-1).push(cell);
1133
+ const output = [[]];
1134
+ appendHard(output, words[0], maxWidth);
1135
+ for (let index = 1; index < words.length; index++) {
1136
+ appendHard(output, [spaces[index - 1]], maxWidth);
1137
+ const word = words[index];
1138
+ const current = output.at(-1);
1139
+ const wordWidth = lineWidth(word);
1140
+ const currentWidth = lineWidth(current);
1141
+ if (wordWidth <= maxWidth && currentWidth + wordWidth > maxWidth) output.push([]);
1142
+ else if (wordWidth > maxWidth && currentWidth > 0) {
1143
+ const remaining = maxWidth - currentWidth;
1144
+ const breaksHere = 1 + Math.floor((wordWidth - remaining - 1) / maxWidth);
1145
+ if (Math.floor((wordWidth - 1) / maxWidth) < breaksHere) output.push([]);
1146
+ }
1147
+ appendHard(output, word, maxWidth);
2662
1148
  }
2663
- return text;
2664
- };
2665
- const renderNodeToScreenReaderOutput = (node, options = {}) => {
1149
+ return output;
1150
+ }
1151
+ function hardWrap(line, maxWidth) {
1152
+ const output = [[]];
1153
+ appendHard(output, line, maxWidth);
1154
+ return output;
1155
+ }
1156
+ function appendHard(output, cells, maxWidth) {
1157
+ for (const cell of cells) {
1158
+ const current = output.at(-1);
1159
+ if (current.length > 0 && lineWidth(current) + cell.width > maxWidth) output.push([]);
1160
+ output.at(-1).push(cell);
1161
+ }
1162
+ }
1163
+ function truncate(line, maxWidth, wrap, baseStyle) {
1164
+ if (maxWidth === 1) return [ellipsis(baseStyle ?? cellStyleIntersection(line.at(0), line.at(-1)))];
1165
+ if (wrap === "truncate-start") {
1166
+ const right = sliceCells(line, lineWidth(line) - maxWidth + 1, lineWidth(line));
1167
+ return [ellipsis(right[0]?.style), ...right];
1168
+ }
1169
+ if (wrap === "truncate-middle") {
1170
+ const half = Math.min(Math.floor(maxWidth / 2), maxWidth - 1);
1171
+ const left = sliceCells(line, 0, half);
1172
+ const rightWidth = maxWidth - half - 1;
1173
+ const right = sliceCells(line, lineWidth(line) - rightWidth, lineWidth(line));
1174
+ return [
1175
+ ...left,
1176
+ ellipsis(baseStyle ?? cellStyleIntersection(left.at(-1), right.at(0))),
1177
+ ...right
1178
+ ];
1179
+ }
1180
+ const left = sliceCells(line, 0, maxWidth - 1);
1181
+ return [...left, ellipsis(left.at(-1)?.style)];
1182
+ }
1183
+ function sliceCells(line, start, end) {
1184
+ const output = [];
1185
+ let column = 0;
1186
+ for (const cell of line) {
1187
+ const nextColumn = column + cell.width;
1188
+ if (column >= start && nextColumn <= end) output.push(cell);
1189
+ column = nextColumn;
1190
+ }
1191
+ return output;
1192
+ }
1193
+ function ellipsis(style = semanticTextStyleToCellStyle(emptySemanticTextStyle)) {
1194
+ return createCell("…", 1, style);
1195
+ }
1196
+ function cellStyleIntersection(left, right) {
1197
+ if (!left) return right?.style ?? semanticTextStyleToCellStyle(emptySemanticTextStyle);
1198
+ if (!right) return left.style;
1199
+ return {
1200
+ ...sameColor(left.style.foreground, right.style.foreground) ? { foreground: left.style.foreground } : {},
1201
+ ...sameColor(left.style.background, right.style.background) ? { background: left.style.background } : {},
1202
+ ...sameColor(left.style.underlineColor, right.style.underlineColor) ? { underlineColor: left.style.underlineColor } : {},
1203
+ underline: left.style.underline === right.style.underline ? left.style.underline : "none",
1204
+ attributes: left.style.attributes & right.style.attributes
1205
+ };
1206
+ }
1207
+ function sameColor(left, right) {
1208
+ return JSON.stringify(left) === JSON.stringify(right);
1209
+ }
1210
+ function lineWidth(line) {
1211
+ return line.reduce((width, cell) => width + cell.width, 0);
1212
+ }
1213
+ function appendNode(node, inherited, lines, ansi) {
1214
+ if (node.nodeName === "#text") {
1215
+ if (ansi) appendAnsiText(node.nodeValue, inherited, lines);
1216
+ else appendText(node.nodeValue, inherited, lines);
1217
+ return;
1218
+ }
1219
+ const style = mergeSemanticTextStyles(inherited, node.internal_textStyle);
1220
+ for (const child of node.childNodes) appendNode(child, style, lines, ansi || node.internal_ansi === true);
1221
+ }
1222
+ function appendAnsiText(text, semantic, lines) {
1223
+ const base = semanticTextStyleToCellStyle(semantic);
1224
+ for (const cell of cellsFromAnsi(text)) {
1225
+ if (cell.grapheme === "\n" || cell.grapheme === "\r\n") {
1226
+ lines.push([]);
1227
+ continue;
1228
+ }
1229
+ const hasForeground = cell.style.foreground !== void 0;
1230
+ const hasBackground = cell.style.background !== void 0;
1231
+ lines.at(-1).push({
1232
+ ...cell,
1233
+ style: {
1234
+ ...cell.style.foreground ?? base.foreground ? { foreground: cell.style.foreground ?? base.foreground } : {},
1235
+ ...cell.style.background ?? base.background ? { background: cell.style.background ?? base.background } : {},
1236
+ ...cell.style.underlineColor ? { underlineColor: cell.style.underlineColor } : {},
1237
+ underline: cell.style.underline === "none" ? base.underline : cell.style.underline,
1238
+ attributes: base.attributes | cell.style.attributes
1239
+ },
1240
+ ...!hasForeground && semantic.foreground ? { foregroundPaint: semantic.foreground } : {},
1241
+ ...!hasBackground && semantic.background ? { backgroundPaint: semantic.background } : {}
1242
+ });
1243
+ }
1244
+ }
1245
+ function appendText(text, style, lines) {
1246
+ const cellStyle = semanticTextStyleToCellStyle(style);
1247
+ const plainText = tokenizeAnsi(text).filter((token) => token.type === "text").map((token) => token.value).join("");
1248
+ for (const segment of graphemes(stripAnsi(plainText))) {
1249
+ if (segment === "\n" || segment === "\r\n") {
1250
+ lines.push([]);
1251
+ continue;
1252
+ }
1253
+ lines.at(-1).push({
1254
+ ...createCell(segment, Math.max(1, stringWidth(segment)), cellStyle, void 0, style.resetForeground || style.resetBackground ? {
1255
+ foreground: style.resetForeground,
1256
+ background: style.resetBackground
1257
+ } : void 0),
1258
+ ...style.foreground ? { foregroundPaint: style.foreground } : {},
1259
+ ...style.background ? { backgroundPaint: style.background } : {}
1260
+ });
1261
+ }
1262
+ }
1263
+ //#endregion
1264
+ //#region src/paint-tree.ts
1265
+ const renderAccessibleText = (node, options = {}) => {
2666
1266
  if (options.skipStaticElements && node.internal_static) return "";
2667
1267
  if (node.yogaNode?.getDisplay() === Yoga.DISPLAY_NONE) return "";
2668
1268
  let output = "";
@@ -2670,7 +1270,7 @@ const renderNodeToScreenReaderOutput = (node, options = {}) => {
2670
1270
  else if (node.nodeName === "ink-box" || node.nodeName === "ink-root") {
2671
1271
  const separator = node.style.flexDirection === "row" || node.style.flexDirection === "row-reverse" ? " " : "\n";
2672
1272
  output = (node.style.flexDirection === "row-reverse" || node.style.flexDirection === "column-reverse" ? [...node.childNodes].reverse() : [...node.childNodes]).map((childNode) => {
2673
- return renderNodeToScreenReaderOutput(childNode, {
1273
+ return renderAccessibleText(childNode, {
2674
1274
  parentRole: node.internal_accessibility?.role,
2675
1275
  skipStaticElements: options.skipStaticElements
2676
1276
  });
@@ -2686,7 +1286,7 @@ const renderNodeToScreenReaderOutput = (node, options = {}) => {
2686
1286
  }
2687
1287
  return output;
2688
1288
  };
2689
- const renderNodeToOutput = (node, output, options) => {
1289
+ const paintTree = (node, output, options) => {
2690
1290
  const { offsetX = 0, offsetY = 0, transformers = [], skipStaticElements } = options;
2691
1291
  if (skipStaticElements && node.internal_static) return;
2692
1292
  const { yogaNode } = node;
@@ -2694,19 +1294,28 @@ const renderNodeToOutput = (node, output, options) => {
2694
1294
  if (yogaNode.getDisplay() === Yoga.DISPLAY_NONE) return;
2695
1295
  const x = offsetX + yogaNode.getComputedLeft();
2696
1296
  const y = offsetY + yogaNode.getComputedTop();
2697
- let newTransformers = transformers;
2698
- if (typeof node.internal_transform === "function") newTransformers = [node.internal_transform, ...transformers];
1297
+ const newTransformers = node.internal_transform ? [node.internal_transform, ...transformers] : transformers;
2699
1298
  if (node.nodeName === "ink-text") {
2700
- let text = squashTextNodes(node);
2701
- if (text.length > 0) {
2702
- const currentWidth = widestLine(text);
1299
+ if (squashTextNodes(node).length > 0) {
2703
1300
  const maxWidth = getMaxWidth(yogaNode);
2704
- if (currentWidth > maxWidth) {
2705
- const textWrap = node.style.textWrap ?? "wrap";
2706
- text = wrapText(text, maxWidth, textWrap);
2707
- }
2708
- text = applyPaddingToText(node, text);
2709
- output.write(x, y, text, { transformers: newTransformers });
1301
+ const firstChildYoga = node.childNodes[0]?.yogaNode;
1302
+ const paddingX = firstChildYoga?.getComputedLeft() ?? 0;
1303
+ const paddingY = firstChildYoga?.getComputedTop() ?? 0;
1304
+ const lines = wrapStructuredText(structuredTextLines(node), maxWidth, node.style.textWrap ?? "wrap", structuredTextBaseStyle(node));
1305
+ const paintBounds = {
1306
+ x: x + paddingX,
1307
+ y: y + paddingY,
1308
+ width: Math.max(1, ...lines.map((line) => line.reduce((width, cell) => width + cell.width, 0))),
1309
+ height: lines.length
1310
+ };
1311
+ const sampled = sampleStructuredText(lines, paintBounds, output.paintContext);
1312
+ if (hasCompatibilityText(node)) {
1313
+ const textTransformers = collectTransformers(node, transformers);
1314
+ output.writeAnsi(paintBounds.x, paintBounds.y, sampled.map((line) => serializeLine(line, {
1315
+ colorProfile: output.paintContext.profile ?? "truecolor",
1316
+ trimEnd: false
1317
+ })).join("\n"), { transformers: textTransformers });
1318
+ } else output.writeCells(paintBounds.x, paintBounds.y, sampled);
2710
1319
  }
2711
1320
  return;
2712
1321
  }
@@ -2731,7 +1340,7 @@ const renderNodeToOutput = (node, output, options) => {
2731
1340
  }
2732
1341
  }
2733
1342
  if (node.nodeName === "ink-root" || node.nodeName === "ink-box") {
2734
- for (const childNode of node.childNodes) renderNodeToOutput(childNode, output, {
1343
+ for (const childNode of node.childNodes) paintTree(childNode, output, {
2735
1344
  offsetX: x,
2736
1345
  offsetY: y,
2737
1346
  transformers: newTransformers,
@@ -2741,47 +1350,235 @@ const renderNodeToOutput = (node, output, options) => {
2741
1350
  }
2742
1351
  }
2743
1352
  };
1353
+ function collectTransformers(node, inherited) {
1354
+ return [
1355
+ ...node.childNodes.flatMap((child) => child.nodeName === "#text" ? [] : collectTransformers(child, [])),
1356
+ ...node.internal_transform ? [node.internal_transform] : [],
1357
+ ...inherited
1358
+ ];
1359
+ }
1360
+ //#endregion
1361
+ //#region src/screen/canvas.ts
1362
+ /** Immediate structured drawing canvas with one explicit ANSI compatibility adapter. */
1363
+ var Canvas = class {
1364
+ width;
1365
+ height;
1366
+ paintContext;
1367
+ #screen;
1368
+ #clips = [];
1369
+ constructor(options) {
1370
+ this.width = options.width;
1371
+ this.height = options.height;
1372
+ this.paintContext = {
1373
+ profile: options.colorProfile ?? "truecolor",
1374
+ ...options.paintContext
1375
+ };
1376
+ this.#screen = new Screen(this.width, this.height);
1377
+ }
1378
+ writeCells(x, y, lines) {
1379
+ const clip = this.#clips.at(-1);
1380
+ for (const [rowOffset, line] of lines.entries()) {
1381
+ const currentY = y + rowOffset;
1382
+ if (!this.#insideY(currentY, clip)) continue;
1383
+ let currentX = x;
1384
+ for (const cell of line) {
1385
+ const endX = currentX + cell.width;
1386
+ if (!(clip?.x1 !== void 0 && currentX < clip.x1 || clip?.x2 !== void 0 && endX > clip.x2)) this.#composeNativeCell(currentX, currentY, cell);
1387
+ currentX = endX;
1388
+ }
1389
+ }
1390
+ }
1391
+ writeAnsi(x, y, text, options) {
1392
+ if (!text) return;
1393
+ let lines = text.split("\n");
1394
+ const clip = this.#clips.at(-1);
1395
+ if (clip?.y1 !== void 0 && y < clip.y1) {
1396
+ lines = lines.slice(clip.y1 - y);
1397
+ y = clip.y1;
1398
+ }
1399
+ if (clip?.y2 !== void 0) lines = lines.slice(0, Math.max(0, clip.y2 - y));
1400
+ for (const [lineIndex, original] of lines.entries()) {
1401
+ const currentY = y + lineIndex;
1402
+ if (!this.#insideY(currentY, clip)) continue;
1403
+ let line = original;
1404
+ let currentX = x;
1405
+ if (clip?.x1 !== void 0 && currentX < clip.x1) {
1406
+ line = sliceAnsi(line, clip.x1 - currentX);
1407
+ currentX = clip.x1;
1408
+ }
1409
+ if (clip?.x2 !== void 0) line = sliceAnsi(line, 0, Math.max(0, clip.x2 - currentX));
1410
+ for (const character of transformAnsiLine(line, lineIndex, options.transformers)) {
1411
+ const width = Math.max(1, stringWidth(character.value));
1412
+ if (clip?.x2 !== void 0 && currentX + width > clip.x2) break;
1413
+ this.#writeCompatibilityCell(currentX, currentY, character, width);
1414
+ currentX += width;
1415
+ }
1416
+ }
1417
+ }
1418
+ clip(clip) {
1419
+ this.#clips.push(clip);
1420
+ }
1421
+ unclip() {
1422
+ this.#clips.pop();
1423
+ }
1424
+ finish() {
1425
+ return this.#screen;
1426
+ }
1427
+ #insideY(y, clip) {
1428
+ return y >= 0 && y < this.#screen.height && (clip?.y1 === void 0 || y >= clip.y1) && (clip?.y2 === void 0 || y < clip.y2);
1429
+ }
1430
+ #composeNativeCell(x, y, cell) {
1431
+ if (x >= this.#screen.width) return;
1432
+ this.#screen.composeCell(x, y, {
1433
+ content: {
1434
+ grapheme: cell.grapheme,
1435
+ width: cell.width
1436
+ },
1437
+ foreground: cell.reset?.foreground ? null : cell.style.foreground ?? null,
1438
+ background: cell.reset?.background ? null : cell.style.background,
1439
+ underlineColor: cell.style.underlineColor ?? null,
1440
+ underline: cell.style.underline,
1441
+ attributes: cell.style.attributes,
1442
+ hyperlink: cell.hyperlink ?? null
1443
+ });
1444
+ }
1445
+ #writeCompatibilityCell(x, y, character, width) {
1446
+ if (x >= this.#screen.width) return;
1447
+ const cell = cellFromStyledChar(character, width);
1448
+ this.#screen.composeCell(x, y, {
1449
+ content: {
1450
+ grapheme: cell.grapheme,
1451
+ width: cell.width
1452
+ },
1453
+ foreground: cell.style.foreground ?? null,
1454
+ background: cell.style.background,
1455
+ underlineColor: cell.style.underlineColor ?? null,
1456
+ underline: cell.style.underline,
1457
+ attributes: cell.style.attributes,
1458
+ hyperlink: cell.hyperlink ?? null
1459
+ });
1460
+ }
1461
+ };
2744
1462
  //#endregion
2745
- //#region src/renderer.ts
2746
- const renderer = (node, isScreenReaderEnabled) => {
1463
+ //#region src/render-frame.ts
1464
+ const renderDimension = (value) => Number.isFinite(value) ? Math.max(0, Math.ceil(value)) : 0;
1465
+ const renderFrame = (node, isScreenReaderEnabled, options = {}) => {
2747
1466
  if (node.yogaNode) {
2748
1467
  if (isScreenReaderEnabled) {
2749
- const output = renderNodeToScreenReaderOutput(node, { skipStaticElements: true });
2750
- const outputHeight = output === "" ? 0 : output.split("\n").length;
1468
+ const output = renderAccessibleText(node, { skipStaticElements: true });
2751
1469
  let staticOutput = "";
2752
- if (node.staticNode) staticOutput = renderNodeToScreenReaderOutput(node.staticNode, { skipStaticElements: false });
1470
+ if (node.staticNode) staticOutput = renderAccessibleText(node.staticNode, { skipStaticElements: false });
2753
1471
  return {
2754
- output,
2755
- outputHeight,
2756
- staticOutput: staticOutput ? `${staticOutput}\n` : ""
1472
+ accessibleText: output,
1473
+ staticAccessibleText: staticOutput
2757
1474
  };
2758
1475
  }
2759
- const output = new Output({
2760
- width: node.yogaNode.getComputedWidth(),
2761
- height: node.yogaNode.getComputedHeight()
1476
+ const output = new Canvas({
1477
+ width: renderDimension(node.yogaNode.getComputedWidth()),
1478
+ height: renderDimension(node.yogaNode.getComputedHeight()),
1479
+ colorProfile: options.colorProfile,
1480
+ paintContext: options.paintContext
2762
1481
  });
2763
- renderNodeToOutput(node, output, { skipStaticElements: true });
1482
+ paintTree(node, output, { skipStaticElements: true });
2764
1483
  let staticOutput;
2765
1484
  if (node.staticNode?.yogaNode) {
2766
- staticOutput = new Output({
2767
- width: node.staticNode.yogaNode.getComputedWidth(),
2768
- height: node.staticNode.yogaNode.getComputedHeight()
1485
+ staticOutput = new Canvas({
1486
+ width: renderDimension(node.staticNode.yogaNode.getComputedWidth()),
1487
+ height: renderDimension(node.staticNode.yogaNode.getComputedHeight()),
1488
+ colorProfile: options.colorProfile,
1489
+ paintContext: options.paintContext
2769
1490
  });
2770
- renderNodeToOutput(node.staticNode, staticOutput, { skipStaticElements: false });
1491
+ paintTree(node.staticNode, staticOutput, { skipStaticElements: false });
1492
+ }
1493
+ return {
1494
+ screen: output.finish(),
1495
+ ...staticOutput ? { staticScreen: staticOutput.finish() } : {}
1496
+ };
1497
+ }
1498
+ return { accessibleText: "" };
1499
+ };
1500
+ //#endregion
1501
+ //#region src/terminal/inline-presenter.ts
1502
+ /** Full-rewrite fallback and cursor state for the structured inline renderer. */
1503
+ function createInlinePresenter(stream, options = {}) {
1504
+ let previousLineCount = 0;
1505
+ let previousOutput = "";
1506
+ let hasHiddenCursor = false;
1507
+ let cursorPosition;
1508
+ let cursorDirty = false;
1509
+ let previousCursorPosition;
1510
+ let cursorWasShown = false;
1511
+ const showCursor = options.showCursor ?? false;
1512
+ const activeCursor = () => cursorDirty ? cursorPosition : void 0;
1513
+ const hasChanges = (output, cursor) => output !== previousOutput || cursorPositionChanged(cursor, previousCursorPosition);
1514
+ const present = (output) => {
1515
+ if (!showCursor && !hasHiddenCursor) {
1516
+ cliCursor.hide(stream);
1517
+ hasHiddenCursor = true;
1518
+ }
1519
+ const cursor = activeCursor();
1520
+ cursorDirty = false;
1521
+ const cursorChanged = cursorPositionChanged(cursor, previousCursorPosition);
1522
+ if (!hasChanges(output, cursor)) return false;
1523
+ const lines = output.split("\n");
1524
+ const suffix = buildCursorSuffix(lines.length - 1, cursor);
1525
+ if (output === previousOutput && cursorChanged) stream.write(buildCursorOnlySequence({
1526
+ cursorWasShown,
1527
+ previousLineCount,
1528
+ previousCursorPosition,
1529
+ cursorPosition: cursor
1530
+ }));
1531
+ else {
1532
+ previousOutput = output;
1533
+ stream.write(buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition) + ansiEscapes.eraseLines(previousLineCount) + output + suffix);
1534
+ previousLineCount = lines.length;
1535
+ }
1536
+ previousCursorPosition = cursor ? { ...cursor } : void 0;
1537
+ cursorWasShown = cursor !== void 0;
1538
+ return true;
1539
+ };
1540
+ present.clear = () => {
1541
+ stream.write(buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition) + ansiEscapes.eraseLines(previousLineCount));
1542
+ previousOutput = "";
1543
+ previousLineCount = 0;
1544
+ previousCursorPosition = void 0;
1545
+ cursorWasShown = false;
1546
+ };
1547
+ present.done = () => {
1548
+ previousOutput = "";
1549
+ previousLineCount = 0;
1550
+ previousCursorPosition = void 0;
1551
+ cursorWasShown = false;
1552
+ if (!showCursor) {
1553
+ cliCursor.show(stream);
1554
+ hasHiddenCursor = false;
2771
1555
  }
2772
- const { output: generatedOutput, height: outputHeight } = output.get();
2773
- return {
2774
- output: generatedOutput,
2775
- outputHeight,
2776
- staticOutput: staticOutput ? `${staticOutput.get().output}\n` : ""
2777
- };
2778
- }
2779
- return {
2780
- output: "",
2781
- outputHeight: 0,
2782
- staticOutput: ""
2783
1556
  };
2784
- };
1557
+ present.reset = () => {
1558
+ previousOutput = "";
1559
+ previousLineCount = 0;
1560
+ previousCursorPosition = void 0;
1561
+ cursorWasShown = false;
1562
+ };
1563
+ present.sync = (output) => {
1564
+ const cursor = activeCursor();
1565
+ cursorDirty = false;
1566
+ const lines = output.split("\n");
1567
+ previousOutput = output;
1568
+ previousLineCount = lines.length;
1569
+ if (!cursor && cursorWasShown) stream.write(hideCursorEscape);
1570
+ if (cursor) stream.write(buildCursorSuffix(lines.length - 1, cursor));
1571
+ previousCursorPosition = cursor ? { ...cursor } : void 0;
1572
+ cursorWasShown = cursor !== void 0;
1573
+ };
1574
+ present.setCursorPosition = (position) => {
1575
+ cursorPosition = position;
1576
+ cursorDirty = true;
1577
+ };
1578
+ present.isCursorDirty = () => cursorDirty;
1579
+ present.willRender = (output) => hasChanges(output, activeCursor());
1580
+ return present;
1581
+ }
2785
1582
  //#endregion
2786
1583
  //#region src/throttle.ts
2787
1584
  /**
@@ -2832,32 +1629,28 @@ const throttle = (fn, wait = 0) => {
2832
1629
  return throttled;
2833
1630
  };
2834
1631
  //#endregion
2835
- //#region src/terminal-size.ts
2836
- const create = (columns, rows) => ({
2837
- columns: Number.parseInt(String(columns), 10),
2838
- rows: Number.parseInt(String(rows), 10)
2839
- });
2840
- const devTty = () => {
2841
- try {
2842
- const { O_EVTONLY: evtOnly } = fs.constants;
2843
- const flags = process.platform === "darwin" && evtOnly !== void 0 ? evtOnly | fs.constants.O_NONBLOCK : fs.constants.O_NONBLOCK;
2844
- const { columns, rows } = new tty.WriteStream(fs.openSync("/dev/tty", flags));
2845
- if (columns && rows) return {
2846
- columns,
2847
- rows
2848
- };
2849
- } catch {}
2850
- };
2851
- const terminalSize = () => {
2852
- const { env, stdout, stderr } = process;
2853
- if (stdout?.columns && stdout?.rows) return create(stdout.columns, stdout.rows);
2854
- if (stderr?.columns && stderr?.rows) return create(stderr.columns, stderr.rows);
2855
- if (env["COLUMNS"] && env["LINES"]) return create(env["COLUMNS"], env["LINES"]);
2856
- return devTty() ?? {
2857
- columns: 80,
2858
- rows: 24
1632
+ //#region src/terminal/render-scheduler.ts
1633
+ /** Owns frame cadence independently of React reconciliation and terminal presentation. */
1634
+ function createRenderScheduler(render, options) {
1635
+ const frameInterval = options.maxFps > 0 ? Math.max(1, Math.ceil(1e3 / options.maxFps)) : 0;
1636
+ let pending = false;
1637
+ const throttled = options.unthrottled ? void 0 : throttle(render, frameInterval);
1638
+ return {
1639
+ intervalMs: options.unthrottled ? 0 : frameInterval,
1640
+ throttled,
1641
+ get pending() {
1642
+ return pending;
1643
+ },
1644
+ schedule: options.unthrottled ? render : () => {
1645
+ pending = true;
1646
+ throttled();
1647
+ },
1648
+ immediate: render,
1649
+ markRendered() {
1650
+ pending = false;
1651
+ }
2859
1652
  };
2860
- };
1653
+ }
2861
1654
  //#endregion
2862
1655
  //#region src/utils.ts
2863
1656
  const resolveDimension = (value, fallback, defaultValue) => {
@@ -2884,57 +1677,31 @@ const getWindowSize = (stdout) => {
2884
1677
  };
2885
1678
  };
2886
1679
  //#endregion
2887
- //#region src/write-synchronized.ts
2888
- function shouldSynchronize(stream, interactive) {
2889
- return "isTTY" in stream && stream.isTTY && (interactive ?? !isInCi);
2890
- }
2891
- //#endregion
2892
1680
  //#region src/ink.tsx
2893
1681
  /** @jsxImportSource react */
2894
1682
  const noop = () => {};
2895
- const textEncoder = new TextEncoder();
2896
- const yieldImmediate = async () => new Promise((resolve) => {
2897
- setImmediate(resolve);
2898
- });
2899
- const kittyQueryEscapeByte = 27;
2900
- const kittyQueryOpenBracketByte = 91;
2901
- const kittyQueryQuestionMarkByte = 63;
2902
- const kittyQueryLetterByte = 117;
2903
- const zeroByte = 48;
2904
- const nineByte = 57;
2905
- const isDigitByte = (byte) => byte >= zeroByte && byte <= nineByte;
2906
- const matchKittyQueryResponse = (buffer, startIndex) => {
2907
- if (buffer[startIndex] !== kittyQueryEscapeByte || buffer[startIndex + 1] !== kittyQueryOpenBracketByte || buffer[startIndex + 2] !== kittyQueryQuestionMarkByte) return;
2908
- let index = startIndex + 3;
2909
- const digitsStartIndex = index;
2910
- while (index < buffer.length && isDigitByte(buffer[index])) index++;
2911
- if (index === digitsStartIndex) return;
2912
- if (index === buffer.length) return { state: "partial" };
2913
- if (buffer[index] === kittyQueryLetterByte) return {
2914
- state: "complete",
2915
- endIndex: index
2916
- };
2917
- };
2918
- const hasCompleteKittyQueryResponse = (buffer) => {
2919
- for (let index = 0; index < buffer.length; index++) if (matchKittyQueryResponse(buffer, index)?.state === "complete") return true;
2920
- return false;
2921
- };
2922
- const stripKittyQueryResponsesAndTrailingPartial = (buffer) => {
2923
- const keptBytes = [];
2924
- let index = 0;
2925
- while (index < buffer.length) {
2926
- const match = matchKittyQueryResponse(buffer, index);
2927
- if (match?.state === "complete") {
2928
- index = match.endIndex + 1;
2929
- continue;
2930
- }
2931
- if (match?.state === "partial") break;
2932
- keptBytes.push(buffer[index]);
2933
- index++;
2934
- }
2935
- return keptBytes;
1683
+ const beforeExitCallbacks = /* @__PURE__ */ new Set();
1684
+ const runBeforeExitCallbacks = () => {
1685
+ for (const callback of beforeExitCallbacks) callback();
2936
1686
  };
2937
- const isWindowsConsole = process.platform === "win32";
1687
+ function registerBeforeExit(callback) {
1688
+ if (beforeExitCallbacks.size === 0) process.on("beforeExit", runBeforeExitCallbacks);
1689
+ beforeExitCallbacks.add(callback);
1690
+ return () => {
1691
+ beforeExitCallbacks.delete(callback);
1692
+ if (beforeExitCallbacks.size === 0) process.off("beforeExit", runBeforeExitCallbacks);
1693
+ };
1694
+ }
1695
+ function bottomRows(screen, height) {
1696
+ if (screen.height <= height) return screen;
1697
+ const cropped = new Screen(screen.width, height);
1698
+ const offset = screen.height - height;
1699
+ for (let y = 0; y < height; y++) for (let x = 0; x < screen.width; x++) {
1700
+ const cell = screen.cellAt(x, y + offset);
1701
+ if (cell && cell.width > 0) cropped.setCell(x, y, cell);
1702
+ }
1703
+ return cropped;
1704
+ }
2938
1705
  const shouldClearTerminalForFrame = ({ isTTY, viewportRows, previousOutputHeight, nextOutputHeight, isUnmounting }) => {
2939
1706
  if (!isTTY) return false;
2940
1707
  const hadPreviousFrame = previousOutputHeight > 0;
@@ -2944,20 +1711,24 @@ const shouldClearTerminalForFrame = ({ isTTY, viewportRows, previousOutputHeight
2944
1711
  const isFullscreen = nextOutputHeight >= viewportRows;
2945
1712
  const isLeavingFullscreen = wasOverflowing && nextOutputHeight < viewportRows;
2946
1713
  const shouldClearOnUnmount = isUnmounting && wasFullscreen;
2947
- if (isWindowsConsole && (wasFullscreen || isFullscreen)) return true;
1714
+ if (isWindows && (wasFullscreen || isFullscreen)) return true;
2948
1715
  return wasOverflowing || isOverflowing && hadPreviousFrame || isLeavingFullscreen || shouldClearOnUnmount;
2949
1716
  };
2950
1717
  const isErrorInput = (value) => {
2951
- return value instanceof Error || Object.prototype.toString.call(value) === "[object Error]";
1718
+ return value instanceof Error || isNativeError(value);
2952
1719
  };
2953
1720
  const getWritableStreamState = (stdout) => {
2954
1721
  return { canWriteToStdout: !stdout.destroyed && !stdout.writableEnded && (stdout.writable ?? true) };
2955
1722
  };
2956
1723
  const settleThrottle = (throttled, canWriteToStdout) => {
2957
- if (!throttled || typeof throttled.flush !== "function") return;
2958
- const throttledValue = throttled;
2959
- if (canWriteToStdout) throttledValue.flush();
2960
- else if (typeof throttledValue.cancel === "function") throttledValue.cancel();
1724
+ if (!throttled) return;
1725
+ if (canWriteToStdout) throttled.flush();
1726
+ else throttled.cancel();
1727
+ };
1728
+ const writeBestEffort = (stream, data) => {
1729
+ try {
1730
+ stream.write(data);
1731
+ } catch {}
2961
1732
  };
2962
1733
  const createRenderPassthrough = (stream) => {
2963
1734
  const passthrough = Object.create(stream);
@@ -2970,645 +1741,597 @@ const createRenderPassthrough = (stream) => {
2970
1741
  passthrough.emit = stream.emit.bind(stream);
2971
1742
  return passthrough;
2972
1743
  };
2973
- var Ink = class {
2974
- /**
2975
- Whether this instance is using concurrent rendering mode.
2976
- */
2977
- isConcurrent;
2978
- options;
2979
- log;
2980
- cursorPosition;
2981
- throttledLog;
2982
- isScreenReaderEnabled;
2983
- interactive;
2984
- renderThrottleMs;
2985
- alternateScreen;
2986
- isUnmounted;
2987
- isUnmounting;
2988
- lastOutput;
2989
- lastOutputToRender;
2990
- lastOutputHeight;
2991
- lastTerminalWidth;
2992
- lastTerminalHeight;
2993
- container;
2994
- rootNode;
2995
- fullStaticOutput;
2996
- exitPromise;
2997
- exitResult;
2998
- beforeExitHandler;
2999
- restoreConsole;
3000
- captureTargets;
3001
- capturedStdioTails = {
1744
+ const createInk = (options) => {
1745
+ let captureTargets;
1746
+ if (options.patchConsole === "stdio") {
1747
+ captureTargets = {
1748
+ stdout: options.stdout,
1749
+ stderr: options.stderr
1750
+ };
1751
+ options = {
1752
+ ...options,
1753
+ stdout: createRenderPassthrough(options.stdout),
1754
+ stderr: createRenderPassthrough(options.stderr)
1755
+ };
1756
+ }
1757
+ const rootNode = createNode("ink-root");
1758
+ rootNode.onComputeLayout = calculateLayout;
1759
+ const isScreenReaderEnabled = options.isScreenReaderEnabled ?? isScreenReader;
1760
+ const interactive = options.interactive ?? (!isInCi && Boolean(options.stdout.isTTY));
1761
+ const terminal = new TerminalSession({
1762
+ stdin: options.stdin,
1763
+ stdout: options.stdout,
1764
+ stderr: options.stderr,
1765
+ colorPolicy: options.colorProfile ?? "auto",
1766
+ onCapabilitiesChange: () => rootNode.onRender?.()
1767
+ });
1768
+ const terminalOsc = {
1769
+ publishProgress: (owner, state, value) => {
1770
+ terminal.publishProgress(owner, state, value);
1771
+ },
1772
+ copyToClipboard: (text, selection) => {
1773
+ terminal.copyToClipboard(text, selection);
1774
+ },
1775
+ publishTitle: (owner, title) => terminal.publishTitle(owner, title),
1776
+ setWorkingDirectory: (directory) => terminal.setWorkingDirectory(directory),
1777
+ notify: (title) => terminal.notify(title),
1778
+ setPointerShape: (shape) => terminal.setPointerShape(shape)
1779
+ };
1780
+ const capabilitiesStore = terminal.capabilities;
1781
+ const renderScheduler = createRenderScheduler(onRender, {
1782
+ unthrottled: options.debug || isScreenReaderEnabled,
1783
+ maxFps: options.maxFps ?? 30
1784
+ });
1785
+ rootNode.onRender = renderScheduler.schedule;
1786
+ rootNode.onImmediateRender = renderScheduler.immediate;
1787
+ rootNode.onStaticChange = handleStaticChange;
1788
+ const accessiblePresenter = isScreenReaderEnabled ? createInlinePresenter(options.stdout, { showCursor: true }) : void 0;
1789
+ let isUnmounted = false;
1790
+ let isUnmounting = false;
1791
+ const isConcurrent = options.concurrent ?? false;
1792
+ let lastOutput = "";
1793
+ let lastOutputToRender = "";
1794
+ let lastOutputHeight = 0;
1795
+ let lastScreen;
1796
+ let lastTerminalWidth = getWindowSize(options.stdout).columns;
1797
+ let lastTerminalHeight = getWindowSize(options.stdout).rows;
1798
+ let fullStaticOutput = "";
1799
+ let exitResult;
1800
+ let unsubscribeBeforeExit;
1801
+ let restoreConsole;
1802
+ const capturedStdioTails = {
3002
1803
  stdout: "",
3003
1804
  stderr: ""
3004
1805
  };
3005
- unsubscribeResize;
3006
- throttledOnRender;
3007
- hasPendingThrottledRender = false;
3008
- kittyProtocolEnabled = false;
3009
- kittyFlags;
3010
- cancelKittyDetection;
3011
- nextRenderCommit;
3012
- isSuspended = false;
3013
- pauseInput;
3014
- resumeInput;
3015
- constructor(options) {
3016
- autoBind(this);
3017
- if (options.patchConsole === "stdio") {
3018
- this.captureTargets = {
3019
- stdout: options.stdout,
3020
- stderr: options.stderr
3021
- };
3022
- options = {
3023
- ...options,
3024
- stdout: createRenderPassthrough(options.stdout),
3025
- stderr: createRenderPassthrough(options.stderr)
3026
- };
3027
- }
3028
- this.options = options;
3029
- this.rootNode = createNode("ink-root");
3030
- this.rootNode.onComputeLayout = this.calculateLayout;
3031
- this.isScreenReaderEnabled = options.isScreenReaderEnabled ?? process.env["SIGIL_SCREEN_READER"] === "true";
3032
- this.interactive = this.resolveInteractiveOption(options.interactive);
3033
- this.alternateScreen = false;
3034
- const unthrottled = options.debug || this.isScreenReaderEnabled;
3035
- const maxFps = options.maxFps ?? 30;
3036
- const renderThrottleMs = maxFps > 0 ? Math.max(1, Math.ceil(1e3 / maxFps)) : 0;
3037
- this.renderThrottleMs = unthrottled ? 0 : renderThrottleMs;
3038
- if (unthrottled) {
3039
- this.rootNode.onRender = this.onRender;
3040
- this.throttledOnRender = void 0;
3041
- } else {
3042
- const throttled = throttle(this.onRender, renderThrottleMs);
3043
- this.rootNode.onRender = () => {
3044
- this.hasPendingThrottledRender = true;
3045
- throttled();
3046
- };
3047
- this.throttledOnRender = throttled;
3048
- }
3049
- this.rootNode.onImmediateRender = this.onRender;
3050
- this.rootNode.onStaticChange = this.handleStaticChange;
3051
- this.log = logUpdate.create(options.stdout, { incremental: options.incrementalRendering });
3052
- this.cursorPosition = void 0;
3053
- this.throttledLog = unthrottled ? this.log : throttle((output) => {
3054
- const shouldWrite = this.log.willRender(output);
3055
- const sync = this.shouldSync();
3056
- if (sync && shouldWrite) this.options.stdout.write(bsu);
3057
- this.log(output);
3058
- if (sync && shouldWrite) this.options.stdout.write(esu);
3059
- });
3060
- this.isUnmounted = false;
3061
- this.isUnmounting = false;
3062
- this.isConcurrent = options.concurrent ?? false;
3063
- this.lastOutput = "";
3064
- this.lastOutputToRender = "";
3065
- this.lastOutputHeight = 0;
3066
- this.lastTerminalWidth = getWindowSize(this.options.stdout).columns;
3067
- this.lastTerminalHeight = getWindowSize(this.options.stdout).rows;
3068
- this.fullStaticOutput = "";
3069
- const rootTag = options.concurrent ? ConcurrentRoot : LegacyRoot;
3070
- this.container = reconciler.createContainer(this.rootNode, rootTag, null, false, null, "id", () => {}, () => {}, () => {}, () => {});
3071
- this.unsubscribeExit = signalExit(this.unmount.bind(this), { alwaysLast: false });
3072
- this.setAlternateScreen(Boolean(options.alternateScreen));
3073
- if (process.env["SIGIL_DEV"] === "true") reconciler.injectIntoDevTools();
3074
- if (options.patchConsole) this.patchConsole();
3075
- if (this.interactive) {
3076
- options.stdout.on("resize", this.resized);
3077
- this.unsubscribeResize = () => {
3078
- options.stdout.off("resize", this.resized);
3079
- };
3080
- }
3081
- this.initKittyKeyboard();
3082
- this.exitPromise = new Promise((resolve, reject) => {
3083
- this.resolveExitPromise = resolve;
3084
- this.rejectExitPromise = reject;
3085
- });
3086
- this.exitPromise.catch(noop);
1806
+ let unsubscribeResize;
1807
+ let kittyProtocolEnabled = false;
1808
+ let kittyFlags;
1809
+ let cancelKittyDetection;
1810
+ let nextRenderCommit;
1811
+ let pauseInput;
1812
+ let resumeInput;
1813
+ const rootTag = isConcurrent ? ConcurrentRoot : LegacyRoot;
1814
+ const container = reconciler.createContainer(rootNode, rootTag, null, false, null, "id", () => {}, () => {}, () => {}, () => {});
1815
+ const unsubscribeExit = signalExit(unmount, { alwaysLast: false });
1816
+ setAlternateScreen(Boolean(options.alternateScreen));
1817
+ if (isSigilDev) reconciler.injectIntoDevTools();
1818
+ if (options.patchConsole) installConsolePatch();
1819
+ if (interactive) {
1820
+ options.stdout.on("resize", resized);
1821
+ unsubscribeResize = () => {
1822
+ options.stdout.off("resize", resized);
1823
+ };
3087
1824
  }
3088
- resized = () => {
3089
- const currentWidth = getWindowSize(this.options.stdout).columns;
3090
- const currentHeight = getWindowSize(this.options.stdout).rows;
3091
- if (currentWidth < this.lastTerminalWidth || currentHeight !== this.lastTerminalHeight) {
3092
- this.log.clear();
3093
- this.lastOutput = "";
3094
- this.lastOutputToRender = "";
3095
- this.lastOutputHeight = 0;
3096
- }
3097
- this.calculateLayout();
3098
- emitLayoutListeners(this.rootNode);
3099
- this.onRender();
3100
- this.lastTerminalWidth = currentWidth;
3101
- this.lastTerminalHeight = currentHeight;
3102
- };
3103
- resolveExitPromise = () => {};
3104
- rejectExitPromise = () => {};
3105
- unsubscribeExit = () => {};
3106
- handleAppExit = (errorOrResult) => {
3107
- if (this.isUnmounted || this.isUnmounting) return;
1825
+ initKittyKeyboard();
1826
+ const { promise: exitPromise, resolve: resolveExitPromise, reject: rejectExitPromise } = Promise.withResolvers();
1827
+ exitPromise.catch(noop);
1828
+ function resized() {
1829
+ const currentWidth = getWindowSize(options.stdout).columns;
1830
+ const currentHeight = getWindowSize(options.stdout).rows;
1831
+ if (currentWidth < lastTerminalWidth || currentHeight !== lastTerminalHeight) {
1832
+ clearLiveOutput();
1833
+ resetLiveOutput();
1834
+ lastOutput = "";
1835
+ lastOutputToRender = "";
1836
+ lastOutputHeight = 0;
1837
+ }
1838
+ calculateLayout();
1839
+ emitLayoutListeners(rootNode);
1840
+ onRender();
1841
+ lastTerminalWidth = currentWidth;
1842
+ lastTerminalHeight = currentHeight;
1843
+ }
1844
+ function handleAppExit(errorOrResult) {
1845
+ if (isUnmounted || isUnmounting) return;
3108
1846
  if (isErrorInput(errorOrResult)) {
3109
- this.unmount(errorOrResult);
1847
+ unmount(errorOrResult);
3110
1848
  return;
3111
1849
  }
3112
- this.exitResult = errorOrResult;
3113
- this.unmount();
3114
- };
3115
- setCursorPosition = (position) => {
3116
- this.cursorPosition = position;
3117
- this.log.setCursorPosition(position);
3118
- };
3119
- restoreLastOutput = () => {
3120
- if (!this.interactive) return;
3121
- this.log.setCursorPosition(this.cursorPosition);
3122
- this.log(this.lastOutputToRender || this.lastOutput + "\n");
3123
- };
3124
- calculateLayout = () => {
3125
- const terminalWidth = getWindowSize(this.options.stdout).columns;
3126
- this.rootNode.yogaNode.setWidth(terminalWidth);
3127
- this.rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
3128
- };
3129
- handleStaticChange = () => {
3130
- this.fullStaticOutput = "";
3131
- };
3132
- onRender = () => {
3133
- this.hasPendingThrottledRender = false;
3134
- if (this.isUnmounted) return;
3135
- if (this.isSuspended) {
3136
- if (this.nextRenderCommit) {
3137
- this.nextRenderCommit.resolve();
3138
- this.nextRenderCommit = void 0;
1850
+ exitResult = errorOrResult;
1851
+ unmount();
1852
+ }
1853
+ function setCursorPosition(position) {
1854
+ terminal.setCursor(position);
1855
+ accessiblePresenter?.setCursorPosition(position);
1856
+ }
1857
+ function restoreLastOutput() {
1858
+ if (!interactive) return;
1859
+ if (isScreenReaderEnabled) {
1860
+ accessiblePresenter.setCursorPosition(terminal.cursor.position);
1861
+ accessiblePresenter(lastOutputToRender || lastOutput + "\n");
1862
+ } else if (lastScreen) terminal.present(lastScreen, {
1863
+ fullscreen: lastOutputToRender === lastOutput,
1864
+ forceRewrite: true
1865
+ });
1866
+ }
1867
+ function clearLiveOutput() {
1868
+ if (isScreenReaderEnabled) accessiblePresenter.clear();
1869
+ else terminal.clearFrame();
1870
+ }
1871
+ function finishLiveOutput() {
1872
+ if (isScreenReaderEnabled) accessiblePresenter.done();
1873
+ else terminal.finishFrame();
1874
+ }
1875
+ function resetLiveOutput() {
1876
+ if (isScreenReaderEnabled) accessiblePresenter.reset();
1877
+ else terminal.resetFrame();
1878
+ }
1879
+ function calculateLayout() {
1880
+ const terminalWidth = getWindowSize(options.stdout).columns;
1881
+ rootNode.yogaNode.setWidth(terminalWidth);
1882
+ rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
1883
+ }
1884
+ function handleStaticChange() {
1885
+ fullStaticOutput = "";
1886
+ }
1887
+ function onRender() {
1888
+ renderScheduler.markRendered();
1889
+ if (isUnmounted) return;
1890
+ if (terminal.suspended) {
1891
+ if (nextRenderCommit) {
1892
+ nextRenderCommit.resolve();
1893
+ nextRenderCommit = void 0;
3139
1894
  }
3140
1895
  return;
3141
1896
  }
3142
- if (this.nextRenderCommit) {
3143
- this.nextRenderCommit.resolve();
3144
- this.nextRenderCommit = void 0;
1897
+ if (nextRenderCommit) {
1898
+ nextRenderCommit.resolve();
1899
+ nextRenderCommit = void 0;
3145
1900
  }
3146
1901
  const startTime = performance.now();
3147
- const { output, outputHeight, staticOutput } = renderer(this.rootNode, this.isScreenReaderEnabled);
3148
- this.options.onRender?.({ renderTime: performance.now() - startTime });
1902
+ const rendered = renderFrame(rootNode, isScreenReaderEnabled, {
1903
+ colorProfile: terminal.colorProfile,
1904
+ paintContext: {
1905
+ appearance: capabilitiesStore.current.theme.appearance,
1906
+ palette: capabilitiesStore.current.theme.palette
1907
+ }
1908
+ });
1909
+ const screen = rendered.screen;
1910
+ const output = rendered.accessibleText ?? (screen ? terminal.encode(screen) : "");
1911
+ const outputHeight = rendered.accessibleText === void 0 ? screen?.height ?? 0 : rendered.accessibleText === "" ? 0 : rendered.accessibleText.split("\n").length;
1912
+ const staticBody = rendered.staticAccessibleText ?? (rendered.staticScreen ? terminal.encode(rendered.staticScreen) : "");
1913
+ const staticOutput = staticBody ? `${staticBody}\n` : "";
1914
+ options.onRender?.({ renderTime: performance.now() - startTime });
3149
1915
  const hasStaticOutput = staticOutput && staticOutput !== "\n";
3150
- if (this.options.debug) {
3151
- if (hasStaticOutput) this.fullStaticOutput += staticOutput;
3152
- this.lastOutput = output;
3153
- this.lastOutputToRender = output;
3154
- this.lastOutputHeight = outputHeight;
3155
- this.options.stdout.write(this.fullStaticOutput + output);
1916
+ if (options.debug) {
1917
+ if (hasStaticOutput) fullStaticOutput += staticOutput;
1918
+ lastOutput = output;
1919
+ lastOutputToRender = output;
1920
+ lastOutputHeight = outputHeight;
1921
+ options.stdout.write(fullStaticOutput + output);
3156
1922
  return;
3157
1923
  }
3158
- if (!this.interactive) {
3159
- if (hasStaticOutput) this.options.stdout.write(staticOutput);
3160
- this.lastOutput = output;
3161
- this.lastOutputToRender = output + "\n";
3162
- this.lastOutputHeight = outputHeight;
1924
+ if (!interactive) {
1925
+ if (hasStaticOutput) options.stdout.write(staticOutput);
1926
+ lastOutput = output;
1927
+ lastOutputToRender = output + "\n";
1928
+ lastOutputHeight = outputHeight;
3163
1929
  return;
3164
1930
  }
3165
- if (this.isScreenReaderEnabled) {
3166
- const sync = this.shouldSync();
3167
- if (sync) this.options.stdout.write(bsu);
1931
+ if (isScreenReaderEnabled) {
1932
+ const sync = shouldSync();
1933
+ if (sync) options.stdout.write(bsu);
3168
1934
  if (hasStaticOutput) {
3169
- const erase = this.lastOutputHeight > 0 ? ansiEscapes.eraseLines(this.lastOutputHeight) : "";
3170
- this.options.stdout.write(erase + staticOutput);
3171
- this.lastOutputHeight = 0;
1935
+ const erase = lastOutputHeight > 0 ? ansiEscapes.eraseLines(lastOutputHeight) : "";
1936
+ options.stdout.write(erase + staticOutput);
1937
+ lastOutputHeight = 0;
3172
1938
  }
3173
- if (output === this.lastOutput && !hasStaticOutput) {
3174
- if (sync) this.options.stdout.write(esu);
1939
+ if (output === lastOutput && !hasStaticOutput) {
1940
+ if (sync) options.stdout.write(esu);
3175
1941
  return;
3176
1942
  }
3177
- const terminalWidth = getWindowSize(this.options.stdout).columns;
1943
+ const terminalWidth = getWindowSize(options.stdout).columns;
3178
1944
  const wrappedOutput = wrapAnsi(output, terminalWidth, {
3179
1945
  trim: false,
3180
1946
  hard: true
3181
1947
  });
3182
- if (hasStaticOutput) this.options.stdout.write(wrappedOutput);
1948
+ if (hasStaticOutput) options.stdout.write(wrappedOutput);
3183
1949
  else {
3184
- const erase = this.lastOutputHeight > 0 ? ansiEscapes.eraseLines(this.lastOutputHeight) : "";
3185
- this.options.stdout.write(erase + wrappedOutput);
1950
+ const erase = lastOutputHeight > 0 ? ansiEscapes.eraseLines(lastOutputHeight) : "";
1951
+ options.stdout.write(erase + wrappedOutput);
3186
1952
  }
3187
- this.lastOutput = output;
3188
- this.lastOutputToRender = wrappedOutput;
3189
- this.lastOutputHeight = wrappedOutput === "" ? 0 : wrappedOutput.split("\n").length;
3190
- if (sync) this.options.stdout.write(esu);
1953
+ lastOutput = output;
1954
+ lastOutputToRender = wrappedOutput;
1955
+ lastOutputHeight = wrappedOutput === "" ? 0 : wrappedOutput.split("\n").length;
1956
+ if (sync) options.stdout.write(esu);
3191
1957
  return;
3192
1958
  }
3193
- if (hasStaticOutput) this.fullStaticOutput += staticOutput;
3194
- this.renderInteractiveFrame(output, outputHeight, hasStaticOutput ? staticOutput : "");
3195
- };
3196
- render(node) {
1959
+ if (hasStaticOutput) fullStaticOutput += staticOutput;
1960
+ renderInteractiveFrame(output, outputHeight, hasStaticOutput ? staticOutput : "", screen, false);
1961
+ }
1962
+ function render(node) {
3197
1963
  const tree = /* @__PURE__ */ jsx(accessibilityContext.Provider, {
3198
- value: { isScreenReaderEnabled: this.isScreenReaderEnabled },
3199
- children: /* @__PURE__ */ jsx(App, {
3200
- stdin: this.options.stdin,
3201
- stdout: this.options.stdout,
3202
- stderr: this.options.stderr,
3203
- exitOnCtrlC: this.options.exitOnCtrlC,
3204
- interactive: this.interactive,
3205
- renderThrottleMs: this.renderThrottleMs,
3206
- writeToStdout: this.writeToStdout.bind(this),
3207
- writeToStderr: this.writeToStderr.bind(this),
3208
- setCursorPosition: this.setCursorPosition.bind(this),
3209
- onExit: this.handleAppExit.bind(this),
3210
- onWaitUntilRenderFlush: this.waitUntilRenderFlush.bind(this),
3211
- onSuspendTerminal: this.suspendTerminal.bind(this),
3212
- onRegisterInputControl: this.registerInputControl.bind(this),
3213
- children: node
1964
+ value: { isScreenReaderEnabled },
1965
+ children: /* @__PURE__ */ jsx(TerminalOscContext.Provider, {
1966
+ value: terminalOsc,
1967
+ children: /* @__PURE__ */ jsx(App, {
1968
+ stdin: options.stdin,
1969
+ stdout: options.stdout,
1970
+ stderr: options.stderr,
1971
+ exitOnCtrlC: options.exitOnCtrlC,
1972
+ interactive,
1973
+ renderThrottleMs: renderScheduler.intervalMs,
1974
+ terminalInput: terminal.input,
1975
+ writeToStdout,
1976
+ writeToStderr,
1977
+ setCursorPosition,
1978
+ onExit: handleAppExit,
1979
+ onWaitUntilRenderFlush: waitUntilRenderFlush,
1980
+ onSuspendTerminal: suspendTerminal,
1981
+ onRegisterInputControl: registerInputControl,
1982
+ children: node
1983
+ })
3214
1984
  })
3215
1985
  });
3216
- if (this.options.concurrent) reconciler.updateContainer(tree, this.container, null, noop);
1986
+ if (isConcurrent) reconciler.updateContainer(tree, container, null, noop);
3217
1987
  else {
3218
- reconciler.updateContainerSync(tree, this.container, null, noop);
1988
+ reconciler.updateContainerSync(tree, container, null, noop);
3219
1989
  reconciler.flushSyncWork();
3220
1990
  }
3221
1991
  }
3222
- writeToStdout(data) {
3223
- if (this.isUnmounted) return;
3224
- if (this.isSuspended) return;
3225
- if (this.options.debug) {
3226
- this.options.stdout.write(data + this.fullStaticOutput + this.lastOutput);
1992
+ function writeToStdout(data) {
1993
+ if (isUnmounted) return;
1994
+ if (terminal.suspended) return;
1995
+ if (options.debug) {
1996
+ options.stdout.write(data + fullStaticOutput + lastOutput);
3227
1997
  return;
3228
1998
  }
3229
- if (!this.interactive) {
3230
- this.options.stdout.write(data);
1999
+ if (!interactive) {
2000
+ options.stdout.write(data);
3231
2001
  return;
3232
2002
  }
3233
- const sync = this.shouldSync();
3234
- if (sync) this.options.stdout.write(bsu);
3235
- this.log.clear();
3236
- this.options.stdout.write(data);
3237
- this.restoreLastOutput();
3238
- if (sync) this.options.stdout.write(esu);
3239
- }
3240
- writeToStderr(data) {
3241
- if (this.isUnmounted) return;
3242
- if (this.isSuspended) return;
3243
- if (this.options.debug) {
3244
- this.options.stderr.write(data);
3245
- this.options.stdout.write(this.fullStaticOutput + this.lastOutput);
2003
+ const sync = shouldSync();
2004
+ if (sync) options.stdout.write(bsu);
2005
+ clearLiveOutput();
2006
+ options.stdout.write(data);
2007
+ restoreLastOutput();
2008
+ if (sync) options.stdout.write(esu);
2009
+ }
2010
+ function writeToStderr(data) {
2011
+ if (isUnmounted) return;
2012
+ if (terminal.suspended) return;
2013
+ if (options.debug) {
2014
+ options.stderr.write(data);
2015
+ options.stdout.write(fullStaticOutput + lastOutput);
3246
2016
  return;
3247
2017
  }
3248
- if (!this.interactive) {
3249
- this.options.stderr.write(data);
2018
+ if (!interactive) {
2019
+ options.stderr.write(data);
3250
2020
  return;
3251
2021
  }
3252
- const sync = this.shouldSync();
3253
- if (sync) this.options.stdout.write(bsu);
3254
- this.log.clear();
3255
- this.options.stderr.write(data);
3256
- this.restoreLastOutput();
3257
- if (sync) this.options.stdout.write(esu);
3258
- }
3259
- unmount(error) {
3260
- if (this.isUnmounted || this.isUnmounting) return;
3261
- this.isUnmounting = true;
3262
- if (this.beforeExitHandler) {
3263
- process.off("beforeExit", this.beforeExitHandler);
3264
- this.beforeExitHandler = void 0;
3265
- }
3266
- const { stdout } = this.options;
3267
- const { canWriteToStdout } = getWritableStreamState(stdout);
3268
- if (canWriteToStdout) this.flushCapturedStdio();
3269
- settleThrottle(this.throttledOnRender, canWriteToStdout);
2022
+ const sync = shouldSync();
2023
+ if (sync) options.stdout.write(bsu);
2024
+ clearLiveOutput();
2025
+ options.stderr.write(data);
2026
+ restoreLastOutput();
2027
+ if (sync) options.stdout.write(esu);
2028
+ }
2029
+ function unmount(error) {
2030
+ if (isUnmounted || isUnmounting) return;
2031
+ isUnmounting = true;
2032
+ unsubscribeBeforeExit?.();
2033
+ unsubscribeBeforeExit = void 0;
2034
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2035
+ if (canWriteToStdout) flushCapturedStdio();
2036
+ settleThrottle(renderScheduler.throttled, canWriteToStdout);
3270
2037
  if (canWriteToStdout) {
3271
- if (!this.throttledOnRender || !this.hasPendingThrottledRender && this.fullStaticOutput === "") {
3272
- this.calculateLayout();
3273
- this.onRender();
2038
+ if (!renderScheduler.throttled || !renderScheduler.pending && fullStaticOutput === "") {
2039
+ calculateLayout();
2040
+ onRender();
3274
2041
  }
3275
2042
  }
3276
- this.isUnmounted = true;
3277
- this.unsubscribeExit();
3278
- settleThrottle(this.throttledLog, canWriteToStdout);
3279
- if (typeof this.restoreConsole === "function") this.restoreConsole();
2043
+ isUnmounted = true;
2044
+ unsubscribeExit();
2045
+ terminal.cleanup();
2046
+ if (typeof restoreConsole === "function") restoreConsole();
3280
2047
  const finishUnmount = () => {
3281
- if (typeof this.unsubscribeResize === "function") this.unsubscribeResize();
3282
- if (this.cancelKittyDetection) this.cancelKittyDetection();
2048
+ if (typeof unsubscribeResize === "function") unsubscribeResize();
2049
+ if (cancelKittyDetection) cancelKittyDetection();
3283
2050
  if (canWriteToStdout) {
3284
- if (this.kittyProtocolEnabled) this.writeBestEffort(this.options.stdout, ansiEscapes.popKittyKeyboard);
3285
- if (this.alternateScreen) {
3286
- this.writeBestEffort(this.options.stdout, ansiEscapes.exitAlternativeScreen);
3287
- this.writeBestEffort(this.options.stdout, showCursorEscape);
3288
- this.alternateScreen = false;
2051
+ if (kittyProtocolEnabled) writeBestEffort(options.stdout, ansiEscapes.popKittyKeyboard);
2052
+ if (terminal.alternateScreen) {
2053
+ terminal.setAlternateScreen(false);
2054
+ terminal.setCursorAppearance({ visible: true });
3289
2055
  }
3290
- if (!this.interactive) this.options.stdout.write(this.options.debug ? "\n" : this.lastOutput + "\n");
3291
- else if (!this.options.debug) this.log.done();
2056
+ if (!interactive) options.stdout.write(options.debug ? "\n" : lastOutput + "\n");
2057
+ else if (!options.debug) finishLiveOutput();
3292
2058
  }
3293
- this.kittyProtocolEnabled = false;
3294
- instances.delete(this.captureTargets?.stdout ?? this.options.stdout);
3295
- const { exitResult } = this;
2059
+ kittyProtocolEnabled = false;
2060
+ instances.delete(captureTargets?.stdout ?? options.stdout);
2061
+ const finalExitResult = exitResult;
3296
2062
  const resolveOrReject = () => {
3297
- if (isErrorInput(error)) this.rejectExitPromise(error);
3298
- else this.resolveExitPromise(exitResult);
2063
+ if (isErrorInput(error)) rejectExitPromise(error);
2064
+ else resolveExitPromise(finalExitResult);
3299
2065
  };
3300
2066
  if (error !== void 0 && !isErrorInput(error)) resolveOrReject();
3301
- else if (canWriteToStdout) this.options.stdout.write("", resolveOrReject);
2067
+ else if (canWriteToStdout) options.stdout.write("", resolveOrReject);
3302
2068
  else setImmediate(resolveOrReject);
3303
2069
  };
3304
2070
  const concurrentReconciler = reconciler;
3305
- if (this.options.concurrent) {
3306
- reconciler.updateContainerSync(null, this.container, null, noop);
2071
+ if (isConcurrent) {
2072
+ reconciler.updateContainerSync(null, container, null, noop);
3307
2073
  reconciler.flushSyncWork();
3308
2074
  concurrentReconciler.flushPassiveEffects?.();
3309
2075
  finishUnmount();
3310
2076
  } else {
3311
- reconciler.updateContainerSync(null, this.container, null, noop);
2077
+ reconciler.updateContainerSync(null, container, null, noop);
3312
2078
  reconciler.flushSyncWork();
3313
2079
  finishUnmount();
3314
2080
  }
3315
2081
  }
3316
- async waitUntilExit() {
3317
- if (!this.beforeExitHandler) {
3318
- this.beforeExitHandler = () => {
3319
- this.unmount();
3320
- };
3321
- process.once("beforeExit", this.beforeExitHandler);
3322
- }
3323
- return this.exitPromise;
2082
+ async function waitUntilExit() {
2083
+ if (!unsubscribeBeforeExit) unsubscribeBeforeExit = registerBeforeExit(() => {
2084
+ unmount();
2085
+ });
2086
+ return exitPromise;
3324
2087
  }
3325
- async waitUntilRenderFlush() {
3326
- if (this.isUnmounted || this.isUnmounting) {
3327
- await this.awaitExit();
2088
+ async function waitUntilRenderFlush() {
2089
+ if (isUnmounted || isUnmounting) {
2090
+ await awaitExit();
3328
2091
  return;
3329
2092
  }
3330
- await yieldImmediate();
3331
- if (this.isUnmounted || this.isUnmounting) {
3332
- await this.awaitExit();
2093
+ await setImmediate$1();
2094
+ if (isUnmounted || isUnmounting) {
2095
+ await awaitExit();
3333
2096
  return;
3334
2097
  }
3335
- if (this.isConcurrent && this.hasPendingConcurrentWork()) {
3336
- await Promise.race([this.awaitNextRender(), this.awaitExit()]);
3337
- if (this.isUnmounted || this.isUnmounting) {
3338
- this.nextRenderCommit = void 0;
3339
- await this.awaitExit();
2098
+ if (isConcurrent && hasPendingConcurrentWork()) {
2099
+ await Promise.race([awaitNextRender(), awaitExit()]);
2100
+ if (isUnmounted || isUnmounting) {
2101
+ nextRenderCommit = void 0;
2102
+ await awaitExit();
3340
2103
  return;
3341
2104
  }
3342
2105
  }
3343
2106
  reconciler.flushSyncWork();
3344
- const { stdout } = this.options;
3345
- const { canWriteToStdout } = getWritableStreamState(stdout);
3346
- settleThrottle(this.throttledOnRender, canWriteToStdout);
3347
- settleThrottle(this.throttledLog, canWriteToStdout);
2107
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2108
+ settleThrottle(renderScheduler.throttled, canWriteToStdout);
3348
2109
  if (canWriteToStdout) {
3349
2110
  await new Promise((resolve) => {
3350
- this.options.stdout.write("", () => {
2111
+ options.stdout.write("", () => {
3351
2112
  resolve();
3352
2113
  });
3353
2114
  });
3354
2115
  return;
3355
2116
  }
3356
- await yieldImmediate();
2117
+ await setImmediate$1();
3357
2118
  }
3358
- clear() {
3359
- if (this.interactive && !this.options.debug) {
3360
- this.log.clear();
3361
- this.log.sync(this.lastOutputToRender || this.lastOutput + "\n");
2119
+ function clear() {
2120
+ if (interactive && !options.debug) {
2121
+ clearLiveOutput();
2122
+ if (isScreenReaderEnabled) accessiblePresenter.sync(lastOutputToRender || lastOutput + "\n");
3362
2123
  }
3363
2124
  }
3364
- patchConsole() {
3365
- if (this.options.debug) return;
2125
+ function installConsolePatch() {
2126
+ if (options.debug) return;
3366
2127
  const restoreConsoleMethods = patchConsole((stream, data) => {
3367
- if (this.options.onCapturedOutput?.(stream, data, "console") === true) return;
3368
- if (stream === "stdout") this.writeToStdout(data);
2128
+ if (options.onCapturedOutput?.(stream, data, "console") === true) return;
2129
+ if (stream === "stdout") writeToStdout(data);
3369
2130
  if (stream === "stderr") {
3370
- if (!data.startsWith("The above error occurred")) this.writeToStderr(data);
2131
+ if (!data.startsWith("The above error occurred")) writeToStderr(data);
3371
2132
  }
3372
2133
  });
3373
- const restoreDirectStdio = this.patchDirectStdio();
3374
- this.restoreConsole = () => {
2134
+ const restoreDirectStdio = patchDirectStdio();
2135
+ restoreConsole = () => {
3375
2136
  restoreConsoleMethods();
3376
2137
  restoreDirectStdio?.();
3377
2138
  };
3378
2139
  }
3379
- patchDirectStdio() {
3380
- const targets = this.captureTargets;
3381
- if (!targets) return;
3382
- const patch = (name, stream) => {
3383
- const originalWrite = stream.write;
3384
- const patchedWrite = (chunk, encodingOrCallback, callback) => {
3385
- const data = typeof chunk === "string" ? chunk : chunk instanceof Uint8Array ? Buffer.from(chunk).toString() : String(chunk);
3386
- this.handleCapturedStdio(name, data);
3387
- (typeof encodingOrCallback === "function" ? encodingOrCallback : typeof callback === "function" ? callback : void 0)?.();
3388
- return true;
3389
- };
3390
- stream.write = patchedWrite;
3391
- return () => {
3392
- stream.write = originalWrite;
3393
- };
3394
- };
3395
- const restoreStdout = patch("stdout", targets.stdout);
3396
- const restoreStderr = patch("stderr", targets.stderr);
2140
+ function patchDirectStdio() {
2141
+ if (!captureTargets) return;
2142
+ const restoreStdout = patchStreamWrite(captureTargets.stdout, (data) => {
2143
+ handleCapturedStdio("stdout", data);
2144
+ });
2145
+ const restoreStderr = patchStreamWrite(captureTargets.stderr, (data) => {
2146
+ handleCapturedStdio("stderr", data);
2147
+ });
3397
2148
  return () => {
3398
2149
  restoreStdout();
3399
2150
  restoreStderr();
3400
2151
  };
3401
2152
  }
3402
- handleCapturedStdio(stream, data) {
3403
- if (this.options.onCapturedOutput?.(stream, data, "stdio") === true) return;
3404
- const parts = (this.capturedStdioTails[stream] + data).split(/\r?\n/);
3405
- this.capturedStdioTails[stream] = parts.pop() ?? "";
2153
+ function handleCapturedStdio(stream, data) {
2154
+ if (options.onCapturedOutput?.(stream, data, "stdio") === true) return;
2155
+ const parts = (capturedStdioTails[stream] + data).split(/\r?\n/);
2156
+ capturedStdioTails[stream] = parts.pop() ?? "";
3406
2157
  if (parts.length === 0) return;
3407
2158
  const payload = parts.join("\n") + "\n";
3408
- if (stream === "stdout") this.writeToStdout(payload);
3409
- else this.writeToStderr(payload);
2159
+ if (stream === "stdout") writeToStdout(payload);
2160
+ else writeToStderr(payload);
3410
2161
  }
3411
- flushCapturedStdio() {
2162
+ function flushCapturedStdio() {
3412
2163
  for (const stream of ["stdout", "stderr"]) {
3413
- const tail = this.capturedStdioTails[stream];
2164
+ const tail = capturedStdioTails[stream];
3414
2165
  if (tail === "") continue;
3415
- this.capturedStdioTails[stream] = "";
3416
- if (stream === "stdout") this.writeToStdout(tail + "\n");
3417
- else this.writeToStderr(tail + "\n");
2166
+ capturedStdioTails[stream] = "";
2167
+ if (stream === "stdout") writeToStdout(tail + "\n");
2168
+ else writeToStderr(tail + "\n");
3418
2169
  }
3419
2170
  }
3420
- registerInputControl(pauseInput, resumeInput) {
3421
- this.pauseInput = pauseInput;
3422
- this.resumeInput = resumeInput;
2171
+ function registerInputControl(pause, resume) {
2172
+ pauseInput = pause;
2173
+ resumeInput = resume;
3423
2174
  }
3424
- async suspendTerminal(callback) {
3425
- this.beginSuspend();
2175
+ async function suspendTerminal(callback) {
2176
+ beginSuspend();
3426
2177
  if (callback) {
3427
2178
  try {
3428
2179
  await callback();
3429
2180
  } finally {
3430
- await this.endSuspend();
2181
+ await endSuspend();
3431
2182
  }
3432
2183
  return;
3433
2184
  }
3434
2185
  const resume = async () => {
3435
- await this.endSuspend();
2186
+ await endSuspend();
3436
2187
  };
3437
2188
  return {
3438
2189
  resume,
3439
2190
  [Symbol.asyncDispose]: resume
3440
2191
  };
3441
2192
  }
3442
- setAlternateScreen(enabled) {
3443
- this.alternateScreen = this.resolveAlternateScreenOption(enabled, this.interactive);
3444
- if (this.alternateScreen) {
3445
- this.writeBestEffort(this.options.stdout, ansiEscapes.enterAlternativeScreen);
3446
- this.writeBestEffort(this.options.stdout, hideCursorEscape);
3447
- }
3448
- }
3449
- resolveInteractiveOption(interactive) {
3450
- return interactive ?? (!isInCi && Boolean(this.options.stdout.isTTY));
3451
- }
3452
- resolveAlternateScreenOption(alternateScreen, interactive) {
3453
- return Boolean(alternateScreen) && interactive && Boolean(this.options.stdout.isTTY);
2193
+ function setAlternateScreen(enabled) {
2194
+ terminal.setAlternateScreen(enabled && interactive && Boolean(options.stdout.isTTY), { hideCursor: true });
3454
2195
  }
3455
- shouldSync() {
3456
- return shouldSynchronize(this.options.stdout, this.interactive);
3457
- }
3458
- writeBestEffort(stream, data) {
3459
- try {
3460
- stream.write(data);
3461
- } catch {}
2196
+ function shouldSync() {
2197
+ return Boolean(options.stdout.isTTY) && interactive;
3462
2198
  }
3463
- async awaitExit() {
2199
+ async function awaitExit() {
3464
2200
  try {
3465
- await this.exitPromise;
2201
+ await exitPromise;
3466
2202
  } catch {}
3467
2203
  }
3468
- hasPendingConcurrentWork() {
3469
- const concurrentContainer = this.container;
2204
+ function hasPendingConcurrentWork() {
2205
+ const concurrentContainer = container;
3470
2206
  return (concurrentContainer.pendingLanes ?? 0) !== 0 && concurrentContainer.callbackNode !== void 0 && concurrentContainer.callbackNode !== null;
3471
2207
  }
3472
- async awaitNextRender() {
3473
- if (!this.nextRenderCommit) {
3474
- let resolveRender;
3475
- const promise = new Promise((resolve) => {
3476
- resolveRender = resolve;
3477
- });
3478
- this.nextRenderCommit = {
3479
- promise,
3480
- resolve: resolveRender
3481
- };
3482
- }
3483
- return this.nextRenderCommit.promise;
2208
+ async function awaitNextRender() {
2209
+ nextRenderCommit ??= Promise.withResolvers();
2210
+ return nextRenderCommit.promise;
3484
2211
  }
3485
- renderInteractiveFrame(output, outputHeight, staticOutput) {
2212
+ function renderInteractiveFrame(output, outputHeight, staticOutput, screen, hasOverflow) {
2213
+ if (!screen) return;
3486
2214
  const hasStaticOutput = staticOutput !== "";
3487
- const isTTY = Boolean(this.options.stdout.isTTY);
3488
- const viewportRows = isTTY ? getWindowSize(this.options.stdout).rows : 24;
2215
+ const isTTY = Boolean(options.stdout.isTTY);
2216
+ const viewportRows = isTTY ? getWindowSize(options.stdout).rows : 24;
3489
2217
  if (isTTY && outputHeight > viewportRows) {
3490
2218
  const lines = output.split("\n");
3491
2219
  output = lines.slice(lines.length - viewportRows).join("\n");
3492
2220
  outputHeight = viewportRows;
2221
+ screen = bottomRows(screen, viewportRows);
3493
2222
  }
3494
- const outputToRender = isTTY && outputHeight >= viewportRows ? output : output + "\n";
2223
+ const isFullscreen = isTTY && outputHeight >= viewportRows;
2224
+ const outputToRender = isFullscreen ? output : output + "\n";
3495
2225
  if (shouldClearTerminalForFrame({
3496
2226
  isTTY,
3497
2227
  viewportRows,
3498
- previousOutputHeight: this.lastOutputHeight,
2228
+ previousOutputHeight: lastOutputHeight,
3499
2229
  nextOutputHeight: outputHeight,
3500
- isUnmounting: this.isUnmounting
2230
+ isUnmounting
3501
2231
  })) {
3502
- const sync = this.shouldSync();
3503
- if (sync) this.options.stdout.write(bsu);
3504
- this.options.stdout.write(ansiEscapes.clearTerminal + this.fullStaticOutput + outputToRender);
3505
- this.lastOutput = output;
3506
- this.lastOutputToRender = outputToRender;
3507
- this.lastOutputHeight = outputHeight;
3508
- this.log.sync(outputToRender);
3509
- if (sync) this.options.stdout.write(esu);
2232
+ const sync = shouldSync();
2233
+ if (sync) options.stdout.write(bsu);
2234
+ options.stdout.write(ansiEscapes.clearTerminal + fullStaticOutput + outputToRender);
2235
+ lastOutput = output;
2236
+ lastOutputToRender = outputToRender;
2237
+ lastOutputHeight = outputHeight;
2238
+ lastScreen = screen;
2239
+ terminal.resetFrame();
2240
+ if (sync) options.stdout.write(esu);
3510
2241
  return;
3511
2242
  }
2243
+ const willPresent = terminal.willPresent(screen, {
2244
+ fullscreen: isFullscreen,
2245
+ forceRewrite: hasStaticOutput || hasOverflow
2246
+ });
2247
+ const sync = shouldSync() && willPresent;
2248
+ if (sync) terminal.write(bsu);
3512
2249
  if (hasStaticOutput) {
3513
- const sync = this.shouldSync();
3514
- if (sync) this.options.stdout.write(bsu);
3515
- this.log.clear();
3516
- this.options.stdout.write(staticOutput);
3517
- this.log(outputToRender);
3518
- if (sync) this.options.stdout.write(esu);
3519
- } else if (output !== this.lastOutput || this.log.isCursorDirty()) this.throttledLog(outputToRender);
3520
- this.lastOutput = output;
3521
- this.lastOutputToRender = outputToRender;
3522
- this.lastOutputHeight = outputHeight;
3523
- }
3524
- initKittyKeyboard() {
3525
- if (!this.options.kittyKeyboard) return;
3526
- const opts = this.options.kittyKeyboard;
2250
+ terminal.clearFrame();
2251
+ terminal.write(staticOutput);
2252
+ }
2253
+ terminal.present(screen, {
2254
+ fullscreen: isFullscreen,
2255
+ forceRewrite: hasStaticOutput || hasOverflow
2256
+ });
2257
+ if (sync) terminal.write(esu);
2258
+ lastOutput = output;
2259
+ lastOutputToRender = outputToRender;
2260
+ lastOutputHeight = outputHeight;
2261
+ lastScreen = screen;
2262
+ }
2263
+ function initKittyKeyboard() {
2264
+ if (!options.kittyKeyboard) return;
2265
+ const opts = options.kittyKeyboard;
3527
2266
  const mode = opts.mode ?? "auto";
3528
2267
  if (mode === "disabled") return;
3529
2268
  const flags = opts.flags ?? ["disambiguateEscapeCodes"];
3530
2269
  if (mode === "enabled") {
3531
- if (isTty(this.options.stdin) && this.options.stdout.isTTY) this.enableKittyProtocol(flags);
2270
+ if (isTty(options.stdin) && options.stdout.isTTY) enableKittyProtocol(flags);
3532
2271
  return;
3533
2272
  }
3534
- if (!this.interactive || !isTty(this.options.stdin) || !this.options.stdout.isTTY) return;
3535
- this.confirmKittySupport(flags);
3536
- }
3537
- confirmKittySupport(flags) {
3538
- const { stdin, stdout } = this.options;
3539
- let responseBuffer = [];
3540
- const cleanup = () => {
3541
- this.cancelKittyDetection = void 0;
3542
- clearTimeout(timer);
3543
- stdin.removeListener("data", onData);
3544
- const remaining = stripKittyQueryResponsesAndTrailingPartial(responseBuffer);
3545
- responseBuffer = [];
3546
- if (remaining.length > 0) stdin.unshift(Uint8Array.from(remaining));
3547
- };
3548
- const onData = (data) => {
3549
- const chunk = typeof data === "string" ? textEncoder.encode(data) : data;
3550
- for (const byte of chunk) responseBuffer.push(byte);
3551
- if (hasCompleteKittyQueryResponse(responseBuffer)) {
3552
- cleanup();
3553
- if (!this.isUnmounted) this.enableKittyProtocol(flags);
3554
- }
3555
- };
3556
- stdin.on("data", onData);
3557
- const timer = setTimeout(cleanup, 200);
3558
- this.cancelKittyDetection = cleanup;
3559
- stdout.write(ansiEscapes.kittyQuery);
2273
+ if (!interactive || !isTty(options.stdin) || !options.stdout.isTTY) return;
2274
+ cancelKittyDetection = detectKittySupport(options.stdin, options.stdout, () => {
2275
+ cancelKittyDetection = void 0;
2276
+ if (!isUnmounted) enableKittyProtocol(flags);
2277
+ });
3560
2278
  }
3561
- enableKittyProtocol(flags) {
3562
- this.options.stdout.write(ansiEscapes.pushKittyKeyboard(resolveFlags(flags)));
3563
- this.kittyProtocolEnabled = true;
3564
- this.kittyFlags = flags;
2279
+ function enableKittyProtocol(flags) {
2280
+ options.stdout.write(ansiEscapes.pushKittyKeyboard(resolveFlags(flags)));
2281
+ kittyProtocolEnabled = true;
2282
+ kittyFlags = flags;
3565
2283
  }
3566
- beginSuspend() {
3567
- if (this.isSuspended) throw new Error("The terminal is already suspended. Resume the current suspension before suspending again.");
3568
- this.isSuspended = true;
3569
- if (!this.interactive || this.isUnmounted || this.isUnmounting) return;
2284
+ function beginSuspend() {
2285
+ terminal.beginSuspension();
2286
+ if (!interactive || isUnmounted || isUnmounting) return;
3570
2287
  try {
3571
- const { stdout } = this.options;
3572
- const { canWriteToStdout } = getWritableStreamState(stdout);
3573
- settleThrottle(this.throttledOnRender, canWriteToStdout);
3574
- settleThrottle(this.throttledLog, canWriteToStdout);
3575
- if (canWriteToStdout) this.flushCapturedStdio();
2288
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
2289
+ settleThrottle(renderScheduler.throttled, canWriteToStdout);
2290
+ if (canWriteToStdout) flushCapturedStdio();
3576
2291
  if (canWriteToStdout) {
3577
- this.log.clear();
3578
- this.log.done();
3579
- if (this.kittyProtocolEnabled) this.writeBestEffort(this.options.stdout, ansiEscapes.popKittyKeyboard);
3580
- if (this.alternateScreen) this.writeBestEffort(this.options.stdout, ansiEscapes.exitAlternativeScreen);
2292
+ clearLiveOutput();
2293
+ finishLiveOutput();
2294
+ if (kittyProtocolEnabled) writeBestEffort(options.stdout, ansiEscapes.popKittyKeyboard);
2295
+ if (terminal.alternateScreen) writeBestEffort(options.stdout, ansiEscapes.exitAlternativeScreen);
3581
2296
  }
3582
- this.pauseInput?.();
2297
+ pauseInput?.();
3583
2298
  } catch (error) {
3584
- this.isSuspended = false;
2299
+ terminal.resume();
3585
2300
  try {
3586
- this.resumeInput?.();
2301
+ resumeInput?.();
3587
2302
  } catch {}
3588
2303
  throw error;
3589
2304
  }
3590
2305
  }
3591
- async endSuspend() {
3592
- if (!this.isSuspended) return;
3593
- this.isSuspended = false;
3594
- this.resumeInput?.();
3595
- if (!this.interactive || this.isUnmounted || this.isUnmounting) return;
3596
- const { stdout } = this.options;
3597
- const { canWriteToStdout } = getWritableStreamState(stdout);
2306
+ async function endSuspend() {
2307
+ if (!terminal.suspended) return;
2308
+ terminal.resume();
2309
+ resumeInput?.();
2310
+ if (!interactive || isUnmounted || isUnmounting) return;
2311
+ const { canWriteToStdout } = getWritableStreamState(options.stdout);
3598
2312
  if (canWriteToStdout) {
3599
- if (this.alternateScreen) this.writeBestEffort(this.options.stdout, ansiEscapes.enterAlternativeScreen);
3600
- if (this.kittyProtocolEnabled && this.kittyFlags) this.writeBestEffort(this.options.stdout, ansiEscapes.pushKittyKeyboard(resolveFlags(this.kittyFlags)));
2313
+ if (terminal.alternateScreen) writeBestEffort(options.stdout, ansiEscapes.enterAlternativeScreen);
2314
+ if (kittyProtocolEnabled && kittyFlags) writeBestEffort(options.stdout, ansiEscapes.pushKittyKeyboard(resolveFlags(kittyFlags)));
3601
2315
  }
3602
- this.lastOutput = "";
3603
- this.lastOutputToRender = "";
3604
- this.lastOutputHeight = 0;
3605
- this.log.reset();
2316
+ lastOutput = "";
2317
+ lastOutputToRender = "";
2318
+ lastOutputHeight = 0;
2319
+ resetLiveOutput();
3606
2320
  try {
3607
- this.calculateLayout();
3608
- this.onRender();
3609
- await this.waitUntilRenderFlush();
2321
+ calculateLayout();
2322
+ onRender();
2323
+ await waitUntilRenderFlush();
3610
2324
  } catch {}
3611
2325
  }
2326
+ return {
2327
+ render,
2328
+ unmount,
2329
+ waitUntilExit,
2330
+ waitUntilRenderFlush,
2331
+ clear,
2332
+ copyToClipboard: (text, selection) => terminal.copyToClipboard(text, selection),
2333
+ setProgress: (state, value) => terminal.setProgress(state, value)
2334
+ };
3612
2335
  };
3613
2336
  //#endregion
3614
2337
  //#region src/render.ts
@@ -3624,24 +2347,25 @@ const render = (node, options) => {
3624
2347
  exitOnCtrlC: true,
3625
2348
  patchConsole: true,
3626
2349
  maxFps: 30,
3627
- incrementalRendering: false,
3628
2350
  concurrent: false,
3629
2351
  alternateScreen: false,
3630
2352
  ...getOptions(options)
3631
2353
  };
3632
- const instance = getInstance(inkOptions.stdout, () => new Ink(inkOptions));
2354
+ const instance = getInstance(inkOptions.stdout, () => createInk(inkOptions));
3633
2355
  instance.render(node);
3634
2356
  return {
3635
- rerender: instance.render.bind(instance),
2357
+ rerender: instance.render,
3636
2358
  unmount() {
3637
2359
  instance.unmount();
3638
2360
  },
3639
- waitUntilExit: instance.waitUntilExit.bind(instance),
3640
- waitUntilRenderFlush: instance.waitUntilRenderFlush.bind(instance),
2361
+ waitUntilExit: instance.waitUntilExit,
2362
+ waitUntilRenderFlush: instance.waitUntilRenderFlush,
3641
2363
  cleanup() {
3642
2364
  instance.unmount();
3643
2365
  },
3644
- clear: instance.clear.bind(instance)
2366
+ clear: instance.clear,
2367
+ copyToClipboard: instance.copyToClipboard,
2368
+ setProgress: instance.setProgress
3645
2369
  };
3646
2370
  };
3647
2371
  const getOptions = (stdout = {}) => {
@@ -3692,6 +2416,7 @@ console.log(output);
3692
2416
  */
3693
2417
  const renderToString = (node, options) => {
3694
2418
  const columns = options?.columns ?? 80;
2419
+ const colorProfile = options?.colorProfile ?? colorProfileFromLevel(detectColorLevel());
3695
2420
  const rootNode = createNode("ink-root");
3696
2421
  let capturedStaticOutput = "";
3697
2422
  rootNode.onComputeLayout = () => {
@@ -3699,7 +2424,11 @@ const renderToString = (node, options) => {
3699
2424
  rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
3700
2425
  };
3701
2426
  rootNode.onImmediateRender = () => {
3702
- const { staticOutput } = renderer(rootNode, false);
2427
+ const { staticScreen } = renderFrame(rootNode, false, { colorProfile });
2428
+ const staticOutput = staticScreen ? `${serializeScreen(staticScreen, {
2429
+ colorProfile,
2430
+ styles: colorProfile !== "none"
2431
+ })}\n` : "";
3703
2432
  if (staticOutput && staticOutput !== "\n") capturedStaticOutput += staticOutput;
3704
2433
  };
3705
2434
  let uncaughtError;
@@ -3708,7 +2437,11 @@ const renderToString = (node, options) => {
3708
2437
  }, () => {}, () => {}, () => {});
3709
2438
  reconciler.updateContainerSync(node, container, null, () => {});
3710
2439
  reconciler.flushSyncWork();
3711
- const { output } = renderer(rootNode, false);
2440
+ const { screen } = renderFrame(rootNode, false, { colorProfile });
2441
+ const output = screen ? serializeScreen(screen, {
2442
+ colorProfile,
2443
+ styles: colorProfile !== "none"
2444
+ }) : "";
3712
2445
  reconciler.updateContainerSync(null, container, null, () => {});
3713
2446
  reconciler.flushSyncWork();
3714
2447
  if (uncaughtError !== void 0) throw uncaughtError instanceof Error ? uncaughtError : new Error(String(uncaughtError));
@@ -3717,6 +2450,30 @@ const renderToString = (node, options) => {
3717
2450
  return normalizedStaticOutput || output;
3718
2451
  };
3719
2452
  //#endregion
2453
+ //#region src/components/AnsiText.tsx
2454
+ /** @jsxImportSource react */
2455
+ /**
2456
+ * Renders explicitly trusted ANSI-styled output as structured terminal cells.
2457
+ * Ordinary `Text` continues to strip terminal control sequences.
2458
+ */
2459
+ function AnsiText({ children, wrap = "wrap", "aria-label": ariaLabel, "aria-hidden": ariaHidden = false }) {
2460
+ const { isScreenReaderEnabled } = useContext(accessibilityContext);
2461
+ if (isScreenReaderEnabled) {
2462
+ if (ariaHidden) return null;
2463
+ return /* @__PURE__ */ jsx(Text, { children: ariaLabel ?? stripAnsi(children) });
2464
+ }
2465
+ return /* @__PURE__ */ jsx("ink-text", {
2466
+ style: {
2467
+ flexGrow: 0,
2468
+ flexShrink: 1,
2469
+ flexDirection: "row",
2470
+ textWrap: wrap
2471
+ },
2472
+ internal_ansi: true,
2473
+ children
2474
+ });
2475
+ }
2476
+ //#endregion
3720
2477
  //#region src/components/Static.tsx
3721
2478
  /** @jsxImportSource react */
3722
2479
  /**
@@ -3769,6 +2526,44 @@ function Transform({ children, transform, accessibilityLabel }) {
3769
2526
  });
3770
2527
  }
3771
2528
  //#endregion
2529
+ //#region src/hooks/use-stdout.ts
2530
+ /**
2531
+ A React hook that returns the stdout stream where Ink renders your app.
2532
+ */
2533
+ const useStdout = () => useContext(StdoutContext);
2534
+ //#endregion
2535
+ //#region src/components/Hyperlink.tsx
2536
+ /**
2537
+ A clickable OSC 8 hyperlink — the counterpart to the router's `<Link>`, which
2538
+ navigates between screens. On terminals without hyperlink support it falls
2539
+ back to `text (url)`.
2540
+
2541
+ ```tsx
2542
+ <Hyperlink url="https://example.com">Documentation</Hyperlink>
2543
+ ```
2544
+ */
2545
+ function Hyperlink({ url, fallback = true, children, ...textProps }) {
2546
+ const { stdout } = useStdout();
2547
+ if (!detectHyperlinkSupport(stdout)) return /* @__PURE__ */ jsxs(Text, {
2548
+ ...textProps,
2549
+ children: [children, fallback ? /* @__PURE__ */ jsxs(Text, {
2550
+ dimColor: true,
2551
+ children: [
2552
+ " (",
2553
+ url,
2554
+ ")"
2555
+ ]
2556
+ }) : null]
2557
+ });
2558
+ return /* @__PURE__ */ jsx(Transform, {
2559
+ transform: (text) => link(text, url),
2560
+ children: /* @__PURE__ */ jsx(Text, {
2561
+ ...textProps,
2562
+ children
2563
+ })
2564
+ });
2565
+ }
2566
+ //#endregion
3772
2567
  //#region src/components/Newline.tsx
3773
2568
  /**
3774
2569
  Adds one or more newline (`\n`) characters. Must be used within `<Text>` components.
@@ -3788,488 +2583,60 @@ function Spacer() {
3788
2583
  return /* @__PURE__ */ jsx(Box, { flexGrow: 1 });
3789
2584
  }
3790
2585
  //#endregion
3791
- //#region src/parse-keypress.ts
3792
- const textDecoder = new TextDecoder();
3793
- const metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
3794
- const fnKeyRe = /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
3795
- const keyName = {
3796
- OP: "f1",
3797
- OQ: "f2",
3798
- OR: "f3",
3799
- OS: "f4",
3800
- "[P": "f1",
3801
- "[Q": "f2",
3802
- "[R": "f3",
3803
- "[S": "f4",
3804
- "[11~": "f1",
3805
- "[12~": "f2",
3806
- "[13~": "f3",
3807
- "[14~": "f4",
3808
- "[[A": "f1",
3809
- "[[B": "f2",
3810
- "[[C": "f3",
3811
- "[[D": "f4",
3812
- "[[E": "f5",
3813
- "[15~": "f5",
3814
- "[17~": "f6",
3815
- "[18~": "f7",
3816
- "[19~": "f8",
3817
- "[20~": "f9",
3818
- "[21~": "f10",
3819
- "[23~": "f11",
3820
- "[24~": "f12",
3821
- "[A": "up",
3822
- "[B": "down",
3823
- "[C": "right",
3824
- "[D": "left",
3825
- "[E": "clear",
3826
- "[F": "end",
3827
- "[H": "home",
3828
- OA: "up",
3829
- OB: "down",
3830
- OC: "right",
3831
- OD: "left",
3832
- OE: "clear",
3833
- OF: "end",
3834
- OH: "home",
3835
- "[1~": "home",
3836
- "[2~": "insert",
3837
- "[3~": "delete",
3838
- "[4~": "end",
3839
- "[5~": "pageup",
3840
- "[6~": "pagedown",
3841
- "[[5~": "pageup",
3842
- "[[6~": "pagedown",
3843
- "[7~": "home",
3844
- "[8~": "end",
3845
- "[a": "up",
3846
- "[b": "down",
3847
- "[c": "right",
3848
- "[d": "left",
3849
- "[e": "clear",
3850
- "[2$": "insert",
3851
- "[3$": "delete",
3852
- "[5$": "pageup",
3853
- "[6$": "pagedown",
3854
- "[7$": "home",
3855
- "[8$": "end",
3856
- Oa: "up",
3857
- Ob: "down",
3858
- Oc: "right",
3859
- Od: "left",
3860
- Oe: "clear",
3861
- "[2^": "insert",
3862
- "[3^": "delete",
3863
- "[5^": "pageup",
3864
- "[6^": "pagedown",
3865
- "[7^": "home",
3866
- "[8^": "end",
3867
- "[Z": "tab"
3868
- };
3869
- const nonAlphanumericKeys = [...Object.values(keyName), "backspace"];
3870
- const isShiftKey = (code) => {
3871
- return [
3872
- "[a",
3873
- "[b",
3874
- "[c",
3875
- "[d",
3876
- "[e",
3877
- "[2$",
3878
- "[3$",
3879
- "[5$",
3880
- "[6$",
3881
- "[7$",
3882
- "[8$",
3883
- "[Z"
3884
- ].includes(code);
3885
- };
3886
- const isCtrlKey = (code) => {
3887
- return [
3888
- "Oa",
3889
- "Ob",
3890
- "Oc",
3891
- "Od",
3892
- "Oe",
3893
- "[2^",
3894
- "[3^",
3895
- "[5^",
3896
- "[6^",
3897
- "[7^",
3898
- "[8^"
3899
- ].includes(code);
3900
- };
3901
- const kittyKeyRe = /^\x1b\[(\d+)(?:;(\d+)(?::(\d+))?(?:;([\d:]+))?)?u$/;
3902
- const kittySpecialKeyRe = /^\x1b\[(\d+);(\d+):(\d+)([A-Za-z~])$/;
3903
- const kittySpecialLetterKeys = {
3904
- A: "up",
3905
- B: "down",
3906
- C: "right",
3907
- D: "left",
3908
- E: "clear",
3909
- F: "end",
3910
- H: "home",
3911
- P: "f1",
3912
- Q: "f2",
3913
- R: "f3",
3914
- S: "f4"
3915
- };
3916
- const kittySpecialNumberKeys = {
3917
- 2: "insert",
3918
- 3: "delete",
3919
- 5: "pageup",
3920
- 6: "pagedown",
3921
- 7: "home",
3922
- 8: "end",
3923
- 11: "f1",
3924
- 12: "f2",
3925
- 13: "f3",
3926
- 14: "f4",
3927
- 15: "f5",
3928
- 17: "f6",
3929
- 18: "f7",
3930
- 19: "f8",
3931
- 20: "f9",
3932
- 21: "f10",
3933
- 23: "f11",
3934
- 24: "f12"
3935
- };
3936
- const kittyCodepointNames = {
3937
- 27: "escape",
3938
- 9: "tab",
3939
- 127: "backspace",
3940
- 8: "backspace",
3941
- 57358: "capslock",
3942
- 57359: "scrolllock",
3943
- 57360: "numlock",
3944
- 57361: "printscreen",
3945
- 57362: "pause",
3946
- 57363: "menu",
3947
- 57376: "f13",
3948
- 57377: "f14",
3949
- 57378: "f15",
3950
- 57379: "f16",
3951
- 57380: "f17",
3952
- 57381: "f18",
3953
- 57382: "f19",
3954
- 57383: "f20",
3955
- 57384: "f21",
3956
- 57385: "f22",
3957
- 57386: "f23",
3958
- 57387: "f24",
3959
- 57388: "f25",
3960
- 57389: "f26",
3961
- 57390: "f27",
3962
- 57391: "f28",
3963
- 57392: "f29",
3964
- 57393: "f30",
3965
- 57394: "f31",
3966
- 57395: "f32",
3967
- 57396: "f33",
3968
- 57397: "f34",
3969
- 57398: "f35",
3970
- 57399: "kp0",
3971
- 57400: "kp1",
3972
- 57401: "kp2",
3973
- 57402: "kp3",
3974
- 57403: "kp4",
3975
- 57404: "kp5",
3976
- 57405: "kp6",
3977
- 57406: "kp7",
3978
- 57407: "kp8",
3979
- 57408: "kp9",
3980
- 57409: "kpdecimal",
3981
- 57410: "kpdivide",
3982
- 57411: "kpmultiply",
3983
- 57412: "kpsubtract",
3984
- 57413: "kpadd",
3985
- 57414: "kpenter",
3986
- 57415: "kpequal",
3987
- 57416: "kpseparator",
3988
- 57417: "kpleft",
3989
- 57418: "kpright",
3990
- 57419: "kpup",
3991
- 57420: "kpdown",
3992
- 57421: "kppageup",
3993
- 57422: "kppagedown",
3994
- 57423: "kphome",
3995
- 57424: "kpend",
3996
- 57425: "kpinsert",
3997
- 57426: "kpdelete",
3998
- 57427: "kpbegin",
3999
- 57428: "mediaplay",
4000
- 57429: "mediapause",
4001
- 57430: "mediaplaypause",
4002
- 57431: "mediareverse",
4003
- 57432: "mediastop",
4004
- 57433: "mediafastforward",
4005
- 57434: "mediarewind",
4006
- 57435: "mediatracknext",
4007
- 57436: "mediatrackprevious",
4008
- 57437: "mediarecord",
4009
- 57438: "lowervolume",
4010
- 57439: "raisevolume",
4011
- 57440: "mutevolume",
4012
- 57441: "leftshift",
4013
- 57442: "leftcontrol",
4014
- 57443: "leftalt",
4015
- 57444: "leftsuper",
4016
- 57445: "lefthyper",
4017
- 57446: "leftmeta",
4018
- 57447: "rightshift",
4019
- 57448: "rightcontrol",
4020
- 57449: "rightalt",
4021
- 57450: "rightsuper",
4022
- 57451: "righthyper",
4023
- 57452: "rightmeta",
4024
- 57453: "isoLevel3Shift",
4025
- 57454: "isoLevel5Shift"
4026
- };
4027
- const isValidCodepoint = (cp) => cp >= 0 && cp <= 1114111 && !(cp >= 55296 && cp <= 57343);
4028
- const safeFromCodePoint = (cp) => isValidCodepoint(cp) ? String.fromCodePoint(cp) : "?";
4029
- function resolveEventType(value) {
4030
- if (value === 3) return "release";
4031
- if (value === 2) return "repeat";
4032
- return "press";
4033
- }
4034
- function parseKittyModifiers(modifiers) {
4035
- return {
4036
- ctrl: !!(modifiers & kittyModifiers.ctrl),
4037
- shift: !!(modifiers & kittyModifiers.shift),
4038
- meta: !!(modifiers & (kittyModifiers.meta | kittyModifiers.alt)),
4039
- super: !!(modifiers & kittyModifiers.super),
4040
- hyper: !!(modifiers & kittyModifiers.hyper),
4041
- capsLock: !!(modifiers & kittyModifiers.capsLock),
4042
- numLock: !!(modifiers & kittyModifiers.numLock)
4043
- };
4044
- }
4045
- const parseKittyKeypress = (s) => {
4046
- const match = kittyKeyRe.exec(s);
4047
- if (!match) return null;
4048
- const codepoint = parseInt(match[1], 10);
4049
- const modifiers = match[2] ? Math.max(0, parseInt(match[2], 10) - 1) : 0;
4050
- const eventType = match[3] ? parseInt(match[3], 10) : 1;
4051
- const textField = match[4];
4052
- if (!isValidCodepoint(codepoint)) return null;
4053
- let text;
4054
- if (textField) text = textField.split(":").map((cp) => safeFromCodePoint(parseInt(cp, 10))).join("");
4055
- let name;
4056
- let isPrintable;
4057
- if (codepoint === 32) {
4058
- name = "space";
4059
- isPrintable = true;
4060
- } else if (codepoint === 13) {
4061
- name = "return";
4062
- isPrintable = true;
4063
- } else if (kittyCodepointNames[codepoint]) {
4064
- name = kittyCodepointNames[codepoint];
4065
- isPrintable = false;
4066
- } else if (codepoint >= 1 && codepoint <= 26) {
4067
- name = String.fromCodePoint(codepoint + 96);
4068
- isPrintable = false;
4069
- } else {
4070
- name = safeFromCodePoint(codepoint).toLowerCase();
4071
- isPrintable = true;
4072
- }
4073
- if (isPrintable && !text) text = safeFromCodePoint(codepoint);
4074
- return {
4075
- name,
4076
- ...parseKittyModifiers(modifiers),
4077
- eventType: resolveEventType(eventType),
4078
- sequence: s,
4079
- raw: s,
4080
- isKittyProtocol: true,
4081
- isPrintable,
4082
- text
4083
- };
4084
- };
4085
- const parseKittySpecialKey = (s) => {
4086
- const match = kittySpecialKeyRe.exec(s);
4087
- if (!match) return null;
4088
- const number = parseInt(match[1], 10);
4089
- const modifiers = Math.max(0, parseInt(match[2], 10) - 1);
4090
- const eventType = parseInt(match[3], 10);
4091
- const terminator = match[4];
4092
- const name = terminator === "~" ? kittySpecialNumberKeys[number] : kittySpecialLetterKeys[terminator];
4093
- if (!name) return null;
4094
- return {
4095
- name,
4096
- ...parseKittyModifiers(modifiers),
4097
- eventType: resolveEventType(eventType),
4098
- sequence: s,
4099
- raw: s,
4100
- isKittyProtocol: true,
4101
- isPrintable: false
4102
- };
4103
- };
4104
- const parseKeypress = (s = "") => {
4105
- let parts;
4106
- if (s instanceof Uint8Array) {
4107
- if (s[0] > 127 && s[1] === void 0) {
4108
- s[0] -= 128;
4109
- s = "\x1B" + textDecoder.decode(s);
4110
- } else s = textDecoder.decode(s);
4111
- } else if (s !== void 0 && typeof s !== "string") s = String(s);
4112
- else if (!s) s = "";
4113
- const kittyResult = parseKittyKeypress(s);
4114
- if (kittyResult) return kittyResult;
4115
- const kittySpecialResult = parseKittySpecialKey(s);
4116
- if (kittySpecialResult) return kittySpecialResult;
4117
- if (kittyKeyRe.test(s)) return {
4118
- name: "",
4119
- ctrl: false,
4120
- meta: false,
4121
- shift: false,
4122
- sequence: s,
4123
- raw: s,
4124
- isKittyProtocol: true,
4125
- isPrintable: false
4126
- };
4127
- const key = {
4128
- name: "",
4129
- ctrl: false,
4130
- meta: false,
4131
- shift: false,
4132
- sequence: s,
4133
- raw: s
4134
- };
4135
- key.sequence = key.sequence || s || key.name;
4136
- if (s === "\r" || s === "\x1B\r") {
4137
- key.raw = void 0;
4138
- key.name = "return";
4139
- key.meta = s.length === 2;
4140
- } else if (s === "\n") key.name = "enter";
4141
- else if (s === " ") key.name = "tab";
4142
- else if (s === "\b" || s === "\x1B\b") {
4143
- key.name = "backspace";
4144
- key.meta = s.charAt(0) === "\x1B";
4145
- } else if (s === "" || s === "\x1B") {
4146
- key.name = "backspace";
4147
- key.meta = s.charAt(0) === "\x1B";
4148
- } else if (s === "\x1B" || s === "\x1B\x1B") {
4149
- key.name = "escape";
4150
- key.meta = s.length === 2;
4151
- } else if (s === " " || s === "\x1B ") {
4152
- key.name = "space";
4153
- key.meta = s.length === 2;
4154
- } else if (s.length === 1 && s <= "") {
4155
- key.name = String.fromCharCode(s.charCodeAt(0) + "a".charCodeAt(0) - 1);
4156
- key.ctrl = true;
4157
- } else if (s.length === 1 && s >= "0" && s <= "9") key.name = "number";
4158
- else if (s.length === 1 && s >= "a" && s <= "z") key.name = s;
4159
- else if (s.length === 1 && s >= "A" && s <= "Z") {
4160
- key.name = s.toLowerCase();
4161
- key.shift = true;
4162
- } else if (parts = metaKeyCodeRe.exec(s)) {
4163
- key.name = parts[1].toLowerCase();
4164
- key.meta = true;
4165
- key.shift = /^[A-Z]$/.test(parts[1]);
4166
- } else if (parts = fnKeyRe.exec(s)) {
4167
- const segs = [...s];
4168
- if (segs[0] === "\x1B" && segs[1] === "\x1B") key.meta = true;
4169
- const code = [
4170
- parts[1],
4171
- parts[2],
4172
- parts[4],
4173
- parts[6]
4174
- ].filter(Boolean).join("");
4175
- const modifier = (parts[3] || parts[5] || 1) - 1;
4176
- key.ctrl = !!(modifier & 4);
4177
- key.meta = key.meta || !!(modifier & 10);
4178
- key.shift = !!(modifier & 1);
4179
- key.code = code;
4180
- key.name = keyName[code] ?? "";
4181
- key.shift = isShiftKey(code) || key.shift;
4182
- key.ctrl = isCtrlKey(code) || key.ctrl;
4183
- }
4184
- return key;
4185
- };
4186
- //#endregion
4187
- //#region src/hooks/use-stdin.ts
2586
+ //#region src/hooks/use-capabilities.ts
4188
2587
  /**
4189
- A React hook that returns the stdin stream and stdin-related utilities.
2588
+ Returns everything knowable about the terminal: size, identity, platform,
2589
+ color depth, theme, and feature support.
2590
+
2591
+ A thin wrapper over the framework-free capabilities store (`getCapabilities`):
2592
+ environment-derived facts are available immediately; facts only the terminal
2593
+ itself can answer fill in after a lazy one-time query, and re-mounting
2594
+ consumers refreshes the dynamic facts (theme colors, pixel geometry).
2595
+ Re-renders on terminal resize and whenever query answers arrive.
4190
2596
  */
4191
- const useStdin = () => useContext(StdinContext);
4192
- const useStdinContext = () => useContext(StdinContext);
4193
- //#endregion
4194
- //#region src/hooks/use-input.ts
2597
+ const useCapabilities = () => {
2598
+ const { stdout } = useStdout();
2599
+ const { stdin } = useStdinContext();
2600
+ const store = getCapabilities(stdin, stdout);
2601
+ const capabilities = useSyncExternalStore(store.subscribe, () => store.current);
2602
+ useEffect(() => {
2603
+ store.query();
2604
+ }, [store]);
2605
+ return capabilities;
2606
+ };
4195
2607
  /**
4196
- A React hook that returns `void` and handles user input.
4197
- It's a more convenient alternative to using `StdinContext` and listening for `data` events. The callback you pass to `useInput` is called for each character when the user enters any input. However, if the user pastes text and it's more than one character, the callback will be called only once, and the whole string will be passed as `input`.
4198
-
4199
- ```
4200
- import {useInput} from 'ink';
2608
+ Calls `onChange` whenever the terminal changes: resizes (including in-band
2609
+ pixel geometry), color scheme switches, window focus, and query answers
2610
+ arriving. The React wrapper over `capabilities.subscribe()` for side effects —
2611
+ for rendering, use `useCapabilities` instead.
4201
2612
 
4202
- const UserInput = () => {
4203
- useInput((input, key) => {
4204
- if (input === 'q') {
4205
- // Exit program
4206
- }
2613
+ The callback always sees the latest render's closure and changing it does not
2614
+ resubscribe. Both the new and previous snapshot are passed, so handlers can
2615
+ react to the specific change:
4207
2616
 
4208
- if (key.leftArrow) {
4209
- // Left arrow key pressed
2617
+ ```tsx
2618
+ useCapabilitiesChange((next, previous) => {
2619
+ if (next.theme.appearance !== previous.theme.appearance) {
2620
+ // re-theme
4210
2621
  }
4211
2622
  });
4212
-
4213
- return …
4214
- };
4215
2623
  ```
4216
2624
  */
4217
- const useInput = (inputHandler, options = {}) => {
4218
- const { setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdinContext();
2625
+ const useCapabilitiesChange = (onChange) => {
2626
+ const { stdout } = useStdout();
2627
+ const { stdin } = useStdinContext();
2628
+ const store = getCapabilities(stdin, stdout);
2629
+ const handleChange = useEffectEvent(onChange);
4219
2630
  useEffect(() => {
4220
- if (options.isActive === false) return;
4221
- setRawMode(true);
4222
- return () => {
4223
- setRawMode(false);
4224
- };
4225
- }, [options.isActive, setRawMode]);
4226
- const handleData = useEffectEvent((data) => {
4227
- const keypress = parseKeypress(data);
4228
- const key = {
4229
- upArrow: keypress.name === "up",
4230
- downArrow: keypress.name === "down",
4231
- leftArrow: keypress.name === "left",
4232
- rightArrow: keypress.name === "right",
4233
- pageDown: keypress.name === "pagedown",
4234
- pageUp: keypress.name === "pageup",
4235
- home: keypress.name === "home",
4236
- end: keypress.name === "end",
4237
- return: keypress.name === "return",
4238
- escape: keypress.name === "escape",
4239
- ctrl: keypress.ctrl,
4240
- shift: keypress.shift,
4241
- tab: keypress.name === "tab",
4242
- backspace: keypress.name === "backspace",
4243
- delete: keypress.name === "delete",
4244
- meta: keypress.meta,
4245
- super: keypress.super ?? false,
4246
- hyper: keypress.hyper ?? false,
4247
- capsLock: keypress.capsLock ?? false,
4248
- numLock: keypress.numLock ?? false,
4249
- eventType: keypress.eventType
4250
- };
4251
- let input;
4252
- if (keypress.isKittyProtocol) {
4253
- if (keypress.isPrintable) input = keypress.text ?? keypress.name;
4254
- else if (keypress.ctrl && keypress.name.length === 1) input = keypress.name;
4255
- else input = "";
4256
- } else if (keypress.ctrl) input = keypress.name ?? "";
4257
- else input = keypress.sequence;
4258
- if (!keypress.isKittyProtocol && nonAlphanumericKeys.includes(keypress.name)) input = "";
4259
- if (input.startsWith("\x1B")) input = input.slice(1);
4260
- if (input.length === 1 && /[A-Z]/.test(input)) key.shift = true;
4261
- if (input === "c" && key.ctrl && internal_exitOnCtrlC) return;
4262
- reconciler.discreteUpdates(() => {
4263
- inputHandler(input, key);
2631
+ let previous = store.current;
2632
+ const unsubscribe = store.subscribe((next) => {
2633
+ const before = previous;
2634
+ previous = next;
2635
+ handleChange(next, before);
4264
2636
  });
4265
- });
4266
- useEffect(() => {
4267
- if (options.isActive === false) return;
4268
- internal_eventEmitter.on("input", handleData);
4269
- return () => {
4270
- internal_eventEmitter.removeListener("input", handleData);
4271
- };
4272
- }, [options.isActive, internal_eventEmitter]);
2637
+ store.query();
2638
+ return unsubscribe;
2639
+ }, [store]);
4273
2640
  };
4274
2641
  //#endregion
4275
2642
  //#region src/hooks/use-paste.ts
@@ -4333,66 +2700,12 @@ A React hook that returns app lifecycle methods like `exit()` and `waitUntilRend
4333
2700
  */
4334
2701
  const useApp = () => useContext(AppContext);
4335
2702
  //#endregion
4336
- //#region src/hooks/use-stdout.ts
4337
- /**
4338
- A React hook that returns the stdout stream where Ink renders your app.
4339
- */
4340
- const useStdout = () => useContext(StdoutContext);
4341
- //#endregion
4342
2703
  //#region src/hooks/use-stderr.ts
4343
2704
  /**
4344
2705
  A React hook that returns the stderr stream.
4345
2706
  */
4346
2707
  const useStderr = () => useContext(StderrContext);
4347
2708
  //#endregion
4348
- //#region src/hooks/use-focus.ts
4349
- /**
4350
- A React hook that returns focus state and focus controls for the current component.
4351
- A component that uses the `useFocus` hook becomes "focusable" to Ink, so when the user presses <kbd>Tab</kbd>, Ink will switch focus to this component. If there are multiple components that execute the `useFocus` hook, focus will be given to them in the order in which these components are rendered.
4352
- */
4353
- const useFocus = ({ isActive = true, autoFocus = false, id: customId } = {}) => {
4354
- const { isRawModeSupported, setRawMode } = useStdin();
4355
- const { activeId, add, remove, activate, deactivate, focus } = useContext(FocusContext);
4356
- const id = useMemo(() => {
4357
- return customId ?? Math.random().toString().slice(2, 7);
4358
- }, [customId]);
4359
- useEffect(() => {
4360
- add(id, { autoFocus });
4361
- return () => {
4362
- remove(id);
4363
- };
4364
- }, [
4365
- id,
4366
- autoFocus,
4367
- add,
4368
- remove
4369
- ]);
4370
- useEffect(() => {
4371
- if (isActive) activate(id);
4372
- else deactivate(id);
4373
- }, [
4374
- isActive,
4375
- id,
4376
- activate,
4377
- deactivate
4378
- ]);
4379
- useEffect(() => {
4380
- if (!isRawModeSupported || !isActive) return;
4381
- setRawMode(true);
4382
- return () => {
4383
- setRawMode(false);
4384
- };
4385
- }, [
4386
- isActive,
4387
- isRawModeSupported,
4388
- setRawMode
4389
- ]);
4390
- return {
4391
- isFocused: Boolean(id) && activeId === id,
4392
- focus
4393
- };
4394
- };
4395
- //#endregion
4396
2709
  //#region src/hooks/use-focus-manager.ts
4397
2710
  /**
4398
2711
  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.
@@ -4535,6 +2848,54 @@ function normalizeAnimationInterval(interval) {
4535
2848
  return Math.min(maximumTimerInterval, Math.max(1, interval));
4536
2849
  }
4537
2850
  //#endregion
2851
+ //#region src/hooks/use-terminal-osc.ts
2852
+ const useProgress = ({ state, value }) => {
2853
+ const terminal = useContext(TerminalOscContext);
2854
+ const owner = useRef(void 0);
2855
+ owner.current ??= Symbol("terminal-progress");
2856
+ useEffect(() => {
2857
+ terminal.publishProgress(owner.current, state, value);
2858
+ }, [
2859
+ state,
2860
+ terminal,
2861
+ value
2862
+ ]);
2863
+ useEffect(() => {
2864
+ return () => {
2865
+ terminal.publishProgress(owner.current, "inactive");
2866
+ };
2867
+ }, [terminal]);
2868
+ };
2869
+ const useClipboard = () => {
2870
+ const terminal = useContext(TerminalOscContext);
2871
+ return useCallback((text, selection) => terminal.copyToClipboard(text, selection), [terminal]);
2872
+ };
2873
+ const useTitle = (title) => {
2874
+ const terminal = useContext(TerminalOscContext);
2875
+ const owner = useRef(void 0);
2876
+ owner.current ??= Symbol("terminal-title");
2877
+ useEffect(() => {
2878
+ terminal.publishTitle(owner.current, title);
2879
+ }, [terminal, title]);
2880
+ useEffect(() => () => terminal.publishTitle(owner.current, void 0), [terminal]);
2881
+ };
2882
+ const useWorkingDirectory = (directory) => {
2883
+ const terminal = useContext(TerminalOscContext);
2884
+ useEffect(() => {
2885
+ terminal.setWorkingDirectory(directory);
2886
+ }, [directory, terminal]);
2887
+ };
2888
+ const useNotification = () => {
2889
+ const terminal = useContext(TerminalOscContext);
2890
+ return useCallback((title) => terminal.notify(title), [terminal]);
2891
+ };
2892
+ const usePointerShape = (shape) => {
2893
+ const terminal = useContext(TerminalOscContext);
2894
+ useEffect(() => {
2895
+ terminal.setPointerShape(shape);
2896
+ }, [shape, terminal]);
2897
+ };
2898
+ //#endregion
4538
2899
  //#region src/hooks/use-window-size.ts
4539
2900
  /**
4540
2901
  A React hook that returns the current terminal window dimensions and re-renders the component whenever the terminal is resized.
@@ -4649,4 +3010,4 @@ const measureElement = (node) => {
4649
3010
  };
4650
3011
  };
4651
3012
  //#endregion
4652
- export { Box, Newline, Spacer, Static, Text, Transform, kittyFlags, kittyModifiers, measureElement, render, renderToString, useAnimation, useApp, useBoxMetrics, useCursor, useFocus, useFocusManager, useInput, useIsScreenReaderEnabled, usePaste, useStderr, useStdin, useStdout, useWindowSize };
3013
+ export { AnsiText, Box, Hyperlink, Newline, Spacer, Static, Text, Transform, 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, useWindowSize, useWorkingDirectory };