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
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransferPrompt = 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 TransferPrompt extends base_1.Prompt {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
super(options);
|
|
12
|
+
this.leftList = [];
|
|
13
|
+
this.rightList = [];
|
|
14
|
+
this.cursorLeft = 0;
|
|
15
|
+
this.cursorRight = 0;
|
|
16
|
+
this.scrollTopLeft = 0;
|
|
17
|
+
this.scrollTopRight = 0;
|
|
18
|
+
this.activeSide = 'left';
|
|
19
|
+
this.pageSize = 10;
|
|
20
|
+
this.leftList = this.normalize(options.source);
|
|
21
|
+
this.rightList = this.normalize(options.target || []);
|
|
22
|
+
}
|
|
23
|
+
normalize(items) {
|
|
24
|
+
return items.map(item => {
|
|
25
|
+
if (typeof item === 'string') {
|
|
26
|
+
return { title: item, value: item };
|
|
27
|
+
}
|
|
28
|
+
return item;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
truncate(str, width) {
|
|
32
|
+
if ((0, utils_1.stringWidth)(str) <= width)
|
|
33
|
+
return str;
|
|
34
|
+
let res = str;
|
|
35
|
+
while ((0, utils_1.stringWidth)(res + '...') > width && res.length > 0) {
|
|
36
|
+
res = res.slice(0, -1);
|
|
37
|
+
}
|
|
38
|
+
return res + '...';
|
|
39
|
+
}
|
|
40
|
+
render(_firstRender) {
|
|
41
|
+
const termWidth = process.stdout.columns || 80;
|
|
42
|
+
const colWidth = Math.floor((termWidth - 6) / 2);
|
|
43
|
+
// Adjust Scroll Top
|
|
44
|
+
if (this.activeSide === 'left') {
|
|
45
|
+
if (this.cursorLeft < this.scrollTopLeft)
|
|
46
|
+
this.scrollTopLeft = this.cursorLeft;
|
|
47
|
+
if (this.cursorLeft >= this.scrollTopLeft + this.pageSize)
|
|
48
|
+
this.scrollTopLeft = this.cursorLeft - this.pageSize + 1;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
if (this.cursorRight < this.scrollTopRight)
|
|
52
|
+
this.scrollTopRight = this.cursorRight;
|
|
53
|
+
if (this.cursorRight >= this.scrollTopRight + this.pageSize)
|
|
54
|
+
this.scrollTopRight = this.cursorRight - this.pageSize + 1;
|
|
55
|
+
}
|
|
56
|
+
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`;
|
|
57
|
+
// Headers
|
|
58
|
+
const leftTitle = this.activeSide === 'left' ? `${theme_1.theme.main}Source${ansi_1.ANSI.RESET}` : 'Source';
|
|
59
|
+
const rightTitle = this.activeSide === 'right' ? `${theme_1.theme.main}Target${ansi_1.ANSI.RESET}` : 'Target';
|
|
60
|
+
output += ` ${leftTitle}`.padEnd(colWidth + 2) + ' ' + ` ${rightTitle}\n`;
|
|
61
|
+
output += ` ${ansi_1.ANSI.DIM}${symbols_1.symbols.line.repeat(colWidth)}${ansi_1.ANSI.RESET} ${ansi_1.ANSI.DIM}${symbols_1.symbols.line.repeat(colWidth)}${ansi_1.ANSI.RESET}\n`;
|
|
62
|
+
for (let i = 0; i < this.pageSize; i++) {
|
|
63
|
+
const idxLeft = this.scrollTopLeft + i;
|
|
64
|
+
const idxRight = this.scrollTopRight + i;
|
|
65
|
+
const itemLeft = this.leftList[idxLeft];
|
|
66
|
+
const itemRight = this.rightList[idxRight];
|
|
67
|
+
// Left Column
|
|
68
|
+
let leftStr = '';
|
|
69
|
+
if (itemLeft) {
|
|
70
|
+
const isSelected = this.activeSide === 'left' && idxLeft === this.cursorLeft;
|
|
71
|
+
const title = this.truncate(itemLeft.title, colWidth - 2);
|
|
72
|
+
if (isSelected) {
|
|
73
|
+
leftStr = `${theme_1.theme.main}${symbols_1.symbols.pointer} ${title}${ansi_1.ANSI.RESET}`;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
leftStr = ` ${title}`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
leftStr = ''; // Empty
|
|
81
|
+
}
|
|
82
|
+
// Right Column
|
|
83
|
+
let rightStr = '';
|
|
84
|
+
if (itemRight) {
|
|
85
|
+
const isSelected = this.activeSide === 'right' && idxRight === this.cursorRight;
|
|
86
|
+
const title = this.truncate(itemRight.title, colWidth - 2);
|
|
87
|
+
if (isSelected) {
|
|
88
|
+
rightStr = `${theme_1.theme.main}${symbols_1.symbols.pointer} ${title}${ansi_1.ANSI.RESET}`;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
rightStr = ` ${title}`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const leftVisualWidth = itemLeft ? ((0, utils_1.stringWidth)(this.truncate(itemLeft.title, colWidth - 2)) + 2) : 0;
|
|
95
|
+
// Pad visually
|
|
96
|
+
const padding = ' '.repeat(Math.max(0, colWidth - leftVisualWidth));
|
|
97
|
+
output += leftStr + padding + ' | ' + rightStr + '\n';
|
|
98
|
+
}
|
|
99
|
+
output += `\n${ansi_1.ANSI.DIM}(Tab: Switch | a: Move All Right | r: Reset/All Left)${ansi_1.ANSI.RESET}`;
|
|
100
|
+
this.renderFrame(output);
|
|
101
|
+
}
|
|
102
|
+
handleInput(char) {
|
|
103
|
+
if (char === '\r' || char === '\n') {
|
|
104
|
+
const leftValues = this.leftList.map(i => i.value);
|
|
105
|
+
const rightValues = this.rightList.map(i => i.value);
|
|
106
|
+
this.submit([leftValues, rightValues]);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (char === '\t' || this.isLeft(char) || this.isRight(char)) {
|
|
110
|
+
// Toggle side
|
|
111
|
+
this.activeSide = this.activeSide === 'left' ? 'right' : 'left';
|
|
112
|
+
this.render(false);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// --- Batch Transfer Shortcuts ---
|
|
116
|
+
// Move All to Right ('a' or '>')
|
|
117
|
+
if (char === 'a' || char === '>') {
|
|
118
|
+
if (this.leftList.length > 0) {
|
|
119
|
+
this.rightList.push(...this.leftList);
|
|
120
|
+
this.leftList = [];
|
|
121
|
+
this.cursorLeft = 0;
|
|
122
|
+
this.activeSide = 'right';
|
|
123
|
+
this.render(false);
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Move All to Left ('r' or '<')
|
|
128
|
+
if (char === 'r' || char === '<') {
|
|
129
|
+
if (this.rightList.length > 0) {
|
|
130
|
+
this.leftList.push(...this.rightList);
|
|
131
|
+
this.rightList = [];
|
|
132
|
+
this.cursorRight = 0;
|
|
133
|
+
this.activeSide = 'left';
|
|
134
|
+
this.render(false);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (this.isUp(char)) {
|
|
139
|
+
if (this.activeSide === 'left') {
|
|
140
|
+
this.cursorLeft = Math.max(0, this.cursorLeft - 1);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.cursorRight = Math.max(0, this.cursorRight - 1);
|
|
144
|
+
}
|
|
145
|
+
this.render(false);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (this.isDown(char)) {
|
|
149
|
+
if (this.activeSide === 'left') {
|
|
150
|
+
this.cursorLeft = Math.min(this.leftList.length - 1, this.cursorLeft + 1);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this.cursorRight = Math.min(this.rightList.length - 1, this.cursorRight + 1);
|
|
154
|
+
}
|
|
155
|
+
this.render(false);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (char === ' ') {
|
|
159
|
+
// Move item
|
|
160
|
+
if (this.activeSide === 'left') {
|
|
161
|
+
if (this.leftList.length > 0) {
|
|
162
|
+
const [item] = this.leftList.splice(this.cursorLeft, 1);
|
|
163
|
+
this.rightList.push(item);
|
|
164
|
+
if (this.cursorLeft >= this.leftList.length) {
|
|
165
|
+
this.cursorLeft = Math.max(0, this.leftList.length - 1);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
if (this.rightList.length > 0) {
|
|
171
|
+
const [item] = this.rightList.splice(this.cursorRight, 1);
|
|
172
|
+
this.leftList.push(item);
|
|
173
|
+
if (this.cursorRight >= this.rightList.length) {
|
|
174
|
+
this.cursorRight = Math.max(0, this.rightList.length - 1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
this.render(false);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
handleMouse(event) {
|
|
182
|
+
if (event.action === 'scroll') {
|
|
183
|
+
if (event.scroll === 'up') {
|
|
184
|
+
if (this.activeSide === 'left') {
|
|
185
|
+
this.cursorLeft = Math.max(0, this.cursorLeft - 1);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
this.cursorRight = Math.max(0, this.cursorRight - 1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
if (this.activeSide === 'left') {
|
|
193
|
+
this.cursorLeft = Math.min(this.leftList.length - 1, this.cursorLeft + 1);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
this.cursorRight = Math.min(this.rightList.length - 1, this.cursorRight + 1);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this.render(false);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
exports.TransferPrompt = TransferPrompt;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { TreeSelectOptions, MouseEvent } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* FIXED-ISH: This prompt may hang on Windows TTY after multiple prompt cycles.
|
|
5
|
+
* Current workaround: User must press 'Enter' to flush the stdin buffer.
|
|
6
|
+
* Potential root cause: Synchronous recursion during initialization blocking setRawMode.
|
|
7
|
+
*/
|
|
8
|
+
export declare class TreeSelectPrompt<V> extends Prompt<V[], TreeSelectOptions<V>> {
|
|
9
|
+
private cursor;
|
|
10
|
+
private expandedNodes;
|
|
11
|
+
private flatList;
|
|
12
|
+
private scrollTop;
|
|
13
|
+
private readonly pageSize;
|
|
14
|
+
private parentMap;
|
|
15
|
+
private static hasWarnedWindows;
|
|
16
|
+
private readonly ICON_CLOSED;
|
|
17
|
+
private readonly ICON_OPEN;
|
|
18
|
+
constructor(options: TreeSelectOptions<V>);
|
|
19
|
+
private buildParentMap;
|
|
20
|
+
private initializeExpanded;
|
|
21
|
+
private initializeSelection;
|
|
22
|
+
private recalculateAllParents;
|
|
23
|
+
private updateNodeStateFromChildren;
|
|
24
|
+
private setNodeState;
|
|
25
|
+
private recalculateFlatList;
|
|
26
|
+
private traverse;
|
|
27
|
+
private toggleRecursive;
|
|
28
|
+
protected render(_firstRender: boolean): void;
|
|
29
|
+
protected handleInput(char: string, _key: Buffer): void;
|
|
30
|
+
private collectSelected;
|
|
31
|
+
protected handleMouse(event: MouseEvent): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TreeSelectPrompt = 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
|
+
/**
|
|
9
|
+
* FIXED-ISH: This prompt may hang on Windows TTY after multiple prompt cycles.
|
|
10
|
+
* Current workaround: User must press 'Enter' to flush the stdin buffer.
|
|
11
|
+
* Potential root cause: Synchronous recursion during initialization blocking setRawMode.
|
|
12
|
+
*/
|
|
13
|
+
class TreeSelectPrompt extends base_1.Prompt {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
super(options);
|
|
16
|
+
this.cursor = 0;
|
|
17
|
+
this.expandedNodes = new Set();
|
|
18
|
+
this.flatList = [];
|
|
19
|
+
this.scrollTop = 0;
|
|
20
|
+
this.pageSize = 15;
|
|
21
|
+
this.parentMap = new Map();
|
|
22
|
+
this.ICON_CLOSED = symbols_1.symbols.pointer === '>' ? '+' : '▸';
|
|
23
|
+
this.ICON_OPEN = symbols_1.symbols.pointer === '>' ? '-' : '▾';
|
|
24
|
+
this.buildParentMap(this.options.data, null);
|
|
25
|
+
this.initializeExpanded(this.options.data);
|
|
26
|
+
if (this.options.initial) {
|
|
27
|
+
const initialSet = new Set(this.options.initial);
|
|
28
|
+
this.initializeSelection(this.options.data, initialSet);
|
|
29
|
+
this.recalculateAllParents(this.options.data);
|
|
30
|
+
}
|
|
31
|
+
if (process.platform === 'win32' && !TreeSelectPrompt.hasWarnedWindows) {
|
|
32
|
+
console.warn(`${ansi_1.ANSI.FG_YELLOW}Warning:${ansi_1.ANSI.RESET} TreeSelectPrompt may hang on Windows TTY after multiple prompt cycles. Please press 'Enter' if the prompt becomes unresponsive.${ansi_1.ANSI.RESET}`);
|
|
33
|
+
TreeSelectPrompt.hasWarnedWindows = true;
|
|
34
|
+
}
|
|
35
|
+
this.recalculateFlatList();
|
|
36
|
+
}
|
|
37
|
+
buildParentMap(nodes, parent) {
|
|
38
|
+
for (const node of nodes) {
|
|
39
|
+
if (parent)
|
|
40
|
+
this.parentMap.set(node, parent);
|
|
41
|
+
if (node.children) {
|
|
42
|
+
this.buildParentMap(node.children, node);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
initializeExpanded(nodes) {
|
|
47
|
+
for (const node of nodes) {
|
|
48
|
+
if (node.expanded) {
|
|
49
|
+
this.expandedNodes.add(node);
|
|
50
|
+
}
|
|
51
|
+
if (node.children) {
|
|
52
|
+
this.initializeExpanded(node.children);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
initializeSelection(nodes, initialValues) {
|
|
57
|
+
for (const node of nodes) {
|
|
58
|
+
if (initialValues.has(node.value)) {
|
|
59
|
+
this.setNodeState(node, true, false);
|
|
60
|
+
}
|
|
61
|
+
if (node.children) {
|
|
62
|
+
this.initializeSelection(node.children, initialValues);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
recalculateAllParents(nodes) {
|
|
67
|
+
for (const node of nodes) {
|
|
68
|
+
if (node.children) {
|
|
69
|
+
this.recalculateAllParents(node.children);
|
|
70
|
+
this.updateNodeStateFromChildren(node);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
updateNodeStateFromChildren(node) {
|
|
75
|
+
if (!node.children || node.children.length === 0)
|
|
76
|
+
return;
|
|
77
|
+
const allChecked = node.children.every(c => c.selected === true);
|
|
78
|
+
const allUnchecked = node.children.every(c => !c.selected);
|
|
79
|
+
if (allChecked) {
|
|
80
|
+
node.selected = true;
|
|
81
|
+
}
|
|
82
|
+
else if (allUnchecked) {
|
|
83
|
+
node.selected = false;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
node.selected = 'indeterminate';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
setNodeState(node, state, updateParents = true) {
|
|
90
|
+
node.selected = state;
|
|
91
|
+
if (node.children) {
|
|
92
|
+
node.children.forEach(c => this.setNodeState(c, state, false));
|
|
93
|
+
}
|
|
94
|
+
if (updateParents) {
|
|
95
|
+
let curr = node;
|
|
96
|
+
while (this.parentMap.has(curr)) {
|
|
97
|
+
const parent = this.parentMap.get(curr);
|
|
98
|
+
this.updateNodeStateFromChildren(parent);
|
|
99
|
+
curr = parent;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
recalculateFlatList() {
|
|
104
|
+
this.flatList = [];
|
|
105
|
+
this.traverse(this.options.data, 0, null);
|
|
106
|
+
if (this.cursor >= this.flatList.length) {
|
|
107
|
+
this.cursor = Math.max(0, this.flatList.length - 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
traverse(nodes, depth, parent) {
|
|
111
|
+
for (const node of nodes) {
|
|
112
|
+
this.flatList.push({ node, depth, parent });
|
|
113
|
+
if (node.children && node.children.length > 0 && this.expandedNodes.has(node)) {
|
|
114
|
+
this.traverse(node.children, depth + 1, node);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
toggleRecursive(node, expand) {
|
|
119
|
+
if (expand)
|
|
120
|
+
this.expandedNodes.add(node);
|
|
121
|
+
else
|
|
122
|
+
this.expandedNodes.delete(node);
|
|
123
|
+
if (node.children) {
|
|
124
|
+
node.children.forEach(child => this.toggleRecursive(child, expand));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
render(_firstRender) {
|
|
128
|
+
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`;
|
|
129
|
+
if (this.flatList.length === 0) {
|
|
130
|
+
output += ` ${theme_1.theme.muted}No data${ansi_1.ANSI.RESET}`;
|
|
131
|
+
this.renderFrame(output);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (this.cursor < this.scrollTop) {
|
|
135
|
+
this.scrollTop = this.cursor;
|
|
136
|
+
}
|
|
137
|
+
else if (this.cursor >= this.scrollTop + this.pageSize) {
|
|
138
|
+
this.scrollTop = this.cursor - this.pageSize + 1;
|
|
139
|
+
}
|
|
140
|
+
const visible = this.flatList.slice(this.scrollTop, this.scrollTop + this.pageSize);
|
|
141
|
+
visible.forEach((item, index) => {
|
|
142
|
+
const actualIndex = this.scrollTop + index;
|
|
143
|
+
const isSelected = actualIndex === this.cursor;
|
|
144
|
+
const indentSize = this.options.indent || 2;
|
|
145
|
+
const indentation = ' '.repeat(item.depth * indentSize);
|
|
146
|
+
const linePrefix = isSelected ? `${theme_1.theme.main}${symbols_1.symbols.pointer} ` : ' ';
|
|
147
|
+
let folderIcon = ' ';
|
|
148
|
+
const hasChildren = item.node.children && item.node.children.length > 0;
|
|
149
|
+
if (hasChildren) {
|
|
150
|
+
folderIcon = this.expandedNodes.has(item.node) ? `${this.ICON_OPEN} ` : `${this.ICON_CLOSED} `;
|
|
151
|
+
}
|
|
152
|
+
// Checkbox
|
|
153
|
+
let checkIcon = `[ ]`;
|
|
154
|
+
if (item.node.selected === true) {
|
|
155
|
+
checkIcon = `${theme_1.theme.success}[x]${ansi_1.ANSI.RESET}`;
|
|
156
|
+
}
|
|
157
|
+
else if (item.node.selected === 'indeterminate') {
|
|
158
|
+
checkIcon = `${ansi_1.ANSI.FG_YELLOW}[-]${ansi_1.ANSI.RESET}`;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
checkIcon = `${theme_1.theme.muted}[ ]${ansi_1.ANSI.RESET}`;
|
|
162
|
+
}
|
|
163
|
+
let title = item.node.title;
|
|
164
|
+
if (item.node.disabled) {
|
|
165
|
+
title = `${theme_1.theme.muted}${title} (disabled)${ansi_1.ANSI.RESET}`;
|
|
166
|
+
}
|
|
167
|
+
let line = `${indentation}${folderIcon}${checkIcon} ${title}`;
|
|
168
|
+
if (isSelected) {
|
|
169
|
+
line = `${theme_1.theme.main}${line}${ansi_1.ANSI.RESET}`;
|
|
170
|
+
}
|
|
171
|
+
output += linePrefix + line;
|
|
172
|
+
if (index < visible.length - 1)
|
|
173
|
+
output += '\n';
|
|
174
|
+
});
|
|
175
|
+
output += `\n${theme_1.theme.muted}(e: Expand All, c: Collapse All, Space: Toggle)${ansi_1.ANSI.RESET}`;
|
|
176
|
+
this.renderFrame(output);
|
|
177
|
+
}
|
|
178
|
+
handleInput(char, _key) {
|
|
179
|
+
if (this.flatList.length === 0)
|
|
180
|
+
return;
|
|
181
|
+
// 1. Priority: Navigation keys (ANSI sequences)
|
|
182
|
+
if (this.isUp(char)) {
|
|
183
|
+
this.cursor = (this.cursor - 1 + this.flatList.length) % this.flatList.length;
|
|
184
|
+
this.render(false);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (this.isDown(char)) {
|
|
188
|
+
this.cursor = (this.cursor + 1) % this.flatList.length;
|
|
189
|
+
this.render(false);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const currentItem = this.flatList[this.cursor];
|
|
193
|
+
if (!currentItem)
|
|
194
|
+
return;
|
|
195
|
+
const node = currentItem.node;
|
|
196
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
197
|
+
// 2. Right Arrow: Expand folder or move to first child
|
|
198
|
+
if (this.isRight(char)) {
|
|
199
|
+
if (hasChildren && !this.expandedNodes.has(node)) {
|
|
200
|
+
this.expandedNodes.add(node);
|
|
201
|
+
this.recalculateFlatList();
|
|
202
|
+
}
|
|
203
|
+
else if (this.cursor + 1 < this.flatList.length) {
|
|
204
|
+
this.cursor++;
|
|
205
|
+
}
|
|
206
|
+
this.render(false);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
// 3. Left Arrow: Collapse folder or move to parent
|
|
210
|
+
if (this.isLeft(char)) {
|
|
211
|
+
if (hasChildren && this.expandedNodes.has(node)) {
|
|
212
|
+
this.expandedNodes.delete(node);
|
|
213
|
+
this.recalculateFlatList();
|
|
214
|
+
}
|
|
215
|
+
else if (currentItem.parent) {
|
|
216
|
+
const parentIndex = this.flatList.findIndex(x => x.node === currentItem.parent);
|
|
217
|
+
if (parentIndex !== -1) {
|
|
218
|
+
this.cursor = parentIndex;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.render(false);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// 4. Space: Toggle selection
|
|
225
|
+
if (char === ' ') {
|
|
226
|
+
if (!node.disabled) {
|
|
227
|
+
const newState = node.selected === true ? false : true;
|
|
228
|
+
this.setNodeState(node, newState);
|
|
229
|
+
this.render(false);
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// 5. Functional keys (Keep it simple to avoid blocking)
|
|
234
|
+
if (char === 'e' && hasChildren) {
|
|
235
|
+
this.toggleRecursive(node, true);
|
|
236
|
+
this.recalculateFlatList();
|
|
237
|
+
this.render(false);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (char === 'c' && hasChildren) {
|
|
241
|
+
this.toggleRecursive(node, false);
|
|
242
|
+
this.recalculateFlatList();
|
|
243
|
+
this.render(false);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// 6. Submit: Enter
|
|
247
|
+
if (char === '\r' || char === '\n') {
|
|
248
|
+
const selectedValues = [];
|
|
249
|
+
this.collectSelected(this.options.data, selectedValues);
|
|
250
|
+
this.submit(selectedValues);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
collectSelected(nodes, result) {
|
|
254
|
+
for (const node of nodes) {
|
|
255
|
+
if (node.selected === true) {
|
|
256
|
+
result.push(node.value);
|
|
257
|
+
}
|
|
258
|
+
if (node.children) {
|
|
259
|
+
this.collectSelected(node.children, result);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
handleMouse(event) {
|
|
264
|
+
if (event.action === 'scroll') {
|
|
265
|
+
if (event.scroll === 'up') {
|
|
266
|
+
this.cursor = (this.cursor - 1 + this.flatList.length) % this.flatList.length;
|
|
267
|
+
this.render(false);
|
|
268
|
+
}
|
|
269
|
+
else if (event.scroll === 'down') {
|
|
270
|
+
this.cursor = (this.cursor + 1) % this.flatList.length;
|
|
271
|
+
this.render(false);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
exports.TreeSelectPrompt = TreeSelectPrompt;
|
|
277
|
+
TreeSelectPrompt.hasWarnedWindows = false; // Static flag to warn only once
|
package/dist/prompts/tree.d.ts
CHANGED
|
@@ -8,13 +8,13 @@ export declare class TreePrompt<V> extends Prompt<V, TreeOptions<V>> {
|
|
|
8
8
|
private readonly pageSize;
|
|
9
9
|
private readonly ICON_CLOSED;
|
|
10
10
|
private readonly ICON_OPEN;
|
|
11
|
-
private readonly ICON_LEAF;
|
|
12
11
|
constructor(options: TreeOptions<V>);
|
|
13
12
|
private expandPathTo;
|
|
14
13
|
private initializeExpanded;
|
|
15
14
|
private recalculateFlatList;
|
|
16
15
|
private traverse;
|
|
17
|
-
|
|
18
|
-
protected
|
|
16
|
+
private toggleRecursive;
|
|
17
|
+
protected render(_firstRender: boolean): void;
|
|
18
|
+
protected handleInput(char: string, _key: Buffer): void;
|
|
19
19
|
protected handleMouse(event: MouseEvent): void;
|
|
20
20
|
}
|
package/dist/prompts/tree.js
CHANGED
|
@@ -16,9 +16,7 @@ class TreePrompt extends base_1.Prompt {
|
|
|
16
16
|
// Icons
|
|
17
17
|
this.ICON_CLOSED = symbols_1.symbols.pointer === '>' ? '+' : '▸';
|
|
18
18
|
this.ICON_OPEN = symbols_1.symbols.pointer === '>' ? '-' : '▾';
|
|
19
|
-
this.ICON_LEAF = symbols_1.symbols.pointer === '>' ? ' ' : ' '; // No specific icon for leaf, just indentation
|
|
20
19
|
this.initializeExpanded(this.options.data);
|
|
21
|
-
// Handle initial value
|
|
22
20
|
if (this.options.initial !== undefined) {
|
|
23
21
|
this.expandPathTo(this.options.initial);
|
|
24
22
|
}
|
|
@@ -59,7 +57,6 @@ class TreePrompt extends base_1.Prompt {
|
|
|
59
57
|
recalculateFlatList() {
|
|
60
58
|
this.flatList = [];
|
|
61
59
|
this.traverse(this.options.data, 0, null);
|
|
62
|
-
// Adjust cursor if it went out of bounds (e.g. collapsing a folder above cursor)
|
|
63
60
|
if (this.cursor >= this.flatList.length) {
|
|
64
61
|
this.cursor = Math.max(0, this.flatList.length - 1);
|
|
65
62
|
}
|
|
@@ -76,14 +73,22 @@ class TreePrompt extends base_1.Prompt {
|
|
|
76
73
|
}
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
|
-
|
|
76
|
+
toggleRecursive(node, expand) {
|
|
77
|
+
if (expand)
|
|
78
|
+
this.expandedNodes.add(node);
|
|
79
|
+
else
|
|
80
|
+
this.expandedNodes.delete(node);
|
|
81
|
+
if (node.children) {
|
|
82
|
+
node.children.forEach(child => this.toggleRecursive(child, expand));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
render(_firstRender) {
|
|
80
86
|
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`;
|
|
81
87
|
if (this.flatList.length === 0) {
|
|
82
88
|
output += ` ${theme_1.theme.muted}No data${ansi_1.ANSI.RESET}`;
|
|
83
89
|
this.renderFrame(output);
|
|
84
90
|
return;
|
|
85
91
|
}
|
|
86
|
-
// Adjust Scroll
|
|
87
92
|
if (this.cursor < this.scrollTop) {
|
|
88
93
|
this.scrollTop = this.cursor;
|
|
89
94
|
}
|
|
@@ -94,10 +99,8 @@ class TreePrompt extends base_1.Prompt {
|
|
|
94
99
|
visible.forEach((item, index) => {
|
|
95
100
|
const actualIndex = this.scrollTop + index;
|
|
96
101
|
const isSelected = actualIndex === this.cursor;
|
|
97
|
-
// Indentation
|
|
98
102
|
const indentSize = this.options.indent || 2;
|
|
99
103
|
const indentation = ' '.repeat(item.depth * indentSize);
|
|
100
|
-
// Pointer
|
|
101
104
|
let linePrefix = '';
|
|
102
105
|
if (isSelected) {
|
|
103
106
|
linePrefix = `${theme_1.theme.main}${symbols_1.symbols.pointer} `;
|
|
@@ -105,8 +108,7 @@ class TreePrompt extends base_1.Prompt {
|
|
|
105
108
|
else {
|
|
106
109
|
linePrefix = ' ';
|
|
107
110
|
}
|
|
108
|
-
|
|
109
|
-
let icon = ' '; // Default 2 spaces for alignment
|
|
111
|
+
let icon = ' ';
|
|
110
112
|
const hasChildren = item.node.children && item.node.children.length > 0;
|
|
111
113
|
if (hasChildren) {
|
|
112
114
|
if (this.expandedNodes.has(item.node)) {
|
|
@@ -116,12 +118,10 @@ class TreePrompt extends base_1.Prompt {
|
|
|
116
118
|
icon = `${this.ICON_CLOSED} `;
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
|
-
// Title
|
|
120
121
|
let title = item.node.title;
|
|
121
122
|
if (item.node.disabled) {
|
|
122
123
|
title = `${theme_1.theme.muted}${title} (disabled)${ansi_1.ANSI.RESET}`;
|
|
123
124
|
}
|
|
124
|
-
// Compose line
|
|
125
125
|
let line = `${indentation}${icon}${title}`;
|
|
126
126
|
if (isSelected) {
|
|
127
127
|
line = `${theme_1.theme.main}${line}${ansi_1.ANSI.RESET}`;
|
|
@@ -130,12 +130,12 @@ class TreePrompt extends base_1.Prompt {
|
|
|
130
130
|
if (index < visible.length - 1)
|
|
131
131
|
output += '\n';
|
|
132
132
|
});
|
|
133
|
+
output += `\n${theme_1.theme.muted}(Arrows: Nav, e: Expand All, c: Collapse All)${ansi_1.ANSI.RESET}`;
|
|
133
134
|
this.renderFrame(output);
|
|
134
135
|
}
|
|
135
|
-
handleInput(char,
|
|
136
|
+
handleInput(char, _key) {
|
|
136
137
|
if (this.flatList.length === 0)
|
|
137
138
|
return;
|
|
138
|
-
// Navigation
|
|
139
139
|
if (this.isUp(char)) {
|
|
140
140
|
this.cursor = (this.cursor - 1 + this.flatList.length) % this.flatList.length;
|
|
141
141
|
this.render(false);
|
|
@@ -149,7 +149,20 @@ class TreePrompt extends base_1.Prompt {
|
|
|
149
149
|
const currentItem = this.flatList[this.cursor];
|
|
150
150
|
const node = currentItem.node;
|
|
151
151
|
const hasChildren = node.children && node.children.length > 0;
|
|
152
|
-
//
|
|
152
|
+
// Recursive Expand (e)
|
|
153
|
+
if (char === 'e' && hasChildren) {
|
|
154
|
+
this.toggleRecursive(node, true);
|
|
155
|
+
this.recalculateFlatList();
|
|
156
|
+
this.render(false);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Recursive Collapse (c)
|
|
160
|
+
if (char === 'c' && hasChildren) {
|
|
161
|
+
this.toggleRecursive(node, false);
|
|
162
|
+
this.recalculateFlatList();
|
|
163
|
+
this.render(false);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
153
166
|
if (this.isRight(char)) {
|
|
154
167
|
if (hasChildren) {
|
|
155
168
|
if (!this.expandedNodes.has(node)) {
|
|
@@ -157,7 +170,6 @@ class TreePrompt extends base_1.Prompt {
|
|
|
157
170
|
this.recalculateFlatList();
|
|
158
171
|
}
|
|
159
172
|
else {
|
|
160
|
-
// Go to first child (next item in flat list)
|
|
161
173
|
if (this.cursor + 1 < this.flatList.length) {
|
|
162
174
|
this.cursor++;
|
|
163
175
|
}
|
|
@@ -166,7 +178,6 @@ class TreePrompt extends base_1.Prompt {
|
|
|
166
178
|
return;
|
|
167
179
|
}
|
|
168
180
|
}
|
|
169
|
-
// Left: Collapse or Go Up (Parent)
|
|
170
181
|
if (this.isLeft(char)) {
|
|
171
182
|
if (hasChildren && this.expandedNodes.has(node)) {
|
|
172
183
|
this.expandedNodes.delete(node);
|
|
@@ -175,7 +186,6 @@ class TreePrompt extends base_1.Prompt {
|
|
|
175
186
|
return;
|
|
176
187
|
}
|
|
177
188
|
else {
|
|
178
|
-
// Go to parent
|
|
179
189
|
if (currentItem.parent) {
|
|
180
190
|
const parentIndex = this.flatList.findIndex(x => x.node === currentItem.parent);
|
|
181
191
|
if (parentIndex !== -1) {
|
|
@@ -186,7 +196,6 @@ class TreePrompt extends base_1.Prompt {
|
|
|
186
196
|
}
|
|
187
197
|
}
|
|
188
198
|
}
|
|
189
|
-
// Toggle (Space)
|
|
190
199
|
if (char === ' ') {
|
|
191
200
|
if (hasChildren) {
|
|
192
201
|
if (this.expandedNodes.has(node)) {
|
|
@@ -200,7 +209,6 @@ class TreePrompt extends base_1.Prompt {
|
|
|
200
209
|
}
|
|
201
210
|
return;
|
|
202
211
|
}
|
|
203
|
-
// Submit (Enter)
|
|
204
212
|
if (char === '\r' || char === '\n') {
|
|
205
213
|
if (!node.disabled) {
|
|
206
214
|
this.submit(node.value);
|