mepcli 0.2.1 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -17
- package/dist/ansi.d.ts +2 -0
- package/dist/ansi.js +2 -0
- package/dist/base.d.ts +44 -0
- package/dist/base.js +87 -0
- package/dist/core.d.ts +6 -1
- package/dist/core.js +35 -775
- package/dist/prompts/checkbox.d.ts +10 -0
- package/dist/prompts/checkbox.js +92 -0
- package/dist/prompts/confirm.d.ts +7 -0
- package/dist/prompts/confirm.js +40 -0
- package/dist/prompts/date.d.ts +10 -0
- package/dist/prompts/date.js +157 -0
- package/dist/prompts/file.d.ts +13 -0
- package/dist/prompts/file.js +194 -0
- package/dist/prompts/list.d.ts +9 -0
- package/dist/prompts/list.js +83 -0
- package/dist/prompts/multi-select.d.ts +14 -0
- package/dist/prompts/multi-select.js +149 -0
- package/dist/prompts/number.d.ts +10 -0
- package/dist/prompts/number.js +137 -0
- package/dist/prompts/select.d.ts +16 -0
- package/dist/prompts/select.js +162 -0
- package/dist/prompts/slider.d.ts +7 -0
- package/dist/prompts/slider.js +48 -0
- package/dist/prompts/text.d.ts +12 -0
- package/dist/prompts/text.js +245 -0
- package/dist/prompts/toggle.d.ts +7 -0
- package/dist/prompts/toggle.js +45 -0
- package/dist/theme.d.ts +2 -0
- package/dist/theme.js +11 -0
- package/dist/types.d.ts +24 -0
- package/example.ts +57 -5
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { CheckboxOptions } from '../types';
|
|
3
|
+
export declare class CheckboxPrompt extends Prompt<any[], CheckboxOptions> {
|
|
4
|
+
private selectedIndex;
|
|
5
|
+
private checkedState;
|
|
6
|
+
private errorMsg;
|
|
7
|
+
constructor(options: CheckboxOptions);
|
|
8
|
+
protected render(firstRender: boolean): void;
|
|
9
|
+
protected handleInput(char: string): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CheckboxPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
// --- Implementation: Checkbox Prompt ---
|
|
8
|
+
class CheckboxPrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
this.selectedIndex = 0;
|
|
12
|
+
this.errorMsg = '';
|
|
13
|
+
this.checkedState = options.choices.map(c => !!c.selected);
|
|
14
|
+
}
|
|
15
|
+
render(firstRender) {
|
|
16
|
+
// Ensure cursor is HIDDEN for menus
|
|
17
|
+
this.print(ansi_1.ANSI.HIDE_CURSOR);
|
|
18
|
+
if (!firstRender) {
|
|
19
|
+
const extraLines = this.errorMsg ? 1 : 0;
|
|
20
|
+
this.print(`\x1b[${this.options.choices.length + 1 + extraLines}A`);
|
|
21
|
+
}
|
|
22
|
+
this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
|
|
23
|
+
const icon = this.errorMsg ? `${theme_1.theme.error}✖` : `${theme_1.theme.success}?`;
|
|
24
|
+
this.print(`${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${theme_1.theme.muted}(Press <space> to select, <enter> to confirm)${ansi_1.ANSI.RESET}\n`);
|
|
25
|
+
this.options.choices.forEach((choice, index) => {
|
|
26
|
+
this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
|
|
27
|
+
const cursor = index === this.selectedIndex ? `${theme_1.theme.main}❯${ansi_1.ANSI.RESET}` : ' ';
|
|
28
|
+
const isChecked = this.checkedState[index];
|
|
29
|
+
const checkbox = isChecked
|
|
30
|
+
? `${theme_1.theme.success}◉${ansi_1.ANSI.RESET}`
|
|
31
|
+
: `${theme_1.theme.muted}◯${ansi_1.ANSI.RESET}`;
|
|
32
|
+
const title = index === this.selectedIndex
|
|
33
|
+
? `${theme_1.theme.main}${choice.title}${ansi_1.ANSI.RESET}`
|
|
34
|
+
: choice.title;
|
|
35
|
+
this.print(`${cursor} ${checkbox} ${title}\n`);
|
|
36
|
+
});
|
|
37
|
+
if (this.errorMsg) {
|
|
38
|
+
this.print(`${ansi_1.ANSI.ERASE_LINE}${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`);
|
|
39
|
+
}
|
|
40
|
+
else if (!firstRender) {
|
|
41
|
+
this.print(`${ansi_1.ANSI.ERASE_LINE}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
handleInput(char) {
|
|
45
|
+
if (char === '\r' || char === '\n') {
|
|
46
|
+
const selectedCount = this.checkedState.filter(Boolean).length;
|
|
47
|
+
const { min = 0, max } = this.options;
|
|
48
|
+
if (selectedCount < min) {
|
|
49
|
+
this.errorMsg = `You must select at least ${min} options.`;
|
|
50
|
+
this.render(false);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (max && selectedCount > max) {
|
|
54
|
+
this.errorMsg = `You can only select up to ${max} options.`;
|
|
55
|
+
this.render(false);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.cleanup();
|
|
59
|
+
this.print(ansi_1.ANSI.SHOW_CURSOR + '\n');
|
|
60
|
+
const results = this.options.choices
|
|
61
|
+
.filter((_, i) => this.checkedState[i])
|
|
62
|
+
.map(c => c.value);
|
|
63
|
+
if (this._resolve)
|
|
64
|
+
this._resolve(results);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (char === ' ') {
|
|
68
|
+
const currentChecked = this.checkedState[this.selectedIndex];
|
|
69
|
+
const selectedCount = this.checkedState.filter(Boolean).length;
|
|
70
|
+
const { max } = this.options;
|
|
71
|
+
if (!currentChecked && max && selectedCount >= max) {
|
|
72
|
+
this.errorMsg = `Max ${max} selections allowed.`;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.checkedState[this.selectedIndex] = !currentChecked;
|
|
76
|
+
this.errorMsg = '';
|
|
77
|
+
}
|
|
78
|
+
this.render(false);
|
|
79
|
+
}
|
|
80
|
+
if (this.isUp(char)) { // Up
|
|
81
|
+
this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.options.choices.length - 1;
|
|
82
|
+
this.errorMsg = '';
|
|
83
|
+
this.render(false);
|
|
84
|
+
}
|
|
85
|
+
if (this.isDown(char)) { // Down
|
|
86
|
+
this.selectedIndex = this.selectedIndex < this.options.choices.length - 1 ? this.selectedIndex + 1 : 0;
|
|
87
|
+
this.errorMsg = '';
|
|
88
|
+
this.render(false);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.CheckboxPrompt = CheckboxPrompt;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { ConfirmOptions } from '../types';
|
|
3
|
+
export declare class ConfirmPrompt extends Prompt<boolean, ConfirmOptions> {
|
|
4
|
+
constructor(options: ConfirmOptions);
|
|
5
|
+
protected render(firstRender: boolean): void;
|
|
6
|
+
protected handleInput(char: string): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfirmPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
// --- Implementation: Confirm Prompt ---
|
|
8
|
+
class ConfirmPrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
this.value = options.initial ?? true;
|
|
12
|
+
}
|
|
13
|
+
render(firstRender) {
|
|
14
|
+
// Hide cursor for confirm, user just hits Y/N or Enter
|
|
15
|
+
this.print(ansi_1.ANSI.HIDE_CURSOR);
|
|
16
|
+
if (!firstRender) {
|
|
17
|
+
this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
|
|
18
|
+
}
|
|
19
|
+
const hint = this.value ? `${ansi_1.ANSI.BOLD}Yes${ansi_1.ANSI.RESET}/no` : `yes/${ansi_1.ANSI.BOLD}No${ansi_1.ANSI.RESET}`;
|
|
20
|
+
this.print(`${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${theme_1.theme.muted}(${hint})${ansi_1.ANSI.RESET} `);
|
|
21
|
+
const text = this.value ? 'Yes' : 'No';
|
|
22
|
+
this.print(`${theme_1.theme.main}${text}${ansi_1.ANSI.RESET}\x1b[${text.length}D`);
|
|
23
|
+
}
|
|
24
|
+
handleInput(char) {
|
|
25
|
+
const c = char.toLowerCase();
|
|
26
|
+
if (c === '\r' || c === '\n') {
|
|
27
|
+
this.submit(this.value);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (c === 'y') {
|
|
31
|
+
this.value = true;
|
|
32
|
+
this.render(false);
|
|
33
|
+
}
|
|
34
|
+
if (c === 'n') {
|
|
35
|
+
this.value = false;
|
|
36
|
+
this.render(false);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.ConfirmPrompt = ConfirmPrompt;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { DateOptions } from '../types';
|
|
3
|
+
export declare class DatePrompt extends Prompt<Date, DateOptions> {
|
|
4
|
+
private selectedField;
|
|
5
|
+
private errorMsg;
|
|
6
|
+
private inputBuffer;
|
|
7
|
+
constructor(options: DateOptions);
|
|
8
|
+
protected render(firstRender: boolean): void;
|
|
9
|
+
protected handleInput(char: string): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DatePrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
// --- Implementation: Date Prompt ---
|
|
8
|
+
class DatePrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
this.selectedField = 0; // 0: Year, 1: Month, 2: Day, 3: Hour, 4: Minute
|
|
12
|
+
this.errorMsg = '';
|
|
13
|
+
this.inputBuffer = '';
|
|
14
|
+
this.value = options.initial || new Date();
|
|
15
|
+
}
|
|
16
|
+
render(firstRender) {
|
|
17
|
+
this.print(ansi_1.ANSI.HIDE_CURSOR);
|
|
18
|
+
if (!firstRender) {
|
|
19
|
+
this.print(ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
|
|
20
|
+
if (this.errorMsg) {
|
|
21
|
+
this.print(ansi_1.ANSI.UP + ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const y = this.value.getFullYear();
|
|
25
|
+
const m = (this.value.getMonth() + 1).toString().padStart(2, '0');
|
|
26
|
+
const d = this.value.getDate().toString().padStart(2, '0');
|
|
27
|
+
const h = this.value.getHours().toString().padStart(2, '0');
|
|
28
|
+
const min = this.value.getMinutes().toString().padStart(2, '0');
|
|
29
|
+
const fields = [y, m, d, h, min];
|
|
30
|
+
const display = fields.map((val, i) => {
|
|
31
|
+
if (i === this.selectedField)
|
|
32
|
+
return `${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${val}${ansi_1.ANSI.RESET}`;
|
|
33
|
+
return val;
|
|
34
|
+
});
|
|
35
|
+
const icon = this.errorMsg ? `${theme_1.theme.error}✖` : `${theme_1.theme.success}?`;
|
|
36
|
+
const dateStr = `${display[0]}-${display[1]}-${display[2]} ${display[3]}:${display[4]}`;
|
|
37
|
+
this.print(`${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${dateStr} ${theme_1.theme.muted}(Use arrows or type)${ansi_1.ANSI.RESET}`);
|
|
38
|
+
if (this.errorMsg) {
|
|
39
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`);
|
|
40
|
+
this.print(ansi_1.ANSI.UP);
|
|
41
|
+
// restore cursor pos logic isn't needed as we are on one line mostly, but for consistency:
|
|
42
|
+
const promptLen = this.options.message.length + 3; // roughly
|
|
43
|
+
this.print(`\x1b[1000D\x1b[${promptLen + 15}C`); // approx move back
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
handleInput(char) {
|
|
47
|
+
if (char === '\r' || char === '\n') {
|
|
48
|
+
this.submit(this.value);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (this.isLeft(char)) { // Left
|
|
52
|
+
this.selectedField = Math.max(0, this.selectedField - 1);
|
|
53
|
+
this.inputBuffer = ''; // Reset buffer on move
|
|
54
|
+
this.errorMsg = '';
|
|
55
|
+
this.render(false);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this.isRight(char)) { // Right
|
|
59
|
+
this.selectedField = Math.min(4, this.selectedField + 1);
|
|
60
|
+
this.inputBuffer = ''; // Reset buffer on move
|
|
61
|
+
this.errorMsg = '';
|
|
62
|
+
this.render(false);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Support numeric input
|
|
66
|
+
if (/^\d$/.test(char)) {
|
|
67
|
+
const maxLen = this.selectedField === 0 ? 4 : 2;
|
|
68
|
+
let nextBuffer = this.inputBuffer + char;
|
|
69
|
+
// If we exceed max length, reset to just the new char (assuming user is starting a new number)
|
|
70
|
+
// Or better: try to parse.
|
|
71
|
+
// Logic:
|
|
72
|
+
// 1. Try appending.
|
|
73
|
+
// 2. Validate.
|
|
74
|
+
// 3. If valid, keep.
|
|
75
|
+
// 4. If invalid (e.g. Month 13), assume new start -> set buffer to just char.
|
|
76
|
+
// However, we must respect field limits first.
|
|
77
|
+
if (nextBuffer.length > maxLen) {
|
|
78
|
+
nextBuffer = char;
|
|
79
|
+
}
|
|
80
|
+
const val = parseInt(nextBuffer, 10);
|
|
81
|
+
let valid = true;
|
|
82
|
+
// Pre-validation to decide if we should append or reset
|
|
83
|
+
if (this.selectedField === 1 && (val < 1 || val > 12))
|
|
84
|
+
valid = false; // Month
|
|
85
|
+
if (this.selectedField === 2 && (val < 1 || val > 31))
|
|
86
|
+
valid = false; // Day (rough check)
|
|
87
|
+
if (this.selectedField === 3 && (val > 23))
|
|
88
|
+
valid = false; // Hour
|
|
89
|
+
if (this.selectedField === 4 && (val > 59))
|
|
90
|
+
valid = false; // Minute
|
|
91
|
+
if (!valid) {
|
|
92
|
+
// If appending made it invalid (e.g. was '1', typed '3' -> '13' invalid month),
|
|
93
|
+
// treat '3' as the start of a new number.
|
|
94
|
+
nextBuffer = char;
|
|
95
|
+
}
|
|
96
|
+
this.inputBuffer = nextBuffer;
|
|
97
|
+
const finalVal = parseInt(this.inputBuffer, 10);
|
|
98
|
+
const d = new Date(this.value);
|
|
99
|
+
if (this.selectedField === 0) {
|
|
100
|
+
// Year is special, we just set it.
|
|
101
|
+
d.setFullYear(finalVal);
|
|
102
|
+
}
|
|
103
|
+
else if (this.selectedField === 1)
|
|
104
|
+
d.setMonth(Math.max(0, Math.min(11, finalVal - 1)));
|
|
105
|
+
else if (this.selectedField === 2)
|
|
106
|
+
d.setDate(Math.max(1, Math.min(31, finalVal)));
|
|
107
|
+
else if (this.selectedField === 3)
|
|
108
|
+
d.setHours(Math.max(0, Math.min(23, finalVal)));
|
|
109
|
+
else if (this.selectedField === 4)
|
|
110
|
+
d.setMinutes(Math.max(0, Math.min(59, finalVal)));
|
|
111
|
+
this.value = d;
|
|
112
|
+
this.errorMsg = '';
|
|
113
|
+
this.render(false);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Support standard and application cursor keys
|
|
117
|
+
const isUp = this.isUp(char);
|
|
118
|
+
const isDown = this.isDown(char);
|
|
119
|
+
if (isUp || isDown) {
|
|
120
|
+
this.inputBuffer = ''; // Reset buffer on arrow move
|
|
121
|
+
const dir = isUp ? 1 : -1;
|
|
122
|
+
const d = new Date(this.value);
|
|
123
|
+
switch (this.selectedField) {
|
|
124
|
+
case 0:
|
|
125
|
+
d.setFullYear(d.getFullYear() + dir);
|
|
126
|
+
break;
|
|
127
|
+
case 1:
|
|
128
|
+
d.setMonth(d.getMonth() + dir);
|
|
129
|
+
break;
|
|
130
|
+
case 2:
|
|
131
|
+
d.setDate(d.getDate() + dir);
|
|
132
|
+
break;
|
|
133
|
+
case 3:
|
|
134
|
+
d.setHours(d.getHours() + dir);
|
|
135
|
+
break;
|
|
136
|
+
case 4:
|
|
137
|
+
d.setMinutes(d.getMinutes() + dir);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
let valid = true;
|
|
141
|
+
if (this.options.min && d < this.options.min) {
|
|
142
|
+
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
143
|
+
valid = false;
|
|
144
|
+
}
|
|
145
|
+
if (this.options.max && d > this.options.max) {
|
|
146
|
+
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
147
|
+
valid = false;
|
|
148
|
+
}
|
|
149
|
+
if (valid) {
|
|
150
|
+
this.value = d;
|
|
151
|
+
this.errorMsg = '';
|
|
152
|
+
}
|
|
153
|
+
this.render(false);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.DatePrompt = DatePrompt;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { FileOptions } from '../types';
|
|
3
|
+
export declare class FilePrompt extends Prompt<string, FileOptions> {
|
|
4
|
+
private input;
|
|
5
|
+
private cursor;
|
|
6
|
+
private suggestions;
|
|
7
|
+
private selectedSuggestion;
|
|
8
|
+
private errorMsg;
|
|
9
|
+
constructor(options: FileOptions);
|
|
10
|
+
private updateSuggestions;
|
|
11
|
+
protected render(firstRender: boolean): void;
|
|
12
|
+
protected handleInput(char: string): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FilePrompt = void 0;
|
|
37
|
+
const ansi_1 = require("../ansi");
|
|
38
|
+
const base_1 = require("../base");
|
|
39
|
+
const theme_1 = require("../theme");
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
// --- Implementation: File Prompt ---
|
|
43
|
+
class FilePrompt extends base_1.Prompt {
|
|
44
|
+
constructor(options) {
|
|
45
|
+
super(options);
|
|
46
|
+
this.input = '';
|
|
47
|
+
this.cursor = 0;
|
|
48
|
+
this.suggestions = [];
|
|
49
|
+
this.selectedSuggestion = -1;
|
|
50
|
+
this.errorMsg = '';
|
|
51
|
+
this.input = options.basePath || '';
|
|
52
|
+
this.cursor = this.input.length;
|
|
53
|
+
}
|
|
54
|
+
updateSuggestions() {
|
|
55
|
+
try {
|
|
56
|
+
const dir = path.dirname(this.input) || '.';
|
|
57
|
+
const partial = path.basename(this.input);
|
|
58
|
+
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
|
|
59
|
+
const files = fs.readdirSync(dir);
|
|
60
|
+
this.suggestions = files
|
|
61
|
+
.filter(f => f.startsWith(partial))
|
|
62
|
+
.filter(f => {
|
|
63
|
+
const fullPath = path.join(dir, f);
|
|
64
|
+
const isDir = fs.statSync(fullPath).isDirectory();
|
|
65
|
+
if (this.options.onlyDirectories && !isDir)
|
|
66
|
+
return false;
|
|
67
|
+
if (this.options.extensions && !isDir) {
|
|
68
|
+
return this.options.extensions.some(ext => f.endsWith(ext));
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
})
|
|
72
|
+
.map(f => {
|
|
73
|
+
const fullPath = path.join(dir, f);
|
|
74
|
+
if (fs.statSync(fullPath).isDirectory())
|
|
75
|
+
return f + '/';
|
|
76
|
+
return f;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.suggestions = [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
this.suggestions = [];
|
|
85
|
+
}
|
|
86
|
+
this.selectedSuggestion = -1;
|
|
87
|
+
}
|
|
88
|
+
render(firstRender) {
|
|
89
|
+
this.print(ansi_1.ANSI.SHOW_CURSOR);
|
|
90
|
+
if (!firstRender) {
|
|
91
|
+
// Clear input line + suggestions
|
|
92
|
+
this.print(ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT); // Input line
|
|
93
|
+
// We need to track how many lines suggestions took
|
|
94
|
+
// For now assume simple clear, or use ANSI.ERASE_DOWN if at bottom?
|
|
95
|
+
// Safer to move up and clear
|
|
96
|
+
this.print(ansi_1.ANSI.ERASE_DOWN);
|
|
97
|
+
}
|
|
98
|
+
const icon = this.errorMsg ? `${theme_1.theme.error}✖` : `${theme_1.theme.success}?`;
|
|
99
|
+
this.print(`${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${this.input}`);
|
|
100
|
+
if (this.suggestions.length > 0) {
|
|
101
|
+
this.print('\n');
|
|
102
|
+
const maxShow = 5;
|
|
103
|
+
this.suggestions.slice(0, maxShow).forEach((s, i) => {
|
|
104
|
+
if (i === this.selectedSuggestion) {
|
|
105
|
+
this.print(`${theme_1.theme.main}❯ ${s}${ansi_1.ANSI.RESET}\n`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.print(` ${s}\n`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
if (this.suggestions.length > maxShow) {
|
|
112
|
+
this.print(` ...and ${this.suggestions.length - maxShow} more\n`);
|
|
113
|
+
}
|
|
114
|
+
// Move cursor back to input line
|
|
115
|
+
const lines = Math.min(this.suggestions.length, maxShow) + (this.suggestions.length > maxShow ? 2 : 1);
|
|
116
|
+
this.print(`\x1b[${lines}A`);
|
|
117
|
+
const inputLen = this.options.message.length + 3 + this.input.length;
|
|
118
|
+
this.print(`\x1b[${inputLen}C`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
handleInput(char) {
|
|
122
|
+
if (char === '\t') { // Tab
|
|
123
|
+
if (this.suggestions.length === 1) {
|
|
124
|
+
const dir = path.dirname(this.input);
|
|
125
|
+
this.input = path.join(dir === '.' ? '' : dir, this.suggestions[0]);
|
|
126
|
+
this.cursor = this.input.length;
|
|
127
|
+
this.suggestions = [];
|
|
128
|
+
this.render(false);
|
|
129
|
+
}
|
|
130
|
+
else if (this.suggestions.length > 1) {
|
|
131
|
+
// Cycle or show? For now cycle if selected
|
|
132
|
+
if (this.selectedSuggestion !== -1) {
|
|
133
|
+
const dir = path.dirname(this.input);
|
|
134
|
+
this.input = path.join(dir === '.' ? '' : dir, this.suggestions[this.selectedSuggestion]);
|
|
135
|
+
this.cursor = this.input.length;
|
|
136
|
+
this.suggestions = [];
|
|
137
|
+
this.render(false);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Just show suggestions (already done in render loop usually, but update logic ensures it)
|
|
141
|
+
this.updateSuggestions();
|
|
142
|
+
this.render(false);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.updateSuggestions();
|
|
147
|
+
this.render(false);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (char === '\r' || char === '\n') {
|
|
152
|
+
if (this.selectedSuggestion !== -1) {
|
|
153
|
+
const dir = path.dirname(this.input);
|
|
154
|
+
this.input = path.join(dir === '.' ? '' : dir, this.suggestions[this.selectedSuggestion]);
|
|
155
|
+
this.cursor = this.input.length;
|
|
156
|
+
this.suggestions = [];
|
|
157
|
+
this.selectedSuggestion = -1;
|
|
158
|
+
this.render(false);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.submit(this.input);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (this.isDown(char)) { // Down
|
|
166
|
+
if (this.suggestions.length > 0) {
|
|
167
|
+
this.selectedSuggestion = (this.selectedSuggestion + 1) % Math.min(this.suggestions.length, 5);
|
|
168
|
+
this.render(false);
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (this.isUp(char)) { // Up
|
|
173
|
+
if (this.suggestions.length > 0) {
|
|
174
|
+
this.selectedSuggestion = (this.selectedSuggestion - 1 + Math.min(this.suggestions.length, 5)) % Math.min(this.suggestions.length, 5);
|
|
175
|
+
this.render(false);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (char === '\u0008' || char === '\x7f') { // Backspace
|
|
180
|
+
if (this.input.length > 0) {
|
|
181
|
+
this.input = this.input.slice(0, -1);
|
|
182
|
+
this.updateSuggestions();
|
|
183
|
+
this.render(false);
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!/^[\x00-\x1F]/.test(char) && !char.startsWith('\x1b')) {
|
|
188
|
+
this.input += char;
|
|
189
|
+
this.updateSuggestions();
|
|
190
|
+
this.render(false);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.FilePrompt = FilePrompt;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { ListOptions } from '../types';
|
|
3
|
+
export declare class ListPrompt extends Prompt<string[], ListOptions> {
|
|
4
|
+
private currentInput;
|
|
5
|
+
private errorMsg;
|
|
6
|
+
constructor(options: ListOptions);
|
|
7
|
+
protected render(firstRender: boolean): void;
|
|
8
|
+
protected handleInput(char: string): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
// --- Implementation: List Prompt ---
|
|
8
|
+
class ListPrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
this.currentInput = '';
|
|
12
|
+
this.errorMsg = '';
|
|
13
|
+
this.value = options.initial || [];
|
|
14
|
+
}
|
|
15
|
+
render(firstRender) {
|
|
16
|
+
this.print(ansi_1.ANSI.SHOW_CURSOR);
|
|
17
|
+
if (!firstRender) {
|
|
18
|
+
this.print(ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
|
|
19
|
+
if (this.errorMsg) {
|
|
20
|
+
this.print(ansi_1.ANSI.UP + ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const icon = this.errorMsg ? `${theme_1.theme.error}✖` : `${theme_1.theme.success}?`;
|
|
24
|
+
this.print(`${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} `);
|
|
25
|
+
// Render Tags
|
|
26
|
+
if (this.value.length > 0) {
|
|
27
|
+
this.value.forEach((tag) => {
|
|
28
|
+
this.print(`${theme_1.theme.main}[${tag}]${ansi_1.ANSI.RESET} `);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Render Current Input
|
|
32
|
+
this.print(`${this.currentInput}`);
|
|
33
|
+
if (this.errorMsg) {
|
|
34
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`);
|
|
35
|
+
this.print(ansi_1.ANSI.UP);
|
|
36
|
+
// Return cursor
|
|
37
|
+
const promptLen = this.options.message.length + 3;
|
|
38
|
+
let tagsLen = 0;
|
|
39
|
+
this.value.forEach((tag) => tagsLen += tag.length + 3); // [tag] + space
|
|
40
|
+
const inputLen = this.currentInput.length;
|
|
41
|
+
this.print(`\x1b[1000D\x1b[${promptLen + tagsLen + inputLen}C`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
handleInput(char) {
|
|
45
|
+
if (char === '\r' || char === '\n') {
|
|
46
|
+
if (this.currentInput.trim()) {
|
|
47
|
+
this.value.push(this.currentInput.trim());
|
|
48
|
+
this.currentInput = '';
|
|
49
|
+
this.errorMsg = '';
|
|
50
|
+
this.render(false);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Done if input is empty
|
|
54
|
+
if (this.options.validate) {
|
|
55
|
+
const result = this.options.validate(this.value);
|
|
56
|
+
if (result !== true) {
|
|
57
|
+
this.errorMsg = typeof result === 'string' ? result : 'Invalid input';
|
|
58
|
+
this.render(false);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
this.submit(this.value);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (char === '\u0008' || char === '\x7f') { // Backspace
|
|
67
|
+
if (this.currentInput.length > 0) {
|
|
68
|
+
this.currentInput = this.currentInput.slice(0, -1);
|
|
69
|
+
this.render(false);
|
|
70
|
+
}
|
|
71
|
+
else if (this.value.length > 0) {
|
|
72
|
+
this.value.pop();
|
|
73
|
+
this.render(false);
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!/^[\x00-\x1F]/.test(char) && !char.startsWith('\x1b')) {
|
|
78
|
+
this.currentInput += char;
|
|
79
|
+
this.render(false);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.ListPrompt = ListPrompt;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { MultiSelectOptions } from '../types';
|
|
3
|
+
export declare class MultiSelectPrompt extends Prompt<any[], MultiSelectOptions> {
|
|
4
|
+
private selectedIndex;
|
|
5
|
+
private checkedState;
|
|
6
|
+
private searchBuffer;
|
|
7
|
+
private scrollTop;
|
|
8
|
+
private readonly pageSize;
|
|
9
|
+
private errorMsg;
|
|
10
|
+
constructor(options: MultiSelectOptions);
|
|
11
|
+
private getFilteredChoices;
|
|
12
|
+
protected render(firstRender: boolean): void;
|
|
13
|
+
protected handleInput(char: string): void;
|
|
14
|
+
}
|