@termuijs/widgets 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/dist/index.cjs +511 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +157 -2
- package/dist/index.d.ts +157 -2
- package/dist/index.js +514 -21
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -20,18 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
BarChart: () => BarChart,
|
|
23
24
|
Box: () => Box,
|
|
24
25
|
Gauge: () => Gauge,
|
|
25
26
|
List: () => List,
|
|
26
27
|
LogView: () => LogView,
|
|
27
28
|
ProgressBar: () => ProgressBar,
|
|
28
29
|
SPINNER_FRAMES: () => SPINNER_FRAMES,
|
|
30
|
+
Scrollbar: () => Scrollbar,
|
|
29
31
|
Sparkline: () => Sparkline,
|
|
30
32
|
Spinner: () => Spinner,
|
|
31
33
|
StatusIndicator: () => StatusIndicator,
|
|
32
34
|
Table: () => Table,
|
|
33
35
|
Text: () => Text,
|
|
34
36
|
TextInput: () => TextInput,
|
|
37
|
+
VirtualList: () => VirtualList,
|
|
35
38
|
Widget: () => Widget
|
|
36
39
|
});
|
|
37
40
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -286,29 +289,63 @@ var Text = class extends Widget {
|
|
|
286
289
|
_content;
|
|
287
290
|
_wrap;
|
|
288
291
|
_align;
|
|
292
|
+
_scrollY;
|
|
293
|
+
_scrollX;
|
|
289
294
|
constructor(content, style = {}, props = {}) {
|
|
290
295
|
super(style);
|
|
291
296
|
this._content = content;
|
|
292
297
|
this._wrap = props.wrap ?? true;
|
|
293
298
|
this._align = props.align ?? "left";
|
|
299
|
+
this._scrollY = props.scrollY ?? 0;
|
|
300
|
+
this._scrollX = props.scrollX ?? 0;
|
|
294
301
|
}
|
|
295
302
|
/** Update the text content */
|
|
296
303
|
setContent(content) {
|
|
297
304
|
this._content = content;
|
|
305
|
+
this.markDirty();
|
|
298
306
|
}
|
|
299
307
|
/** Get current text content */
|
|
300
308
|
getContent() {
|
|
301
309
|
return this._content;
|
|
302
310
|
}
|
|
311
|
+
/** Set vertical scroll offset (lines to skip). */
|
|
312
|
+
setScrollY(offset) {
|
|
313
|
+
this._scrollY = Math.max(0, offset);
|
|
314
|
+
this.markDirty();
|
|
315
|
+
}
|
|
316
|
+
/** Set horizontal scroll offset (columns to skip). */
|
|
317
|
+
setScrollX(offset) {
|
|
318
|
+
this._scrollX = Math.max(0, offset);
|
|
319
|
+
this.markDirty();
|
|
320
|
+
}
|
|
321
|
+
/** Get the total number of lines after wrapping. */
|
|
322
|
+
getLineCount() {
|
|
323
|
+
const contentRect = this._getContentRect();
|
|
324
|
+
const text = this._wrap ? (0, import_core3.wordWrap)(this._content, contentRect.width) : this._content;
|
|
325
|
+
return text.split("\n").length;
|
|
326
|
+
}
|
|
303
327
|
_renderSelf(screen) {
|
|
304
328
|
const contentRect = this._getContentRect();
|
|
305
329
|
const { x, y, width, height } = contentRect;
|
|
306
330
|
if (width <= 0 || height <= 0) return;
|
|
307
331
|
const attrs = (0, import_core3.styleToCellAttrs)(this._style);
|
|
308
332
|
let text = this._wrap ? (0, import_core3.wordWrap)(this._content, width) : this._content;
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
333
|
+
const allLines = text.split("\n");
|
|
334
|
+
const startLine = Math.min(this._scrollY, allLines.length);
|
|
335
|
+
const visibleLines = allLines.slice(startLine, startLine + height);
|
|
336
|
+
for (let i = 0; i < Math.min(visibleLines.length, height); i++) {
|
|
337
|
+
let line = visibleLines[i];
|
|
338
|
+
if (line === void 0) continue;
|
|
339
|
+
if (this._scrollX > 0) {
|
|
340
|
+
let skipped = 0;
|
|
341
|
+
let charIndex = 0;
|
|
342
|
+
for (const ch of line) {
|
|
343
|
+
if (skipped >= this._scrollX) break;
|
|
344
|
+
skipped++;
|
|
345
|
+
charIndex += ch.length;
|
|
346
|
+
}
|
|
347
|
+
line = line.slice(charIndex);
|
|
348
|
+
}
|
|
312
349
|
const lineWidth = (0, import_core3.stringWidth)(line);
|
|
313
350
|
let offsetX = 0;
|
|
314
351
|
if (this._align === "center") {
|
|
@@ -343,12 +380,14 @@ var LogView = class extends Widget {
|
|
|
343
380
|
if (this._autoScroll) {
|
|
344
381
|
this._scrollToBottom();
|
|
345
382
|
}
|
|
383
|
+
this.markDirty();
|
|
346
384
|
}
|
|
347
385
|
appendLine(line) {
|
|
348
386
|
this._lines.push(line);
|
|
349
387
|
if (this._autoScroll) {
|
|
350
388
|
this._scrollToBottom();
|
|
351
389
|
}
|
|
390
|
+
this.markDirty();
|
|
352
391
|
}
|
|
353
392
|
scrollUp(n = 1) {
|
|
354
393
|
this._scrollOffset = Math.max(0, this._scrollOffset - n);
|
|
@@ -410,6 +449,7 @@ var List = class extends Widget {
|
|
|
410
449
|
this._items = items;
|
|
411
450
|
this._selectedIndex = Math.min(this._selectedIndex, items.length - 1);
|
|
412
451
|
this._clampScroll();
|
|
452
|
+
this.markDirty();
|
|
413
453
|
}
|
|
414
454
|
/** Move selection up */
|
|
415
455
|
selectPrev() {
|
|
@@ -418,6 +458,7 @@ var List = class extends Widget {
|
|
|
418
458
|
if (next >= 0) {
|
|
419
459
|
this._selectedIndex = next;
|
|
420
460
|
this._clampScroll();
|
|
461
|
+
this.markDirty();
|
|
421
462
|
}
|
|
422
463
|
}
|
|
423
464
|
/** Move selection down */
|
|
@@ -427,6 +468,7 @@ var List = class extends Widget {
|
|
|
427
468
|
if (next < this._items.length) {
|
|
428
469
|
this._selectedIndex = next;
|
|
429
470
|
this._clampScroll();
|
|
471
|
+
this.markDirty();
|
|
430
472
|
}
|
|
431
473
|
}
|
|
432
474
|
/** Confirm the current selection */
|
|
@@ -475,7 +517,10 @@ var List = class extends Widget {
|
|
|
475
517
|
_clampScroll() {
|
|
476
518
|
const rect = this._getContentRect();
|
|
477
519
|
const visibleHeight = rect.height;
|
|
478
|
-
if (visibleHeight <= 0)
|
|
520
|
+
if (visibleHeight <= 0) {
|
|
521
|
+
this._scrollOffset = 0;
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
479
524
|
if (this._selectedIndex < this._scrollOffset) {
|
|
480
525
|
this._scrollOffset = this._selectedIndex;
|
|
481
526
|
}
|
|
@@ -590,8 +635,173 @@ var TextInput = class extends Widget {
|
|
|
590
635
|
}
|
|
591
636
|
};
|
|
592
637
|
|
|
593
|
-
// src/
|
|
638
|
+
// src/input/VirtualList.ts
|
|
594
639
|
var import_core7 = require("@termuijs/core");
|
|
640
|
+
var VirtualList = class extends Widget {
|
|
641
|
+
_totalItems;
|
|
642
|
+
_itemHeight;
|
|
643
|
+
_renderItem;
|
|
644
|
+
_onSelect;
|
|
645
|
+
_selectedIndex = 0;
|
|
646
|
+
_scrollOffset = 0;
|
|
647
|
+
_overscan;
|
|
648
|
+
_showScrollbar;
|
|
649
|
+
constructor(options) {
|
|
650
|
+
super({ border: "single", ...options.style });
|
|
651
|
+
this._totalItems = options.totalItems;
|
|
652
|
+
this._itemHeight = options.itemHeight ?? 1;
|
|
653
|
+
this._renderItem = options.renderItem;
|
|
654
|
+
this._onSelect = options.onSelect;
|
|
655
|
+
this._overscan = options.overscan ?? 2;
|
|
656
|
+
this._showScrollbar = options.showScrollbar ?? true;
|
|
657
|
+
this.focusable = true;
|
|
658
|
+
}
|
|
659
|
+
// ── Getters ──
|
|
660
|
+
get totalItems() {
|
|
661
|
+
return this._totalItems;
|
|
662
|
+
}
|
|
663
|
+
get selectedIndex() {
|
|
664
|
+
return this._selectedIndex;
|
|
665
|
+
}
|
|
666
|
+
get scrollOffset() {
|
|
667
|
+
return this._scrollOffset;
|
|
668
|
+
}
|
|
669
|
+
// ── Public API ──
|
|
670
|
+
/** Update the total item count (e.g., after data refresh) */
|
|
671
|
+
setTotalItems(count) {
|
|
672
|
+
this._totalItems = count;
|
|
673
|
+
this._selectedIndex = Math.min(this._selectedIndex, Math.max(0, count - 1));
|
|
674
|
+
this._clampScroll();
|
|
675
|
+
this.markDirty();
|
|
676
|
+
}
|
|
677
|
+
/** Update the render function (e.g., when data changes) */
|
|
678
|
+
setRenderItem(fn) {
|
|
679
|
+
this._renderItem = fn;
|
|
680
|
+
this.markDirty();
|
|
681
|
+
}
|
|
682
|
+
/** Move selection up by one */
|
|
683
|
+
selectPrev() {
|
|
684
|
+
if (this._selectedIndex > 0) {
|
|
685
|
+
this._selectedIndex--;
|
|
686
|
+
this._clampScroll();
|
|
687
|
+
this.markDirty();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
/** Move selection down by one */
|
|
691
|
+
selectNext() {
|
|
692
|
+
if (this._selectedIndex < this._totalItems - 1) {
|
|
693
|
+
this._selectedIndex++;
|
|
694
|
+
this._clampScroll();
|
|
695
|
+
this.markDirty();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/** Jump to the first item */
|
|
699
|
+
selectFirst() {
|
|
700
|
+
this._selectedIndex = 0;
|
|
701
|
+
this._clampScroll();
|
|
702
|
+
this.markDirty();
|
|
703
|
+
}
|
|
704
|
+
/** Jump to the last item */
|
|
705
|
+
selectLast() {
|
|
706
|
+
this._selectedIndex = Math.max(0, this._totalItems - 1);
|
|
707
|
+
this._clampScroll();
|
|
708
|
+
this.markDirty();
|
|
709
|
+
}
|
|
710
|
+
/** Page up — move by viewport height */
|
|
711
|
+
pageUp() {
|
|
712
|
+
const rect = this._getContentRect();
|
|
713
|
+
const pageSize = Math.floor(rect.height / this._itemHeight);
|
|
714
|
+
this._selectedIndex = Math.max(0, this._selectedIndex - pageSize);
|
|
715
|
+
this._clampScroll();
|
|
716
|
+
this.markDirty();
|
|
717
|
+
}
|
|
718
|
+
/** Page down — move by viewport height */
|
|
719
|
+
pageDown() {
|
|
720
|
+
const rect = this._getContentRect();
|
|
721
|
+
const pageSize = Math.floor(rect.height / this._itemHeight);
|
|
722
|
+
this._selectedIndex = Math.min(this._totalItems - 1, this._selectedIndex + pageSize);
|
|
723
|
+
this._clampScroll();
|
|
724
|
+
this.markDirty();
|
|
725
|
+
}
|
|
726
|
+
/** Scroll to a specific index */
|
|
727
|
+
scrollTo(index) {
|
|
728
|
+
this._selectedIndex = Math.max(0, Math.min(index, this._totalItems - 1));
|
|
729
|
+
this._clampScroll();
|
|
730
|
+
this.markDirty();
|
|
731
|
+
}
|
|
732
|
+
/** Confirm the current selection */
|
|
733
|
+
confirm() {
|
|
734
|
+
if (this._totalItems > 0) {
|
|
735
|
+
this._onSelect?.(this._selectedIndex);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
// ── Rendering ──
|
|
739
|
+
_renderSelf(screen) {
|
|
740
|
+
const rect = this._getContentRect();
|
|
741
|
+
const { x, y, width, height } = rect;
|
|
742
|
+
if (width <= 0 || height <= 0 || this._totalItems === 0) return;
|
|
743
|
+
const attrs = (0, import_core7.styleToCellAttrs)(this._style);
|
|
744
|
+
const visibleItemCount = Math.floor(height / this._itemHeight);
|
|
745
|
+
const startIdx = Math.max(0, this._scrollOffset - this._overscan);
|
|
746
|
+
const endIdx = Math.min(this._totalItems, this._scrollOffset + visibleItemCount + this._overscan);
|
|
747
|
+
const contentWidth = this._showScrollbar && this._totalItems > visibleItemCount ? width - 1 : width;
|
|
748
|
+
for (let idx = startIdx; idx < endIdx; idx++) {
|
|
749
|
+
const rowY = y + (idx - this._scrollOffset) * this._itemHeight;
|
|
750
|
+
if (rowY < y || rowY >= y + height) continue;
|
|
751
|
+
const isSelected = idx === this._selectedIndex;
|
|
752
|
+
let content;
|
|
753
|
+
try {
|
|
754
|
+
content = this._renderItem(idx);
|
|
755
|
+
} catch {
|
|
756
|
+
content = `[Error: item ${idx}]`;
|
|
757
|
+
}
|
|
758
|
+
const prefix = isSelected ? "\u25B8 " : " ";
|
|
759
|
+
let line = prefix + content;
|
|
760
|
+
line = (0, import_core7.truncate)(line, contentWidth);
|
|
761
|
+
const cellStyle = {
|
|
762
|
+
...attrs,
|
|
763
|
+
bold: isSelected,
|
|
764
|
+
inverse: isSelected && this.isFocused
|
|
765
|
+
};
|
|
766
|
+
screen.writeString(x, rowY, line, cellStyle);
|
|
767
|
+
if (isSelected && this.isFocused) {
|
|
768
|
+
const remaining = contentWidth - (0, import_core7.stringWidth)(line);
|
|
769
|
+
for (let c = 0; c < remaining; c++) {
|
|
770
|
+
screen.setCell(x + (0, import_core7.stringWidth)(line) + c, rowY, { char: " ", ...cellStyle });
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (this._showScrollbar && this._totalItems > visibleItemCount) {
|
|
775
|
+
const scrollbarX = x + width - 1;
|
|
776
|
+
const totalPages = this._totalItems - visibleItemCount;
|
|
777
|
+
const scrollRatio = totalPages > 0 ? this._scrollOffset / totalPages : 0;
|
|
778
|
+
const thumbPos = Math.floor(scrollRatio * (height - 1));
|
|
779
|
+
for (let r = 0; r < height; r++) {
|
|
780
|
+
const scrollChar = r === thumbPos ? "\u2588" : "\u2591";
|
|
781
|
+
screen.setCell(scrollbarX, y + r, { char: scrollChar, ...attrs, dim: r !== thumbPos });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
// ── Internal ──
|
|
786
|
+
_clampScroll() {
|
|
787
|
+
const rect = this._getContentRect();
|
|
788
|
+
const visibleHeight = Math.floor(rect.height / this._itemHeight);
|
|
789
|
+
if (visibleHeight <= 0) {
|
|
790
|
+
this._scrollOffset = 0;
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (this._selectedIndex < this._scrollOffset) {
|
|
794
|
+
this._scrollOffset = this._selectedIndex;
|
|
795
|
+
}
|
|
796
|
+
if (this._selectedIndex >= this._scrollOffset + visibleHeight) {
|
|
797
|
+
this._scrollOffset = this._selectedIndex - visibleHeight + 1;
|
|
798
|
+
}
|
|
799
|
+
this._scrollOffset = Math.max(0, Math.min(this._scrollOffset, this._totalItems - visibleHeight));
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// src/data/Table.ts
|
|
804
|
+
var import_core8 = require("@termuijs/core");
|
|
595
805
|
var Table = class extends Widget {
|
|
596
806
|
_columns;
|
|
597
807
|
_rows;
|
|
@@ -612,13 +822,14 @@ var Table = class extends Widget {
|
|
|
612
822
|
}
|
|
613
823
|
setRows(rows) {
|
|
614
824
|
this._rows = rows;
|
|
825
|
+
this.markDirty();
|
|
615
826
|
}
|
|
616
827
|
_renderSelf(screen) {
|
|
617
828
|
const rect = this._getContentRect();
|
|
618
829
|
const { x, y, width, height } = rect;
|
|
619
830
|
if (width <= 0 || height <= 0) return;
|
|
620
|
-
const attrs = (0,
|
|
621
|
-
const sepWidth = (0,
|
|
831
|
+
const attrs = (0, import_core8.styleToCellAttrs)(this._style);
|
|
832
|
+
const sepWidth = (0, import_core8.stringWidth)(this._separator);
|
|
622
833
|
const colWidths = this._computeColumnWidths(
|
|
623
834
|
width - (this._columns.length - 1) * sepWidth
|
|
624
835
|
);
|
|
@@ -685,8 +896,8 @@ var Table = class extends Widget {
|
|
|
685
896
|
return this._columns.map((c) => c.width ?? flexWidth);
|
|
686
897
|
}
|
|
687
898
|
_alignText(text, width, align) {
|
|
688
|
-
const truncated = (0,
|
|
689
|
-
const textWidth = (0,
|
|
899
|
+
const truncated = (0, import_core8.truncate)(text, width);
|
|
900
|
+
const textWidth = (0, import_core8.stringWidth)(truncated);
|
|
690
901
|
const pad = Math.max(0, width - textWidth);
|
|
691
902
|
switch (align) {
|
|
692
903
|
case "right":
|
|
@@ -704,7 +915,7 @@ var Table = class extends Widget {
|
|
|
704
915
|
};
|
|
705
916
|
|
|
706
917
|
// src/data/Gauge.ts
|
|
707
|
-
var
|
|
918
|
+
var import_core9 = require("@termuijs/core");
|
|
708
919
|
var Gauge = class extends Widget {
|
|
709
920
|
_label;
|
|
710
921
|
_value = 0;
|
|
@@ -718,22 +929,24 @@ var Gauge = class extends Widget {
|
|
|
718
929
|
}
|
|
719
930
|
setValue(value) {
|
|
720
931
|
this._value = Math.max(0, Math.min(1, value));
|
|
932
|
+
this.markDirty();
|
|
721
933
|
}
|
|
722
934
|
getValue() {
|
|
723
935
|
return this._value;
|
|
724
936
|
}
|
|
725
937
|
setLabel(label) {
|
|
726
938
|
this._label = label;
|
|
939
|
+
this.markDirty();
|
|
727
940
|
}
|
|
728
941
|
_renderSelf(screen) {
|
|
729
942
|
const rect = this._getContentRect();
|
|
730
943
|
const { x, y, width, height } = rect;
|
|
731
944
|
if (width <= 0 || height <= 0) return;
|
|
732
|
-
const attrs = (0,
|
|
945
|
+
const attrs = (0, import_core9.styleToCellAttrs)(this._style);
|
|
733
946
|
const labelStr = this._label + " ";
|
|
734
947
|
const percentStr = this._showLabel ? ` ${Math.round(this._value * 100)}%` : "";
|
|
735
|
-
const labelWidth = (0,
|
|
736
|
-
const percentWidth = (0,
|
|
948
|
+
const labelWidth = (0, import_core9.stringWidth)(labelStr);
|
|
949
|
+
const percentWidth = (0, import_core9.stringWidth)(percentStr);
|
|
737
950
|
const barWidth = Math.max(0, width - labelWidth - percentWidth);
|
|
738
951
|
screen.writeString(x, y, labelStr, { ...attrs, bold: true });
|
|
739
952
|
const filled = Math.round(barWidth * this._value);
|
|
@@ -755,7 +968,7 @@ var Gauge = class extends Widget {
|
|
|
755
968
|
};
|
|
756
969
|
|
|
757
970
|
// src/data/Sparkline.ts
|
|
758
|
-
var
|
|
971
|
+
var import_core10 = require("@termuijs/core");
|
|
759
972
|
var SPARK_CHARS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
760
973
|
var Sparkline = class extends Widget {
|
|
761
974
|
_label;
|
|
@@ -770,15 +983,17 @@ var Sparkline = class extends Widget {
|
|
|
770
983
|
}
|
|
771
984
|
setData(data) {
|
|
772
985
|
this._data = data;
|
|
986
|
+
this.markDirty();
|
|
773
987
|
}
|
|
774
988
|
pushValue(value) {
|
|
775
989
|
this._data.push(value);
|
|
990
|
+
this.markDirty();
|
|
776
991
|
}
|
|
777
992
|
_renderSelf(screen) {
|
|
778
993
|
const rect = this._getContentRect();
|
|
779
994
|
const { x, y, width, height } = rect;
|
|
780
995
|
if (width <= 0 || height <= 0) return;
|
|
781
|
-
const attrs = (0,
|
|
996
|
+
const attrs = (0, import_core10.styleToCellAttrs)(this._style);
|
|
782
997
|
const labelStr = this._label + " ";
|
|
783
998
|
const labelWidth = labelStr.length;
|
|
784
999
|
screen.writeString(x, y, labelStr, { ...attrs, bold: true });
|
|
@@ -807,7 +1022,7 @@ var Sparkline = class extends Widget {
|
|
|
807
1022
|
};
|
|
808
1023
|
|
|
809
1024
|
// src/data/StatusIndicator.ts
|
|
810
|
-
var
|
|
1025
|
+
var import_core11 = require("@termuijs/core");
|
|
811
1026
|
var StatusIndicator = class extends Widget {
|
|
812
1027
|
_label;
|
|
813
1028
|
_isUp;
|
|
@@ -833,7 +1048,7 @@ var StatusIndicator = class extends Widget {
|
|
|
833
1048
|
const rect = this._getContentRect();
|
|
834
1049
|
const { x, y, width, height } = rect;
|
|
835
1050
|
if (width <= 0 || height <= 0) return;
|
|
836
|
-
const attrs = (0,
|
|
1051
|
+
const attrs = (0, import_core11.styleToCellAttrs)(this._style);
|
|
837
1052
|
const dot = this._isUp ? "\u25CF" : "\u25CB";
|
|
838
1053
|
const statusText = this._isUp ? "Online" : "Offline";
|
|
839
1054
|
const color = this._isUp ? this._upColor : this._downColor;
|
|
@@ -845,8 +1060,196 @@ var StatusIndicator = class extends Widget {
|
|
|
845
1060
|
}
|
|
846
1061
|
};
|
|
847
1062
|
|
|
1063
|
+
// src/data/BarChart.ts
|
|
1064
|
+
var import_core12 = require("@termuijs/core");
|
|
1065
|
+
var BarChart = class extends Widget {
|
|
1066
|
+
_data = [];
|
|
1067
|
+
_direction;
|
|
1068
|
+
_barWidth;
|
|
1069
|
+
_barGap;
|
|
1070
|
+
_groupGap;
|
|
1071
|
+
_max;
|
|
1072
|
+
_barColor;
|
|
1073
|
+
_valueColor;
|
|
1074
|
+
_labelColor;
|
|
1075
|
+
constructor(data, style = {}, opts = {}) {
|
|
1076
|
+
super(style);
|
|
1077
|
+
this._data = data;
|
|
1078
|
+
this._direction = opts.direction ?? "vertical";
|
|
1079
|
+
this._barWidth = opts.barWidth ?? 1;
|
|
1080
|
+
this._barGap = opts.barGap ?? 1;
|
|
1081
|
+
this._groupGap = opts.groupGap ?? 2;
|
|
1082
|
+
this._max = opts.max;
|
|
1083
|
+
this._barColor = opts.barColor ?? { type: "named", name: "cyan" };
|
|
1084
|
+
this._valueColor = opts.valueColor ?? { type: "named", name: "white" };
|
|
1085
|
+
this._labelColor = opts.labelColor ?? { type: "named", name: "brightBlack" };
|
|
1086
|
+
}
|
|
1087
|
+
setData(data) {
|
|
1088
|
+
this._data = data;
|
|
1089
|
+
this.markDirty();
|
|
1090
|
+
}
|
|
1091
|
+
setMax(max) {
|
|
1092
|
+
this._max = max;
|
|
1093
|
+
this.markDirty();
|
|
1094
|
+
}
|
|
1095
|
+
_renderSelf(screen) {
|
|
1096
|
+
const rect = this._getContentRect();
|
|
1097
|
+
const { x, y, width, height } = rect;
|
|
1098
|
+
if (width <= 0 || height <= 0 || this._data.length === 0) return;
|
|
1099
|
+
const maxVal = this._computeMax();
|
|
1100
|
+
if (maxVal === 0) return;
|
|
1101
|
+
if (this._direction === "vertical") {
|
|
1102
|
+
this._renderVertical(screen, x, y, width, height, maxVal);
|
|
1103
|
+
} else {
|
|
1104
|
+
this._renderHorizontal(screen, x, y, width, height, maxVal);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
_computeMax() {
|
|
1108
|
+
if (this._max !== void 0) return this._max;
|
|
1109
|
+
let max = 0;
|
|
1110
|
+
for (const group of this._data) {
|
|
1111
|
+
for (const bar of group.bars) {
|
|
1112
|
+
if (bar.value > max) max = bar.value;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return max;
|
|
1116
|
+
}
|
|
1117
|
+
// ── Vertical Rendering ───────────────────────────
|
|
1118
|
+
_renderVertical(screen, ox, oy, width, height, maxVal) {
|
|
1119
|
+
const valueRows = 1;
|
|
1120
|
+
const hasLabels = this._data.some(
|
|
1121
|
+
(g) => g.bars.some((b) => b.label !== void 0)
|
|
1122
|
+
);
|
|
1123
|
+
const labelRows = hasLabels ? 1 : 0;
|
|
1124
|
+
const hasGroupLabels = this._data.some((g) => g.label !== void 0);
|
|
1125
|
+
const groupLabelRows = hasGroupLabels ? 1 : 0;
|
|
1126
|
+
const reservedRows = valueRows + labelRows + groupLabelRows;
|
|
1127
|
+
if (height <= reservedRows) return;
|
|
1128
|
+
const barAreaHeight = height - reservedRows;
|
|
1129
|
+
let cx = ox;
|
|
1130
|
+
for (let gi = 0; gi < this._data.length; gi++) {
|
|
1131
|
+
const group = this._data[gi];
|
|
1132
|
+
if (!group) continue;
|
|
1133
|
+
const groupStartX = cx;
|
|
1134
|
+
for (let bi = 0; bi < group.bars.length; bi++) {
|
|
1135
|
+
const bar = group.bars[bi];
|
|
1136
|
+
if (!bar) continue;
|
|
1137
|
+
if (cx + this._barWidth > ox + width) break;
|
|
1138
|
+
const color = bar.color ?? this._barColor;
|
|
1139
|
+
const scaledHeight = bar.value / maxVal * (barAreaHeight * 8);
|
|
1140
|
+
let remaining = Math.round(scaledHeight);
|
|
1141
|
+
for (let row = barAreaHeight - 1; row >= 0; row--) {
|
|
1142
|
+
if (remaining <= 0) break;
|
|
1143
|
+
const level = Math.min(remaining, 8);
|
|
1144
|
+
const symbol = import_core12.VERTICAL_BAR_SYMBOLS[level] ?? " ";
|
|
1145
|
+
const cellY = oy + row;
|
|
1146
|
+
for (let col = 0; col < this._barWidth; col++) {
|
|
1147
|
+
const cellX = cx + col;
|
|
1148
|
+
if (cellX < ox + width) {
|
|
1149
|
+
screen.setCell(cellX, cellY, { char: symbol, fg: color });
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
remaining -= 8;
|
|
1153
|
+
}
|
|
1154
|
+
const valStr = Math.round(bar.value).toString();
|
|
1155
|
+
const valX = cx + Math.floor((this._barWidth - (0, import_core12.stringWidth)(valStr)) / 2);
|
|
1156
|
+
screen.writeString(
|
|
1157
|
+
Math.max(cx, valX),
|
|
1158
|
+
oy + barAreaHeight,
|
|
1159
|
+
valStr.slice(0, this._barWidth),
|
|
1160
|
+
{ fg: this._valueColor }
|
|
1161
|
+
);
|
|
1162
|
+
if (hasLabels && bar.label) {
|
|
1163
|
+
const label = bar.label.slice(0, this._barWidth);
|
|
1164
|
+
const labelX = cx + Math.floor((this._barWidth - (0, import_core12.stringWidth)(label)) / 2);
|
|
1165
|
+
screen.writeString(
|
|
1166
|
+
Math.max(cx, labelX),
|
|
1167
|
+
oy + barAreaHeight + valueRows,
|
|
1168
|
+
label,
|
|
1169
|
+
{ fg: this._labelColor }
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
cx += this._barWidth;
|
|
1173
|
+
if (bi < group.bars.length - 1) cx += this._barGap;
|
|
1174
|
+
}
|
|
1175
|
+
if (hasGroupLabels && group.label) {
|
|
1176
|
+
const groupWidth = cx - groupStartX;
|
|
1177
|
+
const label = group.label.slice(0, groupWidth);
|
|
1178
|
+
const labelX = groupStartX + Math.floor((groupWidth - (0, import_core12.stringWidth)(label)) / 2);
|
|
1179
|
+
screen.writeString(
|
|
1180
|
+
Math.max(groupStartX, labelX),
|
|
1181
|
+
oy + height - 1,
|
|
1182
|
+
label,
|
|
1183
|
+
{ fg: this._labelColor }
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
if (gi < this._data.length - 1) cx += this._groupGap;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
// ── Horizontal Rendering ─────────────────────────
|
|
1190
|
+
_renderHorizontal(screen, ox, oy, width, height, maxVal) {
|
|
1191
|
+
let maxLabelWidth = 0;
|
|
1192
|
+
let maxValueWidth = 0;
|
|
1193
|
+
for (const group of this._data) {
|
|
1194
|
+
for (const bar of group.bars) {
|
|
1195
|
+
if (bar.label) {
|
|
1196
|
+
const w = (0, import_core12.stringWidth)(bar.label);
|
|
1197
|
+
if (w > maxLabelWidth) maxLabelWidth = w;
|
|
1198
|
+
}
|
|
1199
|
+
const vw = Math.round(bar.value).toString().length;
|
|
1200
|
+
if (vw > maxValueWidth) maxValueWidth = vw;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
const labelColWidth = maxLabelWidth > 0 ? maxLabelWidth + 1 : 0;
|
|
1204
|
+
const valueColWidth = maxValueWidth > 0 ? maxValueWidth + 1 : 0;
|
|
1205
|
+
const barAreaWidth = width - labelColWidth - valueColWidth;
|
|
1206
|
+
if (barAreaWidth <= 0) return;
|
|
1207
|
+
let cy = oy;
|
|
1208
|
+
for (let gi = 0; gi < this._data.length; gi++) {
|
|
1209
|
+
const group = this._data[gi];
|
|
1210
|
+
if (!group) continue;
|
|
1211
|
+
for (let bi = 0; bi < group.bars.length; bi++) {
|
|
1212
|
+
const bar = group.bars[bi];
|
|
1213
|
+
if (!bar) continue;
|
|
1214
|
+
for (let row = 0; row < this._barWidth; row++) {
|
|
1215
|
+
const cellY = cy + row;
|
|
1216
|
+
if (cellY >= oy + height) break;
|
|
1217
|
+
if (row === 0 && bar.label) {
|
|
1218
|
+
const label = bar.label.slice(0, maxLabelWidth);
|
|
1219
|
+
const padded = label.padStart(maxLabelWidth);
|
|
1220
|
+
screen.writeString(ox, cellY, padded, { fg: this._labelColor });
|
|
1221
|
+
}
|
|
1222
|
+
const color = bar.color ?? this._barColor;
|
|
1223
|
+
const scaledWidth = bar.value / maxVal * (barAreaWidth * 8);
|
|
1224
|
+
let remaining = Math.round(scaledWidth);
|
|
1225
|
+
const barStartX = ox + labelColWidth;
|
|
1226
|
+
for (let col = 0; col < barAreaWidth; col++) {
|
|
1227
|
+
if (remaining <= 0) break;
|
|
1228
|
+
const level = Math.min(remaining, 8);
|
|
1229
|
+
const symbol = import_core12.HORIZONTAL_BAR_SYMBOLS[level] ?? " ";
|
|
1230
|
+
screen.setCell(barStartX + col, cellY, { char: symbol, fg: color });
|
|
1231
|
+
remaining -= 8;
|
|
1232
|
+
}
|
|
1233
|
+
if (row === 0) {
|
|
1234
|
+
const valStr = Math.round(bar.value).toString();
|
|
1235
|
+
screen.writeString(
|
|
1236
|
+
ox + labelColWidth + barAreaWidth,
|
|
1237
|
+
cellY,
|
|
1238
|
+
` ${valStr}`,
|
|
1239
|
+
{ fg: this._valueColor }
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
cy += this._barWidth;
|
|
1244
|
+
if (bi < group.bars.length - 1) cy += this._barGap;
|
|
1245
|
+
}
|
|
1246
|
+
if (gi < this._data.length - 1) cy += this._groupGap;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
|
|
848
1251
|
// src/feedback/ProgressBar.ts
|
|
849
|
-
var
|
|
1252
|
+
var import_core13 = require("@termuijs/core");
|
|
850
1253
|
var ProgressBar = class extends Widget {
|
|
851
1254
|
_value;
|
|
852
1255
|
_fillChar;
|
|
@@ -868,6 +1271,7 @@ var ProgressBar = class extends Widget {
|
|
|
868
1271
|
/** Set progress value (0–1) */
|
|
869
1272
|
setValue(value) {
|
|
870
1273
|
this._value = Math.max(0, Math.min(1, value));
|
|
1274
|
+
this.markDirty();
|
|
871
1275
|
}
|
|
872
1276
|
get value() {
|
|
873
1277
|
return this._value;
|
|
@@ -876,7 +1280,7 @@ var ProgressBar = class extends Widget {
|
|
|
876
1280
|
const rect = this._getContentRect();
|
|
877
1281
|
const { x, y, width } = rect;
|
|
878
1282
|
if (width <= 0) return;
|
|
879
|
-
const attrs = (0,
|
|
1283
|
+
const attrs = (0, import_core13.styleToCellAttrs)(this._style);
|
|
880
1284
|
let label = "";
|
|
881
1285
|
if (this._showLabel) {
|
|
882
1286
|
if (this._labelFormat === "percent") {
|
|
@@ -901,7 +1305,7 @@ var ProgressBar = class extends Widget {
|
|
|
901
1305
|
};
|
|
902
1306
|
|
|
903
1307
|
// src/feedback/Spinner.ts
|
|
904
|
-
var
|
|
1308
|
+
var import_core14 = require("@termuijs/core");
|
|
905
1309
|
var SPINNER_FRAMES = {
|
|
906
1310
|
dots: {
|
|
907
1311
|
frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
|
|
@@ -971,7 +1375,7 @@ var Spinner = class extends Widget {
|
|
|
971
1375
|
const rect = this._getContentRect();
|
|
972
1376
|
const { x, y, width } = rect;
|
|
973
1377
|
if (width <= 0) return;
|
|
974
|
-
const attrs = (0,
|
|
1378
|
+
const attrs = (0, import_core14.styleToCellAttrs)(this._style);
|
|
975
1379
|
const frame = this._frames[this._frameIndex];
|
|
976
1380
|
screen.writeString(x, y, frame, { ...attrs, fg: this._color });
|
|
977
1381
|
if (this._label) {
|
|
@@ -979,20 +1383,106 @@ var Spinner = class extends Widget {
|
|
|
979
1383
|
}
|
|
980
1384
|
}
|
|
981
1385
|
};
|
|
1386
|
+
|
|
1387
|
+
// src/feedback/Scrollbar.ts
|
|
1388
|
+
var import_core15 = require("@termuijs/core");
|
|
1389
|
+
var Scrollbar = class extends Widget {
|
|
1390
|
+
_contentLength;
|
|
1391
|
+
_viewportLength;
|
|
1392
|
+
_position;
|
|
1393
|
+
_orientation;
|
|
1394
|
+
_thumbColor;
|
|
1395
|
+
_trackColor;
|
|
1396
|
+
_showArrows;
|
|
1397
|
+
constructor(style = {}, opts) {
|
|
1398
|
+
super(style);
|
|
1399
|
+
this._contentLength = opts.contentLength;
|
|
1400
|
+
this._viewportLength = opts.viewportLength;
|
|
1401
|
+
this._position = opts.position ?? 0;
|
|
1402
|
+
this._orientation = opts.orientation ?? "verticalRight";
|
|
1403
|
+
this._thumbColor = opts.thumbColor ?? { type: "named", name: "white" };
|
|
1404
|
+
this._trackColor = opts.trackColor ?? { type: "named", name: "brightBlack" };
|
|
1405
|
+
this._showArrows = opts.showArrows ?? true;
|
|
1406
|
+
}
|
|
1407
|
+
setPosition(position) {
|
|
1408
|
+
this._position = position;
|
|
1409
|
+
this.markDirty();
|
|
1410
|
+
}
|
|
1411
|
+
setContentLength(length) {
|
|
1412
|
+
this._contentLength = length;
|
|
1413
|
+
this.markDirty();
|
|
1414
|
+
}
|
|
1415
|
+
setViewportLength(length) {
|
|
1416
|
+
this._viewportLength = length;
|
|
1417
|
+
this.markDirty();
|
|
1418
|
+
}
|
|
1419
|
+
_renderSelf(screen) {
|
|
1420
|
+
const rect = this._getContentRect();
|
|
1421
|
+
const { x, y, width, height } = rect;
|
|
1422
|
+
if (width <= 0 || height <= 0 || this._contentLength <= 0) return;
|
|
1423
|
+
if (this._contentLength <= this._viewportLength) return;
|
|
1424
|
+
const vertical = this._orientation === "verticalRight" || this._orientation === "verticalLeft";
|
|
1425
|
+
const symbols = vertical ? import_core15.ScrollbarSets.VERTICAL : import_core15.ScrollbarSets.HORIZONTAL;
|
|
1426
|
+
const trackX = this._orientation === "verticalLeft" ? x : this._orientation === "verticalRight" ? x + width - 1 : x;
|
|
1427
|
+
const trackY = this._orientation === "horizontalTop" ? y : this._orientation === "horizontalBottom" ? y + height - 1 : y;
|
|
1428
|
+
const totalLength = vertical ? height : width;
|
|
1429
|
+
if (totalLength <= 0) return;
|
|
1430
|
+
let trackStart = 0;
|
|
1431
|
+
let trackLength = totalLength;
|
|
1432
|
+
if (this._showArrows && totalLength > 2) {
|
|
1433
|
+
const beginX = vertical ? trackX : x;
|
|
1434
|
+
const beginY = vertical ? y : trackY;
|
|
1435
|
+
screen.setCell(beginX, beginY, {
|
|
1436
|
+
char: symbols.begin,
|
|
1437
|
+
fg: this._trackColor
|
|
1438
|
+
});
|
|
1439
|
+
const endX = vertical ? trackX : x + totalLength - 1;
|
|
1440
|
+
const endY = vertical ? y + totalLength - 1 : trackY;
|
|
1441
|
+
screen.setCell(endX, endY, {
|
|
1442
|
+
char: symbols.end,
|
|
1443
|
+
fg: this._trackColor
|
|
1444
|
+
});
|
|
1445
|
+
trackStart = 1;
|
|
1446
|
+
trackLength -= 2;
|
|
1447
|
+
}
|
|
1448
|
+
if (trackLength <= 0) return;
|
|
1449
|
+
const thumbSize = Math.max(1, Math.floor(
|
|
1450
|
+
trackLength * this._viewportLength / this._contentLength
|
|
1451
|
+
));
|
|
1452
|
+
const maxScroll = Math.max(1, this._contentLength - this._viewportLength);
|
|
1453
|
+
const thumbOffset = Math.min(
|
|
1454
|
+
trackLength - thumbSize,
|
|
1455
|
+
Math.floor(this._position * (trackLength - thumbSize) / maxScroll)
|
|
1456
|
+
);
|
|
1457
|
+
for (let i = 0; i < trackLength; i++) {
|
|
1458
|
+
const pos = trackStart + i;
|
|
1459
|
+
const cellX = vertical ? trackX : x + pos;
|
|
1460
|
+
const cellY = vertical ? y + pos : trackY;
|
|
1461
|
+
const isThumb = i >= thumbOffset && i < thumbOffset + thumbSize;
|
|
1462
|
+
screen.setCell(cellX, cellY, {
|
|
1463
|
+
char: isThumb ? symbols.thumb : symbols.track,
|
|
1464
|
+
fg: isThumb ? this._thumbColor : this._trackColor
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
982
1469
|
// Annotate the CommonJS export names for ESM import in node:
|
|
983
1470
|
0 && (module.exports = {
|
|
1471
|
+
BarChart,
|
|
984
1472
|
Box,
|
|
985
1473
|
Gauge,
|
|
986
1474
|
List,
|
|
987
1475
|
LogView,
|
|
988
1476
|
ProgressBar,
|
|
989
1477
|
SPINNER_FRAMES,
|
|
1478
|
+
Scrollbar,
|
|
990
1479
|
Sparkline,
|
|
991
1480
|
Spinner,
|
|
992
1481
|
StatusIndicator,
|
|
993
1482
|
Table,
|
|
994
1483
|
Text,
|
|
995
1484
|
TextInput,
|
|
1485
|
+
VirtualList,
|
|
996
1486
|
Widget
|
|
997
1487
|
});
|
|
998
1488
|
//# sourceMappingURL=index.cjs.map
|