mepcli 0.5.0 → 0.6.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 +182 -6
- package/dist/ansi.d.ts +1 -0
- package/dist/ansi.js +1 -0
- package/dist/base.d.ts +1 -1
- package/dist/base.js +1 -10
- package/dist/core.d.ts +26 -1
- package/dist/core.js +72 -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.d.ts +14 -0
- package/dist/prompts/editor.js +207 -0
- 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 +7 -0
- package/dist/prompts/keypress.js +57 -0
- 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 +14 -32
- 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 +20 -0
- package/dist/prompts/tree.js +231 -0
- package/dist/prompts/wait.d.ts +18 -0
- package/dist/prompts/wait.js +62 -0
- package/dist/types.d.ts +105 -0
- package/dist/utils.js +6 -2
- package/example.ts +213 -27
- package/package.json +14 -4
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ColorPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
class ColorPrompt extends base_1.Prompt {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super(options);
|
|
10
|
+
this.activeChannel = 'r';
|
|
11
|
+
this.inputBuffer = '';
|
|
12
|
+
this.rgb = this.parseHex(options.initial || '#000000');
|
|
13
|
+
}
|
|
14
|
+
parseHex(hex) {
|
|
15
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
16
|
+
return result ? {
|
|
17
|
+
r: parseInt(result[1], 16),
|
|
18
|
+
g: parseInt(result[2], 16),
|
|
19
|
+
b: parseInt(result[3], 16)
|
|
20
|
+
} : { r: 0, g: 0, b: 0 };
|
|
21
|
+
}
|
|
22
|
+
rgbToHex(r, g, b) {
|
|
23
|
+
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
|
|
24
|
+
}
|
|
25
|
+
// Helper to format ANSI TrueColor background
|
|
26
|
+
getBgColorCode(r, g, b) {
|
|
27
|
+
return `\x1b[48;2;${r};${g};${b}m`;
|
|
28
|
+
}
|
|
29
|
+
render(_firstRender) {
|
|
30
|
+
const { r, g, b } = this.rgb;
|
|
31
|
+
const hex = this.rgbToHex(r, g, b);
|
|
32
|
+
let output = `${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} `;
|
|
33
|
+
output += `${ansi_1.ANSI.BOLD}${hex}${ansi_1.ANSI.RESET}\n\n`;
|
|
34
|
+
// Preview Block
|
|
35
|
+
if (this.capabilities.hasTrueColor) {
|
|
36
|
+
const bg = this.getBgColorCode(r, g, b);
|
|
37
|
+
const block = `${bg} ${ansi_1.ANSI.RESET}`; // 8 spaces
|
|
38
|
+
output += ` ${block}\n`;
|
|
39
|
+
output += ` ${block}\n`;
|
|
40
|
+
output += ` ${block}\n\n`;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Fallback for no true color?
|
|
44
|
+
// Maybe just show text or use closest 256 color?
|
|
45
|
+
// For now, simpler fallback:
|
|
46
|
+
output += ` (Preview unavailable in this terminal)\n\n`;
|
|
47
|
+
}
|
|
48
|
+
// Sliders
|
|
49
|
+
const channels = ['r', 'g', 'b'];
|
|
50
|
+
const labels = { r: 'Red ', g: 'Green', b: 'Blue ' };
|
|
51
|
+
channels.forEach(ch => {
|
|
52
|
+
const val = this.rgb[ch];
|
|
53
|
+
const isActive = this.activeChannel === ch;
|
|
54
|
+
// Render slider track
|
|
55
|
+
// Width 20 chars for 0-255
|
|
56
|
+
const width = 20;
|
|
57
|
+
const pos = Math.floor((val / 255) * width);
|
|
58
|
+
// Wait, '⚪' might be wide. using simpler char if needed. 'O' or ANSI reverse space.
|
|
59
|
+
// Let's use standard chars.
|
|
60
|
+
const trackSimple = '━'.repeat(pos) + (isActive ? '●' : '○') + '─'.repeat(width - pos);
|
|
61
|
+
let line = `${labels[ch]}: ${val.toString().padStart(3)} [${trackSimple}]`;
|
|
62
|
+
if (isActive) {
|
|
63
|
+
line = `${theme_1.theme.main}${ansi_1.ANSI.REVERSE} ${line} ${ansi_1.ANSI.RESET}`;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
line = ` ${line} `;
|
|
67
|
+
}
|
|
68
|
+
output += line + '\n';
|
|
69
|
+
});
|
|
70
|
+
output += `\n${theme_1.theme.muted}Arrows: Adjust/Switch | Shift+Arrows: Adjust fast | Enter: Submit${ansi_1.ANSI.RESET}`;
|
|
71
|
+
this.renderFrame(output);
|
|
72
|
+
}
|
|
73
|
+
handleInput(char, _key) {
|
|
74
|
+
const isUp = this.isUp(char);
|
|
75
|
+
const isDown = this.isDown(char);
|
|
76
|
+
const isLeft = this.isLeft(char);
|
|
77
|
+
const isRight = this.isRight(char);
|
|
78
|
+
// Detect Shift key for faster movement (generic check if possible, or mapping specific codes)
|
|
79
|
+
// \x1b[1;2C is Shift+Right usually. \x1b[1;2D is Shift+Left.
|
|
80
|
+
// It varies by terminal.
|
|
81
|
+
const isShiftRight = char === '\x1b[1;2C';
|
|
82
|
+
const isShiftLeft = char === '\x1b[1;2D';
|
|
83
|
+
// Channel Switching (Up/Down)
|
|
84
|
+
if (isUp) {
|
|
85
|
+
if (this.activeChannel === 'g')
|
|
86
|
+
this.activeChannel = 'r';
|
|
87
|
+
else if (this.activeChannel === 'b')
|
|
88
|
+
this.activeChannel = 'g';
|
|
89
|
+
this.render(false);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (isDown) {
|
|
93
|
+
if (this.activeChannel === 'r')
|
|
94
|
+
this.activeChannel = 'g';
|
|
95
|
+
else if (this.activeChannel === 'g')
|
|
96
|
+
this.activeChannel = 'b';
|
|
97
|
+
this.render(false);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Value Adjustment (Left/Right)
|
|
101
|
+
if (isLeft || isRight || isShiftLeft || isShiftRight) {
|
|
102
|
+
let change = 0;
|
|
103
|
+
if (isRight)
|
|
104
|
+
change = 1;
|
|
105
|
+
if (isLeft)
|
|
106
|
+
change = -1;
|
|
107
|
+
if (isShiftRight)
|
|
108
|
+
change = 10;
|
|
109
|
+
if (isShiftLeft)
|
|
110
|
+
change = -10;
|
|
111
|
+
const val = this.rgb[this.activeChannel] + change;
|
|
112
|
+
this.rgb[this.activeChannel] = Math.max(0, Math.min(255, val));
|
|
113
|
+
this.render(false);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Tab to cycle channels
|
|
117
|
+
if (char === '\t') {
|
|
118
|
+
if (this.activeChannel === 'r')
|
|
119
|
+
this.activeChannel = 'g';
|
|
120
|
+
else if (this.activeChannel === 'g')
|
|
121
|
+
this.activeChannel = 'b';
|
|
122
|
+
else
|
|
123
|
+
this.activeChannel = 'r';
|
|
124
|
+
this.render(false);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (char === '\r' || char === '\n') {
|
|
128
|
+
this.submit(this.rgbToHex(this.rgb.r, this.rgb.g, this.rgb.b));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
handleMouse(event) {
|
|
133
|
+
if (event.action === 'scroll') {
|
|
134
|
+
// On scroll, adjust the currently active channel's value.
|
|
135
|
+
// If Ctrl is held, adjust by a larger step (fast adjust).
|
|
136
|
+
const fast = !!event.ctrl;
|
|
137
|
+
const step = fast ? 10 : 1;
|
|
138
|
+
const change = event.scroll === 'up' ? step : (event.scroll === 'down' ? -step : 0);
|
|
139
|
+
if (change !== 0) {
|
|
140
|
+
const val = this.rgb[this.activeChannel] + change;
|
|
141
|
+
this.rgb[this.activeChannel] = Math.max(0, Math.min(255, val));
|
|
142
|
+
this.render(false);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.ColorPrompt = ColorPrompt;
|
|
@@ -2,7 +2,7 @@ import { Prompt } from '../base';
|
|
|
2
2
|
import { ConfirmOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class ConfirmPrompt extends Prompt<boolean, ConfirmOptions> {
|
|
4
4
|
constructor(options: ConfirmOptions);
|
|
5
|
-
protected render(
|
|
5
|
+
protected render(_firstRender: boolean): void;
|
|
6
6
|
protected handleInput(char: string): void;
|
|
7
7
|
protected handleMouse(event: MouseEvent): void;
|
|
8
8
|
}
|
package/dist/prompts/confirm.js
CHANGED
|
@@ -10,7 +10,7 @@ class ConfirmPrompt extends base_1.Prompt {
|
|
|
10
10
|
super(options);
|
|
11
11
|
this.value = options.initial ?? true;
|
|
12
12
|
}
|
|
13
|
-
render(
|
|
13
|
+
render(_firstRender) {
|
|
14
14
|
// Prepare content
|
|
15
15
|
const hint = this.value ? `${ansi_1.ANSI.BOLD}Yes${ansi_1.ANSI.RESET}/no` : `yes/${ansi_1.ANSI.BOLD}No${ansi_1.ANSI.RESET}`;
|
|
16
16
|
let output = `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${theme_1.theme.muted}(${hint})${ansi_1.ANSI.RESET} `;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { CronOptions, MouseEvent } from '../types';
|
|
3
|
+
export declare class CronPrompt extends Prompt<string, CronOptions> {
|
|
4
|
+
private fields;
|
|
5
|
+
private activeField;
|
|
6
|
+
private buffer;
|
|
7
|
+
constructor(options: CronOptions);
|
|
8
|
+
private get currentConfig();
|
|
9
|
+
protected render(_firstRender: boolean): void;
|
|
10
|
+
private validateCurrentField;
|
|
11
|
+
protected handleInput(char: string): void;
|
|
12
|
+
protected handleMouse(event: MouseEvent): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CronPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
const FIELDS = [
|
|
8
|
+
{ label: 'Minute', min: 0, max: 59 },
|
|
9
|
+
{ label: 'Hour', min: 0, max: 23 },
|
|
10
|
+
{ label: 'Day', min: 1, max: 31 },
|
|
11
|
+
{ label: 'Month', min: 1, max: 12 },
|
|
12
|
+
{ label: 'Weekday', min: 0, max: 6 }
|
|
13
|
+
];
|
|
14
|
+
class CronPrompt extends base_1.Prompt {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super(options);
|
|
17
|
+
this.fields = ['*', '*', '*', '*', '*'];
|
|
18
|
+
this.activeField = 0;
|
|
19
|
+
this.buffer = '';
|
|
20
|
+
if (options.initial) {
|
|
21
|
+
const parts = options.initial.split(' ');
|
|
22
|
+
if (parts.length === 5) {
|
|
23
|
+
this.fields = parts;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
get currentConfig() {
|
|
28
|
+
return FIELDS[this.activeField];
|
|
29
|
+
}
|
|
30
|
+
render(_firstRender) {
|
|
31
|
+
let output = `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}\n`;
|
|
32
|
+
// Render fields
|
|
33
|
+
let fieldsStr = '';
|
|
34
|
+
this.fields.forEach((val, index) => {
|
|
35
|
+
const isSelected = index === this.activeField;
|
|
36
|
+
const displayVal = val.padStart(2, ' ');
|
|
37
|
+
if (isSelected) {
|
|
38
|
+
fieldsStr += `${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${displayVal}${ansi_1.ANSI.RESET} `;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
fieldsStr += `${ansi_1.ANSI.DIM}${displayVal}${ansi_1.ANSI.RESET} `;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
output += ` ${fieldsStr}\n`;
|
|
45
|
+
// Render Label and Hints
|
|
46
|
+
const config = this.currentConfig;
|
|
47
|
+
output += ` ${theme_1.theme.muted}${config.label} (${config.min}-${config.max})${ansi_1.ANSI.RESET}\n`;
|
|
48
|
+
output += ` ${ansi_1.ANSI.DIM}(Arrows: Adjust | Space: Toggle * | 0-9: Type | Tab: Next)${ansi_1.ANSI.RESET}`;
|
|
49
|
+
this.renderFrame(output);
|
|
50
|
+
}
|
|
51
|
+
validateCurrentField() {
|
|
52
|
+
const config = this.currentConfig;
|
|
53
|
+
const val = this.fields[this.activeField];
|
|
54
|
+
if (val === '*')
|
|
55
|
+
return;
|
|
56
|
+
let num = parseInt(val);
|
|
57
|
+
if (isNaN(num)) {
|
|
58
|
+
this.fields[this.activeField] = config.min.toString();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
num = Math.max(config.min, Math.min(num, config.max));
|
|
62
|
+
this.fields[this.activeField] = num.toString();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
handleInput(char) {
|
|
66
|
+
if (char === '\r' || char === '\n') {
|
|
67
|
+
this.validateCurrentField();
|
|
68
|
+
this.submit(this.fields.join(' '));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (char === '\t' || this.isRight(char)) {
|
|
72
|
+
this.validateCurrentField();
|
|
73
|
+
this.activeField = (this.activeField + 1) % 5;
|
|
74
|
+
this.buffer = '';
|
|
75
|
+
this.render(false);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Shift+Tab detection is often '\x1b[Z'
|
|
79
|
+
if (char === '\x1b[Z' || this.isLeft(char)) {
|
|
80
|
+
this.validateCurrentField();
|
|
81
|
+
this.activeField = (this.activeField - 1 + 5) % 5;
|
|
82
|
+
this.buffer = '';
|
|
83
|
+
this.render(false);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const config = this.currentConfig;
|
|
87
|
+
const currentVal = this.fields[this.activeField];
|
|
88
|
+
let numVal = parseInt(currentVal);
|
|
89
|
+
if (this.isUp(char)) {
|
|
90
|
+
if (currentVal === '*') {
|
|
91
|
+
this.fields[this.activeField] = config.min.toString();
|
|
92
|
+
}
|
|
93
|
+
else if (!isNaN(numVal)) {
|
|
94
|
+
numVal++;
|
|
95
|
+
if (numVal > config.max)
|
|
96
|
+
numVal = config.min;
|
|
97
|
+
this.fields[this.activeField] = numVal.toString();
|
|
98
|
+
}
|
|
99
|
+
this.buffer = '';
|
|
100
|
+
this.render(false);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (this.isDown(char)) {
|
|
104
|
+
if (currentVal === '*') {
|
|
105
|
+
this.fields[this.activeField] = config.max.toString();
|
|
106
|
+
}
|
|
107
|
+
else if (!isNaN(numVal)) {
|
|
108
|
+
numVal--;
|
|
109
|
+
if (numVal < config.min)
|
|
110
|
+
numVal = config.max;
|
|
111
|
+
this.fields[this.activeField] = numVal.toString();
|
|
112
|
+
}
|
|
113
|
+
this.buffer = '';
|
|
114
|
+
this.render(false);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (char === ' ' || char.toLowerCase() === 'x') {
|
|
118
|
+
this.fields[this.activeField] = '*';
|
|
119
|
+
this.buffer = '';
|
|
120
|
+
this.render(false);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Direct Input
|
|
124
|
+
if (/^[0-9]$/.test(char)) {
|
|
125
|
+
const nextBuffer = this.buffer + char;
|
|
126
|
+
const nextNum = parseInt(nextBuffer);
|
|
127
|
+
// Check if valid
|
|
128
|
+
if (!isNaN(nextNum) && nextNum <= config.max) {
|
|
129
|
+
this.buffer = nextBuffer;
|
|
130
|
+
if (nextNum >= 0) { // Should be positive
|
|
131
|
+
this.fields[this.activeField] = nextNum.toString();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const freshNum = parseInt(char);
|
|
136
|
+
if (!isNaN(freshNum) && freshNum <= config.max) {
|
|
137
|
+
this.buffer = char;
|
|
138
|
+
this.fields[this.activeField] = freshNum.toString();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
this.render(false);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
handleMouse(event) {
|
|
145
|
+
if (event.action === 'scroll') {
|
|
146
|
+
const config = this.currentConfig;
|
|
147
|
+
const currentVal = this.fields[this.activeField];
|
|
148
|
+
let numVal = parseInt(currentVal);
|
|
149
|
+
if (event.scroll === 'up') { // Increase
|
|
150
|
+
if (currentVal === '*') {
|
|
151
|
+
this.fields[this.activeField] = config.min.toString();
|
|
152
|
+
}
|
|
153
|
+
else if (!isNaN(numVal)) {
|
|
154
|
+
numVal++;
|
|
155
|
+
if (numVal > config.max)
|
|
156
|
+
numVal = config.min;
|
|
157
|
+
this.fields[this.activeField] = numVal.toString();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else { // Decrease
|
|
161
|
+
if (currentVal === '*') {
|
|
162
|
+
this.fields[this.activeField] = config.max.toString();
|
|
163
|
+
}
|
|
164
|
+
else if (!isNaN(numVal)) {
|
|
165
|
+
numVal--;
|
|
166
|
+
if (numVal < config.min)
|
|
167
|
+
numVal = config.max;
|
|
168
|
+
this.fields[this.activeField] = numVal.toString();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
this.buffer = '';
|
|
172
|
+
this.render(false);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.CronPrompt = CronPrompt;
|
package/dist/prompts/date.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare class DatePrompt extends Prompt<Date, DateOptions> {
|
|
|
5
5
|
private errorMsg;
|
|
6
6
|
private inputBuffer;
|
|
7
7
|
constructor(options: DateOptions);
|
|
8
|
-
protected render(
|
|
8
|
+
protected render(_firstRender: boolean): void;
|
|
9
9
|
protected handleInput(char: string): void;
|
|
10
10
|
protected handleMouse(event: MouseEvent): void;
|
|
11
11
|
private adjustDate;
|
package/dist/prompts/date.js
CHANGED
|
@@ -14,7 +14,7 @@ class DatePrompt extends base_1.Prompt {
|
|
|
14
14
|
this.inputBuffer = '';
|
|
15
15
|
this.value = options.initial || new Date();
|
|
16
16
|
}
|
|
17
|
-
render(
|
|
17
|
+
render(_firstRender) {
|
|
18
18
|
// Date formatting
|
|
19
19
|
const y = this.value.getFullYear();
|
|
20
20
|
const m = (this.value.getMonth() + 1).toString().padStart(2, '0');
|
|
@@ -52,15 +52,25 @@ class DatePrompt extends base_1.Prompt {
|
|
|
52
52
|
this.submit(this.value);
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
-
if (this.
|
|
56
|
-
|
|
55
|
+
if (char === '\t' || this.isRight(char)) { // Right / Tab
|
|
56
|
+
if (char === '\t') {
|
|
57
|
+
this.selectedField = (this.selectedField + 1) % 5;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.selectedField = Math.min(4, this.selectedField + 1);
|
|
61
|
+
}
|
|
57
62
|
this.inputBuffer = ''; // Reset buffer on move
|
|
58
63
|
this.errorMsg = '';
|
|
59
64
|
this.render(false);
|
|
60
65
|
return;
|
|
61
66
|
}
|
|
62
|
-
if (this.
|
|
63
|
-
|
|
67
|
+
if (char === '\x1b[Z' || this.isLeft(char)) { // Left / Shift+Tab
|
|
68
|
+
if (char === '\x1b[Z') {
|
|
69
|
+
this.selectedField = (this.selectedField - 1 + 5) % 5;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.selectedField = Math.max(0, this.selectedField - 1);
|
|
73
|
+
}
|
|
64
74
|
this.inputBuffer = ''; // Reset buffer on move
|
|
65
75
|
this.errorMsg = '';
|
|
66
76
|
this.render(false);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { EditorOptions } from '../types';
|
|
3
|
+
export declare class EditorPrompt extends Prompt<string, EditorOptions> {
|
|
4
|
+
private errorMsg;
|
|
5
|
+
private status;
|
|
6
|
+
private tempFilePath;
|
|
7
|
+
constructor(options: EditorOptions);
|
|
8
|
+
protected cleanup(): void;
|
|
9
|
+
protected render(firstRender: boolean): void;
|
|
10
|
+
protected handleInput(char: string): void;
|
|
11
|
+
private resolveEditor;
|
|
12
|
+
private spawnEditor;
|
|
13
|
+
private restoreMep;
|
|
14
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.EditorPrompt = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const base_1 = require("../base");
|
|
42
|
+
const theme_1 = require("../theme");
|
|
43
|
+
const symbols_1 = require("../symbols");
|
|
44
|
+
const ansi_1 = require("../ansi");
|
|
45
|
+
class EditorPrompt extends base_1.Prompt {
|
|
46
|
+
constructor(options) {
|
|
47
|
+
super(options);
|
|
48
|
+
this.errorMsg = '';
|
|
49
|
+
this.status = 'pending';
|
|
50
|
+
this.tempFilePath = null;
|
|
51
|
+
// Default waitUserInput to true if not specified
|
|
52
|
+
if (this.options.waitUserInput === undefined) {
|
|
53
|
+
this.options.waitUserInput = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
cleanup() {
|
|
57
|
+
if (this.tempFilePath) {
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(this.tempFilePath)) {
|
|
60
|
+
fs.unlinkSync(this.tempFilePath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (_error) {
|
|
64
|
+
// Ignore cleanup errors
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
super.cleanup();
|
|
68
|
+
}
|
|
69
|
+
render(firstRender) {
|
|
70
|
+
if (this.status === 'editing') {
|
|
71
|
+
return; // Don't render while editor is open (stdio inherited)
|
|
72
|
+
}
|
|
73
|
+
const icon = this.status === 'done' ? theme_1.theme.success + symbols_1.symbols.tick : theme_1.theme.main + '?';
|
|
74
|
+
const message = `${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}`;
|
|
75
|
+
const hint = this.options.waitUserInput
|
|
76
|
+
? ` ${theme_1.theme.muted}[Press <Enter> to launch editor]${ansi_1.ANSI.RESET}`
|
|
77
|
+
: ` ${theme_1.theme.muted}[Launching editor...]${ansi_1.ANSI.RESET}`;
|
|
78
|
+
let output = `${icon} ${ansi_1.ANSI.BOLD}${message}${ansi_1.ANSI.RESET}${hint}`;
|
|
79
|
+
if (this.errorMsg) {
|
|
80
|
+
output += `\n${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`;
|
|
81
|
+
}
|
|
82
|
+
this.renderFrame(output);
|
|
83
|
+
// Auto-launch handling
|
|
84
|
+
if (firstRender && this.options.waitUserInput === false) {
|
|
85
|
+
// We need to delay slightly to ensure the render frame is flushed
|
|
86
|
+
// and raw mode setup is complete from .run()
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
this.spawnEditor();
|
|
89
|
+
}, 50);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
handleInput(char) {
|
|
93
|
+
if (this.status !== 'pending')
|
|
94
|
+
return;
|
|
95
|
+
// Enter
|
|
96
|
+
if (char === '\r' || char === '\n') {
|
|
97
|
+
this.spawnEditor();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
resolveEditor() {
|
|
101
|
+
// 1. Env vars
|
|
102
|
+
const envEditor = process.env.VISUAL || process.env.EDITOR;
|
|
103
|
+
if (envEditor) {
|
|
104
|
+
const parts = envEditor.split(' ');
|
|
105
|
+
return { cmd: parts[0], args: parts.slice(1) };
|
|
106
|
+
}
|
|
107
|
+
// 2. OS specific
|
|
108
|
+
if (process.platform === 'win32') {
|
|
109
|
+
// Priority: notepad -> code -> wordpad
|
|
110
|
+
return { cmd: 'notepad', args: [] };
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Unix/Linux/Mac
|
|
114
|
+
// Priority: vim -> nano -> vi
|
|
115
|
+
// We'll stick to 'vim' as the default safe bet if we can't detect.
|
|
116
|
+
// A more robust solution would check paths, but for now we assume 'vim'.
|
|
117
|
+
return { cmd: 'vim', args: [] };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
spawnEditor() {
|
|
121
|
+
this.status = 'editing';
|
|
122
|
+
// 1. Prepare Temp File
|
|
123
|
+
const ext = this.options.extension || '.txt';
|
|
124
|
+
// Ensure extension has dot
|
|
125
|
+
const safeExt = ext.startsWith('.') ? ext : '.' + ext;
|
|
126
|
+
const filename = `mep-editor-${Date.now()}-${Math.floor(Math.random() * 1000)}${safeExt}`;
|
|
127
|
+
this.tempFilePath = path.join(os.tmpdir(), filename);
|
|
128
|
+
const initialContent = this.options.initial || '';
|
|
129
|
+
try {
|
|
130
|
+
fs.writeFileSync(this.tempFilePath, initialContent, 'utf8');
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
this.errorMsg = `Failed to create temp file: ${e.message}`;
|
|
134
|
+
this.status = 'pending';
|
|
135
|
+
this.render(false);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// 2. Resolve Editor
|
|
139
|
+
const { cmd, args } = this.resolveEditor();
|
|
140
|
+
const editorArgs = [...args, this.tempFilePath];
|
|
141
|
+
// 3. Pause Mep
|
|
142
|
+
// Temporarily disable mouse tracking if it was enabled
|
|
143
|
+
const shouldEnableMouse = this.options.mouse !== false && this.capabilities.hasMouse;
|
|
144
|
+
if (shouldEnableMouse) {
|
|
145
|
+
this.print(ansi_1.ANSI.DISABLE_MOUSE);
|
|
146
|
+
}
|
|
147
|
+
// Pause stdin and raw mode to allow child process to take over TTY
|
|
148
|
+
this.stdin.setRawMode(false);
|
|
149
|
+
this.stdin.pause();
|
|
150
|
+
// 4. Spawn
|
|
151
|
+
const child = (0, child_process_1.spawn)(cmd, editorArgs, {
|
|
152
|
+
stdio: 'inherit',
|
|
153
|
+
shell: true
|
|
154
|
+
});
|
|
155
|
+
child.on('error', (err) => {
|
|
156
|
+
this.restoreMep();
|
|
157
|
+
this.status = 'pending';
|
|
158
|
+
this.errorMsg = `Could not launch editor '${cmd}': ${err.message}`;
|
|
159
|
+
this.render(false);
|
|
160
|
+
});
|
|
161
|
+
child.on('exit', (code) => {
|
|
162
|
+
// 5. Read Result
|
|
163
|
+
let content = initialContent;
|
|
164
|
+
try {
|
|
165
|
+
if (this.tempFilePath && fs.existsSync(this.tempFilePath)) {
|
|
166
|
+
content = fs.readFileSync(this.tempFilePath, 'utf8');
|
|
167
|
+
fs.unlinkSync(this.tempFilePath); // Cleanup
|
|
168
|
+
this.tempFilePath = null; // Mark as cleaned
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (_e) {
|
|
172
|
+
// Ignore read/delete errors
|
|
173
|
+
}
|
|
174
|
+
this.restoreMep();
|
|
175
|
+
if (code !== 0) {
|
|
176
|
+
this.status = 'pending';
|
|
177
|
+
this.errorMsg = `Editor exited with code ${code}`;
|
|
178
|
+
this.render(false);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Success
|
|
182
|
+
this.status = 'done';
|
|
183
|
+
// Trim trailing newline which editors often add
|
|
184
|
+
// We only trim the *last* newline added by the editor if it wasn't there?
|
|
185
|
+
// Usually editors ensure a final newline.
|
|
186
|
+
// If the user entered "abc", vim saves "abc\n". We probably want "abc".
|
|
187
|
+
if (content.endsWith('\n')) {
|
|
188
|
+
content = content.slice(0, -1);
|
|
189
|
+
}
|
|
190
|
+
if (content.endsWith('\r')) {
|
|
191
|
+
content = content.slice(0, -1);
|
|
192
|
+
}
|
|
193
|
+
this.submit(content);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
restoreMep() {
|
|
197
|
+
this.stdin.resume();
|
|
198
|
+
this.stdin.setRawMode(true);
|
|
199
|
+
// Re-enable mouse if it was enabled
|
|
200
|
+
const shouldEnableMouse = this.options.mouse !== false && this.capabilities.hasMouse;
|
|
201
|
+
if (shouldEnableMouse) {
|
|
202
|
+
this.print(ansi_1.ANSI.SET_ANY_EVENT_MOUSE + ansi_1.ANSI.SET_SGR_EXT_MODE_MOUSE);
|
|
203
|
+
}
|
|
204
|
+
this.print(ansi_1.ANSI.HIDE_CURSOR); // Ensure cursor is hidden again for Mep
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.EditorPrompt = EditorPrompt;
|
package/dist/prompts/file.d.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
2
|
import { FileOptions } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of FilePrompt with autocomplete.
|
|
5
|
+
*/
|
|
3
6
|
export declare class FilePrompt extends Prompt<string, FileOptions> {
|
|
4
7
|
private input;
|
|
5
8
|
private cursor;
|
|
6
9
|
private suggestions;
|
|
7
10
|
private selectedSuggestion;
|
|
8
11
|
private errorMsg;
|
|
12
|
+
private lastLinesUp;
|
|
9
13
|
constructor(options: FileOptions);
|
|
14
|
+
/**
|
|
15
|
+
* Updates the suggestions list based on the current input path.
|
|
16
|
+
*/
|
|
10
17
|
private updateSuggestions;
|
|
11
18
|
protected render(firstRender: boolean): void;
|
|
12
19
|
protected handleInput(char: string): void;
|