mepcli 0.6.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +512 -326
  3. package/dist/ansi.d.ts +8 -0
  4. package/dist/ansi.js +8 -0
  5. package/dist/base.d.ts +21 -0
  6. package/dist/base.js +57 -0
  7. package/dist/core.d.ts +48 -1
  8. package/dist/core.js +156 -0
  9. package/dist/data/licenses.d.ts +2 -0
  10. package/dist/data/licenses.js +109 -0
  11. package/dist/highlight.js +58 -26
  12. package/dist/index.d.ts +36 -0
  13. package/dist/index.js +36 -0
  14. package/dist/input.js +0 -3
  15. package/dist/prompts/box.d.ts +21 -0
  16. package/dist/prompts/box.js +192 -0
  17. package/dist/prompts/breadcrumb.d.ts +22 -0
  18. package/dist/prompts/breadcrumb.js +302 -0
  19. package/dist/prompts/byte.d.ts +13 -0
  20. package/dist/prompts/byte.js +159 -0
  21. package/dist/prompts/calculator.d.ts +16 -0
  22. package/dist/prompts/calculator.js +213 -0
  23. package/dist/prompts/calendar.js +0 -5
  24. package/dist/prompts/code.d.ts +2 -0
  25. package/dist/prompts/code.js +104 -70
  26. package/dist/prompts/connection-string.d.ts +18 -0
  27. package/dist/prompts/connection-string.js +97 -0
  28. package/dist/prompts/curl.d.ts +39 -0
  29. package/dist/prompts/curl.js +285 -0
  30. package/dist/prompts/data-inspector.d.ts +22 -0
  31. package/dist/prompts/data-inspector.js +256 -0
  32. package/dist/prompts/dependency.d.ts +16 -0
  33. package/dist/prompts/dependency.js +265 -0
  34. package/dist/prompts/dial.d.ts +10 -0
  35. package/dist/prompts/dial.js +110 -0
  36. package/dist/prompts/diff.d.ts +10 -0
  37. package/dist/prompts/diff.js +101 -0
  38. package/dist/prompts/draw.d.ts +20 -0
  39. package/dist/prompts/draw.js +188 -0
  40. package/dist/prompts/editor.js +0 -4
  41. package/dist/prompts/emoji.d.ts +18 -0
  42. package/dist/prompts/emoji.js +228 -0
  43. package/dist/prompts/exec.d.ts +13 -0
  44. package/dist/prompts/exec.js +83 -0
  45. package/dist/prompts/fuzzy.d.ts +12 -0
  46. package/dist/prompts/fuzzy.js +136 -0
  47. package/dist/prompts/gauge.d.ts +21 -0
  48. package/dist/prompts/gauge.js +130 -0
  49. package/dist/prompts/heatmap.d.ts +13 -0
  50. package/dist/prompts/heatmap.js +141 -0
  51. package/dist/prompts/ip.d.ts +11 -0
  52. package/dist/prompts/ip.js +118 -0
  53. package/dist/prompts/kanban.d.ts +17 -0
  54. package/dist/prompts/kanban.js +228 -0
  55. package/dist/prompts/keypress.js +0 -2
  56. package/dist/prompts/license.d.ts +9 -0
  57. package/dist/prompts/license.js +105 -0
  58. package/dist/prompts/map.d.ts +15 -0
  59. package/dist/prompts/map.js +199 -0
  60. package/dist/prompts/match.d.ts +19 -0
  61. package/dist/prompts/match.js +275 -0
  62. package/dist/prompts/miller.d.ts +15 -0
  63. package/dist/prompts/miller.js +221 -0
  64. package/dist/prompts/multi-column-select.d.ts +10 -0
  65. package/dist/prompts/multi-column-select.js +166 -0
  66. package/dist/prompts/number.js +0 -2
  67. package/dist/prompts/otp.d.ts +10 -0
  68. package/dist/prompts/otp.js +91 -0
  69. package/dist/prompts/pattern.d.ts +22 -0
  70. package/dist/prompts/pattern.js +249 -0
  71. package/dist/prompts/quiz-select.d.ts +10 -0
  72. package/dist/prompts/quiz-select.js +104 -0
  73. package/dist/prompts/quiz-text.d.ts +11 -0
  74. package/dist/prompts/quiz-text.js +82 -0
  75. package/dist/prompts/regex.d.ts +13 -0
  76. package/dist/prompts/regex.js +131 -0
  77. package/dist/prompts/region.d.ts +11 -0
  78. package/dist/prompts/region.js +164 -0
  79. package/dist/prompts/schedule.d.ts +18 -0
  80. package/dist/prompts/schedule.js +221 -0
  81. package/dist/prompts/scroll.d.ts +13 -0
  82. package/dist/prompts/scroll.js +152 -0
  83. package/dist/prompts/seat.d.ts +17 -0
  84. package/dist/prompts/seat.js +165 -0
  85. package/dist/prompts/select-range.d.ts +8 -0
  86. package/dist/prompts/select-range.js +136 -0
  87. package/dist/prompts/select.d.ts +9 -9
  88. package/dist/prompts/semver.d.ts +6 -0
  89. package/dist/prompts/semver.js +32 -0
  90. package/dist/prompts/shortcut.d.ts +9 -0
  91. package/dist/prompts/shortcut.js +135 -0
  92. package/dist/prompts/slot.d.ts +16 -0
  93. package/dist/prompts/slot.js +107 -0
  94. package/dist/prompts/snippet.js +0 -3
  95. package/dist/prompts/sort-grid.d.ts +16 -0
  96. package/dist/prompts/sort-grid.js +146 -0
  97. package/dist/prompts/sort.js +0 -1
  98. package/dist/prompts/spreadsheet.d.ts +21 -0
  99. package/dist/prompts/spreadsheet.js +239 -0
  100. package/dist/prompts/text.d.ts +9 -7
  101. package/dist/prompts/text.js +52 -0
  102. package/dist/prompts/time.d.ts +12 -0
  103. package/dist/prompts/time.js +202 -0
  104. package/dist/prompts/tree-select.d.ts +0 -1
  105. package/dist/prompts/tree-select.js +1 -5
  106. package/dist/symbols.d.ts +12 -0
  107. package/dist/symbols.js +14 -2
  108. package/dist/theme.js +10 -1
  109. package/dist/types.d.ts +264 -1
  110. package/dist/utils.d.ts +53 -0
  111. package/dist/utils.js +252 -0
  112. package/package.json +51 -47
  113. package/example.ts +0 -390
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MatchPrompt = 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 MatchPrompt extends base_1.Prompt {
10
+ constructor(options) {
11
+ super(options);
12
+ // Links: Source ID -> Set of Target IDs
13
+ this.links = new Map();
14
+ this.cursorSource = 0;
15
+ this.cursorTarget = 0;
16
+ this.scrollTopSource = 0;
17
+ this.scrollTopTarget = 0;
18
+ this.activeSide = 'source';
19
+ this.pickedSourceIndex = null;
20
+ this.pageSize = 10;
21
+ this.source = this.normalize(options.source);
22
+ this.target = this.normalize(options.target);
23
+ }
24
+ normalize(items) {
25
+ return items.map(item => {
26
+ if (typeof item === 'string') {
27
+ return { id: item, label: item, value: item };
28
+ }
29
+ return item;
30
+ });
31
+ }
32
+ render(_firstRender) {
33
+ const termWidth = process.stdout.columns || 80;
34
+ const colWidth = Math.floor((termWidth - 8) / 2); // -8 for middle spacing and borders
35
+ // Adjust Scroll Top
36
+ if (this.activeSide === 'source') {
37
+ if (this.cursorSource < this.scrollTopSource)
38
+ this.scrollTopSource = this.cursorSource;
39
+ if (this.cursorSource >= this.scrollTopSource + this.pageSize)
40
+ this.scrollTopSource = this.cursorSource - this.pageSize + 1;
41
+ }
42
+ else {
43
+ if (this.cursorTarget < this.scrollTopTarget)
44
+ this.scrollTopTarget = this.cursorTarget;
45
+ if (this.cursorTarget >= this.scrollTopTarget + this.pageSize)
46
+ this.scrollTopTarget = this.cursorTarget - this.pageSize + 1;
47
+ }
48
+ 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`;
49
+ // Headers
50
+ const sourceTitle = this.activeSide === 'source' ? `${theme_1.theme.main}Source${ansi_1.ANSI.RESET}` : 'Source';
51
+ const targetTitle = this.activeSide === 'target' ? `${theme_1.theme.main}Target${ansi_1.ANSI.RESET}` : 'Target';
52
+ output += ` ${sourceTitle}`.padEnd(colWidth + 2) + ' ' + ` ${targetTitle}\n`;
53
+ 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`;
54
+ // Render Rows
55
+ for (let i = 0; i < this.pageSize; i++) {
56
+ const idxSource = this.scrollTopSource + i;
57
+ const idxTarget = this.scrollTopTarget + i;
58
+ const itemSource = this.source[idxSource];
59
+ const itemTarget = this.target[idxTarget];
60
+ // Source Column
61
+ let sourceStr = '';
62
+ if (itemSource) {
63
+ const isSelected = this.activeSide === 'source' && idxSource === this.cursorSource;
64
+ const isPicked = this.pickedSourceIndex === idxSource;
65
+ const hasLinks = this.links.has(itemSource.id) && this.links.get(itemSource.id).size > 0;
66
+ let prefix = ' ';
67
+ if (isSelected)
68
+ prefix = `${theme_1.theme.main}${symbols_1.symbols.pointer} `;
69
+ if (isPicked)
70
+ prefix = `${theme_1.theme.success}${symbols_1.symbols.pointer} `;
71
+ let title = this.truncate(itemSource.label, colWidth - 4);
72
+ // Color logic
73
+ if (isPicked) {
74
+ title = `${theme_1.theme.success}${title}${ansi_1.ANSI.RESET}`;
75
+ }
76
+ else if (hasLinks) {
77
+ title = `${theme_1.theme.success}${title}${ansi_1.ANSI.RESET}`;
78
+ }
79
+ else if (isSelected) {
80
+ title = `${ansi_1.ANSI.FG_CYAN}${title}${ansi_1.ANSI.RESET}`;
81
+ }
82
+ // Indicator for links
83
+ const linkIndicator = hasLinks ? `${theme_1.theme.success}*${ansi_1.ANSI.RESET}` : ' ';
84
+ sourceStr = `${prefix}${title} ${linkIndicator}`;
85
+ // Highlight connected targets?
86
+ if (isSelected || isPicked) {
87
+ // This is handled in Target render
88
+ }
89
+ }
90
+ else {
91
+ sourceStr = '';
92
+ }
93
+ // Target Column
94
+ let targetStr = '';
95
+ if (itemTarget) {
96
+ const isSelected = this.activeSide === 'target' && idxTarget === this.cursorTarget;
97
+ // Determine if this target is linked to the RELEVANT source
98
+ // Relevant source is: Picked Source OR (if none picked) Cursor Source
99
+ const relevantSourceIdx = this.pickedSourceIndex !== null ? this.pickedSourceIndex : this.cursorSource;
100
+ const relevantSource = this.source[relevantSourceIdx];
101
+ let isLinkedToRelevant = false;
102
+ let isLinkedToAny = false;
103
+ // Check links
104
+ for (const [sId, tIds] of Array.from(this.links.entries())) {
105
+ if (tIds.has(itemTarget.id)) {
106
+ isLinkedToAny = true;
107
+ if (relevantSource && sId === relevantSource.id) {
108
+ isLinkedToRelevant = true;
109
+ }
110
+ }
111
+ }
112
+ let prefix = ' ';
113
+ if (isSelected)
114
+ prefix = `${theme_1.theme.main}${symbols_1.symbols.pointer} `;
115
+ let title = this.truncate(itemTarget.label, colWidth - 4);
116
+ if (isLinkedToRelevant) {
117
+ title = `${theme_1.theme.success}${title}${ansi_1.ANSI.RESET}`;
118
+ }
119
+ else if (isLinkedToAny) {
120
+ // Linked to someone else
121
+ title = `${ansi_1.ANSI.DIM}${title}${ansi_1.ANSI.RESET}`;
122
+ }
123
+ else if (isSelected) {
124
+ title = `${ansi_1.ANSI.FG_CYAN}${title}${ansi_1.ANSI.RESET}`;
125
+ }
126
+ // Indicator
127
+ const linkIndicator = isLinkedToRelevant ? `${theme_1.theme.success}<=${ansi_1.ANSI.RESET}` : (isLinkedToAny ? `${ansi_1.ANSI.DIM}<=${ansi_1.ANSI.RESET}` : ' ');
128
+ targetStr = `${linkIndicator} ${prefix}${title}`;
129
+ }
130
+ // Pad Source
131
+ const sourceVisualLen = itemSource ? ((0, utils_1.stringWidth)(this.stripAnsi(sourceStr))) : 0;
132
+ const padding = ' '.repeat(Math.max(0, colWidth - sourceVisualLen + 2)); // +2 extra space
133
+ output += sourceStr + padding + ' ' + targetStr + '\n';
134
+ }
135
+ // Instructions
136
+ if (this.pickedSourceIndex !== null) {
137
+ output += `\n${theme_1.theme.success}Linking: ${this.source[this.pickedSourceIndex].label}${ansi_1.ANSI.RESET}`;
138
+ output += `\n${ansi_1.ANSI.DIM}Select Target to Link/Unlink. Esc to Cancel.${ansi_1.ANSI.RESET}`;
139
+ }
140
+ else {
141
+ output += `\n${ansi_1.ANSI.DIM}Space to Pick Source, Enter to Submit.${ansi_1.ANSI.RESET}`;
142
+ }
143
+ this.renderFrame(output);
144
+ }
145
+ handleInput(char) {
146
+ // Navigation
147
+ if (this.isUp(char)) {
148
+ if (this.activeSide === 'source') {
149
+ this.cursorSource = Math.max(0, this.cursorSource - 1);
150
+ }
151
+ else {
152
+ this.cursorTarget = Math.max(0, this.cursorTarget - 1);
153
+ }
154
+ this.render(false);
155
+ return;
156
+ }
157
+ if (this.isDown(char)) {
158
+ if (this.activeSide === 'source') {
159
+ this.cursorSource = Math.min(this.source.length - 1, this.cursorSource + 1);
160
+ }
161
+ else {
162
+ this.cursorTarget = Math.min(this.target.length - 1, this.cursorTarget + 1);
163
+ }
164
+ this.render(false);
165
+ return;
166
+ }
167
+ // Space: Action
168
+ if (char === ' ') {
169
+ if (this.activeSide === 'source') {
170
+ // Pick Source
171
+ this.pickedSourceIndex = this.cursorSource;
172
+ this.activeSide = 'target';
173
+ // Try to find first linked target or stay at top?
174
+ // Stay at top or current pos is fine.
175
+ }
176
+ else {
177
+ // Toggle Link
178
+ if (this.pickedSourceIndex !== null) {
179
+ const sId = this.source[this.pickedSourceIndex].id;
180
+ const tId = this.target[this.cursorTarget].id;
181
+ if (!this.links.has(sId)) {
182
+ this.links.set(sId, new Set());
183
+ }
184
+ const sourceLinks = this.links.get(sId);
185
+ if (sourceLinks.has(tId)) {
186
+ // Unlink
187
+ sourceLinks.delete(tId);
188
+ }
189
+ else {
190
+ // Link
191
+ if (this.options.constraints?.oneToMany === false) {
192
+ for (const [otherSId, tIds] of Array.from(this.links.entries())) {
193
+ if (otherSId !== sId && tIds.has(tId)) {
194
+ tIds.delete(tId);
195
+ }
196
+ }
197
+ }
198
+ sourceLinks.add(tId);
199
+ }
200
+ // Stay in Target side to allow picking more?
201
+ }
202
+ }
203
+ this.render(false);
204
+ return;
205
+ }
206
+ // Enter
207
+ if (char === '\r' || char === '\n') {
208
+ // Check Required Constraint
209
+ if (this.options.constraints?.required) {
210
+ // Check if all sources have at least one link
211
+ const allLinked = this.source.every(s => {
212
+ return this.links.has(s.id) && this.links.get(s.id).size > 0;
213
+ });
214
+ if (!allLinked) {
215
+ return;
216
+ }
217
+ }
218
+ // Submit Result: Record<SourceID, TargetValue[]>
219
+ const result = {};
220
+ for (const [sId, tIds] of Array.from(this.links.entries())) {
221
+ if (tIds.size > 0) {
222
+ // Map Target IDs back to Values
223
+ const values = Array.from(tIds).map(tid => {
224
+ return this.target.find(t => t.id === tid)?.value;
225
+ });
226
+ result[sId] = values;
227
+ }
228
+ }
229
+ this.submit(result);
230
+ return;
231
+ }
232
+ // Escape / Backspace to go back to Source
233
+ if (char === '\u001b' || char === '\u0008' || char === '\x7f' || char === 'h') { // h for vim left
234
+ if (this.activeSide === 'target') {
235
+ this.activeSide = 'source';
236
+ this.pickedSourceIndex = null;
237
+ this.render(false);
238
+ }
239
+ return;
240
+ }
241
+ // Tab
242
+ if (char === '\t') {
243
+ this.activeSide = this.activeSide === 'source' ? 'target' : 'source';
244
+ // If switching to target without picking, pickedIndex is null.
245
+ // Just viewing mode.
246
+ if (this.activeSide === 'source') {
247
+ this.pickedSourceIndex = null;
248
+ }
249
+ this.render(false);
250
+ return;
251
+ }
252
+ }
253
+ handleMouse(event) {
254
+ if (event.action === 'scroll') {
255
+ if (event.scroll === 'up') {
256
+ if (this.activeSide === 'source') {
257
+ this.cursorSource = Math.max(0, this.cursorSource - 1);
258
+ }
259
+ else {
260
+ this.cursorTarget = Math.max(0, this.cursorTarget - 1);
261
+ }
262
+ }
263
+ else {
264
+ if (this.activeSide === 'source') {
265
+ this.cursorSource = Math.min(this.source.length - 1, this.cursorSource + 1);
266
+ }
267
+ else {
268
+ this.cursorTarget = Math.min(this.target.length - 1, this.cursorTarget + 1);
269
+ }
270
+ }
271
+ this.render(false);
272
+ }
273
+ }
274
+ }
275
+ exports.MatchPrompt = MatchPrompt;
@@ -0,0 +1,15 @@
1
+ import { Prompt } from '../base';
2
+ import { MillerOptions, MouseEvent } from '../types';
3
+ export declare class MillerPrompt<V> extends Prompt<V[], MillerOptions<V>> {
4
+ private selections;
5
+ private activeCol;
6
+ private scrollTops;
7
+ private colWidth;
8
+ private visibleCols;
9
+ constructor(options: MillerOptions<V>);
10
+ private calculateLayout;
11
+ private getColumnData;
12
+ protected render(_firstRender: boolean): void;
13
+ protected handleInput(char: string): void;
14
+ protected handleMouse(event: MouseEvent): void;
15
+ }
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MillerPrompt = void 0;
4
+ const base_1 = require("../base");
5
+ const theme_1 = require("../theme");
6
+ const ansi_1 = require("../ansi");
7
+ const symbols_1 = require("../symbols");
8
+ const utils_1 = require("../utils");
9
+ class MillerPrompt extends base_1.Prompt {
10
+ constructor(options) {
11
+ super(options);
12
+ this.selections = [];
13
+ this.activeCol = 0;
14
+ this.scrollTops = []; // Scroll position for each column
15
+ // Layout
16
+ this.colWidth = 0;
17
+ this.visibleCols = 3;
18
+ this.selections = [0];
19
+ this.scrollTops = [0];
20
+ // TODO: Map initial values to selections if provided
21
+ this.calculateLayout();
22
+ }
23
+ calculateLayout() {
24
+ const termWidth = process.stdout.columns || 80;
25
+ // We aim for 3 columns? Or based on width?
26
+ // Minimum width per column ~ 20?
27
+ this.visibleCols = Math.floor(termWidth / 20);
28
+ if (this.visibleCols < 1)
29
+ this.visibleCols = 1;
30
+ if (this.visibleCols > 4)
31
+ this.visibleCols = 4; // Cap at 4
32
+ this.colWidth = Math.floor((termWidth - 4) / this.visibleCols); // -4 for margins
33
+ }
34
+ getColumnData(depth) {
35
+ let current = this.options.data;
36
+ for (let i = 0; i < depth; i++) {
37
+ const sel = this.selections[i];
38
+ if (current[sel] && current[sel].children) {
39
+ current = current[sel].children;
40
+ }
41
+ else {
42
+ return undefined;
43
+ }
44
+ }
45
+ return current;
46
+ }
47
+ render(_firstRender) {
48
+ let output = '';
49
+ 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`;
50
+ let startCol = 0;
51
+ if (this.activeCol >= this.visibleCols) {
52
+ startCol = this.activeCol - this.visibleCols + 1;
53
+ }
54
+ // Construct columns content
55
+ const columnsToRender = [];
56
+ for (let c = startCol; c < startCol + this.visibleCols; c++) {
57
+ const data = this.getColumnData(c);
58
+ if (!data)
59
+ break;
60
+ const rows = [];
61
+ const selectedIdx = this.selections[c] ?? -1;
62
+ const isFocusedCol = c === this.activeCol;
63
+ // Scroll handling
64
+ const pageSize = 10; // Fixed height for now
65
+ if (!this.scrollTops[c])
66
+ this.scrollTops[c] = 0;
67
+ if (selectedIdx !== -1) {
68
+ if (selectedIdx < this.scrollTops[c]) {
69
+ this.scrollTops[c] = selectedIdx;
70
+ }
71
+ else if (selectedIdx >= this.scrollTops[c] + pageSize) {
72
+ this.scrollTops[c] = selectedIdx - pageSize + 1;
73
+ }
74
+ }
75
+ // Render rows
76
+ const start = this.scrollTops[c];
77
+ const end = Math.min(data.length, start + pageSize);
78
+ for (let i = start; i < end; i++) {
79
+ const item = data[i];
80
+ const isSelected = i === selectedIdx;
81
+ const isFocused = isSelected && isFocusedCol;
82
+ let prefix = ' ';
83
+ if (isSelected) {
84
+ prefix = isFocused ? `${theme_1.theme.main}${symbols_1.symbols.pointer}${ansi_1.ANSI.RESET}` : `${theme_1.theme.muted}${symbols_1.symbols.pointer}${ansi_1.ANSI.RESET}`;
85
+ }
86
+ let title = item.title;
87
+ // Truncate
88
+ if ((0, utils_1.stringWidth)(title) > this.colWidth - 4) {
89
+ title = title.slice(0, this.colWidth - 5) + '…';
90
+ }
91
+ let line = `${prefix} ${isSelected && isFocused ? theme_1.theme.main : ''}${title}${ansi_1.ANSI.RESET}`;
92
+ // Add arrow if children exist
93
+ if (item.children && item.children.length > 0) {
94
+ const pad = this.colWidth - (0, utils_1.stringWidth)((0, utils_1.stripAnsi)(line)) - 2;
95
+ line += ' '.repeat(Math.max(0, pad)) + `${theme_1.theme.muted}>${ansi_1.ANSI.RESET}`;
96
+ }
97
+ rows.push(line);
98
+ }
99
+ // Pad column to pageSize
100
+ while (rows.length < pageSize) {
101
+ rows.push('');
102
+ }
103
+ columnsToRender.push(rows);
104
+ }
105
+ // Combine columns side by side
106
+ const rowCount = 10;
107
+ for (let r = 0; r < rowCount; r++) {
108
+ let line = '';
109
+ for (let c = 0; c < columnsToRender.length; c++) {
110
+ let cell = columnsToRender[c][r] || '';
111
+ // Pad cell
112
+ const len = (0, utils_1.stringWidth)((0, utils_1.stripAnsi)(cell));
113
+ const pad = this.colWidth - len;
114
+ cell += ' '.repeat(Math.max(0, pad));
115
+ line += cell;
116
+ // Separator
117
+ if (c < columnsToRender.length - 1) {
118
+ line += `${theme_1.theme.muted}│${ansi_1.ANSI.RESET} `;
119
+ }
120
+ }
121
+ output += line + '\n';
122
+ }
123
+ // Breadcrumbs / Status
124
+ const pathTitles = [];
125
+ for (let i = 0; i <= this.activeCol; i++) {
126
+ const data = this.getColumnData(i);
127
+ if (data && typeof this.selections[i] === 'number') {
128
+ pathTitles.push(data[this.selections[i]].title);
129
+ }
130
+ }
131
+ output += `${theme_1.theme.muted}Path: ${pathTitles.join(' / ')}${ansi_1.ANSI.RESET}`;
132
+ this.renderFrame(output);
133
+ }
134
+ handleInput(char) {
135
+ const currentData = this.getColumnData(this.activeCol);
136
+ if (!currentData)
137
+ return; // Should not happen
138
+ // Enter
139
+ if (char === '\r' || char === '\n') {
140
+ // Collect values
141
+ const values = [];
142
+ for (let i = 0; i <= this.activeCol; i++) {
143
+ const d = this.getColumnData(i);
144
+ if (d)
145
+ values.push(d[this.selections[i]].value);
146
+ }
147
+ this.submit(values);
148
+ return;
149
+ }
150
+ if (this.isUp(char)) {
151
+ if (this.selections[this.activeCol] > 0) {
152
+ this.selections[this.activeCol]--;
153
+ // Reset subsequent columns
154
+ this.selections = this.selections.slice(0, this.activeCol + 1);
155
+ }
156
+ else {
157
+ this.selections[this.activeCol] = currentData.length - 1;
158
+ this.selections = this.selections.slice(0, this.activeCol + 1);
159
+ }
160
+ this.render(false);
161
+ return;
162
+ }
163
+ if (this.isDown(char)) {
164
+ if (this.selections[this.activeCol] < currentData.length - 1) {
165
+ this.selections[this.activeCol]++;
166
+ this.selections = this.selections.slice(0, this.activeCol + 1);
167
+ }
168
+ else {
169
+ this.selections[this.activeCol] = 0;
170
+ this.selections = this.selections.slice(0, this.activeCol + 1);
171
+ }
172
+ this.render(false);
173
+ return;
174
+ }
175
+ if (this.isRight(char) || char === '\t') {
176
+ // Expand
177
+ const idx = this.selections[this.activeCol];
178
+ const item = currentData[idx];
179
+ if (item.children && item.children.length > 0) {
180
+ this.activeCol++;
181
+ this.selections[this.activeCol] = 0;
182
+ this.render(false);
183
+ }
184
+ return;
185
+ }
186
+ if (this.isLeft(char) || char === '\x1b[Z') {
187
+ if (this.activeCol > 0) {
188
+ this.activeCol--;
189
+ this.render(false);
190
+ }
191
+ return;
192
+ }
193
+ }
194
+ handleMouse(event) {
195
+ if (event.action === 'scroll') {
196
+ const currentData = this.getColumnData(this.activeCol);
197
+ if (!currentData || currentData.length === 0)
198
+ return;
199
+ if (event.scroll === 'up') {
200
+ if (this.selections[this.activeCol] > 0) {
201
+ this.selections[this.activeCol]--;
202
+ }
203
+ else {
204
+ this.selections[this.activeCol] = currentData.length - 1;
205
+ }
206
+ }
207
+ else if (event.scroll === 'down') {
208
+ if (this.selections[this.activeCol] < currentData.length - 1) {
209
+ this.selections[this.activeCol]++;
210
+ }
211
+ else {
212
+ this.selections[this.activeCol] = 0;
213
+ }
214
+ }
215
+ // Reset subsequent columns
216
+ this.selections = this.selections.slice(0, this.activeCol + 1);
217
+ this.render(false);
218
+ }
219
+ }
220
+ }
221
+ exports.MillerPrompt = MillerPrompt;
@@ -0,0 +1,10 @@
1
+ import { SelectPrompt } from './select';
2
+ import { MultiColumnSelectOptions } from '../types';
3
+ export declare class MultiColumnSelectPrompt<V> extends SelectPrompt<V, MultiColumnSelectOptions<V>> {
4
+ private cols;
5
+ private colWidth;
6
+ constructor(options: MultiColumnSelectOptions<V>);
7
+ private calculateLayout;
8
+ protected render(_firstRender: boolean): void;
9
+ protected handleInput(char: string): void;
10
+ }
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultiColumnSelectPrompt = void 0;
4
+ const select_1 = require("./select");
5
+ const theme_1 = require("../theme");
6
+ const ansi_1 = require("../ansi");
7
+ const utils_1 = require("../utils");
8
+ const symbols_1 = require("../symbols");
9
+ class MultiColumnSelectPrompt extends select_1.SelectPrompt {
10
+ constructor(options) {
11
+ super(options);
12
+ this.cols = 1;
13
+ this.colWidth = 0;
14
+ this.calculateLayout();
15
+ }
16
+ calculateLayout() {
17
+ const termWidth = process.stdout.columns || 80;
18
+ const choices = this.options.choices.filter(c => !this.isSeparator(c));
19
+ if (choices.length === 0) {
20
+ this.cols = 1;
21
+ this.colWidth = termWidth;
22
+ return;
23
+ }
24
+ // Calculate max item width
25
+ let maxLen = 0;
26
+ for (const c of choices) {
27
+ const len = (0, utils_1.stringWidth)(c.title);
28
+ if (len > maxLen)
29
+ maxLen = len;
30
+ }
31
+ // Add padding (pointer + space + item + space)
32
+ const itemWidth = maxLen + 4;
33
+ if (typeof this.options.cols === 'number') {
34
+ this.cols = this.options.cols;
35
+ }
36
+ else {
37
+ // Auto
38
+ this.cols = Math.floor(termWidth / itemWidth);
39
+ if (this.cols < 1)
40
+ this.cols = 1;
41
+ }
42
+ // Final column width
43
+ this.colWidth = Math.floor(termWidth / this.cols);
44
+ }
45
+ render(_firstRender) {
46
+ let output = '';
47
+ const choices = this.getFilteredChoices();
48
+ // Header
49
+ const searchStr = this.searchBuffer ? ` ${theme_1.theme.muted}(Filter: ${this.searchBuffer})${ansi_1.ANSI.RESET}` : '';
50
+ output += `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}${searchStr}\n`;
51
+ if (choices.length === 0) {
52
+ output += ` ${theme_1.theme.muted}No results found${ansi_1.ANSI.RESET}`;
53
+ this.renderFrame(output);
54
+ return;
55
+ }
56
+ // Grid Render
57
+ const totalRows = Math.ceil(choices.length / this.cols);
58
+ // Adjust Scroll
59
+ const currentRow = Math.floor(this.selectedIndex / this.cols);
60
+ if (currentRow < this.scrollTop) {
61
+ this.scrollTop = currentRow;
62
+ }
63
+ else if (currentRow >= this.scrollTop + this.pageSize) {
64
+ this.scrollTop = currentRow - this.pageSize + 1;
65
+ }
66
+ // Edge case: if list shrinks
67
+ if (this.scrollTop > totalRows - 1) {
68
+ this.scrollTop = Math.max(0, totalRows - this.pageSize);
69
+ }
70
+ const startRow = this.scrollTop;
71
+ const endRow = Math.min(totalRows, startRow + this.pageSize);
72
+ for (let r = startRow; r < endRow; r++) {
73
+ let rowStr = '';
74
+ for (let c = 0; c < this.cols; c++) {
75
+ const idx = r * this.cols + c;
76
+ if (idx >= choices.length)
77
+ break;
78
+ const choice = choices[idx];
79
+ // Truncate if needed
80
+ let title = choice.title || '';
81
+ if ((0, utils_1.stringWidth)(title) > this.colWidth - 3) {
82
+ title = title.slice(0, this.colWidth - 4) + '…';
83
+ }
84
+ let cellContent = '';
85
+ if (idx === this.selectedIndex) {
86
+ cellContent = `${theme_1.theme.main}${symbols_1.symbols.pointer} ${title}${ansi_1.ANSI.RESET}`;
87
+ }
88
+ else {
89
+ cellContent = ` ${title}`;
90
+ }
91
+ // Pad cell to colWidth
92
+ const currentWidth = (0, utils_1.stringWidth)(this.stripAnsi(cellContent));
93
+ const padding = Math.max(0, this.colWidth - currentWidth);
94
+ // Add cell to row
95
+ rowStr += cellContent + ' '.repeat(padding);
96
+ }
97
+ output += rowStr + '\n';
98
+ }
99
+ // Remove trailing newline
100
+ if (output.endsWith('\n'))
101
+ output = output.slice(0, -1);
102
+ this.renderFrame(output);
103
+ }
104
+ handleInput(char) {
105
+ const choices = this.getFilteredChoices();
106
+ if (char === '\r' || char === '\n') {
107
+ if (choices.length > 0) {
108
+ this.submit(choices[this.selectedIndex].value);
109
+ }
110
+ return;
111
+ }
112
+ if (this.isUp(char)) {
113
+ const newIndex = this.selectedIndex - this.cols;
114
+ if (newIndex >= 0) {
115
+ this.selectedIndex = newIndex;
116
+ }
117
+ this.render(false);
118
+ return;
119
+ }
120
+ if (this.isDown(char)) {
121
+ const newIndex = this.selectedIndex + this.cols;
122
+ if (newIndex < choices.length) {
123
+ this.selectedIndex = newIndex;
124
+ }
125
+ this.render(false);
126
+ return;
127
+ }
128
+ if (this.isLeft(char)) {
129
+ if (this.selectedIndex > 0) {
130
+ this.selectedIndex--;
131
+ }
132
+ else {
133
+ this.selectedIndex = choices.length - 1;
134
+ }
135
+ this.render(false);
136
+ return;
137
+ }
138
+ if (this.isRight(char)) {
139
+ if (this.selectedIndex < choices.length - 1) {
140
+ this.selectedIndex++;
141
+ }
142
+ else {
143
+ this.selectedIndex = 0;
144
+ }
145
+ this.render(false);
146
+ return;
147
+ }
148
+ // Backspace
149
+ if (char === '\u0008' || char === '\x7f') {
150
+ if (this.searchBuffer.length > 0) {
151
+ this.searchBuffer = this.searchBuffer.slice(0, -1);
152
+ this.selectedIndex = 0;
153
+ this.calculateLayout();
154
+ this.render(false);
155
+ }
156
+ return;
157
+ }
158
+ // Typing
159
+ if (char.length === 1 && !/^[\x00-\x1F]/.test(char) && !char.startsWith('\x1b')) {
160
+ this.searchBuffer += char;
161
+ this.selectedIndex = 0;
162
+ this.render(false);
163
+ }
164
+ }
165
+ }
166
+ exports.MultiColumnSelectPrompt = MultiColumnSelectPrompt;