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,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KanbanPrompt = void 0;
4
+ const base_1 = require("../base");
5
+ const ansi_1 = require("../ansi");
6
+ const utils_1 = require("../utils");
7
+ class KanbanPrompt extends base_1.Prompt {
8
+ constructor(options) {
9
+ super(options);
10
+ this.activeCol = 0;
11
+ this.activeRow = 0;
12
+ this.grabbed = false;
13
+ // Deep copy columns to avoid mutating original options during drag/drop
14
+ this.columns = JSON.parse(JSON.stringify(options.columns));
15
+ this.scrollStates = new Array(this.columns.length).fill(0);
16
+ }
17
+ render(_firstRender) {
18
+ const { columns } = this.stdout; // terminal width
19
+ const colCount = this.columns.length;
20
+ const colWidth = Math.floor(columns / colCount);
21
+ let output = '';
22
+ // Render Title
23
+ output += `${ansi_1.ANSI.FG_CYAN}${ansi_1.ANSI.BOLD}? ${this.options.message}${ansi_1.ANSI.RESET}\n`;
24
+ if (this.grabbed) {
25
+ output += `${ansi_1.ANSI.FG_YELLOW}(Grabbed) Move with arrows, Space to Drop${ansi_1.ANSI.RESET}\n`;
26
+ }
27
+ else {
28
+ output += `${ansi_1.ANSI.FG_GRAY}(Normal) Space to Grab, Arrows to Navigate, Enter to Submit${ansi_1.ANSI.RESET}\n`;
29
+ }
30
+ // Render Headers
31
+ const headers = this.columns.map((col, i) => {
32
+ const isSelected = i === this.activeCol;
33
+ const title = this.truncate(col.title, colWidth - 4);
34
+ const style = isSelected ? `${ansi_1.ANSI.FG_BLUE}${ansi_1.ANSI.BOLD}` : ansi_1.ANSI.BOLD;
35
+ return this.padCenter(title, colWidth, style);
36
+ });
37
+ output += headers.join('') + '\n';
38
+ // Render Separator
39
+ output += ansi_1.ANSI.FG_GRAY + '─'.repeat(columns) + ansi_1.ANSI.RESET + '\n';
40
+ // Render Rows
41
+ const viewportHeight = 10;
42
+ for (let r = 0; r < viewportHeight; r++) {
43
+ const rowLine = this.columns.map((col, cIndex) => {
44
+ const scrollTop = this.scrollStates[cIndex];
45
+ const itemIndex = r + scrollTop;
46
+ const item = col.items[itemIndex];
47
+ let content = '';
48
+ if (item) {
49
+ const isCursor = cIndex === this.activeCol && itemIndex === this.activeRow;
50
+ let prefix = ' ';
51
+ const suffix = ' ';
52
+ let style = '';
53
+ if (isCursor) {
54
+ if (this.grabbed) {
55
+ style = ansi_1.ANSI.BG_YELLOW + ansi_1.ANSI.FG_BLACK;
56
+ prefix = '>';
57
+ }
58
+ else {
59
+ style = ansi_1.ANSI.FG_CYAN + ansi_1.ANSI.BOLD;
60
+ prefix = '>';
61
+ }
62
+ }
63
+ const title = this.truncate(item.title, colWidth - 4);
64
+ content = `${prefix} ${title}${suffix}`;
65
+ content = content.padEnd(colWidth);
66
+ const plain = `${prefix} ${title}${suffix}`.padEnd(colWidth);
67
+ if (style) {
68
+ content = style + plain + ansi_1.ANSI.RESET;
69
+ }
70
+ else {
71
+ content = plain;
72
+ }
73
+ }
74
+ else {
75
+ // Empty slot
76
+ content = ' '.repeat(colWidth);
77
+ }
78
+ return content;
79
+ }).join('');
80
+ output += rowLine + '\n';
81
+ }
82
+ this.renderFrame(output);
83
+ }
84
+ padCenter(str, width, style = '') {
85
+ const visibleLen = (0, utils_1.stringWidth)(str);
86
+ if (visibleLen >= width)
87
+ return style + str + ansi_1.ANSI.RESET;
88
+ const left = Math.floor((width - visibleLen) / 2);
89
+ const right = width - visibleLen - left;
90
+ return ' '.repeat(left) + style + str + ansi_1.ANSI.RESET + ' '.repeat(right);
91
+ }
92
+ handleMouse(event) {
93
+ if (event.action === 'scroll') {
94
+ if (this.grabbed) {
95
+ // Grabbed: Scroll moves Left/Right
96
+ if (event.scroll === 'up') {
97
+ // Up/Left
98
+ if (this.activeCol > 0) {
99
+ this.moveItemHorizontal(-1);
100
+ this.activeCol--;
101
+ this.clampRow();
102
+ this.ensureVisible();
103
+ }
104
+ }
105
+ else if (event.scroll === 'down') {
106
+ // Down/Right
107
+ if (this.activeCol < this.columns.length - 1) {
108
+ this.moveItemHorizontal(1);
109
+ this.activeCol++;
110
+ this.clampRow();
111
+ this.ensureVisible();
112
+ }
113
+ }
114
+ }
115
+ else {
116
+ // Normal: Scroll moves Up/Down
117
+ if (event.scroll === 'up') {
118
+ if (this.activeRow > 0) {
119
+ this.activeRow--;
120
+ this.ensureVisible();
121
+ }
122
+ }
123
+ else if (event.scroll === 'down') {
124
+ const colLen = this.columns[this.activeCol].items.length;
125
+ if (this.activeRow < colLen - 1) {
126
+ this.activeRow++;
127
+ this.ensureVisible();
128
+ }
129
+ }
130
+ }
131
+ this.render(false);
132
+ }
133
+ }
134
+ handleInput(char, _key) {
135
+ if (char === '\r' || char === '\n') { // Enter
136
+ const result = {};
137
+ this.columns.forEach(c => {
138
+ result[c.id] = c.items;
139
+ });
140
+ this.submit(result);
141
+ return;
142
+ }
143
+ if (char === ' ') { // Space
144
+ this.grabbed = !this.grabbed;
145
+ this.render(false);
146
+ return;
147
+ }
148
+ if (this.isLeft(char)) {
149
+ if (this.activeCol > 0) {
150
+ if (this.grabbed) {
151
+ this.moveItemHorizontal(-1);
152
+ }
153
+ this.activeCol--;
154
+ this.clampRow();
155
+ this.ensureVisible();
156
+ }
157
+ }
158
+ else if (this.isRight(char)) {
159
+ if (this.activeCol < this.columns.length - 1) {
160
+ if (this.grabbed) {
161
+ this.moveItemHorizontal(1);
162
+ }
163
+ this.activeCol++;
164
+ this.clampRow();
165
+ this.ensureVisible();
166
+ }
167
+ }
168
+ else if (this.isUp(char)) {
169
+ if (this.activeRow > 0) {
170
+ if (this.grabbed) {
171
+ // Swap with prev
172
+ const col = this.columns[this.activeCol];
173
+ const temp = col.items[this.activeRow];
174
+ col.items[this.activeRow] = col.items[this.activeRow - 1];
175
+ col.items[this.activeRow - 1] = temp;
176
+ }
177
+ this.activeRow--;
178
+ this.ensureVisible();
179
+ }
180
+ }
181
+ else if (this.isDown(char)) {
182
+ const colLen = this.columns[this.activeCol].items.length;
183
+ if (this.activeRow < colLen - 1) {
184
+ if (this.grabbed) {
185
+ // Swap with next
186
+ const col = this.columns[this.activeCol];
187
+ const temp = col.items[this.activeRow];
188
+ col.items[this.activeRow] = col.items[this.activeRow + 1];
189
+ col.items[this.activeRow + 1] = temp;
190
+ }
191
+ this.activeRow++;
192
+ this.ensureVisible();
193
+ }
194
+ }
195
+ this.render(false);
196
+ }
197
+ moveItemHorizontal(direction) {
198
+ const sourceCol = this.columns[this.activeCol];
199
+ const targetCol = this.columns[this.activeCol + direction];
200
+ const item = sourceCol.items.splice(this.activeRow, 1)[0];
201
+ let targetIndex = this.activeRow;
202
+ if (targetIndex > targetCol.items.length) {
203
+ targetIndex = targetCol.items.length;
204
+ }
205
+ targetCol.items.splice(targetIndex, 0, item);
206
+ this.activeRow = targetIndex;
207
+ }
208
+ clampRow() {
209
+ const len = this.columns[this.activeCol].items.length;
210
+ if (len === 0) {
211
+ this.activeRow = 0;
212
+ }
213
+ else if (this.activeRow >= len) {
214
+ this.activeRow = len - 1;
215
+ }
216
+ }
217
+ ensureVisible() {
218
+ const scrollTop = this.scrollStates[this.activeCol];
219
+ const viewportHeight = 10;
220
+ if (this.activeRow < scrollTop) {
221
+ this.scrollStates[this.activeCol] = this.activeRow;
222
+ }
223
+ else if (this.activeRow >= scrollTop + viewportHeight) {
224
+ this.scrollStates[this.activeCol] = this.activeRow - viewportHeight + 1;
225
+ }
226
+ }
227
+ }
228
+ exports.KanbanPrompt = KanbanPrompt;
@@ -18,8 +18,6 @@ class KeypressPrompt extends base_1.Prompt {
18
18
  return 'space';
19
19
  return k;
20
20
  }).join('/');
21
- // Only show hint if it's short enough to be helpful, or always?
22
- // Let's always show it if provided, or maybe just dimmed.
23
21
  output += ` ${theme_1.theme.muted}(${hint})${ansi_1.ANSI.RESET}`;
24
22
  }
25
23
  else {
@@ -0,0 +1,9 @@
1
+ import { SelectPrompt } from './select';
2
+ import { LicenseOptions } from '../types';
3
+ export declare class LicensePrompt extends SelectPrompt<string> {
4
+ private licenses;
5
+ constructor(options: LicenseOptions);
6
+ protected render(_firstRender: boolean): void;
7
+ private renderList;
8
+ private renderDetails;
9
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LicensePrompt = void 0;
4
+ const ansi_1 = require("../ansi");
5
+ const select_1 = require("./select");
6
+ const theme_1 = require("../theme");
7
+ const symbols_1 = require("../symbols");
8
+ const utils_1 = require("../utils");
9
+ const licenses_1 = require("../data/licenses");
10
+ class LicensePrompt extends select_1.SelectPrompt {
11
+ constructor(options) {
12
+ // Map licenses to choices expected by SelectPrompt
13
+ const choices = licenses_1.POPULAR_LICENSES.map(l => ({
14
+ title: l.id,
15
+ value: l.id,
16
+ description: l.name
17
+ }));
18
+ super({
19
+ ...options,
20
+ choices
21
+ });
22
+ this.licenses = licenses_1.POPULAR_LICENSES;
23
+ // Set initial selection if provided
24
+ if (options.defaultLicense) {
25
+ const idx = this.licenses.findIndex(l => l.id === options.defaultLicense);
26
+ if (idx !== -1) {
27
+ this.selectedIndex = idx;
28
+ }
29
+ }
30
+ }
31
+ render(_firstRender) {
32
+ const width = this.stdout.columns || 80;
33
+ const gap = 2;
34
+ const ratio = 0.3;
35
+ // Calculate dimensions
36
+ // Left column: 30% or min 20 chars
37
+ // Right column: rest
38
+ const leftWidth = Math.floor((width - gap) * ratio);
39
+ const rightWidth = width - leftWidth - gap;
40
+ const leftContent = this.renderList();
41
+ const rightContent = this.renderDetails(rightWidth);
42
+ const content = utils_1.Layout.split(leftContent, rightContent, width, { ratio, gap });
43
+ // Header
44
+ const header = `${theme_1.theme.success}? ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${theme_1.theme.muted}(Use arrows to navigate, Enter to select)${ansi_1.ANSI.RESET}`;
45
+ // Combine
46
+ this.renderFrame(`${header}\n${content}`);
47
+ }
48
+ renderList() {
49
+ const selectedIndex = this.selectedIndex;
50
+ const scrollTop = this.scrollTop;
51
+ const pageSize = this.pageSize;
52
+ const choices = this.options.choices; // from super
53
+ let output = '';
54
+ const visibleChoices = choices.slice(scrollTop, scrollTop + pageSize);
55
+ visibleChoices.forEach((choice, index) => {
56
+ const actualIndex = scrollTop + index;
57
+ const isSelected = actualIndex === selectedIndex;
58
+ const cursor = isSelected ? `${theme_1.theme.main}${symbols_1.symbols.pointer}` : ' ';
59
+ // Handle Separator type safety
60
+ if ('separator' in choice) {
61
+ output += `${theme_1.theme.muted}${choice.text || '---'}${ansi_1.ANSI.RESET}\n`;
62
+ return;
63
+ }
64
+ const title = isSelected ? `${theme_1.theme.main}${choice.title}${ansi_1.ANSI.RESET}` : choice.title;
65
+ output += `${cursor} ${title}\n`;
66
+ });
67
+ // Fill remaining lines to maintain height
68
+ const filledLines = visibleChoices.length;
69
+ if (filledLines < pageSize) {
70
+ output += '\n'.repeat(pageSize - filledLines);
71
+ }
72
+ return output;
73
+ }
74
+ renderDetails(maxWidth) {
75
+ const selectedIndex = this.selectedIndex;
76
+ const license = this.licenses[selectedIndex];
77
+ if (!license)
78
+ return '';
79
+ let output = '';
80
+ output += `${ansi_1.ANSI.BOLD}${license.name}${ansi_1.ANSI.RESET}\n`;
81
+ const wrappedDesc = utils_1.Layout.wrap(license.description, maxWidth);
82
+ const coloredDesc = wrappedDesc.split('\n').map(line => `${theme_1.theme.muted}${line}${ansi_1.ANSI.RESET}`).join('\n');
83
+ output += `${coloredDesc}\n\n`;
84
+ // Permissions
85
+ if (license.permissions.length > 0) {
86
+ output += `${ansi_1.ANSI.FG_GREEN}Permissions:${ansi_1.ANSI.RESET}\n`;
87
+ license.permissions.forEach(p => output += ` ${theme_1.theme.success}${symbols_1.symbols.checked} ${p}${ansi_1.ANSI.RESET}\n`);
88
+ output += '\n';
89
+ }
90
+ // Conditions
91
+ if (license.conditions.length > 0) {
92
+ output += `${ansi_1.ANSI.FG_BLUE}Conditions:${ansi_1.ANSI.RESET}\n`;
93
+ // symbols.info doesn't exist, use 'i' or similar
94
+ license.conditions.forEach(c => output += ` ${ansi_1.ANSI.FG_BLUE}ℹ ${c}${ansi_1.ANSI.RESET}\n`);
95
+ output += '\n';
96
+ }
97
+ // Limitations
98
+ if (license.limitations.length > 0) {
99
+ output += `${ansi_1.ANSI.FG_RED}Limitations:${ansi_1.ANSI.RESET}\n`;
100
+ license.limitations.forEach(l => output += ` ${theme_1.theme.error}${symbols_1.symbols.cross} ${l}${ansi_1.ANSI.RESET}\n`);
101
+ }
102
+ return output;
103
+ }
104
+ }
105
+ exports.LicensePrompt = LicensePrompt;
@@ -0,0 +1,15 @@
1
+ import { Prompt } from '../base';
2
+ import { MapOptions, MouseEvent } from '../types';
3
+ export declare class MapPrompt extends Prompt<Record<string, string>, MapOptions> {
4
+ private items;
5
+ private rowIndex;
6
+ private colIndex;
7
+ private scrollTop;
8
+ private readonly pageSize;
9
+ private errorMsg;
10
+ constructor(options: MapOptions);
11
+ protected render(_firstRender: boolean): void;
12
+ private pad;
13
+ protected handleInput(char: string): void;
14
+ protected handleMouse(event: MouseEvent): void;
15
+ }
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MapPrompt = 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 MapPrompt extends base_1.Prompt {
10
+ constructor(options) {
11
+ super(options);
12
+ this.items = [];
13
+ this.rowIndex = 0;
14
+ this.colIndex = 0; // 0: Key, 1: Value
15
+ this.scrollTop = 0;
16
+ this.pageSize = 7;
17
+ this.errorMsg = '';
18
+ if (options.initial) {
19
+ this.items = Object.entries(options.initial).map(([key, value]) => ({ key, value }));
20
+ }
21
+ if (this.items.length === 0) {
22
+ this.items.push({ key: '', value: '' });
23
+ }
24
+ }
25
+ render(_firstRender) {
26
+ let output = '';
27
+ // Title
28
+ const icon = this.errorMsg ? `${theme_1.theme.error}${symbols_1.symbols.cross}` : `${theme_1.theme.success}?`;
29
+ output += `${icon} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}\n`;
30
+ // Layout Calculation
31
+ const maxKeyWidth = Math.max(5, // Minimum width
32
+ ...this.items.map(item => (0, utils_1.stringWidth)(item.key))) + 2; // Padding
33
+ // Scrolling Logic
34
+ if (this.rowIndex < this.scrollTop) {
35
+ this.scrollTop = this.rowIndex;
36
+ }
37
+ else if (this.rowIndex >= this.scrollTop + this.pageSize) {
38
+ this.scrollTop = this.rowIndex - this.pageSize + 1;
39
+ }
40
+ // Clamping
41
+ const maxScroll = Math.max(0, this.items.length - this.pageSize);
42
+ this.scrollTop = Math.min(this.scrollTop, maxScroll);
43
+ // Header
44
+ const keyHeader = this.pad('Key', maxKeyWidth);
45
+ output += ` ${ansi_1.ANSI.BOLD}${keyHeader}Value${ansi_1.ANSI.RESET}\n`;
46
+ // Rows
47
+ const visibleItems = this.items.slice(this.scrollTop, this.scrollTop + this.pageSize);
48
+ visibleItems.forEach((item, index) => {
49
+ const actualIndex = this.scrollTop + index;
50
+ if (index > 0)
51
+ output += '\n';
52
+ const isRowActive = actualIndex === this.rowIndex;
53
+ const pointer = isRowActive ? `${theme_1.theme.main}${symbols_1.symbols.pointer}${ansi_1.ANSI.RESET} ` : ' ';
54
+ // Render Key
55
+ let keyStr = item.key;
56
+ if (isRowActive && this.colIndex === 0) {
57
+ keyStr = `${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${keyStr}${ansi_1.ANSI.RESET}`;
58
+ }
59
+ // Adjust padding manually because ANSI codes mess up simple padEnd
60
+ const keyVisualWidth = (0, utils_1.stringWidth)(item.key);
61
+ const padding = ' '.repeat(Math.max(0, maxKeyWidth - keyVisualWidth));
62
+ // Render Value
63
+ let valStr = item.value;
64
+ if (isRowActive && this.colIndex === 1) {
65
+ valStr = `${theme_1.theme.main}${ansi_1.ANSI.UNDERLINE}${valStr || ' '}${ansi_1.ANSI.RESET}`;
66
+ }
67
+ output += `${pointer}${keyStr}${padding}${valStr}`;
68
+ });
69
+ // Instructions
70
+ output += `\n${ansi_1.ANSI.DIM}(Ctrl+N: Add, Ctrl+D: Del)${ansi_1.ANSI.RESET}`;
71
+ if (this.errorMsg) {
72
+ output += `\n${theme_1.theme.error}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`;
73
+ }
74
+ this.renderFrame(output);
75
+ }
76
+ pad(str, width) {
77
+ const len = (0, utils_1.stringWidth)(str);
78
+ if (len >= width)
79
+ return str;
80
+ return str + ' '.repeat(width - len);
81
+ }
82
+ handleInput(char) {
83
+ this.errorMsg = '';
84
+ // Navigation
85
+ if (this.isUp(char)) {
86
+ if (this.rowIndex > 0)
87
+ this.rowIndex--;
88
+ this.render(false);
89
+ return;
90
+ }
91
+ if (this.isDown(char)) {
92
+ if (this.rowIndex < this.items.length - 1) {
93
+ this.rowIndex++;
94
+ }
95
+ else {
96
+ // Down at last row adds new row
97
+ this.items.push({ key: '', value: '' });
98
+ this.rowIndex++;
99
+ this.colIndex = 0; // Optional: Reset to Key col when creating new row
100
+ }
101
+ this.render(false);
102
+ return;
103
+ }
104
+ if (char === '\t' || this.isRight(char) || this.isLeft(char)) {
105
+ // Toggle column
106
+ this.colIndex = this.colIndex === 0 ? 1 : 0;
107
+ this.render(false);
108
+ return;
109
+ }
110
+ // CRUD
111
+ // Ctrl+N (Standard ASCII for Ctrl+N is \x0e)
112
+ if (char === '\x0e') {
113
+ this.items.push({ key: '', value: '' });
114
+ this.rowIndex = this.items.length - 1;
115
+ this.colIndex = 0;
116
+ this.render(false);
117
+ return;
118
+ }
119
+ // Ctrl+D (Standard ASCII for Ctrl+D is \x04)
120
+ if (char === '\x04') {
121
+ if (this.items.length > 1) {
122
+ this.items.splice(this.rowIndex, 1);
123
+ if (this.rowIndex >= this.items.length) {
124
+ this.rowIndex = this.items.length - 1;
125
+ }
126
+ }
127
+ else {
128
+ // Clear the last remaining item instead of deleting it
129
+ this.items[0] = { key: '', value: '' };
130
+ }
131
+ this.render(false);
132
+ return;
133
+ }
134
+ // Submit
135
+ if (char === '\r' || char === '\n') {
136
+ // Validate duplicates
137
+ const keys = this.items.map(i => i.key);
138
+ const duplicates = keys.filter((item, index) => keys.indexOf(item) !== index && item !== '');
139
+ if (duplicates.length > 0) {
140
+ this.errorMsg = `Duplicate keys found: ${duplicates.join(', ')}`;
141
+ this.render(false);
142
+ return;
143
+ }
144
+ if (keys.some(k => k === '')) {
145
+ this.errorMsg = 'Keys cannot be empty';
146
+ this.render(false);
147
+ return;
148
+ }
149
+ const result = {};
150
+ this.items.forEach(item => {
151
+ result[item.key] = item.value;
152
+ });
153
+ this.submit(result);
154
+ return;
155
+ }
156
+ // Backspace
157
+ if (char === '\u0008' || char === '\x7f') {
158
+ const item = this.items[this.rowIndex];
159
+ if (this.colIndex === 0) {
160
+ if (item.key.length > 0)
161
+ item.key = item.key.slice(0, -1);
162
+ }
163
+ else {
164
+ if (item.value.length > 0)
165
+ item.value = item.value.slice(0, -1);
166
+ }
167
+ this.render(false);
168
+ return;
169
+ }
170
+ // Typing
171
+ if (!/^[\x00-\x1F]/.test(char)) {
172
+ const item = this.items[this.rowIndex];
173
+ if (this.colIndex === 0) {
174
+ item.key += char;
175
+ }
176
+ else {
177
+ item.value += char;
178
+ }
179
+ this.render(false);
180
+ }
181
+ }
182
+ handleMouse(event) {
183
+ if (event.action === 'scroll') {
184
+ if (event.scroll === 'up') {
185
+ if (this.rowIndex > 0) {
186
+ this.rowIndex--;
187
+ this.render(false);
188
+ }
189
+ }
190
+ else if (event.scroll === 'down') {
191
+ if (this.rowIndex < this.items.length - 1) {
192
+ this.rowIndex++;
193
+ this.render(false);
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+ exports.MapPrompt = MapPrompt;
@@ -0,0 +1,19 @@
1
+ import { Prompt } from '../base';
2
+ import { MatchOptions, MouseEvent } from '../types';
3
+ export declare class MatchPrompt extends Prompt<Record<string, any[]>, MatchOptions> {
4
+ private source;
5
+ private target;
6
+ private links;
7
+ private cursorSource;
8
+ private cursorTarget;
9
+ private scrollTopSource;
10
+ private scrollTopTarget;
11
+ private activeSide;
12
+ private pickedSourceIndex;
13
+ private readonly pageSize;
14
+ constructor(options: MatchOptions);
15
+ private normalize;
16
+ protected render(_firstRender: boolean): void;
17
+ protected handleInput(char: string): void;
18
+ protected handleMouse(event: MouseEvent): void;
19
+ }