@opentui/core 0.1.23 → 0.1.25

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/index.js CHANGED
@@ -13,7 +13,6 @@ import {
13
13
  KeyHandler,
14
14
  LayoutEvents,
15
15
  LogLevel,
16
- MeasureMode,
17
16
  MouseButton,
18
17
  MouseEvent,
19
18
  MouseParser,
@@ -28,7 +27,6 @@ import {
28
27
  TerminalConsole,
29
28
  TextAttributes,
30
29
  TextBuffer,
31
- TrackedNode,
32
30
  bg,
33
31
  bgBlack,
34
32
  bgBlue,
@@ -56,16 +54,19 @@ import {
56
54
  coordinateToCharacterIndex,
57
55
  createCliRenderer,
58
56
  createTextAttributes,
59
- createTrackedNode,
60
57
  cyan,
61
58
  delegate,
62
59
  dim,
60
+ env,
61
+ envRegistry,
62
+ exports_src,
63
63
  fg,
64
64
  fonts,
65
+ generateEnvColored,
66
+ generateEnvMarkdown,
65
67
  getBorderFromSides,
66
68
  getBorderSides,
67
69
  getCharacterPositions,
68
- getKeyHandler,
69
70
  getObjectsInViewport,
70
71
  green,
71
72
  h,
@@ -73,15 +74,7 @@ import {
73
74
  hexToRgb,
74
75
  hsvToRgb,
75
76
  instantiate,
76
- isDimensionType,
77
- isFlexBasisType,
78
- isMarginType,
79
- isOverflowType,
80
- isPaddingType,
81
- isPositionType,
82
- isPositionTypeType,
83
77
  isRenderable,
84
- isSizeType,
85
78
  isStyledText,
86
79
  isVNode,
87
80
  isValidPercentage,
@@ -108,6 +101,7 @@ import {
108
101
  parseUnit,
109
102
  parseWrap,
110
103
  red,
104
+ registerEnvVar,
111
105
  renderFontToFrameBuffer,
112
106
  resolveRenderLib,
113
107
  reverse,
@@ -117,10 +111,11 @@ import {
117
111
  stringToStyledText,
118
112
  t,
119
113
  underline,
114
+ visualizeRenderableTree,
120
115
  white,
121
116
  wrapWithDelegates,
122
117
  yellow
123
- } from "./index-a6ydv6yb.js";
118
+ } from "./index-6kvgbzah.js";
124
119
  // src/post/filters.ts
125
120
  function applyScanlines(buffer, strength = 0.8, step = 2) {
126
121
  const width = buffer.width;
@@ -1378,7 +1373,7 @@ class BoxRenderable extends Renderable {
1378
1373
  };
1379
1374
  }
1380
1375
  applyYogaBorders() {
1381
- const node = this.layoutNode.yogaNode;
1376
+ const node = this.yogaNode;
1382
1377
  node.setBorder(Edge.Left, this.borderSides.left ? 1 : 0);
1383
1378
  node.setBorder(Edge.Right, this.borderSides.right ? 1 : 0);
1384
1379
  node.setBorder(Edge.Top, this.borderSides.top ? 1 : 0);
@@ -1386,7 +1381,7 @@ class BoxRenderable extends Renderable {
1386
1381
  this.requestRender();
1387
1382
  }
1388
1383
  applyYogaGap(options) {
1389
- const node = this.layoutNode.yogaNode;
1384
+ const node = this.yogaNode;
1390
1385
  if (isGapType(options.gap)) {
1391
1386
  node.setGap(Gutter.All, options.gap);
1392
1387
  }
@@ -1399,19 +1394,19 @@ class BoxRenderable extends Renderable {
1399
1394
  }
1400
1395
  set gap(gap) {
1401
1396
  if (isGapType(gap)) {
1402
- this.layoutNode.yogaNode.setGap(Gutter.All, gap);
1397
+ this.yogaNode.setGap(Gutter.All, gap);
1403
1398
  this.requestRender();
1404
1399
  }
1405
1400
  }
1406
1401
  set rowGap(rowGap) {
1407
1402
  if (isGapType(rowGap)) {
1408
- this.layoutNode.yogaNode.setGap(Gutter.Row, rowGap);
1403
+ this.yogaNode.setGap(Gutter.Row, rowGap);
1409
1404
  this.requestRender();
1410
1405
  }
1411
1406
  }
1412
1407
  set columnGap(columnGap) {
1413
1408
  if (isGapType(columnGap)) {
1414
- this.layoutNode.yogaNode.setGap(Gutter.Column, columnGap);
1409
+ this.yogaNode.setGap(Gutter.Column, columnGap);
1415
1410
  this.requestRender();
1416
1411
  }
1417
1412
  }
@@ -1437,7 +1432,7 @@ class FrameBufferRenderable extends Renderable {
1437
1432
  this.requestRender();
1438
1433
  }
1439
1434
  renderSelf(buffer) {
1440
- if (!this.visible)
1435
+ if (!this.visible || this.isDestroyed)
1441
1436
  return;
1442
1437
  buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
1443
1438
  }
@@ -1465,16 +1460,16 @@ function styledTextToTextNodes(styledText) {
1465
1460
 
1466
1461
  class TextNodeRenderable extends BaseRenderable {
1467
1462
  [BrandedTextNodeRenderable] = true;
1468
- fg;
1469
- bg;
1470
- attributes;
1463
+ _fg;
1464
+ _bg;
1465
+ _attributes;
1471
1466
  _children = [];
1472
1467
  parent = null;
1473
1468
  constructor(options) {
1474
1469
  super(options);
1475
- this.fg = options.fg ? parseColor(options.fg) : undefined;
1476
- this.bg = options.bg ? parseColor(options.bg) : undefined;
1477
- this.attributes = options.attributes ?? 0;
1470
+ this._fg = options.fg ? parseColor(options.fg) : undefined;
1471
+ this._bg = options.bg ? parseColor(options.bg) : undefined;
1472
+ this._attributes = options.attributes ?? 0;
1478
1473
  }
1479
1474
  get children() {
1480
1475
  return this._children;
@@ -1528,6 +1523,13 @@ class TextNodeRenderable extends BaseRenderable {
1528
1523
  }
1529
1524
  throw new Error("TextNodeRenderable only accepts strings, TextNodeRenderable instances, or StyledText instances");
1530
1525
  }
1526
+ replace(obj, index) {
1527
+ this._children[index] = obj;
1528
+ if (typeof obj !== "string") {
1529
+ obj.parent = this;
1530
+ }
1531
+ this.requestRender();
1532
+ }
1531
1533
  insertBefore(child, anchorNode) {
1532
1534
  if (!anchorNode || !isTextNodeRenderable(anchorNode)) {
1533
1535
  throw new Error("Anchor must be a TextNodeRenderable");
@@ -1569,9 +1571,9 @@ class TextNodeRenderable extends BaseRenderable {
1569
1571
  }
1570
1572
  mergeStyles(parentStyle) {
1571
1573
  return {
1572
- fg: this.fg ?? parentStyle.fg,
1573
- bg: this.bg ?? parentStyle.bg,
1574
- attributes: this.attributes | parentStyle.attributes
1574
+ fg: this._fg ?? parentStyle.fg,
1575
+ bg: this._bg ?? parentStyle.bg,
1576
+ attributes: this._attributes | parentStyle.attributes
1575
1577
  };
1576
1578
  }
1577
1579
  gatherWithInheritedStyle(parentStyle = { fg: undefined, bg: undefined, attributes: 0 }) {
@@ -1618,13 +1620,46 @@ class TextNodeRenderable extends BaseRenderable {
1618
1620
  getRenderable(id) {
1619
1621
  return this._children.find((child) => typeof child !== "string" && child.id === id);
1620
1622
  }
1623
+ get fg() {
1624
+ return this._fg;
1625
+ }
1626
+ set fg(fg2) {
1627
+ if (!fg2) {
1628
+ this._fg = undefined;
1629
+ this.requestRender();
1630
+ return;
1631
+ }
1632
+ this._fg = parseColor(fg2);
1633
+ this.requestRender();
1634
+ }
1635
+ set bg(bg2) {
1636
+ if (!bg2) {
1637
+ this._bg = undefined;
1638
+ this.requestRender();
1639
+ return;
1640
+ }
1641
+ this._bg = parseColor(bg2);
1642
+ this.requestRender();
1643
+ }
1644
+ get bg() {
1645
+ return this._bg;
1646
+ }
1647
+ set attributes(attributes) {
1648
+ this._attributes = attributes;
1649
+ this.requestRender();
1650
+ }
1651
+ get attributes() {
1652
+ return this._attributes;
1653
+ }
1621
1654
  }
1622
1655
 
1623
1656
  class RootTextNodeRenderable extends TextNodeRenderable {
1624
1657
  ctx;
1625
- constructor(ctx, options) {
1658
+ textParent;
1659
+ constructor(ctx, options, textParent) {
1626
1660
  super(options);
1627
1661
  this.ctx = ctx;
1662
+ this.textParent = textParent;
1628
1663
  }
1629
1664
  requestRender() {
1630
1665
  this.markDirty();
@@ -1636,14 +1671,17 @@ class RootTextNodeRenderable extends TextNodeRenderable {
1636
1671
  class TextRenderable extends Renderable {
1637
1672
  selectable = true;
1638
1673
  _text;
1674
+ _hasManualStyledText = false;
1639
1675
  _defaultFg;
1640
1676
  _defaultBg;
1641
1677
  _defaultAttributes;
1642
1678
  _selectionBg;
1643
1679
  _selectionFg;
1680
+ _wrap = false;
1681
+ _wrapMode = "word";
1644
1682
  lastLocalSelection = null;
1645
1683
  textBuffer;
1646
- _lineInfo = { lineStarts: [], lineWidths: [] };
1684
+ _lineInfo = { lineStarts: [], lineWidths: [], maxLineWidth: 0 };
1647
1685
  rootTextNode;
1648
1686
  _defaultOptions = {
1649
1687
  content: "",
@@ -1652,32 +1690,41 @@ class TextRenderable extends Renderable {
1652
1690
  selectionBg: undefined,
1653
1691
  selectionFg: undefined,
1654
1692
  selectable: true,
1655
- attributes: 0
1693
+ attributes: 0,
1694
+ wrap: true,
1695
+ wrapMode: "word"
1656
1696
  };
1657
1697
  constructor(ctx, options) {
1658
1698
  super(ctx, options);
1659
1699
  const content = options.content ?? this._defaultOptions.content;
1660
1700
  const styledText = typeof content === "string" ? stringToStyledText(content) : content;
1661
1701
  this._text = styledText;
1702
+ this._hasManualStyledText = !!options.content;
1662
1703
  this._defaultFg = parseColor(options.fg ?? this._defaultOptions.fg);
1663
1704
  this._defaultBg = parseColor(options.bg ?? this._defaultOptions.bg);
1664
1705
  this._defaultAttributes = options.attributes ?? this._defaultOptions.attributes;
1665
1706
  this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : this._defaultOptions.selectionBg;
1666
1707
  this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : this._defaultOptions.selectionFg;
1667
1708
  this.selectable = options.selectable ?? this._defaultOptions.selectable;
1668
- this.textBuffer = TextBuffer.create(64, this._ctx.widthMethod);
1709
+ this._wrap = options.wrap ?? this._defaultOptions.wrap;
1710
+ this._wrapMode = options.wrapMode ?? this._defaultOptions.wrapMode;
1711
+ this.textBuffer = TextBuffer.create(this._ctx.widthMethod);
1712
+ this.textBuffer.setWrapMode(this._wrapMode);
1713
+ this.setupMeasureFunc();
1669
1714
  this.textBuffer.setDefaultFg(this._defaultFg);
1670
1715
  this.textBuffer.setDefaultBg(this._defaultBg);
1671
1716
  this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1672
- this.setupMeasureFunc();
1673
1717
  this.rootTextNode = new RootTextNodeRenderable(ctx, {
1674
1718
  id: `${this.id}-root`,
1675
1719
  fg: this._defaultFg,
1676
1720
  bg: this._defaultBg,
1677
1721
  attributes: this._defaultAttributes
1678
- });
1722
+ }, this);
1679
1723
  this.updateTextBuffer(styledText);
1680
1724
  this._text.mount(this);
1725
+ if (this._wrap && this.width > 0) {
1726
+ this.updateWrapWidth(this.width);
1727
+ }
1681
1728
  this.updateTextInfo();
1682
1729
  }
1683
1730
  updateTextBuffer(styledText) {
@@ -1697,7 +1744,11 @@ class TextRenderable extends Renderable {
1697
1744
  get chunks() {
1698
1745
  return this._text.chunks;
1699
1746
  }
1747
+ get textNode() {
1748
+ return this.rootTextNode;
1749
+ }
1700
1750
  set content(value) {
1751
+ this._hasManualStyledText = true;
1701
1752
  const styledText = typeof value === "string" ? stringToStyledText(value) : value;
1702
1753
  if (this._text !== styledText) {
1703
1754
  this._text = styledText;
@@ -1711,9 +1762,11 @@ class TextRenderable extends Renderable {
1711
1762
  }
1712
1763
  set fg(value) {
1713
1764
  const newColor = parseColor(value ?? this._defaultOptions.fg);
1765
+ this.rootTextNode.fg = newColor;
1714
1766
  if (this._defaultFg !== newColor) {
1715
1767
  this._defaultFg = newColor;
1716
1768
  this.textBuffer.setDefaultFg(this._defaultFg);
1769
+ this.rootTextNode.fg = newColor;
1717
1770
  this.requestRender();
1718
1771
  }
1719
1772
  }
@@ -1748,9 +1801,11 @@ class TextRenderable extends Renderable {
1748
1801
  }
1749
1802
  set bg(value) {
1750
1803
  const newColor = parseColor(value ?? this._defaultOptions.bg);
1804
+ this.rootTextNode.bg = newColor;
1751
1805
  if (this._defaultBg !== newColor) {
1752
1806
  this._defaultBg = newColor;
1753
1807
  this.textBuffer.setDefaultBg(this._defaultBg);
1808
+ this.rootTextNode.bg = newColor;
1754
1809
  this.requestRender();
1755
1810
  }
1756
1811
  }
@@ -1761,6 +1816,27 @@ class TextRenderable extends Renderable {
1761
1816
  if (this._defaultAttributes !== value) {
1762
1817
  this._defaultAttributes = value;
1763
1818
  this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1819
+ this.rootTextNode.attributes = value;
1820
+ this.requestRender();
1821
+ }
1822
+ }
1823
+ get wrap() {
1824
+ return this._wrap;
1825
+ }
1826
+ set wrap(value) {
1827
+ if (this._wrap !== value) {
1828
+ this._wrap = value;
1829
+ this.textBuffer.setWrapWidth(this._wrap ? this.width : null);
1830
+ this.requestRender();
1831
+ }
1832
+ }
1833
+ get wrapMode() {
1834
+ return this._wrapMode;
1835
+ }
1836
+ set wrapMode(value) {
1837
+ if (this._wrapMode !== value) {
1838
+ this._wrapMode = value;
1839
+ this.textBuffer.setWrapMode(this._wrapMode);
1764
1840
  this.requestRender();
1765
1841
  }
1766
1842
  }
@@ -1780,40 +1856,42 @@ class TextRenderable extends Renderable {
1780
1856
  return this.textBuffer.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
1781
1857
  }
1782
1858
  updateTextInfo() {
1783
- const lineInfo = this.textBuffer.lineInfo;
1784
- this._lineInfo.lineStarts = lineInfo.lineStarts;
1785
- this._lineInfo.lineWidths = lineInfo.lineWidths;
1786
1859
  if (this.lastLocalSelection) {
1787
1860
  const changed = this.updateLocalSelection(this.lastLocalSelection);
1788
1861
  if (changed) {
1789
1862
  this.requestRender();
1790
1863
  }
1791
1864
  }
1792
- this.layoutNode.yogaNode.markDirty();
1865
+ this.yogaNode.markDirty();
1793
1866
  this.requestRender();
1794
1867
  }
1868
+ updateLineInfo() {
1869
+ const lineInfo = this.textBuffer.lineInfo;
1870
+ this._lineInfo.lineStarts = lineInfo.lineStarts;
1871
+ this._lineInfo.lineWidths = lineInfo.lineWidths;
1872
+ this._lineInfo.maxLineWidth = lineInfo.maxLineWidth;
1873
+ }
1874
+ updateWrapWidth(width) {
1875
+ this.textBuffer.setWrapWidth(width);
1876
+ this.updateLineInfo();
1877
+ }
1795
1878
  setupMeasureFunc() {
1796
1879
  const measureFunc = (width, widthMode, height, heightMode) => {
1797
- const maxLineWidth = Math.max(...this._lineInfo.lineWidths, 0);
1798
- const numLines = this._lineInfo.lineStarts.length || 1;
1799
- let measuredWidth = maxLineWidth;
1800
- let measuredHeight = numLines;
1801
- if (widthMode === MeasureMode.Exactly) {
1802
- measuredWidth = width;
1803
- } else if (widthMode === MeasureMode.AtMost) {
1804
- measuredWidth = Math.min(maxLineWidth, width);
1805
- }
1806
- if (heightMode === MeasureMode.Exactly) {
1807
- measuredHeight = height;
1808
- } else if (heightMode === MeasureMode.AtMost) {
1809
- measuredHeight = Math.min(numLines, height);
1880
+ if (this._wrap) {
1881
+ if (this.width !== width) {
1882
+ this.updateWrapWidth(width);
1883
+ }
1884
+ } else {
1885
+ this.updateLineInfo();
1810
1886
  }
1887
+ const measuredWidth = this._lineInfo.maxLineWidth;
1888
+ const measuredHeight = this._lineInfo.lineStarts.length;
1811
1889
  return {
1812
1890
  width: Math.max(1, measuredWidth),
1813
1891
  height: Math.max(1, measuredHeight)
1814
1892
  };
1815
1893
  };
1816
- this.layoutNode.yogaNode.setMeasureFunc(measureFunc);
1894
+ this.yogaNode.setMeasureFunc(measureFunc);
1817
1895
  }
1818
1896
  insertChunk(chunk, index) {
1819
1897
  this.textBuffer.insertChunkGroup(index ?? this.textBuffer.chunkGroupCount, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
@@ -1837,14 +1915,15 @@ class TextRenderable extends Renderable {
1837
1915
  this.clearChunks(this._text);
1838
1916
  }
1839
1917
  updateTextFromNodes() {
1840
- if (this.rootTextNode.isDirty) {
1918
+ if (this.rootTextNode.isDirty && !this._hasManualStyledText) {
1841
1919
  const chunks = this.rootTextNode.gatherWithInheritedStyle({
1842
1920
  fg: this._defaultFg,
1843
1921
  bg: this._defaultBg,
1844
1922
  attributes: this._defaultAttributes
1845
1923
  });
1846
1924
  this.textBuffer.setStyledText(new StyledText(chunks));
1847
- this.updateTextInfo();
1925
+ this.updateLineInfo();
1926
+ this.yogaNode.markDirty();
1848
1927
  }
1849
1928
  }
1850
1929
  add(obj, index) {
@@ -1860,6 +1939,9 @@ class TextRenderable extends Renderable {
1860
1939
  this.rootTextNode.insertBefore(obj, anchor);
1861
1940
  return this.rootTextNode.children.indexOf(obj);
1862
1941
  }
1942
+ getTextChildren() {
1943
+ return this.rootTextNode.getChildren();
1944
+ }
1863
1945
  clear() {
1864
1946
  this.rootTextNode.clear();
1865
1947
  const emptyStyledText = stringToStyledText("");
@@ -1894,9 +1976,9 @@ class TextRenderable extends Renderable {
1894
1976
  getSelection() {
1895
1977
  return this.textBuffer.getSelection();
1896
1978
  }
1897
- onUpdate(deltaTime) {
1979
+ onLifecyclePass = () => {
1898
1980
  this.updateTextFromNodes();
1899
- }
1981
+ };
1900
1982
  render(buffer, deltaTime) {
1901
1983
  if (!this.visible)
1902
1984
  return;
@@ -1937,6 +2019,7 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1937
2019
  const text = options.text || "";
1938
2020
  const measurements = measureText({ text, font });
1939
2021
  super(ctx, {
2022
+ flexShrink: 0,
1940
2023
  ...options,
1941
2024
  width: measurements.width || 1,
1942
2025
  height: measurements.height || 1,
@@ -2030,6 +2113,8 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
2030
2113
  this.renderFontToBuffer();
2031
2114
  }
2032
2115
  renderFontToBuffer() {
2116
+ if (this.isDestroyed)
2117
+ return;
2033
2118
  this.frameBuffer.clear(this._bg);
2034
2119
  renderFontToFrameBuffer(this.frameBuffer, {
2035
2120
  text: this._text,
@@ -2976,43 +3061,71 @@ var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
2976
3061
 
2977
3062
  class SliderRenderable extends Renderable {
2978
3063
  orientation;
2979
- _thumbSize;
2980
- _thumbPosition;
3064
+ _value;
3065
+ _min;
3066
+ _max;
3067
+ _viewPortSize;
2981
3068
  _backgroundColor;
2982
3069
  _foregroundColor;
2983
3070
  _onChange;
2984
3071
  constructor(ctx, options) {
2985
- super(ctx, options);
3072
+ super(ctx, { flexShrink: 0, ...options });
2986
3073
  this.orientation = options.orientation;
2987
- this._thumbSize = options.thumbSize ?? 1;
2988
- this._thumbPosition = options.thumbPosition ?? 0;
3074
+ this._min = options.min ?? 0;
3075
+ this._max = options.max ?? 100;
3076
+ this._value = options.value ?? this._min;
3077
+ this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
2989
3078
  this._onChange = options.onChange;
2990
3079
  this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
2991
3080
  this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
2992
3081
  this.setupMouseHandling();
2993
3082
  }
2994
- get thumbSize() {
2995
- return this._thumbSize;
3083
+ get value() {
3084
+ return this._value;
2996
3085
  }
2997
- set thumbSize(value) {
2998
- const clamped = Math.max(1, Math.min(value, this.orientation === "vertical" ? this.height : this.width));
2999
- if (clamped !== this._thumbSize) {
3000
- this._thumbSize = clamped;
3086
+ set value(newValue) {
3087
+ const clamped = Math.max(this._min, Math.min(this._max, newValue));
3088
+ if (clamped !== this._value) {
3089
+ this._value = clamped;
3090
+ this._onChange?.(clamped);
3091
+ this.emit("change", { value: clamped });
3001
3092
  this.requestRender();
3002
3093
  }
3003
3094
  }
3004
- get thumbPosition() {
3005
- return this._thumbPosition;
3095
+ get min() {
3096
+ return this._min;
3006
3097
  }
3007
- set thumbPosition(value) {
3008
- const clamped = Math.max(0, Math.min(1, value));
3009
- if (clamped !== this._thumbPosition) {
3010
- this._thumbPosition = clamped;
3011
- this._onChange?.(clamped);
3012
- this.emit("change", { position: clamped });
3098
+ set min(newMin) {
3099
+ if (newMin !== this._min) {
3100
+ this._min = newMin;
3101
+ if (this._value < newMin) {
3102
+ this.value = newMin;
3103
+ }
3013
3104
  this.requestRender();
3014
3105
  }
3015
3106
  }
3107
+ get max() {
3108
+ return this._max;
3109
+ }
3110
+ set max(newMax) {
3111
+ if (newMax !== this._max) {
3112
+ this._max = newMax;
3113
+ if (this._value > newMax) {
3114
+ this.value = newMax;
3115
+ }
3116
+ this.requestRender();
3117
+ }
3118
+ }
3119
+ set viewPortSize(size) {
3120
+ const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
3121
+ if (clampedSize !== this._viewPortSize) {
3122
+ this._viewPortSize = clampedSize;
3123
+ this.requestRender();
3124
+ }
3125
+ }
3126
+ get viewPortSize() {
3127
+ return this._viewPortSize;
3128
+ }
3016
3129
  get backgroundColor() {
3017
3130
  return this._backgroundColor;
3018
3131
  }
@@ -3027,69 +3140,183 @@ class SliderRenderable extends Renderable {
3027
3140
  this._foregroundColor = parseColor(value);
3028
3141
  this.requestRender();
3029
3142
  }
3143
+ calculateDragOffsetVirtual(event) {
3144
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
3145
+ const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
3146
+ const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
3147
+ const virtualThumbStart = this.getVirtualThumbStart();
3148
+ const virtualThumbSize = this.getVirtualThumbSize();
3149
+ return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
3150
+ }
3030
3151
  setupMouseHandling() {
3031
3152
  let isDragging = false;
3032
- let relativeStartPos = 0;
3153
+ let dragOffsetVirtual = 0;
3033
3154
  this.onMouseDown = (event) => {
3034
3155
  event.stopPropagation();
3035
3156
  event.preventDefault();
3036
- isDragging = true;
3037
- const thumbRect = this.getThumbRect();
3038
- const isOnThumb = event.x >= thumbRect.x && event.x < thumbRect.x + thumbRect.width && event.y >= thumbRect.y && event.y < thumbRect.y + thumbRect.height;
3039
- if (isOnThumb) {
3040
- relativeStartPos = this.orientation === "vertical" ? event.y - thumbRect.y : event.x - thumbRect.x;
3157
+ const thumb = this.getThumbRect();
3158
+ const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
3159
+ if (inThumb) {
3160
+ isDragging = true;
3161
+ dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
3041
3162
  } else {
3042
- relativeStartPos = this.orientation === "vertical" ? thumbRect.height / 2 : thumbRect.width / 2;
3163
+ this.updateValueFromMouseDirect(event);
3164
+ isDragging = true;
3165
+ dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
3043
3166
  }
3044
- this.updatePositionFromMouse(event, relativeStartPos);
3045
3167
  };
3046
3168
  this.onMouseDrag = (event) => {
3047
3169
  if (!isDragging)
3048
3170
  return;
3049
3171
  event.stopPropagation();
3050
- this.updatePositionFromMouse(event, relativeStartPos);
3172
+ this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
3051
3173
  };
3052
- this.onMouseUp = () => {
3174
+ this.onMouseUp = (event) => {
3175
+ if (isDragging) {
3176
+ this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
3177
+ }
3053
3178
  isDragging = false;
3054
3179
  };
3055
3180
  }
3056
- updatePositionFromMouse(event, relativeStartPos) {
3181
+ updateValueFromMouseDirect(event) {
3057
3182
  const trackStart = this.orientation === "vertical" ? this.y : this.x;
3058
3183
  const trackSize = this.orientation === "vertical" ? this.height : this.width;
3059
3184
  const mousePos = this.orientation === "vertical" ? event.y : event.x;
3060
- const thumbStartPos = mousePos - trackStart - relativeStartPos;
3061
- const maxThumbStartPos = trackSize - this._thumbSize;
3062
- const clampedThumbStartPos = Math.max(0, Math.min(maxThumbStartPos, thumbStartPos));
3063
- const newPosition = maxThumbStartPos > 0 ? clampedThumbStartPos / maxThumbStartPos : 0;
3064
- this.thumbPosition = newPosition;
3065
- }
3066
- getThumbPosition() {
3185
+ const relativeMousePos = mousePos - trackStart;
3186
+ const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
3187
+ const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
3188
+ const range = this._max - this._min;
3189
+ const newValue = this._min + ratio * range;
3190
+ this.value = newValue;
3191
+ }
3192
+ updateValueFromMouseWithOffset(event, offsetVirtual) {
3193
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
3067
3194
  const trackSize = this.orientation === "vertical" ? this.height : this.width;
3068
- const maxPos = trackSize - this._thumbSize;
3069
- return Math.round(this._thumbPosition * maxPos);
3195
+ const mousePos = this.orientation === "vertical" ? event.y : event.x;
3196
+ const virtualTrackSize = trackSize * 2;
3197
+ const relativeMousePos = mousePos - trackStart;
3198
+ const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
3199
+ const virtualMousePos = clampedMousePos * 2;
3200
+ const virtualThumbSize = this.getVirtualThumbSize();
3201
+ const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
3202
+ let desiredThumbStart = virtualMousePos - offsetVirtual;
3203
+ desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
3204
+ const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
3205
+ const range = this._max - this._min;
3206
+ const newValue = this._min + ratio * range;
3207
+ this.value = newValue;
3070
3208
  }
3071
3209
  getThumbRect() {
3072
- const thumbPos = this.getThumbPosition();
3210
+ const virtualThumbSize = this.getVirtualThumbSize();
3211
+ const virtualThumbStart = this.getVirtualThumbStart();
3212
+ const realThumbStart = Math.floor(virtualThumbStart / 2);
3213
+ const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
3073
3214
  if (this.orientation === "vertical") {
3074
3215
  return {
3075
3216
  x: this.x,
3076
- y: this.y + thumbPos,
3217
+ y: this.y + realThumbStart,
3077
3218
  width: this.width,
3078
- height: this._thumbSize
3219
+ height: Math.max(1, realThumbSize)
3079
3220
  };
3080
3221
  } else {
3081
3222
  return {
3082
- x: this.x + thumbPos,
3223
+ x: this.x + realThumbStart,
3083
3224
  y: this.y,
3084
- width: this._thumbSize,
3225
+ width: Math.max(1, realThumbSize),
3085
3226
  height: this.height
3086
3227
  };
3087
3228
  }
3088
3229
  }
3089
3230
  renderSelf(buffer) {
3231
+ if (this.orientation === "horizontal") {
3232
+ this.renderHorizontal(buffer);
3233
+ } else {
3234
+ this.renderVertical(buffer);
3235
+ }
3236
+ }
3237
+ renderHorizontal(buffer) {
3238
+ const virtualThumbSize = this.getVirtualThumbSize();
3239
+ const virtualThumbStart = this.getVirtualThumbStart();
3240
+ const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
3241
+ buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
3242
+ const realStartCell = Math.floor(virtualThumbStart / 2);
3243
+ const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
3244
+ const startX = Math.max(0, realStartCell);
3245
+ const endX = Math.min(this.width - 1, realEndCell);
3246
+ for (let realX = startX;realX <= endX; realX++) {
3247
+ const virtualCellStart = realX * 2;
3248
+ const virtualCellEnd = virtualCellStart + 2;
3249
+ const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
3250
+ const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
3251
+ const coverage = thumbEndInCell - thumbStartInCell;
3252
+ let char = " ";
3253
+ if (coverage >= 2) {
3254
+ char = "\u2588";
3255
+ } else {
3256
+ const isLeftHalf = thumbStartInCell === virtualCellStart;
3257
+ if (isLeftHalf) {
3258
+ char = "\u258C";
3259
+ } else {
3260
+ char = "\u2590";
3261
+ }
3262
+ }
3263
+ for (let y = 0;y < this.height; y++) {
3264
+ buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
3265
+ }
3266
+ }
3267
+ }
3268
+ renderVertical(buffer) {
3269
+ const virtualThumbSize = this.getVirtualThumbSize();
3270
+ const virtualThumbStart = this.getVirtualThumbStart();
3271
+ const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
3090
3272
  buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
3091
- const thumbRect = this.getThumbRect();
3092
- buffer.fillRect(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, this._foregroundColor);
3273
+ const realStartCell = Math.floor(virtualThumbStart / 2);
3274
+ const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
3275
+ const startY = Math.max(0, realStartCell);
3276
+ const endY = Math.min(this.height - 1, realEndCell);
3277
+ for (let realY = startY;realY <= endY; realY++) {
3278
+ const virtualCellStart = realY * 2;
3279
+ const virtualCellEnd = virtualCellStart + 2;
3280
+ const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
3281
+ const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
3282
+ const coverage = thumbEndInCell - thumbStartInCell;
3283
+ let char = " ";
3284
+ if (coverage >= 2) {
3285
+ char = "\u2588";
3286
+ } else if (coverage > 0) {
3287
+ const virtualPositionInCell = thumbStartInCell - virtualCellStart;
3288
+ if (virtualPositionInCell === 0) {
3289
+ char = "\u2580";
3290
+ } else {
3291
+ char = "\u2584";
3292
+ }
3293
+ }
3294
+ for (let x = 0;x < this.width; x++) {
3295
+ buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
3296
+ }
3297
+ }
3298
+ }
3299
+ getVirtualThumbSize() {
3300
+ const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
3301
+ const range = this._max - this._min;
3302
+ if (range === 0)
3303
+ return virtualTrackSize;
3304
+ const viewportSize = Math.max(1, this._viewPortSize);
3305
+ const contentSize = range + viewportSize;
3306
+ if (contentSize <= viewportSize)
3307
+ return virtualTrackSize;
3308
+ const thumbRatio = viewportSize / contentSize;
3309
+ const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
3310
+ return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
3311
+ }
3312
+ getVirtualThumbStart() {
3313
+ const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
3314
+ const range = this._max - this._min;
3315
+ if (range === 0)
3316
+ return 0;
3317
+ const valueRatio = (this._value - this._min) / range;
3318
+ const virtualThumbSize = this.getVirtualThumbSize();
3319
+ return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
3093
3320
  }
3094
3321
  }
3095
3322
 
@@ -3132,6 +3359,7 @@ class ScrollBarRenderable extends Renderable {
3132
3359
  return;
3133
3360
  this._scrollSize = value;
3134
3361
  this.recalculateVisibility();
3362
+ this.updateSliderFromScrollState();
3135
3363
  this.scrollPosition = this.scrollPosition;
3136
3364
  }
3137
3365
  set scrollPosition(value) {
@@ -3139,15 +3367,15 @@ class ScrollBarRenderable extends Renderable {
3139
3367
  if (newPosition !== this._scrollPosition) {
3140
3368
  this._scrollPosition = newPosition;
3141
3369
  this.updateSliderFromScrollState();
3142
- this._onChange?.(newPosition);
3143
- this.emit("change", { position: newPosition });
3144
3370
  }
3145
3371
  }
3146
3372
  set viewportSize(value) {
3147
3373
  if (value === this.viewportSize)
3148
3374
  return;
3149
3375
  this._viewportSize = value;
3376
+ this.slider.viewPortSize = Math.max(1, this._viewportSize);
3150
3377
  this.recalculateVisibility();
3378
+ this.updateSliderFromScrollState();
3151
3379
  this.scrollPosition = this.scrollPosition;
3152
3380
  }
3153
3381
  get showArrows() {
@@ -3170,16 +3398,22 @@ class ScrollBarRenderable extends Renderable {
3170
3398
  this._onChange = options.onChange;
3171
3399
  this.orientation = orientation;
3172
3400
  this._showArrows = showArrows;
3401
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
3402
+ const defaultStepSize = Math.max(1, this._viewportSize);
3403
+ const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
3173
3404
  this.slider = new SliderRenderable(ctx, {
3174
3405
  orientation,
3175
- onChange: (position) => {
3176
- const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
3177
- this._scrollPosition = Math.round(position * scrollRange);
3406
+ min: 0,
3407
+ max: scrollRange,
3408
+ value: this._scrollPosition,
3409
+ viewPortSize: stepSize,
3410
+ onChange: (value) => {
3411
+ this._scrollPosition = Math.round(value);
3178
3412
  this._onChange?.(this._scrollPosition);
3179
3413
  this.emit("change", { position: this._scrollPosition });
3180
3414
  },
3181
3415
  ...orientation === "vertical" ? {
3182
- width: 2,
3416
+ width: Math.max(1, Math.min(2, this.width)),
3183
3417
  height: "100%",
3184
3418
  marginLeft: "auto"
3185
3419
  } : {
@@ -3258,17 +3492,10 @@ class ScrollBarRenderable extends Renderable {
3258
3492
  this.requestRender();
3259
3493
  }
3260
3494
  updateSliderFromScrollState() {
3261
- const trackSize = this.orientation === "vertical" ? this.slider.height : this.slider.width;
3262
3495
  const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
3263
- if (scrollRange === 0) {
3264
- this.slider.thumbSize = trackSize;
3265
- this.slider.thumbPosition = 0;
3266
- } else {
3267
- const sizeRatio = this._viewportSize / this._scrollSize;
3268
- this.slider.thumbSize = Math.max(1, Math.round(sizeRatio * trackSize));
3269
- const positionRatio = this._scrollPosition / scrollRange;
3270
- this.slider.thumbPosition = Math.max(0, Math.min(1, positionRatio));
3271
- }
3496
+ this.slider.min = 0;
3497
+ this.slider.max = scrollRange;
3498
+ this.slider.value = Math.min(this._scrollPosition, scrollRange);
3272
3499
  }
3273
3500
  scrollBy(delta, unit = "absolute") {
3274
3501
  const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
@@ -3338,10 +3565,10 @@ class ArrowRenderable extends Renderable {
3338
3565
  this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
3339
3566
  this._attributes = options.attributes ?? 0;
3340
3567
  this._arrowChars = {
3341
- up: "\u25E2\u25E3",
3342
- down: "\u25E5\u25E4",
3343
- left: " \u25C0 ",
3344
- right: " \u25B6 ",
3568
+ up: "\u25B2",
3569
+ down: "\u25BC",
3570
+ left: "\u25C0",
3571
+ right: "\u25B6",
3345
3572
  ...options.arrowChars
3346
3573
  };
3347
3574
  if (!options.width) {
@@ -3547,13 +3774,12 @@ class ScrollBoxRenderable extends BoxRenderable {
3547
3774
  horizontalScrollbarOptions,
3548
3775
  stickyScroll = false,
3549
3776
  stickyStart,
3777
+ scrollX = false,
3778
+ scrollY = true,
3550
3779
  ...options
3551
3780
  }) {
3552
3781
  super(ctx, {
3553
- flexShrink: 1,
3554
- flexGrow: 1,
3555
3782
  flexDirection: "row",
3556
- flexWrap: "wrap",
3557
3783
  alignItems: "stretch",
3558
3784
  ...options,
3559
3785
  ...rootOptions
@@ -3564,10 +3790,6 @@ class ScrollBoxRenderable extends BoxRenderable {
3564
3790
  this.wrapper = new BoxRenderable(ctx, {
3565
3791
  flexDirection: "column",
3566
3792
  flexGrow: 1,
3567
- flexShrink: 1,
3568
- flexBasis: "auto",
3569
- maxHeight: "100%",
3570
- maxWidth: "100%",
3571
3793
  ...wrapperOptions,
3572
3794
  id: `scroll-box-wrapper-${this.internalId}`
3573
3795
  });
@@ -3575,11 +3797,7 @@ class ScrollBoxRenderable extends BoxRenderable {
3575
3797
  this.viewport = new BoxRenderable(ctx, {
3576
3798
  flexDirection: "column",
3577
3799
  flexGrow: 1,
3578
- flexShrink: 1,
3579
- flexBasis: "auto",
3580
- maxHeight: "100%",
3581
- maxWidth: "100%",
3582
- overflow: "scroll",
3800
+ overflow: "hidden",
3583
3801
  onSizeChange: () => {
3584
3802
  this.recalculateBarProps();
3585
3803
  },
@@ -3589,8 +3807,9 @@ class ScrollBoxRenderable extends BoxRenderable {
3589
3807
  this.wrapper.add(this.viewport);
3590
3808
  this.content = new ContentRenderable(ctx, this.viewport, {
3591
3809
  alignSelf: "flex-start",
3592
- minWidth: "100%",
3593
- minHeight: "100%",
3810
+ flexShrink: 0,
3811
+ ...scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" },
3812
+ ...scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" },
3594
3813
  onSizeChange: () => {
3595
3814
  this.recalculateBarProps();
3596
3815
  },
@@ -3609,6 +3828,8 @@ class ScrollBoxRenderable extends BoxRenderable {
3609
3828
  orientation: "vertical",
3610
3829
  onChange: (position) => {
3611
3830
  this.content.translateY = -position;
3831
+ this._hasManualScroll = true;
3832
+ this.updateStickyState();
3612
3833
  }
3613
3834
  });
3614
3835
  super.add(this.verticalScrollBar);
@@ -3623,6 +3844,8 @@ class ScrollBoxRenderable extends BoxRenderable {
3623
3844
  orientation: "horizontal",
3624
3845
  onChange: (position) => {
3625
3846
  this.content.translateX = -position;
3847
+ this._hasManualScroll = true;
3848
+ this.updateStickyState();
3626
3849
  }
3627
3850
  });
3628
3851
  this.wrapper.add(this.horizontalScrollBar);
@@ -3689,10 +3912,14 @@ class ScrollBoxRenderable extends BoxRenderable {
3689
3912
  }
3690
3913
  }
3691
3914
  handleKeyPress(key) {
3692
- if (this.verticalScrollBar.handleKeyPress(key))
3915
+ if (this.verticalScrollBar.handleKeyPress(key)) {
3916
+ this._hasManualScroll = true;
3693
3917
  return true;
3694
- if (this.horizontalScrollBar.handleKeyPress(key))
3918
+ }
3919
+ if (this.horizontalScrollBar.handleKeyPress(key)) {
3920
+ this._hasManualScroll = true;
3695
3921
  return true;
3922
+ }
3696
3923
  return false;
3697
3924
  }
3698
3925
  startAutoScroll(mouseX, mouseY) {
@@ -3824,6 +4051,9 @@ class ScrollBoxRenderable extends BoxRenderable {
3824
4051
  }
3825
4052
  }
3826
4053
  }
4054
+ process.nextTick(() => {
4055
+ this.requestRender();
4056
+ });
3827
4057
  }
3828
4058
  set rootOptions(options) {
3829
4059
  Object.assign(this, options);
@@ -3936,6 +4166,7 @@ export {
3936
4166
  wrapWithDelegates,
3937
4167
  white,
3938
4168
  vstyles,
4169
+ visualizeRenderableTree,
3939
4170
  underline,
3940
4171
  t,
3941
4172
  stringToStyledText,
@@ -3945,6 +4176,7 @@ export {
3945
4176
  reverse,
3946
4177
  resolveRenderLib,
3947
4178
  renderFontToFrameBuffer,
4179
+ registerEnvVar,
3948
4180
  red,
3949
4181
  parseWrap,
3950
4182
  parseUnit,
@@ -3968,36 +4200,29 @@ export {
3968
4200
  maybeMakeRenderable,
3969
4201
  magenta,
3970
4202
  italic,
3971
- isValidPercentage,
3972
4203
  isVNode,
3973
4204
  isTextNodeRenderable,
3974
4205
  isStyledText,
3975
- isSizeType,
3976
4206
  isRenderable,
3977
- isPositionTypeType,
3978
- isPositionType,
3979
- isPaddingType,
3980
- isOverflowType,
3981
- isMarginType,
3982
- isFlexBasisType,
3983
- isDimensionType,
3984
4207
  instantiate,
3985
4208
  hsvToRgb,
3986
4209
  hexToRgb,
3987
4210
  hastToStyledText,
3988
4211
  h,
3989
4212
  green,
3990
- getKeyHandler,
3991
4213
  getCharacterPositions,
3992
4214
  getBorderSides,
3993
4215
  getBorderFromSides,
4216
+ generateEnvMarkdown,
4217
+ generateEnvColored,
3994
4218
  fonts,
3995
4219
  fg,
4220
+ envRegistry,
4221
+ env,
3996
4222
  engine,
3997
4223
  dim,
3998
4224
  delegate,
3999
4225
  cyan,
4000
- createTrackedNode,
4001
4226
  createTimeline,
4002
4227
  createTextAttributes,
4003
4228
  createCliRenderer,
@@ -4033,9 +4258,9 @@ export {
4033
4258
  applyGrayscale,
4034
4259
  applyChromaticAberration,
4035
4260
  applyAsciiArt,
4261
+ exports_src as Yoga,
4036
4262
  VignetteEffect,
4037
4263
  VRenderable,
4038
- TrackedNode,
4039
4264
  Timeline,
4040
4265
  TextRenderable,
4041
4266
  TextNodeRenderable,
@@ -4091,5 +4316,5 @@ export {
4091
4316
  ASCIIFont
4092
4317
  };
4093
4318
 
4094
- //# debugId=E4AC186230704DBA64756E2164756E21
4319
+ //# debugId=9C915E7AEEBC51B664756E2164756E21
4095
4320
  //# sourceMappingURL=index.js.map