termkit 2.3.1 → 2.4.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 +30 -4
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +294 -44
- package/dist/index.mjs +284 -35
- 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,7 +637,7 @@ 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
642
|
const cleanup = () => {
|
|
630
643
|
deregisterCleanup();
|
|
@@ -635,7 +648,7 @@ var Select = class {
|
|
|
635
648
|
process.stdin.setRawMode(false);
|
|
636
649
|
process.stdin.pause();
|
|
637
650
|
process.stdin.removeListener("data", onKey);
|
|
638
|
-
process.stdout.write(SHOW_CURSOR);
|
|
651
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
639
652
|
};
|
|
640
653
|
if (this._parsedColors.length >= 2) {
|
|
641
654
|
timer = setInterval(() => {
|
|
@@ -678,8 +691,7 @@ var Select = class {
|
|
|
678
691
|
} else {
|
|
679
692
|
const n = parseInt(str);
|
|
680
693
|
if (!isNaN(n) && n >= 0 && n <= Math.min(items.length, 9)) {
|
|
681
|
-
|
|
682
|
-
selectedIndex = n === 0 ? allItems.length - 1 : visibleStart + n - 1;
|
|
694
|
+
selectedIndex = n === 0 ? allItems.length - 1 : n - 1;
|
|
683
695
|
selectedIndex = Math.max(0, Math.min(selectedIndex, allItems.length - 1));
|
|
684
696
|
renderList(true);
|
|
685
697
|
}
|
|
@@ -860,7 +872,7 @@ var Command = class {
|
|
|
860
872
|
constructor(data) {
|
|
861
873
|
this.actionFunction = null;
|
|
862
874
|
this.commandsArray = [];
|
|
863
|
-
this.commandStrings = ["help"];
|
|
875
|
+
this.commandStrings = ["help", "version"];
|
|
864
876
|
this.info = null;
|
|
865
877
|
this.middlewaresArray = [];
|
|
866
878
|
this.name = null;
|
|
@@ -930,6 +942,9 @@ var Command = class {
|
|
|
930
942
|
const recursive = source?.includes("-r") === true || source?.includes("--recursive") === true;
|
|
931
943
|
this.printHelp(this.name ?? "Program", recursive);
|
|
932
944
|
}
|
|
945
|
+
printVersion() {
|
|
946
|
+
console.log(this.versionString ?? "");
|
|
947
|
+
}
|
|
933
948
|
printHelp(fullName, recursive) {
|
|
934
949
|
const table = [];
|
|
935
950
|
let program = fullName;
|
|
@@ -989,29 +1004,32 @@ var Command = class {
|
|
|
989
1004
|
}
|
|
990
1005
|
}
|
|
991
1006
|
}
|
|
992
|
-
async
|
|
993
|
-
const array = [...
|
|
994
|
-
array.splice(0, 2);
|
|
1007
|
+
async _execute(tokens) {
|
|
1008
|
+
const array = [...tokens];
|
|
995
1009
|
let command = this;
|
|
996
1010
|
const options = { _source: Array.from(array) };
|
|
1011
|
+
if (this.versionString && (array.includes("--version") || array.includes("-V"))) {
|
|
1012
|
+
return this.printVersion();
|
|
1013
|
+
}
|
|
997
1014
|
const ddIdx = array.indexOf("--");
|
|
998
1015
|
if (ddIdx !== -1) {
|
|
999
1016
|
options._ = array.splice(ddIdx + 1);
|
|
1000
1017
|
array.splice(ddIdx, 1);
|
|
1001
1018
|
}
|
|
1002
1019
|
while (array.length) {
|
|
1003
|
-
if (!array.includes("help")) {
|
|
1020
|
+
if (!array.includes("help") && !array.includes("version")) {
|
|
1004
1021
|
Object.assign(options, await findOptions(array, command));
|
|
1005
1022
|
const cmdVars = await findCommandVariables(array, command);
|
|
1006
1023
|
if (cmdVars) Object.assign(options, cmdVars);
|
|
1007
1024
|
Object.assign(options, await findOptions(array, command));
|
|
1008
1025
|
}
|
|
1009
1026
|
if (array.length) {
|
|
1010
|
-
if (!array.includes("help")) {
|
|
1027
|
+
if (!array.includes("help") && !array.includes("version")) {
|
|
1011
1028
|
for (const mw of command.middlewaresArray) await mw(options);
|
|
1012
1029
|
}
|
|
1013
1030
|
const next = findCommand(array, command.commandsArray);
|
|
1014
1031
|
if (!next && array[0] === "help") return command.help(options._source);
|
|
1032
|
+
if (!next && array[0] === "version") return this.printVersion();
|
|
1015
1033
|
if (!next) throw new SyntaxError(`Unknown command: ${array[0]}`);
|
|
1016
1034
|
const name = command.name ?? "_base";
|
|
1017
1035
|
if (!options._parents) options._parents = {};
|
|
@@ -1035,11 +1053,226 @@ var Command = class {
|
|
|
1035
1053
|
}
|
|
1036
1054
|
for (const mw of command.middlewaresArray) await mw(options);
|
|
1037
1055
|
if (command.actionFunction) return command.actionFunction(options);
|
|
1038
|
-
|
|
1039
|
-
|
|
1056
|
+
return command.help(options._source);
|
|
1057
|
+
}
|
|
1058
|
+
async parse(input2) {
|
|
1059
|
+
return this._execute(input2.slice(2));
|
|
1040
1060
|
}
|
|
1041
1061
|
};
|
|
1042
1062
|
|
|
1063
|
+
// src/models/Shell.ts
|
|
1064
|
+
var import_cosmetic2 = __toESM(require("cosmetic"));
|
|
1065
|
+
var readline = __toESM(require("readline"));
|
|
1066
|
+
var Shell = class {
|
|
1067
|
+
constructor(root, opts = {}) {
|
|
1068
|
+
this.rl = null;
|
|
1069
|
+
this.root = root;
|
|
1070
|
+
this.opts = {
|
|
1071
|
+
mode: opts.mode ?? "drill",
|
|
1072
|
+
prompt: opts.prompt ?? root.name ?? "shell",
|
|
1073
|
+
promptColor: opts.promptColor ?? "",
|
|
1074
|
+
banner: opts.banner ?? "",
|
|
1075
|
+
exitCommands: opts.exitCommands ?? ["exit", "quit"],
|
|
1076
|
+
historySize: opts.historySize ?? 100
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
async run() {
|
|
1080
|
+
this.rl = readline.createInterface({
|
|
1081
|
+
input: process.stdin,
|
|
1082
|
+
output: process.stdout,
|
|
1083
|
+
terminal: true,
|
|
1084
|
+
historySize: this.opts.historySize,
|
|
1085
|
+
completer: this.opts.mode === "free" ? makeCompleter(this.root) : void 0
|
|
1086
|
+
});
|
|
1087
|
+
if (this.opts.banner) console.log(this.opts.banner);
|
|
1088
|
+
try {
|
|
1089
|
+
if (this.opts.mode === "free") {
|
|
1090
|
+
await this.freeLoop();
|
|
1091
|
+
} else {
|
|
1092
|
+
await this.drillLoop();
|
|
1093
|
+
}
|
|
1094
|
+
} finally {
|
|
1095
|
+
this.rl.close();
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
// ── Drill mode ────────────────────────────────────────────────────────
|
|
1099
|
+
async drillLoop() {
|
|
1100
|
+
while (true) {
|
|
1101
|
+
const exited = await this.drillFrom(this.root, [this.root.name ?? "shell"]);
|
|
1102
|
+
if (exited) return;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
async drillFrom(cmd, breadcrumb) {
|
|
1106
|
+
while (true) {
|
|
1107
|
+
const token = await this.promptDrill(cmd, breadcrumb);
|
|
1108
|
+
if (token === null) {
|
|
1109
|
+
return breadcrumb.length === 1;
|
|
1110
|
+
}
|
|
1111
|
+
if (this.opts.exitCommands.includes(token)) process.exit(0);
|
|
1112
|
+
if (token === "..") return false;
|
|
1113
|
+
if (token === "help") {
|
|
1114
|
+
cmd.help();
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
const sub = cmd.commandsArray.find((c) => c.name === token);
|
|
1118
|
+
if (!sub) {
|
|
1119
|
+
process.stderr.write(`Unknown command: ${token}
|
|
1120
|
+
`);
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (sub.commandsArray.length > 0) {
|
|
1124
|
+
const exited = await this.drillFrom(sub, [...breadcrumb, sub.name ?? ""]);
|
|
1125
|
+
if (exited) return true;
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
const vars = await this.gatherVariables(sub);
|
|
1129
|
+
const tokens = buildTokens(sub, vars);
|
|
1130
|
+
try {
|
|
1131
|
+
await sub._execute(tokens);
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
process.stderr.write(`${err instanceof Error ? err.message : err}
|
|
1134
|
+
`);
|
|
1135
|
+
}
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async promptDrill(cmd, breadcrumb) {
|
|
1140
|
+
const subs = cmd.commandsArray.map((c) => c.name ?? "").filter(Boolean);
|
|
1141
|
+
const label = this.colorize(breadcrumb.join(" "));
|
|
1142
|
+
const choices = [...subs, "help"].join(", ");
|
|
1143
|
+
process.stdout.write(`
|
|
1144
|
+
${label} ${choices}
|
|
1145
|
+
`);
|
|
1146
|
+
return new Promise((resolve) => {
|
|
1147
|
+
let resolved = false;
|
|
1148
|
+
const done = (val) => {
|
|
1149
|
+
if (!resolved) {
|
|
1150
|
+
resolved = true;
|
|
1151
|
+
resolve(val);
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
this.rl.question("> ", (answer) => done(answer.trim() || null));
|
|
1155
|
+
this.rl.once("close", () => done(null));
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
async gatherVariables(cmd) {
|
|
1159
|
+
const result = {};
|
|
1160
|
+
if (!cmd.variables) return result;
|
|
1161
|
+
for (const v of cmd.variables) {
|
|
1162
|
+
const name = v.name ?? "value";
|
|
1163
|
+
const hint = v.hint ? ` ${v.hint}` : "";
|
|
1164
|
+
while (true) {
|
|
1165
|
+
const answer = await new Promise((resolve) => {
|
|
1166
|
+
let resolved = false;
|
|
1167
|
+
const done = (val) => {
|
|
1168
|
+
if (!resolved) {
|
|
1169
|
+
resolved = true;
|
|
1170
|
+
resolve(val);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
this.rl.question(` ${name}${hint}: `, (ans) => done(ans.trim() || null));
|
|
1174
|
+
this.rl.once("close", () => done(null));
|
|
1175
|
+
});
|
|
1176
|
+
const value = answer ?? v.default ?? null;
|
|
1177
|
+
if (!value && v.required) {
|
|
1178
|
+
process.stderr.write(` ${name} is required
|
|
1179
|
+
`);
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
if (value) {
|
|
1183
|
+
if (v.type === "enum" && v.enum && !v.enum.includes(value)) {
|
|
1184
|
+
process.stderr.write(` Must be one of: ${v.enum.join(", ")}
|
|
1185
|
+
`);
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
result[name] = value;
|
|
1189
|
+
}
|
|
1190
|
+
break;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return result;
|
|
1194
|
+
}
|
|
1195
|
+
// ── Free mode ─────────────────────────────────────────────────────────
|
|
1196
|
+
async freeLoop() {
|
|
1197
|
+
const prompt = `${this.colorize(this.opts.prompt)} > `;
|
|
1198
|
+
this.rl.setPrompt(prompt);
|
|
1199
|
+
this.rl.prompt();
|
|
1200
|
+
for await (const line of this.rl) {
|
|
1201
|
+
const trimmed = line.trim();
|
|
1202
|
+
if (!trimmed) {
|
|
1203
|
+
this.rl.prompt();
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
if (this.opts.exitCommands.includes(trimmed)) break;
|
|
1207
|
+
const tokens = tokenize(trimmed);
|
|
1208
|
+
try {
|
|
1209
|
+
await this.root._execute(tokens);
|
|
1210
|
+
} catch (err) {
|
|
1211
|
+
process.stderr.write(`${err instanceof Error ? err.message : err}
|
|
1212
|
+
`);
|
|
1213
|
+
}
|
|
1214
|
+
this.rl.prompt();
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
1218
|
+
colorize(text) {
|
|
1219
|
+
const c = this.opts.promptColor;
|
|
1220
|
+
if (!c || !process.stdout.isTTY) return text;
|
|
1221
|
+
try {
|
|
1222
|
+
if (c.startsWith("#")) return import_cosmetic2.default.hex(c).encoder(text);
|
|
1223
|
+
if (/^\d+$/.test(c)) return import_cosmetic2.default.xterm(Number(c)).encoder(text);
|
|
1224
|
+
const style = import_cosmetic2.default[c];
|
|
1225
|
+
if (style && typeof style.encoder === "function") return style.encoder(text);
|
|
1226
|
+
} catch {
|
|
1227
|
+
}
|
|
1228
|
+
return text;
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
function tokenize(line) {
|
|
1232
|
+
const tokens = [];
|
|
1233
|
+
let current = "";
|
|
1234
|
+
let inQuote = null;
|
|
1235
|
+
for (const ch of line) {
|
|
1236
|
+
if (inQuote) {
|
|
1237
|
+
if (ch === inQuote) {
|
|
1238
|
+
inQuote = null;
|
|
1239
|
+
} else {
|
|
1240
|
+
current += ch;
|
|
1241
|
+
}
|
|
1242
|
+
} else if (ch === '"' || ch === "'") {
|
|
1243
|
+
inQuote = ch;
|
|
1244
|
+
} else if (ch === " " || ch === " ") {
|
|
1245
|
+
if (current) {
|
|
1246
|
+
tokens.push(current);
|
|
1247
|
+
current = "";
|
|
1248
|
+
}
|
|
1249
|
+
} else {
|
|
1250
|
+
current += ch;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (current) tokens.push(current);
|
|
1254
|
+
return tokens;
|
|
1255
|
+
}
|
|
1256
|
+
function buildTokens(cmd, vars) {
|
|
1257
|
+
if (!cmd.variables) return [];
|
|
1258
|
+
return cmd.variables.map((v) => vars[v.name ?? ""]).filter((v) => v !== void 0 && v !== "");
|
|
1259
|
+
}
|
|
1260
|
+
function makeCompleter(root) {
|
|
1261
|
+
return (line) => {
|
|
1262
|
+
const tokens = tokenize(line);
|
|
1263
|
+
let cmd = root;
|
|
1264
|
+
for (const token of tokens.slice(0, -1)) {
|
|
1265
|
+
const sub = cmd.commandsArray.find((c) => c.name === token);
|
|
1266
|
+
if (!sub) break;
|
|
1267
|
+
cmd = sub;
|
|
1268
|
+
}
|
|
1269
|
+
const partial = tokens[tokens.length - 1] ?? "";
|
|
1270
|
+
const names = [...cmd.commandsArray.map((c) => c.name ?? "").filter(Boolean), "help"];
|
|
1271
|
+
const hits = names.filter((n) => n.startsWith(partial));
|
|
1272
|
+
return [hits.length ? hits : names, partial];
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1043
1276
|
// src/models/Bar.ts
|
|
1044
1277
|
var _Bar = class _Bar {
|
|
1045
1278
|
constructor(text, options) {
|
|
@@ -1500,7 +1733,7 @@ __export(Chart_exports, {
|
|
|
1500
1733
|
Sparkline: () => Sparkline,
|
|
1501
1734
|
VerticalBar: () => VerticalBar
|
|
1502
1735
|
});
|
|
1503
|
-
var
|
|
1736
|
+
var import_cosmetic3 = __toESM(require("cosmetic"));
|
|
1504
1737
|
|
|
1505
1738
|
// src/utils/padLeft.ts
|
|
1506
1739
|
var padLeft = (string, padding) => {
|
|
@@ -1521,9 +1754,9 @@ function formatNum(n) {
|
|
|
1521
1754
|
}
|
|
1522
1755
|
function applyConfigColor(s) {
|
|
1523
1756
|
const c = config.color;
|
|
1524
|
-
if (typeof c === "number") return
|
|
1525
|
-
if (c.startsWith("#")) return
|
|
1526
|
-
return
|
|
1757
|
+
if (typeof c === "number") return import_cosmetic3.default.xterm(c).encoder(s);
|
|
1758
|
+
if (c.startsWith("#")) return import_cosmetic3.default.hex(c).encoder(s);
|
|
1759
|
+
return import_cosmetic3.default[c].encoder(s);
|
|
1527
1760
|
}
|
|
1528
1761
|
function applyPadding(str, paddingX, paddingY) {
|
|
1529
1762
|
const lines = str.split("\n");
|
|
@@ -1549,7 +1782,7 @@ var Bar2 = class {
|
|
|
1549
1782
|
const cols = options.width ?? process.stdout.columns ?? 80;
|
|
1550
1783
|
const available = cols - maxKeyLen - maxValueLen - 3;
|
|
1551
1784
|
const scale = maxValue > 0 && available > 0 ? available / maxValue : 0;
|
|
1552
|
-
const encodeKey = (s) => typeof config.color === "number" ?
|
|
1785
|
+
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
1786
|
for (const item of data) {
|
|
1554
1787
|
if (!item) {
|
|
1555
1788
|
this.string += "\n";
|
|
@@ -1559,7 +1792,7 @@ var Bar2 = class {
|
|
|
1559
1792
|
const keyPart = (rawKey ? encodeKey(rawKey) : "") + " ".repeat(maxKeyLen - stringLength(rawKey));
|
|
1560
1793
|
const barWidth = Math.max(1, Math.floor(item.value * scale));
|
|
1561
1794
|
let bar = (item.character ?? " ").repeat(barWidth);
|
|
1562
|
-
bar = item.style ? item.style(bar) :
|
|
1795
|
+
bar = item.style ? item.style(bar) : import_cosmetic3.default.background.white.encoder(bar);
|
|
1563
1796
|
this.string += `${keyPart}|${bar} ${item.value}
|
|
1564
1797
|
`;
|
|
1565
1798
|
}
|
|
@@ -1608,7 +1841,7 @@ var VerticalBar = class {
|
|
|
1608
1841
|
}
|
|
1609
1842
|
this.string += "\u2500".repeat(data.length * colWidth) + "\n";
|
|
1610
1843
|
if (data.some((item) => item?.key)) {
|
|
1611
|
-
const encodeLabel = (s) => typeof config.color === "number" ?
|
|
1844
|
+
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
1845
|
let labels = "";
|
|
1613
1846
|
for (const item of data) {
|
|
1614
1847
|
if (!item?.key) {
|
|
@@ -2108,7 +2341,7 @@ var MultiSelect = class {
|
|
|
2108
2341
|
const checked = /* @__PURE__ */ new Set();
|
|
2109
2342
|
let error = null;
|
|
2110
2343
|
let lastDrawnLines = 0;
|
|
2111
|
-
process.stdout.write(HIDE_CURSOR);
|
|
2344
|
+
process.stdout.write(HIDE_CURSOR + DISABLE_WRAP);
|
|
2112
2345
|
const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
|
|
2113
2346
|
const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
|
|
2114
2347
|
process.stdout.write(`${glyph}${prompt}
|
|
@@ -2118,16 +2351,26 @@ var MultiSelect = class {
|
|
|
2118
2351
|
const q = searchQuery.toLowerCase();
|
|
2119
2352
|
return items.map((item, i) => ({ item, originalIndex: i })).filter(({ item }) => item.label.toLowerCase().includes(q) || (item.description?.toLowerCase().includes(q) ?? false));
|
|
2120
2353
|
};
|
|
2354
|
+
const computeMaxHeight = () => {
|
|
2355
|
+
const termRows = process.stdout.rows ?? 24;
|
|
2356
|
+
const reserved = 1 + (this.searchEnabled ? 1 : 0) + 1 + 1;
|
|
2357
|
+
const fit = Math.max(1, termRows - reserved - 1);
|
|
2358
|
+
return this.maxHeight ? Math.min(this.maxHeight, fit) : fit;
|
|
2359
|
+
};
|
|
2121
2360
|
const renderList = (redraw) => {
|
|
2122
2361
|
const filtered = getFiltered();
|
|
2123
2362
|
if (cursor >= filtered.length) cursor = Math.max(0, filtered.length - 1);
|
|
2124
|
-
|
|
2363
|
+
const maxHeight = computeMaxHeight();
|
|
2364
|
+
const useViewport = filtered.length > maxHeight;
|
|
2365
|
+
if (useViewport) {
|
|
2125
2366
|
if (cursor < viewportOffset) viewportOffset = cursor;
|
|
2126
|
-
else if (cursor >= viewportOffset +
|
|
2127
|
-
viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, filtered.length -
|
|
2367
|
+
else if (cursor >= viewportOffset + maxHeight) viewportOffset = cursor - maxHeight + 1;
|
|
2368
|
+
viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, filtered.length - maxHeight)));
|
|
2369
|
+
} else {
|
|
2370
|
+
viewportOffset = 0;
|
|
2128
2371
|
}
|
|
2129
|
-
const visibleStart =
|
|
2130
|
-
const visibleEnd =
|
|
2372
|
+
const visibleStart = useViewport ? viewportOffset : 0;
|
|
2373
|
+
const visibleEnd = useViewport ? Math.min(filtered.length, viewportOffset + maxHeight) : filtered.length;
|
|
2131
2374
|
if (redraw) {
|
|
2132
2375
|
if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP3(lastDrawnLines));
|
|
2133
2376
|
process.stdout.write("\r\x1B[0J");
|
|
@@ -2143,14 +2386,15 @@ var MultiSelect = class {
|
|
|
2143
2386
|
const { item, originalIndex } = filtered[vi];
|
|
2144
2387
|
const isCursor = vi === cursor;
|
|
2145
2388
|
const isChecked = checked.has(originalIndex);
|
|
2389
|
+
const numStr = `${vi + 1}.`;
|
|
2146
2390
|
const desc = item.description ? ` ${colorText(this.descriptionColor, `\u2014 ${item.description}`)}` : "";
|
|
2147
2391
|
const checkMark = isChecked ? pulse ? `${pulse}${this.checkedPrefix}${RESET}` : colorText(this.promptColor, this.checkedPrefix) : this.uncheckedPrefix;
|
|
2148
2392
|
const label = isCursor ? pulse ? `${pulse}${item.label}${RESET}` : colorText(this.promptColor, item.label) : item.label;
|
|
2149
|
-
process.stdout.write(`\r${indent}${checkMark} ${label}${desc}
|
|
2393
|
+
process.stdout.write(`\r${indent}${numStr} ${checkMark} ${label}${desc}
|
|
2150
2394
|
`);
|
|
2151
2395
|
lastDrawnLines++;
|
|
2152
2396
|
}
|
|
2153
|
-
const hintContent = `\u2191\u2193 move space/tab toggle \u2190\u2192 deselect/select${this.searchEnabled ? " type to filter" : " a all"} enter confirm`;
|
|
2397
|
+
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
2398
|
const maxHintCols = Math.max(10, (process.stdout.columns ?? 80) - stringLength(indent) - 1);
|
|
2155
2399
|
const hint = stringLength(hintContent) > maxHintCols ? hintContent.slice(0, maxHintCols) : hintContent;
|
|
2156
2400
|
process.stdout.write(`\r${indent}${DIM2}${hint}${RESET}
|
|
@@ -2173,7 +2417,7 @@ var MultiSelect = class {
|
|
|
2173
2417
|
process.stdin.setRawMode(false);
|
|
2174
2418
|
process.stdin.pause();
|
|
2175
2419
|
process.stdin.removeListener("data", onKey);
|
|
2176
|
-
process.stdout.write(SHOW_CURSOR);
|
|
2420
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
2177
2421
|
});
|
|
2178
2422
|
const cleanup = (result) => {
|
|
2179
2423
|
deregisterCleanup();
|
|
@@ -2183,6 +2427,7 @@ var MultiSelect = class {
|
|
|
2183
2427
|
}
|
|
2184
2428
|
if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP3(lastDrawnLines));
|
|
2185
2429
|
process.stdout.write("\x1B[0J");
|
|
2430
|
+
process.stdout.write(ENABLE_WRAP);
|
|
2186
2431
|
const bulletWidth = stringLength(this.checkedPrefix) + 1;
|
|
2187
2432
|
for (let i = 0; i < items.length; i++) {
|
|
2188
2433
|
const item = items[i];
|
|
@@ -2194,7 +2439,7 @@ var MultiSelect = class {
|
|
|
2194
2439
|
process.stdin.setRawMode(false);
|
|
2195
2440
|
process.stdin.pause();
|
|
2196
2441
|
process.stdin.removeListener("data", onKey);
|
|
2197
|
-
process.stdout.write(SHOW_CURSOR);
|
|
2442
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
2198
2443
|
resolve(result);
|
|
2199
2444
|
};
|
|
2200
2445
|
if (this._parsedColors.length >= 2) {
|
|
@@ -2283,7 +2528,7 @@ var MultiSelect = class {
|
|
|
2283
2528
|
process.stdin.setRawMode(false);
|
|
2284
2529
|
process.stdin.pause();
|
|
2285
2530
|
process.stdin.removeListener("data", onKey);
|
|
2286
|
-
process.stdout.write(SHOW_CURSOR);
|
|
2531
|
+
process.stdout.write(ENABLE_WRAP + SHOW_CURSOR);
|
|
2287
2532
|
process.exit(130);
|
|
2288
2533
|
} else if (this.searchEnabled) {
|
|
2289
2534
|
if (str === "\x7F" || str === "\b") {
|
|
@@ -2303,7 +2548,7 @@ var MultiSelect = class {
|
|
|
2303
2548
|
const n = parseInt(str);
|
|
2304
2549
|
if (!isNaN(n) && n >= 1 && n <= Math.min(filtered.length, 9)) {
|
|
2305
2550
|
error = null;
|
|
2306
|
-
cursor =
|
|
2551
|
+
cursor = n - 1;
|
|
2307
2552
|
renderList(true);
|
|
2308
2553
|
}
|
|
2309
2554
|
}
|
|
@@ -2677,7 +2922,7 @@ _Spinner.FRAMES = {
|
|
|
2677
2922
|
var Spinner = _Spinner;
|
|
2678
2923
|
|
|
2679
2924
|
// src/models/Table.ts
|
|
2680
|
-
var
|
|
2925
|
+
var import_cosmetic4 = __toESM(require("cosmetic"));
|
|
2681
2926
|
|
|
2682
2927
|
// src/utils/padSides.ts
|
|
2683
2928
|
var padSides = (string, padding) => {
|
|
@@ -2755,7 +3000,7 @@ var Table = class {
|
|
|
2755
3000
|
header += pad(column.title, column.padding + this.margin);
|
|
2756
3001
|
if (i < keys.length - 1) header += this.separator;
|
|
2757
3002
|
}
|
|
2758
|
-
const styled = typeof config.color === "number" ?
|
|
3003
|
+
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
3004
|
this.string += `${styled.underline.encoder(header)}
|
|
2760
3005
|
`;
|
|
2761
3006
|
for (const [ri, row] of this.rows.entries()) {
|
|
@@ -2826,13 +3071,13 @@ _TermKit.commandDefaults = {};
|
|
|
2826
3071
|
var TermKit = _TermKit;
|
|
2827
3072
|
|
|
2828
3073
|
// src/index.ts
|
|
2829
|
-
var
|
|
3074
|
+
var import_cosmetic5 = __toESM(require("cosmetic"));
|
|
2830
3075
|
var base = null;
|
|
2831
3076
|
var commandDefaults = {};
|
|
2832
3077
|
var Program = {
|
|
2833
3078
|
command: (name, variables, info) => {
|
|
2834
3079
|
const cmd = new Command(Object.assign({ name, variables, info }, commandDefaults));
|
|
2835
|
-
|
|
3080
|
+
base = cmd;
|
|
2836
3081
|
return cmd;
|
|
2837
3082
|
},
|
|
2838
3083
|
option: (short, long, variables, info) => new Option({ short, long, variables, info }),
|
|
@@ -2855,6 +3100,10 @@ var Program = {
|
|
|
2855
3100
|
},
|
|
2856
3101
|
setDefaults: (data) => {
|
|
2857
3102
|
commandDefaults = data;
|
|
3103
|
+
},
|
|
3104
|
+
shell: async (options) => {
|
|
3105
|
+
if (!base) throw new Error("No command defined");
|
|
3106
|
+
return new Shell(base, options).run();
|
|
2858
3107
|
}
|
|
2859
3108
|
};
|
|
2860
3109
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -2872,6 +3121,7 @@ var Program = {
|
|
|
2872
3121
|
Program,
|
|
2873
3122
|
Scrollbox,
|
|
2874
3123
|
Select,
|
|
3124
|
+
Shell,
|
|
2875
3125
|
Spinner,
|
|
2876
3126
|
Table,
|
|
2877
3127
|
TermKit,
|