@termuijs/ui 0.1.3 → 0.1.4

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 CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  LogView,
12
12
  ProgressBar,
13
13
  Spinner,
14
- Widget as Widget12
14
+ Widget as Widget17
15
15
  } from "@termuijs/widgets";
16
16
 
17
17
  // src/Divider.ts
@@ -58,7 +58,7 @@ var Spacer = class extends Widget2 {
58
58
 
59
59
  // src/Tabs.ts
60
60
  import { Widget as Widget3 } from "@termuijs/widgets";
61
- import { mergeStyles as mergeStyles3, defaultStyle as defaultStyle3, styleToCellAttrs as styleToCellAttrs2 } from "@termuijs/core";
61
+ import { mergeStyles as mergeStyles3, defaultStyle as defaultStyle3, styleToCellAttrs as styleToCellAttrs2, caps } from "@termuijs/core";
62
62
  var Tabs = class extends Widget3 {
63
63
  _tabs = [];
64
64
  _activeIndex = 0;
@@ -99,7 +99,7 @@ var Tabs = class extends Widget3 {
99
99
  for (let i = 0; i < this._tabs.length; i++) {
100
100
  const tab = this._tabs[i];
101
101
  const isActive = i === this._activeIndex;
102
- const label = isActive ? ` \u25CF ${tab.label} ` : ` ${tab.label} `;
102
+ const label = isActive ? ` ${caps.unicode ? "\u25CF" : "*"} ${tab.label} ` : ` ${tab.label} `;
103
103
  screen.writeString(col, y, label, {
104
104
  ...attrs,
105
105
  fg: isActive ? this._activeColor : this._inactiveColor,
@@ -108,7 +108,7 @@ var Tabs = class extends Widget3 {
108
108
  });
109
109
  col += label.length;
110
110
  if (i < this._tabs.length - 1) {
111
- screen.writeString(col, y, "\u2502", { ...attrs, dim: true });
111
+ screen.writeString(col, y, caps.unicode ? "\u2502" : "|", { ...attrs, dim: true });
112
112
  col++;
113
113
  }
114
114
  }
@@ -118,7 +118,7 @@ var Tabs = class extends Widget3 {
118
118
 
119
119
  // src/Modal.ts
120
120
  import { Widget as Widget4 } from "@termuijs/widgets";
121
- import { mergeStyles as mergeStyles4, defaultStyle as defaultStyle4, styleToCellAttrs as styleToCellAttrs3, getBorderChars } from "@termuijs/core";
121
+ import { mergeStyles as mergeStyles4, defaultStyle as defaultStyle4, styleToCellAttrs as styleToCellAttrs3, getBorderChars, caps as caps2 } from "@termuijs/core";
122
122
  var Modal = class extends Widget4 {
123
123
  _title;
124
124
  _modalWidth;
@@ -133,7 +133,7 @@ var Modal = class extends Widget4 {
133
133
  this._modalWidth = options.width ?? 50;
134
134
  this._modalHeight = options.height ?? 15;
135
135
  this._borderColor = options.borderColor ?? { type: "named", name: "cyan" };
136
- this._backdropChar = options.backdropChar ?? "\u2591";
136
+ this._backdropChar = options.backdropChar ?? (caps2.unicode ? "\u2591" : " ");
137
137
  }
138
138
  get visible() {
139
139
  return this._visible;
@@ -190,7 +190,7 @@ var Modal = class extends Widget4 {
190
190
 
191
191
  // src/Select.ts
192
192
  import { Widget as Widget5 } from "@termuijs/widgets";
193
- import { mergeStyles as mergeStyles5, defaultStyle as defaultStyle5, styleToCellAttrs as styleToCellAttrs4 } from "@termuijs/core";
193
+ import { mergeStyles as mergeStyles5, defaultStyle as defaultStyle5, styleToCellAttrs as styleToCellAttrs4, caps as caps3 } from "@termuijs/core";
194
194
  var Select = class extends Widget5 {
195
195
  _options;
196
196
  _selectedIndex = 0;
@@ -257,13 +257,13 @@ var Select = class extends Widget5 {
257
257
  const attrs = styleToCellAttrs4(this.style);
258
258
  const sel = this._options[this._selectedIndex];
259
259
  const label = sel ? sel.label : this._placeholder;
260
- const prefix = this._isOpen ? "\u25BC " : "\u25B6 ";
260
+ const prefix = this._isOpen ? caps3.unicode ? "\u25BC " : "v " : caps3.unicode ? "\u25B6 " : "> ";
261
261
  screen.writeString(x, y, prefix + label.slice(0, width - 2), { ...attrs, fg: this._activeColor });
262
262
  if (this._isOpen) {
263
263
  for (let i = 0; i < this._options.length; i++) {
264
264
  const o = this._options[i];
265
265
  const isSel = i === this._selectedIndex;
266
- const m = isSel ? "\u25CF " : " ";
266
+ const m = isSel ? caps3.unicode ? "\u25CF " : "* " : " ";
267
267
  screen.writeString(x, y + 1 + i, m + o.label.slice(0, width - 2), {
268
268
  ...attrs,
269
269
  fg: o.disabled ? { type: "named", name: "brightBlack" } : isSel ? this._activeColor : attrs.fg,
@@ -277,7 +277,7 @@ var Select = class extends Widget5 {
277
277
 
278
278
  // src/MultiSelect.ts
279
279
  import { Widget as Widget6 } from "@termuijs/widgets";
280
- import { mergeStyles as mergeStyles6, defaultStyle as defaultStyle6, styleToCellAttrs as styleToCellAttrs5 } from "@termuijs/core";
280
+ import { mergeStyles as mergeStyles6, defaultStyle as defaultStyle6, styleToCellAttrs as styleToCellAttrs5, caps as caps4 } from "@termuijs/core";
281
281
  var MultiSelect = class extends Widget6 {
282
282
  _options;
283
283
  _cursorIndex = 0;
@@ -291,8 +291,8 @@ var MultiSelect = class extends Widget6 {
291
291
  super(mergeStyles6(defaultStyle6(), { height: Math.max(options.length, 1) }));
292
292
  this._options = options;
293
293
  this._activeColor = config.activeColor ?? { type: "named", name: "cyan" };
294
- this._checkChar = config.checkChar ?? "\u25FC";
295
- this._uncheckChar = config.uncheckChar ?? "\u25FB";
294
+ this._checkChar = config.checkChar ?? (caps4.unicode ? "\u25FC" : "[x]");
295
+ this._uncheckChar = config.uncheckChar ?? (caps4.unicode ? "\u25FB" : "[ ]");
296
296
  this._onSubmit = config.onSubmit;
297
297
  }
298
298
  get selectedOptions() {
@@ -332,7 +332,7 @@ var MultiSelect = class extends Widget6 {
332
332
  const o = this._options[i];
333
333
  const active = i === this._cursorIndex;
334
334
  const checked = this._checked.has(i);
335
- const label = `${active ? "\u276F " : " "}${checked ? this._checkChar : this._uncheckChar} ${o.label}`;
335
+ const label = `${active ? caps4.unicode ? "\u276F " : "> " : " "}${checked ? this._checkChar : this._uncheckChar} ${o.label}`;
336
336
  screen.writeString(x, y + i, label.slice(0, width), {
337
337
  ...attrs,
338
338
  fg: o.disabled ? { type: "named", name: "brightBlack" } : active ? this._activeColor : attrs.fg,
@@ -345,7 +345,7 @@ var MultiSelect = class extends Widget6 {
345
345
 
346
346
  // src/Tree.ts
347
347
  import { Widget as Widget7 } from "@termuijs/widgets";
348
- import { mergeStyles as mergeStyles7, defaultStyle as defaultStyle7, styleToCellAttrs as styleToCellAttrs6 } from "@termuijs/core";
348
+ import { mergeStyles as mergeStyles7, defaultStyle as defaultStyle7, styleToCellAttrs as styleToCellAttrs6, caps as caps5 } from "@termuijs/core";
349
349
  var Tree = class extends Widget7 {
350
350
  _roots;
351
351
  _cursorIndex = 0;
@@ -360,12 +360,12 @@ var Tree = class extends Widget7 {
360
360
  }
361
361
  _flatten() {
362
362
  const result = [];
363
- const walk = (nodes, depth, path) => {
363
+ const walk = (nodes, depth, path2) => {
364
364
  for (let i = 0; i < nodes.length; i++) {
365
365
  const node = nodes[i];
366
366
  const hasChildren = (node.children?.length ?? 0) > 0;
367
- result.push({ node, depth, path: [...path, i], hasChildren });
368
- if (hasChildren && node.expanded) walk(node.children, depth + 1, [...path, i]);
367
+ result.push({ node, depth, path: [...path2, i], hasChildren });
368
+ if (hasChildren && node.expanded) walk(node.children, depth + 1, [...path2, i]);
369
369
  }
370
370
  };
371
371
  walk(this._roots, 0, []);
@@ -408,7 +408,7 @@ var Tree = class extends Widget7 {
408
408
  const it = flat[i];
409
409
  const active = i === this._cursorIndex;
410
410
  const indent = " ".repeat(it.depth);
411
- const icon = it.hasChildren ? it.node.expanded ? "\u25BC " : "\u25B6 " : " ";
411
+ const icon = it.hasChildren ? it.node.expanded ? caps5.unicode ? "\u25BC " : "v " : caps5.unicode ? "\u25B6 " : "> " : " ";
412
412
  const nodeIcon = it.node.icon ? `${it.node.icon} ` : "";
413
413
  const line = `${indent}${icon}${nodeIcon}${it.node.label}`;
414
414
  screen.writeString(x, y + i, line.slice(0, width), { ...attrs, fg: active ? this._activeColor : attrs.fg, bold: active });
@@ -418,8 +418,9 @@ var Tree = class extends Widget7 {
418
418
 
419
419
  // src/Toast.ts
420
420
  import { Widget as Widget8 } from "@termuijs/widgets";
421
- import { mergeStyles as mergeStyles8, defaultStyle as defaultStyle8, styleToCellAttrs as styleToCellAttrs7 } from "@termuijs/core";
422
- var ICONS = { info: "\u2139", success: "\u2713", warning: "\u26A0", error: "\u2717" };
421
+ import { mergeStyles as mergeStyles8, defaultStyle as defaultStyle8, styleToCellAttrs as styleToCellAttrs7, caps as caps6 } from "@termuijs/core";
422
+ var ICONS_UNICODE = { info: "\u2139", success: "\u2713", warning: "\u26A0", error: "\u2717" };
423
+ var ICONS_ASCII = { info: "i", success: "+", warning: "!", error: "x" };
423
424
  var COLORS = { info: "cyan", success: "green", warning: "yellow", error: "red" };
424
425
  var Toast = class extends Widget8 {
425
426
  _messages = [];
@@ -459,10 +460,11 @@ var Toast = class extends Widget8 {
459
460
  const isBottom = this._position.includes("bottom");
460
461
  const sx = isRight ? x + width - tw - 1 : x + 1;
461
462
  const sy = isBottom ? y + height - visible.length - 1 : y + 1;
463
+ const icons = caps6.unicode ? ICONS_UNICODE : ICONS_ASCII;
462
464
  const attrs = styleToCellAttrs7(this.style);
463
465
  for (let i = 0; i < visible.length; i++) {
464
466
  const m = visible[i];
465
- const label = ` ${ICONS[m.type]} ${m.text} `.slice(0, tw).padEnd(tw);
467
+ const label = ` ${icons[m.type]} ${m.text} `.slice(0, tw).padEnd(tw);
466
468
  screen.writeString(sx, sy + i, label, { ...attrs, fg: { type: "named", name: COLORS[m.type] }, bold: true });
467
469
  }
468
470
  }
@@ -653,7 +655,7 @@ var Form = class extends Widget10 {
653
655
 
654
656
  // src/CommandPalette.ts
655
657
  import { Widget as Widget11 } from "@termuijs/widgets";
656
- import { mergeStyles as mergeStyles11, defaultStyle as defaultStyle11, styleToCellAttrs as styleToCellAttrs10, getBorderChars as getBorderChars3 } from "@termuijs/core";
658
+ import { mergeStyles as mergeStyles11, defaultStyle as defaultStyle11, styleToCellAttrs as styleToCellAttrs10, getBorderChars as getBorderChars3, caps as caps7 } from "@termuijs/core";
657
659
  var CommandPalette = class extends Widget11 {
658
660
  _commands;
659
661
  _filtered = [];
@@ -746,7 +748,8 @@ var CommandPalette = class extends Widget11 {
746
748
  if (!this._visible) return;
747
749
  const { x, y, width, height } = this._rect;
748
750
  const attrs = styleToCellAttrs10(this.style);
749
- for (let r = 0; r < height; r++) screen.writeString(x, y + r, "\u2591".repeat(width), { ...attrs, dim: true });
751
+ const backdropCh = caps7.unicode ? "\u2591" : " ";
752
+ for (let r = 0; r < height; r++) screen.writeString(x, y + r, backdropCh.repeat(width), { ...attrs, dim: true });
750
753
  const vis = this._filtered.slice(0, this._maxVisible);
751
754
  const bw = Math.min(60, width - 4);
752
755
  const bh = Math.min(vis.length + 3, height - 2);
@@ -758,13 +761,13 @@ var CommandPalette = class extends Widget11 {
758
761
  screen.writeString(bx, by, border.topLeft + border.top.repeat(bw - 2) + border.topRight, ba);
759
762
  screen.writeString(bx, by + 1, border.left, ba);
760
763
  const input = this._query || this._placeholder;
761
- screen.writeString(bx + 1, by + 1, (" \u{1F50D} " + input).slice(0, bw - 2).padEnd(bw - 2), { ...attrs, dim: !this._query });
764
+ screen.writeString(bx + 1, by + 1, (` ${caps7.unicode ? "\u{1F50D}" : "[?]"} ` + input).slice(0, bw - 2).padEnd(bw - 2), { ...attrs, dim: !this._query });
762
765
  screen.writeString(bx + bw - 1, by + 1, border.right, ba);
763
766
  screen.writeString(bx, by + 2, border.left + "\u2500".repeat(bw - 2) + border.right, ba);
764
767
  for (let i = 0; i < vis.length && i + 3 < bh - 1; i++) {
765
768
  const c = vis[i];
766
769
  const active = i === this._selectedIndex;
767
- const label = (active ? "\u276F " : " ") + c.label;
770
+ const label = (active ? caps7.unicode ? "\u276F " : "> " : " ") + c.label;
768
771
  const sc = c.shortcut ?? "";
769
772
  screen.writeString(bx, by + 3 + i, border.left, ba);
770
773
  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 });
@@ -775,6 +778,919 @@ var CommandPalette = class extends Widget11 {
775
778
  screen.writeString(bx, last, border.bottomLeft + border.bottom.repeat(bw - 2) + border.bottomRight, ba);
776
779
  }
777
780
  };
781
+
782
+ // src/prompts.ts
783
+ import * as readline from "readline";
784
+ var NonInteractiveError = class extends Error {
785
+ constructor() {
786
+ super("Prompts require an interactive TTY. stdin is not a TTY.");
787
+ this.name = "NonInteractiveError";
788
+ }
789
+ };
790
+ async function promptText(options) {
791
+ if (!process.stdin.isTTY) throw new NonInteractiveError();
792
+ const defaultHint = options.default ? ` (${options.default})` : "";
793
+ const placeholder = options.placeholder ? ` [${options.placeholder}]` : "";
794
+ return new Promise((resolve) => {
795
+ const rl = readline.createInterface({
796
+ input: process.stdin,
797
+ output: process.stdout
798
+ });
799
+ const ask = () => {
800
+ rl.question(`${options.message}${defaultHint}${placeholder}: `, (answer) => {
801
+ const value = answer.trim() || options.default || "";
802
+ if (options.validate) {
803
+ const error = options.validate(value);
804
+ if (error) {
805
+ process.stdout.write(` ${error}
806
+ `);
807
+ ask();
808
+ return;
809
+ }
810
+ }
811
+ rl.close();
812
+ resolve(value);
813
+ });
814
+ };
815
+ ask();
816
+ });
817
+ }
818
+ async function promptConfirm(options) {
819
+ if (!process.stdin.isTTY) throw new NonInteractiveError();
820
+ const hint = options.default === true ? "Y/n" : options.default === false ? "y/N" : "y/n";
821
+ return new Promise((resolve) => {
822
+ const rl = readline.createInterface({
823
+ input: process.stdin,
824
+ output: process.stdout
825
+ });
826
+ rl.question(`${options.message} [${hint}]: `, (answer) => {
827
+ rl.close();
828
+ const a = answer.trim().toLowerCase();
829
+ if (a === "y" || a === "yes") {
830
+ resolve(true);
831
+ return;
832
+ }
833
+ if (a === "n" || a === "no") {
834
+ resolve(false);
835
+ return;
836
+ }
837
+ if (a === "" && options.default !== void 0) {
838
+ resolve(options.default);
839
+ return;
840
+ }
841
+ resolve(false);
842
+ });
843
+ });
844
+ }
845
+ async function promptSelect(options) {
846
+ if (!process.stdin.isTTY) throw new NonInteractiveError();
847
+ const { options: choices, default: defaultValue } = options;
848
+ process.stdout.write(`${options.message}
849
+ `);
850
+ choices.forEach((opt, i) => {
851
+ const isDefault = opt.value === defaultValue;
852
+ process.stdout.write(` ${i + 1}. ${opt.label}${isDefault ? " (default)" : ""}
853
+ `);
854
+ });
855
+ return new Promise((resolve) => {
856
+ const rl = readline.createInterface({
857
+ input: process.stdin,
858
+ output: process.stdout
859
+ });
860
+ const ask = () => {
861
+ rl.question(`Enter number (1-${choices.length}): `, (answer) => {
862
+ const trimmed = answer.trim();
863
+ if (trimmed === "" && defaultValue !== void 0) {
864
+ rl.close();
865
+ resolve(defaultValue);
866
+ return;
867
+ }
868
+ const n = parseInt(trimmed, 10);
869
+ if (!isNaN(n) && n >= 1 && n <= choices.length) {
870
+ rl.close();
871
+ resolve(choices[n - 1].value);
872
+ return;
873
+ }
874
+ process.stdout.write(` Invalid choice. Enter a number 1-${choices.length}.
875
+ `);
876
+ ask();
877
+ });
878
+ };
879
+ ask();
880
+ });
881
+ }
882
+ var prompt = {
883
+ text: promptText,
884
+ confirm: promptConfirm,
885
+ select: promptSelect
886
+ };
887
+
888
+ // src/NotificationCenter.ts
889
+ import { Widget as Widget12 } from "@termuijs/widgets";
890
+ import { useState, useEffect } from "@termuijs/jsx";
891
+ import { caps as caps8 } from "@termuijs/core";
892
+ var NotificationStore = class _NotificationStore {
893
+ static _instance;
894
+ _notifications = [];
895
+ _subs = /* @__PURE__ */ new Set();
896
+ static getInstance() {
897
+ if (!_NotificationStore._instance) {
898
+ _NotificationStore._instance = new _NotificationStore();
899
+ }
900
+ return _NotificationStore._instance;
901
+ }
902
+ push(message, type = "info", durationMs) {
903
+ const id = Date.now().toString(36) + Math.random().toString(36).slice(2);
904
+ const notification = { id, message, type, durationMs, createdAt: Date.now() };
905
+ this._notifications = [...this._notifications, notification];
906
+ this._emit();
907
+ if (durationMs && durationMs > 0) {
908
+ setTimeout(() => this.dismiss(id), durationMs);
909
+ }
910
+ return id;
911
+ }
912
+ dismiss(id) {
913
+ const prev = this._notifications;
914
+ this._notifications = this._notifications.filter((n) => n.id !== id);
915
+ if (this._notifications.length !== prev.length) {
916
+ this._emit();
917
+ }
918
+ }
919
+ dismissAll() {
920
+ if (this._notifications.length > 0) {
921
+ this._notifications = [];
922
+ this._emit();
923
+ }
924
+ }
925
+ subscribe(fn) {
926
+ this._subs.add(fn);
927
+ return () => this._subs.delete(fn);
928
+ }
929
+ get notifications() {
930
+ return this._notifications;
931
+ }
932
+ _emit() {
933
+ for (const fn of this._subs) fn(this._notifications);
934
+ }
935
+ };
936
+ var notifications = NotificationStore.getInstance();
937
+ function useNotifications() {
938
+ const store = NotificationStore.getInstance();
939
+ const [current, setCurrent] = useState(store.notifications);
940
+ useEffect(() => {
941
+ const unsub = store.subscribe((ns) => setCurrent([...ns]));
942
+ return unsub;
943
+ }, []);
944
+ return {
945
+ notifications: current,
946
+ push: (msg, type, dur) => store.push(msg, type, dur),
947
+ dismiss: (id) => store.dismiss(id),
948
+ dismissAll: () => store.dismissAll()
949
+ };
950
+ }
951
+ var TYPE_ICONS = {
952
+ info: { unicode: "\u2139", ascii: "i" },
953
+ success: { unicode: "\u2713", ascii: "+" },
954
+ warning: { unicode: "\u26A0", ascii: "!" },
955
+ error: { unicode: "\u2717", ascii: "x" }
956
+ };
957
+ var TYPE_COLORS = {
958
+ info: { type: "named", name: "cyan" },
959
+ success: { type: "named", name: "green" },
960
+ warning: { type: "named", name: "yellow" },
961
+ error: { type: "named", name: "red" }
962
+ };
963
+ var NotificationCenter = class extends Widget12 {
964
+ _position;
965
+ _maxVisible;
966
+ _notifWidth;
967
+ _unsub;
968
+ _current = [];
969
+ constructor(options = {}) {
970
+ super();
971
+ this._position = options.position ?? "top-right";
972
+ this._maxVisible = options.maxVisible ?? 5;
973
+ this._notifWidth = options.width ?? 40;
974
+ const store = NotificationStore.getInstance();
975
+ this._current = store.notifications;
976
+ this._unsub = store.subscribe((ns) => {
977
+ this._current = ns;
978
+ this.markDirty();
979
+ });
980
+ }
981
+ unmount() {
982
+ this._unsub?.();
983
+ this._unsub = void 0;
984
+ super.unmount();
985
+ }
986
+ _renderSelf(screen) {
987
+ const visible = this._current.slice(-this._maxVisible);
988
+ if (visible.length === 0) return;
989
+ const { x, y, width, height } = this._rect;
990
+ const tw = Math.min(this._notifWidth, width - 2);
991
+ if (tw <= 0) return;
992
+ const isRight = this._position.includes("right");
993
+ const isBottom = this._position.includes("bottom");
994
+ const sx = isRight ? x + width - tw - 1 : x + 1;
995
+ const sy = isBottom ? y + height - visible.length - 1 : y + 1;
996
+ for (let i = 0; i < visible.length; i++) {
997
+ const notif = visible[i];
998
+ const icon = caps8.unicode ? TYPE_ICONS[notif.type].unicode : TYPE_ICONS[notif.type].ascii;
999
+ const raw = `${icon} ${notif.message}`;
1000
+ const label = ` ${raw} `.slice(0, tw).padEnd(tw);
1001
+ screen.writeString(sx, sy + i, label, {
1002
+ fg: TYPE_COLORS[notif.type],
1003
+ bold: true
1004
+ });
1005
+ }
1006
+ }
1007
+ };
1008
+
1009
+ // src/PasswordInput.ts
1010
+ import { Widget as Widget13 } from "@termuijs/widgets";
1011
+ import { styleToCellAttrs as styleToCellAttrs11, truncate, caps as caps9 } from "@termuijs/core";
1012
+ var PasswordInput = class extends Widget13 {
1013
+ _value = "";
1014
+ _cursorPos = 0;
1015
+ _placeholder;
1016
+ _maxLength;
1017
+ _showText = false;
1018
+ _onChange;
1019
+ _onSubmit;
1020
+ focusable = true;
1021
+ // '●' in unicode terminals, '*' in ASCII fallback
1022
+ get _maskChar() {
1023
+ return caps9.unicode ? "\u25CF" : "*";
1024
+ }
1025
+ constructor(style = {}, options = {}) {
1026
+ super({ border: "single", height: 3, ...style });
1027
+ this._placeholder = options.placeholder ?? "";
1028
+ this._maxLength = options.maxLength ?? Infinity;
1029
+ this._onChange = options.onChange;
1030
+ this._onSubmit = options.onSubmit;
1031
+ }
1032
+ /** The actual (unmasked) value. */
1033
+ get value() {
1034
+ return this._value;
1035
+ }
1036
+ set value(v) {
1037
+ this._value = v.slice(0, this._maxLength);
1038
+ this._cursorPos = Math.min(this._cursorPos, this._value.length);
1039
+ }
1040
+ /** Whether the text is currently visible (unmaksed). */
1041
+ get showText() {
1042
+ return this._showText;
1043
+ }
1044
+ /** Toggle visibility of the actual text (Alt+V). */
1045
+ toggleVisibility() {
1046
+ this._showText = !this._showText;
1047
+ this.markDirty();
1048
+ }
1049
+ insertChar(char) {
1050
+ if (this._value.length >= this._maxLength) return;
1051
+ this._value = this._value.slice(0, this._cursorPos) + char + this._value.slice(this._cursorPos);
1052
+ this._cursorPos++;
1053
+ this._onChange?.(this._value);
1054
+ this.markDirty();
1055
+ }
1056
+ deleteBack() {
1057
+ if (this._cursorPos > 0) {
1058
+ this._value = this._value.slice(0, this._cursorPos - 1) + this._value.slice(this._cursorPos);
1059
+ this._cursorPos--;
1060
+ this._onChange?.(this._value);
1061
+ this.markDirty();
1062
+ }
1063
+ }
1064
+ deleteForward() {
1065
+ if (this._cursorPos < this._value.length) {
1066
+ this._value = this._value.slice(0, this._cursorPos) + this._value.slice(this._cursorPos + 1);
1067
+ this._onChange?.(this._value);
1068
+ this.markDirty();
1069
+ }
1070
+ }
1071
+ moveCursorLeft() {
1072
+ this._cursorPos = Math.max(0, this._cursorPos - 1);
1073
+ this.markDirty();
1074
+ }
1075
+ moveCursorRight() {
1076
+ this._cursorPos = Math.min(this._value.length, this._cursorPos + 1);
1077
+ this.markDirty();
1078
+ }
1079
+ moveCursorHome() {
1080
+ this._cursorPos = 0;
1081
+ this.markDirty();
1082
+ }
1083
+ moveCursorEnd() {
1084
+ this._cursorPos = this._value.length;
1085
+ this.markDirty();
1086
+ }
1087
+ submit() {
1088
+ this._onSubmit?.(this._value);
1089
+ }
1090
+ clear() {
1091
+ this._value = "";
1092
+ this._cursorPos = 0;
1093
+ this._onChange?.("");
1094
+ this.markDirty();
1095
+ }
1096
+ /**
1097
+ * Handle key events. Call this from your input loop.
1098
+ * Alt+V — toggle visibility
1099
+ * Other — standard text editing
1100
+ */
1101
+ handleKey(event) {
1102
+ if (event.alt && event.key === "v") {
1103
+ this.toggleVisibility();
1104
+ return;
1105
+ }
1106
+ switch (event.key) {
1107
+ case "backspace":
1108
+ this.deleteBack();
1109
+ break;
1110
+ case "delete":
1111
+ this.deleteForward();
1112
+ break;
1113
+ case "left":
1114
+ this.moveCursorLeft();
1115
+ break;
1116
+ case "right":
1117
+ this.moveCursorRight();
1118
+ break;
1119
+ case "home":
1120
+ this.moveCursorHome();
1121
+ break;
1122
+ case "end":
1123
+ this.moveCursorEnd();
1124
+ break;
1125
+ case "return":
1126
+ case "enter":
1127
+ this.submit();
1128
+ break;
1129
+ default:
1130
+ if (event.key && event.key.length === 1 && !event.ctrl && !event.alt) {
1131
+ this.insertChar(event.key);
1132
+ }
1133
+ }
1134
+ }
1135
+ _renderSelf(screen) {
1136
+ const rect = this._getContentRect();
1137
+ const { x, y, width, height } = rect;
1138
+ if (width <= 0 || height <= 0) return;
1139
+ const attrs = styleToCellAttrs11(this._style);
1140
+ if (this._value.length === 0 && !this.isFocused) {
1141
+ screen.writeString(x, y, truncate(this._placeholder, width), { ...attrs, dim: true });
1142
+ return;
1143
+ }
1144
+ const displayValue = this._showText ? this._value : this._maskChar.repeat(this._value.length);
1145
+ const visibleWidth = width - 1;
1146
+ let scrollX = 0;
1147
+ if (this._cursorPos > visibleWidth) {
1148
+ scrollX = this._cursorPos - visibleWidth;
1149
+ }
1150
+ const visibleText = displayValue.slice(scrollX, scrollX + visibleWidth);
1151
+ screen.writeString(x, y, visibleText, attrs);
1152
+ if (this.isFocused) {
1153
+ const cursorScreenPos = x + this._cursorPos - scrollX;
1154
+ if (cursorScreenPos >= x && cursorScreenPos < x + width) {
1155
+ const cursorChar = this._cursorPos < displayValue.length ? displayValue[this._cursorPos] : " ";
1156
+ screen.setCell(cursorScreenPos, y, {
1157
+ char: cursorChar,
1158
+ ...attrs,
1159
+ inverse: true
1160
+ });
1161
+ }
1162
+ }
1163
+ if (this._showText && width > 4) {
1164
+ const indicator = caps9.unicode ? " \u{1F441}" : "[v]";
1165
+ screen.writeString(x + width - indicator.length, y, indicator, { ...attrs, dim: true });
1166
+ }
1167
+ }
1168
+ };
1169
+
1170
+ // src/NumberInput.ts
1171
+ import { Widget as Widget14 } from "@termuijs/widgets";
1172
+ import { styleToCellAttrs as styleToCellAttrs12, truncate as truncate2 } from "@termuijs/core";
1173
+ var NumberInput = class extends Widget14 {
1174
+ _raw = "";
1175
+ // raw string the user typed
1176
+ _cursorPos = 0;
1177
+ _placeholder;
1178
+ _step;
1179
+ _min;
1180
+ _max;
1181
+ _allowDecimal;
1182
+ _onChange;
1183
+ _onSubmit;
1184
+ focusable = true;
1185
+ constructor(style = {}, options = {}) {
1186
+ super({ border: "single", height: 3, ...style });
1187
+ this._placeholder = options.placeholder ?? "";
1188
+ this._step = options.step ?? 1;
1189
+ this._min = options.min ?? -Infinity;
1190
+ this._max = options.max ?? Infinity;
1191
+ this._allowDecimal = options.allowDecimal ?? true;
1192
+ this._onChange = options.onChange;
1193
+ this._onSubmit = options.onSubmit;
1194
+ }
1195
+ /** The numeric value, or null if the field is empty / invalid. */
1196
+ get numericValue() {
1197
+ if (this._raw === "" || this._raw === "-") return null;
1198
+ const n = parseFloat(this._raw);
1199
+ return isNaN(n) ? null : n;
1200
+ }
1201
+ /** Raw text string (what the user typed). */
1202
+ get rawValue() {
1203
+ return this._raw;
1204
+ }
1205
+ set rawValue(v) {
1206
+ this._raw = v;
1207
+ this._cursorPos = Math.min(this._cursorPos, this._raw.length);
1208
+ }
1209
+ _clamp(n) {
1210
+ return Math.min(this._max, Math.max(this._min, n));
1211
+ }
1212
+ _notify() {
1213
+ this._onChange?.(this.numericValue);
1214
+ this.markDirty();
1215
+ }
1216
+ /** Accept only digits, '-' at position 0 (if min < 0), and (optionally) one '.'. */
1217
+ _isAllowed(char) {
1218
+ if (char === "-" && this._cursorPos === 0 && !this._raw.includes("-")) {
1219
+ return this._min < 0;
1220
+ }
1221
+ if (char === "." && this._allowDecimal && !this._raw.includes(".")) return true;
1222
+ return /^\d$/.test(char);
1223
+ }
1224
+ insertChar(char) {
1225
+ if (!this._isAllowed(char)) return;
1226
+ this._raw = this._raw.slice(0, this._cursorPos) + char + this._raw.slice(this._cursorPos);
1227
+ this._cursorPos++;
1228
+ this._notify();
1229
+ }
1230
+ deleteBack() {
1231
+ if (this._cursorPos > 0) {
1232
+ this._raw = this._raw.slice(0, this._cursorPos - 1) + this._raw.slice(this._cursorPos);
1233
+ this._cursorPos--;
1234
+ this._notify();
1235
+ }
1236
+ }
1237
+ deleteForward() {
1238
+ if (this._cursorPos < this._raw.length) {
1239
+ this._raw = this._raw.slice(0, this._cursorPos) + this._raw.slice(this._cursorPos + 1);
1240
+ this._notify();
1241
+ }
1242
+ }
1243
+ moveCursorLeft() {
1244
+ this._cursorPos = Math.max(0, this._cursorPos - 1);
1245
+ this.markDirty();
1246
+ }
1247
+ moveCursorRight() {
1248
+ this._cursorPos = Math.min(this._raw.length, this._cursorPos + 1);
1249
+ this.markDirty();
1250
+ }
1251
+ moveCursorHome() {
1252
+ this._cursorPos = 0;
1253
+ this.markDirty();
1254
+ }
1255
+ moveCursorEnd() {
1256
+ this._cursorPos = this._raw.length;
1257
+ this.markDirty();
1258
+ }
1259
+ /** Increment value by step (↑ arrow). */
1260
+ increment() {
1261
+ const current = this.numericValue ?? 0;
1262
+ const next = this._clamp(current + this._step);
1263
+ this._raw = String(next);
1264
+ this._cursorPos = this._raw.length;
1265
+ this._notify();
1266
+ }
1267
+ /** Decrement value by step (↓ arrow). */
1268
+ decrement() {
1269
+ const current = this.numericValue ?? 0;
1270
+ const next = this._clamp(current - this._step);
1271
+ this._raw = String(next);
1272
+ this._cursorPos = this._raw.length;
1273
+ this._notify();
1274
+ }
1275
+ submit() {
1276
+ this._onSubmit?.(this.numericValue);
1277
+ }
1278
+ clear() {
1279
+ this._raw = "";
1280
+ this._cursorPos = 0;
1281
+ this._notify();
1282
+ }
1283
+ /**
1284
+ * Handle key events. Call this from your input loop.
1285
+ */
1286
+ handleKey(event) {
1287
+ switch (event.key) {
1288
+ case "up":
1289
+ this.increment();
1290
+ break;
1291
+ case "down":
1292
+ this.decrement();
1293
+ break;
1294
+ case "backspace":
1295
+ this.deleteBack();
1296
+ break;
1297
+ case "delete":
1298
+ this.deleteForward();
1299
+ break;
1300
+ case "left":
1301
+ this.moveCursorLeft();
1302
+ break;
1303
+ case "right":
1304
+ this.moveCursorRight();
1305
+ break;
1306
+ case "home":
1307
+ this.moveCursorHome();
1308
+ break;
1309
+ case "end":
1310
+ this.moveCursorEnd();
1311
+ break;
1312
+ case "return":
1313
+ case "enter":
1314
+ this.submit();
1315
+ break;
1316
+ default:
1317
+ if (event.key && event.key.length === 1 && !event.ctrl && !event.alt) {
1318
+ this.insertChar(event.key);
1319
+ }
1320
+ }
1321
+ }
1322
+ _renderSelf(screen) {
1323
+ const rect = this._getContentRect();
1324
+ const { x, y, width, height } = rect;
1325
+ if (width <= 0 || height <= 0) return;
1326
+ const attrs = styleToCellAttrs12(this._style);
1327
+ if (this._raw.length === 0 && !this.isFocused) {
1328
+ screen.writeString(x, y, truncate2(this._placeholder, width), { ...attrs, dim: true });
1329
+ return;
1330
+ }
1331
+ const display = this._raw;
1332
+ const visibleWidth = width - 1;
1333
+ let scrollX = 0;
1334
+ if (this._cursorPos > visibleWidth) {
1335
+ scrollX = this._cursorPos - visibleWidth;
1336
+ }
1337
+ const visibleText = display.slice(scrollX, scrollX + visibleWidth);
1338
+ screen.writeString(x, y, visibleText, attrs);
1339
+ if (this.isFocused) {
1340
+ const cursorScreenPos = x + this._cursorPos - scrollX;
1341
+ if (cursorScreenPos >= x && cursorScreenPos < x + width) {
1342
+ const cursorChar = this._cursorPos < display.length ? display[this._cursorPos] : " ";
1343
+ screen.setCell(cursorScreenPos, y, {
1344
+ char: cursorChar,
1345
+ ...attrs,
1346
+ inverse: true
1347
+ });
1348
+ }
1349
+ }
1350
+ if (this.isFocused && width > 8) {
1351
+ const hint = `\xB1${this._step}`;
1352
+ screen.writeString(x + width - hint.length, y, hint, { ...attrs, dim: true });
1353
+ }
1354
+ }
1355
+ };
1356
+
1357
+ // src/PathInput.ts
1358
+ import * as fs from "fs";
1359
+ import * as path from "path";
1360
+ import { Widget as Widget15 } from "@termuijs/widgets";
1361
+ import { styleToCellAttrs as styleToCellAttrs13, truncate as truncate3, caps as caps10 } from "@termuijs/core";
1362
+ var PathInput = class extends Widget15 {
1363
+ _value = "";
1364
+ _cursorPos = 0;
1365
+ _placeholder;
1366
+ _maxLength;
1367
+ _cwd;
1368
+ _maxCompletions;
1369
+ _completions = [];
1370
+ _completionIndex = -1;
1371
+ // -1 = original value, 0..n = cycling
1372
+ _preCompletionValue = "";
1373
+ // value before Tab was pressed
1374
+ _showCompletions = false;
1375
+ _onChange;
1376
+ _onSubmit;
1377
+ focusable = true;
1378
+ constructor(style = {}, options = {}) {
1379
+ super({ border: "single", height: 3, ...style });
1380
+ this._placeholder = options.placeholder ?? "";
1381
+ this._maxLength = options.maxLength ?? 4096;
1382
+ this._cwd = options.cwd ?? process.cwd();
1383
+ this._maxCompletions = options.maxCompletions ?? 5;
1384
+ this._onChange = options.onChange;
1385
+ this._onSubmit = options.onSubmit;
1386
+ }
1387
+ get value() {
1388
+ return this._value;
1389
+ }
1390
+ set value(v) {
1391
+ this._value = v.slice(0, this._maxLength);
1392
+ this._cursorPos = Math.min(this._cursorPos, this._value.length);
1393
+ this._dismissCompletions();
1394
+ }
1395
+ get completions() {
1396
+ return this._completions;
1397
+ }
1398
+ get isShowingCompletions() {
1399
+ return this._showCompletions && this._completions.length > 0;
1400
+ }
1401
+ _dismissCompletions() {
1402
+ this._completions = [];
1403
+ this._completionIndex = -1;
1404
+ this._showCompletions = false;
1405
+ }
1406
+ /** Compute completions for the current value. Does NOT throw. */
1407
+ _computeCompletions() {
1408
+ try {
1409
+ const raw = this._value;
1410
+ const isAbs = path.isAbsolute(raw);
1411
+ const base = isAbs ? raw : path.join(this._cwd, raw);
1412
+ let dir;
1413
+ let prefix;
1414
+ if (raw === "" || raw.endsWith("/") || raw.endsWith(path.sep)) {
1415
+ dir = isAbs ? raw || "/" : path.join(this._cwd, raw || ".");
1416
+ prefix = "";
1417
+ } else {
1418
+ dir = path.dirname(base);
1419
+ prefix = path.basename(base).toLowerCase();
1420
+ }
1421
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
1422
+ const matches = entries.filter((e) => e.name.toLowerCase().startsWith(prefix)).slice(0, this._maxCompletions).map((e) => {
1423
+ const full = path.join(dir, e.name);
1424
+ const candidate = isAbs ? full + (e.isDirectory() ? path.sep : "") : path.relative(this._cwd, full) + (e.isDirectory() ? path.sep : "");
1425
+ return candidate;
1426
+ });
1427
+ return matches;
1428
+ } catch {
1429
+ return [];
1430
+ }
1431
+ }
1432
+ /** Trigger tab-completion. */
1433
+ triggerCompletion() {
1434
+ if (!this._showCompletions || this._completions.length === 0) {
1435
+ this._preCompletionValue = this._value;
1436
+ this._completions = this._computeCompletions();
1437
+ if (this._completions.length === 0) {
1438
+ this._showCompletions = false;
1439
+ return;
1440
+ }
1441
+ this._showCompletions = true;
1442
+ this._completionIndex = 0;
1443
+ } else {
1444
+ this._completionIndex = (this._completionIndex + 1) % this._completions.length;
1445
+ }
1446
+ this._value = this._completions[this._completionIndex];
1447
+ this._cursorPos = this._value.length;
1448
+ this._onChange?.(this._value);
1449
+ this.markDirty();
1450
+ }
1451
+ insertChar(char) {
1452
+ if (this._value.length >= this._maxLength) return;
1453
+ this._value = this._value.slice(0, this._cursorPos) + char + this._value.slice(this._cursorPos);
1454
+ this._cursorPos++;
1455
+ this._dismissCompletions();
1456
+ this._onChange?.(this._value);
1457
+ this.markDirty();
1458
+ }
1459
+ deleteBack() {
1460
+ if (this._cursorPos > 0) {
1461
+ this._value = this._value.slice(0, this._cursorPos - 1) + this._value.slice(this._cursorPos);
1462
+ this._cursorPos--;
1463
+ this._dismissCompletions();
1464
+ this._onChange?.(this._value);
1465
+ this.markDirty();
1466
+ }
1467
+ }
1468
+ deleteForward() {
1469
+ if (this._cursorPos < this._value.length) {
1470
+ this._value = this._value.slice(0, this._cursorPos) + this._value.slice(this._cursorPos + 1);
1471
+ this._dismissCompletions();
1472
+ this._onChange?.(this._value);
1473
+ this.markDirty();
1474
+ }
1475
+ }
1476
+ moveCursorLeft() {
1477
+ this._cursorPos = Math.max(0, this._cursorPos - 1);
1478
+ this.markDirty();
1479
+ }
1480
+ moveCursorRight() {
1481
+ this._cursorPos = Math.min(this._value.length, this._cursorPos + 1);
1482
+ this.markDirty();
1483
+ }
1484
+ moveCursorHome() {
1485
+ this._cursorPos = 0;
1486
+ this.markDirty();
1487
+ }
1488
+ moveCursorEnd() {
1489
+ this._cursorPos = this._value.length;
1490
+ this.markDirty();
1491
+ }
1492
+ submit() {
1493
+ this._dismissCompletions();
1494
+ this._onSubmit?.(this._value);
1495
+ }
1496
+ clear() {
1497
+ this._value = "";
1498
+ this._cursorPos = 0;
1499
+ this._dismissCompletions();
1500
+ this._onChange?.("");
1501
+ this.markDirty();
1502
+ }
1503
+ /**
1504
+ * Handle key events. Call this from your input loop.
1505
+ * Tab — trigger/cycle completions
1506
+ * Esc — dismiss completions
1507
+ * Enter — submit current value
1508
+ */
1509
+ handleKey(event) {
1510
+ switch (event.key) {
1511
+ case "tab":
1512
+ this.triggerCompletion();
1513
+ break;
1514
+ case "escape":
1515
+ this._dismissCompletions();
1516
+ this.markDirty();
1517
+ break;
1518
+ case "backspace":
1519
+ this.deleteBack();
1520
+ break;
1521
+ case "delete":
1522
+ this.deleteForward();
1523
+ break;
1524
+ case "left":
1525
+ this.moveCursorLeft();
1526
+ break;
1527
+ case "right":
1528
+ this.moveCursorRight();
1529
+ break;
1530
+ case "home":
1531
+ this.moveCursorHome();
1532
+ break;
1533
+ case "end":
1534
+ this.moveCursorEnd();
1535
+ break;
1536
+ case "return":
1537
+ case "enter":
1538
+ this.submit();
1539
+ break;
1540
+ default:
1541
+ if (event.key && event.key.length === 1 && !event.ctrl && !event.alt) {
1542
+ this.insertChar(event.key);
1543
+ }
1544
+ }
1545
+ }
1546
+ _renderSelf(screen) {
1547
+ const rect = this._getContentRect();
1548
+ const { x, y, width, height } = rect;
1549
+ if (width <= 0 || height <= 0) return;
1550
+ const attrs = styleToCellAttrs13(this._style);
1551
+ if (height <= 1 && this._showCompletions) {
1552
+ this._showCompletions = false;
1553
+ }
1554
+ if (this._value.length === 0 && !this.isFocused) {
1555
+ screen.writeString(x, y, truncate3(this._placeholder, width), { ...attrs, dim: true });
1556
+ } else {
1557
+ const visibleWidth = width - 1;
1558
+ let scrollX = 0;
1559
+ if (this._cursorPos > visibleWidth) {
1560
+ scrollX = this._cursorPos - visibleWidth;
1561
+ }
1562
+ const visibleText = this._value.slice(scrollX, scrollX + visibleWidth);
1563
+ screen.writeString(x, y, visibleText, attrs);
1564
+ if (this.isFocused) {
1565
+ const cursorScreenPos = x + this._cursorPos - scrollX;
1566
+ if (cursorScreenPos >= x && cursorScreenPos < x + width) {
1567
+ const cursorChar = this._cursorPos < this._value.length ? this._value[this._cursorPos] : " ";
1568
+ screen.setCell(cursorScreenPos, y, {
1569
+ char: cursorChar,
1570
+ ...attrs,
1571
+ inverse: true
1572
+ });
1573
+ }
1574
+ }
1575
+ }
1576
+ if (this._showCompletions && this._completions.length > 0 && height > 1) {
1577
+ const maxRows = Math.min(this._completions.length, height - 1);
1578
+ for (let i = 0; i < maxRows; i++) {
1579
+ const row = y + 1 + i;
1580
+ const isSelected = i === this._completionIndex;
1581
+ const prefix = isSelected ? caps10.unicode ? "\u25B6 " : "> " : " ";
1582
+ const entry = this._completions[i];
1583
+ screen.writeString(x, row, truncate3(prefix + entry, width), {
1584
+ ...attrs,
1585
+ bold: isSelected,
1586
+ inverse: isSelected
1587
+ });
1588
+ }
1589
+ }
1590
+ }
1591
+ };
1592
+
1593
+ // src/KeyboardShortcuts.ts
1594
+ import { Widget as Widget16 } from "@termuijs/widgets";
1595
+ import { styleToCellAttrs as styleToCellAttrs14, caps as caps11 } from "@termuijs/core";
1596
+ var KeyboardShortcuts = class extends Widget16 {
1597
+ _bindings;
1598
+ _keyColor;
1599
+ _categoryColor;
1600
+ _columns;
1601
+ _showCategories;
1602
+ constructor(bindings, options = {}) {
1603
+ super({});
1604
+ this._bindings = bindings;
1605
+ this._keyColor = options.keyColor ?? { type: "named", name: "cyan" };
1606
+ this._categoryColor = options.categoryColor ?? { type: "named", name: "yellow" };
1607
+ this._columns = Math.max(1, options.columns ?? 2);
1608
+ this._showCategories = options.showCategories ?? true;
1609
+ }
1610
+ /** Replace all bindings and trigger a re-render. */
1611
+ setBindings(bindings) {
1612
+ this._bindings = bindings;
1613
+ this.markDirty();
1614
+ }
1615
+ /** Group bindings by category, preserving insertion order. */
1616
+ _groupBindings() {
1617
+ const groups = [];
1618
+ const indexMap = /* @__PURE__ */ new Map();
1619
+ for (const b of this._bindings) {
1620
+ const cat = b.category ?? "";
1621
+ const key = cat;
1622
+ if (!indexMap.has(key)) {
1623
+ indexMap.set(key, groups.length);
1624
+ groups.push({ category: b.category, bindings: [] });
1625
+ }
1626
+ groups[indexMap.get(key)].bindings.push(b);
1627
+ }
1628
+ return groups;
1629
+ }
1630
+ /**
1631
+ * Render a key label in a bordered box style:
1632
+ * ┌─────┐
1633
+ * │ key │
1634
+ * └─────┘
1635
+ *
1636
+ * We fit it on one line: [ key ] — box drawing characters or ASCII fallback.
1637
+ */
1638
+ _renderKeyLabel(screen, kx, ky, key, attrs) {
1639
+ if (caps11.unicode) {
1640
+ const label = `[${key}]`;
1641
+ screen.writeString(kx, ky, label, { ...attrs, fg: this._keyColor, bold: true });
1642
+ return label.length;
1643
+ } else {
1644
+ const label = `[${key}]`;
1645
+ screen.writeString(kx, ky, label, { ...attrs, fg: this._keyColor, bold: true });
1646
+ return label.length;
1647
+ }
1648
+ }
1649
+ _renderSelf(screen) {
1650
+ const { x, y, width, height } = this._rect;
1651
+ if (width <= 0 || height <= 0) return;
1652
+ const attrs = styleToCellAttrs14(this._style);
1653
+ const groups = this._groupBindings();
1654
+ let row = y;
1655
+ const colWidth = Math.floor(width / this._columns);
1656
+ for (const group of groups) {
1657
+ if (row >= y + height) break;
1658
+ if (this._showCategories && group.category) {
1659
+ const heading = group.category.toUpperCase();
1660
+ const divider = caps11.unicode ? "\u2500" : "-";
1661
+ const line = heading + " " + divider.repeat(Math.max(0, width - heading.length - 1));
1662
+ screen.writeString(x, row, line.slice(0, width), {
1663
+ ...attrs,
1664
+ fg: this._categoryColor,
1665
+ bold: true
1666
+ });
1667
+ row++;
1668
+ if (row >= y + height) break;
1669
+ }
1670
+ for (let i = 0; i < group.bindings.length; i += this._columns) {
1671
+ if (row >= y + height) break;
1672
+ for (let col = 0; col < this._columns; col++) {
1673
+ const binding = group.bindings[i + col];
1674
+ if (!binding) continue;
1675
+ const cx = x + col * colWidth;
1676
+ const availWidth = colWidth - 1;
1677
+ if (availWidth <= 0) continue;
1678
+ const labelLen = this._renderKeyLabel(screen, cx, row, binding.key, attrs);
1679
+ const descX = cx + labelLen + 1;
1680
+ const descWidth = availWidth - labelLen - 1;
1681
+ if (descWidth > 0) {
1682
+ const desc = binding.description.slice(0, descWidth);
1683
+ screen.writeString(descX, row, desc, attrs);
1684
+ }
1685
+ }
1686
+ row++;
1687
+ }
1688
+ if (this._showCategories && group.category) {
1689
+ row++;
1690
+ }
1691
+ }
1692
+ }
1693
+ };
778
1694
  export {
779
1695
  Box,
780
1696
  CommandPalette,
@@ -782,10 +1698,17 @@ export {
782
1698
  Divider,
783
1699
  Form,
784
1700
  Gauge,
1701
+ KeyboardShortcuts,
785
1702
  List,
786
1703
  LogView,
787
1704
  Modal,
788
1705
  MultiSelect,
1706
+ NonInteractiveError,
1707
+ NotificationCenter,
1708
+ NotificationStore,
1709
+ NumberInput,
1710
+ PasswordInput,
1711
+ PathInput,
789
1712
  ProgressBar,
790
1713
  Select,
791
1714
  Spacer,
@@ -798,6 +1721,9 @@ export {
798
1721
  TextInput,
799
1722
  Toast,
800
1723
  Tree,
801
- Widget12 as Widget
1724
+ Widget17 as Widget,
1725
+ notifications,
1726
+ prompt,
1727
+ useNotifications
802
1728
  };
803
1729
  //# sourceMappingURL=index.js.map