@spences10/pi-tui-modal 0.0.9 → 0.0.10
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/dist/index.d.ts +2 -79
- package/dist/index.js +1 -651
- package/dist/index.js.map +1 -1
- package/dist/modal/bodies.d.ts +54 -0
- package/dist/modal/bodies.js +259 -0
- package/dist/modal/bodies.js.map +1 -0
- package/dist/modal/frame.d.ts +7 -0
- package/dist/modal/frame.js +42 -0
- package/dist/modal/frame.js.map +1 -0
- package/dist/modal/layout.d.ts +36 -0
- package/dist/modal/layout.js +159 -0
- package/dist/modal/layout.js.map +1 -0
- package/dist/modal/show.d.ts +9 -0
- package/dist/modal/show.js +203 -0
- package/dist/modal/show.js.map +1 -0
- package/dist/modal/types.d.ts +71 -0
- package/dist/modal/types.js +2 -0
- package/dist/modal/types.js.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,652 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
const default_overlay_options = {
|
|
3
|
-
width: '80%',
|
|
4
|
-
minWidth: 60,
|
|
5
|
-
maxHeight: '80%',
|
|
6
|
-
};
|
|
7
|
-
const default_modal_style = {
|
|
8
|
-
border: 'rounded',
|
|
9
|
-
border_color: 'accent',
|
|
10
|
-
};
|
|
11
|
-
const border_characters = {
|
|
12
|
-
rounded: {
|
|
13
|
-
top_left: '╭',
|
|
14
|
-
top: '─',
|
|
15
|
-
top_right: '╮',
|
|
16
|
-
left: '│',
|
|
17
|
-
right: '│',
|
|
18
|
-
bottom_left: '╰',
|
|
19
|
-
bottom: '─',
|
|
20
|
-
bottom_right: '╯',
|
|
21
|
-
},
|
|
22
|
-
square: {
|
|
23
|
-
top_left: '┌',
|
|
24
|
-
top: '─',
|
|
25
|
-
top_right: '┐',
|
|
26
|
-
left: '│',
|
|
27
|
-
right: '│',
|
|
28
|
-
bottom_left: '└',
|
|
29
|
-
bottom: '─',
|
|
30
|
-
bottom_right: '┘',
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
function normalize_text(value) {
|
|
34
|
-
if (!value)
|
|
35
|
-
return [];
|
|
36
|
-
const resolved = typeof value === 'function' ? value() : value;
|
|
37
|
-
return Array.isArray(resolved) ? resolved : [resolved];
|
|
38
|
-
}
|
|
39
|
-
function parse_size_value(value, total) {
|
|
40
|
-
if (value === undefined)
|
|
41
|
-
return undefined;
|
|
42
|
-
if (typeof value === 'number')
|
|
43
|
-
return value;
|
|
44
|
-
const match = value.match(/^(\d+(?:\.\d+)?)%$/);
|
|
45
|
-
return match
|
|
46
|
-
? Math.floor((Number(match[1]) / 100) * total)
|
|
47
|
-
: undefined;
|
|
48
|
-
}
|
|
49
|
-
function get_vertical_margin(margin) {
|
|
50
|
-
if (typeof margin === 'number')
|
|
51
|
-
return Math.max(0, margin) * 2;
|
|
52
|
-
return (Math.max(0, margin?.top ?? 0) + Math.max(0, margin?.bottom ?? 0));
|
|
53
|
-
}
|
|
54
|
-
function get_terminal_rows(tui) {
|
|
55
|
-
const rows = tui
|
|
56
|
-
.terminal?.rows;
|
|
57
|
-
return rows ?? process.stdout.rows ?? 24;
|
|
58
|
-
}
|
|
59
|
-
function get_border_line_count(style) {
|
|
60
|
-
return style?.border === 'none' ? 0 : 2;
|
|
61
|
-
}
|
|
62
|
-
function count_text_lines(value, width) {
|
|
63
|
-
return normalize_text(value).reduce((count, text) => {
|
|
64
|
-
if (text.trim() === '')
|
|
65
|
-
return count;
|
|
66
|
-
return (count +
|
|
67
|
-
wrapTextWithAnsi(text.replace(/\t/g, ' '), width).length);
|
|
68
|
-
}, 0);
|
|
69
|
-
}
|
|
70
|
-
function get_modal_body_line_budget(tui, options, body_width = 80) {
|
|
71
|
-
const terminal_rows = get_terminal_rows(tui);
|
|
72
|
-
const overlay_options = {
|
|
73
|
-
...default_overlay_options,
|
|
74
|
-
...options.overlay_options,
|
|
75
|
-
};
|
|
76
|
-
const available_height = Math.max(1, terminal_rows - get_vertical_margin(overlay_options.margin));
|
|
77
|
-
const max_height = Math.max(1, Math.min(parse_size_value(overlay_options.maxHeight, terminal_rows) ??
|
|
78
|
-
available_height, available_height));
|
|
79
|
-
const fixed_lines = get_border_line_count(options.style) +
|
|
80
|
-
2 +
|
|
81
|
-
count_text_lines(options.title, body_width) +
|
|
82
|
-
count_text_lines(options.subtitle, body_width) +
|
|
83
|
-
count_text_lines(options.footer, body_width);
|
|
84
|
-
return Math.max(1, max_height - fixed_lines);
|
|
85
|
-
}
|
|
86
|
-
function fit_visible_items(item_count, preferred, body_line_budget) {
|
|
87
|
-
const budget = Math.max(1, body_line_budget);
|
|
88
|
-
const candidate = Math.max(1, Math.min(preferred, item_count));
|
|
89
|
-
if (item_count > candidate) {
|
|
90
|
-
return Math.max(1, Math.min(candidate, budget - 1));
|
|
91
|
-
}
|
|
92
|
-
return Math.max(1, Math.min(candidate, budget));
|
|
93
|
-
}
|
|
94
|
-
function set_component_max_visible(component, max_visible) {
|
|
95
|
-
component.maxVisible = max_visible;
|
|
96
|
-
}
|
|
97
|
-
function normalize_metadata(value, item) {
|
|
98
|
-
if (!value)
|
|
99
|
-
return [];
|
|
100
|
-
const resolved = typeof value === 'function' ? value(item) : value;
|
|
101
|
-
if (!resolved)
|
|
102
|
-
return [];
|
|
103
|
-
return Array.isArray(resolved) ? resolved : [resolved];
|
|
104
|
-
}
|
|
105
|
-
function make_select_theme(theme) {
|
|
106
|
-
return {
|
|
107
|
-
selectedPrefix: (text) => theme.fg('accent', text),
|
|
108
|
-
selectedText: (text) => theme.fg('accent', text),
|
|
109
|
-
description: (text) => theme.fg('muted', text),
|
|
110
|
-
scrollInfo: (text) => theme.fg('dim', text),
|
|
111
|
-
noMatch: (text) => theme.fg('warning', text),
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function value_color(value) {
|
|
115
|
-
const normalized = value.trim().toLowerCase();
|
|
116
|
-
if (normalized.startsWith('●') ||
|
|
117
|
-
normalized.startsWith('✓') ||
|
|
118
|
-
normalized.includes('enabled') ||
|
|
119
|
-
normalized.includes('selected') ||
|
|
120
|
-
normalized.includes('imported')) {
|
|
121
|
-
return 'success';
|
|
122
|
-
}
|
|
123
|
-
if (normalized.startsWith('↻') ||
|
|
124
|
-
normalized.includes('sync') ||
|
|
125
|
-
normalized.includes('queued')) {
|
|
126
|
-
return 'warning';
|
|
127
|
-
}
|
|
128
|
-
return 'dim';
|
|
129
|
-
}
|
|
130
|
-
function get_selected_setting(list) {
|
|
131
|
-
const internals = list;
|
|
132
|
-
const items = internals.searchEnabled
|
|
133
|
-
? internals.filteredItems
|
|
134
|
-
: internals.items;
|
|
135
|
-
return items[internals.selectedIndex];
|
|
136
|
-
}
|
|
137
|
-
function make_settings_theme(theme) {
|
|
138
|
-
return {
|
|
139
|
-
cursor: theme.fg('accent', '→ '),
|
|
140
|
-
label: (text, selected) => {
|
|
141
|
-
if (text.startsWith('──') && text.endsWith('──')) {
|
|
142
|
-
return theme.fg('dim', theme.bold(text));
|
|
143
|
-
}
|
|
144
|
-
return selected ? theme.fg('accent', text) : text;
|
|
145
|
-
},
|
|
146
|
-
value: (text, selected) => {
|
|
147
|
-
const rendered = theme.fg(value_color(text), text);
|
|
148
|
-
return selected
|
|
149
|
-
? theme.bold(theme.fg('accent', rendered))
|
|
150
|
-
: rendered;
|
|
151
|
-
},
|
|
152
|
-
description: (text) => theme.fg('muted', text),
|
|
153
|
-
hint: (text) => theme.fg('dim', text),
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function is_focusable(value) {
|
|
157
|
-
return Boolean(value && typeof value === 'object' && 'focused' in value);
|
|
158
|
-
}
|
|
159
|
-
class TextModalBody {
|
|
160
|
-
text;
|
|
161
|
-
max_visible_lines;
|
|
162
|
-
theme;
|
|
163
|
-
on_cancel;
|
|
164
|
-
offset = 0;
|
|
165
|
-
wrapped_lines = [];
|
|
166
|
-
constructor(text, max_visible_lines, theme, on_cancel) {
|
|
167
|
-
this.text = text;
|
|
168
|
-
this.max_visible_lines = max_visible_lines;
|
|
169
|
-
this.theme = theme;
|
|
170
|
-
this.on_cancel = on_cancel;
|
|
171
|
-
}
|
|
172
|
-
set_max_visible_lines(max_visible_lines) {
|
|
173
|
-
this.max_visible_lines = Math.max(1, max_visible_lines);
|
|
174
|
-
}
|
|
175
|
-
render(width) {
|
|
176
|
-
this.wrapped_lines = normalize_text(this.text).flatMap((block) => block
|
|
177
|
-
.split('\n')
|
|
178
|
-
.flatMap((line) => line.length === 0 ? [''] : wrapTextWithAnsi(line, width)));
|
|
179
|
-
const max_offset = Math.max(0, this.wrapped_lines.length - this.max_visible_lines);
|
|
180
|
-
this.offset = Math.max(0, Math.min(this.offset, max_offset));
|
|
181
|
-
const end = Math.min(this.offset + this.max_visible_lines, this.wrapped_lines.length);
|
|
182
|
-
const visible = this.wrapped_lines
|
|
183
|
-
.slice(this.offset, end)
|
|
184
|
-
.map((line) => truncateToWidth(line, width));
|
|
185
|
-
if (this.wrapped_lines.length > this.max_visible_lines) {
|
|
186
|
-
visible.push(this.theme.fg('dim', truncateToWidth(`(${this.offset + 1}-${end}/${this.wrapped_lines.length})`, width)));
|
|
187
|
-
}
|
|
188
|
-
return visible;
|
|
189
|
-
}
|
|
190
|
-
invalidate() {
|
|
191
|
-
// No cached rendering.
|
|
192
|
-
}
|
|
193
|
-
handleInput(data) {
|
|
194
|
-
const keybindings = getKeybindings();
|
|
195
|
-
const max_offset = Math.max(0, this.wrapped_lines.length - this.max_visible_lines);
|
|
196
|
-
if (keybindings.matches(data, 'tui.select.up') || data === 'k') {
|
|
197
|
-
this.offset = Math.max(0, this.offset - 1);
|
|
198
|
-
}
|
|
199
|
-
else if (keybindings.matches(data, 'tui.select.down') ||
|
|
200
|
-
data === 'j') {
|
|
201
|
-
this.offset = Math.min(max_offset, this.offset + 1);
|
|
202
|
-
}
|
|
203
|
-
else if (matchesKey(data, Key.home)) {
|
|
204
|
-
this.offset = 0;
|
|
205
|
-
}
|
|
206
|
-
else if (matchesKey(data, Key.end)) {
|
|
207
|
-
this.offset = max_offset;
|
|
208
|
-
}
|
|
209
|
-
else if (keybindings.matches(data, 'tui.select.cancel') ||
|
|
210
|
-
data === 'q') {
|
|
211
|
-
this.on_cancel();
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
class InputModalBody {
|
|
216
|
-
options;
|
|
217
|
-
theme;
|
|
218
|
-
on_submit;
|
|
219
|
-
on_cancel;
|
|
220
|
-
input = new Input();
|
|
221
|
-
_focused = false;
|
|
222
|
-
get focused() {
|
|
223
|
-
return this._focused;
|
|
224
|
-
}
|
|
225
|
-
set focused(value) {
|
|
226
|
-
this._focused = value;
|
|
227
|
-
this.input.focused = value;
|
|
228
|
-
}
|
|
229
|
-
constructor(options, theme, on_submit, on_cancel) {
|
|
230
|
-
this.options = options;
|
|
231
|
-
this.theme = theme;
|
|
232
|
-
this.on_submit = on_submit;
|
|
233
|
-
this.on_cancel = on_cancel;
|
|
234
|
-
this.input.setValue(options.initial_value ?? '');
|
|
235
|
-
this.input.onSubmit = (value) => {
|
|
236
|
-
const next_value = options.trim === false ? value : value.trim();
|
|
237
|
-
if (!options.allow_empty && next_value.length === 0)
|
|
238
|
-
return;
|
|
239
|
-
this.on_submit(next_value);
|
|
240
|
-
};
|
|
241
|
-
this.input.onEscape = this.on_cancel;
|
|
242
|
-
}
|
|
243
|
-
render(width) {
|
|
244
|
-
const lines = [];
|
|
245
|
-
if (this.options.label) {
|
|
246
|
-
for (const line of wrapTextWithAnsi(this.options.label, width)) {
|
|
247
|
-
lines.push(this.theme.fg('muted', line));
|
|
248
|
-
}
|
|
249
|
-
lines.push('');
|
|
250
|
-
}
|
|
251
|
-
lines.push(...this.input.render(width));
|
|
252
|
-
lines.push('');
|
|
253
|
-
lines.push(this.theme.fg('dim', this.options.allow_empty
|
|
254
|
-
? 'Enter submits • Esc cancels'
|
|
255
|
-
: 'Enter submits non-empty value • Esc cancels'));
|
|
256
|
-
return lines.map((line) => truncateToWidth(line, width));
|
|
257
|
-
}
|
|
258
|
-
invalidate() {
|
|
259
|
-
this.input.invalidate();
|
|
260
|
-
}
|
|
261
|
-
handleInput(data) {
|
|
262
|
-
this.input.handleInput(data);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
class DetailedSettingsList {
|
|
266
|
-
items;
|
|
267
|
-
max_visible;
|
|
268
|
-
theme;
|
|
269
|
-
on_change;
|
|
270
|
-
on_cancel;
|
|
271
|
-
options;
|
|
272
|
-
filtered_items;
|
|
273
|
-
selected_index = 0;
|
|
274
|
-
search_input;
|
|
275
|
-
constructor(items, max_visible, theme, on_change, on_cancel, options) {
|
|
276
|
-
this.items = items;
|
|
277
|
-
this.max_visible = max_visible;
|
|
278
|
-
this.theme = theme;
|
|
279
|
-
this.on_change = on_change;
|
|
280
|
-
this.on_cancel = on_cancel;
|
|
281
|
-
this.options = options;
|
|
282
|
-
this.filtered_items = items;
|
|
283
|
-
if (options.enable_search)
|
|
284
|
-
this.search_input = new Input();
|
|
285
|
-
}
|
|
286
|
-
get_selected_item() {
|
|
287
|
-
return this.get_display_items()[this.selected_index];
|
|
288
|
-
}
|
|
289
|
-
set_max_visible(max_visible) {
|
|
290
|
-
this.max_visible = Math.max(1, max_visible);
|
|
291
|
-
}
|
|
292
|
-
render(width) {
|
|
293
|
-
const lines = [];
|
|
294
|
-
if (this.search_input) {
|
|
295
|
-
lines.push(...this.search_input.render(width));
|
|
296
|
-
lines.push('');
|
|
297
|
-
}
|
|
298
|
-
const display_items = this.get_display_items();
|
|
299
|
-
if (display_items.length === 0) {
|
|
300
|
-
lines.push(truncateToWidth(this.theme.hint(this.items.length === 0
|
|
301
|
-
? ' No settings available'
|
|
302
|
-
: ' No matching settings'), width));
|
|
303
|
-
this.add_hint_line(lines, width);
|
|
304
|
-
return lines;
|
|
305
|
-
}
|
|
306
|
-
const start_index = Math.max(0, Math.min(this.selected_index - Math.floor(this.max_visible / 2), display_items.length - this.max_visible));
|
|
307
|
-
const end_index = Math.min(start_index + this.max_visible, display_items.length);
|
|
308
|
-
const max_label_width = Math.min(30, Math.max(...this.items.map((item) => visibleWidth(item.label))));
|
|
309
|
-
const max_value_width = Math.min(14, Math.max(...this.items.map((item) => visibleWidth(item.currentValue))));
|
|
310
|
-
for (let index = start_index; index < end_index; index++) {
|
|
311
|
-
const item = display_items[index];
|
|
312
|
-
if (!item)
|
|
313
|
-
continue;
|
|
314
|
-
lines.push(this.render_item(item, index === this.selected_index, width, max_label_width, max_value_width));
|
|
315
|
-
}
|
|
316
|
-
if (start_index > 0 || end_index < display_items.length) {
|
|
317
|
-
lines.push(this.theme.hint(truncateToWidth(` (${this.selected_index + 1}/${display_items.length})`, width - 2, '')));
|
|
318
|
-
}
|
|
319
|
-
const selected_item = this.get_selected_item();
|
|
320
|
-
if (selected_item?.description) {
|
|
321
|
-
lines.push('');
|
|
322
|
-
for (const line of wrapTextWithAnsi(selected_item.description, width - 4)) {
|
|
323
|
-
lines.push(this.theme.description(` ${line}`));
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
this.add_hint_line(lines, width);
|
|
327
|
-
return lines;
|
|
328
|
-
}
|
|
329
|
-
invalidate() {
|
|
330
|
-
// No cached rendering.
|
|
331
|
-
}
|
|
332
|
-
handleInput(data) {
|
|
333
|
-
const keybindings = getKeybindings();
|
|
334
|
-
const display_items = this.get_display_items();
|
|
335
|
-
if (keybindings.matches(data, 'tui.select.up')) {
|
|
336
|
-
if (display_items.length === 0)
|
|
337
|
-
return;
|
|
338
|
-
this.selected_index =
|
|
339
|
-
this.selected_index === 0
|
|
340
|
-
? display_items.length - 1
|
|
341
|
-
: this.selected_index - 1;
|
|
342
|
-
}
|
|
343
|
-
else if (keybindings.matches(data, 'tui.select.down')) {
|
|
344
|
-
if (display_items.length === 0)
|
|
345
|
-
return;
|
|
346
|
-
this.selected_index =
|
|
347
|
-
this.selected_index === display_items.length - 1
|
|
348
|
-
? 0
|
|
349
|
-
: this.selected_index + 1;
|
|
350
|
-
}
|
|
351
|
-
else if (keybindings.matches(data, 'tui.select.confirm') ||
|
|
352
|
-
data === ' ') {
|
|
353
|
-
this.activate_item();
|
|
354
|
-
}
|
|
355
|
-
else if (keybindings.matches(data, 'tui.select.cancel')) {
|
|
356
|
-
this.on_cancel();
|
|
357
|
-
}
|
|
358
|
-
else if (this.search_input) {
|
|
359
|
-
const sanitized = data.replace(/ /g, '');
|
|
360
|
-
if (!sanitized)
|
|
361
|
-
return;
|
|
362
|
-
this.search_input.handleInput(sanitized);
|
|
363
|
-
this.apply_filter(this.search_input.getValue());
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
get_display_items() {
|
|
367
|
-
return this.search_input ? this.filtered_items : this.items;
|
|
368
|
-
}
|
|
369
|
-
render_item(item, selected, width, max_label_width, max_value_width) {
|
|
370
|
-
const prefix = selected ? this.theme.cursor : ' ';
|
|
371
|
-
const label = truncateToWidth(item.label, max_label_width, '…');
|
|
372
|
-
const padded_label = label +
|
|
373
|
-
' '.repeat(Math.max(0, max_label_width - visibleWidth(label)));
|
|
374
|
-
const value = truncateToWidth(item.currentValue, max_value_width, '');
|
|
375
|
-
const padded_value = value +
|
|
376
|
-
' '.repeat(Math.max(0, max_value_width - visibleWidth(value)));
|
|
377
|
-
const detail = this.options.detail?.(item) ?? '';
|
|
378
|
-
const line = [
|
|
379
|
-
prefix,
|
|
380
|
-
this.theme.label(padded_label, selected),
|
|
381
|
-
' ',
|
|
382
|
-
this.theme.value(padded_value, selected),
|
|
383
|
-
detail ? ` ${this.theme.description(detail)}` : '',
|
|
384
|
-
].join('');
|
|
385
|
-
return truncateToWidth(line, width);
|
|
386
|
-
}
|
|
387
|
-
activate_item() {
|
|
388
|
-
const item = this.get_selected_item();
|
|
389
|
-
if (!item?.values || item.values.length === 0)
|
|
390
|
-
return;
|
|
391
|
-
const current_index = item.values.indexOf(item.currentValue);
|
|
392
|
-
const next_index = (current_index + 1) % item.values.length;
|
|
393
|
-
const new_value = item.values[next_index];
|
|
394
|
-
item.currentValue = new_value;
|
|
395
|
-
this.on_change(item.id, new_value);
|
|
396
|
-
}
|
|
397
|
-
apply_filter(query) {
|
|
398
|
-
this.filtered_items = fuzzyFilter(this.items, query, (item) => [
|
|
399
|
-
item.label,
|
|
400
|
-
item.currentValue,
|
|
401
|
-
item.description,
|
|
402
|
-
this.options.detail?.(item),
|
|
403
|
-
]
|
|
404
|
-
.filter(Boolean)
|
|
405
|
-
.join(' '));
|
|
406
|
-
this.selected_index = 0;
|
|
407
|
-
}
|
|
408
|
-
add_hint_line(lines, width) {
|
|
409
|
-
lines.push('');
|
|
410
|
-
lines.push(truncateToWidth(this.theme.hint(this.search_input
|
|
411
|
-
? ' Type to search · Enter/Space to change · Esc to cancel'
|
|
412
|
-
: ' Enter/Space to change · Esc to cancel'), width));
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
function pad_to_width(line, width) {
|
|
416
|
-
return line + ' '.repeat(Math.max(0, width - visibleWidth(line)));
|
|
417
|
-
}
|
|
418
|
-
function render_border_line(chars, width, color) {
|
|
419
|
-
if (width <= 1)
|
|
420
|
-
return color(chars.top_left);
|
|
421
|
-
return color(chars.top_left +
|
|
422
|
-
chars.top.repeat(Math.max(0, width - 2)) +
|
|
423
|
-
chars.top_right);
|
|
424
|
-
}
|
|
425
|
-
function render_bottom_border_line(chars, width, color) {
|
|
426
|
-
if (width <= 1)
|
|
427
|
-
return color(chars.bottom_left);
|
|
428
|
-
return color(chars.bottom_left +
|
|
429
|
-
chars.bottom.repeat(Math.max(0, width - 2)) +
|
|
430
|
-
chars.bottom_right);
|
|
431
|
-
}
|
|
432
|
-
function render_framed_modal(content, width, style, theme) {
|
|
433
|
-
const resolved_style = { ...default_modal_style, ...style };
|
|
434
|
-
const color = (text) => theme.fg(resolved_style.border_color, text);
|
|
435
|
-
if (resolved_style.border === 'none') {
|
|
436
|
-
return content.render(width);
|
|
437
|
-
}
|
|
438
|
-
if (resolved_style.border === 'line') {
|
|
439
|
-
const line = color('─'.repeat(Math.max(1, width)));
|
|
440
|
-
return [line, ...content.render(width), line];
|
|
441
|
-
}
|
|
442
|
-
const chars = border_characters[resolved_style.border];
|
|
443
|
-
const inner_width = Math.max(1, width - 2);
|
|
444
|
-
const body_lines = content.render(inner_width).map((line) => {
|
|
445
|
-
const padded = pad_to_width(truncateToWidth(line, inner_width, '', true), inner_width);
|
|
446
|
-
return `${color(chars.left)}${padded}${color(chars.right)}`;
|
|
447
|
-
});
|
|
448
|
-
return [
|
|
449
|
-
render_border_line(chars, width, color),
|
|
450
|
-
...body_lines,
|
|
451
|
-
render_bottom_border_line(chars, width, color),
|
|
452
|
-
];
|
|
453
|
-
}
|
|
454
|
-
export async function show_modal(ctx, options, create_body) {
|
|
455
|
-
return await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
456
|
-
const layout = {
|
|
457
|
-
get_max_body_lines: (body_width) => get_modal_body_line_budget(tui, options, body_width),
|
|
458
|
-
};
|
|
459
|
-
const body = create_body({ done }, theme, layout, tui);
|
|
460
|
-
return {
|
|
461
|
-
get focused() {
|
|
462
|
-
return is_focusable(body) ? body.focused : false;
|
|
463
|
-
},
|
|
464
|
-
set focused(value) {
|
|
465
|
-
if (is_focusable(body))
|
|
466
|
-
body.focused = value;
|
|
467
|
-
},
|
|
468
|
-
render: (width) => {
|
|
469
|
-
const content = new Box(2, 1);
|
|
470
|
-
content.addChild(new Text(theme.fg('accent', theme.bold(options.title)), 0, 0));
|
|
471
|
-
for (const line of normalize_text(options.subtitle)) {
|
|
472
|
-
content.addChild(new Text(theme.fg('muted', line), 0, 0));
|
|
473
|
-
}
|
|
474
|
-
content.addChild({
|
|
475
|
-
render: (body_width) => body
|
|
476
|
-
.render(body_width)
|
|
477
|
-
.slice(0, layout.get_max_body_lines(body_width)),
|
|
478
|
-
invalidate: () => body.invalidate(),
|
|
479
|
-
});
|
|
480
|
-
for (const line of normalize_text(options.footer)) {
|
|
481
|
-
content.addChild(new Text(theme.fg('dim', line), 0, 0));
|
|
482
|
-
}
|
|
483
|
-
return render_framed_modal(content, width, options.style, theme);
|
|
484
|
-
},
|
|
485
|
-
invalidate: () => {
|
|
486
|
-
body.invalidate();
|
|
487
|
-
},
|
|
488
|
-
dispose: () => body.dispose?.(),
|
|
489
|
-
handleInput: (data) => {
|
|
490
|
-
body.handleInput?.(data);
|
|
491
|
-
tui.requestRender();
|
|
492
|
-
},
|
|
493
|
-
};
|
|
494
|
-
}, {
|
|
495
|
-
overlay: true,
|
|
496
|
-
overlayOptions: {
|
|
497
|
-
...default_overlay_options,
|
|
498
|
-
...options.overlay_options,
|
|
499
|
-
},
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
export async function show_picker_modal(ctx, options) {
|
|
503
|
-
if (options.items.length === 0) {
|
|
504
|
-
if (options.empty_message)
|
|
505
|
-
ctx.ui.notify(options.empty_message);
|
|
506
|
-
return undefined;
|
|
507
|
-
}
|
|
508
|
-
return await show_modal(ctx, {
|
|
509
|
-
title: options.title,
|
|
510
|
-
subtitle: options.subtitle,
|
|
511
|
-
footer: options.footer ?? '↑↓ navigate • enter select • esc cancel',
|
|
512
|
-
overlay_options: options.overlay_options,
|
|
513
|
-
style: options.style,
|
|
514
|
-
}, ({ done }, theme, layout) => {
|
|
515
|
-
const preferred_max_visible = options.max_visible ?? Math.min(options.items.length, 12);
|
|
516
|
-
const select_list = new SelectList(options.items, fit_visible_items(options.items.length, preferred_max_visible, layout.get_max_body_lines()), make_select_theme(theme), options.layout);
|
|
517
|
-
if (options.initial_index !== undefined) {
|
|
518
|
-
select_list.setSelectedIndex(options.initial_index);
|
|
519
|
-
}
|
|
520
|
-
select_list.onSelect = (item) => done(item.value);
|
|
521
|
-
select_list.onCancel = () => done(undefined);
|
|
522
|
-
return {
|
|
523
|
-
render: (width) => {
|
|
524
|
-
set_component_max_visible(select_list, fit_visible_items(options.items.length, preferred_max_visible, layout.get_max_body_lines(width)));
|
|
525
|
-
return select_list.render(width);
|
|
526
|
-
},
|
|
527
|
-
invalidate: () => select_list.invalidate(),
|
|
528
|
-
handleInput: (data) => select_list.handleInput(data),
|
|
529
|
-
};
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
export async function show_text_modal(ctx, options) {
|
|
533
|
-
await show_modal(ctx, {
|
|
534
|
-
title: options.title,
|
|
535
|
-
subtitle: options.subtitle,
|
|
536
|
-
footer: options.footer ?? '↑↓ scroll • esc back',
|
|
537
|
-
overlay_options: options.overlay_options,
|
|
538
|
-
style: options.style,
|
|
539
|
-
}, ({ done }, theme, layout) => {
|
|
540
|
-
const preferred_max_visible = options.max_visible_lines ?? 18;
|
|
541
|
-
const body = new TextModalBody(options.text, Math.min(preferred_max_visible, layout.get_max_body_lines()), theme, () => done());
|
|
542
|
-
return {
|
|
543
|
-
render: (width) => {
|
|
544
|
-
body.set_max_visible_lines(Math.min(preferred_max_visible, layout.get_max_body_lines(width)));
|
|
545
|
-
return body.render(width);
|
|
546
|
-
},
|
|
547
|
-
invalidate: () => body.invalidate(),
|
|
548
|
-
handleInput: (data) => body.handleInput(data),
|
|
549
|
-
};
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
export async function show_input_modal(ctx, options) {
|
|
553
|
-
return await show_modal(ctx, {
|
|
554
|
-
title: options.title,
|
|
555
|
-
subtitle: options.subtitle,
|
|
556
|
-
footer: options.footer,
|
|
557
|
-
overlay_options: {
|
|
558
|
-
width: '70%',
|
|
559
|
-
minWidth: 50,
|
|
560
|
-
maxHeight: '60%',
|
|
561
|
-
...options.overlay_options,
|
|
562
|
-
},
|
|
563
|
-
style: options.style,
|
|
564
|
-
}, ({ done }, theme) => new InputModalBody(options, theme, (value) => done(value), () => done(undefined)));
|
|
565
|
-
}
|
|
566
|
-
export async function show_confirm_modal(ctx, options) {
|
|
567
|
-
const selected = await show_picker_modal(ctx, {
|
|
568
|
-
title: options.title,
|
|
569
|
-
subtitle: options.message,
|
|
570
|
-
footer: options.footer ?? 'enter selects • esc cancels',
|
|
571
|
-
overlay_options: {
|
|
572
|
-
width: '70%',
|
|
573
|
-
minWidth: 50,
|
|
574
|
-
maxHeight: '60%',
|
|
575
|
-
...options.overlay_options,
|
|
576
|
-
},
|
|
577
|
-
style: options.style,
|
|
578
|
-
items: [
|
|
579
|
-
{
|
|
580
|
-
value: 'confirm',
|
|
581
|
-
label: options.confirm_label ?? 'Confirm',
|
|
582
|
-
description: 'Proceed with this action',
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
value: 'cancel',
|
|
586
|
-
label: options.cancel_label ?? 'Cancel',
|
|
587
|
-
description: 'Go back without changing anything',
|
|
588
|
-
},
|
|
589
|
-
],
|
|
590
|
-
});
|
|
591
|
-
return selected === 'confirm';
|
|
592
|
-
}
|
|
593
|
-
export async function show_settings_modal(ctx, options) {
|
|
594
|
-
await show_modal(ctx, {
|
|
595
|
-
title: options.title,
|
|
596
|
-
subtitle: options.subtitle,
|
|
597
|
-
footer: options.footer ??
|
|
598
|
-
'search filters • enter toggles • esc close',
|
|
599
|
-
overlay_options: options.overlay_options,
|
|
600
|
-
style: options.style,
|
|
601
|
-
}, ({ done }, theme, layout) => {
|
|
602
|
-
const preferred_max_visible = options.max_visible ??
|
|
603
|
-
Math.min(Math.max(options.items.length + 4, 8), 16);
|
|
604
|
-
const get_max_visible = (width) => fit_visible_items(options.items.length, preferred_max_visible, layout.get_max_body_lines(width) -
|
|
605
|
-
(options.enable_search ? 2 : 0) -
|
|
606
|
-
2 -
|
|
607
|
-
2 -
|
|
608
|
-
(options.metadata ? 3 : 0));
|
|
609
|
-
const settings_theme = make_settings_theme(theme);
|
|
610
|
-
const handle_change = (id, new_value) => {
|
|
611
|
-
if (options.on_change(id, new_value))
|
|
612
|
-
done();
|
|
613
|
-
};
|
|
614
|
-
const handle_cancel = () => {
|
|
615
|
-
options.on_cancel?.();
|
|
616
|
-
done();
|
|
617
|
-
};
|
|
618
|
-
const list = options.detail
|
|
619
|
-
? new DetailedSettingsList(options.items, get_max_visible(), settings_theme, handle_change, handle_cancel, {
|
|
620
|
-
enable_search: options.enable_search,
|
|
621
|
-
detail: options.detail,
|
|
622
|
-
})
|
|
623
|
-
: new SettingsList(options.items, get_max_visible(), settings_theme, handle_change, handle_cancel, { enableSearch: options.enable_search });
|
|
624
|
-
return {
|
|
625
|
-
render: (width) => {
|
|
626
|
-
const max_visible = get_max_visible(width);
|
|
627
|
-
if (list instanceof DetailedSettingsList) {
|
|
628
|
-
list.set_max_visible(max_visible);
|
|
629
|
-
}
|
|
630
|
-
else {
|
|
631
|
-
set_component_max_visible(list, max_visible);
|
|
632
|
-
}
|
|
633
|
-
const lines = list.render(width);
|
|
634
|
-
const selected_item = list instanceof DetailedSettingsList
|
|
635
|
-
? list.get_selected_item()
|
|
636
|
-
: get_selected_setting(list);
|
|
637
|
-
const metadata_lines = normalize_metadata(options.metadata, selected_item);
|
|
638
|
-
if (metadata_lines.length === 0)
|
|
639
|
-
return lines;
|
|
640
|
-
return [
|
|
641
|
-
...lines,
|
|
642
|
-
'',
|
|
643
|
-
theme.fg('accent', theme.bold('Details')),
|
|
644
|
-
...metadata_lines.map((line) => theme.fg('muted', line)),
|
|
645
|
-
];
|
|
646
|
-
},
|
|
647
|
-
invalidate: () => list.invalidate(),
|
|
648
|
-
handleInput: (data) => list.handleInput(data),
|
|
649
|
-
};
|
|
650
|
-
});
|
|
651
|
-
}
|
|
1
|
+
export { show_confirm_modal, show_input_modal, show_modal, show_picker_modal, show_settings_modal, show_text_modal, } from './modal/show.js';
|
|
652
2
|
//# sourceMappingURL=index.js.map
|