termkit 2.3.1 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -4
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +315 -56
- package/dist/index.mjs +305 -47
- package/dist/models/Command.d.ts +2 -0
- package/dist/models/Command.d.ts.map +1 -1
- package/dist/models/MultiSelect.d.ts.map +1 -1
- package/dist/models/Select.d.ts.map +1 -1
- package/dist/models/Shell.d.ts +23 -0
- package/dist/models/Shell.d.ts.map +1 -0
- package/dist/utils/color.d.ts +2 -0
- package/dist/utils/color.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
Bar: () => Bar,
|
|
34
34
|
Chart: () => Chart_exports,
|
|
35
|
-
Color: () =>
|
|
35
|
+
Color: () => import_cosmetic5.default,
|
|
36
36
|
Column: () => Column,
|
|
37
37
|
Command: () => Command,
|
|
38
38
|
Input: () => Input,
|
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
Program: () => Program,
|
|
44
44
|
Scrollbox: () => Scrollbox,
|
|
45
45
|
Select: () => Select,
|
|
46
|
+
Shell: () => Shell,
|
|
46
47
|
Spinner: () => Spinner,
|
|
47
48
|
Table: () => Table,
|
|
48
49
|
TermKit: () => TermKit,
|
|
@@ -191,6 +192,8 @@ function findCommand(array, commands) {
|
|
|
191
192
|
var RESET = "\x1B[0m";
|
|
192
193
|
var SHOW_CURSOR = "\x1B[?25h";
|
|
193
194
|
var HIDE_CURSOR = "\x1B[?25l";
|
|
195
|
+
var DISABLE_WRAP = "\x1B[?7l";
|
|
196
|
+
var ENABLE_WRAP = "\x1B[?7h";
|
|
194
197
|
var BOLD = "\x1B[1m";
|
|
195
198
|
var FAINT = "\x1B[2m";
|
|
196
199
|
var GREEN = "\x1B[32m";
|
|
@@ -557,15 +560,22 @@ var Select = class {
|
|
|
557
560
|
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Select requires an interactive terminal");
|
|
558
561
|
let selectedIndex = 0;
|
|
559
562
|
let viewportOffset = 0;
|
|
563
|
+
let visibleStart = 0;
|
|
560
564
|
let searchQuery = "";
|
|
561
565
|
let lastDrawnLines = 0;
|
|
562
566
|
const skipItem = { label: this.skipLabel };
|
|
567
|
+
const computeMaxHeight = () => {
|
|
568
|
+
const termRows = process.stdout.rows ?? 24;
|
|
569
|
+
const reserved = 1 + (this.searchEnabled ? 1 : 0);
|
|
570
|
+
const fit = Math.max(1, termRows - reserved - 1);
|
|
571
|
+
return this.maxHeight ? Math.min(this.maxHeight, fit) : fit;
|
|
572
|
+
};
|
|
563
573
|
const getFiltered = () => {
|
|
564
574
|
if (!this.searchEnabled || searchQuery === "") return items;
|
|
565
575
|
const q = searchQuery.toLowerCase();
|
|
566
576
|
return items.filter((item) => item.label.toLowerCase().includes(q) || (item.description?.toLowerCase().includes(q) ?? false));
|
|
567
577
|
};
|
|
568
|
-
process.stdout.write(HIDE_CURSOR);
|
|
578
|
+
process.stdout.write(HIDE_CURSOR + DISABLE_WRAP);
|
|
569
579
|
const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
|
|
570
580
|
const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
|
|
571
581
|
process.stdout.write(`${glyph}${prompt}
|
|
@@ -574,13 +584,17 @@ var Select = class {
|
|
|
574
584
|
const filtered = getFiltered();
|
|
575
585
|
const allItems = [...filtered, skipItem];
|
|
576
586
|
if (selectedIndex >= allItems.length) selectedIndex = allItems.length - 1;
|
|
577
|
-
|
|
587
|
+
const maxHeight = computeMaxHeight();
|
|
588
|
+
const useViewport = allItems.length > maxHeight;
|
|
589
|
+
if (useViewport) {
|
|
578
590
|
if (selectedIndex < viewportOffset) viewportOffset = selectedIndex;
|
|
579
|
-
else if (selectedIndex >= viewportOffset +
|
|
580
|
-
viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, allItems.length -
|
|
591
|
+
else if (selectedIndex >= viewportOffset + maxHeight) viewportOffset = selectedIndex - maxHeight + 1;
|
|
592
|
+
viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, allItems.length - maxHeight)));
|
|
593
|
+
} else {
|
|
594
|
+
viewportOffset = 0;
|
|
581
595
|
}
|
|
582
|
-
|
|
583
|
-
const visibleEnd =
|
|
596
|
+
visibleStart = useViewport ? viewportOffset : 0;
|
|
597
|
+
const visibleEnd = useViewport ? Math.min(allItems.length, viewportOffset + maxHeight) : allItems.length;
|
|
584
598
|
if (redraw) {
|
|
585
599
|
if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP(lastDrawnLines));
|
|
586
600
|
process.stdout.write("\r\x1B[0J");
|
|
@@ -596,8 +610,7 @@ var Select = class {
|
|
|
596
610
|
const item = allItems[i];
|
|
597
611
|
const isSelected = i === selectedIndex;
|
|
598
612
|
const isSkip = i === filtered.length;
|
|
599
|
-
const
|
|
600
|
-
const numStr = isSkip ? "0." : `${relativeNum}.`;
|
|
613
|
+
const numStr = isSkip ? "0." : `${i + 1}.`;
|
|
601
614
|
const desc = item.description ? ` ${colorText(this.descriptionColor, `\u2014 ${item.description}`)}` : "";
|
|
602
615
|
let marker, tail;
|
|
603
616
|
if (isSelected && pulse) {
|
|
@@ -624,9 +637,9 @@ var Select = class {
|
|
|
624
637
|
process.stdin.setRawMode(false);
|
|
625
638
|
process.stdin.pause();
|
|
626
639
|
process.stdin.removeListener("data", onKey);
|
|
627
|
-
process.stdout.write(SHOW_CURSOR);
|
|
640
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
628
641
|
});
|
|
629
|
-
const cleanup = () => {
|
|
642
|
+
const cleanup = (selectedLabel) => {
|
|
630
643
|
deregisterCleanup();
|
|
631
644
|
if (timer) {
|
|
632
645
|
clearInterval(timer);
|
|
@@ -635,6 +648,11 @@ var Select = class {
|
|
|
635
648
|
process.stdin.setRawMode(false);
|
|
636
649
|
process.stdin.pause();
|
|
637
650
|
process.stdin.removeListener("data", onKey);
|
|
651
|
+
process.stdout.write(CURSOR_UP(lastDrawnLines + 1));
|
|
652
|
+
process.stdout.write("\r\x1B[0J");
|
|
653
|
+
process.stdout.write(ENABLE_WRAP);
|
|
654
|
+
process.stdout.write(`${glyph}${prompt}: ${selectedLabel ?? this.skipLabel}
|
|
655
|
+
`);
|
|
638
656
|
process.stdout.write(SHOW_CURSOR);
|
|
639
657
|
};
|
|
640
658
|
if (this._parsedColors.length >= 2) {
|
|
@@ -657,10 +675,19 @@ var Select = class {
|
|
|
657
675
|
selectedIndex = (selectedIndex + 1) % allItems.length;
|
|
658
676
|
renderList(true);
|
|
659
677
|
} else if (str === "\r" || str === "\n") {
|
|
660
|
-
|
|
661
|
-
|
|
678
|
+
const result = selectedIndex === filtered.length ? null : filtered[selectedIndex] ?? null;
|
|
679
|
+
cleanup(result?.label ?? null);
|
|
680
|
+
resolve(result);
|
|
662
681
|
} else if (str === "") {
|
|
663
|
-
|
|
682
|
+
deregisterCleanup();
|
|
683
|
+
if (timer) {
|
|
684
|
+
clearInterval(timer);
|
|
685
|
+
timer = null;
|
|
686
|
+
}
|
|
687
|
+
process.stdin.setRawMode(false);
|
|
688
|
+
process.stdin.pause();
|
|
689
|
+
process.stdin.removeListener("data", onKey);
|
|
690
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
664
691
|
process.exit(130);
|
|
665
692
|
} else if (this.searchEnabled) {
|
|
666
693
|
if (str === "\x7F" || str === "\b") {
|
|
@@ -678,8 +705,7 @@ var Select = class {
|
|
|
678
705
|
} else {
|
|
679
706
|
const n = parseInt(str);
|
|
680
707
|
if (!isNaN(n) && n >= 0 && n <= Math.min(items.length, 9)) {
|
|
681
|
-
|
|
682
|
-
selectedIndex = n === 0 ? allItems.length - 1 : visibleStart + n - 1;
|
|
708
|
+
selectedIndex = n === 0 ? allItems.length - 1 : n - 1;
|
|
683
709
|
selectedIndex = Math.max(0, Math.min(selectedIndex, allItems.length - 1));
|
|
684
710
|
renderList(true);
|
|
685
711
|
}
|
|
@@ -860,7 +886,7 @@ var Command = class {
|
|
|
860
886
|
constructor(data) {
|
|
861
887
|
this.actionFunction = null;
|
|
862
888
|
this.commandsArray = [];
|
|
863
|
-
this.commandStrings = ["help"];
|
|
889
|
+
this.commandStrings = ["help", "version"];
|
|
864
890
|
this.info = null;
|
|
865
891
|
this.middlewaresArray = [];
|
|
866
892
|
this.name = null;
|
|
@@ -930,6 +956,9 @@ var Command = class {
|
|
|
930
956
|
const recursive = source?.includes("-r") === true || source?.includes("--recursive") === true;
|
|
931
957
|
this.printHelp(this.name ?? "Program", recursive);
|
|
932
958
|
}
|
|
959
|
+
printVersion() {
|
|
960
|
+
console.log(this.versionString ?? "");
|
|
961
|
+
}
|
|
933
962
|
printHelp(fullName, recursive) {
|
|
934
963
|
const table = [];
|
|
935
964
|
let program = fullName;
|
|
@@ -989,29 +1018,32 @@ var Command = class {
|
|
|
989
1018
|
}
|
|
990
1019
|
}
|
|
991
1020
|
}
|
|
992
|
-
async
|
|
993
|
-
const array = [...
|
|
994
|
-
array.splice(0, 2);
|
|
1021
|
+
async _execute(tokens) {
|
|
1022
|
+
const array = [...tokens];
|
|
995
1023
|
let command = this;
|
|
996
1024
|
const options = { _source: Array.from(array) };
|
|
1025
|
+
if (this.versionString && (array.includes("--version") || array.includes("-V"))) {
|
|
1026
|
+
return this.printVersion();
|
|
1027
|
+
}
|
|
997
1028
|
const ddIdx = array.indexOf("--");
|
|
998
1029
|
if (ddIdx !== -1) {
|
|
999
1030
|
options._ = array.splice(ddIdx + 1);
|
|
1000
1031
|
array.splice(ddIdx, 1);
|
|
1001
1032
|
}
|
|
1002
1033
|
while (array.length) {
|
|
1003
|
-
if (!array.includes("help")) {
|
|
1034
|
+
if (!array.includes("help") && !array.includes("version")) {
|
|
1004
1035
|
Object.assign(options, await findOptions(array, command));
|
|
1005
1036
|
const cmdVars = await findCommandVariables(array, command);
|
|
1006
1037
|
if (cmdVars) Object.assign(options, cmdVars);
|
|
1007
1038
|
Object.assign(options, await findOptions(array, command));
|
|
1008
1039
|
}
|
|
1009
1040
|
if (array.length) {
|
|
1010
|
-
if (!array.includes("help")) {
|
|
1041
|
+
if (!array.includes("help") && !array.includes("version")) {
|
|
1011
1042
|
for (const mw of command.middlewaresArray) await mw(options);
|
|
1012
1043
|
}
|
|
1013
1044
|
const next = findCommand(array, command.commandsArray);
|
|
1014
1045
|
if (!next && array[0] === "help") return command.help(options._source);
|
|
1046
|
+
if (!next && array[0] === "version") return this.printVersion();
|
|
1015
1047
|
if (!next) throw new SyntaxError(`Unknown command: ${array[0]}`);
|
|
1016
1048
|
const name = command.name ?? "_base";
|
|
1017
1049
|
if (!options._parents) options._parents = {};
|
|
@@ -1035,10 +1067,225 @@ var Command = class {
|
|
|
1035
1067
|
}
|
|
1036
1068
|
for (const mw of command.middlewaresArray) await mw(options);
|
|
1037
1069
|
if (command.actionFunction) return command.actionFunction(options);
|
|
1038
|
-
|
|
1039
|
-
|
|
1070
|
+
return command.help(options._source);
|
|
1071
|
+
}
|
|
1072
|
+
async parse(input2) {
|
|
1073
|
+
return this._execute(input2.slice(2));
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
// src/models/Shell.ts
|
|
1078
|
+
var import_cosmetic2 = __toESM(require("cosmetic"));
|
|
1079
|
+
var readline = __toESM(require("readline"));
|
|
1080
|
+
var Shell = class {
|
|
1081
|
+
constructor(root, opts = {}) {
|
|
1082
|
+
this.rl = null;
|
|
1083
|
+
this.root = root;
|
|
1084
|
+
this.opts = {
|
|
1085
|
+
mode: opts.mode ?? "drill",
|
|
1086
|
+
prompt: opts.prompt ?? root.name ?? "shell",
|
|
1087
|
+
promptColor: opts.promptColor ?? "",
|
|
1088
|
+
banner: opts.banner ?? "",
|
|
1089
|
+
exitCommands: opts.exitCommands ?? ["exit", "quit"],
|
|
1090
|
+
historySize: opts.historySize ?? 100
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
async run() {
|
|
1094
|
+
this.rl = readline.createInterface({
|
|
1095
|
+
input: process.stdin,
|
|
1096
|
+
output: process.stdout,
|
|
1097
|
+
terminal: true,
|
|
1098
|
+
historySize: this.opts.historySize,
|
|
1099
|
+
completer: this.opts.mode === "free" ? makeCompleter(this.root) : void 0
|
|
1100
|
+
});
|
|
1101
|
+
if (this.opts.banner) console.log(this.opts.banner);
|
|
1102
|
+
try {
|
|
1103
|
+
if (this.opts.mode === "free") {
|
|
1104
|
+
await this.freeLoop();
|
|
1105
|
+
} else {
|
|
1106
|
+
await this.drillLoop();
|
|
1107
|
+
}
|
|
1108
|
+
} finally {
|
|
1109
|
+
this.rl.close();
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
// ── Drill mode ────────────────────────────────────────────────────────
|
|
1113
|
+
async drillLoop() {
|
|
1114
|
+
while (true) {
|
|
1115
|
+
const exited = await this.drillFrom(this.root, [this.root.name ?? "shell"]);
|
|
1116
|
+
if (exited) return;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
async drillFrom(cmd, breadcrumb) {
|
|
1120
|
+
while (true) {
|
|
1121
|
+
const token = await this.promptDrill(cmd, breadcrumb);
|
|
1122
|
+
if (token === null) {
|
|
1123
|
+
return breadcrumb.length === 1;
|
|
1124
|
+
}
|
|
1125
|
+
if (this.opts.exitCommands.includes(token)) process.exit(0);
|
|
1126
|
+
if (token === "..") return false;
|
|
1127
|
+
if (token === "help") {
|
|
1128
|
+
cmd.help();
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
const sub = cmd.commandsArray.find((c) => c.name === token);
|
|
1132
|
+
if (!sub) {
|
|
1133
|
+
process.stderr.write(`Unknown command: ${token}
|
|
1134
|
+
`);
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
if (sub.commandsArray.length > 0) {
|
|
1138
|
+
const exited = await this.drillFrom(sub, [...breadcrumb, sub.name ?? ""]);
|
|
1139
|
+
if (exited) return true;
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
const vars = await this.gatherVariables(sub);
|
|
1143
|
+
const tokens = buildTokens(sub, vars);
|
|
1144
|
+
try {
|
|
1145
|
+
await sub._execute(tokens);
|
|
1146
|
+
} catch (err) {
|
|
1147
|
+
process.stderr.write(`${err instanceof Error ? err.message : err}
|
|
1148
|
+
`);
|
|
1149
|
+
}
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
async promptDrill(cmd, breadcrumb) {
|
|
1154
|
+
const subs = cmd.commandsArray.map((c) => c.name ?? "").filter(Boolean);
|
|
1155
|
+
const label = this.colorize(breadcrumb.join(" "));
|
|
1156
|
+
const choices = [...subs, "help"].join(", ");
|
|
1157
|
+
process.stdout.write(`
|
|
1158
|
+
${label} ${choices}
|
|
1159
|
+
`);
|
|
1160
|
+
return new Promise((resolve) => {
|
|
1161
|
+
let resolved = false;
|
|
1162
|
+
const done = (val) => {
|
|
1163
|
+
if (!resolved) {
|
|
1164
|
+
resolved = true;
|
|
1165
|
+
resolve(val);
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
this.rl.question("> ", (answer) => done(answer.trim() || null));
|
|
1169
|
+
this.rl.once("close", () => done(null));
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
async gatherVariables(cmd) {
|
|
1173
|
+
const result = {};
|
|
1174
|
+
if (!cmd.variables) return result;
|
|
1175
|
+
for (const v of cmd.variables) {
|
|
1176
|
+
const name = v.name ?? "value";
|
|
1177
|
+
const hint = v.hint ? ` ${v.hint}` : "";
|
|
1178
|
+
while (true) {
|
|
1179
|
+
const answer = await new Promise((resolve) => {
|
|
1180
|
+
let resolved = false;
|
|
1181
|
+
const done = (val) => {
|
|
1182
|
+
if (!resolved) {
|
|
1183
|
+
resolved = true;
|
|
1184
|
+
resolve(val);
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
this.rl.question(` ${name}${hint}: `, (ans) => done(ans.trim() || null));
|
|
1188
|
+
this.rl.once("close", () => done(null));
|
|
1189
|
+
});
|
|
1190
|
+
const value = answer ?? v.default ?? null;
|
|
1191
|
+
if (!value && v.required) {
|
|
1192
|
+
process.stderr.write(` ${name} is required
|
|
1193
|
+
`);
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
if (value) {
|
|
1197
|
+
if (v.type === "enum" && v.enum && !v.enum.includes(value)) {
|
|
1198
|
+
process.stderr.write(` Must be one of: ${v.enum.join(", ")}
|
|
1199
|
+
`);
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
result[name] = value;
|
|
1203
|
+
}
|
|
1204
|
+
break;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return result;
|
|
1208
|
+
}
|
|
1209
|
+
// ── Free mode ─────────────────────────────────────────────────────────
|
|
1210
|
+
async freeLoop() {
|
|
1211
|
+
const prompt = `${this.colorize(this.opts.prompt)} > `;
|
|
1212
|
+
this.rl.setPrompt(prompt);
|
|
1213
|
+
this.rl.prompt();
|
|
1214
|
+
for await (const line of this.rl) {
|
|
1215
|
+
const trimmed = line.trim();
|
|
1216
|
+
if (!trimmed) {
|
|
1217
|
+
this.rl.prompt();
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
if (this.opts.exitCommands.includes(trimmed)) break;
|
|
1221
|
+
const tokens = tokenize(trimmed);
|
|
1222
|
+
try {
|
|
1223
|
+
await this.root._execute(tokens);
|
|
1224
|
+
} catch (err) {
|
|
1225
|
+
process.stderr.write(`${err instanceof Error ? err.message : err}
|
|
1226
|
+
`);
|
|
1227
|
+
}
|
|
1228
|
+
this.rl.prompt();
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
1232
|
+
colorize(text) {
|
|
1233
|
+
const c = this.opts.promptColor;
|
|
1234
|
+
if (!c || !process.stdout.isTTY) return text;
|
|
1235
|
+
try {
|
|
1236
|
+
if (c.startsWith("#")) return import_cosmetic2.default.hex(c).encoder(text);
|
|
1237
|
+
if (/^\d+$/.test(c)) return import_cosmetic2.default.xterm(Number(c)).encoder(text);
|
|
1238
|
+
const style = import_cosmetic2.default[c];
|
|
1239
|
+
if (style && typeof style.encoder === "function") return style.encoder(text);
|
|
1240
|
+
} catch {
|
|
1241
|
+
}
|
|
1242
|
+
return text;
|
|
1040
1243
|
}
|
|
1041
1244
|
};
|
|
1245
|
+
function tokenize(line) {
|
|
1246
|
+
const tokens = [];
|
|
1247
|
+
let current = "";
|
|
1248
|
+
let inQuote = null;
|
|
1249
|
+
for (const ch of line) {
|
|
1250
|
+
if (inQuote) {
|
|
1251
|
+
if (ch === inQuote) {
|
|
1252
|
+
inQuote = null;
|
|
1253
|
+
} else {
|
|
1254
|
+
current += ch;
|
|
1255
|
+
}
|
|
1256
|
+
} else if (ch === '"' || ch === "'") {
|
|
1257
|
+
inQuote = ch;
|
|
1258
|
+
} else if (ch === " " || ch === " ") {
|
|
1259
|
+
if (current) {
|
|
1260
|
+
tokens.push(current);
|
|
1261
|
+
current = "";
|
|
1262
|
+
}
|
|
1263
|
+
} else {
|
|
1264
|
+
current += ch;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
if (current) tokens.push(current);
|
|
1268
|
+
return tokens;
|
|
1269
|
+
}
|
|
1270
|
+
function buildTokens(cmd, vars) {
|
|
1271
|
+
if (!cmd.variables) return [];
|
|
1272
|
+
return cmd.variables.map((v) => vars[v.name ?? ""]).filter((v) => v !== void 0 && v !== "");
|
|
1273
|
+
}
|
|
1274
|
+
function makeCompleter(root) {
|
|
1275
|
+
return (line) => {
|
|
1276
|
+
const tokens = tokenize(line);
|
|
1277
|
+
let cmd = root;
|
|
1278
|
+
for (const token of tokens.slice(0, -1)) {
|
|
1279
|
+
const sub = cmd.commandsArray.find((c) => c.name === token);
|
|
1280
|
+
if (!sub) break;
|
|
1281
|
+
cmd = sub;
|
|
1282
|
+
}
|
|
1283
|
+
const partial = tokens[tokens.length - 1] ?? "";
|
|
1284
|
+
const names = [...cmd.commandsArray.map((c) => c.name ?? "").filter(Boolean), "help"];
|
|
1285
|
+
const hits = names.filter((n) => n.startsWith(partial));
|
|
1286
|
+
return [hits.length ? hits : names, partial];
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1042
1289
|
|
|
1043
1290
|
// src/models/Bar.ts
|
|
1044
1291
|
var _Bar = class _Bar {
|
|
@@ -1500,7 +1747,7 @@ __export(Chart_exports, {
|
|
|
1500
1747
|
Sparkline: () => Sparkline,
|
|
1501
1748
|
VerticalBar: () => VerticalBar
|
|
1502
1749
|
});
|
|
1503
|
-
var
|
|
1750
|
+
var import_cosmetic3 = __toESM(require("cosmetic"));
|
|
1504
1751
|
|
|
1505
1752
|
// src/utils/padLeft.ts
|
|
1506
1753
|
var padLeft = (string, padding) => {
|
|
@@ -1521,9 +1768,9 @@ function formatNum(n) {
|
|
|
1521
1768
|
}
|
|
1522
1769
|
function applyConfigColor(s) {
|
|
1523
1770
|
const c = config.color;
|
|
1524
|
-
if (typeof c === "number") return
|
|
1525
|
-
if (c.startsWith("#")) return
|
|
1526
|
-
return
|
|
1771
|
+
if (typeof c === "number") return import_cosmetic3.default.xterm(c).encoder(s);
|
|
1772
|
+
if (c.startsWith("#")) return import_cosmetic3.default.hex(c).encoder(s);
|
|
1773
|
+
return import_cosmetic3.default[c].encoder(s);
|
|
1527
1774
|
}
|
|
1528
1775
|
function applyPadding(str, paddingX, paddingY) {
|
|
1529
1776
|
const lines = str.split("\n");
|
|
@@ -1549,7 +1796,7 @@ var Bar2 = class {
|
|
|
1549
1796
|
const cols = options.width ?? process.stdout.columns ?? 80;
|
|
1550
1797
|
const available = cols - maxKeyLen - maxValueLen - 3;
|
|
1551
1798
|
const scale = maxValue > 0 && available > 0 ? available / maxValue : 0;
|
|
1552
|
-
const encodeKey = (s) => typeof config.color === "number" ?
|
|
1799
|
+
const encodeKey = (s) => typeof config.color === "number" ? import_cosmetic3.default.xterm(config.color).encoder(s) : config.color.startsWith("#") ? import_cosmetic3.default.hex(config.color).encoder(s) : import_cosmetic3.default[config.color].encoder(s);
|
|
1553
1800
|
for (const item of data) {
|
|
1554
1801
|
if (!item) {
|
|
1555
1802
|
this.string += "\n";
|
|
@@ -1559,7 +1806,7 @@ var Bar2 = class {
|
|
|
1559
1806
|
const keyPart = (rawKey ? encodeKey(rawKey) : "") + " ".repeat(maxKeyLen - stringLength(rawKey));
|
|
1560
1807
|
const barWidth = Math.max(1, Math.floor(item.value * scale));
|
|
1561
1808
|
let bar = (item.character ?? " ").repeat(barWidth);
|
|
1562
|
-
bar = item.style ? item.style(bar) :
|
|
1809
|
+
bar = item.style ? item.style(bar) : import_cosmetic3.default.background.white.encoder(bar);
|
|
1563
1810
|
this.string += `${keyPart}|${bar} ${item.value}
|
|
1564
1811
|
`;
|
|
1565
1812
|
}
|
|
@@ -1608,7 +1855,7 @@ var VerticalBar = class {
|
|
|
1608
1855
|
}
|
|
1609
1856
|
this.string += "\u2500".repeat(data.length * colWidth) + "\n";
|
|
1610
1857
|
if (data.some((item) => item?.key)) {
|
|
1611
|
-
const encodeLabel = (s) => typeof config.color === "number" ?
|
|
1858
|
+
const encodeLabel = (s) => typeof config.color === "number" ? import_cosmetic3.default.xterm(config.color).encoder(s) : config.color.startsWith("#") ? import_cosmetic3.default.hex(config.color).encoder(s) : import_cosmetic3.default[config.color].encoder(s);
|
|
1612
1859
|
let labels = "";
|
|
1613
1860
|
for (const item of data) {
|
|
1614
1861
|
if (!item?.key) {
|
|
@@ -2069,7 +2316,6 @@ var MultiBar = class {
|
|
|
2069
2316
|
};
|
|
2070
2317
|
|
|
2071
2318
|
// src/models/MultiSelect.ts
|
|
2072
|
-
var CLEAR_LINE2 = "\x1B[2K";
|
|
2073
2319
|
var CURSOR_UP3 = (n) => `\x1B[${n}A`;
|
|
2074
2320
|
var DIM2 = "\x1B[2m";
|
|
2075
2321
|
var MultiSelect = class {
|
|
@@ -2108,7 +2354,7 @@ var MultiSelect = class {
|
|
|
2108
2354
|
const checked = /* @__PURE__ */ new Set();
|
|
2109
2355
|
let error = null;
|
|
2110
2356
|
let lastDrawnLines = 0;
|
|
2111
|
-
process.stdout.write(HIDE_CURSOR);
|
|
2357
|
+
process.stdout.write(HIDE_CURSOR + DISABLE_WRAP);
|
|
2112
2358
|
const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
|
|
2113
2359
|
const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
|
|
2114
2360
|
process.stdout.write(`${glyph}${prompt}
|
|
@@ -2118,16 +2364,26 @@ var MultiSelect = class {
|
|
|
2118
2364
|
const q = searchQuery.toLowerCase();
|
|
2119
2365
|
return items.map((item, i) => ({ item, originalIndex: i })).filter(({ item }) => item.label.toLowerCase().includes(q) || (item.description?.toLowerCase().includes(q) ?? false));
|
|
2120
2366
|
};
|
|
2367
|
+
const computeMaxHeight = () => {
|
|
2368
|
+
const termRows = process.stdout.rows ?? 24;
|
|
2369
|
+
const reserved = 1 + (this.searchEnabled ? 1 : 0) + 1 + 1;
|
|
2370
|
+
const fit = Math.max(1, termRows - reserved - 1);
|
|
2371
|
+
return this.maxHeight ? Math.min(this.maxHeight, fit) : fit;
|
|
2372
|
+
};
|
|
2121
2373
|
const renderList = (redraw) => {
|
|
2122
2374
|
const filtered = getFiltered();
|
|
2123
2375
|
if (cursor >= filtered.length) cursor = Math.max(0, filtered.length - 1);
|
|
2124
|
-
|
|
2376
|
+
const maxHeight = computeMaxHeight();
|
|
2377
|
+
const useViewport = filtered.length > maxHeight;
|
|
2378
|
+
if (useViewport) {
|
|
2125
2379
|
if (cursor < viewportOffset) viewportOffset = cursor;
|
|
2126
|
-
else if (cursor >= viewportOffset +
|
|
2127
|
-
viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, filtered.length -
|
|
2380
|
+
else if (cursor >= viewportOffset + maxHeight) viewportOffset = cursor - maxHeight + 1;
|
|
2381
|
+
viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, filtered.length - maxHeight)));
|
|
2382
|
+
} else {
|
|
2383
|
+
viewportOffset = 0;
|
|
2128
2384
|
}
|
|
2129
|
-
const visibleStart =
|
|
2130
|
-
const visibleEnd =
|
|
2385
|
+
const visibleStart = useViewport ? viewportOffset : 0;
|
|
2386
|
+
const visibleEnd = useViewport ? Math.min(filtered.length, viewportOffset + maxHeight) : filtered.length;
|
|
2131
2387
|
if (redraw) {
|
|
2132
2388
|
if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP3(lastDrawnLines));
|
|
2133
2389
|
process.stdout.write("\r\x1B[0J");
|
|
@@ -2143,14 +2399,15 @@ var MultiSelect = class {
|
|
|
2143
2399
|
const { item, originalIndex } = filtered[vi];
|
|
2144
2400
|
const isCursor = vi === cursor;
|
|
2145
2401
|
const isChecked = checked.has(originalIndex);
|
|
2402
|
+
const numStr = `${vi + 1}.`;
|
|
2146
2403
|
const desc = item.description ? ` ${colorText(this.descriptionColor, `\u2014 ${item.description}`)}` : "";
|
|
2147
2404
|
const checkMark = isChecked ? pulse ? `${pulse}${this.checkedPrefix}${RESET}` : colorText(this.promptColor, this.checkedPrefix) : this.uncheckedPrefix;
|
|
2148
2405
|
const label = isCursor ? pulse ? `${pulse}${item.label}${RESET}` : colorText(this.promptColor, item.label) : item.label;
|
|
2149
|
-
process.stdout.write(`\r${indent}${checkMark} ${label}${desc}
|
|
2406
|
+
process.stdout.write(`\r${indent}${numStr} ${checkMark} ${label}${desc}
|
|
2150
2407
|
`);
|
|
2151
2408
|
lastDrawnLines++;
|
|
2152
2409
|
}
|
|
2153
|
-
const hintContent = `\u2191\u2193 move space/tab toggle \u2190\u2192 deselect/select${this.searchEnabled ? " type to filter" : " a all"} enter confirm`;
|
|
2410
|
+
const hintContent = `\u2191\u2193 move space/tab toggle \u2190\u2192 deselect/select${this.searchEnabled ? " type to filter" : " 1-9 jump a all"} enter confirm`;
|
|
2154
2411
|
const maxHintCols = Math.max(10, (process.stdout.columns ?? 80) - stringLength(indent) - 1);
|
|
2155
2412
|
const hint = stringLength(hintContent) > maxHintCols ? hintContent.slice(0, maxHintCols) : hintContent;
|
|
2156
2413
|
process.stdout.write(`\r${indent}${DIM2}${hint}${RESET}
|
|
@@ -2173,7 +2430,7 @@ var MultiSelect = class {
|
|
|
2173
2430
|
process.stdin.setRawMode(false);
|
|
2174
2431
|
process.stdin.pause();
|
|
2175
2432
|
process.stdin.removeListener("data", onKey);
|
|
2176
|
-
process.stdout.write(SHOW_CURSOR);
|
|
2433
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
2177
2434
|
});
|
|
2178
2435
|
const cleanup = (result) => {
|
|
2179
2436
|
deregisterCleanup();
|
|
@@ -2181,16 +2438,13 @@ var MultiSelect = class {
|
|
|
2181
2438
|
clearInterval(timer);
|
|
2182
2439
|
timer = null;
|
|
2183
2440
|
}
|
|
2184
|
-
|
|
2185
|
-
process.stdout.write("\x1B[0J");
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
process.stdout.write(`\r${indent}${bullet}${item.label}
|
|
2441
|
+
process.stdout.write(CURSOR_UP3(lastDrawnLines + 1));
|
|
2442
|
+
process.stdout.write("\r\x1B[0J");
|
|
2443
|
+
process.stdout.write(ENABLE_WRAP);
|
|
2444
|
+
const selectedItems = items.filter((_, i) => checked.has(i));
|
|
2445
|
+
const display = result !== null && selectedItems.length > 0 ? selectedItems.map((i) => i.label).join(", ") : "\u2014";
|
|
2446
|
+
process.stdout.write(`${glyph}${prompt}: ${display}
|
|
2191
2447
|
`);
|
|
2192
|
-
}
|
|
2193
|
-
process.stdout.write(`\r${CLEAR_LINE2}`);
|
|
2194
2448
|
process.stdin.setRawMode(false);
|
|
2195
2449
|
process.stdin.pause();
|
|
2196
2450
|
process.stdin.removeListener("data", onKey);
|
|
@@ -2283,7 +2537,7 @@ var MultiSelect = class {
|
|
|
2283
2537
|
process.stdin.setRawMode(false);
|
|
2284
2538
|
process.stdin.pause();
|
|
2285
2539
|
process.stdin.removeListener("data", onKey);
|
|
2286
|
-
process.stdout.write(SHOW_CURSOR);
|
|
2540
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
2287
2541
|
process.exit(130);
|
|
2288
2542
|
} else if (this.searchEnabled) {
|
|
2289
2543
|
if (str === "\x7F" || str === "\b") {
|
|
@@ -2303,7 +2557,7 @@ var MultiSelect = class {
|
|
|
2303
2557
|
const n = parseInt(str);
|
|
2304
2558
|
if (!isNaN(n) && n >= 1 && n <= Math.min(filtered.length, 9)) {
|
|
2305
2559
|
error = null;
|
|
2306
|
-
cursor =
|
|
2560
|
+
cursor = n - 1;
|
|
2307
2561
|
renderList(true);
|
|
2308
2562
|
}
|
|
2309
2563
|
}
|
|
@@ -2677,7 +2931,7 @@ _Spinner.FRAMES = {
|
|
|
2677
2931
|
var Spinner = _Spinner;
|
|
2678
2932
|
|
|
2679
2933
|
// src/models/Table.ts
|
|
2680
|
-
var
|
|
2934
|
+
var import_cosmetic4 = __toESM(require("cosmetic"));
|
|
2681
2935
|
|
|
2682
2936
|
// src/utils/padSides.ts
|
|
2683
2937
|
var padSides = (string, padding) => {
|
|
@@ -2755,7 +3009,7 @@ var Table = class {
|
|
|
2755
3009
|
header += pad(column.title, column.padding + this.margin);
|
|
2756
3010
|
if (i < keys.length - 1) header += this.separator;
|
|
2757
3011
|
}
|
|
2758
|
-
const styled = typeof config.color === "number" ?
|
|
3012
|
+
const styled = typeof config.color === "number" ? import_cosmetic4.default.xterm(config.color) : config.color.startsWith("#") ? import_cosmetic4.default.hex(config.color) : import_cosmetic4.default[config.color];
|
|
2759
3013
|
this.string += `${styled.underline.encoder(header)}
|
|
2760
3014
|
`;
|
|
2761
3015
|
for (const [ri, row] of this.rows.entries()) {
|
|
@@ -2826,13 +3080,13 @@ _TermKit.commandDefaults = {};
|
|
|
2826
3080
|
var TermKit = _TermKit;
|
|
2827
3081
|
|
|
2828
3082
|
// src/index.ts
|
|
2829
|
-
var
|
|
3083
|
+
var import_cosmetic5 = __toESM(require("cosmetic"));
|
|
2830
3084
|
var base = null;
|
|
2831
3085
|
var commandDefaults = {};
|
|
2832
3086
|
var Program = {
|
|
2833
3087
|
command: (name, variables, info) => {
|
|
2834
3088
|
const cmd = new Command(Object.assign({ name, variables, info }, commandDefaults));
|
|
2835
|
-
|
|
3089
|
+
base = cmd;
|
|
2836
3090
|
return cmd;
|
|
2837
3091
|
},
|
|
2838
3092
|
option: (short, long, variables, info) => new Option({ short, long, variables, info }),
|
|
@@ -2855,6 +3109,10 @@ var Program = {
|
|
|
2855
3109
|
},
|
|
2856
3110
|
setDefaults: (data) => {
|
|
2857
3111
|
commandDefaults = data;
|
|
3112
|
+
},
|
|
3113
|
+
shell: async (options) => {
|
|
3114
|
+
if (!base) throw new Error("No command defined");
|
|
3115
|
+
return new Shell(base, options).run();
|
|
2858
3116
|
}
|
|
2859
3117
|
};
|
|
2860
3118
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -2872,6 +3130,7 @@ var Program = {
|
|
|
2872
3130
|
Program,
|
|
2873
3131
|
Scrollbox,
|
|
2874
3132
|
Select,
|
|
3133
|
+
Shell,
|
|
2875
3134
|
Spinner,
|
|
2876
3135
|
Table,
|
|
2877
3136
|
TermKit,
|