mepcli 0.5.5 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +173 -6
  2. package/dist/ansi.d.ts +1 -0
  3. package/dist/ansi.js +1 -0
  4. package/dist/base.d.ts +5 -34
  5. package/dist/base.js +65 -98
  6. package/dist/core.d.ts +23 -1
  7. package/dist/core.js +60 -0
  8. package/dist/highlight.d.ts +1 -0
  9. package/dist/highlight.js +40 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/input.js +26 -14
  13. package/dist/prompts/autocomplete.d.ts +1 -1
  14. package/dist/prompts/autocomplete.js +2 -7
  15. package/dist/prompts/calendar.d.ts +20 -0
  16. package/dist/prompts/calendar.js +329 -0
  17. package/dist/prompts/checkbox.d.ts +1 -1
  18. package/dist/prompts/checkbox.js +38 -8
  19. package/dist/prompts/code.d.ts +17 -0
  20. package/dist/prompts/code.js +210 -0
  21. package/dist/prompts/color.d.ts +14 -0
  22. package/dist/prompts/color.js +147 -0
  23. package/dist/prompts/confirm.d.ts +1 -1
  24. package/dist/prompts/confirm.js +1 -1
  25. package/dist/prompts/cron.d.ts +13 -0
  26. package/dist/prompts/cron.js +176 -0
  27. package/dist/prompts/date.d.ts +1 -1
  28. package/dist/prompts/date.js +15 -5
  29. package/dist/prompts/editor.js +2 -2
  30. package/dist/prompts/file.d.ts +7 -0
  31. package/dist/prompts/file.js +56 -60
  32. package/dist/prompts/form.d.ts +17 -0
  33. package/dist/prompts/form.js +225 -0
  34. package/dist/prompts/grid.d.ts +14 -0
  35. package/dist/prompts/grid.js +178 -0
  36. package/dist/prompts/keypress.d.ts +2 -2
  37. package/dist/prompts/keypress.js +2 -2
  38. package/dist/prompts/list.d.ts +1 -1
  39. package/dist/prompts/list.js +42 -22
  40. package/dist/prompts/multi-select.d.ts +1 -1
  41. package/dist/prompts/multi-select.js +39 -4
  42. package/dist/prompts/number.d.ts +1 -1
  43. package/dist/prompts/number.js +2 -2
  44. package/dist/prompts/range.d.ts +9 -0
  45. package/dist/prompts/range.js +140 -0
  46. package/dist/prompts/rating.d.ts +1 -1
  47. package/dist/prompts/rating.js +1 -1
  48. package/dist/prompts/select.d.ts +1 -1
  49. package/dist/prompts/select.js +1 -1
  50. package/dist/prompts/slider.d.ts +1 -1
  51. package/dist/prompts/slider.js +1 -1
  52. package/dist/prompts/snippet.d.ts +18 -0
  53. package/dist/prompts/snippet.js +203 -0
  54. package/dist/prompts/sort.d.ts +1 -1
  55. package/dist/prompts/sort.js +1 -4
  56. package/dist/prompts/spam.d.ts +17 -0
  57. package/dist/prompts/spam.js +62 -0
  58. package/dist/prompts/table.d.ts +1 -1
  59. package/dist/prompts/table.js +1 -1
  60. package/dist/prompts/text.d.ts +1 -0
  61. package/dist/prompts/text.js +13 -31
  62. package/dist/prompts/toggle.d.ts +1 -1
  63. package/dist/prompts/toggle.js +1 -1
  64. package/dist/prompts/transfer.d.ts +18 -0
  65. package/dist/prompts/transfer.js +203 -0
  66. package/dist/prompts/tree-select.d.ts +32 -0
  67. package/dist/prompts/tree-select.js +277 -0
  68. package/dist/prompts/tree.d.ts +3 -3
  69. package/dist/prompts/tree.js +27 -19
  70. package/dist/prompts/wait.d.ts +18 -0
  71. package/dist/prompts/wait.js +62 -0
  72. package/dist/types.d.ts +84 -0
  73. package/dist/utils.js +1 -1
  74. package/example.ts +150 -15
  75. package/package.json +2 -2
@@ -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
@@ -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
- protected render(firstRender: boolean): void;
18
- protected handleInput(char: string, key: Buffer): void;
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
  }
@@ -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
- render(firstRender) {
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
- // Folder Icon
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, key) {
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
- // Right: Expand or Go Down
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);