mepcli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/dist/ansi.d.ts +3 -0
- package/dist/ansi.js +4 -0
- package/dist/base.d.ts +6 -0
- package/dist/base.js +18 -0
- package/dist/core.d.ts +2 -1
- package/dist/core.js +4 -0
- package/dist/input.d.ts +1 -0
- package/dist/input.js +62 -8
- package/dist/prompts/checkbox.d.ts +2 -1
- package/dist/prompts/checkbox.js +14 -0
- package/dist/prompts/confirm.d.ts +2 -1
- package/dist/prompts/confirm.js +6 -0
- package/dist/prompts/date.d.ts +3 -1
- package/dist/prompts/date.js +62 -31
- package/dist/prompts/multi-select.d.ts +2 -1
- package/dist/prompts/multi-select.js +15 -0
- package/dist/prompts/number.d.ts +2 -1
- package/dist/prompts/number.js +22 -0
- package/dist/prompts/rating.d.ts +8 -0
- package/dist/prompts/rating.js +78 -0
- package/dist/prompts/select.d.ts +2 -1
- package/dist/prompts/select.js +15 -0
- package/dist/prompts/slider.d.ts +2 -1
- package/dist/prompts/slider.js +14 -0
- package/dist/prompts/text.js +0 -31
- package/dist/prompts/toggle.d.ts +2 -1
- package/dist/prompts/toggle.js +6 -0
- package/dist/symbols.d.ts +4 -0
- package/dist/symbols.js +6 -2
- package/dist/types.d.ts +14 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +4 -1
- package/example.ts +14 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,14 +5,15 @@
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Zero Dependency:** Keeps your project clean and fast.
|
|
8
|
-
- **Comprehensive Prompts:** Includes `text`, `password`, `select`, `checkbox`, `confirm`, `number`, `toggle`, `list`, `slider`, `date`, `file`, and `
|
|
8
|
+
- **Comprehensive Prompts:** Includes `text`, `password`, `select`, `checkbox`, `confirm`, `number`, `toggle`, `list`, `slider`, `date`, `file`, `multiSelect`, and `rating`.
|
|
9
|
+
- **Mouse Support:** Built-in support for mouse interaction (SGR 1006 protocol). Scroll to navigate lists or change values; click to select.
|
|
9
10
|
- **Responsive Input:** Supports cursor movement (Left/Right) and character insertion/deletion in text-based prompts.
|
|
10
11
|
- **Validation:** Built-in support for input validation (sync and async) with custom error messages.
|
|
11
12
|
- **Elegant Look:** Uses ANSI colors for a clean, modern CLI experience.
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
15
|
-
```
|
|
16
|
+
```shell
|
|
16
17
|
npm install mepcli
|
|
17
18
|
# or
|
|
18
19
|
yarn add mepcli
|
|
@@ -66,7 +67,15 @@ async function main() {
|
|
|
66
67
|
]
|
|
67
68
|
});
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
// Rating (Star rating)
|
|
71
|
+
const stars = await MepCLI.rating({
|
|
72
|
+
message: "Rate your experience:",
|
|
73
|
+
min: 1,
|
|
74
|
+
max: 5,
|
|
75
|
+
initial: 5
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log({ name, age, newsletter, lang, tools, stars });
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
main();
|
|
@@ -86,10 +95,23 @@ main();
|
|
|
86
95
|
* `checkbox(options)` - Classic checkbox selection.
|
|
87
96
|
* `list(options)` - Enter a list of tags/strings.
|
|
88
97
|
* `slider(options)` - Select a number within a range using a visual slider.
|
|
98
|
+
* `rating(options)` - Star rating input.
|
|
89
99
|
* `date(options)` - Date and time picker.
|
|
90
100
|
* `file(options)` - File system navigator and selector.
|
|
91
101
|
* `spin(message, promise)` - Display a spinner while waiting for a promise.
|
|
92
102
|
|
|
103
|
+
## Mouse Support
|
|
104
|
+
|
|
105
|
+
MepCLI automatically detects modern terminals and enables **Mouse Tracking** (using SGR 1006 protocol).
|
|
106
|
+
|
|
107
|
+
* **Scrolling:**
|
|
108
|
+
* `select`, `multiSelect`, `checkbox`: Scroll to navigate the list.
|
|
109
|
+
* `number`, `slider`, `rating`, `date`: Scroll to increment/decrement values or fields.
|
|
110
|
+
* `toggle`, `confirm`: Scroll to toggle the state.
|
|
111
|
+
* **Configuration:**
|
|
112
|
+
* Mouse support is enabled by default if the terminal supports it.
|
|
113
|
+
* You can explicitly disable it per prompt by setting `mouse: false` in the options.
|
|
114
|
+
|
|
93
115
|
## License
|
|
94
116
|
|
|
95
117
|
This project is under the **MIT License**.
|
package/dist/ansi.d.ts
CHANGED
package/dist/ansi.js
CHANGED
package/dist/base.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MouseEvent } from './types';
|
|
1
2
|
import { detectCapabilities } from './utils';
|
|
2
3
|
/**
|
|
3
4
|
* Abstract base class for all prompts.
|
|
@@ -29,6 +30,11 @@ export declare abstract class Prompt<T, O> {
|
|
|
29
30
|
* @param key The raw buffer.
|
|
30
31
|
*/
|
|
31
32
|
protected abstract handleInput(char: string, key: Buffer): void;
|
|
33
|
+
/**
|
|
34
|
+
* Optional method to handle mouse events.
|
|
35
|
+
* Subclasses can override this to implement mouse interaction.
|
|
36
|
+
*/
|
|
37
|
+
protected handleMouse(event: MouseEvent): void;
|
|
32
38
|
protected print(text: string): void;
|
|
33
39
|
/**
|
|
34
40
|
* Starts the prompt interaction.
|
package/dist/base.js
CHANGED
|
@@ -20,6 +20,11 @@ class Prompt {
|
|
|
20
20
|
this._inputParser = new input_1.InputParser();
|
|
21
21
|
this.capabilities = (0, utils_1.detectCapabilities)();
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Optional method to handle mouse events.
|
|
25
|
+
* Subclasses can override this to implement mouse interaction.
|
|
26
|
+
*/
|
|
27
|
+
handleMouse(event) { }
|
|
23
28
|
print(text) {
|
|
24
29
|
this.stdout.write(text);
|
|
25
30
|
}
|
|
@@ -36,6 +41,12 @@ class Prompt {
|
|
|
36
41
|
}
|
|
37
42
|
this.stdin.resume();
|
|
38
43
|
this.stdin.setEncoding('utf8');
|
|
44
|
+
// Enable Mouse Tracking if supported and requested
|
|
45
|
+
// Default to true if capabilities support it, unless explicitly disabled in options
|
|
46
|
+
const shouldEnableMouse = this.options.mouse !== false && this.capabilities.hasMouse;
|
|
47
|
+
if (shouldEnableMouse) {
|
|
48
|
+
this.print(ansi_1.ANSI.SET_ANY_EVENT_MOUSE + ansi_1.ANSI.SET_SGR_EXT_MODE_MOUSE);
|
|
49
|
+
}
|
|
39
50
|
// Initial render: Default to hidden cursor (good for menus)
|
|
40
51
|
// Subclasses like TextPrompt will explicitly show it if needed.
|
|
41
52
|
if (this.capabilities.isCI) {
|
|
@@ -57,6 +68,10 @@ class Prompt {
|
|
|
57
68
|
this.handleInput(char, buffer);
|
|
58
69
|
};
|
|
59
70
|
this._inputParser.on('keypress', this._onKeyHandler);
|
|
71
|
+
// Listen to mouse events
|
|
72
|
+
this._inputParser.on('mouse', (event) => {
|
|
73
|
+
this.handleMouse(event);
|
|
74
|
+
});
|
|
60
75
|
this._onDataHandler = (buffer) => {
|
|
61
76
|
this._inputParser.feed(buffer);
|
|
62
77
|
};
|
|
@@ -73,6 +88,9 @@ class Prompt {
|
|
|
73
88
|
if (this._onKeyHandler) {
|
|
74
89
|
this._inputParser.removeListener('keypress', this._onKeyHandler);
|
|
75
90
|
}
|
|
91
|
+
// Cleanup mouse listener - though InputParser is instance specific, so it's fine.
|
|
92
|
+
// Disable Mouse Tracking
|
|
93
|
+
this.print(ansi_1.ANSI.DISABLE_MOUSE);
|
|
76
94
|
if (typeof this.stdin.setRawMode === 'function') {
|
|
77
95
|
this.stdin.setRawMode(false);
|
|
78
96
|
}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextOptions, SelectOptions, ConfirmOptions, CheckboxOptions, ThemeConfig, NumberOptions, ToggleOptions, ListOptions, SliderOptions, DateOptions, FileOptions, MultiSelectOptions } from './types';
|
|
1
|
+
import { TextOptions, SelectOptions, ConfirmOptions, CheckboxOptions, ThemeConfig, NumberOptions, ToggleOptions, ListOptions, SliderOptions, DateOptions, FileOptions, MultiSelectOptions, RatingOptions } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Public Facade for MepCLI
|
|
4
4
|
*/
|
|
@@ -20,4 +20,5 @@ export declare class MepCLI {
|
|
|
20
20
|
static date(options: DateOptions): Promise<Date>;
|
|
21
21
|
static file(options: FileOptions): Promise<string>;
|
|
22
22
|
static multiSelect<const V>(options: MultiSelectOptions<V>): Promise<V[]>;
|
|
23
|
+
static rating(options: RatingOptions): Promise<number>;
|
|
23
24
|
}
|
package/dist/core.js
CHANGED
|
@@ -15,6 +15,7 @@ const slider_1 = require("./prompts/slider");
|
|
|
15
15
|
const date_1 = require("./prompts/date");
|
|
16
16
|
const file_1 = require("./prompts/file");
|
|
17
17
|
const multi_select_1 = require("./prompts/multi-select");
|
|
18
|
+
const rating_1 = require("./prompts/rating");
|
|
18
19
|
/**
|
|
19
20
|
* Public Facade for MepCLI
|
|
20
21
|
*/
|
|
@@ -80,6 +81,9 @@ class MepCLI {
|
|
|
80
81
|
static multiSelect(options) {
|
|
81
82
|
return new multi_select_1.MultiSelectPrompt(options).run();
|
|
82
83
|
}
|
|
84
|
+
static rating(options) {
|
|
85
|
+
return new rating_1.RatingPrompt(options).run();
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
88
|
exports.MepCLI = MepCLI;
|
|
85
89
|
MepCLI.theme = theme_1.theme;
|
package/dist/input.d.ts
CHANGED
package/dist/input.js
CHANGED
|
@@ -14,14 +14,6 @@ class InputParser extends events_1.EventEmitter {
|
|
|
14
14
|
* Feed data into the parser.
|
|
15
15
|
*/
|
|
16
16
|
feed(data) {
|
|
17
|
-
// Convert buffer to string.
|
|
18
|
-
// For partial multi-byte sequences at the chunk boundary,
|
|
19
|
-
// buffer.toString() might produce replacement chars.
|
|
20
|
-
// Ideally we should use StringDecoder, but since we are handling KeyPresses,
|
|
21
|
-
// and usually a keypress is complete, simple toString often works.
|
|
22
|
-
// However, the user mentioned fragmentation issues.
|
|
23
|
-
// But InputParser usually receives data from stdin.
|
|
24
|
-
// The core issue of fragmentation is splitting escape codes like \x1b [ A
|
|
25
17
|
const input = data.toString('utf-8');
|
|
26
18
|
for (let i = 0; i < input.length; i++) {
|
|
27
19
|
const char = input[i];
|
|
@@ -67,6 +59,11 @@ class InputParser extends events_1.EventEmitter {
|
|
|
67
59
|
}
|
|
68
60
|
else if (this.state === 'CSI') {
|
|
69
61
|
this.buffer += char;
|
|
62
|
+
if (char === '<') {
|
|
63
|
+
this.state = 'MOUSE_SGR';
|
|
64
|
+
this.buffer = '<';
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
70
67
|
// CSI sequences end with 0x40-0x7E
|
|
71
68
|
if (char >= '@' && char <= '~') {
|
|
72
69
|
this.emitKey(this.buffer);
|
|
@@ -75,6 +72,63 @@ class InputParser extends events_1.EventEmitter {
|
|
|
75
72
|
}
|
|
76
73
|
// Otherwise, we keep buffering (params like 1;2)
|
|
77
74
|
}
|
|
75
|
+
else if (this.state === 'MOUSE_SGR') {
|
|
76
|
+
this.buffer += char;
|
|
77
|
+
// SGR sequences end with 'm' (release) or 'M' (press)
|
|
78
|
+
if (char === 'm' || char === 'M') {
|
|
79
|
+
this.parseSGRMouse(this.buffer);
|
|
80
|
+
this.buffer = '';
|
|
81
|
+
this.state = 'NORMAL';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
parseSGRMouse(buffer) {
|
|
86
|
+
// console.log('Parsing SGR:', buffer);
|
|
87
|
+
// format: <b;x;yM or <b;x;ym
|
|
88
|
+
// buffer includes the leading < and trailing M/m
|
|
89
|
+
const content = buffer.slice(1, -1);
|
|
90
|
+
const type = buffer.slice(-1); // m or M
|
|
91
|
+
const parts = content.split(';').map(Number);
|
|
92
|
+
if (parts.length >= 3) {
|
|
93
|
+
const [b, x, y] = parts;
|
|
94
|
+
let action = 'press';
|
|
95
|
+
// Interpret button codes
|
|
96
|
+
// 0: Left, 1: Middle, 2: Right
|
|
97
|
+
// +32: Motion
|
|
98
|
+
// 64: Scroll Up
|
|
99
|
+
// 65: Scroll Down
|
|
100
|
+
if (b === 64) {
|
|
101
|
+
action = 'scroll';
|
|
102
|
+
this.emit('mouse', { name: 'mouse', x, y, button: 0, action, scroll: 'up' });
|
|
103
|
+
// Also emit keypress for scroll if needed? No, prompt should listen to mouse.
|
|
104
|
+
// But for "Easy Features", we emit standard names
|
|
105
|
+
this.emit('scrollup');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (b === 65) {
|
|
109
|
+
action = 'scroll';
|
|
110
|
+
this.emit('mouse', { name: 'mouse', x, y, button: 0, action, scroll: 'down' });
|
|
111
|
+
this.emit('scrolldown');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (type === 'm') {
|
|
115
|
+
action = 'release';
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
action = 'press';
|
|
119
|
+
// Check if motion
|
|
120
|
+
if (b & 32) {
|
|
121
|
+
action = 'move';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.emit('mouse', {
|
|
125
|
+
name: 'mouse',
|
|
126
|
+
x,
|
|
127
|
+
y,
|
|
128
|
+
button: b & 3, // Strip modifiers to get raw button 0-2
|
|
129
|
+
action
|
|
130
|
+
});
|
|
131
|
+
}
|
|
78
132
|
}
|
|
79
133
|
emitKey(key) {
|
|
80
134
|
// Normalize Enter
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { CheckboxOptions } from '../types';
|
|
2
|
+
import { CheckboxOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class CheckboxPrompt<V> extends Prompt<any[], CheckboxOptions<V>> {
|
|
4
4
|
private selectedIndex;
|
|
5
5
|
private checkedState;
|
|
@@ -9,4 +9,5 @@ export declare class CheckboxPrompt<V> extends Prompt<any[], CheckboxOptions<V>>
|
|
|
9
9
|
constructor(options: CheckboxOptions<V>);
|
|
10
10
|
protected render(firstRender: boolean): void;
|
|
11
11
|
protected handleInput(char: string): void;
|
|
12
|
+
protected handleMouse(event: MouseEvent): void;
|
|
12
13
|
}
|
package/dist/prompts/checkbox.js
CHANGED
|
@@ -111,5 +111,19 @@ class CheckboxPrompt extends base_1.Prompt {
|
|
|
111
111
|
this.render(false);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
handleMouse(event) {
|
|
115
|
+
if (event.action === 'scroll') {
|
|
116
|
+
if (event.scroll === 'up') {
|
|
117
|
+
this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.options.choices.length - 1;
|
|
118
|
+
this.errorMsg = '';
|
|
119
|
+
this.render(false);
|
|
120
|
+
}
|
|
121
|
+
else if (event.scroll === 'down') {
|
|
122
|
+
this.selectedIndex = this.selectedIndex < this.options.choices.length - 1 ? this.selectedIndex + 1 : 0;
|
|
123
|
+
this.errorMsg = '';
|
|
124
|
+
this.render(false);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
114
128
|
}
|
|
115
129
|
exports.CheckboxPrompt = CheckboxPrompt;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { ConfirmOptions } from '../types';
|
|
2
|
+
import { ConfirmOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class ConfirmPrompt extends Prompt<boolean, ConfirmOptions> {
|
|
4
4
|
constructor(options: ConfirmOptions);
|
|
5
5
|
protected render(firstRender: boolean): void;
|
|
6
6
|
protected handleInput(char: string): void;
|
|
7
|
+
protected handleMouse(event: MouseEvent): void;
|
|
7
8
|
}
|
package/dist/prompts/confirm.js
CHANGED
package/dist/prompts/date.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { DateOptions } from '../types';
|
|
2
|
+
import { DateOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class DatePrompt extends Prompt<Date, DateOptions> {
|
|
4
4
|
private selectedField;
|
|
5
5
|
private errorMsg;
|
|
@@ -7,4 +7,6 @@ export declare class DatePrompt extends Prompt<Date, DateOptions> {
|
|
|
7
7
|
constructor(options: DateOptions);
|
|
8
8
|
protected render(firstRender: boolean): void;
|
|
9
9
|
protected handleInput(char: string): void;
|
|
10
|
+
protected handleMouse(event: MouseEvent): void;
|
|
11
|
+
private adjustDate;
|
|
10
12
|
}
|
package/dist/prompts/date.js
CHANGED
|
@@ -37,6 +37,18 @@ class DatePrompt extends base_1.Prompt {
|
|
|
37
37
|
}
|
|
38
38
|
handleInput(char) {
|
|
39
39
|
if (char === '\r' || char === '\n') {
|
|
40
|
+
// Min constraint check
|
|
41
|
+
if (this.options.min && this.value < this.options.min) {
|
|
42
|
+
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
43
|
+
this.render(false);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Max constraint check
|
|
47
|
+
if (this.options.max && this.value > this.options.max) {
|
|
48
|
+
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
49
|
+
this.render(false);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
40
52
|
this.submit(this.value);
|
|
41
53
|
return;
|
|
42
54
|
}
|
|
@@ -89,7 +101,16 @@ class DatePrompt extends base_1.Prompt {
|
|
|
89
101
|
else if (this.selectedField === 4)
|
|
90
102
|
d.setMinutes(Math.max(0, Math.min(59, finalVal)));
|
|
91
103
|
this.value = d;
|
|
92
|
-
|
|
104
|
+
// Check immediately after updating the value to display an error message (but still allow further input)
|
|
105
|
+
if (this.options.min && this.value < this.options.min) {
|
|
106
|
+
this.errorMsg = 'Warning: Date is before minimum.';
|
|
107
|
+
}
|
|
108
|
+
else if (this.options.max && this.value > this.options.max) {
|
|
109
|
+
this.errorMsg = 'Warning: Date is after maximum.';
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.errorMsg = '';
|
|
113
|
+
}
|
|
93
114
|
this.render(false);
|
|
94
115
|
return;
|
|
95
116
|
}
|
|
@@ -99,39 +120,49 @@ class DatePrompt extends base_1.Prompt {
|
|
|
99
120
|
if (isUp || isDown) {
|
|
100
121
|
this.inputBuffer = ''; // Reset buffer on arrow move
|
|
101
122
|
const dir = isUp ? 1 : -1;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
break;
|
|
110
|
-
case 2:
|
|
111
|
-
d.setDate(d.getDate() + dir);
|
|
112
|
-
break;
|
|
113
|
-
case 3:
|
|
114
|
-
d.setHours(d.getHours() + dir);
|
|
115
|
-
break;
|
|
116
|
-
case 4:
|
|
117
|
-
d.setMinutes(d.getMinutes() + dir);
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
let valid = true;
|
|
121
|
-
if (this.options.min && d < this.options.min) {
|
|
122
|
-
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
123
|
-
valid = false;
|
|
124
|
-
}
|
|
125
|
-
if (this.options.max && d > this.options.max) {
|
|
126
|
-
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
127
|
-
valid = false;
|
|
123
|
+
this.adjustDate(dir);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
handleMouse(event) {
|
|
127
|
+
if (event.action === 'scroll') {
|
|
128
|
+
if (event.scroll === 'up') {
|
|
129
|
+
this.adjustDate(1);
|
|
128
130
|
}
|
|
129
|
-
if (
|
|
130
|
-
this.
|
|
131
|
-
this.errorMsg = '';
|
|
131
|
+
else if (event.scroll === 'down') {
|
|
132
|
+
this.adjustDate(-1);
|
|
132
133
|
}
|
|
133
|
-
this.render(false);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
adjustDate(dir) {
|
|
137
|
+
const d = new Date(this.value);
|
|
138
|
+
switch (this.selectedField) {
|
|
139
|
+
case 0:
|
|
140
|
+
d.setFullYear(d.getFullYear() + dir);
|
|
141
|
+
break;
|
|
142
|
+
case 1:
|
|
143
|
+
d.setMonth(d.getMonth() + dir);
|
|
144
|
+
break;
|
|
145
|
+
case 2:
|
|
146
|
+
d.setDate(d.getDate() + dir);
|
|
147
|
+
break;
|
|
148
|
+
case 3:
|
|
149
|
+
d.setHours(d.getHours() + dir);
|
|
150
|
+
break;
|
|
151
|
+
case 4:
|
|
152
|
+
d.setMinutes(d.getMinutes() + dir);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
this.value = d;
|
|
156
|
+
if (this.options.min && this.value < this.options.min) {
|
|
157
|
+
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
158
|
+
}
|
|
159
|
+
else if (this.options.max && this.value > this.options.max) {
|
|
160
|
+
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.errorMsg = '';
|
|
164
|
+
}
|
|
165
|
+
this.render(false);
|
|
166
|
+
}
|
|
136
167
|
}
|
|
137
168
|
exports.DatePrompt = DatePrompt;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { MultiSelectOptions } from '../types';
|
|
2
|
+
import { MultiSelectOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class MultiSelectPrompt<V> extends Prompt<any[], MultiSelectOptions<V>> {
|
|
4
4
|
private selectedIndex;
|
|
5
5
|
private checkedState;
|
|
@@ -11,4 +11,5 @@ export declare class MultiSelectPrompt<V> extends Prompt<any[], MultiSelectOptio
|
|
|
11
11
|
private getFilteredChoices;
|
|
12
12
|
protected render(firstRender: boolean): void;
|
|
13
13
|
protected handleInput(char: string): void;
|
|
14
|
+
protected handleMouse(event: MouseEvent): void;
|
|
14
15
|
}
|
|
@@ -120,5 +120,20 @@ class MultiSelectPrompt extends base_1.Prompt {
|
|
|
120
120
|
this.render(false);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
handleMouse(event) {
|
|
124
|
+
const choices = this.getFilteredChoices();
|
|
125
|
+
if (choices.length === 0)
|
|
126
|
+
return;
|
|
127
|
+
if (event.action === 'scroll') {
|
|
128
|
+
if (event.scroll === 'up') {
|
|
129
|
+
this.selectedIndex = (this.selectedIndex - 1 + choices.length) % choices.length;
|
|
130
|
+
this.render(false);
|
|
131
|
+
}
|
|
132
|
+
else if (event.scroll === 'down') {
|
|
133
|
+
this.selectedIndex = (this.selectedIndex + 1) % choices.length;
|
|
134
|
+
this.render(false);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
123
138
|
}
|
|
124
139
|
exports.MultiSelectPrompt = MultiSelectPrompt;
|
package/dist/prompts/number.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { NumberOptions } from '../types';
|
|
2
|
+
import { NumberOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class NumberPrompt extends Prompt<number, NumberOptions> {
|
|
4
4
|
private stringValue;
|
|
5
5
|
private cursor;
|
|
@@ -7,4 +7,5 @@ export declare class NumberPrompt extends Prompt<number, NumberOptions> {
|
|
|
7
7
|
constructor(options: NumberOptions);
|
|
8
8
|
protected render(firstRender: boolean): void;
|
|
9
9
|
protected handleInput(char: string): void;
|
|
10
|
+
protected handleMouse(event: MouseEvent): void;
|
|
10
11
|
}
|
package/dist/prompts/number.js
CHANGED
|
@@ -130,5 +130,27 @@ class NumberPrompt extends base_1.Prompt {
|
|
|
130
130
|
this.render(false);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
handleMouse(event) {
|
|
134
|
+
if (event.action === 'scroll') {
|
|
135
|
+
let num = parseFloat(this.stringValue) || 0;
|
|
136
|
+
const step = this.options.step ?? 1;
|
|
137
|
+
if (event.scroll === 'up') {
|
|
138
|
+
num += step;
|
|
139
|
+
if (this.options.max !== undefined && num > this.options.max)
|
|
140
|
+
num = this.options.max;
|
|
141
|
+
}
|
|
142
|
+
else if (event.scroll === 'down') {
|
|
143
|
+
num -= step;
|
|
144
|
+
if (this.options.min !== undefined && num < this.options.min)
|
|
145
|
+
num = this.options.min;
|
|
146
|
+
}
|
|
147
|
+
// Round to avoid float errors
|
|
148
|
+
num = Math.round(num * 10000) / 10000;
|
|
149
|
+
this.stringValue = num.toString();
|
|
150
|
+
this.cursor = this.stringValue.length;
|
|
151
|
+
this.errorMsg = '';
|
|
152
|
+
this.render(false);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
133
155
|
}
|
|
134
156
|
exports.NumberPrompt = NumberPrompt;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { RatingOptions, MouseEvent } from '../types';
|
|
3
|
+
export declare class RatingPrompt extends Prompt<number, RatingOptions> {
|
|
4
|
+
constructor(options: RatingOptions);
|
|
5
|
+
protected render(firstRender: boolean): void;
|
|
6
|
+
protected handleInput(char: string): void;
|
|
7
|
+
protected handleMouse(event: MouseEvent): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RatingPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
const symbols_1 = require("../symbols");
|
|
8
|
+
class RatingPrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
// Default to min if initial is not provided
|
|
12
|
+
this.value = options.initial ?? (options.min || 1);
|
|
13
|
+
}
|
|
14
|
+
render(firstRender) {
|
|
15
|
+
const min = this.options.min || 1;
|
|
16
|
+
const max = this.options.max || 5;
|
|
17
|
+
// Render stars
|
|
18
|
+
let stars = '';
|
|
19
|
+
for (let i = min; i <= max; i++) {
|
|
20
|
+
if (i <= this.value) {
|
|
21
|
+
stars += `${theme_1.theme.success}${symbols_1.symbols.star}${ansi_1.ANSI.RESET} `;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
stars += `${theme_1.theme.muted}${symbols_1.symbols.starEmpty}${ansi_1.ANSI.RESET} `;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const output = `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${stars}`;
|
|
28
|
+
this.renderFrame(output);
|
|
29
|
+
}
|
|
30
|
+
handleInput(char) {
|
|
31
|
+
const min = this.options.min || 1;
|
|
32
|
+
const max = this.options.max || 5;
|
|
33
|
+
if (char === '\r' || char === '\n') {
|
|
34
|
+
this.submit(this.value);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Arrow keys
|
|
38
|
+
if (this.isLeft(char) || this.isDown(char)) { // Left/Down decreases
|
|
39
|
+
if (this.value > min) {
|
|
40
|
+
this.value--;
|
|
41
|
+
this.render(false);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (this.isRight(char) || this.isUp(char)) { // Right/Up increases
|
|
45
|
+
if (this.value < max) {
|
|
46
|
+
this.value++;
|
|
47
|
+
this.render(false);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Number keys (1-9)
|
|
51
|
+
const num = parseInt(char);
|
|
52
|
+
if (!isNaN(num)) {
|
|
53
|
+
if (num >= min && num <= max) {
|
|
54
|
+
this.value = num;
|
|
55
|
+
this.render(false);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
handleMouse(event) {
|
|
60
|
+
const min = this.options.min || 1;
|
|
61
|
+
const max = this.options.max || 5;
|
|
62
|
+
if (event.action === 'scroll') {
|
|
63
|
+
if (event.scroll === 'up') {
|
|
64
|
+
if (this.value < max) {
|
|
65
|
+
this.value++;
|
|
66
|
+
this.render(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (event.scroll === 'down') {
|
|
70
|
+
if (this.value > min) {
|
|
71
|
+
this.value--;
|
|
72
|
+
this.render(false);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.RatingPrompt = RatingPrompt;
|
package/dist/prompts/select.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { SelectOptions } from '../types';
|
|
2
|
+
import { SelectOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class SelectPrompt<V> extends Prompt<any, SelectOptions<V>> {
|
|
4
4
|
private selectedIndex;
|
|
5
5
|
private searchBuffer;
|
|
@@ -11,4 +11,5 @@ export declare class SelectPrompt<V> extends Prompt<any, SelectOptions<V>> {
|
|
|
11
11
|
private getFilteredChoices;
|
|
12
12
|
protected render(firstRender: boolean): void;
|
|
13
13
|
protected handleInput(char: string): void;
|
|
14
|
+
protected handleMouse(event: MouseEvent): void;
|
|
14
15
|
}
|
package/dist/prompts/select.js
CHANGED
|
@@ -145,5 +145,20 @@ class SelectPrompt extends base_1.Prompt {
|
|
|
145
145
|
this.render(false);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
handleMouse(event) {
|
|
149
|
+
const choices = this.getFilteredChoices();
|
|
150
|
+
if (choices.length === 0)
|
|
151
|
+
return;
|
|
152
|
+
if (event.action === 'scroll') {
|
|
153
|
+
if (event.scroll === 'up') {
|
|
154
|
+
this.selectedIndex = this.findNextSelectableIndex(this.selectedIndex, -1);
|
|
155
|
+
this.render(false);
|
|
156
|
+
}
|
|
157
|
+
else if (event.scroll === 'down') {
|
|
158
|
+
this.selectedIndex = this.findNextSelectableIndex(this.selectedIndex, 1);
|
|
159
|
+
this.render(false);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
148
163
|
}
|
|
149
164
|
exports.SelectPrompt = SelectPrompt;
|
package/dist/prompts/slider.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { SliderOptions } from '../types';
|
|
2
|
+
import { SliderOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class SliderPrompt extends Prompt<number, SliderOptions> {
|
|
4
4
|
constructor(options: SliderOptions);
|
|
5
5
|
protected render(firstRender: boolean): void;
|
|
6
6
|
protected handleInput(char: string): void;
|
|
7
|
+
protected handleMouse(event: MouseEvent): void;
|
|
7
8
|
}
|
package/dist/prompts/slider.js
CHANGED
|
@@ -46,5 +46,19 @@ class SliderPrompt extends base_1.Prompt {
|
|
|
46
46
|
this.render(false);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
handleMouse(event) {
|
|
50
|
+
if (event.action === 'scroll') {
|
|
51
|
+
const step = this.options.step || 1;
|
|
52
|
+
if (event.scroll === 'up') { // Scroll Up -> Increase
|
|
53
|
+
this.value = Math.min(this.options.max, this.value + step);
|
|
54
|
+
}
|
|
55
|
+
if (event.scroll === 'down') { // Scroll Down -> Decrease
|
|
56
|
+
this.value = Math.max(this.options.min, this.value - step);
|
|
57
|
+
}
|
|
58
|
+
// Round to avoid float errors
|
|
59
|
+
this.value = Math.round(this.value * 10000) / 10000;
|
|
60
|
+
this.render(false);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
49
63
|
}
|
|
50
64
|
exports.SliderPrompt = SliderPrompt;
|
package/dist/prompts/text.js
CHANGED
|
@@ -77,41 +77,10 @@ class TextPrompt extends base_1.Prompt {
|
|
|
77
77
|
visualColIndex = 0;
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
|
-
// Calculate width of this segment?
|
|
81
|
-
// No, for simple text editor logic we often assume 1 char = 1 pos unless we do full layout.
|
|
82
|
-
// But here we want correct cursor placement over wide chars.
|
|
83
|
-
// So we should sum width.
|
|
84
|
-
// However, standard terminals handle wide chars by advancing cursor 2 spots.
|
|
85
|
-
// So we just need to sum the string length of the segment?
|
|
86
|
-
// Or 2 if it's wide?
|
|
87
|
-
// Standard terminal behavior:
|
|
88
|
-
// If I write an Emoji (2 cols), the cursor advances 2 cols.
|
|
89
|
-
// So visualColIndex should track stringWidth(seg).
|
|
90
|
-
// But if isPassword, it's '*'. Width 1.
|
|
91
80
|
if (this.options.isPassword) {
|
|
92
81
|
visualColIndex += 1;
|
|
93
82
|
}
|
|
94
83
|
else {
|
|
95
|
-
// Use our helper? Or just length?
|
|
96
|
-
// If we used stringWidth, it would be accurate.
|
|
97
|
-
// But we don't have access to stringWidth here easily unless we import it again (we did in base).
|
|
98
|
-
// Let's assume segment.length for now (byte length),
|
|
99
|
-
// because `\x1b[<N>C` moves N COLUMNS? No, N characters?
|
|
100
|
-
// ANSI `CUB` / `CUF` moves N *columns* usually?
|
|
101
|
-
// "The Cursor Forward (CUF) sequence moves the cursor forward by n columns."
|
|
102
|
-
// So if we have an emoji (2 cols), we need to move past it.
|
|
103
|
-
// If we print an emoji, cursor is at +2.
|
|
104
|
-
// Wait, if we use `renderFrame`, we rewrite everything.
|
|
105
|
-
// Then we calculate where to put the cursor.
|
|
106
|
-
// If line is "A <Emoji> B".
|
|
107
|
-
// Output: "A <Emoji> B".
|
|
108
|
-
// If cursor is after Emoji.
|
|
109
|
-
// We need to be at position: width("A") + width("<Emoji>").
|
|
110
|
-
// = 1 + 2 = 3.
|
|
111
|
-
// So `visualColIndex` should use `stringWidth(seg)`.
|
|
112
|
-
// But I didn't export `stringWidth` from `utils.ts` in the last step?
|
|
113
|
-
// Checking `src/utils.ts`... I did export it.
|
|
114
|
-
// But I need to import it here.
|
|
115
84
|
visualColIndex += this.options.isPassword ? 1 : this.getSegmentWidth(seg);
|
|
116
85
|
}
|
|
117
86
|
}
|
package/dist/prompts/toggle.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { ToggleOptions } from '../types';
|
|
2
|
+
import { ToggleOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class TogglePrompt extends Prompt<boolean, ToggleOptions> {
|
|
4
4
|
constructor(options: ToggleOptions);
|
|
5
5
|
protected render(firstRender: boolean): void;
|
|
6
6
|
protected handleInput(char: string): void;
|
|
7
|
+
protected handleMouse(event: MouseEvent): void;
|
|
7
8
|
}
|
package/dist/prompts/toggle.js
CHANGED
package/dist/symbols.d.ts
CHANGED
|
@@ -13,5 +13,9 @@ export interface SymbolDefinition {
|
|
|
13
13
|
unchecked: string;
|
|
14
14
|
/** Animation frames for the spinner */
|
|
15
15
|
spinner: string[];
|
|
16
|
+
/** Star symbol for rating */
|
|
17
|
+
star: string;
|
|
18
|
+
/** Empty star symbol for rating */
|
|
19
|
+
starEmpty: string;
|
|
16
20
|
}
|
|
17
21
|
export declare const symbols: SymbolDefinition;
|
package/dist/symbols.js
CHANGED
|
@@ -9,7 +9,9 @@ const UnicodeSymbols = {
|
|
|
9
9
|
line: '─',
|
|
10
10
|
checked: '◉',
|
|
11
11
|
unchecked: '◯',
|
|
12
|
-
spinner: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
12
|
+
spinner: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
|
|
13
|
+
star: '★',
|
|
14
|
+
starEmpty: '☆'
|
|
13
15
|
};
|
|
14
16
|
const AsciiSymbols = {
|
|
15
17
|
tick: '+',
|
|
@@ -18,7 +20,9 @@ const AsciiSymbols = {
|
|
|
18
20
|
line: '-',
|
|
19
21
|
checked: '[x]',
|
|
20
22
|
unchecked: '[ ]',
|
|
21
|
-
spinner: ['|', '/', '-', '\\']
|
|
23
|
+
spinner: ['|', '/', '-', '\\'],
|
|
24
|
+
star: '*',
|
|
25
|
+
starEmpty: ' '
|
|
22
26
|
};
|
|
23
27
|
const capabilities = (0, utils_1.detectCapabilities)();
|
|
24
28
|
const useUnicode = capabilities.hasUnicode && process.env.MEP_NO_UNICODE !== '1';
|
package/dist/types.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ export interface ThemeConfig {
|
|
|
10
10
|
}
|
|
11
11
|
export interface BaseOptions {
|
|
12
12
|
message: string;
|
|
13
|
+
mouse?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface MouseEvent {
|
|
16
|
+
name: 'mouse';
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
button: number;
|
|
20
|
+
action: 'press' | 'release' | 'move' | 'scroll';
|
|
21
|
+
scroll?: 'up' | 'down';
|
|
13
22
|
}
|
|
14
23
|
export interface TextOptions extends BaseOptions {
|
|
15
24
|
placeholder?: string;
|
|
@@ -65,6 +74,11 @@ export interface SliderOptions extends BaseOptions {
|
|
|
65
74
|
step?: number;
|
|
66
75
|
unit?: string;
|
|
67
76
|
}
|
|
77
|
+
export interface RatingOptions extends BaseOptions {
|
|
78
|
+
min?: number;
|
|
79
|
+
max?: number;
|
|
80
|
+
initial?: number;
|
|
81
|
+
}
|
|
68
82
|
export interface DateOptions extends BaseOptions {
|
|
69
83
|
initial?: Date;
|
|
70
84
|
min?: Date;
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -52,7 +52,10 @@ function detectCapabilities() {
|
|
|
52
52
|
isCI,
|
|
53
53
|
hasTrueColor,
|
|
54
54
|
// Enable Unicode only if it's TTY and environment supports it.
|
|
55
|
-
hasUnicode: isTTY && isUnicodeSupported()
|
|
55
|
+
hasUnicode: isTTY && isUnicodeSupported(),
|
|
56
|
+
// Check if mouse should be enabled (TTY and not CI, or explicit override)
|
|
57
|
+
// SGR is widely supported in modern terminals
|
|
58
|
+
hasMouse: isTTY && !isCI
|
|
56
59
|
};
|
|
57
60
|
}
|
|
58
61
|
/**
|
package/example.ts
CHANGED
|
@@ -96,7 +96,16 @@ async function runComprehensiveDemo() {
|
|
|
96
96
|
});
|
|
97
97
|
console.log(`\n✅ Slider Result: Brightness: ${brightness}%`);
|
|
98
98
|
|
|
99
|
-
// --- 9.
|
|
99
|
+
// --- 9. Rating Prompt (New) ---
|
|
100
|
+
const userRating = await MepCLI.rating({
|
|
101
|
+
message: "How would you rate this CLI tool?",
|
|
102
|
+
min: 1,
|
|
103
|
+
max: 5,
|
|
104
|
+
initial: 5
|
|
105
|
+
});
|
|
106
|
+
console.log(`\n✅ Rating Result: You rated it: ${userRating}/5`);
|
|
107
|
+
|
|
108
|
+
// --- 10. Date / Time Picker (New) ---
|
|
100
109
|
// We capture 'now' once to ensure initial >= min
|
|
101
110
|
const now = new Date();
|
|
102
111
|
const releaseDate = await MepCLI.date({
|
|
@@ -106,14 +115,14 @@ async function runComprehensiveDemo() {
|
|
|
106
115
|
});
|
|
107
116
|
console.log(`\n✅ Date Result: Release set for: ${releaseDate.toLocaleString()}`);
|
|
108
117
|
|
|
109
|
-
// ---
|
|
118
|
+
// --- 11. File Path Selector (New) ---
|
|
110
119
|
const configPath = await MepCLI.file({
|
|
111
120
|
message: "Select configuration file (Tab to autocomplete):",
|
|
112
121
|
basePath: process.cwd()
|
|
113
122
|
});
|
|
114
123
|
console.log(`\n✅ File Result: Path: ${configPath}`);
|
|
115
124
|
|
|
116
|
-
// ---
|
|
125
|
+
// --- 12. Multi-Select Autocomplete (New) ---
|
|
117
126
|
const linters = await MepCLI.multiSelect({
|
|
118
127
|
message: "Select linters to install (Type to search, Space to select):",
|
|
119
128
|
choices: [
|
|
@@ -128,14 +137,14 @@ async function runComprehensiveDemo() {
|
|
|
128
137
|
});
|
|
129
138
|
console.log(`\n✅ MultiSelect Result: Linters: [${linters.join(', ')}]`);
|
|
130
139
|
|
|
131
|
-
// ---
|
|
140
|
+
// --- 13. Confirm Prompt (Simple Yes/No) ---
|
|
132
141
|
const proceed = await MepCLI.confirm({
|
|
133
142
|
message: "Ready to deploy the project now?",
|
|
134
143
|
initial: true
|
|
135
144
|
});
|
|
136
145
|
console.log(`\n✅ Confirm Result: Deployment decision: ${proceed ? 'Proceed' : 'Cancel'}`);
|
|
137
146
|
|
|
138
|
-
// ---
|
|
147
|
+
// --- 14. Spin Utility (Loading/Async Task Indicator) ---
|
|
139
148
|
await MepCLI.spin(
|
|
140
149
|
"Finalizing configuration and deploying...",
|
|
141
150
|
new Promise(resolve => setTimeout(resolve, 1500)) // Simulates a 1.5 second async task
|