@alchemy.run/sigil 0.0.0-alpha.4 → 0.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -9
- package/THIRD_PARTY_NOTICES.md +39 -1
- package/dist/Text-BobFKi74.d.ts +452 -0
- package/dist/ansi.d.ts +122 -131
- package/dist/ansi.js +86 -6
- package/dist/capabilities.d.ts +5 -0
- package/dist/capabilities.js +3 -0
- package/dist/cell-_ZVhbfl0.js +44 -0
- package/dist/color-CkbalRqK.js +2 -0
- package/dist/color-policy-BMzMwV7Q.d.ts +22 -0
- package/dist/color-policy-SVj1pYTA.js +560 -0
- package/dist/color-profile-DHhQHY55.js +36 -0
- package/dist/color-profile-u0Nhe9Nv.d.ts +97 -0
- package/dist/color.d.ts +21 -0
- package/dist/color.js +3 -0
- package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
- package/dist/detect-Bh4yGP6w.d.ts +186 -0
- package/dist/detect-BuTXtY6e.js +373 -0
- package/dist/env-YVw64yZS.js +9 -0
- package/dist/escapes-CB_6CWOE.d.ts +72 -0
- package/dist/geometry-BxXOzJgo.d.ts +11 -0
- package/dist/index-DZ88EXJv.d.ts +21 -0
- package/dist/index.d.ts +143 -498
- package/dist/index.js +1026 -2244
- package/dist/osc-CCH7xDoS.js +71 -0
- package/dist/osc-Cn0fw77g.d.ts +23 -0
- package/dist/paint-C19minOS.d.ts +81 -0
- package/dist/query-vaIeGOkH.d.ts +152 -0
- package/dist/router.d.ts +392 -0
- package/dist/router.js +709 -0
- package/dist/sample-Cqw1bjUL.js +445 -0
- package/dist/screen-BOSLQ8fF.d.ts +49 -0
- package/dist/screen-CiPytswf.js +342 -0
- package/dist/screen.d.ts +5 -0
- package/dist/screen.js +5 -0
- package/dist/semantic-text-style-DIMzC7xt.js +91 -0
- package/dist/serialize-BTkAZgw1.js +79 -0
- package/dist/session-aZr9O8h3.js +665 -0
- package/dist/sgr-BhwaWAJB.js +246 -0
- package/dist/store-CgrG9K4y.d.ts +72 -0
- package/dist/string-width-CijQwpIk.js +69 -0
- package/dist/strip-BvU4toXG.js +6 -0
- package/dist/terminal.d.ts +118 -0
- package/dist/terminal.js +2 -0
- package/dist/tokenize-AjqbvtiT.js +1242 -0
- package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
- package/dist/truncate-D31fhU6i.js +562 -0
- package/dist/use-focus-BzqAJi0n.js +1337 -0
- package/package.json +41 -9
- package/src/ansi/chalk.ts +5 -3
- package/src/ansi/escapes.ts +14 -0
- package/src/ansi/graphemes.ts +8 -0
- package/src/ansi/hyperlink.ts +44 -0
- package/src/ansi/index.ts +3 -1
- package/src/ansi/osc.ts +77 -0
- package/src/ansi/tokenize.ts +3 -4
- package/src/capabilities/color-policy.ts +34 -0
- package/src/capabilities/detect.ts +594 -0
- package/src/capabilities/index.ts +37 -0
- package/src/capabilities/query.ts +657 -0
- package/src/capabilities/store.ts +379 -0
- package/src/color/index.ts +3 -0
- package/src/color/paint.ts +169 -0
- package/src/color/palette.ts +48 -0
- package/src/color/sample.ts +323 -0
- package/src/color.ts +1 -0
- package/src/components/AnsiText.tsx +42 -0
- package/src/components/App.tsx +98 -10
- package/src/components/BackgroundContext.ts +2 -3
- package/src/components/Box.tsx +0 -8
- package/src/components/CursorContext.ts +1 -1
- package/src/components/Hyperlink.tsx +56 -0
- package/src/components/TerminalOscContext.ts +25 -0
- package/src/components/Text.tsx +22 -45
- package/src/components/Transform.tsx +1 -1
- package/src/dom.ts +11 -2
- package/src/global.d.ts +3 -0
- package/src/hooks/use-capabilities.ts +73 -0
- package/src/hooks/use-cursor.ts +2 -2
- package/src/hooks/use-terminal-osc.ts +59 -0
- package/src/index.ts +45 -1
- package/src/ink.tsx +213 -153
- package/src/{render-node-to-output.ts → paint-tree.ts} +75 -48
- package/src/reconciler.ts +24 -3
- package/src/render-background.ts +36 -15
- package/src/render-border.ts +94 -61
- package/src/render-frame.ts +83 -0
- package/src/render-to-string.ts +21 -6
- package/src/render.ts +15 -6
- package/src/router/components.tsx +343 -0
- package/src/router/context.ts +41 -0
- package/src/router/history.ts +194 -0
- package/src/router/hooks.tsx +391 -0
- package/src/router/index.ts +34 -0
- package/src/router/matcher.ts +571 -0
- package/src/screen/ansi.ts +184 -0
- package/src/screen/canvas.ts +160 -0
- package/src/screen/cell.ts +138 -0
- package/src/screen/color-profile.ts +47 -0
- package/src/screen/geometry.ts +9 -0
- package/src/screen/index.ts +6 -0
- package/src/screen/screen.ts +305 -0
- package/src/screen/serialize.ts +129 -0
- package/src/screen.ts +1 -0
- package/src/semantic-text-style.ts +118 -0
- package/src/squash-text-nodes.ts +2 -5
- package/src/structured-text.ts +325 -0
- package/src/styles.ts +19 -14
- package/src/terminal/index.ts +2 -0
- package/src/terminal/inline-presenter.ts +120 -0
- package/src/terminal/input.ts +86 -0
- package/src/terminal/render-scheduler.ts +37 -0
- package/src/terminal/screen-presenter.ts +188 -0
- package/src/terminal/session.ts +407 -0
- package/src/terminal.ts +1 -0
- package/src/testing/browser.ts +588 -0
- package/src/testing/emulators.ts +205 -0
- package/src/testing/explorer-app/index.html +12 -0
- package/src/testing/explorer-app/main.ts +381 -0
- package/src/testing/explorer-app/style.css +194 -0
- package/src/testing/explorer-app/tsconfig.json +15 -0
- package/src/testing/explorer-app/vite-env.d.ts +1 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/keys.ts +56 -0
- package/src/testing/live.ts +85 -0
- package/src/testing/matchers.ts +70 -0
- package/src/testing/public.ts +94 -0
- package/src/testing/terminal.ts +349 -0
- package/src/testing/vitest.ts +157 -0
- package/src/transform-adapter.ts +14 -0
- package/src/wrap-text.ts +4 -0
- package/dist/sgr-CMfEpjSk.d.ts +0 -91
- package/dist/truncate-Cr6xVFMa.js +0 -2330
- package/src/ansi/supports-color.ts +0 -207
- package/src/colorize.ts +0 -60
- package/src/log-update.ts +0 -370
- package/src/output.ts +0 -308
- package/src/renderer.ts +0 -73
package/dist/index.js
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
|
-
import {
|
|
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-BzqAJi0n.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-aZr9O8h3.js";
|
|
2
11
|
import { t as Yoga } from "./yoga-5jKhYCJC.js";
|
|
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-CiPytswf.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";
|
|
3
20
|
import { PassThrough, Stream } from "node:stream";
|
|
4
21
|
import { setImmediate as setImmediate$1 } from "node:timers/promises";
|
|
5
22
|
import { isNativeError } from "node:util/types";
|
|
6
|
-
import { PureComponent, createContext, useCallback, useContext, useEffect, useEffectEvent,
|
|
7
|
-
import
|
|
8
|
-
import { ConcurrentRoot,
|
|
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";
|
|
9
26
|
import { EventEmitter } from "node:events";
|
|
10
|
-
import {
|
|
27
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
28
|
import { cwd } from "node:process";
|
|
12
|
-
import { WriteStream } from "node:tty";
|
|
13
29
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
30
|
import { Console } from "node:console";
|
|
15
|
-
import * as Scheduler from "scheduler";
|
|
16
|
-
//#region src/components/AccessibilityContext.ts
|
|
17
|
-
const accessibilityContext = createContext({ isScreenReaderEnabled: false });
|
|
18
|
-
//#endregion
|
|
19
31
|
//#region src/components/AnimationContext.ts
|
|
20
32
|
const animationContext = createContext({
|
|
21
33
|
renderThrottleMs: 0,
|
|
@@ -49,276 +61,6 @@ const AppContext = createContext({
|
|
|
49
61
|
});
|
|
50
62
|
AppContext.displayName = "InternalAppContext";
|
|
51
63
|
//#endregion
|
|
52
|
-
//#region src/cursor-position.ts
|
|
53
|
-
const showCursorEscape = ansiEscapes.cursorShow;
|
|
54
|
-
const hideCursorEscape = ansiEscapes.cursorHide;
|
|
55
|
-
/**
|
|
56
|
-
Compare two cursor positions. Returns true if they differ.
|
|
57
|
-
*/
|
|
58
|
-
const cursorPositionChanged = (a, b) => a?.x !== b?.x || a?.y !== b?.y;
|
|
59
|
-
/**
|
|
60
|
-
Build escape sequence to move cursor from the bottom of the output to the target position and show it.
|
|
61
|
-
|
|
62
|
-
`bottomLine` is the row the renderer left the cursor on, counted from the top of the output.
|
|
63
|
-
That is always `lines.length - 1` for `lines = str.split('\n')`, whether or not the output ends
|
|
64
|
-
with a newline:
|
|
65
|
-
|
|
66
|
-
- With a trailing newline, `split` yields one extra empty element and the renderer stops just
|
|
67
|
-
past the last visible line — which is `lines.length - 1`.
|
|
68
|
-
- Without one, there is no extra element and the renderer deliberately stops on the last visible
|
|
69
|
-
line instead of moving past it — which is also `lines.length - 1`.
|
|
70
|
-
|
|
71
|
-
This is the same row basis `buildReturnToBottom` measures from, so the two stay in step.
|
|
72
|
-
*/
|
|
73
|
-
const buildCursorSuffix = (bottomLine, cursorPosition) => {
|
|
74
|
-
if (!cursorPosition) return "";
|
|
75
|
-
const moveUp = bottomLine - cursorPosition.y;
|
|
76
|
-
return (moveUp > 0 ? ansiEscapes.cursorUp(moveUp) : "") + ansiEscapes.cursorTo(cursorPosition.x) + showCursorEscape;
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
Build escape sequence to move cursor from previousCursorPosition back to the bottom of output.
|
|
80
|
-
This must be done before eraseLines or any operation that assumes cursor is at the bottom.
|
|
81
|
-
*/
|
|
82
|
-
const buildReturnToBottom = (previousLineCount, previousCursorPosition) => {
|
|
83
|
-
if (!previousCursorPosition) return "";
|
|
84
|
-
const down = previousLineCount - 1 - previousCursorPosition.y;
|
|
85
|
-
return (down > 0 ? ansiEscapes.cursorDown(down) : "") + ansiEscapes.cursorTo(0);
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
Build the escape sequence for cursor-only updates (output unchanged, cursor moved).
|
|
89
|
-
Hides cursor if it was previously shown, returns to bottom, then repositions.
|
|
90
|
-
|
|
91
|
-
`buildReturnToBottom` has just placed the cursor on row `previousLineCount - 1`, so the
|
|
92
|
-
suffix measures from there rather than recomputing the row from the output.
|
|
93
|
-
*/
|
|
94
|
-
const buildCursorOnlySequence = (input) => {
|
|
95
|
-
const hidePrefix = input.cursorWasShown ? hideCursorEscape : "";
|
|
96
|
-
const returnToBottom = buildReturnToBottom(input.previousLineCount, input.previousCursorPosition);
|
|
97
|
-
const cursorSuffix = buildCursorSuffix(input.previousLineCount - 1, input.cursorPosition);
|
|
98
|
-
return hidePrefix + returnToBottom + cursorSuffix;
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
Build the prefix that hides cursor and returns to bottom before erasing or rewriting.
|
|
102
|
-
Returns empty string if cursor was not shown.
|
|
103
|
-
*/
|
|
104
|
-
const buildReturnToBottomPrefix = (cursorWasShown, previousLineCount, previousCursorPosition) => {
|
|
105
|
-
if (!cursorWasShown) return "";
|
|
106
|
-
return hideCursorEscape + buildReturnToBottom(previousLineCount, previousCursorPosition);
|
|
107
|
-
};
|
|
108
|
-
//#endregion
|
|
109
|
-
//#region src/log-update.ts
|
|
110
|
-
const visibleLineCount = (lines, str) => str.endsWith("\n") ? lines.length - 1 : lines.length;
|
|
111
|
-
const createStandard = (stream, { showCursor = false } = {}) => {
|
|
112
|
-
let previousLineCount = 0;
|
|
113
|
-
let previousOutput = "";
|
|
114
|
-
let hasHiddenCursor = false;
|
|
115
|
-
let cursorPosition;
|
|
116
|
-
let cursorDirty = false;
|
|
117
|
-
let previousCursorPosition;
|
|
118
|
-
let cursorWasShown = false;
|
|
119
|
-
const getActiveCursor = () => cursorDirty ? cursorPosition : void 0;
|
|
120
|
-
const hasChanges = (str, activeCursor) => {
|
|
121
|
-
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
122
|
-
return str !== previousOutput || cursorChanged;
|
|
123
|
-
};
|
|
124
|
-
const render = (str) => {
|
|
125
|
-
if (!showCursor && !hasHiddenCursor) {
|
|
126
|
-
cliCursor.hide(stream);
|
|
127
|
-
hasHiddenCursor = true;
|
|
128
|
-
}
|
|
129
|
-
const activeCursor = getActiveCursor();
|
|
130
|
-
cursorDirty = false;
|
|
131
|
-
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
132
|
-
if (!hasChanges(str, activeCursor)) return false;
|
|
133
|
-
const lines = str.split("\n");
|
|
134
|
-
const cursorSuffix = buildCursorSuffix(lines.length - 1, activeCursor);
|
|
135
|
-
if (str === previousOutput && cursorChanged) stream.write(buildCursorOnlySequence({
|
|
136
|
-
cursorWasShown,
|
|
137
|
-
previousLineCount,
|
|
138
|
-
previousCursorPosition,
|
|
139
|
-
cursorPosition: activeCursor
|
|
140
|
-
}));
|
|
141
|
-
else {
|
|
142
|
-
previousOutput = str;
|
|
143
|
-
const returnPrefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
|
|
144
|
-
stream.write(returnPrefix + ansiEscapes.eraseLines(previousLineCount) + str + cursorSuffix);
|
|
145
|
-
previousLineCount = lines.length;
|
|
146
|
-
}
|
|
147
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
148
|
-
cursorWasShown = activeCursor !== void 0;
|
|
149
|
-
return true;
|
|
150
|
-
};
|
|
151
|
-
render.clear = () => {
|
|
152
|
-
const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
|
|
153
|
-
stream.write(prefix + ansiEscapes.eraseLines(previousLineCount));
|
|
154
|
-
previousOutput = "";
|
|
155
|
-
previousLineCount = 0;
|
|
156
|
-
previousCursorPosition = void 0;
|
|
157
|
-
cursorWasShown = false;
|
|
158
|
-
};
|
|
159
|
-
render.done = () => {
|
|
160
|
-
previousOutput = "";
|
|
161
|
-
previousLineCount = 0;
|
|
162
|
-
previousCursorPosition = void 0;
|
|
163
|
-
cursorWasShown = false;
|
|
164
|
-
if (!showCursor) {
|
|
165
|
-
cliCursor.show(stream);
|
|
166
|
-
hasHiddenCursor = false;
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
render.reset = () => {
|
|
170
|
-
previousOutput = "";
|
|
171
|
-
previousLineCount = 0;
|
|
172
|
-
previousCursorPosition = void 0;
|
|
173
|
-
cursorWasShown = false;
|
|
174
|
-
};
|
|
175
|
-
render.sync = (str) => {
|
|
176
|
-
const activeCursor = cursorDirty ? cursorPosition : void 0;
|
|
177
|
-
cursorDirty = false;
|
|
178
|
-
const lines = str.split("\n");
|
|
179
|
-
previousOutput = str;
|
|
180
|
-
previousLineCount = lines.length;
|
|
181
|
-
if (!activeCursor && cursorWasShown) stream.write(hideCursorEscape);
|
|
182
|
-
if (activeCursor) stream.write(buildCursorSuffix(lines.length - 1, activeCursor));
|
|
183
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
184
|
-
cursorWasShown = activeCursor !== void 0;
|
|
185
|
-
};
|
|
186
|
-
render.setCursorPosition = (position) => {
|
|
187
|
-
cursorPosition = position;
|
|
188
|
-
cursorDirty = true;
|
|
189
|
-
};
|
|
190
|
-
render.isCursorDirty = () => cursorDirty;
|
|
191
|
-
render.willRender = (str) => hasChanges(str, getActiveCursor());
|
|
192
|
-
return render;
|
|
193
|
-
};
|
|
194
|
-
const createIncremental = (stream, { showCursor = false } = {}) => {
|
|
195
|
-
let previousLines = [];
|
|
196
|
-
let previousOutput = "";
|
|
197
|
-
let hasHiddenCursor = false;
|
|
198
|
-
let cursorPosition;
|
|
199
|
-
let cursorDirty = false;
|
|
200
|
-
let previousCursorPosition;
|
|
201
|
-
let cursorWasShown = false;
|
|
202
|
-
const getActiveCursor = () => cursorDirty ? cursorPosition : void 0;
|
|
203
|
-
const hasChanges = (str, activeCursor) => {
|
|
204
|
-
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
205
|
-
return str !== previousOutput || cursorChanged;
|
|
206
|
-
};
|
|
207
|
-
const render = (str) => {
|
|
208
|
-
if (!showCursor && !hasHiddenCursor) {
|
|
209
|
-
cliCursor.hide(stream);
|
|
210
|
-
hasHiddenCursor = true;
|
|
211
|
-
}
|
|
212
|
-
const activeCursor = getActiveCursor();
|
|
213
|
-
cursorDirty = false;
|
|
214
|
-
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
215
|
-
if (!hasChanges(str, activeCursor)) return false;
|
|
216
|
-
const nextLines = str.split("\n");
|
|
217
|
-
const visibleCount = visibleLineCount(nextLines, str);
|
|
218
|
-
const previousVisible = visibleLineCount(previousLines, previousOutput);
|
|
219
|
-
if (str === previousOutput && cursorChanged) {
|
|
220
|
-
stream.write(buildCursorOnlySequence({
|
|
221
|
-
cursorWasShown,
|
|
222
|
-
previousLineCount: previousLines.length,
|
|
223
|
-
previousCursorPosition,
|
|
224
|
-
cursorPosition: activeCursor
|
|
225
|
-
}));
|
|
226
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
227
|
-
cursorWasShown = activeCursor !== void 0;
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
const returnPrefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
|
|
231
|
-
if (str === "\n" || previousOutput.length === 0) {
|
|
232
|
-
const cursorSuffix = buildCursorSuffix(nextLines.length - 1, activeCursor);
|
|
233
|
-
stream.write(returnPrefix + ansiEscapes.eraseLines(previousLines.length) + str + cursorSuffix);
|
|
234
|
-
cursorWasShown = activeCursor !== void 0;
|
|
235
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
236
|
-
previousOutput = str;
|
|
237
|
-
previousLines = nextLines;
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
if (visibleCount > previousVisible) {
|
|
241
|
-
const cursorSuffix = buildCursorSuffix(visibleCount, activeCursor);
|
|
242
|
-
stream.write(returnPrefix + ansiEscapes.eraseLines(previousLines.length) + str + cursorSuffix);
|
|
243
|
-
cursorWasShown = activeCursor !== void 0;
|
|
244
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
245
|
-
previousOutput = str;
|
|
246
|
-
previousLines = nextLines;
|
|
247
|
-
return true;
|
|
248
|
-
}
|
|
249
|
-
const hasTrailingNewline = str.endsWith("\n");
|
|
250
|
-
const buffer = [];
|
|
251
|
-
buffer.push(returnPrefix);
|
|
252
|
-
if (visibleCount < previousVisible) {
|
|
253
|
-
const extraSlot = previousOutput.endsWith("\n") ? 1 : 0;
|
|
254
|
-
buffer.push(ansiEscapes.eraseLines(previousVisible - visibleCount + extraSlot), ansiEscapes.cursorUp(visibleCount));
|
|
255
|
-
} else buffer.push(ansiEscapes.cursorUp(previousLines.length - 1));
|
|
256
|
-
for (let i = 0; i < visibleCount; i++) {
|
|
257
|
-
const isLastLine = i === visibleCount - 1;
|
|
258
|
-
if (nextLines[i] === previousLines[i]) {
|
|
259
|
-
if (!isLastLine || hasTrailingNewline) buffer.push(ansiEscapes.cursorNextLine);
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
buffer.push(ansiEscapes.cursorTo(0) + nextLines[i] + ansiEscapes.eraseEndLine + (isLastLine && !hasTrailingNewline ? "" : "\n"));
|
|
263
|
-
}
|
|
264
|
-
const cursorSuffix = buildCursorSuffix(nextLines.length - 1, activeCursor);
|
|
265
|
-
buffer.push(cursorSuffix);
|
|
266
|
-
stream.write(buffer.join(""));
|
|
267
|
-
cursorWasShown = activeCursor !== void 0;
|
|
268
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
269
|
-
previousOutput = str;
|
|
270
|
-
previousLines = nextLines;
|
|
271
|
-
return true;
|
|
272
|
-
};
|
|
273
|
-
render.clear = () => {
|
|
274
|
-
const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
|
|
275
|
-
stream.write(prefix + ansiEscapes.eraseLines(previousLines.length));
|
|
276
|
-
previousOutput = "";
|
|
277
|
-
previousLines = [];
|
|
278
|
-
previousCursorPosition = void 0;
|
|
279
|
-
cursorWasShown = false;
|
|
280
|
-
};
|
|
281
|
-
render.done = () => {
|
|
282
|
-
previousOutput = "";
|
|
283
|
-
previousLines = [];
|
|
284
|
-
previousCursorPosition = void 0;
|
|
285
|
-
cursorWasShown = false;
|
|
286
|
-
if (!showCursor) {
|
|
287
|
-
cliCursor.show(stream);
|
|
288
|
-
hasHiddenCursor = false;
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
render.reset = () => {
|
|
292
|
-
previousOutput = "";
|
|
293
|
-
previousLines = [];
|
|
294
|
-
previousCursorPosition = void 0;
|
|
295
|
-
cursorWasShown = false;
|
|
296
|
-
};
|
|
297
|
-
render.sync = (str) => {
|
|
298
|
-
const activeCursor = cursorDirty ? cursorPosition : void 0;
|
|
299
|
-
cursorDirty = false;
|
|
300
|
-
const lines = str.split("\n");
|
|
301
|
-
previousOutput = str;
|
|
302
|
-
previousLines = lines;
|
|
303
|
-
if (!activeCursor && cursorWasShown) stream.write(hideCursorEscape);
|
|
304
|
-
if (activeCursor) stream.write(buildCursorSuffix(lines.length - 1, activeCursor));
|
|
305
|
-
previousCursorPosition = activeCursor ? { ...activeCursor } : void 0;
|
|
306
|
-
cursorWasShown = activeCursor !== void 0;
|
|
307
|
-
};
|
|
308
|
-
render.setCursorPosition = (position) => {
|
|
309
|
-
cursorPosition = position;
|
|
310
|
-
cursorDirty = true;
|
|
311
|
-
};
|
|
312
|
-
render.isCursorDirty = () => cursorDirty;
|
|
313
|
-
render.willRender = (str) => hasChanges(str, getActiveCursor());
|
|
314
|
-
return render;
|
|
315
|
-
};
|
|
316
|
-
const create$1 = (stream, { showCursor = false, incremental = false } = {}) => {
|
|
317
|
-
if (incremental) return createIncremental(stream, { showCursor });
|
|
318
|
-
return createStandard(stream, { showCursor });
|
|
319
|
-
};
|
|
320
|
-
const logUpdate = { create: create$1 };
|
|
321
|
-
//#endregion
|
|
322
64
|
//#region src/components/CursorContext.ts
|
|
323
65
|
const CursorContext = createContext({ setCursorPosition() {} });
|
|
324
66
|
CursorContext.displayName = "InternalCursorContext";
|
|
@@ -340,131 +82,6 @@ const codeExcerpt = (source, line, options = {}) => {
|
|
|
340
82
|
}));
|
|
341
83
|
};
|
|
342
84
|
//#endregion
|
|
343
|
-
//#region src/components/BackgroundContext.ts
|
|
344
|
-
const backgroundContext = createContext(void 0);
|
|
345
|
-
//#endregion
|
|
346
|
-
//#region src/quick-lru.ts
|
|
347
|
-
var QuickLru = class {
|
|
348
|
-
#size = 0;
|
|
349
|
-
#cache = /* @__PURE__ */ new Map();
|
|
350
|
-
#oldCache = /* @__PURE__ */ new Map();
|
|
351
|
-
#maxSize;
|
|
352
|
-
constructor({ maxSize }) {
|
|
353
|
-
if (!(maxSize && maxSize > 0)) throw new TypeError("`maxSize` must be a number greater than 0");
|
|
354
|
-
this.#maxSize = maxSize;
|
|
355
|
-
}
|
|
356
|
-
get size() {
|
|
357
|
-
let oldCacheSize = 0;
|
|
358
|
-
for (const key of this.#oldCache.keys()) if (!this.#cache.has(key)) oldCacheSize++;
|
|
359
|
-
return Math.min(this.#size + oldCacheSize, this.#maxSize);
|
|
360
|
-
}
|
|
361
|
-
get(key) {
|
|
362
|
-
if (this.#cache.has(key)) return this.#cache.get(key);
|
|
363
|
-
if (this.#oldCache.has(key)) {
|
|
364
|
-
const value = this.#oldCache.get(key);
|
|
365
|
-
this.#oldCache.delete(key);
|
|
366
|
-
this.#set(key, value);
|
|
367
|
-
return value;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
set(key, value) {
|
|
371
|
-
if (this.#cache.has(key)) this.#cache.set(key, value);
|
|
372
|
-
else this.#set(key, value);
|
|
373
|
-
return this;
|
|
374
|
-
}
|
|
375
|
-
has(key) {
|
|
376
|
-
return this.#cache.has(key) || this.#oldCache.has(key);
|
|
377
|
-
}
|
|
378
|
-
delete(key) {
|
|
379
|
-
const deleted = this.#cache.delete(key);
|
|
380
|
-
if (deleted) this.#size--;
|
|
381
|
-
return this.#oldCache.delete(key) || deleted;
|
|
382
|
-
}
|
|
383
|
-
clear() {
|
|
384
|
-
this.#cache.clear();
|
|
385
|
-
this.#oldCache.clear();
|
|
386
|
-
this.#size = 0;
|
|
387
|
-
}
|
|
388
|
-
#set(key, value) {
|
|
389
|
-
this.#cache.set(key, value);
|
|
390
|
-
this.#size++;
|
|
391
|
-
if (this.#size >= this.#maxSize) {
|
|
392
|
-
this.#size = 0;
|
|
393
|
-
this.#oldCache = this.#cache;
|
|
394
|
-
this.#cache = /* @__PURE__ */ new Map();
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
//#endregion
|
|
399
|
-
//#region src/measure-text.ts
|
|
400
|
-
const cache = new QuickLru({ maxSize: 4096 });
|
|
401
|
-
const measureText = (text) => {
|
|
402
|
-
if (text.length === 0) return {
|
|
403
|
-
width: 0,
|
|
404
|
-
height: 0
|
|
405
|
-
};
|
|
406
|
-
const cachedDimensions = cache.get(text);
|
|
407
|
-
if (cachedDimensions) return cachedDimensions;
|
|
408
|
-
const dimensions = {
|
|
409
|
-
width: widestLine(text),
|
|
410
|
-
height: text.split("\n").length
|
|
411
|
-
};
|
|
412
|
-
cache.set(text, dimensions);
|
|
413
|
-
return dimensions;
|
|
414
|
-
};
|
|
415
|
-
//#endregion
|
|
416
|
-
//#region src/get-max-width.ts
|
|
417
|
-
const getMaxWidth = (yogaNode) => {
|
|
418
|
-
return yogaNode.getComputedWidth() - yogaNode.getComputedPadding(Yoga.EDGE_LEFT) - yogaNode.getComputedPadding(Yoga.EDGE_RIGHT) - yogaNode.getComputedBorder(Yoga.EDGE_LEFT) - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT);
|
|
419
|
-
};
|
|
420
|
-
//#endregion
|
|
421
|
-
//#region src/colorize.ts
|
|
422
|
-
const rgbRegex = /^rgb\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/;
|
|
423
|
-
const ansiRegex = /^ansi256\(\s?(\d+)\s?\)$/;
|
|
424
|
-
const isNamedColor = (color) => {
|
|
425
|
-
return color in chalk;
|
|
426
|
-
};
|
|
427
|
-
const colorize = (str, color, type) => {
|
|
428
|
-
if (!color) return str;
|
|
429
|
-
if (isNamedColor(color)) {
|
|
430
|
-
if (type === "foreground") return chalk[color](str);
|
|
431
|
-
const methodName = `bg${color[0].toUpperCase() + color.slice(1)}`;
|
|
432
|
-
return chalk[methodName](str);
|
|
433
|
-
}
|
|
434
|
-
if (color.startsWith("#")) return type === "foreground" ? chalk.hex(color)(str) : chalk.bgHex(color)(str);
|
|
435
|
-
if (color.startsWith("ansi256")) {
|
|
436
|
-
const matches = ansiRegex.exec(color);
|
|
437
|
-
if (!matches) return str;
|
|
438
|
-
const value = Number(matches[1]);
|
|
439
|
-
return type === "foreground" ? chalk.ansi256(value)(str) : chalk.bgAnsi256(value)(str);
|
|
440
|
-
}
|
|
441
|
-
if (color.startsWith("rgb")) {
|
|
442
|
-
const matches = rgbRegex.exec(color);
|
|
443
|
-
if (!matches) return str;
|
|
444
|
-
const firstValue = Number(matches[1]);
|
|
445
|
-
const secondValue = Number(matches[2]);
|
|
446
|
-
const thirdValue = Number(matches[3]);
|
|
447
|
-
return type === "foreground" ? chalk.rgb(firstValue, secondValue, thirdValue)(str) : chalk.bgRgb(firstValue, secondValue, thirdValue)(str);
|
|
448
|
-
}
|
|
449
|
-
return str;
|
|
450
|
-
};
|
|
451
|
-
//#endregion
|
|
452
|
-
//#region src/render-background.ts
|
|
453
|
-
const renderBackground = (x, y, node, output) => {
|
|
454
|
-
if (!node.style.backgroundColor) return;
|
|
455
|
-
const width = node.yogaNode.getComputedWidth();
|
|
456
|
-
const height = node.yogaNode.getComputedHeight();
|
|
457
|
-
const leftBorderWidth = node.style.borderStyle && node.style.borderLeft !== false ? 1 : 0;
|
|
458
|
-
const rightBorderWidth = node.style.borderStyle && node.style.borderRight !== false ? 1 : 0;
|
|
459
|
-
const topBorderHeight = node.style.borderStyle && node.style.borderTop !== false ? 1 : 0;
|
|
460
|
-
const bottomBorderHeight = node.style.borderStyle && node.style.borderBottom !== false ? 1 : 0;
|
|
461
|
-
const contentWidth = width - leftBorderWidth - rightBorderWidth;
|
|
462
|
-
const contentHeight = height - topBorderHeight - bottomBorderHeight;
|
|
463
|
-
if (!(contentWidth > 0 && contentHeight > 0)) return;
|
|
464
|
-
const backgroundLine = colorize(" ".repeat(contentWidth), node.style.backgroundColor, "background");
|
|
465
|
-
for (let row = 0; row < contentHeight; row++) output.write(x + leftBorderWidth, y + topBorderHeight + row, backgroundLine, { transformers: [] });
|
|
466
|
-
};
|
|
467
|
-
//#endregion
|
|
468
85
|
//#region src/glyphs.ts
|
|
469
86
|
const BOXES = {
|
|
470
87
|
single: {
|
|
@@ -549,465 +166,6 @@ const BOXES = {
|
|
|
549
166
|
}
|
|
550
167
|
};
|
|
551
168
|
//#endregion
|
|
552
|
-
//#region src/render-border.ts
|
|
553
|
-
const stylePiece = (segment, fg, bg, dim) => {
|
|
554
|
-
let styled = colorize(segment, fg, "foreground");
|
|
555
|
-
styled = colorize(styled, bg, "background");
|
|
556
|
-
if (dim) styled = chalk.dim(styled);
|
|
557
|
-
return styled;
|
|
558
|
-
};
|
|
559
|
-
const renderBorder = (x, y, node, output) => {
|
|
560
|
-
if (node.style.borderStyle) {
|
|
561
|
-
const width = node.yogaNode.getComputedWidth();
|
|
562
|
-
const height = node.yogaNode.getComputedHeight();
|
|
563
|
-
const box = typeof node.style.borderStyle === "string" ? BOXES[node.style.borderStyle] : node.style.borderStyle;
|
|
564
|
-
const topBorderColor = node.style.borderTopColor ?? node.style.borderColor;
|
|
565
|
-
const bottomBorderColor = node.style.borderBottomColor ?? node.style.borderColor;
|
|
566
|
-
const leftBorderColor = node.style.borderLeftColor ?? node.style.borderColor;
|
|
567
|
-
const rightBorderColor = node.style.borderRightColor ?? node.style.borderColor;
|
|
568
|
-
const topBorderBackgroundColor = node.style.borderTopBackgroundColor ?? node.style.borderBackgroundColor;
|
|
569
|
-
const bottomBorderBackgroundColor = node.style.borderBottomBackgroundColor ?? node.style.borderBackgroundColor;
|
|
570
|
-
const leftBorderBackgroundColor = node.style.borderLeftBackgroundColor ?? node.style.borderBackgroundColor;
|
|
571
|
-
const rightBorderBackgroundColor = node.style.borderRightBackgroundColor ?? node.style.borderBackgroundColor;
|
|
572
|
-
const dimTopBorderColor = node.style.borderTopDimColor ?? node.style.borderDimColor;
|
|
573
|
-
const dimBottomBorderColor = node.style.borderBottomDimColor ?? node.style.borderDimColor;
|
|
574
|
-
const dimLeftBorderColor = node.style.borderLeftDimColor ?? node.style.borderDimColor;
|
|
575
|
-
const dimRightBorderColor = node.style.borderRightDimColor ?? node.style.borderDimColor;
|
|
576
|
-
const showTopBorder = node.style.borderTop !== false;
|
|
577
|
-
const showBottomBorder = node.style.borderBottom !== false;
|
|
578
|
-
const showLeftBorder = node.style.borderLeft !== false;
|
|
579
|
-
const showRightBorder = node.style.borderRight !== false;
|
|
580
|
-
const contentWidth = width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0);
|
|
581
|
-
let topBorder = showTopBorder ? (showLeftBorder ? box.topLeft : "") + box.top.repeat(contentWidth) + (showRightBorder ? box.topRight : "") : void 0;
|
|
582
|
-
topBorder &&= stylePiece(topBorder, topBorderColor, topBorderBackgroundColor, dimTopBorderColor);
|
|
583
|
-
let verticalBorderHeight = height;
|
|
584
|
-
if (showTopBorder) verticalBorderHeight -= 1;
|
|
585
|
-
if (showBottomBorder) verticalBorderHeight -= 1;
|
|
586
|
-
let leftBorder = "";
|
|
587
|
-
if (showLeftBorder) leftBorder = (stylePiece(box.left, leftBorderColor, leftBorderBackgroundColor, dimLeftBorderColor) + "\n").repeat(verticalBorderHeight);
|
|
588
|
-
let rightBorder = "";
|
|
589
|
-
if (showRightBorder) rightBorder = (stylePiece(box.right, rightBorderColor, rightBorderBackgroundColor, dimRightBorderColor) + "\n").repeat(verticalBorderHeight);
|
|
590
|
-
let bottomBorder = showBottomBorder ? (showLeftBorder ? box.bottomLeft : "") + box.bottom.repeat(contentWidth) + (showRightBorder ? box.bottomRight : "") : void 0;
|
|
591
|
-
bottomBorder &&= stylePiece(bottomBorder, bottomBorderColor, bottomBorderBackgroundColor, dimBottomBorderColor);
|
|
592
|
-
const offsetY = showTopBorder ? 1 : 0;
|
|
593
|
-
if (topBorder) output.write(x, y, topBorder, { transformers: [] });
|
|
594
|
-
if (leftBorder) output.write(x, y + offsetY, leftBorder, { transformers: [] });
|
|
595
|
-
if (rightBorder) output.write(x + width - 1, y + offsetY, rightBorder, { transformers: [] });
|
|
596
|
-
if (bottomBorder) output.write(x, y + height - 1, bottomBorder, { transformers: [] });
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
//#endregion
|
|
600
|
-
//#region src/sanitize-ansi.ts
|
|
601
|
-
const sgrParametersRegex = /^[\d:;]*$/;
|
|
602
|
-
const sanitizeAnsi = (text) => {
|
|
603
|
-
if (!hasAnsiControlCharacters(text)) return text;
|
|
604
|
-
let output = "";
|
|
605
|
-
for (const token of tokenizeAnsi(text)) {
|
|
606
|
-
if (token.type === "text" || token.type === "osc") {
|
|
607
|
-
output += token.value;
|
|
608
|
-
continue;
|
|
609
|
-
}
|
|
610
|
-
if (token.type === "csi" && token.finalCharacter === "m" && token.intermediateString === "" && sgrParametersRegex.test(token.parameterString)) output += token.value;
|
|
611
|
-
}
|
|
612
|
-
return output;
|
|
613
|
-
};
|
|
614
|
-
//#endregion
|
|
615
|
-
//#region src/squash-text-nodes.ts
|
|
616
|
-
const squashTextNodes = (node) => {
|
|
617
|
-
let text = "";
|
|
618
|
-
for (let index = 0; index < node.childNodes.length; index++) {
|
|
619
|
-
const childNode = node.childNodes[index];
|
|
620
|
-
if (childNode === void 0) continue;
|
|
621
|
-
let nodeText = "";
|
|
622
|
-
if (childNode.nodeName === "#text") nodeText = childNode.nodeValue;
|
|
623
|
-
else {
|
|
624
|
-
if (childNode.nodeName === "ink-text" || childNode.nodeName === "ink-virtual-text") nodeText = squashTextNodes(childNode);
|
|
625
|
-
if (nodeText.length > 0 && typeof childNode.internal_transform === "function") nodeText = childNode.internal_transform(nodeText, index);
|
|
626
|
-
}
|
|
627
|
-
text += nodeText;
|
|
628
|
-
}
|
|
629
|
-
return sanitizeAnsi(text);
|
|
630
|
-
};
|
|
631
|
-
//#endregion
|
|
632
|
-
//#region src/styles.ts
|
|
633
|
-
const positionEdges = [
|
|
634
|
-
["top", Yoga.EDGE_TOP],
|
|
635
|
-
["right", Yoga.EDGE_RIGHT],
|
|
636
|
-
["bottom", Yoga.EDGE_BOTTOM],
|
|
637
|
-
["left", Yoga.EDGE_LEFT]
|
|
638
|
-
];
|
|
639
|
-
const applyPositionStyles = (node, style) => {
|
|
640
|
-
if ("position" in style) {
|
|
641
|
-
let positionType = Yoga.POSITION_TYPE_RELATIVE;
|
|
642
|
-
if (style.position === "absolute") positionType = Yoga.POSITION_TYPE_ABSOLUTE;
|
|
643
|
-
else if (style.position === "static") positionType = Yoga.POSITION_TYPE_STATIC;
|
|
644
|
-
node.setPositionType(positionType);
|
|
645
|
-
}
|
|
646
|
-
for (const [property, edge] of positionEdges) {
|
|
647
|
-
if (!(property in style)) continue;
|
|
648
|
-
const value = style[property];
|
|
649
|
-
if (typeof value === "string") {
|
|
650
|
-
node.setPositionPercent(edge, Number.parseFloat(value));
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
node.setPosition(edge, value);
|
|
654
|
-
}
|
|
655
|
-
};
|
|
656
|
-
const applyMarginStyles = (node, style) => {
|
|
657
|
-
if ("margin" in style) node.setMargin(Yoga.EDGE_ALL, style.margin ?? 0);
|
|
658
|
-
if ("marginX" in style) node.setMargin(Yoga.EDGE_HORIZONTAL, style.marginX ?? 0);
|
|
659
|
-
if ("marginY" in style) node.setMargin(Yoga.EDGE_VERTICAL, style.marginY ?? 0);
|
|
660
|
-
if ("marginLeft" in style) node.setMargin(Yoga.EDGE_START, style.marginLeft ?? 0);
|
|
661
|
-
if ("marginRight" in style) node.setMargin(Yoga.EDGE_END, style.marginRight ?? 0);
|
|
662
|
-
if ("marginTop" in style) node.setMargin(Yoga.EDGE_TOP, style.marginTop ?? 0);
|
|
663
|
-
if ("marginBottom" in style) node.setMargin(Yoga.EDGE_BOTTOM, style.marginBottom ?? 0);
|
|
664
|
-
};
|
|
665
|
-
const applyPaddingStyles = (node, style) => {
|
|
666
|
-
if ("padding" in style) node.setPadding(Yoga.EDGE_ALL, style.padding ?? 0);
|
|
667
|
-
if ("paddingX" in style) node.setPadding(Yoga.EDGE_HORIZONTAL, style.paddingX ?? 0);
|
|
668
|
-
if ("paddingY" in style) node.setPadding(Yoga.EDGE_VERTICAL, style.paddingY ?? 0);
|
|
669
|
-
if ("paddingLeft" in style) node.setPadding(Yoga.EDGE_LEFT, style.paddingLeft ?? 0);
|
|
670
|
-
if ("paddingRight" in style) node.setPadding(Yoga.EDGE_RIGHT, style.paddingRight ?? 0);
|
|
671
|
-
if ("paddingTop" in style) node.setPadding(Yoga.EDGE_TOP, style.paddingTop ?? 0);
|
|
672
|
-
if ("paddingBottom" in style) node.setPadding(Yoga.EDGE_BOTTOM, style.paddingBottom ?? 0);
|
|
673
|
-
};
|
|
674
|
-
const applyFlexStyles = (node, style) => {
|
|
675
|
-
if ("flexGrow" in style) node.setFlexGrow(style.flexGrow ?? 0);
|
|
676
|
-
if ("flexShrink" in style) node.setFlexShrink(typeof style.flexShrink === "number" ? style.flexShrink : 1);
|
|
677
|
-
if ("flexWrap" in style) {
|
|
678
|
-
if (style.flexWrap === "nowrap") node.setFlexWrap(Yoga.WRAP_NO_WRAP);
|
|
679
|
-
if (style.flexWrap === "wrap") node.setFlexWrap(Yoga.WRAP_WRAP);
|
|
680
|
-
if (style.flexWrap === "wrap-reverse") node.setFlexWrap(Yoga.WRAP_WRAP_REVERSE);
|
|
681
|
-
}
|
|
682
|
-
if ("flexDirection" in style) {
|
|
683
|
-
if (style.flexDirection === "row") node.setFlexDirection(Yoga.FLEX_DIRECTION_ROW);
|
|
684
|
-
if (style.flexDirection === "row-reverse") node.setFlexDirection(Yoga.FLEX_DIRECTION_ROW_REVERSE);
|
|
685
|
-
if (style.flexDirection === "column") node.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN);
|
|
686
|
-
if (style.flexDirection === "column-reverse") node.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN_REVERSE);
|
|
687
|
-
}
|
|
688
|
-
if ("flexBasis" in style) {
|
|
689
|
-
if (typeof style.flexBasis === "number") node.setFlexBasis(style.flexBasis);
|
|
690
|
-
else if (typeof style.flexBasis === "string") node.setFlexBasisPercent(Number.parseInt(style.flexBasis, 10));
|
|
691
|
-
else node.setFlexBasisAuto();
|
|
692
|
-
}
|
|
693
|
-
if ("alignItems" in style) {
|
|
694
|
-
if (style.alignItems === "stretch" || !style.alignItems) node.setAlignItems(Yoga.ALIGN_STRETCH);
|
|
695
|
-
if (style.alignItems === "flex-start") node.setAlignItems(Yoga.ALIGN_FLEX_START);
|
|
696
|
-
if (style.alignItems === "center") node.setAlignItems(Yoga.ALIGN_CENTER);
|
|
697
|
-
if (style.alignItems === "flex-end") node.setAlignItems(Yoga.ALIGN_FLEX_END);
|
|
698
|
-
if (style.alignItems === "baseline") node.setAlignItems(Yoga.ALIGN_BASELINE);
|
|
699
|
-
}
|
|
700
|
-
if ("alignSelf" in style) {
|
|
701
|
-
if (style.alignSelf === "auto" || !style.alignSelf) node.setAlignSelf(Yoga.ALIGN_AUTO);
|
|
702
|
-
if (style.alignSelf === "flex-start") node.setAlignSelf(Yoga.ALIGN_FLEX_START);
|
|
703
|
-
if (style.alignSelf === "center") node.setAlignSelf(Yoga.ALIGN_CENTER);
|
|
704
|
-
if (style.alignSelf === "flex-end") node.setAlignSelf(Yoga.ALIGN_FLEX_END);
|
|
705
|
-
if (style.alignSelf === "stretch") node.setAlignSelf(Yoga.ALIGN_STRETCH);
|
|
706
|
-
if (style.alignSelf === "baseline") node.setAlignSelf(Yoga.ALIGN_BASELINE);
|
|
707
|
-
}
|
|
708
|
-
if ("alignContent" in style) {
|
|
709
|
-
if (style.alignContent === "flex-start" || !style.alignContent) node.setAlignContent(Yoga.ALIGN_FLEX_START);
|
|
710
|
-
if (style.alignContent === "center") node.setAlignContent(Yoga.ALIGN_CENTER);
|
|
711
|
-
if (style.alignContent === "flex-end") node.setAlignContent(Yoga.ALIGN_FLEX_END);
|
|
712
|
-
if (style.alignContent === "space-between") node.setAlignContent(Yoga.ALIGN_SPACE_BETWEEN);
|
|
713
|
-
if (style.alignContent === "space-around") node.setAlignContent(Yoga.ALIGN_SPACE_AROUND);
|
|
714
|
-
if (style.alignContent === "space-evenly") node.setAlignContent(Yoga.ALIGN_SPACE_EVENLY);
|
|
715
|
-
if (style.alignContent === "stretch") node.setAlignContent(Yoga.ALIGN_STRETCH);
|
|
716
|
-
}
|
|
717
|
-
if ("justifyContent" in style) {
|
|
718
|
-
if (style.justifyContent === "flex-start" || !style.justifyContent) node.setJustifyContent(Yoga.JUSTIFY_FLEX_START);
|
|
719
|
-
if (style.justifyContent === "center") node.setJustifyContent(Yoga.JUSTIFY_CENTER);
|
|
720
|
-
if (style.justifyContent === "flex-end") node.setJustifyContent(Yoga.JUSTIFY_FLEX_END);
|
|
721
|
-
if (style.justifyContent === "space-between") node.setJustifyContent(Yoga.JUSTIFY_SPACE_BETWEEN);
|
|
722
|
-
if (style.justifyContent === "space-around") node.setJustifyContent(Yoga.JUSTIFY_SPACE_AROUND);
|
|
723
|
-
if (style.justifyContent === "space-evenly") node.setJustifyContent(Yoga.JUSTIFY_SPACE_EVENLY);
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
const applyDimensionStyles = (node, style) => {
|
|
727
|
-
if ("width" in style) {
|
|
728
|
-
if (typeof style.width === "number") node.setWidth(style.width);
|
|
729
|
-
else if (typeof style.width === "string") node.setWidthPercent(Number.parseInt(style.width, 10));
|
|
730
|
-
else node.setWidthAuto();
|
|
731
|
-
}
|
|
732
|
-
if ("height" in style) {
|
|
733
|
-
if (typeof style.height === "number") node.setHeight(style.height);
|
|
734
|
-
else if (typeof style.height === "string") node.setHeightPercent(Number.parseInt(style.height, 10));
|
|
735
|
-
else node.setHeightAuto();
|
|
736
|
-
}
|
|
737
|
-
if ("minWidth" in style) {
|
|
738
|
-
if (typeof style.minWidth === "string") node.setMinWidthPercent(Number.parseInt(style.minWidth, 10));
|
|
739
|
-
else node.setMinWidth(style.minWidth ?? 0);
|
|
740
|
-
}
|
|
741
|
-
if ("minHeight" in style) {
|
|
742
|
-
if (typeof style.minHeight === "string") node.setMinHeightPercent(Number.parseInt(style.minHeight, 10));
|
|
743
|
-
else node.setMinHeight(style.minHeight ?? 0);
|
|
744
|
-
}
|
|
745
|
-
if ("maxWidth" in style) {
|
|
746
|
-
if (typeof style.maxWidth === "string") node.setMaxWidthPercent(Number.parseInt(style.maxWidth, 10));
|
|
747
|
-
else node.setMaxWidth(style.maxWidth);
|
|
748
|
-
}
|
|
749
|
-
if ("maxHeight" in style) {
|
|
750
|
-
if (typeof style.maxHeight === "string") node.setMaxHeightPercent(Number.parseInt(style.maxHeight, 10));
|
|
751
|
-
else node.setMaxHeight(style.maxHeight);
|
|
752
|
-
}
|
|
753
|
-
if ("aspectRatio" in style) node.setAspectRatio(style.aspectRatio);
|
|
754
|
-
};
|
|
755
|
-
const applyDisplayStyles = (node, style) => {
|
|
756
|
-
if ("display" in style) node.setDisplay(style.display === "flex" ? Yoga.DISPLAY_FLEX : Yoga.DISPLAY_NONE);
|
|
757
|
-
};
|
|
758
|
-
const applyBorderStyles = (node, style, currentStyle) => {
|
|
759
|
-
if (!("borderStyle" in style || "borderTop" in style || "borderBottom" in style || "borderLeft" in style || "borderRight" in style)) return;
|
|
760
|
-
const borderWidth = currentStyle.borderStyle ? 1 : 0;
|
|
761
|
-
node.setBorder(Yoga.EDGE_TOP, currentStyle.borderTop === false ? 0 : borderWidth);
|
|
762
|
-
node.setBorder(Yoga.EDGE_BOTTOM, currentStyle.borderBottom === false ? 0 : borderWidth);
|
|
763
|
-
node.setBorder(Yoga.EDGE_LEFT, currentStyle.borderLeft === false ? 0 : borderWidth);
|
|
764
|
-
node.setBorder(Yoga.EDGE_RIGHT, currentStyle.borderRight === false ? 0 : borderWidth);
|
|
765
|
-
};
|
|
766
|
-
const applyGapStyles = (node, style) => {
|
|
767
|
-
if ("gap" in style) node.setGap(Yoga.GUTTER_ALL, style.gap ?? 0);
|
|
768
|
-
if ("columnGap" in style) node.setGap(Yoga.GUTTER_COLUMN, style.columnGap ?? 0);
|
|
769
|
-
if ("rowGap" in style) node.setGap(Yoga.GUTTER_ROW, style.rowGap ?? 0);
|
|
770
|
-
};
|
|
771
|
-
const styles = (node, style = {}, currentStyle = style) => {
|
|
772
|
-
applyPositionStyles(node, style);
|
|
773
|
-
applyMarginStyles(node, style);
|
|
774
|
-
applyPaddingStyles(node, style);
|
|
775
|
-
applyFlexStyles(node, style);
|
|
776
|
-
applyDimensionStyles(node, style);
|
|
777
|
-
applyDisplayStyles(node, style);
|
|
778
|
-
applyBorderStyles(node, style, currentStyle);
|
|
779
|
-
applyGapStyles(node, style);
|
|
780
|
-
};
|
|
781
|
-
//#endregion
|
|
782
|
-
//#region src/wrap-text.ts
|
|
783
|
-
const wrapTextCache = new QuickLru({ maxSize: 4096 });
|
|
784
|
-
const wrapText = (text, maxWidth, wrapType) => {
|
|
785
|
-
const cacheKey = text + String(maxWidth) + String(wrapType);
|
|
786
|
-
const cachedText = wrapTextCache.get(cacheKey);
|
|
787
|
-
if (cachedText !== void 0) return cachedText;
|
|
788
|
-
let wrappedText = text;
|
|
789
|
-
if (wrapType === "wrap") wrappedText = wrapAnsi(text, maxWidth, {
|
|
790
|
-
trim: false,
|
|
791
|
-
hard: true
|
|
792
|
-
});
|
|
793
|
-
if (wrapType === "hard") wrappedText = wrapAnsi(text, maxWidth, {
|
|
794
|
-
trim: false,
|
|
795
|
-
hard: true,
|
|
796
|
-
wordWrap: false
|
|
797
|
-
});
|
|
798
|
-
if (wrapType.startsWith("truncate")) {
|
|
799
|
-
let position = "end";
|
|
800
|
-
if (wrapType === "truncate-middle") position = "middle";
|
|
801
|
-
if (wrapType === "truncate-start") position = "start";
|
|
802
|
-
wrappedText = cliTruncate(text, maxWidth, { position });
|
|
803
|
-
}
|
|
804
|
-
wrapTextCache.set(cacheKey, wrappedText);
|
|
805
|
-
return wrappedText;
|
|
806
|
-
};
|
|
807
|
-
//#endregion
|
|
808
|
-
//#region src/render-node-to-output.ts
|
|
809
|
-
const applyPaddingToText = (node, text) => {
|
|
810
|
-
const yogaNode = node.childNodes[0]?.yogaNode;
|
|
811
|
-
if (yogaNode) {
|
|
812
|
-
const offsetX = yogaNode.getComputedLeft();
|
|
813
|
-
const offsetY = yogaNode.getComputedTop();
|
|
814
|
-
text = "\n".repeat(offsetY) + (offsetX > 0 ? text.replace(/^(?!\s*$)/gm, " ".repeat(offsetX)) : text);
|
|
815
|
-
}
|
|
816
|
-
return text;
|
|
817
|
-
};
|
|
818
|
-
const renderNodeToScreenReaderOutput = (node, options = {}) => {
|
|
819
|
-
if (options.skipStaticElements && node.internal_static) return "";
|
|
820
|
-
if (node.yogaNode?.getDisplay() === Yoga.DISPLAY_NONE) return "";
|
|
821
|
-
let output = "";
|
|
822
|
-
if (node.nodeName === "ink-text") output = squashTextNodes(node);
|
|
823
|
-
else if (node.nodeName === "ink-box" || node.nodeName === "ink-root") {
|
|
824
|
-
const separator = node.style.flexDirection === "row" || node.style.flexDirection === "row-reverse" ? " " : "\n";
|
|
825
|
-
output = (node.style.flexDirection === "row-reverse" || node.style.flexDirection === "column-reverse" ? [...node.childNodes].reverse() : [...node.childNodes]).map((childNode) => {
|
|
826
|
-
return renderNodeToScreenReaderOutput(childNode, {
|
|
827
|
-
parentRole: node.internal_accessibility?.role,
|
|
828
|
-
skipStaticElements: options.skipStaticElements
|
|
829
|
-
});
|
|
830
|
-
}).filter(Boolean).join(separator);
|
|
831
|
-
}
|
|
832
|
-
if (node.internal_accessibility) {
|
|
833
|
-
const { role, state } = node.internal_accessibility;
|
|
834
|
-
if (state) {
|
|
835
|
-
const stateDescription = Object.keys(state).filter((key) => state[key]).join(", ");
|
|
836
|
-
if (stateDescription) output = `(${stateDescription}) ${output}`;
|
|
837
|
-
}
|
|
838
|
-
if (role && role !== options.parentRole) output = `${role}: ${output}`;
|
|
839
|
-
}
|
|
840
|
-
return output;
|
|
841
|
-
};
|
|
842
|
-
const renderNodeToOutput = (node, output, options) => {
|
|
843
|
-
const { offsetX = 0, offsetY = 0, transformers = [], skipStaticElements } = options;
|
|
844
|
-
if (skipStaticElements && node.internal_static) return;
|
|
845
|
-
const { yogaNode } = node;
|
|
846
|
-
if (yogaNode) {
|
|
847
|
-
if (yogaNode.getDisplay() === Yoga.DISPLAY_NONE) return;
|
|
848
|
-
const x = offsetX + yogaNode.getComputedLeft();
|
|
849
|
-
const y = offsetY + yogaNode.getComputedTop();
|
|
850
|
-
let newTransformers = transformers;
|
|
851
|
-
if (typeof node.internal_transform === "function") newTransformers = [node.internal_transform, ...transformers];
|
|
852
|
-
if (node.nodeName === "ink-text") {
|
|
853
|
-
let text = squashTextNodes(node);
|
|
854
|
-
if (text.length > 0) {
|
|
855
|
-
const currentWidth = widestLine(text);
|
|
856
|
-
const maxWidth = getMaxWidth(yogaNode);
|
|
857
|
-
if (currentWidth > maxWidth) {
|
|
858
|
-
const textWrap = node.style.textWrap ?? "wrap";
|
|
859
|
-
text = wrapText(text, maxWidth, textWrap);
|
|
860
|
-
}
|
|
861
|
-
text = applyPaddingToText(node, text);
|
|
862
|
-
output.write(x, y, text, { transformers: newTransformers });
|
|
863
|
-
}
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
let clipped = false;
|
|
867
|
-
if (node.nodeName === "ink-box") {
|
|
868
|
-
renderBackground(x, y, node, output);
|
|
869
|
-
renderBorder(x, y, node, output);
|
|
870
|
-
const clipHorizontally = node.style.overflowX === "hidden" || node.style.overflow === "hidden";
|
|
871
|
-
const clipVertically = node.style.overflowY === "hidden" || node.style.overflow === "hidden";
|
|
872
|
-
if (clipHorizontally || clipVertically) {
|
|
873
|
-
const x1 = clipHorizontally ? x + yogaNode.getComputedBorder(Yoga.EDGE_LEFT) : void 0;
|
|
874
|
-
const x2 = clipHorizontally ? x + yogaNode.getComputedWidth() - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT) : void 0;
|
|
875
|
-
const y1 = clipVertically ? y + yogaNode.getComputedBorder(Yoga.EDGE_TOP) : void 0;
|
|
876
|
-
const y2 = clipVertically ? y + yogaNode.getComputedHeight() - yogaNode.getComputedBorder(Yoga.EDGE_BOTTOM) : void 0;
|
|
877
|
-
output.clip({
|
|
878
|
-
x1,
|
|
879
|
-
x2,
|
|
880
|
-
y1,
|
|
881
|
-
y2
|
|
882
|
-
});
|
|
883
|
-
clipped = true;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
if (node.nodeName === "ink-root" || node.nodeName === "ink-box") {
|
|
887
|
-
for (const childNode of node.childNodes) renderNodeToOutput(childNode, output, {
|
|
888
|
-
offsetX: x,
|
|
889
|
-
offsetY: y,
|
|
890
|
-
transformers: newTransformers,
|
|
891
|
-
skipStaticElements
|
|
892
|
-
});
|
|
893
|
-
if (clipped) output.unclip();
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
};
|
|
897
|
-
//#endregion
|
|
898
|
-
//#region src/dom.ts
|
|
899
|
-
const createNode = (nodeName) => {
|
|
900
|
-
const node = {
|
|
901
|
-
nodeName,
|
|
902
|
-
style: {},
|
|
903
|
-
attributes: {},
|
|
904
|
-
childNodes: [],
|
|
905
|
-
parentNode: void 0,
|
|
906
|
-
yogaNode: nodeName === "ink-virtual-text" ? void 0 : Yoga.Node.create(),
|
|
907
|
-
internal_accessibility: {}
|
|
908
|
-
};
|
|
909
|
-
if (nodeName === "ink-text") node.yogaNode?.setMeasureFunc((width) => measureTextNode(node, width));
|
|
910
|
-
return node;
|
|
911
|
-
};
|
|
912
|
-
const appendChildNode = (node, childNode) => {
|
|
913
|
-
if (childNode.parentNode) removeChildNode(childNode.parentNode, childNode);
|
|
914
|
-
childNode.parentNode = node;
|
|
915
|
-
node.childNodes.push(childNode);
|
|
916
|
-
if (childNode.yogaNode) node.yogaNode?.insertChild(childNode.yogaNode, node.yogaNode.getChildCount());
|
|
917
|
-
if (node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text") markNodeAsDirty(node);
|
|
918
|
-
};
|
|
919
|
-
const insertBeforeNode = (node, newChildNode, beforeChildNode) => {
|
|
920
|
-
if (newChildNode.parentNode) removeChildNode(newChildNode.parentNode, newChildNode);
|
|
921
|
-
newChildNode.parentNode = node;
|
|
922
|
-
const index = node.childNodes.indexOf(beforeChildNode);
|
|
923
|
-
if (index >= 0) {
|
|
924
|
-
node.childNodes.splice(index, 0, newChildNode);
|
|
925
|
-
if (newChildNode.yogaNode) node.yogaNode?.insertChild(newChildNode.yogaNode, index);
|
|
926
|
-
} else {
|
|
927
|
-
node.childNodes.push(newChildNode);
|
|
928
|
-
if (newChildNode.yogaNode) node.yogaNode?.insertChild(newChildNode.yogaNode, node.yogaNode.getChildCount());
|
|
929
|
-
}
|
|
930
|
-
if (node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text") markNodeAsDirty(node);
|
|
931
|
-
};
|
|
932
|
-
const removeChildNode = (node, removeNode) => {
|
|
933
|
-
if (removeNode.yogaNode) removeNode.parentNode?.yogaNode?.removeChild(removeNode.yogaNode);
|
|
934
|
-
removeNode.parentNode = void 0;
|
|
935
|
-
const index = node.childNodes.indexOf(removeNode);
|
|
936
|
-
if (index >= 0) node.childNodes.splice(index, 1);
|
|
937
|
-
if (node.nodeName === "ink-text" || node.nodeName === "ink-virtual-text") markNodeAsDirty(node);
|
|
938
|
-
};
|
|
939
|
-
const nullifyYogaNodes = (node) => {
|
|
940
|
-
node.yogaNode = void 0;
|
|
941
|
-
if (node.nodeName !== "#text") for (const childNode of node.childNodes) nullifyYogaNodes(childNode);
|
|
942
|
-
};
|
|
943
|
-
/**
|
|
944
|
-
Detach a removed subtree from the layout engine: drop the measure callback
|
|
945
|
-
and null the `yogaNode` reference on every DOM node within it.
|
|
946
|
-
|
|
947
|
-
Nulling the references makes every `?.yogaNode` guard in the codebase
|
|
948
|
-
effective for removed nodes, turns lingering access into a safe no-op (see
|
|
949
|
-
QwenLM/qwen-code#6820), and lets the garbage collector reclaim the Yoga
|
|
950
|
-
tree along with its closures.
|
|
951
|
-
*/
|
|
952
|
-
const detachYogaSubtree = (removeNode) => {
|
|
953
|
-
removeNode.yogaNode?.unsetMeasureFunc();
|
|
954
|
-
nullifyYogaNodes(removeNode);
|
|
955
|
-
};
|
|
956
|
-
const setAttribute = (node, key, value) => {
|
|
957
|
-
if (key === "internal_accessibility") {
|
|
958
|
-
node.internal_accessibility = value;
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
node.attributes[key] = value;
|
|
962
|
-
};
|
|
963
|
-
const setStyle = (node, style) => {
|
|
964
|
-
node.style = style ?? {};
|
|
965
|
-
};
|
|
966
|
-
const createTextNode = (text) => {
|
|
967
|
-
const node = {
|
|
968
|
-
nodeName: "#text",
|
|
969
|
-
nodeValue: text,
|
|
970
|
-
yogaNode: void 0,
|
|
971
|
-
parentNode: void 0,
|
|
972
|
-
style: {}
|
|
973
|
-
};
|
|
974
|
-
setTextNodeValue(node, text);
|
|
975
|
-
return node;
|
|
976
|
-
};
|
|
977
|
-
const measureTextNode = function(node, width) {
|
|
978
|
-
const text = node.nodeName === "#text" ? node.nodeValue : squashTextNodes(node);
|
|
979
|
-
const dimensions = measureText(text);
|
|
980
|
-
if (dimensions.width <= width) return dimensions;
|
|
981
|
-
if (dimensions.width >= 1 && width > 0 && width < 1) return dimensions;
|
|
982
|
-
const textWrap = node.style?.textWrap ?? "wrap";
|
|
983
|
-
const wrappedText = wrapText(text, width, textWrap);
|
|
984
|
-
return measureText(wrappedText);
|
|
985
|
-
};
|
|
986
|
-
const findClosestYogaNode = (node) => {
|
|
987
|
-
if (!node?.parentNode) return;
|
|
988
|
-
return node.yogaNode ?? findClosestYogaNode(node.parentNode);
|
|
989
|
-
};
|
|
990
|
-
const markNodeAsDirty = (node) => {
|
|
991
|
-
findClosestYogaNode(node)?.markDirty();
|
|
992
|
-
};
|
|
993
|
-
const setTextNodeValue = (node, text) => {
|
|
994
|
-
if (typeof text !== "string") text = String(text);
|
|
995
|
-
node.nodeValue = text;
|
|
996
|
-
markNodeAsDirty(node);
|
|
997
|
-
};
|
|
998
|
-
const addLayoutListener = (rootNode, listener) => {
|
|
999
|
-
if (rootNode.nodeName !== "ink-root") return () => {};
|
|
1000
|
-
rootNode.internal_layoutListeners ??= /* @__PURE__ */ new Set();
|
|
1001
|
-
rootNode.internal_layoutListeners.add(listener);
|
|
1002
|
-
return () => {
|
|
1003
|
-
rootNode.internal_layoutListeners?.delete(listener);
|
|
1004
|
-
};
|
|
1005
|
-
};
|
|
1006
|
-
const emitLayoutListeners = (rootNode) => {
|
|
1007
|
-
if (rootNode.nodeName !== "ink-root" || !rootNode.internal_layoutListeners) return;
|
|
1008
|
-
for (const listener of rootNode.internal_layoutListeners) listener();
|
|
1009
|
-
};
|
|
1010
|
-
//#endregion
|
|
1011
169
|
//#region src/components/Box.tsx
|
|
1012
170
|
/** @jsxImportSource react */
|
|
1013
171
|
/**
|
|
@@ -1017,7 +175,7 @@ function Box({ children, ref, backgroundColor, "aria-label": ariaLabel, "aria-hi
|
|
|
1017
175
|
const { isScreenReaderEnabled } = useContext(accessibilityContext);
|
|
1018
176
|
const label = ariaLabel ? /* @__PURE__ */ jsx("ink-text", { children: ariaLabel }) : void 0;
|
|
1019
177
|
if (isScreenReaderEnabled && ariaHidden) return null;
|
|
1020
|
-
|
|
178
|
+
return /* @__PURE__ */ jsx("ink-box", {
|
|
1021
179
|
ref,
|
|
1022
180
|
style: {
|
|
1023
181
|
flexWrap: "nowrap",
|
|
@@ -1035,46 +193,6 @@ function Box({ children, ref, backgroundColor, "aria-label": ariaLabel, "aria-hi
|
|
|
1035
193
|
},
|
|
1036
194
|
children: isScreenReaderEnabled && label ? label : children
|
|
1037
195
|
});
|
|
1038
|
-
if (backgroundColor) return /* @__PURE__ */ jsx(backgroundContext.Provider, {
|
|
1039
|
-
value: backgroundColor,
|
|
1040
|
-
children: boxElement
|
|
1041
|
-
});
|
|
1042
|
-
return boxElement;
|
|
1043
|
-
}
|
|
1044
|
-
//#endregion
|
|
1045
|
-
//#region src/components/Text.tsx
|
|
1046
|
-
/** @jsxImportSource react */
|
|
1047
|
-
/**
|
|
1048
|
-
This component can display text and change its style to make it bold, underlined, italic, or strikethrough.
|
|
1049
|
-
*/
|
|
1050
|
-
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 }) {
|
|
1051
|
-
const { isScreenReaderEnabled } = useContext(accessibilityContext);
|
|
1052
|
-
const inheritedBackgroundColor = useContext(backgroundContext);
|
|
1053
|
-
const childrenOrAriaLabel = isScreenReaderEnabled && ariaLabel ? ariaLabel : children;
|
|
1054
|
-
if (childrenOrAriaLabel === void 0 || childrenOrAriaLabel === null) return null;
|
|
1055
|
-
const transform = (text) => {
|
|
1056
|
-
if (dimColor) text = chalk.dim(text);
|
|
1057
|
-
if (color) text = colorize(text, color, "foreground");
|
|
1058
|
-
const effectiveBackgroundColor = backgroundColor ?? inheritedBackgroundColor;
|
|
1059
|
-
if (effectiveBackgroundColor) text = colorize(text, effectiveBackgroundColor, "background");
|
|
1060
|
-
if (bold) text = chalk.bold(text);
|
|
1061
|
-
if (italic) text = chalk.italic(text);
|
|
1062
|
-
if (underline) text = chalk.underline(text);
|
|
1063
|
-
if (strikethrough) text = chalk.strikethrough(text);
|
|
1064
|
-
if (inverse) text = chalk.inverse(text);
|
|
1065
|
-
return text;
|
|
1066
|
-
};
|
|
1067
|
-
if (isScreenReaderEnabled && ariaHidden) return null;
|
|
1068
|
-
return /* @__PURE__ */ jsx("ink-text", {
|
|
1069
|
-
style: {
|
|
1070
|
-
flexGrow: 0,
|
|
1071
|
-
flexShrink: 1,
|
|
1072
|
-
flexDirection: "row",
|
|
1073
|
-
textWrap: wrap
|
|
1074
|
-
},
|
|
1075
|
-
internal_transform: transform,
|
|
1076
|
-
children: childrenOrAriaLabel
|
|
1077
|
-
});
|
|
1078
196
|
}
|
|
1079
197
|
//#endregion
|
|
1080
198
|
//#region src/parse-stack-line.ts
|
|
@@ -1263,219 +381,37 @@ var ErrorBoundary = class extends PureComponent {
|
|
|
1263
381
|
this.props.onError(error);
|
|
1264
382
|
}
|
|
1265
383
|
render() {
|
|
1266
|
-
if (this.state.error) return /* @__PURE__ */ jsx(ErrorOverview, { error: this.state.error });
|
|
1267
|
-
return this.props.children;
|
|
1268
|
-
}
|
|
1269
|
-
};
|
|
1270
|
-
//#endregion
|
|
1271
|
-
//#region src/components/FocusContext.ts
|
|
1272
|
-
const FocusContext = createContext({
|
|
1273
|
-
activeId: void 0,
|
|
1274
|
-
add() {},
|
|
1275
|
-
remove() {},
|
|
1276
|
-
activate() {},
|
|
1277
|
-
deactivate() {},
|
|
1278
|
-
enableFocus() {},
|
|
1279
|
-
disableFocus() {},
|
|
1280
|
-
focusNext() {},
|
|
1281
|
-
focusPrevious() {},
|
|
1282
|
-
focus() {}
|
|
1283
|
-
});
|
|
1284
|
-
FocusContext.displayName = "InternalFocusContext";
|
|
1285
|
-
//#endregion
|
|
1286
|
-
//#region src/components/StderrContext.ts
|
|
1287
|
-
/**
|
|
1288
|
-
`StderrContext` is a React context that exposes the stderr stream.
|
|
1289
|
-
*/
|
|
1290
|
-
const StderrContext = createContext({
|
|
1291
|
-
stderr: process.stderr,
|
|
1292
|
-
write() {}
|
|
1293
|
-
});
|
|
1294
|
-
StderrContext.displayName = "InternalStderrContext";
|
|
1295
|
-
//#endregion
|
|
1296
|
-
//#region src/components/StdinContext.ts
|
|
1297
|
-
/**
|
|
1298
|
-
`StdinContext` is a React context that exposes the input stream.
|
|
1299
|
-
*/
|
|
1300
|
-
const StdinContext = createContext({
|
|
1301
|
-
stdin: process.stdin,
|
|
1302
|
-
internal_eventEmitter: new EventEmitter(),
|
|
1303
|
-
setRawMode() {},
|
|
1304
|
-
setBracketedPasteMode() {},
|
|
1305
|
-
isRawModeSupported: false,
|
|
1306
|
-
internal_exitOnCtrlC: true
|
|
1307
|
-
});
|
|
1308
|
-
StdinContext.displayName = "InternalStdinContext";
|
|
1309
|
-
//#endregion
|
|
1310
|
-
//#region src/components/StdoutContext.ts
|
|
1311
|
-
/**
|
|
1312
|
-
`StdoutContext` is a React context that exposes the stdout stream where Ink renders your app.
|
|
1313
|
-
*/
|
|
1314
|
-
const StdoutContext = createContext({
|
|
1315
|
-
stdout: process.stdout,
|
|
1316
|
-
write() {}
|
|
1317
|
-
});
|
|
1318
|
-
StdoutContext.displayName = "InternalStdoutContext";
|
|
1319
|
-
//#endregion
|
|
1320
|
-
//#region src/input-parser.ts
|
|
1321
|
-
const escape$1 = "\x1B";
|
|
1322
|
-
const isCsiParameterByte = (byte) => {
|
|
1323
|
-
return byte >= 48 && byte <= 63;
|
|
1324
|
-
};
|
|
1325
|
-
const isCsiIntermediateByte = (byte) => {
|
|
1326
|
-
return byte >= 32 && byte <= 47;
|
|
1327
|
-
};
|
|
1328
|
-
const isCsiFinalByte = (byte) => {
|
|
1329
|
-
return byte >= 64 && byte <= 126;
|
|
1330
|
-
};
|
|
1331
|
-
const parseCsiSequence = (input, startIndex, prefixLength) => {
|
|
1332
|
-
const csiPayloadStart = startIndex + prefixLength + 1;
|
|
1333
|
-
let index = csiPayloadStart;
|
|
1334
|
-
for (; index < input.length; index++) {
|
|
1335
|
-
const byte = input.codePointAt(index);
|
|
1336
|
-
if (byte === void 0) return "pending";
|
|
1337
|
-
if (isCsiParameterByte(byte) || isCsiIntermediateByte(byte)) continue;
|
|
1338
|
-
if (byte === 91 && index === csiPayloadStart) continue;
|
|
1339
|
-
if (isCsiFinalByte(byte)) return {
|
|
1340
|
-
sequence: input.slice(startIndex, index + 1),
|
|
1341
|
-
nextIndex: index + 1
|
|
1342
|
-
};
|
|
1343
|
-
return;
|
|
1344
|
-
}
|
|
1345
|
-
return "pending";
|
|
1346
|
-
};
|
|
1347
|
-
const parseSs3Sequence = (input, startIndex, prefixLength) => {
|
|
1348
|
-
const nextIndex = startIndex + prefixLength + 2;
|
|
1349
|
-
if (nextIndex > input.length) return "pending";
|
|
1350
|
-
const finalByte = input.codePointAt(nextIndex - 1);
|
|
1351
|
-
if (finalByte === void 0 || !isCsiFinalByte(finalByte)) return;
|
|
1352
|
-
return {
|
|
1353
|
-
sequence: input.slice(startIndex, nextIndex),
|
|
1354
|
-
nextIndex
|
|
1355
|
-
};
|
|
1356
|
-
};
|
|
1357
|
-
const parseControlSequence = (input, startIndex, prefixLength) => {
|
|
1358
|
-
const sequenceType = input[startIndex + prefixLength];
|
|
1359
|
-
if (sequenceType === void 0) return "pending";
|
|
1360
|
-
if (sequenceType === "[") return parseCsiSequence(input, startIndex, prefixLength);
|
|
1361
|
-
if (sequenceType === "O") return parseSs3Sequence(input, startIndex, prefixLength);
|
|
1362
|
-
};
|
|
1363
|
-
const parseEscapedCodePoint = (input, escapeIndex) => {
|
|
1364
|
-
const nextCodePoint = input.codePointAt(escapeIndex + 1);
|
|
1365
|
-
const nextCodePointLength = nextCodePoint !== void 0 && nextCodePoint > 65535 ? 2 : 1;
|
|
1366
|
-
const nextIndex = escapeIndex + 1 + nextCodePointLength;
|
|
1367
|
-
return {
|
|
1368
|
-
sequence: input.slice(escapeIndex, nextIndex),
|
|
1369
|
-
nextIndex
|
|
1370
|
-
};
|
|
1371
|
-
};
|
|
1372
|
-
const parseEscapeSequence = (input, escapeIndex) => {
|
|
1373
|
-
if (escapeIndex === input.length - 1) return "pending";
|
|
1374
|
-
if (input[escapeIndex + 1] === escape$1) {
|
|
1375
|
-
if (escapeIndex + 2 >= input.length) return "pending";
|
|
1376
|
-
const doubleEscapeSequence = parseControlSequence(input, escapeIndex, 2);
|
|
1377
|
-
if (doubleEscapeSequence === "pending") return "pending";
|
|
1378
|
-
if (doubleEscapeSequence) return doubleEscapeSequence;
|
|
1379
|
-
return {
|
|
1380
|
-
sequence: input.slice(escapeIndex, escapeIndex + 2),
|
|
1381
|
-
nextIndex: escapeIndex + 2
|
|
1382
|
-
};
|
|
1383
|
-
}
|
|
1384
|
-
const controlSequence = parseControlSequence(input, escapeIndex, 1);
|
|
1385
|
-
if (controlSequence === "pending") return "pending";
|
|
1386
|
-
if (controlSequence) return controlSequence;
|
|
1387
|
-
return parseEscapedCodePoint(input, escapeIndex);
|
|
1388
|
-
};
|
|
1389
|
-
/**
|
|
1390
|
-
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.
|
|
1391
|
-
|
|
1392
|
-
Other control characters like `\r` and `\t` are NOT split because they can legitimately appear inside pasted text.
|
|
1393
|
-
*/
|
|
1394
|
-
const splitBackspaceBytes = (text, events) => {
|
|
1395
|
-
let textSegmentStart = 0;
|
|
1396
|
-
for (let index = 0; index < text.length; index++) {
|
|
1397
|
-
const character = text[index];
|
|
1398
|
-
if (character === "" || character === "\b") {
|
|
1399
|
-
if (index > textSegmentStart) events.push(text.slice(textSegmentStart, index));
|
|
1400
|
-
events.push(character);
|
|
1401
|
-
textSegmentStart = index + 1;
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
if (textSegmentStart < text.length) events.push(text.slice(textSegmentStart));
|
|
1405
|
-
};
|
|
1406
|
-
const parseKeypresses = (input) => {
|
|
1407
|
-
const events = [];
|
|
1408
|
-
let index = 0;
|
|
1409
|
-
const pendingFrom = (pendingStartIndex) => ({
|
|
1410
|
-
events,
|
|
1411
|
-
pending: input.slice(pendingStartIndex)
|
|
1412
|
-
});
|
|
1413
|
-
while (index < input.length) {
|
|
1414
|
-
const escapeIndex = input.indexOf(escape$1, index);
|
|
1415
|
-
if (escapeIndex === -1) {
|
|
1416
|
-
splitBackspaceBytes(input.slice(index), events);
|
|
1417
|
-
return {
|
|
1418
|
-
events,
|
|
1419
|
-
pending: ""
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
if (escapeIndex > index) splitBackspaceBytes(input.slice(index, escapeIndex), events);
|
|
1423
|
-
const parsedEscapeSequence = parseEscapeSequence(input, escapeIndex);
|
|
1424
|
-
if (parsedEscapeSequence === "pending") return pendingFrom(escapeIndex);
|
|
1425
|
-
if (parsedEscapeSequence.sequence === pasteStart) {
|
|
1426
|
-
const afterStart = parsedEscapeSequence.nextIndex;
|
|
1427
|
-
const endIndex = input.indexOf(pasteEnd, afterStart);
|
|
1428
|
-
if (endIndex === -1) return pendingFrom(escapeIndex);
|
|
1429
|
-
events.push({ paste: input.slice(afterStart, endIndex) });
|
|
1430
|
-
index = endIndex + pasteEnd.length;
|
|
1431
|
-
continue;
|
|
1432
|
-
}
|
|
1433
|
-
events.push(parsedEscapeSequence.sequence);
|
|
1434
|
-
index = parsedEscapeSequence.nextIndex;
|
|
1435
|
-
}
|
|
1436
|
-
return {
|
|
1437
|
-
events,
|
|
1438
|
-
pending: ""
|
|
1439
|
-
};
|
|
1440
|
-
};
|
|
1441
|
-
const createInputParser = () => {
|
|
1442
|
-
let pending = "";
|
|
1443
|
-
return {
|
|
1444
|
-
push(chunk) {
|
|
1445
|
-
const parsedInput = parseKeypresses(pending + chunk);
|
|
1446
|
-
pending = parsedInput.pending;
|
|
1447
|
-
return parsedInput.events;
|
|
1448
|
-
},
|
|
1449
|
-
hasPendingEscape() {
|
|
1450
|
-
return pending.startsWith(escape$1) && !pending.startsWith(pasteStart) && pending !== pasteStart.slice(0, -1);
|
|
1451
|
-
},
|
|
1452
|
-
flushPendingEscape() {
|
|
1453
|
-
if (!pending.startsWith(escape$1)) return;
|
|
1454
|
-
const pendingEscape = pending;
|
|
1455
|
-
pending = "";
|
|
1456
|
-
return pendingEscape;
|
|
1457
|
-
},
|
|
1458
|
-
reset() {
|
|
1459
|
-
pending = "";
|
|
1460
|
-
}
|
|
1461
|
-
};
|
|
384
|
+
if (this.state.error) return /* @__PURE__ */ jsx(ErrorOverview, { error: this.state.error });
|
|
385
|
+
return this.props.children;
|
|
386
|
+
}
|
|
1462
387
|
};
|
|
1463
388
|
//#endregion
|
|
1464
|
-
//#region src/
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
const
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
};
|
|
389
|
+
//#region src/components/StderrContext.ts
|
|
390
|
+
/**
|
|
391
|
+
`StderrContext` is a React context that exposes the stderr stream.
|
|
392
|
+
*/
|
|
393
|
+
const StderrContext = createContext({
|
|
394
|
+
stderr: process.stderr,
|
|
395
|
+
write() {}
|
|
396
|
+
});
|
|
397
|
+
StderrContext.displayName = "InternalStderrContext";
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/components/StdoutContext.ts
|
|
400
|
+
/**
|
|
401
|
+
`StdoutContext` is a React context that exposes the stdout stream where Ink renders your app.
|
|
402
|
+
*/
|
|
403
|
+
const StdoutContext = createContext({
|
|
404
|
+
stdout: process.stdout,
|
|
405
|
+
write() {}
|
|
406
|
+
});
|
|
407
|
+
StdoutContext.displayName = "InternalStdoutContext";
|
|
1472
408
|
//#endregion
|
|
1473
409
|
//#region src/components/App.tsx
|
|
1474
410
|
/** @jsxImportSource react */
|
|
1475
411
|
const tab = " ";
|
|
1476
412
|
const shiftTab = `${CSI}Z`;
|
|
1477
413
|
const escape = "\x1B";
|
|
1478
|
-
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 }) {
|
|
1479
415
|
const [isFocusEnabled, setIsFocusEnabled] = useState(true);
|
|
1480
416
|
const [activeFocusId, setActiveFocusId] = useState(void 0);
|
|
1481
417
|
const [, setFocusables] = useState([]);
|
|
@@ -1488,7 +424,6 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
1488
424
|
const internal_eventEmitter = useRef(new EventEmitter());
|
|
1489
425
|
internal_eventEmitter.current.setMaxListeners(Infinity);
|
|
1490
426
|
const readableListenerRef = useRef(void 0);
|
|
1491
|
-
const inputParserRef = useRef(createInputParser());
|
|
1492
427
|
const pendingInputFlushRef = useRef(void 0);
|
|
1493
428
|
const pendingInputFlushDelayMilliseconds = 20;
|
|
1494
429
|
const clearPendingInputFlush = useCallback(() => {
|
|
@@ -1554,10 +489,14 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
1554
489
|
readableListenerRef.current = void 0;
|
|
1555
490
|
}, [stdin]);
|
|
1556
491
|
const clearInputState = useCallback(() => {
|
|
1557
|
-
|
|
492
|
+
terminalInput.reset();
|
|
1558
493
|
clearPendingInputFlush();
|
|
1559
494
|
detachReadableListener();
|
|
1560
|
-
}, [
|
|
495
|
+
}, [
|
|
496
|
+
clearPendingInputFlush,
|
|
497
|
+
detachReadableListener,
|
|
498
|
+
terminalInput
|
|
499
|
+
]);
|
|
1561
500
|
const disableRawMode = useCallback(() => {
|
|
1562
501
|
if (!rawModeStdin) return;
|
|
1563
502
|
pendingDisableRawModeRef.current = false;
|
|
@@ -1593,16 +532,20 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
1593
532
|
clearPendingInputFlush();
|
|
1594
533
|
pendingInputFlushRef.current = setTimeout(() => {
|
|
1595
534
|
pendingInputFlushRef.current = void 0;
|
|
1596
|
-
const pendingEscape =
|
|
535
|
+
const pendingEscape = terminalInput.flushPendingEscape();
|
|
1597
536
|
if (!pendingEscape) return;
|
|
1598
537
|
emitInput(pendingEscape);
|
|
1599
538
|
}, pendingInputFlushDelayMilliseconds);
|
|
1600
|
-
}, [
|
|
539
|
+
}, [
|
|
540
|
+
clearPendingInputFlush,
|
|
541
|
+
emitInput,
|
|
542
|
+
terminalInput
|
|
543
|
+
]);
|
|
1601
544
|
const handleReadable = useCallback(() => {
|
|
1602
545
|
clearPendingInputFlush();
|
|
1603
546
|
let chunk;
|
|
1604
547
|
while ((chunk = stdin.read()) !== null) {
|
|
1605
|
-
const inputEvents =
|
|
548
|
+
const inputEvents = terminalInput.push(chunk);
|
|
1606
549
|
for (const event of inputEvents) if (typeof event === "string") emitInput(event);
|
|
1607
550
|
else {
|
|
1608
551
|
if (internal_eventEmitter.current.listenerCount("paste") === 0) {
|
|
@@ -1612,12 +555,13 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
1612
555
|
internal_eventEmitter.current.emit("paste", event.paste);
|
|
1613
556
|
}
|
|
1614
557
|
}
|
|
1615
|
-
if (
|
|
558
|
+
if (terminalInput.hasPendingEscape()) schedulePendingInputFlush();
|
|
1616
559
|
}, [
|
|
1617
560
|
stdin,
|
|
1618
561
|
emitInput,
|
|
1619
562
|
clearPendingInputFlush,
|
|
1620
|
-
schedulePendingInputFlush
|
|
563
|
+
schedulePendingInputFlush,
|
|
564
|
+
terminalInput
|
|
1621
565
|
]);
|
|
1622
566
|
const attachReadableListener = useCallback(() => {
|
|
1623
567
|
if (readableListenerRef.current) return;
|
|
@@ -1659,6 +603,49 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
1659
603
|
clearInputState,
|
|
1660
604
|
disableRawMode
|
|
1661
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
|
+
]);
|
|
1662
649
|
const handleSetBracketedPasteMode = useCallback((isEnabled) => {
|
|
1663
650
|
if (!stdout.isTTY) return;
|
|
1664
651
|
if (isEnabled) {
|
|
@@ -1932,94 +919,21 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
1932
919
|
}
|
|
1933
920
|
App.displayName = "InternalApp";
|
|
1934
921
|
//#endregion
|
|
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";
|
|
933
|
+
//#endregion
|
|
1935
934
|
//#region src/instances.ts
|
|
1936
935
|
const instances = /* @__PURE__ */ new WeakMap();
|
|
1937
936
|
//#endregion
|
|
1938
|
-
//#region src/kitty-keyboard.ts
|
|
1939
|
-
const kittyFlags = {
|
|
1940
|
-
disambiguateEscapeCodes: 1,
|
|
1941
|
-
reportEventTypes: 2,
|
|
1942
|
-
reportAlternateKeys: 4,
|
|
1943
|
-
reportAllKeysAsEscapeCodes: 8,
|
|
1944
|
-
reportAssociatedText: 16
|
|
1945
|
-
};
|
|
1946
|
-
function resolveFlags(flags) {
|
|
1947
|
-
let result = 0;
|
|
1948
|
-
for (const flag of flags) result |= kittyFlags[flag];
|
|
1949
|
-
return result;
|
|
1950
|
-
}
|
|
1951
|
-
const kittyModifiers = {
|
|
1952
|
-
shift: 1,
|
|
1953
|
-
alt: 2,
|
|
1954
|
-
ctrl: 4,
|
|
1955
|
-
super: 8,
|
|
1956
|
-
hyper: 16,
|
|
1957
|
-
meta: 32,
|
|
1958
|
-
capsLock: 64,
|
|
1959
|
-
numLock: 128
|
|
1960
|
-
};
|
|
1961
|
-
const textEncoder = new TextEncoder();
|
|
1962
|
-
const kittyQueryEscapeByte = 27;
|
|
1963
|
-
const kittyQueryOpenBracketByte = 91;
|
|
1964
|
-
const kittyQueryQuestionMarkByte = 63;
|
|
1965
|
-
const kittyQueryLetterByte = 117;
|
|
1966
|
-
const zeroByte = 48;
|
|
1967
|
-
const nineByte = 57;
|
|
1968
|
-
const isDigitByte = (byte) => byte >= zeroByte && byte <= nineByte;
|
|
1969
|
-
const matchKittyQueryResponse = (buffer, startIndex) => {
|
|
1970
|
-
if (buffer[startIndex] !== kittyQueryEscapeByte || buffer[startIndex + 1] !== kittyQueryOpenBracketByte || buffer[startIndex + 2] !== kittyQueryQuestionMarkByte) return;
|
|
1971
|
-
let index = startIndex + 3;
|
|
1972
|
-
const digitsStartIndex = index;
|
|
1973
|
-
while (index < buffer.length && isDigitByte(buffer[index])) index++;
|
|
1974
|
-
if (index === digitsStartIndex) return;
|
|
1975
|
-
if (index === buffer.length) return { state: "partial" };
|
|
1976
|
-
if (buffer[index] === kittyQueryLetterByte) return {
|
|
1977
|
-
state: "complete",
|
|
1978
|
-
endIndex: index
|
|
1979
|
-
};
|
|
1980
|
-
};
|
|
1981
|
-
const hasCompleteKittyQueryResponse = (buffer) => {
|
|
1982
|
-
for (let index = 0; index < buffer.length; index++) if (matchKittyQueryResponse(buffer, index)?.state === "complete") return true;
|
|
1983
|
-
return false;
|
|
1984
|
-
};
|
|
1985
|
-
const stripKittyQueryResponsesAndTrailingPartial = (buffer) => {
|
|
1986
|
-
const keptBytes = [];
|
|
1987
|
-
let index = 0;
|
|
1988
|
-
while (index < buffer.length) {
|
|
1989
|
-
const match = matchKittyQueryResponse(buffer, index);
|
|
1990
|
-
if (match?.state === "complete") {
|
|
1991
|
-
index = match.endIndex + 1;
|
|
1992
|
-
continue;
|
|
1993
|
-
}
|
|
1994
|
-
if (match?.state === "partial") break;
|
|
1995
|
-
keptBytes.push(buffer[index]);
|
|
1996
|
-
index++;
|
|
1997
|
-
}
|
|
1998
|
-
return keptBytes;
|
|
1999
|
-
};
|
|
2000
|
-
const detectKittySupport = (stdin, stdout, onSupported) => {
|
|
2001
|
-
let responseBuffer = [];
|
|
2002
|
-
const cleanup = () => {
|
|
2003
|
-
clearTimeout(timer);
|
|
2004
|
-
stdin.removeListener("data", onData);
|
|
2005
|
-
const remaining = stripKittyQueryResponsesAndTrailingPartial(responseBuffer);
|
|
2006
|
-
responseBuffer = [];
|
|
2007
|
-
if (remaining.length > 0) stdin.unshift(Uint8Array.from(remaining));
|
|
2008
|
-
};
|
|
2009
|
-
const onData = (data) => {
|
|
2010
|
-
const chunk = typeof data === "string" ? textEncoder.encode(data) : data;
|
|
2011
|
-
for (const byte of chunk) responseBuffer.push(byte);
|
|
2012
|
-
if (hasCompleteKittyQueryResponse(responseBuffer)) {
|
|
2013
|
-
cleanup();
|
|
2014
|
-
onSupported();
|
|
2015
|
-
}
|
|
2016
|
-
};
|
|
2017
|
-
stdin.on("data", onData);
|
|
2018
|
-
const timer = setTimeout(cleanup, 200);
|
|
2019
|
-
stdout.write(kittyQuery);
|
|
2020
|
-
return cleanup;
|
|
2021
|
-
};
|
|
2022
|
-
//#endregion
|
|
2023
937
|
//#region src/patch-console.ts
|
|
2024
938
|
const consoleMethods = [
|
|
2025
939
|
"assert",
|
|
@@ -2076,429 +990,604 @@ const patchStreamWrite = (stream, onData) => {
|
|
|
2076
990
|
};
|
|
2077
991
|
};
|
|
2078
992
|
//#endregion
|
|
2079
|
-
//#region
|
|
2080
|
-
|
|
2081
|
-
|
|
993
|
+
//#region src/get-max-width.ts
|
|
994
|
+
const getMaxWidth = (yogaNode) => {
|
|
995
|
+
return yogaNode.getComputedWidth() - yogaNode.getComputedPadding(Yoga.EDGE_LEFT) - yogaNode.getComputedPadding(Yoga.EDGE_RIGHT) - yogaNode.getComputedBorder(Yoga.EDGE_LEFT) - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT);
|
|
996
|
+
};
|
|
2082
997
|
//#endregion
|
|
2083
|
-
//#region src/
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
if (
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
998
|
+
//#region src/render-background.ts
|
|
999
|
+
const renderBackground = (x, y, node, output) => {
|
|
1000
|
+
if (node.style.backgroundColor === void 0) return;
|
|
1001
|
+
const width = node.yogaNode.getComputedWidth();
|
|
1002
|
+
const height = node.yogaNode.getComputedHeight();
|
|
1003
|
+
const leftBorderWidth = node.style.borderStyle && node.style.borderLeft !== false ? 1 : 0;
|
|
1004
|
+
const rightBorderWidth = node.style.borderStyle && node.style.borderRight !== false ? 1 : 0;
|
|
1005
|
+
const topBorderHeight = node.style.borderStyle && node.style.borderTop !== false ? 1 : 0;
|
|
1006
|
+
const bottomBorderHeight = node.style.borderStyle && node.style.borderBottom !== false ? 1 : 0;
|
|
1007
|
+
const contentWidth = width - leftBorderWidth - rightBorderWidth;
|
|
1008
|
+
const contentHeight = height - topBorderHeight - bottomBorderHeight;
|
|
1009
|
+
if (!(contentWidth > 0 && contentHeight > 0)) return;
|
|
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);
|
|
2101
1026
|
};
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
1027
|
+
//#endregion
|
|
1028
|
+
//#region src/render-border.ts
|
|
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
|
+
});
|
|
1043
|
+
};
|
|
1044
|
+
const renderBorder = (x, y, node, output) => {
|
|
1045
|
+
if (node.style.borderStyle) {
|
|
1046
|
+
const width = node.yogaNode.getComputedWidth();
|
|
1047
|
+
const height = node.yogaNode.getComputedHeight();
|
|
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;
|
|
1055
|
+
const topBorderColor = node.style.borderTopColor ?? node.style.borderColor;
|
|
1056
|
+
const bottomBorderColor = node.style.borderBottomColor ?? node.style.borderColor;
|
|
1057
|
+
const leftBorderColor = node.style.borderLeftColor ?? node.style.borderColor;
|
|
1058
|
+
const rightBorderColor = node.style.borderRightColor ?? node.style.borderColor;
|
|
1059
|
+
const topBorderBackgroundColor = node.style.borderTopBackgroundColor ?? node.style.borderBackgroundColor;
|
|
1060
|
+
const bottomBorderBackgroundColor = node.style.borderBottomBackgroundColor ?? node.style.borderBackgroundColor;
|
|
1061
|
+
const leftBorderBackgroundColor = node.style.borderLeftBackgroundColor ?? node.style.borderBackgroundColor;
|
|
1062
|
+
const rightBorderBackgroundColor = node.style.borderRightBackgroundColor ?? node.style.borderBackgroundColor;
|
|
1063
|
+
const dimTopBorderColor = node.style.borderTopDimColor ?? node.style.borderDimColor;
|
|
1064
|
+
const dimBottomBorderColor = node.style.borderBottomDimColor ?? node.style.borderDimColor;
|
|
1065
|
+
const dimLeftBorderColor = node.style.borderLeftDimColor ?? node.style.borderDimColor;
|
|
1066
|
+
const dimRightBorderColor = node.style.borderRightDimColor ?? node.style.borderDimColor;
|
|
1067
|
+
const showTopBorder = node.style.borderTop !== false;
|
|
1068
|
+
const showBottomBorder = node.style.borderBottom !== false;
|
|
1069
|
+
const showLeftBorder = node.style.borderLeft !== false;
|
|
1070
|
+
const showRightBorder = node.style.borderRight !== false;
|
|
1071
|
+
const contentWidth = width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0);
|
|
1072
|
+
const topBorder = showTopBorder ? (showLeftBorder ? box.topLeft : "") + box.top.repeat(contentWidth) + (showRightBorder ? box.topRight : "") : void 0;
|
|
1073
|
+
let verticalBorderHeight = height;
|
|
1074
|
+
if (showTopBorder) verticalBorderHeight -= 1;
|
|
1075
|
+
if (showBottomBorder) verticalBorderHeight -= 1;
|
|
1076
|
+
const offsetY = showTopBorder ? 1 : 0;
|
|
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)]);
|
|
2107
1082
|
}
|
|
2108
1083
|
};
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
*/
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
1084
|
+
//#endregion
|
|
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 (wrap === "none") return input.map((line) => [...line]);
|
|
1119
|
+
if (maxWidth < 1) return [[]];
|
|
1120
|
+
return input.flatMap((line) => {
|
|
1121
|
+
if (lineWidth(line) <= maxWidth) return [[...line]];
|
|
1122
|
+
if (wrap === "hard") return hardWrap(line, maxWidth);
|
|
1123
|
+
if (wrap === "wrap") return wordWrap(line, maxWidth);
|
|
1124
|
+
return [truncate(line, maxWidth, wrap, baseStyle)];
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
function wordWrap(line, maxWidth) {
|
|
1128
|
+
const words = [[]];
|
|
1129
|
+
const spaces = [];
|
|
1130
|
+
for (const cell of line) if (cell.grapheme === " ") {
|
|
1131
|
+
spaces.push(cell);
|
|
1132
|
+
words.push([]);
|
|
1133
|
+
} else words.at(-1).push(cell);
|
|
1134
|
+
const output = [[]];
|
|
1135
|
+
appendHard(output, words[0], maxWidth);
|
|
1136
|
+
for (let index = 1; index < words.length; index++) {
|
|
1137
|
+
appendHard(output, [spaces[index - 1]], maxWidth);
|
|
1138
|
+
const word = words[index];
|
|
1139
|
+
const current = output.at(-1);
|
|
1140
|
+
const wordWidth = lineWidth(word);
|
|
1141
|
+
const currentWidth = lineWidth(current);
|
|
1142
|
+
if (wordWidth <= maxWidth && currentWidth + wordWidth > maxWidth) output.push([]);
|
|
1143
|
+
else if (wordWidth > maxWidth && currentWidth > 0) {
|
|
1144
|
+
const remaining = maxWidth - currentWidth;
|
|
1145
|
+
const breaksHere = 1 + Math.floor((wordWidth - remaining - 1) / maxWidth);
|
|
1146
|
+
if (Math.floor((wordWidth - 1) / maxWidth) < breaksHere) output.push([]);
|
|
1147
|
+
}
|
|
1148
|
+
appendHard(output, word, maxWidth);
|
|
1149
|
+
}
|
|
1150
|
+
return output;
|
|
1151
|
+
}
|
|
1152
|
+
function hardWrap(line, maxWidth) {
|
|
1153
|
+
const output = [[]];
|
|
1154
|
+
appendHard(output, line, maxWidth);
|
|
1155
|
+
return output;
|
|
1156
|
+
}
|
|
1157
|
+
function appendHard(output, cells, maxWidth) {
|
|
1158
|
+
for (const cell of cells) {
|
|
1159
|
+
const current = output.at(-1);
|
|
1160
|
+
if (current.length > 0 && lineWidth(current) + cell.width > maxWidth) output.push([]);
|
|
1161
|
+
output.at(-1).push(cell);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
function truncate(line, maxWidth, wrap, baseStyle) {
|
|
1165
|
+
if (maxWidth === 1) return [ellipsis(baseStyle ?? cellStyleIntersection(line.at(0), line.at(-1)))];
|
|
1166
|
+
if (wrap === "truncate-start") {
|
|
1167
|
+
const right = sliceCells(line, lineWidth(line) - maxWidth + 1, lineWidth(line));
|
|
1168
|
+
return [ellipsis(right[0]?.style), ...right];
|
|
1169
|
+
}
|
|
1170
|
+
if (wrap === "truncate-middle") {
|
|
1171
|
+
const half = Math.min(Math.floor(maxWidth / 2), maxWidth - 1);
|
|
1172
|
+
const left = sliceCells(line, 0, half);
|
|
1173
|
+
const rightWidth = maxWidth - half - 1;
|
|
1174
|
+
const right = sliceCells(line, lineWidth(line) - rightWidth, lineWidth(line));
|
|
1175
|
+
return [
|
|
1176
|
+
...left,
|
|
1177
|
+
ellipsis(baseStyle ?? cellStyleIntersection(left.at(-1), right.at(0))),
|
|
1178
|
+
...right
|
|
1179
|
+
];
|
|
1180
|
+
}
|
|
1181
|
+
const left = sliceCells(line, 0, maxWidth - 1);
|
|
1182
|
+
return [...left, ellipsis(left.at(-1)?.style)];
|
|
1183
|
+
}
|
|
1184
|
+
function sliceCells(line, start, end) {
|
|
1185
|
+
const output = [];
|
|
1186
|
+
let column = 0;
|
|
1187
|
+
for (const cell of line) {
|
|
1188
|
+
const nextColumn = column + cell.width;
|
|
1189
|
+
if (column >= start && nextColumn <= end) output.push(cell);
|
|
1190
|
+
column = nextColumn;
|
|
1191
|
+
}
|
|
1192
|
+
return output;
|
|
1193
|
+
}
|
|
1194
|
+
function ellipsis(style = semanticTextStyleToCellStyle(emptySemanticTextStyle)) {
|
|
1195
|
+
return createCell("…", 1, style);
|
|
1196
|
+
}
|
|
1197
|
+
function cellStyleIntersection(left, right) {
|
|
1198
|
+
if (!left) return right?.style ?? semanticTextStyleToCellStyle(emptySemanticTextStyle);
|
|
1199
|
+
if (!right) return left.style;
|
|
1200
|
+
return {
|
|
1201
|
+
...sameColor(left.style.foreground, right.style.foreground) ? { foreground: left.style.foreground } : {},
|
|
1202
|
+
...sameColor(left.style.background, right.style.background) ? { background: left.style.background } : {},
|
|
1203
|
+
...sameColor(left.style.underlineColor, right.style.underlineColor) ? { underlineColor: left.style.underlineColor } : {},
|
|
1204
|
+
underline: left.style.underline === right.style.underline ? left.style.underline : "none",
|
|
1205
|
+
attributes: left.style.attributes & right.style.attributes
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
function sameColor(left, right) {
|
|
1209
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
1210
|
+
}
|
|
1211
|
+
function lineWidth(line) {
|
|
1212
|
+
return line.reduce((width, cell) => width + cell.width, 0);
|
|
1213
|
+
}
|
|
1214
|
+
function appendNode(node, inherited, lines, ansi) {
|
|
1215
|
+
if (node.nodeName === "#text") {
|
|
1216
|
+
if (ansi) appendAnsiText(node.nodeValue, inherited, lines);
|
|
1217
|
+
else appendText(node.nodeValue, inherited, lines);
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
const style = mergeSemanticTextStyles(inherited, node.internal_textStyle);
|
|
1221
|
+
for (const child of node.childNodes) appendNode(child, style, lines, ansi || node.internal_ansi === true);
|
|
1222
|
+
}
|
|
1223
|
+
function appendAnsiText(text, semantic, lines) {
|
|
1224
|
+
const base = semanticTextStyleToCellStyle(semantic);
|
|
1225
|
+
for (const cell of cellsFromAnsi(text)) {
|
|
1226
|
+
if (cell.grapheme === "\n" || cell.grapheme === "\r\n") {
|
|
1227
|
+
lines.push([]);
|
|
1228
|
+
continue;
|
|
2130
1229
|
}
|
|
2131
|
-
|
|
1230
|
+
const hasForeground = cell.style.foreground !== void 0;
|
|
1231
|
+
const hasBackground = cell.style.background !== void 0;
|
|
1232
|
+
lines.at(-1).push({
|
|
1233
|
+
...cell,
|
|
1234
|
+
style: {
|
|
1235
|
+
...cell.style.foreground ?? base.foreground ? { foreground: cell.style.foreground ?? base.foreground } : {},
|
|
1236
|
+
...cell.style.background ?? base.background ? { background: cell.style.background ?? base.background } : {},
|
|
1237
|
+
...cell.style.underlineColor ? { underlineColor: cell.style.underlineColor } : {},
|
|
1238
|
+
underline: cell.style.underline === "none" ? base.underline : cell.style.underline,
|
|
1239
|
+
attributes: base.attributes | cell.style.attributes
|
|
1240
|
+
},
|
|
1241
|
+
...!hasForeground && semantic.foreground ? { foregroundPaint: semantic.foreground } : {},
|
|
1242
|
+
...!hasBackground && semantic.background ? { backgroundPaint: semantic.background } : {}
|
|
1243
|
+
});
|
|
2132
1244
|
}
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
if (typeof rootNode.onComputeLayout === "function") rootNode.onComputeLayout();
|
|
2142
|
-
emitLayoutListeners(rootNode);
|
|
2143
|
-
if (rootNode.staticNode !== rootNode.previousStaticNode) {
|
|
2144
|
-
rootNode.previousStaticNode = rootNode.staticNode;
|
|
2145
|
-
if (typeof rootNode.onStaticChange === "function") rootNode.onStaticChange();
|
|
1245
|
+
}
|
|
1246
|
+
function appendText(text, style, lines) {
|
|
1247
|
+
const cellStyle = semanticTextStyleToCellStyle(style);
|
|
1248
|
+
const plainText = tokenizeAnsi(text).filter((token) => token.type === "text").map((token) => token.value).join("");
|
|
1249
|
+
for (const segment of graphemes(stripAnsi(plainText))) {
|
|
1250
|
+
if (segment === "\n" || segment === "\r\n") {
|
|
1251
|
+
lines.push([]);
|
|
1252
|
+
continue;
|
|
2146
1253
|
}
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
1254
|
+
lines.at(-1).push({
|
|
1255
|
+
...createCell(segment, Math.max(1, stringWidth(segment)), cellStyle, void 0, style.resetForeground || style.resetBackground ? {
|
|
1256
|
+
foreground: style.resetForeground,
|
|
1257
|
+
background: style.resetBackground
|
|
1258
|
+
} : void 0),
|
|
1259
|
+
...style.foreground ? { foregroundPaint: style.foreground } : {},
|
|
1260
|
+
...style.background ? { backgroundPaint: style.background } : {}
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
//#endregion
|
|
1265
|
+
//#region src/paint-tree.ts
|
|
1266
|
+
const renderAccessibleText = (node, options = {}) => {
|
|
1267
|
+
if (options.skipStaticElements && node.internal_static) return "";
|
|
1268
|
+
if (node.yogaNode?.getDisplay() === Yoga.DISPLAY_NONE) return "";
|
|
1269
|
+
let output = "";
|
|
1270
|
+
if (node.nodeName === "ink-text") output = squashTextNodes(node);
|
|
1271
|
+
else if (node.nodeName === "ink-box" || node.nodeName === "ink-root") {
|
|
1272
|
+
const separator = node.style.flexDirection === "row" || node.style.flexDirection === "row-reverse" ? " " : "\n";
|
|
1273
|
+
output = (node.style.flexDirection === "row-reverse" || node.style.flexDirection === "column-reverse" ? [...node.childNodes].reverse() : [...node.childNodes]).map((childNode) => {
|
|
1274
|
+
return renderAccessibleText(childNode, {
|
|
1275
|
+
parentRole: node.internal_accessibility?.role,
|
|
1276
|
+
skipStaticElements: options.skipStaticElements
|
|
1277
|
+
});
|
|
1278
|
+
}).filter(Boolean).join(separator);
|
|
1279
|
+
}
|
|
1280
|
+
if (node.internal_accessibility) {
|
|
1281
|
+
const { role, state } = node.internal_accessibility;
|
|
1282
|
+
if (state) {
|
|
1283
|
+
const stateDescription = Object.keys(state).filter((key) => state[key]).join(", ");
|
|
1284
|
+
if (stateDescription) output = `(${stateDescription}) ${output}`;
|
|
2151
1285
|
}
|
|
2152
|
-
if (
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
const
|
|
2164
|
-
const
|
|
2165
|
-
|
|
2166
|
-
if (
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
1286
|
+
if (role && role !== options.parentRole) output = `${role}: ${output}`;
|
|
1287
|
+
}
|
|
1288
|
+
return output;
|
|
1289
|
+
};
|
|
1290
|
+
const paintTree = (node, output, options) => {
|
|
1291
|
+
const { offsetX = 0, offsetY = 0, transformers = [], skipStaticElements } = options;
|
|
1292
|
+
if (skipStaticElements && node.internal_static) return;
|
|
1293
|
+
const { yogaNode } = node;
|
|
1294
|
+
if (yogaNode) {
|
|
1295
|
+
if (yogaNode.getDisplay() === Yoga.DISPLAY_NONE) return;
|
|
1296
|
+
const x = offsetX + yogaNode.getComputedLeft();
|
|
1297
|
+
const y = offsetY + yogaNode.getComputedTop();
|
|
1298
|
+
const newTransformers = node.internal_transform ? [node.internal_transform, ...transformers] : transformers;
|
|
1299
|
+
if (node.nodeName === "ink-text") {
|
|
1300
|
+
if (squashTextNodes(node).length > 0) {
|
|
1301
|
+
const maxWidth = getMaxWidth(yogaNode);
|
|
1302
|
+
const firstChildYoga = node.childNodes[0]?.yogaNode;
|
|
1303
|
+
const paddingX = firstChildYoga?.getComputedLeft() ?? 0;
|
|
1304
|
+
const paddingY = firstChildYoga?.getComputedTop() ?? 0;
|
|
1305
|
+
const textWrap = node.style.textWrap ?? "wrap";
|
|
1306
|
+
const overflow = textWrap === "none";
|
|
1307
|
+
const lines = wrapStructuredText(structuredTextLines(node), maxWidth, textWrap, structuredTextBaseStyle(node));
|
|
1308
|
+
const paintBounds = {
|
|
1309
|
+
x: x + paddingX,
|
|
1310
|
+
y: y + paddingY,
|
|
1311
|
+
width: Math.max(1, ...lines.map((line) => line.reduce((width, cell) => width + cell.width, 0))),
|
|
1312
|
+
height: lines.length
|
|
1313
|
+
};
|
|
1314
|
+
const sampled = sampleStructuredText(lines, paintBounds, output.paintContext);
|
|
1315
|
+
if (hasCompatibilityText(node)) {
|
|
1316
|
+
const textTransformers = collectTransformers(node, transformers);
|
|
1317
|
+
output.writeAnsi(paintBounds.x, paintBounds.y, sampled.map((line) => serializeLine(line, {
|
|
1318
|
+
colorProfile: output.paintContext.profile ?? "truecolor",
|
|
1319
|
+
trimEnd: false
|
|
1320
|
+
})).join("\n"), {
|
|
1321
|
+
transformers: textTransformers,
|
|
1322
|
+
overflow
|
|
1323
|
+
});
|
|
1324
|
+
} else output.writeCells(paintBounds.x, paintBounds.y, sampled, { overflow });
|
|
2181
1325
|
}
|
|
2182
|
-
|
|
2183
|
-
}
|
|
2184
|
-
return node;
|
|
2185
|
-
},
|
|
2186
|
-
createTextInstance(text, _root, hostContext) {
|
|
2187
|
-
if (!hostContext.isInsideText) throw new Error(`Text string "${text}" must be rendered inside <Text> component`);
|
|
2188
|
-
return createTextNode(text);
|
|
2189
|
-
},
|
|
2190
|
-
resetTextContent() {},
|
|
2191
|
-
hideTextInstance(node) {
|
|
2192
|
-
setTextNodeValue(node, "");
|
|
2193
|
-
},
|
|
2194
|
-
unhideTextInstance(node, text) {
|
|
2195
|
-
setTextNodeValue(node, text);
|
|
2196
|
-
},
|
|
2197
|
-
getPublicInstance: (instance) => instance,
|
|
2198
|
-
hideInstance(node) {
|
|
2199
|
-
node.yogaNode?.setDisplay(Yoga.DISPLAY_NONE);
|
|
2200
|
-
},
|
|
2201
|
-
unhideInstance(node) {
|
|
2202
|
-
node.yogaNode?.setDisplay(Yoga.DISPLAY_FLEX);
|
|
2203
|
-
},
|
|
2204
|
-
appendInitialChild: appendChildNode,
|
|
2205
|
-
appendChild: appendChildNode,
|
|
2206
|
-
insertBefore: insertBeforeNode,
|
|
2207
|
-
finalizeInitialChildren() {
|
|
2208
|
-
return false;
|
|
2209
|
-
},
|
|
2210
|
-
isPrimaryRenderer: true,
|
|
2211
|
-
supportsMutation: true,
|
|
2212
|
-
supportsPersistence: false,
|
|
2213
|
-
supportsHydration: false,
|
|
2214
|
-
supportsMicrotasks: true,
|
|
2215
|
-
scheduleMicrotask: queueMicrotask,
|
|
2216
|
-
scheduleCallback: Scheduler.unstable_scheduleCallback,
|
|
2217
|
-
cancelCallback: Scheduler.unstable_cancelCallback,
|
|
2218
|
-
shouldYield: Scheduler.unstable_shouldYield,
|
|
2219
|
-
now: Scheduler.unstable_now,
|
|
2220
|
-
scheduleTimeout: setTimeout,
|
|
2221
|
-
cancelTimeout: clearTimeout,
|
|
2222
|
-
noTimeout: -1,
|
|
2223
|
-
beforeActiveInstanceBlur() {},
|
|
2224
|
-
afterActiveInstanceBlur() {},
|
|
2225
|
-
detachDeletedInstance() {},
|
|
2226
|
-
getInstanceFromNode: () => null,
|
|
2227
|
-
prepareScopeUpdate() {},
|
|
2228
|
-
getInstanceFromScope: () => null,
|
|
2229
|
-
appendChildToContainer: appendChildNode,
|
|
2230
|
-
insertInContainerBefore: insertBeforeNode,
|
|
2231
|
-
removeChildFromContainer(node, removeNode) {
|
|
2232
|
-
clearStaticNodeIfContained(findRootNode$1(node), removeNode);
|
|
2233
|
-
removeChildNode(node, removeNode);
|
|
2234
|
-
detachYogaSubtree(removeNode);
|
|
2235
|
-
},
|
|
2236
|
-
commitUpdate(node, _type, oldProps, newProps) {
|
|
2237
|
-
if (node.internal_static) {
|
|
2238
|
-
const rootNode = findRootNode$1(node);
|
|
2239
|
-
if (rootNode) rootNode.isStaticDirty = true;
|
|
1326
|
+
return;
|
|
2240
1327
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
1328
|
+
let clipped = false;
|
|
1329
|
+
if (node.nodeName === "ink-box") {
|
|
1330
|
+
renderBackground(x, y, node, output);
|
|
1331
|
+
renderBorder(x, y, node, output);
|
|
1332
|
+
const clipHorizontally = node.style.overflowX === "hidden" || node.style.overflow === "hidden";
|
|
1333
|
+
const clipVertically = node.style.overflowY === "hidden" || node.style.overflow === "hidden";
|
|
1334
|
+
if (clipHorizontally || clipVertically) {
|
|
1335
|
+
const x1 = clipHorizontally ? x + yogaNode.getComputedBorder(Yoga.EDGE_LEFT) : void 0;
|
|
1336
|
+
const x2 = clipHorizontally ? x + yogaNode.getComputedWidth() - yogaNode.getComputedBorder(Yoga.EDGE_RIGHT) : void 0;
|
|
1337
|
+
const y1 = clipVertically ? y + yogaNode.getComputedBorder(Yoga.EDGE_TOP) : void 0;
|
|
1338
|
+
const y2 = clipVertically ? y + yogaNode.getComputedHeight() - yogaNode.getComputedBorder(Yoga.EDGE_BOTTOM) : void 0;
|
|
1339
|
+
output.clip({
|
|
1340
|
+
x1,
|
|
1341
|
+
x2,
|
|
1342
|
+
y1,
|
|
1343
|
+
y2
|
|
1344
|
+
});
|
|
1345
|
+
clipped = true;
|
|
2256
1346
|
}
|
|
2257
|
-
setAttribute(node, key, value);
|
|
2258
|
-
}
|
|
2259
|
-
if (style && node.yogaNode) styles(node.yogaNode, style, newProps["style"] ?? {});
|
|
2260
|
-
},
|
|
2261
|
-
commitTextUpdate(node, _oldText, newText) {
|
|
2262
|
-
setTextNodeValue(node, newText);
|
|
2263
|
-
},
|
|
2264
|
-
removeChild(node, removeNode) {
|
|
2265
|
-
clearStaticNodeIfContained(findRootNode$1(node), removeNode);
|
|
2266
|
-
removeChildNode(node, removeNode);
|
|
2267
|
-
detachYogaSubtree(removeNode);
|
|
2268
|
-
},
|
|
2269
|
-
setCurrentUpdatePriority(newPriority) {
|
|
2270
|
-
currentUpdatePriority = newPriority;
|
|
2271
|
-
},
|
|
2272
|
-
getCurrentUpdatePriority: () => currentUpdatePriority,
|
|
2273
|
-
resolveUpdatePriority() {
|
|
2274
|
-
if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
|
|
2275
|
-
return DefaultEventPriority;
|
|
2276
|
-
},
|
|
2277
|
-
maySuspendCommit() {
|
|
2278
|
-
return true;
|
|
2279
|
-
},
|
|
2280
|
-
NotPendingTransition: void 0,
|
|
2281
|
-
HostTransitionContext: createContext(null),
|
|
2282
|
-
resetFormInstance() {},
|
|
2283
|
-
requestPostPaintCallback() {},
|
|
2284
|
-
shouldAttemptEagerTransition() {
|
|
2285
|
-
return false;
|
|
2286
|
-
},
|
|
2287
|
-
trackSchedulerEvent() {},
|
|
2288
|
-
resolveEventType() {
|
|
2289
|
-
return null;
|
|
2290
|
-
},
|
|
2291
|
-
resolveEventTimeStamp() {
|
|
2292
|
-
return -1.1;
|
|
2293
|
-
},
|
|
2294
|
-
preloadInstance() {
|
|
2295
|
-
return true;
|
|
2296
|
-
},
|
|
2297
|
-
startSuspendingCommit() {},
|
|
2298
|
-
suspendInstance() {},
|
|
2299
|
-
waitForCommitToBeReady() {
|
|
2300
|
-
return null;
|
|
2301
|
-
},
|
|
2302
|
-
rendererPackageName: name,
|
|
2303
|
-
rendererVersion: version
|
|
2304
|
-
});
|
|
2305
|
-
//#endregion
|
|
2306
|
-
//#region src/output.ts
|
|
2307
|
-
var OutputCaches = class {
|
|
2308
|
-
widths = /* @__PURE__ */ new Map();
|
|
2309
|
-
blockWidths = /* @__PURE__ */ new Map();
|
|
2310
|
-
styledChars = /* @__PURE__ */ new Map();
|
|
2311
|
-
getStyledChars(line) {
|
|
2312
|
-
let cached = this.styledChars.get(line);
|
|
2313
|
-
if (cached === void 0) {
|
|
2314
|
-
cached = styledCharsFromTokens(tokenize(line));
|
|
2315
|
-
this.styledChars.set(line, cached);
|
|
2316
|
-
}
|
|
2317
|
-
return cached;
|
|
2318
|
-
}
|
|
2319
|
-
getStringWidth(text) {
|
|
2320
|
-
let cached = this.widths.get(text);
|
|
2321
|
-
if (cached === void 0) {
|
|
2322
|
-
cached = stringWidth(text);
|
|
2323
|
-
this.widths.set(text, cached);
|
|
2324
1347
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
this.blockWidths.set(text, cached);
|
|
1348
|
+
if (node.nodeName === "ink-root" || node.nodeName === "ink-box") {
|
|
1349
|
+
for (const childNode of node.childNodes) paintTree(childNode, output, {
|
|
1350
|
+
offsetX: x,
|
|
1351
|
+
offsetY: y,
|
|
1352
|
+
transformers: newTransformers,
|
|
1353
|
+
skipStaticElements
|
|
1354
|
+
});
|
|
1355
|
+
if (clipped) output.unclip();
|
|
2334
1356
|
}
|
|
2335
|
-
return cached;
|
|
2336
1357
|
}
|
|
2337
1358
|
};
|
|
2338
|
-
|
|
1359
|
+
function collectTransformers(node, inherited) {
|
|
1360
|
+
return [
|
|
1361
|
+
...node.childNodes.flatMap((child) => child.nodeName === "#text" ? [] : collectTransformers(child, [])),
|
|
1362
|
+
...node.internal_transform ? [node.internal_transform] : [],
|
|
1363
|
+
...inherited
|
|
1364
|
+
];
|
|
1365
|
+
}
|
|
1366
|
+
//#endregion
|
|
1367
|
+
//#region src/screen/canvas.ts
|
|
1368
|
+
/** Immediate structured drawing canvas with one explicit ANSI compatibility adapter. */
|
|
1369
|
+
var Canvas = class {
|
|
2339
1370
|
width;
|
|
2340
1371
|
height;
|
|
2341
|
-
|
|
2342
|
-
|
|
1372
|
+
paintContext;
|
|
1373
|
+
#screen;
|
|
1374
|
+
#clips = [];
|
|
2343
1375
|
constructor(options) {
|
|
2344
|
-
|
|
2345
|
-
this.
|
|
2346
|
-
this.
|
|
1376
|
+
this.width = options.width;
|
|
1377
|
+
this.height = options.height;
|
|
1378
|
+
this.paintContext = {
|
|
1379
|
+
profile: options.colorProfile ?? "truecolor",
|
|
1380
|
+
...options.paintContext
|
|
1381
|
+
};
|
|
1382
|
+
this.#screen = new Screen(this.width, this.height);
|
|
1383
|
+
}
|
|
1384
|
+
writeCells(x, y, lines, options) {
|
|
1385
|
+
const clip = this.#clips.at(-1);
|
|
1386
|
+
const overflow = options?.overflow === true;
|
|
1387
|
+
for (const [rowOffset, line] of lines.entries()) {
|
|
1388
|
+
const currentY = y + rowOffset;
|
|
1389
|
+
if (!this.#insideY(currentY, clip)) continue;
|
|
1390
|
+
let currentX = x;
|
|
1391
|
+
for (const cell of line) {
|
|
1392
|
+
const endX = currentX + cell.width;
|
|
1393
|
+
if (!(clip?.x1 !== void 0 && currentX < clip.x1 || clip?.x2 !== void 0 && endX > clip.x2)) this.#composeNativeCell(currentX, currentY, cell, overflow);
|
|
1394
|
+
currentX = endX;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
2347
1397
|
}
|
|
2348
|
-
|
|
2349
|
-
const { transformers } = options;
|
|
1398
|
+
writeAnsi(x, y, text, options) {
|
|
2350
1399
|
if (!text) return;
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
}
|
|
1400
|
+
let lines = text.split("\n");
|
|
1401
|
+
const clip = this.#clips.at(-1);
|
|
1402
|
+
const overflow = options.overflow === true;
|
|
1403
|
+
if (clip?.y1 !== void 0 && y < clip.y1) {
|
|
1404
|
+
lines = lines.slice(clip.y1 - y);
|
|
1405
|
+
y = clip.y1;
|
|
1406
|
+
}
|
|
1407
|
+
if (clip?.y2 !== void 0) lines = lines.slice(0, Math.max(0, clip.y2 - y));
|
|
1408
|
+
for (const [lineIndex, original] of lines.entries()) {
|
|
1409
|
+
const currentY = y + lineIndex;
|
|
1410
|
+
if (!this.#insideY(currentY, clip)) continue;
|
|
1411
|
+
let line = original;
|
|
1412
|
+
let currentX = x;
|
|
1413
|
+
if (clip?.x1 !== void 0 && currentX < clip.x1) {
|
|
1414
|
+
line = sliceAnsi(line, clip.x1 - currentX);
|
|
1415
|
+
currentX = clip.x1;
|
|
1416
|
+
}
|
|
1417
|
+
if (clip?.x2 !== void 0) line = sliceAnsi(line, 0, Math.max(0, clip.x2 - currentX));
|
|
1418
|
+
for (const character of transformAnsiLine(line, lineIndex, options.transformers)) {
|
|
1419
|
+
const width = Math.max(1, stringWidth(character.value));
|
|
1420
|
+
if (clip?.x2 !== void 0 && currentX + width > clip.x2) break;
|
|
1421
|
+
this.#writeCompatibilityCell(currentX, currentY, character, width, overflow);
|
|
1422
|
+
currentX += width;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
2358
1425
|
}
|
|
2359
1426
|
clip(clip) {
|
|
2360
|
-
this.
|
|
2361
|
-
type: "clip",
|
|
2362
|
-
clip
|
|
2363
|
-
});
|
|
1427
|
+
this.#clips.push(clip);
|
|
2364
1428
|
}
|
|
2365
1429
|
unclip() {
|
|
2366
|
-
this.
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
const from = y < clip.y1 ? clip.y1 - y : 0;
|
|
2411
|
-
const height = lines.length;
|
|
2412
|
-
const to = y + height > clip.y2 ? clip.y2 - y : height;
|
|
2413
|
-
lines = lines.slice(from, to);
|
|
2414
|
-
if (y < clip.y1) y = clip.y1;
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
let offsetY = 0;
|
|
2418
|
-
for (let [index, line] of lines.entries()) {
|
|
2419
|
-
const currentLine = output[y + offsetY];
|
|
2420
|
-
if (!currentLine) continue;
|
|
2421
|
-
for (const transformer of transformers) line = transformer(line, index);
|
|
2422
|
-
const characters = this.caches.getStyledChars(line);
|
|
2423
|
-
let offsetX = x;
|
|
2424
|
-
if (characters.length === 0) {
|
|
2425
|
-
offsetY++;
|
|
2426
|
-
continue;
|
|
2427
|
-
}
|
|
2428
|
-
const spaceCell = {
|
|
2429
|
-
type: "char",
|
|
2430
|
-
value: " ",
|
|
2431
|
-
fullWidth: false,
|
|
2432
|
-
styles: []
|
|
2433
|
-
};
|
|
2434
|
-
if (currentLine[offsetX]?.value === "" && offsetX > 0 && this.caches.getStringWidth(currentLine[offsetX - 1]?.value ?? "") > 1) currentLine[offsetX - 1] = spaceCell;
|
|
2435
|
-
for (const character of characters) {
|
|
2436
|
-
currentLine[offsetX] = character;
|
|
2437
|
-
const characterWidth = Math.max(1, this.caches.getStringWidth(character.value));
|
|
2438
|
-
if (characterWidth > 1) for (let columnOffset = 1; columnOffset < characterWidth; columnOffset++) currentLine[offsetX + columnOffset] = {
|
|
2439
|
-
type: "char",
|
|
2440
|
-
value: "",
|
|
2441
|
-
fullWidth: false,
|
|
2442
|
-
styles: character.styles
|
|
2443
|
-
};
|
|
2444
|
-
offsetX += characterWidth;
|
|
2445
|
-
}
|
|
2446
|
-
if (currentLine[offsetX]?.value === "") currentLine[offsetX] = spaceCell;
|
|
2447
|
-
offsetY++;
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
return {
|
|
2452
|
-
output: output.map((line) => {
|
|
2453
|
-
const lineWithoutEmptyItems = line.filter((item) => item !== void 0);
|
|
2454
|
-
return styledCharsToString(lineWithoutEmptyItems).trimEnd();
|
|
2455
|
-
}).join("\n"),
|
|
2456
|
-
height: output.length
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
};
|
|
2460
|
-
//#endregion
|
|
2461
|
-
//#region src/renderer.ts
|
|
2462
|
-
const renderer = (node, isScreenReaderEnabled) => {
|
|
1430
|
+
this.#clips.pop();
|
|
1431
|
+
}
|
|
1432
|
+
finish() {
|
|
1433
|
+
return this.#screen;
|
|
1434
|
+
}
|
|
1435
|
+
#insideY(y, clip) {
|
|
1436
|
+
return y >= 0 && y < this.#screen.height && (clip?.y1 === void 0 || y >= clip.y1) && (clip?.y2 === void 0 || y < clip.y2);
|
|
1437
|
+
}
|
|
1438
|
+
#composeNativeCell(x, y, cell, overflow = false) {
|
|
1439
|
+
if (!overflow && x >= this.#screen.width) return;
|
|
1440
|
+
this.#screen.composeCell(x, y, {
|
|
1441
|
+
content: {
|
|
1442
|
+
grapheme: cell.grapheme,
|
|
1443
|
+
width: cell.width
|
|
1444
|
+
},
|
|
1445
|
+
foreground: cell.reset?.foreground ? null : cell.style.foreground ?? null,
|
|
1446
|
+
background: cell.reset?.background ? null : cell.style.background,
|
|
1447
|
+
underlineColor: cell.style.underlineColor ?? null,
|
|
1448
|
+
underline: cell.style.underline,
|
|
1449
|
+
attributes: cell.style.attributes,
|
|
1450
|
+
hyperlink: cell.hyperlink ?? null
|
|
1451
|
+
}, { overflow });
|
|
1452
|
+
}
|
|
1453
|
+
#writeCompatibilityCell(x, y, character, width, overflow = false) {
|
|
1454
|
+
if (!overflow && x >= this.#screen.width) return;
|
|
1455
|
+
const cell = cellFromStyledChar(character, width);
|
|
1456
|
+
this.#screen.composeCell(x, y, {
|
|
1457
|
+
content: {
|
|
1458
|
+
grapheme: cell.grapheme,
|
|
1459
|
+
width: cell.width
|
|
1460
|
+
},
|
|
1461
|
+
foreground: cell.style.foreground ?? null,
|
|
1462
|
+
background: cell.style.background,
|
|
1463
|
+
underlineColor: cell.style.underlineColor ?? null,
|
|
1464
|
+
underline: cell.style.underline,
|
|
1465
|
+
attributes: cell.style.attributes,
|
|
1466
|
+
hyperlink: cell.hyperlink ?? null
|
|
1467
|
+
}, { overflow });
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
//#endregion
|
|
1471
|
+
//#region src/render-frame.ts
|
|
1472
|
+
const renderDimension = (value) => Number.isFinite(value) ? Math.max(0, Math.ceil(value)) : 0;
|
|
1473
|
+
const renderFrame = (node, isScreenReaderEnabled, options = {}) => {
|
|
2463
1474
|
if (node.yogaNode) {
|
|
2464
1475
|
if (isScreenReaderEnabled) {
|
|
2465
|
-
const output =
|
|
2466
|
-
const outputHeight = output === "" ? 0 : output.split("\n").length;
|
|
1476
|
+
const output = renderAccessibleText(node, { skipStaticElements: true });
|
|
2467
1477
|
let staticOutput = "";
|
|
2468
|
-
if (node.staticNode) staticOutput =
|
|
1478
|
+
if (node.staticNode) staticOutput = renderAccessibleText(node.staticNode, { skipStaticElements: false });
|
|
2469
1479
|
return {
|
|
2470
|
-
output,
|
|
2471
|
-
|
|
2472
|
-
staticOutput: staticOutput ? `${staticOutput}\n` : ""
|
|
1480
|
+
accessibleText: output,
|
|
1481
|
+
staticAccessibleText: staticOutput
|
|
2473
1482
|
};
|
|
2474
1483
|
}
|
|
2475
|
-
const output = new
|
|
2476
|
-
width: node.yogaNode.getComputedWidth(),
|
|
2477
|
-
height: node.yogaNode.getComputedHeight()
|
|
1484
|
+
const output = new Canvas({
|
|
1485
|
+
width: renderDimension(node.yogaNode.getComputedWidth()),
|
|
1486
|
+
height: renderDimension(node.yogaNode.getComputedHeight()),
|
|
1487
|
+
colorProfile: options.colorProfile,
|
|
1488
|
+
paintContext: options.paintContext
|
|
2478
1489
|
});
|
|
2479
|
-
|
|
1490
|
+
paintTree(node, output, { skipStaticElements: true });
|
|
2480
1491
|
let staticOutput;
|
|
2481
1492
|
if (node.staticNode?.yogaNode) {
|
|
2482
|
-
staticOutput = new
|
|
2483
|
-
width: node.staticNode.yogaNode.getComputedWidth(),
|
|
2484
|
-
height: node.staticNode.yogaNode.getComputedHeight()
|
|
1493
|
+
staticOutput = new Canvas({
|
|
1494
|
+
width: renderDimension(node.staticNode.yogaNode.getComputedWidth()),
|
|
1495
|
+
height: renderDimension(node.staticNode.yogaNode.getComputedHeight()),
|
|
1496
|
+
colorProfile: options.colorProfile,
|
|
1497
|
+
paintContext: options.paintContext
|
|
2485
1498
|
});
|
|
2486
|
-
|
|
1499
|
+
paintTree(node.staticNode, staticOutput, { skipStaticElements: false });
|
|
2487
1500
|
}
|
|
2488
|
-
const { output: generatedOutput, height: outputHeight } = output.get();
|
|
2489
1501
|
return {
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
staticOutput: staticOutput ? `${staticOutput.get().output}\n` : ""
|
|
1502
|
+
screen: output.finish(),
|
|
1503
|
+
...staticOutput ? { staticScreen: staticOutput.finish() } : {}
|
|
2493
1504
|
};
|
|
2494
1505
|
}
|
|
2495
|
-
return {
|
|
2496
|
-
output: "",
|
|
2497
|
-
outputHeight: 0,
|
|
2498
|
-
staticOutput: ""
|
|
2499
|
-
};
|
|
1506
|
+
return { accessibleText: "" };
|
|
2500
1507
|
};
|
|
2501
1508
|
//#endregion
|
|
1509
|
+
//#region src/terminal/inline-presenter.ts
|
|
1510
|
+
/** Full-rewrite fallback and cursor state for the structured inline renderer. */
|
|
1511
|
+
function createInlinePresenter(stream, options = {}) {
|
|
1512
|
+
let previousLineCount = 0;
|
|
1513
|
+
let previousOutput = "";
|
|
1514
|
+
let hasHiddenCursor = false;
|
|
1515
|
+
let cursorPosition;
|
|
1516
|
+
let cursorDirty = false;
|
|
1517
|
+
let previousCursorPosition;
|
|
1518
|
+
let cursorWasShown = false;
|
|
1519
|
+
const showCursor = options.showCursor ?? false;
|
|
1520
|
+
const activeCursor = () => cursorDirty ? cursorPosition : void 0;
|
|
1521
|
+
const hasChanges = (output, cursor) => output !== previousOutput || cursorPositionChanged(cursor, previousCursorPosition);
|
|
1522
|
+
const present = (output) => {
|
|
1523
|
+
if (!showCursor && !hasHiddenCursor) {
|
|
1524
|
+
cliCursor.hide(stream);
|
|
1525
|
+
hasHiddenCursor = true;
|
|
1526
|
+
}
|
|
1527
|
+
const cursor = activeCursor();
|
|
1528
|
+
cursorDirty = false;
|
|
1529
|
+
const cursorChanged = cursorPositionChanged(cursor, previousCursorPosition);
|
|
1530
|
+
if (!hasChanges(output, cursor)) return false;
|
|
1531
|
+
const lines = output.split("\n");
|
|
1532
|
+
const suffix = buildCursorSuffix(lines.length - 1, cursor);
|
|
1533
|
+
if (output === previousOutput && cursorChanged) stream.write(buildCursorOnlySequence({
|
|
1534
|
+
cursorWasShown,
|
|
1535
|
+
previousLineCount,
|
|
1536
|
+
previousCursorPosition,
|
|
1537
|
+
cursorPosition: cursor
|
|
1538
|
+
}));
|
|
1539
|
+
else {
|
|
1540
|
+
previousOutput = output;
|
|
1541
|
+
stream.write(buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition) + ansiEscapes.eraseLines(previousLineCount) + output + suffix);
|
|
1542
|
+
previousLineCount = lines.length;
|
|
1543
|
+
}
|
|
1544
|
+
previousCursorPosition = cursor ? { ...cursor } : void 0;
|
|
1545
|
+
cursorWasShown = cursor !== void 0;
|
|
1546
|
+
return true;
|
|
1547
|
+
};
|
|
1548
|
+
present.clear = () => {
|
|
1549
|
+
stream.write(buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition) + ansiEscapes.eraseLines(previousLineCount));
|
|
1550
|
+
previousOutput = "";
|
|
1551
|
+
previousLineCount = 0;
|
|
1552
|
+
previousCursorPosition = void 0;
|
|
1553
|
+
cursorWasShown = false;
|
|
1554
|
+
};
|
|
1555
|
+
present.done = () => {
|
|
1556
|
+
previousOutput = "";
|
|
1557
|
+
previousLineCount = 0;
|
|
1558
|
+
previousCursorPosition = void 0;
|
|
1559
|
+
cursorWasShown = false;
|
|
1560
|
+
if (!showCursor) {
|
|
1561
|
+
cliCursor.show(stream);
|
|
1562
|
+
hasHiddenCursor = false;
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
present.reset = () => {
|
|
1566
|
+
previousOutput = "";
|
|
1567
|
+
previousLineCount = 0;
|
|
1568
|
+
previousCursorPosition = void 0;
|
|
1569
|
+
cursorWasShown = false;
|
|
1570
|
+
};
|
|
1571
|
+
present.sync = (output) => {
|
|
1572
|
+
const cursor = activeCursor();
|
|
1573
|
+
cursorDirty = false;
|
|
1574
|
+
const lines = output.split("\n");
|
|
1575
|
+
previousOutput = output;
|
|
1576
|
+
previousLineCount = lines.length;
|
|
1577
|
+
if (!cursor && cursorWasShown) stream.write(hideCursorEscape);
|
|
1578
|
+
if (cursor) stream.write(buildCursorSuffix(lines.length - 1, cursor));
|
|
1579
|
+
previousCursorPosition = cursor ? { ...cursor } : void 0;
|
|
1580
|
+
cursorWasShown = cursor !== void 0;
|
|
1581
|
+
};
|
|
1582
|
+
present.setCursorPosition = (position) => {
|
|
1583
|
+
cursorPosition = position;
|
|
1584
|
+
cursorDirty = true;
|
|
1585
|
+
};
|
|
1586
|
+
present.isCursorDirty = () => cursorDirty;
|
|
1587
|
+
present.willRender = (output) => hasChanges(output, activeCursor());
|
|
1588
|
+
return present;
|
|
1589
|
+
}
|
|
1590
|
+
//#endregion
|
|
2502
1591
|
//#region src/throttle.ts
|
|
2503
1592
|
/**
|
|
2504
1593
|
Invokes `fn` at most once per `wait` milliseconds.
|
|
@@ -2548,32 +1637,28 @@ const throttle = (fn, wait = 0) => {
|
|
|
2548
1637
|
return throttled;
|
|
2549
1638
|
};
|
|
2550
1639
|
//#endregion
|
|
2551
|
-
//#region src/terminal-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
const
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
if (env["COLUMNS"] && env["LINES"]) return create(env["COLUMNS"], env["LINES"]);
|
|
2572
|
-
return devTty() ?? {
|
|
2573
|
-
columns: 80,
|
|
2574
|
-
rows: 24
|
|
1640
|
+
//#region src/terminal/render-scheduler.ts
|
|
1641
|
+
/** Owns frame cadence independently of React reconciliation and terminal presentation. */
|
|
1642
|
+
function createRenderScheduler(render, options) {
|
|
1643
|
+
const frameInterval = options.maxFps > 0 ? Math.max(1, Math.ceil(1e3 / options.maxFps)) : 0;
|
|
1644
|
+
let pending = false;
|
|
1645
|
+
const throttled = options.unthrottled ? void 0 : throttle(render, frameInterval);
|
|
1646
|
+
return {
|
|
1647
|
+
intervalMs: options.unthrottled ? 0 : frameInterval,
|
|
1648
|
+
throttled,
|
|
1649
|
+
get pending() {
|
|
1650
|
+
return pending;
|
|
1651
|
+
},
|
|
1652
|
+
schedule: options.unthrottled ? render : () => {
|
|
1653
|
+
pending = true;
|
|
1654
|
+
throttled();
|
|
1655
|
+
},
|
|
1656
|
+
immediate: render,
|
|
1657
|
+
markRendered() {
|
|
1658
|
+
pending = false;
|
|
1659
|
+
}
|
|
2575
1660
|
};
|
|
2576
|
-
}
|
|
1661
|
+
}
|
|
2577
1662
|
//#endregion
|
|
2578
1663
|
//#region src/utils.ts
|
|
2579
1664
|
const resolveDimension = (value, fallback, defaultValue) => {
|
|
@@ -2603,6 +1688,28 @@ const getWindowSize = (stdout) => {
|
|
|
2603
1688
|
//#region src/ink.tsx
|
|
2604
1689
|
/** @jsxImportSource react */
|
|
2605
1690
|
const noop = () => {};
|
|
1691
|
+
const beforeExitCallbacks = /* @__PURE__ */ new Set();
|
|
1692
|
+
const runBeforeExitCallbacks = () => {
|
|
1693
|
+
for (const callback of beforeExitCallbacks) callback();
|
|
1694
|
+
};
|
|
1695
|
+
function registerBeforeExit(callback) {
|
|
1696
|
+
if (beforeExitCallbacks.size === 0) process.on("beforeExit", runBeforeExitCallbacks);
|
|
1697
|
+
beforeExitCallbacks.add(callback);
|
|
1698
|
+
return () => {
|
|
1699
|
+
beforeExitCallbacks.delete(callback);
|
|
1700
|
+
if (beforeExitCallbacks.size === 0) process.off("beforeExit", runBeforeExitCallbacks);
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
function bottomRows(screen, height) {
|
|
1704
|
+
if (screen.height <= height) return screen;
|
|
1705
|
+
const cropped = new Screen(screen.width, height);
|
|
1706
|
+
const offset = screen.height - height;
|
|
1707
|
+
for (let y = 0; y < height; y++) for (let x = 0; x < screen.width; x++) {
|
|
1708
|
+
const cell = screen.cellAt(x, y + offset);
|
|
1709
|
+
if (cell && cell.width > 0) cropped.setCell(x, y, cell);
|
|
1710
|
+
}
|
|
1711
|
+
return cropped;
|
|
1712
|
+
}
|
|
2606
1713
|
const shouldClearTerminalForFrame = ({ isTTY, viewportRows, previousOutputHeight, nextOutputHeight, isUnmounting }) => {
|
|
2607
1714
|
if (!isTTY) return false;
|
|
2608
1715
|
const hadPreviousFrame = previousOutputHeight > 0;
|
|
@@ -2659,45 +1766,46 @@ const createInk = (options) => {
|
|
|
2659
1766
|
rootNode.onComputeLayout = calculateLayout;
|
|
2660
1767
|
const isScreenReaderEnabled = options.isScreenReaderEnabled ?? isScreenReader;
|
|
2661
1768
|
const interactive = options.interactive ?? (!isInCi && Boolean(options.stdout.isTTY));
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
if (sync && shouldWrite) options.stdout.write(bsu);
|
|
2686
|
-
log(output);
|
|
2687
|
-
if (sync && shouldWrite) options.stdout.write(esu);
|
|
1769
|
+
const terminal = new TerminalSession({
|
|
1770
|
+
stdin: options.stdin,
|
|
1771
|
+
stdout: options.stdout,
|
|
1772
|
+
stderr: options.stderr,
|
|
1773
|
+
colorPolicy: options.colorProfile ?? "auto",
|
|
1774
|
+
onCapabilitiesChange: () => rootNode.onRender?.()
|
|
1775
|
+
});
|
|
1776
|
+
const terminalOsc = {
|
|
1777
|
+
publishProgress: (owner, state, value) => {
|
|
1778
|
+
terminal.publishProgress(owner, state, value);
|
|
1779
|
+
},
|
|
1780
|
+
copyToClipboard: (text, selection) => {
|
|
1781
|
+
terminal.copyToClipboard(text, selection);
|
|
1782
|
+
},
|
|
1783
|
+
publishTitle: (owner, title) => terminal.publishTitle(owner, title),
|
|
1784
|
+
setWorkingDirectory: (directory) => terminal.setWorkingDirectory(directory),
|
|
1785
|
+
notify: (title) => terminal.notify(title),
|
|
1786
|
+
setPointerShape: (shape) => terminal.setPointerShape(shape)
|
|
1787
|
+
};
|
|
1788
|
+
const capabilitiesStore = terminal.capabilities;
|
|
1789
|
+
const renderScheduler = createRenderScheduler(onRender, {
|
|
1790
|
+
unthrottled: options.debug || isScreenReaderEnabled,
|
|
1791
|
+
maxFps: options.maxFps ?? 30
|
|
2688
1792
|
});
|
|
2689
|
-
|
|
1793
|
+
rootNode.onRender = renderScheduler.schedule;
|
|
1794
|
+
rootNode.onImmediateRender = renderScheduler.immediate;
|
|
1795
|
+
rootNode.onStaticChange = handleStaticChange;
|
|
1796
|
+
const accessiblePresenter = isScreenReaderEnabled ? createInlinePresenter(options.stdout, { showCursor: true }) : void 0;
|
|
2690
1797
|
let isUnmounted = false;
|
|
2691
1798
|
let isUnmounting = false;
|
|
2692
1799
|
const isConcurrent = options.concurrent ?? false;
|
|
2693
1800
|
let lastOutput = "";
|
|
2694
1801
|
let lastOutputToRender = "";
|
|
2695
1802
|
let lastOutputHeight = 0;
|
|
1803
|
+
let lastScreen;
|
|
2696
1804
|
let lastTerminalWidth = getWindowSize(options.stdout).columns;
|
|
2697
1805
|
let lastTerminalHeight = getWindowSize(options.stdout).rows;
|
|
2698
1806
|
let fullStaticOutput = "";
|
|
2699
1807
|
let exitResult;
|
|
2700
|
-
let
|
|
1808
|
+
let unsubscribeBeforeExit;
|
|
2701
1809
|
let restoreConsole;
|
|
2702
1810
|
const capturedStdioTails = {
|
|
2703
1811
|
stdout: "",
|
|
@@ -2708,7 +1816,6 @@ const createInk = (options) => {
|
|
|
2708
1816
|
let kittyFlags;
|
|
2709
1817
|
let cancelKittyDetection;
|
|
2710
1818
|
let nextRenderCommit;
|
|
2711
|
-
let isSuspended = false;
|
|
2712
1819
|
let pauseInput;
|
|
2713
1820
|
let resumeInput;
|
|
2714
1821
|
const rootTag = isConcurrent ? ConcurrentRoot : LegacyRoot;
|
|
@@ -2730,7 +1837,8 @@ const createInk = (options) => {
|
|
|
2730
1837
|
const currentWidth = getWindowSize(options.stdout).columns;
|
|
2731
1838
|
const currentHeight = getWindowSize(options.stdout).rows;
|
|
2732
1839
|
if (currentWidth < lastTerminalWidth || currentHeight !== lastTerminalHeight) {
|
|
2733
|
-
|
|
1840
|
+
clearLiveOutput();
|
|
1841
|
+
resetLiveOutput();
|
|
2734
1842
|
lastOutput = "";
|
|
2735
1843
|
lastOutputToRender = "";
|
|
2736
1844
|
lastOutputHeight = 0;
|
|
@@ -2751,13 +1859,30 @@ const createInk = (options) => {
|
|
|
2751
1859
|
unmount();
|
|
2752
1860
|
}
|
|
2753
1861
|
function setCursorPosition(position) {
|
|
2754
|
-
|
|
2755
|
-
|
|
1862
|
+
terminal.setCursor(position);
|
|
1863
|
+
accessiblePresenter?.setCursorPosition(position);
|
|
2756
1864
|
}
|
|
2757
1865
|
function restoreLastOutput() {
|
|
2758
1866
|
if (!interactive) return;
|
|
2759
|
-
|
|
2760
|
-
|
|
1867
|
+
if (isScreenReaderEnabled) {
|
|
1868
|
+
accessiblePresenter.setCursorPosition(terminal.cursor.position);
|
|
1869
|
+
accessiblePresenter(lastOutputToRender || lastOutput + "\n");
|
|
1870
|
+
} else if (lastScreen) terminal.present(lastScreen, {
|
|
1871
|
+
fullscreen: lastOutputToRender === lastOutput,
|
|
1872
|
+
forceRewrite: true
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
function clearLiveOutput() {
|
|
1876
|
+
if (isScreenReaderEnabled) accessiblePresenter.clear();
|
|
1877
|
+
else terminal.clearFrame();
|
|
1878
|
+
}
|
|
1879
|
+
function finishLiveOutput() {
|
|
1880
|
+
if (isScreenReaderEnabled) accessiblePresenter.done();
|
|
1881
|
+
else terminal.finishFrame();
|
|
1882
|
+
}
|
|
1883
|
+
function resetLiveOutput() {
|
|
1884
|
+
if (isScreenReaderEnabled) accessiblePresenter.reset();
|
|
1885
|
+
else terminal.resetFrame();
|
|
2761
1886
|
}
|
|
2762
1887
|
function calculateLayout() {
|
|
2763
1888
|
const terminalWidth = getWindowSize(options.stdout).columns;
|
|
@@ -2768,9 +1893,9 @@ const createInk = (options) => {
|
|
|
2768
1893
|
fullStaticOutput = "";
|
|
2769
1894
|
}
|
|
2770
1895
|
function onRender() {
|
|
2771
|
-
|
|
1896
|
+
renderScheduler.markRendered();
|
|
2772
1897
|
if (isUnmounted) return;
|
|
2773
|
-
if (
|
|
1898
|
+
if (terminal.suspended) {
|
|
2774
1899
|
if (nextRenderCommit) {
|
|
2775
1900
|
nextRenderCommit.resolve();
|
|
2776
1901
|
nextRenderCommit = void 0;
|
|
@@ -2782,7 +1907,18 @@ const createInk = (options) => {
|
|
|
2782
1907
|
nextRenderCommit = void 0;
|
|
2783
1908
|
}
|
|
2784
1909
|
const startTime = performance.now();
|
|
2785
|
-
const
|
|
1910
|
+
const rendered = renderFrame(rootNode, isScreenReaderEnabled, {
|
|
1911
|
+
colorProfile: terminal.colorProfile,
|
|
1912
|
+
paintContext: {
|
|
1913
|
+
appearance: capabilitiesStore.current.theme.appearance,
|
|
1914
|
+
palette: capabilitiesStore.current.theme.palette
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
const screen = rendered.screen;
|
|
1918
|
+
const output = rendered.accessibleText ?? (screen ? terminal.encode(screen) : "");
|
|
1919
|
+
const outputHeight = rendered.accessibleText === void 0 ? screen?.height ?? 0 : rendered.accessibleText === "" ? 0 : rendered.accessibleText.split("\n").length;
|
|
1920
|
+
const staticBody = rendered.staticAccessibleText ?? (rendered.staticScreen ? terminal.encode(rendered.staticScreen) : "");
|
|
1921
|
+
const staticOutput = staticBody ? `${staticBody}\n` : "";
|
|
2786
1922
|
options.onRender?.({ renderTime: performance.now() - startTime });
|
|
2787
1923
|
const hasStaticOutput = staticOutput && staticOutput !== "\n";
|
|
2788
1924
|
if (options.debug) {
|
|
@@ -2829,26 +1965,30 @@ const createInk = (options) => {
|
|
|
2829
1965
|
return;
|
|
2830
1966
|
}
|
|
2831
1967
|
if (hasStaticOutput) fullStaticOutput += staticOutput;
|
|
2832
|
-
renderInteractiveFrame(output, outputHeight, hasStaticOutput ? staticOutput : "");
|
|
1968
|
+
renderInteractiveFrame(output, outputHeight, hasStaticOutput ? staticOutput : "", screen, false);
|
|
2833
1969
|
}
|
|
2834
1970
|
function render(node) {
|
|
2835
1971
|
const tree = /* @__PURE__ */ jsx(accessibilityContext.Provider, {
|
|
2836
1972
|
value: { isScreenReaderEnabled },
|
|
2837
|
-
children: /* @__PURE__ */ jsx(
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
1973
|
+
children: /* @__PURE__ */ jsx(TerminalOscContext.Provider, {
|
|
1974
|
+
value: terminalOsc,
|
|
1975
|
+
children: /* @__PURE__ */ jsx(App, {
|
|
1976
|
+
stdin: options.stdin,
|
|
1977
|
+
stdout: options.stdout,
|
|
1978
|
+
stderr: options.stderr,
|
|
1979
|
+
exitOnCtrlC: options.exitOnCtrlC,
|
|
1980
|
+
interactive,
|
|
1981
|
+
renderThrottleMs: renderScheduler.intervalMs,
|
|
1982
|
+
terminalInput: terminal.input,
|
|
1983
|
+
writeToStdout,
|
|
1984
|
+
writeToStderr,
|
|
1985
|
+
setCursorPosition,
|
|
1986
|
+
onExit: handleAppExit,
|
|
1987
|
+
onWaitUntilRenderFlush: waitUntilRenderFlush,
|
|
1988
|
+
onSuspendTerminal: suspendTerminal,
|
|
1989
|
+
onRegisterInputControl: registerInputControl,
|
|
1990
|
+
children: node
|
|
1991
|
+
})
|
|
2852
1992
|
})
|
|
2853
1993
|
});
|
|
2854
1994
|
if (isConcurrent) reconciler.updateContainer(tree, container, null, noop);
|
|
@@ -2859,7 +1999,7 @@ const createInk = (options) => {
|
|
|
2859
1999
|
}
|
|
2860
2000
|
function writeToStdout(data) {
|
|
2861
2001
|
if (isUnmounted) return;
|
|
2862
|
-
if (
|
|
2002
|
+
if (terminal.suspended) return;
|
|
2863
2003
|
if (options.debug) {
|
|
2864
2004
|
options.stdout.write(data + fullStaticOutput + lastOutput);
|
|
2865
2005
|
return;
|
|
@@ -2870,14 +2010,14 @@ const createInk = (options) => {
|
|
|
2870
2010
|
}
|
|
2871
2011
|
const sync = shouldSync();
|
|
2872
2012
|
if (sync) options.stdout.write(bsu);
|
|
2873
|
-
|
|
2013
|
+
clearLiveOutput();
|
|
2874
2014
|
options.stdout.write(data);
|
|
2875
2015
|
restoreLastOutput();
|
|
2876
2016
|
if (sync) options.stdout.write(esu);
|
|
2877
2017
|
}
|
|
2878
2018
|
function writeToStderr(data) {
|
|
2879
2019
|
if (isUnmounted) return;
|
|
2880
|
-
if (
|
|
2020
|
+
if (terminal.suspended) return;
|
|
2881
2021
|
if (options.debug) {
|
|
2882
2022
|
options.stderr.write(data);
|
|
2883
2023
|
options.stdout.write(fullStaticOutput + lastOutput);
|
|
@@ -2889,7 +2029,7 @@ const createInk = (options) => {
|
|
|
2889
2029
|
}
|
|
2890
2030
|
const sync = shouldSync();
|
|
2891
2031
|
if (sync) options.stdout.write(bsu);
|
|
2892
|
-
|
|
2032
|
+
clearLiveOutput();
|
|
2893
2033
|
options.stderr.write(data);
|
|
2894
2034
|
restoreLastOutput();
|
|
2895
2035
|
if (sync) options.stdout.write(esu);
|
|
@@ -2897,35 +2037,32 @@ const createInk = (options) => {
|
|
|
2897
2037
|
function unmount(error) {
|
|
2898
2038
|
if (isUnmounted || isUnmounting) return;
|
|
2899
2039
|
isUnmounting = true;
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
beforeExitHandler = void 0;
|
|
2903
|
-
}
|
|
2040
|
+
unsubscribeBeforeExit?.();
|
|
2041
|
+
unsubscribeBeforeExit = void 0;
|
|
2904
2042
|
const { canWriteToStdout } = getWritableStreamState(options.stdout);
|
|
2905
2043
|
if (canWriteToStdout) flushCapturedStdio();
|
|
2906
|
-
settleThrottle(
|
|
2044
|
+
settleThrottle(renderScheduler.throttled, canWriteToStdout);
|
|
2907
2045
|
if (canWriteToStdout) {
|
|
2908
|
-
if (!
|
|
2046
|
+
if (!renderScheduler.throttled || !renderScheduler.pending && fullStaticOutput === "") {
|
|
2909
2047
|
calculateLayout();
|
|
2910
2048
|
onRender();
|
|
2911
2049
|
}
|
|
2912
2050
|
}
|
|
2913
2051
|
isUnmounted = true;
|
|
2914
2052
|
unsubscribeExit();
|
|
2915
|
-
|
|
2053
|
+
terminal.cleanup();
|
|
2916
2054
|
if (typeof restoreConsole === "function") restoreConsole();
|
|
2917
2055
|
const finishUnmount = () => {
|
|
2918
2056
|
if (typeof unsubscribeResize === "function") unsubscribeResize();
|
|
2919
2057
|
if (cancelKittyDetection) cancelKittyDetection();
|
|
2920
2058
|
if (canWriteToStdout) {
|
|
2921
2059
|
if (kittyProtocolEnabled) writeBestEffort(options.stdout, ansiEscapes.popKittyKeyboard);
|
|
2922
|
-
if (alternateScreen) {
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
alternateScreen = false;
|
|
2060
|
+
if (terminal.alternateScreen) {
|
|
2061
|
+
terminal.setAlternateScreen(false);
|
|
2062
|
+
terminal.setCursorAppearance({ visible: true });
|
|
2926
2063
|
}
|
|
2927
2064
|
if (!interactive) options.stdout.write(options.debug ? "\n" : lastOutput + "\n");
|
|
2928
|
-
else if (!options.debug)
|
|
2065
|
+
else if (!options.debug) finishLiveOutput();
|
|
2929
2066
|
}
|
|
2930
2067
|
kittyProtocolEnabled = false;
|
|
2931
2068
|
instances.delete(captureTargets?.stdout ?? options.stdout);
|
|
@@ -2951,12 +2088,9 @@ const createInk = (options) => {
|
|
|
2951
2088
|
}
|
|
2952
2089
|
}
|
|
2953
2090
|
async function waitUntilExit() {
|
|
2954
|
-
if (!
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
};
|
|
2958
|
-
process.once("beforeExit", beforeExitHandler);
|
|
2959
|
-
}
|
|
2091
|
+
if (!unsubscribeBeforeExit) unsubscribeBeforeExit = registerBeforeExit(() => {
|
|
2092
|
+
unmount();
|
|
2093
|
+
});
|
|
2960
2094
|
return exitPromise;
|
|
2961
2095
|
}
|
|
2962
2096
|
async function waitUntilRenderFlush() {
|
|
@@ -2979,8 +2113,7 @@ const createInk = (options) => {
|
|
|
2979
2113
|
}
|
|
2980
2114
|
reconciler.flushSyncWork();
|
|
2981
2115
|
const { canWriteToStdout } = getWritableStreamState(options.stdout);
|
|
2982
|
-
settleThrottle(
|
|
2983
|
-
settleThrottle(logThrottle, canWriteToStdout);
|
|
2116
|
+
settleThrottle(renderScheduler.throttled, canWriteToStdout);
|
|
2984
2117
|
if (canWriteToStdout) {
|
|
2985
2118
|
await new Promise((resolve) => {
|
|
2986
2119
|
options.stdout.write("", () => {
|
|
@@ -2993,8 +2126,8 @@ const createInk = (options) => {
|
|
|
2993
2126
|
}
|
|
2994
2127
|
function clear() {
|
|
2995
2128
|
if (interactive && !options.debug) {
|
|
2996
|
-
|
|
2997
|
-
|
|
2129
|
+
clearLiveOutput();
|
|
2130
|
+
if (isScreenReaderEnabled) accessiblePresenter.sync(lastOutputToRender || lastOutput + "\n");
|
|
2998
2131
|
}
|
|
2999
2132
|
}
|
|
3000
2133
|
function installConsolePatch() {
|
|
@@ -3066,11 +2199,7 @@ const createInk = (options) => {
|
|
|
3066
2199
|
};
|
|
3067
2200
|
}
|
|
3068
2201
|
function setAlternateScreen(enabled) {
|
|
3069
|
-
|
|
3070
|
-
if (alternateScreen) {
|
|
3071
|
-
writeBestEffort(options.stdout, ansiEscapes.enterAlternativeScreen);
|
|
3072
|
-
writeBestEffort(options.stdout, hideCursorEscape);
|
|
3073
|
-
}
|
|
2202
|
+
terminal.setAlternateScreen(enabled && interactive && Boolean(options.stdout.isTTY), { hideCursor: true });
|
|
3074
2203
|
}
|
|
3075
2204
|
function shouldSync() {
|
|
3076
2205
|
return Boolean(options.stdout.isTTY) && interactive;
|
|
@@ -3088,7 +2217,8 @@ const createInk = (options) => {
|
|
|
3088
2217
|
nextRenderCommit ??= Promise.withResolvers();
|
|
3089
2218
|
return nextRenderCommit.promise;
|
|
3090
2219
|
}
|
|
3091
|
-
function renderInteractiveFrame(output, outputHeight, staticOutput) {
|
|
2220
|
+
function renderInteractiveFrame(output, outputHeight, staticOutput, screen, hasOverflow) {
|
|
2221
|
+
if (!screen) return;
|
|
3092
2222
|
const hasStaticOutput = staticOutput !== "";
|
|
3093
2223
|
const isTTY = Boolean(options.stdout.isTTY);
|
|
3094
2224
|
const viewportRows = isTTY ? getWindowSize(options.stdout).rows : 24;
|
|
@@ -3096,8 +2226,10 @@ const createInk = (options) => {
|
|
|
3096
2226
|
const lines = output.split("\n");
|
|
3097
2227
|
output = lines.slice(lines.length - viewportRows).join("\n");
|
|
3098
2228
|
outputHeight = viewportRows;
|
|
2229
|
+
screen = bottomRows(screen, viewportRows);
|
|
3099
2230
|
}
|
|
3100
|
-
const
|
|
2231
|
+
const isFullscreen = isTTY && outputHeight >= viewportRows;
|
|
2232
|
+
const outputToRender = isFullscreen ? output : output + "\n";
|
|
3101
2233
|
if (shouldClearTerminalForFrame({
|
|
3102
2234
|
isTTY,
|
|
3103
2235
|
viewportRows,
|
|
@@ -3111,21 +2243,30 @@ const createInk = (options) => {
|
|
|
3111
2243
|
lastOutput = output;
|
|
3112
2244
|
lastOutputToRender = outputToRender;
|
|
3113
2245
|
lastOutputHeight = outputHeight;
|
|
3114
|
-
|
|
2246
|
+
lastScreen = screen;
|
|
2247
|
+
terminal.resetFrame();
|
|
3115
2248
|
if (sync) options.stdout.write(esu);
|
|
3116
2249
|
return;
|
|
3117
2250
|
}
|
|
2251
|
+
const willPresent = terminal.willPresent(screen, {
|
|
2252
|
+
fullscreen: isFullscreen,
|
|
2253
|
+
forceRewrite: hasStaticOutput || hasOverflow
|
|
2254
|
+
});
|
|
2255
|
+
const sync = shouldSync() && willPresent;
|
|
2256
|
+
if (sync) terminal.write(bsu);
|
|
3118
2257
|
if (hasStaticOutput) {
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
}
|
|
2258
|
+
terminal.clearFrame();
|
|
2259
|
+
terminal.write(staticOutput);
|
|
2260
|
+
}
|
|
2261
|
+
terminal.present(screen, {
|
|
2262
|
+
fullscreen: isFullscreen,
|
|
2263
|
+
forceRewrite: hasStaticOutput || hasOverflow
|
|
2264
|
+
});
|
|
2265
|
+
if (sync) terminal.write(esu);
|
|
3126
2266
|
lastOutput = output;
|
|
3127
2267
|
lastOutputToRender = outputToRender;
|
|
3128
2268
|
lastOutputHeight = outputHeight;
|
|
2269
|
+
lastScreen = screen;
|
|
3129
2270
|
}
|
|
3130
2271
|
function initKittyKeyboard() {
|
|
3131
2272
|
if (!options.kittyKeyboard) return;
|
|
@@ -3149,23 +2290,21 @@ const createInk = (options) => {
|
|
|
3149
2290
|
kittyFlags = flags;
|
|
3150
2291
|
}
|
|
3151
2292
|
function beginSuspend() {
|
|
3152
|
-
|
|
3153
|
-
isSuspended = true;
|
|
2293
|
+
terminal.beginSuspension();
|
|
3154
2294
|
if (!interactive || isUnmounted || isUnmounting) return;
|
|
3155
2295
|
try {
|
|
3156
2296
|
const { canWriteToStdout } = getWritableStreamState(options.stdout);
|
|
3157
|
-
settleThrottle(
|
|
3158
|
-
settleThrottle(logThrottle, canWriteToStdout);
|
|
2297
|
+
settleThrottle(renderScheduler.throttled, canWriteToStdout);
|
|
3159
2298
|
if (canWriteToStdout) flushCapturedStdio();
|
|
3160
2299
|
if (canWriteToStdout) {
|
|
3161
|
-
|
|
3162
|
-
|
|
2300
|
+
clearLiveOutput();
|
|
2301
|
+
finishLiveOutput();
|
|
3163
2302
|
if (kittyProtocolEnabled) writeBestEffort(options.stdout, ansiEscapes.popKittyKeyboard);
|
|
3164
|
-
if (alternateScreen) writeBestEffort(options.stdout, ansiEscapes.exitAlternativeScreen);
|
|
2303
|
+
if (terminal.alternateScreen) writeBestEffort(options.stdout, ansiEscapes.exitAlternativeScreen);
|
|
3165
2304
|
}
|
|
3166
2305
|
pauseInput?.();
|
|
3167
2306
|
} catch (error) {
|
|
3168
|
-
|
|
2307
|
+
terminal.resume();
|
|
3169
2308
|
try {
|
|
3170
2309
|
resumeInput?.();
|
|
3171
2310
|
} catch {}
|
|
@@ -3173,19 +2312,19 @@ const createInk = (options) => {
|
|
|
3173
2312
|
}
|
|
3174
2313
|
}
|
|
3175
2314
|
async function endSuspend() {
|
|
3176
|
-
if (!
|
|
3177
|
-
|
|
2315
|
+
if (!terminal.suspended) return;
|
|
2316
|
+
terminal.resume();
|
|
3178
2317
|
resumeInput?.();
|
|
3179
2318
|
if (!interactive || isUnmounted || isUnmounting) return;
|
|
3180
2319
|
const { canWriteToStdout } = getWritableStreamState(options.stdout);
|
|
3181
2320
|
if (canWriteToStdout) {
|
|
3182
|
-
if (alternateScreen) writeBestEffort(options.stdout, ansiEscapes.enterAlternativeScreen);
|
|
2321
|
+
if (terminal.alternateScreen) writeBestEffort(options.stdout, ansiEscapes.enterAlternativeScreen);
|
|
3183
2322
|
if (kittyProtocolEnabled && kittyFlags) writeBestEffort(options.stdout, ansiEscapes.pushKittyKeyboard(resolveFlags(kittyFlags)));
|
|
3184
2323
|
}
|
|
3185
2324
|
lastOutput = "";
|
|
3186
2325
|
lastOutputToRender = "";
|
|
3187
2326
|
lastOutputHeight = 0;
|
|
3188
|
-
|
|
2327
|
+
resetLiveOutput();
|
|
3189
2328
|
try {
|
|
3190
2329
|
calculateLayout();
|
|
3191
2330
|
onRender();
|
|
@@ -3197,7 +2336,9 @@ const createInk = (options) => {
|
|
|
3197
2336
|
unmount,
|
|
3198
2337
|
waitUntilExit,
|
|
3199
2338
|
waitUntilRenderFlush,
|
|
3200
|
-
clear
|
|
2339
|
+
clear,
|
|
2340
|
+
copyToClipboard: (text, selection) => terminal.copyToClipboard(text, selection),
|
|
2341
|
+
setProgress: (state, value) => terminal.setProgress(state, value)
|
|
3201
2342
|
};
|
|
3202
2343
|
};
|
|
3203
2344
|
//#endregion
|
|
@@ -3214,7 +2355,6 @@ const render = (node, options) => {
|
|
|
3214
2355
|
exitOnCtrlC: true,
|
|
3215
2356
|
patchConsole: true,
|
|
3216
2357
|
maxFps: 30,
|
|
3217
|
-
incrementalRendering: false,
|
|
3218
2358
|
concurrent: false,
|
|
3219
2359
|
alternateScreen: false,
|
|
3220
2360
|
...getOptions(options)
|
|
@@ -3231,7 +2371,9 @@ const render = (node, options) => {
|
|
|
3231
2371
|
cleanup() {
|
|
3232
2372
|
instance.unmount();
|
|
3233
2373
|
},
|
|
3234
|
-
clear: instance.clear
|
|
2374
|
+
clear: instance.clear,
|
|
2375
|
+
copyToClipboard: instance.copyToClipboard,
|
|
2376
|
+
setProgress: instance.setProgress
|
|
3235
2377
|
};
|
|
3236
2378
|
};
|
|
3237
2379
|
const getOptions = (stdout = {}) => {
|
|
@@ -3282,6 +2424,7 @@ console.log(output);
|
|
|
3282
2424
|
*/
|
|
3283
2425
|
const renderToString = (node, options) => {
|
|
3284
2426
|
const columns = options?.columns ?? 80;
|
|
2427
|
+
const colorProfile = options?.colorProfile ?? colorProfileFromLevel(detectColorLevel());
|
|
3285
2428
|
const rootNode = createNode("ink-root");
|
|
3286
2429
|
let capturedStaticOutput = "";
|
|
3287
2430
|
rootNode.onComputeLayout = () => {
|
|
@@ -3289,7 +2432,11 @@ const renderToString = (node, options) => {
|
|
|
3289
2432
|
rootNode.yogaNode.calculateLayout(void 0, void 0, Yoga.DIRECTION_LTR);
|
|
3290
2433
|
};
|
|
3291
2434
|
rootNode.onImmediateRender = () => {
|
|
3292
|
-
const {
|
|
2435
|
+
const { staticScreen } = renderFrame(rootNode, false, { colorProfile });
|
|
2436
|
+
const staticOutput = staticScreen ? `${serializeScreen(staticScreen, {
|
|
2437
|
+
colorProfile,
|
|
2438
|
+
styles: colorProfile !== "none"
|
|
2439
|
+
})}\n` : "";
|
|
3293
2440
|
if (staticOutput && staticOutput !== "\n") capturedStaticOutput += staticOutput;
|
|
3294
2441
|
};
|
|
3295
2442
|
let uncaughtError;
|
|
@@ -3298,7 +2445,11 @@ const renderToString = (node, options) => {
|
|
|
3298
2445
|
}, () => {}, () => {}, () => {});
|
|
3299
2446
|
reconciler.updateContainerSync(node, container, null, () => {});
|
|
3300
2447
|
reconciler.flushSyncWork();
|
|
3301
|
-
const {
|
|
2448
|
+
const { screen } = renderFrame(rootNode, false, { colorProfile });
|
|
2449
|
+
const output = screen ? serializeScreen(screen, {
|
|
2450
|
+
colorProfile,
|
|
2451
|
+
styles: colorProfile !== "none"
|
|
2452
|
+
}) : "";
|
|
3302
2453
|
reconciler.updateContainerSync(null, container, null, () => {});
|
|
3303
2454
|
reconciler.flushSyncWork();
|
|
3304
2455
|
if (uncaughtError !== void 0) throw uncaughtError instanceof Error ? uncaughtError : new Error(String(uncaughtError));
|
|
@@ -3307,6 +2458,30 @@ const renderToString = (node, options) => {
|
|
|
3307
2458
|
return normalizedStaticOutput || output;
|
|
3308
2459
|
};
|
|
3309
2460
|
//#endregion
|
|
2461
|
+
//#region src/components/AnsiText.tsx
|
|
2462
|
+
/** @jsxImportSource react */
|
|
2463
|
+
/**
|
|
2464
|
+
* Renders explicitly trusted ANSI-styled output as structured terminal cells.
|
|
2465
|
+
* Ordinary `Text` continues to strip terminal control sequences.
|
|
2466
|
+
*/
|
|
2467
|
+
function AnsiText({ children, wrap = "wrap", "aria-label": ariaLabel, "aria-hidden": ariaHidden = false }) {
|
|
2468
|
+
const { isScreenReaderEnabled } = useContext(accessibilityContext);
|
|
2469
|
+
if (isScreenReaderEnabled) {
|
|
2470
|
+
if (ariaHidden) return null;
|
|
2471
|
+
return /* @__PURE__ */ jsx(Text, { children: ariaLabel ?? stripAnsi(children) });
|
|
2472
|
+
}
|
|
2473
|
+
return /* @__PURE__ */ jsx("ink-text", {
|
|
2474
|
+
style: {
|
|
2475
|
+
flexGrow: 0,
|
|
2476
|
+
flexShrink: 1,
|
|
2477
|
+
flexDirection: "row",
|
|
2478
|
+
textWrap: wrap
|
|
2479
|
+
},
|
|
2480
|
+
internal_ansi: true,
|
|
2481
|
+
children
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
//#endregion
|
|
3310
2485
|
//#region src/components/Static.tsx
|
|
3311
2486
|
/** @jsxImportSource react */
|
|
3312
2487
|
/**
|
|
@@ -3359,6 +2534,44 @@ function Transform({ children, transform, accessibilityLabel }) {
|
|
|
3359
2534
|
});
|
|
3360
2535
|
}
|
|
3361
2536
|
//#endregion
|
|
2537
|
+
//#region src/hooks/use-stdout.ts
|
|
2538
|
+
/**
|
|
2539
|
+
A React hook that returns the stdout stream where Ink renders your app.
|
|
2540
|
+
*/
|
|
2541
|
+
const useStdout = () => useContext(StdoutContext);
|
|
2542
|
+
//#endregion
|
|
2543
|
+
//#region src/components/Hyperlink.tsx
|
|
2544
|
+
/**
|
|
2545
|
+
A clickable OSC 8 hyperlink — the counterpart to the router's `<Link>`, which
|
|
2546
|
+
navigates between screens. On terminals without hyperlink support it falls
|
|
2547
|
+
back to `text (url)`.
|
|
2548
|
+
|
|
2549
|
+
```tsx
|
|
2550
|
+
<Hyperlink url="https://example.com">Documentation</Hyperlink>
|
|
2551
|
+
```
|
|
2552
|
+
*/
|
|
2553
|
+
function Hyperlink({ url, fallback = true, children, ...textProps }) {
|
|
2554
|
+
const { stdout } = useStdout();
|
|
2555
|
+
if (!detectHyperlinkSupport(stdout)) return /* @__PURE__ */ jsxs(Text, {
|
|
2556
|
+
...textProps,
|
|
2557
|
+
children: [children, fallback ? /* @__PURE__ */ jsxs(Text, {
|
|
2558
|
+
dimColor: true,
|
|
2559
|
+
children: [
|
|
2560
|
+
" (",
|
|
2561
|
+
url,
|
|
2562
|
+
")"
|
|
2563
|
+
]
|
|
2564
|
+
}) : null]
|
|
2565
|
+
});
|
|
2566
|
+
return /* @__PURE__ */ jsx(Transform, {
|
|
2567
|
+
transform: (text) => link(text, url),
|
|
2568
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2569
|
+
...textProps,
|
|
2570
|
+
children
|
|
2571
|
+
})
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
//#endregion
|
|
3362
2575
|
//#region src/components/Newline.tsx
|
|
3363
2576
|
/**
|
|
3364
2577
|
Adds one or more newline (`\n`) characters. Must be used within `<Text>` components.
|
|
@@ -3378,486 +2591,60 @@ function Spacer() {
|
|
|
3378
2591
|
return /* @__PURE__ */ jsx(Box, { flexGrow: 1 });
|
|
3379
2592
|
}
|
|
3380
2593
|
//#endregion
|
|
3381
|
-
//#region src/hooks/use-
|
|
2594
|
+
//#region src/hooks/use-capabilities.ts
|
|
3382
2595
|
/**
|
|
3383
|
-
|
|
2596
|
+
Returns everything knowable about the terminal: size, identity, platform,
|
|
2597
|
+
color depth, theme, and feature support.
|
|
2598
|
+
|
|
2599
|
+
A thin wrapper over the framework-free capabilities store (`getCapabilities`):
|
|
2600
|
+
environment-derived facts are available immediately; facts only the terminal
|
|
2601
|
+
itself can answer fill in after a lazy one-time query, and re-mounting
|
|
2602
|
+
consumers refreshes the dynamic facts (theme colors, pixel geometry).
|
|
2603
|
+
Re-renders on terminal resize and whenever query answers arrive.
|
|
3384
2604
|
*/
|
|
3385
|
-
const
|
|
3386
|
-
const
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
const
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
OQ: "f2",
|
|
3395
|
-
OR: "f3",
|
|
3396
|
-
OS: "f4",
|
|
3397
|
-
"[P": "f1",
|
|
3398
|
-
"[Q": "f2",
|
|
3399
|
-
"[R": "f3",
|
|
3400
|
-
"[S": "f4",
|
|
3401
|
-
"[11~": "f1",
|
|
3402
|
-
"[12~": "f2",
|
|
3403
|
-
"[13~": "f3",
|
|
3404
|
-
"[14~": "f4",
|
|
3405
|
-
"[[A": "f1",
|
|
3406
|
-
"[[B": "f2",
|
|
3407
|
-
"[[C": "f3",
|
|
3408
|
-
"[[D": "f4",
|
|
3409
|
-
"[[E": "f5",
|
|
3410
|
-
"[15~": "f5",
|
|
3411
|
-
"[17~": "f6",
|
|
3412
|
-
"[18~": "f7",
|
|
3413
|
-
"[19~": "f8",
|
|
3414
|
-
"[20~": "f9",
|
|
3415
|
-
"[21~": "f10",
|
|
3416
|
-
"[23~": "f11",
|
|
3417
|
-
"[24~": "f12",
|
|
3418
|
-
"[A": "up",
|
|
3419
|
-
"[B": "down",
|
|
3420
|
-
"[C": "right",
|
|
3421
|
-
"[D": "left",
|
|
3422
|
-
"[E": "clear",
|
|
3423
|
-
"[F": "end",
|
|
3424
|
-
"[H": "home",
|
|
3425
|
-
OA: "up",
|
|
3426
|
-
OB: "down",
|
|
3427
|
-
OC: "right",
|
|
3428
|
-
OD: "left",
|
|
3429
|
-
OE: "clear",
|
|
3430
|
-
OF: "end",
|
|
3431
|
-
OH: "home",
|
|
3432
|
-
"[1~": "home",
|
|
3433
|
-
"[2~": "insert",
|
|
3434
|
-
"[3~": "delete",
|
|
3435
|
-
"[4~": "end",
|
|
3436
|
-
"[5~": "pageup",
|
|
3437
|
-
"[6~": "pagedown",
|
|
3438
|
-
"[[5~": "pageup",
|
|
3439
|
-
"[[6~": "pagedown",
|
|
3440
|
-
"[7~": "home",
|
|
3441
|
-
"[8~": "end",
|
|
3442
|
-
"[a": "up",
|
|
3443
|
-
"[b": "down",
|
|
3444
|
-
"[c": "right",
|
|
3445
|
-
"[d": "left",
|
|
3446
|
-
"[e": "clear",
|
|
3447
|
-
"[2$": "insert",
|
|
3448
|
-
"[3$": "delete",
|
|
3449
|
-
"[5$": "pageup",
|
|
3450
|
-
"[6$": "pagedown",
|
|
3451
|
-
"[7$": "home",
|
|
3452
|
-
"[8$": "end",
|
|
3453
|
-
Oa: "up",
|
|
3454
|
-
Ob: "down",
|
|
3455
|
-
Oc: "right",
|
|
3456
|
-
Od: "left",
|
|
3457
|
-
Oe: "clear",
|
|
3458
|
-
"[2^": "insert",
|
|
3459
|
-
"[3^": "delete",
|
|
3460
|
-
"[5^": "pageup",
|
|
3461
|
-
"[6^": "pagedown",
|
|
3462
|
-
"[7^": "home",
|
|
3463
|
-
"[8^": "end",
|
|
3464
|
-
"[Z": "tab"
|
|
3465
|
-
};
|
|
3466
|
-
const nonAlphanumericKeys = [...Object.values(keyName), "backspace"];
|
|
3467
|
-
const isShiftKey = (code) => {
|
|
3468
|
-
return [
|
|
3469
|
-
"[a",
|
|
3470
|
-
"[b",
|
|
3471
|
-
"[c",
|
|
3472
|
-
"[d",
|
|
3473
|
-
"[e",
|
|
3474
|
-
"[2$",
|
|
3475
|
-
"[3$",
|
|
3476
|
-
"[5$",
|
|
3477
|
-
"[6$",
|
|
3478
|
-
"[7$",
|
|
3479
|
-
"[8$",
|
|
3480
|
-
"[Z"
|
|
3481
|
-
].includes(code);
|
|
3482
|
-
};
|
|
3483
|
-
const isCtrlKey = (code) => {
|
|
3484
|
-
return [
|
|
3485
|
-
"Oa",
|
|
3486
|
-
"Ob",
|
|
3487
|
-
"Oc",
|
|
3488
|
-
"Od",
|
|
3489
|
-
"Oe",
|
|
3490
|
-
"[2^",
|
|
3491
|
-
"[3^",
|
|
3492
|
-
"[5^",
|
|
3493
|
-
"[6^",
|
|
3494
|
-
"[7^",
|
|
3495
|
-
"[8^"
|
|
3496
|
-
].includes(code);
|
|
3497
|
-
};
|
|
3498
|
-
const kittyKeyRe = /^\x1b\[(\d+)(?:;(\d+)(?::(\d+))?(?:;([\d:]+))?)?u$/;
|
|
3499
|
-
const kittySpecialKeyRe = /^\x1b\[(\d+);(\d+):(\d+)([A-Za-z~])$/;
|
|
3500
|
-
const kittySpecialLetterKeys = {
|
|
3501
|
-
A: "up",
|
|
3502
|
-
B: "down",
|
|
3503
|
-
C: "right",
|
|
3504
|
-
D: "left",
|
|
3505
|
-
E: "clear",
|
|
3506
|
-
F: "end",
|
|
3507
|
-
H: "home",
|
|
3508
|
-
P: "f1",
|
|
3509
|
-
Q: "f2",
|
|
3510
|
-
R: "f3",
|
|
3511
|
-
S: "f4"
|
|
3512
|
-
};
|
|
3513
|
-
const kittySpecialNumberKeys = {
|
|
3514
|
-
2: "insert",
|
|
3515
|
-
3: "delete",
|
|
3516
|
-
5: "pageup",
|
|
3517
|
-
6: "pagedown",
|
|
3518
|
-
7: "home",
|
|
3519
|
-
8: "end",
|
|
3520
|
-
11: "f1",
|
|
3521
|
-
12: "f2",
|
|
3522
|
-
13: "f3",
|
|
3523
|
-
14: "f4",
|
|
3524
|
-
15: "f5",
|
|
3525
|
-
17: "f6",
|
|
3526
|
-
18: "f7",
|
|
3527
|
-
19: "f8",
|
|
3528
|
-
20: "f9",
|
|
3529
|
-
21: "f10",
|
|
3530
|
-
23: "f11",
|
|
3531
|
-
24: "f12"
|
|
3532
|
-
};
|
|
3533
|
-
const kittyCodepointNames = {
|
|
3534
|
-
27: "escape",
|
|
3535
|
-
9: "tab",
|
|
3536
|
-
127: "backspace",
|
|
3537
|
-
8: "backspace",
|
|
3538
|
-
57358: "capslock",
|
|
3539
|
-
57359: "scrolllock",
|
|
3540
|
-
57360: "numlock",
|
|
3541
|
-
57361: "printscreen",
|
|
3542
|
-
57362: "pause",
|
|
3543
|
-
57363: "menu",
|
|
3544
|
-
57376: "f13",
|
|
3545
|
-
57377: "f14",
|
|
3546
|
-
57378: "f15",
|
|
3547
|
-
57379: "f16",
|
|
3548
|
-
57380: "f17",
|
|
3549
|
-
57381: "f18",
|
|
3550
|
-
57382: "f19",
|
|
3551
|
-
57383: "f20",
|
|
3552
|
-
57384: "f21",
|
|
3553
|
-
57385: "f22",
|
|
3554
|
-
57386: "f23",
|
|
3555
|
-
57387: "f24",
|
|
3556
|
-
57388: "f25",
|
|
3557
|
-
57389: "f26",
|
|
3558
|
-
57390: "f27",
|
|
3559
|
-
57391: "f28",
|
|
3560
|
-
57392: "f29",
|
|
3561
|
-
57393: "f30",
|
|
3562
|
-
57394: "f31",
|
|
3563
|
-
57395: "f32",
|
|
3564
|
-
57396: "f33",
|
|
3565
|
-
57397: "f34",
|
|
3566
|
-
57398: "f35",
|
|
3567
|
-
57399: "kp0",
|
|
3568
|
-
57400: "kp1",
|
|
3569
|
-
57401: "kp2",
|
|
3570
|
-
57402: "kp3",
|
|
3571
|
-
57403: "kp4",
|
|
3572
|
-
57404: "kp5",
|
|
3573
|
-
57405: "kp6",
|
|
3574
|
-
57406: "kp7",
|
|
3575
|
-
57407: "kp8",
|
|
3576
|
-
57408: "kp9",
|
|
3577
|
-
57409: "kpdecimal",
|
|
3578
|
-
57410: "kpdivide",
|
|
3579
|
-
57411: "kpmultiply",
|
|
3580
|
-
57412: "kpsubtract",
|
|
3581
|
-
57413: "kpadd",
|
|
3582
|
-
57414: "kpenter",
|
|
3583
|
-
57415: "kpequal",
|
|
3584
|
-
57416: "kpseparator",
|
|
3585
|
-
57417: "kpleft",
|
|
3586
|
-
57418: "kpright",
|
|
3587
|
-
57419: "kpup",
|
|
3588
|
-
57420: "kpdown",
|
|
3589
|
-
57421: "kppageup",
|
|
3590
|
-
57422: "kppagedown",
|
|
3591
|
-
57423: "kphome",
|
|
3592
|
-
57424: "kpend",
|
|
3593
|
-
57425: "kpinsert",
|
|
3594
|
-
57426: "kpdelete",
|
|
3595
|
-
57427: "kpbegin",
|
|
3596
|
-
57428: "mediaplay",
|
|
3597
|
-
57429: "mediapause",
|
|
3598
|
-
57430: "mediaplaypause",
|
|
3599
|
-
57431: "mediareverse",
|
|
3600
|
-
57432: "mediastop",
|
|
3601
|
-
57433: "mediafastforward",
|
|
3602
|
-
57434: "mediarewind",
|
|
3603
|
-
57435: "mediatracknext",
|
|
3604
|
-
57436: "mediatrackprevious",
|
|
3605
|
-
57437: "mediarecord",
|
|
3606
|
-
57438: "lowervolume",
|
|
3607
|
-
57439: "raisevolume",
|
|
3608
|
-
57440: "mutevolume",
|
|
3609
|
-
57441: "leftshift",
|
|
3610
|
-
57442: "leftcontrol",
|
|
3611
|
-
57443: "leftalt",
|
|
3612
|
-
57444: "leftsuper",
|
|
3613
|
-
57445: "lefthyper",
|
|
3614
|
-
57446: "leftmeta",
|
|
3615
|
-
57447: "rightshift",
|
|
3616
|
-
57448: "rightcontrol",
|
|
3617
|
-
57449: "rightalt",
|
|
3618
|
-
57450: "rightsuper",
|
|
3619
|
-
57451: "righthyper",
|
|
3620
|
-
57452: "rightmeta",
|
|
3621
|
-
57453: "isoLevel3Shift",
|
|
3622
|
-
57454: "isoLevel5Shift"
|
|
3623
|
-
};
|
|
3624
|
-
const isValidCodepoint = (cp) => cp >= 0 && cp <= 1114111 && !(cp >= 55296 && cp <= 57343);
|
|
3625
|
-
const safeFromCodePoint = (cp) => isValidCodepoint(cp) ? String.fromCodePoint(cp) : "?";
|
|
3626
|
-
function resolveEventType(value) {
|
|
3627
|
-
if (value === 3) return "release";
|
|
3628
|
-
if (value === 2) return "repeat";
|
|
3629
|
-
return "press";
|
|
3630
|
-
}
|
|
3631
|
-
function parseKittyModifiers(modifiers) {
|
|
3632
|
-
return {
|
|
3633
|
-
ctrl: !!(modifiers & kittyModifiers.ctrl),
|
|
3634
|
-
shift: !!(modifiers & kittyModifiers.shift),
|
|
3635
|
-
meta: !!(modifiers & (kittyModifiers.meta | kittyModifiers.alt)),
|
|
3636
|
-
super: !!(modifiers & kittyModifiers.super),
|
|
3637
|
-
hyper: !!(modifiers & kittyModifiers.hyper),
|
|
3638
|
-
capsLock: !!(modifiers & kittyModifiers.capsLock),
|
|
3639
|
-
numLock: !!(modifiers & kittyModifiers.numLock)
|
|
3640
|
-
};
|
|
3641
|
-
}
|
|
3642
|
-
const parseKittyKeypress = (s) => {
|
|
3643
|
-
const match = kittyKeyRe.exec(s);
|
|
3644
|
-
if (!match) return null;
|
|
3645
|
-
const codepoint = parseInt(match[1], 10);
|
|
3646
|
-
const modifiers = match[2] ? Math.max(0, parseInt(match[2], 10) - 1) : 0;
|
|
3647
|
-
const eventType = match[3] ? parseInt(match[3], 10) : 1;
|
|
3648
|
-
const textField = match[4];
|
|
3649
|
-
if (!isValidCodepoint(codepoint)) return null;
|
|
3650
|
-
let text;
|
|
3651
|
-
if (textField) text = textField.split(":").map((cp) => safeFromCodePoint(parseInt(cp, 10))).join("");
|
|
3652
|
-
let name;
|
|
3653
|
-
let isPrintable;
|
|
3654
|
-
if (codepoint === 32) {
|
|
3655
|
-
name = "space";
|
|
3656
|
-
isPrintable = true;
|
|
3657
|
-
} else if (codepoint === 13) {
|
|
3658
|
-
name = "return";
|
|
3659
|
-
isPrintable = true;
|
|
3660
|
-
} else if (kittyCodepointNames[codepoint]) {
|
|
3661
|
-
name = kittyCodepointNames[codepoint];
|
|
3662
|
-
isPrintable = false;
|
|
3663
|
-
} else if (codepoint >= 1 && codepoint <= 26) {
|
|
3664
|
-
name = String.fromCodePoint(codepoint + 96);
|
|
3665
|
-
isPrintable = false;
|
|
3666
|
-
} else {
|
|
3667
|
-
name = safeFromCodePoint(codepoint).toLowerCase();
|
|
3668
|
-
isPrintable = true;
|
|
3669
|
-
}
|
|
3670
|
-
if (isPrintable && !text) text = safeFromCodePoint(codepoint);
|
|
3671
|
-
return {
|
|
3672
|
-
name,
|
|
3673
|
-
...parseKittyModifiers(modifiers),
|
|
3674
|
-
eventType: resolveEventType(eventType),
|
|
3675
|
-
sequence: s,
|
|
3676
|
-
raw: s,
|
|
3677
|
-
isKittyProtocol: true,
|
|
3678
|
-
isPrintable,
|
|
3679
|
-
text
|
|
3680
|
-
};
|
|
3681
|
-
};
|
|
3682
|
-
const parseKittySpecialKey = (s) => {
|
|
3683
|
-
const match = kittySpecialKeyRe.exec(s);
|
|
3684
|
-
if (!match) return null;
|
|
3685
|
-
const number = parseInt(match[1], 10);
|
|
3686
|
-
const modifiers = Math.max(0, parseInt(match[2], 10) - 1);
|
|
3687
|
-
const eventType = parseInt(match[3], 10);
|
|
3688
|
-
const terminator = match[4];
|
|
3689
|
-
const name = terminator === "~" ? kittySpecialNumberKeys[number] : kittySpecialLetterKeys[terminator];
|
|
3690
|
-
if (!name) return null;
|
|
3691
|
-
return {
|
|
3692
|
-
name,
|
|
3693
|
-
...parseKittyModifiers(modifiers),
|
|
3694
|
-
eventType: resolveEventType(eventType),
|
|
3695
|
-
sequence: s,
|
|
3696
|
-
raw: s,
|
|
3697
|
-
isKittyProtocol: true,
|
|
3698
|
-
isPrintable: false
|
|
3699
|
-
};
|
|
3700
|
-
};
|
|
3701
|
-
const parseKeypress = (s = "") => {
|
|
3702
|
-
let parts;
|
|
3703
|
-
if (s instanceof Uint8Array) {
|
|
3704
|
-
if (s[0] > 127 && s[1] === void 0) s = "\x1B" + textDecoder.decode(Uint8Array.of(s[0] - 128));
|
|
3705
|
-
else s = textDecoder.decode(s);
|
|
3706
|
-
} else if (s !== void 0 && typeof s !== "string") s = String(s);
|
|
3707
|
-
else if (!s) s = "";
|
|
3708
|
-
const kittyResult = parseKittyKeypress(s);
|
|
3709
|
-
if (kittyResult) return kittyResult;
|
|
3710
|
-
const kittySpecialResult = parseKittySpecialKey(s);
|
|
3711
|
-
if (kittySpecialResult) return kittySpecialResult;
|
|
3712
|
-
if (kittyKeyRe.test(s)) return {
|
|
3713
|
-
name: "",
|
|
3714
|
-
ctrl: false,
|
|
3715
|
-
meta: false,
|
|
3716
|
-
shift: false,
|
|
3717
|
-
sequence: s,
|
|
3718
|
-
raw: s,
|
|
3719
|
-
isKittyProtocol: true,
|
|
3720
|
-
isPrintable: false
|
|
3721
|
-
};
|
|
3722
|
-
const key = {
|
|
3723
|
-
name: "",
|
|
3724
|
-
ctrl: false,
|
|
3725
|
-
meta: false,
|
|
3726
|
-
shift: false,
|
|
3727
|
-
sequence: s,
|
|
3728
|
-
raw: s
|
|
3729
|
-
};
|
|
3730
|
-
key.sequence = key.sequence || s || key.name;
|
|
3731
|
-
if (s === "\r" || s === "\x1B\r") {
|
|
3732
|
-
key.raw = void 0;
|
|
3733
|
-
key.name = "return";
|
|
3734
|
-
key.meta = s.length === 2;
|
|
3735
|
-
} else if (s === "\n") key.name = "enter";
|
|
3736
|
-
else if (s === " ") key.name = "tab";
|
|
3737
|
-
else if (s === "\b" || s === "\x1B\b") {
|
|
3738
|
-
key.name = "backspace";
|
|
3739
|
-
key.meta = s.charAt(0) === "\x1B";
|
|
3740
|
-
} else if (s === "" || s === "\x1B") {
|
|
3741
|
-
key.name = "backspace";
|
|
3742
|
-
key.meta = s.charAt(0) === "\x1B";
|
|
3743
|
-
} else if (s === "\x1B" || s === "\x1B\x1B") {
|
|
3744
|
-
key.name = "escape";
|
|
3745
|
-
key.meta = s.length === 2;
|
|
3746
|
-
} else if (s === " " || s === "\x1B ") {
|
|
3747
|
-
key.name = "space";
|
|
3748
|
-
key.meta = s.length === 2;
|
|
3749
|
-
} else if (s.length === 1 && s <= "") {
|
|
3750
|
-
key.name = String.fromCharCode(s.charCodeAt(0) + "a".charCodeAt(0) - 1);
|
|
3751
|
-
key.ctrl = true;
|
|
3752
|
-
} else if (s.length === 1 && s >= "0" && s <= "9") key.name = "number";
|
|
3753
|
-
else if (s.length === 1 && s >= "a" && s <= "z") key.name = s;
|
|
3754
|
-
else if (s.length === 1 && s >= "A" && s <= "Z") {
|
|
3755
|
-
key.name = s.toLowerCase();
|
|
3756
|
-
key.shift = true;
|
|
3757
|
-
} else if (parts = metaKeyCodeRe.exec(s)) {
|
|
3758
|
-
key.name = parts[1].toLowerCase();
|
|
3759
|
-
key.meta = true;
|
|
3760
|
-
key.shift = /^[A-Z]$/.test(parts[1]);
|
|
3761
|
-
} else if (parts = fnKeyRe.exec(s)) {
|
|
3762
|
-
const segs = [...s];
|
|
3763
|
-
if (segs[0] === "\x1B" && segs[1] === "\x1B") key.meta = true;
|
|
3764
|
-
const code = [
|
|
3765
|
-
parts[1],
|
|
3766
|
-
parts[2],
|
|
3767
|
-
parts[4],
|
|
3768
|
-
parts[6]
|
|
3769
|
-
].filter(Boolean).join("");
|
|
3770
|
-
const modifier = Number(parts[3] || parts[5] || 1) - 1;
|
|
3771
|
-
key.ctrl = !!(modifier & 4);
|
|
3772
|
-
key.meta = key.meta || !!(modifier & 10);
|
|
3773
|
-
key.shift = !!(modifier & 1);
|
|
3774
|
-
key.code = code;
|
|
3775
|
-
key.name = keyName[code] ?? "";
|
|
3776
|
-
key.shift = isShiftKey(code) || key.shift;
|
|
3777
|
-
key.ctrl = isCtrlKey(code) || key.ctrl;
|
|
3778
|
-
}
|
|
3779
|
-
return key;
|
|
2605
|
+
const useCapabilities = () => {
|
|
2606
|
+
const { stdout } = useStdout();
|
|
2607
|
+
const { stdin } = useStdinContext();
|
|
2608
|
+
const store = getCapabilities(stdin, stdout);
|
|
2609
|
+
const capabilities = useSyncExternalStore(store.subscribe, () => store.current);
|
|
2610
|
+
useEffect(() => {
|
|
2611
|
+
store.query();
|
|
2612
|
+
}, [store]);
|
|
2613
|
+
return capabilities;
|
|
3780
2614
|
};
|
|
3781
|
-
//#endregion
|
|
3782
|
-
//#region src/hooks/use-input.ts
|
|
3783
2615
|
/**
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
import {useInput} from 'ink';
|
|
2616
|
+
Calls `onChange` whenever the terminal changes: resizes (including in-band
|
|
2617
|
+
pixel geometry), color scheme switches, window focus, and query answers
|
|
2618
|
+
arriving. The React wrapper over `capabilities.subscribe()` for side effects —
|
|
2619
|
+
for rendering, use `useCapabilities` instead.
|
|
3789
2620
|
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
// Exit program
|
|
3794
|
-
}
|
|
2621
|
+
The callback always sees the latest render's closure and changing it does not
|
|
2622
|
+
resubscribe. Both the new and previous snapshot are passed, so handlers can
|
|
2623
|
+
react to the specific change:
|
|
3795
2624
|
|
|
3796
|
-
|
|
3797
|
-
|
|
2625
|
+
```tsx
|
|
2626
|
+
useCapabilitiesChange((next, previous) => {
|
|
2627
|
+
if (next.theme.appearance !== previous.theme.appearance) {
|
|
2628
|
+
// re-theme
|
|
3798
2629
|
}
|
|
3799
2630
|
});
|
|
3800
|
-
|
|
3801
|
-
return …
|
|
3802
|
-
};
|
|
3803
2631
|
```
|
|
3804
2632
|
*/
|
|
3805
|
-
const
|
|
3806
|
-
const {
|
|
2633
|
+
const useCapabilitiesChange = (onChange) => {
|
|
2634
|
+
const { stdout } = useStdout();
|
|
2635
|
+
const { stdin } = useStdinContext();
|
|
2636
|
+
const store = getCapabilities(stdin, stdout);
|
|
2637
|
+
const handleChange = useEffectEvent(onChange);
|
|
3807
2638
|
useEffect(() => {
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
}, [options.isActive, setRawMode]);
|
|
3814
|
-
const handleData = useEffectEvent((data) => {
|
|
3815
|
-
const keypress = parseKeypress(data);
|
|
3816
|
-
const key = {
|
|
3817
|
-
upArrow: keypress.name === "up",
|
|
3818
|
-
downArrow: keypress.name === "down",
|
|
3819
|
-
leftArrow: keypress.name === "left",
|
|
3820
|
-
rightArrow: keypress.name === "right",
|
|
3821
|
-
pageDown: keypress.name === "pagedown",
|
|
3822
|
-
pageUp: keypress.name === "pageup",
|
|
3823
|
-
home: keypress.name === "home",
|
|
3824
|
-
end: keypress.name === "end",
|
|
3825
|
-
return: keypress.name === "return",
|
|
3826
|
-
escape: keypress.name === "escape",
|
|
3827
|
-
ctrl: keypress.ctrl,
|
|
3828
|
-
shift: keypress.shift,
|
|
3829
|
-
tab: keypress.name === "tab",
|
|
3830
|
-
backspace: keypress.name === "backspace",
|
|
3831
|
-
delete: keypress.name === "delete",
|
|
3832
|
-
meta: keypress.meta,
|
|
3833
|
-
super: keypress.super ?? false,
|
|
3834
|
-
hyper: keypress.hyper ?? false,
|
|
3835
|
-
capsLock: keypress.capsLock ?? false,
|
|
3836
|
-
numLock: keypress.numLock ?? false,
|
|
3837
|
-
eventType: keypress.eventType
|
|
3838
|
-
};
|
|
3839
|
-
let input;
|
|
3840
|
-
if (keypress.isKittyProtocol) {
|
|
3841
|
-
if (keypress.isPrintable) input = keypress.text ?? keypress.name;
|
|
3842
|
-
else if (keypress.ctrl && keypress.name.length === 1) input = keypress.name;
|
|
3843
|
-
else input = "";
|
|
3844
|
-
} else if (keypress.ctrl) input = keypress.name ?? "";
|
|
3845
|
-
else input = keypress.sequence;
|
|
3846
|
-
if (!keypress.isKittyProtocol && nonAlphanumericKeys.includes(keypress.name)) input = "";
|
|
3847
|
-
if (input.startsWith("\x1B")) input = input.slice(1);
|
|
3848
|
-
if (input.length === 1 && /[A-Z]/.test(input)) key.shift = true;
|
|
3849
|
-
if (input === "c" && key.ctrl && internal_exitOnCtrlC) return;
|
|
3850
|
-
reconciler.discreteUpdates(() => {
|
|
3851
|
-
inputHandler(input, key);
|
|
2639
|
+
let previous = store.current;
|
|
2640
|
+
const unsubscribe = store.subscribe((next) => {
|
|
2641
|
+
const before = previous;
|
|
2642
|
+
previous = next;
|
|
2643
|
+
handleChange(next, before);
|
|
3852
2644
|
});
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
internal_eventEmitter.on("input", handleData);
|
|
3857
|
-
return () => {
|
|
3858
|
-
internal_eventEmitter.removeListener("input", handleData);
|
|
3859
|
-
};
|
|
3860
|
-
}, [options.isActive, internal_eventEmitter]);
|
|
2645
|
+
store.query();
|
|
2646
|
+
return unsubscribe;
|
|
2647
|
+
}, [store]);
|
|
3861
2648
|
};
|
|
3862
2649
|
//#endregion
|
|
3863
2650
|
//#region src/hooks/use-paste.ts
|
|
@@ -3921,65 +2708,12 @@ A React hook that returns app lifecycle methods like `exit()` and `waitUntilRend
|
|
|
3921
2708
|
*/
|
|
3922
2709
|
const useApp = () => useContext(AppContext);
|
|
3923
2710
|
//#endregion
|
|
3924
|
-
//#region src/hooks/use-stdout.ts
|
|
3925
|
-
/**
|
|
3926
|
-
A React hook that returns the stdout stream where Ink renders your app.
|
|
3927
|
-
*/
|
|
3928
|
-
const useStdout = () => useContext(StdoutContext);
|
|
3929
|
-
//#endregion
|
|
3930
2711
|
//#region src/hooks/use-stderr.ts
|
|
3931
2712
|
/**
|
|
3932
2713
|
A React hook that returns the stderr stream.
|
|
3933
2714
|
*/
|
|
3934
2715
|
const useStderr = () => useContext(StderrContext);
|
|
3935
2716
|
//#endregion
|
|
3936
|
-
//#region src/hooks/use-focus.ts
|
|
3937
|
-
/**
|
|
3938
|
-
A React hook that returns focus state and focus controls for the current component.
|
|
3939
|
-
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.
|
|
3940
|
-
*/
|
|
3941
|
-
const useFocus = ({ isActive = true, autoFocus = false, id: customId } = {}) => {
|
|
3942
|
-
const { isRawModeSupported, setRawMode } = useStdin();
|
|
3943
|
-
const { activeId, add, remove, activate, deactivate, focus } = useContext(FocusContext);
|
|
3944
|
-
const autoId = useId();
|
|
3945
|
-
const id = customId ?? autoId;
|
|
3946
|
-
useEffect(() => {
|
|
3947
|
-
add(id, { autoFocus });
|
|
3948
|
-
return () => {
|
|
3949
|
-
remove(id);
|
|
3950
|
-
};
|
|
3951
|
-
}, [
|
|
3952
|
-
id,
|
|
3953
|
-
autoFocus,
|
|
3954
|
-
add,
|
|
3955
|
-
remove
|
|
3956
|
-
]);
|
|
3957
|
-
useEffect(() => {
|
|
3958
|
-
if (isActive) activate(id);
|
|
3959
|
-
else deactivate(id);
|
|
3960
|
-
}, [
|
|
3961
|
-
isActive,
|
|
3962
|
-
id,
|
|
3963
|
-
activate,
|
|
3964
|
-
deactivate
|
|
3965
|
-
]);
|
|
3966
|
-
useEffect(() => {
|
|
3967
|
-
if (!isRawModeSupported || !isActive) return;
|
|
3968
|
-
setRawMode(true);
|
|
3969
|
-
return () => {
|
|
3970
|
-
setRawMode(false);
|
|
3971
|
-
};
|
|
3972
|
-
}, [
|
|
3973
|
-
isActive,
|
|
3974
|
-
isRawModeSupported,
|
|
3975
|
-
setRawMode
|
|
3976
|
-
]);
|
|
3977
|
-
return {
|
|
3978
|
-
isFocused: Boolean(id) && activeId === id,
|
|
3979
|
-
focus
|
|
3980
|
-
};
|
|
3981
|
-
};
|
|
3982
|
-
//#endregion
|
|
3983
2717
|
//#region src/hooks/use-focus-manager.ts
|
|
3984
2718
|
/**
|
|
3985
2719
|
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.
|
|
@@ -4122,6 +2856,54 @@ function normalizeAnimationInterval(interval) {
|
|
|
4122
2856
|
return Math.min(maximumTimerInterval, Math.max(1, interval));
|
|
4123
2857
|
}
|
|
4124
2858
|
//#endregion
|
|
2859
|
+
//#region src/hooks/use-terminal-osc.ts
|
|
2860
|
+
const useProgress = ({ state, value }) => {
|
|
2861
|
+
const terminal = useContext(TerminalOscContext);
|
|
2862
|
+
const owner = useRef(void 0);
|
|
2863
|
+
owner.current ??= Symbol("terminal-progress");
|
|
2864
|
+
useEffect(() => {
|
|
2865
|
+
terminal.publishProgress(owner.current, state, value);
|
|
2866
|
+
}, [
|
|
2867
|
+
state,
|
|
2868
|
+
terminal,
|
|
2869
|
+
value
|
|
2870
|
+
]);
|
|
2871
|
+
useEffect(() => {
|
|
2872
|
+
return () => {
|
|
2873
|
+
terminal.publishProgress(owner.current, "inactive");
|
|
2874
|
+
};
|
|
2875
|
+
}, [terminal]);
|
|
2876
|
+
};
|
|
2877
|
+
const useClipboard = () => {
|
|
2878
|
+
const terminal = useContext(TerminalOscContext);
|
|
2879
|
+
return useCallback((text, selection) => terminal.copyToClipboard(text, selection), [terminal]);
|
|
2880
|
+
};
|
|
2881
|
+
const useTitle = (title) => {
|
|
2882
|
+
const terminal = useContext(TerminalOscContext);
|
|
2883
|
+
const owner = useRef(void 0);
|
|
2884
|
+
owner.current ??= Symbol("terminal-title");
|
|
2885
|
+
useEffect(() => {
|
|
2886
|
+
terminal.publishTitle(owner.current, title);
|
|
2887
|
+
}, [terminal, title]);
|
|
2888
|
+
useEffect(() => () => terminal.publishTitle(owner.current, void 0), [terminal]);
|
|
2889
|
+
};
|
|
2890
|
+
const useWorkingDirectory = (directory) => {
|
|
2891
|
+
const terminal = useContext(TerminalOscContext);
|
|
2892
|
+
useEffect(() => {
|
|
2893
|
+
terminal.setWorkingDirectory(directory);
|
|
2894
|
+
}, [directory, terminal]);
|
|
2895
|
+
};
|
|
2896
|
+
const useNotification = () => {
|
|
2897
|
+
const terminal = useContext(TerminalOscContext);
|
|
2898
|
+
return useCallback((title) => terminal.notify(title), [terminal]);
|
|
2899
|
+
};
|
|
2900
|
+
const usePointerShape = (shape) => {
|
|
2901
|
+
const terminal = useContext(TerminalOscContext);
|
|
2902
|
+
useEffect(() => {
|
|
2903
|
+
terminal.setPointerShape(shape);
|
|
2904
|
+
}, [shape, terminal]);
|
|
2905
|
+
};
|
|
2906
|
+
//#endregion
|
|
4125
2907
|
//#region src/hooks/use-window-size.ts
|
|
4126
2908
|
/**
|
|
4127
2909
|
A React hook that returns the current terminal window dimensions and re-renders the component whenever the terminal is resized.
|
|
@@ -4236,4 +3018,4 @@ const measureElement = (node) => {
|
|
|
4236
3018
|
};
|
|
4237
3019
|
};
|
|
4238
3020
|
//#endregion
|
|
4239
|
-
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 };
|
|
3021
|
+
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 };
|