@opentui/core 0.1.14 → 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, JSCallback } 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,48 +4738,42 @@ 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) {
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) {
4893
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();
@@ -4904,9 +4781,6 @@ class OptimizedBuffer {
4904
4781
  const id = options.id && options.id.trim() !== "" ? options.id : "unnamed buffer";
4905
4782
  return lib.createOptimizedBuffer(width, height, widthMethod, respectAlpha, id);
4906
4783
  }
4907
- get buffers() {
4908
- return this.buffer;
4909
- }
4910
4784
  coordsToIndex(x, y) {
4911
4785
  return y * this._width + x;
4912
4786
  }
@@ -4923,91 +4797,18 @@ class OptimizedBuffer {
4923
4797
  getNativeId() {
4924
4798
  return this.lib.bufferGetId(this.bufferPtr);
4925
4799
  }
4926
- clear(bg2 = RGBA.fromValues(0, 0, 0, 1), clearChar = " ") {
4927
- if (this.useFFI) {
4928
- this.clearFFI(bg2);
4929
- } else {
4930
- this.clearLocal(bg2, clearChar);
4931
- }
4932
- }
4933
- clearLocal(bg2 = RGBA.fromValues(0, 0, 0, 1), clearChar = " ") {
4934
- this.buffer.char.fill(clearChar.charCodeAt(0));
4935
- this.buffer.attributes.fill(0);
4936
- for (let i = 0;i < this._width * this._height; i++) {
4937
- const index = i * 4;
4938
- this.buffer.fg[index] = 1;
4939
- this.buffer.fg[index + 1] = 1;
4940
- this.buffer.fg[index + 2] = 1;
4941
- this.buffer.fg[index + 3] = 1;
4942
- this.buffer.bg[index] = bg2.r;
4943
- this.buffer.bg[index + 1] = bg2.g;
4944
- this.buffer.bg[index + 2] = bg2.b;
4945
- this.buffer.bg[index + 3] = bg2.a;
4946
- }
4800
+ clear(bg2 = RGBA.fromValues(0, 0, 0, 1)) {
4801
+ this.lib.bufferClear(this.bufferPtr, bg2);
4947
4802
  }
4948
4803
  setCell(x, y, char, fg2, bg2, attributes = 0) {
4949
- if (x < 0 || x >= this._width || y < 0 || y >= this._height)
4950
- return;
4951
- const index = this.coordsToIndex(x, y);
4952
- const colorIndex = index * 4;
4953
- this.buffer.char[index] = char.charCodeAt(0);
4954
- this.buffer.attributes[index] = attributes;
4955
- this.buffer.fg[colorIndex] = fg2.r;
4956
- this.buffer.fg[colorIndex + 1] = fg2.g;
4957
- this.buffer.fg[colorIndex + 2] = fg2.b;
4958
- this.buffer.fg[colorIndex + 3] = fg2.a;
4959
- this.buffer.bg[colorIndex] = bg2.r;
4960
- this.buffer.bg[colorIndex + 1] = bg2.g;
4961
- this.buffer.bg[colorIndex + 2] = bg2.b;
4962
- this.buffer.bg[colorIndex + 3] = bg2.a;
4963
- }
4964
- get(x, y) {
4965
- if (x < 0 || x >= this._width || y < 0 || y >= this._height)
4966
- return null;
4967
- const index = this.coordsToIndex(x, y);
4968
- const colorIndex = index * 4;
4969
- return {
4970
- char: this.buffer.char[index],
4971
- fg: RGBA.fromArray(this.buffer.fg.slice(colorIndex, colorIndex + 4)),
4972
- bg: RGBA.fromArray(this.buffer.bg.slice(colorIndex, colorIndex + 4)),
4973
- attributes: this.buffer.attributes[index]
4974
- };
4804
+ this.lib.bufferSetCell(this.bufferPtr, x, y, char, fg2, bg2, attributes);
4975
4805
  }
4976
4806
  setCellWithAlphaBlending(x, y, char, fg2, bg2, attributes = 0) {
4977
- if (this.useFFI) {
4978
- this.setCellWithAlphaBlendingFFI(x, y, char, fg2, bg2, attributes);
4979
- } else {
4980
- this.setCellWithAlphaBlendingLocal(x, y, char, fg2, bg2, attributes);
4981
- }
4982
- }
4983
- setCellWithAlphaBlendingLocal(x, y, char, fg2, bg2, attributes = 0) {
4984
- if (x < 0 || x >= this._width || y < 0 || y >= this._height)
4985
- return;
4986
- const hasBgAlpha = isRGBAWithAlpha(bg2);
4987
- const hasFgAlpha = isRGBAWithAlpha(fg2);
4988
- if (hasBgAlpha || hasFgAlpha) {
4989
- const destCell = this.get(x, y);
4990
- if (destCell) {
4991
- const blendedBgRgb = hasBgAlpha ? blendColors(bg2, destCell.bg) : bg2;
4992
- const preserveChar = char === " " && destCell.char !== 0 && String.fromCharCode(destCell.char) !== " ";
4993
- const finalChar = preserveChar ? destCell.char : char.charCodeAt(0);
4994
- let finalFg;
4995
- if (preserveChar) {
4996
- finalFg = blendColors(bg2, destCell.fg);
4997
- } else {
4998
- finalFg = hasFgAlpha ? blendColors(fg2, destCell.bg) : fg2;
4999
- }
5000
- const finalAttributes = preserveChar ? destCell.attributes : attributes;
5001
- const finalBg = RGBA.fromValues(blendedBgRgb.r, blendedBgRgb.g, blendedBgRgb.b, bg2.a);
5002
- this.setCell(x, y, String.fromCharCode(finalChar), finalFg, finalBg, finalAttributes);
5003
- return;
5004
- }
5005
- }
5006
- this.setCell(x, y, char, fg2, bg2, attributes);
4807
+ this.lib.bufferSetCellWithAlphaBlending(this.bufferPtr, x, y, char, fg2, bg2, attributes);
5007
4808
  }
5008
4809
  drawText(text, x, y, fg2, bg2, attributes = 0, selection2) {
5009
4810
  if (!selection2) {
5010
- this.drawTextFFI.call(this, text, x, y, fg2, bg2, attributes);
4811
+ this.lib.bufferDrawText(this.bufferPtr, text, x, y, fg2, bg2, attributes);
5011
4812
  return;
5012
4813
  }
5013
4814
  const { start, end } = selection2;
@@ -5023,123 +4824,22 @@ class OptimizedBuffer {
5023
4824
  }
5024
4825
  if (start > 0) {
5025
4826
  const beforeText = text.slice(0, start);
5026
- this.drawTextFFI.call(this, beforeText, x, y, fg2, bg2, attributes);
4827
+ this.lib.bufferDrawText(this.bufferPtr, beforeText, x, y, fg2, bg2, attributes);
5027
4828
  }
5028
4829
  if (end > start) {
5029
4830
  const selectedText = text.slice(start, end);
5030
- this.drawTextFFI.call(this, selectedText, x + start, y, selectionFg, selectionBg, attributes);
4831
+ this.lib.bufferDrawText(this.bufferPtr, selectedText, x + start, y, selectionFg, selectionBg, attributes);
5031
4832
  }
5032
4833
  if (end < text.length) {
5033
4834
  const afterText = text.slice(end);
5034
- this.drawTextFFI.call(this, afterText, x + end, y, fg2, bg2, attributes);
4835
+ this.lib.bufferDrawText(this.bufferPtr, afterText, x + end, y, fg2, bg2, attributes);
5035
4836
  }
5036
4837
  }
5037
4838
  fillRect(x, y, width, height, bg2) {
5038
- if (this.useFFI) {
5039
- this.fillRectFFI(x, y, width, height, bg2);
5040
- } else {
5041
- this.fillRectLocal(x, y, width, height, bg2);
5042
- }
5043
- }
5044
- fillRectLocal(x, y, width, height, bg2) {
5045
- const startX = Math.max(0, x);
5046
- const startY = Math.max(0, y);
5047
- const endX = Math.min(this.width - 1, x + width - 1);
5048
- const endY = Math.min(this.height - 1, y + height - 1);
5049
- if (startX > endX || startY > endY)
5050
- return;
5051
- const hasAlpha = isRGBAWithAlpha(bg2);
5052
- if (hasAlpha) {
5053
- const fg2 = RGBA.fromValues(1, 1, 1, 1);
5054
- for (let fillY = startY;fillY <= endY; fillY++) {
5055
- for (let fillX = startX;fillX <= endX; fillX++) {
5056
- this.setCellWithAlphaBlending(fillX, fillY, " ", fg2, bg2, 0);
5057
- }
5058
- }
5059
- } else {
5060
- for (let fillY = startY;fillY <= endY; fillY++) {
5061
- for (let fillX = startX;fillX <= endX; fillX++) {
5062
- const index = this.coordsToIndex(fillX, fillY);
5063
- const colorIndex = index * 4;
5064
- this.buffer.char[index] = 32;
5065
- this.buffer.attributes[index] = 0;
5066
- this.buffer.fg[colorIndex] = 1;
5067
- this.buffer.fg[colorIndex + 1] = 1;
5068
- this.buffer.fg[colorIndex + 2] = 1;
5069
- this.buffer.fg[colorIndex + 3] = 1;
5070
- this.buffer.bg[colorIndex] = bg2.r;
5071
- this.buffer.bg[colorIndex + 1] = bg2.g;
5072
- this.buffer.bg[colorIndex + 2] = bg2.b;
5073
- this.buffer.bg[colorIndex + 3] = bg2.a;
5074
- }
5075
- }
5076
- }
4839
+ this.lib.bufferFillRect(this.bufferPtr, x, y, width, height, bg2);
5077
4840
  }
5078
4841
  drawFrameBuffer(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight) {
5079
- this.drawFrameBufferFFI(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight);
5080
- }
5081
- drawFrameBufferLocal(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight) {
5082
- const srcX = sourceX ?? 0;
5083
- const srcY = sourceY ?? 0;
5084
- const srcWidth = sourceWidth ?? frameBuffer.width;
5085
- const srcHeight = sourceHeight ?? frameBuffer.height;
5086
- if (srcX >= frameBuffer.width || srcY >= frameBuffer.height)
5087
- return;
5088
- if (srcWidth === 0 || srcHeight === 0)
5089
- return;
5090
- const clampedSrcWidth = Math.min(srcWidth, frameBuffer.width - srcX);
5091
- const clampedSrcHeight = Math.min(srcHeight, frameBuffer.height - srcY);
5092
- const startDestX = Math.max(0, destX);
5093
- const startDestY = Math.max(0, destY);
5094
- const endDestX = Math.min(this._width - 1, destX + clampedSrcWidth - 1);
5095
- const endDestY = Math.min(this._height - 1, destY + clampedSrcHeight - 1);
5096
- if (!frameBuffer.respectAlpha) {
5097
- for (let dY = startDestY;dY <= endDestY; dY++) {
5098
- for (let dX = startDestX;dX <= endDestX; dX++) {
5099
- const relativeDestX = dX - destX;
5100
- const relativeDestY = dY - destY;
5101
- const sX = srcX + relativeDestX;
5102
- const sY = srcY + relativeDestY;
5103
- if (sX >= frameBuffer.width || sY >= frameBuffer.height)
5104
- continue;
5105
- const destIndex = this.coordsToIndex(dX, dY);
5106
- const srcIndex = frameBuffer.coordsToIndex(sX, sY);
5107
- const destColorIndex = destIndex * 4;
5108
- const srcColorIndex = srcIndex * 4;
5109
- this.buffer.char[destIndex] = frameBuffer.buffer.char[srcIndex];
5110
- this.buffer.attributes[destIndex] = frameBuffer.buffer.attributes[srcIndex];
5111
- this.buffer.fg[destColorIndex] = frameBuffer.buffer.fg[srcColorIndex];
5112
- this.buffer.fg[destColorIndex + 1] = frameBuffer.buffer.fg[srcColorIndex + 1];
5113
- this.buffer.fg[destColorIndex + 2] = frameBuffer.buffer.fg[srcColorIndex + 2];
5114
- this.buffer.fg[destColorIndex + 3] = frameBuffer.buffer.fg[srcColorIndex + 3];
5115
- this.buffer.bg[destColorIndex] = frameBuffer.buffer.bg[srcColorIndex];
5116
- this.buffer.bg[destColorIndex + 1] = frameBuffer.buffer.bg[srcColorIndex + 1];
5117
- this.buffer.bg[destColorIndex + 2] = frameBuffer.buffer.bg[srcColorIndex + 2];
5118
- this.buffer.bg[destColorIndex + 3] = frameBuffer.buffer.bg[srcColorIndex + 3];
5119
- }
5120
- }
5121
- return;
5122
- }
5123
- for (let dY = startDestY;dY <= endDestY; dY++) {
5124
- for (let dX = startDestX;dX <= endDestX; dX++) {
5125
- const relativeDestX = dX - destX;
5126
- const relativeDestY = dY - destY;
5127
- const sX = srcX + relativeDestX;
5128
- const sY = srcY + relativeDestY;
5129
- if (sX >= frameBuffer.width || sY >= frameBuffer.height)
5130
- continue;
5131
- const srcIndex = frameBuffer.coordsToIndex(sX, sY);
5132
- const srcColorIndex = srcIndex * 4;
5133
- if (frameBuffer.buffer.bg[srcColorIndex + 3] === 0 && frameBuffer.buffer.fg[srcColorIndex + 3] === 0) {
5134
- continue;
5135
- }
5136
- const charCode = frameBuffer.buffer.char[srcIndex];
5137
- const fg2 = RGBA.fromArray(frameBuffer.buffer.fg.slice(srcColorIndex, srcColorIndex + 4));
5138
- const bg2 = RGBA.fromArray(frameBuffer.buffer.bg.slice(srcColorIndex, srcColorIndex + 4));
5139
- const attributes = frameBuffer.buffer.attributes[srcIndex];
5140
- this.setCellWithAlphaBlending(dX, dY, String.fromCharCode(charCode), fg2, bg2, attributes);
5141
- }
5142
- }
4842
+ this.lib.drawFrameBuffer(this.bufferPtr, destX, destY, frameBuffer.ptr, sourceX, sourceY, sourceWidth, sourceHeight);
5143
4843
  }
5144
4844
  destroy() {
5145
4845
  this.lib.destroyOptimizedBuffer(this.bufferPtr);
@@ -5148,35 +4848,18 @@ class OptimizedBuffer {
5148
4848
  this.lib.bufferDrawTextBuffer(this.bufferPtr, textBuffer.ptr, x, y, clipRect);
5149
4849
  }
5150
4850
  drawSuperSampleBuffer(x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow) {
5151
- this.drawSuperSampleBufferFFI(x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow);
5152
- }
5153
- drawSuperSampleBufferFFI(x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow) {
5154
4851
  this.lib.bufferDrawSuperSampleBuffer(this.bufferPtr, x, y, pixelDataPtr, pixelDataLength, format, alignedBytesPerRow);
5155
4852
  }
5156
4853
  drawPackedBuffer(dataPtr, dataLen, posX, posY, terminalWidthCells, terminalHeightCells) {
5157
4854
  this.lib.bufferDrawPackedBuffer(this.bufferPtr, dataPtr, dataLen, posX, posY, terminalWidthCells, terminalHeightCells);
5158
4855
  }
5159
- setCellWithAlphaBlendingFFI(x, y, char, fg2, bg2, attributes) {
5160
- this.lib.bufferSetCellWithAlphaBlending(this.bufferPtr, x, y, char, fg2, bg2, attributes);
5161
- }
5162
- fillRectFFI(x, y, width, height, bg2) {
5163
- this.lib.bufferFillRect(this.bufferPtr, x, y, width, height, bg2);
5164
- }
5165
4856
  resize(width, height) {
5166
4857
  if (this._width === width && this._height === height)
5167
4858
  return;
5168
4859
  this._width = width;
5169
4860
  this._height = height;
5170
- this.buffer = this.lib.bufferResize(this.bufferPtr, width, height);
5171
- }
5172
- clearFFI(bg2 = RGBA.fromValues(0, 0, 0, 1)) {
5173
- this.lib.bufferClear(this.bufferPtr, bg2);
5174
- }
5175
- drawTextFFI(text, x, y, fg2 = RGBA.fromValues(1, 1, 1, 1), bg2, attributes = 0) {
5176
- this.lib.bufferDrawText(this.bufferPtr, text, x, y, fg2, bg2, attributes);
5177
- }
5178
- drawFrameBufferFFI(destX, destY, frameBuffer, sourceX, sourceY, sourceWidth, sourceHeight) {
5179
- 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);
5180
4863
  }
5181
4864
  drawBox(options) {
5182
4865
  const style = options.borderStyle || "single";
@@ -5308,6 +4991,10 @@ function getOpenTUILib(libPath) {
5308
4991
  args: ["ptr", "u32", "u32", "u32", "ptr", "ptr", "u8"],
5309
4992
  returns: "void"
5310
4993
  },
4994
+ bufferSetCell: {
4995
+ args: ["ptr", "u32", "u32", "u32", "ptr", "ptr", "u8"],
4996
+ returns: "void"
4997
+ },
5311
4998
  bufferFillRect: {
5312
4999
  args: ["ptr", "u32", "u32", "u32", "u32", "ptr"],
5313
5000
  returns: "void"
@@ -5420,30 +5107,10 @@ function getOpenTUILib(libPath) {
5420
5107
  args: ["ptr"],
5421
5108
  returns: "ptr"
5422
5109
  },
5423
- textBufferGetFgPtr: {
5424
- args: ["ptr"],
5425
- returns: "ptr"
5426
- },
5427
- textBufferGetBgPtr: {
5428
- args: ["ptr"],
5429
- returns: "ptr"
5430
- },
5431
- textBufferGetAttributesPtr: {
5432
- args: ["ptr"],
5433
- returns: "ptr"
5434
- },
5435
5110
  textBufferGetLength: {
5436
5111
  args: ["ptr"],
5437
5112
  returns: "u32"
5438
5113
  },
5439
- textBufferSetCell: {
5440
- args: ["ptr", "u32", "u32", "ptr", "ptr", "u16"],
5441
- returns: "void"
5442
- },
5443
- textBufferConcat: {
5444
- args: ["ptr", "ptr"],
5445
- returns: "ptr"
5446
- },
5447
5114
  textBufferResize: {
5448
5115
  args: ["ptr", "u32"],
5449
5116
  returns: "void"
@@ -5488,17 +5155,29 @@ function getOpenTUILib(libPath) {
5488
5155
  args: ["ptr"],
5489
5156
  returns: "void"
5490
5157
  },
5491
- textBufferGetLineStartsPtr: {
5158
+ textBufferGetLineCount: {
5492
5159
  args: ["ptr"],
5493
- returns: "ptr"
5160
+ returns: "u32"
5161
+ },
5162
+ textBufferGetLineInfoDirect: {
5163
+ args: ["ptr", "ptr", "ptr"],
5164
+ returns: "void"
5494
5165
  },
5495
- textBufferGetLineWidthsPtr: {
5166
+ textBufferGetSelectionInfo: {
5496
5167
  args: ["ptr"],
5497
- returns: "ptr"
5168
+ returns: "u64"
5498
5169
  },
5499
- 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: {
5500
5179
  args: ["ptr"],
5501
- returns: "u32"
5180
+ returns: "void"
5502
5181
  },
5503
5182
  bufferDrawTextBuffer: {
5504
5183
  args: ["ptr", "ptr", "i32", "i32", "i32", "i32", "u32", "u32", "bool"],
@@ -5541,7 +5220,7 @@ class FFIRenderLib {
5541
5220
  if (msgLen === 0 || !msgPtr) {
5542
5221
  return;
5543
5222
  }
5544
- const msgBuffer = toArrayBuffer(msgPtr, 0, msgLen);
5223
+ const msgBuffer = toArrayBuffer2(msgPtr, 0, msgLen);
5545
5224
  const msgBytes = new Uint8Array(msgBuffer);
5546
5225
  const message = this.decoder.decode(msgBytes);
5547
5226
  switch (level) {
@@ -5604,9 +5283,7 @@ class FFIRenderLib {
5604
5283
  }
5605
5284
  const width = this.opentui.symbols.getBufferWidth(bufferPtr);
5606
5285
  const height = this.opentui.symbols.getBufferHeight(bufferPtr);
5607
- const size = width * height;
5608
- const buffers = this.getBuffer(bufferPtr, size);
5609
- return new OptimizedBuffer(this, bufferPtr, buffers, width, height, { id: "next buffer" });
5286
+ return new OptimizedBuffer(this, bufferPtr, width, height, { id: "next buffer" });
5610
5287
  }
5611
5288
  getCurrentBuffer(renderer) {
5612
5289
  const bufferPtr = this.opentui.symbols.getCurrentBuffer(renderer);
@@ -5615,41 +5292,7 @@ class FFIRenderLib {
5615
5292
  }
5616
5293
  const width = this.opentui.symbols.getBufferWidth(bufferPtr);
5617
5294
  const height = this.opentui.symbols.getBufferHeight(bufferPtr);
5618
- const size = width * height;
5619
- const buffers = this.getBuffer(bufferPtr, size);
5620
- return new OptimizedBuffer(this, bufferPtr, buffers, width, height, { id: "current buffer" });
5621
- }
5622
- getBuffer(bufferPtr, size) {
5623
- const charPtr = this.opentui.symbols.bufferGetCharPtr(bufferPtr);
5624
- const fgPtr = this.opentui.symbols.bufferGetFgPtr(bufferPtr);
5625
- const bgPtr = this.opentui.symbols.bufferGetBgPtr(bufferPtr);
5626
- const attributesPtr = this.opentui.symbols.bufferGetAttributesPtr(bufferPtr);
5627
- if (!charPtr || !fgPtr || !bgPtr || !attributesPtr) {
5628
- throw new Error("Failed to get buffer pointers");
5629
- }
5630
- const buffers = {
5631
- char: new Uint32Array(toArrayBuffer(charPtr, 0, size * 4)),
5632
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, size * 4 * 4)),
5633
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, size * 4 * 4)),
5634
- attributes: new Uint8Array(toArrayBuffer(attributesPtr, 0, size))
5635
- };
5636
- return buffers;
5637
- }
5638
- getTextBuffer(bufferPtr, size) {
5639
- const charPtr = this.opentui.symbols.textBufferGetCharPtr(bufferPtr);
5640
- const fgPtr = this.opentui.symbols.textBufferGetFgPtr(bufferPtr);
5641
- const bgPtr = this.opentui.symbols.textBufferGetBgPtr(bufferPtr);
5642
- const attributesPtr = this.opentui.symbols.textBufferGetAttributesPtr(bufferPtr);
5643
- if (!charPtr || !fgPtr || !bgPtr || !attributesPtr) {
5644
- throw new Error("Failed to get text buffer pointers");
5645
- }
5646
- const buffers = {
5647
- char: new Uint32Array(toArrayBuffer(charPtr, 0, size * 4)),
5648
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, size * 4 * 4)),
5649
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, size * 4 * 4)),
5650
- attributes: new Uint16Array(toArrayBuffer(attributesPtr, 0, size * 2))
5651
- };
5652
- return buffers;
5295
+ return new OptimizedBuffer(this, bufferPtr, width, height, { id: "current buffer" });
5653
5296
  }
5654
5297
  bufferGetCharPtr(buffer) {
5655
5298
  const ptr2 = this.opentui.symbols.bufferGetCharPtr(buffer);
@@ -5714,6 +5357,12 @@ class FFIRenderLib {
5714
5357
  const fg2 = color.buffer;
5715
5358
  this.opentui.symbols.bufferSetCellWithAlphaBlending(buffer, x, y, charPtr, fg2, bg2, attributes ?? 0);
5716
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
+ }
5717
5366
  bufferFillRect(buffer, x, y, width, height, color) {
5718
5367
  const bg2 = color.buffer;
5719
5368
  this.opentui.symbols.bufferFillRect(buffer, x, y, width, height, bg2);
@@ -5733,8 +5382,6 @@ class FFIRenderLib {
5733
5382
  }
5734
5383
  bufferResize(buffer, width, height) {
5735
5384
  this.opentui.symbols.bufferResize(buffer, width, height);
5736
- const buffers = this.getBuffer(buffer, width * height);
5737
- return buffers;
5738
5385
  }
5739
5386
  resizeRenderer(renderer, width, height) {
5740
5387
  this.opentui.symbols.resizeRenderer(renderer, width, height);
@@ -5763,9 +5410,7 @@ class FFIRenderLib {
5763
5410
  if (!bufferPtr) {
5764
5411
  throw new Error(`Failed to create optimized buffer: ${width}x${height}`);
5765
5412
  }
5766
- const size = width * height;
5767
- const buffers = this.getBuffer(bufferPtr, size);
5768
- return new OptimizedBuffer(this, bufferPtr, buffers, width, height, { respectAlpha, id });
5413
+ return new OptimizedBuffer(this, bufferPtr, width, height, { respectAlpha, id });
5769
5414
  }
5770
5415
  destroyOptimizedBuffer(bufferPtr) {
5771
5416
  this.opentui.symbols.destroyOptimizedBuffer(bufferPtr);
@@ -5825,17 +5470,7 @@ class FFIRenderLib {
5825
5470
  if (!bufferPtr) {
5826
5471
  throw new Error(`Failed to create TextBuffer with capacity ${capacity}`);
5827
5472
  }
5828
- const charPtr = this.textBufferGetCharPtr(bufferPtr);
5829
- const fgPtr = this.textBufferGetFgPtr(bufferPtr);
5830
- const bgPtr = this.textBufferGetBgPtr(bufferPtr);
5831
- const attributesPtr = this.textBufferGetAttributesPtr(bufferPtr);
5832
- const buffer = {
5833
- char: new Uint32Array(toArrayBuffer(charPtr, 0, capacity * 4)),
5834
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, capacity * 4 * 4)),
5835
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, capacity * 4 * 4)),
5836
- attributes: new Uint16Array(toArrayBuffer(attributesPtr, 0, capacity * 2))
5837
- };
5838
- return new TextBuffer(this, bufferPtr, buffer, capacity);
5473
+ return new TextBuffer(this, bufferPtr, capacity);
5839
5474
  }
5840
5475
  destroyTextBuffer(buffer) {
5841
5476
  this.opentui.symbols.destroyTextBuffer(buffer);
@@ -5847,55 +5482,11 @@ class FFIRenderLib {
5847
5482
  }
5848
5483
  return ptr2;
5849
5484
  }
5850
- textBufferGetFgPtr(buffer) {
5851
- const ptr2 = this.opentui.symbols.textBufferGetFgPtr(buffer);
5852
- if (!ptr2) {
5853
- throw new Error("Failed to get TextBuffer fg pointer");
5854
- }
5855
- return ptr2;
5856
- }
5857
- textBufferGetBgPtr(buffer) {
5858
- const ptr2 = this.opentui.symbols.textBufferGetBgPtr(buffer);
5859
- if (!ptr2) {
5860
- throw new Error("Failed to get TextBuffer bg pointer");
5861
- }
5862
- return ptr2;
5863
- }
5864
- textBufferGetAttributesPtr(buffer) {
5865
- const ptr2 = this.opentui.symbols.textBufferGetAttributesPtr(buffer);
5866
- if (!ptr2) {
5867
- throw new Error("Failed to get TextBuffer attributes pointer");
5868
- }
5869
- return ptr2;
5870
- }
5871
5485
  textBufferGetLength(buffer) {
5872
5486
  return this.opentui.symbols.textBufferGetLength(buffer);
5873
5487
  }
5874
- textBufferSetCell(buffer, index, char, fg2, bg2, attr) {
5875
- this.opentui.symbols.textBufferSetCell(buffer, index, char, fg2, bg2, attr);
5876
- }
5877
- textBufferConcat(buffer1, buffer2) {
5878
- const resultPtr = this.opentui.symbols.textBufferConcat(buffer1, buffer2);
5879
- if (!resultPtr) {
5880
- throw new Error("Failed to concatenate TextBuffers");
5881
- }
5882
- const length = this.textBufferGetLength(resultPtr);
5883
- const charPtr = this.textBufferGetCharPtr(resultPtr);
5884
- const fgPtr = this.textBufferGetFgPtr(resultPtr);
5885
- const bgPtr = this.textBufferGetBgPtr(resultPtr);
5886
- const attributesPtr = this.textBufferGetAttributesPtr(resultPtr);
5887
- const buffer = {
5888
- char: new Uint32Array(toArrayBuffer(charPtr, 0, length * 4)),
5889
- fg: new Float32Array(toArrayBuffer(fgPtr, 0, length * 4 * 4)),
5890
- bg: new Float32Array(toArrayBuffer(bgPtr, 0, length * 4 * 4)),
5891
- attributes: new Uint16Array(toArrayBuffer(attributesPtr, 0, length * 2))
5892
- };
5893
- return new TextBuffer(this, resultPtr, buffer, length);
5894
- }
5895
5488
  textBufferResize(buffer, newLength) {
5896
5489
  this.opentui.symbols.textBufferResize(buffer, newLength);
5897
- const buffers = this.getTextBuffer(buffer, newLength);
5898
- return buffers;
5899
5490
  }
5900
5491
  textBufferReset(buffer) {
5901
5492
  this.opentui.symbols.textBufferReset(buffer);
@@ -5933,24 +5524,56 @@ class FFIRenderLib {
5933
5524
  textBufferFinalizeLineInfo(buffer) {
5934
5525
  this.opentui.symbols.textBufferFinalizeLineInfo(buffer);
5935
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
+ }
5936
5565
  textBufferGetLineInfo(buffer) {
5937
- const lineCount = this.opentui.symbols.textBufferGetLineCount(buffer);
5566
+ const lineCount = this.textBufferGetLineCount(buffer);
5938
5567
  if (lineCount === 0) {
5939
5568
  return { lineStarts: [], lineWidths: [] };
5940
5569
  }
5941
- const lineStartsPtr = this.opentui.symbols.textBufferGetLineStartsPtr(buffer);
5942
- const lineWidthsPtr = this.opentui.symbols.textBufferGetLineWidthsPtr(buffer);
5943
- if (!lineStartsPtr || !lineWidthsPtr) {
5944
- return { lineStarts: [], lineWidths: [] };
5945
- }
5946
- const lineStartsArray = new Uint32Array(toArrayBuffer(lineStartsPtr, 0, lineCount * 4));
5947
- const lineWidthsArray = new Uint32Array(toArrayBuffer(lineWidthsPtr, 0, lineCount * 4));
5948
- const lineStarts = Array.from(lineStartsArray);
5949
- const lineWidths = Array.from(lineWidthsArray);
5950
- return { lineStarts, lineWidths };
5951
- }
5952
- getTextBufferArrays(buffer, size) {
5953
- 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
+ };
5954
5577
  }
5955
5578
  bufferDrawTextBuffer(buffer, textBuffer, x, y, clipRect) {
5956
5579
  const hasClipRect = clipRect !== undefined && clipRect !== null;
@@ -6021,25 +5644,18 @@ try {
6021
5644
  class TextBuffer {
6022
5645
  lib;
6023
5646
  bufferPtr;
6024
- buffer;
6025
5647
  _length = 0;
6026
5648
  _capacity;
6027
5649
  _lineInfo;
6028
- constructor(lib, ptr2, buffer, capacity) {
5650
+ constructor(lib, ptr2, capacity) {
6029
5651
  this.lib = lib;
6030
5652
  this.bufferPtr = ptr2;
6031
- this.buffer = buffer;
6032
5653
  this._capacity = capacity;
6033
5654
  }
6034
5655
  static create(capacity = 256, widthMethod) {
6035
5656
  const lib = resolveRenderLib();
6036
5657
  return lib.createTextBuffer(capacity, widthMethod);
6037
5658
  }
6038
- syncBuffersAfterResize() {
6039
- const capacity = this.lib.textBufferGetCapacity(this.bufferPtr);
6040
- this.buffer = this.lib.getTextBufferArrays(this.bufferPtr, capacity);
6041
- this._capacity = capacity;
6042
- }
6043
5659
  setStyledText(text) {
6044
5660
  this.lib.textBufferReset(this.bufferPtr);
6045
5661
  this._length = 0;
@@ -6047,7 +5663,7 @@ class TextBuffer {
6047
5663
  for (const chunk of text.chunks) {
6048
5664
  const result = this.lib.textBufferWriteChunk(this.bufferPtr, chunk.text, chunk.fg || null, chunk.bg || null, chunk.attributes ?? null);
6049
5665
  if (result & 1) {
6050
- this.syncBuffersAfterResize();
5666
+ this._capacity = this.lib.textBufferGetCapacity(this.bufferPtr);
6051
5667
  }
6052
5668
  }
6053
5669
  this.lib.textBufferFinalizeLineInfo(this.bufferPtr);
@@ -6074,28 +5690,38 @@ class TextBuffer {
6074
5690
  get ptr() {
6075
5691
  return this.bufferPtr;
6076
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
+ }
6077
5701
  get lineInfo() {
6078
5702
  if (!this._lineInfo) {
6079
5703
  this._lineInfo = this.lib.textBufferGetLineInfo(this.bufferPtr);
6080
5704
  }
6081
5705
  return this._lineInfo;
6082
5706
  }
6083
- toString() {
6084
- const chars = [];
6085
- for (let i = 0;i < this._length; i++) {
6086
- chars.push(String.fromCharCode(this.buffer.char[i]));
6087
- }
6088
- return chars.join("");
6089
- }
6090
- concat(other) {
6091
- return this.lib.textBufferConcat(this.bufferPtr, other.bufferPtr);
6092
- }
6093
5707
  setSelection(start, end, bgColor, fgColor) {
6094
5708
  this.lib.textBufferSetSelection(this.bufferPtr, start, end, bgColor || null, fgColor || null);
6095
5709
  }
6096
5710
  resetSelection() {
6097
5711
  this.lib.textBufferResetSelection(this.bufferPtr);
6098
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
+ }
6099
5725
  destroy() {
6100
5726
  this.lib.destroyTextBuffer(this.bufferPtr);
6101
5727
  }
@@ -6103,6 +5729,7 @@ class TextBuffer {
6103
5729
 
6104
5730
  // src/Renderable.ts
6105
5731
  import { EventEmitter as EventEmitter3 } from "events";
5732
+ var BrandedRenderable = Symbol.for("@opentui/core/Renderable");
6106
5733
  var LayoutEvents;
6107
5734
  ((LayoutEvents2) => {
6108
5735
  LayoutEvents2["LAYOUT_CHANGED"] = "layout-changed";
@@ -6186,8 +5813,12 @@ function isSizeType(value) {
6186
5813
  }
6187
5814
  return isValidPercentage(value);
6188
5815
  }
5816
+ function isRenderable(obj) {
5817
+ return !!obj?.[BrandedRenderable];
5818
+ }
6189
5819
 
6190
5820
  class Renderable extends EventEmitter3 {
5821
+ [BrandedRenderable] = true;
6191
5822
  static renderableNumber = 1;
6192
5823
  static renderablesByNumber = new Map;
6193
5824
  id;
@@ -6213,6 +5844,7 @@ class Renderable extends EventEmitter3 {
6213
5844
  keypressHandler = null;
6214
5845
  _live = false;
6215
5846
  _liveCount = 0;
5847
+ _sizeChangeListener = undefined;
6216
5848
  _mouseListener = null;
6217
5849
  _mouseListeners = {};
6218
5850
  _keyListeners = {};
@@ -6220,11 +5852,12 @@ class Renderable extends EventEmitter3 {
6220
5852
  _positionType = "relative";
6221
5853
  _overflow = "visible";
6222
5854
  _position = {};
6223
- _childHostOverride = null;
6224
5855
  renderableMap = new Map;
6225
5856
  renderableArray = [];
6226
5857
  needsZIndexSort = false;
6227
5858
  parent = null;
5859
+ childrenPrimarySortDirty = true;
5860
+ childrenSortedByPrimaryAxis = [];
6228
5861
  renderBefore;
6229
5862
  renderAfter;
6230
5863
  constructor(ctx, options) {
@@ -6294,10 +5927,6 @@ class Renderable extends EventEmitter3 {
6294
5927
  return false;
6295
5928
  }
6296
5929
  focus() {
6297
- if (this.childHost !== this) {
6298
- this.childHost.focus();
6299
- return;
6300
- }
6301
5930
  if (this._focused || !this.focusable)
6302
5931
  return;
6303
5932
  this._focused = true;
@@ -6312,10 +5941,6 @@ class Renderable extends EventEmitter3 {
6312
5941
  this.emit("focused" /* FOCUSED */);
6313
5942
  }
6314
5943
  blur() {
6315
- if (this.childHost !== this) {
6316
- this.childHost.blur();
6317
- return;
6318
- }
6319
5944
  if (!this._focused || !this.focusable)
6320
5945
  return;
6321
5946
  this._focused = false;
@@ -6351,12 +5976,6 @@ class Renderable extends EventEmitter3 {
6351
5976
  get isDirty() {
6352
5977
  return this._dirty;
6353
5978
  }
6354
- get childHost() {
6355
- return this._childHostOverride || this;
6356
- }
6357
- set childHost(host) {
6358
- this._childHostOverride = host;
6359
- }
6360
5979
  findDescendantById(id) {
6361
5980
  for (const child of this.renderableArray) {
6362
5981
  if (child.id === id)
@@ -6367,14 +5986,6 @@ class Renderable extends EventEmitter3 {
6367
5986
  }
6368
5987
  return;
6369
5988
  }
6370
- setChildHostById(id) {
6371
- const found = this.findDescendantById(id);
6372
- if (found) {
6373
- this._childHostOverride = found;
6374
- return true;
6375
- }
6376
- return false;
6377
- }
6378
5989
  markClean() {
6379
5990
  this._dirty = false;
6380
5991
  }
@@ -6390,6 +6001,8 @@ class Renderable extends EventEmitter3 {
6390
6001
  return;
6391
6002
  this._translateX = value;
6392
6003
  this.requestRender();
6004
+ if (this.parent)
6005
+ this.parent.childrenPrimarySortDirty = true;
6393
6006
  }
6394
6007
  get translateY() {
6395
6008
  return this._translateY;
@@ -6399,6 +6012,8 @@ class Renderable extends EventEmitter3 {
6399
6012
  return;
6400
6013
  this._translateY = value;
6401
6014
  this.requestRender();
6015
+ if (this.parent)
6016
+ this.parent.childrenPrimarySortDirty = true;
6402
6017
  }
6403
6018
  get x() {
6404
6019
  if (this.parent && this._positionType === "relative") {
@@ -6488,6 +6103,96 @@ class Renderable extends EventEmitter3 {
6488
6103
  this.needsZIndexSort = false;
6489
6104
  }
6490
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
+ }
6491
6196
  setupYogaProperties(options) {
6492
6197
  const node = this.layoutNode.yogaNode;
6493
6198
  if (isFlexBasisType(options.flexBasis)) {
@@ -6783,6 +6488,8 @@ class Renderable extends EventEmitter3 {
6783
6488
  }
6784
6489
  updateFromLayout() {
6785
6490
  const layout = this.layoutNode.yogaNode.getComputedLayout();
6491
+ const oldX = this._x;
6492
+ const oldY = this._y;
6786
6493
  this._x = layout.left;
6787
6494
  this._y = layout.top;
6788
6495
  const newWidth = Math.max(layout.width, 1);
@@ -6793,6 +6500,10 @@ class Renderable extends EventEmitter3 {
6793
6500
  if (sizeChanged) {
6794
6501
  this.onLayoutResize(newWidth, newHeight);
6795
6502
  }
6503
+ if (oldX !== this._x || oldY !== this._y) {
6504
+ if (this.parent)
6505
+ this.parent.childrenPrimarySortDirty = true;
6506
+ }
6796
6507
  }
6797
6508
  onLayoutResize(width, height) {
6798
6509
  if (this._visible) {
@@ -6828,6 +6539,7 @@ class Renderable extends EventEmitter3 {
6828
6539
  }
6829
6540
  }
6830
6541
  onResize(width, height) {
6542
+ this.onSizeChange?.();
6831
6543
  this.emit("resize");
6832
6544
  }
6833
6545
  replaceParent(obj) {
@@ -6837,9 +6549,6 @@ class Renderable extends EventEmitter3 {
6837
6549
  obj.parent = this;
6838
6550
  }
6839
6551
  add(obj, index) {
6840
- if (this.childHost !== this) {
6841
- return this.childHost.add(obj, index);
6842
- }
6843
6552
  obj = ensureRenderable(this._ctx, obj);
6844
6553
  if (this.renderableMap.has(obj.id)) {
6845
6554
  console.warn(`A renderable with id ${obj.id} already exists in ${this.id}, removing it`);
@@ -6856,6 +6565,7 @@ class Renderable extends EventEmitter3 {
6856
6565
  insertedIndex = this.layoutNode.addChild(childLayoutNode);
6857
6566
  }
6858
6567
  this.needsZIndexSort = true;
6568
+ this.childrenPrimarySortDirty = true;
6859
6569
  this.renderableMap.set(obj.id, obj);
6860
6570
  if (obj._liveCount > 0) {
6861
6571
  this.propagateLiveCount(obj._liveCount);
@@ -6864,10 +6574,6 @@ class Renderable extends EventEmitter3 {
6864
6574
  return insertedIndex;
6865
6575
  }
6866
6576
  insertBefore(obj, anchor) {
6867
- if (this.childHost !== this) {
6868
- const idx = this.childHost.insertBefore(obj, anchor);
6869
- return idx;
6870
- }
6871
6577
  obj = ensureRenderable(this._ctx, obj);
6872
6578
  if (!anchor) {
6873
6579
  return this.add(obj);
@@ -6882,15 +6588,9 @@ class Renderable extends EventEmitter3 {
6882
6588
  return this.add(obj, anchorIndex);
6883
6589
  }
6884
6590
  getRenderable(id) {
6885
- if (this.childHost !== this)
6886
- return this.childHost.getRenderable(id);
6887
6591
  return this.renderableMap.get(id);
6888
6592
  }
6889
6593
  remove(id) {
6890
- if (this.childHost !== this) {
6891
- this.childHost.remove(id);
6892
- return;
6893
- }
6894
6594
  if (!id) {
6895
6595
  return;
6896
6596
  }
@@ -6911,18 +6611,20 @@ class Renderable extends EventEmitter3 {
6911
6611
  if (index !== -1) {
6912
6612
  this.renderableArray.splice(index, 1);
6913
6613
  }
6614
+ this.childrenPrimarySortDirty = true;
6914
6615
  }
6915
6616
  }
6916
6617
  onRemove() {}
6917
6618
  getChildren() {
6918
- if (this.childHost !== this)
6919
- return this.childHost.getChildren();
6920
6619
  return [...this.renderableArray];
6921
6620
  }
6621
+ getChildrenCount() {
6622
+ return this.renderableArray.length;
6623
+ }
6922
6624
  render(buffer, deltaTime) {
6923
6625
  if (!this.visible)
6924
6626
  return;
6925
- this.beforeRender();
6627
+ this.onUpdate(deltaTime);
6926
6628
  this.updateFromLayout();
6927
6629
  let renderBuffer = buffer;
6928
6630
  if (this.buffered && this.frameBuffer) {
@@ -6943,7 +6645,7 @@ class Renderable extends EventEmitter3 {
6943
6645
  const scissorRect = this.getScissorRect();
6944
6646
  renderBuffer.pushScissorRect(scissorRect.x, scissorRect.y, scissorRect.width, scissorRect.height);
6945
6647
  }
6946
- for (const child of this.renderableArray) {
6648
+ for (const child of this._getChildren()) {
6947
6649
  child.render(renderBuffer, deltaTime);
6948
6650
  }
6949
6651
  if (shouldPushScissor) {
@@ -6953,7 +6655,10 @@ class Renderable extends EventEmitter3 {
6953
6655
  buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
6954
6656
  }
6955
6657
  }
6956
- beforeRender() {}
6658
+ _getChildren() {
6659
+ return this.renderableArray;
6660
+ }
6661
+ onUpdate(deltaTime) {}
6957
6662
  getScissorRect() {
6958
6663
  return {
6959
6664
  x: this.buffered ? 0 : this.x,
@@ -6994,7 +6699,7 @@ class Renderable extends EventEmitter3 {
6994
6699
  this._mouseListener?.call(this, event);
6995
6700
  this._mouseListeners[event.type]?.call(this, event);
6996
6701
  this.onMouseEvent(event);
6997
- if (this.parent && !event.defaultPrevented) {
6702
+ if (this.parent && !event.propagationStopped) {
6998
6703
  this.parent.processMouseEvent(event);
6999
6704
  }
7000
6705
  }
@@ -7068,6 +6773,12 @@ class Renderable extends EventEmitter3 {
7068
6773
  get onKeyDown() {
7069
6774
  return this._keyListeners["down"];
7070
6775
  }
6776
+ set onSizeChange(handler) {
6777
+ this._sizeChangeListener = handler;
6778
+ }
6779
+ get onSizeChange() {
6780
+ return this._sizeChangeListener;
6781
+ }
7071
6782
  applyEventOptions(options) {
7072
6783
  this.onMouse = options.onMouse;
7073
6784
  this.onMouseDown = options.onMouseDown;
@@ -7080,6 +6791,7 @@ class Renderable extends EventEmitter3 {
7080
6791
  this.onMouseOut = options.onMouseOut;
7081
6792
  this.onMouseScroll = options.onMouseScroll;
7082
6793
  this.onKeyDown = options.onKeyDown;
6794
+ this.onSizeChange = options.onSizeChange;
7083
6795
  }
7084
6796
  }
7085
6797
 
@@ -7117,7 +6829,7 @@ class RootRenderable extends Renderable {
7117
6829
  this.height = height;
7118
6830
  this.emit("resized" /* RESIZED */, { width, height });
7119
6831
  }
7120
- beforeRender() {
6832
+ onUpdate() {
7121
6833
  if (this.layoutNode.yogaNode.isDirty()) {
7122
6834
  this.calculateLayout();
7123
6835
  }
@@ -7198,7 +6910,7 @@ function isVNode(node) {
7198
6910
  return node && node.__isVNode;
7199
6911
  }
7200
6912
  function ensureRenderable(ctx, node) {
7201
- if (node instanceof Renderable)
6913
+ if (isRenderable(node))
7202
6914
  return node;
7203
6915
  return instantiate(ctx, node);
7204
6916
  }
@@ -7246,7 +6958,7 @@ function wrapWithDelegates(instance, delegateMap) {
7246
6958
  return proxy;
7247
6959
  }
7248
6960
  function instantiate(ctx, node) {
7249
- if (node instanceof Renderable)
6961
+ if (isRenderable(node))
7250
6962
  return node;
7251
6963
  if (!node || typeof node !== "object") {
7252
6964
  throw new TypeError("mount() received an invalid vnode");
@@ -7258,7 +6970,7 @@ function instantiate(ctx, node) {
7258
6970
  if (isRenderableConstructor(type)) {
7259
6971
  const instance = new type(ctx, props || {});
7260
6972
  for (const child of children) {
7261
- if (child instanceof Renderable) {
6973
+ if (isRenderable(child)) {
7262
6974
  instance.add(child);
7263
6975
  } else {
7264
6976
  const mounted = instantiate(ctx, child);
@@ -7283,7 +6995,7 @@ function instantiate(ctx, node) {
7283
6995
  return wrapWithDelegates(inst, delegateMap);
7284
6996
  }
7285
6997
  function delegate(mapping, vnode) {
7286
- if (vnode instanceof Renderable) {
6998
+ if (isRenderable(vnode)) {
7287
6999
  return wrapWithDelegates(vnode, mapping);
7288
7000
  }
7289
7001
  if (!vnode || typeof vnode !== "object")
@@ -7295,9 +7007,9 @@ function delegate(mapping, vnode) {
7295
7007
  // src/console.ts
7296
7008
  import { EventEmitter as EventEmitter5 } from "events";
7297
7009
  import { Console } from "console";
7298
- import util from "util";
7299
7010
  import fs from "fs";
7300
7011
  import path from "path";
7012
+ import util from "util";
7301
7013
 
7302
7014
  // src/lib/output.capture.ts
7303
7015
  import { Writable } from "stream";
@@ -7365,27 +7077,9 @@ function getCallerInfo() {
7365
7077
  const columnNumber = parseInt(match[4], 10) || 0;
7366
7078
  return { functionName, fullPath, fileName, lineNumber, columnNumber };
7367
7079
  }
7368
- var { capture } = singleton("ConsoleCapture", () => {
7369
- const capture2 = new Capture;
7370
- const mockStdout = new CapturedWritableStream("stdout", capture2);
7371
- const mockStderr = new CapturedWritableStream("stderr", capture2);
7372
- if (process.env.SKIP_CONSOLE_CACHE !== "true") {
7373
- global.console = new Console({
7374
- stdout: mockStdout,
7375
- stderr: mockStderr,
7376
- colorMode: true,
7377
- inspectOptions: {
7378
- compact: false,
7379
- breakLength: 80,
7380
- depth: 2
7381
- }
7382
- });
7383
- }
7384
- return { capture: capture2 };
7385
- });
7080
+ var capture = singleton("ConsoleCapture", () => new Capture);
7386
7081
 
7387
7082
  class TerminalConsoleCache extends EventEmitter5 {
7388
- originalConsole;
7389
7083
  _cachedLogs = [];
7390
7084
  MAX_CACHE_SIZE = 1000;
7391
7085
  _collectCallerInfo = false;
@@ -7395,35 +7089,26 @@ class TerminalConsoleCache extends EventEmitter5 {
7395
7089
  }
7396
7090
  constructor() {
7397
7091
  super();
7398
- this.originalConsole = {
7399
- log: console.log,
7400
- info: console.info,
7401
- warn: console.warn,
7402
- error: console.error,
7403
- debug: console.debug
7404
- };
7405
- if (process.env.SKIP_CONSOLE_CACHE !== "true") {
7406
- this.activate();
7407
- }
7408
7092
  }
7409
7093
  activate() {
7094
+ this.setupConsoleCapture();
7410
7095
  this.overrideConsoleMethods();
7411
7096
  }
7412
- setCollectCallerInfo(enabled) {
7413
- this._collectCallerInfo = enabled;
7414
- }
7415
- clearConsole() {
7416
- this._cachedLogs = [];
7417
- }
7418
- setCachingEnabled(enabled) {
7419
- this._cachingEnabled = enabled;
7420
- }
7421
- deactivate() {
7422
- console.log = this.originalConsole.log;
7423
- console.info = this.originalConsole.info;
7424
- console.warn = this.originalConsole.warn;
7425
- console.error = this.originalConsole.error;
7426
- 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
+ });
7427
7112
  }
7428
7113
  overrideConsoleMethods() {
7429
7114
  console.log = (...args) => {
@@ -7442,6 +7127,23 @@ class TerminalConsoleCache extends EventEmitter5 {
7442
7127
  this.appendToConsole("DEBUG" /* DEBUG */, ...args);
7443
7128
  };
7444
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
+ }
7445
7147
  addLogEntry(level, ...args) {
7446
7148
  const callerInfo = this._collectCallerInfo ? getCallerInfo() : null;
7447
7149
  const logEntry = [new Date, level, args, callerInfo];
@@ -7999,9 +7701,10 @@ class MouseEvent {
7999
7701
  modifiers;
8000
7702
  scroll;
8001
7703
  target;
8002
- _defaultPrevented = false;
8003
- get defaultPrevented() {
8004
- return this._defaultPrevented;
7704
+ isSelecting;
7705
+ _propagationStopped = false;
7706
+ get propagationStopped() {
7707
+ return this._propagationStopped;
8005
7708
  }
8006
7709
  constructor(target, attributes) {
8007
7710
  this.target = target;
@@ -8012,9 +7715,10 @@ class MouseEvent {
8012
7715
  this.modifiers = attributes.modifiers;
8013
7716
  this.scroll = attributes.scroll;
8014
7717
  this.source = attributes.source;
7718
+ this.isSelecting = attributes.isSelecting;
8015
7719
  }
8016
- preventDefault() {
8017
- this._defaultPrevented = true;
7720
+ stopPropagation() {
7721
+ this._propagationStopped = true;
8018
7722
  }
8019
7723
  }
8020
7724
  var MouseButton;
@@ -8125,7 +7829,6 @@ class CliRenderer extends EventEmitter6 {
8125
7829
  lastOverRenderableNum = 0;
8126
7830
  lastOverRenderable;
8127
7831
  currentSelection = null;
8128
- selectionState = null;
8129
7832
  selectionContainers = [];
8130
7833
  _splitHeight = 0;
8131
7834
  renderOffset = 0;
@@ -8219,6 +7922,9 @@ Error details:
8219
7922
  process.exit(1);
8220
7923
  });
8221
7924
  };
7925
+ process.on("warning", (warning) => {
7926
+ console.warn(JSON.stringify(warning.message, null, 2));
7927
+ });
8222
7928
  process.on("uncaughtException", handleError);
8223
7929
  process.on("unhandledRejection", handleError);
8224
7930
  process.on("exit", () => {
@@ -8488,22 +8194,38 @@ Error details:
8488
8194
  const sameElement = maybeRenderableId === this.lastOverRenderableNum;
8489
8195
  this.lastOverRenderableNum = maybeRenderableId;
8490
8196
  const maybeRenderable = Renderable.renderablesByNumber.get(maybeRenderableId);
8491
- if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */) {
8197
+ if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */ && !this.currentSelection?.isSelecting && !mouseEvent.modifiers.ctrl) {
8492
8198
  if (maybeRenderable && maybeRenderable.selectable && maybeRenderable.shouldStartSelection(mouseEvent.x, mouseEvent.y)) {
8493
8199
  this.startSelection(maybeRenderable, mouseEvent.x, mouseEvent.y);
8200
+ const event = new MouseEvent(maybeRenderable, mouseEvent);
8201
+ maybeRenderable.processMouseEvent(event);
8494
8202
  return true;
8495
8203
  }
8496
8204
  }
8497
- if (mouseEvent.type === "drag" && this.selectionState?.isSelecting) {
8205
+ if (mouseEvent.type === "drag" && this.currentSelection?.isSelecting) {
8498
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
+ }
8499
8211
  return true;
8500
8212
  }
8501
- 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
+ }
8502
8218
  this.finishSelection();
8503
8219
  return true;
8504
8220
  }
8505
- if (mouseEvent.type === "down" && mouseEvent.button === 0 /* LEFT */ && this.selectionState) {
8506
- 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
+ }
8507
8229
  }
8508
8230
  if (!sameElement && (mouseEvent.type === "drag" || mouseEvent.type === "move")) {
8509
8231
  if (this.lastOverRenderable && this.lastOverRenderable !== this.capturedRenderable) {
@@ -8623,7 +8345,7 @@ Error details:
8623
8345
  this.renderOffset = height - this._splitHeight;
8624
8346
  this.width = width;
8625
8347
  this.height = this._splitHeight;
8626
- this.currentRenderBuffer.clearLocal(RGBA.fromHex("#000000"), "\u0A00");
8348
+ this.currentRenderBuffer.clear(RGBA.fromHex("#000000"));
8627
8349
  this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
8628
8350
  } else {
8629
8351
  this.width = width;
@@ -8904,35 +8626,32 @@ Error details:
8904
8626
  getSelection() {
8905
8627
  return this.currentSelection;
8906
8628
  }
8629
+ get hasSelection() {
8630
+ return !!this.currentSelection;
8631
+ }
8907
8632
  getSelectionContainer() {
8908
8633
  return this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : null;
8909
8634
  }
8910
- hasSelection() {
8911
- return this.currentSelection !== null;
8912
- }
8913
8635
  clearSelection() {
8914
- if (this.selectionState) {
8915
- this.selectionState = null;
8916
- 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;
8917
8643
  }
8918
- this.currentSelection = null;
8919
8644
  this.selectionContainers = [];
8920
8645
  }
8921
8646
  startSelection(startRenderable, x, y) {
8922
8647
  this.clearSelection();
8923
8648
  this.selectionContainers.push(startRenderable.parent || this.root);
8924
- this.selectionState = {
8925
- anchor: { x, y },
8926
- focus: { x, y },
8927
- isActive: true,
8928
- isSelecting: true
8929
- };
8930
- this.currentSelection = new Selection({ x, y }, { x, y });
8649
+ this.currentSelection = new Selection(startRenderable, { x, y }, { x, y });
8931
8650
  this.notifySelectablesOfSelectionChange();
8932
8651
  }
8933
8652
  updateSelection(currentRenderable, x, y) {
8934
- if (this.selectionState) {
8935
- this.selectionState.focus = { x, y };
8653
+ if (this.currentSelection) {
8654
+ this.currentSelection.focus = { x, y };
8936
8655
  if (this.selectionContainers.length > 0) {
8937
8656
  const currentContainer = this.selectionContainers[this.selectionContainers.length - 1];
8938
8657
  if (!currentRenderable || !this.isWithinContainer(currentRenderable, currentContainer)) {
@@ -8949,12 +8668,18 @@ Error details:
8949
8668
  }
8950
8669
  }
8951
8670
  }
8952
- if (this.currentSelection) {
8953
- this.currentSelection = new Selection(this.selectionState.anchor, this.selectionState.focus);
8954
- }
8955
8671
  this.notifySelectablesOfSelectionChange();
8956
8672
  }
8957
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
+ }
8958
8683
  isWithinContainer(renderable, container) {
8959
8684
  let current = renderable;
8960
8685
  while (current) {
@@ -8965,46 +8690,44 @@ Error details:
8965
8690
  return false;
8966
8691
  }
8967
8692
  finishSelection() {
8968
- if (this.selectionState) {
8969
- this.selectionState.isSelecting = false;
8693
+ if (this.currentSelection) {
8694
+ this.currentSelection.isSelecting = false;
8970
8695
  this.emit("selection", this.currentSelection);
8971
8696
  }
8972
8697
  }
8973
8698
  notifySelectablesOfSelectionChange() {
8974
- let normalizedSelection = null;
8975
- if (this.selectionState) {
8976
- normalizedSelection = { ...this.selectionState };
8977
- if (normalizedSelection.anchor.y > normalizedSelection.focus.y || normalizedSelection.anchor.y === normalizedSelection.focus.y && normalizedSelection.anchor.x > normalizedSelection.focus.x) {
8978
- const temp = normalizedSelection.anchor;
8979
- normalizedSelection.anchor = normalizedSelection.focus;
8980
- normalizedSelection.focus = {
8981
- x: temp.x + 1,
8982
- y: temp.y
8983
- };
8984
- }
8985
- }
8986
8699
  const selectedRenderables = [];
8987
- for (const [, renderable] of Renderable.renderablesByNumber) {
8988
- if (renderable.visible && renderable.selectable) {
8989
- const currentContainer = this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : null;
8990
- let hasSelection = false;
8991
- if (!currentContainer || this.isWithinContainer(renderable, currentContainer)) {
8992
- hasSelection = renderable.onSelectionChanged(normalizedSelection);
8993
- } else {
8994
- 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);
8995
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);
8996
8718
  if (hasSelection) {
8997
- selectedRenderables.push(renderable);
8719
+ selectedRenderables.push(child);
8998
8720
  }
8721
+ touchedRenderables.push(child);
8722
+ }
8723
+ if (child.getChildrenCount() > 0) {
8724
+ this.walkSelectableRenderables(child, selectionBounds, selectedRenderables, touchedRenderables);
8999
8725
  }
9000
- }
9001
- if (this.currentSelection) {
9002
- this.currentSelection.updateSelectedRenderables(selectedRenderables);
9003
8726
  }
9004
8727
  }
9005
8728
  }
9006
8729
 
9007
- 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, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, ensureRenderable, wrapWithDelegates, instantiate, delegate, LayoutEvents, RenderableEvents, isValidPercentage, isMarginType, isPaddingType, isPositionType, isPositionTypeType, isOverflowType, 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 };
9008
8731
 
9009
- //# debugId=ADDBBC6DB7FFCF1064756E2164756E21
9010
- //# sourceMappingURL=index-rv93tneq.js.map
8732
+ //# debugId=4847DC8F7CFD610964756E2164756E21
8733
+ //# sourceMappingURL=index-sw194bbj.js.map