giggles 0.3.16 → 0.4.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/README.md +2 -2
- package/dist/{chunk-JTJH45JR.js → chunk-DFB7V4OK.js} +1 -1
- package/dist/{chunk-CKA5JJ4B.js → chunk-ET2WSMEF.js} +60 -38
- package/dist/{chunk-EVD6YPS3.js → chunk-R5I4YOOP.js} +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.js +4 -2
- package/dist/markdown/index.js +2 -2
- package/dist/terminal/index.d.ts +23 -1
- package/dist/terminal/index.js +79 -0
- package/dist/ui/index.js +12 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,11 +14,11 @@ inspired by the [charmbracelet](https://github.com/charmbracelet) ecosystem, it
|
|
|
14
14
|
|
|
15
15
|
- no `useInput` hooks scattered across your app — focus, input routing, and keyboard navigation are handled for you
|
|
16
16
|
- navigate between views with a simple API; the previously focused component is restored when you return
|
|
17
|
-
- a full set of hooks and components — `useFocus`, `FocusGroup`, `FocusTrap`, `useNavigation`, and more — for building any interaction pattern without reimplementing the plumbing
|
|
17
|
+
- a full set of hooks and components — `useFocus`, `useFocusNode`, `FocusGroup`, `FocusTrap`, `useNavigation`, and more — for building any interaction pattern without reimplementing the plumbing
|
|
18
18
|
- built-in keybinding registry so your app can always show users what keys do what, in the current context — context-aware and accessible via a hook
|
|
19
19
|
- a component library covering most TUI use cases, from text inputs and autocomplete to virtual lists for large datasets — with sensible defaults and render props for full customization
|
|
20
20
|
- render markdown in the terminal, with full formatting and syntax-highlighted code block and diff support
|
|
21
|
-
- hand off terminal control to external programs like `vim` or `less` and reclaim it cleanly when they exit
|
|
21
|
+
- hand off terminal control to external programs like `vim` or `less` and reclaim it cleanly when they exit, or spawn processes and stream their output directly into your UI
|
|
22
22
|
- a consistent look out of the box, customizable from a single theme object
|
|
23
23
|
|
|
24
24
|
## your first TUI
|
|
@@ -104,6 +104,7 @@ import { jsx as jsx2 } from "react/jsx-runtime";
|
|
|
104
104
|
var FocusContext = createContext2(null);
|
|
105
105
|
var FocusProvider = ({ children }) => {
|
|
106
106
|
const nodesRef = useRef2(/* @__PURE__ */ new Map());
|
|
107
|
+
const parentMapRef = useRef2(/* @__PURE__ */ new Map());
|
|
107
108
|
const pendingFocusFirstChildRef = useRef2(/* @__PURE__ */ new Set());
|
|
108
109
|
const [focusedId, setFocusedId] = useState(null);
|
|
109
110
|
const focusNode = useCallback2((id) => {
|
|
@@ -141,6 +142,7 @@ var FocusProvider = ({ children }) => {
|
|
|
141
142
|
childrenIds: []
|
|
142
143
|
};
|
|
143
144
|
nodes.set(id, node);
|
|
145
|
+
parentMapRef.current.set(id, parentId);
|
|
144
146
|
if (parentId) {
|
|
145
147
|
const parent = nodes.get(parentId);
|
|
146
148
|
if (parent && !parent.childrenIds.includes(id)) {
|
|
@@ -177,12 +179,25 @@ var FocusProvider = ({ children }) => {
|
|
|
177
179
|
pendingFocusFirstChildRef.current.delete(id);
|
|
178
180
|
setFocusedId((current) => {
|
|
179
181
|
if (current !== id) return current;
|
|
180
|
-
|
|
182
|
+
let candidate = node.parentId;
|
|
183
|
+
while (candidate !== null) {
|
|
184
|
+
if (nodesRef.current.has(candidate)) return candidate;
|
|
185
|
+
candidate = parentMapRef.current.get(candidate) ?? null;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
181
188
|
});
|
|
182
189
|
}, []);
|
|
183
190
|
const isFocused = useCallback2(
|
|
184
191
|
(id) => {
|
|
185
|
-
|
|
192
|
+
if (!focusedId) return false;
|
|
193
|
+
const nodes = nodesRef.current;
|
|
194
|
+
let cursor = focusedId;
|
|
195
|
+
while (cursor) {
|
|
196
|
+
if (cursor === id) return true;
|
|
197
|
+
const node = nodes.get(cursor);
|
|
198
|
+
cursor = (node == null ? void 0 : node.parentId) ?? null;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
186
201
|
},
|
|
187
202
|
[focusedId]
|
|
188
203
|
);
|
|
@@ -220,7 +235,16 @@ var FocusProvider = ({ children }) => {
|
|
|
220
235
|
}
|
|
221
236
|
cursor = (node == null ? void 0 : node.parentId) ?? null;
|
|
222
237
|
}
|
|
223
|
-
if (!currentChildId)
|
|
238
|
+
if (!currentChildId) {
|
|
239
|
+
const targetId2 = direction === "next" ? siblings[0] : siblings[siblings.length - 1];
|
|
240
|
+
const target2 = nodes.get(targetId2);
|
|
241
|
+
if (target2 && target2.childrenIds.length > 0) {
|
|
242
|
+
focusFirstChild(targetId2);
|
|
243
|
+
} else {
|
|
244
|
+
focusNode(targetId2);
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
224
248
|
} else {
|
|
225
249
|
const currentNode = nodes.get(focusedId);
|
|
226
250
|
if (!(currentNode == null ? void 0 : currentNode.parentId)) return;
|
|
@@ -372,9 +396,9 @@ function InputRouter({ children }) {
|
|
|
372
396
|
import { createContext as createContext3 } from "react";
|
|
373
397
|
var FocusBindContext = createContext3(null);
|
|
374
398
|
|
|
375
|
-
// src/core/focus/
|
|
399
|
+
// src/core/focus/useFocusNode.ts
|
|
376
400
|
import { useContext as useContext3, useEffect as useEffect2, useId } from "react";
|
|
377
|
-
var
|
|
401
|
+
var useFocusNode = (id) => {
|
|
378
402
|
const nodeId = useId();
|
|
379
403
|
const parentId = useContext3(FocusNodeContext);
|
|
380
404
|
const bindContext = useContext3(FocusBindContext);
|
|
@@ -400,15 +424,8 @@ var useFocus = (id) => {
|
|
|
400
424
|
|
|
401
425
|
// src/core/focus/FocusGroup.tsx
|
|
402
426
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
403
|
-
function FocusGroup({
|
|
404
|
-
|
|
405
|
-
direction = "vertical",
|
|
406
|
-
value,
|
|
407
|
-
wrap = true,
|
|
408
|
-
navigable = true,
|
|
409
|
-
keybindings: customBindings
|
|
410
|
-
}) {
|
|
411
|
-
const focus = useFocus();
|
|
427
|
+
function FocusGroup({ children, value, wrap = true, keybindings: customBindings }) {
|
|
428
|
+
const focus = useFocusNode();
|
|
412
429
|
const { focusNode, navigateSibling } = useFocusContext();
|
|
413
430
|
const bindMapRef = useRef3(/* @__PURE__ */ new Map());
|
|
414
431
|
const register = useCallback3((logicalId, nodeId) => {
|
|
@@ -429,31 +446,35 @@ function FocusGroup({
|
|
|
429
446
|
}
|
|
430
447
|
}, [value, focusNode]);
|
|
431
448
|
const bindContextValue = useMemo(() => value ? { register, unregister } : null, [value, register, unregister]);
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
l: next,
|
|
443
|
-
h: prev,
|
|
444
|
-
right: next,
|
|
445
|
-
left: prev
|
|
446
|
-
};
|
|
447
|
-
return { ...base, tab: next, "shift+tab": prev };
|
|
448
|
-
}, [navigable, direction, wrap, navigateSibling, focus.id]);
|
|
449
|
-
const mergedBindings = useMemo(
|
|
450
|
-
() => ({ ...navigationKeys, ...customBindings }),
|
|
451
|
-
[navigationKeys, customBindings]
|
|
452
|
-
);
|
|
453
|
-
useKeybindings(focus, mergedBindings);
|
|
449
|
+
const next = useCallback3(() => navigateSibling("next", wrap, focus.id), [navigateSibling, wrap, focus.id]);
|
|
450
|
+
const prev = useCallback3(() => navigateSibling("prev", wrap, focus.id), [navigateSibling, wrap, focus.id]);
|
|
451
|
+
const escape = useCallback3(() => focusNode(focus.id), [focusNode, focus.id]);
|
|
452
|
+
const resolvedBindings = useMemo(() => {
|
|
453
|
+
if (typeof customBindings === "function") {
|
|
454
|
+
return customBindings({ next, prev, escape });
|
|
455
|
+
}
|
|
456
|
+
return customBindings ?? {};
|
|
457
|
+
}, [customBindings, next, prev, escape]);
|
|
458
|
+
useKeybindings(focus, resolvedBindings);
|
|
454
459
|
return /* @__PURE__ */ jsx4(FocusNodeContext.Provider, { value: focus.id, children: /* @__PURE__ */ jsx4(FocusBindContext.Provider, { value: bindContextValue, children }) });
|
|
455
460
|
}
|
|
456
461
|
|
|
462
|
+
// src/core/focus/useFocus.ts
|
|
463
|
+
import { useContext as useContext4 } from "react";
|
|
464
|
+
var useFocus = () => {
|
|
465
|
+
const parentId = useContext4(FocusNodeContext);
|
|
466
|
+
const { focusNode, isFocused } = useFocusContext();
|
|
467
|
+
if (parentId === null) {
|
|
468
|
+
return { id: "", focused: false, focus: () => {
|
|
469
|
+
} };
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
id: parentId,
|
|
473
|
+
focused: isFocused(parentId),
|
|
474
|
+
focus: () => focusNode(parentId)
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
|
|
457
478
|
// src/core/focus/useFocusState.ts
|
|
458
479
|
import { useState as useState2 } from "react";
|
|
459
480
|
function useFocusState(initial) {
|
|
@@ -464,7 +485,7 @@ function useFocusState(initial) {
|
|
|
464
485
|
// src/core/input/FocusTrap.tsx
|
|
465
486
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
466
487
|
function FocusTrap({ children }) {
|
|
467
|
-
const { id } =
|
|
488
|
+
const { id } = useFocusNode();
|
|
468
489
|
const { setTrap, clearTrap } = useInputContext();
|
|
469
490
|
const { focusFirstChild, getFocusedId, focusNode } = useFocusContext();
|
|
470
491
|
const previousFocusRef = useRef4(getFocusedId());
|
|
@@ -492,7 +513,8 @@ export {
|
|
|
492
513
|
useKeybindings,
|
|
493
514
|
useKeybindingRegistry,
|
|
494
515
|
FocusTrap,
|
|
495
|
-
|
|
516
|
+
useFocusNode,
|
|
496
517
|
FocusGroup,
|
|
518
|
+
useFocus,
|
|
497
519
|
useFocusState
|
|
498
520
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ type GigglesTheme = {
|
|
|
18
18
|
hintHighlightColor: string;
|
|
19
19
|
hintHighlightDimColor: string;
|
|
20
20
|
indicator: string;
|
|
21
|
+
indicatorOpen: string;
|
|
21
22
|
checkedIndicator: string;
|
|
22
23
|
uncheckedIndicator: string;
|
|
23
24
|
};
|
|
@@ -49,15 +50,18 @@ type FocusTrapProps = {
|
|
|
49
50
|
};
|
|
50
51
|
declare function FocusTrap({ children }: FocusTrapProps): react_jsx_runtime.JSX.Element;
|
|
51
52
|
|
|
53
|
+
type FocusGroupHelpers = {
|
|
54
|
+
next: () => void;
|
|
55
|
+
prev: () => void;
|
|
56
|
+
escape: () => void;
|
|
57
|
+
};
|
|
52
58
|
type FocusGroupProps = {
|
|
53
59
|
children: React__default.ReactNode;
|
|
54
|
-
direction?: 'vertical' | 'horizontal';
|
|
55
60
|
value?: string;
|
|
56
61
|
wrap?: boolean;
|
|
57
|
-
|
|
58
|
-
keybindings?: Keybindings;
|
|
62
|
+
keybindings?: Keybindings | ((helpers: FocusGroupHelpers) => Keybindings);
|
|
59
63
|
};
|
|
60
|
-
declare function FocusGroup({ children,
|
|
64
|
+
declare function FocusGroup({ children, value, wrap, keybindings: customBindings }: FocusGroupProps): react_jsx_runtime.JSX.Element;
|
|
61
65
|
|
|
62
66
|
type FocusHandle = {
|
|
63
67
|
id: string;
|
|
@@ -65,7 +69,9 @@ type FocusHandle = {
|
|
|
65
69
|
focus: () => void;
|
|
66
70
|
};
|
|
67
71
|
|
|
68
|
-
declare const useFocus: (
|
|
72
|
+
declare const useFocus: () => FocusHandle;
|
|
73
|
+
|
|
74
|
+
declare const useFocusNode: (id?: string) => FocusHandle;
|
|
69
75
|
|
|
70
76
|
declare function useFocusState<T extends string>(initial: T): readonly [T, React$1.Dispatch<React$1.SetStateAction<T>>];
|
|
71
77
|
|
|
@@ -100,4 +106,4 @@ type NavigationContextValue = {
|
|
|
100
106
|
|
|
101
107
|
declare const useNavigation: () => NavigationContextValue;
|
|
102
108
|
|
|
103
|
-
export { FocusGroup, type FocusHandle, FocusTrap, GigglesError, GigglesProvider, type GigglesTheme, KeybindingOptions, type KeybindingRegistry, Keybindings, type NavigationContextValue, RegisteredKeybinding, Router, Screen, ThemeProvider, useFocus, useFocusState, useKeybindingRegistry, useKeybindings, useNavigation, useTheme };
|
|
109
|
+
export { FocusGroup, type FocusGroupHelpers, type FocusHandle, FocusTrap, GigglesError, GigglesProvider, type GigglesTheme, KeybindingOptions, type KeybindingRegistry, Keybindings, type NavigationContextValue, RegisteredKeybinding, Router, Screen, ThemeProvider, useFocus, useFocusNode, useFocusState, useKeybindingRegistry, useKeybindings, useNavigation, useTheme };
|
package/dist/index.js
CHANGED
|
@@ -11,14 +11,15 @@ import {
|
|
|
11
11
|
InputRouter,
|
|
12
12
|
useFocus,
|
|
13
13
|
useFocusContext,
|
|
14
|
+
useFocusNode,
|
|
14
15
|
useFocusState,
|
|
15
16
|
useKeybindingRegistry,
|
|
16
17
|
useKeybindings
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ET2WSMEF.js";
|
|
18
19
|
import {
|
|
19
20
|
ThemeProvider,
|
|
20
21
|
useTheme
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-R5I4YOOP.js";
|
|
22
23
|
|
|
23
24
|
// src/core/GigglesProvider.tsx
|
|
24
25
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -187,6 +188,7 @@ export {
|
|
|
187
188
|
Screen,
|
|
188
189
|
ThemeProvider,
|
|
189
190
|
useFocus,
|
|
191
|
+
useFocusNode,
|
|
190
192
|
useFocusState,
|
|
191
193
|
useKeybindingRegistry,
|
|
192
194
|
useKeybindings,
|
package/dist/markdown/index.js
CHANGED
package/dist/terminal/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SpawnOptionsWithoutStdio } from 'child_process';
|
|
1
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
3
|
import { ReactNode } from 'react';
|
|
3
4
|
|
|
@@ -5,6 +6,25 @@ type TerminalSize = {
|
|
|
5
6
|
rows: number;
|
|
6
7
|
columns: number;
|
|
7
8
|
};
|
|
9
|
+
type SpawnOptions = SpawnOptionsWithoutStdio & {
|
|
10
|
+
/**
|
|
11
|
+
* Inject FORCE_COLOR=1 and TERM=xterm-256color into the child process
|
|
12
|
+
* environment so tools that detect isTTY emit ANSI color codes.
|
|
13
|
+
*/
|
|
14
|
+
pty?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type SpawnOutputLine = {
|
|
17
|
+
type: 'stdout' | 'stderr';
|
|
18
|
+
data: string;
|
|
19
|
+
};
|
|
20
|
+
type SpawnHandle = {
|
|
21
|
+
output: SpawnOutputLine[];
|
|
22
|
+
running: boolean;
|
|
23
|
+
exitCode: number | null;
|
|
24
|
+
error: Error | null;
|
|
25
|
+
run: (command: string, args?: string[], options?: SpawnOptions) => void;
|
|
26
|
+
kill: () => void;
|
|
27
|
+
};
|
|
8
28
|
|
|
9
29
|
declare function useTerminalSize(): TerminalSize;
|
|
10
30
|
|
|
@@ -22,4 +42,6 @@ declare function useShellOut(): {
|
|
|
22
42
|
}>;
|
|
23
43
|
};
|
|
24
44
|
|
|
25
|
-
|
|
45
|
+
declare function useSpawn(): SpawnHandle;
|
|
46
|
+
|
|
47
|
+
export { AlternateScreen, type SpawnHandle, type SpawnOptions, type SpawnOutputLine, type TerminalSize, useShellOut, useSpawn, useTerminalFocus, useTerminalSize };
|
package/dist/terminal/index.js
CHANGED
|
@@ -54,9 +54,88 @@ function useShellOut() {
|
|
|
54
54
|
run
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
// src/terminal/hooks/useSpawn.ts
|
|
59
|
+
import { spawn } from "child_process";
|
|
60
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
61
|
+
function useSpawn() {
|
|
62
|
+
const [output, setOutput] = useState2([]);
|
|
63
|
+
const [running, setRunning] = useState2(false);
|
|
64
|
+
const [exitCode, setExitCode] = useState2(null);
|
|
65
|
+
const [error, setError] = useState2(null);
|
|
66
|
+
const processRef = useRef2(null);
|
|
67
|
+
const cancelRef = useRef2(null);
|
|
68
|
+
useEffect2(() => {
|
|
69
|
+
return () => {
|
|
70
|
+
var _a, _b;
|
|
71
|
+
(_a = cancelRef.current) == null ? void 0 : _a.call(cancelRef);
|
|
72
|
+
(_b = processRef.current) == null ? void 0 : _b.kill();
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
75
|
+
const run = useCallback2((command, args, options) => {
|
|
76
|
+
var _a, _b;
|
|
77
|
+
(_a = cancelRef.current) == null ? void 0 : _a.call(cancelRef);
|
|
78
|
+
(_b = processRef.current) == null ? void 0 : _b.kill();
|
|
79
|
+
processRef.current = null;
|
|
80
|
+
cancelRef.current = null;
|
|
81
|
+
let cancelled = false;
|
|
82
|
+
cancelRef.current = () => {
|
|
83
|
+
cancelled = true;
|
|
84
|
+
};
|
|
85
|
+
const { pty, ...spawnOptions } = options ?? {};
|
|
86
|
+
setOutput([]);
|
|
87
|
+
setRunning(true);
|
|
88
|
+
setExitCode(null);
|
|
89
|
+
setError(null);
|
|
90
|
+
const env = pty ? { ...process.env, FORCE_COLOR: "1", TERM: "xterm-256color", ...spawnOptions.env } : { ...process.env, ...spawnOptions.env };
|
|
91
|
+
let proc;
|
|
92
|
+
try {
|
|
93
|
+
proc = spawn(command, args ?? [], { ...spawnOptions, env, stdio: "pipe" });
|
|
94
|
+
} catch (err) {
|
|
95
|
+
setRunning(false);
|
|
96
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
97
|
+
cancelRef.current = null;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
processRef.current = proc;
|
|
101
|
+
proc.stdout.on("data", (data) => {
|
|
102
|
+
if (!cancelled) setOutput((prev) => [...prev, { type: "stdout", data: data.toString() }]);
|
|
103
|
+
});
|
|
104
|
+
proc.stderr.on("data", (data) => {
|
|
105
|
+
if (!cancelled) setOutput((prev) => [...prev, { type: "stderr", data: data.toString() }]);
|
|
106
|
+
});
|
|
107
|
+
proc.on("error", (err) => {
|
|
108
|
+
if (!cancelled) {
|
|
109
|
+
cancelled = true;
|
|
110
|
+
setError(err);
|
|
111
|
+
setRunning(false);
|
|
112
|
+
processRef.current = null;
|
|
113
|
+
cancelRef.current = null;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
proc.on("close", (code) => {
|
|
117
|
+
if (!cancelled) {
|
|
118
|
+
setRunning(false);
|
|
119
|
+
setExitCode(code);
|
|
120
|
+
processRef.current = null;
|
|
121
|
+
cancelRef.current = null;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}, []);
|
|
125
|
+
const kill = useCallback2(() => {
|
|
126
|
+
var _a, _b;
|
|
127
|
+
(_a = cancelRef.current) == null ? void 0 : _a.call(cancelRef);
|
|
128
|
+
cancelRef.current = null;
|
|
129
|
+
(_b = processRef.current) == null ? void 0 : _b.kill();
|
|
130
|
+
processRef.current = null;
|
|
131
|
+
setRunning(false);
|
|
132
|
+
}, []);
|
|
133
|
+
return { output, running, exitCode, error, run, kill };
|
|
134
|
+
}
|
|
57
135
|
export {
|
|
58
136
|
AlternateScreen,
|
|
59
137
|
useShellOut,
|
|
138
|
+
useSpawn,
|
|
60
139
|
useTerminalFocus,
|
|
61
140
|
useTerminalSize
|
|
62
141
|
};
|
package/dist/ui/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FocusTrap,
|
|
3
3
|
GigglesError,
|
|
4
|
-
|
|
4
|
+
useFocusNode,
|
|
5
5
|
useKeybindingRegistry,
|
|
6
6
|
useKeybindings
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-ET2WSMEF.js";
|
|
8
8
|
import {
|
|
9
9
|
CodeBlock
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-DFB7V4OK.js";
|
|
11
11
|
import {
|
|
12
12
|
useTheme
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-R5I4YOOP.js";
|
|
14
14
|
|
|
15
15
|
// src/ui/CommandPalette.tsx
|
|
16
16
|
import { useState } from "react";
|
|
@@ -49,7 +49,7 @@ function fuzzyMatch(name, query) {
|
|
|
49
49
|
return qi === lowerQuery.length;
|
|
50
50
|
}
|
|
51
51
|
function Inner({ onClose, render }) {
|
|
52
|
-
const focus =
|
|
52
|
+
const focus = useFocusNode();
|
|
53
53
|
const theme = useTheme();
|
|
54
54
|
const [query, setQuery] = useState("");
|
|
55
55
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -138,7 +138,7 @@ import { useReducer, useRef } from "react";
|
|
|
138
138
|
import { Text as Text2 } from "ink";
|
|
139
139
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
140
140
|
function TextInput({ label, value, onChange, onSubmit, placeholder, render }) {
|
|
141
|
-
const focus =
|
|
141
|
+
const focus = useFocusNode();
|
|
142
142
|
const cursorRef = useRef(value.length);
|
|
143
143
|
const [, forceRender] = useReducer((c) => c + 1, 0);
|
|
144
144
|
const cursor = Math.min(cursorRef.current, value.length);
|
|
@@ -360,7 +360,7 @@ function Select({
|
|
|
360
360
|
}
|
|
361
361
|
seen.add(key);
|
|
362
362
|
}
|
|
363
|
-
const focus =
|
|
363
|
+
const focus = useFocusNode();
|
|
364
364
|
const theme = useTheme();
|
|
365
365
|
const [highlightIndex, setHighlightIndex] = useState3(0);
|
|
366
366
|
const safeIndex = options.length === 0 ? -1 : Math.min(highlightIndex, options.length - 1);
|
|
@@ -447,7 +447,7 @@ function MultiSelect({
|
|
|
447
447
|
}
|
|
448
448
|
seen.add(key);
|
|
449
449
|
}
|
|
450
|
-
const focus =
|
|
450
|
+
const focus = useFocusNode();
|
|
451
451
|
const theme = useTheme();
|
|
452
452
|
const [highlightIndex, setHighlightIndex] = useState4(0);
|
|
453
453
|
const safeIndex = options.length === 0 ? -1 : Math.min(highlightIndex, options.length - 1);
|
|
@@ -512,7 +512,7 @@ function MultiSelect({
|
|
|
512
512
|
import { Text as Text6 } from "ink";
|
|
513
513
|
import { jsxs as jsxs7 } from "react/jsx-runtime";
|
|
514
514
|
function Confirm({ message, defaultValue = true, onSubmit }) {
|
|
515
|
-
const focus =
|
|
515
|
+
const focus = useFocusNode();
|
|
516
516
|
useKeybindings(focus, {
|
|
517
517
|
y: () => onSubmit(true),
|
|
518
518
|
n: () => onSubmit(false),
|
|
@@ -564,7 +564,7 @@ function Autocomplete({
|
|
|
564
564
|
}
|
|
565
565
|
seen.add(key);
|
|
566
566
|
}
|
|
567
|
-
const focus =
|
|
567
|
+
const focus = useFocusNode();
|
|
568
568
|
const theme = useTheme();
|
|
569
569
|
const [query, setQuery] = useState5("");
|
|
570
570
|
const [highlightIndex, setHighlightIndex] = useState5(0);
|
|
@@ -716,7 +716,7 @@ function MeasurableItem({
|
|
|
716
716
|
return /* @__PURE__ */ jsx8(Box7, { ref, flexShrink: 0, width: "100%", flexDirection: "column", children });
|
|
717
717
|
}
|
|
718
718
|
var Viewport = forwardRef(function Viewport2({ children, height, keybindings: enableKeybindings = true, footer }, ref) {
|
|
719
|
-
const focus =
|
|
719
|
+
const focus = useFocusNode();
|
|
720
720
|
const [scrollOffset, setScrollOffset] = useState6(0);
|
|
721
721
|
const contentHeightRef = useRef4(0);
|
|
722
722
|
const itemHeightsRef = useRef4({});
|
|
@@ -822,7 +822,7 @@ var Viewport = forwardRef(function Viewport2({ children, height, keybindings: en
|
|
|
822
822
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
823
823
|
import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
824
824
|
function ModalInner({ children, onClose, title, ...boxProps }) {
|
|
825
|
-
const focus =
|
|
825
|
+
const focus = useFocusNode();
|
|
826
826
|
const theme = useTheme();
|
|
827
827
|
useKeybindings(focus, {
|
|
828
828
|
escape: onClose
|