@nick-skriabin/glyph 0.1.38 → 0.1.40
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/dist/index.js +14 -4806
- package/dist/index.js.map +1 -1
- package/package.json +4 -10
- package/dist/index.cjs +0 -4845
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -682
package/dist/index.js
CHANGED
|
@@ -1,4807 +1,15 @@
|
|
|
1
|
-
import React15, { createContext, forwardRef, useState, useContext, useRef, useEffect, useLayoutEffect, useCallback, useMemo } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const style = props.style ?? {};
|
|
15
|
-
return {
|
|
16
|
-
type,
|
|
17
|
-
props,
|
|
18
|
-
style,
|
|
19
|
-
children: [],
|
|
20
|
-
rawTextChildren: [],
|
|
21
|
-
parent: null,
|
|
22
|
-
yogaNode: null,
|
|
23
|
-
text: null,
|
|
24
|
-
layout: { x: 0, y: 0, width: 0, height: 0, innerX: 0, innerY: 0, innerWidth: 0, innerHeight: 0 },
|
|
25
|
-
focusId: type === "input" ? generateFocusId() : props.focusable ? generateFocusId() : null,
|
|
26
|
-
hidden: false
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
function appendChild(parent, child) {
|
|
30
|
-
child.parent = parent;
|
|
31
|
-
parent.children.push(child);
|
|
32
|
-
}
|
|
33
|
-
function removeChild(parent, child) {
|
|
34
|
-
const idx = parent.children.indexOf(child);
|
|
35
|
-
if (idx !== -1) {
|
|
36
|
-
parent.children.splice(idx, 1);
|
|
37
|
-
child.parent = null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function insertBefore(parent, child, beforeChild) {
|
|
41
|
-
child.parent = parent;
|
|
42
|
-
const idx = parent.children.indexOf(beforeChild);
|
|
43
|
-
if (idx !== -1) {
|
|
44
|
-
parent.children.splice(idx, 0, child);
|
|
45
|
-
} else {
|
|
46
|
-
parent.children.push(child);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function getInheritedTextStyle(node) {
|
|
50
|
-
const result = {};
|
|
51
|
-
let current = node;
|
|
52
|
-
while (current) {
|
|
53
|
-
const s = current.style;
|
|
54
|
-
if (result.color === void 0 && s.color !== void 0) result.color = s.color;
|
|
55
|
-
if (result.bg === void 0 && s.bg !== void 0) result.bg = s.bg;
|
|
56
|
-
if (result.bold === void 0 && s.bold !== void 0) result.bold = s.bold;
|
|
57
|
-
if (result.dim === void 0 && s.dim !== void 0) result.dim = s.dim;
|
|
58
|
-
if (result.italic === void 0 && s.italic !== void 0) result.italic = s.italic;
|
|
59
|
-
if (result.underline === void 0 && s.underline !== void 0) result.underline = s.underline;
|
|
60
|
-
current = current.parent;
|
|
61
|
-
}
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
64
|
-
function collectTextContent(node) {
|
|
65
|
-
if (node.text != null) return node.text;
|
|
66
|
-
let result = "";
|
|
67
|
-
for (const child of node.children) {
|
|
68
|
-
result += collectTextContent(child);
|
|
69
|
-
}
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// src/reconciler/hostConfig.ts
|
|
74
|
-
var DefaultEventPriority = 32;
|
|
75
|
-
var hostConfig = {
|
|
76
|
-
supportsMutation: true,
|
|
77
|
-
supportsPersistence: false,
|
|
78
|
-
supportsHydration: false,
|
|
79
|
-
isPrimaryRenderer: true,
|
|
80
|
-
// Timeouts
|
|
81
|
-
scheduleTimeout: setTimeout,
|
|
82
|
-
cancelTimeout: clearTimeout,
|
|
83
|
-
noTimeout: -1,
|
|
84
|
-
supportsMicrotasks: true,
|
|
85
|
-
scheduleMicrotask: queueMicrotask,
|
|
86
|
-
// Priority & event methods required by react-reconciler v0.31
|
|
87
|
-
getCurrentUpdatePriority: () => DefaultEventPriority,
|
|
88
|
-
setCurrentUpdatePriority: (_priority) => {
|
|
89
|
-
},
|
|
90
|
-
resolveUpdatePriority: () => DefaultEventPriority,
|
|
91
|
-
getCurrentEventPriority: () => DefaultEventPriority,
|
|
92
|
-
resolveEventType: () => null,
|
|
93
|
-
resolveEventTimeStamp: () => -1.1,
|
|
94
|
-
shouldAttemptEagerTransition: () => false,
|
|
95
|
-
getInstanceFromNode: () => null,
|
|
96
|
-
beforeActiveInstanceBlur: () => {
|
|
97
|
-
},
|
|
98
|
-
afterActiveInstanceBlur: () => {
|
|
99
|
-
},
|
|
100
|
-
prepareScopeUpdate: () => {
|
|
101
|
-
},
|
|
102
|
-
getInstanceFromScope: () => null,
|
|
103
|
-
detachDeletedInstance: () => {
|
|
104
|
-
},
|
|
105
|
-
requestPostPaintCallback: (_callback) => {
|
|
106
|
-
},
|
|
107
|
-
// Commit suspension stubs (required by react-reconciler v0.31)
|
|
108
|
-
maySuspendCommit: (_type, _props) => false,
|
|
109
|
-
preloadInstance: (_type, _props) => true,
|
|
110
|
-
startSuspendingCommit: () => {
|
|
111
|
-
},
|
|
112
|
-
suspendInstance: (_type, _props) => {
|
|
113
|
-
},
|
|
114
|
-
waitForCommitToBeReady: () => null,
|
|
115
|
-
// Transition stubs
|
|
116
|
-
NotPendingTransition: null,
|
|
117
|
-
HostTransitionContext: { $$typeof: /* @__PURE__ */ Symbol.for("react.context"), _currentValue: null },
|
|
118
|
-
resetFormInstance: (_instance) => {
|
|
119
|
-
},
|
|
120
|
-
// Console binding
|
|
121
|
-
bindToConsole: (methodName, args, _errorPrefix) => {
|
|
122
|
-
return Function.prototype.bind.call(
|
|
123
|
-
console[methodName],
|
|
124
|
-
console,
|
|
125
|
-
...args
|
|
126
|
-
);
|
|
127
|
-
},
|
|
128
|
-
// Resource/singleton stubs
|
|
129
|
-
supportsResources: false,
|
|
130
|
-
isHostHoistableType: (_type, _props) => false,
|
|
131
|
-
supportsSingletons: false,
|
|
132
|
-
isHostSingletonType: (_type) => false,
|
|
133
|
-
supportsTestSelectors: false,
|
|
134
|
-
createInstance(type, props, _rootContainer, _hostContext, _internalHandle) {
|
|
135
|
-
return createGlyphNode(type, props);
|
|
136
|
-
},
|
|
137
|
-
createTextInstance(text, _rootContainer, _hostContext, _internalHandle) {
|
|
138
|
-
return { type: "raw-text", text, parent: null };
|
|
139
|
-
},
|
|
140
|
-
appendInitialChild(parentInstance, child) {
|
|
141
|
-
if (child.type === "raw-text") {
|
|
142
|
-
const textChild = child;
|
|
143
|
-
textChild.parent = parentInstance;
|
|
144
|
-
parentInstance.rawTextChildren.push(textChild);
|
|
145
|
-
parentInstance.text = parentInstance.rawTextChildren.map((t) => t.text).join("");
|
|
146
|
-
} else {
|
|
147
|
-
appendChild(parentInstance, child);
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
finalizeInitialChildren(_instance, _type, _props, _rootContainer, _hostContext) {
|
|
151
|
-
return false;
|
|
152
|
-
},
|
|
153
|
-
shouldSetTextContent(_type, _props) {
|
|
154
|
-
return false;
|
|
155
|
-
},
|
|
156
|
-
getRootHostContext(_rootContainer) {
|
|
157
|
-
return {};
|
|
158
|
-
},
|
|
159
|
-
getChildHostContext(parentHostContext, _type, _rootContainer) {
|
|
160
|
-
return parentHostContext;
|
|
161
|
-
},
|
|
162
|
-
getPublicInstance(instance) {
|
|
163
|
-
return instance;
|
|
164
|
-
},
|
|
165
|
-
prepareForCommit(_containerInfo) {
|
|
166
|
-
return null;
|
|
167
|
-
},
|
|
168
|
-
resetAfterCommit(containerInfo) {
|
|
169
|
-
containerInfo.onCommit();
|
|
170
|
-
},
|
|
171
|
-
preparePortalMount() {
|
|
172
|
-
},
|
|
173
|
-
// Mutation methods
|
|
174
|
-
appendChild(parentInstance, child) {
|
|
175
|
-
if (child.type === "raw-text") {
|
|
176
|
-
const textChild = child;
|
|
177
|
-
textChild.parent = parentInstance;
|
|
178
|
-
parentInstance.rawTextChildren.push(textChild);
|
|
179
|
-
parentInstance.text = parentInstance.rawTextChildren.map((t) => t.text).join("");
|
|
180
|
-
} else {
|
|
181
|
-
appendChild(parentInstance, child);
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
appendChildToContainer(container, child) {
|
|
185
|
-
if (child.type === "raw-text") return;
|
|
186
|
-
const node = child;
|
|
187
|
-
node.parent = null;
|
|
188
|
-
container.children.push(node);
|
|
189
|
-
},
|
|
190
|
-
insertBefore(parentInstance, child, beforeChild) {
|
|
191
|
-
if (child.type === "raw-text" || beforeChild.type === "raw-text") return;
|
|
192
|
-
insertBefore(parentInstance, child, beforeChild);
|
|
193
|
-
},
|
|
194
|
-
insertInContainerBefore(container, child, beforeChild) {
|
|
195
|
-
if (child.type === "raw-text" || beforeChild.type === "raw-text") return;
|
|
196
|
-
const node = child;
|
|
197
|
-
const before = beforeChild;
|
|
198
|
-
const idx = container.children.indexOf(before);
|
|
199
|
-
if (idx !== -1) {
|
|
200
|
-
container.children.splice(idx, 0, node);
|
|
201
|
-
} else {
|
|
202
|
-
container.children.push(node);
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
removeChild(parentInstance, child) {
|
|
206
|
-
if (child.type === "raw-text") {
|
|
207
|
-
const textChild = child;
|
|
208
|
-
textChild.parent = null;
|
|
209
|
-
const idx = parentInstance.rawTextChildren.indexOf(textChild);
|
|
210
|
-
if (idx !== -1) parentInstance.rawTextChildren.splice(idx, 1);
|
|
211
|
-
parentInstance.text = parentInstance.rawTextChildren.map((t) => t.text).join("") || null;
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
removeChild(parentInstance, child);
|
|
215
|
-
},
|
|
216
|
-
removeChildFromContainer(container, child) {
|
|
217
|
-
if (child.type === "raw-text") return;
|
|
218
|
-
const node = child;
|
|
219
|
-
const idx = container.children.indexOf(node);
|
|
220
|
-
if (idx !== -1) {
|
|
221
|
-
container.children.splice(idx, 1);
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
commitTextUpdate(textInstance, _oldText, newText) {
|
|
225
|
-
textInstance.text = newText;
|
|
226
|
-
if (textInstance.parent) {
|
|
227
|
-
textInstance.parent.text = textInstance.parent.rawTextChildren.map((t) => t.text).join("");
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
// v0.31 signature: (instance, type, oldProps, newProps, internalHandle)
|
|
231
|
-
// updatePayload was removed in this version
|
|
232
|
-
commitUpdate(instance, _type, _oldProps, newProps, _internalHandle) {
|
|
233
|
-
instance.props = newProps;
|
|
234
|
-
instance.style = newProps.style ?? {};
|
|
235
|
-
if (newProps.focusable && !instance.focusId) {
|
|
236
|
-
instance.focusId = `focus-${Math.random().toString(36).slice(2, 9)}`;
|
|
237
|
-
}
|
|
238
|
-
},
|
|
239
|
-
hideInstance(instance) {
|
|
240
|
-
instance.hidden = true;
|
|
241
|
-
},
|
|
242
|
-
hideTextInstance(textInstance) {
|
|
243
|
-
textInstance.text = "";
|
|
244
|
-
},
|
|
245
|
-
unhideInstance(instance, _props) {
|
|
246
|
-
instance.hidden = false;
|
|
247
|
-
},
|
|
248
|
-
unhideTextInstance(textInstance, text) {
|
|
249
|
-
textInstance.text = text;
|
|
250
|
-
},
|
|
251
|
-
clearContainer(container) {
|
|
252
|
-
container.children.length = 0;
|
|
253
|
-
},
|
|
254
|
-
resetTextContent(instance) {
|
|
255
|
-
instance.text = null;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// src/reconciler/reconciler.ts
|
|
260
|
-
var reconciler = ReactReconciler(hostConfig);
|
|
261
|
-
reconciler.injectIntoDevTools({
|
|
262
|
-
bundleType: process.env.NODE_ENV === "production" ? 0 : 1,
|
|
263
|
-
version: "0.1.0",
|
|
264
|
-
rendererPackageName: "glyph"
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// src/runtime/terminal.ts
|
|
268
|
-
var ESC = "\x1B";
|
|
269
|
-
var CSI = `${ESC}[`;
|
|
270
|
-
var Terminal = class {
|
|
271
|
-
stdout;
|
|
272
|
-
stdin;
|
|
273
|
-
wasRaw = false;
|
|
274
|
-
cleanedUp = false;
|
|
275
|
-
// Data handler dispatch - single stdin listener, filters OSC, dispatches clean data
|
|
276
|
-
dataHandlers = /* @__PURE__ */ new Set();
|
|
277
|
-
stdinAttached = false;
|
|
278
|
-
// OSC response filtering state
|
|
279
|
-
oscState = "normal";
|
|
280
|
-
oscAccum = "";
|
|
281
|
-
escFlushTimer = null;
|
|
282
|
-
// Terminal palette (populated by queryPalette)
|
|
283
|
-
palette = /* @__PURE__ */ new Map();
|
|
284
|
-
paletteResolve = null;
|
|
285
|
-
constructor(stdout = process.stdout, stdin = process.stdin) {
|
|
286
|
-
this.stdout = stdout;
|
|
287
|
-
this.stdin = stdin;
|
|
288
|
-
}
|
|
289
|
-
get columns() {
|
|
290
|
-
return this.stdout.columns || 80;
|
|
291
|
-
}
|
|
292
|
-
get rows() {
|
|
293
|
-
return this.stdout.rows || 24;
|
|
294
|
-
}
|
|
295
|
-
enterRawMode() {
|
|
296
|
-
if (this.stdin.isTTY) {
|
|
297
|
-
this.wasRaw = this.stdin.isRaw;
|
|
298
|
-
this.stdin.setRawMode(true);
|
|
299
|
-
this.stdin.resume();
|
|
300
|
-
this.stdin.setEncoding("utf-8");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
exitRawMode() {
|
|
304
|
-
if (this.stdin.isTTY && !this.wasRaw) {
|
|
305
|
-
this.stdin.setRawMode(false);
|
|
306
|
-
this.stdin.pause();
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
write(data) {
|
|
310
|
-
this.stdout.write(data);
|
|
311
|
-
}
|
|
312
|
-
hideCursor() {
|
|
313
|
-
this.write(`${CSI}?25l`);
|
|
314
|
-
}
|
|
315
|
-
showCursor() {
|
|
316
|
-
this.write(`${CSI}?25h`);
|
|
317
|
-
}
|
|
318
|
-
/** Move cursor to (x, y) position (0-indexed) */
|
|
319
|
-
moveCursor(x, y) {
|
|
320
|
-
this.write(`${CSI}${y + 1};${x + 1}H`);
|
|
321
|
-
}
|
|
322
|
-
/** Set cursor color using OSC 12 */
|
|
323
|
-
setCursorColor(color) {
|
|
324
|
-
this.write(`${ESC}]12;${color}\x07`);
|
|
325
|
-
}
|
|
326
|
-
/** Reset cursor color to terminal default */
|
|
327
|
-
resetCursorColor() {
|
|
328
|
-
this.write(`${ESC}]112\x07`);
|
|
329
|
-
}
|
|
330
|
-
enterAltScreen() {
|
|
331
|
-
this.write(`${CSI}?1049h`);
|
|
332
|
-
}
|
|
333
|
-
exitAltScreen() {
|
|
334
|
-
this.write(`${CSI}?1049l`);
|
|
335
|
-
}
|
|
336
|
-
clearScreen() {
|
|
337
|
-
this.write(`${CSI}2J${CSI}H`);
|
|
338
|
-
}
|
|
339
|
-
resetStyles() {
|
|
340
|
-
this.write(`${CSI}0m`);
|
|
341
|
-
}
|
|
342
|
-
/** Enable kitty keyboard protocol for enhanced key detection */
|
|
343
|
-
enableKittyKeyboard() {
|
|
344
|
-
this.write(`${CSI}>1u`);
|
|
345
|
-
}
|
|
346
|
-
/** Disable kitty keyboard protocol */
|
|
347
|
-
disableKittyKeyboard() {
|
|
348
|
-
this.write(`${CSI}<u`);
|
|
349
|
-
}
|
|
350
|
-
setup() {
|
|
351
|
-
this.enterRawMode();
|
|
352
|
-
this.enterAltScreen();
|
|
353
|
-
this.enableKittyKeyboard();
|
|
354
|
-
this.hideCursor();
|
|
355
|
-
this.clearScreen();
|
|
356
|
-
this.attachStdinListener();
|
|
357
|
-
this.installCleanupHandlers();
|
|
358
|
-
}
|
|
359
|
-
cleanup() {
|
|
360
|
-
if (this.cleanedUp) return;
|
|
361
|
-
this.cleanedUp = true;
|
|
362
|
-
if (this.escFlushTimer !== null) {
|
|
363
|
-
clearTimeout(this.escFlushTimer);
|
|
364
|
-
this.escFlushTimer = null;
|
|
365
|
-
}
|
|
366
|
-
this.resetStyles();
|
|
367
|
-
this.resetCursorColor();
|
|
368
|
-
this.disableKittyKeyboard();
|
|
369
|
-
this.showCursor();
|
|
370
|
-
this.exitAltScreen();
|
|
371
|
-
this.exitRawMode();
|
|
372
|
-
}
|
|
373
|
-
/** Restore terminal state for background suspension (does NOT mark as cleaned up). */
|
|
374
|
-
suspend() {
|
|
375
|
-
if (this.escFlushTimer !== null) {
|
|
376
|
-
clearTimeout(this.escFlushTimer);
|
|
377
|
-
this.escFlushTimer = null;
|
|
378
|
-
}
|
|
379
|
-
this.oscState = "normal";
|
|
380
|
-
this.oscAccum = "";
|
|
381
|
-
this.resetStyles();
|
|
382
|
-
this.resetCursorColor();
|
|
383
|
-
this.disableKittyKeyboard();
|
|
384
|
-
this.showCursor();
|
|
385
|
-
this.exitAltScreen();
|
|
386
|
-
this.exitRawMode();
|
|
387
|
-
}
|
|
388
|
-
/** Re-enter raw mode and alt screen after SIGCONT resume. */
|
|
389
|
-
resume() {
|
|
390
|
-
this.enterRawMode();
|
|
391
|
-
this.enterAltScreen();
|
|
392
|
-
this.enableKittyKeyboard();
|
|
393
|
-
this.hideCursor();
|
|
394
|
-
this.clearScreen();
|
|
395
|
-
}
|
|
396
|
-
// ---- Data handling with OSC filtering ----
|
|
397
|
-
attachStdinListener() {
|
|
398
|
-
if (this.stdinAttached) return;
|
|
399
|
-
this.stdinAttached = true;
|
|
400
|
-
this.stdin.on("data", (data) => {
|
|
401
|
-
let str = typeof data === "string" ? data : data.toString("utf-8");
|
|
402
|
-
this.dispatchFiltered(str);
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
onData(handler) {
|
|
406
|
-
this.dataHandlers.add(handler);
|
|
407
|
-
return () => {
|
|
408
|
-
this.dataHandlers.delete(handler);
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
// ---- OSC response filtering ----
|
|
412
|
-
dispatchFiltered(raw) {
|
|
413
|
-
if (this.escFlushTimer !== null) {
|
|
414
|
-
clearTimeout(this.escFlushTimer);
|
|
415
|
-
this.escFlushTimer = null;
|
|
416
|
-
}
|
|
417
|
-
const clean = this.filterOsc(raw);
|
|
418
|
-
if (clean.length > 0) {
|
|
419
|
-
for (const handler of this.dataHandlers) {
|
|
420
|
-
handler(clean);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
if (this.oscState === "esc") {
|
|
424
|
-
this.escFlushTimer = setTimeout(() => {
|
|
425
|
-
this.escFlushTimer = null;
|
|
426
|
-
this.oscState = "normal";
|
|
427
|
-
for (const handler of this.dataHandlers) {
|
|
428
|
-
handler("\x1B");
|
|
429
|
-
}
|
|
430
|
-
}, 50);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
filterOsc(raw) {
|
|
434
|
-
let clean = "";
|
|
435
|
-
for (let i = 0; i < raw.length; i++) {
|
|
436
|
-
const ch = raw[i];
|
|
437
|
-
const code = raw.charCodeAt(i);
|
|
438
|
-
switch (this.oscState) {
|
|
439
|
-
case "normal":
|
|
440
|
-
if (code === 27) {
|
|
441
|
-
this.oscState = "esc";
|
|
442
|
-
} else {
|
|
443
|
-
clean += ch;
|
|
444
|
-
}
|
|
445
|
-
break;
|
|
446
|
-
case "esc":
|
|
447
|
-
if (ch === "]") {
|
|
448
|
-
this.oscState = "osc";
|
|
449
|
-
this.oscAccum = "";
|
|
450
|
-
} else {
|
|
451
|
-
clean += "\x1B" + ch;
|
|
452
|
-
this.oscState = "normal";
|
|
453
|
-
}
|
|
454
|
-
break;
|
|
455
|
-
case "osc":
|
|
456
|
-
if (code === 7) {
|
|
457
|
-
this.handleOscResponse(this.oscAccum);
|
|
458
|
-
this.oscAccum = "";
|
|
459
|
-
this.oscState = "normal";
|
|
460
|
-
} else if (code === 27) {
|
|
461
|
-
this.oscState = "osc_esc";
|
|
462
|
-
} else {
|
|
463
|
-
this.oscAccum += ch;
|
|
464
|
-
}
|
|
465
|
-
break;
|
|
466
|
-
case "osc_esc":
|
|
467
|
-
if (ch === "\\") {
|
|
468
|
-
this.handleOscResponse(this.oscAccum);
|
|
469
|
-
this.oscAccum = "";
|
|
470
|
-
this.oscState = "normal";
|
|
471
|
-
} else {
|
|
472
|
-
this.oscAccum += "\x1B" + ch;
|
|
473
|
-
this.oscState = "osc";
|
|
474
|
-
}
|
|
475
|
-
break;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return clean;
|
|
479
|
-
}
|
|
480
|
-
handleOscResponse(data) {
|
|
481
|
-
const match = data.match(
|
|
482
|
-
/^4;(\d+);rgb:([0-9a-fA-F]+)\/([0-9a-fA-F]+)\/([0-9a-fA-F]+)/
|
|
483
|
-
);
|
|
484
|
-
if (match) {
|
|
485
|
-
const index = parseInt(match[1], 10);
|
|
486
|
-
const r = parseInt(match[2].substring(0, 2), 16);
|
|
487
|
-
const g = parseInt(match[3].substring(0, 2), 16);
|
|
488
|
-
const b = parseInt(match[4].substring(0, 2), 16);
|
|
489
|
-
this.palette.set(index, [r, g, b]);
|
|
490
|
-
if (this.palette.size >= 16 && this.paletteResolve) {
|
|
491
|
-
this.paletteResolve();
|
|
492
|
-
this.paletteResolve = null;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
// ---- Palette querying ----
|
|
497
|
-
queryPalette() {
|
|
498
|
-
return new Promise((resolve) => {
|
|
499
|
-
const done = () => resolve(this.palette);
|
|
500
|
-
const timeout = setTimeout(done, 200);
|
|
501
|
-
this.paletteResolve = () => {
|
|
502
|
-
clearTimeout(timeout);
|
|
503
|
-
done();
|
|
504
|
-
};
|
|
505
|
-
let query = "";
|
|
506
|
-
for (let i = 0; i < 16; i++) {
|
|
507
|
-
query += `\x1B]4;${i};?\x07`;
|
|
508
|
-
}
|
|
509
|
-
this.write(query);
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
// ---- Event handling ----
|
|
513
|
-
onResize(handler) {
|
|
514
|
-
this.stdout.on("resize", handler);
|
|
515
|
-
return () => {
|
|
516
|
-
this.stdout.off("resize", handler);
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
installCleanupHandlers() {
|
|
520
|
-
const doCleanup = () => this.cleanup();
|
|
521
|
-
process.on("exit", doCleanup);
|
|
522
|
-
const handleSignal = (signal) => {
|
|
523
|
-
doCleanup();
|
|
524
|
-
process.kill(process.pid, signal);
|
|
525
|
-
};
|
|
526
|
-
process.once("SIGINT", () => handleSignal("SIGINT"));
|
|
527
|
-
process.once("SIGTERM", () => handleSignal("SIGTERM"));
|
|
528
|
-
process.on("uncaughtException", (err) => {
|
|
529
|
-
doCleanup();
|
|
530
|
-
console.error(err);
|
|
531
|
-
process.exit(1);
|
|
532
|
-
});
|
|
533
|
-
process.on("unhandledRejection", (err) => {
|
|
534
|
-
doCleanup();
|
|
535
|
-
console.error(err);
|
|
536
|
-
process.exit(1);
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
// src/runtime/input.ts
|
|
542
|
-
function getKeyNameFromCode(code) {
|
|
543
|
-
switch (code) {
|
|
544
|
-
// Standard ASCII
|
|
545
|
-
case 9:
|
|
546
|
-
return "tab";
|
|
547
|
-
case 13:
|
|
548
|
-
return "return";
|
|
549
|
-
case 27:
|
|
550
|
-
return "escape";
|
|
551
|
-
case 32:
|
|
552
|
-
return " ";
|
|
553
|
-
case 127:
|
|
554
|
-
return "backspace";
|
|
555
|
-
// Kitty protocol special keys
|
|
556
|
-
case 57358:
|
|
557
|
-
return "capslock";
|
|
558
|
-
case 57359:
|
|
559
|
-
return "scrolllock";
|
|
560
|
-
case 57360:
|
|
561
|
-
return "numlock";
|
|
562
|
-
case 57361:
|
|
563
|
-
return "printscreen";
|
|
564
|
-
case 57362:
|
|
565
|
-
return "pause";
|
|
566
|
-
case 57363:
|
|
567
|
-
return "menu";
|
|
568
|
-
// Function keys (kitty uses these codes)
|
|
569
|
-
case 57364:
|
|
570
|
-
return "f13";
|
|
571
|
-
case 57365:
|
|
572
|
-
return "f14";
|
|
573
|
-
case 57366:
|
|
574
|
-
return "f15";
|
|
575
|
-
case 57367:
|
|
576
|
-
return "f16";
|
|
577
|
-
case 57368:
|
|
578
|
-
return "f17";
|
|
579
|
-
case 57369:
|
|
580
|
-
return "f18";
|
|
581
|
-
case 57370:
|
|
582
|
-
return "f19";
|
|
583
|
-
case 57371:
|
|
584
|
-
return "f20";
|
|
585
|
-
case 57372:
|
|
586
|
-
return "f21";
|
|
587
|
-
case 57373:
|
|
588
|
-
return "f22";
|
|
589
|
-
case 57374:
|
|
590
|
-
return "f23";
|
|
591
|
-
case 57375:
|
|
592
|
-
return "f24";
|
|
593
|
-
case 57376:
|
|
594
|
-
return "f25";
|
|
595
|
-
// Keypad keys
|
|
596
|
-
case 57399:
|
|
597
|
-
return "kp0";
|
|
598
|
-
case 57400:
|
|
599
|
-
return "kp1";
|
|
600
|
-
case 57401:
|
|
601
|
-
return "kp2";
|
|
602
|
-
case 57402:
|
|
603
|
-
return "kp3";
|
|
604
|
-
case 57403:
|
|
605
|
-
return "kp4";
|
|
606
|
-
case 57404:
|
|
607
|
-
return "kp5";
|
|
608
|
-
case 57405:
|
|
609
|
-
return "kp6";
|
|
610
|
-
case 57406:
|
|
611
|
-
return "kp7";
|
|
612
|
-
case 57407:
|
|
613
|
-
return "kp8";
|
|
614
|
-
case 57408:
|
|
615
|
-
return "kp9";
|
|
616
|
-
case 57409:
|
|
617
|
-
return "kpdecimal";
|
|
618
|
-
case 57410:
|
|
619
|
-
return "kpdivide";
|
|
620
|
-
case 57411:
|
|
621
|
-
return "kpmultiply";
|
|
622
|
-
case 57412:
|
|
623
|
-
return "kpminus";
|
|
624
|
-
case 57413:
|
|
625
|
-
return "kpplus";
|
|
626
|
-
case 57414:
|
|
627
|
-
return "kpenter";
|
|
628
|
-
case 57415:
|
|
629
|
-
return "kpequal";
|
|
630
|
-
// Navigation (kitty protocol)
|
|
631
|
-
case 57416:
|
|
632
|
-
return "kpleft";
|
|
633
|
-
case 57417:
|
|
634
|
-
return "kpright";
|
|
635
|
-
case 57418:
|
|
636
|
-
return "kpup";
|
|
637
|
-
case 57419:
|
|
638
|
-
return "kpdown";
|
|
639
|
-
case 57420:
|
|
640
|
-
return "kppageup";
|
|
641
|
-
case 57421:
|
|
642
|
-
return "kppagedown";
|
|
643
|
-
case 57422:
|
|
644
|
-
return "kphome";
|
|
645
|
-
case 57423:
|
|
646
|
-
return "kpend";
|
|
647
|
-
case 57424:
|
|
648
|
-
return "kpinsert";
|
|
649
|
-
case 57425:
|
|
650
|
-
return "kpdelete";
|
|
651
|
-
// Media keys
|
|
652
|
-
case 57428:
|
|
653
|
-
return "mediaplaypause";
|
|
654
|
-
case 57429:
|
|
655
|
-
return "mediastop";
|
|
656
|
-
case 57430:
|
|
657
|
-
return "mediaprev";
|
|
658
|
-
case 57431:
|
|
659
|
-
return "medianext";
|
|
660
|
-
case 57432:
|
|
661
|
-
return "mediarewind";
|
|
662
|
-
case 57433:
|
|
663
|
-
return "mediafastforward";
|
|
664
|
-
case 57434:
|
|
665
|
-
return "mediamute";
|
|
666
|
-
case 57435:
|
|
667
|
-
return "volumedown";
|
|
668
|
-
case 57436:
|
|
669
|
-
return "volumeup";
|
|
670
|
-
default:
|
|
671
|
-
if (code >= 32 && code <= 126) {
|
|
672
|
-
return String.fromCharCode(code).toLowerCase();
|
|
673
|
-
}
|
|
674
|
-
return "unknown";
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
function getTildeKeyName(param) {
|
|
678
|
-
const baseParam = param.split(";")[0];
|
|
679
|
-
switch (baseParam) {
|
|
680
|
-
case "1":
|
|
681
|
-
return "home";
|
|
682
|
-
case "2":
|
|
683
|
-
return "insert";
|
|
684
|
-
case "3":
|
|
685
|
-
return "delete";
|
|
686
|
-
case "4":
|
|
687
|
-
return "end";
|
|
688
|
-
case "5":
|
|
689
|
-
return "pageup";
|
|
690
|
-
case "6":
|
|
691
|
-
return "pagedown";
|
|
692
|
-
case "7":
|
|
693
|
-
return "home";
|
|
694
|
-
case "8":
|
|
695
|
-
return "end";
|
|
696
|
-
case "11":
|
|
697
|
-
return "f1";
|
|
698
|
-
case "12":
|
|
699
|
-
return "f2";
|
|
700
|
-
case "13":
|
|
701
|
-
return "f3";
|
|
702
|
-
case "14":
|
|
703
|
-
return "f4";
|
|
704
|
-
case "15":
|
|
705
|
-
return "f5";
|
|
706
|
-
case "17":
|
|
707
|
-
return "f6";
|
|
708
|
-
case "18":
|
|
709
|
-
return "f7";
|
|
710
|
-
case "19":
|
|
711
|
-
return "f8";
|
|
712
|
-
case "20":
|
|
713
|
-
return "f9";
|
|
714
|
-
case "21":
|
|
715
|
-
return "f10";
|
|
716
|
-
case "23":
|
|
717
|
-
return "f11";
|
|
718
|
-
case "24":
|
|
719
|
-
return "f12";
|
|
720
|
-
case "25":
|
|
721
|
-
return "f13";
|
|
722
|
-
case "26":
|
|
723
|
-
return "f14";
|
|
724
|
-
case "28":
|
|
725
|
-
return "f15";
|
|
726
|
-
case "29":
|
|
727
|
-
return "f16";
|
|
728
|
-
case "31":
|
|
729
|
-
return "f17";
|
|
730
|
-
case "32":
|
|
731
|
-
return "f18";
|
|
732
|
-
case "33":
|
|
733
|
-
return "f19";
|
|
734
|
-
case "34":
|
|
735
|
-
return "f20";
|
|
736
|
-
default:
|
|
737
|
-
return "unknown";
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
function applyModifiers(key, mod) {
|
|
741
|
-
const m = mod - 1;
|
|
742
|
-
if (m & 1) key.shift = true;
|
|
743
|
-
if (m & 2) key.alt = true;
|
|
744
|
-
if (m & 4) key.ctrl = true;
|
|
745
|
-
if (m & 8) key.meta = true;
|
|
746
|
-
}
|
|
747
|
-
function parseKeySequence(data) {
|
|
748
|
-
const keys = [];
|
|
749
|
-
let i = 0;
|
|
750
|
-
while (i < data.length) {
|
|
751
|
-
const ch = data[i];
|
|
752
|
-
const code = data.charCodeAt(i);
|
|
753
|
-
if (ch === "\x1B") {
|
|
754
|
-
if (data[i + 1] === "[") {
|
|
755
|
-
const seq = parseCsiSequence(data, i);
|
|
756
|
-
if (seq) {
|
|
757
|
-
keys.push(seq.key);
|
|
758
|
-
i = seq.end;
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
if (data[i + 1] === "O") {
|
|
763
|
-
const seq = parseSs3Sequence(data, i);
|
|
764
|
-
if (seq) {
|
|
765
|
-
keys.push(seq.key);
|
|
766
|
-
i = seq.end;
|
|
767
|
-
continue;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
if (i + 1 < data.length && data.charCodeAt(i + 1) >= 32) {
|
|
771
|
-
keys.push({
|
|
772
|
-
name: data[i + 1].toLowerCase(),
|
|
773
|
-
sequence: data.substring(i, i + 2),
|
|
774
|
-
alt: true
|
|
775
|
-
});
|
|
776
|
-
i += 2;
|
|
777
|
-
continue;
|
|
778
|
-
}
|
|
779
|
-
keys.push({ name: "escape", sequence: "\x1B" });
|
|
780
|
-
i++;
|
|
781
|
-
continue;
|
|
782
|
-
}
|
|
783
|
-
if (code >= 1 && code <= 26) {
|
|
784
|
-
const letter = String.fromCharCode(code + 96);
|
|
785
|
-
if (code === 13) {
|
|
786
|
-
keys.push({ name: "return", sequence: "\r" });
|
|
787
|
-
} else if (code === 9) {
|
|
788
|
-
keys.push({ name: "tab", sequence: " " });
|
|
789
|
-
} else if (code === 8) {
|
|
790
|
-
keys.push({ name: "backspace", sequence: "\b" });
|
|
791
|
-
} else {
|
|
792
|
-
keys.push({ name: letter, sequence: ch, ctrl: true });
|
|
793
|
-
}
|
|
794
|
-
i++;
|
|
795
|
-
continue;
|
|
796
|
-
}
|
|
797
|
-
if (code === 127) {
|
|
798
|
-
keys.push({ name: "backspace", sequence: ch });
|
|
799
|
-
i++;
|
|
800
|
-
continue;
|
|
801
|
-
}
|
|
802
|
-
keys.push({ name: ch, sequence: ch });
|
|
803
|
-
i++;
|
|
804
|
-
}
|
|
805
|
-
return keys;
|
|
806
|
-
}
|
|
807
|
-
function parseSs3Sequence(data, start) {
|
|
808
|
-
if (start + 2 >= data.length) return null;
|
|
809
|
-
const final = data[start + 2];
|
|
810
|
-
const sequence = data.substring(start, start + 3);
|
|
811
|
-
let key;
|
|
812
|
-
switch (final) {
|
|
813
|
-
// Arrow keys (some terminals)
|
|
814
|
-
case "A":
|
|
815
|
-
key = { name: "up", sequence };
|
|
816
|
-
break;
|
|
817
|
-
case "B":
|
|
818
|
-
key = { name: "down", sequence };
|
|
819
|
-
break;
|
|
820
|
-
case "C":
|
|
821
|
-
key = { name: "right", sequence };
|
|
822
|
-
break;
|
|
823
|
-
case "D":
|
|
824
|
-
key = { name: "left", sequence };
|
|
825
|
-
break;
|
|
826
|
-
// Home/End (some terminals)
|
|
827
|
-
case "H":
|
|
828
|
-
key = { name: "home", sequence };
|
|
829
|
-
break;
|
|
830
|
-
case "F":
|
|
831
|
-
key = { name: "end", sequence };
|
|
832
|
-
break;
|
|
833
|
-
// Function keys F1-F4
|
|
834
|
-
case "P":
|
|
835
|
-
key = { name: "f1", sequence };
|
|
836
|
-
break;
|
|
837
|
-
case "Q":
|
|
838
|
-
key = { name: "f2", sequence };
|
|
839
|
-
break;
|
|
840
|
-
case "R":
|
|
841
|
-
key = { name: "f3", sequence };
|
|
842
|
-
break;
|
|
843
|
-
case "S":
|
|
844
|
-
key = { name: "f4", sequence };
|
|
845
|
-
break;
|
|
846
|
-
// Keypad (application mode)
|
|
847
|
-
case "j":
|
|
848
|
-
key = { name: "kpmultiply", sequence };
|
|
849
|
-
break;
|
|
850
|
-
case "k":
|
|
851
|
-
key = { name: "kpplus", sequence };
|
|
852
|
-
break;
|
|
853
|
-
case "l":
|
|
854
|
-
key = { name: "kpcomma", sequence };
|
|
855
|
-
break;
|
|
856
|
-
case "m":
|
|
857
|
-
key = { name: "kpminus", sequence };
|
|
858
|
-
break;
|
|
859
|
-
case "n":
|
|
860
|
-
key = { name: "kpdecimal", sequence };
|
|
861
|
-
break;
|
|
862
|
-
case "o":
|
|
863
|
-
key = { name: "kpdivide", sequence };
|
|
864
|
-
break;
|
|
865
|
-
case "p":
|
|
866
|
-
key = { name: "kp0", sequence };
|
|
867
|
-
break;
|
|
868
|
-
case "q":
|
|
869
|
-
key = { name: "kp1", sequence };
|
|
870
|
-
break;
|
|
871
|
-
case "r":
|
|
872
|
-
key = { name: "kp2", sequence };
|
|
873
|
-
break;
|
|
874
|
-
case "s":
|
|
875
|
-
key = { name: "kp3", sequence };
|
|
876
|
-
break;
|
|
877
|
-
case "t":
|
|
878
|
-
key = { name: "kp4", sequence };
|
|
879
|
-
break;
|
|
880
|
-
case "u":
|
|
881
|
-
key = { name: "kp5", sequence };
|
|
882
|
-
break;
|
|
883
|
-
case "v":
|
|
884
|
-
key = { name: "kp6", sequence };
|
|
885
|
-
break;
|
|
886
|
-
case "w":
|
|
887
|
-
key = { name: "kp7", sequence };
|
|
888
|
-
break;
|
|
889
|
-
case "x":
|
|
890
|
-
key = { name: "kp8", sequence };
|
|
891
|
-
break;
|
|
892
|
-
case "y":
|
|
893
|
-
key = { name: "kp9", sequence };
|
|
894
|
-
break;
|
|
895
|
-
case "M":
|
|
896
|
-
key = { name: "kpenter", sequence };
|
|
897
|
-
break;
|
|
898
|
-
default:
|
|
899
|
-
return null;
|
|
900
|
-
}
|
|
901
|
-
return { key, end: start + 3 };
|
|
902
|
-
}
|
|
903
|
-
function parseCsiSequence(data, start) {
|
|
904
|
-
let i = start + 2;
|
|
905
|
-
let params = "";
|
|
906
|
-
while (i < data.length) {
|
|
907
|
-
const code = data.charCodeAt(i);
|
|
908
|
-
if (code >= 48 && code <= 63) {
|
|
909
|
-
params += data[i];
|
|
910
|
-
i++;
|
|
911
|
-
} else {
|
|
912
|
-
break;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
if (i >= data.length) return null;
|
|
916
|
-
const final = data[i];
|
|
917
|
-
const sequence = data.substring(start, i + 1);
|
|
918
|
-
i++;
|
|
919
|
-
let key;
|
|
920
|
-
switch (final) {
|
|
921
|
-
// Arrow keys
|
|
922
|
-
case "A":
|
|
923
|
-
key = { name: "up", sequence };
|
|
924
|
-
break;
|
|
925
|
-
case "B":
|
|
926
|
-
key = { name: "down", sequence };
|
|
927
|
-
break;
|
|
928
|
-
case "C":
|
|
929
|
-
key = { name: "right", sequence };
|
|
930
|
-
break;
|
|
931
|
-
case "D":
|
|
932
|
-
key = { name: "left", sequence };
|
|
933
|
-
break;
|
|
934
|
-
// Home/End
|
|
935
|
-
case "H":
|
|
936
|
-
key = { name: "home", sequence };
|
|
937
|
-
break;
|
|
938
|
-
case "F":
|
|
939
|
-
key = { name: "end", sequence };
|
|
940
|
-
break;
|
|
941
|
-
// Shift+Tab
|
|
942
|
-
case "Z":
|
|
943
|
-
key = { name: "tab", sequence, shift: true };
|
|
944
|
-
break;
|
|
945
|
-
// Function keys (some terminals)
|
|
946
|
-
case "P":
|
|
947
|
-
key = { name: "f1", sequence };
|
|
948
|
-
break;
|
|
949
|
-
case "Q":
|
|
950
|
-
key = { name: "f2", sequence };
|
|
951
|
-
break;
|
|
952
|
-
case "R":
|
|
953
|
-
key = { name: "f3", sequence };
|
|
954
|
-
break;
|
|
955
|
-
case "S":
|
|
956
|
-
key = { name: "f4", sequence };
|
|
957
|
-
break;
|
|
958
|
-
// ~ terminated sequences (VT-style)
|
|
959
|
-
case "~": {
|
|
960
|
-
if (params.startsWith("27;")) {
|
|
961
|
-
const modParts = params.split(";");
|
|
962
|
-
const mod = parseInt(modParts[1] ?? "1", 10);
|
|
963
|
-
const keyCode = parseInt(modParts[2] ?? "0", 10);
|
|
964
|
-
key = { name: getKeyNameFromCode(keyCode), sequence };
|
|
965
|
-
applyModifiers(key, mod);
|
|
966
|
-
break;
|
|
967
|
-
}
|
|
968
|
-
key = { name: getTildeKeyName(params), sequence };
|
|
969
|
-
if (params.includes(";")) {
|
|
970
|
-
const parts = params.split(";");
|
|
971
|
-
const mod = parseInt(parts[1] ?? "1", 10);
|
|
972
|
-
applyModifiers(key, mod);
|
|
973
|
-
}
|
|
974
|
-
break;
|
|
975
|
-
}
|
|
976
|
-
// Kitty keyboard protocol: CSI code;mod u
|
|
977
|
-
case "u": {
|
|
978
|
-
const parts = params.split(";");
|
|
979
|
-
const keyCode = parseInt(parts[0] ?? "0", 10);
|
|
980
|
-
const mod = parseInt(parts[1] ?? "1", 10);
|
|
981
|
-
key = { name: getKeyNameFromCode(keyCode), sequence };
|
|
982
|
-
applyModifiers(key, mod);
|
|
983
|
-
break;
|
|
984
|
-
}
|
|
985
|
-
// Focus events (if terminal reports them)
|
|
986
|
-
case "I":
|
|
987
|
-
key = { name: "focus", sequence };
|
|
988
|
-
break;
|
|
989
|
-
case "O":
|
|
990
|
-
key = { name: "blur", sequence };
|
|
991
|
-
break;
|
|
992
|
-
default:
|
|
993
|
-
key = { name: "unknown", sequence };
|
|
994
|
-
}
|
|
995
|
-
if (params.includes(";") && !["~", "u"].includes(final)) {
|
|
996
|
-
const parts = params.split(";");
|
|
997
|
-
const mod = parseInt(parts[parts.length - 1] ?? "1", 10);
|
|
998
|
-
if (mod >= 1 && mod <= 16) {
|
|
999
|
-
applyModifiers(key, mod);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
return { key, end: i };
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
// src/paint/color.ts
|
|
1006
|
-
var NAMED_FG = {
|
|
1007
|
-
black: "\x1B[30m",
|
|
1008
|
-
red: "\x1B[31m",
|
|
1009
|
-
green: "\x1B[32m",
|
|
1010
|
-
yellow: "\x1B[33m",
|
|
1011
|
-
blue: "\x1B[34m",
|
|
1012
|
-
magenta: "\x1B[35m",
|
|
1013
|
-
cyan: "\x1B[36m",
|
|
1014
|
-
white: "\x1B[37m",
|
|
1015
|
-
blackBright: "\x1B[90m",
|
|
1016
|
-
redBright: "\x1B[91m",
|
|
1017
|
-
greenBright: "\x1B[92m",
|
|
1018
|
-
yellowBright: "\x1B[93m",
|
|
1019
|
-
blueBright: "\x1B[94m",
|
|
1020
|
-
magentaBright: "\x1B[95m",
|
|
1021
|
-
cyanBright: "\x1B[96m",
|
|
1022
|
-
whiteBright: "\x1B[97m"
|
|
1023
|
-
};
|
|
1024
|
-
var NAMED_BG = {
|
|
1025
|
-
black: "\x1B[40m",
|
|
1026
|
-
red: "\x1B[41m",
|
|
1027
|
-
green: "\x1B[42m",
|
|
1028
|
-
yellow: "\x1B[43m",
|
|
1029
|
-
blue: "\x1B[44m",
|
|
1030
|
-
magenta: "\x1B[45m",
|
|
1031
|
-
cyan: "\x1B[46m",
|
|
1032
|
-
white: "\x1B[47m",
|
|
1033
|
-
blackBright: "\x1B[100m",
|
|
1034
|
-
redBright: "\x1B[101m",
|
|
1035
|
-
greenBright: "\x1B[102m",
|
|
1036
|
-
yellowBright: "\x1B[103m",
|
|
1037
|
-
blueBright: "\x1B[104m",
|
|
1038
|
-
magentaBright: "\x1B[105m",
|
|
1039
|
-
cyanBright: "\x1B[106m",
|
|
1040
|
-
whiteBright: "\x1B[107m"
|
|
1041
|
-
};
|
|
1042
|
-
function parseHex(hex) {
|
|
1043
|
-
const h = hex.replace("#", "");
|
|
1044
|
-
const r = parseInt(h.substring(0, 2), 16);
|
|
1045
|
-
const g = parseInt(h.substring(2, 4), 16);
|
|
1046
|
-
const b = parseInt(h.substring(4, 6), 16);
|
|
1047
|
-
return { r, g, b };
|
|
1048
|
-
}
|
|
1049
|
-
function colorToFg(color) {
|
|
1050
|
-
if (typeof color === "string") {
|
|
1051
|
-
if (color.startsWith("#")) {
|
|
1052
|
-
const { r: r2, g: g2, b: b2 } = parseHex(color);
|
|
1053
|
-
return `\x1B[38;2;${r2};${g2};${b2}m`;
|
|
1054
|
-
}
|
|
1055
|
-
return NAMED_FG[color] ?? "\x1B[39m";
|
|
1056
|
-
}
|
|
1057
|
-
if (typeof color === "number") {
|
|
1058
|
-
return `\x1B[38;5;${color}m`;
|
|
1059
|
-
}
|
|
1060
|
-
const { r, g, b } = color;
|
|
1061
|
-
return `\x1B[38;2;${r};${g};${b}m`;
|
|
1062
|
-
}
|
|
1063
|
-
function colorToBg(color) {
|
|
1064
|
-
if (typeof color === "string") {
|
|
1065
|
-
if (color.startsWith("#")) {
|
|
1066
|
-
const { r: r2, g: g2, b: b2 } = parseHex(color);
|
|
1067
|
-
return `\x1B[48;2;${r2};${g2};${b2}m`;
|
|
1068
|
-
}
|
|
1069
|
-
return NAMED_BG[color] ?? "\x1B[49m";
|
|
1070
|
-
}
|
|
1071
|
-
if (typeof color === "number") {
|
|
1072
|
-
return `\x1B[48;5;${color}m`;
|
|
1073
|
-
}
|
|
1074
|
-
const { r, g, b } = color;
|
|
1075
|
-
return `\x1B[48;2;${r};${g};${b}m`;
|
|
1076
|
-
}
|
|
1077
|
-
var NAMED_RGB = {
|
|
1078
|
-
black: [0, 0, 0],
|
|
1079
|
-
red: [170, 0, 0],
|
|
1080
|
-
green: [0, 170, 0],
|
|
1081
|
-
yellow: [170, 170, 0],
|
|
1082
|
-
blue: [0, 0, 170],
|
|
1083
|
-
magenta: [170, 0, 170],
|
|
1084
|
-
cyan: [0, 170, 170],
|
|
1085
|
-
white: [170, 170, 170],
|
|
1086
|
-
blackBright: [85, 85, 85],
|
|
1087
|
-
redBright: [255, 85, 85],
|
|
1088
|
-
greenBright: [85, 255, 85],
|
|
1089
|
-
yellowBright: [255, 255, 85],
|
|
1090
|
-
blueBright: [85, 85, 255],
|
|
1091
|
-
magentaBright: [255, 85, 255],
|
|
1092
|
-
cyanBright: [85, 255, 255],
|
|
1093
|
-
whiteBright: [255, 255, 255]
|
|
1094
|
-
};
|
|
1095
|
-
var NAMED_INDEX = [
|
|
1096
|
-
"black",
|
|
1097
|
-
"red",
|
|
1098
|
-
"green",
|
|
1099
|
-
"yellow",
|
|
1100
|
-
"blue",
|
|
1101
|
-
"magenta",
|
|
1102
|
-
"cyan",
|
|
1103
|
-
"white",
|
|
1104
|
-
"blackBright",
|
|
1105
|
-
"redBright",
|
|
1106
|
-
"greenBright",
|
|
1107
|
-
"yellowBright",
|
|
1108
|
-
"blueBright",
|
|
1109
|
-
"magentaBright",
|
|
1110
|
-
"cyanBright",
|
|
1111
|
-
"whiteBright"
|
|
1112
|
-
];
|
|
1113
|
-
var terminalPalette = null;
|
|
1114
|
-
function setTerminalPalette(palette) {
|
|
1115
|
-
if (palette.size > 0) {
|
|
1116
|
-
terminalPalette = palette;
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
function resolveNamedRgb(name) {
|
|
1120
|
-
if (terminalPalette) {
|
|
1121
|
-
const idx = NAMED_INDEX.indexOf(name);
|
|
1122
|
-
if (idx !== -1) {
|
|
1123
|
-
const tp = terminalPalette.get(idx);
|
|
1124
|
-
if (tp) return tp;
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
return NAMED_RGB[name] ?? null;
|
|
1128
|
-
}
|
|
1129
|
-
function colorToRgb(color) {
|
|
1130
|
-
if (typeof color === "string") {
|
|
1131
|
-
if (color.startsWith("#")) {
|
|
1132
|
-
const c = parseHex(color);
|
|
1133
|
-
return [c.r, c.g, c.b];
|
|
1134
|
-
}
|
|
1135
|
-
return resolveNamedRgb(color);
|
|
1136
|
-
}
|
|
1137
|
-
if (typeof color === "number") {
|
|
1138
|
-
if (color < 16) {
|
|
1139
|
-
if (terminalPalette) {
|
|
1140
|
-
const tp = terminalPalette.get(color);
|
|
1141
|
-
if (tp) return tp;
|
|
1142
|
-
}
|
|
1143
|
-
return NAMED_RGB[NAMED_INDEX[color]];
|
|
1144
|
-
}
|
|
1145
|
-
if (color >= 232) {
|
|
1146
|
-
const g2 = (color - 232) * 10 + 8;
|
|
1147
|
-
return [g2, g2, g2];
|
|
1148
|
-
}
|
|
1149
|
-
const idx = color - 16;
|
|
1150
|
-
const b = idx % 6 * 51;
|
|
1151
|
-
const g = Math.floor(idx / 6) % 6 * 51;
|
|
1152
|
-
const r = Math.floor(idx / 36) * 51;
|
|
1153
|
-
return [r, g, b];
|
|
1154
|
-
}
|
|
1155
|
-
return [color.r, color.g, color.b];
|
|
1156
|
-
}
|
|
1157
|
-
function isLightColor(color) {
|
|
1158
|
-
const rgb = colorToRgb(color);
|
|
1159
|
-
if (!rgb) return false;
|
|
1160
|
-
const [r, g, b] = rgb.map((c) => {
|
|
1161
|
-
const s = c / 255;
|
|
1162
|
-
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
1163
|
-
});
|
|
1164
|
-
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
1165
|
-
return luminance > 0.4;
|
|
1166
|
-
}
|
|
1167
|
-
function colorsEqual(a, b) {
|
|
1168
|
-
if (a === b) return true;
|
|
1169
|
-
if (a == null || b == null) return false;
|
|
1170
|
-
if (typeof a === "object" && typeof b === "object") {
|
|
1171
|
-
return a.r === b.r && a.g === b.g && a.b === b.b;
|
|
1172
|
-
}
|
|
1173
|
-
return false;
|
|
1174
|
-
}
|
|
1175
|
-
function getContrastCursorColor(bg) {
|
|
1176
|
-
if (!bg) return "white";
|
|
1177
|
-
return isLightColor(bg) ? "black" : "white";
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
// src/paint/framebuffer.ts
|
|
1181
|
-
var Framebuffer = class _Framebuffer {
|
|
1182
|
-
width;
|
|
1183
|
-
height;
|
|
1184
|
-
cells;
|
|
1185
|
-
constructor(width, height) {
|
|
1186
|
-
this.width = width;
|
|
1187
|
-
this.height = height;
|
|
1188
|
-
this.cells = new Array(width * height);
|
|
1189
|
-
this.clear();
|
|
1190
|
-
}
|
|
1191
|
-
clear() {
|
|
1192
|
-
for (let i = 0; i < this.cells.length; i++) {
|
|
1193
|
-
this.cells[i] = { ch: " " };
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
resize(width, height) {
|
|
1197
|
-
this.width = width;
|
|
1198
|
-
this.height = height;
|
|
1199
|
-
this.cells = new Array(width * height);
|
|
1200
|
-
this.clear();
|
|
1201
|
-
}
|
|
1202
|
-
get(x, y) {
|
|
1203
|
-
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return void 0;
|
|
1204
|
-
return this.cells[y * this.width + x];
|
|
1205
|
-
}
|
|
1206
|
-
set(x, y, cell) {
|
|
1207
|
-
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
|
|
1208
|
-
this.cells[y * this.width + x] = cell;
|
|
1209
|
-
}
|
|
1210
|
-
setChar(x, y, ch, fg, bg, bold, dim, italic, underline) {
|
|
1211
|
-
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
|
|
1212
|
-
this.cells[y * this.width + x] = { ch, fg, bg, bold, dim, italic, underline };
|
|
1213
|
-
}
|
|
1214
|
-
fillRect(x, y, w, h, ch, fg, bg) {
|
|
1215
|
-
for (let row = y; row < y + h; row++) {
|
|
1216
|
-
for (let col = x; col < x + w; col++) {
|
|
1217
|
-
this.setChar(col, row, ch, fg, bg);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
clone() {
|
|
1222
|
-
const fb = new _Framebuffer(this.width, this.height);
|
|
1223
|
-
for (let i = 0; i < this.cells.length; i++) {
|
|
1224
|
-
const c = this.cells[i];
|
|
1225
|
-
fb.cells[i] = { ...c };
|
|
1226
|
-
}
|
|
1227
|
-
return fb;
|
|
1228
|
-
}
|
|
1229
|
-
cellsEqual(a, b) {
|
|
1230
|
-
return a.ch === b.ch && colorsEqual(a.fg, b.fg) && colorsEqual(a.bg, b.bg) && (a.bold ?? false) === (b.bold ?? false) && (a.dim ?? false) === (b.dim ?? false) && (a.italic ?? false) === (b.italic ?? false) && (a.underline ?? false) === (b.underline ?? false);
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
|
|
1234
|
-
// src/paint/borders.ts
|
|
1235
|
-
var BORDER_CHARS = {
|
|
1236
|
-
single: {
|
|
1237
|
-
topLeft: "\u250C",
|
|
1238
|
-
topRight: "\u2510",
|
|
1239
|
-
bottomLeft: "\u2514",
|
|
1240
|
-
bottomRight: "\u2518",
|
|
1241
|
-
horizontal: "\u2500",
|
|
1242
|
-
vertical: "\u2502"
|
|
1243
|
-
},
|
|
1244
|
-
double: {
|
|
1245
|
-
topLeft: "\u2554",
|
|
1246
|
-
topRight: "\u2557",
|
|
1247
|
-
bottomLeft: "\u255A",
|
|
1248
|
-
bottomRight: "\u255D",
|
|
1249
|
-
horizontal: "\u2550",
|
|
1250
|
-
vertical: "\u2551"
|
|
1251
|
-
},
|
|
1252
|
-
round: {
|
|
1253
|
-
topLeft: "\u256D",
|
|
1254
|
-
topRight: "\u256E",
|
|
1255
|
-
bottomLeft: "\u2570",
|
|
1256
|
-
bottomRight: "\u256F",
|
|
1257
|
-
horizontal: "\u2500",
|
|
1258
|
-
vertical: "\u2502"
|
|
1259
|
-
},
|
|
1260
|
-
ascii: {
|
|
1261
|
-
topLeft: "+",
|
|
1262
|
-
topRight: "+",
|
|
1263
|
-
bottomLeft: "+",
|
|
1264
|
-
bottomRight: "+",
|
|
1265
|
-
horizontal: "-",
|
|
1266
|
-
vertical: "|"
|
|
1267
|
-
}
|
|
1268
|
-
};
|
|
1269
|
-
function getBorderChars(style) {
|
|
1270
|
-
if (style === "none") return null;
|
|
1271
|
-
return BORDER_CHARS[style];
|
|
1272
|
-
}
|
|
1273
|
-
function measureText(text, maxWidth, widthMode, wrapMode) {
|
|
1274
|
-
if (text.length === 0) {
|
|
1275
|
-
return { width: 0, height: 0 };
|
|
1276
|
-
}
|
|
1277
|
-
const lines = text.split("\n");
|
|
1278
|
-
if (widthMode === MeasureMode.Undefined || wrapMode === "none") {
|
|
1279
|
-
let maxW2 = 0;
|
|
1280
|
-
for (const line of lines) {
|
|
1281
|
-
const w = stringWidth(line);
|
|
1282
|
-
if (w > maxW2) maxW2 = w;
|
|
1283
|
-
}
|
|
1284
|
-
return { width: maxW2, height: lines.length };
|
|
1285
|
-
}
|
|
1286
|
-
const availWidth = Math.max(1, Math.floor(maxWidth));
|
|
1287
|
-
const wrappedLines = wrapLines(lines, availWidth, wrapMode);
|
|
1288
|
-
let maxW = 0;
|
|
1289
|
-
for (const line of wrappedLines) {
|
|
1290
|
-
const w = stringWidth(line);
|
|
1291
|
-
if (w > maxW) maxW = w;
|
|
1292
|
-
}
|
|
1293
|
-
return { width: maxW, height: wrappedLines.length };
|
|
1294
|
-
}
|
|
1295
|
-
function wrapLines(lines, maxWidth, wrapMode) {
|
|
1296
|
-
const result = [];
|
|
1297
|
-
for (const line of lines) {
|
|
1298
|
-
const lineWidth = stringWidth(line);
|
|
1299
|
-
if (lineWidth <= maxWidth) {
|
|
1300
|
-
result.push(line);
|
|
1301
|
-
continue;
|
|
1302
|
-
}
|
|
1303
|
-
if (wrapMode === "truncate") {
|
|
1304
|
-
result.push(truncateLine(line, maxWidth));
|
|
1305
|
-
continue;
|
|
1306
|
-
}
|
|
1307
|
-
if (wrapMode === "ellipsis") {
|
|
1308
|
-
result.push(truncateWithEllipsis(line, maxWidth));
|
|
1309
|
-
continue;
|
|
1310
|
-
}
|
|
1311
|
-
const wrapped = wordWrap(line, maxWidth);
|
|
1312
|
-
result.push(...wrapped);
|
|
1313
|
-
}
|
|
1314
|
-
return result;
|
|
1315
|
-
}
|
|
1316
|
-
function truncateLine(text, maxWidth) {
|
|
1317
|
-
let result = "";
|
|
1318
|
-
let width = 0;
|
|
1319
|
-
for (const char of text) {
|
|
1320
|
-
const charWidth = stringWidth(char);
|
|
1321
|
-
if (width + charWidth > maxWidth) break;
|
|
1322
|
-
result += char;
|
|
1323
|
-
width += charWidth;
|
|
1324
|
-
}
|
|
1325
|
-
return result;
|
|
1326
|
-
}
|
|
1327
|
-
function truncateWithEllipsis(text, maxWidth) {
|
|
1328
|
-
if (maxWidth <= 1) {
|
|
1329
|
-
return maxWidth === 1 ? "\u2026" : "";
|
|
1330
|
-
}
|
|
1331
|
-
const truncated = truncateLine(text, maxWidth - 1);
|
|
1332
|
-
if (stringWidth(truncated) < stringWidth(text)) {
|
|
1333
|
-
return truncated + "\u2026";
|
|
1334
|
-
}
|
|
1335
|
-
return text;
|
|
1336
|
-
}
|
|
1337
|
-
function wordWrap(text, maxWidth) {
|
|
1338
|
-
const lines = [];
|
|
1339
|
-
let currentLine = "";
|
|
1340
|
-
let currentWidth = 0;
|
|
1341
|
-
let wordBuffer = "";
|
|
1342
|
-
let wordBufferWidth = 0;
|
|
1343
|
-
for (let i = 0; i <= text.length; i++) {
|
|
1344
|
-
const char = text[i];
|
|
1345
|
-
const isEnd = i === text.length;
|
|
1346
|
-
const isSpace = char === " ";
|
|
1347
|
-
if (isEnd || isSpace) {
|
|
1348
|
-
if (wordBuffer.length > 0) {
|
|
1349
|
-
if (currentWidth + wordBufferWidth <= maxWidth) {
|
|
1350
|
-
currentLine += wordBuffer;
|
|
1351
|
-
currentWidth += wordBufferWidth;
|
|
1352
|
-
} else if (wordBufferWidth <= maxWidth) {
|
|
1353
|
-
if (currentLine.length > 0) {
|
|
1354
|
-
lines.push(currentLine);
|
|
1355
|
-
}
|
|
1356
|
-
currentLine = wordBuffer;
|
|
1357
|
-
currentWidth = wordBufferWidth;
|
|
1358
|
-
} else {
|
|
1359
|
-
for (const c of wordBuffer) {
|
|
1360
|
-
const cw = stringWidth(c);
|
|
1361
|
-
if (currentWidth + cw > maxWidth && currentLine.length > 0) {
|
|
1362
|
-
lines.push(currentLine);
|
|
1363
|
-
currentLine = "";
|
|
1364
|
-
currentWidth = 0;
|
|
1365
|
-
}
|
|
1366
|
-
currentLine += c;
|
|
1367
|
-
currentWidth += cw;
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
wordBuffer = "";
|
|
1371
|
-
wordBufferWidth = 0;
|
|
1372
|
-
}
|
|
1373
|
-
if (isSpace) {
|
|
1374
|
-
if (currentWidth + 1 <= maxWidth) {
|
|
1375
|
-
currentLine += " ";
|
|
1376
|
-
currentWidth += 1;
|
|
1377
|
-
} else {
|
|
1378
|
-
if (currentLine.length > 0) {
|
|
1379
|
-
lines.push(currentLine);
|
|
1380
|
-
}
|
|
1381
|
-
currentLine = " ";
|
|
1382
|
-
currentWidth = 1;
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
} else {
|
|
1386
|
-
wordBuffer += char;
|
|
1387
|
-
wordBufferWidth += stringWidth(char);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
if (currentLine.length > 0) {
|
|
1391
|
-
lines.push(currentLine);
|
|
1392
|
-
}
|
|
1393
|
-
return lines.length > 0 ? lines : [""];
|
|
1394
|
-
}
|
|
1395
|
-
function paintTree(roots, fb, options = {}) {
|
|
1396
|
-
fb.clear();
|
|
1397
|
-
const result = {};
|
|
1398
|
-
const entries = [];
|
|
1399
|
-
const screenClip = { x: 0, y: 0, width: fb.width, height: fb.height };
|
|
1400
|
-
for (const root of roots) {
|
|
1401
|
-
if (root.hidden) continue;
|
|
1402
|
-
collectPaintEntries(root, screenClip, root.style.zIndex ?? 0, entries);
|
|
1403
|
-
}
|
|
1404
|
-
entries.sort((a, b) => a.zIndex - b.zIndex);
|
|
1405
|
-
for (const entry of entries) {
|
|
1406
|
-
const nodeResult = paintNode(entry.node, fb, entry.clip, options);
|
|
1407
|
-
if (nodeResult?.cursorPosition) {
|
|
1408
|
-
result.cursorPosition = nodeResult.cursorPosition;
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
return result;
|
|
1412
|
-
}
|
|
1413
|
-
function collectPaintEntries(node, parentClip, parentZ, entries) {
|
|
1414
|
-
if (node.hidden) return;
|
|
1415
|
-
const zIndex = node.style.zIndex ?? parentZ;
|
|
1416
|
-
const clip = node.style.clip ? intersectClip(parentClip, {
|
|
1417
|
-
x: node.layout.innerX,
|
|
1418
|
-
y: node.layout.innerY,
|
|
1419
|
-
width: node.layout.innerWidth,
|
|
1420
|
-
height: node.layout.innerHeight
|
|
1421
|
-
}) : parentClip;
|
|
1422
|
-
entries.push({ node, clip: parentClip, zIndex });
|
|
1423
|
-
if (node.type !== "text" && node.type !== "input") {
|
|
1424
|
-
for (const child of node.children) {
|
|
1425
|
-
collectPaintEntries(child, clip, zIndex, entries);
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
function intersectClip(a, b) {
|
|
1430
|
-
const x = Math.max(a.x, b.x);
|
|
1431
|
-
const y = Math.max(a.y, b.y);
|
|
1432
|
-
const right = Math.min(a.x + a.width, b.x + b.width);
|
|
1433
|
-
const bottom = Math.min(a.y + a.height, b.y + b.height);
|
|
1434
|
-
return {
|
|
1435
|
-
x,
|
|
1436
|
-
y,
|
|
1437
|
-
width: Math.max(0, right - x),
|
|
1438
|
-
height: Math.max(0, bottom - y)
|
|
1439
|
-
};
|
|
1440
|
-
}
|
|
1441
|
-
function isInClip(x, y, clip) {
|
|
1442
|
-
return x >= clip.x && x < clip.x + clip.width && y >= clip.y && y < clip.y + clip.height;
|
|
1443
|
-
}
|
|
1444
|
-
function paintNode(node, fb, clip, options = {}) {
|
|
1445
|
-
const { x, y, width, height, innerX, innerY, innerWidth, innerHeight } = node.layout;
|
|
1446
|
-
const style = node.style;
|
|
1447
|
-
if (width <= 0 || height <= 0) return;
|
|
1448
|
-
const inherited = getInheritedTextStyle(node);
|
|
1449
|
-
const effectiveBg = inherited.bg;
|
|
1450
|
-
if (style.bg) {
|
|
1451
|
-
for (let row = y; row < y + height; row++) {
|
|
1452
|
-
for (let col = x; col < x + width; col++) {
|
|
1453
|
-
if (isInClip(col, row, clip)) {
|
|
1454
|
-
fb.setChar(col, row, " ", void 0, style.bg);
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
const borderChars = style.border ? getBorderChars(style.border) : null;
|
|
1460
|
-
if (borderChars && width >= 2 && height >= 2) {
|
|
1461
|
-
const bc = style.borderColor;
|
|
1462
|
-
const bg = effectiveBg;
|
|
1463
|
-
setClipped(fb, clip, x, y, borderChars.topLeft, bc, bg);
|
|
1464
|
-
for (let col = x + 1; col < x + width - 1; col++) {
|
|
1465
|
-
setClipped(fb, clip, col, y, borderChars.horizontal, bc, bg);
|
|
1466
|
-
}
|
|
1467
|
-
setClipped(fb, clip, x + width - 1, y, borderChars.topRight, bc, bg);
|
|
1468
|
-
setClipped(fb, clip, x, y + height - 1, borderChars.bottomLeft, bc, bg);
|
|
1469
|
-
for (let col = x + 1; col < x + width - 1; col++) {
|
|
1470
|
-
setClipped(fb, clip, col, y + height - 1, borderChars.horizontal, bc, bg);
|
|
1471
|
-
}
|
|
1472
|
-
setClipped(fb, clip, x + width - 1, y + height - 1, borderChars.bottomRight, bc, bg);
|
|
1473
|
-
for (let row = y + 1; row < y + height - 1; row++) {
|
|
1474
|
-
setClipped(fb, clip, x, row, borderChars.vertical, bc, bg);
|
|
1475
|
-
setClipped(fb, clip, x + width - 1, row, borderChars.vertical, bc, bg);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
if (node.type === "text") {
|
|
1479
|
-
paintText(node, fb, clip);
|
|
1480
|
-
} else if (node.type === "input") {
|
|
1481
|
-
return paintInput(node, fb, clip, options);
|
|
1482
|
-
}
|
|
1483
|
-
return void 0;
|
|
1484
|
-
}
|
|
1485
|
-
function setClipped(fb, clip, x, y, ch, fg, bg, bold, dim, italic, underline) {
|
|
1486
|
-
if (isInClip(x, y, clip)) {
|
|
1487
|
-
fb.setChar(x, y, ch, fg, bg, bold, dim, italic, underline);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
function autoContrastFg(explicitColor, bg) {
|
|
1491
|
-
if (explicitColor !== void 0) return explicitColor;
|
|
1492
|
-
if (bg === void 0) return void 0;
|
|
1493
|
-
return isLightColor(bg) ? "black" : "white";
|
|
1494
|
-
}
|
|
1495
|
-
function paintText(node, fb, clip) {
|
|
1496
|
-
const { innerX, innerY, innerWidth, innerHeight } = node.layout;
|
|
1497
|
-
const inherited = getInheritedTextStyle(node);
|
|
1498
|
-
const text = collectTextContent(node);
|
|
1499
|
-
if (!text) return;
|
|
1500
|
-
const fg = autoContrastFg(inherited.color, inherited.bg);
|
|
1501
|
-
const wrapMode = node.style.wrap ?? "wrap";
|
|
1502
|
-
const textAlign = node.style.textAlign ?? "left";
|
|
1503
|
-
const rawLines = text.split("\n");
|
|
1504
|
-
const lines = wrapLines(rawLines, innerWidth, wrapMode);
|
|
1505
|
-
for (let lineIdx = 0; lineIdx < lines.length && lineIdx < innerHeight; lineIdx++) {
|
|
1506
|
-
const line = lines[lineIdx];
|
|
1507
|
-
const lineWidth = stringWidth(line);
|
|
1508
|
-
let offsetX = 0;
|
|
1509
|
-
if (textAlign === "center") {
|
|
1510
|
-
offsetX = Math.max(0, Math.floor((innerWidth - lineWidth) / 2));
|
|
1511
|
-
} else if (textAlign === "right") {
|
|
1512
|
-
offsetX = Math.max(0, innerWidth - lineWidth);
|
|
1513
|
-
}
|
|
1514
|
-
let col = 0;
|
|
1515
|
-
for (const char of line) {
|
|
1516
|
-
const charWidth = stringWidth(char);
|
|
1517
|
-
if (charWidth > 0) {
|
|
1518
|
-
setClipped(
|
|
1519
|
-
fb,
|
|
1520
|
-
clip,
|
|
1521
|
-
innerX + offsetX + col,
|
|
1522
|
-
innerY + lineIdx,
|
|
1523
|
-
char,
|
|
1524
|
-
fg,
|
|
1525
|
-
inherited.bg,
|
|
1526
|
-
inherited.bold,
|
|
1527
|
-
inherited.dim,
|
|
1528
|
-
inherited.italic,
|
|
1529
|
-
inherited.underline
|
|
1530
|
-
);
|
|
1531
|
-
}
|
|
1532
|
-
col += charWidth;
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
function paintInput(node, fb, clip, options = {}) {
|
|
1537
|
-
const { cursorInfo, useNativeCursor } = options;
|
|
1538
|
-
const { innerX, innerY, innerWidth, innerHeight } = node.layout;
|
|
1539
|
-
if (innerWidth <= 0 || innerHeight <= 0) return;
|
|
1540
|
-
const value = node.props.value ?? node.props.defaultValue ?? "";
|
|
1541
|
-
const placeholder = node.props.placeholder ?? "";
|
|
1542
|
-
const displayText = value || placeholder;
|
|
1543
|
-
const isPlaceholder = !value && !!placeholder;
|
|
1544
|
-
const multiline = node.props.multiline ?? false;
|
|
1545
|
-
const inherited = getInheritedTextStyle(node);
|
|
1546
|
-
const autoFg = autoContrastFg(inherited.color, inherited.bg);
|
|
1547
|
-
const placeholderFg = inherited.bg ? isLightColor(inherited.bg) ? "blackBright" : "whiteBright" : "blackBright";
|
|
1548
|
-
const fg = isPlaceholder ? placeholderFg : autoFg ?? inherited.color ?? node.style.color;
|
|
1549
|
-
const textFg = isPlaceholder ? placeholderFg : fg;
|
|
1550
|
-
const textDim = isPlaceholder ? true : inherited.dim;
|
|
1551
|
-
const isFocused = cursorInfo && cursorInfo.nodeId === node.focusId;
|
|
1552
|
-
let result;
|
|
1553
|
-
if (multiline && !isPlaceholder) {
|
|
1554
|
-
const wrapMode = node.style.wrap ?? "wrap";
|
|
1555
|
-
const rawLines = displayText.split("\n");
|
|
1556
|
-
const wrappedLines = wrapLines(rawLines, innerWidth, wrapMode);
|
|
1557
|
-
let cursorScreenLine = 0;
|
|
1558
|
-
let cursorScreenCol = 0;
|
|
1559
|
-
if (isFocused) {
|
|
1560
|
-
const pos = cursorInfo.position;
|
|
1561
|
-
let logicalLine = 0;
|
|
1562
|
-
let offsetInLogicalLine = pos;
|
|
1563
|
-
let runningPos = 0;
|
|
1564
|
-
for (let i = 0; i < rawLines.length; i++) {
|
|
1565
|
-
const lineLen = rawLines[i].length;
|
|
1566
|
-
if (pos <= runningPos + lineLen) {
|
|
1567
|
-
logicalLine = i;
|
|
1568
|
-
offsetInLogicalLine = pos - runningPos;
|
|
1569
|
-
break;
|
|
1570
|
-
}
|
|
1571
|
-
runningPos += lineLen + 1;
|
|
1572
|
-
}
|
|
1573
|
-
let wrappedLinesBefore = 0;
|
|
1574
|
-
for (let i = 0; i < logicalLine; i++) {
|
|
1575
|
-
wrappedLinesBefore += wrapLines([rawLines[i]], innerWidth, wrapMode).length;
|
|
1576
|
-
}
|
|
1577
|
-
const wrappedCurrentLine = wrapLines([rawLines[logicalLine]], innerWidth, wrapMode);
|
|
1578
|
-
let charsProcessed = 0;
|
|
1579
|
-
let subLineIdx = 0;
|
|
1580
|
-
for (let i = 0; i < wrappedCurrentLine.length; i++) {
|
|
1581
|
-
const subLine = wrappedCurrentLine[i];
|
|
1582
|
-
if (offsetInLogicalLine <= charsProcessed + subLine.length) {
|
|
1583
|
-
subLineIdx = i;
|
|
1584
|
-
break;
|
|
1585
|
-
}
|
|
1586
|
-
charsProcessed += subLine.length;
|
|
1587
|
-
}
|
|
1588
|
-
cursorScreenLine = wrappedLinesBefore + subLineIdx;
|
|
1589
|
-
cursorScreenCol = stringWidth(rawLines[logicalLine].slice(charsProcessed, charsProcessed + (offsetInLogicalLine - charsProcessed)));
|
|
1590
|
-
}
|
|
1591
|
-
const scrollOffset = Math.max(0, cursorScreenLine - innerHeight + 1);
|
|
1592
|
-
for (let rowIdx = 0; rowIdx < innerHeight; rowIdx++) {
|
|
1593
|
-
const lineNum = scrollOffset + rowIdx;
|
|
1594
|
-
if (lineNum >= wrappedLines.length) break;
|
|
1595
|
-
const line = wrappedLines[lineNum];
|
|
1596
|
-
let col = 0;
|
|
1597
|
-
for (const char of line) {
|
|
1598
|
-
if (col >= innerWidth) break;
|
|
1599
|
-
const charWidth = stringWidth(char);
|
|
1600
|
-
if (charWidth > 0) {
|
|
1601
|
-
setClipped(
|
|
1602
|
-
fb,
|
|
1603
|
-
clip,
|
|
1604
|
-
innerX + col,
|
|
1605
|
-
innerY + rowIdx,
|
|
1606
|
-
char,
|
|
1607
|
-
textFg,
|
|
1608
|
-
inherited.bg,
|
|
1609
|
-
inherited.bold,
|
|
1610
|
-
textDim,
|
|
1611
|
-
inherited.italic,
|
|
1612
|
-
inherited.underline
|
|
1613
|
-
);
|
|
1614
|
-
}
|
|
1615
|
-
col += charWidth;
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
if (isFocused) {
|
|
1619
|
-
const screenRow = cursorScreenLine - scrollOffset;
|
|
1620
|
-
if (screenRow >= 0 && screenRow < innerHeight) {
|
|
1621
|
-
const cCol = Math.min(cursorScreenCol, innerWidth - 1);
|
|
1622
|
-
const cursorX = innerX + cCol;
|
|
1623
|
-
const cursorY = innerY + screenRow;
|
|
1624
|
-
if (isInClip(cursorX, cursorY, clip) && cursorX < innerX + innerWidth) {
|
|
1625
|
-
if (useNativeCursor) {
|
|
1626
|
-
result = { cursorPosition: { x: cursorX, y: cursorY, bg: inherited.bg } };
|
|
1627
|
-
} else {
|
|
1628
|
-
const existing = fb.get(cursorX, cursorY);
|
|
1629
|
-
const cursorChar = existing?.ch && existing.ch !== " " ? existing.ch : "\u258C";
|
|
1630
|
-
const cursorFg = inherited.bg ?? "black";
|
|
1631
|
-
const cursorBg = inherited.color ?? "white";
|
|
1632
|
-
fb.setChar(
|
|
1633
|
-
cursorX,
|
|
1634
|
-
cursorY,
|
|
1635
|
-
cursorChar,
|
|
1636
|
-
cursorFg,
|
|
1637
|
-
cursorBg,
|
|
1638
|
-
existing?.bold,
|
|
1639
|
-
existing?.dim,
|
|
1640
|
-
existing?.italic,
|
|
1641
|
-
false
|
|
1642
|
-
);
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
} else {
|
|
1648
|
-
let col = 0;
|
|
1649
|
-
for (const char of displayText) {
|
|
1650
|
-
if (col >= innerWidth) break;
|
|
1651
|
-
const charWidth = stringWidth(char);
|
|
1652
|
-
if (charWidth > 0) {
|
|
1653
|
-
setClipped(
|
|
1654
|
-
fb,
|
|
1655
|
-
clip,
|
|
1656
|
-
innerX + col,
|
|
1657
|
-
innerY,
|
|
1658
|
-
char,
|
|
1659
|
-
textFg,
|
|
1660
|
-
inherited.bg,
|
|
1661
|
-
inherited.bold,
|
|
1662
|
-
textDim,
|
|
1663
|
-
inherited.italic,
|
|
1664
|
-
inherited.underline
|
|
1665
|
-
);
|
|
1666
|
-
}
|
|
1667
|
-
col += charWidth;
|
|
1668
|
-
}
|
|
1669
|
-
if (isFocused) {
|
|
1670
|
-
const cursorCol = Math.min(cursorInfo.position, innerWidth - 1);
|
|
1671
|
-
const cursorX = innerX + cursorCol;
|
|
1672
|
-
if (isInClip(cursorX, innerY, clip) && cursorX < innerX + innerWidth) {
|
|
1673
|
-
if (useNativeCursor) {
|
|
1674
|
-
result = { cursorPosition: { x: cursorX, y: innerY, bg: inherited.bg } };
|
|
1675
|
-
} else {
|
|
1676
|
-
const existing = fb.get(cursorX, innerY);
|
|
1677
|
-
const cursorChar = existing?.ch && existing.ch !== " " ? existing.ch : "\u258C";
|
|
1678
|
-
const cursorFg = inherited.bg ?? "black";
|
|
1679
|
-
const cursorBg = inherited.color ?? "white";
|
|
1680
|
-
fb.setChar(
|
|
1681
|
-
cursorX,
|
|
1682
|
-
innerY,
|
|
1683
|
-
cursorChar,
|
|
1684
|
-
cursorFg,
|
|
1685
|
-
cursorBg,
|
|
1686
|
-
existing?.bold,
|
|
1687
|
-
existing?.dim,
|
|
1688
|
-
existing?.italic,
|
|
1689
|
-
false
|
|
1690
|
-
);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
return result;
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
// src/paint/diff.ts
|
|
1699
|
-
var ESC2 = "\x1B";
|
|
1700
|
-
var CSI2 = `${ESC2}[`;
|
|
1701
|
-
function moveCursor(x, y) {
|
|
1702
|
-
return `${CSI2}${y + 1};${x + 1}H`;
|
|
1703
|
-
}
|
|
1704
|
-
function buildSGR(cell) {
|
|
1705
|
-
let seq = `${CSI2}0m`;
|
|
1706
|
-
if (cell.bold) seq += `${CSI2}1m`;
|
|
1707
|
-
if (cell.dim) seq += `${CSI2}2m`;
|
|
1708
|
-
if (cell.italic) seq += `${CSI2}3m`;
|
|
1709
|
-
if (cell.underline) seq += `${CSI2}4m`;
|
|
1710
|
-
if (cell.fg != null) seq += colorToFg(cell.fg);
|
|
1711
|
-
if (cell.bg != null) seq += colorToBg(cell.bg);
|
|
1712
|
-
return seq;
|
|
1713
|
-
}
|
|
1714
|
-
function diffFramebuffers(prev, next, fullRedraw) {
|
|
1715
|
-
let out = "";
|
|
1716
|
-
let lastX = -1;
|
|
1717
|
-
let lastY = -1;
|
|
1718
|
-
let lastSGR = "";
|
|
1719
|
-
for (let y = 0; y < next.height; y++) {
|
|
1720
|
-
for (let x = 0; x < next.width; x++) {
|
|
1721
|
-
const nc = next.get(x, y);
|
|
1722
|
-
if (!fullRedraw) {
|
|
1723
|
-
const pc = prev.get(x, y);
|
|
1724
|
-
if (pc && next.cellsEqual(nc, pc)) continue;
|
|
1725
|
-
}
|
|
1726
|
-
if (lastY !== y || lastX !== x) {
|
|
1727
|
-
out += moveCursor(x, y);
|
|
1728
|
-
}
|
|
1729
|
-
const sgr = buildSGR(nc);
|
|
1730
|
-
if (sgr !== lastSGR) {
|
|
1731
|
-
out += sgr;
|
|
1732
|
-
lastSGR = sgr;
|
|
1733
|
-
}
|
|
1734
|
-
out += nc.ch;
|
|
1735
|
-
lastX = x + 1;
|
|
1736
|
-
lastY = y;
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
if (out.length > 0) {
|
|
1740
|
-
out += `${CSI2}0m`;
|
|
1741
|
-
}
|
|
1742
|
-
return out;
|
|
1743
|
-
}
|
|
1744
|
-
var FLEX_DIR_MAP = {
|
|
1745
|
-
row: FlexDirection.Row,
|
|
1746
|
-
column: FlexDirection.Column
|
|
1747
|
-
};
|
|
1748
|
-
var JUSTIFY_MAP = {
|
|
1749
|
-
"flex-start": Justify.FlexStart,
|
|
1750
|
-
center: Justify.Center,
|
|
1751
|
-
"flex-end": Justify.FlexEnd,
|
|
1752
|
-
"space-between": Justify.SpaceBetween,
|
|
1753
|
-
"space-around": Justify.SpaceAround
|
|
1754
|
-
};
|
|
1755
|
-
var ALIGN_MAP = {
|
|
1756
|
-
"flex-start": Align.FlexStart,
|
|
1757
|
-
center: Align.Center,
|
|
1758
|
-
"flex-end": Align.FlexEnd,
|
|
1759
|
-
stretch: Align.Stretch
|
|
1760
|
-
};
|
|
1761
|
-
function setDimension(node, setter, value) {
|
|
1762
|
-
if (value === void 0) return;
|
|
1763
|
-
if (typeof value === "string" && value.endsWith("%")) {
|
|
1764
|
-
setter(value);
|
|
1765
|
-
} else {
|
|
1766
|
-
setter(value);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
function setPosition(node, edge, value) {
|
|
1770
|
-
if (value === void 0) return;
|
|
1771
|
-
if (typeof value === "string" && value.endsWith("%")) {
|
|
1772
|
-
node.setPositionPercent(edge, parseFloat(value));
|
|
1773
|
-
} else {
|
|
1774
|
-
node.setPosition(edge, value);
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
function applyStyleToYogaNode(yogaNode, style, nodeType) {
|
|
1778
|
-
setDimension(yogaNode, (v) => yogaNode.setWidth(v), style.width);
|
|
1779
|
-
setDimension(yogaNode, (v) => yogaNode.setHeight(v), style.height);
|
|
1780
|
-
if (style.minWidth !== void 0) yogaNode.setMinWidth(style.minWidth);
|
|
1781
|
-
if (style.minHeight !== void 0) yogaNode.setMinHeight(style.minHeight);
|
|
1782
|
-
if (style.maxWidth !== void 0) yogaNode.setMaxWidth(style.maxWidth);
|
|
1783
|
-
if (style.maxHeight !== void 0) yogaNode.setMaxHeight(style.maxHeight);
|
|
1784
|
-
if (style.padding !== void 0) yogaNode.setPadding(Edge.All, style.padding);
|
|
1785
|
-
if (style.paddingX !== void 0) yogaNode.setPadding(Edge.Horizontal, style.paddingX);
|
|
1786
|
-
if (style.paddingY !== void 0) yogaNode.setPadding(Edge.Vertical, style.paddingY);
|
|
1787
|
-
if (style.paddingTop !== void 0) yogaNode.setPadding(Edge.Top, style.paddingTop);
|
|
1788
|
-
if (style.paddingRight !== void 0) yogaNode.setPadding(Edge.Right, style.paddingRight);
|
|
1789
|
-
if (style.paddingBottom !== void 0) yogaNode.setPadding(Edge.Bottom, style.paddingBottom);
|
|
1790
|
-
if (style.paddingLeft !== void 0) yogaNode.setPadding(Edge.Left, style.paddingLeft);
|
|
1791
|
-
const hasBorder = style.border != null && style.border !== "none";
|
|
1792
|
-
yogaNode.setBorder(Edge.All, hasBorder ? 1 : 0);
|
|
1793
|
-
if (style.flexDirection) {
|
|
1794
|
-
yogaNode.setFlexDirection(FLEX_DIR_MAP[style.flexDirection] ?? FlexDirection.Column);
|
|
1795
|
-
}
|
|
1796
|
-
if (style.flexWrap) {
|
|
1797
|
-
yogaNode.setFlexWrap(style.flexWrap === "wrap" ? Wrap.Wrap : Wrap.NoWrap);
|
|
1798
|
-
}
|
|
1799
|
-
if (style.justifyContent) {
|
|
1800
|
-
yogaNode.setJustifyContent(JUSTIFY_MAP[style.justifyContent] ?? Justify.FlexStart);
|
|
1801
|
-
}
|
|
1802
|
-
if (style.alignItems) {
|
|
1803
|
-
yogaNode.setAlignItems(ALIGN_MAP[style.alignItems] ?? Align.Stretch);
|
|
1804
|
-
}
|
|
1805
|
-
if (style.flexGrow !== void 0) yogaNode.setFlexGrow(style.flexGrow);
|
|
1806
|
-
if (style.flexShrink !== void 0) yogaNode.setFlexShrink(style.flexShrink);
|
|
1807
|
-
if (style.gap !== void 0) yogaNode.setGap(Gutter.All, style.gap);
|
|
1808
|
-
if (style.position === "absolute") {
|
|
1809
|
-
yogaNode.setPositionType(PositionType.Absolute);
|
|
1810
|
-
} else {
|
|
1811
|
-
yogaNode.setPositionType(PositionType.Relative);
|
|
1812
|
-
}
|
|
1813
|
-
if (style.inset !== void 0) {
|
|
1814
|
-
setPosition(yogaNode, Edge.Top, style.inset);
|
|
1815
|
-
setPosition(yogaNode, Edge.Right, style.inset);
|
|
1816
|
-
setPosition(yogaNode, Edge.Bottom, style.inset);
|
|
1817
|
-
setPosition(yogaNode, Edge.Left, style.inset);
|
|
1818
|
-
}
|
|
1819
|
-
setPosition(yogaNode, Edge.Top, style.top);
|
|
1820
|
-
setPosition(yogaNode, Edge.Right, style.right);
|
|
1821
|
-
setPosition(yogaNode, Edge.Bottom, style.bottom);
|
|
1822
|
-
setPosition(yogaNode, Edge.Left, style.left);
|
|
1823
|
-
if (style.clip) {
|
|
1824
|
-
yogaNode.setOverflow(Overflow.Hidden);
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
function buildYogaTree(node) {
|
|
1828
|
-
const yogaNode = Yoga.Node.create();
|
|
1829
|
-
node.yogaNode = yogaNode;
|
|
1830
|
-
applyStyleToYogaNode(yogaNode, node.style, node.type);
|
|
1831
|
-
if (node.type === "text" || node.type === "input") {
|
|
1832
|
-
yogaNode.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
1833
|
-
let text;
|
|
1834
|
-
if (node.type === "input") {
|
|
1835
|
-
text = node.props.value ?? node.props.defaultValue ?? node.props.placeholder ?? "";
|
|
1836
|
-
if (text.length === 0) text = " ";
|
|
1837
|
-
} else {
|
|
1838
|
-
text = collectAllText(node);
|
|
1839
|
-
}
|
|
1840
|
-
return measureText(
|
|
1841
|
-
text,
|
|
1842
|
-
width,
|
|
1843
|
-
widthMode,
|
|
1844
|
-
node.style.wrap ?? "wrap"
|
|
1845
|
-
);
|
|
1846
|
-
});
|
|
1847
|
-
} else {
|
|
1848
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
1849
|
-
const child = node.children[i];
|
|
1850
|
-
if (child.hidden) continue;
|
|
1851
|
-
buildYogaTree(child);
|
|
1852
|
-
yogaNode.insertChild(child.yogaNode, yogaNode.getChildCount());
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
function collectAllText(node) {
|
|
1857
|
-
if (node.text != null) return node.text;
|
|
1858
|
-
let result = "";
|
|
1859
|
-
for (const child of node.children) {
|
|
1860
|
-
result += collectAllText(child);
|
|
1861
|
-
}
|
|
1862
|
-
if (result === "" && node.props.children != null) {
|
|
1863
|
-
if (typeof node.props.children === "string") return node.props.children;
|
|
1864
|
-
if (typeof node.props.children === "number") return String(node.props.children);
|
|
1865
|
-
}
|
|
1866
|
-
return result;
|
|
1867
|
-
}
|
|
1868
|
-
function extractLayout(node, parentX, parentY) {
|
|
1869
|
-
const yn = node.yogaNode;
|
|
1870
|
-
const computedLayout = yn.getComputedLayout();
|
|
1871
|
-
const x = parentX + computedLayout.left;
|
|
1872
|
-
const y = parentY + computedLayout.top;
|
|
1873
|
-
const width = computedLayout.width;
|
|
1874
|
-
const height = computedLayout.height;
|
|
1875
|
-
const borderWidth = node.style.border && node.style.border !== "none" ? 1 : 0;
|
|
1876
|
-
const paddingTop = yn.getComputedPadding(Edge.Top);
|
|
1877
|
-
const paddingRight = yn.getComputedPadding(Edge.Right);
|
|
1878
|
-
const paddingBottom = yn.getComputedPadding(Edge.Bottom);
|
|
1879
|
-
const paddingLeft = yn.getComputedPadding(Edge.Left);
|
|
1880
|
-
const innerX = x + borderWidth + paddingLeft;
|
|
1881
|
-
const innerY = y + borderWidth + paddingTop;
|
|
1882
|
-
const innerWidth = Math.max(0, width - borderWidth * 2 - paddingLeft - paddingRight);
|
|
1883
|
-
const innerHeight = Math.max(0, height - borderWidth * 2 - paddingTop - paddingBottom);
|
|
1884
|
-
node.layout = { x, y, width, height, innerX, innerY, innerWidth, innerHeight };
|
|
1885
|
-
for (const child of node.children) {
|
|
1886
|
-
if (child.hidden || !child.yogaNode) continue;
|
|
1887
|
-
extractLayout(child, x, y);
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
function computeLayout(roots, screenWidth, screenHeight) {
|
|
1891
|
-
const rootYoga = Yoga.Node.create();
|
|
1892
|
-
rootYoga.setWidth(screenWidth);
|
|
1893
|
-
rootYoga.setHeight(screenHeight);
|
|
1894
|
-
rootYoga.setFlexDirection(FlexDirection.Column);
|
|
1895
|
-
for (const child of roots) {
|
|
1896
|
-
if (child.hidden) continue;
|
|
1897
|
-
buildYogaTree(child);
|
|
1898
|
-
rootYoga.insertChild(child.yogaNode, rootYoga.getChildCount());
|
|
1899
|
-
}
|
|
1900
|
-
rootYoga.calculateLayout(screenWidth, screenHeight, Direction.LTR);
|
|
1901
|
-
for (const child of roots) {
|
|
1902
|
-
if (child.hidden || !child.yogaNode) continue;
|
|
1903
|
-
extractLayout(child, 0, 0);
|
|
1904
|
-
}
|
|
1905
|
-
rootYoga.freeRecursive();
|
|
1906
|
-
clearYogaRefs(roots);
|
|
1907
|
-
}
|
|
1908
|
-
function clearYogaRefs(nodes) {
|
|
1909
|
-
for (const node of nodes) {
|
|
1910
|
-
node.yogaNode = null;
|
|
1911
|
-
clearYogaRefs(node.children);
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
var InputContext = createContext(null);
|
|
1915
|
-
var FocusContext = createContext(null);
|
|
1916
|
-
var LayoutContext = createContext(null);
|
|
1917
|
-
var AppContext = createContext(null);
|
|
1918
|
-
|
|
1919
|
-
// src/render.ts
|
|
1920
|
-
function render(element, opts = {}) {
|
|
1921
|
-
const stdout = opts.stdout ?? process.stdout;
|
|
1922
|
-
const stdin = opts.stdin ?? process.stdin;
|
|
1923
|
-
const debug = opts.debug ?? false;
|
|
1924
|
-
const useNativeCursor = opts.useNativeCursor ?? true;
|
|
1925
|
-
const terminal = new Terminal(stdout, stdin);
|
|
1926
|
-
terminal.setup();
|
|
1927
|
-
let nativeCursorVisible = false;
|
|
1928
|
-
terminal.queryPalette().then((palette) => {
|
|
1929
|
-
setTerminalPalette(palette);
|
|
1930
|
-
fullRedraw = true;
|
|
1931
|
-
scheduleRender();
|
|
1932
|
-
});
|
|
1933
|
-
const prevFb = new Framebuffer(terminal.columns, terminal.rows);
|
|
1934
|
-
const currentFb = new Framebuffer(terminal.columns, terminal.rows);
|
|
1935
|
-
let fullRedraw = true;
|
|
1936
|
-
const inputHandlers = /* @__PURE__ */ new Set();
|
|
1937
|
-
const priorityHandlers = /* @__PURE__ */ new Set();
|
|
1938
|
-
const focusedInputHandlers = /* @__PURE__ */ new Map();
|
|
1939
|
-
const inputContextValue = {
|
|
1940
|
-
subscribe(handler) {
|
|
1941
|
-
inputHandlers.add(handler);
|
|
1942
|
-
return () => inputHandlers.delete(handler);
|
|
1943
|
-
},
|
|
1944
|
-
subscribePriority(handler) {
|
|
1945
|
-
priorityHandlers.add(handler);
|
|
1946
|
-
return () => priorityHandlers.delete(handler);
|
|
1947
|
-
},
|
|
1948
|
-
registerInputHandler(focusId, handler) {
|
|
1949
|
-
focusedInputHandlers.set(focusId, handler);
|
|
1950
|
-
return () => focusedInputHandlers.delete(focusId);
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
|
-
let focusedId = null;
|
|
1954
|
-
const focusRegistry = /* @__PURE__ */ new Map();
|
|
1955
|
-
const focusOrder = [];
|
|
1956
|
-
const skippableIds = /* @__PURE__ */ new Set();
|
|
1957
|
-
let trapStack = [];
|
|
1958
|
-
const focusChangeHandlers = /* @__PURE__ */ new Set();
|
|
1959
|
-
function setFocusedId(id) {
|
|
1960
|
-
if (focusedId !== id) {
|
|
1961
|
-
focusedId = id;
|
|
1962
|
-
scheduleRender();
|
|
1963
|
-
for (const handler of focusChangeHandlers) {
|
|
1964
|
-
handler(focusedId);
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
function getActiveFocusableIds() {
|
|
1969
|
-
let ids = [...focusOrder];
|
|
1970
|
-
if (trapStack.length > 0) {
|
|
1971
|
-
const trap = trapStack[trapStack.length - 1];
|
|
1972
|
-
ids = ids.filter((id) => trap.has(id));
|
|
1973
|
-
}
|
|
1974
|
-
ids = ids.filter((id) => !skippableIds.has(id));
|
|
1975
|
-
ids.sort((a, b) => {
|
|
1976
|
-
const nodeA = focusRegistry.get(a);
|
|
1977
|
-
const nodeB = focusRegistry.get(b);
|
|
1978
|
-
if (!nodeA || !nodeB) return 0;
|
|
1979
|
-
const layoutA = nodeA.layout;
|
|
1980
|
-
const layoutB = nodeB.layout;
|
|
1981
|
-
if (layoutA.y !== layoutB.y) {
|
|
1982
|
-
return layoutA.y - layoutB.y;
|
|
1983
|
-
}
|
|
1984
|
-
return layoutA.x - layoutB.x;
|
|
1985
|
-
});
|
|
1986
|
-
return ids;
|
|
1987
|
-
}
|
|
1988
|
-
const focusContextValue = {
|
|
1989
|
-
get focusedId() {
|
|
1990
|
-
return focusedId;
|
|
1991
|
-
},
|
|
1992
|
-
register(id, node) {
|
|
1993
|
-
focusRegistry.set(id, node);
|
|
1994
|
-
if (!focusOrder.includes(id)) {
|
|
1995
|
-
focusOrder.push(id);
|
|
1996
|
-
}
|
|
1997
|
-
if (trapStack.length > 0) {
|
|
1998
|
-
trapStack[trapStack.length - 1].add(id);
|
|
1999
|
-
}
|
|
2000
|
-
if (focusedId === null) {
|
|
2001
|
-
const activeIds = getActiveFocusableIds();
|
|
2002
|
-
if (activeIds.length > 0) {
|
|
2003
|
-
setFocusedId(activeIds[0]);
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
return () => {
|
|
2007
|
-
focusRegistry.delete(id);
|
|
2008
|
-
const idx = focusOrder.indexOf(id);
|
|
2009
|
-
if (idx !== -1) focusOrder.splice(idx, 1);
|
|
2010
|
-
if (focusedId === id) {
|
|
2011
|
-
const activeIds = getActiveFocusableIds();
|
|
2012
|
-
setFocusedId(activeIds[0] ?? null);
|
|
2013
|
-
}
|
|
2014
|
-
};
|
|
2015
|
-
},
|
|
2016
|
-
requestFocus(id) {
|
|
2017
|
-
setFocusedId(id);
|
|
2018
|
-
},
|
|
2019
|
-
focusNext() {
|
|
2020
|
-
const ids = getActiveFocusableIds();
|
|
2021
|
-
if (ids.length === 0) return;
|
|
2022
|
-
const currentIdx = focusedId ? ids.indexOf(focusedId) : -1;
|
|
2023
|
-
const nextIdx = (currentIdx + 1) % ids.length;
|
|
2024
|
-
setFocusedId(ids[nextIdx]);
|
|
2025
|
-
},
|
|
2026
|
-
focusPrev() {
|
|
2027
|
-
const ids = getActiveFocusableIds();
|
|
2028
|
-
if (ids.length === 0) return;
|
|
2029
|
-
const currentIdx = focusedId ? ids.indexOf(focusedId) : 0;
|
|
2030
|
-
const prevIdx = (currentIdx - 1 + ids.length) % ids.length;
|
|
2031
|
-
setFocusedId(ids[prevIdx]);
|
|
2032
|
-
},
|
|
2033
|
-
setSkippable(id, skippable) {
|
|
2034
|
-
if (skippable) {
|
|
2035
|
-
skippableIds.add(id);
|
|
2036
|
-
if (focusedId === id) {
|
|
2037
|
-
const ids = getActiveFocusableIds();
|
|
2038
|
-
if (ids.length > 0) {
|
|
2039
|
-
setFocusedId(ids[0]);
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
} else {
|
|
2043
|
-
skippableIds.delete(id);
|
|
2044
|
-
}
|
|
2045
|
-
},
|
|
2046
|
-
trapIds: null,
|
|
2047
|
-
pushTrap(ids) {
|
|
2048
|
-
trapStack.push(ids);
|
|
2049
|
-
return () => {
|
|
2050
|
-
const idx = trapStack.indexOf(ids);
|
|
2051
|
-
if (idx !== -1) trapStack.splice(idx, 1);
|
|
2052
|
-
};
|
|
2053
|
-
},
|
|
2054
|
-
onFocusChange(handler) {
|
|
2055
|
-
focusChangeHandlers.add(handler);
|
|
2056
|
-
return () => {
|
|
2057
|
-
focusChangeHandlers.delete(handler);
|
|
2058
|
-
};
|
|
2059
|
-
},
|
|
2060
|
-
getRegisteredElements() {
|
|
2061
|
-
const result = [];
|
|
2062
|
-
for (const id of focusOrder) {
|
|
2063
|
-
if (skippableIds.has(id)) continue;
|
|
2064
|
-
const node = focusRegistry.get(id);
|
|
2065
|
-
if (node) {
|
|
2066
|
-
result.push({ id, node });
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2069
|
-
return result;
|
|
2070
|
-
},
|
|
2071
|
-
getActiveElements() {
|
|
2072
|
-
const activeIds = getActiveFocusableIds();
|
|
2073
|
-
const result = [];
|
|
2074
|
-
for (const id of activeIds) {
|
|
2075
|
-
if (skippableIds.has(id)) continue;
|
|
2076
|
-
const node = focusRegistry.get(id);
|
|
2077
|
-
if (node) {
|
|
2078
|
-
result.push({ id, node });
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
return result;
|
|
2082
|
-
}
|
|
2083
|
-
};
|
|
2084
|
-
const layoutSubscriptions = /* @__PURE__ */ new Map();
|
|
2085
|
-
const layoutContextValue = {
|
|
2086
|
-
getLayout(node) {
|
|
2087
|
-
return node.layout;
|
|
2088
|
-
},
|
|
2089
|
-
subscribe(node, handler) {
|
|
2090
|
-
if (!layoutSubscriptions.has(node)) {
|
|
2091
|
-
layoutSubscriptions.set(node, /* @__PURE__ */ new Set());
|
|
2092
|
-
}
|
|
2093
|
-
layoutSubscriptions.get(node).add(handler);
|
|
2094
|
-
return () => {
|
|
2095
|
-
const subs = layoutSubscriptions.get(node);
|
|
2096
|
-
if (subs) {
|
|
2097
|
-
subs.delete(handler);
|
|
2098
|
-
if (subs.size === 0) layoutSubscriptions.delete(node);
|
|
2099
|
-
}
|
|
2100
|
-
};
|
|
2101
|
-
}
|
|
2102
|
-
};
|
|
2103
|
-
const appContextValue = {
|
|
2104
|
-
registerNode() {
|
|
2105
|
-
},
|
|
2106
|
-
unregisterNode() {
|
|
2107
|
-
},
|
|
2108
|
-
scheduleRender,
|
|
2109
|
-
exit(code) {
|
|
2110
|
-
handle.exit(code);
|
|
2111
|
-
},
|
|
2112
|
-
get columns() {
|
|
2113
|
-
return terminal.columns;
|
|
2114
|
-
},
|
|
2115
|
-
get rows() {
|
|
2116
|
-
return terminal.rows;
|
|
2117
|
-
}
|
|
2118
|
-
};
|
|
2119
|
-
const container = {
|
|
2120
|
-
type: "root",
|
|
2121
|
-
children: [],
|
|
2122
|
-
onCommit() {
|
|
2123
|
-
scheduleRender();
|
|
2124
|
-
}
|
|
2125
|
-
};
|
|
2126
|
-
let renderScheduled = false;
|
|
2127
|
-
function scheduleRender() {
|
|
2128
|
-
if (renderScheduled) return;
|
|
2129
|
-
renderScheduled = true;
|
|
2130
|
-
queueMicrotask(() => {
|
|
2131
|
-
renderScheduled = false;
|
|
2132
|
-
performRender();
|
|
2133
|
-
});
|
|
2134
|
-
}
|
|
2135
|
-
function performRender() {
|
|
2136
|
-
const cols = terminal.columns;
|
|
2137
|
-
const rows = terminal.rows;
|
|
2138
|
-
if (currentFb.width !== cols || currentFb.height !== rows) {
|
|
2139
|
-
currentFb.resize(cols, rows);
|
|
2140
|
-
prevFb.resize(cols, rows);
|
|
2141
|
-
fullRedraw = true;
|
|
2142
|
-
}
|
|
2143
|
-
computeLayout(container.children, cols, rows);
|
|
2144
|
-
notifyLayoutSubscribers(container.children);
|
|
2145
|
-
let cursorInfo;
|
|
2146
|
-
if (focusedId) {
|
|
2147
|
-
const focusedNode = focusRegistry.get(focusedId);
|
|
2148
|
-
if (focusedNode?.type === "input") {
|
|
2149
|
-
cursorInfo = {
|
|
2150
|
-
nodeId: focusedId,
|
|
2151
|
-
position: focusedNode.props.cursorPosition ?? (focusedNode.props.value?.length ?? 0)
|
|
2152
|
-
};
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
const paintResult = paintTree(container.children, currentFb, {
|
|
2156
|
-
cursorInfo,
|
|
2157
|
-
useNativeCursor
|
|
2158
|
-
});
|
|
2159
|
-
const output = diffFramebuffers(prevFb, currentFb, fullRedraw);
|
|
2160
|
-
if (output.length > 0) {
|
|
2161
|
-
terminal.write(output);
|
|
2162
|
-
}
|
|
2163
|
-
if (useNativeCursor) {
|
|
2164
|
-
if (paintResult.cursorPosition) {
|
|
2165
|
-
const cursorColor = getContrastCursorColor(paintResult.cursorPosition.bg);
|
|
2166
|
-
terminal.setCursorColor(cursorColor);
|
|
2167
|
-
terminal.moveCursor(paintResult.cursorPosition.x, paintResult.cursorPosition.y);
|
|
2168
|
-
if (!nativeCursorVisible) {
|
|
2169
|
-
terminal.showCursor();
|
|
2170
|
-
nativeCursorVisible = true;
|
|
2171
|
-
}
|
|
2172
|
-
} else {
|
|
2173
|
-
if (nativeCursorVisible) {
|
|
2174
|
-
terminal.hideCursor();
|
|
2175
|
-
nativeCursorVisible = false;
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
for (let i = 0; i < currentFb.cells.length; i++) {
|
|
2180
|
-
prevFb.cells[i] = { ...currentFb.cells[i] };
|
|
2181
|
-
}
|
|
2182
|
-
fullRedraw = false;
|
|
2183
|
-
}
|
|
2184
|
-
function notifyLayoutSubscribers(nodes) {
|
|
2185
|
-
for (const node of nodes) {
|
|
2186
|
-
const subs = layoutSubscriptions.get(node);
|
|
2187
|
-
if (subs) {
|
|
2188
|
-
for (const handler of subs) {
|
|
2189
|
-
handler(node.layout);
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
notifyLayoutSubscribers(node.children);
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
const removeDataListener = terminal.onData((data) => {
|
|
2196
|
-
const keys = parseKeySequence(data);
|
|
2197
|
-
for (const key of keys) {
|
|
2198
|
-
if (key.ctrl && key.name === "c") {
|
|
2199
|
-
handle.exit();
|
|
2200
|
-
return;
|
|
2201
|
-
}
|
|
2202
|
-
if (key.ctrl && key.name === "z") {
|
|
2203
|
-
terminal.suspend();
|
|
2204
|
-
process.kill(0, "SIGSTOP");
|
|
2205
|
-
return;
|
|
2206
|
-
}
|
|
2207
|
-
if (key.name === "tab" && !key.ctrl && !key.alt) {
|
|
2208
|
-
if (key.shift) {
|
|
2209
|
-
focusContextValue.focusPrev();
|
|
2210
|
-
} else {
|
|
2211
|
-
focusContextValue.focusNext();
|
|
2212
|
-
}
|
|
2213
|
-
continue;
|
|
2214
|
-
}
|
|
2215
|
-
let consumed = false;
|
|
2216
|
-
for (const handler of priorityHandlers) {
|
|
2217
|
-
if (handler(key)) {
|
|
2218
|
-
consumed = true;
|
|
2219
|
-
break;
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
if (!consumed && focusedId) {
|
|
2223
|
-
const inputHandler = focusedInputHandlers.get(focusedId);
|
|
2224
|
-
if (inputHandler) {
|
|
2225
|
-
consumed = inputHandler(key);
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
if (!consumed) {
|
|
2229
|
-
for (const handler of inputHandlers) {
|
|
2230
|
-
handler(key);
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
});
|
|
2235
|
-
const removeResizeListener = terminal.onResize(() => {
|
|
2236
|
-
fullRedraw = true;
|
|
2237
|
-
scheduleRender();
|
|
2238
|
-
});
|
|
2239
|
-
const handleSigcont = () => {
|
|
2240
|
-
terminal.resume();
|
|
2241
|
-
fullRedraw = true;
|
|
2242
|
-
scheduleRender();
|
|
2243
|
-
};
|
|
2244
|
-
process.on("SIGCONT", handleSigcont);
|
|
2245
|
-
const wrappedElement = React15.createElement(
|
|
2246
|
-
AppContext.Provider,
|
|
2247
|
-
{ value: appContextValue },
|
|
2248
|
-
React15.createElement(
|
|
2249
|
-
InputContext.Provider,
|
|
2250
|
-
{ value: inputContextValue },
|
|
2251
|
-
React15.createElement(
|
|
2252
|
-
FocusContext.Provider,
|
|
2253
|
-
{ value: focusContextValue },
|
|
2254
|
-
React15.createElement(
|
|
2255
|
-
LayoutContext.Provider,
|
|
2256
|
-
{ value: layoutContextValue },
|
|
2257
|
-
element
|
|
2258
|
-
)
|
|
2259
|
-
)
|
|
2260
|
-
)
|
|
2261
|
-
);
|
|
2262
|
-
const onUncaughtError = (error) => {
|
|
2263
|
-
if (debug) console.error("Uncaught error:", error);
|
|
2264
|
-
};
|
|
2265
|
-
const onCaughtError = (error) => {
|
|
2266
|
-
if (debug) console.error("Error caught by boundary:", error);
|
|
2267
|
-
};
|
|
2268
|
-
const onRecoverableError = (error) => {
|
|
2269
|
-
if (debug) console.error("Recoverable error:", error);
|
|
2270
|
-
};
|
|
2271
|
-
const root = reconciler.createContainer(
|
|
2272
|
-
container,
|
|
2273
|
-
0,
|
|
2274
|
-
// LegacyRoot tag
|
|
2275
|
-
null,
|
|
2276
|
-
// hydrationCallbacks
|
|
2277
|
-
false,
|
|
2278
|
-
// isStrictMode
|
|
2279
|
-
null,
|
|
2280
|
-
// concurrentUpdatesByDefaultOverride
|
|
2281
|
-
"",
|
|
2282
|
-
// identifierPrefix
|
|
2283
|
-
onUncaughtError,
|
|
2284
|
-
onCaughtError,
|
|
2285
|
-
onRecoverableError,
|
|
2286
|
-
null
|
|
2287
|
-
// transitionCallbacks
|
|
2288
|
-
);
|
|
2289
|
-
reconciler.updateContainer(wrappedElement, root, null, null);
|
|
2290
|
-
const handle = {
|
|
2291
|
-
unmount() {
|
|
2292
|
-
reconciler.updateContainer(null, root, null, null);
|
|
2293
|
-
removeDataListener();
|
|
2294
|
-
removeResizeListener();
|
|
2295
|
-
process.off("SIGCONT", handleSigcont);
|
|
2296
|
-
terminal.cleanup();
|
|
2297
|
-
},
|
|
2298
|
-
exit(code) {
|
|
2299
|
-
handle.unmount();
|
|
2300
|
-
process.exit(code ?? 0);
|
|
2301
|
-
}
|
|
2302
|
-
};
|
|
2303
|
-
return handle;
|
|
2304
|
-
}
|
|
2305
|
-
var Box = forwardRef(
|
|
2306
|
-
function Box2({ children, style, focusable }, ref) {
|
|
2307
|
-
return React15.createElement("box", { style, focusable, ref }, children);
|
|
2308
|
-
}
|
|
2309
|
-
);
|
|
2310
|
-
var Text = forwardRef(
|
|
2311
|
-
function Text2({ children, style, wrap }, ref) {
|
|
2312
|
-
const mergedStyle = wrap ? { ...style, wrap } : style;
|
|
2313
|
-
return React15.createElement("text", { style: mergedStyle, ref }, children);
|
|
2314
|
-
}
|
|
2315
|
-
);
|
|
2316
|
-
function cursorToVisualLine(text, pos, width) {
|
|
2317
|
-
if (width <= 0) {
|
|
2318
|
-
return { visualLine: 0, visualCol: pos, totalVisualLines: 1, lineStartOffset: 0, lineLength: text.length };
|
|
2319
|
-
}
|
|
2320
|
-
const logicalLines = text.split("\n");
|
|
2321
|
-
const allVisualLines = [];
|
|
2322
|
-
let logicalOffset = 0;
|
|
2323
|
-
for (const logicalLine of logicalLines) {
|
|
2324
|
-
const wrapped = wrapLines([logicalLine], width, "wrap");
|
|
2325
|
-
let offsetInLogical = 0;
|
|
2326
|
-
for (const wrappedLine of wrapped) {
|
|
2327
|
-
allVisualLines.push({
|
|
2328
|
-
text: wrappedLine,
|
|
2329
|
-
logicalOffset: logicalOffset + offsetInLogical
|
|
2330
|
-
});
|
|
2331
|
-
offsetInLogical += wrappedLine.length;
|
|
2332
|
-
}
|
|
2333
|
-
logicalOffset += logicalLine.length + 1;
|
|
2334
|
-
}
|
|
2335
|
-
let charCount = 0;
|
|
2336
|
-
for (let i = 0; i < allVisualLines.length; i++) {
|
|
2337
|
-
const vl = allVisualLines[i];
|
|
2338
|
-
const lineLen = vl.text.length;
|
|
2339
|
-
const isEndOfLogicalLine = i + 1 < allVisualLines.length && allVisualLines[i + 1].logicalOffset !== vl.logicalOffset + lineLen;
|
|
2340
|
-
const effectiveLen = lineLen + (isEndOfLogicalLine ? 1 : 0);
|
|
2341
|
-
if (pos < charCount + lineLen || i === allVisualLines.length - 1) {
|
|
2342
|
-
return {
|
|
2343
|
-
visualLine: i,
|
|
2344
|
-
visualCol: Math.min(pos - charCount, lineLen),
|
|
2345
|
-
totalVisualLines: allVisualLines.length,
|
|
2346
|
-
lineStartOffset: charCount,
|
|
2347
|
-
lineLength: lineLen
|
|
2348
|
-
};
|
|
2349
|
-
}
|
|
2350
|
-
charCount += effectiveLen;
|
|
2351
|
-
}
|
|
2352
|
-
const lastIdx = allVisualLines.length - 1;
|
|
2353
|
-
return {
|
|
2354
|
-
visualLine: lastIdx,
|
|
2355
|
-
visualCol: allVisualLines[lastIdx].text.length,
|
|
2356
|
-
totalVisualLines: allVisualLines.length,
|
|
2357
|
-
lineStartOffset: charCount - allVisualLines[lastIdx].text.length,
|
|
2358
|
-
lineLength: allVisualLines[lastIdx].text.length
|
|
2359
|
-
};
|
|
2360
|
-
}
|
|
2361
|
-
function visualLineToCursor(text, visualLine, visualCol, width) {
|
|
2362
|
-
if (width <= 0) {
|
|
2363
|
-
return Math.min(visualCol, text.length);
|
|
2364
|
-
}
|
|
2365
|
-
const logicalLines = text.split("\n");
|
|
2366
|
-
const allVisualLines = [];
|
|
2367
|
-
let offset = 0;
|
|
2368
|
-
for (const logicalLine of logicalLines) {
|
|
2369
|
-
const wrapped = wrapLines([logicalLine], width, "wrap");
|
|
2370
|
-
let offsetInLogical = 0;
|
|
2371
|
-
for (const wrappedLine of wrapped) {
|
|
2372
|
-
allVisualLines.push({
|
|
2373
|
-
text: wrappedLine,
|
|
2374
|
-
startOffset: offset + offsetInLogical
|
|
2375
|
-
});
|
|
2376
|
-
offsetInLogical += wrappedLine.length;
|
|
2377
|
-
}
|
|
2378
|
-
offset += logicalLine.length + 1;
|
|
2379
|
-
}
|
|
2380
|
-
const targetLine = Math.max(0, Math.min(visualLine, allVisualLines.length - 1));
|
|
2381
|
-
const vl = allVisualLines[targetLine];
|
|
2382
|
-
const col = Math.min(visualCol, vl.text.length);
|
|
2383
|
-
return vl.startOffset + col;
|
|
2384
|
-
}
|
|
2385
|
-
function cursorToLineCol(text, pos) {
|
|
2386
|
-
const lines = text.split("\n");
|
|
2387
|
-
let remaining = pos;
|
|
2388
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2389
|
-
if (remaining <= lines[i].length) {
|
|
2390
|
-
return { line: i, col: remaining, lines };
|
|
2391
|
-
}
|
|
2392
|
-
remaining -= lines[i].length + 1;
|
|
2393
|
-
}
|
|
2394
|
-
const last = lines.length - 1;
|
|
2395
|
-
return { line: last, col: lines[last].length, lines };
|
|
2396
|
-
}
|
|
2397
|
-
function lineColToCursor(lines, line, col) {
|
|
2398
|
-
let pos = 0;
|
|
2399
|
-
for (let i = 0; i < line && i < lines.length; i++) {
|
|
2400
|
-
pos += lines[i].length + 1;
|
|
2401
|
-
}
|
|
2402
|
-
return pos + Math.min(col, lines[line]?.length ?? 0);
|
|
2403
|
-
}
|
|
2404
|
-
function Input(props) {
|
|
2405
|
-
const {
|
|
2406
|
-
value: controlledValue,
|
|
2407
|
-
defaultValue = "",
|
|
2408
|
-
onChange,
|
|
2409
|
-
onKeyPress,
|
|
2410
|
-
onBeforeChange,
|
|
2411
|
-
placeholder,
|
|
2412
|
-
style,
|
|
2413
|
-
focusedStyle,
|
|
2414
|
-
multiline,
|
|
2415
|
-
autoFocus,
|
|
2416
|
-
type = "text"
|
|
2417
|
-
} = props;
|
|
2418
|
-
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
2419
|
-
const [cursorPos, setCursorPos] = useState(defaultValue.length);
|
|
2420
|
-
const [innerWidth, setInnerWidth] = useState(0);
|
|
2421
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
2422
|
-
const [nodeReady, setNodeReady] = useState(false);
|
|
2423
|
-
const inputCtx = useContext(InputContext);
|
|
2424
|
-
const focusCtx = useContext(FocusContext);
|
|
2425
|
-
const layoutCtx = useContext(LayoutContext);
|
|
2426
|
-
const nodeRef = useRef(null);
|
|
2427
|
-
const focusIdRef = useRef(null);
|
|
2428
|
-
const isControlled = controlledValue !== void 0;
|
|
2429
|
-
const value = isControlled ? controlledValue : internalValue;
|
|
2430
|
-
useEffect(() => {
|
|
2431
|
-
if (!layoutCtx || !nodeRef.current) return;
|
|
2432
|
-
const layout = layoutCtx.getLayout(nodeRef.current);
|
|
2433
|
-
setInnerWidth(layout.innerWidth);
|
|
2434
|
-
return layoutCtx.subscribe(nodeRef.current, (rect) => {
|
|
2435
|
-
setInnerWidth(rect.innerWidth);
|
|
2436
|
-
});
|
|
2437
|
-
}, [layoutCtx]);
|
|
2438
|
-
const workingValueRef = useRef(value);
|
|
2439
|
-
const workingCursorRef = useRef(cursorPos);
|
|
2440
|
-
useEffect(() => {
|
|
2441
|
-
workingValueRef.current = value;
|
|
2442
|
-
if (workingCursorRef.current > value.length) {
|
|
2443
|
-
workingCursorRef.current = value.length;
|
|
2444
|
-
setCursorPos(value.length);
|
|
2445
|
-
}
|
|
2446
|
-
}, [value]);
|
|
2447
|
-
useEffect(() => {
|
|
2448
|
-
workingCursorRef.current = cursorPos;
|
|
2449
|
-
}, [cursorPos]);
|
|
2450
|
-
const stateRef = useRef({
|
|
2451
|
-
isControlled,
|
|
2452
|
-
onChange,
|
|
2453
|
-
onKeyPress,
|
|
2454
|
-
onBeforeChange,
|
|
2455
|
-
multiline: multiline ?? false,
|
|
2456
|
-
innerWidth,
|
|
2457
|
-
type
|
|
2458
|
-
});
|
|
2459
|
-
stateRef.current = {
|
|
2460
|
-
isControlled,
|
|
2461
|
-
onChange,
|
|
2462
|
-
onKeyPress,
|
|
2463
|
-
onBeforeChange,
|
|
2464
|
-
multiline: multiline ?? false,
|
|
2465
|
-
innerWidth,
|
|
2466
|
-
type
|
|
2467
|
-
};
|
|
2468
|
-
useEffect(() => {
|
|
2469
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current) return;
|
|
2470
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
2471
|
-
}, [focusCtx, nodeReady]);
|
|
2472
|
-
const autoFocusedRef = useRef(false);
|
|
2473
|
-
useEffect(() => {
|
|
2474
|
-
if (autoFocus && !autoFocusedRef.current && focusCtx && focusIdRef.current) {
|
|
2475
|
-
autoFocusedRef.current = true;
|
|
2476
|
-
const fid = focusIdRef.current;
|
|
2477
|
-
queueMicrotask(() => {
|
|
2478
|
-
focusCtx.requestFocus(fid);
|
|
2479
|
-
});
|
|
2480
|
-
}
|
|
2481
|
-
}, [autoFocus, focusCtx, nodeReady]);
|
|
2482
|
-
useEffect(() => {
|
|
2483
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
2484
|
-
const fid = focusIdRef.current;
|
|
2485
|
-
setIsFocused(focusCtx.focusedId === fid);
|
|
2486
|
-
return focusCtx.onFocusChange((newId) => {
|
|
2487
|
-
setIsFocused(newId === fid);
|
|
2488
|
-
});
|
|
2489
|
-
}, [focusCtx, nodeReady]);
|
|
2490
|
-
useEffect(() => {
|
|
2491
|
-
if (!inputCtx || !focusIdRef.current) return;
|
|
2492
|
-
const fid = focusIdRef.current;
|
|
2493
|
-
const handler = (key) => {
|
|
2494
|
-
const {
|
|
2495
|
-
isControlled: ctrl,
|
|
2496
|
-
onChange: cb,
|
|
2497
|
-
onKeyPress: onKey,
|
|
2498
|
-
onBeforeChange: onBefore,
|
|
2499
|
-
multiline: ml
|
|
2500
|
-
} = stateRef.current;
|
|
2501
|
-
if (onKey?.(key) === true) {
|
|
2502
|
-
return true;
|
|
2503
|
-
}
|
|
2504
|
-
const val = workingValueRef.current;
|
|
2505
|
-
const pos = workingCursorRef.current;
|
|
2506
|
-
if (key.name === "escape") return false;
|
|
2507
|
-
const updateValue = (newVal, newCursor) => {
|
|
2508
|
-
let finalVal = newVal;
|
|
2509
|
-
let finalCursor = newCursor;
|
|
2510
|
-
if (onBefore) {
|
|
2511
|
-
const result = onBefore(newVal, val);
|
|
2512
|
-
if (result === false) {
|
|
2513
|
-
return;
|
|
2514
|
-
}
|
|
2515
|
-
if (typeof result === "string") {
|
|
2516
|
-
finalVal = result;
|
|
2517
|
-
finalCursor = result.length;
|
|
2518
|
-
}
|
|
2519
|
-
}
|
|
2520
|
-
workingValueRef.current = finalVal;
|
|
2521
|
-
workingCursorRef.current = finalCursor;
|
|
2522
|
-
if (!ctrl) setInternalValue(finalVal);
|
|
2523
|
-
cb?.(finalVal);
|
|
2524
|
-
setCursorPos(finalCursor);
|
|
2525
|
-
};
|
|
2526
|
-
const updateCursor = (newCursor) => {
|
|
2527
|
-
workingCursorRef.current = newCursor;
|
|
2528
|
-
setCursorPos(newCursor);
|
|
2529
|
-
};
|
|
2530
|
-
if (key.name === "return") {
|
|
2531
|
-
if (ml) {
|
|
2532
|
-
const newVal = val.slice(0, pos) + "\n" + val.slice(pos);
|
|
2533
|
-
updateValue(newVal, pos + 1);
|
|
2534
|
-
return true;
|
|
2535
|
-
}
|
|
2536
|
-
return false;
|
|
2537
|
-
}
|
|
2538
|
-
if (key.ctrl) {
|
|
2539
|
-
if (key.name === "w") {
|
|
2540
|
-
if (pos > 0) {
|
|
2541
|
-
let i = pos;
|
|
2542
|
-
while (i > 0 && val[i - 1] === " ") i--;
|
|
2543
|
-
while (i > 0 && val[i - 1] !== " " && (!ml || val[i - 1] !== "\n"))
|
|
2544
|
-
i--;
|
|
2545
|
-
const newVal = val.slice(0, i) + val.slice(pos);
|
|
2546
|
-
updateValue(newVal, i);
|
|
2547
|
-
}
|
|
2548
|
-
return true;
|
|
2549
|
-
}
|
|
2550
|
-
if (key.name === "a") {
|
|
2551
|
-
if (ml) {
|
|
2552
|
-
const { line, lines } = cursorToLineCol(val, pos);
|
|
2553
|
-
updateCursor(lineColToCursor(lines, line, 0));
|
|
2554
|
-
} else {
|
|
2555
|
-
updateCursor(0);
|
|
2556
|
-
}
|
|
2557
|
-
return true;
|
|
2558
|
-
}
|
|
2559
|
-
if (key.name === "e") {
|
|
2560
|
-
if (ml) {
|
|
2561
|
-
const { line, lines } = cursorToLineCol(val, pos);
|
|
2562
|
-
updateCursor(lineColToCursor(lines, line, lines[line].length));
|
|
2563
|
-
} else {
|
|
2564
|
-
updateCursor(val.length);
|
|
2565
|
-
}
|
|
2566
|
-
return true;
|
|
2567
|
-
}
|
|
2568
|
-
if (key.name === "k") {
|
|
2569
|
-
if (ml) {
|
|
2570
|
-
const { line, lines } = cursorToLineCol(val, pos);
|
|
2571
|
-
const lineEnd = lineColToCursor(lines, line, lines[line].length);
|
|
2572
|
-
if (pos < lineEnd) {
|
|
2573
|
-
const newVal = val.slice(0, pos) + val.slice(lineEnd);
|
|
2574
|
-
updateValue(newVal, pos);
|
|
2575
|
-
}
|
|
2576
|
-
} else {
|
|
2577
|
-
if (pos < val.length) {
|
|
2578
|
-
const newVal = val.slice(0, pos);
|
|
2579
|
-
updateValue(newVal, pos);
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
2582
|
-
return true;
|
|
2583
|
-
}
|
|
2584
|
-
return false;
|
|
2585
|
-
}
|
|
2586
|
-
if (key.alt) {
|
|
2587
|
-
if (key.name === "left" || key.name === "b") {
|
|
2588
|
-
let i = pos;
|
|
2589
|
-
while (i > 0 && val[i - 1] === " ") i--;
|
|
2590
|
-
while (i > 0 && val[i - 1] !== " " && val[i - 1] !== "\n") i--;
|
|
2591
|
-
updateCursor(i);
|
|
2592
|
-
return true;
|
|
2593
|
-
}
|
|
2594
|
-
if (key.name === "right" || key.name === "f") {
|
|
2595
|
-
let i = pos;
|
|
2596
|
-
while (i < val.length && val[i] !== " " && val[i] !== "\n") i++;
|
|
2597
|
-
while (i < val.length && val[i] === " ") i++;
|
|
2598
|
-
updateCursor(i);
|
|
2599
|
-
return true;
|
|
2600
|
-
}
|
|
2601
|
-
if (key.name === "backspace" || key.name === "d") {
|
|
2602
|
-
if (key.name === "backspace") {
|
|
2603
|
-
if (pos > 0) {
|
|
2604
|
-
let i = pos;
|
|
2605
|
-
while (i > 0 && val[i - 1] === " ") i--;
|
|
2606
|
-
while (i > 0 && val[i - 1] !== " " && val[i - 1] !== "\n") i--;
|
|
2607
|
-
const newVal = val.slice(0, i) + val.slice(pos);
|
|
2608
|
-
updateValue(newVal, i);
|
|
2609
|
-
}
|
|
2610
|
-
return true;
|
|
2611
|
-
} else {
|
|
2612
|
-
if (pos < val.length) {
|
|
2613
|
-
let i = pos;
|
|
2614
|
-
while (i < val.length && val[i] !== " " && val[i] !== "\n") i++;
|
|
2615
|
-
while (i < val.length && val[i] === " ") i++;
|
|
2616
|
-
const newVal = val.slice(0, pos) + val.slice(i);
|
|
2617
|
-
updateValue(newVal, pos);
|
|
2618
|
-
}
|
|
2619
|
-
return true;
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
return false;
|
|
2623
|
-
}
|
|
2624
|
-
if (key.name === "left") {
|
|
2625
|
-
updateCursor(Math.max(0, pos - 1));
|
|
2626
|
-
return true;
|
|
2627
|
-
}
|
|
2628
|
-
if (key.name === "right") {
|
|
2629
|
-
updateCursor(Math.min(val.length, pos + 1));
|
|
2630
|
-
return true;
|
|
2631
|
-
}
|
|
2632
|
-
if (key.name === "up") {
|
|
2633
|
-
const { innerWidth: w } = stateRef.current;
|
|
2634
|
-
const info = cursorToVisualLine(val, pos, w);
|
|
2635
|
-
if (info.visualLine > 0) {
|
|
2636
|
-
updateCursor(visualLineToCursor(val, info.visualLine - 1, info.visualCol, w));
|
|
2637
|
-
}
|
|
2638
|
-
return true;
|
|
2639
|
-
}
|
|
2640
|
-
if (key.name === "down") {
|
|
2641
|
-
const { innerWidth: w } = stateRef.current;
|
|
2642
|
-
const info = cursorToVisualLine(val, pos, w);
|
|
2643
|
-
if (info.visualLine < info.totalVisualLines - 1) {
|
|
2644
|
-
updateCursor(visualLineToCursor(val, info.visualLine + 1, info.visualCol, w));
|
|
2645
|
-
}
|
|
2646
|
-
return true;
|
|
2647
|
-
}
|
|
2648
|
-
if (key.name === "home") {
|
|
2649
|
-
if (ml) {
|
|
2650
|
-
const { line, lines } = cursorToLineCol(val, pos);
|
|
2651
|
-
updateCursor(lineColToCursor(lines, line, 0));
|
|
2652
|
-
} else {
|
|
2653
|
-
updateCursor(0);
|
|
2654
|
-
}
|
|
2655
|
-
return true;
|
|
2656
|
-
}
|
|
2657
|
-
if (key.name === "end") {
|
|
2658
|
-
if (ml) {
|
|
2659
|
-
const { line, lines } = cursorToLineCol(val, pos);
|
|
2660
|
-
updateCursor(lineColToCursor(lines, line, lines[line].length));
|
|
2661
|
-
} else {
|
|
2662
|
-
updateCursor(val.length);
|
|
2663
|
-
}
|
|
2664
|
-
return true;
|
|
2665
|
-
}
|
|
2666
|
-
if (key.name === "backspace") {
|
|
2667
|
-
if (pos > 0) {
|
|
2668
|
-
const newVal = val.slice(0, pos - 1) + val.slice(pos);
|
|
2669
|
-
updateValue(newVal, pos - 1);
|
|
2670
|
-
}
|
|
2671
|
-
return true;
|
|
2672
|
-
}
|
|
2673
|
-
if (key.name === "delete") {
|
|
2674
|
-
if (pos < val.length) {
|
|
2675
|
-
const newVal = val.slice(0, pos) + val.slice(pos + 1);
|
|
2676
|
-
updateValue(newVal, pos);
|
|
2677
|
-
}
|
|
2678
|
-
return true;
|
|
2679
|
-
}
|
|
2680
|
-
if (key.name.length > 1) return false;
|
|
2681
|
-
const ch = key.sequence;
|
|
2682
|
-
if (ch.length === 1 && ch.charCodeAt(0) >= 32) {
|
|
2683
|
-
const { type: inputType } = stateRef.current;
|
|
2684
|
-
if (inputType === "number") {
|
|
2685
|
-
const isDigit = /[0-9]/.test(ch);
|
|
2686
|
-
const isDecimal = ch === "." && !val.includes(".");
|
|
2687
|
-
const isMinus = ch === "-" && pos === 0 && !val.includes("-");
|
|
2688
|
-
if (!isDigit && !isDecimal && !isMinus) {
|
|
2689
|
-
return true;
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2692
|
-
const newVal = val.slice(0, pos) + ch + val.slice(pos);
|
|
2693
|
-
updateValue(newVal, pos + 1);
|
|
2694
|
-
return true;
|
|
2695
|
-
}
|
|
2696
|
-
return false;
|
|
2697
|
-
};
|
|
2698
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
2699
|
-
}, [inputCtx, nodeReady]);
|
|
2700
|
-
const mergedStyle = {
|
|
2701
|
-
...style,
|
|
2702
|
-
...isFocused && focusedStyle ? focusedStyle : {}
|
|
2703
|
-
};
|
|
2704
|
-
return React15.createElement("input", {
|
|
2705
|
-
style: mergedStyle,
|
|
2706
|
-
value,
|
|
2707
|
-
defaultValue,
|
|
2708
|
-
placeholder,
|
|
2709
|
-
onChange,
|
|
2710
|
-
cursorPosition: cursorPos,
|
|
2711
|
-
multiline: multiline ?? false,
|
|
2712
|
-
focused: isFocused,
|
|
2713
|
-
ref: (node) => {
|
|
2714
|
-
if (node) {
|
|
2715
|
-
nodeRef.current = node;
|
|
2716
|
-
focusIdRef.current = node.focusId;
|
|
2717
|
-
setNodeReady(true);
|
|
2718
|
-
} else {
|
|
2719
|
-
nodeRef.current = null;
|
|
2720
|
-
focusIdRef.current = null;
|
|
2721
|
-
setNodeReady(false);
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
});
|
|
2725
|
-
}
|
|
2726
|
-
function FocusScope({ trap = false, children }) {
|
|
2727
|
-
const focusCtx = useContext(FocusContext);
|
|
2728
|
-
const prevFocusRef = useRef(null);
|
|
2729
|
-
const scopeIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
2730
|
-
useLayoutEffect(() => {
|
|
2731
|
-
if (!trap || !focusCtx) return;
|
|
2732
|
-
prevFocusRef.current = focusCtx.focusedId;
|
|
2733
|
-
const cleanup = focusCtx.pushTrap(scopeIdsRef.current);
|
|
2734
|
-
return () => {
|
|
2735
|
-
cleanup();
|
|
2736
|
-
if (prevFocusRef.current) {
|
|
2737
|
-
focusCtx.requestFocus(prevFocusRef.current);
|
|
2738
|
-
}
|
|
2739
|
-
};
|
|
2740
|
-
}, [trap, focusCtx]);
|
|
2741
|
-
useEffect(() => {
|
|
2742
|
-
if (!trap || !focusCtx) return;
|
|
2743
|
-
if (scopeIdsRef.current.size > 0) {
|
|
2744
|
-
const firstId = scopeIdsRef.current.values().next().value;
|
|
2745
|
-
if (firstId) focusCtx.requestFocus(firstId);
|
|
2746
|
-
}
|
|
2747
|
-
}, [trap, focusCtx]);
|
|
2748
|
-
return React15.createElement(React15.Fragment, null, children);
|
|
2749
|
-
}
|
|
2750
|
-
function Spacer({ size }) {
|
|
2751
|
-
return React15.createElement("box", {
|
|
2752
|
-
style: { flexGrow: size ?? 1 }
|
|
2753
|
-
});
|
|
2754
|
-
}
|
|
2755
|
-
function parseKeyDescriptor(descriptor) {
|
|
2756
|
-
const parts = descriptor.toLowerCase().split("+");
|
|
2757
|
-
const name = parts[parts.length - 1];
|
|
2758
|
-
return {
|
|
2759
|
-
name,
|
|
2760
|
-
ctrl: parts.includes("ctrl"),
|
|
2761
|
-
alt: parts.includes("alt"),
|
|
2762
|
-
shift: parts.includes("shift"),
|
|
2763
|
-
meta: parts.includes("meta") || parts.includes("cmd") || parts.includes("super") || parts.includes("win")
|
|
2764
|
-
};
|
|
2765
|
-
}
|
|
2766
|
-
function matchesKey(matcher, key) {
|
|
2767
|
-
if (key.name !== matcher.name) return false;
|
|
2768
|
-
if (matcher.ctrl !== !!key.ctrl) return false;
|
|
2769
|
-
if (matcher.alt !== !!key.alt) return false;
|
|
2770
|
-
if (matcher.shift !== !!key.shift) return false;
|
|
2771
|
-
if (matcher.meta !== !!key.meta) return false;
|
|
2772
|
-
return true;
|
|
2773
|
-
}
|
|
2774
|
-
function Keybind({
|
|
2775
|
-
keypress,
|
|
2776
|
-
onPress,
|
|
2777
|
-
whenFocused,
|
|
2778
|
-
priority,
|
|
2779
|
-
disabled
|
|
2780
|
-
}) {
|
|
2781
|
-
const inputCtx = useContext(InputContext);
|
|
2782
|
-
const focusCtx = useContext(FocusContext);
|
|
2783
|
-
const onPressRef = useRef(onPress);
|
|
2784
|
-
onPressRef.current = onPress;
|
|
2785
|
-
const matcherRef = useRef(parseKeyDescriptor(keypress));
|
|
2786
|
-
matcherRef.current = parseKeyDescriptor(keypress);
|
|
2787
|
-
useEffect(() => {
|
|
2788
|
-
if (!inputCtx || disabled) return;
|
|
2789
|
-
if (priority) {
|
|
2790
|
-
const handler = (key) => {
|
|
2791
|
-
if (!matchesKey(matcherRef.current, key)) return false;
|
|
2792
|
-
if (whenFocused && focusCtx?.focusedId !== whenFocused) return false;
|
|
2793
|
-
onPressRef.current();
|
|
2794
|
-
return true;
|
|
2795
|
-
};
|
|
2796
|
-
return inputCtx.subscribePriority(handler);
|
|
2797
|
-
} else {
|
|
2798
|
-
const handler = (key) => {
|
|
2799
|
-
if (!matchesKey(matcherRef.current, key)) return;
|
|
2800
|
-
if (whenFocused && focusCtx?.focusedId !== whenFocused) return;
|
|
2801
|
-
onPressRef.current();
|
|
2802
|
-
};
|
|
2803
|
-
return inputCtx.subscribe(handler);
|
|
2804
|
-
}
|
|
2805
|
-
}, [inputCtx, focusCtx, whenFocused, priority, disabled]);
|
|
2806
|
-
return null;
|
|
2807
|
-
}
|
|
2808
|
-
function Portal({ children, zIndex = 1e3 }) {
|
|
2809
|
-
return React15.createElement(
|
|
2810
|
-
"box",
|
|
2811
|
-
{
|
|
2812
|
-
style: {
|
|
2813
|
-
position: "absolute",
|
|
2814
|
-
top: 0,
|
|
2815
|
-
left: 0,
|
|
2816
|
-
width: "100%",
|
|
2817
|
-
height: "100%",
|
|
2818
|
-
zIndex
|
|
2819
|
-
}
|
|
2820
|
-
},
|
|
2821
|
-
children
|
|
2822
|
-
);
|
|
2823
|
-
}
|
|
2824
|
-
function Button({
|
|
2825
|
-
onPress,
|
|
2826
|
-
style,
|
|
2827
|
-
focusedStyle,
|
|
2828
|
-
children,
|
|
2829
|
-
disabled
|
|
2830
|
-
}) {
|
|
2831
|
-
const focusCtx = useContext(FocusContext);
|
|
2832
|
-
const inputCtx = useContext(InputContext);
|
|
2833
|
-
const nodeRef = useRef(null);
|
|
2834
|
-
const focusIdRef = useRef(null);
|
|
2835
|
-
const onPressRef = useRef(onPress);
|
|
2836
|
-
onPressRef.current = onPress;
|
|
2837
|
-
const [nodeReady, setNodeReady] = useState(false);
|
|
2838
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
2839
|
-
useEffect(() => {
|
|
2840
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current || disabled) return;
|
|
2841
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
2842
|
-
}, [focusCtx, disabled, nodeReady]);
|
|
2843
|
-
useEffect(() => {
|
|
2844
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
2845
|
-
const fid = focusIdRef.current;
|
|
2846
|
-
setIsFocused(focusCtx.focusedId === fid);
|
|
2847
|
-
return focusCtx.onFocusChange((newId) => {
|
|
2848
|
-
setIsFocused(newId === fid);
|
|
2849
|
-
});
|
|
2850
|
-
}, [focusCtx, nodeReady]);
|
|
2851
|
-
useEffect(() => {
|
|
2852
|
-
if (!inputCtx || !focusIdRef.current || disabled) return;
|
|
2853
|
-
const fid = focusIdRef.current;
|
|
2854
|
-
const handler = (key) => {
|
|
2855
|
-
if (focusCtx?.focusedId !== fid) return false;
|
|
2856
|
-
if (key.name === "return" || key.name === " " || key.sequence === " ") {
|
|
2857
|
-
onPressRef.current?.();
|
|
2858
|
-
return true;
|
|
2859
|
-
}
|
|
2860
|
-
return false;
|
|
2861
|
-
};
|
|
2862
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
2863
|
-
}, [inputCtx, focusCtx, disabled, nodeReady]);
|
|
2864
|
-
const mergedStyle = {
|
|
2865
|
-
...style,
|
|
2866
|
-
...isFocused && focusedStyle ? focusedStyle : {}
|
|
2867
|
-
};
|
|
2868
|
-
return React15.createElement(
|
|
2869
|
-
"box",
|
|
2870
|
-
{
|
|
2871
|
-
style: mergedStyle,
|
|
2872
|
-
focusable: !disabled,
|
|
2873
|
-
ref: (node) => {
|
|
2874
|
-
if (node) {
|
|
2875
|
-
nodeRef.current = node;
|
|
2876
|
-
focusIdRef.current = node.focusId;
|
|
2877
|
-
setNodeReady(true);
|
|
2878
|
-
} else {
|
|
2879
|
-
nodeRef.current = null;
|
|
2880
|
-
focusIdRef.current = null;
|
|
2881
|
-
setNodeReady(false);
|
|
2882
|
-
}
|
|
2883
|
-
}
|
|
2884
|
-
},
|
|
2885
|
-
children
|
|
2886
|
-
);
|
|
2887
|
-
}
|
|
2888
|
-
var DEFAULT_RECT = {
|
|
2889
|
-
x: 0,
|
|
2890
|
-
y: 0,
|
|
2891
|
-
width: 0,
|
|
2892
|
-
height: 0,
|
|
2893
|
-
innerX: 0,
|
|
2894
|
-
innerY: 0,
|
|
2895
|
-
innerWidth: 0,
|
|
2896
|
-
innerHeight: 0
|
|
2897
|
-
};
|
|
2898
|
-
function useLayout(nodeRef) {
|
|
2899
|
-
const ctx = useContext(LayoutContext);
|
|
2900
|
-
const [layout, setLayout] = useState(DEFAULT_RECT);
|
|
2901
|
-
useEffect(() => {
|
|
2902
|
-
if (!ctx || !nodeRef?.current) return;
|
|
2903
|
-
setLayout(ctx.getLayout(nodeRef.current));
|
|
2904
|
-
return ctx.subscribe(nodeRef.current, setLayout);
|
|
2905
|
-
}, [ctx, nodeRef]);
|
|
2906
|
-
return layout;
|
|
2907
|
-
}
|
|
2908
|
-
function useInput(handler, deps = []) {
|
|
2909
|
-
const ctx = useContext(InputContext);
|
|
2910
|
-
useEffect(() => {
|
|
2911
|
-
if (!ctx) return;
|
|
2912
|
-
return ctx.subscribe(handler);
|
|
2913
|
-
}, [ctx, ...deps]);
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
// src/components/ScrollView.tsx
|
|
2917
|
-
function ScrollView({
|
|
2918
|
-
children,
|
|
2919
|
-
style,
|
|
2920
|
-
scrollOffset: controlledOffset,
|
|
2921
|
-
onScroll,
|
|
2922
|
-
defaultScrollOffset = 0,
|
|
2923
|
-
scrollStep = 1,
|
|
2924
|
-
disableKeyboard,
|
|
2925
|
-
scrollToFocus = true,
|
|
2926
|
-
showScrollbar = true,
|
|
2927
|
-
focusable = true,
|
|
2928
|
-
focusedStyle
|
|
2929
|
-
}) {
|
|
2930
|
-
const isControlled = controlledOffset !== void 0;
|
|
2931
|
-
const [internalOffset, setInternalOffset] = useState(defaultScrollOffset);
|
|
2932
|
-
const offset = isControlled ? controlledOffset : internalOffset;
|
|
2933
|
-
const viewportRef = useRef(null);
|
|
2934
|
-
const contentRef = useRef(null);
|
|
2935
|
-
const viewportLayout = useLayout(viewportRef);
|
|
2936
|
-
const contentLayout = useLayout(contentRef);
|
|
2937
|
-
const focusCtx = useContext(FocusContext);
|
|
2938
|
-
const layoutCtx = useContext(LayoutContext);
|
|
2939
|
-
const focusIdRef = useRef(null);
|
|
2940
|
-
if (focusable && !focusIdRef.current) {
|
|
2941
|
-
focusIdRef.current = `scrollview-${Math.random().toString(36).slice(2, 9)}`;
|
|
2942
|
-
}
|
|
2943
|
-
const focusId = focusable ? focusIdRef.current : null;
|
|
2944
|
-
useEffect(() => {
|
|
2945
|
-
if (!focusable || !focusId || !focusCtx || !viewportRef.current) return;
|
|
2946
|
-
return focusCtx.register(focusId, viewportRef.current);
|
|
2947
|
-
}, [focusable, focusId, focusCtx]);
|
|
2948
|
-
const isSelfFocused = focusable && focusId && focusCtx?.focusedId === focusId;
|
|
2949
|
-
const viewportHeight = viewportLayout.innerHeight;
|
|
2950
|
-
const contentHeight = contentLayout.height;
|
|
2951
|
-
const maxOffset = Math.max(0, contentHeight - viewportHeight);
|
|
2952
|
-
const effectiveOffset = Math.max(0, Math.min(offset, maxOffset));
|
|
2953
|
-
const setOffset = useCallback(
|
|
2954
|
-
(next) => {
|
|
2955
|
-
const clamped = Math.max(0, Math.min(next, maxOffset));
|
|
2956
|
-
if (isControlled) {
|
|
2957
|
-
onScroll?.(clamped);
|
|
2958
|
-
} else {
|
|
2959
|
-
setInternalOffset(clamped);
|
|
2960
|
-
}
|
|
2961
|
-
},
|
|
2962
|
-
[isControlled, onScroll, maxOffset]
|
|
2963
|
-
);
|
|
2964
|
-
useEffect(() => {
|
|
2965
|
-
if (offset > maxOffset && maxOffset >= 0) {
|
|
2966
|
-
setOffset(maxOffset);
|
|
2967
|
-
}
|
|
2968
|
-
}, [offset, maxOffset, setOffset]);
|
|
2969
|
-
useEffect(() => {
|
|
2970
|
-
if (!scrollToFocus || !focusCtx || !layoutCtx || !contentRef.current) return;
|
|
2971
|
-
const unsubscribe = focusCtx.onFocusChange((focusedId) => {
|
|
2972
|
-
if (!focusedId || !contentRef.current) return;
|
|
2973
|
-
const findNode = (node) => {
|
|
2974
|
-
if (node.focusId === focusedId) return node;
|
|
2975
|
-
for (const child of node.children) {
|
|
2976
|
-
const found = findNode(child);
|
|
2977
|
-
if (found) return found;
|
|
2978
|
-
}
|
|
2979
|
-
return null;
|
|
2980
|
-
};
|
|
2981
|
-
const focusedNode = findNode(contentRef.current);
|
|
2982
|
-
if (!focusedNode) return;
|
|
2983
|
-
const focusedLayout = layoutCtx.getLayout(focusedNode);
|
|
2984
|
-
const contentTopY = contentRef.current.layout?.y ?? 0;
|
|
2985
|
-
const elementTop = focusedLayout.y - contentTopY;
|
|
2986
|
-
const elementBottom = elementTop + focusedLayout.height;
|
|
2987
|
-
const visibleTop = offset;
|
|
2988
|
-
const visibleBottom = offset + viewportHeight;
|
|
2989
|
-
if (elementTop < visibleTop) {
|
|
2990
|
-
setOffset(elementTop);
|
|
2991
|
-
} else if (elementBottom > visibleBottom) {
|
|
2992
|
-
setOffset(elementBottom - viewportHeight);
|
|
2993
|
-
}
|
|
2994
|
-
});
|
|
2995
|
-
return unsubscribe;
|
|
2996
|
-
}, [scrollToFocus, focusCtx, layoutCtx, offset, viewportHeight, setOffset]);
|
|
2997
|
-
const containsFocus = useCallback(() => {
|
|
2998
|
-
if (!focusCtx) return false;
|
|
2999
|
-
const currentFocusId = focusCtx.focusedId;
|
|
3000
|
-
if (!currentFocusId) return false;
|
|
3001
|
-
if (focusable && focusId && currentFocusId === focusId) return true;
|
|
3002
|
-
if (!contentRef.current) return false;
|
|
3003
|
-
const findNode = (node) => {
|
|
3004
|
-
if (node.focusId === currentFocusId) return true;
|
|
3005
|
-
for (const child of node.children) {
|
|
3006
|
-
if (findNode(child)) return true;
|
|
3007
|
-
}
|
|
3008
|
-
return false;
|
|
3009
|
-
};
|
|
3010
|
-
return findNode(contentRef.current);
|
|
3011
|
-
}, [focusCtx, focusable, focusId]);
|
|
3012
|
-
useInput((key) => {
|
|
3013
|
-
if (disableKeyboard) return;
|
|
3014
|
-
if (!containsFocus()) return;
|
|
3015
|
-
const halfPage = Math.max(1, Math.floor(viewportHeight / 2));
|
|
3016
|
-
const fullPage = Math.max(1, viewportHeight);
|
|
3017
|
-
switch (key.name) {
|
|
3018
|
-
// Page keys - always safe, inputs don't use these
|
|
3019
|
-
case "pageup":
|
|
3020
|
-
setOffset(offset - fullPage);
|
|
3021
|
-
break;
|
|
3022
|
-
case "pagedown":
|
|
3023
|
-
setOffset(offset + fullPage);
|
|
3024
|
-
break;
|
|
3025
|
-
default:
|
|
3026
|
-
if (key.ctrl) {
|
|
3027
|
-
if (key.name === "d") {
|
|
3028
|
-
setOffset(offset + halfPage);
|
|
3029
|
-
} else if (key.name === "u") {
|
|
3030
|
-
setOffset(offset - halfPage);
|
|
3031
|
-
} else if (key.name === "f") {
|
|
3032
|
-
setOffset(offset + fullPage);
|
|
3033
|
-
} else if (key.name === "b") {
|
|
3034
|
-
setOffset(offset - fullPage);
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3037
|
-
break;
|
|
3038
|
-
}
|
|
3039
|
-
}, [offset, scrollStep, viewportHeight, maxOffset, disableKeyboard, setOffset, containsFocus]);
|
|
3040
|
-
const {
|
|
3041
|
-
padding: _pad,
|
|
3042
|
-
paddingX: _px,
|
|
3043
|
-
paddingY: _py,
|
|
3044
|
-
paddingTop: _pt,
|
|
3045
|
-
paddingRight: _pr,
|
|
3046
|
-
paddingBottom: _pb,
|
|
3047
|
-
paddingLeft: _pl,
|
|
3048
|
-
...styleRest
|
|
3049
|
-
} = style ?? {};
|
|
3050
|
-
const hasBorder = styleRest.border != null && styleRest.border !== "none";
|
|
3051
|
-
const borderHeight = hasBorder ? 2 : 0;
|
|
3052
|
-
const intrinsicHeight = contentHeight > 0 ? contentHeight + borderHeight : void 0;
|
|
3053
|
-
const outerStyle = {
|
|
3054
|
-
...styleRest,
|
|
3055
|
-
...isSelfFocused ? focusedStyle : {},
|
|
3056
|
-
clip: true,
|
|
3057
|
-
// Only set intrinsic height if user didn't set explicit height
|
|
3058
|
-
...styleRest.height === void 0 && intrinsicHeight !== void 0 ? {
|
|
3059
|
-
height: intrinsicHeight,
|
|
3060
|
-
flexShrink: styleRest.flexShrink ?? 1,
|
|
3061
|
-
minHeight: styleRest.minHeight ?? 0
|
|
3062
|
-
} : {}
|
|
3063
|
-
};
|
|
3064
|
-
const innerStyle = {
|
|
3065
|
-
position: "absolute",
|
|
3066
|
-
top: -effectiveOffset,
|
|
3067
|
-
left: 0,
|
|
3068
|
-
right: 0,
|
|
3069
|
-
flexDirection: "column",
|
|
3070
|
-
..._pad !== void 0 && { padding: _pad },
|
|
3071
|
-
..._px !== void 0 && { paddingX: _px },
|
|
3072
|
-
..._py !== void 0 && { paddingY: _py },
|
|
3073
|
-
..._pt !== void 0 && { paddingTop: _pt },
|
|
3074
|
-
..._pr !== void 0 && { paddingRight: _pr },
|
|
3075
|
-
..._pb !== void 0 && { paddingBottom: _pb },
|
|
3076
|
-
..._pl !== void 0 && { paddingLeft: _pl }
|
|
3077
|
-
};
|
|
3078
|
-
const isScrollable = contentHeight > viewportHeight && viewportHeight > 0;
|
|
3079
|
-
const scrollbarVisible = showScrollbar && isScrollable;
|
|
3080
|
-
const thumbHeight = Math.max(1, Math.floor(viewportHeight / contentHeight * viewportHeight));
|
|
3081
|
-
const scrollableRange = contentHeight - viewportHeight;
|
|
3082
|
-
const thumbPosition = scrollableRange > 0 ? Math.floor(effectiveOffset / scrollableRange * (viewportHeight - thumbHeight)) : 0;
|
|
3083
|
-
const scrollbarChars = [];
|
|
3084
|
-
if (scrollbarVisible) {
|
|
3085
|
-
for (let i = 0; i < viewportHeight; i++) {
|
|
3086
|
-
if (i >= thumbPosition && i < thumbPosition + thumbHeight) {
|
|
3087
|
-
scrollbarChars.push("\u2588");
|
|
3088
|
-
} else {
|
|
3089
|
-
scrollbarChars.push("\u2591");
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
const scrollbarStyle = {
|
|
3094
|
-
position: "absolute",
|
|
3095
|
-
top: 0,
|
|
3096
|
-
right: 0,
|
|
3097
|
-
width: 1,
|
|
3098
|
-
height: viewportHeight,
|
|
3099
|
-
flexDirection: "column"
|
|
3100
|
-
};
|
|
3101
|
-
return React15.createElement(
|
|
3102
|
-
"box",
|
|
3103
|
-
{
|
|
3104
|
-
style: outerStyle,
|
|
3105
|
-
ref: (node) => {
|
|
3106
|
-
viewportRef.current = node ?? null;
|
|
3107
|
-
},
|
|
3108
|
-
...focusable ? { focusable: true, focusId } : {}
|
|
3109
|
-
},
|
|
3110
|
-
// Content (absolutely positioned, scrolls via top offset)
|
|
3111
|
-
React15.createElement(
|
|
3112
|
-
"box",
|
|
3113
|
-
{
|
|
3114
|
-
style: {
|
|
3115
|
-
...innerStyle,
|
|
3116
|
-
// Reserve space for scrollbar when visible
|
|
3117
|
-
paddingRight: scrollbarVisible ? (innerStyle.paddingRight ?? 0) + 1 : innerStyle.paddingRight
|
|
3118
|
-
},
|
|
3119
|
-
ref: (node) => {
|
|
3120
|
-
contentRef.current = node ?? null;
|
|
3121
|
-
}
|
|
3122
|
-
},
|
|
3123
|
-
children
|
|
3124
|
-
),
|
|
3125
|
-
// Scrollbar
|
|
3126
|
-
scrollbarVisible && React15.createElement(
|
|
3127
|
-
"box",
|
|
3128
|
-
{ style: scrollbarStyle },
|
|
3129
|
-
React15.createElement(
|
|
3130
|
-
"text",
|
|
3131
|
-
{ style: { color: "blackBright" } },
|
|
3132
|
-
scrollbarChars.join("\n")
|
|
3133
|
-
)
|
|
3134
|
-
)
|
|
3135
|
-
);
|
|
3136
|
-
}
|
|
3137
|
-
function List({
|
|
3138
|
-
count,
|
|
3139
|
-
renderItem,
|
|
3140
|
-
selectedIndex: controlledIndex,
|
|
3141
|
-
onSelectionChange,
|
|
3142
|
-
onSelect,
|
|
3143
|
-
defaultSelectedIndex = 0,
|
|
3144
|
-
disabledIndices,
|
|
3145
|
-
style,
|
|
3146
|
-
focusable = true
|
|
3147
|
-
}) {
|
|
3148
|
-
const isControlled = controlledIndex !== void 0;
|
|
3149
|
-
const [internalIndex, setInternalIndex] = useState(defaultSelectedIndex);
|
|
3150
|
-
const selectedIndex = isControlled ? controlledIndex : internalIndex;
|
|
3151
|
-
const focusCtx = useContext(FocusContext);
|
|
3152
|
-
const inputCtx = useContext(InputContext);
|
|
3153
|
-
const nodeRef = useRef(null);
|
|
3154
|
-
const focusIdRef = useRef(null);
|
|
3155
|
-
const onSelectRef = useRef(onSelect);
|
|
3156
|
-
onSelectRef.current = onSelect;
|
|
3157
|
-
const [nodeReady, setNodeReady] = useState(false);
|
|
3158
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
3159
|
-
const lastKeyRef = useRef(null);
|
|
3160
|
-
const setIndex = useCallback(
|
|
3161
|
-
(next) => {
|
|
3162
|
-
const clamped = Math.max(0, Math.min(next, count - 1));
|
|
3163
|
-
if (isControlled) {
|
|
3164
|
-
onSelectionChange?.(clamped);
|
|
3165
|
-
} else {
|
|
3166
|
-
setInternalIndex(clamped);
|
|
3167
|
-
}
|
|
3168
|
-
},
|
|
3169
|
-
[isControlled, onSelectionChange, count]
|
|
3170
|
-
);
|
|
3171
|
-
const findNextEnabled = useCallback(
|
|
3172
|
-
(from, direction) => {
|
|
3173
|
-
if (!disabledIndices || disabledIndices.size === 0) {
|
|
3174
|
-
return Math.max(0, Math.min(from + direction, count - 1));
|
|
3175
|
-
}
|
|
3176
|
-
let next = from + direction;
|
|
3177
|
-
while (next >= 0 && next < count && disabledIndices.has(next)) {
|
|
3178
|
-
next += direction;
|
|
3179
|
-
}
|
|
3180
|
-
if (next < 0 || next >= count) return from;
|
|
3181
|
-
return next;
|
|
3182
|
-
},
|
|
3183
|
-
[disabledIndices, count]
|
|
3184
|
-
);
|
|
3185
|
-
useEffect(() => {
|
|
3186
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current || !focusable) return;
|
|
3187
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
3188
|
-
}, [focusCtx, focusable, nodeReady]);
|
|
3189
|
-
useEffect(() => {
|
|
3190
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
3191
|
-
const fid = focusIdRef.current;
|
|
3192
|
-
setIsFocused(focusCtx.focusedId === fid);
|
|
3193
|
-
return focusCtx.onFocusChange((newId) => {
|
|
3194
|
-
setIsFocused(newId === fid);
|
|
3195
|
-
});
|
|
3196
|
-
}, [focusCtx, nodeReady]);
|
|
3197
|
-
const findFirstEnabled = useCallback(
|
|
3198
|
-
(fromEnd) => {
|
|
3199
|
-
const start = fromEnd ? count - 1 : 0;
|
|
3200
|
-
const direction = fromEnd ? -1 : 1;
|
|
3201
|
-
let index = start;
|
|
3202
|
-
while (index >= 0 && index < count && disabledIndices?.has(index)) {
|
|
3203
|
-
index += direction;
|
|
3204
|
-
}
|
|
3205
|
-
return index >= 0 && index < count ? index : fromEnd ? count - 1 : 0;
|
|
3206
|
-
},
|
|
3207
|
-
[disabledIndices, count]
|
|
3208
|
-
);
|
|
3209
|
-
useEffect(() => {
|
|
3210
|
-
if (!inputCtx || !focusIdRef.current || !focusable) return;
|
|
3211
|
-
const fid = focusIdRef.current;
|
|
3212
|
-
const handler = (key) => {
|
|
3213
|
-
if (focusCtx?.focusedId !== fid) return false;
|
|
3214
|
-
if (key.name === "g" && !key.ctrl && !key.alt) {
|
|
3215
|
-
if (lastKeyRef.current === "g") {
|
|
3216
|
-
setIndex(findFirstEnabled(false));
|
|
3217
|
-
lastKeyRef.current = null;
|
|
3218
|
-
return true;
|
|
3219
|
-
}
|
|
3220
|
-
lastKeyRef.current = "g";
|
|
3221
|
-
return true;
|
|
3222
|
-
}
|
|
3223
|
-
if (key.name === "G" || key.name === "g" && key.shift) {
|
|
3224
|
-
lastKeyRef.current = null;
|
|
3225
|
-
setIndex(findFirstEnabled(true));
|
|
3226
|
-
return true;
|
|
3227
|
-
}
|
|
3228
|
-
lastKeyRef.current = null;
|
|
3229
|
-
if (key.name === "up" || key.name === "k") {
|
|
3230
|
-
setIndex(findNextEnabled(selectedIndex, -1));
|
|
3231
|
-
return true;
|
|
3232
|
-
}
|
|
3233
|
-
if (key.name === "down" || key.name === "j") {
|
|
3234
|
-
setIndex(findNextEnabled(selectedIndex, 1));
|
|
3235
|
-
return true;
|
|
3236
|
-
}
|
|
3237
|
-
if (key.name === "return") {
|
|
3238
|
-
if (!disabledIndices?.has(selectedIndex)) {
|
|
3239
|
-
onSelectRef.current?.(selectedIndex);
|
|
3240
|
-
}
|
|
3241
|
-
return true;
|
|
3242
|
-
}
|
|
3243
|
-
return false;
|
|
3244
|
-
};
|
|
3245
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
3246
|
-
}, [inputCtx, focusCtx, focusable, selectedIndex, setIndex, findNextEnabled, findFirstEnabled, disabledIndices, nodeReady]);
|
|
3247
|
-
const items = [];
|
|
3248
|
-
for (let i = 0; i < count; i++) {
|
|
3249
|
-
items.push(
|
|
3250
|
-
React15.createElement(
|
|
3251
|
-
React15.Fragment,
|
|
3252
|
-
{ key: i },
|
|
3253
|
-
renderItem({ index: i, selected: i === selectedIndex, focused: isFocused })
|
|
3254
|
-
)
|
|
3255
|
-
);
|
|
3256
|
-
}
|
|
3257
|
-
return React15.createElement(
|
|
3258
|
-
"box",
|
|
3259
|
-
{
|
|
3260
|
-
style: { flexDirection: "column", ...style },
|
|
3261
|
-
focusable,
|
|
3262
|
-
ref: (node) => {
|
|
3263
|
-
if (node) {
|
|
3264
|
-
nodeRef.current = node;
|
|
3265
|
-
focusIdRef.current = node.focusId;
|
|
3266
|
-
setNodeReady(true);
|
|
3267
|
-
} else {
|
|
3268
|
-
nodeRef.current = null;
|
|
3269
|
-
focusIdRef.current = null;
|
|
3270
|
-
setNodeReady(false);
|
|
3271
|
-
}
|
|
3272
|
-
}
|
|
3273
|
-
},
|
|
3274
|
-
...items
|
|
3275
|
-
);
|
|
3276
|
-
}
|
|
3277
|
-
function Menu({
|
|
3278
|
-
items,
|
|
3279
|
-
selectedIndex,
|
|
3280
|
-
onSelectionChange,
|
|
3281
|
-
onSelect,
|
|
3282
|
-
defaultSelectedIndex = 0,
|
|
3283
|
-
style,
|
|
3284
|
-
highlightColor = "cyan",
|
|
3285
|
-
focusable = true
|
|
3286
|
-
}) {
|
|
3287
|
-
const disabledIndices = /* @__PURE__ */ new Set();
|
|
3288
|
-
for (let i = 0; i < items.length; i++) {
|
|
3289
|
-
if (items[i].disabled) disabledIndices.add(i);
|
|
3290
|
-
}
|
|
3291
|
-
const handleSelect = (index) => {
|
|
3292
|
-
const item = items[index];
|
|
3293
|
-
if (item && !item.disabled) {
|
|
3294
|
-
onSelect?.(item.value, index);
|
|
3295
|
-
}
|
|
3296
|
-
};
|
|
3297
|
-
return React15.createElement(List, {
|
|
3298
|
-
count: items.length,
|
|
3299
|
-
selectedIndex,
|
|
3300
|
-
onSelectionChange,
|
|
3301
|
-
onSelect: handleSelect,
|
|
3302
|
-
defaultSelectedIndex,
|
|
3303
|
-
disabledIndices: disabledIndices.size > 0 ? disabledIndices : void 0,
|
|
3304
|
-
style,
|
|
3305
|
-
focusable,
|
|
3306
|
-
renderItem: ({ index, selected, focused }) => {
|
|
3307
|
-
const item = items[index];
|
|
3308
|
-
const isDisabled = item.disabled;
|
|
3309
|
-
const isHighlighted = selected && focused;
|
|
3310
|
-
const indicator = selected ? ">" : " ";
|
|
3311
|
-
return React15.createElement(
|
|
3312
|
-
"box",
|
|
3313
|
-
{
|
|
3314
|
-
style: {
|
|
3315
|
-
flexDirection: "row",
|
|
3316
|
-
...isHighlighted ? { bg: highlightColor } : {}
|
|
3317
|
-
}
|
|
3318
|
-
},
|
|
3319
|
-
React15.createElement(
|
|
3320
|
-
"text",
|
|
3321
|
-
{
|
|
3322
|
-
style: isHighlighted ? { bold: true, color: "black" } : isDisabled ? { dim: true } : {}
|
|
3323
|
-
},
|
|
3324
|
-
`${indicator} ${item.label}`
|
|
3325
|
-
)
|
|
3326
|
-
);
|
|
3327
|
-
}
|
|
3328
|
-
});
|
|
3329
|
-
}
|
|
3330
|
-
function Progress({
|
|
3331
|
-
value,
|
|
3332
|
-
indeterminate = false,
|
|
3333
|
-
width = "100%",
|
|
3334
|
-
label,
|
|
3335
|
-
showPercent = false,
|
|
3336
|
-
style,
|
|
3337
|
-
filled = "\u2588",
|
|
3338
|
-
empty = "\u2591"
|
|
3339
|
-
}) {
|
|
3340
|
-
const trackRef = useRef(null);
|
|
3341
|
-
const trackLayout = useLayout(trackRef);
|
|
3342
|
-
const trackWidth = trackLayout.innerWidth;
|
|
3343
|
-
const [indeterminatePos, setIndeterminatePos] = useState(0);
|
|
3344
|
-
useEffect(() => {
|
|
3345
|
-
if (!indeterminate) return;
|
|
3346
|
-
const timer = setInterval(() => {
|
|
3347
|
-
setIndeterminatePos((p) => (p + 1) % Math.max(1, trackWidth + 6));
|
|
3348
|
-
}, 100);
|
|
3349
|
-
return () => clearInterval(timer);
|
|
3350
|
-
}, [indeterminate, trackWidth]);
|
|
3351
|
-
const clamped = Math.max(0, Math.min(1, value ?? 0));
|
|
3352
|
-
const pctText = showPercent ? ` ${Math.round(clamped * 100)}%` : "";
|
|
3353
|
-
let barText = "";
|
|
3354
|
-
if (trackWidth > 0) {
|
|
3355
|
-
if (indeterminate && value === void 0) {
|
|
3356
|
-
const chunkSize = Math.max(1, Math.min(3, Math.floor(trackWidth / 4)));
|
|
3357
|
-
const chars = [];
|
|
3358
|
-
for (let i = 0; i < trackWidth; i++) {
|
|
3359
|
-
if (i >= indeterminatePos - chunkSize && i < indeterminatePos) {
|
|
3360
|
-
chars.push(filled);
|
|
3361
|
-
} else {
|
|
3362
|
-
chars.push(empty);
|
|
3363
|
-
}
|
|
3364
|
-
}
|
|
3365
|
-
barText = chars.join("");
|
|
3366
|
-
} else {
|
|
3367
|
-
const filledCount = Math.round(clamped * trackWidth);
|
|
3368
|
-
barText = filled.repeat(filledCount) + empty.repeat(trackWidth - filledCount);
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3371
|
-
const children = [];
|
|
3372
|
-
if (label) {
|
|
3373
|
-
children.push(
|
|
3374
|
-
React15.createElement("text", { key: "label", style: { bold: true } }, label + " ")
|
|
3375
|
-
);
|
|
3376
|
-
}
|
|
3377
|
-
children.push(
|
|
3378
|
-
React15.createElement(
|
|
3379
|
-
"box",
|
|
3380
|
-
{
|
|
3381
|
-
key: "track",
|
|
3382
|
-
style: { flexGrow: 1, flexShrink: 1 },
|
|
3383
|
-
ref: (node) => {
|
|
3384
|
-
trackRef.current = node ?? null;
|
|
3385
|
-
}
|
|
3386
|
-
},
|
|
3387
|
-
React15.createElement("text", { key: "bar", style: {} }, barText)
|
|
3388
|
-
)
|
|
3389
|
-
);
|
|
3390
|
-
if (showPercent) {
|
|
3391
|
-
children.push(
|
|
3392
|
-
React15.createElement("text", { key: "pct", style: { bold: true } }, pctText)
|
|
3393
|
-
);
|
|
3394
|
-
}
|
|
3395
|
-
return React15.createElement(
|
|
3396
|
-
"box",
|
|
3397
|
-
{
|
|
3398
|
-
style: {
|
|
3399
|
-
flexDirection: "row",
|
|
3400
|
-
width,
|
|
3401
|
-
...style
|
|
3402
|
-
}
|
|
3403
|
-
},
|
|
3404
|
-
...children
|
|
3405
|
-
);
|
|
3406
|
-
}
|
|
3407
|
-
var BRAILLE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3408
|
-
function Spinner({
|
|
3409
|
-
frames = BRAILLE_FRAMES,
|
|
3410
|
-
intervalMs = 80,
|
|
3411
|
-
label,
|
|
3412
|
-
style
|
|
3413
|
-
}) {
|
|
3414
|
-
const [frameIndex, setFrameIndex] = useState(0);
|
|
3415
|
-
useEffect(() => {
|
|
3416
|
-
const timer = setInterval(() => {
|
|
3417
|
-
setFrameIndex((i) => (i + 1) % frames.length);
|
|
3418
|
-
}, intervalMs);
|
|
3419
|
-
return () => clearInterval(timer);
|
|
3420
|
-
}, [frames.length, intervalMs]);
|
|
3421
|
-
const children = [
|
|
3422
|
-
React15.createElement("text", { key: "frame", style }, frames[frameIndex])
|
|
3423
|
-
];
|
|
3424
|
-
if (label) {
|
|
3425
|
-
children.push(
|
|
3426
|
-
React15.createElement("text", { key: "label", style: {} }, " " + label)
|
|
3427
|
-
);
|
|
3428
|
-
}
|
|
3429
|
-
return React15.createElement(
|
|
3430
|
-
"box",
|
|
3431
|
-
{ style: { flexDirection: "row" } },
|
|
3432
|
-
...children
|
|
3433
|
-
);
|
|
3434
|
-
}
|
|
3435
|
-
var ToastContext = createContext(null);
|
|
3436
|
-
var nextToastId = 0;
|
|
3437
|
-
function useToast() {
|
|
3438
|
-
const ctx = useContext(ToastContext);
|
|
3439
|
-
if (!ctx) throw new Error("useToast must be used within a <ToastHost>");
|
|
3440
|
-
return ctx.push;
|
|
3441
|
-
}
|
|
3442
|
-
var VARIANT_COLORS = {
|
|
3443
|
-
info: { bg: "blackBright", title: "cyanBright", text: "white" },
|
|
3444
|
-
success: { bg: "blackBright", title: "greenBright", text: "white" },
|
|
3445
|
-
warning: { bg: "blackBright", title: "yellowBright", text: "white" },
|
|
3446
|
-
error: { bg: "blackBright", title: "redBright", text: "white" }
|
|
3447
|
-
};
|
|
3448
|
-
function ToastHost({
|
|
3449
|
-
position = "bottom-right",
|
|
3450
|
-
maxVisible = 5,
|
|
3451
|
-
children
|
|
3452
|
-
}) {
|
|
3453
|
-
const [toasts, setToasts] = useState([]);
|
|
3454
|
-
const timersRef = useRef(/* @__PURE__ */ new Map());
|
|
3455
|
-
const push = useCallback((toast) => {
|
|
3456
|
-
const id = `toast-${nextToastId++}`;
|
|
3457
|
-
const full = { id, durationMs: 3e3, variant: "info", ...toast };
|
|
3458
|
-
setToasts((prev) => [...prev, full]);
|
|
3459
|
-
if (full.durationMs && full.durationMs > 0) {
|
|
3460
|
-
const timer = setTimeout(() => {
|
|
3461
|
-
timersRef.current.delete(id);
|
|
3462
|
-
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
3463
|
-
}, full.durationMs);
|
|
3464
|
-
timersRef.current.set(id, timer);
|
|
3465
|
-
}
|
|
3466
|
-
}, []);
|
|
3467
|
-
useEffect(() => {
|
|
3468
|
-
return () => {
|
|
3469
|
-
for (const timer of timersRef.current.values()) {
|
|
3470
|
-
clearTimeout(timer);
|
|
3471
|
-
}
|
|
3472
|
-
timersRef.current.clear();
|
|
3473
|
-
};
|
|
3474
|
-
}, []);
|
|
3475
|
-
const ctxValue = useRef({ push });
|
|
3476
|
-
ctxValue.current.push = push;
|
|
3477
|
-
const isTop = position.startsWith("top");
|
|
3478
|
-
const isRight = position.endsWith("right");
|
|
3479
|
-
const portalStyle = {
|
|
3480
|
-
position: "absolute",
|
|
3481
|
-
top: 0,
|
|
3482
|
-
left: 0,
|
|
3483
|
-
width: "100%",
|
|
3484
|
-
height: "100%",
|
|
3485
|
-
zIndex: 900,
|
|
3486
|
-
flexDirection: "column",
|
|
3487
|
-
justifyContent: isTop ? "flex-start" : "flex-end",
|
|
3488
|
-
alignItems: isRight ? "flex-end" : "flex-start",
|
|
3489
|
-
padding: 1
|
|
3490
|
-
};
|
|
3491
|
-
const visible = toasts.slice(-maxVisible);
|
|
3492
|
-
const toastElements = visible.map((toast) => {
|
|
3493
|
-
const variant = toast.variant ?? "info";
|
|
3494
|
-
const colors = VARIANT_COLORS[variant];
|
|
3495
|
-
const innerChildren = [];
|
|
3496
|
-
if (toast.title) {
|
|
3497
|
-
innerChildren.push(
|
|
3498
|
-
React15.createElement("text", {
|
|
3499
|
-
key: "title",
|
|
3500
|
-
style: { bold: true, color: colors.title }
|
|
3501
|
-
}, toast.title)
|
|
3502
|
-
);
|
|
3503
|
-
}
|
|
3504
|
-
innerChildren.push(
|
|
3505
|
-
React15.createElement("text", {
|
|
3506
|
-
key: "msg",
|
|
3507
|
-
style: { color: colors.text }
|
|
3508
|
-
}, toast.message)
|
|
3509
|
-
);
|
|
3510
|
-
return React15.createElement(
|
|
3511
|
-
"box",
|
|
3512
|
-
{
|
|
3513
|
-
key: toast.id,
|
|
3514
|
-
style: {
|
|
3515
|
-
bg: colors.bg,
|
|
3516
|
-
paddingX: 1,
|
|
3517
|
-
flexDirection: "column",
|
|
3518
|
-
minWidth: 20,
|
|
3519
|
-
maxWidth: 50
|
|
3520
|
-
}
|
|
3521
|
-
},
|
|
3522
|
-
...innerChildren
|
|
3523
|
-
);
|
|
3524
|
-
});
|
|
3525
|
-
return React15.createElement(
|
|
3526
|
-
ToastContext.Provider,
|
|
3527
|
-
{ value: ctxValue.current },
|
|
3528
|
-
children,
|
|
3529
|
-
toastElements.length > 0 ? React15.createElement("box", { style: portalStyle }, ...toastElements) : null
|
|
3530
|
-
);
|
|
3531
|
-
}
|
|
3532
|
-
function Select({
|
|
3533
|
-
items,
|
|
3534
|
-
value,
|
|
3535
|
-
onChange,
|
|
3536
|
-
placeholder = "Select...",
|
|
3537
|
-
style,
|
|
3538
|
-
focusedStyle,
|
|
3539
|
-
dropdownStyle,
|
|
3540
|
-
highlightColor = "cyan",
|
|
3541
|
-
maxVisible = 8,
|
|
3542
|
-
searchable = true,
|
|
3543
|
-
disabled
|
|
3544
|
-
}) {
|
|
3545
|
-
const focusCtx = useContext(FocusContext);
|
|
3546
|
-
const inputCtx = useContext(InputContext);
|
|
3547
|
-
const appCtx = useContext(AppContext);
|
|
3548
|
-
const nodeRef = useRef(null);
|
|
3549
|
-
const focusIdRef = useRef(null);
|
|
3550
|
-
const onChangeRef = useRef(onChange);
|
|
3551
|
-
onChangeRef.current = onChange;
|
|
3552
|
-
const [nodeReady, setNodeReady] = useState(false);
|
|
3553
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
3554
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
3555
|
-
const [highlightIndex, setHighlightIndex] = useState(0);
|
|
3556
|
-
const [searchText, setSearchText] = useState("");
|
|
3557
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
3558
|
-
const triggerLayout = useLayout(nodeRef);
|
|
3559
|
-
const screenRows = appCtx?.rows ?? 24;
|
|
3560
|
-
const selectedItem = items.find((item) => item.value === value);
|
|
3561
|
-
const selectedLabel = selectedItem?.label ?? "";
|
|
3562
|
-
const filteredItems = useMemo(() => {
|
|
3563
|
-
if (!searchText) return items;
|
|
3564
|
-
const lower = searchText.toLowerCase();
|
|
3565
|
-
return items.filter((item) => item.label.toLowerCase().includes(lower));
|
|
3566
|
-
}, [items, searchText]);
|
|
3567
|
-
const visibleCount = Math.min(maxVisible, filteredItems.length);
|
|
3568
|
-
const visibleItems = filteredItems.slice(
|
|
3569
|
-
scrollOffset,
|
|
3570
|
-
scrollOffset + visibleCount
|
|
3571
|
-
);
|
|
3572
|
-
useEffect(() => {
|
|
3573
|
-
setHighlightIndex(0);
|
|
3574
|
-
setScrollOffset(0);
|
|
3575
|
-
}, [searchText]);
|
|
3576
|
-
useEffect(() => {
|
|
3577
|
-
if (!isFocused && isOpen) {
|
|
3578
|
-
setIsOpen(false);
|
|
3579
|
-
setSearchText("");
|
|
3580
|
-
}
|
|
3581
|
-
}, [isFocused, isOpen]);
|
|
3582
|
-
useEffect(() => {
|
|
3583
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current) return;
|
|
3584
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
3585
|
-
}, [focusCtx, nodeReady]);
|
|
3586
|
-
useEffect(() => {
|
|
3587
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
3588
|
-
focusCtx.setSkippable(focusIdRef.current, !!disabled);
|
|
3589
|
-
}, [focusCtx, disabled, nodeReady]);
|
|
3590
|
-
useEffect(() => {
|
|
3591
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
3592
|
-
const fid = focusIdRef.current;
|
|
3593
|
-
setIsFocused(focusCtx.focusedId === fid);
|
|
3594
|
-
return focusCtx.onFocusChange((newId) => {
|
|
3595
|
-
setIsFocused(newId === fid);
|
|
3596
|
-
});
|
|
3597
|
-
}, [focusCtx, nodeReady]);
|
|
3598
|
-
const findNextEnabled = useCallback(
|
|
3599
|
-
(from, direction) => {
|
|
3600
|
-
let next = from + direction;
|
|
3601
|
-
while (next >= 0 && next < filteredItems.length) {
|
|
3602
|
-
if (!filteredItems[next].disabled) return next;
|
|
3603
|
-
next += direction;
|
|
3604
|
-
}
|
|
3605
|
-
return from;
|
|
3606
|
-
},
|
|
3607
|
-
[filteredItems]
|
|
3608
|
-
);
|
|
3609
|
-
const ensureVisible = useCallback(
|
|
3610
|
-
(index) => {
|
|
3611
|
-
if (index < scrollOffset) {
|
|
3612
|
-
setScrollOffset(index);
|
|
3613
|
-
} else if (index >= scrollOffset + visibleCount) {
|
|
3614
|
-
setScrollOffset(index - visibleCount + 1);
|
|
3615
|
-
}
|
|
3616
|
-
},
|
|
3617
|
-
[scrollOffset, visibleCount]
|
|
3618
|
-
);
|
|
3619
|
-
useEffect(() => {
|
|
3620
|
-
if (!inputCtx || !focusIdRef.current || disabled) return;
|
|
3621
|
-
const fid = focusIdRef.current;
|
|
3622
|
-
const handler = (key) => {
|
|
3623
|
-
if (focusCtx?.focusedId !== fid) return false;
|
|
3624
|
-
if (!isOpen) {
|
|
3625
|
-
if (key.name === "return" || key.name === " " || key.sequence === " " || key.name === "down") {
|
|
3626
|
-
setIsOpen(true);
|
|
3627
|
-
setSearchText("");
|
|
3628
|
-
const idx = filteredItems.findIndex((item) => item.value === value);
|
|
3629
|
-
const start = idx >= 0 ? idx : 0;
|
|
3630
|
-
setHighlightIndex(start);
|
|
3631
|
-
setScrollOffset(
|
|
3632
|
-
Math.max(0, start - Math.floor(maxVisible / 2))
|
|
3633
|
-
);
|
|
3634
|
-
return true;
|
|
3635
|
-
}
|
|
3636
|
-
return false;
|
|
3637
|
-
}
|
|
3638
|
-
if (key.name === "tab") {
|
|
3639
|
-
setIsOpen(false);
|
|
3640
|
-
setSearchText("");
|
|
3641
|
-
return false;
|
|
3642
|
-
}
|
|
3643
|
-
if (key.name === "escape") {
|
|
3644
|
-
setIsOpen(false);
|
|
3645
|
-
setSearchText("");
|
|
3646
|
-
return true;
|
|
3647
|
-
}
|
|
3648
|
-
if (key.name === "return") {
|
|
3649
|
-
const item = filteredItems[highlightIndex];
|
|
3650
|
-
if (item && !item.disabled) {
|
|
3651
|
-
onChangeRef.current?.(item.value);
|
|
3652
|
-
setIsOpen(false);
|
|
3653
|
-
setSearchText("");
|
|
3654
|
-
}
|
|
3655
|
-
return true;
|
|
3656
|
-
}
|
|
3657
|
-
if (key.name === "up") {
|
|
3658
|
-
const next = findNextEnabled(highlightIndex, -1);
|
|
3659
|
-
setHighlightIndex(next);
|
|
3660
|
-
ensureVisible(next);
|
|
3661
|
-
return true;
|
|
3662
|
-
}
|
|
3663
|
-
if (key.name === "down") {
|
|
3664
|
-
const next = findNextEnabled(highlightIndex, 1);
|
|
3665
|
-
setHighlightIndex(next);
|
|
3666
|
-
ensureVisible(next);
|
|
3667
|
-
return true;
|
|
3668
|
-
}
|
|
3669
|
-
if (key.name === "backspace") {
|
|
3670
|
-
if (searchable && searchText.length > 0) {
|
|
3671
|
-
setSearchText((prev) => prev.slice(0, -1));
|
|
3672
|
-
}
|
|
3673
|
-
return true;
|
|
3674
|
-
}
|
|
3675
|
-
if (key.name === "home") {
|
|
3676
|
-
const first = findNextEnabled(-1, 1);
|
|
3677
|
-
setHighlightIndex(first);
|
|
3678
|
-
ensureVisible(first);
|
|
3679
|
-
return true;
|
|
3680
|
-
}
|
|
3681
|
-
if (key.name === "end") {
|
|
3682
|
-
const last = findNextEnabled(filteredItems.length, -1);
|
|
3683
|
-
setHighlightIndex(last);
|
|
3684
|
-
ensureVisible(last);
|
|
3685
|
-
return true;
|
|
3686
|
-
}
|
|
3687
|
-
if (searchable && key.sequence && key.sequence.length === 1 && !key.ctrl && !key.alt) {
|
|
3688
|
-
const ch = key.sequence;
|
|
3689
|
-
if (ch >= " " && ch <= "~") {
|
|
3690
|
-
setSearchText((prev) => prev + ch);
|
|
3691
|
-
return true;
|
|
3692
|
-
}
|
|
3693
|
-
}
|
|
3694
|
-
return true;
|
|
3695
|
-
};
|
|
3696
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
3697
|
-
}, [
|
|
3698
|
-
inputCtx,
|
|
3699
|
-
focusCtx,
|
|
3700
|
-
disabled,
|
|
3701
|
-
isOpen,
|
|
3702
|
-
highlightIndex,
|
|
3703
|
-
filteredItems,
|
|
3704
|
-
value,
|
|
3705
|
-
maxVisible,
|
|
3706
|
-
searchable,
|
|
3707
|
-
searchText,
|
|
3708
|
-
findNextEnabled,
|
|
3709
|
-
ensureVisible,
|
|
3710
|
-
nodeReady
|
|
3711
|
-
]);
|
|
3712
|
-
const useDefaultBorder = !style?.bg && style?.border === void 0;
|
|
3713
|
-
const triggerStyle = {
|
|
3714
|
-
flexDirection: "row",
|
|
3715
|
-
width: "100%",
|
|
3716
|
-
...useDefaultBorder ? { border: "single" } : {},
|
|
3717
|
-
...style,
|
|
3718
|
-
...isFocused && focusedStyle ? focusedStyle : {}
|
|
3719
|
-
};
|
|
3720
|
-
const labelColor = selectedLabel ? style?.color ?? void 0 : "blackBright";
|
|
3721
|
-
const triggerChildren = [
|
|
3722
|
-
React15.createElement(
|
|
3723
|
-
"text",
|
|
3724
|
-
{
|
|
3725
|
-
key: "label",
|
|
3726
|
-
style: {
|
|
3727
|
-
flexGrow: 1,
|
|
3728
|
-
flexShrink: 1,
|
|
3729
|
-
color: labelColor,
|
|
3730
|
-
wrap: "ellipsis",
|
|
3731
|
-
...selectedLabel ? {} : { dim: true }
|
|
3732
|
-
}
|
|
3733
|
-
},
|
|
3734
|
-
selectedLabel || placeholder
|
|
3735
|
-
),
|
|
3736
|
-
React15.createElement(
|
|
3737
|
-
"text",
|
|
3738
|
-
{
|
|
3739
|
-
key: "arrow",
|
|
3740
|
-
style: { flexShrink: 0, color: isFocused ? highlightColor : "blackBright" }
|
|
3741
|
-
},
|
|
3742
|
-
isOpen ? " \u25B2" : " \u25BC"
|
|
3743
|
-
)
|
|
3744
|
-
];
|
|
3745
|
-
let dropdownElement = null;
|
|
3746
|
-
if (isOpen) {
|
|
3747
|
-
const dropdownChildren = [];
|
|
3748
|
-
if (searchable && searchText) {
|
|
3749
|
-
dropdownChildren.push(
|
|
3750
|
-
React15.createElement(
|
|
3751
|
-
"box",
|
|
3752
|
-
{ key: "search", style: { paddingX: 1 } },
|
|
3753
|
-
React15.createElement(
|
|
3754
|
-
"text",
|
|
3755
|
-
{ style: { color: "blackBright", dim: true } },
|
|
3756
|
-
`/${searchText}`
|
|
3757
|
-
)
|
|
3758
|
-
)
|
|
3759
|
-
);
|
|
3760
|
-
}
|
|
3761
|
-
if (filteredItems.length === 0) {
|
|
3762
|
-
dropdownChildren.push(
|
|
3763
|
-
React15.createElement(
|
|
3764
|
-
"box",
|
|
3765
|
-
{ key: "empty", style: { paddingX: 1 } },
|
|
3766
|
-
React15.createElement(
|
|
3767
|
-
"text",
|
|
3768
|
-
{ style: { dim: true, color: "blackBright" } },
|
|
3769
|
-
"No matches"
|
|
3770
|
-
)
|
|
3771
|
-
)
|
|
3772
|
-
);
|
|
3773
|
-
}
|
|
3774
|
-
if (scrollOffset > 0) {
|
|
3775
|
-
dropdownChildren.push(
|
|
3776
|
-
React15.createElement(
|
|
3777
|
-
"box",
|
|
3778
|
-
{
|
|
3779
|
-
key: "scroll-up",
|
|
3780
|
-
style: { justifyContent: "center", alignItems: "center" }
|
|
3781
|
-
},
|
|
3782
|
-
React15.createElement(
|
|
3783
|
-
"text",
|
|
3784
|
-
{ style: { dim: true, color: "blackBright" } },
|
|
3785
|
-
"\u25B2"
|
|
3786
|
-
)
|
|
3787
|
-
)
|
|
3788
|
-
);
|
|
3789
|
-
}
|
|
3790
|
-
visibleItems.forEach((item, vi) => {
|
|
3791
|
-
const actualIndex = scrollOffset + vi;
|
|
3792
|
-
const isHighlighted = actualIndex === highlightIndex;
|
|
3793
|
-
const isDisabled = item.disabled;
|
|
3794
|
-
const itemStyle = {
|
|
3795
|
-
paddingX: 1,
|
|
3796
|
-
...isHighlighted && !isDisabled ? { bg: highlightColor } : {}
|
|
3797
|
-
};
|
|
3798
|
-
const textStyle = {
|
|
3799
|
-
...isHighlighted && !isDisabled ? { color: "black", bold: true } : {},
|
|
3800
|
-
...isDisabled ? { dim: true, color: "blackBright" } : {}
|
|
3801
|
-
};
|
|
3802
|
-
dropdownChildren.push(
|
|
3803
|
-
React15.createElement(
|
|
3804
|
-
"box",
|
|
3805
|
-
{ key: `item-${item.value}`, style: itemStyle },
|
|
3806
|
-
React15.createElement(
|
|
3807
|
-
"text",
|
|
3808
|
-
{ style: textStyle },
|
|
3809
|
-
item.label
|
|
3810
|
-
)
|
|
3811
|
-
)
|
|
3812
|
-
);
|
|
3813
|
-
});
|
|
3814
|
-
if (scrollOffset + visibleCount < filteredItems.length) {
|
|
3815
|
-
dropdownChildren.push(
|
|
3816
|
-
React15.createElement(
|
|
3817
|
-
"box",
|
|
3818
|
-
{
|
|
3819
|
-
key: "scroll-down",
|
|
3820
|
-
style: { justifyContent: "center", alignItems: "center" }
|
|
3821
|
-
},
|
|
3822
|
-
React15.createElement(
|
|
3823
|
-
"text",
|
|
3824
|
-
{ style: { dim: true, color: "blackBright" } },
|
|
3825
|
-
"\u25BC"
|
|
3826
|
-
)
|
|
3827
|
-
)
|
|
3828
|
-
);
|
|
3829
|
-
}
|
|
3830
|
-
const hasScrollUp = scrollOffset > 0;
|
|
3831
|
-
const hasScrollDown = scrollOffset + visibleCount < filteredItems.length;
|
|
3832
|
-
const hasSearch = searchable && searchText;
|
|
3833
|
-
const hasNoMatches = filteredItems.length === 0;
|
|
3834
|
-
const useDropdownBorder = !dropdownStyle?.bg && dropdownStyle?.border === void 0;
|
|
3835
|
-
const borderSize = useDropdownBorder ? 2 : 0;
|
|
3836
|
-
let dropdownHeight = visibleCount + borderSize;
|
|
3837
|
-
if (hasScrollUp) dropdownHeight += 1;
|
|
3838
|
-
if (hasScrollDown) dropdownHeight += 1;
|
|
3839
|
-
if (hasSearch) dropdownHeight += 1;
|
|
3840
|
-
if (hasNoMatches) dropdownHeight += 1;
|
|
3841
|
-
const triggerBottom = triggerLayout.y + triggerLayout.height;
|
|
3842
|
-
const spaceBelow = screenRows - triggerBottom;
|
|
3843
|
-
const spaceAbove = triggerLayout.y;
|
|
3844
|
-
const openUpward = spaceBelow < dropdownHeight && spaceAbove >= dropdownHeight;
|
|
3845
|
-
const dropdownTop = openUpward ? -dropdownHeight : triggerLayout.height || 1;
|
|
3846
|
-
dropdownElement = React15.createElement(
|
|
3847
|
-
"box",
|
|
3848
|
-
{
|
|
3849
|
-
style: {
|
|
3850
|
-
position: "absolute",
|
|
3851
|
-
top: dropdownTop,
|
|
3852
|
-
left: 0,
|
|
3853
|
-
right: 0,
|
|
3854
|
-
zIndex: 9999,
|
|
3855
|
-
...useDropdownBorder ? { border: "single" } : {},
|
|
3856
|
-
bg: "black",
|
|
3857
|
-
flexDirection: "column",
|
|
3858
|
-
...dropdownStyle
|
|
3859
|
-
}
|
|
3860
|
-
},
|
|
3861
|
-
...dropdownChildren
|
|
3862
|
-
);
|
|
3863
|
-
}
|
|
3864
|
-
const outerStyle = {
|
|
3865
|
-
flexDirection: "column",
|
|
3866
|
-
width: triggerStyle.width ?? "100%",
|
|
3867
|
-
minWidth: triggerStyle.minWidth,
|
|
3868
|
-
maxWidth: triggerStyle.maxWidth,
|
|
3869
|
-
flexGrow: triggerStyle.flexGrow,
|
|
3870
|
-
flexShrink: triggerStyle.flexShrink ?? 1
|
|
3871
|
-
};
|
|
3872
|
-
return React15.createElement(
|
|
3873
|
-
"box",
|
|
3874
|
-
{ style: outerStyle },
|
|
3875
|
-
// Trigger
|
|
3876
|
-
React15.createElement(
|
|
3877
|
-
"box",
|
|
3878
|
-
{
|
|
3879
|
-
style: triggerStyle,
|
|
3880
|
-
// Always focusable - disabled state is handled in input handler
|
|
3881
|
-
// This ensures focusId is assigned on mount, even if initially disabled
|
|
3882
|
-
focusable: true,
|
|
3883
|
-
ref: (node) => {
|
|
3884
|
-
if (node) {
|
|
3885
|
-
nodeRef.current = node;
|
|
3886
|
-
focusIdRef.current = node.focusId;
|
|
3887
|
-
setNodeReady(true);
|
|
3888
|
-
} else {
|
|
3889
|
-
nodeRef.current = null;
|
|
3890
|
-
focusIdRef.current = null;
|
|
3891
|
-
setNodeReady(false);
|
|
3892
|
-
}
|
|
3893
|
-
}
|
|
3894
|
-
},
|
|
3895
|
-
...triggerChildren
|
|
3896
|
-
),
|
|
3897
|
-
// Dropdown overlay
|
|
3898
|
-
dropdownElement
|
|
3899
|
-
);
|
|
3900
|
-
}
|
|
3901
|
-
function Checkbox({
|
|
3902
|
-
checked,
|
|
3903
|
-
onChange,
|
|
3904
|
-
label,
|
|
3905
|
-
style,
|
|
3906
|
-
focusedStyle,
|
|
3907
|
-
disabled,
|
|
3908
|
-
checkedChar = "\u2713",
|
|
3909
|
-
uncheckedChar = " "
|
|
3910
|
-
}) {
|
|
3911
|
-
const focusCtx = useContext(FocusContext);
|
|
3912
|
-
const inputCtx = useContext(InputContext);
|
|
3913
|
-
const nodeRef = useRef(null);
|
|
3914
|
-
const focusIdRef = useRef(null);
|
|
3915
|
-
const onChangeRef = useRef(onChange);
|
|
3916
|
-
onChangeRef.current = onChange;
|
|
3917
|
-
const checkedRef = useRef(checked);
|
|
3918
|
-
checkedRef.current = checked;
|
|
3919
|
-
const [nodeReady, setNodeReady] = useState(false);
|
|
3920
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
3921
|
-
useEffect(() => {
|
|
3922
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current || disabled) return;
|
|
3923
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
3924
|
-
}, [focusCtx, disabled, nodeReady]);
|
|
3925
|
-
useEffect(() => {
|
|
3926
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
3927
|
-
const fid = focusIdRef.current;
|
|
3928
|
-
setIsFocused(focusCtx.focusedId === fid);
|
|
3929
|
-
return focusCtx.onFocusChange((newId) => {
|
|
3930
|
-
setIsFocused(newId === fid);
|
|
3931
|
-
});
|
|
3932
|
-
}, [focusCtx, nodeReady]);
|
|
3933
|
-
useEffect(() => {
|
|
3934
|
-
if (!inputCtx || !focusIdRef.current || disabled) return;
|
|
3935
|
-
const fid = focusIdRef.current;
|
|
3936
|
-
const handler = (key) => {
|
|
3937
|
-
if (focusCtx?.focusedId !== fid) return false;
|
|
3938
|
-
if (key.name === "return" || key.name === " " || key.sequence === " ") {
|
|
3939
|
-
onChangeRef.current(!checkedRef.current);
|
|
3940
|
-
return true;
|
|
3941
|
-
}
|
|
3942
|
-
return false;
|
|
3943
|
-
};
|
|
3944
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
3945
|
-
}, [inputCtx, focusCtx, disabled, nodeReady]);
|
|
3946
|
-
const mergedStyle = {
|
|
3947
|
-
flexDirection: "row",
|
|
3948
|
-
gap: 1,
|
|
3949
|
-
...style,
|
|
3950
|
-
...isFocused && focusedStyle ? focusedStyle : {}
|
|
3951
|
-
};
|
|
3952
|
-
const boxChar = checked ? checkedChar : uncheckedChar;
|
|
3953
|
-
const boxStyle = {
|
|
3954
|
-
color: disabled ? "blackBright" : isFocused ? "white" : style?.color
|
|
3955
|
-
};
|
|
3956
|
-
const labelStyle = {
|
|
3957
|
-
color: disabled ? "blackBright" : style?.color
|
|
3958
|
-
};
|
|
3959
|
-
return React15.createElement(
|
|
3960
|
-
"box",
|
|
3961
|
-
{
|
|
3962
|
-
style: mergedStyle,
|
|
3963
|
-
focusable: !disabled,
|
|
3964
|
-
ref: (node) => {
|
|
3965
|
-
if (node) {
|
|
3966
|
-
nodeRef.current = node;
|
|
3967
|
-
focusIdRef.current = node.focusId;
|
|
3968
|
-
setNodeReady(true);
|
|
3969
|
-
} else {
|
|
3970
|
-
nodeRef.current = null;
|
|
3971
|
-
focusIdRef.current = null;
|
|
3972
|
-
setNodeReady(false);
|
|
3973
|
-
}
|
|
3974
|
-
}
|
|
3975
|
-
},
|
|
3976
|
-
React15.createElement(
|
|
3977
|
-
"text",
|
|
3978
|
-
{ key: "box", style: boxStyle },
|
|
3979
|
-
`[${boxChar}]`
|
|
3980
|
-
),
|
|
3981
|
-
label ? React15.createElement(
|
|
3982
|
-
"text",
|
|
3983
|
-
{ key: "label", style: labelStyle },
|
|
3984
|
-
label
|
|
3985
|
-
) : null
|
|
3986
|
-
);
|
|
3987
|
-
}
|
|
3988
|
-
function Radio({
|
|
3989
|
-
items,
|
|
3990
|
-
value,
|
|
3991
|
-
onChange,
|
|
3992
|
-
style,
|
|
3993
|
-
itemStyle,
|
|
3994
|
-
focusedItemStyle,
|
|
3995
|
-
selectedItemStyle,
|
|
3996
|
-
disabled,
|
|
3997
|
-
direction = "column",
|
|
3998
|
-
gap = 0,
|
|
3999
|
-
selectedChar = "\u25CF",
|
|
4000
|
-
unselectedChar = "\u25CB"
|
|
4001
|
-
}) {
|
|
4002
|
-
const focusCtx = useContext(FocusContext);
|
|
4003
|
-
const inputCtx = useContext(InputContext);
|
|
4004
|
-
const nodeRef = useRef(null);
|
|
4005
|
-
const focusIdRef = useRef(null);
|
|
4006
|
-
const onChangeRef = useRef(onChange);
|
|
4007
|
-
onChangeRef.current = onChange;
|
|
4008
|
-
const [nodeReady, setNodeReady] = useState(false);
|
|
4009
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
4010
|
-
const [highlightedIndex, setHighlightedIndex] = useState(() => {
|
|
4011
|
-
const selectedIdx = items.findIndex((item) => item.value === value);
|
|
4012
|
-
if (selectedIdx >= 0) return selectedIdx;
|
|
4013
|
-
return items.findIndex((item) => !item.disabled);
|
|
4014
|
-
});
|
|
4015
|
-
const findNextEnabled = useCallback(
|
|
4016
|
-
(startIndex, direction2) => {
|
|
4017
|
-
let index = startIndex;
|
|
4018
|
-
for (let i = 0; i < items.length; i++) {
|
|
4019
|
-
index = (index + direction2 + items.length) % items.length;
|
|
4020
|
-
if (!items[index]?.disabled) return index;
|
|
4021
|
-
}
|
|
4022
|
-
return startIndex;
|
|
4023
|
-
},
|
|
4024
|
-
[items]
|
|
4025
|
-
);
|
|
4026
|
-
useEffect(() => {
|
|
4027
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current || disabled) return;
|
|
4028
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
4029
|
-
}, [focusCtx, disabled, nodeReady]);
|
|
4030
|
-
useEffect(() => {
|
|
4031
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
4032
|
-
const fid = focusIdRef.current;
|
|
4033
|
-
setIsFocused(focusCtx.focusedId === fid);
|
|
4034
|
-
return focusCtx.onFocusChange((newId) => {
|
|
4035
|
-
setIsFocused(newId === fid);
|
|
4036
|
-
});
|
|
4037
|
-
}, [focusCtx, nodeReady]);
|
|
4038
|
-
useEffect(() => {
|
|
4039
|
-
if (!inputCtx || !focusIdRef.current || disabled) return;
|
|
4040
|
-
const fid = focusIdRef.current;
|
|
4041
|
-
const handler = (key) => {
|
|
4042
|
-
if (focusCtx?.focusedId !== fid) return false;
|
|
4043
|
-
if (key.name === "up" || key.name === "left" || key.name === "k" || key.name === "tab" && key.shift) {
|
|
4044
|
-
setHighlightedIndex((idx) => findNextEnabled(idx, -1));
|
|
4045
|
-
return true;
|
|
4046
|
-
}
|
|
4047
|
-
if (key.name === "down" || key.name === "right" || key.name === "j" || key.name === "tab" && !key.shift) {
|
|
4048
|
-
setHighlightedIndex((idx) => findNextEnabled(idx, 1));
|
|
4049
|
-
return true;
|
|
4050
|
-
}
|
|
4051
|
-
if (key.name === "return" || key.name === " " || key.sequence === " ") {
|
|
4052
|
-
const item = items[highlightedIndex];
|
|
4053
|
-
if (item && !item.disabled) {
|
|
4054
|
-
onChangeRef.current(item.value);
|
|
4055
|
-
}
|
|
4056
|
-
return true;
|
|
4057
|
-
}
|
|
4058
|
-
return false;
|
|
4059
|
-
};
|
|
4060
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
4061
|
-
}, [inputCtx, focusCtx, disabled, items, highlightedIndex, findNextEnabled, nodeReady]);
|
|
4062
|
-
useEffect(() => {
|
|
4063
|
-
const selectedIdx = items.findIndex((item) => item.value === value);
|
|
4064
|
-
if (selectedIdx >= 0) {
|
|
4065
|
-
setHighlightedIndex(selectedIdx);
|
|
4066
|
-
}
|
|
4067
|
-
}, [value, items]);
|
|
4068
|
-
const containerStyle = {
|
|
4069
|
-
flexDirection: direction,
|
|
4070
|
-
gap,
|
|
4071
|
-
...style
|
|
4072
|
-
};
|
|
4073
|
-
const radioItems = items.map((item, index) => {
|
|
4074
|
-
const isSelected = item.value === value;
|
|
4075
|
-
const isHighlighted = index === highlightedIndex;
|
|
4076
|
-
const isItemDisabled = disabled || item.disabled;
|
|
4077
|
-
const radioChar = isSelected ? selectedChar : unselectedChar;
|
|
4078
|
-
let computedStyle = {
|
|
4079
|
-
flexDirection: "row",
|
|
4080
|
-
gap: 1,
|
|
4081
|
-
...itemStyle
|
|
4082
|
-
};
|
|
4083
|
-
if (isSelected && selectedItemStyle) {
|
|
4084
|
-
computedStyle = { ...computedStyle, ...selectedItemStyle };
|
|
4085
|
-
}
|
|
4086
|
-
if (isFocused && isHighlighted && focusedItemStyle) {
|
|
4087
|
-
computedStyle = { ...computedStyle, ...focusedItemStyle };
|
|
4088
|
-
}
|
|
4089
|
-
const textColor = isItemDisabled ? "blackBright" : isFocused && isHighlighted ? focusedItemStyle?.color ?? "white" : isSelected ? selectedItemStyle?.color ?? itemStyle?.color : itemStyle?.color;
|
|
4090
|
-
return React15.createElement(
|
|
4091
|
-
"box",
|
|
4092
|
-
{ key: index, style: computedStyle },
|
|
4093
|
-
React15.createElement(
|
|
4094
|
-
"text",
|
|
4095
|
-
{ key: "radio", style: { color: textColor } },
|
|
4096
|
-
`(${radioChar})`
|
|
4097
|
-
),
|
|
4098
|
-
React15.createElement(
|
|
4099
|
-
"text",
|
|
4100
|
-
{ key: "label", style: { color: textColor } },
|
|
4101
|
-
item.label
|
|
4102
|
-
)
|
|
4103
|
-
);
|
|
4104
|
-
});
|
|
4105
|
-
return React15.createElement(
|
|
4106
|
-
"box",
|
|
4107
|
-
{
|
|
4108
|
-
style: containerStyle,
|
|
4109
|
-
focusable: !disabled,
|
|
4110
|
-
ref: (node) => {
|
|
4111
|
-
if (node) {
|
|
4112
|
-
nodeRef.current = node;
|
|
4113
|
-
focusIdRef.current = node.focusId;
|
|
4114
|
-
setNodeReady(true);
|
|
4115
|
-
} else {
|
|
4116
|
-
nodeRef.current = null;
|
|
4117
|
-
focusIdRef.current = null;
|
|
4118
|
-
setNodeReady(false);
|
|
4119
|
-
}
|
|
4120
|
-
}
|
|
4121
|
-
},
|
|
4122
|
-
...radioItems
|
|
4123
|
-
);
|
|
4124
|
-
}
|
|
4125
|
-
var DialogContext = createContext(null);
|
|
4126
|
-
function useDialog() {
|
|
4127
|
-
const ctx = useContext(DialogContext);
|
|
4128
|
-
if (!ctx) {
|
|
4129
|
-
throw new Error("useDialog must be used within a DialogHost");
|
|
4130
|
-
}
|
|
4131
|
-
return ctx;
|
|
4132
|
-
}
|
|
4133
|
-
function DialogHost({ children }) {
|
|
4134
|
-
const [dialogs, setDialogs] = useState([]);
|
|
4135
|
-
const idCounter = useRef(0);
|
|
4136
|
-
const alert = useCallback((content, options) => {
|
|
4137
|
-
return new Promise((resolve) => {
|
|
4138
|
-
const id = ++idCounter.current;
|
|
4139
|
-
setDialogs((prev) => [
|
|
4140
|
-
...prev,
|
|
4141
|
-
{
|
|
4142
|
-
id,
|
|
4143
|
-
type: "alert",
|
|
4144
|
-
content,
|
|
4145
|
-
okText: options?.okText ?? "OK",
|
|
4146
|
-
cancelText: "",
|
|
4147
|
-
style: options?.style,
|
|
4148
|
-
resolve: () => resolve()
|
|
4149
|
-
}
|
|
4150
|
-
]);
|
|
4151
|
-
});
|
|
4152
|
-
}, []);
|
|
4153
|
-
const confirm = useCallback((content, options) => {
|
|
4154
|
-
return new Promise((resolve) => {
|
|
4155
|
-
const id = ++idCounter.current;
|
|
4156
|
-
setDialogs((prev) => [
|
|
4157
|
-
...prev,
|
|
4158
|
-
{
|
|
4159
|
-
id,
|
|
4160
|
-
type: "confirm",
|
|
4161
|
-
content,
|
|
4162
|
-
okText: options?.okText ?? "OK",
|
|
4163
|
-
cancelText: options?.cancelText ?? "Cancel",
|
|
4164
|
-
style: options?.style,
|
|
4165
|
-
resolve
|
|
4166
|
-
}
|
|
4167
|
-
]);
|
|
4168
|
-
});
|
|
4169
|
-
}, []);
|
|
4170
|
-
const dismissDialog = useCallback((id, result) => {
|
|
4171
|
-
setDialogs((prev) => {
|
|
4172
|
-
const dialog = prev.find((d) => d.id === id);
|
|
4173
|
-
if (dialog) {
|
|
4174
|
-
dialog.resolve(result);
|
|
4175
|
-
}
|
|
4176
|
-
return prev.filter((d) => d.id !== id);
|
|
4177
|
-
});
|
|
4178
|
-
}, []);
|
|
4179
|
-
const contextValue = { alert, confirm };
|
|
4180
|
-
const activeDialog = dialogs[dialogs.length - 1];
|
|
4181
|
-
return React15.createElement(
|
|
4182
|
-
DialogContext.Provider,
|
|
4183
|
-
{ value: contextValue },
|
|
4184
|
-
children,
|
|
4185
|
-
activeDialog && React15.createElement(DialogOverlay, {
|
|
4186
|
-
key: activeDialog.id,
|
|
4187
|
-
dialog: activeDialog,
|
|
4188
|
-
onDismiss: dismissDialog
|
|
4189
|
-
})
|
|
4190
|
-
);
|
|
4191
|
-
}
|
|
4192
|
-
function DialogOverlay({ dialog, onDismiss }) {
|
|
4193
|
-
const focusCtx = useContext(FocusContext);
|
|
4194
|
-
const okButtonRef = useRef(null);
|
|
4195
|
-
const cancelButtonRef = useRef(null);
|
|
4196
|
-
const okFocusIdRef = useRef(null);
|
|
4197
|
-
const cancelFocusIdRef = useRef(null);
|
|
4198
|
-
const [focusedButton, setFocusedButton] = useState("ok");
|
|
4199
|
-
const [refsReady, setRefsReady] = useState(0);
|
|
4200
|
-
useEffect(() => {
|
|
4201
|
-
if (!focusCtx || refsReady === 0) return;
|
|
4202
|
-
const cleanups = [];
|
|
4203
|
-
if (okButtonRef.current && okFocusIdRef.current) {
|
|
4204
|
-
cleanups.push(focusCtx.register(okFocusIdRef.current, okButtonRef.current));
|
|
4205
|
-
}
|
|
4206
|
-
if (cancelButtonRef.current && cancelFocusIdRef.current) {
|
|
4207
|
-
cleanups.push(focusCtx.register(cancelFocusIdRef.current, cancelButtonRef.current));
|
|
4208
|
-
}
|
|
4209
|
-
if (okFocusIdRef.current) {
|
|
4210
|
-
focusCtx.requestFocus(okFocusIdRef.current);
|
|
4211
|
-
}
|
|
4212
|
-
return () => cleanups.forEach((fn) => fn());
|
|
4213
|
-
}, [focusCtx, refsReady]);
|
|
4214
|
-
useEffect(() => {
|
|
4215
|
-
if (!focusCtx) return;
|
|
4216
|
-
return focusCtx.onFocusChange((id) => {
|
|
4217
|
-
if (id === okFocusIdRef.current) {
|
|
4218
|
-
setFocusedButton("ok");
|
|
4219
|
-
} else if (id === cancelFocusIdRef.current) {
|
|
4220
|
-
setFocusedButton("cancel");
|
|
4221
|
-
}
|
|
4222
|
-
});
|
|
4223
|
-
}, [focusCtx]);
|
|
4224
|
-
useInput((key) => {
|
|
4225
|
-
if (key.name === "return" || key.name === "space") {
|
|
4226
|
-
if (dialog.type === "alert") {
|
|
4227
|
-
onDismiss(dialog.id, true);
|
|
4228
|
-
} else {
|
|
4229
|
-
onDismiss(dialog.id, focusedButton === "ok");
|
|
4230
|
-
}
|
|
4231
|
-
return;
|
|
4232
|
-
}
|
|
4233
|
-
if (key.name === "escape") {
|
|
4234
|
-
onDismiss(dialog.id, false);
|
|
4235
|
-
return;
|
|
4236
|
-
}
|
|
4237
|
-
if (dialog.type === "confirm" && focusCtx) {
|
|
4238
|
-
if (key.name === "left" || key.name === "right") {
|
|
4239
|
-
if (focusedButton === "ok" && cancelFocusIdRef.current) {
|
|
4240
|
-
focusCtx.requestFocus(cancelFocusIdRef.current);
|
|
4241
|
-
} else if (okFocusIdRef.current) {
|
|
4242
|
-
focusCtx.requestFocus(okFocusIdRef.current);
|
|
4243
|
-
}
|
|
4244
|
-
}
|
|
4245
|
-
}
|
|
4246
|
-
}, [dialog, focusedButton, focusCtx, onDismiss]);
|
|
4247
|
-
const contentIsString = typeof dialog.content === "string";
|
|
4248
|
-
const contentLength = contentIsString ? dialog.content.length : 0;
|
|
4249
|
-
const minWidth = Math.max(20, contentIsString ? Math.min(contentLength + 6, 50) : 30);
|
|
4250
|
-
const boxStyle = {
|
|
4251
|
-
minWidth,
|
|
4252
|
-
maxWidth: 50,
|
|
4253
|
-
bg: "black",
|
|
4254
|
-
border: "round",
|
|
4255
|
-
borderColor: "white",
|
|
4256
|
-
padding: 1,
|
|
4257
|
-
flexDirection: "column",
|
|
4258
|
-
gap: 1,
|
|
4259
|
-
...dialog.style
|
|
4260
|
-
};
|
|
4261
|
-
const getButtonStyle = (isSelected) => ({
|
|
4262
|
-
paddingX: 2,
|
|
4263
|
-
bg: isSelected ? "white" : "blackBright",
|
|
4264
|
-
color: isSelected ? "black" : "white",
|
|
4265
|
-
bold: isSelected
|
|
4266
|
-
});
|
|
4267
|
-
return React15.createElement(
|
|
4268
|
-
FocusScope,
|
|
4269
|
-
{ trap: true },
|
|
4270
|
-
// Backdrop
|
|
4271
|
-
React15.createElement("box", {
|
|
4272
|
-
style: {
|
|
4273
|
-
position: "absolute",
|
|
4274
|
-
top: 0,
|
|
4275
|
-
left: 0,
|
|
4276
|
-
right: 0,
|
|
4277
|
-
bottom: 0,
|
|
4278
|
-
zIndex: 999
|
|
4279
|
-
}
|
|
4280
|
-
}),
|
|
4281
|
-
// Centering wrapper
|
|
4282
|
-
React15.createElement(
|
|
4283
|
-
"box",
|
|
4284
|
-
{
|
|
4285
|
-
style: {
|
|
4286
|
-
position: "absolute",
|
|
4287
|
-
top: 0,
|
|
4288
|
-
left: 0,
|
|
4289
|
-
right: 0,
|
|
4290
|
-
bottom: 0,
|
|
4291
|
-
justifyContent: "center",
|
|
4292
|
-
alignItems: "center",
|
|
4293
|
-
zIndex: 1e3
|
|
4294
|
-
}
|
|
4295
|
-
},
|
|
4296
|
-
// Dialog box
|
|
4297
|
-
React15.createElement(
|
|
4298
|
-
"box",
|
|
4299
|
-
{ style: boxStyle },
|
|
4300
|
-
// Content
|
|
4301
|
-
React15.createElement(
|
|
4302
|
-
"box",
|
|
4303
|
-
{ style: { flexDirection: "column" } },
|
|
4304
|
-
typeof dialog.content === "string" ? React15.createElement("text", null, dialog.content) : dialog.content
|
|
4305
|
-
),
|
|
4306
|
-
// Buttons row
|
|
4307
|
-
React15.createElement(
|
|
4308
|
-
"box",
|
|
4309
|
-
{
|
|
4310
|
-
style: {
|
|
4311
|
-
flexDirection: "row",
|
|
4312
|
-
justifyContent: "flex-end",
|
|
4313
|
-
gap: 1
|
|
4314
|
-
}
|
|
4315
|
-
},
|
|
4316
|
-
// Cancel button (confirm only)
|
|
4317
|
-
dialog.type === "confirm" && React15.createElement(
|
|
4318
|
-
"box",
|
|
4319
|
-
{
|
|
4320
|
-
style: getButtonStyle(focusedButton === "cancel"),
|
|
4321
|
-
focusable: true,
|
|
4322
|
-
ref: (node) => {
|
|
4323
|
-
if (node && node.focusId && !cancelFocusIdRef.current) {
|
|
4324
|
-
cancelButtonRef.current = node;
|
|
4325
|
-
cancelFocusIdRef.current = node.focusId;
|
|
4326
|
-
setRefsReady((r) => r + 1);
|
|
4327
|
-
}
|
|
4328
|
-
}
|
|
4329
|
-
},
|
|
4330
|
-
React15.createElement("text", null, dialog.cancelText)
|
|
4331
|
-
),
|
|
4332
|
-
// OK button
|
|
4333
|
-
React15.createElement(
|
|
4334
|
-
"box",
|
|
4335
|
-
{
|
|
4336
|
-
style: getButtonStyle(focusedButton === "ok"),
|
|
4337
|
-
focusable: true,
|
|
4338
|
-
ref: (node) => {
|
|
4339
|
-
if (node && node.focusId && !okFocusIdRef.current) {
|
|
4340
|
-
okButtonRef.current = node;
|
|
4341
|
-
okFocusIdRef.current = node.focusId;
|
|
4342
|
-
setRefsReady((r) => r + 1);
|
|
4343
|
-
}
|
|
4344
|
-
}
|
|
4345
|
-
},
|
|
4346
|
-
React15.createElement("text", null, dialog.okText)
|
|
4347
|
-
)
|
|
4348
|
-
)
|
|
4349
|
-
)
|
|
4350
|
-
)
|
|
4351
|
-
);
|
|
4352
|
-
}
|
|
4353
|
-
function generateHints(count, chars) {
|
|
4354
|
-
const hints = [];
|
|
4355
|
-
const charList = chars.split("");
|
|
4356
|
-
if (count <= charList.length) {
|
|
4357
|
-
for (let i = 0; i < count; i++) {
|
|
4358
|
-
hints.push(charList[i]);
|
|
4359
|
-
}
|
|
4360
|
-
} else {
|
|
4361
|
-
for (let i = 0; i < charList.length && hints.length < count; i++) {
|
|
4362
|
-
for (let j = 0; j < charList.length && hints.length < count; j++) {
|
|
4363
|
-
hints.push(charList[i] + charList[j]);
|
|
4364
|
-
}
|
|
4365
|
-
}
|
|
4366
|
-
}
|
|
4367
|
-
return hints;
|
|
4368
|
-
}
|
|
4369
|
-
function JumpNav({
|
|
4370
|
-
children,
|
|
4371
|
-
activationKey = "ctrl+o",
|
|
4372
|
-
hintStyle,
|
|
4373
|
-
hintBg = "yellow",
|
|
4374
|
-
hintFg = "black",
|
|
4375
|
-
hintChars = "asdfghjklqwertyuiopzxcvbnm",
|
|
4376
|
-
enabled = true,
|
|
4377
|
-
debug = false
|
|
4378
|
-
}) {
|
|
4379
|
-
const log = debug ? (...args) => console.error("[JumpNav]", ...args) : () => {
|
|
4380
|
-
};
|
|
4381
|
-
const [isActive, setIsActive] = useState(false);
|
|
4382
|
-
const [inputBuffer, setInputBuffer] = useState("");
|
|
4383
|
-
const [elements, setElements] = useState([]);
|
|
4384
|
-
const inputCtx = useContext(InputContext);
|
|
4385
|
-
const focusCtx = useContext(FocusContext);
|
|
4386
|
-
const layoutCtx = useContext(LayoutContext);
|
|
4387
|
-
useEffect(() => {
|
|
4388
|
-
log("Mounted, inputCtx:", !!inputCtx, "focusCtx:", !!focusCtx, "enabled:", enabled);
|
|
4389
|
-
}, []);
|
|
4390
|
-
const parseKey = useCallback((keyStr) => {
|
|
4391
|
-
const parts = keyStr.toLowerCase().split("+");
|
|
4392
|
-
return {
|
|
4393
|
-
ctrl: parts.includes("ctrl"),
|
|
4394
|
-
alt: parts.includes("alt"),
|
|
4395
|
-
shift: parts.includes("shift"),
|
|
4396
|
-
meta: parts.includes("meta"),
|
|
4397
|
-
name: parts[parts.length - 1] ?? ""
|
|
4398
|
-
};
|
|
4399
|
-
}, []);
|
|
4400
|
-
const activationKeyParsed = parseKey(activationKey);
|
|
4401
|
-
const refreshElements = useCallback(() => {
|
|
4402
|
-
if (!focusCtx?.getActiveElements) {
|
|
4403
|
-
log("refreshElements: no getActiveElements");
|
|
4404
|
-
return;
|
|
4405
|
-
}
|
|
4406
|
-
const active = focusCtx.getActiveElements();
|
|
4407
|
-
log("getActiveElements returned", active.length, "elements");
|
|
4408
|
-
const mapped = active.map(({ id, node }) => ({
|
|
4409
|
-
id,
|
|
4410
|
-
node,
|
|
4411
|
-
layout: layoutCtx?.getLayout(node) ?? node.layout
|
|
4412
|
-
}));
|
|
4413
|
-
mapped.sort((a, b) => {
|
|
4414
|
-
if (a.layout.y !== b.layout.y) {
|
|
4415
|
-
return a.layout.y - b.layout.y;
|
|
4416
|
-
}
|
|
4417
|
-
return a.layout.x - b.layout.x;
|
|
4418
|
-
});
|
|
4419
|
-
setElements(mapped);
|
|
4420
|
-
}, [focusCtx, layoutCtx, log]);
|
|
4421
|
-
const wasActiveRef = useRef(false);
|
|
4422
|
-
useEffect(() => {
|
|
4423
|
-
if (isActive && !wasActiveRef.current) {
|
|
4424
|
-
log("Activated! Refreshing elements...");
|
|
4425
|
-
refreshElements();
|
|
4426
|
-
}
|
|
4427
|
-
wasActiveRef.current = isActive;
|
|
4428
|
-
}, [isActive, refreshElements, log]);
|
|
4429
|
-
const visibleElements = elements.filter(
|
|
4430
|
-
(el) => el.layout.width > 0 && el.layout.height > 0
|
|
4431
|
-
);
|
|
4432
|
-
const visibleHints = generateHints(visibleElements.length, hintChars);
|
|
4433
|
-
const visibleHintMap = useMemo(() => {
|
|
4434
|
-
const map = /* @__PURE__ */ new Map();
|
|
4435
|
-
visibleElements.forEach((el, i) => {
|
|
4436
|
-
if (visibleHints[i]) {
|
|
4437
|
-
map.set(visibleHints[i], el.id);
|
|
4438
|
-
}
|
|
4439
|
-
});
|
|
4440
|
-
return map;
|
|
4441
|
-
}, [visibleElements, visibleHints]);
|
|
4442
|
-
useEffect(() => {
|
|
4443
|
-
if (!inputCtx || !enabled) {
|
|
4444
|
-
log("Not subscribing - inputCtx:", !!inputCtx, "enabled:", enabled);
|
|
4445
|
-
return;
|
|
4446
|
-
}
|
|
4447
|
-
log("Subscribing to priority input, activation key:", activationKey);
|
|
4448
|
-
const handler = (key) => {
|
|
4449
|
-
const nameMatch = key.name === activationKeyParsed.name;
|
|
4450
|
-
const ctrlMatch = !!key.ctrl === activationKeyParsed.ctrl;
|
|
4451
|
-
const altMatch = !!key.alt === activationKeyParsed.alt;
|
|
4452
|
-
const shiftMatch = !!key.shift === activationKeyParsed.shift;
|
|
4453
|
-
const metaMatch = !!key.meta === activationKeyParsed.meta;
|
|
4454
|
-
if (!isActive && nameMatch && ctrlMatch && altMatch && shiftMatch && metaMatch) {
|
|
4455
|
-
log("Activation key matched! Activating...");
|
|
4456
|
-
setIsActive(true);
|
|
4457
|
-
setInputBuffer("");
|
|
4458
|
-
return true;
|
|
4459
|
-
}
|
|
4460
|
-
if (isActive) {
|
|
4461
|
-
if (key.name === "escape") {
|
|
4462
|
-
log("Escape pressed, deactivating");
|
|
4463
|
-
setIsActive(false);
|
|
4464
|
-
setInputBuffer("");
|
|
4465
|
-
return true;
|
|
4466
|
-
}
|
|
4467
|
-
if (key.name === "backspace") {
|
|
4468
|
-
setInputBuffer("");
|
|
4469
|
-
return true;
|
|
4470
|
-
}
|
|
4471
|
-
if (key.sequence && key.sequence.length === 1 && /[a-z]/i.test(key.sequence)) {
|
|
4472
|
-
const newBuffer = inputBuffer + key.sequence.toLowerCase();
|
|
4473
|
-
log("Buffer:", newBuffer);
|
|
4474
|
-
const targetId = visibleHintMap.get(newBuffer);
|
|
4475
|
-
if (targetId) {
|
|
4476
|
-
log("Jumping to", targetId);
|
|
4477
|
-
focusCtx?.requestFocus(targetId);
|
|
4478
|
-
setIsActive(false);
|
|
4479
|
-
setInputBuffer("");
|
|
4480
|
-
return true;
|
|
4481
|
-
}
|
|
4482
|
-
const hasPartialMatch = [...visibleHintMap.keys()].some((h) => h.startsWith(newBuffer));
|
|
4483
|
-
if (hasPartialMatch) {
|
|
4484
|
-
setInputBuffer(newBuffer);
|
|
4485
|
-
return true;
|
|
4486
|
-
}
|
|
4487
|
-
setInputBuffer("");
|
|
4488
|
-
return true;
|
|
4489
|
-
}
|
|
4490
|
-
return true;
|
|
4491
|
-
}
|
|
4492
|
-
return false;
|
|
4493
|
-
};
|
|
4494
|
-
return inputCtx.subscribePriority(handler);
|
|
4495
|
-
}, [inputCtx, enabled, isActive, activationKeyParsed, inputBuffer, visibleHintMap, focusCtx, activationKey, log]);
|
|
4496
|
-
const hintsOverlay = isActive ? React15.createElement(
|
|
4497
|
-
"box",
|
|
4498
|
-
{
|
|
4499
|
-
// Portal-like wrapper - fullscreen absolute overlay
|
|
4500
|
-
style: {
|
|
4501
|
-
position: "absolute",
|
|
4502
|
-
top: 0,
|
|
4503
|
-
left: 0,
|
|
4504
|
-
width: "100%",
|
|
4505
|
-
height: "100%",
|
|
4506
|
-
zIndex: 99998
|
|
4507
|
-
}
|
|
4508
|
-
},
|
|
4509
|
-
...visibleElements.map((el, i) => {
|
|
4510
|
-
const hint = visibleHints[i];
|
|
4511
|
-
if (!hint) return null;
|
|
4512
|
-
const { x, y } = el.layout;
|
|
4513
|
-
const isPartialMatch = hint.startsWith(inputBuffer) && inputBuffer.length > 0;
|
|
4514
|
-
return React15.createElement(
|
|
4515
|
-
"box",
|
|
4516
|
-
{
|
|
4517
|
-
key: el.id,
|
|
4518
|
-
style: {
|
|
4519
|
-
position: "absolute",
|
|
4520
|
-
top: y,
|
|
4521
|
-
left: Math.max(0, x - hint.length - 2),
|
|
4522
|
-
bg: isPartialMatch ? "cyan" : hintBg,
|
|
4523
|
-
color: hintFg,
|
|
4524
|
-
paddingX: 1,
|
|
4525
|
-
zIndex: 99999,
|
|
4526
|
-
...hintStyle
|
|
4527
|
-
}
|
|
4528
|
-
},
|
|
4529
|
-
React15.createElement("text", {
|
|
4530
|
-
style: { bold: true, color: hintFg }
|
|
4531
|
-
}, hint)
|
|
4532
|
-
);
|
|
4533
|
-
}),
|
|
4534
|
-
// Status bar at bottom
|
|
4535
|
-
React15.createElement(
|
|
4536
|
-
"box",
|
|
4537
|
-
{
|
|
4538
|
-
style: {
|
|
4539
|
-
position: "absolute",
|
|
4540
|
-
bottom: 0,
|
|
4541
|
-
left: 0,
|
|
4542
|
-
right: 0,
|
|
4543
|
-
bg: "blackBright",
|
|
4544
|
-
paddingX: 1,
|
|
4545
|
-
zIndex: 99999
|
|
4546
|
-
}
|
|
4547
|
-
},
|
|
4548
|
-
React15.createElement("text", {
|
|
4549
|
-
style: { color: "white" }
|
|
4550
|
-
}, inputBuffer ? `Jump: ${inputBuffer}_` : "Press a key to jump \u2022 ESC to cancel")
|
|
4551
|
-
)
|
|
4552
|
-
) : null;
|
|
4553
|
-
return React15.createElement(
|
|
4554
|
-
React15.Fragment,
|
|
4555
|
-
null,
|
|
4556
|
-
children,
|
|
4557
|
-
hintsOverlay
|
|
4558
|
-
);
|
|
4559
|
-
}
|
|
4560
|
-
function useFocus(nodeRef) {
|
|
4561
|
-
const focusCtx = useContext(FocusContext);
|
|
4562
|
-
const [id] = useState(() => `focus-${Math.random().toString(36).slice(2, 9)}`);
|
|
4563
|
-
const isFocused = focusCtx ? focusCtx.focusedId === id : false;
|
|
4564
|
-
useEffect(() => {
|
|
4565
|
-
if (!focusCtx || !nodeRef?.current) return;
|
|
4566
|
-
nodeRef.current.focusId = id;
|
|
4567
|
-
return focusCtx.register(id, nodeRef.current);
|
|
4568
|
-
}, [focusCtx, id, nodeRef]);
|
|
4569
|
-
const focus = useMemo(() => {
|
|
4570
|
-
return () => {
|
|
4571
|
-
focusCtx?.requestFocus(id);
|
|
4572
|
-
};
|
|
4573
|
-
}, [focusCtx, id]);
|
|
4574
|
-
return { focused: isFocused, focus };
|
|
4575
|
-
}
|
|
4576
|
-
function useFocusable(options = {}) {
|
|
4577
|
-
const { disabled, onFocus, onBlur, onKeyPress } = options;
|
|
4578
|
-
const focusCtx = useContext(FocusContext);
|
|
4579
|
-
const inputCtx = useContext(InputContext);
|
|
4580
|
-
const nodeRef = useRef(null);
|
|
4581
|
-
const focusIdRef = useRef(null);
|
|
4582
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
4583
|
-
const onFocusRef = useRef(onFocus);
|
|
4584
|
-
const onBlurRef = useRef(onBlur);
|
|
4585
|
-
const onKeyPressRef = useRef(onKeyPress);
|
|
4586
|
-
onFocusRef.current = onFocus;
|
|
4587
|
-
onBlurRef.current = onBlur;
|
|
4588
|
-
onKeyPressRef.current = onKeyPress;
|
|
4589
|
-
const ref = useCallback((node) => {
|
|
4590
|
-
nodeRef.current = node;
|
|
4591
|
-
if (node) {
|
|
4592
|
-
focusIdRef.current = node.focusId ?? null;
|
|
4593
|
-
} else {
|
|
4594
|
-
focusIdRef.current = null;
|
|
4595
|
-
}
|
|
4596
|
-
}, []);
|
|
4597
|
-
useEffect(() => {
|
|
4598
|
-
if (!focusCtx || !focusIdRef.current || !nodeRef.current) return;
|
|
4599
|
-
return focusCtx.register(focusIdRef.current, nodeRef.current);
|
|
4600
|
-
}, [focusCtx]);
|
|
4601
|
-
useEffect(() => {
|
|
4602
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
4603
|
-
focusCtx.setSkippable(focusIdRef.current, !!disabled);
|
|
4604
|
-
}, [focusCtx, disabled]);
|
|
4605
|
-
useEffect(() => {
|
|
4606
|
-
if (!focusCtx || !focusIdRef.current) return;
|
|
4607
|
-
const fid = focusIdRef.current;
|
|
4608
|
-
const initiallyFocused = focusCtx.focusedId === fid;
|
|
4609
|
-
setIsFocused(initiallyFocused);
|
|
4610
|
-
return focusCtx.onFocusChange((newId) => {
|
|
4611
|
-
const nowFocused = newId === fid;
|
|
4612
|
-
setIsFocused((wasFocused) => {
|
|
4613
|
-
if (nowFocused && !wasFocused) {
|
|
4614
|
-
onFocusRef.current?.();
|
|
4615
|
-
} else if (!nowFocused && wasFocused) {
|
|
4616
|
-
onBlurRef.current?.();
|
|
4617
|
-
}
|
|
4618
|
-
return nowFocused;
|
|
4619
|
-
});
|
|
4620
|
-
});
|
|
4621
|
-
}, [focusCtx]);
|
|
4622
|
-
useEffect(() => {
|
|
4623
|
-
if (!inputCtx || !focusIdRef.current || disabled) return;
|
|
4624
|
-
const fid = focusIdRef.current;
|
|
4625
|
-
const handler = (key) => {
|
|
4626
|
-
if (focusCtx?.focusedId !== fid) return false;
|
|
4627
|
-
return onKeyPressRef.current?.(key) === true;
|
|
4628
|
-
};
|
|
4629
|
-
return inputCtx.registerInputHandler(fid, handler);
|
|
4630
|
-
}, [inputCtx, focusCtx, disabled]);
|
|
4631
|
-
const focus = useCallback(() => {
|
|
4632
|
-
if (focusCtx && focusIdRef.current) {
|
|
4633
|
-
focusCtx.requestFocus(focusIdRef.current);
|
|
4634
|
-
}
|
|
4635
|
-
}, [focusCtx]);
|
|
4636
|
-
return {
|
|
4637
|
-
ref,
|
|
4638
|
-
isFocused,
|
|
4639
|
-
focus,
|
|
4640
|
-
focusId: focusIdRef.current
|
|
4641
|
-
};
|
|
4642
|
-
}
|
|
4643
|
-
function useApp() {
|
|
4644
|
-
const ctx = useContext(AppContext);
|
|
4645
|
-
if (!ctx) {
|
|
4646
|
-
throw new Error("useApp must be used within a Glyph render tree");
|
|
4647
|
-
}
|
|
4648
|
-
return {
|
|
4649
|
-
exit: ctx.exit,
|
|
4650
|
-
get columns() {
|
|
4651
|
-
return ctx.columns;
|
|
4652
|
-
},
|
|
4653
|
-
get rows() {
|
|
4654
|
-
return ctx.rows;
|
|
4655
|
-
}
|
|
4656
|
-
};
|
|
4657
|
-
}
|
|
4658
|
-
function useFocusRegistry() {
|
|
4659
|
-
const focusCtx = useContext(FocusContext);
|
|
4660
|
-
const layoutCtx = useContext(LayoutContext);
|
|
4661
|
-
const [elements, setElements] = useState([]);
|
|
4662
|
-
const updateRef = useRef(() => {
|
|
4663
|
-
});
|
|
4664
|
-
const updateElements = useCallback(() => {
|
|
4665
|
-
if (!focusCtx) return;
|
|
4666
|
-
const registered = focusCtx.getActiveElements?.() ?? focusCtx.getRegisteredElements?.() ?? [];
|
|
4667
|
-
const mapped = registered.map(({ id, node }) => ({
|
|
4668
|
-
id,
|
|
4669
|
-
node,
|
|
4670
|
-
layout: layoutCtx?.getLayout(node) ?? node.layout,
|
|
4671
|
-
type: node.type
|
|
4672
|
-
}));
|
|
4673
|
-
mapped.sort((a, b) => {
|
|
4674
|
-
if (a.layout.y !== b.layout.y) {
|
|
4675
|
-
return a.layout.y - b.layout.y;
|
|
4676
|
-
}
|
|
4677
|
-
return a.layout.x - b.layout.x;
|
|
4678
|
-
});
|
|
4679
|
-
setElements(mapped);
|
|
4680
|
-
}, [focusCtx, layoutCtx]);
|
|
4681
|
-
updateRef.current = updateElements;
|
|
4682
|
-
useEffect(() => {
|
|
4683
|
-
if (!focusCtx) return;
|
|
4684
|
-
updateElements();
|
|
4685
|
-
const unsubscribe = focusCtx.onFocusChange(() => {
|
|
4686
|
-
updateElements();
|
|
4687
|
-
});
|
|
4688
|
-
const timer = setTimeout(updateElements, 50);
|
|
4689
|
-
return () => {
|
|
4690
|
-
unsubscribe();
|
|
4691
|
-
clearTimeout(timer);
|
|
4692
|
-
};
|
|
4693
|
-
}, [focusCtx, layoutCtx, updateElements]);
|
|
4694
|
-
if (!focusCtx) return null;
|
|
4695
|
-
return {
|
|
4696
|
-
elements,
|
|
4697
|
-
focusedId: focusCtx.focusedId,
|
|
4698
|
-
requestFocus: focusCtx.requestFocus,
|
|
4699
|
-
focusNext: focusCtx.focusNext,
|
|
4700
|
-
focusPrev: focusCtx.focusPrev,
|
|
4701
|
-
refresh: () => updateRef.current()
|
|
4702
|
-
};
|
|
4703
|
-
}
|
|
4704
|
-
|
|
4705
|
-
// src/utils/mask.ts
|
|
4706
|
-
function parseMask(mask) {
|
|
4707
|
-
const result = [];
|
|
4708
|
-
for (const char of mask) {
|
|
4709
|
-
switch (char) {
|
|
4710
|
-
case "9":
|
|
4711
|
-
result.push({ type: "digit", char });
|
|
4712
|
-
break;
|
|
4713
|
-
case "a":
|
|
4714
|
-
result.push({ type: "letter", char });
|
|
4715
|
-
break;
|
|
4716
|
-
case "*":
|
|
4717
|
-
result.push({ type: "alphanumeric", char });
|
|
4718
|
-
break;
|
|
4719
|
-
default:
|
|
4720
|
-
result.push({ type: "literal", char });
|
|
4721
|
-
break;
|
|
4722
|
-
}
|
|
4723
|
-
}
|
|
4724
|
-
return result;
|
|
4725
|
-
}
|
|
4726
|
-
function isValidChar(char, type) {
|
|
4727
|
-
switch (type) {
|
|
4728
|
-
case "digit":
|
|
4729
|
-
return /\d/.test(char);
|
|
4730
|
-
case "letter":
|
|
4731
|
-
return /[a-zA-Z]/.test(char);
|
|
4732
|
-
case "alphanumeric":
|
|
4733
|
-
return /[a-zA-Z0-9]/.test(char);
|
|
4734
|
-
case "literal":
|
|
4735
|
-
return true;
|
|
4736
|
-
}
|
|
4737
|
-
}
|
|
4738
|
-
function createMask(maskOrOptions) {
|
|
4739
|
-
const options = typeof maskOrOptions === "string" ? { mask: maskOrOptions } : maskOrOptions;
|
|
4740
|
-
const { mask, placeholder = "_", showPlaceholder = false } = options;
|
|
4741
|
-
const maskChars = parseMask(mask);
|
|
4742
|
-
return (newValue, _oldValue) => {
|
|
4743
|
-
const inputChars = [];
|
|
4744
|
-
for (const char of newValue) {
|
|
4745
|
-
if (char !== placeholder && !/[\s\-\(\)\/\.\:]/.test(char) || /[a-zA-Z0-9]/.test(char)) {
|
|
4746
|
-
if (/[a-zA-Z0-9]/.test(char)) {
|
|
4747
|
-
inputChars.push(char);
|
|
4748
|
-
}
|
|
4749
|
-
}
|
|
4750
|
-
}
|
|
4751
|
-
let result = "";
|
|
4752
|
-
let inputIndex = 0;
|
|
4753
|
-
for (const maskChar of maskChars) {
|
|
4754
|
-
if (maskChar.type === "literal") {
|
|
4755
|
-
if (inputIndex < inputChars.length || showPlaceholder) {
|
|
4756
|
-
result += maskChar.char;
|
|
4757
|
-
}
|
|
4758
|
-
} else {
|
|
4759
|
-
if (inputIndex < inputChars.length) {
|
|
4760
|
-
const char = inputChars[inputIndex];
|
|
4761
|
-
if (isValidChar(char, maskChar.type)) {
|
|
4762
|
-
result += char;
|
|
4763
|
-
inputIndex++;
|
|
4764
|
-
} else {
|
|
4765
|
-
inputIndex++;
|
|
4766
|
-
continue;
|
|
4767
|
-
}
|
|
4768
|
-
} else if (showPlaceholder) {
|
|
4769
|
-
result += placeholder;
|
|
4770
|
-
}
|
|
4771
|
-
}
|
|
4772
|
-
}
|
|
4773
|
-
return result;
|
|
4774
|
-
};
|
|
4775
|
-
}
|
|
4776
|
-
var masks = {
|
|
4777
|
-
/** US Phone: (123) 456-7890 */
|
|
4778
|
-
usPhone: createMask("(999) 999-9999"),
|
|
4779
|
-
/** International Phone: +1 234 567 8900 */
|
|
4780
|
-
intlPhone: createMask("+9 999 999 9999"),
|
|
4781
|
-
/** Date MM/DD/YYYY */
|
|
4782
|
-
dateUS: createMask("99/99/9999"),
|
|
4783
|
-
/** Date DD/MM/YYYY */
|
|
4784
|
-
dateEU: createMask("99/99/9999"),
|
|
4785
|
-
/** Date YYYY-MM-DD */
|
|
4786
|
-
dateISO: createMask("9999-99-99"),
|
|
4787
|
-
/** Time HH:MM */
|
|
4788
|
-
time: createMask("99:99"),
|
|
4789
|
-
/** Time HH:MM:SS */
|
|
4790
|
-
timeFull: createMask("99:99:99"),
|
|
4791
|
-
/** Credit Card: 1234 5678 9012 3456 */
|
|
4792
|
-
creditCard: createMask("9999 9999 9999 9999"),
|
|
4793
|
-
/** SSN: 123-45-6789 */
|
|
4794
|
-
ssn: createMask("999-99-9999"),
|
|
4795
|
-
/** ZIP Code: 12345 */
|
|
4796
|
-
zip: createMask("99999"),
|
|
4797
|
-
/** ZIP+4: 12345-6789 */
|
|
4798
|
-
zipPlus4: createMask("99999-9999"),
|
|
4799
|
-
/** IPv4: 192.168.001.001 */
|
|
4800
|
-
ipv4: createMask("999.999.999.999"),
|
|
4801
|
-
/** MAC Address: AA:BB:CC:DD:EE:FF */
|
|
4802
|
-
mac: createMask("**:**:**:**:**:**")
|
|
4803
|
-
};
|
|
4804
|
-
|
|
4805
|
-
export { Box, Button, Checkbox, DialogHost, FocusScope, Input, JumpNav, Keybind, List, Menu, Portal, Progress, Radio, ScrollView, Select, Spacer, Spinner, Text, ToastHost, createMask, masks, render, useApp, useDialog, useFocus, useFocusRegistry, useFocusable, useInput, useLayout, useToast };
|
|
4806
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import ee,{createContext,forwardRef,useState,useContext,useRef,useEffect,useLayoutEffect,useMemo,useCallback}from'react';import An from'react-reconciler';import ue from'string-width';import nn,{FlexDirection,Justify,Align,Direction,Edge,Wrap,Gutter,PositionType,Overflow,MeasureMode}from'yoga-layout';var Hn=0;function Lt(){return `glyph-focus-${Hn++}`}function Bt(e,t){let n=t.style??{};return {type:e,props:t,style:n,children:[],rawTextChildren:[],parent:null,yogaNode:null,text:null,layout:{x:0,y:0,width:0,height:0,innerX:0,innerY:0,innerWidth:0,innerHeight:0},focusId:e==="input"||t.focusable?Lt():null,hidden:false}}function st(e,t){t.parent=e,e.children.push(t);}function Mt(e,t){let n=e.children.indexOf(t);n!==-1&&(e.children.splice(n,1),t.parent=null);}function Vt(e,t,n){t.parent=e;let r=e.children.indexOf(n);r!==-1?e.children.splice(r,0,t):e.children.push(t);}function qe(e){let t={},n=e;for(;n;){let r=n.style;t.color===void 0&&r.color!==void 0&&(t.color=r.color),t.bg===void 0&&r.bg!==void 0&&(t.bg=r.bg),t.bold===void 0&&r.bold!==void 0&&(t.bold=r.bold),t.dim===void 0&&r.dim!==void 0&&(t.dim=r.dim),t.italic===void 0&&r.italic!==void 0&&(t.italic=r.italic),t.underline===void 0&&r.underline!==void 0&&(t.underline=r.underline),n=n.parent;}return t}function it(e){if(e.text!=null)return e.text;let t="";for(let n of e.children)t+=it(n);return t}var lt=32,Ht={supportsMutation:true,supportsPersistence:false,supportsHydration:false,isPrimaryRenderer:true,scheduleTimeout:setTimeout,cancelTimeout:clearTimeout,noTimeout:-1,supportsMicrotasks:true,scheduleMicrotask:queueMicrotask,getCurrentUpdatePriority:()=>lt,setCurrentUpdatePriority:e=>{},resolveUpdatePriority:()=>lt,getCurrentEventPriority:()=>lt,resolveEventType:()=>null,resolveEventTimeStamp:()=>-1.1,shouldAttemptEagerTransition:()=>false,getInstanceFromNode:()=>null,beforeActiveInstanceBlur:()=>{},afterActiveInstanceBlur:()=>{},prepareScopeUpdate:()=>{},getInstanceFromScope:()=>null,detachDeletedInstance:()=>{},requestPostPaintCallback:e=>{},maySuspendCommit:(e,t)=>false,preloadInstance:(e,t)=>true,startSuspendingCommit:()=>{},suspendInstance:(e,t)=>{},waitForCommitToBeReady:()=>null,NotPendingTransition:null,HostTransitionContext:{$$typeof:Symbol.for("react.context"),_currentValue:null},resetFormInstance:e=>{},bindToConsole:(e,t,n)=>Function.prototype.bind.call(console[e],console,...t),supportsResources:false,isHostHoistableType:(e,t)=>false,supportsSingletons:false,isHostSingletonType:e=>false,supportsTestSelectors:false,createInstance(e,t,n,r,o){return Bt(e,t)},createTextInstance(e,t,n,r){return {type:"raw-text",text:e,parent:null}},appendInitialChild(e,t){if(t.type==="raw-text"){let n=t;n.parent=e,e.rawTextChildren.push(n),e.text=e.rawTextChildren.map(r=>r.text).join("");}else st(e,t);},finalizeInitialChildren(e,t,n,r,o){return false},shouldSetTextContent(e,t){return false},getRootHostContext(e){return {}},getChildHostContext(e,t,n){return e},getPublicInstance(e){return e},prepareForCommit(e){return null},resetAfterCommit(e){e.onCommit();},preparePortalMount(){},appendChild(e,t){if(t.type==="raw-text"){let n=t;n.parent=e,e.rawTextChildren.push(n),e.text=e.rawTextChildren.map(r=>r.text).join("");}else st(e,t);},appendChildToContainer(e,t){if(t.type==="raw-text")return;let n=t;n.parent=null,e.children.push(n);},insertBefore(e,t,n){t.type==="raw-text"||n.type==="raw-text"||Vt(e,t,n);},insertInContainerBefore(e,t,n){if(t.type==="raw-text"||n.type==="raw-text")return;let r=t,o=n,s=e.children.indexOf(o);s!==-1?e.children.splice(s,0,r):e.children.push(r);},removeChild(e,t){if(t.type==="raw-text"){let n=t;n.parent=null;let r=e.rawTextChildren.indexOf(n);r!==-1&&e.rawTextChildren.splice(r,1),e.text=e.rawTextChildren.map(o=>o.text).join("")||null;return}Mt(e,t);},removeChildFromContainer(e,t){if(t.type==="raw-text")return;let n=t,r=e.children.indexOf(n);r!==-1&&e.children.splice(r,1);},commitTextUpdate(e,t,n){e.text=n,e.parent&&(e.parent.text=e.parent.rawTextChildren.map(r=>r.text).join(""));},commitUpdate(e,t,n,r,o){e.props=r,e.style=r.style??{},r.focusable&&!e.focusId&&(e.focusId=`focus-${Math.random().toString(36).slice(2,9)}`);},hideInstance(e){e.hidden=true;},hideTextInstance(e){e.text="";},unhideInstance(e,t){e.hidden=false;},unhideTextInstance(e,t){e.text=t;},clearContainer(e){e.children.length=0;},resetTextContent(e){e.text=null;}};var Ie=An(Ht);Ie.injectIntoDevTools({bundleType:process.env.NODE_ENV==="production"?0:1,version:"0.1.0",rendererPackageName:"glyph"});var _e=class{stdout;stdin;wasRaw=false;cleanedUp=false;dataHandlers=new Set;stdinAttached=false;oscState="normal";oscAccum="";escFlushTimer=null;palette=new Map;paletteResolve=null;constructor(t=process.stdout,n=process.stdin){this.stdout=t,this.stdin=n;}get columns(){return this.stdout.columns||80}get rows(){return this.stdout.rows||24}enterRawMode(){this.stdin.isTTY&&(this.wasRaw=this.stdin.isRaw,this.stdin.setRawMode(true),this.stdin.resume(),this.stdin.setEncoding("utf-8"));}exitRawMode(){this.stdin.isTTY&&!this.wasRaw&&(this.stdin.setRawMode(false),this.stdin.pause());}write(t){this.stdout.write(t);}hideCursor(){this.write("\x1B[?25l");}showCursor(){this.write("\x1B[?25h");}moveCursor(t,n){this.write(`\x1B[${n+1};${t+1}H`);}setCursorColor(t){this.write(`\x1B]12;${t}\x07`);}resetCursorColor(){this.write("\x1B]112\x07");}enterAltScreen(){this.write("\x1B[?1049h");}exitAltScreen(){this.write("\x1B[?1049l");}clearScreen(){this.write("\x1B[2J\x1B[H");}resetStyles(){this.write("\x1B[0m");}enableKittyKeyboard(){this.write("\x1B[>1u");}disableKittyKeyboard(){this.write("\x1B[<u");}setup(){this.enterRawMode(),this.enterAltScreen(),this.enableKittyKeyboard(),this.hideCursor(),this.clearScreen(),this.attachStdinListener(),this.installCleanupHandlers();}cleanup(){this.cleanedUp||(this.cleanedUp=true,this.escFlushTimer!==null&&(clearTimeout(this.escFlushTimer),this.escFlushTimer=null),this.resetStyles(),this.resetCursorColor(),this.disableKittyKeyboard(),this.showCursor(),this.exitAltScreen(),this.exitRawMode());}suspend(){this.escFlushTimer!==null&&(clearTimeout(this.escFlushTimer),this.escFlushTimer=null),this.oscState="normal",this.oscAccum="",this.resetStyles(),this.resetCursorColor(),this.disableKittyKeyboard(),this.showCursor(),this.exitAltScreen(),this.exitRawMode();}resume(){this.enterRawMode(),this.enterAltScreen(),this.enableKittyKeyboard(),this.hideCursor(),this.clearScreen();}attachStdinListener(){this.stdinAttached||(this.stdinAttached=true,this.stdin.on("data",t=>{let n=typeof t=="string"?t:t.toString("utf-8");this.dispatchFiltered(n);}));}onData(t){return this.dataHandlers.add(t),()=>{this.dataHandlers.delete(t);}}dispatchFiltered(t){this.escFlushTimer!==null&&(clearTimeout(this.escFlushTimer),this.escFlushTimer=null);let n=this.filterOsc(t);if(n.length>0)for(let r of this.dataHandlers)r(n);this.oscState==="esc"&&(this.escFlushTimer=setTimeout(()=>{this.escFlushTimer=null,this.oscState="normal";for(let r of this.dataHandlers)r("\x1B");},50));}filterOsc(t){let n="";for(let r=0;r<t.length;r++){let o=t[r],s=t.charCodeAt(r);switch(this.oscState){case "normal":s===27?this.oscState="esc":n+=o;break;case "esc":o==="]"?(this.oscState="osc",this.oscAccum=""):(n+="\x1B"+o,this.oscState="normal");break;case "osc":s===7?(this.handleOscResponse(this.oscAccum),this.oscAccum="",this.oscState="normal"):s===27?this.oscState="osc_esc":this.oscAccum+=o;break;case "osc_esc":o==="\\"?(this.handleOscResponse(this.oscAccum),this.oscAccum="",this.oscState="normal"):(this.oscAccum+="\x1B"+o,this.oscState="osc");break}}return n}handleOscResponse(t){let n=t.match(/^4;(\d+);rgb:([0-9a-fA-F]+)\/([0-9a-fA-F]+)\/([0-9a-fA-F]+)/);if(n){let r=parseInt(n[1],10),o=parseInt(n[2].substring(0,2),16),s=parseInt(n[3].substring(0,2),16),i=parseInt(n[4].substring(0,2),16);this.palette.set(r,[o,s,i]),this.palette.size>=16&&this.paletteResolve&&(this.paletteResolve(),this.paletteResolve=null);}}queryPalette(){return new Promise(t=>{let n=()=>t(this.palette),r=setTimeout(n,200);this.paletteResolve=()=>{clearTimeout(r),n();};let o="";for(let s=0;s<16;s++)o+=`\x1B]4;${s};?\x07`;this.write(o);})}onResize(t){return this.stdout.on("resize",t),()=>{this.stdout.off("resize",t);}}installCleanupHandlers(){let t=()=>this.cleanup();process.on("exit",t);let n=r=>{t(),process.kill(process.pid,r);};process.once("SIGINT",()=>n("SIGINT")),process.once("SIGTERM",()=>n("SIGTERM")),process.on("uncaughtException",r=>{t(),console.error(r),process.exit(1);}),process.on("unhandledRejection",r=>{t(),console.error(r),process.exit(1);});}};function At(e){switch(e){case 9:return "tab";case 13:return "return";case 27:return "escape";case 32:return " ";case 127:return "backspace";case 57358:return "capslock";case 57359:return "scrolllock";case 57360:return "numlock";case 57361:return "printscreen";case 57362:return "pause";case 57363:return "menu";case 57364:return "f13";case 57365:return "f14";case 57366:return "f15";case 57367:return "f16";case 57368:return "f17";case 57369:return "f18";case 57370:return "f19";case 57371:return "f20";case 57372:return "f21";case 57373:return "f22";case 57374:return "f23";case 57375:return "f24";case 57376:return "f25";case 57399:return "kp0";case 57400:return "kp1";case 57401:return "kp2";case 57402:return "kp3";case 57403:return "kp4";case 57404:return "kp5";case 57405:return "kp6";case 57406:return "kp7";case 57407:return "kp8";case 57408:return "kp9";case 57409:return "kpdecimal";case 57410:return "kpdivide";case 57411:return "kpmultiply";case 57412:return "kpminus";case 57413:return "kpplus";case 57414:return "kpenter";case 57415:return "kpequal";case 57416:return "kpleft";case 57417:return "kpright";case 57418:return "kpup";case 57419:return "kpdown";case 57420:return "kppageup";case 57421:return "kppagedown";case 57422:return "kphome";case 57423:return "kpend";case 57424:return "kpinsert";case 57425:return "kpdelete";case 57428:return "mediaplaypause";case 57429:return "mediastop";case 57430:return "mediaprev";case 57431:return "medianext";case 57432:return "mediarewind";case 57433:return "mediafastforward";case 57434:return "mediamute";case 57435:return "volumedown";case 57436:return "volumeup";default:return e>=32&&e<=126?String.fromCharCode(e).toLowerCase():"unknown"}}function jn(e){switch(e.split(";")[0]){case "1":return "home";case "2":return "insert";case "3":return "delete";case "4":return "end";case "5":return "pageup";case "6":return "pagedown";case "7":return "home";case "8":return "end";case "11":return "f1";case "12":return "f2";case "13":return "f3";case "14":return "f4";case "15":return "f5";case "17":return "f6";case "18":return "f7";case "19":return "f8";case "20":return "f9";case "21":return "f10";case "23":return "f11";case "24":return "f12";case "25":return "f13";case "26":return "f14";case "28":return "f15";case "29":return "f16";case "31":return "f17";case "32":return "f18";case "33":return "f19";case "34":return "f20";default:return "unknown"}}function $e(e,t){let n=t-1;n&1&&(e.shift=true),n&2&&(e.alt=true),n&4&&(e.ctrl=true),n&8&&(e.meta=true);}function jt(e){let t=[],n=0;for(;n<e.length;){let r=e[n],o=e.charCodeAt(n);if(r==="\x1B"){if(e[n+1]==="["){let s=Dn(e,n);if(s){t.push(s.key),n=s.end;continue}}if(e[n+1]==="O"){let s=On(e,n);if(s){t.push(s.key),n=s.end;continue}}if(n+1<e.length&&e.charCodeAt(n+1)>=32){t.push({name:e[n+1].toLowerCase(),sequence:e.substring(n,n+2),alt:true}),n+=2;continue}t.push({name:"escape",sequence:"\x1B"}),n++;continue}if(o>=1&&o<=26){let s=String.fromCharCode(o+96);o===13?t.push({name:"return",sequence:"\r"}):o===9?t.push({name:"tab",sequence:" "}):o===8?t.push({name:"backspace",sequence:"\b"}):t.push({name:s,sequence:r,ctrl:true}),n++;continue}if(o===127){t.push({name:"backspace",sequence:r}),n++;continue}t.push({name:r,sequence:r}),n++;}return t}function On(e,t){if(t+2>=e.length)return null;let n=e[t+2],r=e.substring(t,t+3),o;switch(n){case "A":o={name:"up",sequence:r};break;case "B":o={name:"down",sequence:r};break;case "C":o={name:"right",sequence:r};break;case "D":o={name:"left",sequence:r};break;case "H":o={name:"home",sequence:r};break;case "F":o={name:"end",sequence:r};break;case "P":o={name:"f1",sequence:r};break;case "Q":o={name:"f2",sequence:r};break;case "R":o={name:"f3",sequence:r};break;case "S":o={name:"f4",sequence:r};break;case "j":o={name:"kpmultiply",sequence:r};break;case "k":o={name:"kpplus",sequence:r};break;case "l":o={name:"kpcomma",sequence:r};break;case "m":o={name:"kpminus",sequence:r};break;case "n":o={name:"kpdecimal",sequence:r};break;case "o":o={name:"kpdivide",sequence:r};break;case "p":o={name:"kp0",sequence:r};break;case "q":o={name:"kp1",sequence:r};break;case "r":o={name:"kp2",sequence:r};break;case "s":o={name:"kp3",sequence:r};break;case "t":o={name:"kp4",sequence:r};break;case "u":o={name:"kp5",sequence:r};break;case "v":o={name:"kp6",sequence:r};break;case "w":o={name:"kp7",sequence:r};break;case "x":o={name:"kp8",sequence:r};break;case "y":o={name:"kp9",sequence:r};break;case "M":o={name:"kpenter",sequence:r};break;default:return null}return {key:o,end:t+3}}function Dn(e,t){let n=t+2,r="";for(;n<e.length;){let c=e.charCodeAt(n);if(c>=48&&c<=63)r+=e[n],n++;else break}if(n>=e.length)return null;let o=e[n],s=e.substring(t,n+1);n++;let i;switch(o){case "A":i={name:"up",sequence:s};break;case "B":i={name:"down",sequence:s};break;case "C":i={name:"right",sequence:s};break;case "D":i={name:"left",sequence:s};break;case "H":i={name:"home",sequence:s};break;case "F":i={name:"end",sequence:s};break;case "Z":i={name:"tab",sequence:s,shift:true};break;case "P":i={name:"f1",sequence:s};break;case "Q":i={name:"f2",sequence:s};break;case "R":i={name:"f3",sequence:s};break;case "S":i={name:"f4",sequence:s};break;case "~":{if(r.startsWith("27;")){let c=r.split(";"),l=parseInt(c[1]??"1",10),a=parseInt(c[2]??"0",10);i={name:At(a),sequence:s},$e(i,l);break}if(i={name:jn(r),sequence:s},r.includes(";")){let c=r.split(";"),l=parseInt(c[1]??"1",10);$e(i,l);}break}case "u":{let c=r.split(";"),l=parseInt(c[0]??"0",10),a=parseInt(c[1]??"1",10);i={name:At(l),sequence:s},$e(i,a);break}case "I":i={name:"focus",sequence:s};break;case "O":i={name:"blur",sequence:s};break;default:i={name:"unknown",sequence:s};}if(r.includes(";")&&!["~","u"].includes(o)){let c=r.split(";"),l=parseInt(c[c.length-1]??"1",10);l>=1&&l<=16&&$e(i,l);}return {key:i,end:n}}var Kn={black:"\x1B[30m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",blackBright:"\x1B[90m",redBright:"\x1B[91m",greenBright:"\x1B[92m",yellowBright:"\x1B[93m",blueBright:"\x1B[94m",magentaBright:"\x1B[95m",cyanBright:"\x1B[96m",whiteBright:"\x1B[97m"},Wn={black:"\x1B[40m",red:"\x1B[41m",green:"\x1B[42m",yellow:"\x1B[43m",blue:"\x1B[44m",magenta:"\x1B[45m",cyan:"\x1B[46m",white:"\x1B[47m",blackBright:"\x1B[100m",redBright:"\x1B[101m",greenBright:"\x1B[102m",yellowBright:"\x1B[103m",blueBright:"\x1B[104m",magentaBright:"\x1B[105m",cyanBright:"\x1B[106m",whiteBright:"\x1B[107m"};function ct(e){let t=e.replace("#",""),n=parseInt(t.substring(0,2),16),r=parseInt(t.substring(2,4),16),o=parseInt(t.substring(4,6),16);return {r:n,g:r,b:o}}function Ot(e){if(typeof e=="string"){if(e.startsWith("#")){let{r:o,g:s,b:i}=ct(e);return `\x1B[38;2;${o};${s};${i}m`}return Kn[e]??"\x1B[39m"}if(typeof e=="number")return `\x1B[38;5;${e}m`;let{r:t,g:n,b:r}=e;return `\x1B[38;2;${t};${n};${r}m`}function Dt(e){if(typeof e=="string"){if(e.startsWith("#")){let{r:o,g:s,b:i}=ct(e);return `\x1B[48;2;${o};${s};${i}m`}return Wn[e]??"\x1B[49m"}if(typeof e=="number")return `\x1B[48;5;${e}m`;let{r:t,g:n,b:r}=e;return `\x1B[48;2;${t};${n};${r}m`}var Kt={black:[0,0,0],red:[170,0,0],green:[0,170,0],yellow:[170,170,0],blue:[0,0,170],magenta:[170,0,170],cyan:[0,170,170],white:[170,170,170],blackBright:[85,85,85],redBright:[255,85,85],greenBright:[85,255,85],yellowBright:[255,255,85],blueBright:[85,85,255],magentaBright:[255,85,255],cyanBright:[85,255,255],whiteBright:[255,255,255]},Wt=["black","red","green","yellow","blue","magenta","cyan","white","blackBright","redBright","greenBright","yellowBright","blueBright","magentaBright","cyanBright","whiteBright"],Te=null;function qt(e){e.size>0&&(Te=e);}function qn(e){if(Te){let t=Wt.indexOf(e);if(t!==-1){let n=Te.get(t);if(n)return n}}return Kt[e]??null}function _n(e){if(typeof e=="string"){if(e.startsWith("#")){let t=ct(e);return [t.r,t.g,t.b]}return qn(e)}if(typeof e=="number"){if(e<16){if(Te){let s=Te.get(e);if(s)return s}return Kt[Wt[e]]}if(e>=232){let s=(e-232)*10+8;return [s,s,s]}let t=e-16,n=t%6*51,r=Math.floor(t/6)%6*51;return [Math.floor(t/36)*51,r,n]}return [e.r,e.g,e.b]}function ze(e){let t=_n(e);if(!t)return false;let[n,r,o]=t.map(i=>{let c=i/255;return c<=.03928?c/12.92:Math.pow((c+.055)/1.055,2.4)});return .2126*n+.7152*r+.0722*o>.4}function ut(e,t){return e===t?true:e==null||t==null?false:typeof e=="object"&&typeof t=="object"?e.r===t.r&&e.g===t.g&&e.b===t.b:false}function _t(e){return e&&ze(e)?"black":"white"}var Ne=class e{width;height;cells;constructor(t,n){this.width=t,this.height=n,this.cells=new Array(t*n),this.clear();}clear(){for(let t=0;t<this.cells.length;t++)this.cells[t]={ch:" "};}resize(t,n){this.width=t,this.height=n,this.cells=new Array(t*n),this.clear();}get(t,n){if(!(t<0||t>=this.width||n<0||n>=this.height))return this.cells[n*this.width+t]}set(t,n,r){t<0||t>=this.width||n<0||n>=this.height||(this.cells[n*this.width+t]=r);}setChar(t,n,r,o,s,i,c,l,a){t<0||t>=this.width||n<0||n>=this.height||(this.cells[n*this.width+t]={ch:r,fg:o,bg:s,bold:i,dim:c,italic:l,underline:a});}fillRect(t,n,r,o,s,i,c){for(let l=n;l<n+o;l++)for(let a=t;a<t+r;a++)this.setChar(a,l,s,i,c);}clone(){let t=new e(this.width,this.height);for(let n=0;n<this.cells.length;n++){let r=this.cells[n];t.cells[n]={...r};}return t}cellsEqual(t,n){return t.ch===n.ch&&ut(t.fg,n.fg)&&ut(t.bg,n.bg)&&(t.bold??false)===(n.bold??false)&&(t.dim??false)===(n.dim??false)&&(t.italic??false)===(n.italic??false)&&(t.underline??false)===(n.underline??false)}};var $n={single:{topLeft:"\u250C",topRight:"\u2510",bottomLeft:"\u2514",bottomRight:"\u2518",horizontal:"\u2500",vertical:"\u2502"},double:{topLeft:"\u2554",topRight:"\u2557",bottomLeft:"\u255A",bottomRight:"\u255D",horizontal:"\u2550",vertical:"\u2551"},round:{topLeft:"\u256D",topRight:"\u256E",bottomLeft:"\u2570",bottomRight:"\u256F",horizontal:"\u2500",vertical:"\u2502"},ascii:{topLeft:"+",topRight:"+",bottomLeft:"+",bottomRight:"+",horizontal:"-",vertical:"|"}};function $t(e){return e==="none"?null:$n[e]}function zt(e,t,n,r){if(e.length===0)return {width:0,height:0};let o=e.split(`
|
|
2
|
+
`);if(n===MeasureMode.Undefined||r==="none"){let l=0;for(let a of o){let p=ue(a);p>l&&(l=p);}return {width:l,height:o.length}}let s=Math.max(1,Math.floor(t)),i=ce(o,s,r),c=0;for(let l of i){let a=ue(l);a>c&&(c=a);}return {width:c,height:i.length}}function ce(e,t,n){let r=[];for(let o of e){if(ue(o)<=t){r.push(o);continue}if(n==="truncate"){r.push(Xt(o,t));continue}if(n==="ellipsis"){r.push(Xn(o,t));continue}let i=Jn(o,t);r.push(...i);}return r}function Xt(e,t){let n="",r=0;for(let o of e){let s=ue(o);if(r+s>t)break;n+=o,r+=s;}return n}function Xn(e,t){if(t<=1)return t===1?"\u2026":"";let n=Xt(e,t-1);return ue(n)<ue(e)?n+"\u2026":e}function Jn(e,t){let n=[],r="",o=0,s="",i=0;for(let c=0;c<=e.length;c++){let l=e[c],a=c===e.length,p=l===" ";if(a||p){if(s.length>0){if(o+i<=t)r+=s,o+=i;else if(i<=t)r.length>0&&n.push(r),r=s,o=i;else for(let d of s){let S=ue(d);o+S>t&&r.length>0&&(n.push(r),r="",o=0),r+=d,o+=S;}s="",i=0;}p&&(o+1<=t?(r+=" ",o+=1):(r.length>0&&n.push(r),r=" ",o=1));}else l&&(s+=l,i+=ue(l));}return r.length>0&&n.push(r),n.length>0?n:[""]}function Jt(e,t,n={}){t.clear();let r={},o=[],s={x:0,y:0,width:t.width,height:t.height};for(let i of e)i.hidden||Yt(i,s,i.style.zIndex??0,o);o.sort((i,c)=>i.zIndex-c.zIndex);for(let i of o){let c=Un(i.node,t,i.clip,n);c?.cursorPosition&&(r.cursorPosition=c.cursorPosition);}return r}function Yt(e,t,n,r){if(e.hidden)return;let o=e.style.zIndex??n,s=e.style.clip?Yn(t,{x:e.layout.innerX,y:e.layout.innerY,width:e.layout.innerWidth,height:e.layout.innerHeight}):t;if(r.push({node:e,clip:t,zIndex:o}),e.type!=="text"&&e.type!=="input")for(let i of e.children)Yt(i,s,o,r);}function Yn(e,t){let n=Math.max(e.x,t.x),r=Math.max(e.y,t.y),o=Math.min(e.x+e.width,t.x+t.width),s=Math.min(e.y+e.height,t.y+t.height);return {x:n,y:r,width:Math.max(0,o-n),height:Math.max(0,s-r)}}function Xe(e,t,n){return e>=n.x&&e<n.x+n.width&&t>=n.y&&t<n.y+n.height}function Un(e,t,n,r={}){let{x:o,y:s,width:i,height:c,innerX:l,innerY:a,innerWidth:p,innerHeight:d}=e.layout,S=e.style;if(i<=0||c<=0)return;let I=qe(e).bg;if(S.bg)for(let u=s;u<s+c;u++)for(let R=o;R<o+i;R++)Xe(R,u,n)&&t.setChar(R,u," ",void 0,S.bg);let f=S.border?$t(S.border):null;if(f&&i>=2&&c>=2){let u=S.borderColor,R=I;ie(t,n,o,s,f.topLeft,u,R);for(let P=o+1;P<o+i-1;P++)ie(t,n,P,s,f.horizontal,u,R);ie(t,n,o+i-1,s,f.topRight,u,R),ie(t,n,o,s+c-1,f.bottomLeft,u,R);for(let P=o+1;P<o+i-1;P++)ie(t,n,P,s+c-1,f.horizontal,u,R);ie(t,n,o+i-1,s+c-1,f.bottomRight,u,R);for(let P=s+1;P<s+c-1;P++)ie(t,n,o,P,f.vertical,u,R),ie(t,n,o+i-1,P,f.vertical,u,R);}if(e.type==="text")Zn(e,t,n);else if(e.type==="input")return Qn(e,t,n,r)}function ie(e,t,n,r,o,s,i,c,l,a,p){Xe(n,r,t)&&e.setChar(n,r,o,s,i,c,l,a,p);}function Ut(e,t){if(e!==void 0)return e;if(t!==void 0)return ze(t)?"black":"white"}function Zn(e,t,n){let{innerX:r,innerY:o,innerWidth:s,innerHeight:i}=e.layout,c=qe(e),l=it(e);if(!l)return;let a=Ut(c.color,c.bg),p=e.style.wrap??"wrap",d=e.style.textAlign??"left",S=l.split(`
|
|
3
|
+
`),v=ce(S,s,p);for(let I=0;I<v.length&&I<i;I++){let f=v[I],u=ue(f),R=0;d==="center"?R=Math.max(0,Math.floor((s-u)/2)):d==="right"&&(R=Math.max(0,s-u));let P=0;for(let E of f){let A=ue(E);A>0&&ie(t,n,r+R+P,o+I,E,a,c.bg,c.bold,c.dim,c.italic,c.underline),P+=A;}}}function Qn(e,t,n,r={}){let{cursorInfo:o,useNativeCursor:s}=r,{innerX:i,innerY:c,innerWidth:l,innerHeight:a}=e.layout;if(l<=0||a<=0)return;let p=e.props.value??e.props.defaultValue??"",d=e.props.placeholder??"",S=p||d,v=!p&&!!d,I=e.props.multiline??false,f=qe(e),u=Ut(f.color,f.bg),R=f.bg?ze(f.bg)?"blackBright":"whiteBright":"blackBright",P=v?R:u??f.color??e.style.color,E=v?R:P,A=v?true:f.dim,W=o&&o.nodeId===e.focusId,k;if(I&&!v){let G=e.style.wrap??"wrap",T=S.split(`
|
|
4
|
+
`),B=ce(T,l,G),C=0,x=0;if(W){let b=o.position,M=0,_=b,q=0;for(let X=0;X<T.length;X++){let J=T[X].length;if(b<=q+J){M=X,_=b-q;break}q+=J+1;}let z=0;for(let X=0;X<M;X++)z+=ce([T[X]],l,G).length;let j=ce([T[M]],l,G),D=0,L=0;for(let X=0;X<j.length;X++){let J=j[X];if(_<=D+J.length){L=X;break}D+=J.length;}C=z+L,x=ue(T[M].slice(D,D+(_-D)));}let w=Math.max(0,C-a+1);for(let b=0;b<a;b++){let M=w+b;if(M>=B.length)break;let _=B[M],q=0;for(let z of _){if(q>=l)break;let j=ue(z);j>0&&ie(t,n,i+q,c+b,z,E,f.bg,f.bold,A,f.italic,f.underline),q+=j;}}if(W){let b=C-w;if(b>=0&&b<a){let M=Math.min(x,l-1),_=i+M,q=c+b;if(Xe(_,q,n)&&_<i+l)if(s)k={cursorPosition:{x:_,y:q,bg:f.bg}};else {let z=t.get(_,q),j=z?.ch&&z.ch!==" "?z.ch:"\u258C",D=f.bg??"black",L=f.color??"white";t.setChar(_,q,j,D,L,z?.bold,z?.dim,z?.italic,false);}}}}else {let G=0;for(let T of S){if(G>=l)break;let B=ue(T);B>0&&ie(t,n,i+G,c,T,E,f.bg,f.bold,A,f.italic,f.underline),G+=B;}if(W){let T=Math.min(o.position,l-1),B=i+T;if(Xe(B,c,n)&&B<i+l)if(s)k={cursorPosition:{x:B,y:c,bg:f.bg}};else {let C=t.get(B,c),x=C?.ch&&C.ch!==" "?C.ch:"\u258C",w=f.bg??"black",b=f.color??"white";t.setChar(B,c,x,w,b,C?.bold,C?.dim,C?.italic,false);}}}return k}var er="\x1B",de=`${er}[`;function tr(e,t){return `${de}${t+1};${e+1}H`}function nr(e){let t=`${de}0m`;return e.bold&&(t+=`${de}1m`),e.dim&&(t+=`${de}2m`),e.italic&&(t+=`${de}3m`),e.underline&&(t+=`${de}4m`),e.fg!=null&&(t+=Ot(e.fg)),e.bg!=null&&(t+=Dt(e.bg)),t}function Zt(e,t,n){let r="",o=-1,s=-1,i="";for(let c=0;c<t.height;c++)for(let l=0;l<t.width;l++){let a=t.get(l,c);if(!n){let d=e.get(l,c);if(d&&t.cellsEqual(a,d))continue}(s!==c||o!==l)&&(r+=tr(l,c));let p=nr(a);p!==i&&(r+=p,i=p),r+=a.ch,o=l+1,s=c;}return r.length>0&&(r+=`${de}0m`),r}var ir={row:FlexDirection.Row,column:FlexDirection.Column},lr={"flex-start":Justify.FlexStart,center:Justify.Center,"flex-end":Justify.FlexEnd,"space-between":Justify.SpaceBetween,"space-around":Justify.SpaceAround},cr={"flex-start":Align.FlexStart,center:Align.Center,"flex-end":Align.FlexEnd,stretch:Align.Stretch};function tn(e,t,n){n!==void 0&&(typeof n=="string"&&n.endsWith("%"),t(n));}function ae(e,t,n){n!==void 0&&(typeof n=="string"&&n.endsWith("%")?e.setPositionPercent(t,parseFloat(n)):e.setPosition(t,n));}function ur(e,t,n){tn(e,o=>e.setWidth(o),t.width),tn(e,o=>e.setHeight(o),t.height),t.minWidth!==void 0&&e.setMinWidth(t.minWidth),t.minHeight!==void 0&&e.setMinHeight(t.minHeight),t.maxWidth!==void 0&&e.setMaxWidth(t.maxWidth),t.maxHeight!==void 0&&e.setMaxHeight(t.maxHeight),t.padding!==void 0&&e.setPadding(Edge.All,t.padding),t.paddingX!==void 0&&e.setPadding(Edge.Horizontal,t.paddingX),t.paddingY!==void 0&&e.setPadding(Edge.Vertical,t.paddingY),t.paddingTop!==void 0&&e.setPadding(Edge.Top,t.paddingTop),t.paddingRight!==void 0&&e.setPadding(Edge.Right,t.paddingRight),t.paddingBottom!==void 0&&e.setPadding(Edge.Bottom,t.paddingBottom),t.paddingLeft!==void 0&&e.setPadding(Edge.Left,t.paddingLeft);let r=t.border!=null&&t.border!=="none";e.setBorder(Edge.All,r?1:0),t.flexDirection&&e.setFlexDirection(ir[t.flexDirection]??FlexDirection.Column),t.flexWrap&&e.setFlexWrap(t.flexWrap==="wrap"?Wrap.Wrap:Wrap.NoWrap),t.justifyContent&&e.setJustifyContent(lr[t.justifyContent]??Justify.FlexStart),t.alignItems&&e.setAlignItems(cr[t.alignItems]??Align.Stretch),t.flexGrow!==void 0&&e.setFlexGrow(t.flexGrow),t.flexShrink!==void 0&&e.setFlexShrink(t.flexShrink),t.gap!==void 0&&e.setGap(Gutter.All,t.gap),t.position==="absolute"?e.setPositionType(PositionType.Absolute):e.setPositionType(PositionType.Relative),t.inset!==void 0&&(ae(e,Edge.Top,t.inset),ae(e,Edge.Right,t.inset),ae(e,Edge.Bottom,t.inset),ae(e,Edge.Left,t.inset)),ae(e,Edge.Top,t.top),ae(e,Edge.Right,t.right),ae(e,Edge.Bottom,t.bottom),ae(e,Edge.Left,t.left),t.clip&&e.setOverflow(Overflow.Hidden);}function rn(e){let t=nn.Node.create();if(e.yogaNode=t,ur(t,e.style,e.type),e.type==="text"||e.type==="input")t.setMeasureFunc((n,r,o,s)=>{let i;return e.type==="input"?(i=e.props.value??e.props.defaultValue??e.props.placeholder??"",i.length===0&&(i=" ")):i=on(e),zt(i,n,r,e.style.wrap??"wrap")});else for(let n=0;n<e.children.length;n++){let r=e.children[n];r.hidden||(rn(r),t.insertChild(r.yogaNode,t.getChildCount()));}}function on(e){if(e.text!=null)return e.text;let t="";for(let n of e.children)t+=on(n);if(t===""&&e.props.children!=null){if(typeof e.props.children=="string")return e.props.children;if(typeof e.props.children=="number")return String(e.props.children)}return t}function sn(e,t,n){let r=e.yogaNode,o=r.getComputedLayout(),s=t+o.left,i=n+o.top,c=o.width,l=o.height,a=e.style.border&&e.style.border!=="none"?1:0,p=r.getComputedPadding(Edge.Top),d=r.getComputedPadding(Edge.Right),S=r.getComputedPadding(Edge.Bottom),v=r.getComputedPadding(Edge.Left),I=s+a+v,f=i+a+p,u=Math.max(0,c-a*2-v-d),R=Math.max(0,l-a*2-p-S);e.layout={x:s,y:i,width:c,height:l,innerX:I,innerY:f,innerWidth:u,innerHeight:R};for(let P of e.children)P.hidden||!P.yogaNode||sn(P,s,i);}function ln(e,t,n){let r=nn.Node.create();r.setWidth(t),r.setHeight(n),r.setFlexDirection(FlexDirection.Column);for(let o of e)o.hidden||(rn(o),r.insertChild(o.yogaNode,r.getChildCount()));r.calculateLayout(t,n,Direction.LTR);for(let o of e)o.hidden||!o.yogaNode||sn(o,0,0);r.freeRecursive(),cn(e);}function cn(e){for(let t of e)t.yogaNode=null,cn(t.children);}var Y=createContext(null),$=createContext(null),oe=createContext(null),be=createContext(null),Ye=createContext(null);function ar(e,t={}){let n=t.stdout??process.stdout,r=t.stdin??process.stdin,o=t.debug??false,s=t.useNativeCursor??true,i=new _e(n,r);i.setup();let c=false;i.queryPalette().then(m=>{qt(m),p=true,b();});let l=new Ne(i.columns,i.rows),a=new Ne(i.columns,i.rows),p=true,d=new Set,S=new Set,v=new Map,I={subscribe(m){return d.add(m),()=>d.delete(m)},subscribePriority(m){return S.add(m),()=>S.delete(m)},registerInputHandler(m,g){return v.set(m,g),()=>v.delete(m)}},f=null,u=new Map,R=[],P=new Set,E=[],A=new Set;function W(m){if(f!==m){f=m,b();for(let g of A)g(f);}}function k(){let m=[...R];if(E.length>0){let g=E[E.length-1];m=m.filter(y=>g.has(y));}return m=m.filter(g=>!P.has(g)),m.sort((g,y)=>{let O=u.get(g),K=u.get(y);if(!O||!K)return 0;let F=O.layout,h=K.layout;return F.y!==h.y?F.y-h.y:F.x-h.x}),m}let G={get focusedId(){return f},register(m,g){if(u.set(m,g),R.includes(m)||R.push(m),E.length>0&&E[E.length-1].add(m),f===null){let y=k();y.length>0&&W(y[0]);}return ()=>{u.delete(m);let y=R.indexOf(m);if(y!==-1&&R.splice(y,1),f===m){let O=k();W(O[0]??null);}}},requestFocus(m){W(m);},focusNext(){let m=k();if(m.length===0)return;let y=((f?m.indexOf(f):-1)+1)%m.length;W(m[y]);},focusPrev(){let m=k();if(m.length===0)return;let y=((f?m.indexOf(f):0)-1+m.length)%m.length;W(m[y]);},setSkippable(m,g){if(g){if(P.add(m),f===m){let y=k();y.length>0&&W(y[0]);}}else P.delete(m);},trapIds:null,pushTrap(m){return E.push(m),()=>{let g=E.indexOf(m);g!==-1&&E.splice(g,1);}},onFocusChange(m){return A.add(m),()=>{A.delete(m);}},getRegisteredElements(){let m=[];for(let g of R){if(P.has(g))continue;let y=u.get(g);y&&m.push({id:g,node:y});}return m},getActiveElements(){let m=k(),g=[];for(let y of m){if(P.has(y))continue;let O=u.get(y);O&&g.push({id:y,node:O});}return g}},T=new Map,B={getLayout(m){return m.layout},subscribe(m,g){return T.has(m)||T.set(m,new Set),T.get(m).add(g),()=>{let y=T.get(m);y&&(y.delete(g),y.size===0&&T.delete(m));}}},C={registerNode(){},unregisterNode(){},scheduleRender:b,exit(m){le.exit(m);},get columns(){return i.columns},get rows(){return i.rows}},x={type:"root",children:[],onCommit(){b();}},w=false;function b(){w||(w=true,queueMicrotask(()=>{w=false,M();}));}function M(){let m=i.columns,g=i.rows;(a.width!==m||a.height!==g)&&(a.resize(m,g),l.resize(m,g),p=true),ln(x.children,m,g),_(x.children);let y;if(f){let F=u.get(f);F?.type==="input"&&(y={nodeId:f,position:F.props.cursorPosition??F.props.value?.length??0});}let O=Jt(x.children,a,{cursorInfo:y,useNativeCursor:s}),K=Zt(l,a,p);if(K.length>0&&i.write(K),s)if(O.cursorPosition){let F=_t(O.cursorPosition.bg);i.setCursorColor(F),i.moveCursor(O.cursorPosition.x,O.cursorPosition.y),c||(i.showCursor(),c=true);}else c&&(i.hideCursor(),c=false);for(let F=0;F<a.cells.length;F++)l.cells[F]={...a.cells[F]};p=false;}function _(m){for(let g of m){let y=T.get(g);if(y)for(let O of y)O(g.layout);_(g.children);}}let q=i.onData(m=>{let g=jt(m);for(let y of g){if(y.ctrl&&y.name==="c"){le.exit();return}if(y.ctrl&&y.name==="z"){i.suspend(),process.kill(0,"SIGSTOP");return}if(y.name==="tab"&&!y.ctrl&&!y.alt){y.shift?G.focusPrev():G.focusNext();continue}let O=false;for(let K of S)if(K(y)){O=true;break}if(!O&&f){let K=v.get(f);K&&(O=K(y));}if(!O)for(let K of d)K(y);}}),z=i.onResize(()=>{p=true,b();}),j=()=>{i.resume(),p=true,b();};process.on("SIGCONT",j);let D=ee.createElement(be.Provider,{value:C},ee.createElement(Y.Provider,{value:I},ee.createElement($.Provider,{value:G},ee.createElement(oe.Provider,{value:B},e)))),L=m=>{o&&console.error("Uncaught error:",m);},X=m=>{o&&console.error("Error caught by boundary:",m);},J=m=>{o&&console.error("Recoverable error:",m);},se=Ie.createContainer(x,0,null,false,null,"",L,X,J,null);Ie.updateContainer(D,se,null,null);let le={unmount(){Ie.updateContainer(null,se,null,null),q(),z(),process.off("SIGCONT",j),i.cleanup();},exit(m){le.unmount(),process.exit(m??0);}};return le}var dr=forwardRef(function({children:t,style:n,focusable:r},o){return ee.createElement("box",{style:n,focusable:r,ref:o},t)});var gr=forwardRef(function({children:t,style:n,wrap:r},o){let s=r?{...n,wrap:r}:n;return ee.createElement("text",{style:s,ref:o},t)});function un(e,t,n){if(n<=0)return {visualLine:0,visualCol:t,totalVisualLines:1,lineStartOffset:0,lineLength:e.length};let r=e.split(`
|
|
5
|
+
`),o=[],s=0;for(let l of r){let a=ce([l],n,"wrap"),p=0;for(let d of a)o.push({text:d,logicalOffset:s+p}),p+=d.length;s+=l.length+1;}let i=0;for(let l=0;l<o.length;l++){let a=o[l],p=a.text.length,d=l+1<o.length&&o[l+1].logicalOffset!==a.logicalOffset+p,S=p+(d?1:0);if(t<i+p||l===o.length-1)return {visualLine:l,visualCol:Math.min(t-i,p),totalVisualLines:o.length,lineStartOffset:i,lineLength:p};i+=S;}let c=o.length-1;return {visualLine:c,visualCol:o[c].text.length,totalVisualLines:o.length,lineStartOffset:i-o[c].text.length,lineLength:o[c].text.length}}function an(e,t,n,r){if(r<=0)return Math.min(n,e.length);let o=e.split(`
|
|
6
|
+
`),s=[],i=0;for(let p of o){let d=ce([p],r,"wrap"),S=0;for(let v of d)s.push({text:v,startOffset:i+S}),S+=v.length;i+=p.length+1;}let c=Math.max(0,Math.min(t,s.length-1)),l=s[c],a=Math.min(n,l.text.length);return l.startOffset+a}function Ge(e,t){let n=e.split(`
|
|
7
|
+
`),r=t;for(let s=0;s<n.length;s++){if(r<=n[s].length)return {line:s,col:r,lines:n};r-=n[s].length+1;}let o=n.length-1;return {line:o,col:n[o].length,lines:n}}function Le(e,t,n){let r=0;for(let o=0;o<t&&o<e.length;o++)r+=e[o].length+1;return r+Math.min(n,e[t]?.length??0)}function xr(e){let{value:t,defaultValue:n="",onChange:r,onKeyPress:o,onBeforeChange:s,placeholder:i,style:c,focusedStyle:l,multiline:a,autoFocus:p,type:d="text"}=e,[S,v]=useState(n),[I,f]=useState(n.length),[u,R]=useState(0),[P,E]=useState(false),[A,W]=useState(false),k=useContext(Y),G=useContext($),T=useContext(oe),B=useRef(null),C=useRef(null),x=t!==void 0,w=x?t:S;useEffect(()=>{if(!T||!B.current)return;let j=T.getLayout(B.current);return R(j.innerWidth),T.subscribe(B.current,D=>{R(D.innerWidth);})},[T]);let b=useRef(w),M=useRef(I);useEffect(()=>{b.current=w,M.current>w.length&&(M.current=w.length,f(w.length));},[w]),useEffect(()=>{M.current=I;},[I]);let _=useRef({isControlled:x,onChange:r,onKeyPress:o,onBeforeChange:s,multiline:a??false,innerWidth:u,type:d});_.current={isControlled:x,onChange:r,onKeyPress:o,onBeforeChange:s,multiline:a??false,innerWidth:u,type:d},useEffect(()=>{if(!(!G||!C.current||!B.current))return G.register(C.current,B.current)},[G,A]);let q=useRef(false);useEffect(()=>{if(p&&!q.current&&G&&C.current){q.current=true;let j=C.current;queueMicrotask(()=>{G.requestFocus(j);});}},[p,G,A]),useEffect(()=>{if(!G||!C.current)return;let j=C.current;return E(G.focusedId===j),G.onFocusChange(D=>{E(D===j);})},[G,A]),useEffect(()=>{if(!k||!C.current)return;let j=C.current,D=L=>{let{isControlled:X,onChange:J,onKeyPress:se,onBeforeChange:le,multiline:m}=_.current;if(se?.(L)===true)return true;let g=b.current,y=M.current;if(L.name==="escape")return false;let O=(h,N)=>{let V=h,Q=N;if(le){let H=le(h,g);if(H===false)return;typeof H=="string"&&(V=H,Q=H.length);}b.current=V,M.current=Q,X||v(V),J?.(V),f(Q);},K=h=>{M.current=h,f(h);};if(L.name==="return"){if(m){let h=g.slice(0,y)+`
|
|
8
|
+
`+g.slice(y);return O(h,y+1),true}return false}if(L.ctrl){if(L.name==="w"){if(y>0){let h=y;for(;h>0&&g[h-1]===" ";)h--;for(;h>0&&g[h-1]!==" "&&(!m||g[h-1]!==`
|
|
9
|
+
`);)h--;let N=g.slice(0,h)+g.slice(y);O(N,h);}return true}if(L.name==="a"){if(m){let{line:h,lines:N}=Ge(g,y);K(Le(N,h,0));}else K(0);return true}if(L.name==="e"){if(m){let{line:h,lines:N}=Ge(g,y);K(Le(N,h,N[h].length));}else K(g.length);return true}if(L.name==="k"){if(m){let{line:h,lines:N}=Ge(g,y),V=Le(N,h,N[h].length);if(y<V){let Q=g.slice(0,y)+g.slice(V);O(Q,y);}}else if(y<g.length){let h=g.slice(0,y);O(h,y);}return true}return false}if(L.alt){if(L.name==="left"||L.name==="b"){let h=y;for(;h>0&&g[h-1]===" ";)h--;for(;h>0&&g[h-1]!==" "&&g[h-1]!==`
|
|
10
|
+
`;)h--;return K(h),true}if(L.name==="right"||L.name==="f"){let h=y;for(;h<g.length&&g[h]!==" "&&g[h]!==`
|
|
11
|
+
`;)h++;for(;h<g.length&&g[h]===" ";)h++;return K(h),true}if(L.name==="backspace"||L.name==="d")if(L.name==="backspace"){if(y>0){let h=y;for(;h>0&&g[h-1]===" ";)h--;for(;h>0&&g[h-1]!==" "&&g[h-1]!==`
|
|
12
|
+
`;)h--;let N=g.slice(0,h)+g.slice(y);O(N,h);}return true}else {if(y<g.length){let h=y;for(;h<g.length&&g[h]!==" "&&g[h]!==`
|
|
13
|
+
`;)h++;for(;h<g.length&&g[h]===" ";)h++;let N=g.slice(0,y)+g.slice(h);O(N,y);}return true}return false}if(L.name==="left")return K(Math.max(0,y-1)),true;if(L.name==="right")return K(Math.min(g.length,y+1)),true;if(L.name==="up"){let{innerWidth:h}=_.current,N=un(g,y,h);return N.visualLine>0&&K(an(g,N.visualLine-1,N.visualCol,h)),true}if(L.name==="down"){let{innerWidth:h}=_.current,N=un(g,y,h);return N.visualLine<N.totalVisualLines-1&&K(an(g,N.visualLine+1,N.visualCol,h)),true}if(L.name==="home"){if(m){let{line:h,lines:N}=Ge(g,y);K(Le(N,h,0));}else K(0);return true}if(L.name==="end"){if(m){let{line:h,lines:N}=Ge(g,y);K(Le(N,h,N[h].length));}else K(g.length);return true}if(L.name==="backspace"){if(y>0){let h=g.slice(0,y-1)+g.slice(y);O(h,y-1);}return true}if(L.name==="delete"){if(y<g.length){let h=g.slice(0,y)+g.slice(y+1);O(h,y);}return true}if(L.name.length>1)return false;let F=L.sequence;if(F.length===1&&F.charCodeAt(0)>=32){let{type:h}=_.current;if(h==="number"){let V=/[0-9]/.test(F),Q=F==="."&&!g.includes("."),H=F==="-"&&y===0&&!g.includes("-");if(!V&&!Q&&!H)return true}let N=g.slice(0,y)+F+g.slice(y);return O(N,y+1),true}return false};return k.registerInputHandler(j,D)},[k,A]);let z={...c,...P&&l?l:{}};return ee.createElement("input",{style:z,value:w,defaultValue:n,placeholder:i,onChange:r,cursorPosition:I,multiline:a??false,focused:P,ref:j=>{j?(B.current=j,C.current=j.focusId,W(true)):(B.current=null,C.current=null,W(false));}})}function ft({trap:e=false,children:t}){let n=useContext($),r=useRef(null),o=useRef(new Set);return useLayoutEffect(()=>{if(!e||!n)return;r.current=n.focusedId;let s=n.pushTrap(o.current);return ()=>{s(),r.current&&n.requestFocus(r.current);}},[e,n]),useEffect(()=>{if(!(!e||!n)&&o.current.size>0){let s=o.current.values().next().value;s&&n.requestFocus(s);}},[e,n]),ee.createElement(ee.Fragment,null,t)}function Rr({size:e}){return ee.createElement("box",{style:{flexGrow:e??1}})}function hn(e){let t=e.toLowerCase().split("+");return {name:t[t.length-1],ctrl:t.includes("ctrl"),alt:t.includes("alt"),shift:t.includes("shift"),meta:t.includes("meta")||t.includes("cmd")||t.includes("super")||t.includes("win")}}function gn(e,t){return !(t.name!==e.name||e.ctrl!==!!t.ctrl||e.alt!==!!t.alt||e.shift!==!!t.shift||e.meta!==!!t.meta)}function Ir({keypress:e,onPress:t,whenFocused:n,priority:r,disabled:o}){let s=useContext(Y),i=useContext($),c=useRef(t);c.current=t;let l=useRef(hn(e));return l.current=hn(e),useEffect(()=>{if(!(!s||o))if(r){let a=p=>!gn(l.current,p)||n&&i?.focusedId!==n?false:(c.current(),true);return s.subscribePriority(a)}else {let a=p=>{gn(l.current,p)&&(n&&i?.focusedId!==n||c.current());};return s.subscribe(a)}},[s,i,n,r,o]),null}function Nr({children:e,zIndex:t=1e3}){return ee.createElement("box",{style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",zIndex:t}},e)}function Er({onPress:e,style:t,focusedStyle:n,children:r,disabled:o}){let s=useContext($),i=useContext(Y),c=useRef(null),l=useRef(null),a=useRef(e);a.current=e;let[p,d]=useState(false),[S,v]=useState(false);useEffect(()=>{if(!(!s||!l.current||!c.current||o))return s.register(l.current,c.current)},[s,o,p]),useEffect(()=>{if(!s||!l.current)return;let f=l.current;return v(s.focusedId===f),s.onFocusChange(u=>{v(u===f);})},[s,p]),useEffect(()=>{if(!i||!l.current||o)return;let f=l.current,u=R=>s?.focusedId!==f?false:R.name==="return"||R.name===" "||R.sequence===" "?(a.current?.(),true):false;return i.registerInputHandler(f,u)},[i,s,o,p]);let I={...t,...S&&n?n:{}};return ee.createElement("box",{style:I,focusable:!o,ref:f=>{f?(c.current=f,l.current=f.focusId,d(true)):(c.current=null,l.current=null,d(false));}},r)}var Lr={x:0,y:0,width:0,height:0,innerX:0,innerY:0,innerWidth:0,innerHeight:0};function fe(e){let t=useContext(oe),[n,r]=useState(Lr);return useEffect(()=>{if(!(!t||!e?.current))return r(t.getLayout(e.current)),t.subscribe(e.current,r)},[t,e]),n}function Be(e,t=[]){let n=useContext(Y);useEffect(()=>{if(n)return n.subscribe(e)},[n,...t]);}function Ar({children:e,style:t,scrollOffset:n,onScroll:r,defaultScrollOffset:o=0,scrollStep:s=1,disableKeyboard:i,scrollToFocus:c=true,showScrollbar:l=true,focusable:a=true,focusedStyle:p}){let d=n!==void 0,[S,v]=useState(o),I=d?n:S,f=useRef(null),u=useRef(null),R=fe(f),P=fe(u),E=useContext($),A=useContext(oe),W=useRef(null);a&&!W.current&&(W.current=`scrollview-${Math.random().toString(36).slice(2,9)}`);let k=a?W.current:null;useEffect(()=>{if(!(!a||!k||!E||!f.current))return E.register(k,f.current)},[a,k,E]);let G=a&&k&&E?.focusedId===k,T=R.innerHeight,B=P.height,C=Math.max(0,B-T),x=Math.max(0,Math.min(I,C)),w=useMemo(()=>({getBounds:()=>{let H=R.y;return {visibleTop:H,visibleBottom:H+T,viewportHeight:T,scrollOffset:x}}}),[R.y,T,x]),b=useCallback(H=>{let re=Math.max(0,Math.min(H,C));d?r?.(re):v(re);},[d,r,C]);useEffect(()=>{I>C&&C>=0&&b(C);},[I,C,b]),useEffect(()=>!c||!E||!A||!u.current?void 0:E.onFocusChange(re=>{if(!re||!u.current)return;let Z=Ke=>{if(Ke.focusId===re)return Ke;for(let We of Ke.children){let ye=Z(We);if(ye)return ye}return null},pe=Z(u.current);if(!pe)return;let ge=A.getLayout(pe),Re=u.current.layout?.y??0,Oe=ge.y-Re,De=Oe+ge.height,ve=I,ot=I+T;Oe<ve?b(Oe):De>ot&&b(De-T);}),[c,E,A,I,T,b]);let M=useCallback(()=>{if(!E)return false;let H=E.focusedId;if(!H)return false;if(a&&k&&H===k)return true;if(!u.current)return false;let re=Z=>{if(Z.focusId===H)return true;for(let pe of Z.children)if(re(pe))return true;return false};return re(u.current)},[E,a,k]);Be(H=>{if(i||!M())return;let re=Math.max(1,Math.floor(T/2)),Z=Math.max(1,T);switch(H.name){case "pageup":b(I-Z);break;case "pagedown":b(I+Z);break;default:H.ctrl&&(H.name==="d"?b(I+re):H.name==="u"?b(I-re):H.name==="f"?b(I+Z):H.name==="b"&&b(I-Z));break}},[I,s,T,C,i,b,M]);let{padding:_,paddingX:q,paddingY:z,paddingTop:j,paddingRight:D,paddingBottom:L,paddingLeft:X,...J}=t??{},le=J.border!=null&&J.border!=="none"?2:0,m=B>0?B+le:void 0,g={...J,...G?p:{},clip:true,...J.height===void 0&&m!==void 0?{height:m,flexShrink:J.flexShrink??1,minHeight:J.minHeight??0}:{}},y={position:"absolute",top:-x,left:0,right:0,flexDirection:"column",..._!==void 0&&{padding:_},...q!==void 0&&{paddingX:q},...z!==void 0&&{paddingY:z},...j!==void 0&&{paddingTop:j},...D!==void 0&&{paddingRight:D},...L!==void 0&&{paddingBottom:L},...X!==void 0&&{paddingLeft:X}},O=B>T&&T>0,K=l&&O,F=Math.max(1,Math.floor(T/B*T)),h=B-T,N=h>0?Math.floor(x/h*(T-F)):0,V=[];if(K)for(let H=0;H<T;H++)H>=N&&H<N+F?V.push("\u2588"):V.push("\u2591");let Q={position:"absolute",top:0,right:0,width:1,height:T,flexDirection:"column"};return ee.createElement(Ye.Provider,{value:w},ee.createElement("box",{style:g,ref:H=>{f.current=H??null;},...a?{focusable:true,focusId:k}:{}},ee.createElement("box",{style:{...y,paddingRight:K?(y.paddingRight??0)+1:y.paddingRight},ref:H=>{u.current=H??null;}},e),K&&ee.createElement("box",{style:Q},ee.createElement("text",{style:{color:"blackBright"}},V.join(`
|
|
14
|
+
`)))))}function Ct({count:e,renderItem:t,selectedIndex:n,onSelectionChange:r,onSelect:o,defaultSelectedIndex:s=0,disabledIndices:i,style:c,focusable:l=true}){let a=n!==void 0,[p,d]=useState(s),S=a?n:p,v=useContext($),I=useContext(Y),f=useRef(null),u=useRef(null),R=useRef(o);R.current=o;let[P,E]=useState(false),[A,W]=useState(false),k=useRef(null),G=useCallback(x=>{let w=Math.max(0,Math.min(x,e-1));a?r?.(w):d(w);},[a,r,e]),T=useCallback((x,w)=>{if(!i||i.size===0)return Math.max(0,Math.min(x+w,e-1));let b=x+w;for(;b>=0&&b<e&&i.has(b);)b+=w;return b<0||b>=e?x:b},[i,e]);useEffect(()=>{if(!(!v||!u.current||!f.current||!l))return v.register(u.current,f.current)},[v,l,P]),useEffect(()=>{if(!v||!u.current)return;let x=u.current;return W(v.focusedId===x),v.onFocusChange(w=>{W(w===x);})},[v,P]);let B=useCallback(x=>{let w=x?e-1:0,b=x?-1:1,M=w;for(;M>=0&&M<e&&i?.has(M);)M+=b;return M>=0&&M<e?M:x?e-1:0},[i,e]);useEffect(()=>{if(!I||!u.current||!l)return;let x=u.current,w=b=>v?.focusedId!==x?false:b.name==="g"&&!b.ctrl&&!b.alt?k.current==="g"?(G(B(false)),k.current=null,true):(k.current="g",true):b.name==="G"||b.name==="g"&&b.shift?(k.current=null,G(B(true)),true):(k.current=null,b.name==="up"||b.name==="k"?(G(T(S,-1)),true):b.name==="down"||b.name==="j"?(G(T(S,1)),true):b.name==="return"?(i?.has(S)||R.current?.(S),true):false);return I.registerInputHandler(x,w)},[I,v,l,S,G,T,B,i,P]);let C=[];for(let x=0;x<e;x++)C.push(ee.createElement(ee.Fragment,{key:x},t({index:x,selected:x===S,focused:A})));return ee.createElement("box",{style:{flexDirection:"column",...c},focusable:l,ref:x=>{x?(f.current=x,u.current=x.focusId,E(true)):(f.current=null,u.current=null,E(false));}},...C)}function jr({items:e,selectedIndex:t,onSelectionChange:n,onSelect:r,defaultSelectedIndex:o=0,style:s,highlightColor:i="cyan",focusable:c=true}){let l=new Set;for(let p=0;p<e.length;p++)e[p].disabled&&l.add(p);let a=p=>{let d=e[p];d&&!d.disabled&&r?.(d.value,p);};return ee.createElement(Ct,{count:e.length,selectedIndex:t,onSelectionChange:n,onSelect:a,defaultSelectedIndex:o,disabledIndices:l.size>0?l:void 0,style:s,focusable:c,renderItem:({index:p,selected:d,focused:S})=>{let v=e[p],I=v.disabled,f=d&&S,u=d?">":" ";return ee.createElement("box",{style:{flexDirection:"row",...f?{bg:i}:{}}},ee.createElement("text",{style:f?{bold:true,color:"black"}:I?{dim:true}:{}},`${u} ${v.label}`))}})}function Wr({value:e,indeterminate:t=false,width:n="100%",label:r,showPercent:o=false,style:s,filled:i="\u2588",empty:c="\u2591"}){let l=useRef(null),p=fe(l).innerWidth,[d,S]=useState(0);useEffect(()=>{if(!t)return;let R=setInterval(()=>{S(P=>(P+1)%Math.max(1,p+6));},100);return ()=>clearInterval(R)},[t,p]);let v=Math.max(0,Math.min(1,e??0)),I=o?` ${Math.round(v*100)}%`:"",f="";if(p>0)if(t&&e===void 0){let R=Math.max(1,Math.min(3,Math.floor(p/4))),P=[];for(let E=0;E<p;E++)E>=d-R&&E<d?P.push(i):P.push(c);f=P.join("");}else {let R=Math.round(v*p);f=i.repeat(R)+c.repeat(p-R);}let u=[];return r&&u.push(ee.createElement("text",{key:"label",style:{bold:true}},r+" ")),u.push(ee.createElement("box",{key:"track",style:{flexGrow:1,flexShrink:1},ref:R=>{l.current=R??null;}},ee.createElement("text",{key:"bar",style:{}},f))),o&&u.push(ee.createElement("text",{key:"pct",style:{bold:true}},I)),ee.createElement("box",{style:{flexDirection:"row",width:n,...s}},...u)}var $r=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function zr({frames:e=$r,intervalMs:t=80,label:n,style:r}){let[o,s]=useState(0);useEffect(()=>{let c=setInterval(()=>{s(l=>(l+1)%e.length);},t);return ()=>clearInterval(c)},[e.length,t]);let i=[ee.createElement("text",{key:"frame",style:r},e[o])];return n&&i.push(ee.createElement("text",{key:"label",style:{}}," "+n)),ee.createElement("box",{style:{flexDirection:"row"}},...i)}var Rn=createContext(null),Qr=0;function eo(){let e=useContext(Rn);if(!e)throw new Error("useToast must be used within a <ToastHost>");return e.push}var to={info:{bg:"blackBright",title:"cyanBright",text:"white"},success:{bg:"blackBright",title:"greenBright",text:"white"},warning:{bg:"blackBright",title:"yellowBright",text:"white"},error:{bg:"blackBright",title:"redBright",text:"white"}};function no({position:e="bottom-right",maxVisible:t=5,children:n}){let[r,o]=useState([]),s=useRef(new Map),i=useCallback(v=>{let I=`toast-${Qr++}`,f={id:I,durationMs:3e3,variant:"info",...v};if(o(u=>[...u,f]),f.durationMs&&f.durationMs>0){let u=setTimeout(()=>{s.current.delete(I),o(R=>R.filter(P=>P.id!==I));},f.durationMs);s.current.set(I,u);}},[]);useEffect(()=>()=>{for(let v of s.current.values())clearTimeout(v);s.current.clear();},[]);let c=useRef({push:i});c.current.push=i;let l=e.startsWith("top"),a=e.endsWith("right"),p={position:"absolute",top:0,left:0,width:"100%",height:"100%",zIndex:900,flexDirection:"column",justifyContent:l?"flex-start":"flex-end",alignItems:a?"flex-end":"flex-start",padding:1},S=r.slice(-t).map(v=>{let I=v.variant??"info",f=to[I],u=[];return v.title&&u.push(ee.createElement("text",{key:"title",style:{bold:true,color:f.title}},v.title)),u.push(ee.createElement("text",{key:"msg",style:{color:f.text}},v.message)),ee.createElement("box",{key:v.id,style:{bg:f.bg,paddingX:1,flexDirection:"column",minWidth:20,maxWidth:50}},...u)});return ee.createElement(Rn.Provider,{value:c.current},n,S.length>0?ee.createElement("box",{style:p},...S):null)}function oo({items:e,value:t,onChange:n,placeholder:r="Select...",style:o,focusedStyle:s,dropdownStyle:i,highlightColor:c="cyan",maxVisible:l=8,searchable:a=true,disabled:p}){let d=useContext($),S=useContext(Y),v=useContext(be),I=useContext(Ye),f=useRef(null),u=useRef(null),R=useRef(n);R.current=n;let[P,E]=useState(false),[A,W]=useState(false),[k,G]=useState(false),[T,B]=useState(0),[C,x]=useState(""),[w,b]=useState(0),M=fe(f),_=v?.rows??24,q=I?.getBounds(),j=e.find(F=>F.value===t)?.label??"",D=useMemo(()=>{if(!C)return e;let F=C.toLowerCase();return e.filter(h=>h.label.toLowerCase().includes(F))},[e,C]),L=Math.min(l,D.length),X=D.slice(w,w+L);useEffect(()=>{B(0),b(0);},[C]),useEffect(()=>{!A&&k&&(G(false),x(""));},[A,k]),useEffect(()=>{if(!(!d||!u.current||!f.current))return d.register(u.current,f.current)},[d,P]),useEffect(()=>{!d||!u.current||d.setSkippable(u.current,!!p);},[d,p,P]),useEffect(()=>{if(!d||!u.current)return;let F=u.current;return W(d.focusedId===F),d.onFocusChange(h=>{W(h===F);})},[d,P]);let J=useCallback((F,h)=>{let N=F+h;for(;N>=0&&N<D.length;){if(!D[N].disabled)return N;N+=h;}return F},[D]),se=useCallback(F=>{F<w?b(F):F>=w+L&&b(F-L+1);},[w,L]);useEffect(()=>{if(!S||!u.current||p)return;let F=u.current,h=N=>{if(d?.focusedId!==F)return false;if(!k){if(N.name==="return"||N.name===" "||N.sequence===" "||N.name==="down"){G(true),x("");let V=D.findIndex(H=>H.value===t),Q=V>=0?V:0;return B(Q),b(Math.max(0,Q-Math.floor(l/2))),true}return false}if(N.name==="tab")return G(false),x(""),false;if(N.name==="escape")return G(false),x(""),true;if(N.name==="return"){let V=D[T];return V&&!V.disabled&&(R.current?.(V.value),G(false),x("")),true}if(N.name==="up"){let V=J(T,-1);return B(V),se(V),true}if(N.name==="down"){let V=J(T,1);return B(V),se(V),true}if(N.name==="backspace")return a&&C.length>0&&x(V=>V.slice(0,-1)),true;if(N.name==="home"){let V=J(-1,1);return B(V),se(V),true}if(N.name==="end"){let V=J(D.length,-1);return B(V),se(V),true}if(a&&N.sequence&&N.sequence.length===1&&!N.ctrl&&!N.alt){let V=N.sequence;if(V>=" "&&V<="~")return x(Q=>Q+V),true}return true};return S.registerInputHandler(F,h)},[S,d,p,k,T,D,t,l,a,C,J,se,P]);let m={flexDirection:"row",width:"100%",...!o?.bg&&o?.border===void 0?{border:"single"}:{},...o,...A&&s?s:{}},g=j?o?.color??void 0:"blackBright",y=[ee.createElement("text",{key:"label",style:{flexGrow:1,flexShrink:1,color:g,wrap:"ellipsis",...j?{}:{dim:true}}},j||r),ee.createElement("text",{key:"arrow",style:{flexShrink:0,color:A?c:"blackBright"}},k?" \u25B2":" \u25BC")],O=null;if(k){let F=[];a&&C&&F.push(ee.createElement("box",{key:"search",style:{paddingX:1}},ee.createElement("text",{style:{color:"blackBright",dim:true}},`/${C}`))),D.length===0&&F.push(ee.createElement("box",{key:"empty",style:{paddingX:1}},ee.createElement("text",{style:{dim:true,color:"blackBright"}},"No matches"))),w>0&&F.push(ee.createElement("box",{key:"scroll-up",style:{justifyContent:"center",alignItems:"center"}},ee.createElement("text",{style:{dim:true,color:"blackBright"}},"\u25B2"))),X.forEach((ve,ot)=>{let We=w+ot===T,ye=ve.disabled,Mn={paddingX:1,...We&&!ye?{bg:c}:{}},Vn={...We&&!ye?{color:"black",bold:true}:{},...ye?{dim:true,color:"blackBright"}:{}};F.push(ee.createElement("box",{key:`item-${ve.value}`,style:Mn},ee.createElement("text",{style:Vn},ve.label)));}),w+L<D.length&&F.push(ee.createElement("box",{key:"scroll-down",style:{justifyContent:"center",alignItems:"center"}},ee.createElement("text",{style:{dim:true,color:"blackBright"}},"\u25BC")));let h=w>0,N=w+L<D.length,V=a&&C,Q=D.length===0,H=!i?.bg&&i?.border===void 0,Z=L+(H?2:0);h&&(Z+=1),N&&(Z+=1),V&&(Z+=1),Q&&(Z+=1);let pe=M.y+M.height,ge,Re;q?(ge=q.visibleBottom-pe,Re=M.y-q.visibleTop):(ge=_-pe,Re=M.y);let De=ge<Z&&Re>=Z?-Z:M.height||1;O=ee.createElement("box",{style:{position:"absolute",top:De,left:0,right:0,zIndex:9999,...H?{border:"single"}:{},bg:"black",flexDirection:"column",...i}},...F);}let K={flexDirection:"column",width:m.width??"100%",minWidth:m.minWidth,maxWidth:m.maxWidth,flexGrow:m.flexGrow,flexShrink:m.flexShrink??1};return ee.createElement("box",{style:K},ee.createElement("box",{style:m,focusable:true,ref:F=>{F?(f.current=F,u.current=F.focusId,E(true)):(f.current=null,u.current=null,E(false));}},...y),O)}function so({checked:e,onChange:t,label:n,style:r,focusedStyle:o,disabled:s,checkedChar:i="\u2713",uncheckedChar:c=" "}){let l=useContext($),a=useContext(Y),p=useRef(null),d=useRef(null),S=useRef(t);S.current=t;let v=useRef(e);v.current=e;let[I,f]=useState(false),[u,R]=useState(false);useEffect(()=>{if(!(!l||!d.current||!p.current||s))return l.register(d.current,p.current)},[l,s,I]),useEffect(()=>{if(!l||!d.current)return;let k=d.current;return R(l.focusedId===k),l.onFocusChange(G=>{R(G===k);})},[l,I]),useEffect(()=>{if(!a||!d.current||s)return;let k=d.current,G=T=>l?.focusedId!==k?false:T.name==="return"||T.name===" "||T.sequence===" "?(S.current(!v.current),true):false;return a.registerInputHandler(k,G)},[a,l,s,I]);let P={flexDirection:"row",gap:1,...r,...u&&o?o:{}},E=e?i:c,A={color:s?"blackBright":u?"white":r?.color},W={color:s?"blackBright":r?.color};return ee.createElement("box",{style:P,focusable:!s,ref:k=>{k?(p.current=k,d.current=k.focusId,f(true)):(p.current=null,d.current=null,f(false));}},ee.createElement("text",{key:"box",style:A},`[${E}]`),n?ee.createElement("text",{key:"label",style:W},n):null)}function lo({items:e,value:t,onChange:n,style:r,itemStyle:o,focusedItemStyle:s,selectedItemStyle:i,disabled:c,direction:l="column",gap:a=0,selectedChar:p="\u25CF",unselectedChar:d="\u25CB"}){let S=useContext($),v=useContext(Y),I=useRef(null),f=useRef(null),u=useRef(n);u.current=n;let[R,P]=useState(false),[E,A]=useState(false),[W,k]=useState(()=>{let C=e.findIndex(x=>x.value===t);return C>=0?C:e.findIndex(x=>!x.disabled)}),G=useCallback((C,x)=>{let w=C;for(let b=0;b<e.length;b++)if(w=(w+x+e.length)%e.length,!e[w]?.disabled)return w;return C},[e]);useEffect(()=>{if(!(!S||!f.current||!I.current||c))return S.register(f.current,I.current)},[S,c,R]),useEffect(()=>{if(!S||!f.current)return;let C=f.current;return A(S.focusedId===C),S.onFocusChange(x=>{A(x===C);})},[S,R]),useEffect(()=>{if(!v||!f.current||c)return;let C=f.current,x=w=>{if(S?.focusedId!==C)return false;if(w.name==="up"||w.name==="left"||w.name==="k"||w.name==="tab"&&w.shift)return k(b=>G(b,-1)),true;if(w.name==="down"||w.name==="right"||w.name==="j"||w.name==="tab"&&!w.shift)return k(b=>G(b,1)),true;if(w.name==="return"||w.name===" "||w.sequence===" "){let b=e[W];return b&&!b.disabled&&u.current(b.value),true}return false};return v.registerInputHandler(C,x)},[v,S,c,e,W,G,R]),useEffect(()=>{let C=e.findIndex(x=>x.value===t);C>=0&&k(C);},[t,e]);let T={flexDirection:l,gap:a,...r},B=e.map((C,x)=>{let w=C.value===t,b=x===W,M=c||C.disabled,_=w?p:d,q={flexDirection:"row",gap:1,...o};w&&i&&(q={...q,...i}),E&&b&&s&&(q={...q,...s});let z=M?"blackBright":E&&b?s?.color??"white":w?i?.color??o?.color:o?.color;return ee.createElement("box",{key:x,style:q},ee.createElement("text",{key:"radio",style:{color:z}},`(${_})`),ee.createElement("text",{key:"label",style:{color:z}},C.label))});return ee.createElement("box",{style:T,focusable:!c,ref:C=>{C?(I.current=C,f.current=C.focusId,P(true)):(I.current=null,f.current=null,P(false));}},...B)}var kn=createContext(null);function uo(){let e=useContext(kn);if(!e)throw new Error("useDialog must be used within a DialogHost");return e}function ao({children:e}){let[t,n]=useState([]),r=useRef(0),o=useCallback((a,p)=>new Promise(d=>{let S=++r.current;n(v=>[...v,{id:S,type:"alert",content:a,okText:p?.okText??"OK",cancelText:"",style:p?.style,resolve:()=>d()}]);}),[]),s=useCallback((a,p)=>new Promise(d=>{let S=++r.current;n(v=>[...v,{id:S,type:"confirm",content:a,okText:p?.okText??"OK",cancelText:p?.cancelText??"Cancel",style:p?.style,resolve:d}]);}),[]),i=useCallback((a,p)=>{n(d=>{let S=d.find(v=>v.id===a);return S&&S.resolve(p),d.filter(v=>v.id!==a)});},[]),c={alert:o,confirm:s},l=t[t.length-1];return ee.createElement(kn.Provider,{value:c},e,l&&ee.createElement(fo,{key:l.id,dialog:l,onDismiss:i}))}function fo({dialog:e,onDismiss:t}){let n=useContext($),r=useRef(null),o=useRef(null),s=useRef(null),i=useRef(null),[c,l]=useState("ok"),[a,p]=useState(0);useEffect(()=>{if(!n||a===0)return;let u=[];return r.current&&s.current&&u.push(n.register(s.current,r.current)),o.current&&i.current&&u.push(n.register(i.current,o.current)),s.current&&n.requestFocus(s.current),()=>u.forEach(R=>R())},[n,a]),useEffect(()=>{if(n)return n.onFocusChange(u=>{u===s.current?l("ok"):u===i.current&&l("cancel");})},[n]),Be(u=>{if(u.name==="return"||u.name==="space"){e.type==="alert"?t(e.id,true):t(e.id,c==="ok");return}if(u.name==="escape"){t(e.id,false);return}e.type==="confirm"&&n&&(u.name==="left"||u.name==="right")&&(c==="ok"&&i.current?n.requestFocus(i.current):s.current&&n.requestFocus(s.current));},[e,c,n,t]);let d=typeof e.content=="string",S=d?e.content.length:0,I={minWidth:Math.max(20,d?Math.min(S+6,50):30),maxWidth:50,bg:"black",border:"round",borderColor:"white",padding:1,flexDirection:"column",gap:1,...e.style},f=u=>({paddingX:2,bg:u?"white":"blackBright",color:u?"black":"white",bold:u});return ee.createElement(ft,{trap:true},ee.createElement("box",{style:{position:"absolute",top:0,left:0,right:0,bottom:0,zIndex:999}}),ee.createElement("box",{style:{position:"absolute",top:0,left:0,right:0,bottom:0,justifyContent:"center",alignItems:"center",zIndex:1e3}},ee.createElement("box",{style:I},ee.createElement("box",{style:{flexDirection:"column"}},typeof e.content=="string"?ee.createElement("text",null,e.content):e.content),ee.createElement("box",{style:{flexDirection:"row",justifyContent:"flex-end",gap:1}},e.type==="confirm"&&ee.createElement("box",{style:f(c==="cancel"),focusable:true,ref:u=>{u&&u.focusId&&!i.current&&(o.current=u,i.current=u.focusId,p(R=>R+1));}},ee.createElement("text",null,e.cancelText)),ee.createElement("box",{style:f(c==="ok"),focusable:true,ref:u=>{u&&u.focusId&&!s.current&&(r.current=u,s.current=u.focusId,p(R=>R+1));}},ee.createElement("text",null,e.okText))))))}function ho(e,t){let n=[],r=t.split("");if(e<=r.length)for(let o=0;o<e;o++)n.push(r[o]);else for(let o=0;o<r.length&&n.length<e;o++)for(let s=0;s<r.length&&n.length<e;s++)n.push(r[o]+r[s]);return n}function go({children:e,activationKey:t="ctrl+o",hintStyle:n,hintBg:r="yellow",hintFg:o="black",hintChars:s="asdfghjklqwertyuiopzxcvbnm",enabled:i=true,debug:c=false}){let l=c?(...C)=>console.error("[JumpNav]",...C):()=>{},[a,p]=useState(false),[d,S]=useState(""),[v,I]=useState([]),f=useContext(Y),u=useContext($),R=useContext(oe);useEffect(()=>{l("Mounted, inputCtx:",!!f,"focusCtx:",!!u,"enabled:",i);},[]);let E=useCallback(C=>{let x=C.toLowerCase().split("+");return {ctrl:x.includes("ctrl"),alt:x.includes("alt"),shift:x.includes("shift"),meta:x.includes("meta"),name:x[x.length-1]??""}},[])(t),A=useCallback(()=>{if(!u?.getActiveElements){l("refreshElements: no getActiveElements");return}let C=u.getActiveElements();l("getActiveElements returned",C.length,"elements");let x=C.map(({id:w,node:b})=>({id:w,node:b,layout:R?.getLayout(b)??b.layout}));x.sort((w,b)=>w.layout.y!==b.layout.y?w.layout.y-b.layout.y:w.layout.x-b.layout.x),I(x);},[u,R,l]),W=useRef(false);useEffect(()=>{a&&!W.current&&(l("Activated! Refreshing elements..."),A()),W.current=a;},[a,A,l]);let k=v.filter(C=>C.layout.width>0&&C.layout.height>0),G=ho(k.length,s),T=useMemo(()=>{let C=new Map;return k.forEach((x,w)=>{G[w]&&C.set(G[w],x.id);}),C},[k,G]);useEffect(()=>{if(!f||!i){l("Not subscribing - inputCtx:",!!f,"enabled:",i);return}l("Subscribing to priority input, activation key:",t);let C=x=>{let w=x.name===E.name,b=!!x.ctrl===E.ctrl,M=!!x.alt===E.alt,_=!!x.shift===E.shift,q=!!x.meta===E.meta;if(!a&&w&&b&&M&&_&&q)return l("Activation key matched! Activating..."),p(true),S(""),true;if(a){if(x.name==="escape")return l("Escape pressed, deactivating"),p(false),S(""),true;if(x.name==="backspace")return S(""),true;if(x.sequence&&x.sequence.length===1&&/[a-z]/i.test(x.sequence)){let z=d+x.sequence.toLowerCase();l("Buffer:",z);let j=T.get(z);return j?(l("Jumping to",j),u?.requestFocus(j),p(false),S(""),true):[...T.keys()].some(L=>L.startsWith(z))?(S(z),true):(S(""),true)}return true}return false};return f.subscribePriority(C)},[f,i,a,E,d,T,u,t,l]);let B=a?ee.createElement("box",{style:{position:"absolute",top:0,left:0,width:"100%",height:"100%",zIndex:99998}},...k.map((C,x)=>{let w=G[x];if(!w)return null;let{x:b,y:M}=C.layout,_=w.startsWith(d)&&d.length>0;return ee.createElement("box",{key:C.id,style:{position:"absolute",top:M,left:Math.max(0,b-w.length-2),bg:_?"cyan":r,color:o,paddingX:1,zIndex:99999,...n}},ee.createElement("text",{style:{bold:true,color:o}},w))}),ee.createElement("box",{style:{position:"absolute",bottom:0,left:0,right:0,bg:"blackBright",paddingX:1,zIndex:99999}},ee.createElement("text",{style:{color:"white"}},d?`Jump: ${d}_`:"Press a key to jump \u2022 ESC to cancel"))):null;return ee.createElement(ee.Fragment,null,e,B)}function So(e){let t=useContext($),[n]=useState(()=>`focus-${Math.random().toString(36).slice(2,9)}`),r=t?t.focusedId===n:false;useEffect(()=>{if(!(!t||!e?.current))return e.current.focusId=n,t.register(n,e.current)},[t,n,e]);let o=useMemo(()=>()=>{t?.requestFocus(n);},[t,n]);return {focused:r,focus:o}}function Ro(e={}){let{disabled:t,onFocus:n,onBlur:r,onKeyPress:o}=e,s=useContext($),i=useContext(Y),c=useRef(null),l=useRef(null),[a,p]=useState(false),d=useRef(n),S=useRef(r),v=useRef(o);d.current=n,S.current=r,v.current=o;let I=useCallback(u=>{c.current=u,u?l.current=u.focusId??null:l.current=null;},[]);useEffect(()=>{if(!(!s||!l.current||!c.current))return s.register(l.current,c.current)},[s]),useEffect(()=>{!s||!l.current||s.setSkippable(l.current,!!t);},[s,t]),useEffect(()=>{if(!s||!l.current)return;let u=l.current,R=s.focusedId===u;return p(R),s.onFocusChange(P=>{let E=P===u;p(A=>(E&&!A?d.current?.():!E&&A&&S.current?.(),E));})},[s]),useEffect(()=>{if(!i||!l.current||t)return;let u=l.current,R=P=>s?.focusedId!==u?false:v.current?.(P)===true;return i.registerInputHandler(u,R)},[i,s,t]);let f=useCallback(()=>{s&&l.current&&s.requestFocus(l.current);},[s]);return {ref:I,isFocused:a,focus:f,focusId:l.current}}function Io(){let e=useContext(be);if(!e)throw new Error("useApp must be used within a Glyph render tree");return {exit:e.exit,get columns(){return e.columns},get rows(){return e.rows}}}function ko(){let e=useContext($),t=useContext(oe),[n,r]=useState([]),o=useRef(()=>{}),s=useCallback(()=>{if(!e)return;let c=(e.getActiveElements?.()??e.getRegisteredElements?.()??[]).map(({id:l,node:a})=>({id:l,node:a,layout:t?.getLayout(a)??a.layout,type:a.type}));c.sort((l,a)=>l.layout.y!==a.layout.y?l.layout.y-a.layout.y:l.layout.x-a.layout.x),r(c);},[e,t]);return o.current=s,useEffect(()=>{if(!e)return;s();let i=e.onFocusChange(()=>{s();}),c=setTimeout(s,50);return ()=>{i(),clearTimeout(c);}},[e,t,s]),e?{elements:n,focusedId:e.focusedId,requestFocus:e.requestFocus,focusNext:e.focusNext,focusPrev:e.focusPrev,refresh:()=>o.current()}:null}function Fo(e){let t=[];for(let n of e)switch(n){case "9":t.push({type:"digit",char:n});break;case "a":t.push({type:"letter",char:n});break;case "*":t.push({type:"alphanumeric",char:n});break;default:t.push({type:"literal",char:n});break}return t}function Go(e,t){switch(t){case "digit":return /\d/.test(e);case "letter":return /[a-zA-Z]/.test(e);case "alphanumeric":return /[a-zA-Z0-9]/.test(e);case "literal":return true}}function te(e){let t=typeof e=="string"?{mask:e}:e,{mask:n,placeholder:r="_",showPlaceholder:o=false}=t,s=Fo(n);return (i,c)=>{let l=[];for(let d of i)(d!==r&&!/[\s\-\(\)\/\.\:]/.test(d)||/[a-zA-Z0-9]/.test(d))&&/[a-zA-Z0-9]/.test(d)&&l.push(d);let a="",p=0;for(let d of s)if(d.type==="literal")(p<l.length||o)&&(a+=d.char);else if(p<l.length){let S=l[p];if(Go(S,d.type))a+=S,p++;else {p++;continue}}else o&&(a+=r);return a}}var Lo={usPhone:te("(999) 999-9999"),intlPhone:te("+9 999 999 9999"),dateUS:te("99/99/9999"),dateEU:te("99/99/9999"),dateISO:te("9999-99-99"),time:te("99:99"),timeFull:te("99:99:99"),creditCard:te("9999 9999 9999 9999"),ssn:te("999-99-9999"),zip:te("99999"),zipPlus4:te("99999-9999"),ipv4:te("999.999.999.999"),mac:te("**:**:**:**:**:**")};export{dr as Box,Er as Button,so as Checkbox,ao as DialogHost,ft as FocusScope,xr as Input,go as JumpNav,Ir as Keybind,Ct as List,jr as Menu,Nr as Portal,Wr as Progress,lo as Radio,Ar as ScrollView,oo as Select,Rr as Spacer,zr as Spinner,gr as Text,no as ToastHost,te as createMask,Lo as masks,ar as render,Io as useApp,uo as useDialog,So as useFocus,ko as useFocusRegistry,Ro as useFocusable,Be as useInput,fe as useLayout,eo as useToast};//# sourceMappingURL=index.js.map
|
|
4807
15
|
//# sourceMappingURL=index.js.map
|