mepcli 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.js +202 -46
- package/dist/types.d.ts +2 -1
- package/example.ts +43 -23
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -78,6 +78,7 @@ class TextPrompt extends Prompt {
|
|
|
78
78
|
this.errorMsg = '';
|
|
79
79
|
this.cursor = 0;
|
|
80
80
|
this.hasTyped = false;
|
|
81
|
+
this.renderLines = 1;
|
|
81
82
|
this.value = options.initial || '';
|
|
82
83
|
this.cursor = this.value.length;
|
|
83
84
|
}
|
|
@@ -85,58 +86,142 @@ class TextPrompt extends Prompt {
|
|
|
85
86
|
// TextPrompt needs the cursor visible!
|
|
86
87
|
this.print(ansi_1.ANSI.SHOW_CURSOR);
|
|
87
88
|
if (!firstRender) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// Clear previous lines
|
|
90
|
+
// Note: renderLines now represents visual wrapped lines
|
|
91
|
+
for (let i = 0; i < this.renderLines; i++) {
|
|
92
|
+
this.print(ansi_1.ANSI.ERASE_LINE);
|
|
93
|
+
if (i < this.renderLines - 1)
|
|
94
|
+
this.print(ansi_1.ANSI.UP);
|
|
91
95
|
}
|
|
96
|
+
this.print(ansi_1.ANSI.CURSOR_LEFT);
|
|
92
97
|
}
|
|
98
|
+
let output = '';
|
|
93
99
|
// 1. Render the Prompt Message
|
|
94
|
-
this.print(ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
|
|
95
100
|
const icon = this.errorMsg ? `${MepCLI.theme.error}✖` : `${MepCLI.theme.success}?`;
|
|
96
|
-
this.
|
|
101
|
+
const multilineHint = this.options.multiline ? ` ${MepCLI.theme.muted}(Press Ctrl+D to submit)${ansi_1.ANSI.RESET}` : '';
|
|
102
|
+
output += `${icon} ${ansi_1.ANSI.BOLD}${MepCLI.theme.title}${this.options.message}${ansi_1.ANSI.RESET}${multilineHint} `;
|
|
97
103
|
// 2. Render the Value or Placeholder
|
|
104
|
+
let displayValue = '';
|
|
98
105
|
if (!this.value && this.options.placeholder && !this.errorMsg && !this.hasTyped) {
|
|
99
|
-
|
|
100
|
-
// Move cursor back to start so typing overwrites placeholder visually
|
|
101
|
-
this.print(`\x1b[${this.options.placeholder.length}D`);
|
|
106
|
+
displayValue = `${MepCLI.theme.muted}${this.options.placeholder}${ansi_1.ANSI.RESET}`;
|
|
102
107
|
}
|
|
103
108
|
else {
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
displayValue = this.options.isPassword ? '*'.repeat(this.value.length) : this.value;
|
|
110
|
+
displayValue = `${MepCLI.theme.main}${displayValue}${ansi_1.ANSI.RESET}`;
|
|
106
111
|
}
|
|
112
|
+
output += displayValue;
|
|
107
113
|
// 3. Handle Error Message
|
|
108
114
|
if (this.errorMsg) {
|
|
109
|
-
|
|
110
|
-
this.print(ansi_1.ANSI.UP); // Go back to input line
|
|
111
|
-
// Re-calculate position to end of input
|
|
112
|
-
const promptLen = this.options.message.length + 3; // Icon + 2 spaces
|
|
113
|
-
const valLen = this.value.length;
|
|
114
|
-
// Move to absolute start of line, then move right to end of string
|
|
115
|
-
this.print(`\x1b[1000D\x1b[${promptLen + valLen}C`);
|
|
115
|
+
output += `\n${MepCLI.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`;
|
|
116
116
|
}
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
this.print(output);
|
|
118
|
+
// 4. Calculate Visual Metrics for Wrapping
|
|
119
|
+
const cols = process.stdout.columns || 80;
|
|
120
|
+
const stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
121
|
+
// Prompt String (visual part before value)
|
|
122
|
+
const promptStr = `${icon} ${MepCLI.theme.title}${this.options.message} ${multilineHint} `;
|
|
123
|
+
const promptVisualLen = stripAnsi(promptStr).length;
|
|
124
|
+
// Value String (visual part)
|
|
125
|
+
const rawValue = (!this.value && this.options.placeholder && !this.errorMsg && !this.hasTyped)
|
|
126
|
+
? this.options.placeholder || ''
|
|
127
|
+
: (this.options.isPassword ? '*'.repeat(this.value.length) : this.value);
|
|
128
|
+
// Error String (visual part)
|
|
129
|
+
const errorVisualLines = this.errorMsg ? Math.ceil((3 + this.errorMsg.length) / cols) : 0;
|
|
130
|
+
// Calculate Total Lines and Cursor Position
|
|
131
|
+
// We simulate printing the prompt + value + error
|
|
132
|
+
let currentVisualLine = 0;
|
|
133
|
+
let currentCol = 0;
|
|
134
|
+
// State tracking for cursor
|
|
135
|
+
let cursorRow = 0;
|
|
136
|
+
let cursorCol = 0;
|
|
137
|
+
// Add Prompt
|
|
138
|
+
currentCol += promptVisualLen;
|
|
139
|
+
while (currentCol >= cols) {
|
|
140
|
+
currentVisualLine++;
|
|
141
|
+
currentCol -= cols;
|
|
142
|
+
}
|
|
143
|
+
// Add Value (Character by character to handle wrapping and cursor tracking accurately)
|
|
144
|
+
// Note: This doesn't handle multi-width chars perfectly, but handles wrapping better than before
|
|
145
|
+
const valueLen = rawValue.length;
|
|
146
|
+
// If placeholder, we treat it as value for render height, but cursor is at 0
|
|
147
|
+
const isPlaceholder = (!this.value && this.options.placeholder && !this.errorMsg && !this.hasTyped);
|
|
148
|
+
for (let i = 0; i < valueLen; i++) {
|
|
149
|
+
// Check if we are at cursor position
|
|
150
|
+
if (!isPlaceholder && i === this.cursor) {
|
|
151
|
+
cursorRow = currentVisualLine;
|
|
152
|
+
cursorCol = currentCol;
|
|
153
|
+
}
|
|
154
|
+
const char = rawValue[i];
|
|
155
|
+
if (char === '\n') {
|
|
156
|
+
currentVisualLine++;
|
|
157
|
+
currentCol = 0;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
currentCol++;
|
|
161
|
+
if (currentCol >= cols) {
|
|
162
|
+
currentVisualLine++;
|
|
163
|
+
currentCol = 0;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// If cursor is at the very end
|
|
168
|
+
if (!isPlaceholder && this.cursor === valueLen) {
|
|
169
|
+
cursorRow = currentVisualLine;
|
|
170
|
+
cursorCol = currentCol;
|
|
171
|
+
}
|
|
172
|
+
// If placeholder, cursor is at start of value
|
|
173
|
+
if (isPlaceholder) {
|
|
174
|
+
// Re-calc cursor position as if it's at index 0 of value
|
|
175
|
+
// Which is effectively where prompt ends
|
|
176
|
+
// We already updated currentCol/Line for prompt above, but loop continued for placeholder
|
|
177
|
+
// So we need to recalculate or store prompt end state
|
|
178
|
+
// Let's just use prompt end state:
|
|
179
|
+
let pCol = promptVisualLen;
|
|
180
|
+
let pRow = 0;
|
|
181
|
+
while (pCol >= cols) {
|
|
182
|
+
pRow++;
|
|
183
|
+
pCol -= cols;
|
|
184
|
+
}
|
|
185
|
+
cursorRow = pRow;
|
|
186
|
+
cursorCol = pCol;
|
|
187
|
+
}
|
|
188
|
+
// Final height
|
|
189
|
+
// If we are at col 0 of a new line (e.g. just wrapped or \n), we count that line
|
|
190
|
+
// currentVisualLine is 0-indexed index of the line we are on.
|
|
191
|
+
// Total lines = currentVisualLine + 1 + errorLines
|
|
192
|
+
// Special case: if input ends with \n, we are on a new empty line
|
|
193
|
+
const totalValueRows = currentVisualLine + 1;
|
|
194
|
+
this.renderLines = totalValueRows + errorVisualLines;
|
|
195
|
+
// 5. Position Cursor Logic
|
|
196
|
+
// We are currently at the end of output.
|
|
197
|
+
// End row relative to start: this.renderLines - 1
|
|
198
|
+
const endRow = this.renderLines - 1;
|
|
199
|
+
// Move up to cursor row
|
|
200
|
+
const linesUp = endRow - cursorRow;
|
|
201
|
+
if (linesUp > 0) {
|
|
202
|
+
this.print(`\x1b[${linesUp}A`);
|
|
203
|
+
}
|
|
204
|
+
// Move to cursor col
|
|
205
|
+
this.print(ansi_1.ANSI.CURSOR_LEFT); // Go to col 0
|
|
206
|
+
if (cursorCol > 0) {
|
|
207
|
+
this.print(`\x1b[${cursorCol}C`);
|
|
123
208
|
}
|
|
124
209
|
}
|
|
125
210
|
handleInput(char) {
|
|
126
211
|
// Enter
|
|
127
212
|
if (char === '\r' || char === '\n') {
|
|
128
|
-
if (this.options.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (this.errorMsg) {
|
|
137
|
-
this.print(`\n${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.UP}`);
|
|
213
|
+
if (this.options.multiline) {
|
|
214
|
+
this.value = this.value.slice(0, this.cursor) + '\n' + this.value.slice(this.cursor);
|
|
215
|
+
this.cursor++;
|
|
216
|
+
this.render(false);
|
|
217
|
+
return;
|
|
138
218
|
}
|
|
139
|
-
this.
|
|
219
|
+
this.validateAndSubmit();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// Ctrl+D (EOF) or Ctrl+S for Submit in Multiline
|
|
223
|
+
if (this.options.multiline && (char === '\u0004' || char === '\u0013')) {
|
|
224
|
+
this.validateAndSubmit();
|
|
140
225
|
return;
|
|
141
226
|
}
|
|
142
227
|
// Backspace
|
|
@@ -176,15 +261,66 @@ class TextPrompt extends Prompt {
|
|
|
176
261
|
}
|
|
177
262
|
return;
|
|
178
263
|
}
|
|
179
|
-
// Regular Typing
|
|
180
|
-
if (
|
|
264
|
+
// Regular Typing & Paste
|
|
265
|
+
if (!/^[\x00-\x1F]/.test(char) && !char.startsWith('\x1b')) {
|
|
181
266
|
this.hasTyped = true;
|
|
182
267
|
this.value = this.value.slice(0, this.cursor) + char + this.value.slice(this.cursor);
|
|
183
|
-
this.cursor
|
|
268
|
+
this.cursor += char.length;
|
|
184
269
|
this.errorMsg = '';
|
|
185
270
|
this.render(false);
|
|
186
271
|
}
|
|
187
272
|
}
|
|
273
|
+
validateAndSubmit() {
|
|
274
|
+
if (this.options.validate) {
|
|
275
|
+
const result = this.options.validate(this.value);
|
|
276
|
+
// Handle Promise validation
|
|
277
|
+
if (result instanceof Promise) {
|
|
278
|
+
// Show loading state
|
|
279
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}${MepCLI.theme.main}Validating...${ansi_1.ANSI.RESET}`);
|
|
280
|
+
this.print(ansi_1.ANSI.UP);
|
|
281
|
+
result.then(valid => {
|
|
282
|
+
// Clear loading message
|
|
283
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}`);
|
|
284
|
+
this.print(ansi_1.ANSI.UP);
|
|
285
|
+
if (typeof valid === 'string' && valid.length > 0) {
|
|
286
|
+
this.errorMsg = valid;
|
|
287
|
+
this.render(false);
|
|
288
|
+
}
|
|
289
|
+
else if (valid === false) {
|
|
290
|
+
this.errorMsg = 'Invalid input';
|
|
291
|
+
this.render(false);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
if (this.errorMsg) {
|
|
295
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.UP}`);
|
|
296
|
+
}
|
|
297
|
+
this.submit(this.value);
|
|
298
|
+
}
|
|
299
|
+
}).catch(err => {
|
|
300
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}`);
|
|
301
|
+
this.print(ansi_1.ANSI.UP);
|
|
302
|
+
this.errorMsg = err.message || 'Validation failed';
|
|
303
|
+
this.render(false);
|
|
304
|
+
});
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
// Handle Sync validation
|
|
308
|
+
if (typeof result === 'string' && result.length > 0) {
|
|
309
|
+
this.errorMsg = result;
|
|
310
|
+
this.render(false);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (result === false) {
|
|
314
|
+
this.errorMsg = 'Invalid input';
|
|
315
|
+
this.render(false);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (this.errorMsg) {
|
|
320
|
+
this.print(`\n${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.UP}`);
|
|
321
|
+
}
|
|
322
|
+
this.submit(this.value);
|
|
323
|
+
}
|
|
188
324
|
}
|
|
189
325
|
// --- Implementation: Select Prompt ---
|
|
190
326
|
class SelectPrompt extends Prompt {
|
|
@@ -192,6 +328,8 @@ class SelectPrompt extends Prompt {
|
|
|
192
328
|
super(options);
|
|
193
329
|
this.selectedIndex = 0;
|
|
194
330
|
this.searchBuffer = '';
|
|
331
|
+
this.scrollTop = 0;
|
|
332
|
+
this.pageSize = 7;
|
|
195
333
|
// Custom render to handle variable height clearing
|
|
196
334
|
this.lastRenderHeight = 0;
|
|
197
335
|
// Find first non-separator index
|
|
@@ -237,6 +375,17 @@ class SelectPrompt extends Prompt {
|
|
|
237
375
|
}
|
|
238
376
|
let output = '';
|
|
239
377
|
const choices = this.getFilteredChoices();
|
|
378
|
+
// Adjust Scroll Top
|
|
379
|
+
if (this.selectedIndex < this.scrollTop) {
|
|
380
|
+
this.scrollTop = this.selectedIndex;
|
|
381
|
+
}
|
|
382
|
+
else if (this.selectedIndex >= this.scrollTop + this.pageSize) {
|
|
383
|
+
this.scrollTop = this.selectedIndex - this.pageSize + 1;
|
|
384
|
+
}
|
|
385
|
+
// Handle Filtering Edge Case: if list shrinks, scrollTop might be too high
|
|
386
|
+
if (this.scrollTop > choices.length - 1) {
|
|
387
|
+
this.scrollTop = Math.max(0, choices.length - this.pageSize);
|
|
388
|
+
}
|
|
240
389
|
// Header
|
|
241
390
|
const searchStr = this.searchBuffer ? ` ${MepCLI.theme.muted}(Filter: ${this.searchBuffer})${ansi_1.ANSI.RESET}` : '';
|
|
242
391
|
output += `${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}${MepCLI.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${MepCLI.theme.title}${this.options.message}${ansi_1.ANSI.RESET}${searchStr}\n`;
|
|
@@ -244,13 +393,15 @@ class SelectPrompt extends Prompt {
|
|
|
244
393
|
output += `${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT} ${MepCLI.theme.muted}No results found${ansi_1.ANSI.RESET}\n`;
|
|
245
394
|
}
|
|
246
395
|
else {
|
|
247
|
-
choices.
|
|
396
|
+
const visibleChoices = choices.slice(this.scrollTop, this.scrollTop + this.pageSize);
|
|
397
|
+
visibleChoices.forEach((choice, index) => {
|
|
398
|
+
const actualIndex = this.scrollTop + index;
|
|
248
399
|
output += `${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`;
|
|
249
400
|
if (this.isSeparator(choice)) {
|
|
250
401
|
output += ` ${ansi_1.ANSI.DIM}${choice.text || '────────'}${ansi_1.ANSI.RESET}\n`;
|
|
251
402
|
}
|
|
252
403
|
else {
|
|
253
|
-
if (
|
|
404
|
+
if (actualIndex === this.selectedIndex) {
|
|
254
405
|
output += `${MepCLI.theme.main}❯ ${choice.title}${ansi_1.ANSI.RESET}\n`;
|
|
255
406
|
}
|
|
256
407
|
else {
|
|
@@ -261,7 +412,8 @@ class SelectPrompt extends Prompt {
|
|
|
261
412
|
}
|
|
262
413
|
this.print(output);
|
|
263
414
|
// Clear remaining lines if list shrunk
|
|
264
|
-
const
|
|
415
|
+
const visibleCount = Math.min(choices.length, this.pageSize);
|
|
416
|
+
const currentHeight = visibleCount + 1 + (choices.length === 0 ? 1 : 0);
|
|
265
417
|
const linesToClear = this.lastRenderHeight - currentHeight;
|
|
266
418
|
if (linesToClear > 0) {
|
|
267
419
|
for (let i = 0; i < linesToClear; i++) {
|
|
@@ -595,13 +747,17 @@ class NumberPrompt extends Prompt {
|
|
|
595
747
|
return;
|
|
596
748
|
}
|
|
597
749
|
// Numeric Input (and . and -)
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (char
|
|
602
|
-
|
|
750
|
+
// Simple paste support for numbers is also good
|
|
751
|
+
if (/^[0-9.\-]+$/.test(char)) {
|
|
752
|
+
// Basic validation for pasted content
|
|
753
|
+
if (char.includes('-') && (this.cursor !== 0 || this.stringValue.includes('-') || char.lastIndexOf('-') > 0)) {
|
|
754
|
+
// If complex paste fails simple checks, ignore or let user correct
|
|
755
|
+
// For now, strict check on single char logic is preserved if we want,
|
|
756
|
+
// but let's allow pasting valid number strings
|
|
757
|
+
}
|
|
758
|
+
// Allow if it looks like a number part
|
|
603
759
|
this.stringValue = this.stringValue.slice(0, this.cursor) + char + this.stringValue.slice(this.cursor);
|
|
604
|
-
this.cursor
|
|
760
|
+
this.cursor += char.length;
|
|
605
761
|
this.errorMsg = '';
|
|
606
762
|
this.render(false);
|
|
607
763
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -14,8 +14,9 @@ export interface BaseOptions {
|
|
|
14
14
|
export interface TextOptions extends BaseOptions {
|
|
15
15
|
placeholder?: string;
|
|
16
16
|
initial?: string;
|
|
17
|
-
validate?: (value: string) => string | boolean
|
|
17
|
+
validate?: (value: string) => string | boolean | Promise<string | boolean>;
|
|
18
18
|
isPassword?: boolean;
|
|
19
|
+
multiline?: boolean;
|
|
19
20
|
}
|
|
20
21
|
export interface Separator {
|
|
21
22
|
separator: true;
|
package/example.ts
CHANGED
|
@@ -1,77 +1,97 @@
|
|
|
1
|
-
import { MepCLI } from './src'; // Or mepcli if installed via NPM
|
|
1
|
+
import { MepCLI } from './src'; // Or 'mepcli' if installed via NPM
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Runs a comprehensive
|
|
4
|
+
* Runs a comprehensive demo showcasing all MepCLI prompt types and utilities.
|
|
5
|
+
* This demonstrates all core functionalities including Text, Password, Select,
|
|
6
|
+
* Checkbox, Number, Toggle, Confirm, and the Spin utility.
|
|
5
7
|
*/
|
|
6
|
-
async function
|
|
8
|
+
async function runComprehensiveDemo() {
|
|
7
9
|
console.clear();
|
|
8
|
-
console.log("--- MepCLI Comprehensive
|
|
10
|
+
console.log("--- MepCLI Comprehensive Demo (All 7 Prompts + Spin Utility) ---\n");
|
|
9
11
|
|
|
10
12
|
try {
|
|
11
|
-
// --- 1. Text Prompt
|
|
13
|
+
// --- 1. Text Prompt (Input with Validation and initial value) ---
|
|
12
14
|
const projectName = await MepCLI.text({
|
|
13
15
|
message: "Enter the name for your new project:",
|
|
14
16
|
placeholder: "e.g., minimalist-cli-app",
|
|
15
17
|
initial: "MepProject",
|
|
16
18
|
validate: (value) => {
|
|
17
|
-
if (value.length < 3)
|
|
18
|
-
return "Project name must be at least 3 characters long.";
|
|
19
|
-
}
|
|
20
|
-
if (value.includes('&')) {
|
|
21
|
-
return "Project name cannot contain '&' symbol.";
|
|
22
|
-
}
|
|
19
|
+
if (value.length < 3) return "Project name must be at least 3 characters long.";
|
|
23
20
|
return true;
|
|
24
21
|
}
|
|
25
22
|
});
|
|
26
23
|
console.log(`\n✅ Text Result: Project name set to '${projectName}'`);
|
|
27
24
|
|
|
28
|
-
// --- 2. Password Prompt
|
|
25
|
+
// --- 2. Password Prompt (Hidden input) ---
|
|
29
26
|
const apiKey = await MepCLI.password({
|
|
30
27
|
message: "Enter the project's external API key:",
|
|
31
28
|
placeholder: "Input will be hidden..."
|
|
32
29
|
});
|
|
33
|
-
// Note: Do not log the actual key in a real app.
|
|
34
30
|
console.log(`\n✅ Password Result: API key entered (length: ${apiKey.length})`);
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
// --- 3. Select Prompt Test (Single Choice) ---
|
|
32
|
+
// --- 3. Select Prompt (Single choice, supports filtering/searching by typing) ---
|
|
38
33
|
const theme = await MepCLI.select({
|
|
39
34
|
message: "Choose your preferred editor color theme:",
|
|
40
35
|
choices: [
|
|
41
36
|
{ title: "Dark Mode (Default)", value: "dark" },
|
|
42
37
|
{ title: "Light Mode (Classic)", value: "light" },
|
|
43
38
|
{ title: "High Contrast (Accessibility)", value: "contrast" },
|
|
39
|
+
// Demonstrates a separator option
|
|
40
|
+
{ separator: true, text: "--- Pro Themes ---" },
|
|
44
41
|
{ title: "Monokai Pro", value: "monokai" },
|
|
45
42
|
]
|
|
46
43
|
});
|
|
47
44
|
console.log(`\n✅ Select Result: Chosen theme is: ${theme}`);
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
// --- 4. Checkbox Prompt Test (Multi-Choice with Min/Max) ---
|
|
46
|
+
// --- 4. Checkbox Prompt (Multi-choice with Min/Max limits) ---
|
|
51
47
|
const buildTools = await MepCLI.checkbox({
|
|
52
48
|
message: "Select your required bundlers/build tools (Min 1, Max 2):",
|
|
53
49
|
min: 1,
|
|
54
50
|
max: 2,
|
|
55
51
|
choices: [
|
|
56
52
|
{ title: "Webpack", value: "webpack" },
|
|
57
|
-
{ title: "Vite", value: "vite", selected: true },
|
|
53
|
+
{ title: "Vite", value: "vite", selected: true }, // Default selected state
|
|
58
54
|
{ title: "Rollup", value: "rollup" },
|
|
59
55
|
{ title: "esbuild", value: "esbuild" }
|
|
60
56
|
]
|
|
61
57
|
});
|
|
62
58
|
console.log(`\n✅ Checkbox Result: Selected build tools: [${buildTools.join(', ')}]`);
|
|
63
59
|
|
|
60
|
+
// --- 5. Number Prompt (Numeric input, supports Min/Max and Up/Down arrow for Step) ---
|
|
61
|
+
const port = await MepCLI.number({
|
|
62
|
+
message: "Which port should the server run on?",
|
|
63
|
+
initial: 3000,
|
|
64
|
+
min: 1024,
|
|
65
|
+
max: 65535,
|
|
66
|
+
step: 100 // Increments/decrements by 100 with arrows
|
|
67
|
+
});
|
|
68
|
+
console.log(`\n✅ Number Result: Server port: ${port}`);
|
|
69
|
+
|
|
70
|
+
// --- 6. Toggle Prompt (Boolean input, supports custom labels) ---
|
|
71
|
+
const isSecure = await MepCLI.toggle({
|
|
72
|
+
message: "Enable HTTPS/SSL for production?",
|
|
73
|
+
initial: false,
|
|
74
|
+
activeText: "SECURE", // Custom 'on' label
|
|
75
|
+
inactiveText: "INSECURE" // Custom 'off' label
|
|
76
|
+
});
|
|
77
|
+
console.log(`\n✅ Toggle Result: HTTPS enabled: ${isSecure}`);
|
|
64
78
|
|
|
65
|
-
// ---
|
|
79
|
+
// --- 7. Confirm Prompt (Simple Yes/No) ---
|
|
66
80
|
const proceed = await MepCLI.confirm({
|
|
67
|
-
message: "
|
|
81
|
+
message: "Ready to deploy the project now?",
|
|
68
82
|
initial: true
|
|
69
83
|
});
|
|
70
|
-
console.log(`\n✅ Confirm Result:
|
|
84
|
+
console.log(`\n✅ Confirm Result: Deployment decision: ${proceed ? 'Proceed' : 'Cancel'}`);
|
|
71
85
|
|
|
72
|
-
|
|
86
|
+
// --- 8. Spin Utility (Loading/Async Task Indicator) ---
|
|
87
|
+
await MepCLI.spin(
|
|
88
|
+
"Finalizing configuration and deploying to Teaserverse...",
|
|
89
|
+
new Promise(resolve => setTimeout(resolve, 1500)) // Simulates a 1.5 second async task
|
|
90
|
+
);
|
|
91
|
+
console.log("\n--- Deployment successful! All MepCLI features demonstrated! ---");
|
|
73
92
|
|
|
74
93
|
} catch (e) {
|
|
94
|
+
// Global handler for Ctrl+C closure
|
|
75
95
|
if (e instanceof Error && e.message === 'User force closed') {
|
|
76
96
|
console.log("\nOperation cancelled by user (Ctrl+C).");
|
|
77
97
|
} else {
|
|
@@ -80,4 +100,4 @@ async function runAllTests() {
|
|
|
80
100
|
}
|
|
81
101
|
}
|
|
82
102
|
|
|
83
|
-
|
|
103
|
+
runComprehensiveDemo();
|