@solid-tui/runtime 0.1.0

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.mjs ADDED
@@ -0,0 +1,446 @@
1
+ import { $ as createMemo, A as createElement, F as memo, H as addLayoutListener, I as mergeProps, J as catchError, L as setProp, M as effect, N as insert, P as insertNode, Q as createEffect, R as spread, T as kittyModifiers, X as createComponent$1, Y as children, Z as createContext, at as splitProps, b as nonAlphanumericKeys, d as AnimationSchedulerKey, et as createRenderEffect, f as AppContextKey, h as TextContextKey, it as onCleanup, j as createTextNode, k as createComponent, l as Text, m as StdinContextKey, nt as createSignal, ot as untrack, p as FocusContextKey, q as batch, r as createApp, rt as on, s as useWindowSize, st as useContext, t as renderToString, tt as createRoot, u as Box, v as createAnimationScheduler, w as kittyFlags, x as parseKeypress, y as normalizeInterval, z as use } from "./render-to-string-zweiEGvu.mjs";
2
+ //#region src/components/newline.ts
3
+ function Newline(props) {
4
+ const el = createElement(useContext(TextContextKey) ? "tui-virtual-text" : "tui-text");
5
+ insert(el, () => "\n".repeat(props.count ?? 1));
6
+ return el;
7
+ }
8
+ //#endregion
9
+ //#region src/components/spacer.ts
10
+ function Spacer(_props) {
11
+ const el = createElement("tui-box");
12
+ setProp(el, "flexGrow", 1);
13
+ setProp(el, "flexShrink", 1);
14
+ return el;
15
+ }
16
+ //#endregion
17
+ //#region src/components/static.ts
18
+ function Static(props) {
19
+ const [cursor, setCursor] = createSignal(0);
20
+ const el = createElement("tui-static");
21
+ const onWritten = () => {
22
+ setCursor(props.items.length);
23
+ };
24
+ createEffect(() => {
25
+ const len = props.items.length;
26
+ if (len < cursor()) setCursor(len);
27
+ });
28
+ spread(el, () => ({
29
+ position: "absolute",
30
+ flexDirection: "column",
31
+ ...props.style,
32
+ internal_onWritten: onWritten
33
+ }), true);
34
+ insert(el, () => {
35
+ const renderItem = props.children;
36
+ if (!renderItem) return null;
37
+ return props.items.slice(cursor()).map((item, i) => renderItem({
38
+ item,
39
+ index: cursor() + i
40
+ }));
41
+ });
42
+ return el;
43
+ }
44
+ //#endregion
45
+ //#region src/components/transform.ts
46
+ function isEmpty(value) {
47
+ if (value == null || value === false || value === true) return true;
48
+ if (Array.isArray(value)) return value.every(isEmpty);
49
+ return false;
50
+ }
51
+ function Transform(props) {
52
+ const appCtx = useContext(AppContextKey);
53
+ const [local, rest] = splitProps(props, ["children", "accessibilityLabel"]);
54
+ const content = createComponent$1(TextContextKey.Provider, {
55
+ value: true,
56
+ get children() {
57
+ return local.children;
58
+ }
59
+ });
60
+ const resolved = children(() => content);
61
+ const el = createElement("tui-transform");
62
+ spread(el, rest, true);
63
+ insert(el, () => {
64
+ return (appCtx?.isScreenReaderEnabled ?? false) && local.accessibilityLabel ? local.accessibilityLabel : resolved();
65
+ });
66
+ return (() => isEmpty(resolved()) ? null : el);
67
+ }
68
+ //#endregion
69
+ //#region src/hooks/useApp.ts
70
+ function useApp() {
71
+ const ctx = useContext(AppContextKey);
72
+ if (!ctx) throw new Error("useApp() must be called inside a solid-tui render tree");
73
+ return {
74
+ exit: ctx.exit,
75
+ waitUntilRenderFlush: ctx.waitUntilRenderFlush
76
+ };
77
+ }
78
+ //#endregion
79
+ //#region src/hooks/useInput.ts
80
+ const read$3 = (value) => typeof value === "function" ? value() : value;
81
+ function useInput(handler, options = {}) {
82
+ const stdin = useContext(StdinContextKey);
83
+ if (!stdin) throw new Error("useInput() must be called inside a solid-tui render tree");
84
+ const stdinCtx = stdin;
85
+ let attached = false;
86
+ function listener(data) {
87
+ const keypress = parseKeypress(data);
88
+ if (keypress.ignore) return;
89
+ const key = {
90
+ upArrow: keypress.name === "up",
91
+ downArrow: keypress.name === "down",
92
+ leftArrow: keypress.name === "left",
93
+ rightArrow: keypress.name === "right",
94
+ pageDown: keypress.name === "pagedown",
95
+ pageUp: keypress.name === "pageup",
96
+ home: keypress.name === "home",
97
+ end: keypress.name === "end",
98
+ return: keypress.name === "return",
99
+ escape: keypress.name === "escape",
100
+ ctrl: keypress.ctrl,
101
+ shift: keypress.shift,
102
+ tab: keypress.name === "tab",
103
+ backspace: keypress.name === "backspace",
104
+ delete: keypress.name === "delete",
105
+ meta: keypress.meta,
106
+ super: keypress.super ?? false,
107
+ hyper: keypress.hyper ?? false,
108
+ capsLock: keypress.capsLock ?? false,
109
+ numLock: keypress.numLock ?? false,
110
+ eventType: keypress.eventType
111
+ };
112
+ let input;
113
+ if (keypress.isKittyProtocol) if (keypress.isPrintable) input = keypress.text ?? keypress.name;
114
+ else if (keypress.ctrl && keypress.name.length === 1) input = keypress.name;
115
+ else input = "";
116
+ else if (keypress.ctrl) input = keypress.name ?? "";
117
+ else input = keypress.sequence;
118
+ if (!keypress.isKittyProtocol && nonAlphanumericKeys.includes(keypress.name)) input = "";
119
+ if (input.startsWith("\x1B")) input = input.slice(1);
120
+ if (input.length === 1 && /[A-Z]/.test(input)) key.shift = true;
121
+ handler(input, key);
122
+ }
123
+ function attach() {
124
+ if (attached) return;
125
+ attached = true;
126
+ stdinCtx.acquireRawMode();
127
+ stdinCtx.internal_eventEmitter.on("input", listener);
128
+ }
129
+ function detach() {
130
+ if (!attached) return;
131
+ attached = false;
132
+ stdinCtx.internal_eventEmitter.off("input", listener);
133
+ stdinCtx.releaseRawMode();
134
+ }
135
+ createEffect(() => {
136
+ if (read$3(options.isActive ?? true)) attach();
137
+ else detach();
138
+ });
139
+ onCleanup(detach);
140
+ }
141
+ //#endregion
142
+ //#region src/hooks/usePaste.ts
143
+ const read$2 = (value) => typeof value === "function" ? value() : value;
144
+ function usePaste(handler, options = {}) {
145
+ const stdin = useContext(StdinContextKey);
146
+ if (!stdin) throw new Error("usePaste() must be called inside a solid-tui render tree");
147
+ const stdinCtx = stdin;
148
+ let attached = false;
149
+ function listener(text) {
150
+ handler(text);
151
+ }
152
+ function attach() {
153
+ if (attached) return;
154
+ attached = true;
155
+ stdinCtx.acquireRawMode();
156
+ stdinCtx.setBracketedPasteMode(true);
157
+ stdinCtx.internal_eventEmitter.on("paste", listener);
158
+ }
159
+ function detach() {
160
+ if (!attached) return;
161
+ attached = false;
162
+ stdinCtx.internal_eventEmitter.off("paste", listener);
163
+ stdinCtx.setBracketedPasteMode(false);
164
+ stdinCtx.releaseRawMode();
165
+ }
166
+ createEffect(() => {
167
+ if (read$2(options.isActive ?? true)) attach();
168
+ else detach();
169
+ });
170
+ onCleanup(detach);
171
+ }
172
+ //#endregion
173
+ //#region src/hooks/useFocus.ts
174
+ let nextAutoId = 0;
175
+ const read$1 = (value) => typeof value === "function" ? value() : value;
176
+ function useFocus(options = {}) {
177
+ const ctx = useContext(FocusContextKey);
178
+ const stdin = useContext(StdinContextKey);
179
+ if (!ctx) throw new Error("useFocus() must be called inside a solid-tui render tree");
180
+ const fallbackId = `__auto-${nextAutoId++}`;
181
+ const [isFocused, setFocused] = createSignal(false);
182
+ let rawModeAcquired = false;
183
+ let currentId;
184
+ let unsubscribe;
185
+ const unregister = () => {
186
+ if (currentId === void 0) return;
187
+ unsubscribe?.();
188
+ unsubscribe = void 0;
189
+ ctx.remove(currentId);
190
+ currentId = void 0;
191
+ };
192
+ const acquireRaw = () => {
193
+ if (!rawModeAcquired && stdin?.isRawModeSupported) {
194
+ stdin.acquireRawMode();
195
+ rawModeAcquired = true;
196
+ }
197
+ };
198
+ const releaseRaw = () => {
199
+ if (rawModeAcquired && stdin) {
200
+ stdin.releaseRawMode();
201
+ rawModeAcquired = false;
202
+ }
203
+ };
204
+ createEffect(() => {
205
+ const id = read$1(options.id ?? fallbackId) ?? fallbackId;
206
+ const autoFocus = read$1(options.autoFocus ?? false);
207
+ if (id === currentId) return;
208
+ unregister();
209
+ setFocused(false);
210
+ unsubscribe = ctx.subscribe(id, setFocused);
211
+ ctx.add(id, { autoFocus });
212
+ currentId = id;
213
+ setFocused(ctx.activeId === id);
214
+ if (read$1(options.isActive ?? true)) {
215
+ ctx.activate(id);
216
+ acquireRaw();
217
+ } else ctx.deactivate(id);
218
+ });
219
+ createEffect(() => {
220
+ if (currentId === void 0) return;
221
+ if (read$1(options.isActive ?? true)) {
222
+ ctx.activate(currentId);
223
+ acquireRaw();
224
+ } else {
225
+ ctx.deactivate(currentId);
226
+ releaseRaw();
227
+ }
228
+ });
229
+ onCleanup(() => {
230
+ unregister();
231
+ releaseRaw();
232
+ });
233
+ return {
234
+ isFocused,
235
+ focus: ctx.focus
236
+ };
237
+ }
238
+ //#endregion
239
+ //#region src/hooks/useFocusManager.ts
240
+ function useFocusManager() {
241
+ const ctx = useContext(FocusContextKey);
242
+ if (!ctx) throw new Error("useFocusManager() must be called inside a solid-tui render tree");
243
+ return {
244
+ enableFocus: ctx.enableFocus.bind(ctx),
245
+ disableFocus: ctx.disableFocus.bind(ctx),
246
+ focusNext: ctx.focusNext.bind(ctx),
247
+ focusPrevious: ctx.focusPrevious.bind(ctx),
248
+ focus: ctx.focus.bind(ctx),
249
+ activeId: ctx.activeIdValue
250
+ };
251
+ }
252
+ //#endregion
253
+ //#region src/hooks/useStdin.ts
254
+ function useStdin() {
255
+ const ctx = useContext(StdinContextKey);
256
+ if (!ctx) throw new Error("useStdin() must be called inside a solid-tui render tree");
257
+ return ctx;
258
+ }
259
+ //#endregion
260
+ //#region src/hooks/useStdout.ts
261
+ function useStdout() {
262
+ const ctx = useContext(AppContextKey);
263
+ if (!ctx) throw new Error("useStdout() must be called inside a solid-tui render tree");
264
+ return {
265
+ stdout: ctx.stdout,
266
+ write: (data) => ctx.writeToStdout(data)
267
+ };
268
+ }
269
+ //#endregion
270
+ //#region src/hooks/useStderr.ts
271
+ function useStderr() {
272
+ const ctx = useContext(AppContextKey);
273
+ if (!ctx) throw new Error("useStderr() must be called inside a solid-tui render tree");
274
+ return {
275
+ stderr: ctx.stderr,
276
+ write: (data) => ctx.writeToStderr(data)
277
+ };
278
+ }
279
+ //#endregion
280
+ //#region src/hooks/useCursor.ts
281
+ function useCursor() {
282
+ const ctx = useContext(AppContextKey);
283
+ if (!ctx) throw new Error("useCursor() must be called inside a solid-tui render tree");
284
+ const [position, setPosition] = createSignal(void 0);
285
+ createEffect(() => {
286
+ ctx.setCursorPosition(position());
287
+ });
288
+ onCleanup(() => {
289
+ ctx.setCursorPosition(void 0);
290
+ });
291
+ return { setCursorPosition: setPosition };
292
+ }
293
+ //#endregion
294
+ //#region src/hooks/useIsScreenReaderEnabled.ts
295
+ function useIsScreenReaderEnabled() {
296
+ const ctx = useContext(AppContextKey);
297
+ if (!ctx) throw new Error("useIsScreenReaderEnabled() must be called inside a solid-tui render tree");
298
+ return ctx.isScreenReaderEnabled;
299
+ }
300
+ //#endregion
301
+ //#region src/hooks/useAnimation.ts
302
+ const read = (value) => typeof value === "function" ? value() : value;
303
+ function useAnimation(options = {}) {
304
+ const [frame, setFrame] = createSignal(0);
305
+ const [time, setTime] = createSignal(0);
306
+ const [delta, setDelta] = createSignal(0);
307
+ const scheduler = useContext(AnimationSchedulerKey) ?? createAnimationScheduler();
308
+ const renderThrottleMs = scheduler.renderThrottleMs;
309
+ let interval = normalizeInterval(read(options.interval ?? 100));
310
+ let handle;
311
+ let startTime = 0;
312
+ let lastRenderedTime = 0;
313
+ let nextRenderTime = 0;
314
+ function tick(now) {
315
+ if (renderThrottleMs > 0 && now < nextRenderTime) return;
316
+ setFrame(Math.floor((now - startTime) / interval));
317
+ setTime(now - startTime);
318
+ setDelta(now - lastRenderedTime);
319
+ lastRenderedTime = now;
320
+ nextRenderTime = now + renderThrottleMs;
321
+ }
322
+ function stop() {
323
+ handle?.unsubscribe();
324
+ handle = void 0;
325
+ }
326
+ function start() {
327
+ stop();
328
+ setFrame(0);
329
+ setTime(0);
330
+ setDelta(0);
331
+ handle = scheduler.subscribe(tick, interval);
332
+ startTime = handle.startTime;
333
+ lastRenderedTime = handle.startTime;
334
+ nextRenderTime = handle.startTime + renderThrottleMs;
335
+ }
336
+ function reset() {
337
+ if (handle) start();
338
+ }
339
+ createEffect((prev) => {
340
+ const active = read(options.isActive ?? true);
341
+ const nextInterval = normalizeInterval(read(options.interval ?? 100));
342
+ const intervalChanged = prev !== void 0 && nextInterval !== prev.interval;
343
+ const becameActive = prev === void 0 || !prev.active;
344
+ interval = nextInterval;
345
+ if (!active) {
346
+ stop();
347
+ return {
348
+ active,
349
+ interval
350
+ };
351
+ }
352
+ if (becameActive || intervalChanged) start();
353
+ return {
354
+ active,
355
+ interval
356
+ };
357
+ });
358
+ onCleanup(stop);
359
+ return {
360
+ frame,
361
+ time,
362
+ delta,
363
+ reset
364
+ };
365
+ }
366
+ //#endregion
367
+ //#region src/hooks/useBoxMetrics.ts
368
+ function resolveYogaNode(value) {
369
+ if (!value || typeof value !== "object") return null;
370
+ const obj = value;
371
+ if (obj.yoga) return obj;
372
+ return null;
373
+ }
374
+ function resolveTuiNode(value) {
375
+ if (!value || typeof value !== "object") return null;
376
+ const obj = value;
377
+ return typeof obj.type === "string" ? obj : null;
378
+ }
379
+ function findRootNode(node) {
380
+ let current = node;
381
+ while (current) {
382
+ if (current.type === "root") return current;
383
+ current = current.parent;
384
+ }
385
+ return null;
386
+ }
387
+ function measureElement(node) {
388
+ const tuiNode = resolveYogaNode(node);
389
+ if (!tuiNode) return {
390
+ width: 0,
391
+ height: 0
392
+ };
393
+ const width = tuiNode.yoga.getComputedWidth();
394
+ const height = tuiNode.yoga.getComputedHeight();
395
+ return {
396
+ width: Number.isFinite(width) ? width : 0,
397
+ height: Number.isFinite(height) ? height : 0
398
+ };
399
+ }
400
+ function useBoxMetrics(ref) {
401
+ const [width, setWidth] = createSignal(0);
402
+ const [height, setHeight] = createSignal(0);
403
+ const [left, setLeft] = createSignal(0);
404
+ const [top, setTop] = createSignal(0);
405
+ const [hasMeasured, setHasMeasured] = createSignal(false);
406
+ let unsubscribeLayout;
407
+ function updateMetrics() {
408
+ const node = resolveYogaNode(ref());
409
+ if (!node) {
410
+ setWidth(0);
411
+ setHeight(0);
412
+ setLeft(0);
413
+ setTop(0);
414
+ setHasMeasured(false);
415
+ return;
416
+ }
417
+ const w = node.yoga.getComputedWidth();
418
+ const h = node.yoga.getComputedHeight();
419
+ const l = node.yoga.getComputedLeft();
420
+ const t = node.yoga.getComputedTop();
421
+ setWidth(Number.isFinite(w) ? w : 0);
422
+ setHeight(Number.isFinite(h) ? h : 0);
423
+ setLeft(Number.isFinite(l) ? l : 0);
424
+ setTop(Number.isFinite(t) ? t : 0);
425
+ setHasMeasured(true);
426
+ }
427
+ createEffect(() => {
428
+ unsubscribeLayout?.();
429
+ unsubscribeLayout = void 0;
430
+ const root = findRootNode(resolveTuiNode(ref()));
431
+ if (root) {
432
+ unsubscribeLayout = addLayoutListener(root, updateMetrics);
433
+ updateMetrics();
434
+ } else updateMetrics();
435
+ });
436
+ onCleanup(() => unsubscribeLayout?.());
437
+ return {
438
+ width,
439
+ height,
440
+ left,
441
+ top,
442
+ hasMeasured
443
+ };
444
+ }
445
+ //#endregion
446
+ export { Box, Newline, Spacer, Static, Text, Transform, batch, catchError, children, createApp, createComponent, createContext, createEffect, createElement, createMemo, createRenderEffect, createRoot, createSignal, createTextNode, effect, insert, insertNode, kittyFlags, kittyModifiers, measureElement, memo, mergeProps, on, onCleanup, renderToString, setProp, splitProps, spread, untrack, use, useAnimation, useApp, useBoxMetrics, useContext, useCursor, useFocus, useFocusManager, useInput, useIsScreenReaderEnabled, usePaste, useStderr, useStdin, useStdout, useWindowSize };