mepcli 0.6.1 → 1.0.0-beta.2

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 (113) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +512 -326
  3. package/dist/ansi.d.ts +8 -0
  4. package/dist/ansi.js +8 -0
  5. package/dist/base.d.ts +21 -0
  6. package/dist/base.js +57 -0
  7. package/dist/core.d.ts +48 -1
  8. package/dist/core.js +156 -0
  9. package/dist/data/licenses.d.ts +2 -0
  10. package/dist/data/licenses.js +109 -0
  11. package/dist/highlight.js +58 -26
  12. package/dist/index.d.ts +36 -0
  13. package/dist/index.js +36 -0
  14. package/dist/input.js +0 -3
  15. package/dist/prompts/box.d.ts +21 -0
  16. package/dist/prompts/box.js +192 -0
  17. package/dist/prompts/breadcrumb.d.ts +22 -0
  18. package/dist/prompts/breadcrumb.js +302 -0
  19. package/dist/prompts/byte.d.ts +13 -0
  20. package/dist/prompts/byte.js +159 -0
  21. package/dist/prompts/calculator.d.ts +16 -0
  22. package/dist/prompts/calculator.js +213 -0
  23. package/dist/prompts/calendar.js +0 -5
  24. package/dist/prompts/code.d.ts +2 -0
  25. package/dist/prompts/code.js +104 -70
  26. package/dist/prompts/connection-string.d.ts +18 -0
  27. package/dist/prompts/connection-string.js +97 -0
  28. package/dist/prompts/curl.d.ts +39 -0
  29. package/dist/prompts/curl.js +285 -0
  30. package/dist/prompts/data-inspector.d.ts +22 -0
  31. package/dist/prompts/data-inspector.js +256 -0
  32. package/dist/prompts/dependency.d.ts +16 -0
  33. package/dist/prompts/dependency.js +265 -0
  34. package/dist/prompts/dial.d.ts +10 -0
  35. package/dist/prompts/dial.js +110 -0
  36. package/dist/prompts/diff.d.ts +10 -0
  37. package/dist/prompts/diff.js +101 -0
  38. package/dist/prompts/draw.d.ts +20 -0
  39. package/dist/prompts/draw.js +188 -0
  40. package/dist/prompts/editor.js +0 -4
  41. package/dist/prompts/emoji.d.ts +18 -0
  42. package/dist/prompts/emoji.js +228 -0
  43. package/dist/prompts/exec.d.ts +13 -0
  44. package/dist/prompts/exec.js +83 -0
  45. package/dist/prompts/fuzzy.d.ts +12 -0
  46. package/dist/prompts/fuzzy.js +136 -0
  47. package/dist/prompts/gauge.d.ts +21 -0
  48. package/dist/prompts/gauge.js +130 -0
  49. package/dist/prompts/heatmap.d.ts +13 -0
  50. package/dist/prompts/heatmap.js +141 -0
  51. package/dist/prompts/ip.d.ts +11 -0
  52. package/dist/prompts/ip.js +118 -0
  53. package/dist/prompts/kanban.d.ts +17 -0
  54. package/dist/prompts/kanban.js +228 -0
  55. package/dist/prompts/keypress.js +0 -2
  56. package/dist/prompts/license.d.ts +9 -0
  57. package/dist/prompts/license.js +105 -0
  58. package/dist/prompts/map.d.ts +15 -0
  59. package/dist/prompts/map.js +199 -0
  60. package/dist/prompts/match.d.ts +19 -0
  61. package/dist/prompts/match.js +275 -0
  62. package/dist/prompts/miller.d.ts +15 -0
  63. package/dist/prompts/miller.js +221 -0
  64. package/dist/prompts/multi-column-select.d.ts +10 -0
  65. package/dist/prompts/multi-column-select.js +166 -0
  66. package/dist/prompts/number.js +0 -2
  67. package/dist/prompts/otp.d.ts +10 -0
  68. package/dist/prompts/otp.js +91 -0
  69. package/dist/prompts/pattern.d.ts +22 -0
  70. package/dist/prompts/pattern.js +249 -0
  71. package/dist/prompts/quiz-select.d.ts +10 -0
  72. package/dist/prompts/quiz-select.js +104 -0
  73. package/dist/prompts/quiz-text.d.ts +11 -0
  74. package/dist/prompts/quiz-text.js +82 -0
  75. package/dist/prompts/regex.d.ts +13 -0
  76. package/dist/prompts/regex.js +131 -0
  77. package/dist/prompts/region.d.ts +11 -0
  78. package/dist/prompts/region.js +164 -0
  79. package/dist/prompts/schedule.d.ts +18 -0
  80. package/dist/prompts/schedule.js +221 -0
  81. package/dist/prompts/scroll.d.ts +13 -0
  82. package/dist/prompts/scroll.js +152 -0
  83. package/dist/prompts/seat.d.ts +17 -0
  84. package/dist/prompts/seat.js +165 -0
  85. package/dist/prompts/select-range.d.ts +8 -0
  86. package/dist/prompts/select-range.js +136 -0
  87. package/dist/prompts/select.d.ts +9 -9
  88. package/dist/prompts/semver.d.ts +6 -0
  89. package/dist/prompts/semver.js +32 -0
  90. package/dist/prompts/shortcut.d.ts +9 -0
  91. package/dist/prompts/shortcut.js +135 -0
  92. package/dist/prompts/slot.d.ts +16 -0
  93. package/dist/prompts/slot.js +107 -0
  94. package/dist/prompts/snippet.js +0 -3
  95. package/dist/prompts/sort-grid.d.ts +16 -0
  96. package/dist/prompts/sort-grid.js +146 -0
  97. package/dist/prompts/sort.js +0 -1
  98. package/dist/prompts/spreadsheet.d.ts +21 -0
  99. package/dist/prompts/spreadsheet.js +239 -0
  100. package/dist/prompts/text.d.ts +9 -7
  101. package/dist/prompts/text.js +52 -0
  102. package/dist/prompts/time.d.ts +12 -0
  103. package/dist/prompts/time.js +202 -0
  104. package/dist/prompts/tree-select.d.ts +0 -1
  105. package/dist/prompts/tree-select.js +1 -5
  106. package/dist/symbols.d.ts +12 -0
  107. package/dist/symbols.js +14 -2
  108. package/dist/theme.js +10 -1
  109. package/dist/types.d.ts +264 -1
  110. package/dist/utils.d.ts +53 -0
  111. package/dist/utils.js +252 -0
  112. package/package.json +51 -47
  113. package/example.ts +0 -390
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BytePrompt = 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
+ const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
9
+ class BytePrompt extends base_1.Prompt {
10
+ constructor(options) {
11
+ super(options);
12
+ this.inputValue = 0;
13
+ this.buffer = '';
14
+ this.unitIndex = 0;
15
+ this.errorMsg = '';
16
+ // Parse initial bytes to best unit
17
+ // e.g. 1073741824 -> 1.00 GB
18
+ let val = options.initial || 0;
19
+ let idx = 0;
20
+ while (val >= 1024 && idx < UNITS.length - 1) {
21
+ val /= 1024;
22
+ idx++;
23
+ }
24
+ // Round to 2 decimals for display if float
25
+ this.inputValue = Math.round(val * 100) / 100;
26
+ this.buffer = this.inputValue.toString();
27
+ this.unitIndex = idx;
28
+ }
29
+ render(_firstRender) {
30
+ const unitStr = UNITS[this.unitIndex];
31
+ // 1. Render Question & Input
32
+ const icon = this.errorMsg ? `${theme_1.theme.error}${symbols_1.symbols.cross}` : `${theme_1.theme.success}?`;
33
+ let output = `${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} `;
34
+ // Value part
35
+ output += `${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${this.buffer}${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${unitStr}${ansi_1.ANSI.RESET}\n`;
36
+ // 2. Render Unit Bar
37
+ // (Unit: B > KB > [MB] > GB)
38
+ let bar = ' ';
39
+ UNITS.forEach((u, i) => {
40
+ if (i === this.unitIndex) {
41
+ bar += `${theme_1.theme.main}${ansi_1.ANSI.REVERSE} ${u} ${ansi_1.ANSI.RESET}`;
42
+ }
43
+ else {
44
+ bar += `${theme_1.theme.muted} ${u} ${ansi_1.ANSI.RESET}`;
45
+ }
46
+ if (i < UNITS.length - 1) {
47
+ bar += ` ${theme_1.theme.muted}›${ansi_1.ANSI.RESET} `;
48
+ }
49
+ });
50
+ output += `\n${bar}\n`;
51
+ // 3. Hints & Errors
52
+ output += `${theme_1.theme.muted} (Arrows: Adjust | Tab: Switch Unit | Enter: Submit)${ansi_1.ANSI.RESET}`;
53
+ if (this.errorMsg) {
54
+ output += `\n${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`;
55
+ }
56
+ this.renderFrame(output);
57
+ }
58
+ handleInput(char) {
59
+ this.errorMsg = '';
60
+ // Enter
61
+ if (char === '\r' || char === '\n') {
62
+ const finalVal = parseFloat(this.buffer);
63
+ if (isNaN(finalVal)) {
64
+ this.errorMsg = 'Invalid number';
65
+ this.render(false);
66
+ return;
67
+ }
68
+ // Calculate Bytes: value * 1024^index
69
+ const bytes = finalVal * Math.pow(1024, this.unitIndex);
70
+ // Min/Max Validation (in bytes)
71
+ if (this.options.min !== undefined && bytes < this.options.min) {
72
+ this.errorMsg = `Minimum value is ${this.formatBytes(this.options.min)}`;
73
+ this.render(false);
74
+ return;
75
+ }
76
+ if (this.options.max !== undefined && bytes > this.options.max) {
77
+ this.errorMsg = `Maximum value is ${this.formatBytes(this.options.max)}`;
78
+ this.render(false);
79
+ return;
80
+ }
81
+ this.submit(Math.round(bytes)); // Return integer bytes? Or float? Usually bytes are int.
82
+ return;
83
+ }
84
+ // Tab / Shift+Tab -> Switch Unit
85
+ if (char === '\t' || this.isRight(char)) {
86
+ if (this.unitIndex < UNITS.length - 1) {
87
+ this.unitIndex++;
88
+ this.render(false);
89
+ }
90
+ return;
91
+ }
92
+ if (char === '\x1b[Z' || this.isLeft(char)) { // Shift+Tab or Left
93
+ if (this.unitIndex > 0) {
94
+ this.unitIndex--;
95
+ this.render(false);
96
+ }
97
+ return;
98
+ }
99
+ // Arrows -> Adjust Value
100
+ if (this.isUp(char) || this.isDown(char)) {
101
+ let val = parseFloat(this.buffer) || 0;
102
+ // Adaptive step: 1 if integer, 0.1 if float
103
+ const step = this.buffer.includes('.') ? 0.1 : 1;
104
+ if (this.isUp(char))
105
+ val += step;
106
+ if (this.isDown(char))
107
+ val -= step;
108
+ // Fix Float Precision
109
+ val = Math.round(val * 100) / 100;
110
+ if (val < 0)
111
+ val = 0; // No negative bytes usually
112
+ this.buffer = val.toString();
113
+ this.render(false);
114
+ return;
115
+ }
116
+ // Typing / Backspace
117
+ if (char === '\u0008' || char === '\x7f') {
118
+ if (this.buffer.length > 0) {
119
+ this.buffer = this.buffer.slice(0, -1);
120
+ this.render(false);
121
+ }
122
+ return;
123
+ }
124
+ // Digits & Dot
125
+ if (/^[0-9.]$/.test(char)) {
126
+ // Prevent multiple dots
127
+ if (char === '.' && this.buffer.includes('.'))
128
+ return;
129
+ this.buffer += char;
130
+ this.render(false);
131
+ }
132
+ }
133
+ // Helper for error messages
134
+ formatBytes(bytes) {
135
+ if (bytes === 0)
136
+ return '0 B';
137
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
138
+ return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + UNITS[i];
139
+ }
140
+ handleMouse(event) {
141
+ if (event.action === 'scroll') {
142
+ if (event.scroll === 'up') {
143
+ // Scroll up -> Increase Value
144
+ let val = parseFloat(this.buffer) || 0;
145
+ val += 1;
146
+ this.buffer = val.toString();
147
+ this.render(false);
148
+ }
149
+ else if (event.scroll === 'down') {
150
+ // Scroll down -> Decrease Value
151
+ let val = parseFloat(this.buffer) || 0;
152
+ val = Math.max(0, val - 1);
153
+ this.buffer = val.toString();
154
+ this.render(false);
155
+ }
156
+ }
157
+ }
158
+ }
159
+ exports.BytePrompt = BytePrompt;
@@ -0,0 +1,16 @@
1
+ import { Prompt } from '../base';
2
+ import { CalculatorOptions } from '../types';
3
+ export declare class CalculatorPrompt extends Prompt<number, CalculatorOptions> {
4
+ protected errorMsg: string;
5
+ protected cursor: number;
6
+ protected hasTyped: boolean;
7
+ protected segments: string[];
8
+ protected lastLinesUp: number;
9
+ protected previewValue: string;
10
+ constructor(options: CalculatorOptions);
11
+ private updatePreview;
12
+ private evaluate;
13
+ protected render(firstRender: boolean): void;
14
+ private getVisualCursorPosition;
15
+ protected handleInput(char: string): void;
16
+ }
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CalculatorPrompt = 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
+ const utils_1 = require("../utils");
9
+ class CalculatorPrompt extends base_1.Prompt {
10
+ constructor(options) {
11
+ super(options);
12
+ this.errorMsg = '';
13
+ this.cursor = 0;
14
+ this.hasTyped = false;
15
+ this.segments = [];
16
+ this.lastLinesUp = 0;
17
+ this.previewValue = '';
18
+ this.value = options.initial ? String(options.initial) : '';
19
+ this.segments = (0, utils_1.safeSplit)(this.value);
20
+ this.cursor = this.segments.length;
21
+ this.updatePreview();
22
+ }
23
+ updatePreview() {
24
+ const input = this.segments.join('');
25
+ if (!input.trim()) {
26
+ this.previewValue = '';
27
+ return;
28
+ }
29
+ try {
30
+ const result = this.evaluate(input);
31
+ if (result !== null && !isNaN(result) && isFinite(result)) {
32
+ // Format precision if needed
33
+ if (this.options.precision !== undefined) {
34
+ this.previewValue = parseFloat(result.toFixed(this.options.precision)).toString();
35
+ }
36
+ else {
37
+ this.previewValue = result.toString();
38
+ }
39
+ }
40
+ else {
41
+ this.previewValue = '';
42
+ }
43
+ }
44
+ catch (_e) {
45
+ this.previewValue = '';
46
+ }
47
+ }
48
+ evaluate(expression) {
49
+ let expr = expression;
50
+ // 1. Substitute variables
51
+ if (this.options.variables) {
52
+ // Sort keys by length desc to avoid partial replacement issues (e.g. 'var11' vs 'var1')
53
+ const keys = Object.keys(this.options.variables).sort((a, b) => b.length - a.length);
54
+ for (const key of keys) {
55
+ const val = this.options.variables[key];
56
+ const regex = new RegExp(`\\b${key}\\b`, 'g');
57
+ expr = expr.replace(regex, String(val));
58
+ }
59
+ }
60
+ // 2. Validate characters
61
+ if (/[a-zA-Z_]/.test(expr)) {
62
+ // Support basic Math functions: sin, cos, tan, log, sqrt, abs, pow, floor, ceil, round
63
+ const allowedMath = ['sin', 'cos', 'tan', 'log', 'sqrt', 'abs', 'pow', 'floor', 'ceil', 'round', 'PI', 'E'];
64
+ // We can prefix them with Math.
65
+ let checkStr = expr;
66
+ allowedMath.forEach(k => {
67
+ checkStr = checkStr.replace(new RegExp(k, 'g'), '');
68
+ });
69
+ if (/[a-zA-Z_]/.test(checkStr)) {
70
+ return null; // Contains unknown variables or functions
71
+ }
72
+ // Now prefix Math functions in the real expr
73
+ allowedMath.forEach(k => {
74
+ // Replace 'sin(' with 'Math.sin('
75
+ // Be careful with PI and E
76
+ if (['PI', 'E'].includes(k)) {
77
+ expr = expr.replace(new RegExp(`\\b${k}\\b`, 'g'), `Math.${k}`);
78
+ }
79
+ else {
80
+ expr = expr.replace(new RegExp(`\\b${k}\\(`, 'g'), `Math.${k}(`);
81
+ }
82
+ });
83
+ }
84
+ // 3. Safe Eval using Function
85
+ try {
86
+ const func = new Function(`return (${expr})`);
87
+ const res = func();
88
+ return typeof res === 'number' ? res : null;
89
+ }
90
+ catch (_err) {
91
+ return null;
92
+ }
93
+ }
94
+ render(firstRender) {
95
+ if (!firstRender && this.lastLinesUp > 0) {
96
+ this.print(`\x1b[${this.lastLinesUp}B`);
97
+ }
98
+ this.lastLinesUp = 0;
99
+ const icon = this.errorMsg ? `${theme_1.theme.error}${symbols_1.symbols.cross}` : `${theme_1.theme.success}?`;
100
+ const prefix = `${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} `;
101
+ const prefixVisualLen = this.stripAnsi(prefix).length;
102
+ // Render Input Line
103
+ let displayValue = '';
104
+ const inputStr = this.segments.join('');
105
+ if (!inputStr && this.options.placeholder && !this.errorMsg && !this.hasTyped) {
106
+ displayValue = `${theme_1.theme.muted}${this.options.placeholder}${ansi_1.ANSI.RESET}`;
107
+ }
108
+ else {
109
+ displayValue = theme_1.theme.main + inputStr + ansi_1.ANSI.RESET;
110
+ }
111
+ let output = `${prefix}${displayValue}`;
112
+ // Add Preview Line (if available and no error)
113
+ let previewStr = '';
114
+ if (this.previewValue && !this.errorMsg) {
115
+ // Display as "= Result" in muted color
116
+ previewStr = `\n${theme_1.theme.muted}= ${this.previewValue}${ansi_1.ANSI.RESET}`;
117
+ output += previewStr;
118
+ }
119
+ if (this.errorMsg) {
120
+ output += `\n${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`;
121
+ }
122
+ this.renderFrame(output);
123
+ this.print(ansi_1.ANSI.SHOW_CURSOR);
124
+ let totalRows = 1;
125
+ if (previewStr)
126
+ totalRows++;
127
+ if (this.errorMsg)
128
+ totalRows++;
129
+ const linesUp = (totalRows - 1);
130
+ if (linesUp > 0) {
131
+ this.print(`\x1b[${linesUp}A`);
132
+ }
133
+ this.lastLinesUp = linesUp;
134
+ // Move cursor to correct column
135
+ // We assume single line input for now
136
+ const cursorVisualCol = this.getVisualCursorPosition();
137
+ const targetCol = prefixVisualLen + cursorVisualCol;
138
+ this.print(ansi_1.ANSI.CURSOR_LEFT);
139
+ if (targetCol > 0) {
140
+ this.print(`\x1b[${targetCol}C`);
141
+ }
142
+ }
143
+ getVisualCursorPosition() {
144
+ let width = 0;
145
+ for (let i = 0; i < this.cursor; i++) {
146
+ width += (0, utils_1.stringWidth)(this.segments[i]);
147
+ }
148
+ return width;
149
+ }
150
+ handleInput(char) {
151
+ // Enter
152
+ if (char === '\r' || char === '\n') {
153
+ const input = this.segments.join('');
154
+ const result = this.evaluate(input);
155
+ if (result === null || isNaN(result)) {
156
+ this.errorMsg = 'Invalid expression';
157
+ this.render(false);
158
+ return;
159
+ }
160
+ this.submit(result);
161
+ return;
162
+ }
163
+ // Backspace
164
+ if (char === '\u0008' || char === '\x7f') {
165
+ this.hasTyped = true;
166
+ if (this.cursor > 0) {
167
+ this.segments.splice(this.cursor - 1, 1);
168
+ this.cursor--;
169
+ this.errorMsg = '';
170
+ this.updatePreview();
171
+ this.render(false);
172
+ }
173
+ return;
174
+ }
175
+ // Arrows
176
+ if (this.isLeft(char)) {
177
+ if (this.cursor > 0) {
178
+ this.cursor--;
179
+ this.render(false);
180
+ }
181
+ return;
182
+ }
183
+ if (this.isRight(char)) {
184
+ if (this.cursor < this.segments.length) {
185
+ this.cursor++;
186
+ this.render(false);
187
+ }
188
+ return;
189
+ }
190
+ // Delete
191
+ if (char === '\u001b[3~') {
192
+ this.hasTyped = true;
193
+ if (this.cursor < this.segments.length) {
194
+ this.segments.splice(this.cursor, 1);
195
+ this.errorMsg = '';
196
+ this.updatePreview();
197
+ this.render(false);
198
+ }
199
+ return;
200
+ }
201
+ // Normal typing
202
+ if (!/^[\x00-\x1F]/.test(char) && !char.startsWith('\x1b')) {
203
+ this.hasTyped = true;
204
+ const newSegments = (0, utils_1.safeSplit)(char);
205
+ this.segments.splice(this.cursor, 0, ...newSegments);
206
+ this.cursor += newSegments.length;
207
+ this.errorMsg = '';
208
+ this.updatePreview();
209
+ this.render(false);
210
+ }
211
+ }
212
+ }
213
+ exports.CalculatorPrompt = CalculatorPrompt;
@@ -85,8 +85,6 @@ class CalendarPrompt extends base_1.Prompt {
85
85
  if (this.options.mode === 'range') {
86
86
  if (Array.isArray(this.selection)) {
87
87
  const [start, end] = this.selection;
88
- // If the range is complete, highlight everything in between
89
- // Sort to ensure valid range check even if start > end (though we should normalize)
90
88
  const s = start < end ? start : end;
91
89
  const e = start < end ? end : start;
92
90
  // Set times to midnight for comparison
@@ -96,9 +94,6 @@ class CalendarPrompt extends base_1.Prompt {
96
94
  return dTime >= sTime && dTime <= eTime;
97
95
  }
98
96
  else {
99
- // Selecting range, but only first point picked?
100
- // Usually when picking range, first click sets start, moving cursor highlights?
101
- // For now, if selection is single date in range mode, just highlight it
102
97
  return this.isSameDay(d, this.selection);
103
98
  }
104
99
  }
@@ -9,7 +9,9 @@ export declare class CodePrompt extends Prompt<string, CodeOptions> {
9
9
  private lastLinesUp;
10
10
  constructor(options: CodeOptions);
11
11
  private parseTemplate;
12
+ protected cleanup(): void;
12
13
  protected render(firstRender: boolean): void;
14
+ private appendSegment;
13
15
  protected handleInput(char: string, _key: Buffer): void;
14
16
  protected handleMouse(event: MouseEvent): void;
15
17
  private moveFocus;
@@ -4,7 +4,7 @@ exports.CodePrompt = void 0;
4
4
  const ansi_1 = require("../ansi");
5
5
  const base_1 = require("../base");
6
6
  const theme_1 = require("../theme");
7
- const highlight_1 = require("../highlight");
7
+ const utils_1 = require("../utils");
8
8
  class CodePrompt extends base_1.Prompt {
9
9
  constructor(options) {
10
10
  super(options);
@@ -18,11 +18,12 @@ class CodePrompt extends base_1.Prompt {
18
18
  // Init values
19
19
  this.variableTokens.forEach(idx => {
20
20
  const name = this.tokens[idx].value;
21
- this.values[name] = '';
21
+ this.values[name] = (this.options.values && this.options.values[name]) || '';
22
22
  });
23
23
  // Init cursor at end of first var
24
24
  if (this.variableTokens.length > 0) {
25
- this.cursor = 0; // Start empty
25
+ const activeName = this.tokens[this.variableTokens[0]].value;
26
+ this.cursor = this.values[activeName].length;
26
27
  }
27
28
  }
28
29
  parseTemplate() {
@@ -41,80 +42,98 @@ class CodePrompt extends base_1.Prompt {
41
42
  this.tokens.push({ type: 'static', value: this.options.template.substring(lastIndex) });
42
43
  }
43
44
  }
45
+ // FIX: Override cleanup to ensure cursor is reset to bottom
46
+ cleanup() {
47
+ // If the cursor is currently moved up (editing mode), move it back down
48
+ // before the prompt exits. This prevents the next output (or result)
49
+ // from overwriting the bottom part of our UI.
50
+ if (this.lastLinesUp > 0) {
51
+ this.print(`\x1b[${this.lastLinesUp}B`);
52
+ this.lastLinesUp = 0;
53
+ }
54
+ super.cleanup();
55
+ }
44
56
  render(firstRender) {
45
- // Reset cursor from previous render relative position
46
57
  if (!firstRender && this.lastLinesUp > 0) {
47
58
  this.print(`\x1b[${this.lastLinesUp}B`);
48
59
  }
49
60
  this.lastLinesUp = 0;
50
- // 1. Construct Raw String for Highlighting
51
- const ACTIVE_PLACEHOLDER = '___ACTIVE___';
52
- let rawWithPlaceholder = '';
61
+ // 1. Build Full Raw Text & Identify Active Variable Range
62
+ let fullRawText = '';
63
+ let activeVarStart = -1;
64
+ let activeVarEnd = -1;
65
+ const activeTokenIdx = this.variableTokens[this.activeVarIndex];
66
+ const activeVarName = this.tokens[activeTokenIdx].value;
53
67
  this.tokens.forEach((token, idx) => {
54
- if (token.type === 'static') {
55
- rawWithPlaceholder += token.value;
56
- }
57
- else {
58
- if (this.variableTokens[this.activeVarIndex] === idx) {
59
- rawWithPlaceholder += ACTIVE_PLACEHOLDER;
60
- }
61
- else {
62
- rawWithPlaceholder += this.values[token.value] || '';
63
- }
68
+ const val = (token.type === 'static') ? token.value : (this.values[token.value] || '');
69
+ if (idx === activeTokenIdx) {
70
+ activeVarStart = fullRawText.length;
71
+ activeVarEnd = activeVarStart + val.length;
64
72
  }
73
+ fullRawText += val;
65
74
  });
66
- // 2. Highlight
75
+ // 2. Syntax Highlight with Overlap Logic
67
76
  let highlighted = '';
68
- const shouldHighlight = this.options.highlight !== false; // Default true
77
+ const shouldHighlight = this.options.highlight !== false;
69
78
  if (shouldHighlight) {
70
- highlighted = (0, highlight_1.highlightJson)(rawWithPlaceholder);
79
+ const jsonTokenRegex = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"?)|(-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)|(true|false|null)|([{}[\],:])/g;
80
+ let match;
81
+ let lastIndex = 0;
82
+ while ((match = jsonTokenRegex.exec(fullRawText)) !== null) {
83
+ if (match.index > lastIndex) {
84
+ const gap = fullRawText.substring(lastIndex, match.index);
85
+ this.appendSegment(gap, lastIndex, activeVarStart, activeVarEnd, ansi_1.ANSI.FG_WHITE, (s) => highlighted += s);
86
+ }
87
+ const tokenText = match[0];
88
+ const tokenStart = match.index;
89
+ let color = theme_1.theme.syntax?.punctuation || ansi_1.ANSI.FG_WHITE;
90
+ if (tokenText.startsWith('"')) {
91
+ const remaining = fullRawText.substring(jsonTokenRegex.lastIndex);
92
+ if (/^\s*:/.test(remaining)) {
93
+ color = theme_1.theme.syntax?.key || ansi_1.ANSI.FG_CYAN;
94
+ }
95
+ else {
96
+ color = theme_1.theme.syntax?.string || ansi_1.ANSI.FG_GREEN;
97
+ }
98
+ }
99
+ else if (/^-?\d/.test(tokenText)) {
100
+ color = theme_1.theme.syntax?.number || ansi_1.ANSI.FG_YELLOW;
101
+ }
102
+ else if (/^(true|false|null)$/.test(tokenText)) {
103
+ color = (tokenText === 'null')
104
+ ? (theme_1.theme.syntax?.null || ansi_1.ANSI.FG_RED)
105
+ : (theme_1.theme.syntax?.boolean || ansi_1.ANSI.FG_MAGENTA);
106
+ }
107
+ else if (/^[{}[\],:]$/.test(tokenText)) {
108
+ color = theme_1.theme.syntax?.punctuation || ansi_1.ANSI.FG_WHITE;
109
+ }
110
+ this.appendSegment(tokenText, tokenStart, activeVarStart, activeVarEnd, color, (s) => highlighted += s);
111
+ lastIndex = jsonTokenRegex.lastIndex;
112
+ }
113
+ if (lastIndex < fullRawText.length) {
114
+ const tail = fullRawText.substring(lastIndex);
115
+ this.appendSegment(tail, lastIndex, activeVarStart, activeVarEnd, ansi_1.ANSI.FG_WHITE, (s) => highlighted += s);
116
+ }
71
117
  }
72
118
  else {
73
- highlighted = rawWithPlaceholder;
74
- }
75
- // 3. Replace Placeholder with Styled Active Value
76
- const activeVarName = this.tokens[this.variableTokens[this.activeVarIndex]].value;
77
- const activeVal = this.values[activeVarName] || '';
78
- // Use Main color + Underline. RESET restores default.
79
- const styledActive = `${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${activeVal}${ansi_1.ANSI.RESET}`;
80
- highlighted = highlighted.replace(ACTIVE_PLACEHOLDER, styledActive);
81
- // 4. Output
82
- const prefix = `${theme_1.theme.success}? ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}\n`;
119
+ this.appendSegment(fullRawText, 0, activeVarStart, activeVarEnd, ansi_1.ANSI.RESET, (s) => highlighted += s);
120
+ }
121
+ // 3. Output Frame
122
+ const warningMsg = shouldHighlight
123
+ ? `${ansi_1.ANSI.FG_YELLOW}Warning:${ansi_1.ANSI.RESET} Syntax highlighting is an experimental feature.\n`
124
+ : '';
125
+ const prefix = `${warningMsg}${theme_1.theme.success}? ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}\n`;
83
126
  const suffix = `\n${theme_1.theme.muted}(Tab to next, Enter to submit)${ansi_1.ANSI.RESET}`;
84
127
  const fullOutput = prefix + highlighted + suffix;
85
128
  this.renderFrame(fullOutput);
86
- // 5. Cursor Calculation
87
- // Calculate (row, col) relative to start of snippet
88
- let textBeforeCursor = '';
89
- for (let i = 0; i < this.tokens.length; i++) {
90
- const token = this.tokens[i];
91
- if (token.type === 'static') {
92
- textBeforeCursor += token.value;
93
- }
94
- else {
95
- if (this.variableTokens[this.activeVarIndex] === i) {
96
- textBeforeCursor += activeVal.substring(0, this.cursor);
97
- break;
98
- }
99
- else {
100
- textBeforeCursor += this.values[token.value] || '';
101
- }
102
- }
103
- }
129
+ // 4. Cursor Calculation
130
+ const cursorAbsPos = activeVarStart + this.cursor;
131
+ const textBeforeCursor = fullRawText.substring(0, cursorAbsPos);
104
132
  const rowsBefore = textBeforeCursor.split('\n');
105
133
  const cursorRow = rowsBefore.length - 1;
106
- const cursorCol = rowsBefore[rowsBefore.length - 1].length;
107
- // Calculate total lines in snippet
108
- let fullRaw = '';
109
- this.tokens.forEach(token => {
110
- fullRaw += (token.type === 'static' ? token.value : (this.values[token.value] || ''));
111
- });
112
- const totalSnippetLines = fullRaw.split('\n').length;
113
- // Calculate linesUp from the bottom of snippet
114
- // Suffix is 1 line.
115
- // CursorRow is 0-based index from top of snippet.
116
- // If cursorRow is at bottom (totalSnippetLines-1), linesUp = 1 (Suffix).
117
- // If cursorRow is at top (0), linesUp = 1 + (totalSnippetLines - 1).
134
+ const cursorCol = (0, utils_1.stringWidth)(rowsBefore[rowsBefore.length - 1]);
135
+ const totalSnippetLines = fullRawText.split('\n').length;
136
+ // linesUp calculation: 1 (suffix) + remaining snippet lines
118
137
  const linesUp = 1 + (totalSnippetLines - 1 - cursorRow);
119
138
  this.print(ansi_1.ANSI.SHOW_CURSOR);
120
139
  if (linesUp > 0) {
@@ -126,9 +145,28 @@ class CodePrompt extends base_1.Prompt {
126
145
  this.print(`\x1b[${cursorCol}C`);
127
146
  }
128
147
  }
148
+ appendSegment(text, absStart, activeStart, activeEnd, syntaxColor, append) {
149
+ const absEnd = absStart + text.length;
150
+ const overlapStart = Math.max(absStart, activeStart);
151
+ const overlapEnd = Math.min(absEnd, activeEnd);
152
+ if (overlapStart < overlapEnd) {
153
+ if (absStart < overlapStart) {
154
+ const part = text.substring(0, overlapStart - absStart);
155
+ append(`${syntaxColor}${part}${ansi_1.ANSI.RESET}`);
156
+ }
157
+ const activePart = text.substring(overlapStart - absStart, overlapEnd - absStart);
158
+ append(`${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${activePart}${ansi_1.ANSI.RESET}`);
159
+ if (absEnd > overlapEnd) {
160
+ const part = text.substring(overlapEnd - absStart);
161
+ append(`${syntaxColor}${part}${ansi_1.ANSI.RESET}`);
162
+ }
163
+ }
164
+ else {
165
+ append(`${syntaxColor}${text}${ansi_1.ANSI.RESET}`);
166
+ }
167
+ }
129
168
  handleInput(char, _key) {
130
- // Nav
131
- if (char === '\u001b[Z') { // Shift Tab
169
+ if (char === '\u001b[Z') {
132
170
  this.moveFocus(-1);
133
171
  return;
134
172
  }
@@ -136,7 +174,6 @@ class CodePrompt extends base_1.Prompt {
136
174
  this.moveFocus(1);
137
175
  return;
138
176
  }
139
- // Enter
140
177
  if (char === '\r' || char === '\n') {
141
178
  this.submitCode();
142
179
  return;
@@ -144,8 +181,7 @@ class CodePrompt extends base_1.Prompt {
144
181
  const activeTokenIdx = this.variableTokens[this.activeVarIndex];
145
182
  const varName = this.tokens[activeTokenIdx].value;
146
183
  const val = this.values[varName] || '';
147
- // Editing
148
- if (char === '\u0008' || char === '\x7f') { // Backspace
184
+ if (char === '\u0008' || char === '\x7f') {
149
185
  if (this.cursor > 0) {
150
186
  const pre = val.slice(0, this.cursor - 1);
151
187
  const post = val.slice(this.cursor);
@@ -177,12 +213,10 @@ class CodePrompt extends base_1.Prompt {
177
213
  }
178
214
  handleMouse(event) {
179
215
  if (event.action === 'scroll') {
180
- if (event.scroll === 'up') {
216
+ if (event.scroll === 'up')
181
217
  this.moveFocus(-1);
182
- }
183
- else if (event.scroll === 'down') {
218
+ else if (event.scroll === 'down')
184
219
  this.moveFocus(1);
185
- }
186
220
  }
187
221
  }
188
222
  moveFocus(direction) {
@@ -190,7 +224,7 @@ class CodePrompt extends base_1.Prompt {
190
224
  if (nextIndex >= 0 && nextIndex < this.variableTokens.length) {
191
225
  this.activeVarIndex = nextIndex;
192
226
  const varName = this.tokens[this.variableTokens[this.activeVarIndex]].value;
193
- this.cursor = (this.values[varName] || '').length; // Move cursor to end
227
+ this.cursor = (this.values[varName] || '').length;
194
228
  this.render(false);
195
229
  }
196
230
  }