@nmakarov/cli-toolkit 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/args.cjs +35 -5
- package/dist/args.cjs.map +1 -1
- package/dist/args.js +35 -5
- package/dist/args.js.map +1 -1
- package/dist/index.cjs +1938 -1690
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1888 -1645
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +1380 -57
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +1384 -57
- package/dist/init.js.map +1 -1
- package/dist/logger.cjs +98 -22
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.js +97 -21
- package/dist/logger.js.map +1 -1
- package/dist/params.cjs +90 -20
- package/dist/params.cjs.map +1 -1
- package/dist/params.js +90 -18
- package/dist/params.js.map +1 -1
- package/dist/screen.cjs +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
|
|
3
|
+
var __esmCache = {};
|
|
4
|
+
var __loadESMSync = function(moduleName) {
|
|
5
|
+
if (!__esmCache[moduleName]) {
|
|
6
|
+
throw new Error(`ESM module "${moduleName}" not loaded. Please call the load() function first: const toolkit = require("@nmakarov/cli-toolkit"); await toolkit.load();`);
|
|
7
|
+
}
|
|
8
|
+
return __esmCache[moduleName];
|
|
9
|
+
};
|
|
2
10
|
var __create = Object.create;
|
|
3
11
|
var __defProp = Object.defineProperty;
|
|
4
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
14
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __esm = (fn, res) => function __init() {
|
|
17
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
18
|
+
};
|
|
8
19
|
var __export = (target, all) => {
|
|
9
20
|
for (var name in all)
|
|
10
21
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,1788 +38,1941 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
38
|
));
|
|
28
39
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
40
|
|
|
30
|
-
// src/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
var Args = class {
|
|
89
|
-
args = {};
|
|
90
|
-
flags = {};
|
|
91
|
-
options = {};
|
|
92
|
-
commands = [];
|
|
93
|
-
usedKeys = /* @__PURE__ */ new Set();
|
|
94
|
-
aliases = {};
|
|
95
|
-
overrides = {};
|
|
96
|
-
defaults = {};
|
|
97
|
-
prefixes = [];
|
|
98
|
-
nots = [];
|
|
99
|
-
configValues = {};
|
|
100
|
-
configsLoaded = [];
|
|
101
|
-
env = "local";
|
|
102
|
-
constructor(config2 = {}) {
|
|
103
|
-
this.aliases = config2.aliases || {};
|
|
104
|
-
this.overrides = config2.overrides || {};
|
|
105
|
-
this.defaults = config2.defaults || {};
|
|
106
|
-
this.prefixes = config2.prefixes || ["not", "no"];
|
|
107
|
-
const args = config2.args || process.argv.slice(2);
|
|
108
|
-
this.parseArgs(args);
|
|
109
|
-
this.env = this.get("env")?.toLowerCase() || "local";
|
|
110
|
-
this.loadDotEnv();
|
|
111
|
-
this.loadConfigFiles();
|
|
112
|
-
this.checkConflicts();
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Parse command line arguments
|
|
116
|
-
*/
|
|
117
|
-
parseArgs(args) {
|
|
118
|
-
let i = 0;
|
|
119
|
-
while (i < args.length) {
|
|
120
|
-
const arg = args[i];
|
|
121
|
-
if (arg.startsWith("--")) {
|
|
122
|
-
const [key, value] = this.parseLongOption(arg);
|
|
123
|
-
this.setValue(key, value);
|
|
124
|
-
i++;
|
|
125
|
-
} else if (arg.startsWith("-")) {
|
|
126
|
-
const result = this.parseShortOption(arg, args, i);
|
|
127
|
-
if (result.consumed > 0) {
|
|
128
|
-
i += result.consumed;
|
|
41
|
+
// src/screen/components.ts
|
|
42
|
+
function getScreenWidth(maxWidth = null) {
|
|
43
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
44
|
+
const availableWidth = Math.max(20, terminalWidth - 4);
|
|
45
|
+
return maxWidth ? Math.min(availableWidth, maxWidth) : availableWidth;
|
|
46
|
+
}
|
|
47
|
+
function ScreenContainer({ children }) {
|
|
48
|
+
const width = getScreenWidth();
|
|
49
|
+
return (0, import_react.createElement)(import_ink.Box, {
|
|
50
|
+
flexDirection: "column",
|
|
51
|
+
marginTop: 1,
|
|
52
|
+
borderStyle: "single",
|
|
53
|
+
borderColor: "cyan",
|
|
54
|
+
paddingX: 1,
|
|
55
|
+
width
|
|
56
|
+
// Use the calculated width directly
|
|
57
|
+
}, children);
|
|
58
|
+
}
|
|
59
|
+
function ScreenRow({ children }) {
|
|
60
|
+
return (0, import_react.createElement)(import_ink.Box, { flexDirection: "column" }, children);
|
|
61
|
+
}
|
|
62
|
+
function ScreenTitle({ text }) {
|
|
63
|
+
return (0, import_react.createElement)(
|
|
64
|
+
ScreenRow,
|
|
65
|
+
{},
|
|
66
|
+
(0, import_react.createElement)(import_ink.Text, { bold: true, color: "cyan" }, text)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function ScreenDivider({ width }) {
|
|
70
|
+
const dividerWidth = width || getScreenWidth() - 4;
|
|
71
|
+
return (0, import_react.createElement)(import_ink.Text, { color: "cyan", dimColor: true }, "\u2500".repeat(dividerWidth));
|
|
72
|
+
}
|
|
73
|
+
function ScreenBody({ children, alignItems = "flex-start" }) {
|
|
74
|
+
return (0, import_react.createElement)(import_ink.Box, { flexDirection: "column", alignItems }, children);
|
|
75
|
+
}
|
|
76
|
+
function ScreenFooter({ lines, textStyle }) {
|
|
77
|
+
const defaultTextStyle = {
|
|
78
|
+
dimColor: true,
|
|
79
|
+
color: "white"
|
|
80
|
+
};
|
|
81
|
+
const finalTextStyle = { ...defaultTextStyle, ...textStyle };
|
|
82
|
+
const flattenAndWrap = (items, keyPrefix = "") => {
|
|
83
|
+
const result = [];
|
|
84
|
+
let keyIndex = 0;
|
|
85
|
+
items.forEach((item, index) => {
|
|
86
|
+
if (Array.isArray(item)) {
|
|
87
|
+
const nested = flattenAndWrap(item, `${keyPrefix}-${index}`);
|
|
88
|
+
result.push(...nested);
|
|
89
|
+
} else if (typeof item === "string") {
|
|
90
|
+
result.push(
|
|
91
|
+
(0, import_react.createElement)(import_ink.Text, { key: `${keyPrefix}-${keyIndex++}`, ...finalTextStyle }, item)
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
const element = item;
|
|
95
|
+
if (element.key === null || element.key === void 0) {
|
|
96
|
+
result.push(
|
|
97
|
+
(0, import_react.createElement)(import_ink.Text, { key: `${keyPrefix}-${keyIndex++}`, ...finalTextStyle }, element)
|
|
98
|
+
);
|
|
129
99
|
} else {
|
|
130
|
-
|
|
100
|
+
result.push(element);
|
|
131
101
|
}
|
|
132
|
-
} else {
|
|
133
|
-
this.commands.push(arg);
|
|
134
|
-
i++;
|
|
135
102
|
}
|
|
136
|
-
}
|
|
103
|
+
});
|
|
104
|
+
return result;
|
|
105
|
+
};
|
|
106
|
+
const wrappedItems = flattenAndWrap(lines);
|
|
107
|
+
return (0, import_react.createElement)(
|
|
108
|
+
import_ink.Box,
|
|
109
|
+
{ flexDirection: "column" },
|
|
110
|
+
(0, import_react.createElement)(import_ink.Box, { flexDirection: "row" }, ...wrappedItems)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
var import_react, import_ink;
|
|
114
|
+
var init_components = __esm({
|
|
115
|
+
"src/screen/components.ts"() {
|
|
116
|
+
"use strict";
|
|
117
|
+
import_react = require("react");
|
|
118
|
+
import_ink = require("ink");
|
|
137
119
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// src/screen/list-components.ts
|
|
123
|
+
function MultiColumnListComponent({ items, ctx, selectedIndexRef }) {
|
|
124
|
+
const [, forceUpdate] = (0, import_react2.useState)({});
|
|
125
|
+
const termWidth = (process.stdout.columns || 80) - 8;
|
|
126
|
+
const maxItemLength = Math.max(...items.map((w) => w.length));
|
|
127
|
+
const columnWidth = maxItemLength + 3;
|
|
128
|
+
const columns = Math.max(1, Math.floor(termWidth / columnWidth));
|
|
129
|
+
const itemsPerColumn = Math.ceil(items.length / columns);
|
|
130
|
+
(0, import_react2.useEffect)(() => {
|
|
131
|
+
ctx.setAction("moveUp", () => {
|
|
132
|
+
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - 1);
|
|
133
|
+
forceUpdate({});
|
|
134
|
+
});
|
|
135
|
+
ctx.setAction("moveDown", () => {
|
|
136
|
+
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + 1);
|
|
137
|
+
forceUpdate({});
|
|
138
|
+
});
|
|
139
|
+
ctx.setAction("moveLeft", () => {
|
|
140
|
+
if (selectedIndexRef.current === 0) {
|
|
141
|
+
ctx.goBack();
|
|
142
|
+
} else {
|
|
143
|
+
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - itemsPerColumn);
|
|
144
|
+
forceUpdate({});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
ctx.setAction("moveRight", () => {
|
|
148
|
+
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + itemsPerColumn);
|
|
149
|
+
forceUpdate({});
|
|
150
|
+
});
|
|
151
|
+
ctx.setKeyBinding([
|
|
152
|
+
{ key: "leftArrow", caption: "navigate", action: "moveLeft", order: 0 },
|
|
153
|
+
{ key: "rightArrow", caption: "navigate", action: "moveRight", order: 0 },
|
|
154
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
155
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
156
|
+
]);
|
|
157
|
+
ctx.addFooter(`Total: ${items.length} items`);
|
|
158
|
+
}, []);
|
|
159
|
+
const selectedIndex = selectedIndexRef.current;
|
|
160
|
+
const rows = [];
|
|
161
|
+
for (let row = 0; row < itemsPerColumn; row++) {
|
|
162
|
+
const cols = [];
|
|
163
|
+
for (let col = 0; col < columns; col++) {
|
|
164
|
+
const index = col * itemsPerColumn + row;
|
|
165
|
+
if (index < items.length) {
|
|
166
|
+
const isSelected = index === selectedIndex;
|
|
167
|
+
cols.push(
|
|
168
|
+
h2(
|
|
169
|
+
import_ink2.Box,
|
|
170
|
+
{ key: index, width: columnWidth },
|
|
171
|
+
h2(import_ink2.Text, {
|
|
172
|
+
color: isSelected ? "black" : "white",
|
|
173
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
174
|
+
bold: isSelected
|
|
175
|
+
}, items[index].padEnd(maxItemLength))
|
|
176
|
+
)
|
|
177
|
+
);
|
|
148
178
|
}
|
|
149
|
-
this.nots.push(key);
|
|
150
|
-
return [strippedKey, false];
|
|
151
|
-
}
|
|
152
|
-
if (key.includes("=")) {
|
|
153
|
-
const eqIndex = key.indexOf("=");
|
|
154
|
-
const optionKey = key.slice(0, eqIndex);
|
|
155
|
-
const value = key.slice(eqIndex + 1);
|
|
156
|
-
return [optionKey, this.parseValue(value)];
|
|
157
|
-
} else {
|
|
158
|
-
return [key, true];
|
|
159
179
|
}
|
|
180
|
+
rows.push(
|
|
181
|
+
h2(ScreenRow, { key: row, children: h2(import_ink2.Box, { flexDirection: "row" }, ...cols) })
|
|
182
|
+
);
|
|
160
183
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
return h2(import_ink2.Box, { flexDirection: "column" }, ...rows);
|
|
185
|
+
}
|
|
186
|
+
function MultiColumnListWithPreviewComponent({
|
|
187
|
+
items,
|
|
188
|
+
getPreviewContent,
|
|
189
|
+
ctx,
|
|
190
|
+
selectedIndexRef
|
|
191
|
+
}) {
|
|
192
|
+
const [, forceUpdate] = (0, import_react2.useState)({});
|
|
193
|
+
const termWidth = (process.stdout.columns || 80) - 8;
|
|
194
|
+
const maxItemLength = Math.max(...items.map((w) => w.length));
|
|
195
|
+
const columnWidth = maxItemLength + 3;
|
|
196
|
+
const columns = Math.max(1, Math.floor(termWidth / columnWidth));
|
|
197
|
+
const itemsPerColumn = Math.ceil(items.length / columns);
|
|
198
|
+
(0, import_react2.useEffect)(() => {
|
|
199
|
+
ctx.setAction("moveUp", () => {
|
|
200
|
+
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - 1);
|
|
201
|
+
forceUpdate({});
|
|
202
|
+
});
|
|
203
|
+
ctx.setAction("moveDown", () => {
|
|
204
|
+
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + 1);
|
|
205
|
+
forceUpdate({});
|
|
206
|
+
});
|
|
207
|
+
ctx.setAction("moveLeft", () => {
|
|
208
|
+
if (selectedIndexRef.current === 0) {
|
|
209
|
+
ctx.goBack();
|
|
210
|
+
} else {
|
|
211
|
+
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - itemsPerColumn);
|
|
212
|
+
forceUpdate({});
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
ctx.setAction("moveRight", () => {
|
|
216
|
+
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + itemsPerColumn);
|
|
217
|
+
forceUpdate({});
|
|
218
|
+
});
|
|
219
|
+
ctx.setKeyBinding([
|
|
220
|
+
{ key: "leftArrow", caption: "navigate", action: "moveLeft", order: 0 },
|
|
221
|
+
{ key: "rightArrow", caption: "navigate", action: "moveRight", order: 0 },
|
|
222
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
223
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
224
|
+
]);
|
|
225
|
+
ctx.addFooter(`Total: ${items.length} items`);
|
|
226
|
+
}, []);
|
|
227
|
+
const selectedIndex = selectedIndexRef.current;
|
|
228
|
+
const selectedItem = items[selectedIndex];
|
|
229
|
+
const rows = [];
|
|
230
|
+
for (let row = 0; row < itemsPerColumn; row++) {
|
|
231
|
+
const cols = [];
|
|
232
|
+
for (let col = 0; col < columns; col++) {
|
|
233
|
+
const index = col * itemsPerColumn + row;
|
|
234
|
+
if (index < items.length) {
|
|
235
|
+
const isSelected = index === selectedIndex;
|
|
236
|
+
cols.push(
|
|
237
|
+
h2(
|
|
238
|
+
import_ink2.Box,
|
|
239
|
+
{ key: index, width: columnWidth },
|
|
240
|
+
h2(import_ink2.Text, {
|
|
241
|
+
color: isSelected ? "black" : "white",
|
|
242
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
243
|
+
bold: isSelected
|
|
244
|
+
}, items[index].padEnd(maxItemLength))
|
|
245
|
+
)
|
|
246
|
+
);
|
|
247
|
+
}
|
|
170
248
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
249
|
+
rows.push(
|
|
250
|
+
h2(ScreenRow, { key: row, children: h2(import_ink2.Box, { flexDirection: "row" }, ...cols) })
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
const previewContent = getPreviewContent ? getPreviewContent(selectedItem) : selectedItem;
|
|
254
|
+
const previewRows = [];
|
|
255
|
+
if (typeof previewContent === "string") {
|
|
256
|
+
previewRows.push(h2(ScreenRow, { key: "preview-string", children: h2(import_ink2.Text, { bold: true }, previewContent) }));
|
|
257
|
+
} else if (typeof previewContent === "object" && !import_react2.default.isValidElement(previewContent) && previewContent !== null) {
|
|
258
|
+
Object.entries(previewContent).forEach(([key, value], idx) => {
|
|
259
|
+
previewRows.push(h2(ScreenRow, { key: `preview-${key}-${idx}`, children: h2(import_ink2.Text, {}, `${key}: ${value}`) }));
|
|
260
|
+
});
|
|
261
|
+
} else if (import_react2.default.isValidElement(previewContent)) {
|
|
262
|
+
previewRows.push(h2(ScreenRow, { key: "preview-element", children: previewContent }));
|
|
263
|
+
}
|
|
264
|
+
return h2(
|
|
265
|
+
import_ink2.Box,
|
|
266
|
+
{ flexDirection: "column" },
|
|
267
|
+
...rows,
|
|
268
|
+
h2(ScreenRow, { key: "spacer-1", children: h2(import_ink2.Text, {}, " ") }),
|
|
269
|
+
h2(ScreenDivider, { key: "divider" }),
|
|
270
|
+
h2(ScreenRow, { key: "spacer-2", children: h2(import_ink2.Text, {}, " ") }),
|
|
271
|
+
...previewRows
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sortable = false, maxHeight, sortHighlightStyle, selectionMarker = " " }) {
|
|
275
|
+
const [, forceUpdate] = (0, import_react2.useState)({});
|
|
276
|
+
const [sortOrder, setSortOrder] = (0, import_react2.useState)("none");
|
|
277
|
+
const [scrollOffset, setScrollOffset] = (0, import_react2.useState)(0);
|
|
278
|
+
const scrollStateRef = (0, import_react2.useRef)({ scrollOffset: 0, maxHeight: 0, totalItems: 0 });
|
|
279
|
+
const defaultGetTitle = (item) => {
|
|
280
|
+
return getTitle ? getTitle(item) : typeof item.value === "string" ? item.value : item.value?.title || item.name;
|
|
281
|
+
};
|
|
282
|
+
const titleGetter = getTitle || defaultGetTitle;
|
|
283
|
+
const displayItems = sortable && sortOrder !== "none" ? [...items].sort((a, b) => {
|
|
284
|
+
const titleA = titleGetter(a).toLowerCase();
|
|
285
|
+
const titleB = titleGetter(b).toLowerCase();
|
|
286
|
+
if (sortOrder === "asc") {
|
|
287
|
+
return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
|
|
288
|
+
} else {
|
|
289
|
+
return titleA > titleB ? -1 : titleA < titleB ? 1 : 0;
|
|
290
|
+
}
|
|
291
|
+
}) : items;
|
|
292
|
+
const effectiveMaxHeight = maxHeight || displayItems.length;
|
|
293
|
+
const canScroll = displayItems.length > effectiveMaxHeight;
|
|
294
|
+
const maxScrollOffset = Math.max(0, displayItems.length - effectiveMaxHeight);
|
|
295
|
+
const clampedScrollOffset = Math.min(Math.max(0, scrollOffset), maxScrollOffset);
|
|
296
|
+
const visibleItems = displayItems.slice(clampedScrollOffset, clampedScrollOffset + effectiveMaxHeight);
|
|
297
|
+
const canScrollUp = clampedScrollOffset > 0;
|
|
298
|
+
const canScrollDown = clampedScrollOffset < maxScrollOffset;
|
|
299
|
+
scrollStateRef.current = { scrollOffset, maxHeight: effectiveMaxHeight, totalItems: displayItems.length };
|
|
300
|
+
(0, import_react2.useEffect)(() => {
|
|
301
|
+
ctx.setAction("moveUp", () => {
|
|
302
|
+
const newIndex = Math.max(0, selectedIndexRef.current - 1);
|
|
303
|
+
selectedIndexRef.current = newIndex;
|
|
304
|
+
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
305
|
+
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
306
|
+
const currentClampedScrollOffset = Math.min(Math.max(0, currentScrollOffset), currentMaxScrollOffset);
|
|
307
|
+
if (newIndex < currentClampedScrollOffset) {
|
|
308
|
+
setScrollOffset(newIndex);
|
|
309
|
+
}
|
|
310
|
+
forceUpdate({});
|
|
311
|
+
});
|
|
312
|
+
ctx.setAction("moveDown", () => {
|
|
313
|
+
const currentItems = sortable && sortOrder !== "none" ? [...items].sort((a, b) => {
|
|
314
|
+
const titleA = titleGetter(a).toLowerCase();
|
|
315
|
+
const titleB = titleGetter(b).toLowerCase();
|
|
316
|
+
if (sortOrder === "asc") {
|
|
317
|
+
return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
|
|
176
318
|
} else {
|
|
177
|
-
|
|
319
|
+
return titleA > titleB ? -1 : titleA < titleB ? 1 : 0;
|
|
178
320
|
}
|
|
321
|
+
}) : items;
|
|
322
|
+
const maxIndex = currentItems.length - 1;
|
|
323
|
+
const newIndex = Math.min(maxIndex, selectedIndexRef.current + 1);
|
|
324
|
+
selectedIndexRef.current = newIndex;
|
|
325
|
+
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
326
|
+
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
327
|
+
const currentClampedScrollOffset = Math.min(Math.max(0, currentScrollOffset), currentMaxScrollOffset);
|
|
328
|
+
if (newIndex >= currentClampedScrollOffset + currentMaxHeight) {
|
|
329
|
+
setScrollOffset(newIndex - currentMaxHeight + 1);
|
|
179
330
|
}
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
331
|
+
forceUpdate({});
|
|
332
|
+
});
|
|
333
|
+
ctx.setAction("scrollUp", () => {
|
|
334
|
+
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
335
|
+
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
336
|
+
const newScrollOffset = Math.max(0, currentScrollOffset - 1);
|
|
337
|
+
setScrollOffset(newScrollOffset);
|
|
338
|
+
forceUpdate({});
|
|
339
|
+
});
|
|
340
|
+
ctx.setAction("scrollDown", () => {
|
|
341
|
+
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
342
|
+
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
343
|
+
const newScrollOffset = Math.min(currentMaxScrollOffset, currentScrollOffset + 1);
|
|
344
|
+
setScrollOffset(newScrollOffset);
|
|
345
|
+
forceUpdate({});
|
|
346
|
+
});
|
|
347
|
+
if (sortable) {
|
|
348
|
+
ctx.setAction("toggleSort", () => {
|
|
349
|
+
const nextSort = sortOrder === "none" ? "asc" : sortOrder === "asc" ? "desc" : "none";
|
|
350
|
+
const currentSelectedItem = displayItems[selectedIndexRef.current];
|
|
351
|
+
setSortOrder(nextSort);
|
|
352
|
+
const newSortedItems = nextSort !== "none" ? [...items].sort((a, b) => {
|
|
353
|
+
const titleA = titleGetter(a).toLowerCase();
|
|
354
|
+
const titleB = titleGetter(b).toLowerCase();
|
|
355
|
+
if (nextSort === "asc") {
|
|
356
|
+
return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
|
|
191
357
|
} else {
|
|
192
|
-
|
|
358
|
+
return titleA > titleB ? -1 : titleA < titleB ? 1 : 0;
|
|
193
359
|
}
|
|
360
|
+
}) : items;
|
|
361
|
+
const newIndex = newSortedItems.findIndex((item) => item === currentSelectedItem);
|
|
362
|
+
if (newIndex !== -1) {
|
|
363
|
+
selectedIndexRef.current = newIndex;
|
|
364
|
+
setScrollOffset(newIndex);
|
|
365
|
+
} else {
|
|
366
|
+
selectedIndexRef.current = 0;
|
|
367
|
+
setScrollOffset(0);
|
|
194
368
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
369
|
+
forceUpdate({});
|
|
370
|
+
});
|
|
371
|
+
const defaultHighlightStyle = {
|
|
372
|
+
color: "black",
|
|
373
|
+
backgroundColor: "green",
|
|
374
|
+
bold: true
|
|
375
|
+
};
|
|
376
|
+
const highlightStyle = { ...defaultHighlightStyle, ...sortHighlightStyle };
|
|
377
|
+
const sortCaption = () => {
|
|
378
|
+
if (sortOrder === "none") {
|
|
379
|
+
return h2(import_ink2.Text, {}, "s to toggle sort");
|
|
198
380
|
} else {
|
|
199
|
-
|
|
381
|
+
const sortLabel = sortOrder === "asc" ? "ASC" : "DESC";
|
|
382
|
+
return h2(
|
|
383
|
+
import_ink2.Text,
|
|
384
|
+
{},
|
|
385
|
+
"s to toggle ",
|
|
386
|
+
h2(import_ink2.Text, { color: "white", bold: true }, "sort"),
|
|
387
|
+
" ",
|
|
388
|
+
h2(import_ink2.Text, highlightStyle, ` ${sortLabel} `)
|
|
389
|
+
);
|
|
200
390
|
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
391
|
+
};
|
|
392
|
+
ctx.setKeyBinding([
|
|
393
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
394
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
395
|
+
{
|
|
396
|
+
key: "s",
|
|
397
|
+
caption: sortCaption,
|
|
398
|
+
action: "toggleSort",
|
|
399
|
+
order: 5
|
|
400
|
+
}
|
|
401
|
+
]);
|
|
402
|
+
ctx.update();
|
|
205
403
|
} else {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Parse value (handle quotes)
|
|
212
|
-
*/
|
|
213
|
-
parseValue(value) {
|
|
214
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
215
|
-
return value.slice(1, -1);
|
|
404
|
+
ctx.setKeyBinding([
|
|
405
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
406
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
407
|
+
]);
|
|
216
408
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
|
|
409
|
+
}, [sortOrder, sortable]);
|
|
410
|
+
const selectedIndex = selectedIndexRef.current;
|
|
411
|
+
const defaultRenderItem = (item, isSelected, displayIndex, actualIndex) => {
|
|
412
|
+
const isFirstVisible = displayIndex === 0;
|
|
413
|
+
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
414
|
+
let arrowPrefix = "";
|
|
415
|
+
let selectionPrefix = "";
|
|
416
|
+
if (isFirstVisible && canScrollUp) {
|
|
417
|
+
arrowPrefix = "\u2191 ";
|
|
418
|
+
} else if (isLastVisible && canScrollDown) {
|
|
419
|
+
arrowPrefix = "\u2193 ";
|
|
226
420
|
} else {
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
this.args[resolvedKey.toLowerCase()] = value;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Check for conflicts (short + long form of same option)
|
|
233
|
-
*/
|
|
234
|
-
checkConflicts() {
|
|
235
|
-
const conflicts = [];
|
|
236
|
-
for (const [shortKey, longKey] of Object.entries(this.aliases)) {
|
|
237
|
-
const hasShort = this.args[shortKey] !== void 0;
|
|
238
|
-
const hasLong = this.args[longKey] !== void 0;
|
|
239
|
-
if (hasShort && hasLong) {
|
|
240
|
-
conflicts.push(`Both -${shortKey} and --${longKey} specified`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (conflicts.length > 0) {
|
|
244
|
-
throw new Error(`Argument conflicts: ${conflicts.join(", ")}`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Get a value with precedence order
|
|
249
|
-
*/
|
|
250
|
-
get(key) {
|
|
251
|
-
const resolvedKey = this.aliases[key] || key;
|
|
252
|
-
this.usedKeys.add(resolvedKey);
|
|
253
|
-
if (this.overrides[resolvedKey] !== void 0) {
|
|
254
|
-
return this.overrides[resolvedKey];
|
|
255
|
-
}
|
|
256
|
-
const lcKey = resolvedKey.toLowerCase();
|
|
257
|
-
const lcKeyWithEnv = `${lcKey}${this.env ? `_${this.env.toLowerCase()}` : ""}`;
|
|
258
|
-
if (this.env && this.args[lcKeyWithEnv] !== void 0) {
|
|
259
|
-
return this.args[lcKeyWithEnv];
|
|
260
|
-
} else if (this.args[lcKey] !== void 0) {
|
|
261
|
-
return this.args[lcKey];
|
|
421
|
+
arrowPrefix = " ";
|
|
262
422
|
}
|
|
263
|
-
if (
|
|
264
|
-
|
|
423
|
+
if (isSelected) {
|
|
424
|
+
selectionPrefix = selectionMarker;
|
|
425
|
+
} else {
|
|
426
|
+
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
265
427
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
(
|
|
428
|
+
return h2(
|
|
429
|
+
import_ink2.Box,
|
|
430
|
+
{ flexDirection: "row" },
|
|
431
|
+
// Arrow (clickable if functional, not highlighted)
|
|
432
|
+
h2(import_ink2.Text, {
|
|
433
|
+
key: `arrow-${actualIndex}`,
|
|
434
|
+
color: "white"
|
|
435
|
+
}, arrowPrefix),
|
|
436
|
+
// Selection marker space (always same width, not highlighted)
|
|
437
|
+
h2(import_ink2.Text, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
438
|
+
// Item name (highlighted if selected)
|
|
439
|
+
h2(import_ink2.Text, {
|
|
440
|
+
key: `name-${actualIndex}`,
|
|
441
|
+
color: isSelected ? "black" : "white",
|
|
442
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
443
|
+
bold: isSelected
|
|
444
|
+
}, item.name)
|
|
270
445
|
);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
446
|
+
};
|
|
447
|
+
const itemRenderer = renderItem || defaultRenderItem;
|
|
448
|
+
return h2(
|
|
449
|
+
import_ink2.Box,
|
|
450
|
+
{ flexDirection: "column" },
|
|
451
|
+
...visibleItems.map((item, displayIndex) => {
|
|
452
|
+
const actualIndex = clampedScrollOffset + displayIndex;
|
|
453
|
+
const isSelected = actualIndex === selectedIndex;
|
|
454
|
+
if (renderItem) {
|
|
455
|
+
const isFirstVisible = displayIndex === 0;
|
|
456
|
+
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
457
|
+
let arrowPrefix = "";
|
|
458
|
+
let selectionPrefix = "";
|
|
459
|
+
if (isFirstVisible && canScrollUp) {
|
|
460
|
+
arrowPrefix = "\u2191 ";
|
|
461
|
+
} else if (isLastVisible && canScrollDown) {
|
|
462
|
+
arrowPrefix = "\u2193 ";
|
|
463
|
+
} else {
|
|
464
|
+
arrowPrefix = " ";
|
|
465
|
+
}
|
|
466
|
+
if (isSelected) {
|
|
467
|
+
selectionPrefix = selectionMarker;
|
|
468
|
+
} else {
|
|
469
|
+
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
470
|
+
}
|
|
471
|
+
return h2(ScreenRow, {
|
|
472
|
+
key: `item-${actualIndex}`,
|
|
473
|
+
children: h2(
|
|
474
|
+
import_ink2.Box,
|
|
475
|
+
{ flexDirection: "row" },
|
|
476
|
+
// Arrow (clickable if functional, not highlighted)
|
|
477
|
+
h2(import_ink2.Text, {
|
|
478
|
+
key: `arrow-${actualIndex}`,
|
|
479
|
+
color: "white"
|
|
480
|
+
}, arrowPrefix),
|
|
481
|
+
// Selection marker space (always same width, not highlighted)
|
|
482
|
+
h2(import_ink2.Text, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
483
|
+
// Custom rendered content
|
|
484
|
+
renderItem(item, isSelected, displayIndex)
|
|
485
|
+
)
|
|
486
|
+
});
|
|
487
|
+
} else {
|
|
488
|
+
return h2(ScreenRow, {
|
|
489
|
+
key: `item-${actualIndex}`,
|
|
490
|
+
children: itemRenderer(item, isSelected, displayIndex, actualIndex)
|
|
491
|
+
});
|
|
317
492
|
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
493
|
+
})
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
var import_react2, import_ink2, h2;
|
|
497
|
+
var init_list_components = __esm({
|
|
498
|
+
"src/screen/list-components.ts"() {
|
|
499
|
+
"use strict";
|
|
500
|
+
import_react2 = __toESM(require("react"), 1);
|
|
501
|
+
import_ink2 = require("ink");
|
|
502
|
+
init_components();
|
|
503
|
+
h2 = import_react2.createElement;
|
|
329
504
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// src/screen/screens.ts
|
|
508
|
+
function groupKeyBindings(bindings) {
|
|
509
|
+
const groups = {};
|
|
510
|
+
const enabledBindings = bindings.filter((b) => b.enabled !== false);
|
|
511
|
+
enabledBindings.forEach((binding) => {
|
|
512
|
+
const caption = typeof binding.caption === "string" ? binding.caption : "";
|
|
513
|
+
if (!groups[caption]) {
|
|
514
|
+
groups[caption] = {
|
|
515
|
+
keys: [],
|
|
516
|
+
caption,
|
|
517
|
+
order: binding.order || 999
|
|
518
|
+
};
|
|
342
519
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
520
|
+
groups[caption].keys.push(binding.key);
|
|
521
|
+
});
|
|
522
|
+
return Object.values(groups);
|
|
523
|
+
}
|
|
524
|
+
function formatKeyBindings(bindings, mode = "long") {
|
|
525
|
+
const resolvedBindings = bindings.map((binding) => {
|
|
526
|
+
let resolvedCaption = binding.caption;
|
|
527
|
+
if (typeof binding.caption === "function") {
|
|
528
|
+
resolvedCaption = binding.caption();
|
|
348
529
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
530
|
+
return {
|
|
531
|
+
...binding,
|
|
532
|
+
resolvedCaption
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
const groups = groupKeyBindings(resolvedBindings.map((b) => ({
|
|
536
|
+
...b,
|
|
537
|
+
caption: typeof b.resolvedCaption === "string" ? b.resolvedCaption : ""
|
|
538
|
+
})));
|
|
539
|
+
groups.sort((a, b) => a.order - b.order);
|
|
540
|
+
const items = [];
|
|
541
|
+
groups.forEach((group) => {
|
|
542
|
+
const bindingWithCustom = resolvedBindings.find(
|
|
543
|
+
(b) => group.keys.includes(b.key) && typeof b.resolvedCaption !== "string"
|
|
544
|
+
);
|
|
545
|
+
if (bindingWithCustom && bindingWithCustom.resolvedCaption) {
|
|
546
|
+
items.push(bindingWithCustom.resolvedCaption);
|
|
547
|
+
} else {
|
|
548
|
+
const keyStr = formatKeys(group.keys);
|
|
549
|
+
if (mode === "long") {
|
|
550
|
+
items.push(`${keyStr} to ${group.caption}`);
|
|
551
|
+
} else {
|
|
552
|
+
items.push(keyStr);
|
|
354
553
|
}
|
|
355
554
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
555
|
+
});
|
|
556
|
+
return items;
|
|
557
|
+
}
|
|
558
|
+
function formatKeys(keys) {
|
|
559
|
+
const keyMap = {
|
|
560
|
+
"escape": "esc",
|
|
561
|
+
"leftArrow": "\u2190",
|
|
562
|
+
"rightArrow": "\u2192",
|
|
563
|
+
"upArrow": "\u2191",
|
|
564
|
+
"downArrow": "\u2193",
|
|
565
|
+
"return": "enter"
|
|
566
|
+
};
|
|
567
|
+
return keys.map((k) => keyMap[k] || k).join("/");
|
|
568
|
+
}
|
|
569
|
+
async function showScreen(config2) {
|
|
570
|
+
const {
|
|
571
|
+
title,
|
|
572
|
+
onRender,
|
|
573
|
+
parentData = {}
|
|
574
|
+
} = config2;
|
|
575
|
+
return new Promise((resolve2) => {
|
|
576
|
+
let instance2;
|
|
577
|
+
const keyBindings = [];
|
|
578
|
+
const actions = {};
|
|
579
|
+
const customFooterItems = [];
|
|
580
|
+
let renderResult = null;
|
|
581
|
+
let initialized = false;
|
|
582
|
+
const Screen = () => {
|
|
583
|
+
const [updateCounter, setUpdateCounter] = (0, import_react3.useState)(0);
|
|
584
|
+
if (!initialized) {
|
|
585
|
+
const defaultBindings = [
|
|
586
|
+
{ key: "escape", caption: "go back", action: "back", protected: true, order: 1 },
|
|
587
|
+
{ key: "leftArrow", caption: "go back", action: "back", protected: false, order: 1 }
|
|
588
|
+
// Note: 'select' is not a default - components add it if needed
|
|
589
|
+
];
|
|
590
|
+
defaultBindings.forEach((binding) => {
|
|
591
|
+
keyBindings.push(binding);
|
|
592
|
+
});
|
|
593
|
+
actions.back = () => {
|
|
594
|
+
cleanup(null);
|
|
595
|
+
};
|
|
596
|
+
initialized = true;
|
|
597
|
+
}
|
|
598
|
+
const context = {
|
|
599
|
+
setAction: (actionName, handlerFn) => {
|
|
600
|
+
actions[actionName] = handlerFn;
|
|
601
|
+
},
|
|
602
|
+
setKeyBinding: (bindingOrBindings) => {
|
|
603
|
+
const bindingsToSet = Array.isArray(bindingOrBindings) ? bindingOrBindings : [bindingOrBindings];
|
|
604
|
+
bindingsToSet.forEach((binding) => {
|
|
605
|
+
const existingIndex = keyBindings.findIndex((b) => b.key === binding.key);
|
|
606
|
+
if (existingIndex >= 0) {
|
|
607
|
+
const existing = keyBindings[existingIndex];
|
|
608
|
+
if (existing.protected) {
|
|
609
|
+
console.warn(`Cannot override protected key: ${binding.key}`);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
keyBindings[existingIndex] = {
|
|
613
|
+
...existing,
|
|
614
|
+
...binding,
|
|
615
|
+
order: binding.order !== void 0 ? binding.order : existing.order,
|
|
616
|
+
enabled: binding.enabled !== void 0 ? binding.enabled : existing.enabled !== void 0 ? existing.enabled : true
|
|
617
|
+
};
|
|
618
|
+
} else {
|
|
619
|
+
keyBindings.push({
|
|
620
|
+
protected: false,
|
|
621
|
+
order: 999,
|
|
622
|
+
enabled: true,
|
|
623
|
+
...binding
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
},
|
|
628
|
+
updateKeyBinding: (keyName, updates) => {
|
|
629
|
+
const index = keyBindings.findIndex((b) => b.key === keyName);
|
|
630
|
+
if (index >= 0) {
|
|
631
|
+
keyBindings[index] = {
|
|
632
|
+
...keyBindings[index],
|
|
633
|
+
...updates
|
|
634
|
+
};
|
|
366
635
|
}
|
|
367
|
-
}
|
|
636
|
+
},
|
|
637
|
+
removeKeyBinding: (keyName) => {
|
|
638
|
+
const index = keyBindings.findIndex((b) => b.key === keyName);
|
|
639
|
+
if (index >= 0) {
|
|
640
|
+
if (keyBindings[index].protected) {
|
|
641
|
+
console.warn(`Cannot remove protected key: ${keyName}`);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
keyBindings.splice(index, 1);
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
addFooter: (item) => {
|
|
648
|
+
customFooterItems.push(item);
|
|
649
|
+
},
|
|
650
|
+
clearFooter: () => {
|
|
651
|
+
customFooterItems.length = 0;
|
|
652
|
+
},
|
|
653
|
+
setFooter: (items) => {
|
|
654
|
+
customFooterItems.length = 0;
|
|
655
|
+
const itemsArray = Array.isArray(items) ? items : [items];
|
|
656
|
+
customFooterItems.push(...itemsArray);
|
|
657
|
+
},
|
|
658
|
+
update: () => {
|
|
659
|
+
setUpdateCounter((c) => c + 1);
|
|
660
|
+
},
|
|
661
|
+
goBack: () => {
|
|
662
|
+
if (actions.back) {
|
|
663
|
+
actions.back();
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
close: (result) => {
|
|
667
|
+
cleanup(result);
|
|
668
|
+
},
|
|
669
|
+
parentData
|
|
670
|
+
};
|
|
671
|
+
if (!renderResult) {
|
|
672
|
+
renderResult = onRender(context);
|
|
368
673
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Load configuration files
|
|
376
|
-
*/
|
|
377
|
-
loadConfigFiles() {
|
|
378
|
-
this.configsLoaded = [];
|
|
379
|
-
this.configValues = {};
|
|
380
|
-
const _defaultConfigExtension = this.get("defaultConfigExtension") || "js";
|
|
381
|
-
const optConfigFiles = this.get("config") || this.get("configs") || "";
|
|
382
|
-
const configFiles = optConfigFiles ? optConfigFiles.split(/,\s*/) : [];
|
|
383
|
-
const optConfigFilePath = this.get("configPath");
|
|
384
|
-
if (configFiles.length > 0) {
|
|
385
|
-
for (const cfgFile of configFiles) {
|
|
386
|
-
let notLoaded = false;
|
|
387
|
-
let notLoadedEnvSpecific = false;
|
|
388
|
-
const cfgFileWithPath = this.resolveFileWithPath(optConfigFilePath, cfgFile);
|
|
389
|
-
try {
|
|
390
|
-
const cfgContents = this.requireConfigFile(cfgFileWithPath);
|
|
391
|
-
this.configValues = { ...this.configValues, ...cfgContents };
|
|
392
|
-
this.configsLoaded.push(cfgFileWithPath);
|
|
393
|
-
} catch {
|
|
394
|
-
notLoaded = true;
|
|
674
|
+
(0, import_ink3.useInput)((input, key) => {
|
|
675
|
+
if (key.ctrl && input === "c") {
|
|
676
|
+
cleanup(null);
|
|
677
|
+
process.exit(0);
|
|
678
|
+
return;
|
|
395
679
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
680
|
+
let matchedBinding = null;
|
|
681
|
+
for (const binding of keyBindings) {
|
|
682
|
+
let keyMatches = false;
|
|
683
|
+
if (key[binding.key]) {
|
|
684
|
+
keyMatches = true;
|
|
685
|
+
} else if (input === binding.key) {
|
|
686
|
+
keyMatches = true;
|
|
687
|
+
}
|
|
688
|
+
if (keyMatches) {
|
|
689
|
+
if (binding.enabled === false) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
if (binding.condition && !binding.condition(context)) {
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
matchedBinding = binding;
|
|
696
|
+
break;
|
|
408
697
|
}
|
|
409
|
-
} else {
|
|
410
|
-
notLoadedEnvSpecific = true;
|
|
411
698
|
}
|
|
412
|
-
if (
|
|
413
|
-
|
|
699
|
+
if (matchedBinding && actions[matchedBinding.action]) {
|
|
700
|
+
const actionResult = actions[matchedBinding.action]({
|
|
701
|
+
input,
|
|
702
|
+
key,
|
|
703
|
+
binding: matchedBinding
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
const footerLines = [];
|
|
708
|
+
const bindingItems = formatKeyBindings(keyBindings, "long");
|
|
709
|
+
if (bindingItems.length > 0) {
|
|
710
|
+
const bindingsLine = [];
|
|
711
|
+
bindingItems.forEach((item, idx) => {
|
|
712
|
+
if (idx > 0) {
|
|
713
|
+
bindingsLine.push(", ");
|
|
714
|
+
}
|
|
715
|
+
bindingsLine.push(item);
|
|
716
|
+
});
|
|
717
|
+
const allStrings = bindingItems.every((item) => typeof item === "string");
|
|
718
|
+
if (allStrings) {
|
|
719
|
+
footerLines.push(bindingsLine.join(""));
|
|
720
|
+
} else {
|
|
721
|
+
const wrappedBindingsLine = bindingsLine.map(
|
|
722
|
+
(item) => typeof item === "string" ? (0, import_react3.createElement)(import_ink3.Text, {}, item) : item
|
|
723
|
+
);
|
|
724
|
+
footerLines.push(wrappedBindingsLine);
|
|
414
725
|
}
|
|
415
726
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
splitPath(filePath) {
|
|
435
|
-
const basePathWithName = (0, import_path.join)((0, import_path.dirname)(filePath), (0, import_path.basename)(filePath, (0, import_path.extname)(filePath)));
|
|
436
|
-
const extension = (0, import_path.extname)(filePath).slice(1);
|
|
437
|
-
return { basePathWithName, extension };
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Require a configuration file (supports .js and .json)
|
|
441
|
-
*/
|
|
442
|
-
requireConfigFile(filePath) {
|
|
443
|
-
if (!(0, import_fs.existsSync)(filePath)) {
|
|
444
|
-
throw new Error(`Config file not found: ${filePath}`);
|
|
445
|
-
}
|
|
446
|
-
const ext = (0, import_path.extname)(filePath).toLowerCase();
|
|
447
|
-
if (ext === ".json") {
|
|
448
|
-
const content = (0, import_fs.readFileSync)(filePath, "utf8");
|
|
449
|
-
return JSON.parse(content);
|
|
450
|
-
} else if (ext === ".js") {
|
|
451
|
-
try {
|
|
452
|
-
delete require.cache[require.resolve(filePath)];
|
|
453
|
-
return require(filePath);
|
|
454
|
-
} catch (error) {
|
|
455
|
-
throw new Error(`Failed to load JS config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
456
|
-
}
|
|
457
|
-
} else {
|
|
458
|
-
throw new Error(`Unsupported file extension: ${ext}`);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* Get all parsed data
|
|
463
|
-
*/
|
|
464
|
-
getParsed() {
|
|
465
|
-
return {
|
|
466
|
-
command: this.commands[0] || "",
|
|
467
|
-
flags: { ...this.flags },
|
|
468
|
-
options: { ...this.options },
|
|
469
|
-
usedKeys: Array.from(this.usedKeys)
|
|
727
|
+
customFooterItems.forEach((item) => {
|
|
728
|
+
if (typeof item === "string") {
|
|
729
|
+
footerLines.push(item);
|
|
730
|
+
} else {
|
|
731
|
+
footerLines.push(item);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
return (0, import_react3.createElement)(
|
|
735
|
+
ScreenContainer,
|
|
736
|
+
{},
|
|
737
|
+
(0, import_react3.createElement)(ScreenTitle, { text: title }),
|
|
738
|
+
(0, import_react3.createElement)(ScreenDivider),
|
|
739
|
+
(0, import_react3.createElement)(ScreenRow, {}, (0, import_react3.createElement)(import_ink3.Text, {}, " ")),
|
|
740
|
+
renderResult,
|
|
741
|
+
(0, import_react3.createElement)(ScreenRow, {}, (0, import_react3.createElement)(import_ink3.Text, {}, " ")),
|
|
742
|
+
(0, import_react3.createElement)(ScreenDivider),
|
|
743
|
+
(0, import_react3.createElement)(ScreenFooter, { lines: footerLines })
|
|
744
|
+
);
|
|
470
745
|
};
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const sortedArr = arr.sort(
|
|
478
|
-
(a, b) => a.length < b.length ? 1 : a.length > b.length ? -1 : 0
|
|
479
|
-
);
|
|
480
|
-
this.prefixes = sortedArr.map((el) => el.toLowerCase());
|
|
481
|
-
const args = process.argv.slice(2);
|
|
482
|
-
this.parseArgs(args);
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
var instance = null;
|
|
486
|
-
function getArgsInstance() {
|
|
487
|
-
return instance;
|
|
746
|
+
const cleanup = (result) => {
|
|
747
|
+
if (instance2) instance2.unmount();
|
|
748
|
+
setTimeout(() => resolve2(result), 50);
|
|
749
|
+
};
|
|
750
|
+
instance2 = (0, import_ink3.render)((0, import_react3.createElement)(Screen));
|
|
751
|
+
});
|
|
488
752
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
var joiEdateType = (value, helpers) => {
|
|
509
|
-
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(value)) {
|
|
510
|
-
const testDate = new Date(value);
|
|
511
|
-
if (!isNaN(testDate.getTime())) {
|
|
512
|
-
return value;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
if (value instanceof Date) {
|
|
516
|
-
return value.toISOString();
|
|
517
|
-
}
|
|
518
|
-
if (typeof value !== "string") {
|
|
519
|
-
value = String(value);
|
|
520
|
-
}
|
|
521
|
-
if (value.toLowerCase() === "now") {
|
|
522
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
523
|
-
}
|
|
524
|
-
const referenceRegex = /^@(\w+)([+-]\d+[smhdwy])$/i;
|
|
525
|
-
const referenceMatch = value.match(referenceRegex);
|
|
526
|
-
if (referenceMatch) {
|
|
527
|
-
const [, paramName, relativeExpr] = referenceMatch;
|
|
528
|
-
const context = helpers.prefs?.context;
|
|
529
|
-
if (!context || !context.params) {
|
|
530
|
-
throw new ParamError(`Cannot resolve cross-parameter reference @${paramName}: context not available. Ensure parameters are processed with proper context.`);
|
|
531
|
-
}
|
|
532
|
-
const referencedValue = context.params[paramName];
|
|
533
|
-
if (referencedValue === void 0 || referencedValue === null) {
|
|
534
|
-
throw new ParamError(`Cannot resolve @${paramName}: parameter "${paramName}" is not defined or has no value. Parameters are evaluated left-to-right.`);
|
|
535
|
-
}
|
|
536
|
-
let referenceDate;
|
|
537
|
-
if (referencedValue instanceof Date) {
|
|
538
|
-
referenceDate = referencedValue;
|
|
539
|
-
} else if (typeof referencedValue === "string") {
|
|
540
|
-
referenceDate = new Date(referencedValue);
|
|
541
|
-
if (isNaN(referenceDate.getTime())) {
|
|
542
|
-
throw new ParamError(`Referenced parameter @${paramName} has invalid date value: ${referencedValue}`);
|
|
753
|
+
async function showListScreen(config2) {
|
|
754
|
+
const { title, items, onSelect, onEscape, parentData, initialSelectedIndex = 0, renderItem, getTitle, sortable, maxHeight, sortHighlightStyle, selectionMarker } = config2;
|
|
755
|
+
return showScreen({
|
|
756
|
+
title,
|
|
757
|
+
parentData,
|
|
758
|
+
onRender: (ctx) => {
|
|
759
|
+
const selectedIndexRef = { current: initialSelectedIndex };
|
|
760
|
+
ctx.setAction("select", () => {
|
|
761
|
+
const selected = items[selectedIndexRef.current];
|
|
762
|
+
if (onSelect) {
|
|
763
|
+
const result = onSelect(selected.value, selectedIndexRef.current);
|
|
764
|
+
ctx.close(result);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
if (onEscape) {
|
|
768
|
+
ctx.setAction("back", () => {
|
|
769
|
+
const result = onEscape(selectedIndexRef.current);
|
|
770
|
+
ctx.close(result);
|
|
771
|
+
});
|
|
543
772
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
const relativeMatch2 = relativeExpr.match(/^([+-])(\d+)([smhdwy])$/i);
|
|
548
|
-
if (!relativeMatch2) {
|
|
549
|
-
throw new ParamError(`Invalid relative time expression in @${paramName}${relativeExpr}`);
|
|
550
|
-
}
|
|
551
|
-
const [, sign, amount, unit] = relativeMatch2;
|
|
552
|
-
const offset = calculateTimeOffset(parseInt(amount, 10), unit, sign);
|
|
553
|
-
const resultDate = new Date(referenceDate.getTime() + offset);
|
|
554
|
-
return resultDate.toISOString();
|
|
555
|
-
}
|
|
556
|
-
const relativeTimeRegex = /^([+-])(\d+)([smhdwy])$/i;
|
|
557
|
-
const relativeMatch = value.match(relativeTimeRegex);
|
|
558
|
-
if (relativeMatch) {
|
|
559
|
-
const [, sign, amount, unit] = relativeMatch;
|
|
560
|
-
const numAmount = parseInt(amount, 10);
|
|
561
|
-
if (isNaN(numAmount)) {
|
|
562
|
-
throw new ParamError(`Invalid relative time amount: ${amount}`);
|
|
773
|
+
ctx.setKeyBinding({ key: "return", caption: "select", action: "select", order: 2 });
|
|
774
|
+
return (0, import_react3.createElement)(ListComponent, { items, ctx, selectedIndexRef, renderItem, getTitle, sortable, maxHeight, sortHighlightStyle, selectionMarker });
|
|
563
775
|
}
|
|
564
|
-
|
|
565
|
-
const resultDate = new Date(Date.now() + offset);
|
|
566
|
-
return resultDate.toISOString();
|
|
567
|
-
}
|
|
568
|
-
const parsedDate = new Date(value);
|
|
569
|
-
if (isNaN(parsedDate.getTime())) {
|
|
570
|
-
throw new ParamError(`Invalid date format: ${value}. Expected a valid date string, "now", relative time expression (e.g., "-2h", "+1d"), or cross-parameter reference (e.g., "@startTime+2h")`);
|
|
571
|
-
}
|
|
572
|
-
return parsedDate.toISOString();
|
|
573
|
-
};
|
|
574
|
-
function calculateTimeOffset(amount, unit, sign) {
|
|
575
|
-
let multiplier = 1;
|
|
576
|
-
switch (unit.toLowerCase()) {
|
|
577
|
-
case "s":
|
|
578
|
-
multiplier = 1e3;
|
|
579
|
-
break;
|
|
580
|
-
case "m":
|
|
581
|
-
multiplier = 60 * 1e3;
|
|
582
|
-
break;
|
|
583
|
-
case "h":
|
|
584
|
-
multiplier = 60 * 60 * 1e3;
|
|
585
|
-
break;
|
|
586
|
-
case "d":
|
|
587
|
-
multiplier = 24 * 60 * 60 * 1e3;
|
|
588
|
-
break;
|
|
589
|
-
case "w":
|
|
590
|
-
multiplier = 7 * 24 * 60 * 60 * 1e3;
|
|
591
|
-
break;
|
|
592
|
-
case "y":
|
|
593
|
-
multiplier = 365 * 24 * 60 * 60 * 1e3;
|
|
594
|
-
break;
|
|
595
|
-
default:
|
|
596
|
-
throw new ParamError(`Invalid time unit: ${unit}. Supported units: s, m, h, d, w, y`);
|
|
597
|
-
}
|
|
598
|
-
return sign === "+" ? amount * multiplier : -amount * multiplier;
|
|
776
|
+
});
|
|
599
777
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
778
|
+
async function showMultiColumnListScreen(config2) {
|
|
779
|
+
const { title, items, onSelect, onEscape, parentData, initialSelectedIndex = 0 } = config2;
|
|
780
|
+
return showScreen({
|
|
781
|
+
title,
|
|
782
|
+
parentData,
|
|
783
|
+
onRender: (ctx) => {
|
|
784
|
+
const selectedIndexRef = { current: initialSelectedIndex };
|
|
785
|
+
ctx.setAction("select", () => {
|
|
786
|
+
const selected = items[selectedIndexRef.current];
|
|
787
|
+
if (onSelect) {
|
|
788
|
+
const result = onSelect(selected, selectedIndexRef.current);
|
|
789
|
+
ctx.close(result);
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
if (onEscape) {
|
|
793
|
+
ctx.setAction("back", () => {
|
|
794
|
+
const result = onEscape(selectedIndexRef.current);
|
|
795
|
+
ctx.close(result);
|
|
796
|
+
});
|
|
609
797
|
}
|
|
610
|
-
return
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
798
|
+
ctx.setKeyBinding({ key: "return", caption: "select", action: "select", order: 2 });
|
|
799
|
+
return (0, import_react3.createElement)(MultiColumnListComponent, { items, ctx, selectedIndexRef });
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
async function showMultiColumnListWithPreviewScreen(config2) {
|
|
804
|
+
const { title, items, getPreviewContent, onSelect, onEscape, parentData, initialSelectedIndex = 0 } = config2;
|
|
805
|
+
return showScreen({
|
|
806
|
+
title,
|
|
807
|
+
parentData,
|
|
808
|
+
onRender: (ctx) => {
|
|
809
|
+
const selectedIndexRef = { current: initialSelectedIndex };
|
|
810
|
+
ctx.setAction("select", () => {
|
|
811
|
+
const selected = items[selectedIndexRef.current];
|
|
812
|
+
if (onSelect) {
|
|
813
|
+
const result = onSelect(selected, selectedIndexRef.current);
|
|
814
|
+
ctx.close(result);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
if (onEscape) {
|
|
818
|
+
ctx.setAction("back", () => {
|
|
819
|
+
const result = onEscape(selectedIndexRef.current);
|
|
820
|
+
ctx.close(result);
|
|
821
|
+
});
|
|
615
822
|
}
|
|
616
|
-
return
|
|
617
|
-
|
|
618
|
-
return el;
|
|
619
|
-
} else {
|
|
620
|
-
throw new ParamError(`unknown type "${type}" for array elements`);
|
|
823
|
+
ctx.setKeyBinding({ key: "return", caption: "select", action: "select", order: 2 });
|
|
824
|
+
return (0, import_react3.createElement)(MultiColumnListWithPreviewComponent, { items, getPreviewContent, ctx, selectedIndexRef });
|
|
621
825
|
}
|
|
622
826
|
});
|
|
623
|
-
|
|
624
|
-
|
|
827
|
+
}
|
|
828
|
+
var import_react3, import_ink3, showMenuScreen, showWordGridScreen;
|
|
829
|
+
var init_screens = __esm({
|
|
830
|
+
"src/screen/screens.ts"() {
|
|
831
|
+
"use strict";
|
|
832
|
+
import_react3 = require("react");
|
|
833
|
+
import_ink3 = require("ink");
|
|
834
|
+
init_components();
|
|
835
|
+
init_list_components();
|
|
836
|
+
showMenuScreen = showListScreen;
|
|
837
|
+
showWordGridScreen = showMultiColumnListScreen;
|
|
838
|
+
}
|
|
839
|
+
});
|
|
625
840
|
|
|
626
|
-
// src/
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
841
|
+
// src/screen/ui-elements.ts
|
|
842
|
+
function ListItem({
|
|
843
|
+
children,
|
|
844
|
+
isSelected = false,
|
|
845
|
+
color = "white",
|
|
846
|
+
backgroundColor,
|
|
847
|
+
bold = false,
|
|
848
|
+
dimColor = false
|
|
849
|
+
}) {
|
|
850
|
+
return (0, import_react4.createElement)(
|
|
851
|
+
import_ink4.Box,
|
|
852
|
+
{},
|
|
853
|
+
(0, import_react4.createElement)(import_ink4.Text, {
|
|
854
|
+
color: isSelected ? backgroundColor || "green" : color,
|
|
855
|
+
backgroundColor: isSelected ? color : backgroundColor,
|
|
856
|
+
bold: isSelected || bold,
|
|
857
|
+
dimColor: !isSelected && dimColor
|
|
858
|
+
}, children)
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
function TextBlock({
|
|
862
|
+
text,
|
|
863
|
+
color = "white",
|
|
864
|
+
dimmed = false,
|
|
865
|
+
bold = false,
|
|
866
|
+
maxWidth
|
|
867
|
+
}) {
|
|
868
|
+
return (0, import_react4.createElement)(
|
|
869
|
+
import_ink4.Box,
|
|
870
|
+
{},
|
|
871
|
+
(0, import_react4.createElement)(import_ink4.Text, {
|
|
872
|
+
color,
|
|
873
|
+
dimColor: dimmed,
|
|
874
|
+
bold
|
|
875
|
+
}, text)
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
function Divider({ character = "\u2500", width = 80 }) {
|
|
879
|
+
return (0, import_react4.createElement)(
|
|
880
|
+
import_ink4.Box,
|
|
881
|
+
{ marginY: 1 },
|
|
882
|
+
(0, import_react4.createElement)(import_ink4.Text, { dimColor: true }, character.repeat(width))
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
function GridCell({
|
|
886
|
+
children,
|
|
887
|
+
width,
|
|
888
|
+
color = "white",
|
|
889
|
+
backgroundColor,
|
|
890
|
+
bold = false,
|
|
891
|
+
dimColor = false,
|
|
892
|
+
align = "left"
|
|
893
|
+
}) {
|
|
894
|
+
return (0, import_react4.createElement)(
|
|
895
|
+
import_ink4.Box,
|
|
896
|
+
{ width },
|
|
897
|
+
(0, import_react4.createElement)(import_ink4.Text, {
|
|
898
|
+
color,
|
|
899
|
+
backgroundColor,
|
|
900
|
+
bold,
|
|
901
|
+
dimColor,
|
|
902
|
+
textAlign: align
|
|
903
|
+
}, children)
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
function InputField({ prompt, value, onChange, onSubmit }) {
|
|
907
|
+
return (0, import_react4.createElement)(
|
|
908
|
+
import_ink4.Box,
|
|
909
|
+
{ flexDirection: "column" },
|
|
910
|
+
(0, import_react4.createElement)(import_ink4.Text, {}, prompt),
|
|
911
|
+
(0, import_react4.createElement)(
|
|
912
|
+
import_ink4.Box,
|
|
913
|
+
{ marginTop: 1 },
|
|
914
|
+
(0, import_react4.createElement)(import_ink4.Text, { color: "cyan" }, " > ", value, "_")
|
|
915
|
+
)
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
var import_react4, import_ink4;
|
|
919
|
+
var init_ui_elements = __esm({
|
|
920
|
+
"src/screen/ui-elements.ts"() {
|
|
921
|
+
"use strict";
|
|
922
|
+
import_react4 = require("react");
|
|
923
|
+
import_ink4 = require("ink");
|
|
638
924
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
type = definition;
|
|
651
|
-
} else if (import_joi.default.isSchema(definition.type)) {
|
|
652
|
-
type = definition.type;
|
|
653
|
-
} else if (typeof definition === "string") {
|
|
654
|
-
type = this.toJoi(definition);
|
|
655
|
-
} else if (typeof definition.type === "string") {
|
|
656
|
-
type = this.toJoi(definition.type);
|
|
657
|
-
} else if (!definition.type) {
|
|
658
|
-
type = import_joi.default.string();
|
|
659
|
-
} else {
|
|
660
|
-
type = import_joi.default.string();
|
|
661
|
-
}
|
|
662
|
-
if (!this.definitions[key]) {
|
|
663
|
-
this.definitions[key] = {};
|
|
664
|
-
}
|
|
665
|
-
this.definitions[key].type = type;
|
|
666
|
-
if (definition && definition.values) {
|
|
667
|
-
if (Array.isArray(definition.values)) {
|
|
668
|
-
this.definitions[key].values = definition.values;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
return this.definitions[key];
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
// src/screen/utils.ts
|
|
928
|
+
function buildBreadcrumb(parts) {
|
|
929
|
+
if (parts.length === 0) return "";
|
|
930
|
+
if (parts.length === 1) return parts[0];
|
|
931
|
+
return parts.slice(1).map((part) => `\u2190 ${part}`).join(" ");
|
|
932
|
+
}
|
|
933
|
+
function buildDetailBreadcrumb(path4, suffix = "") {
|
|
934
|
+
if (path4.length <= 1) {
|
|
935
|
+
return suffix ? `\u2190 ${suffix}` : path4[0] || "";
|
|
672
936
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
type = import_joi.default.string();
|
|
680
|
-
} else if (str.match(/^number|^integer|^int/i)) {
|
|
681
|
-
type = import_joi.default.number();
|
|
682
|
-
} else if (str.match(/^boolean|^bool/i)) {
|
|
683
|
-
type = import_joi.default.boolean();
|
|
684
|
-
} else if (str.match(/^date/i)) {
|
|
685
|
-
type = import_joi.default.custom(joiEdateType);
|
|
686
|
-
} else if (str.match(/^duration/i)) {
|
|
687
|
-
type = import_joi.default.string().isoDuration();
|
|
688
|
-
} else if (str.match(/^array/i)) {
|
|
689
|
-
let elementTypes = "string";
|
|
690
|
-
const tmp = str.match(/\((.*)\)/);
|
|
691
|
-
if (tmp && tmp[1].match(/string/i)) {
|
|
692
|
-
elementTypes = "string";
|
|
693
|
-
} else if (tmp && tmp[1].match(/number|integer|int/i)) {
|
|
694
|
-
elementTypes = "number";
|
|
695
|
-
} else if (tmp && tmp[1].match(/boolean|bool/i)) {
|
|
696
|
-
elementTypes = "boolean";
|
|
697
|
-
}
|
|
698
|
-
type = import_joi.default.custom(joiStringArrayType(elementTypes));
|
|
699
|
-
} else {
|
|
700
|
-
type = import_joi.default.string();
|
|
701
|
-
}
|
|
702
|
-
const regexForDefault = /\bdefault\s+([^\s]+)/;
|
|
703
|
-
const matchForDefault = str.match(regexForDefault);
|
|
704
|
-
if (matchForDefault) {
|
|
705
|
-
const defValObj = type.validate(matchForDefault[1]);
|
|
706
|
-
if (defValObj.error) {
|
|
707
|
-
throw new ParamError(`default value "${defValObj.value}" type mismatch`);
|
|
708
|
-
}
|
|
709
|
-
type = type.default(defValObj.value);
|
|
710
|
-
} else if (str.match(/required/)) {
|
|
711
|
-
type = type.required();
|
|
712
|
-
}
|
|
713
|
-
return type;
|
|
937
|
+
const breadcrumb = buildBreadcrumb(path4);
|
|
938
|
+
return suffix ? `${breadcrumb} ${suffix}` : breadcrumb;
|
|
939
|
+
}
|
|
940
|
+
var init_utils = __esm({
|
|
941
|
+
"src/screen/utils.ts"() {
|
|
942
|
+
"use strict";
|
|
714
943
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
// src/screen/footer-builder.ts
|
|
947
|
+
function buildFooter(config2 = {}) {
|
|
948
|
+
const {
|
|
949
|
+
navigation = null,
|
|
950
|
+
actions = null,
|
|
951
|
+
info = null,
|
|
952
|
+
escape = "Esc to go back",
|
|
953
|
+
custom = null
|
|
954
|
+
} = config2;
|
|
955
|
+
const lines = [];
|
|
956
|
+
const mainParts = [];
|
|
957
|
+
if (navigation) {
|
|
958
|
+
mainParts.push(navigation);
|
|
725
959
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
*/
|
|
729
|
-
get(key, definition) {
|
|
730
|
-
const def = this.assignDefinition(key, definition);
|
|
731
|
-
let valFromGetters = void 0;
|
|
732
|
-
if (def.volatile || true) {
|
|
733
|
-
valFromGetters = this.runAllRegisteredGetters(key);
|
|
734
|
-
}
|
|
735
|
-
const valFromArgs = this.args.get(key);
|
|
736
|
-
const valFromParams = this.params[key];
|
|
737
|
-
const res = valFromGetters ? this.validate(key, valFromGetters, def) : valFromArgs ? this.validate(key, valFromArgs, def) : this.validate(key, valFromParams, def);
|
|
738
|
-
if (res !== void 0 && def.values && !def.values.includes(res)) {
|
|
739
|
-
throw new ParamError(`key ${key} should be one of ${def.values}`);
|
|
740
|
-
}
|
|
741
|
-
return res;
|
|
960
|
+
if (actions) {
|
|
961
|
+
mainParts.push(actions);
|
|
742
962
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
*/
|
|
746
|
-
set(key, val, definition) {
|
|
747
|
-
if (val && val.type && val.value) {
|
|
748
|
-
definition = val;
|
|
749
|
-
val = val.value;
|
|
750
|
-
}
|
|
751
|
-
const def = this.assignDefinition(key, definition);
|
|
752
|
-
if (!this.runAllRegisteredSetters(key, val)) {
|
|
753
|
-
this.params[key] = val;
|
|
754
|
-
}
|
|
963
|
+
if (escape) {
|
|
964
|
+
mainParts.push(escape);
|
|
755
965
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
* Processes parameters left-to-right to support cross-parameter references
|
|
759
|
-
*/
|
|
760
|
-
getAll(defs) {
|
|
761
|
-
const res = {};
|
|
762
|
-
for (const [k, def] of Object.entries(defs)) {
|
|
763
|
-
const value = this.get(k, def);
|
|
764
|
-
res[k] = value;
|
|
765
|
-
if (value !== void 0) {
|
|
766
|
-
this.params[k] = value;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return res;
|
|
966
|
+
if (mainParts.length > 0) {
|
|
967
|
+
lines.push(mainParts.join(", "));
|
|
770
968
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
runAllRegisteredGetters(key) {
|
|
775
|
-
let val = null;
|
|
776
|
-
for (const getter of this.paramGetters) {
|
|
777
|
-
val = getter(key, this.definitions[key]);
|
|
778
|
-
if (val !== void 0) {
|
|
779
|
-
break;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
return val;
|
|
969
|
+
if (info) {
|
|
970
|
+
const infoLines = Array.isArray(info) ? info : [info];
|
|
971
|
+
lines.push(...infoLines);
|
|
783
972
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
973
|
+
if (custom) {
|
|
974
|
+
const customLines = Array.isArray(custom) ? custom : [custom];
|
|
975
|
+
lines.push(...customLines);
|
|
976
|
+
}
|
|
977
|
+
return lines;
|
|
978
|
+
}
|
|
979
|
+
function organizeFooterMessages(messages) {
|
|
980
|
+
if (!messages || messages.length === 0) {
|
|
981
|
+
return ["Esc to go back"];
|
|
982
|
+
}
|
|
983
|
+
const navigation = messages.filter((m) => m.includes("\u2191") || m.includes("\u2193") || m.includes("\u2190") || m.includes("\u2192"));
|
|
984
|
+
const actions = messages.filter((m) => m.includes("Enter") || m.includes("select") || m.includes("submit"));
|
|
985
|
+
const escape = messages.filter((m) => m.includes("Esc"));
|
|
986
|
+
const others = messages.filter(
|
|
987
|
+
(m) => !navigation.includes(m) && !actions.includes(m) && !escape.includes(m)
|
|
988
|
+
);
|
|
989
|
+
const lines = [];
|
|
990
|
+
const mainLine = [...navigation, ...actions, ...escape].join(", ");
|
|
991
|
+
if (mainLine) lines.push(mainLine);
|
|
992
|
+
lines.push(...others);
|
|
993
|
+
return lines;
|
|
994
|
+
}
|
|
995
|
+
var FooterPresets;
|
|
996
|
+
var init_footer_builder = __esm({
|
|
997
|
+
"src/screen/footer-builder.ts"() {
|
|
998
|
+
"use strict";
|
|
999
|
+
FooterPresets = {
|
|
1000
|
+
/**
|
|
1001
|
+
* Menu screen footer
|
|
1002
|
+
*/
|
|
1003
|
+
menu: (customInfo = null) => buildFooter({
|
|
1004
|
+
navigation: "\u2191/\u2193 to navigate",
|
|
1005
|
+
actions: "Enter to select",
|
|
1006
|
+
escape: "Esc to go back",
|
|
1007
|
+
info: customInfo
|
|
1008
|
+
}),
|
|
1009
|
+
/**
|
|
1010
|
+
* Word grid footer
|
|
1011
|
+
*/
|
|
1012
|
+
wordGrid: (totalWords) => buildFooter({
|
|
1013
|
+
navigation: "\u2191\u2193\u2190\u2192 to navigate",
|
|
1014
|
+
actions: "Enter to select",
|
|
1015
|
+
escape: "Esc to go back",
|
|
1016
|
+
info: `Total: ${totalWords} words`
|
|
1017
|
+
}),
|
|
1018
|
+
/**
|
|
1019
|
+
* Text input footer
|
|
1020
|
+
*/
|
|
1021
|
+
textInput: () => buildFooter({
|
|
1022
|
+
actions: "Type and press Enter to submit",
|
|
1023
|
+
escape: "Esc to cancel"
|
|
1024
|
+
}),
|
|
1025
|
+
/**
|
|
1026
|
+
* Info/static screen footer
|
|
1027
|
+
*/
|
|
1028
|
+
info: () => buildFooter({
|
|
1029
|
+
escape: "Esc to continue"
|
|
1030
|
+
}),
|
|
1031
|
+
/**
|
|
1032
|
+
* Main menu footer (escape exits)
|
|
1033
|
+
*/
|
|
1034
|
+
mainMenu: () => buildFooter({
|
|
1035
|
+
navigation: "\u2191/\u2193 to navigate",
|
|
1036
|
+
actions: "Enter to select",
|
|
1037
|
+
escape: "Esc to exit"
|
|
1038
|
+
}),
|
|
1039
|
+
/**
|
|
1040
|
+
* Action menu footer (for word cards, etc.)
|
|
1041
|
+
*/
|
|
1042
|
+
actionMenu: (hasAudio = false) => {
|
|
1043
|
+
const parts = buildFooter({
|
|
1044
|
+
navigation: "\u2191/\u2193 to navigate",
|
|
1045
|
+
actions: "Enter to select",
|
|
1046
|
+
escape: "Esc to go back"
|
|
1047
|
+
});
|
|
1048
|
+
if (hasAudio) {
|
|
1049
|
+
parts.push("Audio available");
|
|
1050
|
+
}
|
|
1051
|
+
return parts;
|
|
793
1052
|
}
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
// src/screen/index.ts
|
|
1058
|
+
async function load() {
|
|
1059
|
+
if (loadPromise) return loadPromise;
|
|
1060
|
+
|
|
1061
|
+
loadPromise = Promise.resolve();
|
|
1062
|
+
return loadPromise;
|
|
1063
|
+
}
|
|
1064
|
+
var import_react5, import_ink5, loadPromise;
|
|
1065
|
+
var init_screen = __esm({
|
|
1066
|
+
"src/screen/index.ts"() {
|
|
1067
|
+
"use strict";
|
|
1068
|
+
import_react5 = __toESM(require("react"), 1);
|
|
1069
|
+
import_ink5 = require("ink");
|
|
1070
|
+
init_screens();
|
|
1071
|
+
init_list_components();
|
|
1072
|
+
init_components();
|
|
1073
|
+
init_ui_elements();
|
|
1074
|
+
init_utils();
|
|
1075
|
+
init_footer_builder();
|
|
1076
|
+
loadPromise = null;
|
|
1077
|
+
if (typeof window === "undefined") {
|
|
1078
|
+
load().catch(() => {
|
|
1079
|
+
});
|
|
794
1080
|
}
|
|
795
|
-
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
// src/index.ts
|
|
1085
|
+
var src_exports = {};
|
|
1086
|
+
__export(src_exports, {
|
|
1087
|
+
Args: () => Args,
|
|
1088
|
+
Box: () => import_ink5.Box,
|
|
1089
|
+
Db: () => Db,
|
|
1090
|
+
Divider: () => Divider,
|
|
1091
|
+
FileDatabase: () => FileDatabase,
|
|
1092
|
+
FileDatabaseError: () => FileDatabaseError,
|
|
1093
|
+
FooterPresets: () => FooterPresets,
|
|
1094
|
+
GridCell: () => GridCell,
|
|
1095
|
+
InputField: () => InputField,
|
|
1096
|
+
ListComponent: () => ListComponent,
|
|
1097
|
+
ListItem: () => ListItem,
|
|
1098
|
+
MultiColumnListComponent: () => MultiColumnListComponent,
|
|
1099
|
+
MultiColumnListWithPreviewComponent: () => MultiColumnListWithPreviewComponent,
|
|
1100
|
+
Params: () => Params,
|
|
1101
|
+
React: () => import_react5.default,
|
|
1102
|
+
ScreenBody: () => ScreenBody,
|
|
1103
|
+
ScreenContainer: () => ScreenContainer,
|
|
1104
|
+
ScreenDivider: () => ScreenDivider,
|
|
1105
|
+
ScreenFooter: () => ScreenFooter,
|
|
1106
|
+
ScreenRow: () => ScreenRow,
|
|
1107
|
+
ScreenTitle: () => ScreenTitle,
|
|
1108
|
+
Text: () => import_ink5.Text,
|
|
1109
|
+
TextBlock: () => TextBlock,
|
|
1110
|
+
buildBreadcrumb: () => buildBreadcrumb,
|
|
1111
|
+
buildDetailBreadcrumb: () => buildDetailBreadcrumb,
|
|
1112
|
+
buildFooter: () => buildFooter,
|
|
1113
|
+
defaultFileSynopsisFunction: () => defaultFileSynopsisFunction,
|
|
1114
|
+
defaultVersionSynopsisFunction: () => defaultVersionSynopsisFunction,
|
|
1115
|
+
getArgsInstance: () => getArgsInstance,
|
|
1116
|
+
h: () => import_react5.createElement,
|
|
1117
|
+
joiEdateType: () => joiEdateType,
|
|
1118
|
+
joiStringArrayType: () => joiStringArrayType,
|
|
1119
|
+
load: () => load,
|
|
1120
|
+
organizeFooterMessages: () => organizeFooterMessages,
|
|
1121
|
+
setupContext: () => setupContext,
|
|
1122
|
+
showListScreen: () => showListScreen,
|
|
1123
|
+
showMenuScreen: () => showMenuScreen,
|
|
1124
|
+
showMultiColumnListScreen: () => showMultiColumnListScreen,
|
|
1125
|
+
showMultiColumnListWithPreviewScreen: () => showMultiColumnListWithPreviewScreen,
|
|
1126
|
+
showScreen: () => showScreen,
|
|
1127
|
+
showWordGridScreen: () => showWordGridScreen,
|
|
1128
|
+
useCallback: () => import_react5.useCallback,
|
|
1129
|
+
useEffect: () => import_react5.useEffect,
|
|
1130
|
+
useInput: () => import_ink5.useInput,
|
|
1131
|
+
useMemo: () => import_react5.useMemo,
|
|
1132
|
+
useRef: () => import_react5.useRef,
|
|
1133
|
+
useState: () => import_react5.useState
|
|
1134
|
+
});
|
|
1135
|
+
module.exports = __toCommonJS(src_exports);
|
|
1136
|
+
|
|
1137
|
+
// src/args/index.ts
|
|
1138
|
+
var import_fs = require("fs");
|
|
1139
|
+
var import_path = require("path");
|
|
1140
|
+
var import_dotenv = require("dotenv");
|
|
1141
|
+
var Args = class _Args {
|
|
1142
|
+
args = {};
|
|
1143
|
+
flags = {};
|
|
1144
|
+
options = {};
|
|
1145
|
+
commands = [];
|
|
1146
|
+
usedKeys = /* @__PURE__ */ new Set();
|
|
1147
|
+
aliases = {};
|
|
1148
|
+
overrides = {};
|
|
1149
|
+
defaults = {};
|
|
1150
|
+
prefixes = [];
|
|
1151
|
+
nots = [];
|
|
1152
|
+
configValues = {};
|
|
1153
|
+
configsLoaded = [];
|
|
1154
|
+
env = "local";
|
|
1155
|
+
constructor(config2 = {}) {
|
|
1156
|
+
this.aliases = {};
|
|
1157
|
+
this.overrides = {};
|
|
1158
|
+
this.defaults = {};
|
|
1159
|
+
this.prefixes = ["not", "no"];
|
|
1160
|
+
if (Object.keys(config2).length > 0) {
|
|
1161
|
+
this.configure(config2);
|
|
1162
|
+
}
|
|
1163
|
+
const args = config2.args || process.argv.slice(2);
|
|
1164
|
+
this.parseArgs(args);
|
|
1165
|
+
this.env = this.get("env")?.toLowerCase() || "local";
|
|
1166
|
+
this.loadDotEnv();
|
|
1167
|
+
this.loadConfigFiles();
|
|
1168
|
+
this.checkConflicts();
|
|
796
1169
|
}
|
|
797
1170
|
/**
|
|
798
|
-
*
|
|
1171
|
+
* Configure Args options
|
|
1172
|
+
* Only parameters present in config are updated
|
|
1173
|
+
* Note: Args is special - it's initialized first, so it can't take context
|
|
799
1174
|
*/
|
|
800
|
-
|
|
801
|
-
|
|
1175
|
+
configure(config2) {
|
|
1176
|
+
if (config2.aliases !== void 0) {
|
|
1177
|
+
this.aliases = config2.aliases;
|
|
1178
|
+
}
|
|
1179
|
+
if (config2.overrides !== void 0) {
|
|
1180
|
+
this.overrides = config2.overrides;
|
|
1181
|
+
}
|
|
1182
|
+
if (config2.defaults !== void 0) {
|
|
1183
|
+
this.defaults = config2.defaults;
|
|
1184
|
+
}
|
|
1185
|
+
if (config2.prefixes !== void 0) {
|
|
1186
|
+
this.prefixes = config2.prefixes;
|
|
1187
|
+
}
|
|
802
1188
|
}
|
|
803
1189
|
/**
|
|
804
|
-
*
|
|
1190
|
+
* Initialize Args instance
|
|
1191
|
+
* Note: Args is special - it's initialized first, so it can't take context
|
|
1192
|
+
* This static method is for consistency with other components
|
|
805
1193
|
*/
|
|
806
|
-
|
|
807
|
-
|
|
1194
|
+
static init(config2 = {}) {
|
|
1195
|
+
return new _Args(config2);
|
|
808
1196
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
function getScreenWidth(maxWidth = null) {
|
|
825
|
-
const terminalWidth = process.stdout.columns || 80;
|
|
826
|
-
const availableWidth = Math.max(20, terminalWidth - 4);
|
|
827
|
-
return maxWidth ? Math.min(availableWidth, maxWidth) : availableWidth;
|
|
828
|
-
}
|
|
829
|
-
function ScreenContainer({ children }) {
|
|
830
|
-
const width = getScreenWidth();
|
|
831
|
-
return (0, import_react.createElement)(import_ink.Box, {
|
|
832
|
-
flexDirection: "column",
|
|
833
|
-
marginTop: 1,
|
|
834
|
-
borderStyle: "single",
|
|
835
|
-
borderColor: "cyan",
|
|
836
|
-
paddingX: 1,
|
|
837
|
-
width
|
|
838
|
-
// Use the calculated width directly
|
|
839
|
-
}, children);
|
|
840
|
-
}
|
|
841
|
-
function ScreenRow({ children }) {
|
|
842
|
-
return (0, import_react.createElement)(import_ink.Box, { flexDirection: "column" }, children);
|
|
843
|
-
}
|
|
844
|
-
function ScreenTitle({ text }) {
|
|
845
|
-
return (0, import_react.createElement)(
|
|
846
|
-
ScreenRow,
|
|
847
|
-
{},
|
|
848
|
-
(0, import_react.createElement)(import_ink.Text, { bold: true, color: "cyan" }, text)
|
|
849
|
-
);
|
|
850
|
-
}
|
|
851
|
-
function ScreenDivider({ width }) {
|
|
852
|
-
const dividerWidth = width || getScreenWidth() - 4;
|
|
853
|
-
return (0, import_react.createElement)(import_ink.Text, { color: "cyan", dimColor: true }, "\u2500".repeat(dividerWidth));
|
|
854
|
-
}
|
|
855
|
-
function ScreenBody({ children, alignItems = "flex-start" }) {
|
|
856
|
-
return (0, import_react.createElement)(import_ink.Box, { flexDirection: "column", alignItems }, children);
|
|
857
|
-
}
|
|
858
|
-
function ScreenFooter({ lines, textStyle }) {
|
|
859
|
-
const defaultTextStyle = {
|
|
860
|
-
dimColor: true,
|
|
861
|
-
color: "white"
|
|
862
|
-
};
|
|
863
|
-
const finalTextStyle = { ...defaultTextStyle, ...textStyle };
|
|
864
|
-
const flattenAndWrap = (items, keyPrefix = "") => {
|
|
865
|
-
const result = [];
|
|
866
|
-
let keyIndex = 0;
|
|
867
|
-
items.forEach((item, index) => {
|
|
868
|
-
if (Array.isArray(item)) {
|
|
869
|
-
const nested = flattenAndWrap(item, `${keyPrefix}-${index}`);
|
|
870
|
-
result.push(...nested);
|
|
871
|
-
} else if (typeof item === "string") {
|
|
872
|
-
result.push(
|
|
873
|
-
(0, import_react.createElement)(import_ink.Text, { key: `${keyPrefix}-${keyIndex++}`, ...finalTextStyle }, item)
|
|
874
|
-
);
|
|
875
|
-
} else {
|
|
876
|
-
const element = item;
|
|
877
|
-
if (element.key === null || element.key === void 0) {
|
|
878
|
-
result.push(
|
|
879
|
-
(0, import_react.createElement)(import_ink.Text, { key: `${keyPrefix}-${keyIndex++}`, ...finalTextStyle }, element)
|
|
880
|
-
);
|
|
1197
|
+
/**
|
|
1198
|
+
* Parse command line arguments
|
|
1199
|
+
*/
|
|
1200
|
+
parseArgs(args) {
|
|
1201
|
+
let i = 0;
|
|
1202
|
+
while (i < args.length) {
|
|
1203
|
+
const arg = args[i];
|
|
1204
|
+
if (arg.startsWith("--")) {
|
|
1205
|
+
const [key, value] = this.parseLongOption(arg);
|
|
1206
|
+
this.setValue(key, value);
|
|
1207
|
+
i++;
|
|
1208
|
+
} else if (arg.startsWith("-")) {
|
|
1209
|
+
const result = this.parseShortOption(arg, args, i);
|
|
1210
|
+
if (result.consumed > 0) {
|
|
1211
|
+
i += result.consumed;
|
|
881
1212
|
} else {
|
|
882
|
-
|
|
1213
|
+
i++;
|
|
883
1214
|
}
|
|
884
|
-
}
|
|
885
|
-
});
|
|
886
|
-
return result;
|
|
887
|
-
};
|
|
888
|
-
const wrappedItems = flattenAndWrap(lines);
|
|
889
|
-
return (0, import_react.createElement)(
|
|
890
|
-
import_ink.Box,
|
|
891
|
-
{ flexDirection: "column" },
|
|
892
|
-
(0, import_react.createElement)(import_ink.Box, { flexDirection: "row" }, ...wrappedItems)
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// src/screen/list-components.ts
|
|
897
|
-
var import_react2 = __toESM(require("react"), 1);
|
|
898
|
-
var import_ink2 = require("ink");
|
|
899
|
-
var h2 = import_react2.createElement;
|
|
900
|
-
function MultiColumnListComponent({ items, ctx, selectedIndexRef }) {
|
|
901
|
-
const [, forceUpdate] = (0, import_react2.useState)({});
|
|
902
|
-
const termWidth = (process.stdout.columns || 80) - 8;
|
|
903
|
-
const maxItemLength = Math.max(...items.map((w) => w.length));
|
|
904
|
-
const columnWidth = maxItemLength + 3;
|
|
905
|
-
const columns = Math.max(1, Math.floor(termWidth / columnWidth));
|
|
906
|
-
const itemsPerColumn = Math.ceil(items.length / columns);
|
|
907
|
-
(0, import_react2.useEffect)(() => {
|
|
908
|
-
ctx.setAction("moveUp", () => {
|
|
909
|
-
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - 1);
|
|
910
|
-
forceUpdate({});
|
|
911
|
-
});
|
|
912
|
-
ctx.setAction("moveDown", () => {
|
|
913
|
-
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + 1);
|
|
914
|
-
forceUpdate({});
|
|
915
|
-
});
|
|
916
|
-
ctx.setAction("moveLeft", () => {
|
|
917
|
-
if (selectedIndexRef.current === 0) {
|
|
918
|
-
ctx.goBack();
|
|
919
1215
|
} else {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}
|
|
923
|
-
});
|
|
924
|
-
ctx.setAction("moveRight", () => {
|
|
925
|
-
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + itemsPerColumn);
|
|
926
|
-
forceUpdate({});
|
|
927
|
-
});
|
|
928
|
-
ctx.setKeyBinding([
|
|
929
|
-
{ key: "leftArrow", caption: "navigate", action: "moveLeft", order: 0 },
|
|
930
|
-
{ key: "rightArrow", caption: "navigate", action: "moveRight", order: 0 },
|
|
931
|
-
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
932
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
933
|
-
]);
|
|
934
|
-
ctx.addFooter(`Total: ${items.length} items`);
|
|
935
|
-
}, []);
|
|
936
|
-
const selectedIndex = selectedIndexRef.current;
|
|
937
|
-
const rows = [];
|
|
938
|
-
for (let row = 0; row < itemsPerColumn; row++) {
|
|
939
|
-
const cols = [];
|
|
940
|
-
for (let col = 0; col < columns; col++) {
|
|
941
|
-
const index = col * itemsPerColumn + row;
|
|
942
|
-
if (index < items.length) {
|
|
943
|
-
const isSelected = index === selectedIndex;
|
|
944
|
-
cols.push(
|
|
945
|
-
h2(
|
|
946
|
-
import_ink2.Box,
|
|
947
|
-
{ key: index, width: columnWidth },
|
|
948
|
-
h2(import_ink2.Text, {
|
|
949
|
-
color: isSelected ? "black" : "white",
|
|
950
|
-
backgroundColor: isSelected ? "cyan" : void 0,
|
|
951
|
-
bold: isSelected
|
|
952
|
-
}, items[index].padEnd(maxItemLength))
|
|
953
|
-
)
|
|
954
|
-
);
|
|
1216
|
+
this.commands.push(arg);
|
|
1217
|
+
i++;
|
|
955
1218
|
}
|
|
956
1219
|
}
|
|
957
|
-
rows.push(
|
|
958
|
-
h2(ScreenRow, { key: row, children: h2(import_ink2.Box, { flexDirection: "row" }, ...cols) })
|
|
959
|
-
);
|
|
960
1220
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
const maxItemLength = Math.max(...items.map((w) => w.length));
|
|
972
|
-
const columnWidth = maxItemLength + 3;
|
|
973
|
-
const columns = Math.max(1, Math.floor(termWidth / columnWidth));
|
|
974
|
-
const itemsPerColumn = Math.ceil(items.length / columns);
|
|
975
|
-
(0, import_react2.useEffect)(() => {
|
|
976
|
-
ctx.setAction("moveUp", () => {
|
|
977
|
-
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - 1);
|
|
978
|
-
forceUpdate({});
|
|
979
|
-
});
|
|
980
|
-
ctx.setAction("moveDown", () => {
|
|
981
|
-
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + 1);
|
|
982
|
-
forceUpdate({});
|
|
983
|
-
});
|
|
984
|
-
ctx.setAction("moveLeft", () => {
|
|
985
|
-
if (selectedIndexRef.current === 0) {
|
|
986
|
-
ctx.goBack();
|
|
987
|
-
} else {
|
|
988
|
-
selectedIndexRef.current = Math.max(0, selectedIndexRef.current - itemsPerColumn);
|
|
989
|
-
forceUpdate({});
|
|
990
|
-
}
|
|
991
|
-
});
|
|
992
|
-
ctx.setAction("moveRight", () => {
|
|
993
|
-
selectedIndexRef.current = Math.min(items.length - 1, selectedIndexRef.current + itemsPerColumn);
|
|
994
|
-
forceUpdate({});
|
|
995
|
-
});
|
|
996
|
-
ctx.setKeyBinding([
|
|
997
|
-
{ key: "leftArrow", caption: "navigate", action: "moveLeft", order: 0 },
|
|
998
|
-
{ key: "rightArrow", caption: "navigate", action: "moveRight", order: 0 },
|
|
999
|
-
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
1000
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
1001
|
-
]);
|
|
1002
|
-
ctx.addFooter(`Total: ${items.length} items`);
|
|
1003
|
-
}, []);
|
|
1004
|
-
const selectedIndex = selectedIndexRef.current;
|
|
1005
|
-
const selectedItem = items[selectedIndex];
|
|
1006
|
-
const rows = [];
|
|
1007
|
-
for (let row = 0; row < itemsPerColumn; row++) {
|
|
1008
|
-
const cols = [];
|
|
1009
|
-
for (let col = 0; col < columns; col++) {
|
|
1010
|
-
const index = col * itemsPerColumn + row;
|
|
1011
|
-
if (index < items.length) {
|
|
1012
|
-
const isSelected = index === selectedIndex;
|
|
1013
|
-
cols.push(
|
|
1014
|
-
h2(
|
|
1015
|
-
import_ink2.Box,
|
|
1016
|
-
{ key: index, width: columnWidth },
|
|
1017
|
-
h2(import_ink2.Text, {
|
|
1018
|
-
color: isSelected ? "black" : "white",
|
|
1019
|
-
backgroundColor: isSelected ? "cyan" : void 0,
|
|
1020
|
-
bold: isSelected
|
|
1021
|
-
}, items[index].padEnd(maxItemLength))
|
|
1022
|
-
)
|
|
1023
|
-
);
|
|
1221
|
+
/**
|
|
1222
|
+
* Parse long option (--key=value or --key)
|
|
1223
|
+
*/
|
|
1224
|
+
parseLongOption(arg) {
|
|
1225
|
+
const key = arg.slice(2);
|
|
1226
|
+
const prefix = this.prefixes.find((p) => key.startsWith(p));
|
|
1227
|
+
if (prefix) {
|
|
1228
|
+
let strippedKey = key.slice(prefix.length);
|
|
1229
|
+
if (strippedKey.startsWith("-")) {
|
|
1230
|
+
strippedKey = strippedKey.slice(1);
|
|
1024
1231
|
}
|
|
1232
|
+
this.nots.push(key);
|
|
1233
|
+
return [strippedKey, false];
|
|
1025
1234
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
const previewRows = [];
|
|
1032
|
-
if (typeof previewContent === "string") {
|
|
1033
|
-
previewRows.push(h2(ScreenRow, { key: "preview-string", children: h2(import_ink2.Text, { bold: true }, previewContent) }));
|
|
1034
|
-
} else if (typeof previewContent === "object" && !import_react2.default.isValidElement(previewContent) && previewContent !== null) {
|
|
1035
|
-
Object.entries(previewContent).forEach(([key, value], idx) => {
|
|
1036
|
-
previewRows.push(h2(ScreenRow, { key: `preview-${key}-${idx}`, children: h2(import_ink2.Text, {}, `${key}: ${value}`) }));
|
|
1037
|
-
});
|
|
1038
|
-
} else if (import_react2.default.isValidElement(previewContent)) {
|
|
1039
|
-
previewRows.push(h2(ScreenRow, { key: "preview-element", children: previewContent }));
|
|
1040
|
-
}
|
|
1041
|
-
return h2(
|
|
1042
|
-
import_ink2.Box,
|
|
1043
|
-
{ flexDirection: "column" },
|
|
1044
|
-
...rows,
|
|
1045
|
-
h2(ScreenRow, { key: "spacer-1", children: h2(import_ink2.Text, {}, " ") }),
|
|
1046
|
-
h2(ScreenDivider, { key: "divider" }),
|
|
1047
|
-
h2(ScreenRow, { key: "spacer-2", children: h2(import_ink2.Text, {}, " ") }),
|
|
1048
|
-
...previewRows
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sortable = false, maxHeight, sortHighlightStyle, selectionMarker = " " }) {
|
|
1052
|
-
const [, forceUpdate] = (0, import_react2.useState)({});
|
|
1053
|
-
const [sortOrder, setSortOrder] = (0, import_react2.useState)("none");
|
|
1054
|
-
const [scrollOffset, setScrollOffset] = (0, import_react2.useState)(0);
|
|
1055
|
-
const scrollStateRef = (0, import_react2.useRef)({ scrollOffset: 0, maxHeight: 0, totalItems: 0 });
|
|
1056
|
-
const defaultGetTitle = (item) => {
|
|
1057
|
-
return getTitle ? getTitle(item) : typeof item.value === "string" ? item.value : item.value?.title || item.name;
|
|
1058
|
-
};
|
|
1059
|
-
const titleGetter = getTitle || defaultGetTitle;
|
|
1060
|
-
const displayItems = sortable && sortOrder !== "none" ? [...items].sort((a, b) => {
|
|
1061
|
-
const titleA = titleGetter(a).toLowerCase();
|
|
1062
|
-
const titleB = titleGetter(b).toLowerCase();
|
|
1063
|
-
if (sortOrder === "asc") {
|
|
1064
|
-
return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
|
|
1235
|
+
if (key.includes("=")) {
|
|
1236
|
+
const eqIndex = key.indexOf("=");
|
|
1237
|
+
const optionKey = key.slice(0, eqIndex);
|
|
1238
|
+
const value = key.slice(eqIndex + 1);
|
|
1239
|
+
return [optionKey, this.parseValue(value)];
|
|
1065
1240
|
} else {
|
|
1066
|
-
return
|
|
1241
|
+
return [key, true];
|
|
1067
1242
|
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
if (newIndex < currentClampedScrollOffset) {
|
|
1085
|
-
setScrollOffset(newIndex);
|
|
1086
|
-
}
|
|
1087
|
-
forceUpdate({});
|
|
1088
|
-
});
|
|
1089
|
-
ctx.setAction("moveDown", () => {
|
|
1090
|
-
const currentItems = sortable && sortOrder !== "none" ? [...items].sort((a, b) => {
|
|
1091
|
-
const titleA = titleGetter(a).toLowerCase();
|
|
1092
|
-
const titleB = titleGetter(b).toLowerCase();
|
|
1093
|
-
if (sortOrder === "asc") {
|
|
1094
|
-
return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Parse short option (-k=value, -k, or bundled -vsd)
|
|
1246
|
+
*/
|
|
1247
|
+
parseShortOption(arg, args, index) {
|
|
1248
|
+
const key = arg.slice(1);
|
|
1249
|
+
if (key.length === 1 && index + 1 < args.length && !args[index + 1].startsWith("-")) {
|
|
1250
|
+
const value = args[index + 1];
|
|
1251
|
+
this.setValue(key, this.parseValue(value));
|
|
1252
|
+
return { consumed: 2 };
|
|
1253
|
+
}
|
|
1254
|
+
if (key.length > 1 && !key.includes("=")) {
|
|
1255
|
+
for (let i = 0; i < key.length; i++) {
|
|
1256
|
+
const shortKey = key[i];
|
|
1257
|
+
if (shortKey in this.aliases) {
|
|
1258
|
+
this.setValue(shortKey, true);
|
|
1095
1259
|
} else {
|
|
1096
|
-
|
|
1260
|
+
this.args[shortKey] = true;
|
|
1097
1261
|
}
|
|
1098
|
-
}) : items;
|
|
1099
|
-
const maxIndex = currentItems.length - 1;
|
|
1100
|
-
const newIndex = Math.min(maxIndex, selectedIndexRef.current + 1);
|
|
1101
|
-
selectedIndexRef.current = newIndex;
|
|
1102
|
-
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
1103
|
-
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
1104
|
-
const currentClampedScrollOffset = Math.min(Math.max(0, currentScrollOffset), currentMaxScrollOffset);
|
|
1105
|
-
if (newIndex >= currentClampedScrollOffset + currentMaxHeight) {
|
|
1106
|
-
setScrollOffset(newIndex - currentMaxHeight + 1);
|
|
1107
1262
|
}
|
|
1108
|
-
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
const
|
|
1112
|
-
const
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
1120
|
-
const newScrollOffset = Math.min(currentMaxScrollOffset, currentScrollOffset + 1);
|
|
1121
|
-
setScrollOffset(newScrollOffset);
|
|
1122
|
-
forceUpdate({});
|
|
1123
|
-
});
|
|
1124
|
-
if (sortable) {
|
|
1125
|
-
ctx.setAction("toggleSort", () => {
|
|
1126
|
-
const nextSort = sortOrder === "none" ? "asc" : sortOrder === "asc" ? "desc" : "none";
|
|
1127
|
-
const currentSelectedItem = displayItems[selectedIndexRef.current];
|
|
1128
|
-
setSortOrder(nextSort);
|
|
1129
|
-
const newSortedItems = nextSort !== "none" ? [...items].sort((a, b) => {
|
|
1130
|
-
const titleA = titleGetter(a).toLowerCase();
|
|
1131
|
-
const titleB = titleGetter(b).toLowerCase();
|
|
1132
|
-
if (nextSort === "asc") {
|
|
1133
|
-
return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
|
|
1263
|
+
return { consumed: 1 };
|
|
1264
|
+
}
|
|
1265
|
+
if (key.includes("=")) {
|
|
1266
|
+
const eqIndex = key.indexOf("=");
|
|
1267
|
+
const optionKey = key.slice(0, eqIndex);
|
|
1268
|
+
const value = key.slice(eqIndex + 1);
|
|
1269
|
+
if (optionKey.length > 1) {
|
|
1270
|
+
for (let i = 0; i < optionKey.length - 1; i++) {
|
|
1271
|
+
const shortKey = optionKey[i];
|
|
1272
|
+
if (shortKey in this.aliases) {
|
|
1273
|
+
this.setValue(shortKey, true);
|
|
1134
1274
|
} else {
|
|
1135
|
-
|
|
1275
|
+
this.args[shortKey] = true;
|
|
1136
1276
|
}
|
|
1137
|
-
}) : items;
|
|
1138
|
-
const newIndex = newSortedItems.findIndex((item) => item === currentSelectedItem);
|
|
1139
|
-
if (newIndex !== -1) {
|
|
1140
|
-
selectedIndexRef.current = newIndex;
|
|
1141
|
-
setScrollOffset(newIndex);
|
|
1142
|
-
} else {
|
|
1143
|
-
selectedIndexRef.current = 0;
|
|
1144
|
-
setScrollOffset(0);
|
|
1145
1277
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
color: "black",
|
|
1150
|
-
backgroundColor: "green",
|
|
1151
|
-
bold: true
|
|
1152
|
-
};
|
|
1153
|
-
const highlightStyle = { ...defaultHighlightStyle, ...sortHighlightStyle };
|
|
1154
|
-
const sortCaption = () => {
|
|
1155
|
-
if (sortOrder === "none") {
|
|
1156
|
-
return h2(import_ink2.Text, {}, "s to toggle sort");
|
|
1278
|
+
const lastKey = optionKey[optionKey.length - 1];
|
|
1279
|
+
if (lastKey in this.aliases) {
|
|
1280
|
+
this.setValue(lastKey, this.parseValue(value));
|
|
1157
1281
|
} else {
|
|
1158
|
-
|
|
1159
|
-
return h2(
|
|
1160
|
-
import_ink2.Text,
|
|
1161
|
-
{},
|
|
1162
|
-
"s to toggle ",
|
|
1163
|
-
h2(import_ink2.Text, { color: "white", bold: true }, "sort"),
|
|
1164
|
-
" ",
|
|
1165
|
-
h2(import_ink2.Text, highlightStyle, ` ${sortLabel} `)
|
|
1166
|
-
);
|
|
1167
|
-
}
|
|
1168
|
-
};
|
|
1169
|
-
ctx.setKeyBinding([
|
|
1170
|
-
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
1171
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
1172
|
-
{
|
|
1173
|
-
key: "s",
|
|
1174
|
-
caption: sortCaption,
|
|
1175
|
-
action: "toggleSort",
|
|
1176
|
-
order: 5
|
|
1282
|
+
this.args[lastKey] = this.parseValue(value);
|
|
1177
1283
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1284
|
+
} else {
|
|
1285
|
+
this.setValue(optionKey, this.parseValue(value));
|
|
1286
|
+
}
|
|
1287
|
+
return { consumed: 1 };
|
|
1180
1288
|
} else {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
1184
|
-
]);
|
|
1289
|
+
this.setValue(key, true);
|
|
1290
|
+
return { consumed: 1 };
|
|
1185
1291
|
}
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
if (isFirstVisible && canScrollUp) {
|
|
1194
|
-
arrowPrefix = "\u2191 ";
|
|
1195
|
-
} else if (isLastVisible && canScrollDown) {
|
|
1196
|
-
arrowPrefix = "\u2193 ";
|
|
1197
|
-
} else {
|
|
1198
|
-
arrowPrefix = " ";
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Parse value (handle quotes)
|
|
1295
|
+
*/
|
|
1296
|
+
parseValue(value) {
|
|
1297
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1298
|
+
return value.slice(1, -1);
|
|
1199
1299
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1300
|
+
return value;
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Set a value with proper categorization
|
|
1304
|
+
*/
|
|
1305
|
+
setValue(key, value) {
|
|
1306
|
+
const resolvedKey = this.aliases[key] || key;
|
|
1307
|
+
if (typeof value === "boolean") {
|
|
1308
|
+
this.flags[resolvedKey] = value;
|
|
1202
1309
|
} else {
|
|
1203
|
-
|
|
1310
|
+
this.options[resolvedKey] = value;
|
|
1204
1311
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
key: `name-${actualIndex}`,
|
|
1218
|
-
color: isSelected ? "black" : "white",
|
|
1219
|
-
backgroundColor: isSelected ? "cyan" : void 0,
|
|
1220
|
-
bold: isSelected
|
|
1221
|
-
}, item.name)
|
|
1222
|
-
);
|
|
1223
|
-
};
|
|
1224
|
-
const itemRenderer = renderItem || defaultRenderItem;
|
|
1225
|
-
return h2(
|
|
1226
|
-
import_ink2.Box,
|
|
1227
|
-
{ flexDirection: "column" },
|
|
1228
|
-
...visibleItems.map((item, displayIndex) => {
|
|
1229
|
-
const actualIndex = clampedScrollOffset + displayIndex;
|
|
1230
|
-
const isSelected = actualIndex === selectedIndex;
|
|
1231
|
-
if (renderItem) {
|
|
1232
|
-
const isFirstVisible = displayIndex === 0;
|
|
1233
|
-
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
1234
|
-
let arrowPrefix = "";
|
|
1235
|
-
let selectionPrefix = "";
|
|
1236
|
-
if (isFirstVisible && canScrollUp) {
|
|
1237
|
-
arrowPrefix = "\u2191 ";
|
|
1238
|
-
} else if (isLastVisible && canScrollDown) {
|
|
1239
|
-
arrowPrefix = "\u2193 ";
|
|
1240
|
-
} else {
|
|
1241
|
-
arrowPrefix = " ";
|
|
1242
|
-
}
|
|
1243
|
-
if (isSelected) {
|
|
1244
|
-
selectionPrefix = selectionMarker;
|
|
1245
|
-
} else {
|
|
1246
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
1247
|
-
}
|
|
1248
|
-
return h2(ScreenRow, {
|
|
1249
|
-
key: `item-${actualIndex}`,
|
|
1250
|
-
children: h2(
|
|
1251
|
-
import_ink2.Box,
|
|
1252
|
-
{ flexDirection: "row" },
|
|
1253
|
-
// Arrow (clickable if functional, not highlighted)
|
|
1254
|
-
h2(import_ink2.Text, {
|
|
1255
|
-
key: `arrow-${actualIndex}`,
|
|
1256
|
-
color: "white"
|
|
1257
|
-
}, arrowPrefix),
|
|
1258
|
-
// Selection marker space (always same width, not highlighted)
|
|
1259
|
-
h2(import_ink2.Text, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
1260
|
-
// Custom rendered content
|
|
1261
|
-
renderItem(item, isSelected, displayIndex)
|
|
1262
|
-
)
|
|
1263
|
-
});
|
|
1264
|
-
} else {
|
|
1265
|
-
return h2(ScreenRow, {
|
|
1266
|
-
key: `item-${actualIndex}`,
|
|
1267
|
-
children: itemRenderer(item, isSelected, displayIndex, actualIndex)
|
|
1268
|
-
});
|
|
1312
|
+
this.args[resolvedKey.toLowerCase()] = value;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Check for conflicts (short + long form of same option)
|
|
1316
|
+
*/
|
|
1317
|
+
checkConflicts() {
|
|
1318
|
+
const conflicts = [];
|
|
1319
|
+
for (const [shortKey, longKey] of Object.entries(this.aliases)) {
|
|
1320
|
+
const hasShort = this.args[shortKey] !== void 0;
|
|
1321
|
+
const hasLong = this.args[longKey] !== void 0;
|
|
1322
|
+
if (hasShort && hasLong) {
|
|
1323
|
+
conflicts.push(`Both -${shortKey} and --${longKey} specified`);
|
|
1269
1324
|
}
|
|
1270
|
-
})
|
|
1271
|
-
);
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
// src/screen/screens.ts
|
|
1275
|
-
function groupKeyBindings(bindings) {
|
|
1276
|
-
const groups = {};
|
|
1277
|
-
const enabledBindings = bindings.filter((b) => b.enabled !== false);
|
|
1278
|
-
enabledBindings.forEach((binding) => {
|
|
1279
|
-
const caption = typeof binding.caption === "string" ? binding.caption : "";
|
|
1280
|
-
if (!groups[caption]) {
|
|
1281
|
-
groups[caption] = {
|
|
1282
|
-
keys: [],
|
|
1283
|
-
caption,
|
|
1284
|
-
order: binding.order || 999
|
|
1285
|
-
};
|
|
1286
1325
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
return Object.values(groups);
|
|
1290
|
-
}
|
|
1291
|
-
function formatKeyBindings(bindings, mode = "long") {
|
|
1292
|
-
const resolvedBindings = bindings.map((binding) => {
|
|
1293
|
-
let resolvedCaption = binding.caption;
|
|
1294
|
-
if (typeof binding.caption === "function") {
|
|
1295
|
-
resolvedCaption = binding.caption();
|
|
1326
|
+
if (conflicts.length > 0) {
|
|
1327
|
+
throw new Error(`Argument conflicts: ${conflicts.join(", ")}`);
|
|
1296
1328
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Get a value with precedence order
|
|
1332
|
+
*/
|
|
1333
|
+
get(key) {
|
|
1334
|
+
const resolvedKey = this.aliases[key] || key;
|
|
1335
|
+
this.usedKeys.add(resolvedKey);
|
|
1336
|
+
if (this.overrides[resolvedKey] !== void 0) {
|
|
1337
|
+
return this.overrides[resolvedKey];
|
|
1338
|
+
}
|
|
1339
|
+
const lcKey = resolvedKey.toLowerCase();
|
|
1340
|
+
const lcKeyWithEnv = `${lcKey}${this.env ? `_${this.env.toLowerCase()}` : ""}`;
|
|
1341
|
+
if (this.env && this.args[lcKeyWithEnv] !== void 0) {
|
|
1342
|
+
return this.args[lcKeyWithEnv];
|
|
1343
|
+
} else if (this.args[lcKey] !== void 0) {
|
|
1344
|
+
return this.args[lcKey];
|
|
1345
|
+
}
|
|
1346
|
+
if (this.configValues[resolvedKey] !== void 0) {
|
|
1347
|
+
return this.configValues[resolvedKey];
|
|
1348
|
+
}
|
|
1349
|
+
const envKey = this.toEnvKey(resolvedKey);
|
|
1350
|
+
const envKeyWithEnv = `${envKey}${this.env ? `_${this.env.toUpperCase()}` : ""}`;
|
|
1351
|
+
const envSpecificKey = Object.keys(process.env).find(
|
|
1352
|
+
(k) => this.env && k.toUpperCase() === envKeyWithEnv
|
|
1311
1353
|
);
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1354
|
+
const envKeyFound = Object.keys(process.env).find((k) => k.toUpperCase() === envKey);
|
|
1355
|
+
if (envSpecificKey) {
|
|
1356
|
+
return process.env[envSpecificKey];
|
|
1357
|
+
} else if (envKeyFound) {
|
|
1358
|
+
return process.env[envKeyFound];
|
|
1359
|
+
}
|
|
1360
|
+
if (this.defaults[resolvedKey] !== void 0) {
|
|
1361
|
+
return this.defaults[resolvedKey];
|
|
1362
|
+
}
|
|
1363
|
+
if (resolvedKey === "env" && process.env.NODE_ENV !== void 0) {
|
|
1364
|
+
return process.env.NODE_ENV;
|
|
1365
|
+
}
|
|
1366
|
+
return void 0;
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Set a value (for testing/internal use)
|
|
1370
|
+
*/
|
|
1371
|
+
set(key, value) {
|
|
1372
|
+
this.args[key] = value;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Check if a command exists (case-insensitive)
|
|
1376
|
+
*/
|
|
1377
|
+
hasCommand(cmd) {
|
|
1378
|
+
return this.commands.some((command) => command.toLowerCase() === cmd.toLowerCase());
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Get all commands
|
|
1382
|
+
*/
|
|
1383
|
+
getCommands() {
|
|
1384
|
+
return [...this.commands];
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Get used keys (as array)
|
|
1388
|
+
*/
|
|
1389
|
+
getUsed() {
|
|
1390
|
+
return Array.from(this.usedKeys);
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Get unused keys (as array)
|
|
1394
|
+
*/
|
|
1395
|
+
getUnused() {
|
|
1396
|
+
const unused = [];
|
|
1397
|
+
for (const key of Object.keys(this.args)) {
|
|
1398
|
+
if (!this.usedKeys.has(key) && !this.nots.includes(key)) {
|
|
1399
|
+
unused.push(key);
|
|
1320
1400
|
}
|
|
1321
1401
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
let instance2;
|
|
1344
|
-
const keyBindings = [];
|
|
1345
|
-
const actions = {};
|
|
1346
|
-
const customFooterItems = [];
|
|
1347
|
-
let renderResult = null;
|
|
1348
|
-
let initialized = false;
|
|
1349
|
-
const Screen = () => {
|
|
1350
|
-
const [updateCounter, setUpdateCounter] = (0, import_react3.useState)(0);
|
|
1351
|
-
if (!initialized) {
|
|
1352
|
-
const defaultBindings = [
|
|
1353
|
-
{ key: "escape", caption: "go back", action: "back", protected: true, order: 1 },
|
|
1354
|
-
{ key: "leftArrow", caption: "go back", action: "back", protected: false, order: 1 }
|
|
1355
|
-
// Note: 'select' is not a default - components add it if needed
|
|
1356
|
-
];
|
|
1357
|
-
defaultBindings.forEach((binding) => {
|
|
1358
|
-
keyBindings.push(binding);
|
|
1359
|
-
});
|
|
1360
|
-
actions.back = () => {
|
|
1361
|
-
cleanup(null);
|
|
1362
|
-
};
|
|
1363
|
-
initialized = true;
|
|
1402
|
+
return unused;
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Convert key to environment variable format
|
|
1406
|
+
*/
|
|
1407
|
+
toEnvKey(key) {
|
|
1408
|
+
return key.replace(
|
|
1409
|
+
/[A-Z0-9]/g,
|
|
1410
|
+
(match, offset) => offset === 0 ? match : "_" + match.toLowerCase()
|
|
1411
|
+
).toUpperCase();
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Load .env file
|
|
1415
|
+
*/
|
|
1416
|
+
loadDotEnv() {
|
|
1417
|
+
const dotEnvPath = this.get("dotEnvPath") || process.cwd();
|
|
1418
|
+
const dotEnvFile = this.get("dotEnvFile") || ".env";
|
|
1419
|
+
if (this.get("dotEnvFile")) {
|
|
1420
|
+
const customPath = (0, import_path.resolve)(dotEnvPath, dotEnvFile);
|
|
1421
|
+
if ((0, import_fs.existsSync)(customPath)) {
|
|
1422
|
+
(0, import_dotenv.config)({ path: customPath, quiet: true });
|
|
1364
1423
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1379
|
-
keyBindings[existingIndex] = {
|
|
1380
|
-
...existing,
|
|
1381
|
-
...binding,
|
|
1382
|
-
order: binding.order !== void 0 ? binding.order : existing.order,
|
|
1383
|
-
enabled: binding.enabled !== void 0 ? binding.enabled : existing.enabled !== void 0 ? existing.enabled : true
|
|
1384
|
-
};
|
|
1385
|
-
} else {
|
|
1386
|
-
keyBindings.push({
|
|
1387
|
-
protected: false,
|
|
1388
|
-
order: 999,
|
|
1389
|
-
enabled: true,
|
|
1390
|
-
...binding
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
});
|
|
1394
|
-
},
|
|
1395
|
-
updateKeyBinding: (keyName, updates) => {
|
|
1396
|
-
const index = keyBindings.findIndex((b) => b.key === keyName);
|
|
1397
|
-
if (index >= 0) {
|
|
1398
|
-
keyBindings[index] = {
|
|
1399
|
-
...keyBindings[index],
|
|
1400
|
-
...updates
|
|
1401
|
-
};
|
|
1402
|
-
}
|
|
1403
|
-
},
|
|
1404
|
-
removeKeyBinding: (keyName) => {
|
|
1405
|
-
const index = keyBindings.findIndex((b) => b.key === keyName);
|
|
1406
|
-
if (index >= 0) {
|
|
1407
|
-
if (keyBindings[index].protected) {
|
|
1408
|
-
console.warn(`Cannot remove protected key: ${keyName}`);
|
|
1409
|
-
return;
|
|
1410
|
-
}
|
|
1411
|
-
keyBindings.splice(index, 1);
|
|
1412
|
-
}
|
|
1413
|
-
},
|
|
1414
|
-
addFooter: (item) => {
|
|
1415
|
-
customFooterItems.push(item);
|
|
1416
|
-
},
|
|
1417
|
-
clearFooter: () => {
|
|
1418
|
-
customFooterItems.length = 0;
|
|
1419
|
-
},
|
|
1420
|
-
setFooter: (items) => {
|
|
1421
|
-
customFooterItems.length = 0;
|
|
1422
|
-
const itemsArray = Array.isArray(items) ? items : [items];
|
|
1423
|
-
customFooterItems.push(...itemsArray);
|
|
1424
|
-
},
|
|
1425
|
-
update: () => {
|
|
1426
|
-
setUpdateCounter((c) => c + 1);
|
|
1427
|
-
},
|
|
1428
|
-
goBack: () => {
|
|
1429
|
-
if (actions.back) {
|
|
1430
|
-
actions.back();
|
|
1431
|
-
}
|
|
1432
|
-
},
|
|
1433
|
-
close: (result) => {
|
|
1434
|
-
cleanup(result);
|
|
1435
|
-
},
|
|
1436
|
-
parentData
|
|
1437
|
-
};
|
|
1438
|
-
if (!renderResult) {
|
|
1439
|
-
renderResult = onRender(context);
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
let dotEnvPathFile = null;
|
|
1427
|
+
const envSpecificFile = `.env.${this.env}`;
|
|
1428
|
+
const envSpecificPath = (0, import_path.resolve)(dotEnvPath, envSpecificFile);
|
|
1429
|
+
if ((0, import_fs.existsSync)(envSpecificPath)) {
|
|
1430
|
+
dotEnvPathFile = envSpecificPath;
|
|
1431
|
+
}
|
|
1432
|
+
if (!dotEnvPathFile && !this.get("dotEnvPath")) {
|
|
1433
|
+
const examplesPath = (0, import_path.resolve)(dotEnvPath, "examples");
|
|
1434
|
+
const examplesEnvSpecificPath = (0, import_path.resolve)(examplesPath, envSpecificFile);
|
|
1435
|
+
if ((0, import_fs.existsSync)(examplesEnvSpecificPath)) {
|
|
1436
|
+
dotEnvPathFile = examplesEnvSpecificPath;
|
|
1440
1437
|
}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
} else if (input === binding.key) {
|
|
1453
|
-
keyMatches = true;
|
|
1454
|
-
}
|
|
1455
|
-
if (keyMatches) {
|
|
1456
|
-
if (binding.enabled === false) {
|
|
1457
|
-
continue;
|
|
1458
|
-
}
|
|
1459
|
-
if (binding.condition && !binding.condition(context)) {
|
|
1460
|
-
continue;
|
|
1461
|
-
}
|
|
1462
|
-
matchedBinding = binding;
|
|
1463
|
-
break;
|
|
1438
|
+
}
|
|
1439
|
+
if (!dotEnvPathFile) {
|
|
1440
|
+
dotEnvPathFile = (0, import_path.resolve)(dotEnvPath, dotEnvFile);
|
|
1441
|
+
if (!(0, import_fs.existsSync)(dotEnvPathFile)) {
|
|
1442
|
+
if (!this.get("dotEnvPath")) {
|
|
1443
|
+
const examplesPath = (0, import_path.resolve)(dotEnvPath, "examples");
|
|
1444
|
+
const examplesEnvFile = (0, import_path.resolve)(examplesPath, dotEnvFile);
|
|
1445
|
+
if ((0, import_fs.existsSync)(examplesEnvFile)) {
|
|
1446
|
+
dotEnvPathFile = examplesEnvFile;
|
|
1447
|
+
} else {
|
|
1448
|
+
dotEnvPathFile = (0, import_path.resolve)(dotEnvPath, "..", dotEnvFile);
|
|
1464
1449
|
}
|
|
1465
1450
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (dotEnvPathFile && (0, import_fs.existsSync)(dotEnvPathFile)) {
|
|
1454
|
+
(0, import_dotenv.config)({ path: dotEnvPathFile, quiet: true });
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Load configuration files
|
|
1459
|
+
*/
|
|
1460
|
+
loadConfigFiles() {
|
|
1461
|
+
this.configsLoaded = [];
|
|
1462
|
+
this.configValues = {};
|
|
1463
|
+
const _defaultConfigExtension = this.get("defaultConfigExtension") || "js";
|
|
1464
|
+
const optConfigFiles = this.get("config") || this.get("configs") || "";
|
|
1465
|
+
const configFiles = optConfigFiles ? optConfigFiles.split(/,\s*/) : [];
|
|
1466
|
+
const optConfigFilePath = this.get("configPath");
|
|
1467
|
+
if (configFiles.length > 0) {
|
|
1468
|
+
for (const cfgFile of configFiles) {
|
|
1469
|
+
let notLoaded = false;
|
|
1470
|
+
let notLoadedEnvSpecific = false;
|
|
1471
|
+
const cfgFileWithPath = this.resolveFileWithPath(optConfigFilePath, cfgFile);
|
|
1472
|
+
try {
|
|
1473
|
+
const cfgContents = this.requireConfigFile(cfgFileWithPath);
|
|
1474
|
+
this.configValues = { ...this.configValues, ...cfgContents };
|
|
1475
|
+
this.configsLoaded.push(cfgFileWithPath);
|
|
1476
|
+
} catch {
|
|
1477
|
+
notLoaded = true;
|
|
1472
1478
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1479
|
+
const cfgEnvFileWithPath = this.resolveFileWithPath(
|
|
1480
|
+
optConfigFilePath,
|
|
1481
|
+
cfgFile,
|
|
1482
|
+
this.env
|
|
1483
|
+
);
|
|
1484
|
+
if (cfgEnvFileWithPath !== cfgFileWithPath) {
|
|
1485
|
+
try {
|
|
1486
|
+
const cfgContents = this.requireConfigFile(cfgEnvFileWithPath);
|
|
1487
|
+
this.configValues = { ...this.configValues, ...cfgContents };
|
|
1488
|
+
this.configsLoaded.push(cfgEnvFileWithPath);
|
|
1489
|
+
} catch {
|
|
1490
|
+
notLoadedEnvSpecific = true;
|
|
1481
1491
|
}
|
|
1482
|
-
bindingsLine.push(item);
|
|
1483
|
-
});
|
|
1484
|
-
const allStrings = bindingItems.every((item) => typeof item === "string");
|
|
1485
|
-
if (allStrings) {
|
|
1486
|
-
footerLines.push(bindingsLine.join(""));
|
|
1487
1492
|
} else {
|
|
1488
|
-
|
|
1489
|
-
(item) => typeof item === "string" ? (0, import_react3.createElement)(import_ink3.Text, {}, item) : item
|
|
1490
|
-
);
|
|
1491
|
-
footerLines.push(wrappedBindingsLine);
|
|
1493
|
+
notLoadedEnvSpecific = true;
|
|
1492
1494
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
if (typeof item === "string") {
|
|
1496
|
-
footerLines.push(item);
|
|
1497
|
-
} else {
|
|
1498
|
-
footerLines.push(item);
|
|
1495
|
+
if (notLoaded && notLoadedEnvSpecific) {
|
|
1496
|
+
throw new Error(`can't load config file "${cfgFileWithPath}"`);
|
|
1499
1497
|
}
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Resolve file path with environment-specific naming
|
|
1503
|
+
*/
|
|
1504
|
+
resolveFileWithPath(optConfigFilePath, cfgFile, env) {
|
|
1505
|
+
let cfgFileWithPath = optConfigFilePath ? (0, import_path.isAbsolute)(optConfigFilePath) ? (0, import_path.resolve)(optConfigFilePath, cfgFile) : (0, import_path.resolve)(process.cwd(), optConfigFilePath, cfgFile) : (0, import_path.isAbsolute)(cfgFile) ? cfgFile : (0, import_path.resolve)(process.cwd(), cfgFile);
|
|
1506
|
+
const { basePathWithName, extension } = this.splitPath(cfgFileWithPath);
|
|
1507
|
+
if (env) {
|
|
1508
|
+
cfgFileWithPath = `${basePathWithName}.${env}.${extension || "js"}`;
|
|
1509
|
+
} else {
|
|
1510
|
+
cfgFileWithPath = `${basePathWithName}.${extension || "js"}`;
|
|
1511
|
+
}
|
|
1512
|
+
return cfgFileWithPath;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Split file path into base path and extension
|
|
1516
|
+
*/
|
|
1517
|
+
splitPath(filePath) {
|
|
1518
|
+
const basePathWithName = (0, import_path.join)((0, import_path.dirname)(filePath), (0, import_path.basename)(filePath, (0, import_path.extname)(filePath)));
|
|
1519
|
+
const extension = (0, import_path.extname)(filePath).slice(1);
|
|
1520
|
+
return { basePathWithName, extension };
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Require a configuration file (supports .js and .json)
|
|
1524
|
+
*/
|
|
1525
|
+
requireConfigFile(filePath) {
|
|
1526
|
+
if (!(0, import_fs.existsSync)(filePath)) {
|
|
1527
|
+
throw new Error(`Config file not found: ${filePath}`);
|
|
1528
|
+
}
|
|
1529
|
+
const ext = (0, import_path.extname)(filePath).toLowerCase();
|
|
1530
|
+
if (ext === ".json") {
|
|
1531
|
+
const content = (0, import_fs.readFileSync)(filePath, "utf8");
|
|
1532
|
+
return JSON.parse(content);
|
|
1533
|
+
} else if (ext === ".js") {
|
|
1534
|
+
try {
|
|
1535
|
+
delete require.cache[require.resolve(filePath)];
|
|
1536
|
+
return require(filePath);
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
throw new Error(`Failed to load JS config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
1539
|
+
}
|
|
1540
|
+
} else {
|
|
1541
|
+
throw new Error(`Unsupported file extension: ${ext}`);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Get all parsed data
|
|
1546
|
+
*/
|
|
1547
|
+
getParsed() {
|
|
1548
|
+
return {
|
|
1549
|
+
command: this.commands[0] || "",
|
|
1550
|
+
flags: { ...this.flags },
|
|
1551
|
+
options: { ...this.options },
|
|
1552
|
+
usedKeys: Array.from(this.usedKeys)
|
|
1516
1553
|
};
|
|
1517
|
-
|
|
1518
|
-
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Set prefixes dynamically and re-parse arguments (like legacy)
|
|
1557
|
+
*/
|
|
1558
|
+
setPrefixes(prefixes) {
|
|
1559
|
+
const arr = Array.isArray(prefixes) ? prefixes : prefixes.split(/,\s*/);
|
|
1560
|
+
const sortedArr = arr.sort(
|
|
1561
|
+
(a, b) => a.length < b.length ? 1 : a.length > b.length ? -1 : 0
|
|
1562
|
+
);
|
|
1563
|
+
this.prefixes = sortedArr.map((el) => el.toLowerCase());
|
|
1564
|
+
const args = process.argv.slice(2);
|
|
1565
|
+
this.parseArgs(args);
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
var instance = null;
|
|
1569
|
+
function getArgsInstance() {
|
|
1570
|
+
return instance;
|
|
1519
1571
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1572
|
+
|
|
1573
|
+
// src/params/index.ts
|
|
1574
|
+
var import_joi = __toESM(require("joi"), 1);
|
|
1575
|
+
|
|
1576
|
+
// src/errors.ts
|
|
1577
|
+
var FrameworkError = class extends Error {
|
|
1578
|
+
constructor(message) {
|
|
1579
|
+
super(message);
|
|
1580
|
+
this.name = "FrameworkError";
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
var ParamError = class extends FrameworkError {
|
|
1584
|
+
constructor(message) {
|
|
1585
|
+
super(message);
|
|
1586
|
+
this.name = "ParamError";
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1590
|
+
// src/params/custom-types.ts
|
|
1591
|
+
var joiEdateType = (value, helpers) => {
|
|
1592
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(value)) {
|
|
1593
|
+
const testDate = new Date(value);
|
|
1594
|
+
if (!isNaN(testDate.getTime())) {
|
|
1595
|
+
return value;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
if (value instanceof Date) {
|
|
1599
|
+
return value.toISOString();
|
|
1600
|
+
}
|
|
1601
|
+
if (typeof value !== "string") {
|
|
1602
|
+
value = String(value);
|
|
1603
|
+
}
|
|
1604
|
+
if (value.toLowerCase() === "now") {
|
|
1605
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1606
|
+
}
|
|
1607
|
+
const referenceRegex = /^@(\w+)([+-]\d+[smhdwy])$/i;
|
|
1608
|
+
const referenceMatch = value.match(referenceRegex);
|
|
1609
|
+
if (referenceMatch) {
|
|
1610
|
+
const [, paramName, relativeExpr] = referenceMatch;
|
|
1611
|
+
const context = helpers.prefs?.context;
|
|
1612
|
+
if (!context || !context.params) {
|
|
1613
|
+
throw new ParamError(`Cannot resolve cross-parameter reference @${paramName}: context not available. Ensure parameters are processed with proper context.`);
|
|
1542
1614
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
if (onSelect) {
|
|
1555
|
-
const result = onSelect(selected, selectedIndexRef.current);
|
|
1556
|
-
ctx.close(result);
|
|
1557
|
-
}
|
|
1558
|
-
});
|
|
1559
|
-
if (onEscape) {
|
|
1560
|
-
ctx.setAction("back", () => {
|
|
1561
|
-
const result = onEscape(selectedIndexRef.current);
|
|
1562
|
-
ctx.close(result);
|
|
1563
|
-
});
|
|
1615
|
+
const referencedValue = context.params[paramName];
|
|
1616
|
+
if (referencedValue === void 0 || referencedValue === null) {
|
|
1617
|
+
throw new ParamError(`Cannot resolve @${paramName}: parameter "${paramName}" is not defined or has no value. Parameters are evaluated left-to-right.`);
|
|
1618
|
+
}
|
|
1619
|
+
let referenceDate;
|
|
1620
|
+
if (referencedValue instanceof Date) {
|
|
1621
|
+
referenceDate = referencedValue;
|
|
1622
|
+
} else if (typeof referencedValue === "string") {
|
|
1623
|
+
referenceDate = new Date(referencedValue);
|
|
1624
|
+
if (isNaN(referenceDate.getTime())) {
|
|
1625
|
+
throw new ParamError(`Referenced parameter @${paramName} has invalid date value: ${referencedValue}`);
|
|
1564
1626
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1627
|
+
} else {
|
|
1628
|
+
throw new ParamError(`Referenced parameter @${paramName} is not a valid date type (found: ${typeof referencedValue})`);
|
|
1567
1629
|
}
|
|
1568
|
-
|
|
1630
|
+
const relativeMatch2 = relativeExpr.match(/^([+-])(\d+)([smhdwy])$/i);
|
|
1631
|
+
if (!relativeMatch2) {
|
|
1632
|
+
throw new ParamError(`Invalid relative time expression in @${paramName}${relativeExpr}`);
|
|
1633
|
+
}
|
|
1634
|
+
const [, sign, amount, unit] = relativeMatch2;
|
|
1635
|
+
const offset = calculateTimeOffset(parseInt(amount, 10), unit, sign);
|
|
1636
|
+
const resultDate = new Date(referenceDate.getTime() + offset);
|
|
1637
|
+
return resultDate.toISOString();
|
|
1638
|
+
}
|
|
1639
|
+
const relativeTimeRegex = /^([+-])(\d+)([smhdwy])$/i;
|
|
1640
|
+
const relativeMatch = value.match(relativeTimeRegex);
|
|
1641
|
+
if (relativeMatch) {
|
|
1642
|
+
const [, sign, amount, unit] = relativeMatch;
|
|
1643
|
+
const numAmount = parseInt(amount, 10);
|
|
1644
|
+
if (isNaN(numAmount)) {
|
|
1645
|
+
throw new ParamError(`Invalid relative time amount: ${amount}`);
|
|
1646
|
+
}
|
|
1647
|
+
const offset = calculateTimeOffset(numAmount, unit, sign);
|
|
1648
|
+
const resultDate = new Date(Date.now() + offset);
|
|
1649
|
+
return resultDate.toISOString();
|
|
1650
|
+
}
|
|
1651
|
+
const parsedDate = new Date(value);
|
|
1652
|
+
if (isNaN(parsedDate.getTime())) {
|
|
1653
|
+
throw new ParamError(`Invalid date format: ${value}. Expected a valid date string, "now", relative time expression (e.g., "-2h", "+1d"), or cross-parameter reference (e.g., "@startTime+2h")`);
|
|
1654
|
+
}
|
|
1655
|
+
return parsedDate.toISOString();
|
|
1656
|
+
};
|
|
1657
|
+
function calculateTimeOffset(amount, unit, sign) {
|
|
1658
|
+
let multiplier = 1;
|
|
1659
|
+
switch (unit.toLowerCase()) {
|
|
1660
|
+
case "s":
|
|
1661
|
+
multiplier = 1e3;
|
|
1662
|
+
break;
|
|
1663
|
+
case "m":
|
|
1664
|
+
multiplier = 60 * 1e3;
|
|
1665
|
+
break;
|
|
1666
|
+
case "h":
|
|
1667
|
+
multiplier = 60 * 60 * 1e3;
|
|
1668
|
+
break;
|
|
1669
|
+
case "d":
|
|
1670
|
+
multiplier = 24 * 60 * 60 * 1e3;
|
|
1671
|
+
break;
|
|
1672
|
+
case "w":
|
|
1673
|
+
multiplier = 7 * 24 * 60 * 60 * 1e3;
|
|
1674
|
+
break;
|
|
1675
|
+
case "y":
|
|
1676
|
+
multiplier = 365 * 24 * 60 * 60 * 1e3;
|
|
1677
|
+
break;
|
|
1678
|
+
default:
|
|
1679
|
+
throw new ParamError(`Invalid time unit: ${unit}. Supported units: s, m, h, d, w, y`);
|
|
1680
|
+
}
|
|
1681
|
+
return sign === "+" ? amount * multiplier : -amount * multiplier;
|
|
1569
1682
|
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
if (onSelect) {
|
|
1580
|
-
const result = onSelect(selected, selectedIndexRef.current);
|
|
1581
|
-
ctx.close(result);
|
|
1582
|
-
}
|
|
1583
|
-
});
|
|
1584
|
-
if (onEscape) {
|
|
1585
|
-
ctx.setAction("back", () => {
|
|
1586
|
-
const result = onEscape(selectedIndexRef.current);
|
|
1587
|
-
ctx.close(result);
|
|
1588
|
-
});
|
|
1683
|
+
var joiStringArrayType = (type) => (value, helpers) => {
|
|
1684
|
+
if (value === void 0 || typeof value === "function") {
|
|
1685
|
+
return [];
|
|
1686
|
+
}
|
|
1687
|
+
const arr = value.split(/,\s*/).map((el) => {
|
|
1688
|
+
if (type === "number") {
|
|
1689
|
+
const v = parseInt(el, 10);
|
|
1690
|
+
if (isNaN(v)) {
|
|
1691
|
+
throw new ParamError(`array element "${el}" should be numeric`);
|
|
1589
1692
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1693
|
+
return v;
|
|
1694
|
+
} else if (type === "boolean") {
|
|
1695
|
+
const v = el.match(/true|t|yes|1/i) ? true : el.match(/false|f|no|0/i) ? false : null;
|
|
1696
|
+
if (v === null) {
|
|
1697
|
+
throw new ParamError(`array element "${el}" should be boolean`);
|
|
1698
|
+
}
|
|
1699
|
+
return v;
|
|
1700
|
+
} else if (type === "string") {
|
|
1701
|
+
return el;
|
|
1702
|
+
} else {
|
|
1703
|
+
throw new ParamError(`unknown type "${type}" for array elements`);
|
|
1592
1704
|
}
|
|
1593
1705
|
});
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
var showWordGridScreen = showMultiColumnListScreen;
|
|
1597
|
-
|
|
1598
|
-
// src/screen/ui-elements.ts
|
|
1599
|
-
var import_react4 = require("react");
|
|
1600
|
-
var import_ink4 = require("ink");
|
|
1601
|
-
function ListItem({
|
|
1602
|
-
children,
|
|
1603
|
-
isSelected = false,
|
|
1604
|
-
color = "white",
|
|
1605
|
-
backgroundColor,
|
|
1606
|
-
bold = false,
|
|
1607
|
-
dimColor = false
|
|
1608
|
-
}) {
|
|
1609
|
-
return (0, import_react4.createElement)(
|
|
1610
|
-
import_ink4.Box,
|
|
1611
|
-
{},
|
|
1612
|
-
(0, import_react4.createElement)(import_ink4.Text, {
|
|
1613
|
-
color: isSelected ? backgroundColor || "green" : color,
|
|
1614
|
-
backgroundColor: isSelected ? color : backgroundColor,
|
|
1615
|
-
bold: isSelected || bold,
|
|
1616
|
-
dimColor: !isSelected && dimColor
|
|
1617
|
-
}, children)
|
|
1618
|
-
);
|
|
1619
|
-
}
|
|
1620
|
-
function TextBlock({
|
|
1621
|
-
text,
|
|
1622
|
-
color = "white",
|
|
1623
|
-
dimmed = false,
|
|
1624
|
-
bold = false,
|
|
1625
|
-
maxWidth
|
|
1626
|
-
}) {
|
|
1627
|
-
return (0, import_react4.createElement)(
|
|
1628
|
-
import_ink4.Box,
|
|
1629
|
-
{},
|
|
1630
|
-
(0, import_react4.createElement)(import_ink4.Text, {
|
|
1631
|
-
color,
|
|
1632
|
-
dimColor: dimmed,
|
|
1633
|
-
bold
|
|
1634
|
-
}, text)
|
|
1635
|
-
);
|
|
1636
|
-
}
|
|
1637
|
-
function Divider({ character = "\u2500", width = 80 }) {
|
|
1638
|
-
return (0, import_react4.createElement)(
|
|
1639
|
-
import_ink4.Box,
|
|
1640
|
-
{ marginY: 1 },
|
|
1641
|
-
(0, import_react4.createElement)(import_ink4.Text, { dimColor: true }, character.repeat(width))
|
|
1642
|
-
);
|
|
1643
|
-
}
|
|
1644
|
-
function GridCell({
|
|
1645
|
-
children,
|
|
1646
|
-
width,
|
|
1647
|
-
color = "white",
|
|
1648
|
-
backgroundColor,
|
|
1649
|
-
bold = false,
|
|
1650
|
-
dimColor = false,
|
|
1651
|
-
align = "left"
|
|
1652
|
-
}) {
|
|
1653
|
-
return (0, import_react4.createElement)(
|
|
1654
|
-
import_ink4.Box,
|
|
1655
|
-
{ width },
|
|
1656
|
-
(0, import_react4.createElement)(import_ink4.Text, {
|
|
1657
|
-
color,
|
|
1658
|
-
backgroundColor,
|
|
1659
|
-
bold,
|
|
1660
|
-
dimColor,
|
|
1661
|
-
textAlign: align
|
|
1662
|
-
}, children)
|
|
1663
|
-
);
|
|
1664
|
-
}
|
|
1665
|
-
function InputField({ prompt, value, onChange, onSubmit }) {
|
|
1666
|
-
return (0, import_react4.createElement)(
|
|
1667
|
-
import_ink4.Box,
|
|
1668
|
-
{ flexDirection: "column" },
|
|
1669
|
-
(0, import_react4.createElement)(import_ink4.Text, {}, prompt),
|
|
1670
|
-
(0, import_react4.createElement)(
|
|
1671
|
-
import_ink4.Box,
|
|
1672
|
-
{ marginTop: 1 },
|
|
1673
|
-
(0, import_react4.createElement)(import_ink4.Text, { color: "cyan" }, " > ", value, "_")
|
|
1674
|
-
)
|
|
1675
|
-
);
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
// src/screen/utils.ts
|
|
1679
|
-
function buildBreadcrumb(parts) {
|
|
1680
|
-
if (parts.length === 0) return "";
|
|
1681
|
-
if (parts.length === 1) return parts[0];
|
|
1682
|
-
return parts.slice(1).map((part) => `\u2190 ${part}`).join(" ");
|
|
1683
|
-
}
|
|
1684
|
-
function buildDetailBreadcrumb(path4, suffix = "") {
|
|
1685
|
-
if (path4.length <= 1) {
|
|
1686
|
-
return suffix ? `\u2190 ${suffix}` : path4[0] || "";
|
|
1687
|
-
}
|
|
1688
|
-
const breadcrumb = buildBreadcrumb(path4);
|
|
1689
|
-
return suffix ? `${breadcrumb} ${suffix}` : breadcrumb;
|
|
1690
|
-
}
|
|
1706
|
+
return arr;
|
|
1707
|
+
};
|
|
1691
1708
|
|
|
1692
|
-
// src/
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1709
|
+
// src/params/index.ts
|
|
1710
|
+
var Params = class _Params {
|
|
1711
|
+
context;
|
|
1712
|
+
// Partial context during initialization
|
|
1713
|
+
params = {};
|
|
1714
|
+
definitions = {};
|
|
1715
|
+
args;
|
|
1716
|
+
paramSetters = [];
|
|
1717
|
+
paramGetters = [];
|
|
1718
|
+
trackedParams = [];
|
|
1719
|
+
constructor(context, options = {}) {
|
|
1720
|
+
this.context = context;
|
|
1721
|
+
this.args = context.args;
|
|
1722
|
+
if (Object.keys(options).length > 0) {
|
|
1723
|
+
this.configure(options);
|
|
1724
|
+
}
|
|
1705
1725
|
}
|
|
1706
|
-
|
|
1707
|
-
|
|
1726
|
+
/**
|
|
1727
|
+
* Configure parameters
|
|
1728
|
+
* Only parameters present in options are updated
|
|
1729
|
+
*/
|
|
1730
|
+
configure(options) {
|
|
1731
|
+
for (const [k, v] of Object.entries(options)) {
|
|
1732
|
+
this.params[k] = v;
|
|
1733
|
+
}
|
|
1708
1734
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1735
|
+
/**
|
|
1736
|
+
* Initialize Params from context and CLI parameters
|
|
1737
|
+
* Note: Params is special - it's initialized early with partial context
|
|
1738
|
+
*/
|
|
1739
|
+
static init(context, options) {
|
|
1740
|
+
return new _Params(context, options || {});
|
|
1711
1741
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1742
|
+
/**
|
|
1743
|
+
* Track a parameter request for --stopAfter=init feature
|
|
1744
|
+
*/
|
|
1745
|
+
trackParam(key, definition, value, source) {
|
|
1746
|
+
this.trackedParams.push({
|
|
1747
|
+
key,
|
|
1748
|
+
definition,
|
|
1749
|
+
value,
|
|
1750
|
+
source
|
|
1751
|
+
});
|
|
1714
1752
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1753
|
+
/**
|
|
1754
|
+
* Get all tracked parameters (for --stopAfter=init)
|
|
1755
|
+
*/
|
|
1756
|
+
getTrackedParams() {
|
|
1757
|
+
return [...this.trackedParams];
|
|
1718
1758
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1759
|
+
/**
|
|
1760
|
+
* Get all figured parameters as a record
|
|
1761
|
+
* Returns all parameters that were collected during initialization,
|
|
1762
|
+
* whether from CLI args, options, or defaults
|
|
1763
|
+
*/
|
|
1764
|
+
getAllFigured() {
|
|
1765
|
+
const result = {};
|
|
1766
|
+
for (const param of this.trackedParams) {
|
|
1767
|
+
result[param.key] = {
|
|
1768
|
+
value: param.value,
|
|
1769
|
+
source: param.source
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
return result;
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Clear tracked parameters
|
|
1776
|
+
*/
|
|
1777
|
+
clearTrackedParams() {
|
|
1778
|
+
this.trackedParams = [];
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Assign a parameter definition
|
|
1782
|
+
*/
|
|
1783
|
+
assignDefinition(key, definition) {
|
|
1784
|
+
if (this.definitions[key] && !definition) {
|
|
1785
|
+
return this.definitions[key];
|
|
1786
|
+
}
|
|
1787
|
+
let type;
|
|
1788
|
+
if (!definition) {
|
|
1789
|
+
type = import_joi.default.string();
|
|
1790
|
+
} else if (import_joi.default.isSchema(definition)) {
|
|
1791
|
+
type = definition;
|
|
1792
|
+
} else if (import_joi.default.isSchema(definition.type)) {
|
|
1793
|
+
type = definition.type;
|
|
1794
|
+
} else if (typeof definition === "string") {
|
|
1795
|
+
type = this.toJoi(definition);
|
|
1796
|
+
} else if (typeof definition.type === "string") {
|
|
1797
|
+
type = this.toJoi(definition.type);
|
|
1798
|
+
} else if (!definition.type) {
|
|
1799
|
+
type = import_joi.default.string();
|
|
1800
|
+
} else {
|
|
1801
|
+
type = import_joi.default.string();
|
|
1802
|
+
}
|
|
1803
|
+
if (!this.definitions[key]) {
|
|
1804
|
+
this.definitions[key] = {};
|
|
1805
|
+
}
|
|
1806
|
+
this.definitions[key].type = type;
|
|
1807
|
+
if (definition && definition.values) {
|
|
1808
|
+
if (Array.isArray(definition.values)) {
|
|
1809
|
+
this.definitions[key].values = definition.values;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
return this.definitions[key];
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Convert string definition to Joi schema
|
|
1816
|
+
*/
|
|
1817
|
+
toJoi(str) {
|
|
1818
|
+
let type;
|
|
1819
|
+
if (str.match(/^string|^text/i)) {
|
|
1820
|
+
type = import_joi.default.string();
|
|
1821
|
+
} else if (str.match(/^number|^integer|^int/i)) {
|
|
1822
|
+
type = import_joi.default.number();
|
|
1823
|
+
} else if (str.match(/^boolean|^bool/i)) {
|
|
1824
|
+
type = import_joi.default.boolean();
|
|
1825
|
+
} else if (str.match(/^date/i)) {
|
|
1826
|
+
type = import_joi.default.custom(joiEdateType);
|
|
1827
|
+
} else if (str.match(/^duration/i)) {
|
|
1828
|
+
type = import_joi.default.string().isoDuration();
|
|
1829
|
+
} else if (str.match(/^array/i)) {
|
|
1830
|
+
let elementTypes = "string";
|
|
1831
|
+
const tmp = str.match(/\((.*)\)/);
|
|
1832
|
+
if (tmp && tmp[1].match(/string/i)) {
|
|
1833
|
+
elementTypes = "string";
|
|
1834
|
+
} else if (tmp && tmp[1].match(/number|integer|int/i)) {
|
|
1835
|
+
elementTypes = "number";
|
|
1836
|
+
} else if (tmp && tmp[1].match(/boolean|bool/i)) {
|
|
1837
|
+
elementTypes = "boolean";
|
|
1838
|
+
}
|
|
1839
|
+
type = import_joi.default.custom(joiStringArrayType(elementTypes));
|
|
1840
|
+
} else {
|
|
1841
|
+
type = import_joi.default.string();
|
|
1842
|
+
}
|
|
1843
|
+
const regexForDefault = /\bdefault\s+([^\s]+)/;
|
|
1844
|
+
const matchForDefault = str.match(regexForDefault);
|
|
1845
|
+
if (matchForDefault) {
|
|
1846
|
+
const defValObj = type.validate(matchForDefault[1]);
|
|
1847
|
+
if (defValObj.error) {
|
|
1848
|
+
throw new ParamError(`default value "${defValObj.value}" type mismatch`);
|
|
1849
|
+
}
|
|
1850
|
+
type = type.default(defValObj.value);
|
|
1851
|
+
} else if (str.match(/required/)) {
|
|
1852
|
+
type = type.required();
|
|
1853
|
+
} else {
|
|
1854
|
+
type = type.optional();
|
|
1855
|
+
}
|
|
1856
|
+
return type;
|
|
1722
1857
|
}
|
|
1723
|
-
return lines;
|
|
1724
|
-
}
|
|
1725
|
-
var FooterPresets = {
|
|
1726
1858
|
/**
|
|
1727
|
-
*
|
|
1859
|
+
* Validate a value against a definition
|
|
1728
1860
|
*/
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1861
|
+
validate(key, val, def) {
|
|
1862
|
+
const normalizedVal = val === null ? void 0 : val;
|
|
1863
|
+
const { value, error } = def.type.validate(normalizedVal, {
|
|
1864
|
+
context: { params: this.params },
|
|
1865
|
+
abortEarly: false,
|
|
1866
|
+
allowUnknown: false
|
|
1867
|
+
});
|
|
1868
|
+
if (error) {
|
|
1869
|
+
const errs = error.details.map((el) => el.message).join(", ");
|
|
1870
|
+
throw new ParamError(`"${key}" validation error: ${errs}`);
|
|
1871
|
+
}
|
|
1872
|
+
return value;
|
|
1873
|
+
}
|
|
1735
1874
|
/**
|
|
1736
|
-
*
|
|
1875
|
+
* Get a parameter value with validation
|
|
1737
1876
|
*/
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1877
|
+
get(key, definition) {
|
|
1878
|
+
const def = this.assignDefinition(key, definition);
|
|
1879
|
+
let valFromGetters = void 0;
|
|
1880
|
+
if (def.volatile || true) {
|
|
1881
|
+
valFromGetters = this.runAllRegisteredGetters(key);
|
|
1882
|
+
}
|
|
1883
|
+
const valFromArgs = this.args.get(key);
|
|
1884
|
+
const valFromParams = this.params[key];
|
|
1885
|
+
let source = "default";
|
|
1886
|
+
let value;
|
|
1887
|
+
if (valFromGetters !== void 0 && valFromGetters !== null) {
|
|
1888
|
+
value = this.validate(key, valFromGetters, def);
|
|
1889
|
+
source = "options";
|
|
1890
|
+
} else if (valFromArgs !== void 0 && valFromArgs !== null) {
|
|
1891
|
+
value = this.validate(key, valFromArgs, def);
|
|
1892
|
+
source = "cli";
|
|
1893
|
+
} else if (valFromParams !== void 0 && valFromParams !== null) {
|
|
1894
|
+
value = this.validate(key, valFromParams, def);
|
|
1895
|
+
source = "options";
|
|
1896
|
+
} else {
|
|
1897
|
+
value = this.validate(key, void 0, def);
|
|
1898
|
+
source = "default";
|
|
1899
|
+
}
|
|
1900
|
+
this.trackParam(key, definition || "string", value, source);
|
|
1901
|
+
if (value !== void 0 && def.values && !def.values.includes(value)) {
|
|
1902
|
+
throw new ParamError(`key ${key} should be one of ${def.values}`);
|
|
1903
|
+
}
|
|
1904
|
+
return value;
|
|
1905
|
+
}
|
|
1744
1906
|
/**
|
|
1745
|
-
*
|
|
1907
|
+
* Set a parameter value with validation
|
|
1746
1908
|
*/
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1909
|
+
set(key, val, definition) {
|
|
1910
|
+
if (val && val.type && val.value) {
|
|
1911
|
+
definition = val;
|
|
1912
|
+
val = val.value;
|
|
1913
|
+
}
|
|
1914
|
+
const def = this.assignDefinition(key, definition);
|
|
1915
|
+
if (!this.runAllRegisteredSetters(key, val)) {
|
|
1916
|
+
this.params[key] = val;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1751
1919
|
/**
|
|
1752
|
-
*
|
|
1920
|
+
* Get all parameters from definitions
|
|
1921
|
+
* Processes parameters left-to-right to support cross-parameter references
|
|
1753
1922
|
*/
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1923
|
+
getAll(defs) {
|
|
1924
|
+
const res = {};
|
|
1925
|
+
for (const [k, def] of Object.entries(defs)) {
|
|
1926
|
+
const value = this.get(k, def);
|
|
1927
|
+
res[k] = value;
|
|
1928
|
+
if (value !== void 0) {
|
|
1929
|
+
this.params[k] = value;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
return res;
|
|
1933
|
+
}
|
|
1757
1934
|
/**
|
|
1758
|
-
*
|
|
1935
|
+
* Run all registered getters for a key
|
|
1759
1936
|
*/
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1937
|
+
runAllRegisteredGetters(key) {
|
|
1938
|
+
let val = void 0;
|
|
1939
|
+
for (const getter of this.paramGetters) {
|
|
1940
|
+
val = getter(key, this.definitions[key]);
|
|
1941
|
+
if (val !== void 0 && val !== null) {
|
|
1942
|
+
break;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
return val;
|
|
1946
|
+
}
|
|
1765
1947
|
/**
|
|
1766
|
-
*
|
|
1948
|
+
* Run all registered setters for a key
|
|
1767
1949
|
*/
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
parts.push("Audio available");
|
|
1950
|
+
runAllRegisteredSetters(key, value) {
|
|
1951
|
+
let setterUsed = false;
|
|
1952
|
+
for (const setter of this.paramSetters) {
|
|
1953
|
+
setterUsed = setter(key, value);
|
|
1954
|
+
if (setterUsed) {
|
|
1955
|
+
break;
|
|
1956
|
+
}
|
|
1776
1957
|
}
|
|
1777
|
-
return
|
|
1958
|
+
return setterUsed;
|
|
1778
1959
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1960
|
+
/**
|
|
1961
|
+
* Register a parameter getter
|
|
1962
|
+
*/
|
|
1963
|
+
registerParamGetter(fn) {
|
|
1964
|
+
this.paramGetters.push(fn);
|
|
1783
1965
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
const mainLine = [...navigation, ...actions, ...escape].join(", ");
|
|
1792
|
-
if (mainLine) lines.push(mainLine);
|
|
1793
|
-
lines.push(...others);
|
|
1794
|
-
return lines;
|
|
1795
|
-
}
|
|
1966
|
+
/**
|
|
1967
|
+
* Register a parameter setter
|
|
1968
|
+
*/
|
|
1969
|
+
registerParamSetter(fn) {
|
|
1970
|
+
this.paramSetters.push(fn);
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1796
1973
|
|
|
1797
|
-
// src/screen
|
|
1798
|
-
|
|
1799
|
-
async function load() {
|
|
1800
|
-
if (loadPromise) return loadPromise;
|
|
1801
|
-
loadPromise = Promise.all([
|
|
1802
|
-
import("react"),
|
|
1803
|
-
import("ink")
|
|
1804
|
-
]).then(() => {
|
|
1805
|
-
});
|
|
1806
|
-
return loadPromise;
|
|
1807
|
-
}
|
|
1808
|
-
if (typeof window === "undefined") {
|
|
1809
|
-
load().catch(() => {
|
|
1810
|
-
});
|
|
1811
|
-
}
|
|
1974
|
+
// src/screen.ts
|
|
1975
|
+
init_screen();
|
|
1812
1976
|
|
|
1813
1977
|
// src/filedatabase/index.ts
|
|
1814
1978
|
var import_fs4 = __toESM(require("fs"), 1);
|
|
@@ -3031,6 +3195,7 @@ var ALL_LEVELS = [
|
|
|
3031
3195
|
"response",
|
|
3032
3196
|
"progress"
|
|
3033
3197
|
];
|
|
3198
|
+
var MAX_LEVEL_LENGTH = Math.max(...ALL_LEVELS.map((level) => level.toUpperCase().length));
|
|
3034
3199
|
var LEVEL_COLORS = {
|
|
3035
3200
|
error: import_chalk.default.red.bold,
|
|
3036
3201
|
warn: import_chalk.default.rgb(255, 165, 0),
|
|
@@ -3044,13 +3209,104 @@ var LEVEL_COLORS = {
|
|
|
3044
3209
|
progress: import_chalk.default.green,
|
|
3045
3210
|
results: import_chalk.default.magenta
|
|
3046
3211
|
};
|
|
3047
|
-
var
|
|
3212
|
+
var Logger = class _Logger {
|
|
3213
|
+
context;
|
|
3214
|
+
// Partial context during initialization
|
|
3048
3215
|
options;
|
|
3049
3216
|
transport;
|
|
3050
3217
|
startTimes = {};
|
|
3051
3218
|
lastProgressTimes = {};
|
|
3052
|
-
constructor(options = {}) {
|
|
3053
|
-
this.
|
|
3219
|
+
constructor(context, options = {}) {
|
|
3220
|
+
this.context = context;
|
|
3221
|
+
this.options = this.getDefaultOptions();
|
|
3222
|
+
if (options) {
|
|
3223
|
+
this.configure(options);
|
|
3224
|
+
}
|
|
3225
|
+
this.updateTransport();
|
|
3226
|
+
}
|
|
3227
|
+
/**
|
|
3228
|
+
* Configure logger options
|
|
3229
|
+
* Only parameters present in options are updated
|
|
3230
|
+
*/
|
|
3231
|
+
configure(options) {
|
|
3232
|
+
if (options.mode !== void 0) {
|
|
3233
|
+
this.options.mode = this.isValidMode(options.mode) ? options.mode : "text";
|
|
3234
|
+
}
|
|
3235
|
+
if (options.route !== void 0) {
|
|
3236
|
+
this.options.route = options.route;
|
|
3237
|
+
this.updateTransport();
|
|
3238
|
+
}
|
|
3239
|
+
if (options.prefix !== void 0) {
|
|
3240
|
+
this.options.prefix = options.prefix;
|
|
3241
|
+
}
|
|
3242
|
+
if (options.silent !== void 0) {
|
|
3243
|
+
this.options.silent = options.silent;
|
|
3244
|
+
}
|
|
3245
|
+
if (options.showLevel !== void 0) {
|
|
3246
|
+
this.options.showLevel = options.showLevel;
|
|
3247
|
+
}
|
|
3248
|
+
if (options.timestamp !== void 0) {
|
|
3249
|
+
this.options.timestamp = options.timestamp;
|
|
3250
|
+
}
|
|
3251
|
+
if (options.levels !== void 0) {
|
|
3252
|
+
this.options.levels = this.normalizeLevels(options.levels);
|
|
3253
|
+
}
|
|
3254
|
+
if (options.progress !== void 0) {
|
|
3255
|
+
if (options.progress.withTimes !== void 0) {
|
|
3256
|
+
this.options.progressTimes = options.progress.withTimes;
|
|
3257
|
+
}
|
|
3258
|
+
if (options.progress.throttleMs !== void 0) {
|
|
3259
|
+
this.options.progressThrottle = options.progress.throttleMs;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
/**
|
|
3264
|
+
* Initialize logger from context and CLI parameters
|
|
3265
|
+
*/
|
|
3266
|
+
static init(context, options) {
|
|
3267
|
+
const paramDefs = {
|
|
3268
|
+
mode: "string default text",
|
|
3269
|
+
route: "string default console",
|
|
3270
|
+
prefix: "string",
|
|
3271
|
+
silent: "boolean default false",
|
|
3272
|
+
showLevel: "boolean default true",
|
|
3273
|
+
timestamp: "boolean default false",
|
|
3274
|
+
levels: "string",
|
|
3275
|
+
progressWithTimes: "boolean default false",
|
|
3276
|
+
progressThrottleMs: "number"
|
|
3277
|
+
};
|
|
3278
|
+
const cliParams = context.params.getAll(paramDefs);
|
|
3279
|
+
const config2 = {
|
|
3280
|
+
mode: options?.mode ?? cliParams.mode,
|
|
3281
|
+
route: options?.route ?? cliParams.route,
|
|
3282
|
+
prefix: options?.prefix ?? cliParams.prefix,
|
|
3283
|
+
silent: options?.silent ?? cliParams.silent,
|
|
3284
|
+
showLevel: options?.showLevel ?? cliParams.showLevel,
|
|
3285
|
+
timestamp: options?.timestamp ?? cliParams.timestamp,
|
|
3286
|
+
levels: options?.levels ?? (cliParams.levels ? cliParams.levels.split(",") : void 0),
|
|
3287
|
+
progress: options?.progress ?? {
|
|
3288
|
+
withTimes: cliParams.progressWithTimes,
|
|
3289
|
+
throttleMs: cliParams.progressThrottleMs
|
|
3290
|
+
}
|
|
3291
|
+
};
|
|
3292
|
+
const logger = new _Logger(context, config2);
|
|
3293
|
+
context.logger = logger;
|
|
3294
|
+
return logger;
|
|
3295
|
+
}
|
|
3296
|
+
getDefaultOptions() {
|
|
3297
|
+
return {
|
|
3298
|
+
mode: "text",
|
|
3299
|
+
route: this.shouldUseIpcRoute() ? "ipc" : "console",
|
|
3300
|
+
prefix: void 0,
|
|
3301
|
+
silent: false,
|
|
3302
|
+
showLevel: true,
|
|
3303
|
+
timestamp: false,
|
|
3304
|
+
levels: ALL_LEVELS,
|
|
3305
|
+
progressTimes: false,
|
|
3306
|
+
progressThrottle: void 0
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3309
|
+
updateTransport() {
|
|
3054
3310
|
this.transport = this.options.route === "ipc" ? new ParentProcessTransport() : new ConsoleTransport();
|
|
3055
3311
|
}
|
|
3056
3312
|
setMode(mode) {
|
|
@@ -3159,7 +3415,7 @@ var CliToolkitLogger = class {
|
|
|
3159
3415
|
parts.push(now.toISOString());
|
|
3160
3416
|
}
|
|
3161
3417
|
if (this.options.showLevel) {
|
|
3162
|
-
parts.push(struct.level.toUpperCase());
|
|
3418
|
+
parts.push(struct.level.toUpperCase().padEnd(MAX_LEVEL_LENGTH));
|
|
3163
3419
|
}
|
|
3164
3420
|
if (struct.level === "progress") {
|
|
3165
3421
|
if (struct.prefix) {
|
|
@@ -3193,22 +3449,6 @@ var CliToolkitLogger = class {
|
|
|
3193
3449
|
inspectChunks(chunks) {
|
|
3194
3450
|
return chunks.map((chunk) => import_util.default.inspect(chunk, { colors: true, depth: null })).join(" ");
|
|
3195
3451
|
}
|
|
3196
|
-
normalizeOptions(options) {
|
|
3197
|
-
const { route, mode, prefix, silent, showLevel, timestamp, levels, progress } = options;
|
|
3198
|
-
const shouldUseIpc = this.shouldUseIpcRoute();
|
|
3199
|
-
const normalized = {
|
|
3200
|
-
mode: this.isValidMode(mode) ? mode : "text",
|
|
3201
|
-
route: route ?? (shouldUseIpc ? "ipc" : "console"),
|
|
3202
|
-
prefix,
|
|
3203
|
-
silent: silent ?? false,
|
|
3204
|
-
showLevel: showLevel ?? true,
|
|
3205
|
-
timestamp: timestamp ?? false,
|
|
3206
|
-
levels: this.normalizeLevels(levels),
|
|
3207
|
-
progressTimes: progress?.withTimes ?? false,
|
|
3208
|
-
progressThrottle: progress?.throttleMs
|
|
3209
|
-
};
|
|
3210
|
-
return normalized;
|
|
3211
|
-
}
|
|
3212
3452
|
shouldUseIpcRoute() {
|
|
3213
3453
|
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
3214
3454
|
return false;
|
|
@@ -3239,35 +3479,44 @@ var CliToolkitLogger = class {
|
|
|
3239
3479
|
|
|
3240
3480
|
// src/init/index.ts
|
|
3241
3481
|
var import_events = require("events");
|
|
3482
|
+
function extractComponentOptions(opts, componentName) {
|
|
3483
|
+
const reservedKeys = ["overrides", "defaults", "modules"];
|
|
3484
|
+
const componentOptions = {};
|
|
3485
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
3486
|
+
if (!reservedKeys.includes(key)) {
|
|
3487
|
+
componentOptions[key] = value;
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
return componentOptions;
|
|
3491
|
+
}
|
|
3242
3492
|
function setup(opts = {}) {
|
|
3243
|
-
const args =
|
|
3493
|
+
const args = Args.init({
|
|
3244
3494
|
overrides: opts.overrides || {},
|
|
3245
3495
|
defaults: opts.defaults || {}
|
|
3246
3496
|
});
|
|
3247
|
-
const
|
|
3248
|
-
const loggerOptions = opts.logger || {};
|
|
3249
|
-
const logger = new CliToolkitLogger({
|
|
3250
|
-
mode: loggerOptions.mode || "text",
|
|
3251
|
-
route: loggerOptions.route || "console",
|
|
3252
|
-
prefix: loggerOptions.prefix,
|
|
3253
|
-
silent: loggerOptions.silent,
|
|
3254
|
-
showLevel: loggerOptions.showLevel,
|
|
3255
|
-
timestamp: loggerOptions.timestamp,
|
|
3256
|
-
levels: loggerOptions.levels
|
|
3257
|
-
});
|
|
3258
|
-
const cleanupFunctions = [];
|
|
3259
|
-
const context = {
|
|
3497
|
+
const partialContext = {
|
|
3260
3498
|
args,
|
|
3261
|
-
params,
|
|
3262
|
-
logger,
|
|
3263
3499
|
emitter: new import_events.EventEmitter(),
|
|
3264
3500
|
isStop: () => false,
|
|
3265
|
-
|
|
3266
|
-
cleanupFunctions,
|
|
3501
|
+
cleanupFunctions: [],
|
|
3267
3502
|
registerCleanup: (fn) => {
|
|
3268
|
-
cleanupFunctions.push(fn);
|
|
3503
|
+
partialContext.cleanupFunctions.push(fn);
|
|
3269
3504
|
}
|
|
3270
3505
|
};
|
|
3506
|
+
const params = Params.init(partialContext, opts.overrides || {});
|
|
3507
|
+
partialContext.params = params;
|
|
3508
|
+
const loggerOptions = extractComponentOptions(opts, "logger");
|
|
3509
|
+
const logger = Logger.init(partialContext, loggerOptions);
|
|
3510
|
+
partialContext.logger = logger;
|
|
3511
|
+
const context = {
|
|
3512
|
+
args,
|
|
3513
|
+
params,
|
|
3514
|
+
logger,
|
|
3515
|
+
emitter: partialContext.emitter,
|
|
3516
|
+
isStop: partialContext.isStop,
|
|
3517
|
+
cleanupFunctions: partialContext.cleanupFunctions,
|
|
3518
|
+
registerCleanup: partialContext.registerCleanup
|
|
3519
|
+
};
|
|
3271
3520
|
logger.debug("[setup] completed successfully");
|
|
3272
3521
|
return context;
|
|
3273
3522
|
}
|
|
@@ -3305,7 +3554,6 @@ function setupContext(opts = {}) {
|
|
|
3305
3554
|
defaultFileSynopsisFunction,
|
|
3306
3555
|
defaultVersionSynopsisFunction,
|
|
3307
3556
|
getArgsInstance,
|
|
3308
|
-
getParamsInstance,
|
|
3309
3557
|
h,
|
|
3310
3558
|
joiEdateType,
|
|
3311
3559
|
joiStringArrayType,
|