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.
Files changed (75) hide show
  1. package/README.md +173 -6
  2. package/dist/ansi.d.ts +1 -0
  3. package/dist/ansi.js +1 -0
  4. package/dist/base.d.ts +5 -34
  5. package/dist/base.js +65 -98
  6. package/dist/core.d.ts +23 -1
  7. package/dist/core.js +60 -0
  8. package/dist/highlight.d.ts +1 -0
  9. package/dist/highlight.js +40 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/input.js +26 -14
  13. package/dist/prompts/autocomplete.d.ts +1 -1
  14. package/dist/prompts/autocomplete.js +2 -7
  15. package/dist/prompts/calendar.d.ts +20 -0
  16. package/dist/prompts/calendar.js +329 -0
  17. package/dist/prompts/checkbox.d.ts +1 -1
  18. package/dist/prompts/checkbox.js +38 -8
  19. package/dist/prompts/code.d.ts +17 -0
  20. package/dist/prompts/code.js +210 -0
  21. package/dist/prompts/color.d.ts +14 -0
  22. package/dist/prompts/color.js +147 -0
  23. package/dist/prompts/confirm.d.ts +1 -1
  24. package/dist/prompts/confirm.js +1 -1
  25. package/dist/prompts/cron.d.ts +13 -0
  26. package/dist/prompts/cron.js +176 -0
  27. package/dist/prompts/date.d.ts +1 -1
  28. package/dist/prompts/date.js +15 -5
  29. package/dist/prompts/editor.js +2 -2
  30. package/dist/prompts/file.d.ts +7 -0
  31. package/dist/prompts/file.js +56 -60
  32. package/dist/prompts/form.d.ts +17 -0
  33. package/dist/prompts/form.js +225 -0
  34. package/dist/prompts/grid.d.ts +14 -0
  35. package/dist/prompts/grid.js +178 -0
  36. package/dist/prompts/keypress.d.ts +2 -2
  37. package/dist/prompts/keypress.js +2 -2
  38. package/dist/prompts/list.d.ts +1 -1
  39. package/dist/prompts/list.js +42 -22
  40. package/dist/prompts/multi-select.d.ts +1 -1
  41. package/dist/prompts/multi-select.js +39 -4
  42. package/dist/prompts/number.d.ts +1 -1
  43. package/dist/prompts/number.js +2 -2
  44. package/dist/prompts/range.d.ts +9 -0
  45. package/dist/prompts/range.js +140 -0
  46. package/dist/prompts/rating.d.ts +1 -1
  47. package/dist/prompts/rating.js +1 -1
  48. package/dist/prompts/select.d.ts +1 -1
  49. package/dist/prompts/select.js +1 -1
  50. package/dist/prompts/slider.d.ts +1 -1
  51. package/dist/prompts/slider.js +1 -1
  52. package/dist/prompts/snippet.d.ts +18 -0
  53. package/dist/prompts/snippet.js +203 -0
  54. package/dist/prompts/sort.d.ts +1 -1
  55. package/dist/prompts/sort.js +1 -4
  56. package/dist/prompts/spam.d.ts +17 -0
  57. package/dist/prompts/spam.js +62 -0
  58. package/dist/prompts/table.d.ts +1 -1
  59. package/dist/prompts/table.js +1 -1
  60. package/dist/prompts/text.d.ts +1 -0
  61. package/dist/prompts/text.js +13 -31
  62. package/dist/prompts/toggle.d.ts +1 -1
  63. package/dist/prompts/toggle.js +1 -1
  64. package/dist/prompts/transfer.d.ts +18 -0
  65. package/dist/prompts/transfer.js +203 -0
  66. package/dist/prompts/tree-select.d.ts +32 -0
  67. package/dist/prompts/tree-select.js +277 -0
  68. package/dist/prompts/tree.d.ts +3 -3
  69. package/dist/prompts/tree.js +27 -19
  70. package/dist/prompts/wait.d.ts +18 -0
  71. package/dist/prompts/wait.js +62 -0
  72. package/dist/types.d.ts +84 -0
  73. package/dist/utils.js +1 -1
  74. package/example.ts +150 -15
  75. package/package.json +2 -2
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # Mep
2
2
 
3
- **Mep** is a minimalist 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.
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 `keypress`.
9
- - **Mouse Support:** Built-in support for mouse interaction (SGR 1006 protocol). Scroll to navigate lists or change values; click to select.
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
- console.log({ name, age, newsletter, lang, tools, stars, city, priorities, user, bio });
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
- * `number`, `slider`, `rating`, `date`: Scroll to increment/decrement values or fields.
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
@@ -8,6 +8,7 @@ export declare const ANSI: {
8
8
  DIM: string;
9
9
  ITALIC: string;
10
10
  UNDERLINE: string;
11
+ REVERSE: string;
11
12
  FG_GREEN: string;
12
13
  FG_CYAN: string;
13
14
  FG_YELLOW: string;
package/dist/ansi.js CHANGED
@@ -11,6 +11,7 @@ exports.ANSI = {
11
11
  DIM: '\x1b[2m',
12
12
  ITALIC: '\x1b[3m',
13
13
  UNDERLINE: '\x1b[4m',
14
+ REVERSE: '\x1b[7m',
14
15
  // Colors
15
16
  FG_GREEN: '\x1b[32m',
16
17
  FG_CYAN: '\x1b[36m',
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
- * Handles common logic like stdin management, raw mode, and cleanup
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
- * Render Method with Diffing (Virtual DOM for CLI).
54
- * Calculates new lines, compares with old lines, and updates only changed parts.
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
- * Handles common logic like stdin management, raw mode, and cleanup
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
- // Global Exit Handler (Ctrl+C)
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 current frame by clearing the previous output and writing the new content.
116
- * This approach ("Move Up -> Erase Down -> Print") is more robust against artifacts
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 each line to fit terminal width to avoid wrapping issues
88
+ // Truncate lines to prevent wrapping artifacts
123
89
  const newLines = rawLines.map(line => this.truncate(line, width));
124
- // 1. Move cursor to the start of the current line
125
- this.print(ansi_1.ANSI.CURSOR_LEFT);
126
- // 2. Move cursor up to the top of the previously rendered frame
127
- if (this.lastRenderHeight > 0) {
128
- // If the previous render had multiple lines, move up to the first line
129
- if (this.lastRenderHeight > 1) {
130
- this.print(`\x1b[${this.lastRenderHeight - 1}A`);
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
- // 3. Clear everything from the cursor down
134
- // This ensures all previous content (including "ghost" lines) is removed
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
- this.print(newLines[i]);
139
- // Add newline character between lines, but not after the last line
140
- if (i < newLines.length - 1) {
141
- this.print('\n');
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
- // 5. Update state for the next render cycle
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
- const code = str.charCodeAt(i);
163
- if (str[i] === '\x1b') {
158
+ if (str[i] === '\x1b')
164
159
  inAnsi = true;
165
- }
166
- if (inAnsi) {
167
- if ((str[i] >= '@' && str[i] <= '~') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z')) {
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
- if (cp > 0xFFFF) {
201
- i++; // Skip low surrogate
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
  }