@termuijs/ui 0.1.0

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.js ADDED
@@ -0,0 +1,746 @@
1
+ // src/index.ts
2
+ import {
3
+ Box,
4
+ Text,
5
+ Table,
6
+ List,
7
+ TextInput,
8
+ Gauge,
9
+ Sparkline,
10
+ StatusIndicator,
11
+ LogView,
12
+ ProgressBar,
13
+ Spinner,
14
+ Widget as Widget12
15
+ } from "@termuijs/widgets";
16
+
17
+ // src/Divider.ts
18
+ import { Widget } from "@termuijs/widgets";
19
+ import { mergeStyles, defaultStyle, styleToCellAttrs } from "@termuijs/core";
20
+ var Divider = class extends Widget {
21
+ _char;
22
+ _color;
23
+ _title;
24
+ constructor(options = {}) {
25
+ super(mergeStyles(defaultStyle(), { height: 1 }));
26
+ this._char = options.char ?? "\u2500";
27
+ this._color = options.color;
28
+ this._title = options.title ?? "";
29
+ }
30
+ _renderSelf(screen) {
31
+ const { x, y, width } = this._rect;
32
+ if (width <= 0) return;
33
+ const attrs = styleToCellAttrs(this.style);
34
+ if (this._color) attrs.fg = this._color;
35
+ attrs.dim = true;
36
+ if (this._title) {
37
+ const titleStr = ` ${this._title} `;
38
+ const padLeft = Math.max(0, Math.floor((width - titleStr.length) / 2));
39
+ const padRight = Math.max(0, width - padLeft - titleStr.length);
40
+ const line = this._char.repeat(padLeft) + titleStr + this._char.repeat(padRight);
41
+ screen.writeString(x, y, line.slice(0, width), attrs);
42
+ } else {
43
+ screen.writeString(x, y, this._char.repeat(width), attrs);
44
+ }
45
+ }
46
+ };
47
+
48
+ // src/Spacer.ts
49
+ import { Widget as Widget2 } from "@termuijs/widgets";
50
+ import { mergeStyles as mergeStyles2, defaultStyle as defaultStyle2 } from "@termuijs/core";
51
+ var Spacer = class extends Widget2 {
52
+ constructor(grow = 1) {
53
+ super(mergeStyles2(defaultStyle2(), { flexGrow: grow }));
54
+ }
55
+ _renderSelf(_screen) {
56
+ }
57
+ };
58
+
59
+ // src/Tabs.ts
60
+ import { Widget as Widget3 } from "@termuijs/widgets";
61
+ import { mergeStyles as mergeStyles3, defaultStyle as defaultStyle3, styleToCellAttrs as styleToCellAttrs2 } from "@termuijs/core";
62
+ var Tabs = class extends Widget3 {
63
+ _tabs = [];
64
+ _activeIndex = 0;
65
+ _activeColor;
66
+ _inactiveColor;
67
+ focusable = true;
68
+ constructor(tabs, options = {}) {
69
+ super(mergeStyles3(defaultStyle3(), { flexGrow: 1, border: options.border ?? "single" }));
70
+ this._tabs = tabs;
71
+ this._activeColor = options.activeColor ?? { type: "named", name: "cyan" };
72
+ this._inactiveColor = options.inactiveColor ?? { type: "named", name: "brightBlack" };
73
+ }
74
+ get activeIndex() {
75
+ return this._activeIndex;
76
+ }
77
+ selectTab(i) {
78
+ if (i >= 0 && i < this._tabs.length) this._activeIndex = i;
79
+ }
80
+ nextTab() {
81
+ this._activeIndex = (this._activeIndex + 1) % this._tabs.length;
82
+ }
83
+ prevTab() {
84
+ this._activeIndex = (this._activeIndex - 1 + this._tabs.length) % this._tabs.length;
85
+ }
86
+ get activeContent() {
87
+ return this._tabs[this._activeIndex]?.content;
88
+ }
89
+ _renderSelf(screen) {
90
+ const { x, y, width, height } = this._rect;
91
+ if (width <= 0 || height <= 0) return;
92
+ const attrs = styleToCellAttrs2(this.style);
93
+ let col = x;
94
+ for (let i = 0; i < this._tabs.length; i++) {
95
+ const tab = this._tabs[i];
96
+ const isActive = i === this._activeIndex;
97
+ const label = isActive ? ` \u25CF ${tab.label} ` : ` ${tab.label} `;
98
+ screen.writeString(col, y, label, {
99
+ ...attrs,
100
+ fg: isActive ? this._activeColor : this._inactiveColor,
101
+ bold: isActive,
102
+ dim: !isActive
103
+ });
104
+ col += label.length;
105
+ if (i < this._tabs.length - 1) {
106
+ screen.writeString(col, y, "\u2502", { ...attrs, dim: true });
107
+ col++;
108
+ }
109
+ }
110
+ if (height > 1) screen.writeString(x, y + 1, "\u2500".repeat(width), { ...attrs, dim: true });
111
+ }
112
+ };
113
+
114
+ // src/Modal.ts
115
+ import { Widget as Widget4 } from "@termuijs/widgets";
116
+ import { mergeStyles as mergeStyles4, defaultStyle as defaultStyle4, styleToCellAttrs as styleToCellAttrs3, getBorderChars } from "@termuijs/core";
117
+ var Modal = class extends Widget4 {
118
+ _title;
119
+ _modalWidth;
120
+ _modalHeight;
121
+ _borderColor;
122
+ _backdropChar;
123
+ _visible = false;
124
+ _content = null;
125
+ constructor(options = {}) {
126
+ super(mergeStyles4(defaultStyle4(), {}));
127
+ this._title = options.title ?? "";
128
+ this._modalWidth = options.width ?? 50;
129
+ this._modalHeight = options.height ?? 15;
130
+ this._borderColor = options.borderColor ?? { type: "named", name: "cyan" };
131
+ this._backdropChar = options.backdropChar ?? "\u2591";
132
+ }
133
+ get visible() {
134
+ return this._visible;
135
+ }
136
+ show() {
137
+ this._visible = true;
138
+ }
139
+ hide() {
140
+ this._visible = false;
141
+ }
142
+ toggle() {
143
+ this._visible = !this._visible;
144
+ }
145
+ setContent(content) {
146
+ this._content = content;
147
+ }
148
+ _renderSelf(screen) {
149
+ if (!this._visible) return;
150
+ const { x, y, width, height } = this._rect;
151
+ const attrs = styleToCellAttrs3(this.style);
152
+ for (let r = 0; r < height; r++) {
153
+ screen.writeString(x, y + r, this._backdropChar.repeat(width), { ...attrs, dim: true });
154
+ }
155
+ const mw = Math.min(this._modalWidth, width - 4);
156
+ const mh = Math.min(this._modalHeight, height - 2);
157
+ const mx = x + Math.floor((width - mw) / 2);
158
+ const my = y + Math.floor((height - mh) / 2);
159
+ const border = getBorderChars("single");
160
+ if (!border) return;
161
+ const bAttrs = { ...attrs, fg: this._borderColor };
162
+ const titleStr = this._title ? ` ${this._title} ` : "";
163
+ const topFill = mw - 2 - titleStr.length;
164
+ const tl = Math.floor(topFill / 2);
165
+ const tr = topFill - tl;
166
+ screen.writeString(mx, my, border.topLeft + border.top.repeat(tl) + titleStr + border.top.repeat(Math.max(0, tr)) + border.topRight, bAttrs);
167
+ const clr = styleToCellAttrs3(this.style);
168
+ for (let r = 1; r < mh - 1; r++) {
169
+ screen.writeString(mx, my + r, border.left, bAttrs);
170
+ screen.writeString(mx + 1, my + r, " ".repeat(mw - 2), clr);
171
+ screen.writeString(mx + mw - 1, my + r, border.right, bAttrs);
172
+ }
173
+ screen.writeString(mx, my + mh - 1, border.bottomLeft + border.bottom.repeat(mw - 2) + border.bottomRight, bAttrs);
174
+ if (this._content) {
175
+ const cr = { x: mx + 2, y: my + 1, width: mw - 4, height: mh - 2 };
176
+ this._content._rect = cr;
177
+ this._content._renderSelf(screen);
178
+ }
179
+ }
180
+ };
181
+
182
+ // src/Select.ts
183
+ import { Widget as Widget5 } from "@termuijs/widgets";
184
+ import { mergeStyles as mergeStyles5, defaultStyle as defaultStyle5, styleToCellAttrs as styleToCellAttrs4 } from "@termuijs/core";
185
+ var Select = class extends Widget5 {
186
+ _options;
187
+ _selectedIndex = 0;
188
+ _isOpen = false;
189
+ _placeholder;
190
+ _activeColor;
191
+ _onSelect;
192
+ focusable = true;
193
+ constructor(options, config = {}) {
194
+ super(mergeStyles5(defaultStyle5(), { height: 1 }));
195
+ this._options = options;
196
+ this._placeholder = config.placeholder ?? "Select...";
197
+ this._activeColor = config.activeColor ?? { type: "named", name: "cyan" };
198
+ this._onSelect = config.onSelect;
199
+ }
200
+ get selectedOption() {
201
+ return this._options[this._selectedIndex];
202
+ }
203
+ get selectedIndex() {
204
+ return this._selectedIndex;
205
+ }
206
+ get isOpen() {
207
+ return this._isOpen;
208
+ }
209
+ open() {
210
+ this._isOpen = true;
211
+ }
212
+ close() {
213
+ this._isOpen = false;
214
+ }
215
+ toggle() {
216
+ this._isOpen = !this._isOpen;
217
+ }
218
+ selectNext() {
219
+ let n = this._selectedIndex + 1;
220
+ while (n < this._options.length && this._options[n].disabled) n++;
221
+ if (n < this._options.length) this._selectedIndex = n;
222
+ }
223
+ selectPrev() {
224
+ let n = this._selectedIndex - 1;
225
+ while (n >= 0 && this._options[n].disabled) n--;
226
+ if (n >= 0) this._selectedIndex = n;
227
+ }
228
+ confirm() {
229
+ const opt = this._options[this._selectedIndex];
230
+ if (opt && !opt.disabled) {
231
+ this._onSelect?.(opt, this._selectedIndex);
232
+ this._isOpen = false;
233
+ }
234
+ }
235
+ _renderSelf(screen) {
236
+ const { x, y, width } = this._rect;
237
+ if (width <= 0) return;
238
+ const attrs = styleToCellAttrs4(this.style);
239
+ const sel = this._options[this._selectedIndex];
240
+ const label = sel ? sel.label : this._placeholder;
241
+ const prefix = this._isOpen ? "\u25BC " : "\u25B6 ";
242
+ screen.writeString(x, y, prefix + label.slice(0, width - 2), { ...attrs, fg: this._activeColor });
243
+ if (this._isOpen) {
244
+ for (let i = 0; i < this._options.length; i++) {
245
+ const o = this._options[i];
246
+ const isSel = i === this._selectedIndex;
247
+ const m = isSel ? "\u25CF " : " ";
248
+ screen.writeString(x, y + 1 + i, m + o.label.slice(0, width - 2), {
249
+ ...attrs,
250
+ fg: o.disabled ? { type: "named", name: "brightBlack" } : isSel ? this._activeColor : attrs.fg,
251
+ bold: isSel,
252
+ dim: o.disabled
253
+ });
254
+ }
255
+ }
256
+ }
257
+ };
258
+
259
+ // src/MultiSelect.ts
260
+ import { Widget as Widget6 } from "@termuijs/widgets";
261
+ import { mergeStyles as mergeStyles6, defaultStyle as defaultStyle6, styleToCellAttrs as styleToCellAttrs5 } from "@termuijs/core";
262
+ var MultiSelect = class extends Widget6 {
263
+ _options;
264
+ _cursorIndex = 0;
265
+ _checked = /* @__PURE__ */ new Set();
266
+ _activeColor;
267
+ _checkChar;
268
+ _uncheckChar;
269
+ _onSubmit;
270
+ focusable = true;
271
+ constructor(options, config = {}) {
272
+ super(mergeStyles6(defaultStyle6(), { height: Math.max(options.length, 1) }));
273
+ this._options = options;
274
+ this._activeColor = config.activeColor ?? { type: "named", name: "cyan" };
275
+ this._checkChar = config.checkChar ?? "\u25FC";
276
+ this._uncheckChar = config.uncheckChar ?? "\u25FB";
277
+ this._onSubmit = config.onSubmit;
278
+ }
279
+ get selectedOptions() {
280
+ return [...this._checked].sort().map((i) => this._options[i]);
281
+ }
282
+ selectNext() {
283
+ let n = this._cursorIndex + 1;
284
+ while (n < this._options.length && this._options[n].disabled) n++;
285
+ if (n < this._options.length) this._cursorIndex = n;
286
+ }
287
+ selectPrev() {
288
+ let n = this._cursorIndex - 1;
289
+ while (n >= 0 && this._options[n].disabled) n--;
290
+ if (n >= 0) this._cursorIndex = n;
291
+ }
292
+ toggleCurrent() {
293
+ const o = this._options[this._cursorIndex];
294
+ if (o && !o.disabled) {
295
+ this._checked.has(this._cursorIndex) ? this._checked.delete(this._cursorIndex) : this._checked.add(this._cursorIndex);
296
+ }
297
+ }
298
+ submit() {
299
+ this._onSubmit?.(this.selectedOptions);
300
+ }
301
+ _renderSelf(screen) {
302
+ const { x, y, width, height } = this._rect;
303
+ if (width <= 0 || height <= 0) return;
304
+ const attrs = styleToCellAttrs5(this.style);
305
+ for (let i = 0; i < this._options.length && i < height; i++) {
306
+ const o = this._options[i];
307
+ const active = i === this._cursorIndex;
308
+ const checked = this._checked.has(i);
309
+ const label = `${active ? "\u276F " : " "}${checked ? this._checkChar : this._uncheckChar} ${o.label}`;
310
+ screen.writeString(x, y + i, label.slice(0, width), {
311
+ ...attrs,
312
+ fg: o.disabled ? { type: "named", name: "brightBlack" } : active ? this._activeColor : attrs.fg,
313
+ bold: active,
314
+ dim: o.disabled
315
+ });
316
+ }
317
+ }
318
+ };
319
+
320
+ // src/Tree.ts
321
+ import { Widget as Widget7 } from "@termuijs/widgets";
322
+ import { mergeStyles as mergeStyles7, defaultStyle as defaultStyle7, styleToCellAttrs as styleToCellAttrs6 } from "@termuijs/core";
323
+ var Tree = class extends Widget7 {
324
+ _roots;
325
+ _cursorIndex = 0;
326
+ _activeColor;
327
+ _onSelect;
328
+ focusable = true;
329
+ constructor(roots, options = {}) {
330
+ super(mergeStyles7(defaultStyle7(), { flexGrow: 1 }));
331
+ this._roots = roots;
332
+ this._activeColor = options.activeColor ?? { type: "named", name: "cyan" };
333
+ this._onSelect = options.onSelect;
334
+ }
335
+ _flatten() {
336
+ const result = [];
337
+ const walk = (nodes, depth, path) => {
338
+ for (let i = 0; i < nodes.length; i++) {
339
+ const node = nodes[i];
340
+ const hasChildren = (node.children?.length ?? 0) > 0;
341
+ result.push({ node, depth, path: [...path, i], hasChildren });
342
+ if (hasChildren && node.expanded) walk(node.children, depth + 1, [...path, i]);
343
+ }
344
+ };
345
+ walk(this._roots, 0, []);
346
+ return result;
347
+ }
348
+ selectNext() {
349
+ const f = this._flatten();
350
+ if (this._cursorIndex < f.length - 1) this._cursorIndex++;
351
+ }
352
+ selectPrev() {
353
+ if (this._cursorIndex > 0) this._cursorIndex--;
354
+ }
355
+ toggleExpand() {
356
+ const f = this._flatten();
357
+ const it = f[this._cursorIndex];
358
+ if (it?.hasChildren) it.node.expanded = !it.node.expanded;
359
+ }
360
+ confirm() {
361
+ const f = this._flatten();
362
+ const it = f[this._cursorIndex];
363
+ if (it) {
364
+ it.hasChildren ? this.toggleExpand() : this._onSelect?.(it.node, it.path);
365
+ }
366
+ }
367
+ _renderSelf(screen) {
368
+ const { x, y, width, height } = this._rect;
369
+ if (width <= 0 || height <= 0) return;
370
+ const attrs = styleToCellAttrs6(this.style);
371
+ const flat = this._flatten();
372
+ for (let i = 0; i < flat.length && i < height; i++) {
373
+ const it = flat[i];
374
+ const active = i === this._cursorIndex;
375
+ const indent = " ".repeat(it.depth);
376
+ const icon = it.hasChildren ? it.node.expanded ? "\u25BC " : "\u25B6 " : " ";
377
+ const nodeIcon = it.node.icon ? `${it.node.icon} ` : "";
378
+ const line = `${indent}${icon}${nodeIcon}${it.node.label}`;
379
+ screen.writeString(x, y + i, line.slice(0, width), { ...attrs, fg: active ? this._activeColor : attrs.fg, bold: active });
380
+ }
381
+ }
382
+ };
383
+
384
+ // src/Toast.ts
385
+ import { Widget as Widget8 } from "@termuijs/widgets";
386
+ import { mergeStyles as mergeStyles8, defaultStyle as defaultStyle8, styleToCellAttrs as styleToCellAttrs7 } from "@termuijs/core";
387
+ var ICONS = { info: "\u2139", success: "\u2713", warning: "\u26A0", error: "\u2717" };
388
+ var COLORS = { info: "cyan", success: "green", warning: "yellow", error: "red" };
389
+ var Toast = class extends Widget8 {
390
+ _messages = [];
391
+ _position;
392
+ _durationMs;
393
+ _maxVisible;
394
+ constructor(options = {}) {
395
+ super(mergeStyles8(defaultStyle8(), {}));
396
+ this._position = options.position ?? "top-right";
397
+ this._durationMs = options.durationMs ?? 3e3;
398
+ this._maxVisible = options.maxVisible ?? 5;
399
+ }
400
+ push(text, type = "info") {
401
+ this._messages.push({ text, type, expireAt: Date.now() + this._durationMs });
402
+ }
403
+ info(text) {
404
+ this.push(text, "info");
405
+ }
406
+ success(text) {
407
+ this.push(text, "success");
408
+ }
409
+ warning(text) {
410
+ this.push(text, "warning");
411
+ }
412
+ error(text) {
413
+ this.push(text, "error");
414
+ }
415
+ _renderSelf(screen) {
416
+ const now = Date.now();
417
+ this._messages = this._messages.filter((m) => m.expireAt > now);
418
+ if (this._messages.length === 0) return;
419
+ const { x, y, width, height } = this._rect;
420
+ const visible = this._messages.slice(-this._maxVisible);
421
+ const tw = Math.min(40, width - 2);
422
+ const isRight = this._position.includes("right");
423
+ const isBottom = this._position.includes("bottom");
424
+ const sx = isRight ? x + width - tw - 1 : x + 1;
425
+ const sy = isBottom ? y + height - visible.length - 1 : y + 1;
426
+ const attrs = styleToCellAttrs7(this.style);
427
+ for (let i = 0; i < visible.length; i++) {
428
+ const m = visible[i];
429
+ const label = ` ${ICONS[m.type]} ${m.text} `.slice(0, tw).padEnd(tw);
430
+ screen.writeString(sx, sy + i, label, { ...attrs, fg: { type: "named", name: COLORS[m.type] }, bold: true });
431
+ }
432
+ }
433
+ };
434
+
435
+ // src/ConfirmDialog.ts
436
+ import { Widget as Widget9 } from "@termuijs/widgets";
437
+ import { mergeStyles as mergeStyles9, defaultStyle as defaultStyle9, styleToCellAttrs as styleToCellAttrs8, getBorderChars as getBorderChars2 } from "@termuijs/core";
438
+ var ConfirmDialog = class extends Widget9 {
439
+ _message;
440
+ _confirmLabel;
441
+ _cancelLabel;
442
+ _borderColor;
443
+ _selected = "confirm";
444
+ _visible = false;
445
+ _onConfirm;
446
+ _onCancel;
447
+ focusable = true;
448
+ constructor(options) {
449
+ super(mergeStyles9(defaultStyle9(), {}));
450
+ this._message = options.message;
451
+ this._confirmLabel = options.confirmLabel ?? "Yes";
452
+ this._cancelLabel = options.cancelLabel ?? "No";
453
+ this._borderColor = options.borderColor ?? { type: "named", name: "yellow" };
454
+ this._onConfirm = options.onConfirm;
455
+ this._onCancel = options.onCancel;
456
+ }
457
+ get visible() {
458
+ return this._visible;
459
+ }
460
+ show() {
461
+ this._visible = true;
462
+ this._selected = "confirm";
463
+ }
464
+ hide() {
465
+ this._visible = false;
466
+ }
467
+ selectConfirm() {
468
+ this._selected = "confirm";
469
+ }
470
+ selectCancel() {
471
+ this._selected = "cancel";
472
+ }
473
+ toggleSelection() {
474
+ this._selected = this._selected === "confirm" ? "cancel" : "confirm";
475
+ }
476
+ confirm() {
477
+ (this._selected === "confirm" ? this._onConfirm : this._onCancel)?.();
478
+ this._visible = false;
479
+ }
480
+ _renderSelf(screen) {
481
+ if (!this._visible) return;
482
+ const { x, y, width, height } = this._rect;
483
+ const attrs = styleToCellAttrs8(this.style);
484
+ for (let r = 0; r < height; r++) screen.writeString(x, y + r, "\u2591".repeat(width), { ...attrs, dim: true });
485
+ const bw = Math.min(40, width - 4);
486
+ const bh = 5;
487
+ const bx = x + Math.floor((width - bw) / 2);
488
+ const by = y + Math.floor((height - bh) / 2);
489
+ const border = getBorderChars2("single");
490
+ if (!border) return;
491
+ const ba = { ...attrs, fg: this._borderColor };
492
+ screen.writeString(bx, by, border.topLeft + border.top.repeat(bw - 2) + border.topRight, ba);
493
+ for (let r = 1; r < bh - 1; r++) {
494
+ screen.writeString(bx, by + r, border.left, ba);
495
+ screen.writeString(bx + 1, by + r, " ".repeat(bw - 2), attrs);
496
+ screen.writeString(bx + bw - 1, by + r, border.right, ba);
497
+ }
498
+ screen.writeString(bx, by + bh - 1, border.bottomLeft + border.bottom.repeat(bw - 2) + border.bottomRight, ba);
499
+ screen.writeString(bx + 2, by + 1, this._message.slice(0, bw - 4), { ...attrs, bold: true });
500
+ const yesStr = this._selected === "confirm" ? ` [${this._confirmLabel}] ` : ` ${this._confirmLabel} `;
501
+ const noStr = this._selected === "cancel" ? ` [${this._cancelLabel}] ` : ` ${this._cancelLabel} `;
502
+ screen.writeString(bx + 2, by + 3, yesStr, { ...attrs, fg: this._selected === "confirm" ? { type: "named", name: "green" } : attrs.fg, bold: this._selected === "confirm" });
503
+ screen.writeString(bx + 2 + yesStr.length + 2, by + 3, noStr, { ...attrs, fg: this._selected === "cancel" ? { type: "named", name: "red" } : attrs.fg, bold: this._selected === "cancel" });
504
+ }
505
+ };
506
+
507
+ // src/Form.ts
508
+ import { Widget as Widget10 } from "@termuijs/widgets";
509
+ import { mergeStyles as mergeStyles10, defaultStyle as defaultStyle10, styleToCellAttrs as styleToCellAttrs9 } from "@termuijs/core";
510
+ var Form = class extends Widget10 {
511
+ _fields;
512
+ _values = /* @__PURE__ */ new Map();
513
+ _errors = /* @__PURE__ */ new Map();
514
+ _activeField = 0;
515
+ _cursorPos = 0;
516
+ _labelColor;
517
+ _errorColor;
518
+ _activeColor;
519
+ _onSubmit;
520
+ focusable = true;
521
+ constructor(fields, options = {}) {
522
+ super(mergeStyles10(defaultStyle10(), { height: fields.length * 2 + 1, flexGrow: 1 }));
523
+ this._fields = fields;
524
+ this._labelColor = options.labelColor ?? { type: "named", name: "cyan" };
525
+ this._errorColor = options.errorColor ?? { type: "named", name: "red" };
526
+ this._activeColor = options.activeColor ?? { type: "named", name: "cyan" };
527
+ this._onSubmit = options.onSubmit;
528
+ for (const f of fields) this._values.set(f.name, "");
529
+ }
530
+ get values() {
531
+ const r = {};
532
+ for (const [k, v] of this._values) r[k] = v;
533
+ return r;
534
+ }
535
+ nextField() {
536
+ if (this._activeField < this._fields.length) {
537
+ this._activeField++;
538
+ this._cursorPos = 0;
539
+ }
540
+ }
541
+ prevField() {
542
+ if (this._activeField > 0) {
543
+ this._activeField--;
544
+ this._cursorPos = (this._values.get(this._fields[this._activeField].name) ?? "").length;
545
+ }
546
+ }
547
+ insertChar(ch) {
548
+ if (this._activeField >= this._fields.length) return;
549
+ const f = this._fields[this._activeField];
550
+ const cur = this._values.get(f.name) ?? "";
551
+ this._values.set(f.name, cur.slice(0, this._cursorPos) + ch + cur.slice(this._cursorPos));
552
+ this._cursorPos++;
553
+ this._errors.delete(f.name);
554
+ }
555
+ deleteBack() {
556
+ if (this._activeField >= this._fields.length) return;
557
+ const f = this._fields[this._activeField];
558
+ const cur = this._values.get(f.name) ?? "";
559
+ if (this._cursorPos > 0) {
560
+ this._values.set(f.name, cur.slice(0, this._cursorPos - 1) + cur.slice(this._cursorPos));
561
+ this._cursorPos--;
562
+ }
563
+ }
564
+ submit() {
565
+ this._errors.clear();
566
+ let hasErr = false;
567
+ for (const f of this._fields) {
568
+ const v = this._values.get(f.name) ?? "";
569
+ if (f.required && !v.trim()) {
570
+ this._errors.set(f.name, `${f.label} is required`);
571
+ hasErr = true;
572
+ }
573
+ if (f.validate) {
574
+ const e = f.validate(v);
575
+ if (e) {
576
+ this._errors.set(f.name, e);
577
+ hasErr = true;
578
+ }
579
+ }
580
+ }
581
+ if (!hasErr) this._onSubmit?.(this.values);
582
+ }
583
+ _renderSelf(screen) {
584
+ const { x, y, width, height } = this._rect;
585
+ if (width <= 0 || height <= 0) return;
586
+ const attrs = styleToCellAttrs9(this.style);
587
+ let row = 0;
588
+ for (let i = 0; i < this._fields.length && row < height - 1; i++) {
589
+ const f = this._fields[i];
590
+ const active = i === this._activeField;
591
+ const val = this._values.get(f.name) ?? "";
592
+ const err = this._errors.get(f.name);
593
+ screen.writeString(x, y + row, `${f.label}${f.required ? " *" : ""}:`.slice(0, width), { ...attrs, fg: err ? this._errorColor : this._labelColor, bold: active });
594
+ row++;
595
+ if (row >= height) break;
596
+ const display = val || (f.placeholder ?? "");
597
+ screen.writeString(x, y + row, `${active ? "\u276F " : " "}${display}`.slice(0, width), { ...attrs, fg: active ? this._activeColor : attrs.fg, dim: !val && !!f.placeholder });
598
+ row++;
599
+ }
600
+ if (row < height) {
601
+ const isSub = this._activeField >= this._fields.length;
602
+ screen.writeString(x, y + row, isSub ? " [ Submit ]" : " Submit ", { ...attrs, fg: isSub ? { type: "named", name: "green" } : attrs.fg, bold: isSub });
603
+ }
604
+ }
605
+ };
606
+
607
+ // src/CommandPalette.ts
608
+ import { Widget as Widget11 } from "@termuijs/widgets";
609
+ import { mergeStyles as mergeStyles11, defaultStyle as defaultStyle11, styleToCellAttrs as styleToCellAttrs10, getBorderChars as getBorderChars3 } from "@termuijs/core";
610
+ var CommandPalette = class extends Widget11 {
611
+ _commands;
612
+ _filtered = [];
613
+ _query = "";
614
+ _cursorPos = 0;
615
+ _selectedIndex = 0;
616
+ _visible = false;
617
+ _placeholder;
618
+ _borderColor;
619
+ _activeColor;
620
+ _maxVisible;
621
+ focusable = true;
622
+ constructor(commands, options = {}) {
623
+ super(mergeStyles11(defaultStyle11(), {}));
624
+ this._commands = commands;
625
+ this._filtered = [...commands];
626
+ this._placeholder = options.placeholder ?? "Type a command...";
627
+ this._borderColor = options.borderColor ?? { type: "named", name: "cyan" };
628
+ this._activeColor = options.activeColor ?? { type: "named", name: "cyan" };
629
+ this._maxVisible = options.maxVisible ?? 10;
630
+ }
631
+ get visible() {
632
+ return this._visible;
633
+ }
634
+ show() {
635
+ this._visible = true;
636
+ this._query = "";
637
+ this._cursorPos = 0;
638
+ this._selectedIndex = 0;
639
+ this._filtered = [...this._commands];
640
+ }
641
+ hide() {
642
+ this._visible = false;
643
+ }
644
+ toggle() {
645
+ this._visible ? this.hide() : this.show();
646
+ }
647
+ insertChar(ch) {
648
+ this._query = this._query.slice(0, this._cursorPos) + ch + this._query.slice(this._cursorPos);
649
+ this._cursorPos++;
650
+ this._filter();
651
+ }
652
+ deleteBack() {
653
+ if (this._cursorPos > 0) {
654
+ this._query = this._query.slice(0, this._cursorPos - 1) + this._query.slice(this._cursorPos);
655
+ this._cursorPos--;
656
+ this._filter();
657
+ }
658
+ }
659
+ selectNext() {
660
+ if (this._selectedIndex < this._filtered.length - 1) this._selectedIndex++;
661
+ }
662
+ selectPrev() {
663
+ if (this._selectedIndex > 0) this._selectedIndex--;
664
+ }
665
+ confirm() {
666
+ const c = this._filtered[this._selectedIndex];
667
+ if (c) {
668
+ this.hide();
669
+ c.action();
670
+ }
671
+ }
672
+ _filter() {
673
+ const q = this._query.toLowerCase();
674
+ if (!q) {
675
+ this._filtered = [...this._commands];
676
+ } else {
677
+ this._filtered = this._commands.filter((c) => {
678
+ const l = c.label.toLowerCase();
679
+ let qi = 0;
680
+ for (let i = 0; i < l.length && qi < q.length; i++) {
681
+ if (l[i] === q[qi]) qi++;
682
+ }
683
+ return qi === q.length;
684
+ });
685
+ }
686
+ this._selectedIndex = 0;
687
+ }
688
+ _renderSelf(screen) {
689
+ if (!this._visible) return;
690
+ const { x, y, width, height } = this._rect;
691
+ const attrs = styleToCellAttrs10(this.style);
692
+ for (let r = 0; r < height; r++) screen.writeString(x, y + r, "\u2591".repeat(width), { ...attrs, dim: true });
693
+ const vis = this._filtered.slice(0, this._maxVisible);
694
+ const bw = Math.min(60, width - 4);
695
+ const bh = Math.min(vis.length + 3, height - 2);
696
+ const bx = x + Math.floor((width - bw) / 2);
697
+ const by = y + 2;
698
+ const border = getBorderChars3("single");
699
+ if (!border) return;
700
+ const ba = { ...attrs, fg: this._borderColor };
701
+ screen.writeString(bx, by, border.topLeft + border.top.repeat(bw - 2) + border.topRight, ba);
702
+ screen.writeString(bx, by + 1, border.left, ba);
703
+ const input = this._query || this._placeholder;
704
+ screen.writeString(bx + 1, by + 1, (" \u{1F50D} " + input).slice(0, bw - 2).padEnd(bw - 2), { ...attrs, dim: !this._query });
705
+ screen.writeString(bx + bw - 1, by + 1, border.right, ba);
706
+ screen.writeString(bx, by + 2, border.left + "\u2500".repeat(bw - 2) + border.right, ba);
707
+ for (let i = 0; i < vis.length && i + 3 < bh - 1; i++) {
708
+ const c = vis[i];
709
+ const active = i === this._selectedIndex;
710
+ const label = (active ? "\u276F " : " ") + c.label;
711
+ const sc = c.shortcut ?? "";
712
+ screen.writeString(bx, by + 3 + i, border.left, ba);
713
+ screen.writeString(bx + 1, by + 3 + i, (" " + label).slice(0, bw - sc.length - 3).padEnd(bw - sc.length - 3), { ...attrs, fg: active ? this._activeColor : attrs.fg, bold: active });
714
+ if (sc) screen.writeString(bx + bw - sc.length - 2, by + 3 + i, sc, { ...attrs, dim: true });
715
+ screen.writeString(bx + bw - 1, by + 3 + i, border.right, ba);
716
+ }
717
+ const last = Math.min(by + 3 + vis.length, by + bh - 1);
718
+ screen.writeString(bx, last, border.bottomLeft + border.bottom.repeat(bw - 2) + border.bottomRight, ba);
719
+ }
720
+ };
721
+ export {
722
+ Box,
723
+ CommandPalette,
724
+ ConfirmDialog,
725
+ Divider,
726
+ Form,
727
+ Gauge,
728
+ List,
729
+ LogView,
730
+ Modal,
731
+ MultiSelect,
732
+ ProgressBar,
733
+ Select,
734
+ Spacer,
735
+ Sparkline,
736
+ Spinner,
737
+ StatusIndicator,
738
+ Table,
739
+ Tabs,
740
+ Text,
741
+ TextInput,
742
+ Toast,
743
+ Tree,
744
+ Widget12 as Widget
745
+ };
746
+ //# sourceMappingURL=index.js.map