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,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
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { TreeOptions, MouseEvent } from '../types';
|
|
3
|
+
export declare class TreePrompt<V> extends Prompt<V, TreeOptions<V>> {
|
|
4
|
+
private cursor;
|
|
5
|
+
private expandedNodes;
|
|
6
|
+
private flatList;
|
|
7
|
+
private scrollTop;
|
|
8
|
+
private readonly pageSize;
|
|
9
|
+
private readonly ICON_CLOSED;
|
|
10
|
+
private readonly ICON_OPEN;
|
|
11
|
+
constructor(options: TreeOptions<V>);
|
|
12
|
+
private expandPathTo;
|
|
13
|
+
private initializeExpanded;
|
|
14
|
+
private recalculateFlatList;
|
|
15
|
+
private traverse;
|
|
16
|
+
private toggleRecursive;
|
|
17
|
+
protected render(_firstRender: boolean): void;
|
|
18
|
+
protected handleInput(char: string, _key: Buffer): void;
|
|
19
|
+
protected handleMouse(event: MouseEvent): void;
|
|
20
|
+
}
|