giggles 0.2.4 → 0.3.1

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 CHANGED
@@ -1,427 +1,29 @@
1
- // src/core/GigglesError.ts
2
- var GigglesError = class extends Error {
3
- constructor(message) {
4
- super(`[giggles] ${message}`);
5
- this.name = "GigglesError";
6
- }
7
- };
8
-
9
- // src/core/focus/FocusContext.tsx
10
- import { createContext, useCallback, useContext, useRef, useState } from "react";
11
- import { jsx } from "react/jsx-runtime";
12
- var FocusContext = createContext(null);
13
- var FocusProvider = ({ children }) => {
14
- const nodesRef = useRef(/* @__PURE__ */ new Map());
15
- const pendingFocusFirstChildRef = useRef(/* @__PURE__ */ new Set());
16
- const [focusedId, setFocusedId] = useState(null);
17
- const [activeBranchNodes, setActiveBranchNodes] = useState(/* @__PURE__ */ new Set());
18
- const [activeBranchPath, setActiveBranchPath] = useState([]);
19
- const focusNode = useCallback((id) => {
20
- const nodes = nodesRef.current;
21
- if (!nodes.has(id)) return;
22
- setFocusedId((current) => {
23
- if (current === id) return current;
24
- const pathSet = /* @__PURE__ */ new Set();
25
- const pathArray = [];
26
- let currentNode = id;
27
- while (currentNode) {
28
- pathSet.add(currentNode);
29
- pathArray.push(currentNode);
30
- const node = nodes.get(currentNode);
31
- currentNode = (node == null ? void 0 : node.parentId) ?? null;
32
- }
33
- setActiveBranchNodes(pathSet);
34
- setActiveBranchPath(pathArray);
35
- return id;
36
- });
37
- }, []);
38
- const focusFirstChild = useCallback(
39
- (parentId) => {
40
- const nodes = nodesRef.current;
41
- const parent = nodes.get(parentId);
42
- if (parent && parent.childrenIds.length > 0) {
43
- focusNode(parent.childrenIds[0]);
44
- } else {
45
- pendingFocusFirstChildRef.current.add(parentId);
46
- }
47
- },
48
- [focusNode]
49
- );
50
- const registerNode = useCallback(
51
- (id, parentId) => {
52
- const nodes = nodesRef.current;
53
- const node = {
54
- id,
55
- parentId,
56
- childrenIds: []
57
- };
58
- nodes.set(id, node);
59
- if (parentId) {
60
- const parent = nodes.get(parentId);
61
- if (parent && !parent.childrenIds.includes(id)) {
62
- const wasEmpty = parent.childrenIds.length === 0;
63
- parent.childrenIds.push(id);
64
- if (wasEmpty && pendingFocusFirstChildRef.current.has(parentId)) {
65
- pendingFocusFirstChildRef.current.delete(parentId);
66
- focusNode(id);
67
- }
68
- }
69
- }
70
- nodes.forEach((existingNode) => {
71
- if (existingNode.parentId === id && !node.childrenIds.includes(existingNode.id)) {
72
- node.childrenIds.push(existingNode.id);
73
- }
74
- });
75
- if (nodes.size === 1) {
76
- focusNode(id);
77
- }
78
- },
79
- [focusNode]
80
- );
81
- const unregisterNode = useCallback((id) => {
82
- const nodes = nodesRef.current;
83
- const node = nodes.get(id);
84
- if (!node) return;
85
- if (node.parentId) {
86
- const parent = nodes.get(node.parentId);
87
- if (parent) {
88
- parent.childrenIds = parent.childrenIds.filter((childId) => childId !== id);
89
- }
90
- }
91
- nodes.delete(id);
92
- pendingFocusFirstChildRef.current.delete(id);
93
- setFocusedId((current) => {
94
- if (current !== id) return current;
95
- if (node.parentId) {
96
- const pathSet = /* @__PURE__ */ new Set();
97
- const pathArray = [];
98
- let currentNode = node.parentId;
99
- while (currentNode) {
100
- pathSet.add(currentNode);
101
- pathArray.push(currentNode);
102
- const n = nodes.get(currentNode);
103
- currentNode = (n == null ? void 0 : n.parentId) ?? null;
104
- }
105
- setActiveBranchNodes(pathSet);
106
- setActiveBranchPath(pathArray);
107
- return node.parentId;
108
- }
109
- setActiveBranchNodes(/* @__PURE__ */ new Set());
110
- setActiveBranchPath([]);
111
- return null;
112
- });
113
- }, []);
114
- const isFocused = useCallback(
115
- (id) => {
116
- return id === focusedId;
117
- },
118
- [focusedId]
119
- );
120
- const getFocusedId = useCallback(() => {
121
- return focusedId;
122
- }, [focusedId]);
123
- const isInActiveBranch = useCallback(
124
- (id) => {
125
- return activeBranchNodes.has(id);
126
- },
127
- [activeBranchNodes]
128
- );
129
- const getActiveBranchPath = useCallback(() => {
130
- return activeBranchPath;
131
- }, [activeBranchPath]);
132
- const navigateSibling = useCallback(
133
- (direction, wrap = true) => {
134
- const currentId = focusedId;
135
- if (!currentId) return;
136
- const nodes = nodesRef.current;
137
- const currentNode = nodes.get(currentId);
138
- if (!(currentNode == null ? void 0 : currentNode.parentId)) return;
139
- const parent = nodes.get(currentNode.parentId);
140
- if (!parent || parent.childrenIds.length === 0) return;
141
- const siblings = parent.childrenIds;
142
- const currentIndex = siblings.indexOf(currentId);
143
- if (currentIndex === -1) return;
144
- let nextIndex;
145
- if (wrap) {
146
- nextIndex = direction === "next" ? (currentIndex + 1) % siblings.length : (currentIndex - 1 + siblings.length) % siblings.length;
147
- } else {
148
- nextIndex = direction === "next" ? Math.min(currentIndex + 1, siblings.length - 1) : Math.max(currentIndex - 1, 0);
149
- }
150
- focusNode(siblings[nextIndex]);
151
- },
152
- [focusedId, focusNode]
153
- );
154
- return /* @__PURE__ */ jsx(
155
- FocusContext.Provider,
156
- {
157
- value: {
158
- registerNode,
159
- unregisterNode,
160
- focusNode,
161
- focusFirstChild,
162
- isFocused,
163
- getFocusedId,
164
- isInActiveBranch,
165
- getActiveBranchPath,
166
- navigateSibling
167
- },
168
- children
169
- }
170
- );
171
- };
172
- var FocusNodeContext = createContext(null);
173
- var useFocusContext = () => {
174
- const context = useContext(FocusContext);
175
- if (!context) {
176
- throw new GigglesError("useFocusContext must be used within a FocusProvider");
177
- }
178
- return context;
179
- };
180
-
181
- // src/core/focus/FocusGroup.tsx
182
- import { useCallback as useCallback3, useEffect as useEffect4, useMemo, useRef as useRef3 } from "react";
183
-
184
- // src/core/input/InputContext.tsx
185
- import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useRef as useRef2 } from "react";
186
- import { jsx as jsx2 } from "react/jsx-runtime";
187
- var InputContext = createContext2(null);
188
- var InputProvider = ({ children }) => {
189
- const bindingsRef = useRef2(/* @__PURE__ */ new Map());
190
- const trapNodeIdRef = useRef2(null);
191
- const registerKeybindings = useCallback2((nodeId, bindings, options) => {
192
- const registration = {
193
- bindings: new Map(Object.entries(bindings).filter((entry) => entry[1] != null)),
194
- capture: (options == null ? void 0 : options.capture) ?? false,
195
- onKeypress: options == null ? void 0 : options.onKeypress,
196
- layer: options == null ? void 0 : options.layer
197
- };
198
- bindingsRef.current.set(nodeId, registration);
199
- }, []);
200
- const unregisterKeybindings = useCallback2((nodeId) => {
201
- bindingsRef.current.delete(nodeId);
202
- }, []);
203
- const getNodeBindings = useCallback2((nodeId) => {
204
- return bindingsRef.current.get(nodeId);
205
- }, []);
206
- const setTrap = useCallback2((nodeId) => {
207
- trapNodeIdRef.current = nodeId;
208
- }, []);
209
- const clearTrap = useCallback2((nodeId) => {
210
- if (trapNodeIdRef.current === nodeId) {
211
- trapNodeIdRef.current = null;
212
- }
213
- }, []);
214
- const getTrapNodeId = useCallback2(() => {
215
- return trapNodeIdRef.current;
216
- }, []);
217
- const getAllBindings = useCallback2(() => {
218
- const allBindings = [];
219
- bindingsRef.current.forEach((nodeBindings, nodeId) => {
220
- nodeBindings.bindings.forEach((handler, key) => {
221
- allBindings.push({
222
- nodeId,
223
- key,
224
- handler,
225
- layer: nodeBindings.layer
226
- });
227
- });
228
- });
229
- return allBindings;
230
- }, []);
231
- return /* @__PURE__ */ jsx2(
232
- InputContext.Provider,
233
- {
234
- value: {
235
- registerKeybindings,
236
- unregisterKeybindings,
237
- getNodeBindings,
238
- setTrap,
239
- clearTrap,
240
- getTrapNodeId,
241
- getAllBindings
242
- },
243
- children
244
- }
245
- );
246
- };
247
- function useInputContext() {
248
- const context = useContext2(InputContext);
249
- if (!context) {
250
- throw new GigglesError("useInputContext must be used within an InputProvider");
251
- }
252
- return context;
253
- }
254
-
255
- // src/core/input/InputRouter.tsx
256
- import { useInput } from "ink";
257
-
258
- // src/core/input/normalizeKey.ts
259
- function normalizeKey(input, key) {
260
- if (key.downArrow) return "down";
261
- if (key.upArrow) return "up";
262
- if (key.leftArrow) return "left";
263
- if (key.rightArrow) return "right";
264
- if (key.return) return "enter";
265
- if (key.escape) return "escape";
266
- if (key.tab) return "tab";
267
- if (key.backspace) return "backspace";
268
- if (key.delete) return "delete";
269
- if (key.pageUp) return "pageup";
270
- if (key.pageDown) return "pagedown";
271
- if (key.home) return "home";
272
- if (key.end) return "end";
273
- return input;
274
- }
275
-
276
- // src/core/input/InputRouter.tsx
277
- import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
278
- function InputRouter({ children }) {
279
- const { getFocusedId, getActiveBranchPath } = useFocusContext();
280
- const { getNodeBindings, getTrapNodeId } = useInputContext();
281
- useInput((input, key) => {
282
- const focusedId = getFocusedId();
283
- if (!focusedId) return;
284
- const path = getActiveBranchPath();
285
- const trapNodeId = getTrapNodeId();
286
- const keyName = normalizeKey(input, key);
287
- for (const nodeId of path) {
288
- const nodeBindings = getNodeBindings(nodeId);
289
- if (!nodeBindings) continue;
290
- const handler = nodeBindings.bindings.get(keyName);
291
- if (handler) {
292
- handler(input, key);
293
- return;
294
- }
295
- if (nodeBindings.capture && nodeBindings.onKeypress) {
296
- nodeBindings.onKeypress(input, key);
297
- return;
298
- }
299
- if (nodeId === trapNodeId) {
300
- return;
301
- }
302
- }
303
- });
304
- return /* @__PURE__ */ jsx3(Fragment, { children });
305
- }
306
-
307
- // src/core/input/useKeybindings.tsx
308
- import { useEffect } from "react";
309
- function useKeybindings(focus, bindings, options) {
310
- const { registerKeybindings, unregisterKeybindings } = useInputContext();
311
- registerKeybindings(focus.id, bindings, options);
312
- useEffect(() => {
313
- return () => unregisterKeybindings(focus.id);
314
- }, [focus.id, unregisterKeybindings]);
315
- }
316
-
317
- // src/core/input/FocusTrap.tsx
318
- import { useEffect as useEffect2 } from "react";
319
- import { Fragment as Fragment2, jsx as jsx4 } from "react/jsx-runtime";
320
- function FocusTrap({ children }) {
321
- const { id } = useFocus();
322
- const { setTrap, clearTrap } = useInputContext();
323
- useEffect2(() => {
324
- setTrap(id);
325
- return () => clearTrap(id);
326
- }, [id, setTrap, clearTrap]);
327
- return /* @__PURE__ */ jsx4(Fragment2, { children });
328
- }
329
-
330
- // src/core/focus/FocusBindContext.tsx
331
- import { createContext as createContext3 } from "react";
332
- var FocusBindContext = createContext3(null);
333
-
334
- // src/core/focus/useFocus.ts
335
- import { useContext as useContext3, useEffect as useEffect3, useId } from "react";
336
- var useFocus = (id) => {
337
- const nodeId = useId();
338
- const parentId = useContext3(FocusNodeContext);
339
- const bindContext = useContext3(FocusBindContext);
340
- const { focusNode, registerNode, unregisterNode, isFocused } = useFocusContext();
341
- useEffect3(() => {
342
- registerNode(nodeId, parentId);
343
- if (id && bindContext) {
344
- bindContext.register(id, nodeId);
345
- }
346
- return () => {
347
- unregisterNode(nodeId);
348
- if (id && bindContext) {
349
- bindContext.unregister(id);
350
- }
351
- };
352
- }, [nodeId, parentId, id, bindContext, registerNode, unregisterNode]);
353
- return {
354
- id: nodeId,
355
- focused: isFocused(nodeId),
356
- focus: () => focusNode(nodeId)
357
- };
358
- };
359
-
360
- // src/core/focus/FocusGroup.tsx
361
- import { jsx as jsx5 } from "react/jsx-runtime";
362
- function FocusGroup({
363
- children,
364
- direction = "vertical",
365
- value,
366
- wrap = true,
367
- navigable = true
368
- }) {
369
- const focus = useFocus();
370
- const { focusNode, navigateSibling } = useFocusContext();
371
- const bindMapRef = useRef3(/* @__PURE__ */ new Map());
372
- const register = useCallback3((logicalId, nodeId) => {
373
- if (bindMapRef.current.has(logicalId)) {
374
- throw new GigglesError(`FocusGroup: Duplicate id "${logicalId}". Each child must have a unique id.`);
375
- }
376
- bindMapRef.current.set(logicalId, nodeId);
377
- }, []);
378
- const unregister = useCallback3((logicalId) => {
379
- bindMapRef.current.delete(logicalId);
380
- }, []);
381
- useEffect4(() => {
382
- if (value) {
383
- const nodeId = bindMapRef.current.get(value);
384
- if (nodeId) {
385
- focusNode(nodeId);
386
- }
387
- }
388
- }, [value, focusNode]);
389
- const bindContextValue = value ? { register, unregister } : null;
390
- const navigationKeys = useMemo(() => {
391
- if (!navigable) return {};
392
- const next = () => navigateSibling("next", wrap);
393
- const prev = () => navigateSibling("prev", wrap);
394
- return direction === "vertical" ? {
395
- j: next,
396
- k: prev,
397
- down: next,
398
- up: prev
399
- } : {
400
- l: next,
401
- h: prev,
402
- right: next,
403
- left: prev
404
- };
405
- }, [navigable, direction, wrap, navigateSibling]);
406
- useKeybindings(focus, navigationKeys);
407
- return /* @__PURE__ */ jsx5(FocusNodeContext.Provider, { value: focus.id, children: /* @__PURE__ */ jsx5(FocusBindContext.Provider, { value: bindContextValue, children }) });
408
- }
409
-
410
- // src/core/focus/useFocusState.ts
411
- import { useState as useState2 } from "react";
412
- function useFocusState(initial) {
413
- const [focused, setFocused] = useState2(initial);
414
- return [focused, setFocused];
415
- }
1
+ import {
2
+ AlternateScreen
3
+ } from "./chunk-7PDVDYFB.js";
4
+ import {
5
+ FocusGroup,
6
+ FocusNodeContext,
7
+ FocusProvider,
8
+ FocusTrap,
9
+ GigglesError,
10
+ InputProvider,
11
+ InputRouter,
12
+ useFocus,
13
+ useFocusContext,
14
+ useFocusState,
15
+ useKeybindingRegistry,
16
+ useKeybindings
17
+ } from "./chunk-4LED4GXQ.js";
416
18
 
417
19
  // src/core/GigglesProvider.tsx
418
- import { jsx as jsx6 } from "react/jsx-runtime";
20
+ import { jsx } from "react/jsx-runtime";
419
21
  function GigglesProvider({ children }) {
420
- return /* @__PURE__ */ jsx6(FocusProvider, { children: /* @__PURE__ */ jsx6(InputProvider, { children: /* @__PURE__ */ jsx6(InputRouter, { children }) }) });
22
+ return /* @__PURE__ */ jsx(AlternateScreen, { children: /* @__PURE__ */ jsx(FocusProvider, { children: /* @__PURE__ */ jsx(InputProvider, { children: /* @__PURE__ */ jsx(InputRouter, { children }) }) }) });
421
23
  }
422
24
 
423
25
  // src/core/router/Router.tsx
424
- import React4, { useCallback as useCallback4, useReducer, useRef as useRef5 } from "react";
26
+ import React2, { useCallback, useReducer, useRef as useRef2 } from "react";
425
27
 
426
28
  // src/core/router/Screen.tsx
427
29
  function Screen(_props) {
@@ -429,14 +31,14 @@ function Screen(_props) {
429
31
  }
430
32
 
431
33
  // src/core/router/ScreenEntry.tsx
432
- import React3, { useEffect as useEffect5, useId as useId2, useMemo as useMemo2, useRef as useRef4 } from "react";
34
+ import React, { useEffect, useId, useMemo, useRef } from "react";
433
35
  import { Box } from "ink";
434
36
 
435
37
  // src/core/router/NavigationContext.tsx
436
- import { createContext as createContext4, useContext as useContext4 } from "react";
437
- var NavigationContext = createContext4(null);
38
+ import { createContext, useContext } from "react";
39
+ var NavigationContext = createContext(null);
438
40
  var useNavigation = () => {
439
- const context = useContext4(NavigationContext);
41
+ const context = useContext(NavigationContext);
440
42
  if (!context) {
441
43
  throw new GigglesError("useNavigation must be used within a Router");
442
44
  }
@@ -444,7 +46,7 @@ var useNavigation = () => {
444
46
  };
445
47
 
446
48
  // src/core/router/ScreenEntry.tsx
447
- import { jsx as jsx7 } from "react/jsx-runtime";
49
+ import { jsx as jsx2 } from "react/jsx-runtime";
448
50
  function ScreenEntry({
449
51
  entry,
450
52
  isTop,
@@ -456,18 +58,18 @@ function ScreenEntry({
456
58
  replace,
457
59
  reset
458
60
  }) {
459
- const screenNodeId = useId2();
460
- const parentId = React3.useContext(FocusNodeContext);
61
+ const screenNodeId = useId();
62
+ const parentId = React.useContext(FocusNodeContext);
461
63
  const { registerNode, unregisterNode, focusFirstChild, focusNode, getFocusedId } = useFocusContext();
462
- const lastFocusedChildRef = useRef4(null);
463
- const wasTopRef = useRef4(isTop);
464
- useEffect5(() => {
64
+ const lastFocusedChildRef = useRef(null);
65
+ const wasTopRef = useRef(isTop);
66
+ useEffect(() => {
465
67
  registerNode(screenNodeId, parentId);
466
68
  return () => {
467
69
  unregisterNode(screenNodeId);
468
70
  };
469
71
  }, [screenNodeId, parentId, registerNode, unregisterNode]);
470
- useEffect5(() => {
72
+ useEffect(() => {
471
73
  if (!wasTopRef.current && isTop) {
472
74
  const saved = restoreFocus ? lastFocusedChildRef.current : null;
473
75
  if (saved) {
@@ -482,15 +84,15 @@ function ScreenEntry({
482
84
  }
483
85
  wasTopRef.current = isTop;
484
86
  }, [isTop, screenNodeId, restoreFocus, focusFirstChild, focusNode, getFocusedId]);
485
- const value = useMemo2(
87
+ const value = useMemo(
486
88
  () => ({ currentRoute: entry, active: isTop, canGoBack, push, pop, replace, reset }),
487
89
  [entry, isTop, canGoBack, push, pop, replace, reset]
488
90
  );
489
- return /* @__PURE__ */ jsx7(NavigationContext.Provider, { value, children: /* @__PURE__ */ jsx7(FocusNodeContext.Provider, { value: screenNodeId, children: /* @__PURE__ */ jsx7(Box, { display: isTop ? "flex" : "none", children: /* @__PURE__ */ jsx7(Component, { ...entry.params }) }) }) });
91
+ return /* @__PURE__ */ jsx2(NavigationContext.Provider, { value, children: /* @__PURE__ */ jsx2(FocusNodeContext.Provider, { value: screenNodeId, children: /* @__PURE__ */ jsx2(Box, { display: isTop ? "flex" : "none", children: /* @__PURE__ */ jsx2(Component, { ...entry.params }) }) }) });
490
92
  }
491
93
 
492
94
  // src/core/router/Router.tsx
493
- import { Fragment as Fragment3, jsx as jsx8 } from "react/jsx-runtime";
95
+ import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
494
96
  function routerReducer(stack, action) {
495
97
  switch (action.type) {
496
98
  case "push":
@@ -504,11 +106,11 @@ function routerReducer(stack, action) {
504
106
  }
505
107
  }
506
108
  function Router({ children, initialScreen, initialParams, restoreFocus = true }) {
507
- const screenId = useRef5(0);
508
- const routes = React4.Children.toArray(children).filter((child) => React4.isValidElement(child) && child.type === Screen).map((child) => child.props);
509
- const screenNamesRef = useRef5(/* @__PURE__ */ new Set());
109
+ const screenId = useRef2(0);
110
+ const routes = React2.Children.toArray(children).filter((child) => React2.isValidElement(child) && child.type === Screen).map((child) => child.props);
111
+ const screenNamesRef = useRef2(/* @__PURE__ */ new Set());
510
112
  screenNamesRef.current = new Set(routes.map((r) => r.name));
511
- const assertScreen = useCallback4((name) => {
113
+ const assertScreen = useCallback((name) => {
512
114
  if (!screenNamesRef.current.has(name)) {
513
115
  throw new GigglesError(
514
116
  `Screen "${name}" is not registered. Available screens: ${[...screenNamesRef.current].join(", ")}`
@@ -523,24 +125,24 @@ function Router({ children, initialScreen, initialParams, restoreFocus = true })
523
125
  }
524
126
  return [{ id: screenId.current++, name, params: initialParams }];
525
127
  });
526
- const push = useCallback4(
128
+ const push = useCallback(
527
129
  (name, params) => {
528
130
  assertScreen(name);
529
131
  dispatch({ type: "push", route: { id: screenId.current++, name, params } });
530
132
  },
531
133
  [assertScreen]
532
134
  );
533
- const pop = useCallback4(() => {
135
+ const pop = useCallback(() => {
534
136
  dispatch({ type: "pop" });
535
137
  }, []);
536
- const replace = useCallback4(
138
+ const replace = useCallback(
537
139
  (name, params) => {
538
140
  assertScreen(name);
539
141
  dispatch({ type: "replace", route: { id: screenId.current++, name, params } });
540
142
  },
541
143
  [assertScreen]
542
144
  );
543
- const reset = useCallback4(
145
+ const reset = useCallback(
544
146
  (name, params) => {
545
147
  assertScreen(name);
546
148
  dispatch({ type: "reset", route: { id: screenId.current++, name, params } });
@@ -552,10 +154,10 @@ function Router({ children, initialScreen, initialParams, restoreFocus = true })
552
154
  components.set(route.name, route.component);
553
155
  }
554
156
  const canGoBack = stack.length > 1;
555
- return /* @__PURE__ */ jsx8(Fragment3, { children: stack.map((entry, i) => {
157
+ return /* @__PURE__ */ jsx3(Fragment, { children: stack.map((entry, i) => {
556
158
  const Component = components.get(entry.name);
557
159
  if (!Component) return null;
558
- return /* @__PURE__ */ jsx8(
160
+ return /* @__PURE__ */ jsx3(
559
161
  ScreenEntry,
560
162
  {
561
163
  entry,
@@ -581,6 +183,7 @@ export {
581
183
  Screen,
582
184
  useFocus,
583
185
  useFocusState,
186
+ useKeybindingRegistry,
584
187
  useKeybindings,
585
188
  useNavigation
586
189
  };
@@ -0,0 +1,23 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ type TerminalSize = {
5
+ rows: number;
6
+ columns: number;
7
+ };
8
+
9
+ declare function useTerminalSize(): TerminalSize;
10
+
11
+ declare function useTerminalFocus(callback: (focused: boolean) => void): void;
12
+
13
+ declare function AlternateScreen({ children }: {
14
+ children: ReactNode;
15
+ }): react_jsx_runtime.JSX.Element | null;
16
+
17
+ declare function useShellOut(): {
18
+ run: (command: string) => Promise<{
19
+ exitCode: number;
20
+ }>;
21
+ };
22
+
23
+ export { AlternateScreen, type TerminalSize, useShellOut, useTerminalFocus, useTerminalSize };
@@ -0,0 +1,83 @@
1
+ import {
2
+ AlternateScreen
3
+ } from "../chunk-7PDVDYFB.js";
4
+
5
+ // src/terminal/hooks/useTerminalSize.ts
6
+ import { useEffect, useState } from "react";
7
+ function useTerminalSize() {
8
+ const [size, setSize] = useState({
9
+ rows: process.stdout.rows,
10
+ columns: process.stdout.columns
11
+ });
12
+ useEffect(() => {
13
+ const handleResize = () => {
14
+ setSize({
15
+ rows: process.stdout.rows,
16
+ columns: process.stdout.columns
17
+ });
18
+ };
19
+ process.stdout.on("resize", handleResize);
20
+ return () => {
21
+ process.stdout.off("resize", handleResize);
22
+ };
23
+ }, []);
24
+ return size;
25
+ }
26
+
27
+ // src/terminal/hooks/useTerminalFocus.ts
28
+ import { useEffect as useEffect2, useRef } from "react";
29
+ function useTerminalFocus(callback) {
30
+ const callbackRef = useRef(callback);
31
+ callbackRef.current = callback;
32
+ useEffect2(() => {
33
+ const handler = (data) => {
34
+ const str = data.toString();
35
+ if (str.includes("\x1B[I")) callbackRef.current(true);
36
+ if (str.includes("\x1B[O")) callbackRef.current(false);
37
+ };
38
+ process.stdin.on("data", handler);
39
+ const timer = setTimeout(() => {
40
+ process.stdout.write("\x1B[?1004h");
41
+ }, 0);
42
+ return () => {
43
+ clearTimeout(timer);
44
+ process.stdout.write("\x1B[?1004l");
45
+ process.stdin.off("data", handler);
46
+ };
47
+ }, []);
48
+ }
49
+
50
+ // src/terminal/hooks/useShellout.ts
51
+ import { execa } from "execa";
52
+ import { useCallback, useState as useState2 } from "react";
53
+ import { useStdin } from "ink";
54
+ function useShellOut() {
55
+ const [, setRedrawCount] = useState2(0);
56
+ const { setRawMode } = useStdin();
57
+ const run = useCallback(
58
+ async (command) => {
59
+ process.stdout.write("\x1B[?1049l");
60
+ setRawMode(false);
61
+ try {
62
+ const result = await execa(command, { stdio: "inherit", shell: true, reject: false });
63
+ return { exitCode: result.exitCode ?? 0 };
64
+ } finally {
65
+ setRawMode(true);
66
+ process.stdout.write("\x1B[?1049h");
67
+ process.stdout.write("\x1B[2J");
68
+ process.stdout.write("\x1B[H");
69
+ setRedrawCount((c) => c + 1);
70
+ }
71
+ },
72
+ [setRawMode]
73
+ );
74
+ return {
75
+ run
76
+ };
77
+ }
78
+ export {
79
+ AlternateScreen,
80
+ useShellOut,
81
+ useTerminalFocus,
82
+ useTerminalSize
83
+ };