mepcli 0.5.5 → 0.6.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/README.md +173 -6
- package/dist/ansi.d.ts +1 -0
- package/dist/ansi.js +1 -0
- package/dist/base.d.ts +5 -34
- package/dist/base.js +65 -98
- package/dist/core.d.ts +23 -1
- package/dist/core.js +60 -0
- package/dist/highlight.d.ts +1 -0
- package/dist/highlight.js +40 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/input.js +26 -14
- package/dist/prompts/autocomplete.d.ts +1 -1
- package/dist/prompts/autocomplete.js +2 -7
- package/dist/prompts/calendar.d.ts +20 -0
- package/dist/prompts/calendar.js +329 -0
- package/dist/prompts/checkbox.d.ts +1 -1
- package/dist/prompts/checkbox.js +38 -8
- package/dist/prompts/code.d.ts +17 -0
- package/dist/prompts/code.js +210 -0
- package/dist/prompts/color.d.ts +14 -0
- package/dist/prompts/color.js +147 -0
- package/dist/prompts/confirm.d.ts +1 -1
- package/dist/prompts/confirm.js +1 -1
- package/dist/prompts/cron.d.ts +13 -0
- package/dist/prompts/cron.js +176 -0
- package/dist/prompts/date.d.ts +1 -1
- package/dist/prompts/date.js +15 -5
- package/dist/prompts/editor.js +2 -2
- package/dist/prompts/file.d.ts +7 -0
- package/dist/prompts/file.js +56 -60
- package/dist/prompts/form.d.ts +17 -0
- package/dist/prompts/form.js +225 -0
- package/dist/prompts/grid.d.ts +14 -0
- package/dist/prompts/grid.js +178 -0
- package/dist/prompts/keypress.d.ts +2 -2
- package/dist/prompts/keypress.js +2 -2
- package/dist/prompts/list.d.ts +1 -1
- package/dist/prompts/list.js +42 -22
- package/dist/prompts/multi-select.d.ts +1 -1
- package/dist/prompts/multi-select.js +39 -4
- package/dist/prompts/number.d.ts +1 -1
- package/dist/prompts/number.js +2 -2
- package/dist/prompts/range.d.ts +9 -0
- package/dist/prompts/range.js +140 -0
- package/dist/prompts/rating.d.ts +1 -1
- package/dist/prompts/rating.js +1 -1
- package/dist/prompts/select.d.ts +1 -1
- package/dist/prompts/select.js +1 -1
- package/dist/prompts/slider.d.ts +1 -1
- package/dist/prompts/slider.js +1 -1
- package/dist/prompts/snippet.d.ts +18 -0
- package/dist/prompts/snippet.js +203 -0
- package/dist/prompts/sort.d.ts +1 -1
- package/dist/prompts/sort.js +1 -4
- package/dist/prompts/spam.d.ts +17 -0
- package/dist/prompts/spam.js +62 -0
- package/dist/prompts/table.d.ts +1 -1
- package/dist/prompts/table.js +1 -1
- package/dist/prompts/text.d.ts +1 -0
- package/dist/prompts/text.js +13 -31
- package/dist/prompts/toggle.d.ts +1 -1
- package/dist/prompts/toggle.js +1 -1
- package/dist/prompts/transfer.d.ts +18 -0
- package/dist/prompts/transfer.js +203 -0
- package/dist/prompts/tree-select.d.ts +32 -0
- package/dist/prompts/tree-select.js +277 -0
- package/dist/prompts/tree.d.ts +3 -3
- package/dist/prompts/tree.js +27 -19
- package/dist/prompts/wait.d.ts +18 -0
- package/dist/prompts/wait.js +62 -0
- package/dist/types.d.ts +84 -0
- package/dist/utils.js +1 -1
- package/example.ts +150 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Mep
|
|
2
2
|
|
|
3
|
-
**Mep** is a
|
|
3
|
+
**Mep** is a lightweight and zero-dependency library for creating interactive command-line prompts in Node.js. It focuses on simplicity, modern design, and robust input handling, including support for cursor movement and input validation.
|
|
4
|
+
|
|
5
|
+
A **CodeTease** project.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
9
|
- **Zero Dependency:** Keeps your project clean and fast.
|
|
8
|
-
- **Comprehensive Prompts:** Includes `text`, `password`, `select`, `checkbox`, `confirm`, `number`, `toggle`, `list`, `slider`, `date`, `file`, `multiSelect`, `autocomplete`, `sort`, `table`, `rating`, `editor`, `tree`, and `
|
|
9
|
-
- **Mouse Support:** Built-in support for mouse interaction (SGR 1006 protocol). Scroll to navigate lists or change values
|
|
10
|
+
- **Comprehensive Prompts:** Includes `text`, `password`, `secret`, `select`, `checkbox`, `confirm`, `number`, `toggle`, `list`, `slider`, `range`, `date`, `file`, `multiSelect`, `autocomplete`, `sort`, `transfer`, `cron`, `table`, `rating`, `editor`, `tree`, `keypress`, `color`, `grid`, and `calendar`.
|
|
11
|
+
- **Mouse Support:** Built-in support for mouse interaction (SGR 1006 protocol). Scroll to navigate lists or change values.
|
|
10
12
|
- **Responsive Input:** Supports cursor movement (Left/Right) and character insertion/deletion in text-based prompts.
|
|
11
13
|
- **Validation:** Built-in support for input validation (sync and async) with custom error messages.
|
|
12
14
|
- **Elegant Look:** Uses ANSI colors for a clean, modern CLI experience.
|
|
@@ -104,13 +106,79 @@ async function main() {
|
|
|
104
106
|
]
|
|
105
107
|
});
|
|
106
108
|
|
|
109
|
+
// Color (RGB Picker)
|
|
110
|
+
const color = await MepCLI.color({
|
|
111
|
+
message: "Choose theme color:",
|
|
112
|
+
initial: "#3B82F6"
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Grid (Matrix Selection)
|
|
116
|
+
const permissions = await MepCLI.grid({
|
|
117
|
+
message: "Manage Permissions:",
|
|
118
|
+
rows: ["Admin", "User", "Guest"],
|
|
119
|
+
columns: ["Read", "Write", "Delete"]
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Calendar (Date/Range Picker)
|
|
123
|
+
const booking = await MepCLI.calendar({
|
|
124
|
+
message: "Select dates:",
|
|
125
|
+
mode: "range"
|
|
126
|
+
});
|
|
127
|
+
|
|
107
128
|
// Editor (External text editor)
|
|
108
129
|
const bio = await MepCLI.editor({
|
|
109
130
|
message: "Write your biography:",
|
|
110
131
|
extension: ".md"
|
|
111
132
|
});
|
|
112
133
|
|
|
113
|
-
|
|
134
|
+
// Form (Multi-field input)
|
|
135
|
+
const userDetails = await MepCLI.form({
|
|
136
|
+
message: "User Details:",
|
|
137
|
+
fields: [
|
|
138
|
+
{ name: "firstname", message: "First Name", initial: "John" },
|
|
139
|
+
{ name: "lastname", message: "Last Name", validate: (v) => v.length > 0 ? true : "Required" },
|
|
140
|
+
{ name: "email", message: "Email", validate: (v) => v.includes("@") || "Invalid email" }
|
|
141
|
+
]
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Snippet (Template filling)
|
|
145
|
+
const commitMsg = await MepCLI.snippet({
|
|
146
|
+
message: "Commit Message:",
|
|
147
|
+
template: "feat(${scope}): ${message}",
|
|
148
|
+
values: {
|
|
149
|
+
scope: "core"
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Code Prompt (JSON/YAML Editing)
|
|
154
|
+
const config = await MepCLI.code({
|
|
155
|
+
message: "Configure Server:",
|
|
156
|
+
language: "json",
|
|
157
|
+
template: `
|
|
158
|
+
{
|
|
159
|
+
"host": "\${host}",
|
|
160
|
+
"port": \${port},
|
|
161
|
+
"debug": \${debug}
|
|
162
|
+
}
|
|
163
|
+
`
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Tree Select (Hierarchical Multi-Select)
|
|
167
|
+
const selectedFiles = await MepCLI.treeSelect({
|
|
168
|
+
message: "Select files to backup:",
|
|
169
|
+
data: [
|
|
170
|
+
{
|
|
171
|
+
title: "src",
|
|
172
|
+
value: "src",
|
|
173
|
+
children: [
|
|
174
|
+
{ title: "index.ts", value: "src/index.ts" },
|
|
175
|
+
{ title: "utils.ts", value: "src/utils.ts" }
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
console.log({ name, age, newsletter, lang, tools, stars, city, priorities, user, color, permissions, booking, bio, userDetails, commitMsg, config, selectedFiles });
|
|
114
182
|
}
|
|
115
183
|
|
|
116
184
|
main();
|
|
@@ -122,6 +190,7 @@ main();
|
|
|
122
190
|
|
|
123
191
|
* `text(options)` - Single line or multiline text input.
|
|
124
192
|
* `password(options)` - Masked text input.
|
|
193
|
+
* `secret(options)` - Completely hidden text input.
|
|
125
194
|
* `number(options)` - Numeric input with increment/decrement support.
|
|
126
195
|
* `confirm(options)` - Yes/No question.
|
|
127
196
|
* `toggle(options)` - On/Off switch.
|
|
@@ -130,15 +199,27 @@ main();
|
|
|
130
199
|
* `checkbox(options)` - Classic checkbox selection.
|
|
131
200
|
* `list(options)` - Enter a list of tags/strings.
|
|
132
201
|
* `slider(options)` - Select a number within a range using a visual slider.
|
|
202
|
+
* `range(options)` - Select a numerical range (min/max) using a dual-handle slider.
|
|
133
203
|
* `rating(options)` - Star rating input.
|
|
134
204
|
* `date(options)` - Date and time picker.
|
|
205
|
+
* `color(options)` - RGB color picker with hex output and TrueColor preview.
|
|
206
|
+
* `grid(options)` - 2D matrix selection (rows x columns).
|
|
207
|
+
* `calendar(options)` - Interactive monthly calendar for single dates or ranges.
|
|
135
208
|
* `file(options)` - File system navigator and selector.
|
|
136
209
|
* `autocomplete(options)` - Searchable selection with async suggestions.
|
|
137
210
|
* `sort(options)` - Reorder a list of items.
|
|
211
|
+
* `transfer(options)` - Move items between two lists (Source/Target).
|
|
212
|
+
* `cron(options)` - Visually build a cron schedule (Minute, Hour, Day, Month, Weekday).
|
|
138
213
|
* `table(options)` - Display data in columns and select rows.
|
|
139
214
|
* `tree(options)` - Navigate and select from a hierarchical tree structure.
|
|
140
215
|
* `keypress(options)` - Wait for a specific key press or any key.
|
|
141
216
|
* `editor(options)` - Launch an external editor (Vim, Nano, Notepad, etc.) to capture multi-line content.
|
|
217
|
+
* `form(options)` - Multi-field input form with navigation.
|
|
218
|
+
* `snippet(options)` - Template string filling with variable navigation.
|
|
219
|
+
* `code(options)` - Edit variables within a code block (JSON/YAML). Syntax highlighting is supported (Experimental).
|
|
220
|
+
* `treeSelect(options)` - Hierarchical multi-selection with cascading checkboxes.
|
|
221
|
+
* `spam(options)` - Confirm a dangerous action or fun.
|
|
222
|
+
* `wait(options)` - Wait for a specified number of seconds.
|
|
142
223
|
* `spinner(message)` - Returns a `Spinner` instance for manual control (`start`, `stop`, `update`, `success`, `error`).
|
|
143
224
|
|
|
144
225
|
## Mouse Support
|
|
@@ -146,14 +227,100 @@ main();
|
|
|
146
227
|
MepCLI automatically detects modern terminals and enables **Mouse Tracking** (using SGR 1006 protocol).
|
|
147
228
|
|
|
148
229
|
* **Scrolling:**
|
|
149
|
-
* `select`, `multiSelect`, `checkbox`, `autocomplete`, `table`, `tree`: Scroll to navigate the list.
|
|
150
|
-
* `
|
|
230
|
+
* `select`, `multiSelect`, `checkbox`, `autocomplete`, `table`, `tree`, `transfer`: Scroll to navigate the list.
|
|
231
|
+
* `form`, `snippet`, `cron`: Scroll to navigate between fields or values.
|
|
232
|
+
* `number`, `slider`, `range`, `rating`, `date`: Scroll to increment/decrement values or fields.
|
|
151
233
|
* `sort`: Scroll to navigate or reorder items (when grabbed).
|
|
152
234
|
* `toggle`, `confirm`: Scroll to toggle the state.
|
|
235
|
+
* `calendar`: Scroll to switch months.
|
|
236
|
+
* `color`: Scroll to adjust RGB channels.
|
|
237
|
+
* `grid`: Scroll to move selection.
|
|
153
238
|
* **Configuration:**
|
|
154
239
|
* Mouse support is enabled by default if the terminal supports it.
|
|
155
240
|
* You can explicitly disable it per prompt by setting `mouse: false` in the options.
|
|
156
241
|
|
|
242
|
+
## Advanced Shortcuts
|
|
243
|
+
|
|
244
|
+
### Calendar Prompt
|
|
245
|
+
|
|
246
|
+
Mep's Calendar prompt supports advanced navigation and selection shortcuts for power users.
|
|
247
|
+
|
|
248
|
+
* **Keyboard:**
|
|
249
|
+
* `Arrow Keys`: Move cursor day by day.
|
|
250
|
+
* `PageUp` / `PageDown`: Jump to previous/next **Month**.
|
|
251
|
+
* `Ctrl + Up` / `Ctrl + Down`: Jump to previous/next **Year**.
|
|
252
|
+
* `Home` / `End`: Jump to the first/last day of the current month.
|
|
253
|
+
* `t`: Jump immediately to **Today**.
|
|
254
|
+
* `Enter`: Select date (or start/end of range).
|
|
255
|
+
|
|
256
|
+
* **Mouse:**
|
|
257
|
+
* `Scroll`: Navigate **Months**.
|
|
258
|
+
* `Ctrl + Scroll`: Adjust the selected **Day** (cursor movement).
|
|
259
|
+
|
|
260
|
+
### Color Prompt
|
|
261
|
+
|
|
262
|
+
* **Keyboard:**
|
|
263
|
+
* `Tab`: Switch between RGB channels.
|
|
264
|
+
* `Up` / `Down`: Move between channels.
|
|
265
|
+
* `Left` / `Right`: Adjust current channel value.
|
|
266
|
+
* `Shift + Left` / `Shift + Right`: Fast adjust current channel value.
|
|
267
|
+
|
|
268
|
+
* **Mouse:**
|
|
269
|
+
* `Scroll`: Adjust the current channel value.
|
|
270
|
+
* `Ctrl + Scroll`: Fast adjust.
|
|
271
|
+
|
|
272
|
+
### Checkbox Prompt
|
|
273
|
+
|
|
274
|
+
* **Keyboard:**
|
|
275
|
+
* `Space`: Toggle selection.
|
|
276
|
+
* `a`: Select **All**.
|
|
277
|
+
* `x` / `n`: Select **None**.
|
|
278
|
+
* `i`: **Invert** selection.
|
|
279
|
+
|
|
280
|
+
### MultiSelect Prompt
|
|
281
|
+
|
|
282
|
+
* **Keyboard:**
|
|
283
|
+
* `Space`: Toggle selection.
|
|
284
|
+
* `Ctrl + A`: Select **All** (Visible).
|
|
285
|
+
* `Ctrl + X`: Deselect **All** (Visible).
|
|
286
|
+
* `Typing`: Filter list.
|
|
287
|
+
|
|
288
|
+
### Transfer Prompt
|
|
289
|
+
|
|
290
|
+
* **Keyboard:**
|
|
291
|
+
* `Tab` / `Left` / `Right`: Switch focus between Source and Target.
|
|
292
|
+
* `Space`: Move selected item.
|
|
293
|
+
* `a` / `>`: Move **All** to Target.
|
|
294
|
+
* `r` / `<`: Move **All** to Source (Reset).
|
|
295
|
+
|
|
296
|
+
### Tree & TreeSelect Prompt
|
|
297
|
+
|
|
298
|
+
* **Keyboard:**
|
|
299
|
+
* `Right`: Expand folder or jump to child.
|
|
300
|
+
* `Left`: Collapse folder or jump to parent.
|
|
301
|
+
* `Space`: Toggle expansion (Tree) or Checkbox (TreeSelect).
|
|
302
|
+
* `e`: **Expand** all recursively.
|
|
303
|
+
* `c`: **Collapse** all recursively.
|
|
304
|
+
|
|
305
|
+
### Grid Prompt
|
|
306
|
+
|
|
307
|
+
The Grid prompt (Matrix selection) includes robust shortcuts for bulk actions.
|
|
308
|
+
|
|
309
|
+
* **Keyboard:**
|
|
310
|
+
* `Arrow Keys`: Move cursor.
|
|
311
|
+
* `PageUp` / `PageDown`: Jump to the first/last **Row**.
|
|
312
|
+
* `Home` / `End`: Jump to the first/last **Column**.
|
|
313
|
+
* `Space`: Toggle current cell.
|
|
314
|
+
* `r`: Toggle entire **Row**.
|
|
315
|
+
* `c`: Toggle entire **Column**.
|
|
316
|
+
* `a`: Select **All**.
|
|
317
|
+
* `x`: Deselect **All** (None).
|
|
318
|
+
* `i`: **Invert** selection.
|
|
319
|
+
|
|
320
|
+
* **Mouse:**
|
|
321
|
+
* `Scroll`: Vertical navigation (Rows).
|
|
322
|
+
* `Shift + Scroll`: Horizontal navigation (Columns).
|
|
323
|
+
|
|
157
324
|
## License
|
|
158
325
|
|
|
159
326
|
This project is under the **MIT License**.
|
package/dist/ansi.d.ts
CHANGED
package/dist/ansi.js
CHANGED
package/dist/base.d.ts
CHANGED
|
@@ -2,8 +2,7 @@ import { MouseEvent } from './types';
|
|
|
2
2
|
import { detectCapabilities } from './utils';
|
|
3
3
|
/**
|
|
4
4
|
* Abstract base class for all prompts.
|
|
5
|
-
*
|
|
6
|
-
* to enforce DRY (Don't Repeat Yourself) principles.
|
|
5
|
+
* Implements a Robust Linear Scan Diffing Engine.
|
|
7
6
|
*/
|
|
8
7
|
export declare abstract class Prompt<T, O> {
|
|
9
8
|
protected options: O;
|
|
@@ -15,48 +14,20 @@ export declare abstract class Prompt<T, O> {
|
|
|
15
14
|
private _inputParser;
|
|
16
15
|
private _onKeyHandler?;
|
|
17
16
|
private _onDataHandler?;
|
|
18
|
-
protected lastRenderHeight: number;
|
|
19
17
|
protected lastRenderLines: string[];
|
|
18
|
+
protected lastRenderHeight: number;
|
|
20
19
|
protected capabilities: ReturnType<typeof detectCapabilities>;
|
|
21
20
|
constructor(options: O);
|
|
22
|
-
/**
|
|
23
|
-
* Renders the UI. Must be implemented by subclasses.
|
|
24
|
-
* @param firstRender Indicates if this is the initial render.
|
|
25
|
-
*/
|
|
26
21
|
protected abstract render(firstRender: boolean): void;
|
|
27
|
-
/**
|
|
28
|
-
* Handles specific key inputs. Must be implemented by subclasses.
|
|
29
|
-
* @param char The string representation of the key.
|
|
30
|
-
* @param key The raw buffer.
|
|
31
|
-
*/
|
|
32
22
|
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;
|
|
23
|
+
protected handleMouse(_event: MouseEvent): void;
|
|
38
24
|
protected print(text: string): void;
|
|
39
|
-
/**
|
|
40
|
-
* Starts the prompt interaction.
|
|
41
|
-
* Sets up raw mode and listeners, returning a Promise.
|
|
42
|
-
*/
|
|
43
25
|
run(): Promise<T>;
|
|
44
|
-
/**
|
|
45
|
-
* Cleans up listeners and restores stdin state.
|
|
46
|
-
*/
|
|
47
26
|
protected cleanup(): void;
|
|
48
|
-
/**
|
|
49
|
-
* Submits the final value and resolves the promise.
|
|
50
|
-
*/
|
|
51
27
|
protected submit(result: T): void;
|
|
52
28
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*/
|
|
56
|
-
/**
|
|
57
|
-
* Renders the current frame by clearing the previous output and writing the new content.
|
|
58
|
-
* This approach ("Move Up -> Erase Down -> Print") is more robust against artifacts
|
|
59
|
-
* than line-by-line diffing, especially when the number of lines changes (e.g., filtering).
|
|
29
|
+
* Renders the frame using a linear scan diffing algorithm.
|
|
30
|
+
* Prevents flicker and handles height changes (expand/collapse) robustly.
|
|
60
31
|
*/
|
|
61
32
|
protected renderFrame(content: string): void;
|
|
62
33
|
protected stripAnsi(str: string): string;
|
package/dist/base.js
CHANGED
|
@@ -6,32 +6,22 @@ const input_1 = require("./input");
|
|
|
6
6
|
const utils_1 = require("./utils");
|
|
7
7
|
/**
|
|
8
8
|
* Abstract base class for all prompts.
|
|
9
|
-
*
|
|
10
|
-
* to enforce DRY (Don't Repeat Yourself) principles.
|
|
9
|
+
* Implements a Robust Linear Scan Diffing Engine.
|
|
11
10
|
*/
|
|
12
11
|
class Prompt {
|
|
13
12
|
constructor(options) {
|
|
14
|
-
// Smart Cursor State
|
|
15
|
-
this.lastRenderHeight = 0;
|
|
16
13
|
this.lastRenderLines = [];
|
|
14
|
+
this.lastRenderHeight = 0;
|
|
17
15
|
this.options = options;
|
|
18
16
|
this.stdin = process.stdin;
|
|
19
17
|
this.stdout = process.stdout;
|
|
20
18
|
this._inputParser = new input_1.InputParser();
|
|
21
19
|
this.capabilities = (0, utils_1.detectCapabilities)();
|
|
22
20
|
}
|
|
23
|
-
|
|
24
|
-
* Optional method to handle mouse events.
|
|
25
|
-
* Subclasses can override this to implement mouse interaction.
|
|
26
|
-
*/
|
|
27
|
-
handleMouse(event) { }
|
|
21
|
+
handleMouse(_event) { }
|
|
28
22
|
print(text) {
|
|
29
23
|
this.stdout.write(text);
|
|
30
24
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Starts the prompt interaction.
|
|
33
|
-
* Sets up raw mode and listeners, returning a Promise.
|
|
34
|
-
*/
|
|
35
25
|
run() {
|
|
36
26
|
return new Promise((resolve, reject) => {
|
|
37
27
|
this._resolve = resolve;
|
|
@@ -41,24 +31,15 @@ class Prompt {
|
|
|
41
31
|
}
|
|
42
32
|
this.stdin.resume();
|
|
43
33
|
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
34
|
const shouldEnableMouse = this.options.mouse !== false && this.capabilities.hasMouse;
|
|
47
35
|
if (shouldEnableMouse) {
|
|
48
36
|
this.print(ansi_1.ANSI.SET_ANY_EVENT_MOUSE + ansi_1.ANSI.SET_SGR_EXT_MODE_MOUSE);
|
|
49
37
|
}
|
|
50
|
-
// Initial render: Default to hidden cursor (good for menus)
|
|
51
|
-
// Subclasses like TextPrompt will explicitly show it if needed.
|
|
52
|
-
if (this.capabilities.isCI) {
|
|
53
|
-
// In CI, maybe don't hide cursor or do nothing?
|
|
54
|
-
// But for now follow standard flow.
|
|
55
|
-
}
|
|
56
38
|
this.print(ansi_1.ANSI.HIDE_CURSOR);
|
|
39
|
+
// Initial render
|
|
57
40
|
this.render(true);
|
|
58
|
-
// Setup Input Parser Listeners
|
|
59
41
|
this._onKeyHandler = (char, buffer) => {
|
|
60
|
-
|
|
61
|
-
if (char === '\u0003') {
|
|
42
|
+
if (char === '\u0003') { // Ctrl+C
|
|
62
43
|
this.cleanup();
|
|
63
44
|
this.print(ansi_1.ANSI.SHOW_CURSOR + '\n');
|
|
64
45
|
if (this._reject)
|
|
@@ -68,7 +49,6 @@ class Prompt {
|
|
|
68
49
|
this.handleInput(char, buffer);
|
|
69
50
|
};
|
|
70
51
|
this._inputParser.on('keypress', this._onKeyHandler);
|
|
71
|
-
// Listen to mouse events
|
|
72
52
|
this._inputParser.on('mouse', (event) => {
|
|
73
53
|
this.handleMouse(event);
|
|
74
54
|
});
|
|
@@ -78,9 +58,6 @@ class Prompt {
|
|
|
78
58
|
this.stdin.on('data', this._onDataHandler);
|
|
79
59
|
});
|
|
80
60
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Cleans up listeners and restores stdin state.
|
|
83
|
-
*/
|
|
84
61
|
cleanup() {
|
|
85
62
|
if (this._onDataHandler) {
|
|
86
63
|
this.stdin.removeListener('data', this._onDataHandler);
|
|
@@ -88,8 +65,6 @@ class Prompt {
|
|
|
88
65
|
if (this._onKeyHandler) {
|
|
89
66
|
this._inputParser.removeListener('keypress', this._onKeyHandler);
|
|
90
67
|
}
|
|
91
|
-
// Cleanup mouse listener - though InputParser is instance specific, so it's fine.
|
|
92
|
-
// Disable Mouse Tracking
|
|
93
68
|
this.print(ansi_1.ANSI.DISABLE_MOUSE);
|
|
94
69
|
if (typeof this.stdin.setRawMode === 'function') {
|
|
95
70
|
this.stdin.setRawMode(false);
|
|
@@ -97,51 +72,74 @@ class Prompt {
|
|
|
97
72
|
this.stdin.pause();
|
|
98
73
|
this.print(ansi_1.ANSI.SHOW_CURSOR);
|
|
99
74
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Submits the final value and resolves the promise.
|
|
102
|
-
*/
|
|
103
75
|
submit(result) {
|
|
104
76
|
this.cleanup();
|
|
105
77
|
this.print('\n');
|
|
106
78
|
if (this._resolve)
|
|
107
79
|
this._resolve(result);
|
|
108
80
|
}
|
|
109
|
-
// --- Rendering Utilities ---
|
|
110
|
-
/**
|
|
111
|
-
* Render Method with Diffing (Virtual DOM for CLI).
|
|
112
|
-
* Calculates new lines, compares with old lines, and updates only changed parts.
|
|
113
|
-
*/
|
|
114
81
|
/**
|
|
115
|
-
* Renders the
|
|
116
|
-
*
|
|
117
|
-
* than line-by-line diffing, especially when the number of lines changes (e.g., filtering).
|
|
82
|
+
* Renders the frame using a linear scan diffing algorithm.
|
|
83
|
+
* Prevents flicker and handles height changes (expand/collapse) robustly.
|
|
118
84
|
*/
|
|
119
85
|
renderFrame(content) {
|
|
120
86
|
const width = this.stdout.columns || 80;
|
|
121
87
|
const rawLines = content.split('\n');
|
|
122
|
-
// Truncate
|
|
88
|
+
// Truncate lines to prevent wrapping artifacts
|
|
123
89
|
const newLines = rawLines.map(line => this.truncate(line, width));
|
|
124
|
-
// 1.
|
|
125
|
-
this.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
90
|
+
// 1. First Render Case
|
|
91
|
+
if (this.lastRenderLines.length === 0) {
|
|
92
|
+
this.print(newLines.join('\n'));
|
|
93
|
+
this.lastRenderLines = newLines;
|
|
94
|
+
this.lastRenderHeight = newLines.length;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let outputBuffer = '';
|
|
98
|
+
// 2. Return Cursor to the Top of the Prompt
|
|
99
|
+
if (this.lastRenderHeight > 1) {
|
|
100
|
+
outputBuffer += `\x1b[${this.lastRenderHeight - 1}A`;
|
|
132
101
|
}
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
this.print(ansi_1.ANSI.ERASE_DOWN);
|
|
136
|
-
// 4. Print the new frame content
|
|
102
|
+
outputBuffer += '\r'; // Ensure column 0
|
|
103
|
+
// 3. Linear Scan & Update
|
|
137
104
|
for (let i = 0; i < newLines.length; i++) {
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
if (i
|
|
141
|
-
this.
|
|
105
|
+
const newLine = newLines[i];
|
|
106
|
+
// Logic for moving to the next line
|
|
107
|
+
if (i > 0) {
|
|
108
|
+
if (i < this.lastRenderLines.length) {
|
|
109
|
+
// Moving within the previously existing area.
|
|
110
|
+
// Use 'Down' (B) to avoid scrolling/shifting existing content.
|
|
111
|
+
outputBuffer += '\x1b[B\r';
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Moving into NEW area (Append).
|
|
115
|
+
// Must use '\n' to create the new line.
|
|
116
|
+
outputBuffer += '\n';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Printing logic
|
|
120
|
+
if (i < this.lastRenderLines.length) {
|
|
121
|
+
const oldLine = this.lastRenderLines[i];
|
|
122
|
+
if (newLine !== oldLine) {
|
|
123
|
+
// Overwrite existing line
|
|
124
|
+
outputBuffer += ansi_1.ANSI.ERASE_LINE + newLine;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// Print new line (we are already at start due to '\n' above)
|
|
129
|
+
outputBuffer += newLine;
|
|
142
130
|
}
|
|
143
131
|
}
|
|
144
|
-
//
|
|
132
|
+
// 4. Handle Shrinkage (Clear garbage below)
|
|
133
|
+
if (newLines.length < this.lastRenderLines.length) {
|
|
134
|
+
// Move down to the first obsolete line
|
|
135
|
+
outputBuffer += '\n';
|
|
136
|
+
// Clear everything below
|
|
137
|
+
outputBuffer += ansi_1.ANSI.ERASE_DOWN;
|
|
138
|
+
// Move back up to the last valid line to maintain cursor state consistency
|
|
139
|
+
outputBuffer += `\x1b[A`;
|
|
140
|
+
}
|
|
141
|
+
this.print(outputBuffer);
|
|
142
|
+
// Update State
|
|
145
143
|
this.lastRenderLines = newLines;
|
|
146
144
|
this.lastRenderHeight = newLines.length;
|
|
147
145
|
}
|
|
@@ -153,59 +151,28 @@ class Prompt {
|
|
|
153
151
|
if (visualWidth <= width) {
|
|
154
152
|
return str;
|
|
155
153
|
}
|
|
156
|
-
// Heuristic truncation using stringWidth
|
|
157
|
-
// We iterate and sum width until we hit limit - 3
|
|
158
154
|
let currentWidth = 0;
|
|
159
155
|
let cutIndex = 0;
|
|
160
156
|
let inAnsi = false;
|
|
161
157
|
for (let i = 0; i < str.length; i++) {
|
|
162
|
-
|
|
163
|
-
if (str[i] === '\x1b') {
|
|
158
|
+
if (str[i] === '\x1b')
|
|
164
159
|
inAnsi = true;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
inAnsi = false;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
// Width check
|
|
173
|
-
// Handle surrogates roughly (we don't need perfect width here during loop, just enough to stop)
|
|
174
|
-
// But wait, we imported `stringWidth`.
|
|
175
|
-
// We can't easily use `stringWidth` incrementally without re-parsing.
|
|
176
|
-
// Let's just trust the loop for cut index.
|
|
177
|
-
// Re-implement basic width logic here for the cut index finding
|
|
178
|
-
let charWidth = 1;
|
|
179
|
-
let cp = code;
|
|
180
|
-
if (code >= 0xD800 && code <= 0xDBFF && i + 1 < str.length) {
|
|
181
|
-
const next = str.charCodeAt(i + 1);
|
|
182
|
-
if (next >= 0xDC00 && next <= 0xDFFF) {
|
|
183
|
-
cp = (code - 0xD800) * 0x400 + (next - 0xDC00) + 0x10000;
|
|
184
|
-
// i is incremented in main loop but we need to skip next char
|
|
185
|
-
// We'll handle i increment in the loop
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// Check range (simplified or call helper)
|
|
189
|
-
// We don't have isWideCodePoint exported.
|
|
190
|
-
// But generally, we can just say:
|
|
191
|
-
if (cp >= 0x1100) { // Quick check for potentially wide
|
|
192
|
-
// It's acceptable to be slightly aggressive on wide chars for truncation
|
|
193
|
-
charWidth = 2;
|
|
194
|
-
}
|
|
160
|
+
if (!inAnsi) {
|
|
161
|
+
const code = str.charCodeAt(i);
|
|
162
|
+
const charWidth = code > 255 ? 2 : 1;
|
|
195
163
|
if (currentWidth + charWidth > width - 3) {
|
|
196
|
-
cutIndex = i;
|
|
197
164
|
break;
|
|
198
165
|
}
|
|
199
166
|
currentWidth += charWidth;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
if (str[i] === 'm' || (str[i] >= 'A' && str[i] <= 'Z'))
|
|
170
|
+
inAnsi = false;
|
|
203
171
|
}
|
|
204
172
|
cutIndex = i + 1;
|
|
205
173
|
}
|
|
206
174
|
return str.substring(0, cutIndex) + '...' + ansi_1.ANSI.RESET;
|
|
207
175
|
}
|
|
208
|
-
// Helper to check for arrow keys including application mode
|
|
209
176
|
isUp(char) {
|
|
210
177
|
return char === '\u001b[A' || char === '\u001bOA';
|
|
211
178
|
}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TextOptions, SelectOptions, ConfirmOptions, CheckboxOptions, ThemeConfig, NumberOptions, ToggleOptions, ListOptions, SliderOptions, DateOptions, FileOptions, MultiSelectOptions, RatingOptions, AutocompleteOptions, SortOptions, TableOptions, EditorOptions, TreeOptions, KeypressOptions } from './types';
|
|
1
|
+
import { TextOptions, SelectOptions, ConfirmOptions, CheckboxOptions, ThemeConfig, NumberOptions, ToggleOptions, ListOptions, SliderOptions, DateOptions, FileOptions, MultiSelectOptions, RatingOptions, AutocompleteOptions, SortOptions, TableOptions, EditorOptions, TreeOptions, KeypressOptions, FormOptions, SnippetOptions, SpamOptions, WaitOptions, CodeOptions, TreeSelectOptions, RangeOptions, TransferOptions, CronOptions, ColorOptions, GridOptions, CalendarOptions } from './types';
|
|
2
2
|
import { Spinner } from './spinner';
|
|
3
3
|
/**
|
|
4
4
|
* Public Facade for MepCLI
|
|
@@ -14,10 +14,14 @@ export declare class MepCLI {
|
|
|
14
14
|
static checkbox<const V>(options: CheckboxOptions<V>): Promise<V[]>;
|
|
15
15
|
static confirm(options: ConfirmOptions): Promise<boolean>;
|
|
16
16
|
static password(options: TextOptions): Promise<string>;
|
|
17
|
+
static secret(options: TextOptions): Promise<string>;
|
|
17
18
|
static number(options: NumberOptions): Promise<number>;
|
|
18
19
|
static toggle(options: ToggleOptions): Promise<boolean>;
|
|
19
20
|
static list(options: ListOptions): Promise<string[]>;
|
|
20
21
|
static slider(options: SliderOptions): Promise<number>;
|
|
22
|
+
static range(options: RangeOptions): Promise<[number, number]>;
|
|
23
|
+
static transfer<const V>(options: TransferOptions<V>): Promise<[V[], V[]]>;
|
|
24
|
+
static cron(options: CronOptions): Promise<string>;
|
|
21
25
|
static date(options: DateOptions): Promise<Date>;
|
|
22
26
|
static file(options: FileOptions): Promise<string>;
|
|
23
27
|
static multiSelect<const V>(options: MultiSelectOptions<V>): Promise<V[]>;
|
|
@@ -28,4 +32,22 @@ export declare class MepCLI {
|
|
|
28
32
|
static editor(options: EditorOptions): Promise<string>;
|
|
29
33
|
static tree<const V>(options: TreeOptions<V>): Promise<V>;
|
|
30
34
|
static keypress(options: KeypressOptions): Promise<string>;
|
|
35
|
+
static form(options: FormOptions): Promise<Record<string, string>>;
|
|
36
|
+
static snippet(options: SnippetOptions): Promise<string>;
|
|
37
|
+
static spam(options: SpamOptions): Promise<boolean>;
|
|
38
|
+
static wait(options: WaitOptions): Promise<void>;
|
|
39
|
+
static code(options: CodeOptions): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Tree Select Prompt (Multi-selection)
|
|
42
|
+
* * @param options Configuration for the tree selection
|
|
43
|
+
* @returns A promise that resolves to an array of selected values
|
|
44
|
+
* * @notice Windows Compatibility:
|
|
45
|
+
* When used in a long sequence of prompts, this component may experience
|
|
46
|
+
* an input delay. If it feels "blocked", simply press 'Enter' once
|
|
47
|
+
* to refresh the TTY stream.
|
|
48
|
+
*/
|
|
49
|
+
static treeSelect<const V>(options: TreeSelectOptions<V>): Promise<V[]>;
|
|
50
|
+
static color(options: ColorOptions): Promise<string>;
|
|
51
|
+
static grid(options: GridOptions): Promise<boolean[][]>;
|
|
52
|
+
static calendar(options: CalendarOptions): Promise<Date | [Date, Date]>;
|
|
31
53
|
}
|