ink-cartridge 3.6.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/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/binary-storage/BinaryStorage.d.ts +308 -0
- package/dist/binary-storage/BinaryStorage.js +548 -0
- package/dist/binary-storage/StreamingReader.d.ts +53 -0
- package/dist/binary-storage/StreamingReader.js +530 -0
- package/dist/binary-storage/index.d.ts +91 -0
- package/dist/binary-storage/index.js +91 -0
- package/dist/binary-storage/types.d.ts +400 -0
- package/dist/binary-storage/types.js +15 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +214 -0
- package/dist/cli/initTheme.d.ts +14 -0
- package/dist/cli/initTheme.js +124 -0
- package/dist/cli/makeLanguageType.d.ts +46 -0
- package/dist/cli/makeLanguageType.js +264 -0
- package/dist/cli/makeThemeType.d.ts +43 -0
- package/dist/cli/makeThemeType.js +222 -0
- package/dist/components/badge/Badge.d.ts +7 -0
- package/dist/components/badge/Badge.js +9 -0
- package/dist/components/dialog/ConfirmDialog.d.ts +23 -0
- package/dist/components/dialog/ConfirmDialog.js +57 -0
- package/dist/components/dialog/types.d.ts +20 -0
- package/dist/components/dialog/types.js +1 -0
- package/dist/components/divider/Divider.d.ts +8 -0
- package/dist/components/divider/Divider.js +10 -0
- package/dist/components/fold/Fold.d.ts +3 -0
- package/dist/components/fold/Fold.js +44 -0
- package/dist/components/fold/types.d.ts +28 -0
- package/dist/components/fold/types.js +1 -0
- package/dist/components/form/Field.d.ts +30 -0
- package/dist/components/form/Field.js +59 -0
- package/dist/components/form/Form.d.ts +32 -0
- package/dist/components/form/Form.js +163 -0
- package/dist/components/form/context.d.ts +14 -0
- package/dist/components/form/context.js +20 -0
- package/dist/components/form/types.d.ts +115 -0
- package/dist/components/form/types.js +1 -0
- package/dist/components/key-hint/KeyHint.d.ts +10 -0
- package/dist/components/key-hint/KeyHint.js +12 -0
- package/dist/components/multi-select/MultiSelectInput.d.ts +125 -0
- package/dist/components/multi-select/MultiSelectInput.js +287 -0
- package/dist/components/multi-select/types.d.ts +100 -0
- package/dist/components/multi-select/types.js +1 -0
- package/dist/components/number-input/NumberInput.d.ts +11 -0
- package/dist/components/number-input/NumberInput.js +44 -0
- package/dist/components/progress-bar/ProgressBar.d.ts +11 -0
- package/dist/components/progress-bar/ProgressBar.js +9 -0
- package/dist/components/search-input/SearchInput.d.ts +10 -0
- package/dist/components/search-input/SearchInput.js +15 -0
- package/dist/components/select/SelectInput.d.ts +100 -0
- package/dist/components/select/SelectInput.js +147 -0
- package/dist/components/select/types.d.ts +63 -0
- package/dist/components/select/types.js +1 -0
- package/dist/components/spinner/Spinner.d.ts +12 -0
- package/dist/components/spinner/Spinner.js +24 -0
- package/dist/components/tabs/Tabs.d.ts +3 -0
- package/dist/components/tabs/Tabs.js +57 -0
- package/dist/components/tabs/types.d.ts +32 -0
- package/dist/components/tabs/types.js +1 -0
- package/dist/components/text/TextInput.d.ts +39 -0
- package/dist/components/text/TextInput.js +233 -0
- package/dist/components/text/types.d.ts +62 -0
- package/dist/components/text/types.js +1 -0
- package/dist/dev/dev-screen.d.ts +36 -0
- package/dist/dev/dev-screen.js +142 -0
- package/dist/dev/entrance.d.ts +51 -0
- package/dist/dev/entrance.js +67 -0
- package/dist/dev/globalKey-display.d.ts +2 -0
- package/dist/dev/globalKey-display.js +8 -0
- package/dist/dev/types.d.ts +9 -0
- package/dist/dev/types.js +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +50 -0
- package/dist/keyboard/check-global-key.d.ts +18 -0
- package/dist/keyboard/check-global-key.js +52 -0
- package/dist/keyboard/context.d.ts +398 -0
- package/dist/keyboard/context.js +9 -0
- package/dist/keyboard/global-key-processor/index.d.ts +16 -0
- package/dist/keyboard/global-key-processor/index.js +75 -0
- package/dist/keyboard/global-sequence-processor/index.d.ts +14 -0
- package/dist/keyboard/global-sequence-processor/index.js +165 -0
- package/dist/keyboard/hook.d.ts +51 -0
- package/dist/keyboard/hook.js +105 -0
- package/dist/keyboard/index.d.ts +5 -0
- package/dist/keyboard/index.js +3 -0
- package/dist/keyboard/keyNormalizer.d.ts +37 -0
- package/dist/keyboard/keyNormalizer.js +119 -0
- package/dist/keyboard/layer-handler.d.ts +57 -0
- package/dist/keyboard/layer-handler.js +274 -0
- package/dist/keyboard/modal-processor/index.d.ts +16 -0
- package/dist/keyboard/modal-processor/index.js +97 -0
- package/dist/keyboard/overlay-processor/index.d.ts +16 -0
- package/dist/keyboard/overlay-processor/index.js +30 -0
- package/dist/keyboard/pipeline/chain.d.ts +10 -0
- package/dist/keyboard/pipeline/chain.js +45 -0
- package/dist/keyboard/pipeline/context.d.ts +34 -0
- package/dist/keyboard/pipeline/context.js +42 -0
- package/dist/keyboard/pipeline/index.d.ts +3 -0
- package/dist/keyboard/pipeline/index.js +2 -0
- package/dist/keyboard/provider.d.ts +32 -0
- package/dist/keyboard/provider.js +985 -0
- package/dist/keyboard/screen-stack-processor/index.d.ts +14 -0
- package/dist/keyboard/screen-stack-processor/index.js +32 -0
- package/dist/keyboard/types.d.ts +632 -0
- package/dist/keyboard/types.js +1 -0
- package/dist/language/context.d.ts +7 -0
- package/dist/language/context.js +7 -0
- package/dist/language/hook.d.ts +19 -0
- package/dist/language/hook.js +26 -0
- package/dist/language/index.d.ts +3 -0
- package/dist/language/index.js +2 -0
- package/dist/language/provider.d.ts +30 -0
- package/dist/language/provider.js +155 -0
- package/dist/language/types.d.ts +124 -0
- package/dist/language/types.js +1 -0
- package/dist/screen/ModalContext.d.ts +11 -0
- package/dist/screen/ModalContext.js +12 -0
- package/dist/screen/OverlayContext.d.ts +9 -0
- package/dist/screen/OverlayContext.js +10 -0
- package/dist/screen/context.d.ts +61 -0
- package/dist/screen/context.js +8 -0
- package/dist/screen/current-screen.d.ts +16 -0
- package/dist/screen/current-screen.js +38 -0
- package/dist/screen/hook.d.ts +13 -0
- package/dist/screen/hook.js +20 -0
- package/dist/screen/index.d.ts +7 -0
- package/dist/screen/index.js +5 -0
- package/dist/screen/provider.d.ts +112 -0
- package/dist/screen/provider.js +577 -0
- package/dist/screen/registry.d.ts +27 -0
- package/dist/screen/registry.js +71 -0
- package/dist/screen/types.d.ts +227 -0
- package/dist/screen/types.js +1 -0
- package/dist/storage/index.d.ts +45 -0
- package/dist/storage/index.js +46 -0
- package/dist/storage/storage.d.ts +139 -0
- package/dist/storage/storage.js +236 -0
- package/dist/storage/types.d.ts +145 -0
- package/dist/storage/types.js +1 -0
- package/dist/theme/context.d.ts +7 -0
- package/dist/theme/context.js +7 -0
- package/dist/theme/hook.d.ts +11 -0
- package/dist/theme/hook.js +18 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/provider.d.ts +18 -0
- package/dist/theme/provider.js +308 -0
- package/dist/theme/types.d.ts +53 -0
- package/dist/theme/types.js +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DevProps } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Developer debugging modal for the ink-cartridge screen system.
|
|
5
|
+
*
|
|
6
|
+
* Renders as an absolutely-positioned panel showing real-time navigation
|
|
7
|
+
* state: the current screen path as a breadcrumb, a list of open overlays
|
|
8
|
+
* (green = active, gray = inactive), open modals, screen count, and the
|
|
9
|
+
* panel's vertical offset.
|
|
10
|
+
*
|
|
11
|
+
* Keyboard controls (bound on mount):
|
|
12
|
+
* - **↑ / ↓** — move the panel vertically, clamped to the terminal bounds.
|
|
13
|
+
* The position automatically re-clamps on terminal resize so the panel
|
|
14
|
+
* never drifts off-screen.
|
|
15
|
+
* - **Escape** — close the DevScreen modal.
|
|
16
|
+
*
|
|
17
|
+
* Must be opened via {@link openDevTool} (which calls `openModal`).
|
|
18
|
+
* The panel registers itself via `registerComponent` so it participates
|
|
19
|
+
* in the modal keyboard layer automatically.
|
|
20
|
+
*
|
|
21
|
+
* As a modal, DevScreen blocks all keyboard events from reaching overlays
|
|
22
|
+
* and screens while open.
|
|
23
|
+
*
|
|
24
|
+
* @param top - Initial vertical position in rows (0 = top of terminal).
|
|
25
|
+
* @param left - Horizontal position in columns.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Open from any screen or keyboard handler:
|
|
30
|
+
* import { openDevTool } from 'ink-cartridge/dev';
|
|
31
|
+
* openDevTool({ top: 0, left: 0 });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @2026-06-23 3.6.1
|
|
35
|
+
*/
|
|
36
|
+
export declare function DevScreen({ top: initialTop, left }: DevProps): React.JSX.Element;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text, useWindowSize } from "ink";
|
|
3
|
+
import { useScreenSystem } from "../screen/hook.js";
|
|
4
|
+
import { useKeyboard, useModalMissListener } from "../keyboard/hook.js";
|
|
5
|
+
import { ModalContext } from "../screen/ModalContext.js";
|
|
6
|
+
import { registerComponent } from "../screen/registry.js";
|
|
7
|
+
import { closeDevTool } from "./entrance.js";
|
|
8
|
+
const PANEL_HEIGHT = 30;
|
|
9
|
+
/**
|
|
10
|
+
* Developer debugging modal for the ink-cartridge screen system.
|
|
11
|
+
*
|
|
12
|
+
* Renders as an absolutely-positioned panel showing real-time navigation
|
|
13
|
+
* state: the current screen path as a breadcrumb, a list of open overlays
|
|
14
|
+
* (green = active, gray = inactive), open modals, screen count, and the
|
|
15
|
+
* panel's vertical offset.
|
|
16
|
+
*
|
|
17
|
+
* Keyboard controls (bound on mount):
|
|
18
|
+
* - **↑ / ↓** — move the panel vertically, clamped to the terminal bounds.
|
|
19
|
+
* The position automatically re-clamps on terminal resize so the panel
|
|
20
|
+
* never drifts off-screen.
|
|
21
|
+
* - **Escape** — close the DevScreen modal.
|
|
22
|
+
*
|
|
23
|
+
* Must be opened via {@link openDevTool} (which calls `openModal`).
|
|
24
|
+
* The panel registers itself via `registerComponent` so it participates
|
|
25
|
+
* in the modal keyboard layer automatically.
|
|
26
|
+
*
|
|
27
|
+
* As a modal, DevScreen blocks all keyboard events from reaching overlays
|
|
28
|
+
* and screens while open.
|
|
29
|
+
*
|
|
30
|
+
* @param top - Initial vertical position in rows (0 = top of terminal).
|
|
31
|
+
* @param left - Horizontal position in columns.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* // Open from any screen or keyboard handler:
|
|
36
|
+
* import { openDevTool } from 'ink-cartridge/dev';
|
|
37
|
+
* openDevTool({ top: 0, left: 0 });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @2026-06-23 3.6.1
|
|
41
|
+
*/
|
|
42
|
+
export function DevScreen({ top: initialTop, left }) {
|
|
43
|
+
const { currentPath, displayedOverlays, activeOverlayIds, modalQueue, activeModalId, } = useScreenSystem();
|
|
44
|
+
const { boundKeyboard } = useKeyboard();
|
|
45
|
+
const modalId = useContext(ModalContext);
|
|
46
|
+
const { rows } = useWindowSize();
|
|
47
|
+
const [offsetTop, setOffsetTop] = useState(initialTop);
|
|
48
|
+
const [flashBorder, setFlashBorder] = useState(false);
|
|
49
|
+
const flashTimerRef = useRef(null);
|
|
50
|
+
// Stable ref so the []-deps keyboard effect always reads the latest
|
|
51
|
+
// rows-based clamp, preventing a stale-closure on the initial size.
|
|
52
|
+
const clampTopRef = useRef((next) => next);
|
|
53
|
+
clampTopRef.current = (next) => Math.max(0, Math.min(next, rows - PANEL_HEIGHT));
|
|
54
|
+
useModalMissListener(useCallback((evt) => {
|
|
55
|
+
if (evt.miss) {
|
|
56
|
+
setFlashBorder(true);
|
|
57
|
+
if (flashTimerRef.current)
|
|
58
|
+
clearTimeout(flashTimerRef.current);
|
|
59
|
+
flashTimerRef.current = setTimeout(() => {
|
|
60
|
+
setFlashBorder(false);
|
|
61
|
+
flashTimerRef.current = null;
|
|
62
|
+
}, 200);
|
|
63
|
+
}
|
|
64
|
+
}, []));
|
|
65
|
+
// Cleanup flash timer on unmount
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
return () => {
|
|
68
|
+
if (flashTimerRef.current)
|
|
69
|
+
clearTimeout(flashTimerRef.current);
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const u1 = boundKeyboard(['up'], () => setOffsetTop(prev => clampTopRef.current(prev - 1)));
|
|
74
|
+
const u2 = boundKeyboard(['down'], () => setOffsetTop(prev => clampTopRef.current(prev + 1)));
|
|
75
|
+
const u3 = boundKeyboard(['escape'], () => {
|
|
76
|
+
if (modalId)
|
|
77
|
+
closeDevTool();
|
|
78
|
+
});
|
|
79
|
+
return () => {
|
|
80
|
+
u1();
|
|
81
|
+
u2();
|
|
82
|
+
u3();
|
|
83
|
+
};
|
|
84
|
+
}, [modalId]);
|
|
85
|
+
// When the terminal is resized the current position may land outside
|
|
86
|
+
// the new bounds — re-clamp so the panel stays visible immediately.
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
setOffsetTop(prev => clampTopRef.current(prev));
|
|
89
|
+
}, [rows]);
|
|
90
|
+
return (React.createElement(Box, { borderColor: flashBorder ? 'yellow' : 'blue', borderStyle: 'round', position: "absolute", top: offsetTop, left: left, height: PANEL_HEIGHT, width: '100%', flexDirection: "column", paddingX: 1, paddingY: 1, backgroundColor: 'black' },
|
|
91
|
+
React.createElement(Box, { flexDirection: "row" },
|
|
92
|
+
React.createElement(Text, { color: "cyan", bold: true }, "Path "),
|
|
93
|
+
currentPath.map((component, index) => {
|
|
94
|
+
const name = component.displayName || component.name || 'Unknown';
|
|
95
|
+
const isLast = index === currentPath.length - 1;
|
|
96
|
+
return (React.createElement(Box, { key: index, flexDirection: "row" },
|
|
97
|
+
React.createElement(Text, { color: "gray" },
|
|
98
|
+
" ",
|
|
99
|
+
'>',
|
|
100
|
+
" "),
|
|
101
|
+
React.createElement(Text, { color: isLast ? 'yellow' : 'gray', bold: isLast }, name)));
|
|
102
|
+
})),
|
|
103
|
+
React.createElement(Box, { flexDirection: "row", paddingTop: 1 },
|
|
104
|
+
React.createElement(Text, { color: "cyan" }, "Overlays "),
|
|
105
|
+
React.createElement(Text, { dimColor: true },
|
|
106
|
+
"(",
|
|
107
|
+
displayedOverlays.length,
|
|
108
|
+
")"),
|
|
109
|
+
displayedOverlays.length === 0 ? (React.createElement(Text, { color: "gray" }, " \u2014 none")) : (displayedOverlays.map((entry, i) => {
|
|
110
|
+
const name = entry.component.displayName || entry.component.name || 'Unknown';
|
|
111
|
+
const active = activeOverlayIds.includes(entry.id);
|
|
112
|
+
return (React.createElement(Box, { key: entry.id, flexDirection: "row" },
|
|
113
|
+
React.createElement(Text, { color: "gray" }, i > 0 ? ', ' : ' — '),
|
|
114
|
+
React.createElement(Text, { color: active ? 'green' : 'gray' }, name)));
|
|
115
|
+
}))),
|
|
116
|
+
React.createElement(Box, { flexDirection: "row", paddingTop: 0 },
|
|
117
|
+
React.createElement(Text, { color: "cyan" }, "Modals "),
|
|
118
|
+
React.createElement(Text, { dimColor: true },
|
|
119
|
+
"(",
|
|
120
|
+
modalQueue.length,
|
|
121
|
+
")"),
|
|
122
|
+
modalQueue.length === 0 ? (React.createElement(Text, { color: "gray" }, " \u2014 none")) : (modalQueue.map((entry, i) => {
|
|
123
|
+
const name = entry.component.displayName || entry.component.name || 'Unknown';
|
|
124
|
+
const active = entry.id === activeModalId;
|
|
125
|
+
return (React.createElement(Box, { key: entry.id, flexDirection: "row" },
|
|
126
|
+
React.createElement(Text, { color: "gray" }, i > 0 ? ', ' : ' — '),
|
|
127
|
+
React.createElement(Text, { color: active ? 'green' : 'gray' }, name)));
|
|
128
|
+
}))),
|
|
129
|
+
React.createElement(Box, { width: '100%', paddingY: 1 },
|
|
130
|
+
React.createElement(Text, { color: "blue", dimColor: true }, "─".repeat(60))),
|
|
131
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
132
|
+
React.createElement(Text, { dimColor: true },
|
|
133
|
+
"Screens: ",
|
|
134
|
+
currentPath.length,
|
|
135
|
+
" | \u2191\u2193 Move | Esc Close"),
|
|
136
|
+
React.createElement(Text, { dimColor: true },
|
|
137
|
+
"Top: ",
|
|
138
|
+
offsetTop,
|
|
139
|
+
"/",
|
|
140
|
+
rows - PANEL_HEIGHT))));
|
|
141
|
+
}
|
|
142
|
+
registerComponent(DevScreen, { top: 0, left: 0 });
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DevProps } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Open the developer debugging modal.
|
|
4
|
+
*
|
|
5
|
+
* Calls {@link openModal} with the fixed modal ID `_Dev-Tool_` and
|
|
6
|
+
* the {@link DevScreen} component. Repeated calls while the modal is
|
|
7
|
+
* already open will throw — guard with a ref or state flag to implement
|
|
8
|
+
* a toggle (see example).
|
|
9
|
+
*
|
|
10
|
+
* When open, DevScreen blocks all keyboard events from reaching
|
|
11
|
+
* overlays and screens. The modal provides its own close mechanism
|
|
12
|
+
* via the Escape key.
|
|
13
|
+
*
|
|
14
|
+
* @param top - Initial vertical position in rows (0 = top of terminal).
|
|
15
|
+
* @param left - Horizontal position in columns.
|
|
16
|
+
*
|
|
17
|
+
* @throws If `ScenarioManagementProvider` is not mounted, or the modal
|
|
18
|
+
* ID `_Dev-Tool_` is already open.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* // Toggle with a ref to avoid duplicate-open errors:
|
|
23
|
+
* const devOpenRef = useRef(false);
|
|
24
|
+
* boundKeyboard(['ctrl+d'], () => {
|
|
25
|
+
* if (devOpenRef.current) {
|
|
26
|
+
* closeDevTool();
|
|
27
|
+
* devOpenRef.current = false;
|
|
28
|
+
* } else {
|
|
29
|
+
* openDevTool({ top: 0, left: 0 });
|
|
30
|
+
* devOpenRef.current = true;
|
|
31
|
+
* }
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function openDevTool({ top, left, zindex }: DevProps & {
|
|
36
|
+
zindex?: number;
|
|
37
|
+
}): void;
|
|
38
|
+
/**
|
|
39
|
+
* Close the developer debugging modal.
|
|
40
|
+
*
|
|
41
|
+
* Calls {@link closeModal} for the fixed modal ID `_Dev-Tool_`.
|
|
42
|
+
* Safe to call even if the modal is not currently open — acts as
|
|
43
|
+
* a no-op in that case to support toggle patterns where the modal
|
|
44
|
+
* may have been closed by its own Escape binding.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* boundKeyboard(['escape'], () => closeDevTool());
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function closeDevTool(): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { closeModal, openModal } from "../screen/provider.js";
|
|
2
|
+
import { DevScreen } from "./dev-screen.js";
|
|
3
|
+
/**
|
|
4
|
+
* Open the developer debugging modal.
|
|
5
|
+
*
|
|
6
|
+
* Calls {@link openModal} with the fixed modal ID `_Dev-Tool_` and
|
|
7
|
+
* the {@link DevScreen} component. Repeated calls while the modal is
|
|
8
|
+
* already open will throw — guard with a ref or state flag to implement
|
|
9
|
+
* a toggle (see example).
|
|
10
|
+
*
|
|
11
|
+
* When open, DevScreen blocks all keyboard events from reaching
|
|
12
|
+
* overlays and screens. The modal provides its own close mechanism
|
|
13
|
+
* via the Escape key.
|
|
14
|
+
*
|
|
15
|
+
* @param top - Initial vertical position in rows (0 = top of terminal).
|
|
16
|
+
* @param left - Horizontal position in columns.
|
|
17
|
+
*
|
|
18
|
+
* @throws If `ScenarioManagementProvider` is not mounted, or the modal
|
|
19
|
+
* ID `_Dev-Tool_` is already open.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* // Toggle with a ref to avoid duplicate-open errors:
|
|
24
|
+
* const devOpenRef = useRef(false);
|
|
25
|
+
* boundKeyboard(['ctrl+d'], () => {
|
|
26
|
+
* if (devOpenRef.current) {
|
|
27
|
+
* closeDevTool();
|
|
28
|
+
* devOpenRef.current = false;
|
|
29
|
+
* } else {
|
|
30
|
+
* openDevTool({ top: 0, left: 0 });
|
|
31
|
+
* devOpenRef.current = true;
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function openDevTool({ top, left, zindex }) {
|
|
37
|
+
openModal('_Dev-Tool_', DevScreen, {
|
|
38
|
+
top: top,
|
|
39
|
+
left: left
|
|
40
|
+
}, {
|
|
41
|
+
zIndex: zindex,
|
|
42
|
+
renderNow: true
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Close the developer debugging modal.
|
|
47
|
+
*
|
|
48
|
+
* Calls {@link closeModal} for the fixed modal ID `_Dev-Tool_`.
|
|
49
|
+
* Safe to call even if the modal is not currently open — acts as
|
|
50
|
+
* a no-op in that case to support toggle patterns where the modal
|
|
51
|
+
* may have been closed by its own Escape binding.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* boundKeyboard(['escape'], () => closeDevTool());
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function closeDevTool() {
|
|
59
|
+
try {
|
|
60
|
+
closeModal('_Dev-Tool_');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Modal already closed — no-op. This handles the case where
|
|
64
|
+
// the Escape key inside DevScreen closed the modal, leaving
|
|
65
|
+
// an external toggle ref stale.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Box } from "ink";
|
|
2
|
+
import React from "react";
|
|
3
|
+
// This modal box is used to display information about all registered global keys.
|
|
4
|
+
// Because it is a modal box, its keyboard is independent and will not be affected by DevTool.
|
|
5
|
+
// Also make sure that the zindex is greater than the zindex of the DevTool so that the visual effects and keyboard reception are applied correctly when the modal box is opened
|
|
6
|
+
export default function GlobalKeyDisplayBox() {
|
|
7
|
+
return (React.createElement(Box, { position: "absolute" }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props accepted by the {@link DevScreen} debugging overlay.
|
|
3
|
+
*/
|
|
4
|
+
export interface DevProps {
|
|
5
|
+
/** Vertical position in rows — 0 is the top of the terminal. */
|
|
6
|
+
top: number;
|
|
7
|
+
/** Horizontal position in columns — 0 is the left edge. */
|
|
8
|
+
left: number;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export { registerComponent, ScenarioManagementProvider, CurrentScreen, skip, back, gotoScreen, openOverlay, closeOverlay, closeAllOverlays, activateOverlay, deactivateOverlay, openModal, closeModal, closeAllModals, useScreenSystem, ModalContext, } from "./screen/index.js";
|
|
2
|
+
export type { SkipOptions, SkipFn, BackFn, GotoScreenFn, OpenOverlayFn, CloseOverlayFn, CloseAllOverlaysFn, ActivateOverlayFn, DeactivateOverlayFn, OpenModalFn, CloseModalFn, CloseAllModalsFn, OpenOverlayOptions, OpenModalOptions, OverlayEntry, ModalEntry, RegisterOptions, ScenarioManagementProviderProps, } from "./screen/index.js";
|
|
3
|
+
export { KeyboardProvider, useKeyboard } from "./keyboard/index.js";
|
|
4
|
+
export { normalizeKeyNames, isNormalCharacter } from "./keyboard/index.js";
|
|
5
|
+
export type { KeyHandler, BoundKeyboardOptions, BoundKeyEntry, ScreenKeyboardLayer, KeyboardProviderProps, GlobalKeyEntry, GlobalSequenceEntry, } from "./keyboard/index.js";
|
|
6
|
+
export type { BlockedKeyOptions, StopOptions, FocusTarget, SequenceOptions, ShortcutOperationEntry, SequenceOperationEntry, ModalMissEvent, ModalMissCallback, ModalMissOptions, ResolvedGlobalKeyEntry, } from "./keyboard/index.js";
|
|
7
|
+
export { useFocusState } from "./keyboard/index.js";
|
|
8
|
+
export { SelectInput } from "./components/select/SelectInput.js";
|
|
9
|
+
export type { Item } from "./components/select/types.js";
|
|
10
|
+
export type { SelectInputProps } from "./components/select/types.js";
|
|
11
|
+
export { MultiSelectInput } from "./components/multi-select/MultiSelectInput.js";
|
|
12
|
+
export type { MultiSelectInputProps } from "./components/multi-select/types.js";
|
|
13
|
+
export { TextInput, UncontrolledTextInput } from "./components/text/TextInput.js";
|
|
14
|
+
export type { TextInputProps, UncontrolledTextInputProps } from "./components/text/types.js";
|
|
15
|
+
export { ConfirmDialog } from "./components/dialog/ConfirmDialog.js";
|
|
16
|
+
export type { ConfirmDialogProps } from "./components/dialog/types.js";
|
|
17
|
+
export { Spinner } from "./components/spinner/Spinner.js";
|
|
18
|
+
export type { SpinnerType } from "./components/spinner/Spinner.js";
|
|
19
|
+
export { ProgressBar } from "./components/progress-bar/ProgressBar.js";
|
|
20
|
+
export { Divider } from "./components/divider/Divider.js";
|
|
21
|
+
export { Badge } from "./components/badge/Badge.js";
|
|
22
|
+
export { KeyHint } from "./components/key-hint/KeyHint.js";
|
|
23
|
+
export { NumberInput } from "./components/number-input/NumberInput.js";
|
|
24
|
+
export { SearchInput } from "./components/search-input/SearchInput.js";
|
|
25
|
+
export { Tabs } from "./components/tabs/Tabs.js";
|
|
26
|
+
export type { Tab, TabsProps } from "./components/tabs/types.js";
|
|
27
|
+
export { Fold } from "./components/fold/Fold.js";
|
|
28
|
+
export type { FoldProps } from "./components/fold/types.js";
|
|
29
|
+
export { Form } from "./components/form/Form.js";
|
|
30
|
+
export { Field } from "./components/form/Field.js";
|
|
31
|
+
export { useFormContext } from "./components/form/context.js";
|
|
32
|
+
export type { FormProps, FieldProps, FieldRenderProps, FormContextValue, Validator, } from "./components/form/types.js";
|
|
33
|
+
export { LanguageProvider } from "./language/index.js";
|
|
34
|
+
export { useI18n } from "./language/index.js";
|
|
35
|
+
export type { LanguageProviderProps, I18nContextValue } from "./language/index.js";
|
|
36
|
+
export { ThemeProvider } from "./theme/index.js";
|
|
37
|
+
export { useTheme } from "./theme/index.js";
|
|
38
|
+
export type { ThemeProviderProps, ThemeContextValue, ThemeDefinition } from "./theme/index.js";
|
|
39
|
+
export { createStorage } from "./storage/index.js";
|
|
40
|
+
export type { StorageOptions, StorageAPI } from "./storage/index.js";
|
|
41
|
+
export { createBinaryStorage, TypeTag, TAG_NAMES } from "./binary-storage/index.js";
|
|
42
|
+
export type { BinaryStorageOptions, BinaryStorageAPI } from "./binary-storage/index.js";
|
|
43
|
+
export { createStreamingReader, StreamCorruptError } from "./binary-storage/StreamingReader.js";
|
|
44
|
+
export type { StreamingReaderOptions, StreamingReaderAPI } from "./binary-storage/StreamingReader.js";
|
|
45
|
+
export { openDevTool, closeDevTool } from "./dev/entrance.js";
|
|
46
|
+
export type { DevProps } from "./dev/types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ── Screen System ──────────────────────────────────────────
|
|
2
|
+
export { registerComponent, ScenarioManagementProvider, CurrentScreen, skip, back, gotoScreen, openOverlay, closeOverlay, closeAllOverlays, activateOverlay, deactivateOverlay, openModal, closeModal, closeAllModals, useScreenSystem, ModalContext, } from "./screen/index.js";
|
|
3
|
+
// ── Keyboard System ────────────────────────────────────────
|
|
4
|
+
export { KeyboardProvider, useKeyboard } from "./keyboard/index.js";
|
|
5
|
+
export { normalizeKeyNames, isNormalCharacter } from "./keyboard/index.js";
|
|
6
|
+
export { useFocusState } from "./keyboard/index.js";
|
|
7
|
+
// Components — SelectInput
|
|
8
|
+
export { SelectInput } from "./components/select/SelectInput.js";
|
|
9
|
+
// Components — MultiSelectInput
|
|
10
|
+
export { MultiSelectInput } from "./components/multi-select/MultiSelectInput.js";
|
|
11
|
+
// Components — TextInput
|
|
12
|
+
export { TextInput, UncontrolledTextInput } from "./components/text/TextInput.js";
|
|
13
|
+
// Components — Dialog
|
|
14
|
+
export { ConfirmDialog } from "./components/dialog/ConfirmDialog.js";
|
|
15
|
+
// Components — Spinner
|
|
16
|
+
export { Spinner } from "./components/spinner/Spinner.js";
|
|
17
|
+
// Components — ProgressBar
|
|
18
|
+
export { ProgressBar } from "./components/progress-bar/ProgressBar.js";
|
|
19
|
+
// Components — Divider
|
|
20
|
+
export { Divider } from "./components/divider/Divider.js";
|
|
21
|
+
// Components — Badge
|
|
22
|
+
export { Badge } from "./components/badge/Badge.js";
|
|
23
|
+
// Components — KeyHint
|
|
24
|
+
export { KeyHint } from "./components/key-hint/KeyHint.js";
|
|
25
|
+
// Components — NumberInput
|
|
26
|
+
export { NumberInput } from "./components/number-input/NumberInput.js";
|
|
27
|
+
// Components — SearchInput
|
|
28
|
+
export { SearchInput } from "./components/search-input/SearchInput.js";
|
|
29
|
+
// Components — Tabs
|
|
30
|
+
export { Tabs } from "./components/tabs/Tabs.js";
|
|
31
|
+
// Components — Fold
|
|
32
|
+
export { Fold } from "./components/fold/Fold.js";
|
|
33
|
+
// Components — Form
|
|
34
|
+
export { Form } from "./components/form/Form.js";
|
|
35
|
+
export { Field } from "./components/form/Field.js";
|
|
36
|
+
export { useFormContext } from "./components/form/context.js";
|
|
37
|
+
// I18n — Language
|
|
38
|
+
export { LanguageProvider } from "./language/index.js";
|
|
39
|
+
export { useI18n } from "./language/index.js";
|
|
40
|
+
// Theme System
|
|
41
|
+
export { ThemeProvider } from "./theme/index.js";
|
|
42
|
+
export { useTheme } from "./theme/index.js";
|
|
43
|
+
// Persistence System
|
|
44
|
+
export { createStorage } from "./storage/index.js";
|
|
45
|
+
// Binary Persistence System
|
|
46
|
+
export { createBinaryStorage, TAG_NAMES } from "./binary-storage/index.js";
|
|
47
|
+
// Binary Streaming Reader
|
|
48
|
+
export { createStreamingReader, StreamCorruptError } from "./binary-storage/StreamingReader.js";
|
|
49
|
+
// Dev Tool
|
|
50
|
+
export { openDevTool, closeDevTool } from "./dev/entrance.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { LayerOwner } from './context.js';
|
|
3
|
+
import type { GlobalKeyEntry, ScreenKeyboardLayer } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Check whether a global key entry should fire for the current event.
|
|
6
|
+
*
|
|
7
|
+
* Evaluates key-name matching, category whitelist, and screen-level
|
|
8
|
+
* global-key override (cover mechanism).
|
|
9
|
+
*
|
|
10
|
+
* @param entry The global key entry to evaluate.
|
|
11
|
+
* @param eventNames Normalized key names from the current event.
|
|
12
|
+
* @param topComponent The topmost screen component, or null if no screen is active.
|
|
13
|
+
* @param layersRef Mutable ref to all keyboard layers.
|
|
14
|
+
* @returns true if the global key matches and is not overridden.
|
|
15
|
+
*
|
|
16
|
+
* @2026-06-14 v3.4.0
|
|
17
|
+
*/
|
|
18
|
+
export declare function checkGlobalKey(entry: GlobalKeyEntry, eventNames: string[], topComponent: React.ComponentType<any> | null, layersRef: React.MutableRefObject<Map<LayerOwner, ScreenKeyboardLayer>>): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a global key entry should fire for the current event.
|
|
3
|
+
*
|
|
4
|
+
* Evaluates key-name matching, category whitelist, and screen-level
|
|
5
|
+
* global-key override (cover mechanism).
|
|
6
|
+
*
|
|
7
|
+
* @param entry The global key entry to evaluate.
|
|
8
|
+
* @param eventNames Normalized key names from the current event.
|
|
9
|
+
* @param topComponent The topmost screen component, or null if no screen is active.
|
|
10
|
+
* @param layersRef Mutable ref to all keyboard layers.
|
|
11
|
+
* @returns true if the global key matches and is not overridden.
|
|
12
|
+
*
|
|
13
|
+
* @2026-06-14 v3.4.0
|
|
14
|
+
*/
|
|
15
|
+
export function checkGlobalKey(entry, eventNames, topComponent, layersRef) {
|
|
16
|
+
const keyNames = Array.isArray(entry.key) ? entry.key : [entry.key];
|
|
17
|
+
if (!keyNames.some((k) => eventNames.includes(k)))
|
|
18
|
+
return false;
|
|
19
|
+
if (!topComponent)
|
|
20
|
+
return false;
|
|
21
|
+
const cat = entry.category;
|
|
22
|
+
if (cat === undefined || cat === '*') {
|
|
23
|
+
}
|
|
24
|
+
else if (Array.isArray(cat) && cat.length === 0) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
else if (Array.isArray(cat)) {
|
|
28
|
+
if (!cat.includes(topComponent))
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const topLayer = layersRef.current.get(topComponent);
|
|
32
|
+
// Global Key rules (affectOverlay + cover)
|
|
33
|
+
//
|
|
34
|
+
// When an Overlay is active:
|
|
35
|
+
// - [true, true] : Affects Overlay, can be overridden only by Overlay
|
|
36
|
+
// - [true, false]: Affects Overlay, cannot be overridden by anyone
|
|
37
|
+
// - [false, true] : Does NOT affect Overlay; works on Screen Stack, can be overridden by Screen Stack
|
|
38
|
+
// - [false, false]: Does NOT affect Overlay; works on Screen Stack, cannot be overridden by Screen Stack
|
|
39
|
+
//
|
|
40
|
+
// When NO Overlay is active (default):
|
|
41
|
+
// - affectOverlay = true → Key becomes inactive
|
|
42
|
+
// - affectOverlay = false → Key remains active on Screen Stack (override rules as above)
|
|
43
|
+
//
|
|
44
|
+
// Option executeWhenNoOverlay (only if affectOverlay = true):
|
|
45
|
+
// Keeps the key active on Screen Stack even without an Overlay, while preserving the original cover rule.
|
|
46
|
+
// @2026-06-11 v3.3.0
|
|
47
|
+
if (topLayer && !entry.affectOverlay && (entry.cover ?? true)) {
|
|
48
|
+
if (keyNames.some((k) => topLayer.globalKeyOverrides.has(k)))
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|