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,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { RegisterOptions } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register a component as a screen in the navigation tree.
|
|
5
|
+
*
|
|
6
|
+
* @param component The React component (used as the unique token).
|
|
7
|
+
* @param template Default props for the component.
|
|
8
|
+
* @param options Optional registration options (e.g. `parent` to attach
|
|
9
|
+
* the component under an existing node in the tree).
|
|
10
|
+
*
|
|
11
|
+
* @throws If the component has already been registered.
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerComponent<C extends React.ComponentType<any>>(component: C, template: React.ComponentProps<C>, options?: RegisterOptions): void;
|
|
14
|
+
/** 获取组件的模板参数 */
|
|
15
|
+
export declare function getTemplate(component: React.ComponentType<any>): Record<string, unknown> | undefined;
|
|
16
|
+
/** 获取组件的父节点 */
|
|
17
|
+
export declare function getParent(component: React.ComponentType<any>): React.ComponentType<any> | null | undefined;
|
|
18
|
+
/** 获取组件的子节点列表 */
|
|
19
|
+
export declare function getChildren(component: React.ComponentType<any>): React.ComponentType<any>[];
|
|
20
|
+
/** 检查组件是否已注册 */
|
|
21
|
+
export declare function hasComponent(component: React.ComponentType<any>): boolean;
|
|
22
|
+
/** 获取所有根节点(parent 为 null 的组件) */
|
|
23
|
+
export declare function getRoots(): React.ComponentType<any>[];
|
|
24
|
+
/** 判断 child 是否是 parent 的直接子节点 */
|
|
25
|
+
export declare function isChildOf(child: React.ComponentType<any>, parent: React.ComponentType<any>): boolean;
|
|
26
|
+
/** 清除所有注册(仅供测试使用) */
|
|
27
|
+
export declare function clearRegistry(): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** 模块级注册表:组件 → 注册信息 */
|
|
2
|
+
const registry = new Map();
|
|
3
|
+
/**
|
|
4
|
+
* Register a component as a screen in the navigation tree.
|
|
5
|
+
*
|
|
6
|
+
* @param component The React component (used as the unique token).
|
|
7
|
+
* @param template Default props for the component.
|
|
8
|
+
* @param options Optional registration options (e.g. `parent` to attach
|
|
9
|
+
* the component under an existing node in the tree).
|
|
10
|
+
*
|
|
11
|
+
* @throws If the component has already been registered.
|
|
12
|
+
*/
|
|
13
|
+
export function registerComponent(component, template, options) {
|
|
14
|
+
if (registry.has(component)) {
|
|
15
|
+
throw new Error(`[Ink-Cartridge] Component "${component.displayName || component.name || "anonymous"}" is already registered. Duplicate registration is not allowed.`);
|
|
16
|
+
}
|
|
17
|
+
registry.set(component, {
|
|
18
|
+
template: template,
|
|
19
|
+
parent: options?.parent ?? null,
|
|
20
|
+
children: new Set(),
|
|
21
|
+
});
|
|
22
|
+
// 如果声明了父节点,将自己添加到父节点的 children 中
|
|
23
|
+
if (options?.parent) {
|
|
24
|
+
const parentEntry = registry.get(options.parent);
|
|
25
|
+
if (!parentEntry) {
|
|
26
|
+
const compName = component.displayName || component.name || "anonymous";
|
|
27
|
+
const parentName = options.parent.displayName ||
|
|
28
|
+
options.parent.name ||
|
|
29
|
+
"anonymous";
|
|
30
|
+
throw new Error(`[Ink-Cartridge] registerComponent("${compName}"): parent component "${parentName}" is not registered. ` +
|
|
31
|
+
`Register the parent first with registerComponent(${parentName}, template).`);
|
|
32
|
+
}
|
|
33
|
+
parentEntry.children.add(component);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** 获取组件的模板参数 */
|
|
37
|
+
export function getTemplate(component) {
|
|
38
|
+
return registry.get(component)?.template;
|
|
39
|
+
}
|
|
40
|
+
/** 获取组件的父节点 */
|
|
41
|
+
export function getParent(component) {
|
|
42
|
+
return registry.get(component)?.parent;
|
|
43
|
+
}
|
|
44
|
+
/** 获取组件的子节点列表 */
|
|
45
|
+
export function getChildren(component) {
|
|
46
|
+
const entry = registry.get(component);
|
|
47
|
+
return entry ? Array.from(entry.children) : [];
|
|
48
|
+
}
|
|
49
|
+
/** 检查组件是否已注册 */
|
|
50
|
+
export function hasComponent(component) {
|
|
51
|
+
return registry.has(component);
|
|
52
|
+
}
|
|
53
|
+
/** 获取所有根节点(parent 为 null 的组件) */
|
|
54
|
+
export function getRoots() {
|
|
55
|
+
const roots = [];
|
|
56
|
+
for (const [component, entry] of registry) {
|
|
57
|
+
if (entry.parent === null) {
|
|
58
|
+
roots.push(component);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return roots;
|
|
62
|
+
}
|
|
63
|
+
/** 判断 child 是否是 parent 的直接子节点 */
|
|
64
|
+
export function isChildOf(child, parent) {
|
|
65
|
+
const entry = registry.get(child);
|
|
66
|
+
return entry?.parent === parent;
|
|
67
|
+
}
|
|
68
|
+
/** 清除所有注册(仅供测试使用) */
|
|
69
|
+
export function clearRegistry() {
|
|
70
|
+
registry.clear();
|
|
71
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface RegisterOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The parent component in the navigation tree.
|
|
5
|
+
* If not provided, the component is considered a root candidate.
|
|
6
|
+
*/
|
|
7
|
+
parent?: React.ComponentType<any>;
|
|
8
|
+
}
|
|
9
|
+
export interface SkipOptions {
|
|
10
|
+
/**
|
|
11
|
+
* When true, only the component's props are updated without remounting the component.
|
|
12
|
+
* This is useful for performance or when preserving internal state.
|
|
13
|
+
*/
|
|
14
|
+
onlyAttribute?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Options for {@link openOverlay}.
|
|
18
|
+
*/
|
|
19
|
+
export interface OpenOverlayOptions {
|
|
20
|
+
/** Whether to activate the overlay immediately. Defaults to true. */
|
|
21
|
+
activate?: boolean;
|
|
22
|
+
/** Visual stacking order. Smaller values render behind larger values. */
|
|
23
|
+
zIndex?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Options for {@link openModal}.
|
|
27
|
+
*/
|
|
28
|
+
export interface OpenModalOptions {
|
|
29
|
+
/** Visual stacking order. Smaller values render behind larger values. Defaults to the current modal count. */
|
|
30
|
+
zIndex?: number;
|
|
31
|
+
/** Whether to render the modal even when it is not the active modal (zIndex not highest). Defaults to false. */
|
|
32
|
+
renderNow?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A single overlay entry in the multi-overlay system.
|
|
36
|
+
*/
|
|
37
|
+
export interface OverlayEntry {
|
|
38
|
+
/** Unique identifier for this overlay. */
|
|
39
|
+
id: string;
|
|
40
|
+
/** The overlay component to render. */
|
|
41
|
+
component: React.ComponentType<any>;
|
|
42
|
+
/** Props passed to the overlay component. */
|
|
43
|
+
props: Record<string, unknown>;
|
|
44
|
+
/** Visual stacking order (lower = behind, higher = front). */
|
|
45
|
+
zIndex: number;
|
|
46
|
+
/** Timestamp for tie-breaking when zIndex values are equal. */
|
|
47
|
+
createdAt: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A single modal entry in the modal system.
|
|
51
|
+
*
|
|
52
|
+
* Modals are architecturally symmetric to overlays but with absolute
|
|
53
|
+
* keyboard priority: the modal with the highest zIndex is the single
|
|
54
|
+
* active modal and consumes all keyboard events.
|
|
55
|
+
*/
|
|
56
|
+
export interface ModalEntry {
|
|
57
|
+
/** Unique identifier for this modal. */
|
|
58
|
+
id: string;
|
|
59
|
+
/** The modal component to render. */
|
|
60
|
+
component: React.ComponentType<any>;
|
|
61
|
+
/** Props passed to the modal component. */
|
|
62
|
+
props: Record<string, unknown>;
|
|
63
|
+
/** Visual stacking order (lower = behind, higher = front). Also determines activation order. */
|
|
64
|
+
zIndex: number;
|
|
65
|
+
/** Timestamp for tie-breaking when zIndex values are equal. */
|
|
66
|
+
createdAt: number;
|
|
67
|
+
/** Whether to render even when not the active modal. Defaults to false. */
|
|
68
|
+
renderNow: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Internal state of the screen management provider.
|
|
72
|
+
*/
|
|
73
|
+
export interface ScreenState {
|
|
74
|
+
/** The full navigation path from the root component to the current screen. */
|
|
75
|
+
path: React.ComponentType<any>[];
|
|
76
|
+
/** Parameters for each component along the path, in the same order. */
|
|
77
|
+
pathParams: Record<string, unknown>[];
|
|
78
|
+
/** All open overlays, sorted by zIndex (ascending). */
|
|
79
|
+
overlays: OverlayEntry[];
|
|
80
|
+
/** Set of overlay IDs that are currently active (receiving keyboard events). */
|
|
81
|
+
activeOverlayIds: Set<string>;
|
|
82
|
+
/** All open modals, sorted by zIndex (ascending). */
|
|
83
|
+
modals: ModalEntry[];
|
|
84
|
+
/** ID of the currently active modal (zIndex highest), or null if none. */
|
|
85
|
+
activeModalId: string | null;
|
|
86
|
+
/** Auto-incrementing counter used as a React key to force remounts when needed. */
|
|
87
|
+
counter: number;
|
|
88
|
+
}
|
|
89
|
+
/** Discriminated union type discriminator. */
|
|
90
|
+
export type ScreenActionType = 'skip' | 'back' | 'gotoScreen' | 'openOverlay' | 'closeOverlay' | 'closeAllOverlays' | 'activateOverlay' | 'deactivateOverlay' | 'openModal' | 'closeModal' | 'closeAllModals';
|
|
91
|
+
/** Action dispatched when navigating down to a child screen. */
|
|
92
|
+
export interface SkipAction {
|
|
93
|
+
type: 'skip';
|
|
94
|
+
/** The target component to navigate to. */
|
|
95
|
+
component: React.ComponentType<any>;
|
|
96
|
+
/** Props to merge with the component's registered template. */
|
|
97
|
+
params: Record<string, unknown>;
|
|
98
|
+
/** Whether to only update props without remounting. */
|
|
99
|
+
onlyAttribute: boolean;
|
|
100
|
+
}
|
|
101
|
+
/** Action dispatched when navigating back to the parent screen. */
|
|
102
|
+
export interface BackAction {
|
|
103
|
+
type: 'back';
|
|
104
|
+
/** Number of levels to go back. Defaults to 1. */
|
|
105
|
+
levels?: number;
|
|
106
|
+
}
|
|
107
|
+
/** Action dispatched when jumping to any registered screen across branches. */
|
|
108
|
+
export interface GotoScreenAction {
|
|
109
|
+
type: 'gotoScreen';
|
|
110
|
+
/** The target component to navigate to. */
|
|
111
|
+
component: React.ComponentType<any>;
|
|
112
|
+
/** Props to merge with the component's registered template. */
|
|
113
|
+
params: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
/** Action dispatched when opening a new overlay. */
|
|
116
|
+
export interface OpenOverlayAction {
|
|
117
|
+
type: 'openOverlay';
|
|
118
|
+
/** Unique identifier for this overlay. */
|
|
119
|
+
id: string;
|
|
120
|
+
/** The overlay component to render. */
|
|
121
|
+
component: React.ComponentType<any>;
|
|
122
|
+
/** Props to pass to the overlay component. */
|
|
123
|
+
params: Record<string, unknown>;
|
|
124
|
+
/** Whether to activate the overlay immediately. */
|
|
125
|
+
activate: boolean;
|
|
126
|
+
/** Optional zIndex for visual stacking. */
|
|
127
|
+
zIndex?: number;
|
|
128
|
+
}
|
|
129
|
+
/** Action dispatched when closing a specific overlay by ID. */
|
|
130
|
+
export interface CloseOverlayAction {
|
|
131
|
+
type: 'closeOverlay';
|
|
132
|
+
/** The ID of the overlay to close. */
|
|
133
|
+
id: string;
|
|
134
|
+
}
|
|
135
|
+
/** Action dispatched when closing all overlays. */
|
|
136
|
+
export interface CloseAllOverlaysAction {
|
|
137
|
+
type: 'closeAllOverlays';
|
|
138
|
+
}
|
|
139
|
+
/** Action dispatched when activating an overlay by ID. */
|
|
140
|
+
export interface ActivateOverlayAction {
|
|
141
|
+
type: 'activateOverlay';
|
|
142
|
+
/** The ID of the overlay to activate. */
|
|
143
|
+
id: string;
|
|
144
|
+
}
|
|
145
|
+
/** Action dispatched when deactivating an overlay by ID. */
|
|
146
|
+
export interface DeactivateOverlayAction {
|
|
147
|
+
type: 'deactivateOverlay';
|
|
148
|
+
/** The ID of the overlay to deactivate. */
|
|
149
|
+
id: string;
|
|
150
|
+
}
|
|
151
|
+
/** Action dispatched when opening a new modal. */
|
|
152
|
+
export interface OpenModalAction {
|
|
153
|
+
type: 'openModal';
|
|
154
|
+
/** Unique identifier for this modal. */
|
|
155
|
+
id: string;
|
|
156
|
+
/** The modal component to render. */
|
|
157
|
+
component: React.ComponentType<any>;
|
|
158
|
+
/** Props to pass to the modal component. */
|
|
159
|
+
params: Record<string, unknown>;
|
|
160
|
+
/** Optional zIndex for visual stacking and activation order. */
|
|
161
|
+
zIndex?: number;
|
|
162
|
+
/** Whether to render even when not active. Defaults to false. */
|
|
163
|
+
renderNow?: boolean;
|
|
164
|
+
}
|
|
165
|
+
/** Action dispatched when closing a specific modal by ID. */
|
|
166
|
+
export interface CloseModalAction {
|
|
167
|
+
type: 'closeModal';
|
|
168
|
+
/** The ID of the modal to close. */
|
|
169
|
+
id: string;
|
|
170
|
+
}
|
|
171
|
+
/** Action dispatched when closing all modals. */
|
|
172
|
+
export interface CloseAllModalsAction {
|
|
173
|
+
type: 'closeAllModals';
|
|
174
|
+
}
|
|
175
|
+
/** Union of all possible screen actions. */
|
|
176
|
+
export type ScreenAction = SkipAction | BackAction | GotoScreenAction | OpenOverlayAction | CloseOverlayAction | CloseAllOverlaysAction | ActivateOverlayAction | DeactivateOverlayAction | OpenModalAction | CloseModalAction | CloseAllModalsAction;
|
|
177
|
+
/**
|
|
178
|
+
* Function signature for navigating to a direct child of the current screen.
|
|
179
|
+
*
|
|
180
|
+
* @typeParam C - The component type (must be a React component).
|
|
181
|
+
* @param component - The child component (must be registered and a direct child).
|
|
182
|
+
* @param params - Props to pass to the component.
|
|
183
|
+
* @param options - Optional navigation flags.
|
|
184
|
+
*/
|
|
185
|
+
export type SkipFn = <C extends React.ComponentType<any>>(component: C, params: React.ComponentProps<C>, options?: SkipOptions) => void;
|
|
186
|
+
/** Function signature for navigating back to the parent screen. */
|
|
187
|
+
export type BackFn = (levels?: number) => void;
|
|
188
|
+
/**
|
|
189
|
+
* Function signature for jumping to any registered screen across branches.
|
|
190
|
+
*
|
|
191
|
+
* @typeParam C - The target component type.
|
|
192
|
+
* @param component - The target component (must be registered).
|
|
193
|
+
* @param params - Props to pass to the component.
|
|
194
|
+
*/
|
|
195
|
+
export type GotoScreenFn = <C extends React.ComponentType<any>>(component: C, params: React.ComponentProps<C>) => void;
|
|
196
|
+
/**
|
|
197
|
+
* Function signature for opening a new overlay.
|
|
198
|
+
*
|
|
199
|
+
* @typeParam C - The overlay component type.
|
|
200
|
+
* @param id - Unique identifier for this overlay.
|
|
201
|
+
* @param component - The overlay component (must be registered).
|
|
202
|
+
* @param params - Props to pass to the overlay.
|
|
203
|
+
* @param options - Optional activation and zIndex settings.
|
|
204
|
+
*/
|
|
205
|
+
export type OpenOverlayFn = <C extends React.ComponentType<any>>(id: string, component: C, params: React.ComponentProps<C>, options?: OpenOverlayOptions) => void;
|
|
206
|
+
/** Function signature for closing a specific overlay by ID. */
|
|
207
|
+
export type CloseOverlayFn = (id: string) => void;
|
|
208
|
+
/** Function signature for closing all overlays. */
|
|
209
|
+
export type CloseAllOverlaysFn = () => void;
|
|
210
|
+
/** Function signature for activating an overlay by ID. */
|
|
211
|
+
export type ActivateOverlayFn = (id: string) => void;
|
|
212
|
+
/** Function signature for deactivating an overlay by ID. */
|
|
213
|
+
export type DeactivateOverlayFn = (id: string) => void;
|
|
214
|
+
/**
|
|
215
|
+
* Function signature for opening a new modal.
|
|
216
|
+
*
|
|
217
|
+
* @typeParam C - The modal component type.
|
|
218
|
+
* @param id - Unique identifier for this modal.
|
|
219
|
+
* @param component - The modal component (must be registered).
|
|
220
|
+
* @param params - Props to pass to the modal.
|
|
221
|
+
* @param options - Optional zIndex and renderNow settings.
|
|
222
|
+
*/
|
|
223
|
+
export type OpenModalFn = <C extends React.ComponentType<any>>(id: string, component: C, params: React.ComponentProps<C>, options?: OpenModalOptions) => void;
|
|
224
|
+
/** Function signature for closing a specific modal by ID. */
|
|
225
|
+
export type CloseModalFn = (id: string) => void;
|
|
226
|
+
/** Function signature for closing all modals. */
|
|
227
|
+
export type CloseAllModalsFn = () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { StorageOptions, StorageAPI } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a key-value storage station backed by a JSON file.
|
|
4
|
+
*
|
|
5
|
+
* The returned {@link StorageAPI} provides typed `read.*` and `write.*`
|
|
6
|
+
* methods that validate types at runtime and automatically repair
|
|
7
|
+
* mismatches by writing the default value back to disk. All operations
|
|
8
|
+
* are serialised internally — concurrent callers never interleave.
|
|
9
|
+
*
|
|
10
|
+
* @param options Optional configuration for the storage directory,
|
|
11
|
+
* file name, and flush behaviour.
|
|
12
|
+
* @returns A typed storage API. Each instance is independent;
|
|
13
|
+
* you can create as many stations as you need.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Default storage station (./data/config.json)
|
|
18
|
+
* const config = createStorage();
|
|
19
|
+
*
|
|
20
|
+
* // Custom station for game saves
|
|
21
|
+
* const saves = createStorage({ dir: './saves', file: 'slot1.json' });
|
|
22
|
+
*
|
|
23
|
+
* // Write typed values
|
|
24
|
+
* await config.write.str('username', 'Alice');
|
|
25
|
+
* await config.write.num('volume', 80);
|
|
26
|
+
* await config.write.obj('window', { width: 120, height: 40 });
|
|
27
|
+
*
|
|
28
|
+
* // Read with defaults (auto-repairs on mismatch)
|
|
29
|
+
* const name = await config.read.str('username', 'Guest');
|
|
30
|
+
* const vol = await config.read.num('volume', 50);
|
|
31
|
+
* const win = await config.read.obj('window', { width: 80, height: 24 });
|
|
32
|
+
*
|
|
33
|
+
* // Key management
|
|
34
|
+
* if (await config.has('username')) { ... }
|
|
35
|
+
* await config.delete('temp-key');
|
|
36
|
+
* await config.clear();
|
|
37
|
+
* const all = await config.getAll();
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @throws Never throws during construction. Read/write errors from
|
|
41
|
+
* the filesystem (permissions, disk full) propagate to the
|
|
42
|
+
* caller as standard Node.js `ErrnoException` objects.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createStorage(options?: StorageOptions): StorageAPI;
|
|
45
|
+
export type { StorageOptions, StorageAPI };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Storage } from './storage.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a key-value storage station backed by a JSON file.
|
|
4
|
+
*
|
|
5
|
+
* The returned {@link StorageAPI} provides typed `read.*` and `write.*`
|
|
6
|
+
* methods that validate types at runtime and automatically repair
|
|
7
|
+
* mismatches by writing the default value back to disk. All operations
|
|
8
|
+
* are serialised internally — concurrent callers never interleave.
|
|
9
|
+
*
|
|
10
|
+
* @param options Optional configuration for the storage directory,
|
|
11
|
+
* file name, and flush behaviour.
|
|
12
|
+
* @returns A typed storage API. Each instance is independent;
|
|
13
|
+
* you can create as many stations as you need.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Default storage station (./data/config.json)
|
|
18
|
+
* const config = createStorage();
|
|
19
|
+
*
|
|
20
|
+
* // Custom station for game saves
|
|
21
|
+
* const saves = createStorage({ dir: './saves', file: 'slot1.json' });
|
|
22
|
+
*
|
|
23
|
+
* // Write typed values
|
|
24
|
+
* await config.write.str('username', 'Alice');
|
|
25
|
+
* await config.write.num('volume', 80);
|
|
26
|
+
* await config.write.obj('window', { width: 120, height: 40 });
|
|
27
|
+
*
|
|
28
|
+
* // Read with defaults (auto-repairs on mismatch)
|
|
29
|
+
* const name = await config.read.str('username', 'Guest');
|
|
30
|
+
* const vol = await config.read.num('volume', 50);
|
|
31
|
+
* const win = await config.read.obj('window', { width: 80, height: 24 });
|
|
32
|
+
*
|
|
33
|
+
* // Key management
|
|
34
|
+
* if (await config.has('username')) { ... }
|
|
35
|
+
* await config.delete('temp-key');
|
|
36
|
+
* await config.clear();
|
|
37
|
+
* const all = await config.getAll();
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @throws Never throws during construction. Read/write errors from
|
|
41
|
+
* the filesystem (permissions, disk full) propagate to the
|
|
42
|
+
* caller as standard Node.js `ErrnoException` objects.
|
|
43
|
+
*/
|
|
44
|
+
export function createStorage(options) {
|
|
45
|
+
return new Storage(options);
|
|
46
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { ZodType } from 'zod';
|
|
2
|
+
import type { StorageOptions, StorageAPI } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A key-value storage station backed by a single JSON file.
|
|
5
|
+
*
|
|
6
|
+
* Each instance manages one JSON file on disk. All read/write operations
|
|
7
|
+
* are asynchronous and serialised internally — concurrent callers never
|
|
8
|
+
* interleave. The file is loaded lazily on the first access, and writes
|
|
9
|
+
* use atomic rename (write to `.tmp` then rename) to prevent data
|
|
10
|
+
* corruption from partial writes or crashes.
|
|
11
|
+
*
|
|
12
|
+
* Instances are created via {@link createStorage}, not directly.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Storage implements StorageAPI {
|
|
15
|
+
private options;
|
|
16
|
+
private data;
|
|
17
|
+
private pending;
|
|
18
|
+
private filePath;
|
|
19
|
+
constructor(options?: StorageOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Typed write methods.
|
|
22
|
+
*
|
|
23
|
+
* Each method writes a value of the corresponding type to the given key.
|
|
24
|
+
* The write is serialised: it waits for any pending writes to complete,
|
|
25
|
+
* then atomically persists the updated JSON to disk.
|
|
26
|
+
*
|
|
27
|
+
* `any` is the untyped escape hatch — it accepts any value and performs
|
|
28
|
+
* no type-checking on subsequent reads unless you use a typed read method.
|
|
29
|
+
*/
|
|
30
|
+
write: {
|
|
31
|
+
/** Persist a numeric value under `key`. */
|
|
32
|
+
num: (key: string, value: number) => Promise<void>;
|
|
33
|
+
/** Persist a string value under `key`. */
|
|
34
|
+
str: (key: string, value: string) => Promise<void>;
|
|
35
|
+
/** Persist a boolean value under `key`. */
|
|
36
|
+
b: (key: string, value: boolean) => Promise<void>;
|
|
37
|
+
/** Persist a plain object under `key`. */
|
|
38
|
+
obj: <T extends object>(key: string, value: T) => Promise<void>;
|
|
39
|
+
/** Persist an array under `key`. */
|
|
40
|
+
arr: <T>(key: string, value: T[]) => Promise<void>;
|
|
41
|
+
/** Persist any value under `key`. No type constraint. */
|
|
42
|
+
any: (key: string, value: unknown) => Promise<void>;
|
|
43
|
+
/** Persist any value under `key`. Symmetric alias for `any`. */
|
|
44
|
+
schema: <T>(key: string, value: T) => Promise<void>;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Typed read methods.
|
|
48
|
+
*
|
|
49
|
+
* Each method reads the value at `key` and validates that it matches the
|
|
50
|
+
* expected type. If the key is missing or the stored value has a different
|
|
51
|
+
* type, `defaultValue` is returned **and the file is repaired** so that
|
|
52
|
+
* subsequent reads hit the correct value.
|
|
53
|
+
*/
|
|
54
|
+
read: {
|
|
55
|
+
/** Read a number. Returns `defaultValue` if missing or wrong type. */
|
|
56
|
+
num: (key: string, defaultValue: number) => Promise<number>;
|
|
57
|
+
/** Read a string. Returns `defaultValue` if missing or wrong type. */
|
|
58
|
+
str: (key: string, defaultValue: string) => Promise<string>;
|
|
59
|
+
/** Read a boolean. Returns `defaultValue` if missing or wrong type. */
|
|
60
|
+
b: (key: string, defaultValue: boolean) => Promise<boolean>;
|
|
61
|
+
/** Read a plain object (generic). Returns `defaultValue` if missing or wrong type. */
|
|
62
|
+
obj: <T extends object>(key: string, defaultValue: T) => Promise<T>;
|
|
63
|
+
/** Read an array (generic). Returns `defaultValue` if missing or wrong type. */
|
|
64
|
+
arr: <T>(key: string, defaultValue: T[]) => Promise<T[]>;
|
|
65
|
+
/** Read any value. Returns `defaultValue` if missing. No type validation. */
|
|
66
|
+
any: <T>(key: string, defaultValue: T) => Promise<T>;
|
|
67
|
+
/** Read a value validated against a zod schema. Auto-repairs on parse failure. */
|
|
68
|
+
schema: <T>(key: string, schema: ZodType<T>, defaultValue: T) => Promise<T>;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Check whether a key exists in the store.
|
|
72
|
+
*
|
|
73
|
+
* @returns `true` if the key has an entry (even if its value is `null`
|
|
74
|
+
* or `undefined` — it checks own-property presence).
|
|
75
|
+
*/
|
|
76
|
+
has(key: string): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Remove a key and its value from the store.
|
|
79
|
+
*
|
|
80
|
+
* If the key does not exist this is a no-op (no error thrown).
|
|
81
|
+
*/
|
|
82
|
+
delete(key: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Remove all keys from the store, resetting it to an empty object.
|
|
85
|
+
*/
|
|
86
|
+
clear(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Return a shallow copy of the entire raw data object.
|
|
89
|
+
*
|
|
90
|
+
* Modifications to the returned object do **not** affect the store
|
|
91
|
+
* (use the write methods to persist changes).
|
|
92
|
+
*/
|
|
93
|
+
getAll(): Promise<Record<string, unknown>>;
|
|
94
|
+
private ensureDir;
|
|
95
|
+
/**
|
|
96
|
+
* Serialise access so concurrent callers never interleave.
|
|
97
|
+
*
|
|
98
|
+
* Each task is chained onto `this.pending` using `.then(task, task)`.
|
|
99
|
+
* The second argument ensures that even if a previous task rejects,
|
|
100
|
+
* the next task still runs (the chain is never broken).
|
|
101
|
+
*/
|
|
102
|
+
private enqueue;
|
|
103
|
+
private setValue;
|
|
104
|
+
/**
|
|
105
|
+
* Read a value with type validation and automatic repair.
|
|
106
|
+
*
|
|
107
|
+
* If the key is missing the default is written to disk so that
|
|
108
|
+
* subsequent reads return it directly. If the stored value has a
|
|
109
|
+
* different type than expected, the default replaces it on disk.
|
|
110
|
+
*/
|
|
111
|
+
private getValue;
|
|
112
|
+
/**
|
|
113
|
+
* Read a value and validate it against a zod schema.
|
|
114
|
+
*
|
|
115
|
+
* On success the parsed value is returned (including any transforms
|
|
116
|
+
* or coercions the schema applies). On failure — missing key or
|
|
117
|
+
* schema parse error — `defaultValue` is written to disk and returned.
|
|
118
|
+
*/
|
|
119
|
+
private getValueWithSchema;
|
|
120
|
+
/**
|
|
121
|
+
* Load data from disk on first access (lazy initialisation).
|
|
122
|
+
*
|
|
123
|
+
* Once loaded the data stays in memory for the lifetime of the
|
|
124
|
+
* instance. File-not-found and corrupt-JSON errors are silently
|
|
125
|
+
* swallowed — in both cases we start with an empty store.
|
|
126
|
+
*/
|
|
127
|
+
private ensureLoaded;
|
|
128
|
+
/**
|
|
129
|
+
* Atomically persist the in-memory data to disk.
|
|
130
|
+
*
|
|
131
|
+
* Writes to a `.tmp` file first, then renames it over the target
|
|
132
|
+
* file. This guarantees that the on-disk file is always either the
|
|
133
|
+
* complete previous version or the complete new version — never a
|
|
134
|
+
* partially-written mess.
|
|
135
|
+
*
|
|
136
|
+
* No-op when `options.flush` is `false`.
|
|
137
|
+
*/
|
|
138
|
+
private flush;
|
|
139
|
+
}
|