instant-cli 1.0.33 → 1.0.34
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/.turbo/turbo-build.log +1 -1
- package/__tests__/multiSelect.test.ts +236 -0
- package/__tests__/select.test.ts +224 -0
- package/__tests__/webhooks.test.ts +728 -0
- package/dist/commands/webhooks/add.d.ts +9 -0
- package/dist/commands/webhooks/add.d.ts.map +1 -0
- package/dist/commands/webhooks/add.js +75 -0
- package/dist/commands/webhooks/add.js.map +1 -0
- package/dist/commands/webhooks/delete.d.ts +6 -0
- package/dist/commands/webhooks/delete.d.ts.map +1 -0
- package/dist/commands/webhooks/delete.js +17 -0
- package/dist/commands/webhooks/delete.js.map +1 -0
- package/dist/commands/webhooks/disable.d.ts +7 -0
- package/dist/commands/webhooks/disable.d.ts.map +1 -0
- package/dist/commands/webhooks/disable.js +18 -0
- package/dist/commands/webhooks/disable.js.map +1 -0
- package/dist/commands/webhooks/enable.d.ts +6 -0
- package/dist/commands/webhooks/enable.d.ts.map +1 -0
- package/dist/commands/webhooks/enable.js +18 -0
- package/dist/commands/webhooks/enable.js.map +1 -0
- package/dist/commands/webhooks/events/list.d.ts +7 -0
- package/dist/commands/webhooks/events/list.d.ts.map +1 -0
- package/dist/commands/webhooks/events/list.js +31 -0
- package/dist/commands/webhooks/events/list.js.map +1 -0
- package/dist/commands/webhooks/events/payload.d.ts +8 -0
- package/dist/commands/webhooks/events/payload.d.ts.map +1 -0
- package/dist/commands/webhooks/events/payload.js +39 -0
- package/dist/commands/webhooks/events/payload.js.map +1 -0
- package/dist/commands/webhooks/events/resend.d.ts +8 -0
- package/dist/commands/webhooks/events/resend.d.ts.map +1 -0
- package/dist/commands/webhooks/events/resend.js +43 -0
- package/dist/commands/webhooks/events/resend.js.map +1 -0
- package/dist/commands/webhooks/list.d.ts +8 -0
- package/dist/commands/webhooks/list.d.ts.map +1 -0
- package/dist/commands/webhooks/list.js +29 -0
- package/dist/commands/webhooks/list.js.map +1 -0
- package/dist/commands/webhooks/shared.d.ts +40 -0
- package/dist/commands/webhooks/shared.d.ts.map +1 -0
- package/dist/commands/webhooks/shared.js +248 -0
- package/dist/commands/webhooks/shared.js.map +1 -0
- package/dist/commands/webhooks/update.d.ts +10 -0
- package/dist/commands/webhooks/update.d.ts.map +1 -0
- package/dist/commands/webhooks/update.js +189 -0
- package/dist/commands/webhooks/update.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +2 -2
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +30 -1
- package/dist/layer.js.map +1 -1
- package/dist/lib/webhooks.d.ts +28 -0
- package/dist/lib/webhooks.d.ts.map +1 -0
- package/dist/lib/webhooks.js +102 -0
- package/dist/lib/webhooks.js.map +1 -0
- package/dist/ui/index.d.ts +39 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +387 -25
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/lib.d.ts +7 -0
- package/dist/ui/lib.d.ts.map +1 -1
- package/dist/ui/lib.js +40 -1
- package/dist/ui/lib.js.map +1 -1
- package/package.json +4 -4
- package/src/commands/webhooks/add.ts +111 -0
- package/src/commands/webhooks/delete.ts +23 -0
- package/src/commands/webhooks/disable.ts +24 -0
- package/src/commands/webhooks/enable.ts +24 -0
- package/src/commands/webhooks/events/list.ts +38 -0
- package/src/commands/webhooks/events/payload.ts +56 -0
- package/src/commands/webhooks/events/resend.ts +66 -0
- package/src/commands/webhooks/list.ts +41 -0
- package/src/commands/webhooks/shared.ts +339 -0
- package/src/commands/webhooks/update.ts +276 -0
- package/src/index.ts +242 -0
- package/src/layer.ts +33 -1
- package/src/lib/webhooks.ts +127 -0
- package/src/ui/index.ts +465 -32
- package/src/ui/lib.ts +41 -1
package/dist/ui/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import boxen from 'boxen';
|
|
3
3
|
import stringWidth from 'string-width';
|
|
4
4
|
import { Prompt, SelectState } from "./lib.js";
|
|
5
|
-
export { render, renderUnwrap, setRawModeWindowsFriendly } from "./lib.js";
|
|
5
|
+
export { clearPromptTrail, render, renderUnwrap, setRawModeWindowsFriendly, } from "./lib.js";
|
|
6
6
|
export var UI;
|
|
7
7
|
(function (UI) {
|
|
8
8
|
UI.modifiers = {
|
|
@@ -71,24 +71,29 @@ export var UI;
|
|
|
71
71
|
};
|
|
72
72
|
class Select extends Prompt {
|
|
73
73
|
config(status) {
|
|
74
|
-
console.log('config', status);
|
|
75
74
|
return status;
|
|
76
75
|
}
|
|
77
76
|
data;
|
|
78
77
|
options;
|
|
79
78
|
params;
|
|
79
|
+
expandedIdx = null;
|
|
80
|
+
stickyExpanded = false;
|
|
81
|
+
expansionCache = new Map();
|
|
82
|
+
expansionLoading = new Set();
|
|
80
83
|
constructor(params) {
|
|
81
84
|
super(params.modifyOutput);
|
|
82
85
|
this.on('attach', (terminal) => terminal.toggleCursor('hide'));
|
|
83
|
-
this.on('input', (input) => {
|
|
84
|
-
if (input === 'j') {
|
|
86
|
+
this.on('input', (input, key) => {
|
|
87
|
+
if (input === 'j' || (key?.ctrl && key.name === 'n')) {
|
|
85
88
|
this.data.selectedIdx =
|
|
86
89
|
(this.data.selectedIdx + 1) % this.options.length;
|
|
90
|
+
this.applyNavigation();
|
|
87
91
|
}
|
|
88
|
-
else if (input === 'k') {
|
|
92
|
+
else if (input === 'k' || (key?.ctrl && key.name === 'p')) {
|
|
89
93
|
this.data.selectedIdx =
|
|
90
94
|
(this.data.selectedIdx - 1 + this.options.length) %
|
|
91
95
|
this.options.length;
|
|
96
|
+
this.applyNavigation();
|
|
92
97
|
}
|
|
93
98
|
this.requestLayout();
|
|
94
99
|
});
|
|
@@ -106,6 +111,73 @@ export var UI;
|
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
this.data.bind(this);
|
|
114
|
+
this.on('input', (_input, key) => {
|
|
115
|
+
if (key?.name === 'tab') {
|
|
116
|
+
const hasExpandable = this.options.some((o) => o.expandableLabel);
|
|
117
|
+
if (!hasExpandable)
|
|
118
|
+
return;
|
|
119
|
+
this.stickyExpanded = !this.stickyExpanded;
|
|
120
|
+
if (this.stickyExpanded) {
|
|
121
|
+
this.expandFocused();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
this.expandedIdx = null;
|
|
125
|
+
}
|
|
126
|
+
this.requestLayout();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (key?.name === 'up' || key?.name === 'down') {
|
|
130
|
+
this.applyNavigation();
|
|
131
|
+
this.requestLayout();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
applyNavigation() {
|
|
136
|
+
if (this.stickyExpanded) {
|
|
137
|
+
this.expandFocused();
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
this.expandedIdx = null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
expandFocused() {
|
|
144
|
+
const focused = this.data.selectedIdx;
|
|
145
|
+
const focusedOption = this.options[focused];
|
|
146
|
+
const exp = focusedOption?.expandableLabel;
|
|
147
|
+
if (!exp) {
|
|
148
|
+
this.expandedIdx = null;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.expandedIdx = focused;
|
|
152
|
+
if (typeof exp === 'function' &&
|
|
153
|
+
!this.expansionCache.has(focused) &&
|
|
154
|
+
!this.expansionLoading.has(focused)) {
|
|
155
|
+
this.expansionLoading.add(focused);
|
|
156
|
+
Promise.resolve()
|
|
157
|
+
.then(() => exp())
|
|
158
|
+
.then((content) => {
|
|
159
|
+
this.expansionLoading.delete(focused);
|
|
160
|
+
this.expansionCache.set(focused, content);
|
|
161
|
+
if (this.expandedIdx === focused)
|
|
162
|
+
this.requestLayout();
|
|
163
|
+
}, (err) => {
|
|
164
|
+
this.expansionLoading.delete(focused);
|
|
165
|
+
this.expansionCache.set(focused, chalk.red(` Error loading expansion: ${err?.message ?? err}`));
|
|
166
|
+
if (this.expandedIdx === focused)
|
|
167
|
+
this.requestLayout();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
getExpansionContent(option, idx) {
|
|
172
|
+
const exp = option.expandableLabel;
|
|
173
|
+
if (!exp)
|
|
174
|
+
return null;
|
|
175
|
+
if (typeof exp === 'string')
|
|
176
|
+
return exp;
|
|
177
|
+
if (this.expansionLoading.has(idx))
|
|
178
|
+
return chalk.dim(' Loading…');
|
|
179
|
+
const cached = this.expansionCache.get(idx);
|
|
180
|
+
return cached ?? chalk.dim(' Loading…');
|
|
109
181
|
}
|
|
110
182
|
result() {
|
|
111
183
|
return this.data.items[this.data.selectedIdx];
|
|
@@ -115,34 +187,318 @@ export var UI;
|
|
|
115
187
|
return `${this.params.promptText}
|
|
116
188
|
${chalk.hex('#EA570B').bold('●')} ${this.params.options[this.data.selectedIdx]?.label}`;
|
|
117
189
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
.map((option, idx) => {
|
|
121
|
-
const isSelected = idx === this.data.selectedIdx;
|
|
122
|
-
const cursor = isSelected ? chalk.hex('#EA570B').bold('●') : '○';
|
|
123
|
-
const label = isSelected
|
|
124
|
-
? chalk.bold(option.label)
|
|
125
|
-
: chalk.dim(option.label);
|
|
126
|
-
return `${cursor} ${label}`;
|
|
127
|
-
})
|
|
128
|
-
.join('\n');
|
|
129
|
-
const secondaryOptionsList = this.options
|
|
130
|
-
.filter((option) => option.secondary)
|
|
131
|
-
.map((option, idx) => {
|
|
132
|
-
const realIdx = idx + this.options.filter((o) => !o.secondary).length;
|
|
133
|
-
const isSelected = realIdx === this.data.selectedIdx;
|
|
190
|
+
const renderRow = (option, originalIdx) => {
|
|
191
|
+
const isSelected = originalIdx === this.data.selectedIdx;
|
|
134
192
|
const cursor = isSelected ? chalk.hex('#EA570B').bold('●') : '○';
|
|
135
193
|
const label = isSelected
|
|
136
194
|
? chalk.bold(option.label)
|
|
137
195
|
: chalk.dim(option.label);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
196
|
+
const expandedContent = isSelected && this.expandedIdx === originalIdx
|
|
197
|
+
? this.getExpansionContent(option, originalIdx)
|
|
198
|
+
: null;
|
|
199
|
+
const expanded = expandedContent !== null ? '\n' + expandedContent : '';
|
|
200
|
+
return `${cursor} ${label}${expanded}`;
|
|
201
|
+
};
|
|
202
|
+
const rowLineCount = (originalIdx) => {
|
|
203
|
+
const opt = this.options[originalIdx];
|
|
204
|
+
const labelLines = opt.label.split('\n').length;
|
|
205
|
+
const isFocused = originalIdx === this.data.selectedIdx;
|
|
206
|
+
const expContent = isFocused && this.expandedIdx === originalIdx
|
|
207
|
+
? this.getExpansionContent(opt, originalIdx)
|
|
208
|
+
: null;
|
|
209
|
+
const expLines = expContent !== null ? expContent.split('\n').length : 0;
|
|
210
|
+
return labelLines + expLines;
|
|
211
|
+
};
|
|
212
|
+
const mainEntries = [];
|
|
213
|
+
const secondaryEntries = [];
|
|
214
|
+
this.options.forEach((o, i) => {
|
|
215
|
+
if (o.secondary)
|
|
216
|
+
secondaryEntries.push({ opt: o, originalIdx: i });
|
|
217
|
+
else
|
|
218
|
+
mainEntries.push({ opt: o, originalIdx: i });
|
|
219
|
+
});
|
|
220
|
+
const totalRows = process.stdout.rows ?? 24;
|
|
221
|
+
const secondaryHeight = secondaryEntries.length === 0
|
|
222
|
+
? 0
|
|
223
|
+
: secondaryEntries.reduce((acc, { originalIdx }) => acc + rowLineCount(originalIdx), 0) + 1; // +1 for divider
|
|
224
|
+
const hasExpandable = this.options.some((o) => o.expandableLabel);
|
|
225
|
+
// chrome = prompt(1) + secondaries(+divider) + hint + safety(2)
|
|
226
|
+
const chrome = 1 + secondaryHeight + (hasExpandable ? 1 : 0) + 2;
|
|
227
|
+
const mainBudget = Math.max(3, totalRows - chrome);
|
|
228
|
+
const focusedMainPos = mainEntries.findIndex((e) => e.originalIdx === this.data.selectedIdx);
|
|
229
|
+
let visiblePositions = [];
|
|
230
|
+
if (focusedMainPos >= 0) {
|
|
231
|
+
let used = rowLineCount(mainEntries[focusedMainPos].originalIdx);
|
|
232
|
+
visiblePositions = [focusedMainPos];
|
|
233
|
+
let lo = focusedMainPos - 1;
|
|
234
|
+
let hi = focusedMainPos + 1;
|
|
235
|
+
while (lo >= 0 || hi < mainEntries.length) {
|
|
236
|
+
let progressed = false;
|
|
237
|
+
if (lo >= 0) {
|
|
238
|
+
const h = rowLineCount(mainEntries[lo].originalIdx);
|
|
239
|
+
if (used + h <= mainBudget) {
|
|
240
|
+
visiblePositions.unshift(lo);
|
|
241
|
+
used += h;
|
|
242
|
+
lo--;
|
|
243
|
+
progressed = true;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
lo = -1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (hi < mainEntries.length) {
|
|
250
|
+
const h = rowLineCount(mainEntries[hi].originalIdx);
|
|
251
|
+
if (used + h <= mainBudget) {
|
|
252
|
+
visiblePositions.push(hi);
|
|
253
|
+
used += h;
|
|
254
|
+
hi++;
|
|
255
|
+
progressed = true;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
hi = mainEntries.length;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!progressed)
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// Cursor is on a secondary — show as many mains as fit, top-down.
|
|
267
|
+
let used = 0;
|
|
268
|
+
for (let i = 0; i < mainEntries.length; i++) {
|
|
269
|
+
const h = rowLineCount(mainEntries[i].originalIdx);
|
|
270
|
+
if (used + h > mainBudget)
|
|
271
|
+
break;
|
|
272
|
+
visiblePositions.push(i);
|
|
273
|
+
used += h;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const firstVisible = visiblePositions[0] ?? 0;
|
|
277
|
+
const lastVisible = visiblePositions[visiblePositions.length - 1] ?? mainEntries.length - 1;
|
|
278
|
+
const aboveCount = firstVisible;
|
|
279
|
+
const belowCount = Math.max(0, mainEntries.length - 1 - lastVisible);
|
|
280
|
+
const mainLines = [];
|
|
281
|
+
if (aboveCount > 0)
|
|
282
|
+
mainLines.push(chalk.dim(` ↑ ${aboveCount} more`));
|
|
283
|
+
for (const pos of visiblePositions) {
|
|
284
|
+
mainLines.push(renderRow(mainEntries[pos].opt, mainEntries[pos].originalIdx));
|
|
285
|
+
}
|
|
286
|
+
if (belowCount > 0)
|
|
287
|
+
mainLines.push(chalk.dim(` ↓ ${belowCount} more`));
|
|
288
|
+
const mainBlock = mainLines.join('\n');
|
|
289
|
+
const secondaryBlock = secondaryEntries.length === 0
|
|
290
|
+
? ''
|
|
291
|
+
: chalk.gray('\n───────────────── Additional Options ─────────────────\n') +
|
|
292
|
+
secondaryEntries
|
|
293
|
+
.map((e) => renderRow(e.opt, e.originalIdx))
|
|
294
|
+
.join('\n');
|
|
295
|
+
const expandHint = hasExpandable
|
|
296
|
+
? '\n' +
|
|
297
|
+
chalk.dim(this.stickyExpanded ? ' (tab to collapse)' : ' (tab to expand)')
|
|
298
|
+
: '';
|
|
141
299
|
return `${this.params.promptText}
|
|
142
|
-
${
|
|
300
|
+
${mainBlock}${secondaryBlock}${expandHint}`;
|
|
143
301
|
}
|
|
144
302
|
}
|
|
145
303
|
UI.Select = Select;
|
|
304
|
+
const DEFAULT_MULTI_SELECT_PAGE_SIZE = 10;
|
|
305
|
+
const defaultMultiSelectFilter = (filter, option) => option.label.toLowerCase().includes(filter.toLowerCase());
|
|
306
|
+
class MultiSelect extends Prompt {
|
|
307
|
+
filterText = '';
|
|
308
|
+
cursorIdx = 0;
|
|
309
|
+
windowStart = 0;
|
|
310
|
+
selected;
|
|
311
|
+
errorText;
|
|
312
|
+
props;
|
|
313
|
+
pageSize;
|
|
314
|
+
constructor(props) {
|
|
315
|
+
super(props.modifyOutput);
|
|
316
|
+
this.props = props;
|
|
317
|
+
this.pageSize = props.pageSize ?? DEFAULT_MULTI_SELECT_PAGE_SIZE;
|
|
318
|
+
this.selected = new Set((props.initialSelected ?? [])
|
|
319
|
+
.map((v) => props.options.findIndex((o) => o.value === v))
|
|
320
|
+
.filter((i) => i >= 0));
|
|
321
|
+
this.on('attach', (terminal) => {
|
|
322
|
+
terminal.setAllowInteraction(false);
|
|
323
|
+
terminal.toggleCursor('hide');
|
|
324
|
+
});
|
|
325
|
+
this.on('detach', (terminal) => terminal.toggleCursor('show'));
|
|
326
|
+
this.on('input', (input, key) => this.handleKey(input, key));
|
|
327
|
+
}
|
|
328
|
+
visibleIndices() {
|
|
329
|
+
const fn = this.props.filter ?? defaultMultiSelectFilter;
|
|
330
|
+
if (!this.filterText)
|
|
331
|
+
return this.props.options.map((_, i) => i);
|
|
332
|
+
return this.props.options
|
|
333
|
+
.map((opt, i) => ({ opt, i }))
|
|
334
|
+
.filter(({ opt }) => fn(this.filterText, opt))
|
|
335
|
+
.map(({ i }) => i);
|
|
336
|
+
}
|
|
337
|
+
clampCursor(visible) {
|
|
338
|
+
if (visible.length === 0) {
|
|
339
|
+
this.cursorIdx = 0;
|
|
340
|
+
}
|
|
341
|
+
else if (this.cursorIdx >= visible.length) {
|
|
342
|
+
this.cursorIdx = visible.length - 1;
|
|
343
|
+
}
|
|
344
|
+
else if (this.cursorIdx < 0) {
|
|
345
|
+
this.cursorIdx = visible.length - 1;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
adjustWindow(visible) {
|
|
349
|
+
if (visible.length <= this.pageSize) {
|
|
350
|
+
this.windowStart = 0;
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (this.cursorIdx < this.windowStart) {
|
|
354
|
+
this.windowStart = this.cursorIdx;
|
|
355
|
+
}
|
|
356
|
+
else if (this.cursorIdx >= this.windowStart + this.pageSize) {
|
|
357
|
+
this.windowStart = this.cursorIdx - this.pageSize + 1;
|
|
358
|
+
}
|
|
359
|
+
const maxStart = Math.max(0, visible.length - this.pageSize);
|
|
360
|
+
if (this.windowStart > maxStart)
|
|
361
|
+
this.windowStart = maxStart;
|
|
362
|
+
if (this.windowStart < 0)
|
|
363
|
+
this.windowStart = 0;
|
|
364
|
+
}
|
|
365
|
+
handleKey(input, key) {
|
|
366
|
+
if (key.name === 'escape') {
|
|
367
|
+
return this.terminal?.resolve({ data: undefined, status: 'aborted' });
|
|
368
|
+
}
|
|
369
|
+
if (key.name === 'return') {
|
|
370
|
+
const min = this.props.minSelected ?? 0;
|
|
371
|
+
if (this.selected.size < min) {
|
|
372
|
+
this.errorText =
|
|
373
|
+
min === 1
|
|
374
|
+
? 'Select at least one option.'
|
|
375
|
+
: `Select at least ${min} options.`;
|
|
376
|
+
this.requestLayout();
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
return this.terminal?.resolve({
|
|
380
|
+
data: this.result(),
|
|
381
|
+
status: 'submitted',
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
const visible = this.visibleIndices();
|
|
385
|
+
if (key.name === 'up' || (key.ctrl && key.name === 'p')) {
|
|
386
|
+
this.cursorIdx -= 1;
|
|
387
|
+
this.clampCursor(visible);
|
|
388
|
+
this.adjustWindow(visible);
|
|
389
|
+
this.requestLayout();
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (key.name === 'down' || (key.ctrl && key.name === 'n')) {
|
|
393
|
+
this.cursorIdx += 1;
|
|
394
|
+
if (visible.length > 0)
|
|
395
|
+
this.cursorIdx %= visible.length;
|
|
396
|
+
this.adjustWindow(visible);
|
|
397
|
+
this.requestLayout();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (key.name === 'pageup' || (key.ctrl && key.name === 'u')) {
|
|
401
|
+
this.cursorIdx -= this.pageSize;
|
|
402
|
+
if (this.cursorIdx < 0)
|
|
403
|
+
this.cursorIdx = 0;
|
|
404
|
+
this.adjustWindow(visible);
|
|
405
|
+
this.requestLayout();
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (key.name === 'pagedown' || (key.ctrl && key.name === 'd')) {
|
|
409
|
+
this.cursorIdx += this.pageSize;
|
|
410
|
+
if (this.cursorIdx >= visible.length) {
|
|
411
|
+
this.cursorIdx = Math.max(0, visible.length - 1);
|
|
412
|
+
}
|
|
413
|
+
this.adjustWindow(visible);
|
|
414
|
+
this.requestLayout();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (key.name === 'space') {
|
|
418
|
+
if (visible.length === 0)
|
|
419
|
+
return;
|
|
420
|
+
const optIdx = visible[this.cursorIdx];
|
|
421
|
+
if (this.selected.has(optIdx)) {
|
|
422
|
+
this.selected.delete(optIdx);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
this.selected.add(optIdx);
|
|
426
|
+
}
|
|
427
|
+
this.errorText = undefined;
|
|
428
|
+
this.requestLayout();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (key.name === 'backspace') {
|
|
432
|
+
if (this.filterText.length > 0) {
|
|
433
|
+
this.filterText = this.filterText.slice(0, -1);
|
|
434
|
+
this.cursorIdx = 0;
|
|
435
|
+
this.windowStart = 0;
|
|
436
|
+
this.requestLayout();
|
|
437
|
+
}
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (key.name?.length === 1 && !key.ctrl && !key.meta && input) {
|
|
441
|
+
this.filterText += input;
|
|
442
|
+
this.cursorIdx = 0;
|
|
443
|
+
this.windowStart = 0;
|
|
444
|
+
this.requestLayout();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
result() {
|
|
448
|
+
return [...this.selected]
|
|
449
|
+
.sort((a, b) => a - b)
|
|
450
|
+
.map((i) => this.props.options[i].value);
|
|
451
|
+
}
|
|
452
|
+
renderOption(optIdx, isCursor) {
|
|
453
|
+
const option = this.props.options[optIdx];
|
|
454
|
+
const isSelected = this.selected.has(optIdx);
|
|
455
|
+
const checkbox = isSelected ? chalk.green('◉') : '○';
|
|
456
|
+
const cursorMark = isCursor ? chalk.hex('#EA570B').bold('❯') : ' ';
|
|
457
|
+
const label = isCursor ? chalk.bold(option.label) : option.label;
|
|
458
|
+
return `${cursorMark} ${checkbox} ${label}`;
|
|
459
|
+
}
|
|
460
|
+
render(status) {
|
|
461
|
+
if (status === 'submitted') {
|
|
462
|
+
const labels = [...this.selected]
|
|
463
|
+
.sort((a, b) => a - b)
|
|
464
|
+
.map((i) => this.props.options[i].label);
|
|
465
|
+
return `${this.props.promptText}
|
|
466
|
+
${chalk.hex('#EA570B').bold('●')} ${labels.length === 0 ? chalk.dim('(none)') : labels.join(', ')}`;
|
|
467
|
+
}
|
|
468
|
+
const visible = this.visibleIndices();
|
|
469
|
+
this.adjustWindow(visible);
|
|
470
|
+
const filterLine = `${chalk.dim('filter:')} ${this.filterText}${chalk.inverse(' ')}`;
|
|
471
|
+
const errorLine = this.errorText ? ` ${chalk.red(this.errorText)}` : '';
|
|
472
|
+
let optionsBlock;
|
|
473
|
+
if (visible.length === 0) {
|
|
474
|
+
optionsBlock = chalk.dim(' (no matches)');
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
const windowEnd = Math.min(this.windowStart + this.pageSize, visible.length);
|
|
478
|
+
const aboveCount = this.windowStart;
|
|
479
|
+
const belowCount = visible.length - windowEnd;
|
|
480
|
+
const lines = [];
|
|
481
|
+
lines.push(aboveCount > 0
|
|
482
|
+
? chalk.dim(` ↑ ${aboveCount} more`)
|
|
483
|
+
: chalk.dim(' '));
|
|
484
|
+
for (let i = this.windowStart; i < windowEnd; i++) {
|
|
485
|
+
lines.push(this.renderOption(visible[i], i === this.cursorIdx));
|
|
486
|
+
}
|
|
487
|
+
lines.push(belowCount > 0
|
|
488
|
+
? chalk.dim(` ↓ ${belowCount} more`)
|
|
489
|
+
: chalk.dim(' '));
|
|
490
|
+
optionsBlock = lines.join('\n');
|
|
491
|
+
}
|
|
492
|
+
const hint = chalk.dim(` ↑↓ or ctrl-n/p navigate · pgup/pgdn or ctrl-u/d page · space toggle · enter submit · esc cancel`);
|
|
493
|
+
const counts = chalk.dim(` ${visible.length} of ${this.props.options.length} shown · ${this.selected.size} selected`);
|
|
494
|
+
return `${this.props.promptText}${errorLine}
|
|
495
|
+
${filterLine}
|
|
496
|
+
${optionsBlock}
|
|
497
|
+
${counts}
|
|
498
|
+
${hint}`;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
UI.MultiSelect = MultiSelect;
|
|
146
502
|
/**
|
|
147
503
|
* @deprecated use the modifyOutput prop instead
|
|
148
504
|
* left as an example of how to do wrapper prompts
|
|
@@ -251,6 +607,8 @@ ${inputDisplay}`;
|
|
|
251
607
|
const validationResult = this.props.validate(this.value);
|
|
252
608
|
if (validationResult) {
|
|
253
609
|
this.errorText = validationResult;
|
|
610
|
+
this.requestLayout();
|
|
611
|
+
return;
|
|
254
612
|
}
|
|
255
613
|
else {
|
|
256
614
|
return this.terminal?.resolve({
|
|
@@ -268,15 +626,19 @@ ${inputDisplay}`;
|
|
|
268
626
|
}
|
|
269
627
|
if (keyInfo.name === 'backspace') {
|
|
270
628
|
this.value = this.value.slice(0, -1);
|
|
629
|
+
this.errorText = undefined;
|
|
271
630
|
}
|
|
272
631
|
else if (keyInfo.name?.length === 1) {
|
|
273
632
|
this.value += input;
|
|
633
|
+
this.errorText = undefined;
|
|
274
634
|
}
|
|
275
635
|
else if (keyInfo.name === 'space') {
|
|
276
636
|
this.value += ' ';
|
|
637
|
+
this.errorText = undefined;
|
|
277
638
|
}
|
|
278
639
|
else if (input !== undefined) {
|
|
279
640
|
this.value += input;
|
|
641
|
+
this.errorText = undefined;
|
|
280
642
|
}
|
|
281
643
|
this.requestLayout();
|
|
282
644
|
});
|