@opentui/core 0.1.13 → 0.1.15

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.
@@ -3867,7 +3867,7 @@ function renderFontToFrameBuffer(buffer, {
3867
3867
  if (renderX >= 0 && renderX < width) {
3868
3868
  const fontChar = segment.text[charIdx];
3869
3869
  if (fontChar !== " ") {
3870
- buffer.setCell(renderX, renderY, fontChar, segmentColor, bg);
3870
+ buffer.setCellWithAlphaBlending(renderX, renderY, fontChar, segmentColor, bg);
3871
3871
  }
3872
3872
  }
3873
3873
  }
@@ -3942,44 +3942,23 @@ var textEncoder = new TextEncoder;
3942
3942
 
3943
3943
  class StyledText {
3944
3944
  chunks;
3945
- _plainText = "";
3946
3945
  constructor(chunks) {
3947
3946
  this.chunks = chunks;
3948
- for (let i = 0;i < chunks.length; i++) {
3949
- this._plainText += chunks[i].plainText;
3950
- }
3951
- }
3952
- toString() {
3953
- return this._plainText;
3954
3947
  }
3955
- static _createInstance(chunks, plainText) {
3948
+ static _createInstance(chunks) {
3956
3949
  const newInstance = Object.create(StyledText.prototype);
3957
3950
  newInstance.chunks = chunks;
3958
- newInstance._plainText = plainText;
3959
3951
  return newInstance;
3960
3952
  }
3961
- static _chunksToPlainText(chunks) {
3962
- let plainText = "";
3963
- for (const chunk of chunks) {
3964
- plainText += chunk.plainText;
3965
- }
3966
- return plainText;
3967
- }
3968
3953
  insert(chunk, index) {
3969
3954
  const originalLength = this.chunks.length;
3970
3955
  let newChunks;
3971
- let newPlainText;
3972
- if (index === undefined) {
3973
- newChunks = [...this.chunks, chunk];
3974
- newPlainText = this._plainText + chunk.plainText;
3975
- } else if (index === originalLength) {
3956
+ if (index === undefined || index === originalLength) {
3976
3957
  newChunks = [...this.chunks, chunk];
3977
- newPlainText = this._plainText + chunk.plainText;
3978
3958
  } else {
3979
3959
  newChunks = [...this.chunks.slice(0, index), chunk, ...this.chunks.slice(index)];
3980
- newPlainText = StyledText._chunksToPlainText(newChunks);
3981
3960
  }
3982
- return StyledText._createInstance(newChunks, newPlainText);
3961
+ return StyledText._createInstance(newChunks);
3983
3962
  }
3984
3963
  remove(chunk) {
3985
3964
  const originalLength = this.chunks.length;
@@ -3987,30 +3966,24 @@ class StyledText {
3987
3966
  if (index === -1)
3988
3967
  return this;
3989
3968
  let newChunks;
3990
- let newPlainText;
3991
3969
  if (index === originalLength - 1) {
3992
3970
  newChunks = this.chunks.slice(0, -1);
3993
- newPlainText = this._plainText.slice(0, this._plainText.length - chunk.plainText.length);
3994
3971
  } else {
3995
3972
  newChunks = [...this.chunks.slice(0, index), ...this.chunks.slice(index + 1)];
3996
- newPlainText = StyledText._chunksToPlainText(newChunks);
3997
3973
  }
3998
- return StyledText._createInstance(newChunks, newPlainText);
3974
+ return StyledText._createInstance(newChunks);
3999
3975
  }
4000
3976
  replace(chunk, oldChunk) {
4001
3977
  const index = this.chunks.indexOf(oldChunk);
4002
3978
  if (index === -1)
4003
3979
  return this;
4004
3980
  let newChunks;
4005
- let newPlainText;
4006
3981
  if (index === this.chunks.length - 1) {
4007
3982
  newChunks = [...this.chunks.slice(0, -1), chunk];
4008
- newPlainText = this._plainText.slice(0, this._plainText.length - oldChunk.plainText.length) + chunk.plainText;
4009
3983
  } else {
4010
3984
  newChunks = [...this.chunks.slice(0, index), chunk, ...this.chunks.slice(index + 1)];
4011
- newPlainText = StyledText._chunksToPlainText(newChunks);
4012
3985
  }
4013
- return StyledText._createInstance(newChunks, newPlainText);
3986
+ return StyledText._createInstance(newChunks);
4014
3987
  }
4015
3988
  }
4016
3989
  function stringToStyledText(content) {
@@ -4089,8 +4062,6 @@ var fg = (color) => (input) => applyStyle(input, { fg: color });
4089
4062
  var bg = (color) => (input) => applyStyle(input, { bg: color });
4090
4063
  function tn(strings, ...values) {
4091
4064
  const chunks = [];
4092
- let length = 0;
4093
- let plainText = "";
4094
4065
  for (let i = 0;i < strings.length; i++) {
4095
4066
  const raw = strings[i];
4096
4067
  if (raw) {
@@ -4100,14 +4071,10 @@ function tn(strings, ...values) {
4100
4071
  plainText: raw,
4101
4072
  attributes: 0
4102
4073
  });
4103
- length += raw.length;
4104
- plainText += raw;
4105
4074
  }
4106
4075
  const val = values[i];
4107
4076
  if (typeof val === "object" && "__isChunk" in val) {
4108
4077
  chunks.push(val);
4109
- length += val.plainText.length;
4110
- plainText += val.plainText;
4111
4078
  } else if (val !== undefined) {
4112
4079
  const plainTextStr = String(val);
4113
4080
  chunks.push({
@@ -4116,16 +4083,12 @@ function tn(strings, ...values) {
4116
4083
  plainText: plainTextStr,
4117
4084
  attributes: 0
4118
4085
  });
4119
- length += plainTextStr.length;
4120
- plainText += plainTextStr;
4121
4086
  }
4122
4087
  }
4123
4088
  return new StyledText(chunks);
4124
4089
  }
4125
4090
  function t(strings, ...values) {
4126
4091
  let cachedStringChunks = templateCache.get(strings);
4127
- let length = 0;
4128
- let plainText = "";
4129
4092
  if (!cachedStringChunks) {
4130
4093
  cachedStringChunks = [];
4131
4094
  for (let i = 0;i < strings.length; i++) {
@@ -4148,14 +4111,10 @@ function t(strings, ...values) {
4148
4111
  const stringChunk = cachedStringChunks[i];
4149
4112
  if (stringChunk) {
4150
4113
  chunks.push(stringChunk);
4151
- length += stringChunk.plainText.length;
4152
- plainText += stringChunk.plainText;
4153
4114
  }
4154
4115
  const val = values[i];
4155
4116
  if (typeof val === "object" && "__isChunk" in val) {
4156
4117
  chunks.push(val);
4157
- length += val.plainText.length;
4158
- plainText += val.plainText;
4159
4118
  } else if (val !== undefined) {
4160
4119
  const plainTextStr = String(val);
4161
4120
  chunks.push({
@@ -4164,8 +4123,6 @@ function t(strings, ...values) {
4164
4123
  plainText: plainTextStr,
4165
4124
  attributes: 0
4166
4125
  });
4167
- length += plainTextStr.length;
4168
- plainText += plainTextStr;
4169
4126
  }
4170
4127
  }
4171
4128
  return new StyledText(chunks);
@@ -4478,10 +4435,10 @@ function parseWrap(value) {
4478
4435
  class MouseParser {
4479
4436
  mouseButtonsPressed = new Set;
4480
4437
  static SCROLL_DIRECTIONS = {
4481
- 64: "up",
4482
- 65: "down",
4483
- 66: "left",
4484
- 67: "right"
4438
+ 0: "up",
4439
+ 1: "down",
4440
+ 2: "left",
4441
+ 3: "right"
4485
4442
  };
4486
4443
  reset() {
4487
4444
  this.mouseButtonsPressed.clear();
@@ -4492,9 +4449,9 @@ class MouseParser {
4492
4449
  if (sgrMatch) {
4493
4450
  const [, buttonCode, x, y, pressRelease] = sgrMatch;
4494
4451
  const rawButtonCode = parseInt(buttonCode);
4495
- const scrollDirection = MouseParser.SCROLL_DIRECTIONS[rawButtonCode];
4496
- const isScroll = scrollDirection !== undefined;
4497
4452
  const button = rawButtonCode & 3;
4453
+ const isScroll = (rawButtonCode & 64) !== 0;
4454
+ const scrollDirection = !isScroll ? undefined : MouseParser.SCROLL_DIRECTIONS[button];
4498
4455
  const isMotion = (rawButtonCode & 32) !== 0;
4499
4456
  const modifiers = {
4500
4457
  shift: (rawButtonCode & 4) !== 0,
@@ -4539,9 +4496,9 @@ class MouseParser {
4539
4496
  const buttonByte = str.charCodeAt(3) - 32;
4540
4497
  const x = str.charCodeAt(4) - 33;
4541
4498
  const y = str.charCodeAt(5) - 33;
4542
- const scrollDirection = MouseParser.SCROLL_DIRECTIONS[buttonByte];
4543
- const isScroll = scrollDirection !== undefined;
4544
4499
  const button = buttonByte & 3;
4500
+ const isScroll = (buttonByte & 64) !== 0;
4501
+ const scrollDirection = !isScroll ? undefined : MouseParser.SCROLL_DIRECTIONS[button];
4545
4502
  const modifiers = {
4546
4503
  shift: (buttonByte & 4) !== 0,
4547
4504
  alt: (buttonByte & 8) !== 0,
@@ -4575,31 +4532,89 @@ class MouseParser {
4575
4532
  }
4576
4533
 
4577
4534
  // src/lib/selection.ts
4535
+ class SelectionAnchor {
4536
+ renderable;
4537
+ relativeX;
4538
+ relativeY;
4539
+ constructor(renderable, absoluteX, absoluteY) {
4540
+ this.renderable = renderable;
4541
+ this.relativeX = absoluteX - this.renderable.x;
4542
+ this.relativeY = absoluteY - this.renderable.y;
4543
+ }
4544
+ get x() {
4545
+ return this.renderable.x + this.relativeX;
4546
+ }
4547
+ get y() {
4548
+ return this.renderable.y + this.relativeY;
4549
+ }
4550
+ }
4551
+
4578
4552
  class Selection {
4579
4553
  _anchor;
4580
- _focus;
4554
+ _originalFocus;
4555
+ _normalizedAnchor;
4556
+ _normalizedFocus;
4581
4557
  _selectedRenderables = [];
4582
- constructor(anchor, focus) {
4583
- this._anchor = { ...anchor };
4584
- this._focus = { ...focus };
4558
+ _touchedRenderables = [];
4559
+ _isActive = true;
4560
+ _isSelecting = true;
4561
+ constructor(anchorRenderable, anchor, focus) {
4562
+ this._anchor = new SelectionAnchor(anchorRenderable, anchor.x, anchor.y);
4563
+ this._originalFocus = { ...focus };
4564
+ this._updateNormalizedSelection();
4585
4565
  }
4586
4566
  get anchor() {
4587
- return { ...this._anchor };
4567
+ return { ...this._normalizedAnchor };
4588
4568
  }
4589
4569
  get focus() {
4590
- return { ...this._focus };
4570
+ return { ...this._normalizedFocus };
4571
+ }
4572
+ set focus(value) {
4573
+ this._originalFocus = { ...value };
4574
+ this._updateNormalizedSelection();
4575
+ }
4576
+ _updateNormalizedSelection() {
4577
+ const anchorBeforeFocus = this._anchor.y < this._originalFocus.y || this._anchor.y === this._originalFocus.y && this._anchor.x <= this._originalFocus.x;
4578
+ if (anchorBeforeFocus) {
4579
+ this._normalizedAnchor = { x: this._anchor.x, y: this._anchor.y };
4580
+ this._normalizedFocus = { ...this._originalFocus };
4581
+ } else {
4582
+ this._normalizedAnchor = { ...this._originalFocus };
4583
+ this._normalizedFocus = { x: this._anchor.x + 1, y: this._anchor.y };
4584
+ }
4585
+ }
4586
+ get isActive() {
4587
+ return this._isActive;
4588
+ }
4589
+ set isActive(value) {
4590
+ this._isActive = value;
4591
+ }
4592
+ get isSelecting() {
4593
+ return this._isSelecting;
4594
+ }
4595
+ set isSelecting(value) {
4596
+ this._isSelecting = value;
4591
4597
  }
4592
4598
  get bounds() {
4593
4599
  return {
4594
- startX: Math.min(this._anchor.x, this._focus.x),
4595
- startY: Math.min(this._anchor.y, this._focus.y),
4596
- endX: Math.max(this._anchor.x, this._focus.x),
4597
- endY: Math.max(this._anchor.y, this._focus.y)
4600
+ x: Math.min(this._normalizedAnchor.x, this._normalizedFocus.x),
4601
+ y: Math.min(this._normalizedAnchor.y, this._normalizedFocus.y),
4602
+ width: Math.max(this._normalizedAnchor.x, this._normalizedFocus.x) - Math.min(this._normalizedAnchor.x, this._normalizedFocus.x),
4603
+ height: Math.max(this._normalizedAnchor.y, this._normalizedFocus.y) - Math.min(this._normalizedAnchor.y, this._normalizedFocus.y)
4598
4604
  };
4599
4605
  }
4600
4606
  updateSelectedRenderables(selectedRenderables) {
4601
4607
  this._selectedRenderables = selectedRenderables;
4602
4608
  }
4609
+ get selectedRenderables() {
4610
+ return this._selectedRenderables;
4611
+ }
4612
+ updateTouchedRenderables(touchedRenderables) {
4613
+ this._touchedRenderables = touchedRenderables;
4614
+ }
4615
+ get touchedRenderables() {
4616
+ return this._touchedRenderables;
4617
+ }
4603
4618
  getSelectedText() {
4604
4619
  const selectedTexts = this._selectedRenderables.sort((a, b) => {
4605
4620
  const aY = a.y;
@@ -4613,133 +4628,24 @@ class Selection {
4613
4628
  `);
4614
4629
  }
4615
4630
  }
4616
-
4617
- class TextSelectionHelper {
4618
- getX;
4619
- getY;
4620
- getTextLength;
4621
- getLineInfo;
4622
- localSelection = null;
4623
- cachedGlobalSelection = null;
4624
- constructor(getX, getY, getTextLength, getLineInfo) {
4625
- this.getX = getX;
4626
- this.getY = getY;
4627
- this.getTextLength = getTextLength;
4628
- this.getLineInfo = getLineInfo;
4629
- }
4630
- hasSelection() {
4631
- return this.localSelection !== null;
4632
- }
4633
- getSelection() {
4634
- return this.localSelection;
4635
- }
4636
- reevaluateSelection(width, height = 1) {
4637
- if (!this.cachedGlobalSelection) {
4638
- return false;
4639
- }
4640
- return this.onSelectionChanged(this.cachedGlobalSelection, width, height);
4641
- }
4642
- shouldStartSelection(x, y, width, height) {
4643
- const localX = x - this.getX();
4644
- const localY = y - this.getY();
4645
- return localX >= 0 && localX < width && localY >= 0 && localY < height;
4646
- }
4647
- onSelectionChanged(selection, width, height = 1) {
4648
- this.cachedGlobalSelection = selection;
4649
- const previousSelection = this.localSelection;
4650
- if (!selection?.isActive) {
4651
- this.localSelection = null;
4652
- return previousSelection !== null;
4653
- }
4654
- const myY = this.getY();
4655
- const myEndY = myY + height - 1;
4656
- if (myEndY < selection.anchor.y || myY > selection.focus.y) {
4657
- this.localSelection = null;
4658
- return previousSelection !== null;
4659
- }
4660
- if (height === 1) {
4661
- this.localSelection = this.calculateSingleLineSelection(myY, selection.anchor.y, selection.focus.y, selection.anchor.x, selection.focus.x, width);
4662
- } else {
4663
- this.localSelection = this.calculateMultiLineSelection(myY, selection.anchor.y, selection.focus.y, selection.anchor.x, selection.focus.x);
4664
- }
4665
- return this.localSelection !== null !== (previousSelection !== null) || this.localSelection?.start !== previousSelection?.start || this.localSelection?.end !== previousSelection?.end;
4666
- }
4667
- calculateSingleLineSelection(lineY, anchorY, focusY, anchorX, focusX, width) {
4668
- const textLength = this.getTextLength();
4669
- const myX = this.getX();
4670
- if (lineY > anchorY && lineY < focusY) {
4671
- return { start: 0, end: textLength };
4672
- }
4673
- if (lineY === anchorY && lineY === focusY) {
4674
- const start = Math.max(0, Math.min(anchorX - myX, textLength));
4675
- const end = Math.max(0, Math.min(focusX - myX, textLength));
4676
- return start < end ? { start, end } : null;
4677
- }
4678
- if (lineY === anchorY) {
4679
- const start = Math.max(0, Math.min(anchorX - myX, textLength));
4680
- return start < textLength ? { start, end: textLength } : null;
4681
- }
4682
- if (lineY === focusY) {
4683
- const end = Math.max(0, Math.min(focusX - myX, textLength));
4684
- return end > 0 ? { start: 0, end } : null;
4685
- }
4631
+ function convertGlobalToLocalSelection(globalSelection, localX, localY) {
4632
+ if (!globalSelection?.isActive) {
4686
4633
  return null;
4687
4634
  }
4688
- calculateMultiLineSelection(startY, anchorY, focusY, anchorX, focusX) {
4689
- const lineInfo = this.getLineInfo?.();
4690
- if (!lineInfo) {
4691
- return { start: 0, end: this.getTextLength() };
4692
- }
4693
- const myX = this.getX();
4694
- let selectionStart = null;
4695
- let selectionEnd = null;
4696
- for (let i = 0;i < lineInfo.lineStarts.length; i++) {
4697
- const lineY = startY + i;
4698
- if (lineY < anchorY || lineY > focusY)
4699
- continue;
4700
- const lineStart = lineInfo.lineStarts[i];
4701
- const lineEnd = i < lineInfo.lineStarts.length - 1 ? lineInfo.lineStarts[i + 1] - 1 : this.getTextLength();
4702
- const lineWidth = lineInfo.lineWidths[i];
4703
- if (lineY > anchorY && lineY < focusY) {
4704
- if (selectionStart === null)
4705
- selectionStart = lineStart;
4706
- selectionEnd = lineEnd;
4707
- } else if (lineY === anchorY && lineY === focusY) {
4708
- const localStartX = Math.max(0, Math.min(anchorX - myX, lineWidth));
4709
- const localEndX = Math.max(0, Math.min(focusX - myX, lineWidth));
4710
- if (localStartX < localEndX) {
4711
- selectionStart = lineStart + localStartX;
4712
- selectionEnd = lineStart + localEndX;
4713
- }
4714
- } else if (lineY === anchorY) {
4715
- const localStartX = Math.max(0, Math.min(anchorX - myX, lineWidth));
4716
- if (localStartX < lineWidth) {
4717
- selectionStart = lineStart + localStartX;
4718
- selectionEnd = lineEnd;
4719
- }
4720
- } else if (lineY === focusY) {
4721
- const localEndX = Math.max(0, Math.min(focusX - myX, lineWidth));
4722
- if (localEndX > 0) {
4723
- if (selectionStart === null)
4724
- selectionStart = lineStart;
4725
- selectionEnd = lineStart + localEndX;
4726
- }
4727
- }
4728
- }
4729
- return selectionStart !== null && selectionEnd !== null && selectionStart < selectionEnd ? { start: selectionStart, end: selectionEnd } : null;
4730
- }
4635
+ return {
4636
+ anchorX: globalSelection.anchor.x - localX,
4637
+ anchorY: globalSelection.anchor.y - localY,
4638
+ focusX: globalSelection.focus.x - localX,
4639
+ focusY: globalSelection.focus.y - localY,
4640
+ isActive: true
4641
+ };
4731
4642
  }
4732
4643
 
4733
4644
  class ASCIIFontSelectionHelper {
4734
- getX;
4735
- getY;
4736
4645
  getText;
4737
4646
  getFont;
4738
4647
  localSelection = null;
4739
- cachedGlobalSelection = null;
4740
- constructor(getX, getY, getText, getFont) {
4741
- this.getX = getX;
4742
- this.getY = getY;
4648
+ constructor(getText, getFont) {
4743
4649
  this.getText = getText;
4744
4650
  this.getFont = getFont;
4745
4651
  }
@@ -4749,9 +4655,7 @@ class ASCIIFontSelectionHelper {
4749
4655
  getSelection() {
4750
4656
  return this.localSelection;
4751
4657
  }
4752
- shouldStartSelection(x, y, width, height) {
4753
- const localX = x - this.getX();
4754
- const localY = y - this.getY();
4658
+ shouldStartSelection(localX, localY, width, height) {
4755
4659
  if (localX < 0 || localX >= width || localY < 0 || localY >= height) {
4756
4660
  return false;
4757
4661
  }
@@ -4760,49 +4664,36 @@ class ASCIIFontSelectionHelper {
4760
4664
  const charIndex = coordinateToCharacterIndex(localX, text, font);
4761
4665
  return charIndex >= 0 && charIndex <= text.length;
4762
4666
  }
4763
- onSelectionChanged(selection, width, height) {
4764
- this.cachedGlobalSelection = selection;
4667
+ onLocalSelectionChanged(localSelection, width, height) {
4765
4668
  const previousSelection = this.localSelection;
4766
- if (!selection?.isActive) {
4669
+ if (!localSelection?.isActive) {
4767
4670
  this.localSelection = null;
4768
4671
  return previousSelection !== null;
4769
4672
  }
4770
- const myX = this.getX();
4771
- const myY = this.getY();
4772
- const myEndY = myY + height - 1;
4773
4673
  const text = this.getText();
4774
4674
  const font = this.getFont();
4775
- let selStart;
4776
- let selEnd;
4777
- if (selection.anchor.y < selection.focus.y || selection.anchor.y === selection.focus.y && selection.anchor.x <= selection.focus.x) {
4778
- selStart = selection.anchor;
4779
- selEnd = selection.focus;
4780
- } else {
4781
- selStart = selection.focus;
4782
- selEnd = selection.anchor;
4783
- }
4784
- if (myEndY < selStart.y || myY > selEnd.y) {
4675
+ const selStart = { x: localSelection.anchorX, y: localSelection.anchorY };
4676
+ const selEnd = { x: localSelection.focusX, y: localSelection.focusY };
4677
+ if (height - 1 < selStart.y || 0 > selEnd.y) {
4785
4678
  this.localSelection = null;
4786
4679
  return previousSelection !== null;
4787
4680
  }
4788
4681
  let startCharIndex = 0;
4789
4682
  let endCharIndex = text.length;
4790
- if (selStart.y > myEndY) {
4683
+ if (selStart.y > height - 1) {
4791
4684
  this.localSelection = null;
4792
4685
  return previousSelection !== null;
4793
- } else if (selStart.y >= myY && selStart.y <= myEndY) {
4794
- const localX = selStart.x - myX;
4795
- if (localX > 0) {
4796
- startCharIndex = coordinateToCharacterIndex(localX, text, font);
4686
+ } else if (selStart.y >= 0 && selStart.y <= height - 1) {
4687
+ if (selStart.x > 0) {
4688
+ startCharIndex = coordinateToCharacterIndex(selStart.x, text, font);
4797
4689
  }
4798
4690
  }
4799
- if (selEnd.y < myY) {
4691
+ if (selEnd.y < 0) {
4800
4692
  this.localSelection = null;
4801
4693
  return previousSelection !== null;
4802
- } else if (selEnd.y >= myY && selEnd.y <= myEndY) {
4803
- const localX = selEnd.x - myX;
4804
- if (localX >= 0) {
4805
- endCharIndex = coordinateToCharacterIndex(localX, text, font);
4694
+ } else if (selEnd.y >= 0 && selEnd.y <= height - 1) {
4695
+ if (selEnd.x >= 0) {
4696
+ endCharIndex = coordinateToCharacterIndex(selEnd.x, text, font);
4806
4697
  } else {
4807
4698
  endCharIndex = 0;
4808
4699
  }
@@ -4812,23 +4703,15 @@ class ASCIIFontSelectionHelper {
4812
4703
  } else {
4813
4704
  this.localSelection = null;
4814
4705
  }
4815
- return this.localSelection !== null !== (previousSelection !== null) || this.localSelection?.start !== previousSelection?.start || this.localSelection?.end !== previousSelection?.end;
4816
- }
4817
- reevaluateSelection(width, height) {
4818
- if (!this.cachedGlobalSelection) {
4819
- return false;
4820
- }
4821
- return this.onSelectionChanged(this.cachedGlobalSelection, width, height);
4706
+ return previousSelection?.start !== this.localSelection?.start || previousSelection?.end !== this.localSelection?.end;
4822
4707
  }
4823
4708
  }
4824
4709
  // src/zig.ts
4825
- import { dlopen, toArrayBuffer } from "bun:ffi";
4710
+ import { dlopen, toArrayBuffer as toArrayBuffer2, JSCallback, ptr } from "bun:ffi";
4826
4711
  import { existsSync } from "fs";
4827
4712
 
4828
4713
  // src/buffer.ts
4829
- function isRGBAWithAlpha(color) {
4830
- return color.a < 1;
4831
- }
4714
+ import { toArrayBuffer } from "bun:ffi";
4832
4715
  function packDrawOptions(border2, shouldFill, titleAlignment) {
4833
4716
  let packed = 0;
4834
4717
  if (border2 === true) {
@@ -4855,56 +4738,48 @@ function packDrawOptions(border2, shouldFill, titleAlignment) {
4855
4738
  packed |= alignment << 5;
4856
4739
  return packed;
4857
4740
  }
4858
- function blendColors(overlay, text) {
4859
- const [overlayR, overlayG, overlayB, overlayA] = overlay.buffer;
4860
- const [textR, textG, textB, textA] = text.buffer;
4861
- if (overlayA === 1) {
4862
- return overlay;
4863
- }
4864
- const alpha = overlayA;
4865
- let perceptualAlpha;
4866
- if (alpha > 0.8) {
4867
- const normalizedHighAlpha = (alpha - 0.8) * 5;
4868
- const curvedHighAlpha = Math.pow(normalizedHighAlpha, 0.2);
4869
- perceptualAlpha = 0.8 + curvedHighAlpha * 0.2;
4870
- } else {
4871
- perceptualAlpha = Math.pow(alpha, 0.9);
4872
- }
4873
- const r = overlayR * perceptualAlpha + textR * (1 - perceptualAlpha);
4874
- const g = overlayG * perceptualAlpha + textG * (1 - perceptualAlpha);
4875
- const b = overlayB * perceptualAlpha + textB * (1 - perceptualAlpha);
4876
- return RGBA.fromValues(r, g, b, textA);
4877
- }
4878
4741
 
4879
4742
  class OptimizedBuffer {
4880
4743
  static fbIdCounter = 0;
4881
4744
  id;
4882
4745
  lib;
4883
4746
  bufferPtr;
4884
- buffer;
4885
4747
  _width;
4886
4748
  _height;
4887
4749
  respectAlpha = false;
4888
- useFFI = true;
4750
+ _rawBuffers = null;
4889
4751
  get ptr() {
4890
4752
  return this.bufferPtr;
4891
4753
  }
4892
- constructor(lib, ptr, buffer, width, height, options) {
4893
- this.id = `fb_${OptimizedBuffer.fbIdCounter++}`;
4754
+ get buffers() {
4755
+ if (this._rawBuffers === null) {
4756
+ const size = this._width * this._height;
4757
+ const charPtr = this.lib.bufferGetCharPtr(this.bufferPtr);
4758
+ const fgPtr = this.lib.bufferGetFgPtr(this.bufferPtr);
4759
+ const bgPtr = this.lib.bufferGetBgPtr(this.bufferPtr);
4760
+ const attributesPtr = this.lib.bufferGetAttributesPtr(this.bufferPtr);
4761
+ this._rawBuffers = {
4762
+ char: new Uint32Array(toArrayBuffer(charPtr, 0, size * 4)),
4763
+ fg: new Float32Array(toArrayBuffer(fgPtr, 0, size * 4 * 4)),
4764
+ bg: new Float32Array(toArrayBuffer(bgPtr, 0, size * 4 * 4)),
4765
+ attributes: new Uint8Array(toArrayBuffer(attributesPtr, 0, size))
4766
+ };
4767
+ }
4768
+ return this._rawBuffers;
4769
+ }
4770
+ constructor(lib, ptr, width, height, options) {
4771
+ this.id = options.id || `fb_${OptimizedBuffer.fbIdCounter++}`;
4894
4772
  this.lib = lib;
4895
4773
  this.respectAlpha = options.respectAlpha || false;
4896
4774
  this._width = width;
4897
4775
  this._height = height;
4898
4776
  this.bufferPtr = ptr;
4899
- this.buffer = buffer;
4900
4777
  }
4901
4778
  static create(width, height, widthMethod, options = {}) {
4902
4779
  const lib = resolveRenderLib();
4903
4780
  const respectAlpha = options.respectAlpha || false;
4904
- return lib.createOptimizedBuffer(width, height, widthMethod, respectAlpha);
4905
- }
4906
- get buffers() {
4907
- return this.buffer;
4781
+ const id = options.id && options.id.trim() !== "" ? options.id : "unnamed buffer";
4782
+ return lib.createOptimizedBuffer(width, height, widthMethod, respectAlpha, id);
4908
4783
  }
4909
4784
  coordsToIndex(x, y) {
4910
4785
  return y * this._width + x;
@@ -4919,91 +4794,21 @@ class OptimizedBuffer {
4919
4794
  this.lib.bufferSetRespectAlpha(this.bufferPtr, respectAlpha);
4920
4795
  this.respectAlpha = respectAlpha;
4921
4796
  }
4922
- clear(bg2 = RGBA.fromValues(0, 0, 0, 1), clearChar = " ") {
4923
- if (this.useFFI) {
4924
- this.clearFFI(bg2);
4925
- } else {
4926
- this.clearLocal(bg2, clearChar);
4927
- }
4797
+ getNativeId() {
4798
+ return this.lib.bufferGetId(this.bufferPtr);
4928
4799
  }
4929
- clearLocal(bg2 = RGBA.fromValues(0, 0, 0, 1), clearChar = " ") {
4930
- this.buffer.char.fill(clearChar.charCodeAt(0));
4931
- this.buffer.attributes.fill(0);
4932
- for (let i = 0;i < this._width * this._height; i++) {
4933
- const index = i * 4;
4934
- this.buffer.fg[index] = 1;
4935
- this.buffer.fg[index + 1] = 1;
4936
- this.buffer.fg[index + 2] = 1;
4937
- this.buffer.fg[index + 3] = 1;
4938
- this.buffer.bg[index] = bg2.r;
4939
- this.buffer.bg[index + 1] = bg2.g;
4940
- this.buffer.bg[index + 2] = bg2.b;
4941
- this.buffer.bg[index + 3] = bg2.a;
4942
- }
4800
+ clear(bg2 = RGBA.fromValues(0, 0, 0, 1)) {
4801
+ this.lib.bufferClear(this.bufferPtr, bg2);
4943
4802
  }
4944
4803
  setCell(x, y, char, fg2, bg2, attributes = 0) {
4945
- if (x < 0 || x >= this._width || y < 0 || y >= this._height)
4946
- return;
4947
- const index = this.coordsToIndex(x, y);
4948
- const colorIndex = index * 4;
4949
- this.buffer.char[index] = char.charCodeAt(0);
4950
- this.buffer.attributes[index] = attributes;
4951
- this.buffer.fg[colorIndex] = fg2.r;
4952
- this.buffer.fg[colorIndex + 1] = fg2.g;
4953
- this.buffer.fg[colorIndex + 2] = fg2.b;
4954
- this.buffer.fg[colorIndex + 3] = fg2.a;
4955
- this.buffer.bg[colorIndex] = bg2.r;
4956
- this.buffer.bg[colorIndex + 1] = bg2.g;
4957
- this.buffer.bg[colorIndex + 2] = bg2.b;
4958
- this.buffer.bg[colorIndex + 3] = bg2.a;
4959
- }
4960
- get(x, y) {
4961
- if (x < 0 || x >= this._width || y < 0 || y >= this._height)
4962
- return null;
4963
- const index = this.coordsToIndex(x, y);
4964
- const colorIndex = index * 4;
4965
- return {
4966
- char: this.buffer.char[index],
4967
- fg: RGBA.fromArray(this.buffer.fg.slice(colorIndex, colorIndex + 4)),
4968
- bg: RGBA.fromArray(this.buffer.bg.slice(colorIndex, colorIndex + 4)),
4969
- attributes: this.buffer.attributes[index]
4970
- };
4804
+ this.lib.bufferSetCell(this.bufferPtr, x, y, char, fg2, bg2, attributes);
4971
4805
  }
4972
4806
  setCellWithAlphaBlending(x, y, char, fg2, bg2, attributes = 0) {
4973
- if (this.useFFI) {
4974
- this.setCellWithAlphaBlendingFFI(x, y, char, fg2, bg2, attributes);
4975
- } else {
4976
- this.setCellWithAlphaBlendingLocal(x, y, char, fg2, bg2, attributes);
4977
- }
4978
- }
4979
- setCellWithAlphaBlendingLocal(x, y, char, fg2, bg2, attributes = 0) {
4980
- if (x < 0 || x >= this._width || y < 0 || y >= this._height)
4981
- return;
4982
- const hasBgAlpha = isRGBAWithAlpha(bg2);
4983
- const hasFgAlpha = isRGBAWithAlpha(fg2);
4984
- if (hasBgAlpha || hasFgAlpha) {
4985
- const destCell = this.get(x, y);
4986
- if (destCell) {
4987
- const blendedBgRgb = hasBgAlpha ? blendColors(bg2, destCell.bg) : bg2;
4988
- const preserveChar = char === " " && destCell.char !== 0 && String.fromCharCode(destCell.char) !== " ";
4989
- const finalChar = preserveChar ? destCell.char : char.charCodeAt(0);
4990
- let finalFg;
4991
- if (preserveChar) {
4992
- finalFg = blendColors(bg2, destCell.fg);
4993
- } else {
4994
- finalFg = hasFgAlpha ? blendColors(fg2, destCell.bg) : fg2;
4995
- }
4996
- const finalAttributes = preserveChar ? destCell.attributes : attributes;
4997
- const finalBg = RGBA.fromValues(blendedBgRgb.r, blendedBgRgb.g, blendedBgRgb.b, bg2.a);
4998
- this.setCell(x, y, String.fromCharCode(finalChar), finalFg, finalBg, finalAttributes);
4999
- return;
5000
- }
5001
- }
5002
- this.setCell(x, y, char, fg2, bg2, attributes);
4807
+ this.lib.bufferSetCellWithAlphaBlending(this.bufferPtr, x, y, char, fg2, bg2, attributes);
5003
4808
  }
5004
4809
  drawText(text, x, y, fg2, bg2, attributes = 0, selection2) {
5005
4810
  if (!selection2) {
5006
- this.drawTextFFI.call(this, text, x, y, fg2, bg2, attributes);
4811
+ this.lib.bufferDrawText(this.bufferPtr, text, x, y, fg2, bg2, attributes);
5007
4812
  return;
5008
4813
  }
5009
4814
  const { start, end } = selection2;
@@ -5019,123 +4824,22 @@ class OptimizedBuffer {
5019
4824
  }
5020
4825
  if (start > 0) {
5021
4826
  const beforeText = text.slice(0, start);
5022
- this.drawTextFFI.call(this, beforeText, x, y, fg2, bg2, attributes);
4827
+ this.lib.bufferDrawText(this.bufferPtr, beforeText, x, y, fg2, bg2, attributes);
5023
4828
  }
5024
4829
  if (end > start) {
5025
4830
  const selectedText = text.slice(start, end);
5026
- this.drawTextFFI.call(this, selectedText, x + start, y, selectionFg, selectionBg, attributes);
4831
+ this.lib.bufferDrawText(this.bufferPtr, selectedText, x + start, y, selectionFg, selectionBg, attributes);
5027
4832
  }
5028
4833
  if (end < text.length) {
5029
4834
  const afterText = text.slice(end);
5030
- this.drawTextFFI.call(this, afterText, x + end, y, fg2, bg2, attributes);
4835
+ this.lib.bufferDrawText(this.bufferPtr, afterText, x + end, y, fg2, bg2, attributes);
5031
4836
  }
5032
4837
  }
5033
4838
  fillRect(x, y, width, height, bg2) {
5034
- if (this.useFFI) {
5035
- this.fillRectFFI(x, y, width, height, bg2);
5036
- } else {
5037
- this.fillRectLocal(x, y, width, height, bg2);
5038
- }
5039
- }
5040
- fillRectLocal(x, y, width, height, bg2) {
5041
- const startX = Math.max(0, x);
5042
- const startY = Math.max(0, y);
5043
- const endX = Math.min(this.width - 1, x + width - 1);
5044
- const endY = Math.min(this.height - 1, y + height - 1);
5045
- if (startX > endX || startY > endY)
5046
- return;
5047
- const hasAlpha = isRGBAWithAlpha(bg2);
5048
- if (hasAlpha) {
5049
- const fg2 = RGBA.fromValues(1, 1, 1, 1);
5050
- for (let fillY = startY;fillY <= endY; fillY++) {
5051
- for (let fillX = startX;fillX <= endX; fillX++) {
5052
- this.setCellWithAlphaBlending(fillX, fillY, " ", fg2, bg2, 0);
5053
- }
5054
- }
5055
- } else {
5056
- for (let fillY = startY;fillY <= endY; fillY++) {
5057
- for (let fillX = startX;fillX <= endX; fillX++) {
5058
- const index = this.coordsToIndex(fillX, fillY);
5059
- const colorIndex = index * 4;
5060
- this.buffer.char[index] = 32;
5061
- this.buffer.attributes[index] = 0;
5062
- this.buffer.fg[colorIndex] = 1;
5063
- this.buffer.fg[colorIndex + 1] = 1;
5064
- this.buffer.fg[colorIndex + 2] = 1;
5065
- this.buffer.fg[colorIndex + 3] = 1;
5066
- this.buffer.bg[colorIndex] = bg2.r;
5067
- this.buffer.bg[colorIndex + 1] = bg2.g;
5068
- this.buffer.bg[colorIndex + 2] = bg2.b;
5069
- this.buffer.bg[colorIndex + 3] = bg2.a;
5070
- }
5071
- }
5072
- }
4839
+ this.lib.bufferFillRect(this.bufferPtr, x, y, width, height, bg2);
5073
4840
  }
5074
4841
  drawFrameBuffer(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight) {
5075
- this.drawFrameBufferFFI(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight);
5076
- }
5077
- drawFrameBufferLocal(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight) {
5078
- const srcX = sourceX ?? 0;
5079
- const srcY = sourceY ?? 0;
5080
- const srcWidth = sourceWidth ?? frameBuffer.width;
5081
- const srcHeight = sourceHeight ?? frameBuffer.height;
5082
- if (srcX >= frameBuffer.width || srcY >= frameBuffer.height)
5083
- return;
5084
- if (srcWidth === 0 || srcHeight === 0)
5085
- return;
5086
- const clampedSrcWidth = Math.min(srcWidth, frameBuffer.width - srcX);
5087
- const clampedSrcHeight = Math.min(srcHeight, frameBuffer.height - srcY);
5088
- const startDestX = Math.max(0, destX);
5089
- const startDestY = Math.max(0, destY);
5090
- const endDestX = Math.min(this._width - 1, destX + clampedSrcWidth - 1);
5091
- const endDestY = Math.min(this._height - 1, destY + clampedSrcHeight - 1);
5092
- if (!frameBuffer.respectAlpha) {
5093
- for (let dY = startDestY;dY <= endDestY; dY++) {
5094
- for (let dX = startDestX;dX <= endDestX; dX++) {
5095
- const relativeDestX = dX - destX;
5096
- const relativeDestY = dY - destY;
5097
- const sX = srcX + relativeDestX;
5098
- const sY = srcY + relativeDestY;
5099
- if (sX >= frameBuffer.width || sY >= frameBuffer.height)
5100
- continue;
5101
- const destIndex = this.coordsToIndex(dX, dY);
5102
- const srcIndex = frameBuffer.coordsToIndex(sX, sY);
5103
- const destColorIndex = destIndex * 4;
5104
- const srcColorIndex = srcIndex * 4;
5105
- this.buffer.char[destIndex] = frameBuffer.buffer.char[srcIndex];
5106
- this.buffer.attributes[destIndex] = frameBuffer.buffer.attributes[srcIndex];
5107
- this.buffer.fg[destColorIndex] = frameBuffer.buffer.fg[srcColorIndex];
5108
- this.buffer.fg[destColorIndex + 1] = frameBuffer.buffer.fg[srcColorIndex + 1];
5109
- this.buffer.fg[destColorIndex + 2] = frameBuffer.buffer.fg[srcColorIndex + 2];
5110
- this.buffer.fg[destColorIndex + 3] = frameBuffer.buffer.fg[srcColorIndex + 3];
5111
- this.buffer.bg[destColorIndex] = frameBuffer.buffer.bg[srcColorIndex];
5112
- this.buffer.bg[destColorIndex + 1] = frameBuffer.buffer.bg[srcColorIndex + 1];
5113
- this.buffer.bg[destColorIndex + 2] = frameBuffer.buffer.bg[srcColorIndex + 2];
5114
- this.buffer.bg[destColorIndex + 3] = frameBuffer.buffer.bg[srcColorIndex + 3];
5115
- }
5116
- }
5117
- return;
5118
- }
5119
- for (let dY = startDestY;dY <= endDestY; dY++) {
5120
- for (let dX = startDestX;dX <= endDestX; dX++) {
5121
- const relativeDestX = dX - destX;
5122
- const relativeDestY = dY - destY;
5123
- const sX = srcX + relativeDestX;
5124
- const sY = srcY + relativeDestY;
5125
- if (sX >= frameBuffer.width || sY >= frameBuffer.height)
5126
- continue;
5127
- const srcIndex = frameBuffer.coordsToIndex(sX, sY);
5128
- const srcColorIndex = srcIndex * 4;
5129
- if (frameBuffer.buffer.bg[srcColorIndex + 3] === 0 && frameBuffer.buffer.fg[srcColorIndex + 3] === 0) {
5130
- continue;
5131
- }
5132
- const charCode = frameBuffer.buffer.char[srcIndex];
5133
- const fg2 = RGBA.fromArray(frameBuffer.buffer.fg.slice(srcColorIndex, srcColorIndex + 4));
5134
- const bg2 = RGBA.fromArray(frameBuffer.buffer.bg.slice(srcColorIndex, srcColorIndex + 4));
5135
- const attributes = frameBuffer.buffer.attributes[srcIndex];
5136
- this.setCellWithAlphaBlending(dX, dY, String.fromCharCode(charCode), fg2, bg2, attributes);
5137
- }
5138
- }
4842
+ this.lib.drawFrameBuffer(this.bufferPtr, destX, destY, frameBuffer.ptr, sourceX, sourceY, sourceWidth, sourceHeight);
5139
4843
  }
5140
4844
  destroy() {
5141
4845
  this.lib.destroyOptimizedBuffer(this.bufferPtr);
@@ -5144,35 +4848,18 @@ class OptimizedBuffer {
5144
4848
  this.lib.bufferDrawTextBuffer(this.bufferPtr, textBuffer.ptr, x, y, clipRect);
5145
4849
  }
5146
4850
  drawSuperSampleBuffer(x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow) {
5147
- this.drawSuperSampleBufferFFI(x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow);
5148
- }
5149
- drawSuperSampleBufferFFI(x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow) {
5150
4851
  this.lib.bufferDrawSuperSampleBuffer(this.bufferPtr, x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow);
5151
4852
  }
5152
4853
  drawPackedBuffer(dataPtr, dataLen, posX, posY, terminalWidthCells, terminalHeightCells) {
5153
4854
  this.lib.bufferDrawPackedBuffer(this.bufferPtr, dataPtr, dataLen, posX, posY, terminalWidthCells, terminalHeightCells);
5154
4855
  }
5155
- setCellWithAlphaBlendingFFI(x, y, char, fg2, bg2, attributes) {
5156
- this.lib.bufferSetCellWithAlphaBlending(this.bufferPtr, x, y, char, fg2, bg2, attributes);
5157
- }
5158
- fillRectFFI(x, y, width, height, bg2) {
5159
- this.lib.bufferFillRect(this.bufferPtr, x, y, width, height, bg2);
5160
- }
5161
4856
  resize(width, height) {
5162
4857
  if (this._width === width && this._height === height)
5163
4858
  return;
5164
4859
  this._width = width;
5165
4860
  this._height = height;
5166
- this.buffer = this.lib.bufferResize(this.bufferPtr, width, height);
5167
- }
5168
- clearFFI(bg2 = RGBA.fromValues(0, 0, 0, 1)) {
5169
- this.lib.bufferClear(this.bufferPtr, bg2);
5170
- }
5171
- drawTextFFI(text, x, y, fg2 = RGBA.fromValues(1, 1, 1, 1), bg2, attributes = 0) {
5172
- this.lib.bufferDrawText(this.bufferPtr, text, x, y, fg2, bg2, attributes);
5173
- }
5174
- drawFrameBufferFFI(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight) {
5175
- this.lib.drawFrameBuffer(this.bufferPtr, destX, destY, frameBuffer.ptr, sourceX, sourceY, sourceWidth, sourceHeight);
4861
+ this._rawBuffers = null;
4862
+ this.lib.bufferResize(this.bufferPtr, width, height);
5176
4863
  }
5177
4864
  drawBox(options) {
5178
4865
  const style = options.borderStyle || "single";
@@ -5180,6 +4867,15 @@ class OptimizedBuffer {
5180
4867
  const packedOptions = packDrawOptions(options.border, options.shouldFill ?? false, options.titleAlignment || "left");
5181
4868
  this.lib.bufferDrawBox(this.bufferPtr, options.x, options.y, options.width, options.height, borderChars, packedOptions, options.borderColor, options.backgroundColor, options.title ?? null);
5182
4869
  }
4870
+ pushScissorRect(x, y, width, height) {
4871
+ this.lib.bufferPushScissorRect(this.bufferPtr, x, y, width, height);
4872
+ }
4873
+ popScissorRect() {
4874
+ this.lib.bufferPopScissorRect(this.bufferPtr);
4875
+ }
4876
+ clearScissorRects() {
4877
+ this.lib.bufferClearScissorRects(this.bufferPtr);
4878
+ }
5183
4879
  }
5184
4880
 
5185
4881
  // src/zig.ts
@@ -5191,6 +4887,10 @@ if (!existsSync(targetLibPath)) {
5191
4887
  function getOpenTUILib(libPath) {
5192
4888
  const resolvedLibPath = libPath || targetLibPath;
5193
4889
  return dlopen(resolvedLibPath, {
4890
+ setLogCallback: {
4891
+ args: ["ptr"],
4892
+ returns: "void"
4893
+ },
5194
4894
  createRenderer: {
5195
4895
  args: ["u32", "u32"],
5196
4896
  returns: "ptr"
@@ -5232,7 +4932,7 @@ function getOpenTUILib(libPath) {
5232
4932
  returns: "ptr"
5233
4933
  },
5234
4934
  createOptimizedBuffer: {
5235
- args: ["u32", "u32", "bool", "u8"],
4935
+ args: ["u32", "u32", "bool", "u8", "ptr", "usize"],
5236
4936
  returns: "ptr"
5237
4937
  },
5238
4938
  destroyOptimizedBuffer: {
@@ -5279,6 +4979,10 @@ function getOpenTUILib(libPath) {
5279
4979
  args: ["ptr", "bool"],
5280
4980
  returns: "void"
5281
4981
  },
4982
+ bufferGetId: {
4983
+ args: ["ptr", "ptr", "usize"],
4984
+ returns: "usize"
4985
+ },
5282
4986
  bufferDrawText: {
5283
4987
  args: ["ptr", "ptr", "u32", "u32", "u32", "ptr", "ptr", "u8"],
5284
4988
  returns: "void"
@@ -5287,6 +4991,10 @@ function getOpenTUILib(libPath) {
5287
4991
  args: ["ptr", "u32", "u32", "u32", "ptr", "ptr", "u8"],
5288
4992
  returns: "void"
5289
4993
  },
4994
+ bufferSetCell: {
4995
+ args: ["ptr", "u32", "u32", "u32", "ptr", "ptr", "u8"],
4996
+ returns: "void"
4997
+ },
5290
4998
  bufferFillRect: {
5291
4999
  args: ["ptr", "u32", "u32", "u32", "u32", "ptr"],
5292
5000
  returns: "void"
@@ -5335,6 +5043,18 @@ function getOpenTUILib(libPath) {
5335
5043
  args: ["ptr", "i32", "i32", "u32", "u32", "ptr", "u32", "ptr", "ptr", "ptr", "u32"],
5336
5044
  returns: "void"
5337
5045
  },
5046
+ bufferPushScissorRect: {
5047
+ args: ["ptr", "i32", "i32", "u32", "u32"],
5048
+ returns: "void"
5049
+ },
5050
+ bufferPopScissorRect: {
5051
+ args: ["ptr"],
5052
+ returns: "void"
5053
+ },
5054
+ bufferClearScissorRects: {
5055
+ args: ["ptr"],
5056
+ returns: "void"
5057
+ },
5338
5058
  addToHitGrid: {
5339
5059
  args: ["ptr", "i32", "i32", "u32", "u32", "u32"],
5340
5060
  returns: "void"
@@ -5387,30 +5107,10 @@ function getOpenTUILib(libPath) {
5387
5107
  args: ["ptr"],
5388
5108
  returns: "ptr"
5389
5109
  },
5390
- textBufferGetFgPtr: {
5391
- args: ["ptr"],
5392
- returns: "ptr"
5393
- },
5394
- textBufferGetBgPtr: {
5395
- args: ["ptr"],
5396
- returns: "ptr"
5397
- },
5398
- textBufferGetAttributesPtr: {
5399
- args: ["ptr"],
5400
- returns: "ptr"
5401
- },
5402
5110
  textBufferGetLength: {
5403
5111
  args: ["ptr"],
5404
5112
  returns: "u32"
5405
5113
  },
5406
- textBufferSetCell: {
5407
- args: ["ptr", "u32", "u32", "ptr", "ptr", "u16"],
5408
- returns: "void"
5409
- },
5410
- textBufferConcat: {
5411
- args: ["ptr", "ptr"],
5412
- returns: "ptr"
5413
- },
5414
5114
  textBufferResize: {
5415
5115
  args: ["ptr", "u32"],
5416
5116
  returns: "void"
@@ -5455,17 +5155,29 @@ function getOpenTUILib(libPath) {
5455
5155
  args: ["ptr"],
5456
5156
  returns: "void"
5457
5157
  },
5458
- textBufferGetLineStartsPtr: {
5158
+ textBufferGetLineCount: {
5459
5159
  args: ["ptr"],
5460
- returns: "ptr"
5160
+ returns: "u32"
5461
5161
  },
5462
- textBufferGetLineWidthsPtr: {
5162
+ textBufferGetLineInfoDirect: {
5163
+ args: ["ptr", "ptr", "ptr"],
5164
+ returns: "void"
5165
+ },
5166
+ textBufferGetSelectionInfo: {
5463
5167
  args: ["ptr"],
5464
- returns: "ptr"
5168
+ returns: "u64"
5465
5169
  },
5466
- textBufferGetLineCount: {
5170
+ textBufferGetSelectedText: {
5171
+ args: ["ptr", "ptr", "usize"],
5172
+ returns: "usize"
5173
+ },
5174
+ textBufferSetLocalSelection: {
5175
+ args: ["ptr", "i32", "i32", "i32", "i32", "ptr", "ptr"],
5176
+ returns: "bool"
5177
+ },
5178
+ textBufferResetLocalSelection: {
5467
5179
  args: ["ptr"],
5468
- returns: "u32"
5180
+ returns: "void"
5469
5181
  },
5470
5182
  bufferDrawTextBuffer: {
5471
5183
  args: ["ptr", "ptr", "i32", "i32", "i32", "i32", "u32", "u32", "bool"],
@@ -5481,12 +5193,67 @@ function getOpenTUILib(libPath) {
5481
5193
  }
5482
5194
  });
5483
5195
  }
5196
+ var LogLevel2;
5197
+ ((LogLevel3) => {
5198
+ LogLevel3[LogLevel3["Error"] = 0] = "Error";
5199
+ LogLevel3[LogLevel3["Warn"] = 1] = "Warn";
5200
+ LogLevel3[LogLevel3["Info"] = 2] = "Info";
5201
+ LogLevel3[LogLevel3["Debug"] = 3] = "Debug";
5202
+ })(LogLevel2 ||= {});
5484
5203
 
5485
5204
  class FFIRenderLib {
5486
5205
  opentui;
5487
5206
  encoder = new TextEncoder;
5207
+ decoder = new TextDecoder;
5208
+ logCallbackWrapper;
5488
5209
  constructor(libPath) {
5489
5210
  this.opentui = getOpenTUILib(libPath);
5211
+ this.setupLogging();
5212
+ }
5213
+ setupLogging() {
5214
+ if (this.logCallbackWrapper) {
5215
+ return;
5216
+ }
5217
+ const logCallback = new JSCallback((level, msgPtr, msgLenBigInt) => {
5218
+ try {
5219
+ const msgLen = typeof msgLenBigInt === "bigint" ? Number(msgLenBigInt) : msgLenBigInt;
5220
+ if (msgLen === 0 || !msgPtr) {
5221
+ return;
5222
+ }
5223
+ const msgBuffer = toArrayBuffer2(msgPtr, 0, msgLen);
5224
+ const msgBytes = new Uint8Array(msgBuffer);
5225
+ const message = this.decoder.decode(msgBytes);
5226
+ switch (level) {
5227
+ case 0 /* Error */:
5228
+ console.error(message);
5229
+ break;
5230
+ case 1 /* Warn */:
5231
+ console.warn(message);
5232
+ break;
5233
+ case 2 /* Info */:
5234
+ console.info(message);
5235
+ break;
5236
+ case 3 /* Debug */:
5237
+ console.debug(message);
5238
+ break;
5239
+ default:
5240
+ console.log(message);
5241
+ }
5242
+ } catch (error) {
5243
+ console.error("Error in Zig log callback:", error);
5244
+ }
5245
+ }, {
5246
+ args: ["u8", "ptr", "usize"],
5247
+ returns: "void"
5248
+ });
5249
+ this.logCallbackWrapper = logCallback;
5250
+ if (!logCallback.ptr) {
5251
+ throw new Error("Failed to create log callback");
5252
+ }
5253
+ this.setLogCallback(logCallback.ptr);
5254
+ }
5255
+ setLogCallback(callbackPtr) {
5256
+ this.opentui.symbols.setLogCallback(callbackPtr);
5490
5257
  }
5491
5258
  createRenderer(width, height) {
5492
5259
  return this.opentui.symbols.createRenderer(width, height);
@@ -5516,9 +5283,7 @@ class FFIRenderLib {
5516
5283
  }
5517
5284
  const width = this.opentui.symbols.getBufferWidth(bufferPtr);
5518
5285
  const height = this.opentui.symbols.getBufferHeight(bufferPtr);
5519
- const size = width * height;
5520
- const buffers = this.getBuffer(bufferPtr, size);
5521
- return new OptimizedBuffer(this, bufferPtr, buffers, width, height, {});
5286
+ return new OptimizedBuffer(this, bufferPtr, width, height, { id: "next buffer" });
5522
5287
  }
5523
5288
  getCurrentBuffer(renderer) {
5524
5289
  const bufferPtr = this.opentui.symbols.getCurrentBuffer(renderer);
@@ -5527,69 +5292,35 @@ class FFIRenderLib {
5527
5292
  }
5528
5293
  const width = this.opentui.symbols.getBufferWidth(bufferPtr);
5529
5294
  const height = this.opentui.symbols.getBufferHeight(bufferPtr);
5530
- const size = width * height;
5531
- const buffers = this.getBuffer(bufferPtr, size);
5532
- return new OptimizedBuffer(this, bufferPtr, buffers, width, height, {});
5533
- }
5534
- getBuffer(bufferPtr, size) {
5535
- const charPtr = this.opentui.symbols.bufferGetCharPtr(bufferPtr);
5536
- const fgPtr = this.opentui.symbols.bufferGetFgPtr(bufferPtr);
5537
- const bgPtr = this.opentui.symbols.bufferGetBgPtr(bufferPtr);
5538
- const attributesPtr = this.opentui.symbols.bufferGetAttributesPtr(bufferPtr);
5539
- if (!charPtr || !fgPtr || !bgPtr || !attributesPtr) {
5540
- throw new Error("Failed to get buffer pointers");
5541
- }
5542
- const buffers = {
5543
- char: new Uint32Array(toArrayBuffer(charPtr, 0, size * 4)),
5544
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, size * 4 * 4)),
5545
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, size * 4 * 4)),
5546
- attributes: new Uint8Array(toArrayBuffer(attributesPtr, 0, size))
5547
- };
5548
- return buffers;
5549
- }
5550
- getTextBuffer(bufferPtr, size) {
5551
- const charPtr = this.opentui.symbols.textBufferGetCharPtr(bufferPtr);
5552
- const fgPtr = this.opentui.symbols.textBufferGetFgPtr(bufferPtr);
5553
- const bgPtr = this.opentui.symbols.textBufferGetBgPtr(bufferPtr);
5554
- const attributesPtr = this.opentui.symbols.textBufferGetAttributesPtr(bufferPtr);
5555
- if (!charPtr || !fgPtr || !bgPtr || !attributesPtr) {
5556
- throw new Error("Failed to get text buffer pointers");
5557
- }
5558
- const buffers = {
5559
- char: new Uint32Array(toArrayBuffer(charPtr, 0, size * 4)),
5560
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, size * 4 * 4)),
5561
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, size * 4 * 4)),
5562
- attributes: new Uint16Array(toArrayBuffer(attributesPtr, 0, size * 2))
5563
- };
5564
- return buffers;
5295
+ return new OptimizedBuffer(this, bufferPtr, width, height, { id: "current buffer" });
5565
5296
  }
5566
5297
  bufferGetCharPtr(buffer) {
5567
- const ptr = this.opentui.symbols.bufferGetCharPtr(buffer);
5568
- if (!ptr) {
5298
+ const ptr2 = this.opentui.symbols.bufferGetCharPtr(buffer);
5299
+ if (!ptr2) {
5569
5300
  throw new Error("Failed to get char pointer");
5570
5301
  }
5571
- return ptr;
5302
+ return ptr2;
5572
5303
  }
5573
5304
  bufferGetFgPtr(buffer) {
5574
- const ptr = this.opentui.symbols.bufferGetFgPtr(buffer);
5575
- if (!ptr) {
5305
+ const ptr2 = this.opentui.symbols.bufferGetFgPtr(buffer);
5306
+ if (!ptr2) {
5576
5307
  throw new Error("Failed to get fg pointer");
5577
5308
  }
5578
- return ptr;
5309
+ return ptr2;
5579
5310
  }
5580
5311
  bufferGetBgPtr(buffer) {
5581
- const ptr = this.opentui.symbols.bufferGetBgPtr(buffer);
5582
- if (!ptr) {
5312
+ const ptr2 = this.opentui.symbols.bufferGetBgPtr(buffer);
5313
+ if (!ptr2) {
5583
5314
  throw new Error("Failed to get bg pointer");
5584
5315
  }
5585
- return ptr;
5316
+ return ptr2;
5586
5317
  }
5587
5318
  bufferGetAttributesPtr(buffer) {
5588
- const ptr = this.opentui.symbols.bufferGetAttributesPtr(buffer);
5589
- if (!ptr) {
5319
+ const ptr2 = this.opentui.symbols.bufferGetAttributesPtr(buffer);
5320
+ if (!ptr2) {
5590
5321
  throw new Error("Failed to get attributes pointer");
5591
5322
  }
5592
- return ptr;
5323
+ return ptr2;
5593
5324
  }
5594
5325
  bufferGetRespectAlpha(buffer) {
5595
5326
  return this.opentui.symbols.bufferGetRespectAlpha(buffer);
@@ -5597,6 +5328,13 @@ class FFIRenderLib {
5597
5328
  bufferSetRespectAlpha(buffer, respectAlpha) {
5598
5329
  this.opentui.symbols.bufferSetRespectAlpha(buffer, respectAlpha);
5599
5330
  }
5331
+ bufferGetId(buffer) {
5332
+ const maxLen = 256;
5333
+ const outBuffer = new Uint8Array(maxLen);
5334
+ const actualLen = this.opentui.symbols.bufferGetId(buffer, outBuffer, maxLen);
5335
+ const len = typeof actualLen === "bigint" ? Number(actualLen) : actualLen;
5336
+ return this.decoder.decode(outBuffer.slice(0, len));
5337
+ }
5600
5338
  getBufferWidth(buffer) {
5601
5339
  return this.opentui.symbols.getBufferWidth(buffer);
5602
5340
  }
@@ -5619,6 +5357,12 @@ class FFIRenderLib {
5619
5357
  const fg2 = color.buffer;
5620
5358
  this.opentui.symbols.bufferSetCellWithAlphaBlending(buffer, x, y, charPtr, fg2, bg2, attributes ?? 0);
5621
5359
  }
5360
+ bufferSetCell(buffer, x, y, char, color, bgColor, attributes) {
5361
+ const charPtr = char.codePointAt(0) ?? " ".codePointAt(0);
5362
+ const bg2 = bgColor.buffer;
5363
+ const fg2 = color.buffer;
5364
+ this.opentui.symbols.bufferSetCell(buffer, x, y, charPtr, fg2, bg2, attributes ?? 0);
5365
+ }
5622
5366
  bufferFillRect(buffer, x, y, width, height, color) {
5623
5367
  const bg2 = color.buffer;
5624
5368
  this.opentui.symbols.bufferFillRect(buffer, x, y, width, height, bg2);
@@ -5638,8 +5382,6 @@ class FFIRenderLib {
5638
5382
  }
5639
5383
  bufferResize(buffer, width, height) {
5640
5384
  this.opentui.symbols.bufferResize(buffer, width, height);
5641
- const buffers = this.getBuffer(buffer, width * height);
5642
- return buffers;
5643
5385
  }
5644
5386
  resizeRenderer(renderer, width, height) {
5645
5387
  this.opentui.symbols.resizeRenderer(renderer, width, height);
@@ -5657,18 +5399,18 @@ class FFIRenderLib {
5657
5399
  render(renderer, force) {
5658
5400
  this.opentui.symbols.render(renderer, force);
5659
5401
  }
5660
- createOptimizedBuffer(width, height, widthMethod, respectAlpha = false) {
5402
+ createOptimizedBuffer(width, height, widthMethod, respectAlpha = false, id) {
5661
5403
  if (Number.isNaN(width) || Number.isNaN(height)) {
5662
5404
  console.error(new Error(`Invalid dimensions for OptimizedBuffer: ${width}x${height}`).stack);
5663
5405
  }
5664
5406
  const widthMethodCode = widthMethod === "wcwidth" ? 0 : 1;
5665
- const bufferPtr = this.opentui.symbols.createOptimizedBuffer(width, height, respectAlpha, widthMethodCode);
5407
+ const idToUse = id || "unnamed buffer";
5408
+ const idBytes = this.encoder.encode(idToUse);
5409
+ const bufferPtr = this.opentui.symbols.createOptimizedBuffer(width, height, respectAlpha, widthMethodCode, idBytes, idBytes.length);
5666
5410
  if (!bufferPtr) {
5667
5411
  throw new Error(`Failed to create optimized buffer: ${width}x${height}`);
5668
5412
  }
5669
- const size = width * height;
5670
- const buffers = this.getBuffer(bufferPtr, size);
5671
- return new OptimizedBuffer(this, bufferPtr, buffers, width, height, { respectAlpha });
5413
+ return new OptimizedBuffer(this, bufferPtr, width, height, { respectAlpha, id });
5672
5414
  }
5673
5415
  destroyOptimizedBuffer(bufferPtr) {
5674
5416
  this.opentui.symbols.destroyOptimizedBuffer(bufferPtr);
@@ -5728,77 +5470,23 @@ class FFIRenderLib {
5728
5470
  if (!bufferPtr) {
5729
5471
  throw new Error(`Failed to create TextBuffer with capacity ${capacity}`);
5730
5472
  }
5731
- const charPtr = this.textBufferGetCharPtr(bufferPtr);
5732
- const fgPtr = this.textBufferGetFgPtr(bufferPtr);
5733
- const bgPtr = this.textBufferGetBgPtr(bufferPtr);
5734
- const attributesPtr = this.textBufferGetAttributesPtr(bufferPtr);
5735
- const buffer = {
5736
- char: new Uint32Array(toArrayBuffer(charPtr, 0, capacity * 4)),
5737
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, capacity * 4 * 4)),
5738
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, capacity * 4 * 4)),
5739
- attributes: new Uint16Array(toArrayBuffer(attributesPtr, 0, capacity * 2))
5740
- };
5741
- return new TextBuffer(this, bufferPtr, buffer, capacity);
5473
+ return new TextBuffer(this, bufferPtr, capacity);
5742
5474
  }
5743
5475
  destroyTextBuffer(buffer) {
5744
5476
  this.opentui.symbols.destroyTextBuffer(buffer);
5745
5477
  }
5746
5478
  textBufferGetCharPtr(buffer) {
5747
- const ptr = this.opentui.symbols.textBufferGetCharPtr(buffer);
5748
- if (!ptr) {
5479
+ const ptr2 = this.opentui.symbols.textBufferGetCharPtr(buffer);
5480
+ if (!ptr2) {
5749
5481
  throw new Error("Failed to get TextBuffer char pointer");
5750
5482
  }
5751
- return ptr;
5752
- }
5753
- textBufferGetFgPtr(buffer) {
5754
- const ptr = this.opentui.symbols.textBufferGetFgPtr(buffer);
5755
- if (!ptr) {
5756
- throw new Error("Failed to get TextBuffer fg pointer");
5757
- }
5758
- return ptr;
5759
- }
5760
- textBufferGetBgPtr(buffer) {
5761
- const ptr = this.opentui.symbols.textBufferGetBgPtr(buffer);
5762
- if (!ptr) {
5763
- throw new Error("Failed to get TextBuffer bg pointer");
5764
- }
5765
- return ptr;
5766
- }
5767
- textBufferGetAttributesPtr(buffer) {
5768
- const ptr = this.opentui.symbols.textBufferGetAttributesPtr(buffer);
5769
- if (!ptr) {
5770
- throw new Error("Failed to get TextBuffer attributes pointer");
5771
- }
5772
- return ptr;
5483
+ return ptr2;
5773
5484
  }
5774
5485
  textBufferGetLength(buffer) {
5775
5486
  return this.opentui.symbols.textBufferGetLength(buffer);
5776
5487
  }
5777
- textBufferSetCell(buffer, index, char, fg2, bg2, attr) {
5778
- this.opentui.symbols.textBufferSetCell(buffer, index, char, fg2, bg2, attr);
5779
- }
5780
- textBufferConcat(buffer1, buffer2) {
5781
- const resultPtr = this.opentui.symbols.textBufferConcat(buffer1, buffer2);
5782
- if (!resultPtr) {
5783
- throw new Error("Failed to concatenate TextBuffers");
5784
- }
5785
- const length = this.textBufferGetLength(resultPtr);
5786
- const charPtr = this.textBufferGetCharPtr(resultPtr);
5787
- const fgPtr = this.textBufferGetFgPtr(resultPtr);
5788
- const bgPtr = this.textBufferGetBgPtr(resultPtr);
5789
- const attributesPtr = this.textBufferGetAttributesPtr(resultPtr);
5790
- const buffer = {
5791
- char: new Uint32Array(toArrayBuffer(charPtr, 0, length * 4)),
5792
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, length * 4 * 4)),
5793
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, length * 4 * 4)),
5794
- attributes: new Uint16Array(toArrayBuffer(attributesPtr, 0, length * 2))
5795
- };
5796
- return new TextBuffer(this, resultPtr, buffer, length);
5797
- }
5798
5488
  textBufferResize(buffer, newLength) {
5799
5489
  this.opentui.symbols.textBufferResize(buffer, newLength);
5800
- const buffers = this.getTextBuffer(buffer, newLength);
5801
- return buffers;
5802
5490
  }
5803
5491
  textBufferReset(buffer) {
5804
5492
  this.opentui.symbols.textBufferReset(buffer);
@@ -5836,24 +5524,56 @@ class FFIRenderLib {
5836
5524
  textBufferFinalizeLineInfo(buffer) {
5837
5525
  this.opentui.symbols.textBufferFinalizeLineInfo(buffer);
5838
5526
  }
5527
+ textBufferGetLineCount(buffer) {
5528
+ return this.opentui.symbols.textBufferGetLineCount(buffer);
5529
+ }
5530
+ textBufferGetLineInfoDirect(buffer, lineStartsPtr, lineWidthsPtr) {
5531
+ this.opentui.symbols.textBufferGetLineInfoDirect(buffer, lineStartsPtr, lineWidthsPtr);
5532
+ }
5533
+ textBufferGetSelection(buffer) {
5534
+ const packedInfo = this.textBufferGetSelectionInfo(buffer);
5535
+ if (packedInfo === 0xffff_ffff_ffff_ffffn) {
5536
+ return null;
5537
+ }
5538
+ const start = Number(packedInfo >> 32n);
5539
+ const end = Number(packedInfo & 0xffff_ffffn);
5540
+ return { start, end };
5541
+ }
5542
+ textBufferGetSelectionInfo(buffer) {
5543
+ return this.opentui.symbols.textBufferGetSelectionInfo(buffer);
5544
+ }
5545
+ textBufferGetSelectedText(buffer, outPtr, maxLen) {
5546
+ const result = this.opentui.symbols.textBufferGetSelectedText(buffer, outPtr, maxLen);
5547
+ return typeof result === "bigint" ? Number(result) : result;
5548
+ }
5549
+ getSelectedTextBytes(buffer, maxLength) {
5550
+ const outBuffer = new Uint8Array(maxLength);
5551
+ const actualLen = this.textBufferGetSelectedText(buffer, ptr(outBuffer), maxLength);
5552
+ if (actualLen === 0) {
5553
+ return null;
5554
+ }
5555
+ return outBuffer.slice(0, actualLen);
5556
+ }
5557
+ textBufferSetLocalSelection(buffer, anchorX, anchorY, focusX, focusY, bgColor, fgColor) {
5558
+ const bg2 = bgColor ? bgColor.buffer : null;
5559
+ const fg2 = fgColor ? fgColor.buffer : null;
5560
+ return this.opentui.symbols.textBufferSetLocalSelection(buffer, anchorX, anchorY, focusX, focusY, bg2, fg2);
5561
+ }
5562
+ textBufferResetLocalSelection(buffer) {
5563
+ this.opentui.symbols.textBufferResetLocalSelection(buffer);
5564
+ }
5839
5565
  textBufferGetLineInfo(buffer) {
5840
- const lineCount = this.opentui.symbols.textBufferGetLineCount(buffer);
5566
+ const lineCount = this.textBufferGetLineCount(buffer);
5841
5567
  if (lineCount === 0) {
5842
5568
  return { lineStarts: [], lineWidths: [] };
5843
5569
  }
5844
- const lineStartsPtr = this.opentui.symbols.textBufferGetLineStartsPtr(buffer);
5845
- const lineWidthsPtr = this.opentui.symbols.textBufferGetLineWidthsPtr(buffer);
5846
- if (!lineStartsPtr || !lineWidthsPtr) {
5847
- return { lineStarts: [], lineWidths: [] };
5848
- }
5849
- const lineStartsArray = new Uint32Array(toArrayBuffer(lineStartsPtr, 0, lineCount * 4));
5850
- const lineWidthsArray = new Uint32Array(toArrayBuffer(lineWidthsPtr, 0, lineCount * 4));
5851
- const lineStarts = Array.from(lineStartsArray);
5852
- const lineWidths = Array.from(lineWidthsArray);
5853
- return { lineStarts, lineWidths };
5854
- }
5855
- getTextBufferArrays(buffer, size) {
5856
- return this.getTextBuffer(buffer, size);
5570
+ const lineStarts = new Uint32Array(lineCount);
5571
+ const lineWidths = new Uint32Array(lineCount);
5572
+ this.textBufferGetLineInfoDirect(buffer, ptr(lineStarts), ptr(lineWidths));
5573
+ return {
5574
+ lineStarts: Array.from(lineStarts),
5575
+ lineWidths: Array.from(lineWidths)
5576
+ };
5857
5577
  }
5858
5578
  bufferDrawTextBuffer(buffer, textBuffer, x, y, clipRect) {
5859
5579
  const hasClipRect = clipRect !== undefined && clipRect !== null;
@@ -5863,6 +5583,15 @@ class FFIRenderLib {
5863
5583
  const clipHeight = clipRect?.height ?? 0;
5864
5584
  this.opentui.symbols.bufferDrawTextBuffer(buffer, textBuffer, x, y, clipX, clipY, clipWidth, clipHeight, hasClipRect);
5865
5585
  }
5586
+ bufferPushScissorRect(buffer, x, y, width, height) {
5587
+ this.opentui.symbols.bufferPushScissorRect(buffer, x, y, width, height);
5588
+ }
5589
+ bufferPopScissorRect(buffer) {
5590
+ this.opentui.symbols.bufferPopScissorRect(buffer);
5591
+ }
5592
+ bufferClearScissorRects(buffer) {
5593
+ this.opentui.symbols.bufferClearScissorRects(buffer);
5594
+ }
5866
5595
  getTerminalCapabilities(renderer) {
5867
5596
  const capsBuffer = new Uint8Array(64);
5868
5597
  this.opentui.symbols.getTerminalCapabilities(renderer, capsBuffer);
@@ -5915,25 +5644,18 @@ try {
5915
5644
  class TextBuffer {
5916
5645
  lib;
5917
5646
  bufferPtr;
5918
- buffer;
5919
5647
  _length = 0;
5920
5648
  _capacity;
5921
5649
  _lineInfo;
5922
- constructor(lib, ptr, buffer, capacity) {
5650
+ constructor(lib, ptr2, capacity) {
5923
5651
  this.lib = lib;
5924
- this.bufferPtr = ptr;
5925
- this.buffer = buffer;
5652
+ this.bufferPtr = ptr2;
5926
5653
  this._capacity = capacity;
5927
5654
  }
5928
5655
  static create(capacity = 256, widthMethod) {
5929
5656
  const lib = resolveRenderLib();
5930
5657
  return lib.createTextBuffer(capacity, widthMethod);
5931
5658
  }
5932
- syncBuffersAfterResize() {
5933
- const capacity = this.lib.textBufferGetCapacity(this.bufferPtr);
5934
- this.buffer = this.lib.getTextBufferArrays(this.bufferPtr, capacity);
5935
- this._capacity = capacity;
5936
- }
5937
5659
  setStyledText(text) {
5938
5660
  this.lib.textBufferReset(this.bufferPtr);
5939
5661
  this._length = 0;
@@ -5941,7 +5663,7 @@ class TextBuffer {
5941
5663
  for (const chunk of text.chunks) {
5942
5664
  const result = this.lib.textBufferWriteChunk(this.bufferPtr, chunk.text, chunk.fg || null, chunk.bg || null, chunk.attributes ?? null);
5943
5665
  if (result & 1) {
5944
- this.syncBuffersAfterResize();
5666
+ this._capacity = this.lib.textBufferGetCapacity(this.bufferPtr);
5945
5667
  }
5946
5668
  }
5947
5669
  this.lib.textBufferFinalizeLineInfo(this.bufferPtr);
@@ -5968,28 +5690,38 @@ class TextBuffer {
5968
5690
  get ptr() {
5969
5691
  return this.bufferPtr;
5970
5692
  }
5693
+ getSelectedText() {
5694
+ if (this._length === 0)
5695
+ return "";
5696
+ const selectedBytes = this.lib.getSelectedTextBytes(this.bufferPtr, this._length);
5697
+ if (!selectedBytes)
5698
+ return "";
5699
+ return this.lib.decoder.decode(selectedBytes);
5700
+ }
5971
5701
  get lineInfo() {
5972
5702
  if (!this._lineInfo) {
5973
5703
  this._lineInfo = this.lib.textBufferGetLineInfo(this.bufferPtr);
5974
5704
  }
5975
5705
  return this._lineInfo;
5976
5706
  }
5977
- toString() {
5978
- const chars = [];
5979
- for (let i = 0;i < this._length; i++) {
5980
- chars.push(String.fromCharCode(this.buffer.char[i]));
5981
- }
5982
- return chars.join("");
5983
- }
5984
- concat(other) {
5985
- return this.lib.textBufferConcat(this.bufferPtr, other.bufferPtr);
5986
- }
5987
5707
  setSelection(start, end, bgColor, fgColor) {
5988
5708
  this.lib.textBufferSetSelection(this.bufferPtr, start, end, bgColor || null, fgColor || null);
5989
5709
  }
5990
5710
  resetSelection() {
5991
5711
  this.lib.textBufferResetSelection(this.bufferPtr);
5992
5712
  }
5713
+ setLocalSelection(anchorX, anchorY, focusX, focusY, bgColor, fgColor) {
5714
+ return this.lib.textBufferSetLocalSelection(this.bufferPtr, anchorX, anchorY, focusX, focusY, bgColor || null, fgColor || null);
5715
+ }
5716
+ resetLocalSelection() {
5717
+ this.lib.textBufferResetLocalSelection(this.bufferPtr);
5718
+ }
5719
+ getSelection() {
5720
+ return this.lib.textBufferGetSelection(this.bufferPtr);
5721
+ }
5722
+ hasSelection() {
5723
+ return this.getSelection() !== null;
5724
+ }
5993
5725
  destroy() {
5994
5726
  this.lib.destroyTextBuffer(this.bufferPtr);
5995
5727
  }
@@ -5997,6 +5729,7 @@ class TextBuffer {
5997
5729
 
5998
5730
  // src/Renderable.ts
5999
5731
  import { EventEmitter as EventEmitter3 } from "events";
5732
+ var BrandedRenderable = Symbol.for("@opentui/core/Renderable");
6000
5733
  var LayoutEvents;
6001
5734
  ((LayoutEvents2) => {
6002
5735
  LayoutEvents2["LAYOUT_CHANGED"] = "layout-changed";
@@ -6053,9 +5786,12 @@ function isPositionType(value) {
6053
5786
  }
6054
5787
  return isValidPercentage(value);
6055
5788
  }
6056
- function isPostionTypeType(value) {
5789
+ function isPositionTypeType(value) {
6057
5790
  return value === "relative" || value === "absolute";
6058
5791
  }
5792
+ function isOverflowType(value) {
5793
+ return value === "visible" || value === "hidden" || value === "scroll";
5794
+ }
6059
5795
  function isDimensionType(value) {
6060
5796
  return isPositionType(value);
6061
5797
  }
@@ -6077,13 +5813,19 @@ function isSizeType(value) {
6077
5813
  }
6078
5814
  return isValidPercentage(value);
6079
5815
  }
5816
+ function isRenderable(obj) {
5817
+ return !!obj?.[BrandedRenderable];
5818
+ }
6080
5819
 
6081
5820
  class Renderable extends EventEmitter3 {
5821
+ [BrandedRenderable] = true;
6082
5822
  static renderableNumber = 1;
6083
5823
  static renderablesByNumber = new Map;
6084
5824
  id;
6085
5825
  num;
6086
5826
  _ctx;
5827
+ _translateX = 0;
5828
+ _translateY = 0;
6087
5829
  _x = 0;
6088
5830
  _y = 0;
6089
5831
  _width;
@@ -6102,17 +5844,20 @@ class Renderable extends EventEmitter3 {
6102
5844
  keypressHandler = null;
6103
5845
  _live = false;
6104
5846
  _liveCount = 0;
5847
+ _sizeChangeListener = undefined;
6105
5848
  _mouseListener = null;
6106
5849
  _mouseListeners = {};
6107
5850
  _keyListeners = {};
6108
5851
  layoutNode;
6109
5852
  _positionType = "relative";
5853
+ _overflow = "visible";
6110
5854
  _position = {};
6111
- _childHostOverride = null;
6112
5855
  renderableMap = new Map;
6113
5856
  renderableArray = [];
6114
5857
  needsZIndexSort = false;
6115
5858
  parent = null;
5859
+ childrenPrimarySortDirty = true;
5860
+ childrenSortedByPrimaryAxis = [];
6116
5861
  renderBefore;
6117
5862
  renderAfter;
6118
5863
  constructor(ctx, options) {
@@ -6167,7 +5912,7 @@ class Renderable extends EventEmitter3 {
6167
5912
  if (this._focused) {
6168
5913
  this.blur();
6169
5914
  }
6170
- this.needsUpdate();
5915
+ this.requestRender();
6171
5916
  }
6172
5917
  hasSelection() {
6173
5918
  return false;
@@ -6182,14 +5927,10 @@ class Renderable extends EventEmitter3 {
6182
5927
  return false;
6183
5928
  }
6184
5929
  focus() {
6185
- if (this.childHost !== this) {
6186
- this.childHost.focus();
6187
- return;
6188
- }
6189
5930
  if (this._focused || !this.focusable)
6190
5931
  return;
6191
5932
  this._focused = true;
6192
- this.needsUpdate();
5933
+ this.requestRender();
6193
5934
  this.keypressHandler = (key) => {
6194
5935
  this._keyListeners["down"]?.(key);
6195
5936
  if (this.handleKeyPress) {
@@ -6200,14 +5941,10 @@ class Renderable extends EventEmitter3 {
6200
5941
  this.emit("focused" /* FOCUSED */);
6201
5942
  }
6202
5943
  blur() {
6203
- if (this.childHost !== this) {
6204
- this.childHost.blur();
6205
- return;
6206
- }
6207
5944
  if (!this._focused || !this.focusable)
6208
5945
  return;
6209
5946
  this._focused = false;
6210
- this.needsUpdate();
5947
+ this.requestRender();
6211
5948
  if (this.keypressHandler) {
6212
5949
  this.keyHandler.off("keypress", this.keypressHandler);
6213
5950
  this.keypressHandler = null;
@@ -6239,12 +5976,6 @@ class Renderable extends EventEmitter3 {
6239
5976
  get isDirty() {
6240
5977
  return this._dirty;
6241
5978
  }
6242
- get childHost() {
6243
- return this._childHostOverride || this;
6244
- }
6245
- set childHost(host) {
6246
- this._childHostOverride = host;
6247
- }
6248
5979
  findDescendantById(id) {
6249
5980
  for (const child of this.renderableArray) {
6250
5981
  if (child.id === id)
@@ -6255,26 +5986,40 @@ class Renderable extends EventEmitter3 {
6255
5986
  }
6256
5987
  return;
6257
5988
  }
6258
- setChildHostById(id) {
6259
- const found = this.findDescendantById(id);
6260
- if (found) {
6261
- this._childHostOverride = found;
6262
- return true;
6263
- }
6264
- return false;
6265
- }
6266
5989
  markClean() {
6267
5990
  this._dirty = false;
6268
5991
  }
6269
- needsUpdate() {
5992
+ requestRender() {
6270
5993
  this._dirty = true;
6271
- this._ctx.needsUpdate();
5994
+ this._ctx.requestRender();
5995
+ }
5996
+ get translateX() {
5997
+ return this._translateX;
5998
+ }
5999
+ set translateX(value) {
6000
+ if (this._translateX === value)
6001
+ return;
6002
+ this._translateX = value;
6003
+ this.requestRender();
6004
+ if (this.parent)
6005
+ this.parent.childrenPrimarySortDirty = true;
6006
+ }
6007
+ get translateY() {
6008
+ return this._translateY;
6009
+ }
6010
+ set translateY(value) {
6011
+ if (this._translateY === value)
6012
+ return;
6013
+ this._translateY = value;
6014
+ this.requestRender();
6015
+ if (this.parent)
6016
+ this.parent.childrenPrimarySortDirty = true;
6272
6017
  }
6273
6018
  get x() {
6274
6019
  if (this.parent && this._positionType === "relative") {
6275
- return this.parent.x + this._x;
6020
+ return this.parent.x + this._x + this._translateX;
6276
6021
  }
6277
- return this._x;
6022
+ return this._x + this._translateX;
6278
6023
  }
6279
6024
  set x(value) {
6280
6025
  this.left = value;
@@ -6313,9 +6058,9 @@ class Renderable extends EventEmitter3 {
6313
6058
  }
6314
6059
  get y() {
6315
6060
  if (this.parent && this._positionType === "relative") {
6316
- return this.parent.y + this._y;
6061
+ return this.parent.y + this._y + this._translateY;
6317
6062
  }
6318
- return this._y;
6063
+ return this._y + this._translateY;
6319
6064
  }
6320
6065
  set y(value) {
6321
6066
  this.top = value;
@@ -6327,7 +6072,7 @@ class Renderable extends EventEmitter3 {
6327
6072
  if (isDimensionType(value)) {
6328
6073
  this._width = value;
6329
6074
  this.layoutNode.setWidth(value);
6330
- this.needsUpdate();
6075
+ this.requestRender();
6331
6076
  }
6332
6077
  }
6333
6078
  get height() {
@@ -6337,7 +6082,7 @@ class Renderable extends EventEmitter3 {
6337
6082
  if (isDimensionType(value)) {
6338
6083
  this._height = value;
6339
6084
  this.layoutNode.setHeight(value);
6340
- this.needsUpdate();
6085
+ this.requestRender();
6341
6086
  }
6342
6087
  }
6343
6088
  get zIndex() {
@@ -6358,6 +6103,96 @@ class Renderable extends EventEmitter3 {
6358
6103
  this.needsZIndexSort = false;
6359
6104
  }
6360
6105
  }
6106
+ getChildrenInViewport(viewport, padding = 10, minTriggerSize = 16) {
6107
+ if (this.renderableArray.length < minTriggerSize)
6108
+ return this.renderableArray;
6109
+ const viewportTop = viewport.y - padding;
6110
+ const viewportBottom = viewport.y + viewport.height + padding;
6111
+ const viewportLeft = viewport.x - padding;
6112
+ const viewportRight = viewport.x + viewport.width + padding;
6113
+ const dir = this.layoutNode.yogaNode.getFlexDirection();
6114
+ const isRow = dir === 2 || dir === 3;
6115
+ const children = this.getChildrenSortedByPrimaryAxis();
6116
+ const totalChildren = children.length;
6117
+ if (totalChildren === 0)
6118
+ return [];
6119
+ const vpStart = isRow ? viewportLeft : viewportTop;
6120
+ const vpEnd = isRow ? viewportRight : viewportBottom;
6121
+ let lo = 0;
6122
+ let hi = totalChildren - 1;
6123
+ let candidate = -1;
6124
+ while (lo <= hi) {
6125
+ const mid = lo + hi >> 1;
6126
+ const c = children[mid];
6127
+ const start = isRow ? c.x : c.y;
6128
+ const end = isRow ? c.x + c.width : c.y + c.height;
6129
+ if (end < vpStart) {
6130
+ lo = mid + 1;
6131
+ } else if (start > vpEnd) {
6132
+ hi = mid - 1;
6133
+ } else {
6134
+ candidate = mid;
6135
+ break;
6136
+ }
6137
+ }
6138
+ const visibleChildren = [];
6139
+ if (candidate === -1) {
6140
+ return visibleChildren;
6141
+ }
6142
+ let left = candidate;
6143
+ while (left - 1 >= 0) {
6144
+ const prev = children[left - 1];
6145
+ if ((isRow ? prev.x + prev.width : prev.y + prev.height) < vpStart)
6146
+ break;
6147
+ left--;
6148
+ }
6149
+ let right = candidate + 1;
6150
+ while (right < totalChildren) {
6151
+ const next = children[right];
6152
+ if ((isRow ? next.x : next.y) > vpEnd)
6153
+ break;
6154
+ right++;
6155
+ }
6156
+ for (let i = left;i < right; i++) {
6157
+ const child = children[i];
6158
+ if (isRow) {
6159
+ const childBottom = child.y + child.height;
6160
+ if (childBottom < viewportTop)
6161
+ continue;
6162
+ const childTop = child.y;
6163
+ if (childTop > viewportBottom)
6164
+ continue;
6165
+ } else {
6166
+ const childRight = child.x + child.width;
6167
+ if (childRight < viewportLeft)
6168
+ continue;
6169
+ const childLeft = child.x;
6170
+ if (childLeft > viewportRight)
6171
+ continue;
6172
+ }
6173
+ visibleChildren.push(child);
6174
+ }
6175
+ if (visibleChildren.length > 1) {
6176
+ visibleChildren.sort((a, b) => a.zIndex > b.zIndex ? 1 : a.zIndex < b.zIndex ? -1 : 0);
6177
+ }
6178
+ return visibleChildren;
6179
+ }
6180
+ getChildrenSortedByPrimaryAxis() {
6181
+ if (!this.childrenPrimarySortDirty && this.childrenSortedByPrimaryAxis.length === this.renderableArray.length) {
6182
+ return this.childrenSortedByPrimaryAxis;
6183
+ }
6184
+ const dir = this.layoutNode.yogaNode.getFlexDirection();
6185
+ const axis = dir === 2 || dir === 3 ? "x" : "y";
6186
+ const sorted = [...this.renderableArray];
6187
+ sorted.sort((a, b) => {
6188
+ const va = axis === "y" ? a.y : a.x;
6189
+ const vb = axis === "y" ? b.y : b.x;
6190
+ return va - vb;
6191
+ });
6192
+ this.childrenSortedByPrimaryAxis = sorted;
6193
+ this.childrenPrimarySortDirty = false;
6194
+ return this.childrenSortedByPrimaryAxis;
6195
+ }
6361
6196
  setupYogaProperties(options) {
6362
6197
  const node = this.layoutNode.yogaNode;
6363
6198
  if (isFlexBasisType(options.flexBasis)) {
@@ -6383,12 +6218,18 @@ class Renderable extends EventEmitter3 {
6383
6218
  if (options.flexDirection !== undefined) {
6384
6219
  node.setFlexDirection(parseFlexDirection(options.flexDirection));
6385
6220
  }
6221
+ if (options.flexWrap !== undefined) {
6222
+ node.setFlexWrap(parseWrap(options.flexWrap));
6223
+ }
6386
6224
  if (options.alignItems !== undefined) {
6387
6225
  node.setAlignItems(parseAlign(options.alignItems));
6388
6226
  }
6389
6227
  if (options.justifyContent !== undefined) {
6390
6228
  node.setJustifyContent(parseJustify(options.justifyContent));
6391
6229
  }
6230
+ if (options.alignSelf !== undefined) {
6231
+ node.setAlignSelf(parseAlign(options.alignSelf));
6232
+ }
6392
6233
  if (isDimensionType(options.width)) {
6393
6234
  this._width = options.width;
6394
6235
  this.layoutNode.setWidth(options.width);
@@ -6401,6 +6242,10 @@ class Renderable extends EventEmitter3 {
6401
6242
  if (this._positionType !== "relative") {
6402
6243
  node.setPositionType(parsePositionType(this._positionType));
6403
6244
  }
6245
+ this._overflow = options.overflow === "hidden" ? "hidden" : options.overflow === "scroll" ? "scroll" : "visible";
6246
+ if (this._overflow !== "visible") {
6247
+ node.setOverflow(parseOverflow(this._overflow));
6248
+ }
6404
6249
  const hasPositionProps = options.top !== undefined || options.right !== undefined || options.bottom !== undefined || options.left !== undefined;
6405
6250
  if (hasPositionProps) {
6406
6251
  this._position = {
@@ -6459,11 +6304,21 @@ class Renderable extends EventEmitter3 {
6459
6304
  }
6460
6305
  }
6461
6306
  set position(positionType) {
6462
- if (!isPostionTypeType(positionType) || this._positionType === positionType)
6307
+ if (!isPositionTypeType(positionType) || this._positionType === positionType)
6463
6308
  return;
6464
6309
  this._positionType = positionType;
6465
6310
  this.layoutNode.yogaNode.setPositionType(parsePositionType(positionType));
6466
- this.needsUpdate();
6311
+ this.requestRender();
6312
+ }
6313
+ get overflow() {
6314
+ return this._overflow;
6315
+ }
6316
+ set overflow(overflow) {
6317
+ if (!isOverflowType(overflow) || this._overflow === overflow)
6318
+ return;
6319
+ this._overflow = overflow;
6320
+ this.layoutNode.yogaNode.setOverflow(parseOverflow(overflow));
6321
+ this.requestRender();
6467
6322
  }
6468
6323
  setPosition(position) {
6469
6324
  this._position = { ...this._position, ...position };
@@ -6500,56 +6355,64 @@ class Renderable extends EventEmitter3 {
6500
6355
  node.setPosition(Edge.Left, left);
6501
6356
  }
6502
6357
  }
6503
- this.needsUpdate();
6358
+ this.requestRender();
6504
6359
  }
6505
6360
  set flexGrow(grow) {
6506
6361
  this.layoutNode.yogaNode.setFlexGrow(grow);
6507
- this.needsUpdate();
6362
+ this.requestRender();
6508
6363
  }
6509
6364
  set flexShrink(shrink) {
6510
6365
  this.layoutNode.yogaNode.setFlexShrink(shrink);
6511
- this.needsUpdate();
6366
+ this.requestRender();
6512
6367
  }
6513
6368
  set flexDirection(direction) {
6514
6369
  this.layoutNode.yogaNode.setFlexDirection(parseFlexDirection(direction));
6515
- this.needsUpdate();
6370
+ this.requestRender();
6371
+ }
6372
+ set flexWrap(wrap) {
6373
+ this.layoutNode.yogaNode.setFlexWrap(parseWrap(wrap));
6374
+ this.requestRender();
6516
6375
  }
6517
6376
  set alignItems(alignItems) {
6518
6377
  this.layoutNode.yogaNode.setAlignItems(parseAlign(alignItems));
6519
- this.needsUpdate();
6378
+ this.requestRender();
6520
6379
  }
6521
6380
  set justifyContent(justifyContent) {
6522
6381
  this.layoutNode.yogaNode.setJustifyContent(parseJustify(justifyContent));
6523
- this.needsUpdate();
6382
+ this.requestRender();
6383
+ }
6384
+ set alignSelf(alignSelf) {
6385
+ this.layoutNode.yogaNode.setAlignSelf(parseAlign(alignSelf));
6386
+ this.requestRender();
6524
6387
  }
6525
6388
  set flexBasis(basis) {
6526
6389
  if (isFlexBasisType(basis)) {
6527
6390
  this.layoutNode.yogaNode.setFlexBasis(basis);
6528
- this.needsUpdate();
6391
+ this.requestRender();
6529
6392
  }
6530
6393
  }
6531
6394
  set minWidth(minWidth) {
6532
6395
  if (isSizeType(minWidth)) {
6533
6396
  this.layoutNode.yogaNode.setMinWidth(minWidth);
6534
- this.needsUpdate();
6397
+ this.requestRender();
6535
6398
  }
6536
6399
  }
6537
6400
  set maxWidth(maxWidth) {
6538
6401
  if (isSizeType(maxWidth)) {
6539
6402
  this.layoutNode.yogaNode.setMaxWidth(maxWidth);
6540
- this.needsUpdate();
6403
+ this.requestRender();
6541
6404
  }
6542
6405
  }
6543
6406
  set minHeight(minHeight) {
6544
6407
  if (isSizeType(minHeight)) {
6545
6408
  this.layoutNode.yogaNode.setMinHeight(minHeight);
6546
- this.needsUpdate();
6409
+ this.requestRender();
6547
6410
  }
6548
6411
  }
6549
6412
  set maxHeight(maxHeight) {
6550
6413
  if (isSizeType(maxHeight)) {
6551
6414
  this.layoutNode.yogaNode.setMaxHeight(maxHeight);
6552
- this.needsUpdate();
6415
+ this.requestRender();
6553
6416
  }
6554
6417
  }
6555
6418
  set margin(margin) {
@@ -6559,31 +6422,31 @@ class Renderable extends EventEmitter3 {
6559
6422
  node.setMargin(Edge.Right, margin);
6560
6423
  node.setMargin(Edge.Bottom, margin);
6561
6424
  node.setMargin(Edge.Left, margin);
6562
- this.needsUpdate();
6425
+ this.requestRender();
6563
6426
  }
6564
6427
  }
6565
6428
  set marginTop(margin) {
6566
6429
  if (isMarginType(margin)) {
6567
6430
  this.layoutNode.yogaNode.setMargin(Edge.Top, margin);
6568
- this.needsUpdate();
6431
+ this.requestRender();
6569
6432
  }
6570
6433
  }
6571
6434
  set marginRight(margin) {
6572
6435
  if (isMarginType(margin)) {
6573
6436
  this.layoutNode.yogaNode.setMargin(Edge.Right, margin);
6574
- this.needsUpdate();
6437
+ this.requestRender();
6575
6438
  }
6576
6439
  }
6577
6440
  set marginBottom(margin) {
6578
6441
  if (isMarginType(margin)) {
6579
6442
  this.layoutNode.yogaNode.setMargin(Edge.Bottom, margin);
6580
- this.needsUpdate();
6443
+ this.requestRender();
6581
6444
  }
6582
6445
  }
6583
6446
  set marginLeft(margin) {
6584
6447
  if (isMarginType(margin)) {
6585
6448
  this.layoutNode.yogaNode.setMargin(Edge.Left, margin);
6586
- this.needsUpdate();
6449
+ this.requestRender();
6587
6450
  }
6588
6451
  }
6589
6452
  set padding(padding) {
@@ -6593,31 +6456,31 @@ class Renderable extends EventEmitter3 {
6593
6456
  node.setPadding(Edge.Right, padding);
6594
6457
  node.setPadding(Edge.Bottom, padding);
6595
6458
  node.setPadding(Edge.Left, padding);
6596
- this.needsUpdate();
6459
+ this.requestRender();
6597
6460
  }
6598
6461
  }
6599
6462
  set paddingTop(padding) {
6600
6463
  if (isPaddingType(padding)) {
6601
6464
  this.layoutNode.yogaNode.setPadding(Edge.Top, padding);
6602
- this.needsUpdate();
6465
+ this.requestRender();
6603
6466
  }
6604
6467
  }
6605
6468
  set paddingRight(padding) {
6606
6469
  if (isPaddingType(padding)) {
6607
6470
  this.layoutNode.yogaNode.setPadding(Edge.Right, padding);
6608
- this.needsUpdate();
6471
+ this.requestRender();
6609
6472
  }
6610
6473
  }
6611
6474
  set paddingBottom(padding) {
6612
6475
  if (isPaddingType(padding)) {
6613
6476
  this.layoutNode.yogaNode.setPadding(Edge.Bottom, padding);
6614
- this.needsUpdate();
6477
+ this.requestRender();
6615
6478
  }
6616
6479
  }
6617
6480
  set paddingLeft(padding) {
6618
6481
  if (isPaddingType(padding)) {
6619
6482
  this.layoutNode.yogaNode.setPadding(Edge.Left, padding);
6620
- this.needsUpdate();
6483
+ this.requestRender();
6621
6484
  }
6622
6485
  }
6623
6486
  getLayoutNode() {
@@ -6625,6 +6488,8 @@ class Renderable extends EventEmitter3 {
6625
6488
  }
6626
6489
  updateFromLayout() {
6627
6490
  const layout = this.layoutNode.yogaNode.getComputedLayout();
6491
+ const oldX = this._x;
6492
+ const oldY = this._y;
6628
6493
  this._x = layout.left;
6629
6494
  this._y = layout.top;
6630
6495
  const newWidth = Math.max(layout.width, 1);
@@ -6635,12 +6500,16 @@ class Renderable extends EventEmitter3 {
6635
6500
  if (sizeChanged) {
6636
6501
  this.onLayoutResize(newWidth, newHeight);
6637
6502
  }
6503
+ if (oldX !== this._x || oldY !== this._y) {
6504
+ if (this.parent)
6505
+ this.parent.childrenPrimarySortDirty = true;
6506
+ }
6638
6507
  }
6639
6508
  onLayoutResize(width, height) {
6640
6509
  if (this._visible) {
6641
6510
  this.handleFrameBufferResize(width, height);
6642
6511
  this.onResize(width, height);
6643
- this.needsUpdate();
6512
+ this.requestRender();
6644
6513
  }
6645
6514
  }
6646
6515
  handleFrameBufferResize(width, height) {
@@ -6669,7 +6538,10 @@ class Renderable extends EventEmitter3 {
6669
6538
  this.frameBuffer = null;
6670
6539
  }
6671
6540
  }
6672
- onResize(width, height) {}
6541
+ onResize(width, height) {
6542
+ this.onSizeChange?.();
6543
+ this.emit("resize");
6544
+ }
6673
6545
  replaceParent(obj) {
6674
6546
  if (obj.parent) {
6675
6547
  obj.parent.remove(obj.id);
@@ -6677,9 +6549,6 @@ class Renderable extends EventEmitter3 {
6677
6549
  obj.parent = this;
6678
6550
  }
6679
6551
  add(obj, index) {
6680
- if (this.childHost !== this) {
6681
- return this.childHost.add(obj, index);
6682
- }
6683
6552
  obj = ensureRenderable(this._ctx, obj);
6684
6553
  if (this.renderableMap.has(obj.id)) {
6685
6554
  console.warn(`A renderable with id ${obj.id} already exists in ${this.id}, removing it`);
@@ -6696,18 +6565,15 @@ class Renderable extends EventEmitter3 {
6696
6565
  insertedIndex = this.layoutNode.addChild(childLayoutNode);
6697
6566
  }
6698
6567
  this.needsZIndexSort = true;
6568
+ this.childrenPrimarySortDirty = true;
6699
6569
  this.renderableMap.set(obj.id, obj);
6700
6570
  if (obj._liveCount > 0) {
6701
6571
  this.propagateLiveCount(obj._liveCount);
6702
6572
  }
6703
- this.needsUpdate();
6573
+ this.requestRender();
6704
6574
  return insertedIndex;
6705
6575
  }
6706
6576
  insertBefore(obj, anchor) {
6707
- if (this.childHost !== this) {
6708
- const idx = this.childHost.insertBefore(obj, anchor);
6709
- return idx;
6710
- }
6711
6577
  obj = ensureRenderable(this._ctx, obj);
6712
6578
  if (!anchor) {
6713
6579
  return this.add(obj);
@@ -6722,15 +6588,9 @@ class Renderable extends EventEmitter3 {
6722
6588
  return this.add(obj, anchorIndex);
6723
6589
  }
6724
6590
  getRenderable(id) {
6725
- if (this.childHost !== this)
6726
- return this.childHost.getRenderable(id);
6727
6591
  return this.renderableMap.get(id);
6728
6592
  }
6729
6593
  remove(id) {
6730
- if (this.childHost !== this) {
6731
- this.childHost.remove(id);
6732
- return;
6733
- }
6734
6594
  if (!id) {
6735
6595
  return;
6736
6596
  }
@@ -6742,7 +6602,7 @@ class Renderable extends EventEmitter3 {
6742
6602
  }
6743
6603
  const childLayoutNode = obj.getLayoutNode();
6744
6604
  this.layoutNode.removeChild(childLayoutNode);
6745
- this.needsUpdate();
6605
+ this.requestRender();
6746
6606
  obj.onRemove();
6747
6607
  obj.parent = null;
6748
6608
  }
@@ -6751,18 +6611,20 @@ class Renderable extends EventEmitter3 {
6751
6611
  if (index !== -1) {
6752
6612
  this.renderableArray.splice(index, 1);
6753
6613
  }
6614
+ this.childrenPrimarySortDirty = true;
6754
6615
  }
6755
6616
  }
6756
6617
  onRemove() {}
6757
6618
  getChildren() {
6758
- if (this.childHost !== this)
6759
- return this.childHost.getChildren();
6760
6619
  return [...this.renderableArray];
6761
6620
  }
6621
+ getChildrenCount() {
6622
+ return this.renderableArray.length;
6623
+ }
6762
6624
  render(buffer, deltaTime) {
6763
6625
  if (!this.visible)
6764
6626
  return;
6765
- this.beforeRender();
6627
+ this.onUpdate(deltaTime);
6766
6628
  this.updateFromLayout();
6767
6629
  let renderBuffer = buffer;
6768
6630
  if (this.buffered && this.frameBuffer) {
@@ -6778,14 +6640,33 @@ class Renderable extends EventEmitter3 {
6778
6640
  this.markClean();
6779
6641
  this._ctx.addToHitGrid(this.x, this.y, this.width, this.height, this.num);
6780
6642
  this.ensureZIndexSorted();
6781
- for (const child of this.renderableArray) {
6643
+ const shouldPushScissor = this._overflow !== "visible" && this.width > 0 && this.height > 0;
6644
+ if (shouldPushScissor) {
6645
+ const scissorRect = this.getScissorRect();
6646
+ renderBuffer.pushScissorRect(scissorRect.x, scissorRect.y, scissorRect.width, scissorRect.height);
6647
+ }
6648
+ for (const child of this._getChildren()) {
6782
6649
  child.render(renderBuffer, deltaTime);
6783
6650
  }
6651
+ if (shouldPushScissor) {
6652
+ renderBuffer.popScissorRect();
6653
+ }
6784
6654
  if (this.buffered && this.frameBuffer) {
6785
6655
  buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
6786
6656
  }
6787
6657
  }
6788
- beforeRender() {}
6658
+ _getChildren() {
6659
+ return this.renderableArray;
6660
+ }
6661
+ onUpdate(deltaTime) {}
6662
+ getScissorRect() {
6663
+ return {
6664
+ x: this.buffered ? 0 : this.x,
6665
+ y: this.buffered ? 0 : this.y,
6666
+ width: this.width,
6667
+ height: this.height
6668
+ };
6669
+ }
6789
6670
  renderSelf(buffer, deltaTime) {}
6790
6671
  destroy() {
6791
6672
  if (this.parent) {
@@ -6818,7 +6699,7 @@ class Renderable extends EventEmitter3 {
6818
6699
  this._mouseListener?.call(this, event);
6819
6700
  this._mouseListeners[event.type]?.call(this, event);
6820
6701
  this.onMouseEvent(event);
6821
- if (this.parent && !event.defaultPrevented) {
6702
+ if (this.parent && !event.propagationStopped) {
6822
6703
  this.parent.processMouseEvent(event);
6823
6704
  }
6824
6705
  }
@@ -6892,6 +6773,12 @@ class Renderable extends EventEmitter3 {
6892
6773
  get onKeyDown() {
6893
6774
  return this._keyListeners["down"];
6894
6775
  }
6776
+ set onSizeChange(handler) {
6777
+ this._sizeChangeListener = handler;
6778
+ }
6779
+ get onSizeChange() {
6780
+ return this._sizeChangeListener;
6781
+ }
6895
6782
  applyEventOptions(options) {
6896
6783
  this.onMouse = options.onMouse;
6897
6784
  this.onMouseDown = options.onMouseDown;
@@ -6904,6 +6791,7 @@ class Renderable extends EventEmitter3 {
6904
6791
  this.onMouseOut = options.onMouseOut;
6905
6792
  this.onMouseScroll = options.onMouseScroll;
6906
6793
  this.onKeyDown = options.onKeyDown;
6794
+ this.onSizeChange = options.onSizeChange;
6907
6795
  }
6908
6796
  }
6909
6797
 
@@ -6941,7 +6829,7 @@ class RootRenderable extends Renderable {
6941
6829
  this.height = height;
6942
6830
  this.emit("resized" /* RESIZED */, { width, height });
6943
6831
  }
6944
- beforeRender() {
6832
+ onUpdate() {
6945
6833
  if (this.layoutNode.yogaNode.isDirty()) {
6946
6834
  this.calculateLayout();
6947
6835
  }
@@ -7022,7 +6910,7 @@ function isVNode(node) {
7022
6910
  return node && node.__isVNode;
7023
6911
  }
7024
6912
  function ensureRenderable(ctx, node) {
7025
- if (node instanceof Renderable)
6913
+ if (isRenderable(node))
7026
6914
  return node;
7027
6915
  return instantiate(ctx, node);
7028
6916
  }
@@ -7070,7 +6958,7 @@ function wrapWithDelegates(instance, delegateMap) {
7070
6958
  return proxy;
7071
6959
  }
7072
6960
  function instantiate(ctx, node) {
7073
- if (node instanceof Renderable)
6961
+ if (isRenderable(node))
7074
6962
  return node;
7075
6963
  if (!node || typeof node !== "object") {
7076
6964
  throw new TypeError("mount() received an invalid vnode");
@@ -7082,7 +6970,7 @@ function instantiate(ctx, node) {
7082
6970
  if (isRenderableConstructor(type)) {
7083
6971
  const instance = new type(ctx, props || {});
7084
6972
  for (const child of children) {
7085
- if (child instanceof Renderable) {
6973
+ if (isRenderable(child)) {
7086
6974
  instance.add(child);
7087
6975
  } else {
7088
6976
  const mounted = instantiate(ctx, child);
@@ -7107,7 +6995,7 @@ function instantiate(ctx, node) {
7107
6995
  return wrapWithDelegates(inst, delegateMap);
7108
6996
  }
7109
6997
  function delegate(mapping, vnode) {
7110
- if (vnode instanceof Renderable) {
6998
+ if (isRenderable(vnode)) {
7111
6999
  return wrapWithDelegates(vnode, mapping);
7112
7000
  }
7113
7001
  if (!vnode || typeof vnode !== "object")
@@ -7119,9 +7007,9 @@ function delegate(mapping, vnode) {
7119
7007
  // src/console.ts
7120
7008
  import { EventEmitter as EventEmitter5 } from "events";
7121
7009
  import { Console } from "console";
7122
- import util from "util";
7123
7010
  import fs from "fs";
7124
7011
  import path from "path";
7012
+ import util from "util";
7125
7013
 
7126
7014
  // src/lib/output.capture.ts
7127
7015
  import { Writable } from "stream";
@@ -7189,27 +7077,9 @@ function getCallerInfo() {
7189
7077
  const columnNumber = parseInt(match[4], 10) || 0;
7190
7078
  return { functionName, fullPath, fileName, lineNumber, columnNumber };
7191
7079
  }
7192
- var { capture } = singleton("ConsoleCapture", () => {
7193
- const capture2 = new Capture;
7194
- const mockStdout = new CapturedWritableStream("stdout", capture2);
7195
- const mockStderr = new CapturedWritableStream("stderr", capture2);
7196
- if (process.env.SKIP_CONSOLE_CACHE !== "true") {
7197
- global.console = new Console({
7198
- stdout: mockStdout,
7199
- stderr: mockStderr,
7200
- colorMode: true,
7201
- inspectOptions: {
7202
- compact: false,
7203
- breakLength: 80,
7204
- depth: 2
7205
- }
7206
- });
7207
- }
7208
- return { capture: capture2 };
7209
- });
7080
+ var capture = singleton("ConsoleCapture", () => new Capture);
7210
7081
 
7211
7082
  class TerminalConsoleCache extends EventEmitter5 {
7212
- originalConsole;
7213
7083
  _cachedLogs = [];
7214
7084
  MAX_CACHE_SIZE = 1000;
7215
7085
  _collectCallerInfo = false;
@@ -7219,35 +7089,26 @@ class TerminalConsoleCache extends EventEmitter5 {
7219
7089
  }
7220
7090
  constructor() {
7221
7091
  super();
7222
- this.originalConsole = {
7223
- log: console.log,
7224
- info: console.info,
7225
- warn: console.warn,
7226
- error: console.error,
7227
- debug: console.debug
7228
- };
7229
- if (process.env.SKIP_CONSOLE_CACHE !== "true") {
7230
- this.activate();
7231
- }
7232
7092
  }
7233
7093
  activate() {
7094
+ this.setupConsoleCapture();
7234
7095
  this.overrideConsoleMethods();
7235
7096
  }
7236
- setCollectCallerInfo(enabled) {
7237
- this._collectCallerInfo = enabled;
7238
- }
7239
- clearConsole() {
7240
- this._cachedLogs = [];
7241
- }
7242
- setCachingEnabled(enabled) {
7243
- this._cachingEnabled = enabled;
7244
- }
7245
- deactivate() {
7246
- console.log = this.originalConsole.log;
7247
- console.info = this.originalConsole.info;
7248
- console.warn = this.originalConsole.warn;
7249
- console.error = this.originalConsole.error;
7250
- console.debug = this.originalConsole.debug;
7097
+ setupConsoleCapture() {
7098
+ if (process.env.OTUI_USE_CONSOLE === "false")
7099
+ return;
7100
+ const mockStdout = new CapturedWritableStream("stdout", capture);
7101
+ const mockStderr = new CapturedWritableStream("stderr", capture);
7102
+ global.console = new Console({
7103
+ stdout: mockStdout,
7104
+ stderr: mockStderr,
7105
+ colorMode: true,
7106
+ inspectOptions: {
7107
+ compact: false,
7108
+ breakLength: 80,
7109
+ depth: 2
7110
+ }
7111
+ });
7251
7112
  }
7252
7113
  overrideConsoleMethods() {
7253
7114
  console.log = (...args) => {
@@ -7266,6 +7127,23 @@ class TerminalConsoleCache extends EventEmitter5 {
7266
7127
  this.appendToConsole("DEBUG" /* DEBUG */, ...args);
7267
7128
  };
7268
7129
  }
7130
+ setCollectCallerInfo(enabled) {
7131
+ this._collectCallerInfo = enabled;
7132
+ }
7133
+ clearConsole() {
7134
+ this._cachedLogs = [];
7135
+ }
7136
+ setCachingEnabled(enabled) {
7137
+ this._cachingEnabled = enabled;
7138
+ }
7139
+ deactivate() {
7140
+ this.restoreOriginalConsole();
7141
+ }
7142
+ restoreOriginalConsole() {
7143
+ const originalNodeConsole = __require("console");
7144
+ global.console = originalNodeConsole;
7145
+ this.setupConsoleCapture();
7146
+ }
7269
7147
  addLogEntry(level, ...args) {
7270
7148
  const callerInfo = this._collectCallerInfo ? getCallerInfo() : null;
7271
7149
  const logEntry = [new Date, level, args, callerInfo];
@@ -7340,9 +7218,9 @@ class TerminalConsole extends EventEmitter5 {
7340
7218
  _displayLines = [];
7341
7219
  _allLogEntries = [];
7342
7220
  _needsFrameBufferUpdate = false;
7343
- markNeedsUpdate() {
7221
+ markNeedsRerender() {
7344
7222
  this._needsFrameBufferUpdate = true;
7345
- this.renderer.needsUpdate();
7223
+ this.renderer.requestRender();
7346
7224
  }
7347
7225
  _rgbaInfo;
7348
7226
  _rgbaWarn;
@@ -7409,7 +7287,7 @@ class TerminalConsole extends EventEmitter5 {
7409
7287
  if (this.isScrolledToBottom) {
7410
7288
  this._scrollToBottom();
7411
7289
  }
7412
- this.markNeedsUpdate();
7290
+ this.markNeedsRerender();
7413
7291
  }
7414
7292
  _updateConsoleDimensions() {
7415
7293
  const termWidth = this.renderer.terminalWidth;
@@ -7514,7 +7392,7 @@ class TerminalConsole extends EventEmitter5 {
7514
7392
  break;
7515
7393
  }
7516
7394
  if (needsRedraw) {
7517
- this.markNeedsUpdate();
7395
+ this.markNeedsRerender();
7518
7396
  }
7519
7397
  }
7520
7398
  attachStdin() {
@@ -7571,7 +7449,7 @@ class TerminalConsole extends EventEmitter5 {
7571
7449
  const visibleLineCount = Math.min(logAreaHeight, displayLineCount - this.scrollTopIndex);
7572
7450
  this.currentLineIndex = Math.max(0, Math.min(this.currentLineIndex, visibleLineCount - 1));
7573
7451
  if (this.isVisible) {
7574
- this.markNeedsUpdate();
7452
+ this.markNeedsRerender();
7575
7453
  }
7576
7454
  }
7577
7455
  }
@@ -7579,7 +7457,7 @@ class TerminalConsole extends EventEmitter5 {
7579
7457
  terminalConsoleCache.clearConsole();
7580
7458
  this._allLogEntries = [];
7581
7459
  this._displayLines = [];
7582
- this.markNeedsUpdate();
7460
+ this.markNeedsRerender();
7583
7461
  }
7584
7462
  toggle() {
7585
7463
  if (this.isVisible) {
@@ -7592,17 +7470,17 @@ class TerminalConsole extends EventEmitter5 {
7592
7470
  this.show();
7593
7471
  }
7594
7472
  if (!this.renderer.isRunning) {
7595
- this.renderer.needsUpdate();
7473
+ this.renderer.requestRender();
7596
7474
  }
7597
7475
  }
7598
7476
  focus() {
7599
7477
  this.attachStdin();
7600
7478
  this._scrollToBottom(true);
7601
- this.markNeedsUpdate();
7479
+ this.markNeedsRerender();
7602
7480
  }
7603
7481
  blur() {
7604
7482
  this.detachStdin();
7605
- this.markNeedsUpdate();
7483
+ this.markNeedsRerender();
7606
7484
  }
7607
7485
  show() {
7608
7486
  if (!this.isVisible) {
@@ -7611,7 +7489,8 @@ class TerminalConsole extends EventEmitter5 {
7611
7489
  terminalConsoleCache.setCachingEnabled(false);
7612
7490
  if (!this.frameBuffer) {
7613
7491
  this.frameBuffer = OptimizedBuffer.create(this.consoleWidth, this.consoleHeight, this.renderer.widthMethod, {
7614
- respectAlpha: this.backgroundColor.a < 1
7492
+ respectAlpha: this.backgroundColor.a < 1,
7493
+ id: "console framebuffer"
7615
7494
  });
7616
7495
  }
7617
7496
  const logCount = terminalConsoleCache.cachedLogs.length;
@@ -7620,7 +7499,7 @@ class TerminalConsole extends EventEmitter5 {
7620
7499
  this.scrollTopIndex = 0;
7621
7500
  this._scrollToBottom(true);
7622
7501
  this.focus();
7623
- this.markNeedsUpdate();
7502
+ this.markNeedsRerender();
7624
7503
  }
7625
7504
  }
7626
7505
  hide() {
@@ -7693,7 +7572,7 @@ class TerminalConsole extends EventEmitter5 {
7693
7572
  this._debugModeEnabled = enabled;
7694
7573
  terminalConsoleCache.setCollectCallerInfo(enabled);
7695
7574
  if (this.isVisible) {
7696
- this.markNeedsUpdate();
7575
+ this.markNeedsRerender();
7697
7576
  }
7698
7577
  }
7699
7578
  toggleDebugMode() {
@@ -7822,9 +7701,10 @@ class MouseEvent {
7822
7701
  modifiers;
7823
7702
  scroll;
7824
7703
  target;
7825
- _defaultPrevented = false;
7826
- get defaultPrevented() {
7827
- return this._defaultPrevented;
7704
+ isSelecting;
7705
+ _propagationStopped = false;
7706
+ get propagationStopped() {
7707
+ return this._propagationStopped;
7828
7708
  }
7829
7709
  constructor(target, attributes) {
7830
7710
  this.target = target;
@@ -7835,9 +7715,10 @@ class MouseEvent {
7835
7715
  this.modifiers = attributes.modifiers;
7836
7716
  this.scroll = attributes.scroll;
7837
7717
  this.source = attributes.source;
7718
+ this.isSelecting = attributes.isSelecting;
7838
7719
  }
7839
- preventDefault() {
7840
- this._defaultPrevented = true;
7720
+ stopPropagation() {
7721
+ this._propagationStopped = true;
7841
7722
  }
7842
7723
  }
7843
7724
  var MouseButton;
@@ -7948,7 +7829,6 @@ class CliRenderer extends EventEmitter6 {
7948
7829
  lastOverRenderableNum = 0;
7949
7830
  lastOverRenderable;
7950
7831
  currentSelection = null;
7951
- selectionState = null;
7952
7832
  selectionContainers = [];
7953
7833
  _splitHeight = 0;
7954
7834
  renderOffset = 0;
@@ -7958,7 +7838,7 @@ class CliRenderer extends EventEmitter6 {
7958
7838
  realStdoutWrite;
7959
7839
  captureCallback = () => {
7960
7840
  if (this._splitHeight > 0) {
7961
- this.needsUpdate();
7841
+ this.requestRender();
7962
7842
  }
7963
7843
  };
7964
7844
  _useConsole = true;
@@ -8042,6 +7922,9 @@ Error details:
8042
7922
  process.exit(1);
8043
7923
  });
8044
7924
  };
7925
+ process.on("warning", (warning) => {
7926
+ console.warn(JSON.stringify(warning.message, null, 2));
7927
+ });
8045
7928
  process.on("uncaughtException", handleError);
8046
7929
  process.on("unhandledRejection", handleError);
8047
7930
  process.on("exit", () => {
@@ -8082,8 +7965,8 @@ Error details:
8082
7965
  writeOut(chunk, encoding, callback) {
8083
7966
  return this.realStdoutWrite.call(this.stdout, chunk, encoding, callback);
8084
7967
  }
8085
- needsUpdate() {
8086
- if (!this.updateScheduled && !this._isRunning) {
7968
+ requestRender() {
7969
+ if (!this.rendering && !this.updateScheduled && !this._isRunning) {
8087
7970
  this.updateScheduled = true;
8088
7971
  process.nextTick(() => {
8089
7972
  this.loop();
@@ -8185,13 +8068,13 @@ Error details:
8185
8068
  this._console.resize(this.width, this.height);
8186
8069
  this.root.resize(this.width, this.height);
8187
8070
  this.emit("resize", this.width, this.height);
8188
- this.needsUpdate();
8071
+ this.requestRender();
8189
8072
  }
8190
8073
  interceptStdoutWrite = (chunk, encoding, callback) => {
8191
8074
  const text = chunk.toString();
8192
8075
  capture.write("stdout", text);
8193
8076
  if (this._splitHeight > 0) {
8194
- this.needsUpdate();
8077
+ this.requestRender();
8195
8078
  }
8196
8079
  if (typeof callback === "function") {
8197
8080
  process.nextTick(callback);
@@ -8311,22 +8194,38 @@ Error details:
8311
8194
  const sameElement = maybeRenderableId === this.lastOverRenderableNum;
8312
8195
  this.lastOverRenderableNum = maybeRenderableId;
8313
8196
  const maybeRenderable = Renderable.renderablesByNumber.get(maybeRenderableId);
8314
- if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */) {
8197
+ if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */ && !this.currentSelection?.isSelecting && !mouseEvent.modifiers.ctrl) {
8315
8198
  if (maybeRenderable && maybeRenderable.selectable && maybeRenderable.shouldStartSelection(mouseEvent.x, mouseEvent.y)) {
8316
8199
  this.startSelection(maybeRenderable, mouseEvent.x, mouseEvent.y);
8200
+ const event = new MouseEvent(maybeRenderable, mouseEvent);
8201
+ maybeRenderable.processMouseEvent(event);
8317
8202
  return true;
8318
8203
  }
8319
8204
  }
8320
- if (mouseEvent.type === "drag" && this.selectionState?.isSelecting) {
8205
+ if (mouseEvent.type === "drag" && this.currentSelection?.isSelecting) {
8321
8206
  this.updateSelection(maybeRenderable, mouseEvent.x, mouseEvent.y);
8207
+ if (maybeRenderable) {
8208
+ const event = new MouseEvent(maybeRenderable, { ...mouseEvent, isSelecting: true });
8209
+ maybeRenderable.processMouseEvent(event);
8210
+ }
8322
8211
  return true;
8323
8212
  }
8324
- if (mouseEvent.type === "up" && this.selectionState?.isSelecting) {
8213
+ if (mouseEvent.type === "up" && this.currentSelection?.isSelecting) {
8214
+ if (maybeRenderable) {
8215
+ const event = new MouseEvent(maybeRenderable, { ...mouseEvent, isSelecting: true });
8216
+ maybeRenderable.processMouseEvent(event);
8217
+ }
8325
8218
  this.finishSelection();
8326
8219
  return true;
8327
8220
  }
8328
- if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */ && this.selectionState) {
8329
- this.clearSelection();
8221
+ if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */ && this.currentSelection) {
8222
+ if (mouseEvent.modifiers.ctrl) {
8223
+ this.currentSelection.isSelecting = true;
8224
+ this.updateSelection(maybeRenderable, mouseEvent.x, mouseEvent.y);
8225
+ return true;
8226
+ } else {
8227
+ this.clearSelection();
8228
+ }
8330
8229
  }
8331
8230
  if (!sameElement && (mouseEvent.type === "drag" || mouseEvent.type === "move")) {
8332
8231
  if (this.lastOverRenderable && this.lastOverRenderable !== this.capturedRenderable) {
@@ -8363,7 +8262,7 @@ Error details:
8363
8262
  this.lastOverRenderable = this.capturedRenderable;
8364
8263
  this.lastOverRenderableNum = this.capturedRenderable.num;
8365
8264
  this.capturedRenderable = undefined;
8366
- this.needsUpdate();
8265
+ this.requestRender();
8367
8266
  }
8368
8267
  if (maybeRenderable) {
8369
8268
  if (mouseEvent.type === "drag" && mouseEvent.button === 0 /* LEFT */) {
@@ -8446,7 +8345,7 @@ Error details:
8446
8345
  this.renderOffset = height - this._splitHeight;
8447
8346
  this.width = width;
8448
8347
  this.height = this._splitHeight;
8449
- this.currentRenderBuffer.clearLocal(RGBA.fromHex("#000000"), "\u0A00");
8348
+ this.currentRenderBuffer.clear(RGBA.fromHex("#000000"));
8450
8349
  this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
8451
8350
  } else {
8452
8351
  this.width = width;
@@ -8458,26 +8357,26 @@ Error details:
8458
8357
  this._console.resize(this.width, this.height);
8459
8358
  this.root.resize(this.width, this.height);
8460
8359
  this.emit("resize", this.width, this.height);
8461
- this.needsUpdate();
8360
+ this.requestRender();
8462
8361
  }
8463
8362
  setBackgroundColor(color) {
8464
8363
  const parsedColor = parseColor(color);
8465
8364
  this.lib.setBackgroundColor(this.rendererPtr, parsedColor);
8466
8365
  this.backgroundColor = parsedColor;
8467
8366
  this.nextRenderBuffer.clear(parsedColor);
8468
- this.needsUpdate();
8367
+ this.requestRender();
8469
8368
  }
8470
8369
  toggleDebugOverlay() {
8471
8370
  this.debugOverlay.enabled = !this.debugOverlay.enabled;
8472
8371
  this.lib.setDebugOverlay(this.rendererPtr, this.debugOverlay.enabled, this.debugOverlay.corner);
8473
8372
  this.emit("debugOverlay:toggle" /* DEBUG_OVERLAY_TOGGLE */, this.debugOverlay.enabled);
8474
- this.needsUpdate();
8373
+ this.requestRender();
8475
8374
  }
8476
8375
  configureDebugOverlay(options) {
8477
8376
  this.debugOverlay.enabled = options.enabled ?? this.debugOverlay.enabled;
8478
8377
  this.debugOverlay.corner = options.corner ?? this.debugOverlay.corner;
8479
8378
  this.lib.setDebugOverlay(this.rendererPtr, this.debugOverlay.enabled, this.debugOverlay.corner);
8480
- this.needsUpdate();
8379
+ this.requestRender();
8481
8380
  }
8482
8381
  clearTerminal() {
8483
8382
  this.lib.clearTerminal(this.rendererPtr);
@@ -8557,6 +8456,9 @@ Error details:
8557
8456
  this.controlState = "explicit_started" /* EXPLICIT_STARTED */;
8558
8457
  this.internalStart();
8559
8458
  }
8459
+ auto() {
8460
+ this.controlState = this._isRunning ? "auto_started" /* AUTO_STARTED */ : "idle" /* IDLE */;
8461
+ }
8560
8462
  internalStart() {
8561
8463
  if (!this._isRunning && !this.isDestroyed) {
8562
8464
  this._isRunning = true;
@@ -8724,35 +8626,32 @@ Error details:
8724
8626
  getSelection() {
8725
8627
  return this.currentSelection;
8726
8628
  }
8629
+ get hasSelection() {
8630
+ return !!this.currentSelection;
8631
+ }
8727
8632
  getSelectionContainer() {
8728
8633
  return this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : null;
8729
8634
  }
8730
- hasSelection() {
8731
- return this.currentSelection !== null;
8732
- }
8733
8635
  clearSelection() {
8734
- if (this.selectionState) {
8735
- this.selectionState = null;
8736
- this.notifySelectablesOfSelectionChange();
8636
+ if (this.currentSelection) {
8637
+ for (const renderable of this.currentSelection.touchedRenderables) {
8638
+ if (renderable.selectable) {
8639
+ renderable.onSelectionChanged(null);
8640
+ }
8641
+ }
8642
+ this.currentSelection = null;
8737
8643
  }
8738
- this.currentSelection = null;
8739
8644
  this.selectionContainers = [];
8740
8645
  }
8741
8646
  startSelection(startRenderable, x, y) {
8742
8647
  this.clearSelection();
8743
8648
  this.selectionContainers.push(startRenderable.parent || this.root);
8744
- this.selectionState = {
8745
- anchor: { x, y },
8746
- focus: { x, y },
8747
- isActive: true,
8748
- isSelecting: true
8749
- };
8750
- this.currentSelection = new Selection({ x, y }, { x, y });
8649
+ this.currentSelection = new Selection(startRenderable, { x, y }, { x, y });
8751
8650
  this.notifySelectablesOfSelectionChange();
8752
8651
  }
8753
8652
  updateSelection(currentRenderable, x, y) {
8754
- if (this.selectionState) {
8755
- this.selectionState.focus = { x, y };
8653
+ if (this.currentSelection) {
8654
+ this.currentSelection.focus = { x, y };
8756
8655
  if (this.selectionContainers.length > 0) {
8757
8656
  const currentContainer = this.selectionContainers[this.selectionContainers.length - 1];
8758
8657
  if (!currentRenderable || !this.isWithinContainer(currentRenderable, currentContainer)) {
@@ -8769,12 +8668,18 @@ Error details:
8769
8668
  }
8770
8669
  }
8771
8670
  }
8772
- if (this.currentSelection) {
8773
- this.currentSelection = new Selection(this.selectionState.anchor, this.selectionState.focus);
8774
- }
8775
8671
  this.notifySelectablesOfSelectionChange();
8776
8672
  }
8777
8673
  }
8674
+ requestSelectionUpdate() {
8675
+ if (this.currentSelection?.isSelecting) {
8676
+ const lastMouseX = this.currentSelection.focus.x;
8677
+ const lastMouseY = this.currentSelection.focus.y;
8678
+ const maybeRenderableId = this.lib.checkHit(this.rendererPtr, lastMouseX, lastMouseY);
8679
+ const maybeRenderable = Renderable.renderablesByNumber.get(maybeRenderableId);
8680
+ this.updateSelection(maybeRenderable, lastMouseX, lastMouseY);
8681
+ }
8682
+ }
8778
8683
  isWithinContainer(renderable, container) {
8779
8684
  let current = renderable;
8780
8685
  while (current) {
@@ -8785,46 +8690,44 @@ Error details:
8785
8690
  return false;
8786
8691
  }
8787
8692
  finishSelection() {
8788
- if (this.selectionState) {
8789
- this.selectionState.isSelecting = false;
8693
+ if (this.currentSelection) {
8694
+ this.currentSelection.isSelecting = false;
8790
8695
  this.emit("selection", this.currentSelection);
8791
8696
  }
8792
8697
  }
8793
8698
  notifySelectablesOfSelectionChange() {
8794
- let normalizedSelection = null;
8795
- if (this.selectionState) {
8796
- normalizedSelection = { ...this.selectionState };
8797
- if (normalizedSelection.anchor.y > normalizedSelection.focus.y || normalizedSelection.anchor.y === normalizedSelection.focus.y && normalizedSelection.anchor.x > normalizedSelection.focus.x) {
8798
- const temp = normalizedSelection.anchor;
8799
- normalizedSelection.anchor = normalizedSelection.focus;
8800
- normalizedSelection.focus = {
8801
- x: temp.x + 1,
8802
- y: temp.y
8803
- };
8804
- }
8805
- }
8806
8699
  const selectedRenderables = [];
8807
- for (const [, renderable] of Renderable.renderablesByNumber) {
8808
- if (renderable.visible && renderable.selectable) {
8809
- const currentContainer = this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : null;
8810
- let hasSelection = false;
8811
- if (!currentContainer || this.isWithinContainer(renderable, currentContainer)) {
8812
- hasSelection = renderable.onSelectionChanged(normalizedSelection);
8813
- } else {
8814
- hasSelection = renderable.onSelectionChanged(normalizedSelection ? { ...normalizedSelection, isActive: false } : null);
8700
+ const touchedRenderables = [];
8701
+ const currentContainer = this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : this.root;
8702
+ if (this.currentSelection) {
8703
+ this.walkSelectableRenderables(currentContainer, this.currentSelection.bounds, selectedRenderables, touchedRenderables);
8704
+ for (const renderable of this.currentSelection.touchedRenderables) {
8705
+ if (!touchedRenderables.includes(renderable)) {
8706
+ renderable.onSelectionChanged(null);
8815
8707
  }
8708
+ }
8709
+ this.currentSelection.updateSelectedRenderables(selectedRenderables);
8710
+ this.currentSelection.updateTouchedRenderables(touchedRenderables);
8711
+ }
8712
+ }
8713
+ walkSelectableRenderables(container, selectionBounds, selectedRenderables, touchedRenderables) {
8714
+ const children = container.getChildrenInViewport(selectionBounds, 0);
8715
+ for (const child of children) {
8716
+ if (child.selectable) {
8717
+ const hasSelection = child.onSelectionChanged(this.currentSelection);
8816
8718
  if (hasSelection) {
8817
- selectedRenderables.push(renderable);
8719
+ selectedRenderables.push(child);
8818
8720
  }
8721
+ touchedRenderables.push(child);
8722
+ }
8723
+ if (child.getChildrenCount() > 0) {
8724
+ this.walkSelectableRenderables(child, selectionBounds, selectedRenderables, touchedRenderables);
8819
8725
  }
8820
- }
8821
- if (this.currentSelection) {
8822
- this.currentSelection.updateSelectedRenderables(selectedRenderables);
8823
8726
  }
8824
8727
  }
8825
8728
  }
8826
8729
 
8827
- export { __toESM, __commonJS, __export, __require, Edge, Gutter, MeasureMode, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, TrackedNode, createTrackedNode, nonAlphanumericKeys, parseKeypress, KeyHandler, getKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, DebugOverlayCorner, createTextAttributes, StyledText, stringToStyledText, black, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite, bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, bold, italic, underline, strikethrough, dim, reverse, blink, fg, bg, tn, t, SyntaxStyle, hastToStyledText, parseAlign, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, TextSelectionHelper, ASCIIFontSelectionHelper, TextBuffer, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, ensureRenderable, wrapWithDelegates, instantiate, delegate, LayoutEvents, RenderableEvents, isValidPercentage, isMarginType, isPaddingType, isPositionType, isPostionTypeType, isDimensionType, isFlexBasisType, isSizeType, Renderable, RootRenderable, capture, ConsolePosition, TerminalConsole, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, CliRenderer };
8730
+ export { __toESM, __commonJS, __export, __require, Edge, Gutter, MeasureMode, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, TrackedNode, createTrackedNode, nonAlphanumericKeys, parseKeypress, KeyHandler, getKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, DebugOverlayCorner, createTextAttributes, StyledText, stringToStyledText, black, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite, bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, bold, italic, underline, strikethrough, dim, reverse, blink, fg, bg, tn, t, SyntaxStyle, hastToStyledText, parseAlign, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, convertGlobalToLocalSelection, ASCIIFontSelectionHelper, TextBuffer, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, ensureRenderable, wrapWithDelegates, instantiate, delegate, LayoutEvents, RenderableEvents, isValidPercentage, isMarginType, isPaddingType, isPositionType, isPositionTypeType, isOverflowType, isDimensionType, isFlexBasisType, isSizeType, isRenderable, Renderable, RootRenderable, capture, ConsolePosition, TerminalConsole, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, CliRenderer };
8828
8731
 
8829
- //# debugId=00A84341316A9BF864756E2164756E21
8830
- //# sourceMappingURL=index-4gez9k7q.js.map
8732
+ //# debugId=4847DC8F7CFD610964756E2164756E21
8733
+ //# sourceMappingURL=index-sw194bbj.js.map