termkit 2.0.2 → 2.2.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.
Files changed (73) hide show
  1. package/README.md +582 -37
  2. package/dist/config.d.ts +11 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/index.d.ts +49 -96
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +2584 -170
  7. package/dist/index.mjs +2563 -165
  8. package/dist/models/Bar.d.ts +125 -0
  9. package/dist/models/Bar.d.ts.map +1 -0
  10. package/dist/models/Chart.d.ts +106 -0
  11. package/dist/models/Chart.d.ts.map +1 -0
  12. package/dist/models/Column.d.ts +20 -0
  13. package/dist/models/Column.d.ts.map +1 -0
  14. package/dist/models/Command.d.ts +38 -0
  15. package/dist/models/Command.d.ts.map +1 -0
  16. package/dist/models/Input.d.ts +58 -0
  17. package/dist/models/Input.d.ts.map +1 -0
  18. package/dist/models/Log.d.ts +24 -0
  19. package/dist/models/Log.d.ts.map +1 -0
  20. package/dist/models/Markup.d.ts +17 -0
  21. package/dist/models/Markup.d.ts.map +1 -0
  22. package/dist/models/MultiBar.d.ts +17 -0
  23. package/dist/models/MultiBar.d.ts.map +1 -0
  24. package/dist/models/MultiSelect.d.ts +45 -0
  25. package/dist/models/MultiSelect.d.ts.map +1 -0
  26. package/dist/models/Option.d.ts +17 -0
  27. package/dist/models/Option.d.ts.map +1 -0
  28. package/dist/models/Scrollbox.d.ts +20 -0
  29. package/dist/models/Scrollbox.d.ts.map +1 -0
  30. package/dist/models/Select.d.ts +39 -0
  31. package/dist/models/Select.d.ts.map +1 -0
  32. package/dist/models/Spinner.d.ts +67 -0
  33. package/dist/models/Spinner.d.ts.map +1 -0
  34. package/dist/models/Table.d.ts +26 -0
  35. package/dist/models/Table.d.ts.map +1 -0
  36. package/dist/models/TermKit.d.ts +19 -0
  37. package/dist/models/TermKit.d.ts.map +1 -0
  38. package/dist/models/Variable.d.ts +28 -0
  39. package/dist/models/Variable.d.ts.map +1 -0
  40. package/dist/types.d.ts +10 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/utils/cleanup.d.ts +2 -0
  43. package/dist/utils/cleanup.d.ts.map +1 -0
  44. package/dist/utils/color.d.ts +29 -0
  45. package/dist/utils/color.d.ts.map +1 -0
  46. package/dist/utils/findCommand.d.ts +3 -0
  47. package/dist/utils/findCommand.d.ts.map +1 -0
  48. package/dist/utils/findCommandVariables.d.ts +3 -0
  49. package/dist/utils/findCommandVariables.d.ts.map +1 -0
  50. package/dist/utils/findOption.d.ts +3 -0
  51. package/dist/utils/findOption.d.ts.map +1 -0
  52. package/dist/utils/findOptions.d.ts +3 -0
  53. package/dist/utils/findOptions.d.ts.map +1 -0
  54. package/dist/utils/findVariable.d.ts +5 -0
  55. package/dist/utils/findVariable.d.ts.map +1 -0
  56. package/dist/utils/findVariables.d.ts +3 -0
  57. package/dist/utils/findVariables.d.ts.map +1 -0
  58. package/dist/utils/getVariables.d.ts +3 -0
  59. package/dist/utils/getVariables.d.ts.map +1 -0
  60. package/dist/utils/padLeft.d.ts +2 -0
  61. package/dist/utils/padLeft.d.ts.map +1 -0
  62. package/dist/utils/padRight.d.ts +2 -0
  63. package/dist/utils/padRight.d.ts.map +1 -0
  64. package/dist/utils/padSides.d.ts +2 -0
  65. package/dist/utils/padSides.d.ts.map +1 -0
  66. package/dist/utils/stringLength.d.ts +2 -0
  67. package/dist/utils/stringLength.d.ts.map +1 -0
  68. package/dist/utils/truncate.d.ts +2 -0
  69. package/dist/utils/truncate.d.ts.map +1 -0
  70. package/dist/utils/wrap.d.ts +2 -0
  71. package/dist/utils/wrap.d.ts.map +1 -0
  72. package/package.json +35 -7
  73. package/dist/index.d.mts +0 -99
package/dist/index.js CHANGED
@@ -30,75 +30,769 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ Bar: () => Bar,
34
+ Chart: () => Chart_exports,
35
+ Color: () => import_cosmetic4.default,
36
+ Column: () => Column,
33
37
  Command: () => Command,
38
+ Input: () => Input,
39
+ Log: () => Log,
40
+ MultiBar: () => MultiBar,
41
+ MultiSelect: () => MultiSelect,
34
42
  Option: () => Option,
43
+ Program: () => Program,
44
+ Scrollbox: () => Scrollbox,
45
+ Select: () => Select,
46
+ Spinner: () => Spinner,
47
+ Table: () => Table,
35
48
  TermKit: () => TermKit,
36
49
  Variable: () => Variable,
37
- command: () => command,
38
- middleware: () => middleware,
39
- option: () => option,
40
- parse: () => parse,
41
- setDefaults: () => setDefaults
50
+ configure: () => configure,
51
+ confirm: () => confirm,
52
+ input: () => input,
53
+ log: () => log,
54
+ markup: () => markup,
55
+ multiSelect: () => multiSelect,
56
+ padLeft: () => padLeft,
57
+ padRight: () => padRight,
58
+ padSides: () => padSides,
59
+ scrollbox: () => scrollbox,
60
+ select: () => select,
61
+ stringLength: () => stringLength,
62
+ truncate: () => truncate,
63
+ wrap: () => wrap
42
64
  });
43
65
  module.exports = __toCommonJS(index_exports);
44
66
 
67
+ // src/config.ts
68
+ var isLegacyTerminal = process.env.TERM === "dumb" || process.platform === "win32" && !process.env.WT_SESSION && process.env.TERM_PROGRAM !== "vscode" && process.env.TERM_PROGRAM !== "Hyper";
69
+ var config = {
70
+ color: "cyan",
71
+ pulseColors: ["#06b6d4", "#67e8f9"],
72
+ glyphs: !isLegacyTerminal,
73
+ interactive: false
74
+ };
75
+ function configure(opts) {
76
+ if (opts.color) config.color = opts.color;
77
+ if (opts.pulseColors) config.pulseColors = opts.pulseColors;
78
+ if (opts.glyphs !== void 0) config.glyphs = opts.glyphs;
79
+ if (opts.interactive !== void 0) config.interactive = opts.interactive;
80
+ }
81
+
45
82
  // src/models/Command.ts
46
83
  var import_cosmetic = __toESM(require("cosmetic"));
47
84
 
48
- // src/helpers/findCommand.ts
85
+ // src/models/Variable.ts
86
+ var Variable = class {
87
+ constructor(data) {
88
+ this.array = false;
89
+ this.default = null;
90
+ this.enum = null;
91
+ this.max = null;
92
+ this.min = null;
93
+ this.name = null;
94
+ this.raw = null;
95
+ this.required = false;
96
+ this.type = "string";
97
+ this.value = null;
98
+ if (!data) return;
99
+ if (data.array) this.array = data.array;
100
+ if (data.default !== void 0) this.default = data.default;
101
+ if (data.enum) this.enum = data.enum;
102
+ if (data.max !== void 0) this.max = data.max;
103
+ if (data.min !== void 0) this.min = data.min;
104
+ if (data.name) this.name = data.name;
105
+ if (data.raw) this.raw = data.raw;
106
+ if (data.required) this.required = data.required;
107
+ if (data.type) this.type = data.type;
108
+ if (this.array) this.value = [];
109
+ }
110
+ get hint() {
111
+ const parts = [];
112
+ if (this.type === "enum" && this.enum) parts.push(this.enum.join("|"));
113
+ if (this.min !== null && this.max !== null) parts.push(`${this.min}\u2013${this.max}`);
114
+ else if (this.min !== null) parts.push(`>= ${this.min}`);
115
+ else if (this.max !== null) parts.push(`<= ${this.max}`);
116
+ if (this.default !== null) parts.push(`default: ${this.default}`);
117
+ return parts.length > 0 ? `(${parts.join(", ")})` : null;
118
+ }
119
+ };
120
+
121
+ // src/utils/getVariables.ts
122
+ function parseName(raw) {
123
+ const colon = raw.indexOf(":");
124
+ if (colon === -1) return { name: raw, type: "string" };
125
+ const name = raw.slice(0, colon);
126
+ const rest = raw.slice(colon + 1);
127
+ const eqIdx = rest.lastIndexOf("=");
128
+ const typeStr = eqIdx !== -1 ? rest.slice(0, eqIdx) : rest;
129
+ const defaultValue = eqIdx !== -1 ? rest.slice(eqIdx + 1) : void 0;
130
+ const rangeMatch = typeStr.match(/^([a-z]+)\((-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)\)$/);
131
+ if (rangeMatch) {
132
+ return { name, type: rangeMatch[1], min: Number(rangeMatch[2]), max: Number(rangeMatch[3]), default: defaultValue };
133
+ }
134
+ if (typeStr.includes("|")) {
135
+ return { name, type: "enum", enum: typeStr.split("|"), default: defaultValue };
136
+ }
137
+ return { name, type: typeStr, default: defaultValue };
138
+ }
139
+ function getVariables(string) {
140
+ const results = [];
141
+ for (const part of string.split(" ")) {
142
+ const trimmed = part.trim();
143
+ if (!trimmed) continue;
144
+ if (trimmed.startsWith("<") && trimmed.endsWith(">")) {
145
+ const { name, type, enum: enumValues, default: def, min, max } = parseName(trimmed.slice(1, -1));
146
+ results.push(new Variable({ default: def, enum: enumValues, max, min, name, raw: trimmed, required: true, type }));
147
+ } else if (trimmed.startsWith("[") && trimmed.endsWith("...]")) {
148
+ const { name, type, enum: enumValues, default: def, min, max } = parseName(trimmed.slice(1, -4));
149
+ results.push(new Variable({ array: true, default: def, enum: enumValues, max, min, name, raw: trimmed, type }));
150
+ } else if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
151
+ const { name, type, enum: enumValues, default: def, min, max } = parseName(trimmed.slice(1, -1));
152
+ results.push(new Variable({ default: def, enum: enumValues, max, min, name, raw: trimmed, type }));
153
+ } else {
154
+ throw new Error(`Unrecognized variable description: ${trimmed}`);
155
+ }
156
+ }
157
+ return results;
158
+ }
159
+
160
+ // src/models/Option.ts
161
+ var Option = class {
162
+ constructor(data) {
163
+ this.short = null;
164
+ this.long = null;
165
+ this.info = null;
166
+ this.variables = null;
167
+ if (!data) return;
168
+ if (data.short) this.short = data.short;
169
+ if (data.long) this.long = data.long;
170
+ if (data.info) this.info = data.info;
171
+ if (data.variables) this.variables = getVariables(data.variables);
172
+ }
173
+ description(info) {
174
+ this.info = info;
175
+ return this;
176
+ }
177
+ };
178
+
179
+ // src/utils/findCommand.ts
49
180
  function findCommand(array, commands) {
50
- for (const command2 of commands) {
51
- if (array[0] === command2.name) {
181
+ for (const command of commands) {
182
+ if (array[0] === command.name) {
52
183
  array.shift();
53
- return command2;
184
+ return command;
54
185
  }
55
186
  }
56
187
  return null;
57
188
  }
58
189
 
59
- // src/helpers/findVariable.ts
60
- function coerce(value, type) {
61
- if (type === "number") return Number(value);
190
+ // src/utils/color.ts
191
+ var RESET = "\x1B[0m";
192
+ var SHOW_CURSOR = "\x1B[?25h";
193
+ var HIDE_CURSOR = "\x1B[?25l";
194
+ var BOLD = "\x1B[1m";
195
+ var FAINT = "\x1B[2m";
196
+ var GREEN = "\x1B[32m";
197
+ var RED = "\x1B[31m";
198
+ var YELLOW = "\x1B[33m";
199
+ var BLUE = "\x1B[34m";
200
+ var MAGENTA = "\x1B[35m";
201
+ var CYAN = "\x1B[36m";
202
+ var DIM = 0.35;
203
+ var SHIMMER_SPEED = 0.5;
204
+ process.on("exit", () => {
205
+ if (process.stdout.isTTY) process.stdout.write(SHOW_CURSOR);
206
+ });
207
+ process.on("SIGINT", () => process.exit());
208
+ function parseHex(hex) {
209
+ const n = parseInt(hex.replace("#", ""), 16);
210
+ return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
211
+ }
212
+ function lerpColor(a, b, t) {
213
+ return {
214
+ r: Math.round(a.r + (b.r - a.r) * t),
215
+ g: Math.round(a.g + (b.g - a.g) * t),
216
+ b: Math.round(a.b + (b.b - a.b) * t)
217
+ };
218
+ }
219
+ function interpolateColor(colors, t) {
220
+ const clamped = Math.max(0, Math.min(1, t));
221
+ const segments = colors.length - 1;
222
+ const scaled = clamped * segments;
223
+ const i = Math.min(Math.floor(scaled), segments - 1);
224
+ return lerpColor(colors[i], colors[i + 1], scaled - i);
225
+ }
226
+ function formatColor(code, color) {
227
+ if (!process.stdout.isTTY) return "";
228
+ return `\x1B[${code};2;${color.r};${color.g};${color.b}m`;
229
+ }
230
+ function colorText(colorOrAnsi, text) {
231
+ if (!process.stdout.isTTY) return text;
232
+ if (colorOrAnsi.startsWith("#")) {
233
+ const c = parseHex(colorOrAnsi);
234
+ return `\x1B[38;2;${c.r};${c.g};${c.b}m${text}${RESET}`;
235
+ }
236
+ return `${colorOrAnsi}${text}${RESET}`;
237
+ }
238
+ function dimColor(c) {
239
+ return { r: Math.round(c.r * DIM), g: Math.round(c.g * DIM), b: Math.round(c.b * DIM) };
240
+ }
241
+ function shimmerFactor(t, phase, amplitude) {
242
+ const wave = 0.5 + 0.5 * Math.sin(2 * Math.PI * (t - phase));
243
+ return 1 - amplitude * (1 - wave);
244
+ }
245
+ function resolveColor(color) {
246
+ if (typeof color === "number") return `\x1B[38;5;${color}m`;
247
+ if (color.startsWith("#")) return color;
248
+ const map = {
249
+ black: "\x1B[30m",
250
+ red: "\x1B[31m",
251
+ green: "\x1B[32m",
252
+ yellow: "\x1B[33m",
253
+ blue: "\x1B[34m",
254
+ magenta: "\x1B[35m",
255
+ cyan: "\x1B[36m",
256
+ white: "\x1B[37m"
257
+ };
258
+ return map[color] ?? "\x1B[35m";
259
+ }
260
+ function applyShimmer(color, t, phase, amplitude) {
261
+ if (amplitude === 0) return color;
262
+ const factor = shimmerFactor(t, phase, amplitude);
263
+ return {
264
+ r: Math.round(color.r * factor),
265
+ g: Math.round(color.g * factor),
266
+ b: Math.round(color.b * factor)
267
+ };
268
+ }
269
+
270
+ // src/utils/stringLength.ts
271
+ var ANSI_PATTERN = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
272
+ var stringLength = (string) => {
273
+ if (!string) return 0;
274
+ return string.replace(ANSI_PATTERN, "").length;
275
+ };
276
+
277
+ // src/models/Input.ts
278
+ var CLEAR_LINE = "\x1B[2K";
279
+ var Input = class {
280
+ constructor(options = {}) {
281
+ this.promptColor = options.promptColor ?? GREEN;
282
+ this.promptGlyph = options.promptGlyph ?? (config.glyphs ? "\u25C6" : ">");
283
+ this.inputColor = options.inputColor ?? "";
284
+ this.errorColor = options.errorColor ?? RED;
285
+ this.placeholder = options.placeholder ?? "";
286
+ this.mask = options.mask ?? false;
287
+ this.inline = options.inline ?? false;
288
+ this.required = options.required ?? true;
289
+ this.type = options.type ?? "string";
290
+ this.defaultValue = options.default ?? null;
291
+ this.enumValues = options.enum ?? null;
292
+ this.match = options.match ?? null;
293
+ this.errorMsg = options.errorMessage ?? null;
294
+ this.regex = options.regex ?? null;
295
+ this.min = options.min ?? null;
296
+ this.max = options.max ?? null;
297
+ this.minLength = options.minLength ?? null;
298
+ this.maxLength = options.maxLength ?? null;
299
+ }
300
+ validate(value) {
301
+ const fail = (msg) => this.errorMsg ?? msg;
302
+ if (this.type === "number") {
303
+ const n = Number(value);
304
+ if (isNaN(n)) return fail("Must be a valid number");
305
+ if (this.min !== null && n < this.min) return fail(`Must be at least ${this.min}`);
306
+ if (this.max !== null && n > this.max) return fail(`Must be at most ${this.max}`);
307
+ return null;
308
+ }
309
+ if (this.type === "integer") {
310
+ const n = Number(value);
311
+ if (!Number.isInteger(n)) return fail("Must be a whole number");
312
+ if (this.min !== null && n < this.min) return fail(`Must be at least ${this.min}`);
313
+ if (this.max !== null && n > this.max) return fail(`Must be at most ${this.max}`);
314
+ return null;
315
+ }
316
+ if (this.type === "enum" && this.enumValues) {
317
+ if (!this.enumValues.includes(value)) return fail(`Must be one of: ${this.enumValues.join(", ")}`);
318
+ }
319
+ if (this.match !== null && value !== this.match) return fail("Does not match");
320
+ if (this.regex && !this.regex.test(value)) return fail("Invalid format");
321
+ if (this.minLength !== null && value.length < this.minLength) return fail(`Must be at least ${this.minLength} characters`);
322
+ if (this.maxLength !== null && value.length > this.maxLength) return fail(`Must be at most ${this.maxLength} characters`);
323
+ return null;
324
+ }
325
+ coerce(value) {
326
+ if (this.type === "number") return Number(value);
327
+ if (this.type === "integer") return parseInt(value, 10);
328
+ return value;
329
+ }
330
+ async ask(prompt) {
331
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Input requires an interactive terminal");
332
+ if (this.type === "boolean") return this.askBoolean(prompt);
333
+ let inputStr = "";
334
+ let cursorPos = 0;
335
+ let error = null;
336
+ let hasErrorLine = false;
337
+ const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
338
+ const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
339
+ let promptLine = prompt;
340
+ if (this.type === "enum" && this.enumValues) {
341
+ promptLine += ` \x1B[2m(${this.enumValues.join("|")})\x1B[0m`;
342
+ }
343
+ if (!this.inline) process.stdout.write(`${glyph}${promptLine}
344
+ `);
345
+ const defaultStr = this.defaultValue !== null ? String(this.defaultValue) : "";
346
+ const getDisplayText = () => {
347
+ const raw = this.mask ? "\u2022".repeat(inputStr.length) : inputStr;
348
+ if (inputStr.length === 0 && defaultStr) return `\x1B[2m${this.placeholder || defaultStr}${RESET}`;
349
+ return this.inputColor ? colorText(this.inputColor, raw) : raw;
350
+ };
351
+ const renderInput = (redraw) => {
352
+ if (redraw && hasErrorLine) process.stdout.write("\x1B[1A");
353
+ const text = getDisplayText();
354
+ const line = this.inline ? `\r${CLEAR_LINE}${glyph}${promptLine} ${text}` : `\r${CLEAR_LINE}${indent}${text}`;
355
+ process.stdout.write(line);
356
+ if (error) {
357
+ process.stdout.write(`
358
+ \r${CLEAR_LINE}${indent}${colorText(this.errorColor, `\u2717 ${error}`)}`);
359
+ hasErrorLine = true;
360
+ } else {
361
+ if (hasErrorLine) {
362
+ process.stdout.write(`
363
+ \r${CLEAR_LINE}\x1B[1A${line}`);
364
+ }
365
+ hasErrorLine = false;
366
+ }
367
+ if (inputStr.length > 0 && cursorPos < inputStr.length && !error) {
368
+ process.stdout.write(`\x1B[${inputStr.length - cursorPos}D`);
369
+ }
370
+ };
371
+ renderInput(false);
372
+ return new Promise((resolve) => {
373
+ const cleanup = () => {
374
+ if (hasErrorLine) process.stdout.write(`\r${CLEAR_LINE}\x1B[1A`);
375
+ const text = getDisplayText();
376
+ const line = this.inline ? `\r${CLEAR_LINE}${glyph}${promptLine} ${text}` : `\r${CLEAR_LINE}${indent}${text}`;
377
+ process.stdout.write(`${line}
378
+ `);
379
+ process.stdin.setRawMode(false);
380
+ process.stdin.pause();
381
+ process.stdin.removeListener("data", onKey);
382
+ };
383
+ const onKey = (key) => {
384
+ const str = key.toString();
385
+ if (str === "\r" || str === "\n") {
386
+ const value = inputStr.length === 0 && defaultStr ? defaultStr : inputStr;
387
+ const err = this.validate(value);
388
+ if (err) {
389
+ error = err;
390
+ renderInput(true);
391
+ } else {
392
+ const usingDefault = inputStr.length === 0 && this.defaultValue !== null;
393
+ if (usingDefault) inputStr = defaultStr;
394
+ cleanup();
395
+ resolve(usingDefault ? this.defaultValue : this.coerce(value));
396
+ }
397
+ } else if (str === "\x1B[D") {
398
+ cursorPos = Math.max(0, cursorPos - 1);
399
+ renderInput(true);
400
+ } else if (str === "\x1B[C") {
401
+ cursorPos = Math.min(inputStr.length, cursorPos + 1);
402
+ renderInput(true);
403
+ } else if (str === "\x1B[H" || str === "\x1B[1~" || str === "") {
404
+ cursorPos = 0;
405
+ renderInput(true);
406
+ } else if (str === "\x1B[F" || str === "\x1B[4~" || str === "") {
407
+ cursorPos = inputStr.length;
408
+ renderInput(true);
409
+ } else if (str === "\x1B[3~") {
410
+ if (cursorPos < inputStr.length) {
411
+ inputStr = inputStr.slice(0, cursorPos) + inputStr.slice(cursorPos + 1);
412
+ error = null;
413
+ renderInput(true);
414
+ }
415
+ } else if (str === "\x1B") {
416
+ if (!this.required) {
417
+ cleanup();
418
+ resolve(null);
419
+ }
420
+ } else if (str === "") {
421
+ cleanup();
422
+ process.exit();
423
+ } else if (str === "\x7F" || str === "\b") {
424
+ if (cursorPos > 0) {
425
+ inputStr = inputStr.slice(0, cursorPos - 1) + inputStr.slice(cursorPos);
426
+ cursorPos--;
427
+ error = null;
428
+ renderInput(true);
429
+ }
430
+ } else if (str.charCodeAt(0) >= 32) {
431
+ if (this.maxLength !== null && inputStr.length >= this.maxLength) return;
432
+ inputStr = inputStr.slice(0, cursorPos) + str + inputStr.slice(cursorPos);
433
+ cursorPos++;
434
+ error = null;
435
+ renderInput(true);
436
+ }
437
+ };
438
+ process.stdin.setRawMode(true);
439
+ process.stdin.resume();
440
+ process.stdin.on("data", onKey);
441
+ });
442
+ }
443
+ async askBoolean(prompt) {
444
+ const hasDefault = this.defaultValue !== null;
445
+ const defaultBool = this.defaultValue === true || this.defaultValue === "y" || this.defaultValue === "Y" || this.defaultValue === 1;
446
+ const hint = hasDefault ? defaultBool ? "[Y/n]" : "[y/N]" : "[y/n]";
447
+ const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
448
+ const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
449
+ if (this.inline) {
450
+ process.stdout.write(`${glyph}${prompt} \x1B[2m${hint}${RESET} `);
451
+ } else {
452
+ process.stdout.write(`${glyph}${prompt} \x1B[2m${hint}${RESET}
453
+ `);
454
+ process.stdout.write(`${indent}`);
455
+ }
456
+ return new Promise((resolve) => {
457
+ const finish = (selection, value) => {
458
+ process.stdin.setRawMode(false);
459
+ process.stdin.pause();
460
+ process.stdin.removeListener("data", onKey);
461
+ if (this.inline) {
462
+ process.stdout.write(`\r${CLEAR_LINE}${glyph}${prompt} \x1B[2m${hint}${RESET} ${selection}
463
+ `);
464
+ } else {
465
+ process.stdout.write(`\r${CLEAR_LINE}${indent}${selection}
466
+ `);
467
+ }
468
+ resolve(value);
469
+ };
470
+ const onKey = (key) => {
471
+ const str = key.toString();
472
+ if (str === "\r" || str === "\n") {
473
+ if (hasDefault) finish(defaultBool ? "yes" : "no", defaultBool);
474
+ } else if (str === "y" || str === "Y") {
475
+ finish("yes", true);
476
+ } else if (str === "n" || str === "N") {
477
+ finish("no", false);
478
+ } else if (str === "\x1B") {
479
+ if (!this.required) finish("", null);
480
+ } else if (str === "") {
481
+ process.stdin.setRawMode(false);
482
+ process.stdin.pause();
483
+ process.stdin.removeListener("data", onKey);
484
+ process.exit();
485
+ }
486
+ };
487
+ process.stdin.setRawMode(true);
488
+ process.stdin.resume();
489
+ process.stdin.on("data", onKey);
490
+ });
491
+ }
492
+ };
493
+ async function input(prompt, options = {}) {
494
+ return new Input(options).ask(prompt);
495
+ }
496
+ async function confirm(prompt, options) {
497
+ return new Input({ ...options, type: "boolean" }).ask(prompt);
498
+ }
499
+
500
+ // src/utils/cleanup.ts
501
+ var fns = /* @__PURE__ */ new Set();
502
+ var installed = false;
503
+ function install() {
504
+ if (installed) return;
505
+ installed = true;
506
+ process.on("SIGINT", () => {
507
+ flush();
508
+ process.exit(130);
509
+ });
510
+ process.on("exit", flush);
511
+ }
512
+ function flush() {
513
+ for (const fn of fns) {
514
+ try {
515
+ fn();
516
+ } catch {
517
+ }
518
+ }
519
+ fns.clear();
520
+ }
521
+ function registerCleanup(fn) {
522
+ install();
523
+ fns.add(fn);
524
+ return () => {
525
+ fns.delete(fn);
526
+ };
527
+ }
528
+
529
+ // src/models/Select.ts
530
+ var CURSOR_UP = (n) => `\x1B[${n}A`;
531
+ var Select = class {
532
+ constructor(options = {}) {
533
+ this._colorOffset = 0;
534
+ this._shimmerPhase = 0;
535
+ this.promptColor = options.promptColor ?? resolveColor(config.color);
536
+ this.promptGlyph = options.promptGlyph ?? (config.glyphs ? "\u25C6" : ">");
537
+ this.descriptionColor = options.descriptionColor ?? BLUE;
538
+ this.skipLabel = options.skipLabel ?? "Skip";
539
+ this.selectedPrefix = options.selectedPrefix ?? ">";
540
+ this.selectedSuffix = options.selectedSuffix ?? "<";
541
+ this.colorCycle = options.colorCycle ?? 1;
542
+ this.shimmer = options.shimmer ?? 0;
543
+ this.interval = options.interval ?? 80;
544
+ this.searchEnabled = options.search ?? false;
545
+ this.maxHeight = options.maxHeight;
546
+ const colors = options.colors ?? config.pulseColors;
547
+ this._parsedColors = colors.length >= 2 ? colors.map(parseHex) : [];
548
+ }
549
+ pulseColor() {
550
+ if (this._parsedColors.length < 2) return "";
551
+ const t = (this._colorOffset % 1 + 1) % 1;
552
+ const base2 = interpolateColor(this._parsedColors, t);
553
+ const color = applyShimmer(base2, 0.5, this._shimmerPhase, this.shimmer);
554
+ return formatColor(38, color);
555
+ }
556
+ async ask(prompt, items) {
557
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Select requires an interactive terminal");
558
+ let selectedIndex = 0;
559
+ let viewportOffset = 0;
560
+ let searchQuery = "";
561
+ let lastDrawnLines = 0;
562
+ const skipItem = { label: this.skipLabel };
563
+ const getFiltered = () => {
564
+ if (!this.searchEnabled || searchQuery === "") return items;
565
+ const q = searchQuery.toLowerCase();
566
+ return items.filter((item) => item.label.toLowerCase().includes(q) || (item.description?.toLowerCase().includes(q) ?? false));
567
+ };
568
+ process.stdout.write(HIDE_CURSOR);
569
+ const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
570
+ const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
571
+ process.stdout.write(`${glyph}${prompt}
572
+ `);
573
+ const renderList = (redraw) => {
574
+ const filtered = getFiltered();
575
+ const allItems = [...filtered, skipItem];
576
+ if (selectedIndex >= allItems.length) selectedIndex = allItems.length - 1;
577
+ if (this.maxHeight) {
578
+ if (selectedIndex < viewportOffset) viewportOffset = selectedIndex;
579
+ else if (selectedIndex >= viewportOffset + this.maxHeight) viewportOffset = selectedIndex - this.maxHeight + 1;
580
+ viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, allItems.length - this.maxHeight)));
581
+ }
582
+ const visibleStart = this.maxHeight ? viewportOffset : 0;
583
+ const visibleEnd = this.maxHeight ? Math.min(allItems.length, viewportOffset + this.maxHeight) : allItems.length;
584
+ if (redraw) {
585
+ if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP(lastDrawnLines));
586
+ process.stdout.write("\r\x1B[0J");
587
+ }
588
+ lastDrawnLines = 0;
589
+ if (this.searchEnabled) {
590
+ process.stdout.write(`\r${indent}${colorText(this.promptColor, "/")} ${searchQuery}|
591
+ `);
592
+ lastDrawnLines++;
593
+ }
594
+ const pulse = this.pulseColor();
595
+ for (let i = visibleStart; i < visibleEnd; i++) {
596
+ const item = allItems[i];
597
+ const isSelected = i === selectedIndex;
598
+ const isSkip = i === filtered.length;
599
+ const relativeNum = i - visibleStart + 1;
600
+ const numStr = isSkip ? "0." : `${relativeNum}.`;
601
+ const desc = item.description ? ` ${colorText(this.descriptionColor, `\u2014 ${item.description}`)}` : "";
602
+ let marker, tail;
603
+ if (isSelected && pulse) {
604
+ marker = `${pulse}${this.selectedPrefix}${RESET}`;
605
+ tail = ` ${pulse}${this.selectedSuffix}${RESET}`;
606
+ } else {
607
+ const fallback = isSelected ? this.promptColor : "";
608
+ marker = fallback ? colorText(fallback, this.selectedPrefix) : " ".repeat(stringLength(this.selectedPrefix));
609
+ tail = isSelected ? ` ${colorText(this.promptColor, this.selectedSuffix)}` : "";
610
+ }
611
+ process.stdout.write(`\r${indent}${marker} ${numStr} ${item.label}${desc}${tail}
612
+ `);
613
+ lastDrawnLines++;
614
+ }
615
+ };
616
+ renderList(false);
617
+ return new Promise((resolve) => {
618
+ let timer = null;
619
+ const deregisterCleanup = registerCleanup(() => {
620
+ if (timer) {
621
+ clearInterval(timer);
622
+ timer = null;
623
+ }
624
+ process.stdin.setRawMode(false);
625
+ process.stdin.pause();
626
+ process.stdin.removeListener("data", onKey);
627
+ process.stdout.write(SHOW_CURSOR);
628
+ });
629
+ const cleanup = () => {
630
+ deregisterCleanup();
631
+ if (timer) {
632
+ clearInterval(timer);
633
+ timer = null;
634
+ }
635
+ process.stdin.setRawMode(false);
636
+ process.stdin.pause();
637
+ process.stdin.removeListener("data", onKey);
638
+ process.stdout.write(SHOW_CURSOR);
639
+ };
640
+ if (this._parsedColors.length >= 2) {
641
+ timer = setInterval(() => {
642
+ this._colorOffset = (this._colorOffset + this.colorCycle * this.interval / 1e3) % 1;
643
+ if (this.shimmer > 0) {
644
+ this._shimmerPhase = (this._shimmerPhase + SHIMMER_SPEED * this.interval / 1e3) % 1;
645
+ }
646
+ renderList(true);
647
+ }, this.interval);
648
+ }
649
+ const onKey = (key) => {
650
+ const str = key.toString();
651
+ const filtered = getFiltered();
652
+ const allItems = [...filtered, skipItem];
653
+ if (str === "\x1B[A") {
654
+ selectedIndex = (selectedIndex - 1 + allItems.length) % allItems.length;
655
+ renderList(true);
656
+ } else if (str === "\x1B[B") {
657
+ selectedIndex = (selectedIndex + 1) % allItems.length;
658
+ renderList(true);
659
+ } else if (str === "\r" || str === "\n") {
660
+ cleanup();
661
+ resolve(selectedIndex === filtered.length ? null : filtered[selectedIndex] ?? null);
662
+ } else if (str === "") {
663
+ cleanup();
664
+ process.exit(130);
665
+ } else if (this.searchEnabled) {
666
+ if (str === "\x7F" || str === "\b") {
667
+ searchQuery = searchQuery.slice(0, -1);
668
+ const newFiltered = getFiltered();
669
+ if (selectedIndex >= newFiltered.length + 1) selectedIndex = Math.max(0, newFiltered.length);
670
+ viewportOffset = 0;
671
+ renderList(true);
672
+ } else if (str.length === 1 && str >= " ") {
673
+ searchQuery += str;
674
+ selectedIndex = 0;
675
+ viewportOffset = 0;
676
+ renderList(true);
677
+ }
678
+ } else {
679
+ const n = parseInt(str);
680
+ if (!isNaN(n) && n >= 0 && n <= Math.min(items.length, 9)) {
681
+ const visibleStart = this.maxHeight ? viewportOffset : 0;
682
+ selectedIndex = n === 0 ? allItems.length - 1 : visibleStart + n - 1;
683
+ selectedIndex = Math.max(0, Math.min(selectedIndex, allItems.length - 1));
684
+ renderList(true);
685
+ }
686
+ }
687
+ };
688
+ process.stdin.setRawMode(true);
689
+ process.stdin.resume();
690
+ process.stdin.on("data", onKey);
691
+ });
692
+ }
693
+ };
694
+ async function select(prompt, items, options) {
695
+ return new Select(options).ask(prompt, items);
696
+ }
697
+
698
+ // src/utils/findVariable.ts
699
+ function coerce(value, type, enumValues, min = null, max = null) {
700
+ if (type === "number" || type === "integer") {
701
+ const n = Number(value);
702
+ if (type === "integer" && !Number.isInteger(n)) throw new Error(`Invalid value "${value}" \u2014 expected an integer`);
703
+ if (min !== null && n < min) throw new Error(`Invalid value "${value}" \u2014 must be >= ${min}`);
704
+ if (max !== null && n > max) throw new Error(`Invalid value "${value}" \u2014 must be <= ${max}`);
705
+ return n;
706
+ }
707
+ if (type === "string") {
708
+ if (min !== null && value.length < min) throw new Error(`Invalid value "${value}" \u2014 must be at least ${min} characters`);
709
+ if (max !== null && value.length > max) throw new Error(`Invalid value "${value}" \u2014 must be at most ${max} characters`);
710
+ return value;
711
+ }
62
712
  if (type === "boolean") return value === "true";
713
+ if (type === "enum" && enumValues) {
714
+ if (!enumValues.includes(value)) {
715
+ throw new Error(`Invalid value "${value}" \u2014 expected one of: ${enumValues.join(", ")}`);
716
+ }
717
+ }
63
718
  return value;
64
719
  }
65
- function findVariable(array, variable, commands) {
720
+ async function promptForVariable(variable) {
721
+ const promptColor = resolveColor(config.color);
722
+ if (variable.type === "enum" && variable.enum) {
723
+ const items = variable.enum.map((label) => ({ label }));
724
+ const result = await new Select({ promptColor }).ask(`<${variable.name}>`, items);
725
+ return result !== null ? result.label : null;
726
+ }
727
+ const isNumeric = variable.type === "number" || variable.type === "integer";
728
+ return new Input({
729
+ type: variable.type,
730
+ promptColor,
731
+ min: isNumeric ? variable.min ?? void 0 : void 0,
732
+ max: isNumeric ? variable.max ?? void 0 : void 0,
733
+ minLength: !isNumeric ? variable.min ?? void 0 : void 0,
734
+ maxLength: !isNumeric ? variable.max ?? void 0 : void 0,
735
+ required: true
736
+ }).ask(`<${variable.name}>`);
737
+ }
738
+ async function findVariable(array, variable, commands) {
66
739
  if (variable.array) {
67
740
  const result = [];
68
741
  while (array.length > 0 && !array[0].startsWith("-")) {
69
742
  if (commands.includes(array[0])) break;
70
- result.push(coerce(array.shift(), variable.type));
743
+ result.push(coerce(array.shift(), variable.type, variable.enum, variable.min, variable.max));
744
+ }
745
+ if (result.length === 0 && variable.required) {
746
+ if (config.interactive && process.stdout.isTTY) {
747
+ try {
748
+ const value = await promptForVariable(variable);
749
+ if (value !== null) return [value];
750
+ } catch {
751
+ throw new Error(`Missing required variable <${variable.name}> \u2014 stdin is not interactive`);
752
+ }
753
+ }
754
+ throw new Error(`Missing required variable <${variable.name}>`);
71
755
  }
72
- if (result.length === 0 && variable.required) throw new Error(`Missing required variable <${variable.name}>`);
73
756
  return result.length > 0 ? result : true;
74
757
  }
75
758
  if (array.length > 0 && !array[0].startsWith("-")) {
76
759
  if ((!commands.includes(array[0]) || variable.required) && array[0] !== "help") {
77
- return coerce(array.shift(), variable.type);
760
+ return coerce(array.shift(), variable.type, variable.enum, variable.min, variable.max);
761
+ }
762
+ }
763
+ if (variable.required) {
764
+ if (config.interactive && process.stdout.isTTY) {
765
+ try {
766
+ const value = await promptForVariable(variable);
767
+ if (value !== null) return value;
768
+ } catch {
769
+ throw new Error(`Missing required variable <${variable.name}> \u2014 stdin is not interactive`);
770
+ }
78
771
  }
772
+ throw new Error(`Missing required variable <${variable.name}>`);
79
773
  }
80
- if (variable.required) throw new Error(`Missing required variable <${variable.name}>`);
774
+ if (variable.default !== null) return coerce(variable.default, variable.type, variable.enum);
81
775
  return true;
82
776
  }
83
777
 
84
- // src/helpers/findCommandVariables.ts
85
- function findCommandVariables(array, command2) {
86
- if (!command2.variables) return null;
778
+ // src/utils/findCommandVariables.ts
779
+ async function findCommandVariables(array, command) {
780
+ if (!command.variables) return null;
87
781
  const result = {};
88
- for (const variable of command2.variables) {
89
- const value = findVariable(array, variable, command2.commandStrings);
782
+ for (const variable of command.variables) {
783
+ const value = await findVariable(array, variable, command.commandStrings);
90
784
  if (value !== true) result[variable.name] = value;
91
785
  }
92
786
  return Object.keys(result).length > 0 ? result : null;
93
787
  }
94
788
 
95
- // src/helpers/findOption.ts
789
+ // src/utils/findOption.ts
96
790
  function findOption(string, options) {
97
791
  return options.find((o) => o.short === string || o.long === string);
98
792
  }
99
793
 
100
- // src/helpers/findVariables.ts
101
- function findVariables(base2, array, variables, commands) {
794
+ // src/utils/findVariables.ts
795
+ async function findVariables(base2, array, variables, commands) {
102
796
  const result = {};
103
797
  if (!variables) {
104
798
  if (base2) result[base2] = true;
@@ -106,7 +800,7 @@ function findVariables(base2, array, variables, commands) {
106
800
  }
107
801
  if (variables.length > 1 && base2) result[base2] = {};
108
802
  for (const variable of variables) {
109
- const value = findVariable(array, variable, commands);
803
+ const value = await findVariable(array, variable, commands);
110
804
  if (variables.length > 1 && base2) {
111
805
  ;
112
806
  result[base2][variable.name] = value;
@@ -119,33 +813,41 @@ function findVariables(base2, array, variables, commands) {
119
813
  return result;
120
814
  }
121
815
 
122
- // src/helpers/findOptions.ts
123
- function findOptions(array, command2) {
816
+ // src/utils/findOptions.ts
817
+ async function findOptions(array, command) {
124
818
  const result = {};
125
819
  while (array.length > 0 && array[0].startsWith("-")) {
126
820
  if (array[0].startsWith("--")) {
127
821
  const raw = array.shift().slice(2);
128
- const option2 = findOption(raw, command2.optionsArray);
129
- if (!option2) throw new Error(`Unknown Option: --${raw}`);
822
+ if (raw.startsWith("no-")) {
823
+ const longName = raw.slice(3);
824
+ const negated = findOption(longName, command.optionsArray);
825
+ if (negated?.long) {
826
+ result[negated.long] = false;
827
+ continue;
828
+ }
829
+ }
830
+ const option = findOption(raw, command.optionsArray);
831
+ if (!option) throw new Error(`Unknown Option: --${raw}`);
130
832
  try {
131
- Object.assign(result, findVariables(option2.long, array, option2.variables, command2.commandStrings));
833
+ Object.assign(result, await findVariables(option.long, array, option.variables, command.commandStrings));
132
834
  } catch (err) {
133
835
  ;
134
- err.message += ` for --${option2.long}`;
836
+ err.message += ` for --${option.long}`;
135
837
  throw err;
136
838
  }
137
839
  } else {
138
840
  let string = array.shift();
139
841
  const short = string.slice(1, 2);
140
- const option2 = findOption(short, command2.optionsArray);
141
- if (!option2) throw new Error(`Unknown Option: -${short}`);
142
- string = string.replace(short, "");
842
+ const option = findOption(short, command.optionsArray);
843
+ if (!option) throw new Error(`Unknown Option: -${short}`);
844
+ string = "-" + string.slice(2);
143
845
  if (string !== "-") array.unshift(string);
144
846
  try {
145
- Object.assign(result, findVariables(option2.long, array, option2.variables, command2.commandStrings));
847
+ Object.assign(result, await findVariables(option.long, array, option.variables, command.commandStrings));
146
848
  } catch (err) {
147
849
  ;
148
- err.message += ` for --${option2.long}`;
850
+ err.message += ` for --${option.long}`;
149
851
  throw err;
150
852
  }
151
853
  }
@@ -153,74 +855,6 @@ function findOptions(array, command2) {
153
855
  return result;
154
856
  }
155
857
 
156
- // src/models/Variable.ts
157
- var Variable = class {
158
- constructor(data) {
159
- this.array = false;
160
- this.name = null;
161
- this.raw = null;
162
- this.required = false;
163
- this.type = "string";
164
- this.value = null;
165
- if (!data) return;
166
- if (data.array) this.array = data.array;
167
- if (data.name) this.name = data.name;
168
- if (data.raw) this.raw = data.raw;
169
- if (data.required) this.required = data.required;
170
- if (data.type) this.type = data.type;
171
- if (this.array) this.value = [];
172
- }
173
- };
174
-
175
- // src/helpers/getVariables.ts
176
- function parseName(raw) {
177
- const colon = raw.indexOf(":");
178
- if (colon === -1) return { name: raw, type: "string" };
179
- return {
180
- name: raw.slice(0, colon),
181
- type: raw.slice(colon + 1)
182
- };
183
- }
184
- function getVariables(string) {
185
- const results = [];
186
- for (const part of string.split(" ")) {
187
- const trimmed = part.trim();
188
- if (!trimmed) continue;
189
- if (trimmed.startsWith("<") && trimmed.endsWith(">")) {
190
- const { name, type } = parseName(trimmed.slice(1, -1));
191
- results.push(new Variable({ name, raw: trimmed, required: true, type }));
192
- } else if (trimmed.startsWith("[") && trimmed.endsWith("...]")) {
193
- const { name, type } = parseName(trimmed.slice(1, -4));
194
- results.push(new Variable({ array: true, name, raw: trimmed, type }));
195
- } else if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
196
- const { name, type } = parseName(trimmed.slice(1, -1));
197
- results.push(new Variable({ name, raw: trimmed, type }));
198
- } else {
199
- throw new Error(`Unrecognized variable description: ${trimmed}`);
200
- }
201
- }
202
- return results;
203
- }
204
-
205
- // src/models/Option.ts
206
- var Option = class {
207
- constructor(data) {
208
- this.short = null;
209
- this.long = null;
210
- this.info = null;
211
- this.variables = null;
212
- if (!data) return;
213
- if (data.short) this.short = data.short;
214
- if (data.long) this.long = data.long;
215
- if (data.info) this.info = data.info;
216
- if (data.variables) this.variables = getVariables(data.variables);
217
- }
218
- description(info) {
219
- this.info = info;
220
- return this;
221
- }
222
- };
223
-
224
858
  // src/models/Command.ts
225
859
  var Command = class {
226
860
  constructor(data) {
@@ -272,8 +906,8 @@ var Command = class {
272
906
  this.middlewaresArray.push(fn);
273
907
  return this;
274
908
  }
275
- middlewares(fns) {
276
- this.middlewaresArray.push(...fns);
909
+ middlewares(fns2) {
910
+ this.middlewaresArray.push(...fns2);
277
911
  return this;
278
912
  }
279
913
  option(short, long, variables, info) {
@@ -288,9 +922,17 @@ var Command = class {
288
922
  this.versionString = v;
289
923
  return this;
290
924
  }
291
- help(_source) {
925
+ buildInfo(info, variables) {
926
+ const hints = variables?.map((v) => v.hint).filter(Boolean) ?? [];
927
+ return [info, ...hints].filter(Boolean).join(" ");
928
+ }
929
+ help(source) {
930
+ const recursive = source?.includes("-r") === true || source?.includes("--recursive") === true;
931
+ this.printHelp(this.name ?? "Program", recursive);
932
+ }
933
+ printHelp(fullName, recursive) {
292
934
  const table = [];
293
- let program = this.name ?? "Program";
935
+ let program = fullName;
294
936
  if (this.variables) for (const v of this.variables) program += ` ${v.raw}`;
295
937
  if (this.optionsArray.length > 0) program += " [...options]";
296
938
  table.push({ title: "\nCommand", info: program, data: [] });
@@ -304,7 +946,7 @@ var Command = class {
304
946
  if (opt.short && opt.long) name += ", ";
305
947
  if (opt.long) name += `--${opt.long}`;
306
948
  if (opt.variables) for (const v of opt.variables) name += ` ${v.raw}`;
307
- section.data.push([name, opt.info ?? ""]);
949
+ section.data.push([name, this.buildInfo(opt.info, opt.variables)]);
308
950
  }
309
951
  table.push(section);
310
952
  }
@@ -313,7 +955,7 @@ var Command = class {
313
955
  for (const cmd of this.commandsArray) {
314
956
  let name = cmd.name ?? "";
315
957
  if (cmd.variables) for (const v of cmd.variables) name += ` ${v.raw}`;
316
- section.data.push([name, cmd.info ?? ""]);
958
+ section.data.push([name, this.buildInfo(cmd.info, cmd.variables)]);
317
959
  }
318
960
  table.push(section);
319
961
  }
@@ -327,7 +969,8 @@ var Command = class {
327
969
  }
328
970
  const lines = [];
329
971
  for (const section of table) {
330
- lines.push(section.title ? import_cosmetic.default.cyan.underline.encoder(section.title) : "");
972
+ const styled = typeof config.color === "number" ? import_cosmetic.default.xterm(config.color) : config.color.startsWith("#") ? import_cosmetic.default.hex(config.color) : import_cosmetic.default[config.color];
973
+ lines.push(section.title ? styled.underline.encoder(section.title) : "");
331
974
  if (section.info) lines.push(section.info);
332
975
  for (const row of section.data) {
333
976
  let line = "";
@@ -340,27 +983,37 @@ var Command = class {
340
983
  lines.push("");
341
984
  }
342
985
  for (const line of lines) console.log(line);
986
+ if (recursive) {
987
+ for (const cmd of this.commandsArray) {
988
+ cmd.printHelp(`${fullName} ${cmd.name ?? ""}`, true);
989
+ }
990
+ }
343
991
  }
344
- async parse(input) {
345
- const array = [...input];
992
+ async parse(input2) {
993
+ const array = [...input2];
346
994
  array.splice(0, 2);
347
- let command2 = this;
995
+ let command = this;
348
996
  const options = { _source: Array.from(array) };
997
+ const ddIdx = array.indexOf("--");
998
+ if (ddIdx !== -1) {
999
+ options._ = array.splice(ddIdx + 1);
1000
+ array.splice(ddIdx, 1);
1001
+ }
349
1002
  while (array.length) {
350
1003
  if (!array.includes("help")) {
351
- Object.assign(options, findOptions(array, command2));
352
- const cmdVars = findCommandVariables(array, command2);
1004
+ Object.assign(options, await findOptions(array, command));
1005
+ const cmdVars = await findCommandVariables(array, command);
353
1006
  if (cmdVars) Object.assign(options, cmdVars);
354
- Object.assign(options, findOptions(array, command2));
1007
+ Object.assign(options, await findOptions(array, command));
355
1008
  }
356
1009
  if (array.length) {
357
1010
  if (!array.includes("help")) {
358
- for (const mw of command2.middlewaresArray) await mw(options);
1011
+ for (const mw of command.middlewaresArray) await mw(options);
359
1012
  }
360
- const next = findCommand(array, command2.commandsArray);
361
- if (!next && array[0] === "help") return command2.help(options._source);
1013
+ const next = findCommand(array, command.commandsArray);
1014
+ if (!next && array[0] === "help") return command.help(options._source);
362
1015
  if (!next) throw new SyntaxError(`Unknown command: ${array[0]}`);
363
- const name = command2.name ?? "_base";
1016
+ const name = command.name ?? "_base";
364
1017
  if (!options._parents) options._parents = {};
365
1018
  options._parents[name] = {};
366
1019
  for (const key of Object.keys(options)) {
@@ -369,70 +1022,1831 @@ var Command = class {
369
1022
  delete options[key];
370
1023
  }
371
1024
  }
372
- command2 = next;
1025
+ command = next;
373
1026
  }
374
1027
  }
375
- for (const mw of command2.middlewaresArray) await mw(options);
376
- if (command2.actionFunction) return command2.actionFunction(options);
377
- if (options._source.length === 2) return command2.help(options._source);
378
- throw new Error(`No action for command: ${command2.name ?? "_base"}`);
1028
+ for (const opt of command.optionsArray) {
1029
+ if (!opt.long || !opt.variables) continue;
1030
+ for (const v of opt.variables) {
1031
+ if (v.default !== null && !(opt.long in options)) {
1032
+ options[opt.long] = coerce(v.default, v.type, v.enum, v.min, v.max);
1033
+ }
1034
+ }
1035
+ }
1036
+ for (const mw of command.middlewaresArray) await mw(options);
1037
+ if (command.actionFunction) return command.actionFunction(options);
1038
+ if (options._source.length === 2) return command.help(options._source);
1039
+ throw new Error(`No action for command: ${command.name ?? "_base"}`);
379
1040
  }
380
1041
  };
381
1042
 
382
- // src/models/TermKit.ts
383
- var _TermKit = class _TermKit {
384
- static set defaults(obj) {
385
- _TermKit.commandDefaults = obj;
1043
+ // src/models/Bar.ts
1044
+ var Bar = class {
1045
+ constructor(options = {}) {
1046
+ // used by MultiBar — not intended for direct external use
1047
+ this._isManaged = false;
1048
+ this._managedFinalLine = null;
1049
+ this._colors = [];
1050
+ this._parsedColors = [];
1051
+ this._dimmedColors = [];
1052
+ this._bgColors = [];
1053
+ this._parsedBgColors = [];
1054
+ this._colorOffset = 0;
1055
+ this._shimmerPhase = 0;
1056
+ this._completed = false;
1057
+ this._resizeListener = null;
1058
+ this._cleanupDeregister = null;
1059
+ this._total = null;
1060
+ this._tickCount = 0;
1061
+ this._startTime = null;
1062
+ this._etaSuffix = "";
1063
+ this.running = false;
1064
+ this.forwardMotion = true;
1065
+ this._autoLength = options.length === void 0;
1066
+ this.length = options.length ?? process.stdout.columns ?? 80;
1067
+ this.prefixString = options.prefix ?? "[";
1068
+ this.suffixString = options.suffix ?? "]";
1069
+ this.character = options.character ?? "\u2500\u2500";
1070
+ this.beforeEmpty = options.before ?? " ";
1071
+ this.afterEmpty = options.after ?? " ";
1072
+ this.position = 1;
1073
+ this.interval = options.interval ?? 35;
1074
+ this.mode = options.mode ?? "bounce";
1075
+ this.progress = options.progress;
1076
+ this.colorFill = options.colorFill ?? false;
1077
+ this.colorCycle = options.colorCycle ?? 0.5;
1078
+ this.shimmer = options.shimmer ?? 0;
1079
+ this.text = options.text ?? "";
1080
+ this.reverse = options.reverse ?? false;
1081
+ this.onBounce = options.onBounce;
1082
+ this.onLoop = options.onLoop;
1083
+ this.onComplete = options.onComplete;
1084
+ this.colors = options.colors ?? ["#c026d3", "#e879f9"];
1085
+ this.bgColors = options.bgColors ?? [];
1086
+ this._successColor = options.successColor ?? GREEN;
1087
+ this._failColor = options.failColor ?? RED;
1088
+ this._warnColor = options.warnColor ?? YELLOW;
1089
+ this._infoColor = options.infoColor ?? BLUE;
1090
+ this._glyphs = options.glyphs ?? true;
1091
+ this._showRate = options.showRate ?? false;
1092
+ this._showEta = options.showEta ?? false;
1093
+ this._rateUnit = options.rateUnit ?? "";
386
1094
  }
387
- static setDefaults(obj) {
388
- _TermKit.commandDefaults = obj;
1095
+ get colors() {
1096
+ return this._colors;
389
1097
  }
390
- static command(name, variables, info) {
391
- const cmd = new Command(Object.assign({ name, variables, info }, _TermKit.commandDefaults));
392
- if (!_TermKit.base) _TermKit.base = cmd;
393
- return cmd;
1098
+ set colors(value) {
1099
+ this._colors = value;
1100
+ this._parsedColors = value.length > 1 ? value.map(parseHex) : [];
1101
+ this._dimmedColors = this._parsedColors.map(dimColor);
394
1102
  }
395
- static middleware(action) {
396
- return action;
1103
+ get bgColors() {
1104
+ return this._bgColors;
397
1105
  }
398
- static option(short, long, variables, info) {
399
- return new Option({ short, long, variables, info });
1106
+ set bgColors(value) {
1107
+ this._bgColors = value;
1108
+ this._parsedBgColors = value.length > 1 ? value.map(parseHex) : [];
400
1109
  }
401
- static parse(arr) {
402
- if (!_TermKit.base) throw new Error("No command defined");
403
- return _TermKit.base.parse(arr);
1110
+ set prefix(string) {
1111
+ if (stringLength(string) > stringLength(this.prefixString)) {
1112
+ this.position -= stringLength(string) - 1;
1113
+ if (this.position < 1) this.position = 1;
1114
+ } else {
1115
+ this.position += stringLength(this.prefixString) - 1;
1116
+ }
1117
+ this.prefixString = string;
1118
+ }
1119
+ set suffix(string) {
1120
+ const length = this.length - stringLength(this.prefixString) - stringLength(this.character) - stringLength(string);
1121
+ if (this.position > length) {
1122
+ this.position = length;
1123
+ if (this.forwardMotion) this.forwardMotion = false;
1124
+ }
1125
+ this.suffixString = string;
1126
+ }
1127
+ set before(string) {
1128
+ this.beforeEmpty = stringLength(string) > 1 ? string.charAt(0) : string;
1129
+ }
1130
+ set after(string) {
1131
+ this.afterEmpty = stringLength(string) > 1 ? string.charAt(0) : string;
1132
+ }
1133
+ set empty(string) {
1134
+ const char = stringLength(string) > 1 ? string.charAt(0) : string;
1135
+ this.beforeEmpty = char;
1136
+ this.afterEmpty = char;
1137
+ }
1138
+ start() {
1139
+ if (this._isManaged) return;
1140
+ if (!process.stdout.isTTY) return;
1141
+ this.running = true;
1142
+ process.stdout.write(HIDE_CURSOR);
1143
+ this._cleanupDeregister = registerCleanup(() => {
1144
+ this.running = false;
1145
+ if (this._resizeListener) {
1146
+ process.stdout.off("resize", this._resizeListener);
1147
+ this._resizeListener = null;
1148
+ }
1149
+ process.stdout.clearLine?.(0);
1150
+ process.stdout.write(SHOW_CURSOR);
1151
+ });
1152
+ if (this._autoLength) {
1153
+ this._resizeListener = () => {
1154
+ this.length = process.stdout.columns ?? 80;
1155
+ process.stdout.write("\x1B[0J");
1156
+ };
1157
+ process.stdout.on("resize", this._resizeListener);
1158
+ }
1159
+ this.run();
1160
+ }
1161
+ stop(message) {
1162
+ this.running = false;
1163
+ if (this._isManaged) {
1164
+ this._managedFinalLine = message ?? "";
1165
+ return this;
1166
+ }
1167
+ this._cleanupDeregister?.();
1168
+ this._cleanupDeregister = null;
1169
+ if (process.stdout.isTTY) {
1170
+ if (this._resizeListener) {
1171
+ process.stdout.off("resize", this._resizeListener);
1172
+ this._resizeListener = null;
1173
+ }
1174
+ this.clear();
1175
+ process.stdout.write(SHOW_CURSOR);
1176
+ }
1177
+ if (message) process.stdout.write(`${message}
1178
+ `);
1179
+ return this;
1180
+ }
1181
+ message(string) {
1182
+ this.text = string;
1183
+ return this;
1184
+ }
1185
+ track(total, options) {
1186
+ this._total = total;
1187
+ this._tickCount = 0;
1188
+ this._startTime = Date.now();
1189
+ this._etaSuffix = "";
1190
+ if (options?.unit !== void 0) this._rateUnit = options.unit;
1191
+ if (options?.showRate !== void 0) this._showRate = options.showRate;
1192
+ if (options?.showEta !== void 0) this._showEta = options.showEta;
1193
+ if (!this._showRate && !this._showEta) {
1194
+ this._showRate = true;
1195
+ this._showEta = true;
1196
+ }
1197
+ return this;
1198
+ }
1199
+ tick(n = 1) {
1200
+ this._tickCount += n;
1201
+ if (this._total !== null && this._startTime !== null) {
1202
+ this.progress = Math.min(1, this._tickCount / this._total);
1203
+ const elapsed = (Date.now() - this._startTime) / 1e3;
1204
+ const rate = elapsed > 0 ? this._tickCount / elapsed : 0;
1205
+ const remaining = this._total - this._tickCount;
1206
+ const parts = [];
1207
+ if (this._showRate) {
1208
+ const rateStr = rate >= 1e3 ? `${(rate / 1e3).toFixed(1)}k` : rate >= 1 ? rate.toFixed(1) : rate.toFixed(2);
1209
+ parts.push(`${rateStr}/s${this._rateUnit ? ` ${this._rateUnit}` : ""}`);
1210
+ }
1211
+ if (this._showEta && remaining > 0) {
1212
+ const eta = rate > 0 ? remaining / rate : 0;
1213
+ const etaStr = eta >= 3600 ? `${Math.floor(eta / 3600)}h${Math.floor(eta % 3600 / 60)}m` : eta >= 60 ? `${Math.floor(eta / 60)}m${Math.floor(eta % 60)}s` : `${Math.ceil(eta)}s`;
1214
+ parts.push(`ETA ${etaStr}`);
1215
+ }
1216
+ this._etaSuffix = parts.join(" \xB7 ");
1217
+ }
1218
+ return this;
1219
+ }
1220
+ get rate() {
1221
+ if (this._startTime === null || this._tickCount === 0) return 0;
1222
+ const elapsed = (Date.now() - this._startTime) / 1e3;
1223
+ return elapsed > 0 ? this._tickCount / elapsed : 0;
1224
+ }
1225
+ get eta() {
1226
+ if (this._total === null || this.rate === 0) return 0;
1227
+ return Math.max(0, (this._total - this._tickCount) / this.rate);
1228
+ }
1229
+ succeed(string) {
1230
+ this.running = false;
1231
+ if (this._isManaged) {
1232
+ const glyph = this._glyphs ? colorText(this._successColor, "\u2714") + " " : "";
1233
+ this._managedFinalLine = `${glyph}${string ?? ""}`;
1234
+ return this;
1235
+ }
1236
+ this._cleanupDeregister?.();
1237
+ this._cleanupDeregister = null;
1238
+ if (process.stdout.isTTY) {
1239
+ if (this._resizeListener) {
1240
+ process.stdout.off("resize", this._resizeListener);
1241
+ this._resizeListener = null;
1242
+ }
1243
+ this.clear();
1244
+ process.stdout.write(`${SHOW_CURSOR}${colorText(this._successColor, "\u2714")}${string ? ` ${string}` : ""}
1245
+ `);
1246
+ } else {
1247
+ process.stdout.write(`${this._glyphs ? `\u2714${string ? ` ${string}` : ""}` : string ?? ""}
1248
+ `);
1249
+ }
1250
+ return this;
1251
+ }
1252
+ fail(string) {
1253
+ this.running = false;
1254
+ if (this._isManaged) {
1255
+ const glyph = this._glyphs ? colorText(this._failColor, "\u2716") + " " : "";
1256
+ this._managedFinalLine = `${glyph}${string ?? ""}`;
1257
+ return this;
1258
+ }
1259
+ this._cleanupDeregister?.();
1260
+ this._cleanupDeregister = null;
1261
+ if (process.stdout.isTTY) {
1262
+ if (this._resizeListener) {
1263
+ process.stdout.off("resize", this._resizeListener);
1264
+ this._resizeListener = null;
1265
+ }
1266
+ this.clear();
1267
+ process.stdout.write(`${SHOW_CURSOR}${colorText(this._failColor, "\u2716")}${string ? ` ${string}` : ""}
1268
+ `);
1269
+ } else {
1270
+ process.stdout.write(`${this._glyphs ? `\u2716${string ? ` ${string}` : ""}` : string ?? ""}
1271
+ `);
1272
+ }
1273
+ return this;
1274
+ }
1275
+ warn(string) {
1276
+ this.running = false;
1277
+ if (this._isManaged) {
1278
+ const glyph = this._glyphs ? colorText(this._warnColor, "\u26A0") + " " : "";
1279
+ this._managedFinalLine = `${glyph}${string ?? ""}`;
1280
+ return this;
1281
+ }
1282
+ this._cleanupDeregister?.();
1283
+ this._cleanupDeregister = null;
1284
+ if (process.stdout.isTTY) {
1285
+ if (this._resizeListener) {
1286
+ process.stdout.off("resize", this._resizeListener);
1287
+ this._resizeListener = null;
1288
+ }
1289
+ this.clear();
1290
+ process.stdout.write(`${SHOW_CURSOR}${colorText(this._warnColor, "\u26A0")}${string ? ` ${string}` : ""}
1291
+ `);
1292
+ } else {
1293
+ process.stdout.write(`${this._glyphs ? `\u26A0${string ? ` ${string}` : ""}` : string ?? ""}
1294
+ `);
1295
+ }
1296
+ return this;
1297
+ }
1298
+ info(string) {
1299
+ this.running = false;
1300
+ if (this._isManaged) {
1301
+ const glyph = this._glyphs ? colorText(this._infoColor, "\u2139") + " " : "";
1302
+ this._managedFinalLine = `${glyph}${string ?? ""}`;
1303
+ return this;
1304
+ }
1305
+ this._cleanupDeregister?.();
1306
+ this._cleanupDeregister = null;
1307
+ if (process.stdout.isTTY) {
1308
+ if (this._resizeListener) {
1309
+ process.stdout.off("resize", this._resizeListener);
1310
+ this._resizeListener = null;
1311
+ }
1312
+ this.clear();
1313
+ process.stdout.write(`${SHOW_CURSOR}${colorText(this._infoColor, "\u2139")}${string ? ` ${string}` : ""}
1314
+ `);
1315
+ } else {
1316
+ process.stdout.write(`${this._glyphs ? `\u2139${string ? ` ${string}` : ""}` : string ?? ""}
1317
+ `);
1318
+ }
1319
+ return this;
1320
+ }
1321
+ // Returns the rendered bar line string for the current animation frame.
1322
+ // width overrides this.length for use by MultiBar.
1323
+ renderLine(width) {
1324
+ return this.buildLine(width);
1325
+ }
1326
+ // Advances animation state (color cycle, shimmer, indeterminate position).
1327
+ // Called by MultiBar after reading renderLine() each tick.
1328
+ advanceFrame() {
1329
+ const totalLength = this.length;
1330
+ const textReserved = this.text ? stringLength(this.text) + 1 : 0;
1331
+ const suffixReserved = this._etaSuffix ? stringLength(this._etaSuffix) + 1 : 0;
1332
+ const length = Math.max(1, totalLength - textReserved - suffixReserved - stringLength(this.prefixString) - stringLength(this.character) - stringLength(this.suffixString));
1333
+ if (this.progress === void 0) this.advanceIndeterminate(length);
1334
+ if (this.colorCycle > 0) this._colorOffset = (this._colorOffset + this.colorCycle * this.interval / 1e3) % 1;
1335
+ if (this.shimmer > 0) this._shimmerPhase = (this._shimmerPhase + SHIMMER_SPEED * this.interval / 1e3) % 1;
1336
+ }
1337
+ clear() {
1338
+ process.stdout.clearLine?.(0);
1339
+ }
1340
+ effectiveT(t) {
1341
+ if (this.colorCycle === 0) return t;
1342
+ return ((t + this._colorOffset) % 1 + 1) % 1;
1343
+ }
1344
+ coloredChar(colors, code, t) {
1345
+ const et = this.effectiveT(t);
1346
+ const base2 = interpolateColor(colors, et);
1347
+ const shimmered = applyShimmer(base2, t, this._shimmerPhase, this.shimmer);
1348
+ return formatColor(code, shimmered);
1349
+ }
1350
+ colorChar(t) {
1351
+ const fg = this._parsedColors.length >= 2 ? this.coloredChar(this._parsedColors, 38, t) : "";
1352
+ const bg = this._parsedBgColors.length >= 2 ? this.coloredChar(this._parsedBgColors, 48, t) : "";
1353
+ return fg || bg ? fg + bg + this.character + RESET : this.character;
1354
+ }
1355
+ colorFillChar(char, t) {
1356
+ const fg = this.colorFill && this._dimmedColors.length >= 2 ? this.coloredChar(this._dimmedColors, 38, t) : "";
1357
+ const bg = this._parsedBgColors.length >= 2 ? this.coloredChar(this._parsedBgColors, 48, t) : "";
1358
+ return fg || bg ? fg + bg + char + RESET : char;
1359
+ }
1360
+ buildDeterminate(length) {
1361
+ const charWidth = stringLength(this.character);
1362
+ const clamped = Math.max(0, Math.min(1, this.progress));
1363
+ const filled = Math.round(clamped * length);
1364
+ let working = this.prefixString;
1365
+ for (let i = 0; i < filled; i++) {
1366
+ const t = length > 1 ? i / (length - 1) : 0;
1367
+ working += this.colorChar(t);
1368
+ }
1369
+ for (let i = filled; i < length; i++) {
1370
+ const t = length > 1 ? i / (length - 1) : 0;
1371
+ for (let j = 0; j < charWidth; j++) {
1372
+ working += this.colorFillChar(this.afterEmpty, t);
1373
+ }
1374
+ }
1375
+ working += this.suffixString;
1376
+ if (clamped >= 1 && !this._completed) {
1377
+ this._completed = true;
1378
+ this.onComplete?.();
1379
+ } else if (clamped < 1) {
1380
+ this._completed = false;
1381
+ }
1382
+ return working;
1383
+ }
1384
+ buildIndeterminate(length) {
1385
+ const pos = Math.max(1, Math.min(length, this.position));
1386
+ let working = this.prefixString;
1387
+ let i = 1;
1388
+ while (i < pos) {
1389
+ const t = length > 1 ? (i - 1) / (length - 1) : 0;
1390
+ working += this.colorFillChar(this.beforeEmpty, t);
1391
+ i++;
1392
+ }
1393
+ const charT = length > 1 ? (pos - 1) / (length - 1) : 0;
1394
+ working += this.colorChar(charT);
1395
+ i++;
1396
+ while (i <= length) {
1397
+ const t = length > 1 ? (i - 1) / (length - 1) : 0;
1398
+ working += this.colorFillChar(this.afterEmpty, t);
1399
+ i++;
1400
+ }
1401
+ working += this.suffixString;
1402
+ return working;
1403
+ }
1404
+ advanceIndeterminate(length) {
1405
+ if (this.position > length) {
1406
+ this.position = length;
1407
+ this.forwardMotion = false;
1408
+ }
1409
+ if (this.position < 1) {
1410
+ this.position = 1;
1411
+ this.forwardMotion = true;
1412
+ }
1413
+ if (this.mode === "loop") {
1414
+ this.position++;
1415
+ if (this.position > length) {
1416
+ this.position = 1;
1417
+ this.onLoop?.();
1418
+ }
1419
+ } else if (this.mode === "loop-reverse") {
1420
+ this.position--;
1421
+ if (this.position < 1) {
1422
+ this.position = length;
1423
+ this.onLoop?.();
1424
+ }
1425
+ } else {
1426
+ if (this.forwardMotion) {
1427
+ this.position++;
1428
+ if (this.position >= length) {
1429
+ this.forwardMotion = false;
1430
+ this.onBounce?.();
1431
+ }
1432
+ } else {
1433
+ this.position--;
1434
+ if (this.position <= 1) {
1435
+ this.forwardMotion = true;
1436
+ this.onBounce?.();
1437
+ }
1438
+ }
1439
+ }
1440
+ }
1441
+ buildLine(width) {
1442
+ const totalLength = width ?? this.length;
1443
+ const charWidth = stringLength(this.character);
1444
+ const textReserved = this.text ? stringLength(this.text) + 1 : 0;
1445
+ const suffixReserved = this._etaSuffix ? stringLength(this._etaSuffix) + 1 : 0;
1446
+ const available = totalLength - textReserved - suffixReserved - stringLength(this.prefixString) - stringLength(this.suffixString);
1447
+ const length = this.progress !== void 0 ? (
1448
+ // Determinate: every position (filled or empty) is charWidth wide → constant bar width
1449
+ Math.max(1, Math.floor((available - 1) / charWidth))
1450
+ ) : (
1451
+ // Indeterminate: one position uses character, rest use 1-char fill (unchanged formula)
1452
+ Math.max(1, available - charWidth)
1453
+ );
1454
+ const working = this.progress !== void 0 ? this.buildDeterminate(length) : this.buildIndeterminate(length);
1455
+ const t = this.text;
1456
+ const suffix = this._etaSuffix ? ` ${this._etaSuffix}` : "";
1457
+ return this.reverse ? t ? `${t} ${working}` : working : t ? `${working} ${t}${suffix}` : `${working}${suffix}`;
1458
+ }
1459
+ run() {
1460
+ process.stdout.write(`${this.buildLine()}\x1B[K\r`);
1461
+ this.advanceFrame();
1462
+ setTimeout(() => {
1463
+ if (this.running) this.run();
1464
+ }, this.interval);
1465
+ }
1466
+ };
1467
+ Bar.COLORS = {
1468
+ blueRed: ["#0000ff", "#ff0000"],
1469
+ redBlue: ["#ff0000", "#0000ff"],
1470
+ rainbow: ["#ff0000", "#ff7f00", "#ffff00", "#00ff00", "#0000ff", "#8b00ff"],
1471
+ heat: ["#0000ff", "#00ffff", "#ffff00", "#ff0000"],
1472
+ cool: ["#00ffff", "#0000ff", "#8b00ff"],
1473
+ sunset: ["#8b00ff", "#ff0000", "#ff7f00"]
1474
+ };
1475
+
1476
+ // src/models/Chart.ts
1477
+ var Chart_exports = {};
1478
+ __export(Chart_exports, {
1479
+ Bar: () => Bar2,
1480
+ Heatmap: () => Heatmap,
1481
+ Line: () => Line,
1482
+ Scatter: () => Scatter,
1483
+ Sparkline: () => Sparkline,
1484
+ VerticalBar: () => VerticalBar
1485
+ });
1486
+ var import_cosmetic2 = __toESM(require("cosmetic"));
1487
+
1488
+ // src/utils/padLeft.ts
1489
+ var padLeft = (string, padding) => {
1490
+ while (stringLength(string) < padding) string = ` ${string}`;
1491
+ return string;
1492
+ };
1493
+
1494
+ // src/utils/padRight.ts
1495
+ var padRight = (string, padding) => {
1496
+ while (stringLength(string) < padding) string += " ";
1497
+ return string;
1498
+ };
1499
+
1500
+ // src/models/Chart.ts
1501
+ function formatNum(n) {
1502
+ if (Number.isInteger(n)) return String(n);
1503
+ return parseFloat(n.toFixed(2)).toString();
1504
+ }
1505
+ function applyConfigColor(s) {
1506
+ const c = config.color;
1507
+ if (typeof c === "number") return import_cosmetic2.default.xterm(c).encoder(s);
1508
+ if (c.startsWith("#")) return import_cosmetic2.default.hex(c).encoder(s);
1509
+ return import_cosmetic2.default[c].encoder(s);
1510
+ }
1511
+ function applyPadding(str, paddingX, paddingY) {
1512
+ const lines = str.split("\n");
1513
+ if (lines[lines.length - 1] === "") lines.pop();
1514
+ const indented = lines.map((l) => " ".repeat(paddingX) + l).join("\n") + "\n";
1515
+ return "\n".repeat(paddingY) + indented + "\n".repeat(paddingY);
1516
+ }
1517
+ var BLOCKS = [" ", "\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
1518
+ var Bar2 = class {
1519
+ constructor(data, options = {}) {
1520
+ this.string = "";
1521
+ const paddingX = options.paddingX ?? 0;
1522
+ const paddingY = options.paddingY ?? 0;
1523
+ let maxKeyLen = 0;
1524
+ let maxValue = 0;
1525
+ let maxValueLen = 0;
1526
+ for (const item of data) {
1527
+ if (!item) continue;
1528
+ if (item.key) maxKeyLen = Math.max(maxKeyLen, stringLength(item.key));
1529
+ maxValue = Math.max(maxValue, item.value);
1530
+ maxValueLen = Math.max(maxValueLen, String(item.value).length);
1531
+ }
1532
+ const cols = options.width ?? process.stdout.columns ?? 80;
1533
+ const available = cols - maxKeyLen - maxValueLen - 3;
1534
+ const scale = maxValue > 0 && available > 0 ? available / maxValue : 0;
1535
+ const encodeKey = (s) => typeof config.color === "number" ? import_cosmetic2.default.xterm(config.color).encoder(s) : config.color.startsWith("#") ? import_cosmetic2.default.hex(config.color).encoder(s) : import_cosmetic2.default[config.color].encoder(s);
1536
+ for (const item of data) {
1537
+ if (!item) {
1538
+ this.string += "\n";
1539
+ continue;
1540
+ }
1541
+ const rawKey = item.key ?? "";
1542
+ const keyPart = (rawKey ? encodeKey(rawKey) : "") + " ".repeat(maxKeyLen - stringLength(rawKey));
1543
+ const barWidth = Math.max(1, Math.floor(item.value * scale));
1544
+ let bar = (item.character ?? " ").repeat(barWidth);
1545
+ bar = item.style ? item.style(bar) : import_cosmetic2.default.background.white.encoder(bar);
1546
+ this.string += `${keyPart}|${bar} ${item.value}
1547
+ `;
1548
+ }
1549
+ if (paddingX > 0 || paddingY > 0) this.string = applyPadding(this.string, paddingX, paddingY);
1550
+ }
1551
+ print() {
1552
+ process.stdout.write(this.string);
1553
+ }
1554
+ toString() {
1555
+ return this.string;
1556
+ }
1557
+ };
1558
+ var VerticalBar = class {
1559
+ constructor(data, options = {}) {
1560
+ this.string = "";
1561
+ const paddingX = options.paddingX ?? 0;
1562
+ const paddingY = options.paddingY ?? 0;
1563
+ const height = options.height ?? 10;
1564
+ const colWidth = options.colWidth ?? (options.width ? Math.max(1, Math.floor(options.width / data.length)) : 2);
1565
+ let maxValue = 0;
1566
+ for (const item of data) {
1567
+ if (item) maxValue = Math.max(maxValue, item.value);
1568
+ }
1569
+ for (let row = height; row >= 1; row--) {
1570
+ let line = "";
1571
+ for (const item of data) {
1572
+ if (!item) {
1573
+ line += " ".repeat(colWidth);
1574
+ continue;
1575
+ }
1576
+ const barHeight = maxValue > 0 ? item.value / maxValue * height : 0;
1577
+ const full = Math.floor(barHeight);
1578
+ const frac = barHeight - full;
1579
+ let chars;
1580
+ if (row <= full) {
1581
+ chars = "\u2588".repeat(colWidth);
1582
+ } else if (row === full + 1 && frac > 0) {
1583
+ chars = (BLOCKS[Math.round(frac * 8)] ?? " ").repeat(colWidth);
1584
+ } else {
1585
+ chars = " ".repeat(colWidth);
1586
+ }
1587
+ if (item.style && chars.trim()) chars = item.style(chars);
1588
+ line += chars;
1589
+ }
1590
+ this.string += line + "\n";
1591
+ }
1592
+ this.string += "\u2500".repeat(data.length * colWidth) + "\n";
1593
+ if (data.some((item) => item?.key)) {
1594
+ const encodeLabel = (s) => typeof config.color === "number" ? import_cosmetic2.default.xterm(config.color).encoder(s) : config.color.startsWith("#") ? import_cosmetic2.default.hex(config.color).encoder(s) : import_cosmetic2.default[config.color].encoder(s);
1595
+ let labels = "";
1596
+ for (const item of data) {
1597
+ if (!item?.key) {
1598
+ labels += " ".repeat(colWidth);
1599
+ } else {
1600
+ const key = item.key.length > colWidth ? item.key.slice(0, colWidth) : item.key;
1601
+ labels += encodeLabel(key) + " ".repeat(colWidth - key.length);
1602
+ }
1603
+ }
1604
+ this.string += labels + "\n";
1605
+ }
1606
+ if (paddingX > 0 || paddingY > 0) this.string = applyPadding(this.string, paddingX, paddingY);
1607
+ }
1608
+ print() {
1609
+ process.stdout.write(this.string);
1610
+ }
1611
+ toString() {
1612
+ return this.string;
1613
+ }
1614
+ };
1615
+ var Heatmap = class {
1616
+ constructor(data, options = {}) {
1617
+ this.string = "";
1618
+ const paddingX = options.paddingX ?? 0;
1619
+ const paddingY = options.paddingY ?? 0;
1620
+ const cellWidth = options.cellWidth ?? 2;
1621
+ const parsedColors = (options.colors ?? ["#0000ff", "#ff0000"]).map(parseHex);
1622
+ let min = options.min;
1623
+ let max = options.max;
1624
+ for (const row of data) {
1625
+ for (const val of row) {
1626
+ if (min === void 0 || val < min) min = val;
1627
+ if (max === void 0 || val > max) max = val;
1628
+ }
1629
+ }
1630
+ const lo = min ?? 0;
1631
+ const hi = max ?? 1;
1632
+ const range = hi - lo || 1;
1633
+ const rowLabelWidth = options.rowLabels ? Math.max(...options.rowLabels.map((l) => stringLength(l))) + 1 : 0;
1634
+ if (options.colLabels) {
1635
+ this.string += " ".repeat(rowLabelWidth);
1636
+ for (const label of options.colLabels) {
1637
+ this.string += label.length > cellWidth ? label.slice(0, cellWidth) : label.padEnd(cellWidth);
1638
+ }
1639
+ this.string += "\n";
1640
+ }
1641
+ for (let r = 0; r < data.length; r++) {
1642
+ if (options.rowLabels) {
1643
+ this.string += padRight(options.rowLabels[r] ?? "", rowLabelWidth - 1) + " ";
1644
+ }
1645
+ for (const val of data[r]) {
1646
+ const t = (val - lo) / range;
1647
+ const color = interpolateColor(parsedColors, t);
1648
+ this.string += `${formatColor(48, color)}${" ".repeat(cellWidth)}${RESET}`;
1649
+ }
1650
+ this.string += "\n";
1651
+ }
1652
+ if (paddingX > 0 || paddingY > 0) this.string = applyPadding(this.string, paddingX, paddingY);
1653
+ }
1654
+ print() {
1655
+ process.stdout.write(this.string);
1656
+ }
1657
+ toString() {
1658
+ return this.string;
1659
+ }
1660
+ };
1661
+ var Scatter = class {
1662
+ constructor(data, options = {}) {
1663
+ this.string = "";
1664
+ const paddingX = options.paddingX ?? 0;
1665
+ const paddingY = options.paddingY ?? 0;
1666
+ const defaultChar = options.character ?? "\u2022";
1667
+ const showAxes = options.axes ?? true;
1668
+ let xMin = options.xMin ?? (data.length ? Math.min(...data.map((p) => p.x)) : 0);
1669
+ let xMax = options.xMax ?? (data.length ? Math.max(...data.map((p) => p.x)) : 1);
1670
+ let yMin = options.yMin ?? (data.length ? Math.min(...data.map((p) => p.y)) : 0);
1671
+ let yMax = options.yMax ?? (data.length ? Math.max(...data.map((p) => p.y)) : 1);
1672
+ if (xMin === xMax) {
1673
+ xMin -= 1;
1674
+ xMax += 1;
1675
+ }
1676
+ if (yMin === yMax) {
1677
+ yMin -= 1;
1678
+ yMax += 1;
1679
+ }
1680
+ const totalWidth = options.width ?? process.stdout.columns ?? 80;
1681
+ const totalHeight = options.height ?? 20;
1682
+ const plotWidth = showAxes ? Math.max(1, totalWidth - Math.max(formatNum(yMax).length, formatNum(yMin).length, formatNum((yMin + yMax) / 2).length) - 1) : totalWidth;
1683
+ const plotHeight = showAxes ? Math.max(1, totalHeight - 2) : totalHeight;
1684
+ const grid = Array.from({ length: plotHeight }, () => Array(plotWidth).fill(" "));
1685
+ for (const point of data) {
1686
+ const col = Math.round((point.x - xMin) / (xMax - xMin) * (plotWidth - 1));
1687
+ const row = plotHeight - 1 - Math.round((point.y - yMin) / (yMax - yMin) * (plotHeight - 1));
1688
+ if (col >= 0 && col < plotWidth && row >= 0 && row < plotHeight) {
1689
+ const char = point.character ?? defaultChar;
1690
+ grid[row][col] = point.style ? point.style(char) : char;
1691
+ }
1692
+ }
1693
+ if (showAxes) {
1694
+ const yAxisWidth = Math.max(formatNum(yMax).length, formatNum(yMin).length, formatNum((yMin + yMax) / 2).length);
1695
+ const midRow = Math.floor((plotHeight - 1) / 2);
1696
+ for (let r = 0; r < plotHeight; r++) {
1697
+ let yLabel = "";
1698
+ if (r === 0) yLabel = formatNum(yMax);
1699
+ else if (r === midRow) yLabel = formatNum((yMin + yMax) / 2);
1700
+ else if (r === plotHeight - 1) yLabel = formatNum(yMin);
1701
+ const coloredLabel = yLabel ? applyConfigColor(yLabel) : "";
1702
+ this.string += padLeft(coloredLabel, yAxisWidth) + "\u2502" + grid[r].join("") + "\n";
1703
+ }
1704
+ this.string += " ".repeat(yAxisWidth) + "\u2514" + "\u2500".repeat(plotWidth) + "\n";
1705
+ const xMinRaw = formatNum(xMin);
1706
+ const xMidRaw = formatNum((xMin + xMax) / 2);
1707
+ const xMaxRaw = formatNum(xMax);
1708
+ const midCol = Math.floor(plotWidth / 2) - Math.floor(xMidRaw.length / 2);
1709
+ const gap1 = Math.max(0, midCol - xMinRaw.length);
1710
+ const gap2 = Math.max(0, plotWidth - xMinRaw.length - gap1 - xMidRaw.length - xMaxRaw.length);
1711
+ this.string += " ".repeat(yAxisWidth + 1) + applyConfigColor(xMinRaw) + " ".repeat(gap1) + applyConfigColor(xMidRaw) + " ".repeat(gap2) + applyConfigColor(xMaxRaw) + "\n";
1712
+ } else {
1713
+ for (const row of grid) {
1714
+ this.string += row.join("") + "\n";
1715
+ }
1716
+ }
1717
+ if (paddingX > 0 || paddingY > 0) this.string = applyPadding(this.string, paddingX, paddingY);
1718
+ }
1719
+ print() {
1720
+ process.stdout.write(this.string);
1721
+ }
1722
+ toString() {
1723
+ return this.string;
1724
+ }
1725
+ };
1726
+ var Line = class {
1727
+ constructor(data, options = {}) {
1728
+ this.string = "";
1729
+ const paddingX = options.paddingX ?? 0;
1730
+ const paddingY = options.paddingY ?? 0;
1731
+ const defaultChar = options.character ?? "\u2022";
1732
+ const showAxes = options.axes ?? true;
1733
+ const fill = options.fill ?? false;
1734
+ let xMin = options.xMin ?? (data.length ? Math.min(...data.map((p) => p.x)) : 0);
1735
+ let xMax = options.xMax ?? (data.length ? Math.max(...data.map((p) => p.x)) : 1);
1736
+ let yMin = options.yMin ?? (data.length ? Math.min(...data.map((p) => p.y)) : 0);
1737
+ let yMax = options.yMax ?? (data.length ? Math.max(...data.map((p) => p.y)) : 1);
1738
+ if (xMin === xMax) {
1739
+ xMin -= 1;
1740
+ xMax += 1;
1741
+ }
1742
+ if (yMin === yMax) {
1743
+ yMin -= 1;
1744
+ yMax += 1;
1745
+ }
1746
+ const totalWidth = options.width ?? process.stdout.columns ?? 80;
1747
+ const totalHeight = options.height ?? 20;
1748
+ const yAxisWidth = showAxes ? Math.max(formatNum(yMax).length, formatNum(yMin).length, formatNum((yMin + yMax) / 2).length) : 0;
1749
+ const plotWidth = showAxes ? Math.max(1, totalWidth - yAxisWidth - 1) : totalWidth;
1750
+ const plotHeight = showAxes ? Math.max(1, totalHeight - 2) : totalHeight;
1751
+ const grid = Array.from({ length: plotHeight }, () => Array(plotWidth).fill(" "));
1752
+ const sorted = [...data].sort((a, b) => a.x - b.x);
1753
+ const xRange = xMax - xMin;
1754
+ const yRange = yMax - yMin;
1755
+ for (let col = 0; col < plotWidth; col++) {
1756
+ const xVal = xMin + col / Math.max(1, plotWidth - 1) * xRange;
1757
+ let yVal = null;
1758
+ let pointStyle;
1759
+ for (let pi = 0; pi < sorted.length; pi++) {
1760
+ const p = sorted[pi];
1761
+ const next = sorted[pi + 1];
1762
+ if (next === void 0) {
1763
+ if (Math.abs(p.x - xVal) <= xRange / plotWidth) {
1764
+ yVal = p.y;
1765
+ pointStyle = p.style;
1766
+ }
1767
+ break;
1768
+ }
1769
+ if (p.x <= xVal && next.x >= xVal) {
1770
+ const t = next.x === p.x ? 0 : (xVal - p.x) / (next.x - p.x);
1771
+ yVal = p.y + t * (next.y - p.y);
1772
+ if (t < 0.5 && p.style) pointStyle = p.style;
1773
+ else if (t >= 0.5 && next.style) pointStyle = next.style;
1774
+ break;
1775
+ }
1776
+ }
1777
+ if (yVal !== null) {
1778
+ const row = Math.round((1 - (yVal - yMin) / yRange) * (plotHeight - 1));
1779
+ const clampedRow = Math.max(0, Math.min(plotHeight - 1, row));
1780
+ const char = defaultChar;
1781
+ grid[clampedRow][col] = pointStyle ? pointStyle(char) : char;
1782
+ if (fill) {
1783
+ for (let r = clampedRow + 1; r < plotHeight; r++) {
1784
+ if (grid[r][col] === " ") grid[r][col] = "\u2591";
1785
+ }
1786
+ }
1787
+ }
1788
+ }
1789
+ if (showAxes) {
1790
+ const midRow = Math.floor((plotHeight - 1) / 2);
1791
+ for (let r = 0; r < plotHeight; r++) {
1792
+ let yLabel = "";
1793
+ if (r === 0) yLabel = formatNum(yMax);
1794
+ else if (r === midRow) yLabel = formatNum((yMin + yMax) / 2);
1795
+ else if (r === plotHeight - 1) yLabel = formatNum(yMin);
1796
+ const coloredLabel = yLabel ? applyConfigColor(yLabel) : "";
1797
+ this.string += padLeft(coloredLabel, yAxisWidth) + "\u2502" + grid[r].join("") + "\n";
1798
+ }
1799
+ this.string += " ".repeat(yAxisWidth) + "\u2514" + "\u2500".repeat(plotWidth) + "\n";
1800
+ const xMinRaw = formatNum(xMin);
1801
+ const xMidRaw = formatNum((xMin + xMax) / 2);
1802
+ const xMaxRaw = formatNum(xMax);
1803
+ const midCol = Math.floor(plotWidth / 2) - Math.floor(xMidRaw.length / 2);
1804
+ const gap1 = Math.max(0, midCol - xMinRaw.length);
1805
+ const gap2 = Math.max(0, plotWidth - xMinRaw.length - gap1 - xMidRaw.length - xMaxRaw.length);
1806
+ this.string += " ".repeat(yAxisWidth + 1) + applyConfigColor(xMinRaw) + " ".repeat(gap1) + applyConfigColor(xMidRaw) + " ".repeat(gap2) + applyConfigColor(xMaxRaw) + "\n";
1807
+ } else {
1808
+ for (const row of grid) {
1809
+ this.string += row.join("") + "\n";
1810
+ }
1811
+ }
1812
+ if (paddingX > 0 || paddingY > 0) this.string = applyPadding(this.string, paddingX, paddingY);
1813
+ }
1814
+ print() {
1815
+ process.stdout.write(this.string);
1816
+ }
1817
+ toString() {
1818
+ return this.string;
1819
+ }
1820
+ };
1821
+ function Sparkline(data, options = {}) {
1822
+ const lo = options.min ?? Math.min(...data);
1823
+ const hi = options.max ?? Math.max(...data);
1824
+ const range = hi === lo ? 1 : hi - lo;
1825
+ const chars = data.map((v) => {
1826
+ const idx = Math.min(8, Math.round(Math.max(0, Math.min(1, (v - lo) / range)) * 8));
1827
+ return BLOCKS[idx] ?? "\u2588";
1828
+ }).join("");
1829
+ return options.style ? options.style(chars) : chars;
1830
+ }
1831
+
1832
+ // src/models/Column.ts
1833
+ var Column = class {
1834
+ constructor(data) {
1835
+ if (typeof data === "string") data = { key: data };
1836
+ this.key = data.key ?? "";
1837
+ this.title = data.title ?? this.key;
1838
+ this.padding = data.padding ?? 0;
1839
+ this.align = data.align;
1840
+ this.hidden = data.hidden;
1841
+ if (stringLength(this.title) > this.padding) this.padding = stringLength(this.title);
1842
+ if (data.minimumPadding && data.minimumPadding > this.padding) this.padding = data.minimumPadding;
1843
+ this.value = data.value ?? ((v) => v == null ? "" : String(v));
1844
+ }
1845
+ };
1846
+
1847
+ // src/models/Markup.ts
1848
+ var TAB = " ";
1849
+ function markup(data, options = {}) {
1850
+ const translations = options.translations ?? {};
1851
+ const styles = {
1852
+ date: options.styles?.date ?? ((v) => colorText(MAGENTA, v)),
1853
+ null: options.styles?.null ?? ((v) => colorText(BOLD, v)),
1854
+ undefined: options.styles?.undefined ?? ((v) => colorText(FAINT, v)),
1855
+ number: options.styles?.number ?? ((v) => colorText(YELLOW, v)),
1856
+ bigint: options.styles?.bigint ?? ((v) => colorText(YELLOW, v)),
1857
+ boolean: options.styles?.boolean ?? ((v) => colorText(CYAN, v)),
1858
+ string: options.styles?.string ?? ((v) => colorText(GREEN, v)),
1859
+ symbol: options.styles?.symbol ?? ((v) => colorText(BLUE, v))
1860
+ };
1861
+ let result = "";
1862
+ function formatPrimitive(value) {
1863
+ if (value instanceof Date) return styles.date(String(value));
1864
+ if (value === null) return styles.null("null");
1865
+ if (value === void 0) return styles.undefined("undefined");
1866
+ if (typeof value === "number") return styles.number(String(value));
1867
+ if (typeof value === "bigint") return styles.bigint(String(value));
1868
+ if (typeof value === "boolean") return styles.boolean(String(value));
1869
+ if (typeof value === "string") return styles.string(value);
1870
+ if (typeof value === "symbol") return styles.symbol(String(value));
1871
+ return String(value);
1872
+ }
1873
+ function isPrimitive(value) {
1874
+ return !Array.isArray(value) && (value === null || value === void 0 || typeof value !== "object" || value instanceof Date);
1875
+ }
1876
+ function isPlainObject(value) {
1877
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
1878
+ }
1879
+ function formatObjectContents(obj, pad) {
1880
+ const keys = Object.keys(obj);
1881
+ for (const [i, key] of keys.entries()) {
1882
+ result += `${pad}${TAB}${key}: `;
1883
+ let val = obj[key];
1884
+ if (translations[key]) val = translations[key](val);
1885
+ if (typeof val === "object" && val !== null) {
1886
+ format(val, `${pad}${TAB}`);
1887
+ } else {
1888
+ format(val, "");
1889
+ }
1890
+ result += i === keys.length - 1 ? "\n" : ",\n";
1891
+ }
1892
+ }
1893
+ function format(value, pad) {
1894
+ if (Array.isArray(value)) {
1895
+ if (value.length === 0) {
1896
+ result += result.endsWith(": ") ? "[]" : `${pad}[]`;
1897
+ } else if (value.every(isPrimitive)) {
1898
+ const inline = `[${value.map(formatPrimitive).join(", ")}]`;
1899
+ result += result.endsWith(": ") ? inline : `${pad}${inline}`;
1900
+ } else if (value.every(isPlainObject)) {
1901
+ const prefix = result.endsWith(": ") ? "" : pad;
1902
+ for (const [i, item] of value.entries()) {
1903
+ result += i === 0 ? `${prefix}[{
1904
+ ` : `${pad}},{
1905
+ `;
1906
+ formatObjectContents(item, pad);
1907
+ }
1908
+ result += `${pad}}]`;
1909
+ } else {
1910
+ result += result.endsWith(": ") ? "[" : `${pad}[`;
1911
+ result += "\n";
1912
+ for (const [i, item] of value.entries()) {
1913
+ format(item, `${pad}${TAB}`);
1914
+ result += i === value.length - 1 ? "\n" : ",\n";
1915
+ }
1916
+ result += `${pad}]`;
1917
+ }
1918
+ } else if (value instanceof Date) {
1919
+ result += styles.date(String(value));
1920
+ } else if (value === null) {
1921
+ result += styles.null("null");
1922
+ } else if (value === void 0) {
1923
+ result += styles.undefined("undefined");
1924
+ } else if (isPlainObject(value)) {
1925
+ result += result.endsWith(": ") ? "{" : `${pad}{`;
1926
+ const keys = Object.keys(value);
1927
+ if (keys.length === 0) {
1928
+ result += "}";
1929
+ } else {
1930
+ result += "\n";
1931
+ formatObjectContents(value, pad);
1932
+ result += `${pad}}`;
1933
+ }
1934
+ } else if (typeof value === "number") {
1935
+ result += styles.number(`${pad}${value}`);
1936
+ } else if (typeof value === "bigint") {
1937
+ result += styles.bigint(`${pad}${value}`);
1938
+ } else if (typeof value === "boolean") {
1939
+ result += styles.boolean(String(value));
1940
+ } else if (typeof value === "string") {
1941
+ result += styles.string(`${pad}${value}`);
1942
+ } else if (typeof value === "symbol") {
1943
+ result += styles.symbol(String(value));
1944
+ }
1945
+ }
1946
+ format(data, "");
1947
+ return result;
1948
+ }
1949
+
1950
+ // src/models/Log.ts
1951
+ var Log = class {
1952
+ constructor(options = {}) {
1953
+ this._successColor = options.successColor ?? GREEN;
1954
+ this._failColor = options.failColor ?? RED;
1955
+ this._warnColor = options.warnColor ?? YELLOW;
1956
+ this._infoColor = options.infoColor ?? BLUE;
1957
+ this._glyphs = options.glyphs ?? true;
1958
+ }
1959
+ write(glyph, color, message) {
1960
+ if (process.stdout.isTTY) {
1961
+ process.stdout.write(`${colorText(color, glyph)}${message ? ` ${message}` : ""}
1962
+ `);
1963
+ } else {
1964
+ process.stdout.write(`${this._glyphs ? `${glyph}${message ? ` ${message}` : ""}` : message ?? ""}
1965
+ `);
1966
+ }
1967
+ }
1968
+ succeed(message) {
1969
+ this.write("\u2714", this._successColor, message);
1970
+ }
1971
+ fail(message) {
1972
+ this.write("\u2716", this._failColor, message);
1973
+ }
1974
+ warn(message) {
1975
+ this.write("\u26A0", this._warnColor, message);
1976
+ }
1977
+ info(message) {
1978
+ this.write("\u2139", this._infoColor, message);
1979
+ }
1980
+ data(value, options) {
1981
+ process.stdout.write(`${markup(value, options)}
1982
+ `);
1983
+ }
1984
+ };
1985
+ var log = new Log();
1986
+
1987
+ // src/models/MultiBar.ts
1988
+ var CURSOR_UP2 = (n) => `\x1B[${n}A`;
1989
+ var MultiBar = class {
1990
+ constructor(options = {}) {
1991
+ this._bars = [];
1992
+ this._running = false;
1993
+ this._linesWritten = 0;
1994
+ this._cleanupDeregister = null;
1995
+ this._interval = options.interval ?? 35;
1996
+ }
1997
+ // Add a bar to the group. Must be called before start().
1998
+ // Returns the Bar instance — use .message(), .progress, .tick(), .succeed(), etc.
1999
+ add(options = {}) {
2000
+ const bar = new Bar(options);
2001
+ bar._isManaged = true;
2002
+ this._bars.push(bar);
2003
+ return bar;
2004
+ }
2005
+ start() {
2006
+ if (!process.stdout.isTTY) return;
2007
+ this._running = true;
2008
+ this._linesWritten = 0;
2009
+ process.stdout.write(HIDE_CURSOR);
2010
+ this._cleanupDeregister = registerCleanup(() => {
2011
+ this._running = false;
2012
+ if (this._linesWritten > 0) process.stdout.write(CURSOR_UP2(this._linesWritten));
2013
+ process.stdout.write("\x1B[0J");
2014
+ process.stdout.write(SHOW_CURSOR);
2015
+ });
2016
+ this.render();
2017
+ }
2018
+ stop() {
2019
+ this._running = false;
2020
+ this._cleanupDeregister?.();
2021
+ this._cleanupDeregister = null;
2022
+ if (process.stdout.isTTY) process.stdout.write(SHOW_CURSOR);
2023
+ }
2024
+ render() {
2025
+ const width = Math.max(20, (process.stdout.columns ?? 80) - 1);
2026
+ if (this._linesWritten > 0) {
2027
+ process.stdout.write(CURSOR_UP2(this._linesWritten));
2028
+ }
2029
+ for (const bar of this._bars) {
2030
+ if (bar._managedFinalLine !== null) {
2031
+ process.stdout.write(`\r${bar._managedFinalLine}\x1B[K
2032
+ `);
2033
+ } else {
2034
+ process.stdout.write(`\r${bar.renderLine(width)}\x1B[K
2035
+ `);
2036
+ bar.advanceFrame();
2037
+ }
2038
+ }
2039
+ this._linesWritten = this._bars.length;
2040
+ if (!this._running) return;
2041
+ if (this._bars.every((b) => b._managedFinalLine !== null)) {
2042
+ this._running = false;
2043
+ this._cleanupDeregister?.();
2044
+ this._cleanupDeregister = null;
2045
+ process.stdout.write(SHOW_CURSOR);
2046
+ return;
2047
+ }
2048
+ setTimeout(() => {
2049
+ if (this._running) this.render();
2050
+ }, this._interval);
2051
+ }
2052
+ };
2053
+
2054
+ // src/models/MultiSelect.ts
2055
+ var CLEAR_LINE2 = "\x1B[2K";
2056
+ var CURSOR_UP3 = (n) => `\x1B[${n}A`;
2057
+ var DIM2 = "\x1B[2m";
2058
+ var MultiSelect = class {
2059
+ constructor(options = {}) {
2060
+ this._colorOffset = 0;
2061
+ this._shimmerPhase = 0;
2062
+ this.promptColor = options.promptColor ?? resolveColor(config.color);
2063
+ this.promptGlyph = options.promptGlyph ?? (config.glyphs ? "\u25C6" : ">");
2064
+ this.descriptionColor = options.descriptionColor ?? BLUE;
2065
+ this.errorColor = options.errorColor ?? RED;
2066
+ this.checkedPrefix = options.checkedPrefix ?? (config.glyphs ? "\u25C9" : "[x]");
2067
+ this.uncheckedPrefix = options.uncheckedPrefix ?? (config.glyphs ? "\u25CB" : "[ ]");
2068
+ this.colorCycle = options.colorCycle ?? 1;
2069
+ this.shimmer = options.shimmer ?? 0;
2070
+ this.interval = options.interval ?? 80;
2071
+ this.min = options.min ?? 0;
2072
+ this.max = options.max ?? null;
2073
+ this.allowSkip = options.allowSkip ?? false;
2074
+ this.searchEnabled = options.search ?? false;
2075
+ this.maxHeight = options.maxHeight;
2076
+ const colors = options.colors ?? config.pulseColors;
2077
+ this._parsedColors = colors.length >= 2 ? colors.map(parseHex) : [];
2078
+ }
2079
+ pulseColor() {
2080
+ if (this._parsedColors.length < 2) return "";
2081
+ const t = (this._colorOffset % 1 + 1) % 1;
2082
+ const base2 = interpolateColor(this._parsedColors, t);
2083
+ const color = applyShimmer(base2, 0.5, this._shimmerPhase, this.shimmer);
2084
+ return formatColor(38, color);
2085
+ }
2086
+ async ask(prompt, items) {
2087
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("MultiSelect requires an interactive terminal");
2088
+ let cursor = 0;
2089
+ let viewportOffset = 0;
2090
+ let searchQuery = "";
2091
+ const checked = /* @__PURE__ */ new Set();
2092
+ let error = null;
2093
+ let lastDrawnLines = 0;
2094
+ process.stdout.write(HIDE_CURSOR);
2095
+ const glyph = this.promptGlyph ? `${colorText(this.promptColor, this.promptGlyph)} ` : "";
2096
+ const indent = " ".repeat(this.promptGlyph ? stringLength(this.promptGlyph) + 1 : 0);
2097
+ process.stdout.write(`${glyph}${prompt}
2098
+ `);
2099
+ const getFiltered = () => {
2100
+ if (!this.searchEnabled || searchQuery === "") return items.map((item, i) => ({ item, originalIndex: i }));
2101
+ const q = searchQuery.toLowerCase();
2102
+ return items.map((item, i) => ({ item, originalIndex: i })).filter(({ item }) => item.label.toLowerCase().includes(q) || (item.description?.toLowerCase().includes(q) ?? false));
2103
+ };
2104
+ const renderList = (redraw) => {
2105
+ const filtered = getFiltered();
2106
+ if (cursor >= filtered.length) cursor = Math.max(0, filtered.length - 1);
2107
+ if (this.maxHeight) {
2108
+ if (cursor < viewportOffset) viewportOffset = cursor;
2109
+ else if (cursor >= viewportOffset + this.maxHeight) viewportOffset = cursor - this.maxHeight + 1;
2110
+ viewportOffset = Math.max(0, Math.min(viewportOffset, Math.max(0, filtered.length - this.maxHeight)));
2111
+ }
2112
+ const visibleStart = this.maxHeight ? viewportOffset : 0;
2113
+ const visibleEnd = this.maxHeight ? Math.min(filtered.length, viewportOffset + this.maxHeight) : filtered.length;
2114
+ if (redraw) {
2115
+ if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP3(lastDrawnLines));
2116
+ process.stdout.write("\r\x1B[0J");
2117
+ }
2118
+ lastDrawnLines = 0;
2119
+ if (this.searchEnabled) {
2120
+ process.stdout.write(`\r${indent}${colorText(this.promptColor, "/")} ${searchQuery}|
2121
+ `);
2122
+ lastDrawnLines++;
2123
+ }
2124
+ const pulse = this.pulseColor();
2125
+ for (let vi = visibleStart; vi < visibleEnd; vi++) {
2126
+ const { item, originalIndex } = filtered[vi];
2127
+ const isCursor = vi === cursor;
2128
+ const isChecked = checked.has(originalIndex);
2129
+ const desc = item.description ? ` ${colorText(this.descriptionColor, `\u2014 ${item.description}`)}` : "";
2130
+ const checkMark = isChecked ? pulse ? `${pulse}${this.checkedPrefix}${RESET}` : colorText(this.promptColor, this.checkedPrefix) : this.uncheckedPrefix;
2131
+ const label = isCursor ? pulse ? `${pulse}${item.label}${RESET}` : colorText(this.promptColor, item.label) : item.label;
2132
+ process.stdout.write(`\r${indent}${checkMark} ${label}${desc}
2133
+ `);
2134
+ lastDrawnLines++;
2135
+ }
2136
+ const hintContent = `\u2191\u2193 move space/tab toggle \u2190\u2192 deselect/select${this.searchEnabled ? " type to filter" : " a all"} enter confirm`;
2137
+ const maxHintCols = Math.max(10, (process.stdout.columns ?? 80) - stringLength(indent) - 1);
2138
+ const hint = stringLength(hintContent) > maxHintCols ? hintContent.slice(0, maxHintCols) : hintContent;
2139
+ process.stdout.write(`\r${indent}${DIM2}${hint}${RESET}
2140
+ `);
2141
+ lastDrawnLines++;
2142
+ if (error) {
2143
+ process.stdout.write(`\r${indent}${colorText(this.errorColor, `\u2717 ${error}`)}
2144
+ `);
2145
+ lastDrawnLines++;
2146
+ }
2147
+ };
2148
+ renderList(false);
2149
+ return new Promise((resolve) => {
2150
+ let timer = null;
2151
+ const deregisterCleanup = registerCleanup(() => {
2152
+ if (timer) {
2153
+ clearInterval(timer);
2154
+ timer = null;
2155
+ }
2156
+ process.stdin.setRawMode(false);
2157
+ process.stdin.pause();
2158
+ process.stdin.removeListener("data", onKey);
2159
+ process.stdout.write(SHOW_CURSOR);
2160
+ });
2161
+ const cleanup = (result) => {
2162
+ deregisterCleanup();
2163
+ if (timer) {
2164
+ clearInterval(timer);
2165
+ timer = null;
2166
+ }
2167
+ if (lastDrawnLines > 0) process.stdout.write(CURSOR_UP3(lastDrawnLines));
2168
+ process.stdout.write("\x1B[0J");
2169
+ const bulletWidth = stringLength(this.checkedPrefix) + 1;
2170
+ for (let i = 0; i < items.length; i++) {
2171
+ const item = items[i];
2172
+ const bullet = checked.has(i) ? `${colorText(this.promptColor, this.checkedPrefix)} ` : " ".repeat(bulletWidth);
2173
+ process.stdout.write(`\r${indent}${bullet}${item.label}
2174
+ `);
2175
+ }
2176
+ process.stdout.write(`\r${CLEAR_LINE2}`);
2177
+ process.stdin.setRawMode(false);
2178
+ process.stdin.pause();
2179
+ process.stdin.removeListener("data", onKey);
2180
+ process.stdout.write(SHOW_CURSOR);
2181
+ resolve(result);
2182
+ };
2183
+ if (this._parsedColors.length >= 2) {
2184
+ timer = setInterval(() => {
2185
+ this._colorOffset = (this._colorOffset + this.colorCycle * this.interval / 1e3) % 1;
2186
+ if (this.shimmer > 0) this._shimmerPhase = (this._shimmerPhase + SHIMMER_SPEED * this.interval / 1e3) % 1;
2187
+ renderList(true);
2188
+ }, this.interval);
2189
+ }
2190
+ const onKey = (key) => {
2191
+ const str = key.toString();
2192
+ const filtered = getFiltered();
2193
+ const safeLen = Math.max(1, filtered.length);
2194
+ if (str === "\x1B[A" || str === "\x1B[Z") {
2195
+ error = null;
2196
+ cursor = (cursor - 1 + safeLen) % safeLen;
2197
+ renderList(true);
2198
+ } else if (str === "\x1B[B") {
2199
+ error = null;
2200
+ cursor = (cursor + 1) % safeLen;
2201
+ renderList(true);
2202
+ } else if (str === " " || str === " ") {
2203
+ const entry = filtered[cursor];
2204
+ if (!entry) return;
2205
+ const oi = entry.originalIndex;
2206
+ if (checked.has(oi)) {
2207
+ error = null;
2208
+ checked.delete(oi);
2209
+ } else if (this.max === null || checked.size < this.max) {
2210
+ error = null;
2211
+ checked.add(oi);
2212
+ } else {
2213
+ error = `Maximum ${this.max} item${this.max !== 1 ? "s" : ""} allowed`;
2214
+ }
2215
+ renderList(true);
2216
+ } else if (str === "\x1B[C") {
2217
+ const entry = filtered[cursor];
2218
+ if (!entry) return;
2219
+ const oi = entry.originalIndex;
2220
+ if (!checked.has(oi)) {
2221
+ if (this.max === null || checked.size < this.max) {
2222
+ error = null;
2223
+ checked.add(oi);
2224
+ } else {
2225
+ error = `Maximum ${this.max} item${this.max !== 1 ? "s" : ""} allowed`;
2226
+ }
2227
+ renderList(true);
2228
+ }
2229
+ } else if (str === "\x1B[D") {
2230
+ const entry = filtered[cursor];
2231
+ if (!entry) return;
2232
+ const oi = entry.originalIndex;
2233
+ if (checked.has(oi)) {
2234
+ error = null;
2235
+ checked.delete(oi);
2236
+ renderList(true);
2237
+ }
2238
+ } else if (str === "a" && !this.searchEnabled) {
2239
+ if (checked.size === items.length) {
2240
+ checked.clear();
2241
+ error = null;
2242
+ } else if (this.max !== null && items.length > this.max) {
2243
+ error = `Maximum ${this.max} item${this.max !== 1 ? "s" : ""} allowed`;
2244
+ } else {
2245
+ for (let i = 0; i < items.length; i++) checked.add(i);
2246
+ error = null;
2247
+ }
2248
+ renderList(true);
2249
+ } else if (str === "\r" || str === "\n") {
2250
+ const entry = filtered[cursor];
2251
+ if (checked.size === 0 && entry) checked.add(entry.originalIndex);
2252
+ if (checked.size < this.min) {
2253
+ error = `Select at least ${this.min} item${this.min !== 1 ? "s" : ""}`;
2254
+ renderList(true);
2255
+ return;
2256
+ }
2257
+ cleanup(items.filter((_, i) => checked.has(i)));
2258
+ } else if (str === "\x1B") {
2259
+ if (this.allowSkip) cleanup(null);
2260
+ } else if (str === "") {
2261
+ deregisterCleanup();
2262
+ if (timer) {
2263
+ clearInterval(timer);
2264
+ timer = null;
2265
+ }
2266
+ process.stdin.setRawMode(false);
2267
+ process.stdin.pause();
2268
+ process.stdin.removeListener("data", onKey);
2269
+ process.stdout.write(SHOW_CURSOR);
2270
+ process.exit(130);
2271
+ } else if (this.searchEnabled) {
2272
+ if (str === "\x7F" || str === "\b") {
2273
+ searchQuery = searchQuery.slice(0, -1);
2274
+ cursor = 0;
2275
+ viewportOffset = 0;
2276
+ error = null;
2277
+ renderList(true);
2278
+ } else if (str.length === 1 && str >= " ") {
2279
+ searchQuery += str;
2280
+ cursor = 0;
2281
+ viewportOffset = 0;
2282
+ error = null;
2283
+ renderList(true);
2284
+ }
2285
+ } else {
2286
+ const n = parseInt(str);
2287
+ if (!isNaN(n) && n >= 1 && n <= Math.min(filtered.length, 9)) {
2288
+ error = null;
2289
+ cursor = viewportOffset + n - 1;
2290
+ renderList(true);
2291
+ }
2292
+ }
2293
+ };
2294
+ process.stdin.setRawMode(true);
2295
+ process.stdin.resume();
2296
+ process.stdin.on("data", onKey);
2297
+ });
2298
+ }
2299
+ };
2300
+ async function multiSelect(prompt, items, options) {
2301
+ return new MultiSelect(options).ask(prompt, items);
2302
+ }
2303
+
2304
+ // src/utils/truncate.ts
2305
+ var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
2306
+ var RESET2 = "\x1B[0m";
2307
+ function truncate(str, maxLength, suffix = "\u2026") {
2308
+ if (stringLength(str) <= maxLength) return str;
2309
+ const hasAnsi = ANSI_RE.test(str);
2310
+ ANSI_RE.lastIndex = 0;
2311
+ const suffixLen = stringLength(suffix);
2312
+ const target = Math.max(0, maxLength - suffixLen);
2313
+ let visible = 0;
2314
+ let i = 0;
2315
+ while (i < str.length && visible < target) {
2316
+ if (str[i] === "\x1B") {
2317
+ const m = str.slice(i).match(ANSI_RE);
2318
+ if (m) {
2319
+ i += m[0].length;
2320
+ continue;
2321
+ }
2322
+ }
2323
+ visible++;
2324
+ i++;
2325
+ }
2326
+ return str.slice(0, i) + (hasAnsi ? RESET2 : "") + suffix;
2327
+ }
2328
+
2329
+ // src/utils/wrap.ts
2330
+ function wrap(str, width) {
2331
+ const words = str.split(" ");
2332
+ const lines = [];
2333
+ let current = "";
2334
+ let currentLen = 0;
2335
+ for (const word of words) {
2336
+ const wordLen = stringLength(word);
2337
+ if (currentLen === 0) {
2338
+ current = word;
2339
+ currentLen = wordLen;
2340
+ } else if (currentLen + 1 + wordLen <= width) {
2341
+ current += " " + word;
2342
+ currentLen += 1 + wordLen;
2343
+ } else {
2344
+ lines.push(current);
2345
+ current = word;
2346
+ currentLen = wordLen;
2347
+ }
2348
+ }
2349
+ if (current) lines.push(current);
2350
+ return lines.join("\n");
2351
+ }
2352
+
2353
+ // src/models/Scrollbox.ts
2354
+ var CURSOR_UP4 = (n) => `\x1B[${n}A`;
2355
+ var DIM3 = "\x1B[2m";
2356
+ var Scrollbox = class {
2357
+ constructor(options = {}) {
2358
+ this.height = options.height ?? 10;
2359
+ this.title = options.title;
2360
+ this.lineNumbers = options.lineNumbers ?? false;
2361
+ this.showScrollbar = options.scrollbar ?? true;
2362
+ this.borderColor = options.borderColor ?? DIM3;
2363
+ this.wrapLines = options.wrapLines ?? false;
2364
+ }
2365
+ async show(lines) {
2366
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Scrollbox requires an interactive terminal");
2367
+ const termWidth = process.stdout.columns ?? 80;
2368
+ const lineNumWidth = this.lineNumbers ? String(lines.length).length + 2 : 0;
2369
+ const gutterWidth = this.showScrollbar ? 1 : 0;
2370
+ const contentWidth = Math.max(10, termWidth - lineNumWidth - gutterWidth);
2371
+ const displayLines = this.wrapLines ? lines.flatMap((line) => wrap(line, contentWidth).split("\n")) : lines;
2372
+ const total = displayLines.length;
2373
+ let offset = 0;
2374
+ let lastDrawnLines = 0;
2375
+ process.stdout.write(HIDE_CURSOR);
2376
+ const render = (redraw) => {
2377
+ offset = Math.max(0, Math.min(offset, Math.max(0, total - this.height)));
2378
+ if (redraw && lastDrawnLines > 0) {
2379
+ process.stdout.write(CURSOR_UP4(lastDrawnLines));
2380
+ process.stdout.write("\x1B[0J");
2381
+ }
2382
+ lastDrawnLines = 0;
2383
+ if (this.title) {
2384
+ process.stdout.write(`\r${colorText(this.borderColor, this.title)}
2385
+ `);
2386
+ lastDrawnLines++;
2387
+ }
2388
+ let thumbStart = 0;
2389
+ let thumbEnd = this.height;
2390
+ if (this.showScrollbar && total > this.height) {
2391
+ const thumbSize = Math.max(1, Math.floor(this.height / total * this.height));
2392
+ thumbStart = Math.floor(offset / Math.max(1, total - this.height) * (this.height - thumbSize));
2393
+ thumbEnd = thumbStart + thumbSize;
2394
+ }
2395
+ const visibleEnd = Math.min(total, offset + this.height);
2396
+ for (let i = offset; i < visibleEnd; i++) {
2397
+ const relIdx = i - offset;
2398
+ const numStr = this.lineNumbers ? `${colorText(this.borderColor, String(i + 1).padStart(lineNumWidth - 1))} ` : "";
2399
+ const content = this.wrapLines ? displayLines[i] : truncate(displayLines[i], contentWidth);
2400
+ const paddedContent = padRight(content, contentWidth);
2401
+ const gutter = this.showScrollbar ? total > this.height && relIdx >= thumbStart && relIdx < thumbEnd ? "\u2588" : colorText(this.borderColor, "\u2595") : "";
2402
+ process.stdout.write(`\r${numStr}${paddedContent}${gutter}
2403
+ `);
2404
+ lastDrawnLines++;
2405
+ }
2406
+ const pct = total <= this.height ? "100%" : `${Math.round(offset / (total - this.height) * 100)}%`;
2407
+ process.stdout.write(`\r${DIM3}\u2191\u2193/jk line space/b page g/G top/end q close ${pct}${RESET}
2408
+ `);
2409
+ lastDrawnLines++;
2410
+ };
2411
+ render(false);
2412
+ return new Promise((resolve) => {
2413
+ const deregisterCleanup = registerCleanup(() => {
2414
+ process.stdin.setRawMode(false);
2415
+ process.stdin.pause();
2416
+ process.stdin.removeListener("data", onKey);
2417
+ process.stdout.write(SHOW_CURSOR);
2418
+ });
2419
+ const cleanup = () => {
2420
+ deregisterCleanup();
2421
+ process.stdin.setRawMode(false);
2422
+ process.stdin.pause();
2423
+ process.stdin.removeListener("data", onKey);
2424
+ process.stdout.write(SHOW_CURSOR);
2425
+ resolve();
2426
+ };
2427
+ const onKey = (key) => {
2428
+ const str = key.toString();
2429
+ if (str === "\x1B[A" || str === "k") {
2430
+ offset--;
2431
+ render(true);
2432
+ } else if (str === "\x1B[B" || str === "j") {
2433
+ offset++;
2434
+ render(true);
2435
+ } else if (str === " " || str === "\x1B[6~") {
2436
+ offset += this.height;
2437
+ render(true);
2438
+ } else if (str === "b" || str === "\x1B[5~") {
2439
+ offset -= this.height;
2440
+ render(true);
2441
+ } else if (str === "g") {
2442
+ offset = 0;
2443
+ render(true);
2444
+ } else if (str === "G") {
2445
+ offset = total;
2446
+ render(true);
2447
+ } else if (str === "q" || str === "\x1B" || str === "\r" || str === "\n") {
2448
+ cleanup();
2449
+ } else if (str === "") {
2450
+ cleanup();
2451
+ process.exit(130);
2452
+ }
2453
+ };
2454
+ process.stdin.setRawMode(true);
2455
+ process.stdin.resume();
2456
+ process.stdin.on("data", onKey);
2457
+ });
2458
+ }
2459
+ };
2460
+ async function scrollbox(lines, options) {
2461
+ return new Scrollbox(options).show(lines);
2462
+ }
2463
+
2464
+ // src/models/Spinner.ts
2465
+ var _Spinner = class _Spinner {
2466
+ constructor(options = {}) {
2467
+ this._colors = [];
2468
+ this._parsedColors = [];
2469
+ this._bgColors = [];
2470
+ this._parsedBgColors = [];
2471
+ this._colorOffset = 0;
2472
+ this._shimmerPhase = 0;
2473
+ this._lastLineLength = 0;
2474
+ this._cleanupDeregister = null;
2475
+ this.running = false;
2476
+ this.frameIndex = 0;
2477
+ this.frames = options.frames ?? (config.glyphs ? _Spinner.FRAMES.braille : _Spinner.FRAMES.line);
2478
+ this.prefixString = options.prefix ?? "";
2479
+ this.suffixString = options.suffix ?? "";
2480
+ this.text = options.text ?? "";
2481
+ this.reverse = options.reverse ?? false;
2482
+ this.interval = options.interval ?? 80;
2483
+ this.colorCycle = options.colorCycle ?? 1;
2484
+ this.shimmer = options.shimmer ?? 0;
2485
+ this.onSpin = options.onSpin;
2486
+ this.colors = options.colors ?? ["#c026d3", "#e879f9"];
2487
+ this.bgColors = options.bgColors ?? [];
2488
+ this._successColor = options.successColor ?? GREEN;
2489
+ this._failColor = options.failColor ?? RED;
2490
+ this._warnColor = options.warnColor ?? YELLOW;
2491
+ this._infoColor = options.infoColor ?? BLUE;
2492
+ this._glyphs = options.glyphs ?? config.glyphs;
2493
+ }
2494
+ get colors() {
2495
+ return this._colors;
2496
+ }
2497
+ set colors(value) {
2498
+ this._colors = value;
2499
+ this._parsedColors = value.length > 1 ? value.map(parseHex) : [];
2500
+ }
2501
+ get bgColors() {
2502
+ return this._bgColors;
2503
+ }
2504
+ set bgColors(value) {
2505
+ this._bgColors = value;
2506
+ this._parsedBgColors = value.length > 1 ? value.map(parseHex) : [];
2507
+ }
2508
+ start() {
2509
+ if (!process.stdout.isTTY) return;
2510
+ this.running = true;
2511
+ process.stdout.write(HIDE_CURSOR);
2512
+ this._cleanupDeregister = registerCleanup(() => {
2513
+ this.running = false;
2514
+ process.stdout.clearLine?.(0);
2515
+ process.stdout.write(SHOW_CURSOR);
2516
+ });
2517
+ this.run();
2518
+ }
2519
+ stop(message) {
2520
+ this.running = false;
2521
+ this._cleanupDeregister?.();
2522
+ this._cleanupDeregister = null;
2523
+ if (process.stdout.isTTY) {
2524
+ this.clear();
2525
+ process.stdout.write(SHOW_CURSOR);
2526
+ }
2527
+ if (message) process.stdout.write(`${message}
2528
+ `);
2529
+ return this;
2530
+ }
2531
+ message(string) {
2532
+ this.text = string;
2533
+ return this;
2534
+ }
2535
+ succeed(string) {
2536
+ this.running = false;
2537
+ this._cleanupDeregister?.();
2538
+ this._cleanupDeregister = null;
2539
+ if (process.stdout.isTTY) {
2540
+ this.clear();
2541
+ const glyph = this._glyphs ? colorText(this._successColor, "\u2714") + " " : "";
2542
+ process.stdout.write(`${SHOW_CURSOR}${glyph}${string ?? ""}
2543
+ `);
2544
+ } else {
2545
+ process.stdout.write(`${this._glyphs ? `\u2714 ${string ?? ""}` : string ?? ""}
2546
+ `);
2547
+ }
2548
+ return this;
2549
+ }
2550
+ fail(string) {
2551
+ this.running = false;
2552
+ this._cleanupDeregister?.();
2553
+ this._cleanupDeregister = null;
2554
+ if (process.stdout.isTTY) {
2555
+ this.clear();
2556
+ const glyph = this._glyphs ? colorText(this._failColor, "\u2716") + " " : "";
2557
+ process.stdout.write(`${SHOW_CURSOR}${glyph}${string ?? ""}
2558
+ `);
2559
+ } else {
2560
+ process.stdout.write(`${this._glyphs ? `\u2716 ${string ?? ""}` : string ?? ""}
2561
+ `);
2562
+ }
2563
+ return this;
2564
+ }
2565
+ warn(string) {
2566
+ this.running = false;
2567
+ this._cleanupDeregister?.();
2568
+ this._cleanupDeregister = null;
2569
+ if (process.stdout.isTTY) {
2570
+ this.clear();
2571
+ const glyph = this._glyphs ? colorText(this._warnColor, "\u26A0") + " " : "";
2572
+ process.stdout.write(`${SHOW_CURSOR}${glyph}${string ?? ""}
2573
+ `);
2574
+ } else {
2575
+ process.stdout.write(`${this._glyphs ? `\u26A0 ${string ?? ""}` : string ?? ""}
2576
+ `);
2577
+ }
2578
+ return this;
2579
+ }
2580
+ info(string) {
2581
+ this.running = false;
2582
+ this._cleanupDeregister?.();
2583
+ this._cleanupDeregister = null;
2584
+ if (process.stdout.isTTY) {
2585
+ this.clear();
2586
+ const glyph = this._glyphs ? colorText(this._infoColor, "\u2139") + " " : "";
2587
+ process.stdout.write(`${SHOW_CURSOR}${glyph}${string ?? ""}
2588
+ `);
2589
+ } else {
2590
+ process.stdout.write(`${this._glyphs ? `\u2139 ${string ?? ""}` : string ?? ""}
2591
+ `);
2592
+ }
2593
+ return this;
2594
+ }
2595
+ clear() {
2596
+ process.stdout.clearLine?.(0);
2597
+ this._lastLineLength = 0;
2598
+ }
2599
+ coloredFrame() {
2600
+ const t = this._parsedColors.length >= 2 && this.frames.length > 1 ? this.frameIndex / (this.frames.length - 1) : 0;
2601
+ const et = this.colorCycle > 0 ? ((t + this._colorOffset) % 1 + 1) % 1 : t;
2602
+ const frame = this.frames[this.frameIndex];
2603
+ const fg = this._parsedColors.length >= 2 ? formatColor(38, applyShimmer(interpolateColor(this._parsedColors, et), 0.5, this._shimmerPhase, this.shimmer)) : "";
2604
+ const bg = this._parsedBgColors.length >= 2 ? formatColor(48, applyShimmer(interpolateColor(this._parsedBgColors, et), 0.5, this._shimmerPhase, this.shimmer)) : "";
2605
+ return fg || bg ? fg + bg + frame + RESET : frame;
2606
+ }
2607
+ run() {
2608
+ const frame = this.coloredFrame();
2609
+ const t = this.text;
2610
+ const content = this.reverse ? t ? `${t} ${frame}` : frame : t ? `${frame} ${t}` : frame;
2611
+ const raw = `${this.prefixString}${content}${this.suffixString}`;
2612
+ const displayLen = stringLength(raw);
2613
+ const padding = " ".repeat(Math.max(0, this._lastLineLength - displayLen));
2614
+ this._lastLineLength = displayLen;
2615
+ process.stdout.write(`${raw}${padding}\r`);
2616
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
2617
+ if (this.frameIndex === 0) this.onSpin?.();
2618
+ if (this.colorCycle > 0) {
2619
+ this._colorOffset = (this._colorOffset + this.colorCycle * this.interval / 1e3) % 1;
2620
+ }
2621
+ if (this.shimmer > 0) {
2622
+ this._shimmerPhase = (this._shimmerPhase + SHIMMER_SPEED * this.interval / 1e3) % 1;
2623
+ }
2624
+ setTimeout(() => {
2625
+ if (this.running) this.run();
2626
+ }, this.interval);
2627
+ }
2628
+ };
2629
+ _Spinner.FRAMES = {
2630
+ braille: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
2631
+ dots: ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"],
2632
+ line: ["|", "/", "-", "\\"],
2633
+ arrow: ["\u2190", "\u2196", "\u2191", "\u2197", "\u2192", "\u2198", "\u2193", "\u2199"],
2634
+ bounce: ["\u2801", "\u2802", "\u2804", "\u2802"]
2635
+ };
2636
+ var Spinner = _Spinner;
2637
+
2638
+ // src/models/Table.ts
2639
+ var import_cosmetic3 = __toESM(require("cosmetic"));
2640
+
2641
+ // src/utils/padSides.ts
2642
+ var padSides = (string, padding) => {
2643
+ let alt = false;
2644
+ while (stringLength(string) < padding) {
2645
+ if (alt) {
2646
+ string = ` ${string}`;
2647
+ } else {
2648
+ string += " ";
2649
+ }
2650
+ alt = !alt;
2651
+ }
2652
+ return string;
2653
+ };
2654
+
2655
+ // src/models/Table.ts
2656
+ var alignPad = (align) => {
2657
+ switch (align) {
2658
+ case "center":
2659
+ return padSides;
2660
+ case "right":
2661
+ return padLeft;
2662
+ default:
2663
+ return padRight;
2664
+ }
2665
+ };
2666
+ var Table = class {
2667
+ constructor(rows, options = {}) {
2668
+ this.rows = rows;
2669
+ this.columns = {};
2670
+ this.separator = options.separator ?? "|";
2671
+ this.align = options.align ?? "left";
2672
+ this.margin = options.margin ?? 0;
2673
+ this.string = "";
2674
+ if (options.title) this.title = options.title;
2675
+ if (options.columns) {
2676
+ for (const col of options.columns) {
2677
+ const column = new Column(col);
2678
+ if (!column.hidden) this.columns[column.key] = column;
2679
+ }
2680
+ }
2681
+ for (const row of this.rows) {
2682
+ for (const key of Object.keys(row)) {
2683
+ if (Object.keys(this.columns).length) {
2684
+ const column = this.columns[key];
2685
+ if (!column) continue;
2686
+ const cellLen = stringLength(column.value(row[key]));
2687
+ if (column.padding < cellLen) this.columns[key].padding = cellLen;
2688
+ } else {
2689
+ const keyLen = stringLength(key);
2690
+ const valLen = stringLength(String(row[key] ?? ""));
2691
+ this.columns[key] = new Column({ key, padding: Math.max(keyLen, valLen) });
2692
+ }
2693
+ }
2694
+ }
2695
+ if (options.meta) {
2696
+ this.meta = options.meta;
2697
+ for (const meta of options.meta) {
2698
+ for (const key of Object.keys(meta)) {
2699
+ const column = this.columns[key];
2700
+ if (!column) continue;
2701
+ const cellLen = stringLength(column.value(meta[key]));
2702
+ if (column.padding < cellLen) column.padding = cellLen;
2703
+ }
2704
+ }
2705
+ }
2706
+ if (this.title) this.string += `${alignPad(this.align)(this.title, this.width)}
2707
+
2708
+ `;
2709
+ const keys = Object.keys(this.columns);
2710
+ let header = "";
2711
+ for (const [i, key] of keys.entries()) {
2712
+ const column = this.columns[key];
2713
+ const pad = alignPad(column.align ?? this.align);
2714
+ header += pad(column.title, column.padding + this.margin);
2715
+ if (i < keys.length - 1) header += this.separator;
2716
+ }
2717
+ const styled = typeof config.color === "number" ? import_cosmetic3.default.xterm(config.color) : config.color.startsWith("#") ? import_cosmetic3.default.hex(config.color) : import_cosmetic3.default[config.color];
2718
+ this.string += `${styled.underline.encoder(header)}
2719
+ `;
2720
+ for (const [ri, row] of this.rows.entries()) {
2721
+ for (const [ci, key] of keys.entries()) {
2722
+ const column = this.columns[key];
2723
+ const pad = alignPad(column.align ?? this.align);
2724
+ const raw = row[column.key];
2725
+ this.string += pad(column.value(raw == null ? "" : raw), column.padding + this.margin);
2726
+ if (ci < keys.length - 1) this.string += this.separator;
2727
+ }
2728
+ if (ri < this.rows.length - 1) this.string += "\n";
2729
+ }
2730
+ if (this.meta) {
2731
+ this.string += "\n\n";
2732
+ for (const [mi, meta] of this.meta.entries()) {
2733
+ for (const [ci, key] of keys.entries()) {
2734
+ const column = this.columns[key];
2735
+ const pad = alignPad(column.align ?? this.align);
2736
+ const raw = meta[column.key];
2737
+ this.string += pad(column.value(raw == null ? "" : raw), column.padding + this.margin);
2738
+ if (ci < keys.length - 1) this.string += this.separator;
2739
+ }
2740
+ if (mi < this.meta.length - 1) this.string += "\n";
2741
+ }
2742
+ }
2743
+ }
2744
+ print() {
2745
+ process.stdout.write(this.string + "\n");
2746
+ }
2747
+ get width() {
2748
+ let width = 0;
2749
+ for (const key of Object.keys(this.columns)) {
2750
+ if (!this.columns[key].hidden) width += this.columns[key].padding + this.margin + 1;
2751
+ }
2752
+ return width - 1;
2753
+ }
2754
+ };
2755
+ Table.left = "left";
2756
+ Table.center = "center";
2757
+ Table.right = "right";
2758
+
2759
+ // src/models/TermKit.ts
2760
+ var _TermKit = class _TermKit {
2761
+ static set defaults(obj) {
2762
+ _TermKit.commandDefaults = obj;
2763
+ }
2764
+ static setDefaults(obj) {
2765
+ _TermKit.commandDefaults = obj;
2766
+ }
2767
+ static command(name, variables, info) {
2768
+ const cmd = new Command(Object.assign({ name, variables, info }, _TermKit.commandDefaults));
2769
+ if (!_TermKit.base) _TermKit.base = cmd;
2770
+ return cmd;
2771
+ }
2772
+ static middleware(action) {
2773
+ return action;
2774
+ }
2775
+ static option(short, long, variables, info) {
2776
+ return new Option({ short, long, variables, info });
2777
+ }
2778
+ static parse(arr) {
2779
+ if (!_TermKit.base) throw new Error("No command defined");
2780
+ return _TermKit.base.parse(arr);
2781
+ }
2782
+ };
2783
+ _TermKit.base = null;
2784
+ _TermKit.commandDefaults = {};
2785
+ var TermKit = _TermKit;
2786
+
2787
+ // src/index.ts
2788
+ var import_cosmetic4 = __toESM(require("cosmetic"));
2789
+ var base = null;
2790
+ var commandDefaults = {};
2791
+ var Program = {
2792
+ command: (name, variables, info) => {
2793
+ const cmd = new Command(Object.assign({ name, variables, info }, commandDefaults));
2794
+ if (!base) base = cmd;
2795
+ return cmd;
2796
+ },
2797
+ option: (short, long, variables, info) => new Option({ short, long, variables, info }),
2798
+ middleware: (fn) => fn,
2799
+ parse: async (arr) => {
2800
+ if (!base) throw new Error("No command defined");
2801
+ try {
2802
+ await base.parse(arr);
2803
+ } catch (err) {
2804
+ const msg = err instanceof Error ? err.message : String(err);
2805
+ if (process.stderr.isTTY && config.glyphs) {
2806
+ process.stderr.write(`\x1B[31m\u2716\x1B[0m ${msg}
2807
+ `);
2808
+ } else {
2809
+ process.stderr.write(`Error: ${msg}
2810
+ `);
2811
+ }
2812
+ process.exit(1);
2813
+ }
2814
+ },
2815
+ setDefaults: (data) => {
2816
+ commandDefaults = data;
404
2817
  }
405
- };
406
- _TermKit.base = null;
407
- _TermKit.commandDefaults = {};
408
- var TermKit = _TermKit;
409
-
410
- // src/index.ts
411
- var base = null;
412
- var commandDefaults = {};
413
- var command = (name, variables, info) => {
414
- const cmd = new Command(Object.assign({ name, variables, info }, commandDefaults));
415
- if (!base) base = cmd;
416
- return cmd;
417
- };
418
- var middleware = (fn) => fn;
419
- var option = (short, long, variables, info) => new Option({ short, long, variables, info });
420
- var parse = (arr) => {
421
- if (!base) throw new Error("No command defined");
422
- return base.parse(arr);
423
- };
424
- var setDefaults = (data) => {
425
- commandDefaults = data;
426
2818
  };
427
2819
  // Annotate the CommonJS export names for ESM import in node:
428
2820
  0 && (module.exports = {
2821
+ Bar,
2822
+ Chart,
2823
+ Color,
2824
+ Column,
429
2825
  Command,
2826
+ Input,
2827
+ Log,
2828
+ MultiBar,
2829
+ MultiSelect,
430
2830
  Option,
2831
+ Program,
2832
+ Scrollbox,
2833
+ Select,
2834
+ Spinner,
2835
+ Table,
431
2836
  TermKit,
432
2837
  Variable,
433
- command,
434
- middleware,
435
- option,
436
- parse,
437
- setDefaults
2838
+ configure,
2839
+ confirm,
2840
+ input,
2841
+ log,
2842
+ markup,
2843
+ multiSelect,
2844
+ padLeft,
2845
+ padRight,
2846
+ padSides,
2847
+ scrollbox,
2848
+ select,
2849
+ stringLength,
2850
+ truncate,
2851
+ wrap
438
2852
  });