@cascadetui/core 0.1.5 → 0.1.7

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.
@@ -6112,15 +6112,16 @@ class StdinBuffer extends EventEmitter2 {
6112
6112
  this.timeout = null;
6113
6113
  }
6114
6114
  let str;
6115
- if (Buffer.isBuffer(data)) {
6116
- if (data.length === 1 && data[0] > 127) {
6117
- const byte = data[0] - 128;
6115
+ if (typeof data === "string") {
6116
+ str = data;
6117
+ } else {
6118
+ const chunk = this.toBuffer(data);
6119
+ if (chunk.length === 1 && chunk[0] > 127) {
6120
+ const byte = chunk[0] - 128;
6118
6121
  str = "\x1B" + String.fromCharCode(byte);
6119
6122
  } else {
6120
- str = data.toString();
6123
+ str = chunk.toString();
6121
6124
  }
6122
- } else {
6123
- str = data;
6124
6125
  }
6125
6126
  if (str.length === 0 && this.buffer.length === 0) {
6126
6127
  this.emit("data", "");
@@ -6210,6 +6211,15 @@ class StdinBuffer extends EventEmitter2 {
6210
6211
  destroy() {
6211
6212
  this.clear();
6212
6213
  }
6214
+ toBuffer(data) {
6215
+ if (Buffer.isBuffer(data)) {
6216
+ return data;
6217
+ }
6218
+ if (data instanceof Uint8Array) {
6219
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
6220
+ }
6221
+ return Buffer.from(data);
6222
+ }
6213
6223
  }
6214
6224
 
6215
6225
  // src/lib/yoga.options.ts
@@ -6515,7 +6525,16 @@ class MouseParser {
6515
6525
  this.mouseButtonsPressed.clear();
6516
6526
  }
6517
6527
  decodeInput(data) {
6518
- return data.toString();
6528
+ if (typeof data === "string") {
6529
+ return data;
6530
+ }
6531
+ if (Buffer.isBuffer(data)) {
6532
+ return data.toString();
6533
+ }
6534
+ if (data instanceof Uint8Array) {
6535
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString();
6536
+ }
6537
+ return Buffer.from(data).toString();
6519
6538
  }
6520
6539
  parseMouseEvent(data) {
6521
6540
  const str = this.decodeInput(data);
@@ -13351,6 +13370,15 @@ class Renderable extends BaseRenderable {
13351
13370
  childrenPrimarySortDirty = true;
13352
13371
  childrenSortedByPrimaryAxis = [];
13353
13372
  _shouldUpdateBefore = new Set;
13373
+ renderRequestDepth = 0;
13374
+ renderRequestPending = false;
13375
+ lastLayoutVersion = -1;
13376
+ dirtyLayout = false;
13377
+ dirtyPaint = false;
13378
+ dirtyStructure = false;
13379
+ subtreeDirtyLayout = false;
13380
+ subtreeDirtyPaint = false;
13381
+ subtreeDirtyStructure = false;
13354
13382
  onLifecyclePass = null;
13355
13383
  renderBefore;
13356
13384
  renderAfter;
@@ -13415,6 +13443,7 @@ class Renderable extends BaseRenderable {
13415
13443
  const wasVisible = this._visible;
13416
13444
  this._visible = value;
13417
13445
  this.yogaNode.setDisplay(value ? Display.Flex : Display.None);
13446
+ this.markDirtyStructure();
13418
13447
  if (this._live) {
13419
13448
  if (!wasVisible && value) {
13420
13449
  this.propagateLiveCount(1);
@@ -13434,6 +13463,7 @@ class Renderable extends BaseRenderable {
13434
13463
  const clamped = Math.max(0, Math.min(1, value));
13435
13464
  if (this._opacity !== clamped) {
13436
13465
  this._opacity = clamped;
13466
+ this.markDirtyStructure();
13437
13467
  this.requestRender();
13438
13468
  }
13439
13469
  }
@@ -13449,6 +13479,15 @@ class Renderable extends BaseRenderable {
13449
13479
  shouldStartSelection(x, y) {
13450
13480
  return false;
13451
13481
  }
13482
+ selectWord(_x, _y) {
13483
+ return false;
13484
+ }
13485
+ selectLine(_x, _y) {
13486
+ return false;
13487
+ }
13488
+ updateSelectionWordSnap(_x, _y) {
13489
+ return false;
13490
+ }
13452
13491
  focus() {
13453
13492
  if (this._isDestroyed || this._focused || !this._focusable)
13454
13493
  return;
@@ -13517,21 +13556,89 @@ class Renderable extends BaseRenderable {
13517
13556
  this.parent?.propagateLiveCount(delta);
13518
13557
  }
13519
13558
  findDescendantById(id) {
13520
- for (const child of this._childrenInLayoutOrder) {
13521
- if (child.id === id)
13522
- return child;
13523
- if (isRenderable(child)) {
13524
- const found = child.findDescendantById(id);
13525
- if (found)
13526
- return found;
13559
+ const visited = new Set;
13560
+ const stack = [...this._childrenInLayoutOrder];
13561
+ while (stack.length > 0) {
13562
+ const current = stack.pop();
13563
+ if (visited.has(current)) {
13564
+ continue;
13565
+ }
13566
+ visited.add(current);
13567
+ if (current.id === id) {
13568
+ return current;
13569
+ }
13570
+ const children = current.getChildren();
13571
+ for (let i = children.length - 1;i >= 0; i -= 1) {
13572
+ const child = children[i];
13573
+ if (isRenderable(child)) {
13574
+ stack.push(child);
13575
+ }
13527
13576
  }
13528
13577
  }
13529
13578
  return;
13530
13579
  }
13580
+ markClean() {
13581
+ super.markClean();
13582
+ this.dirtyPaint = false;
13583
+ }
13584
+ markSubtreeDirtyPaint(force = false) {
13585
+ if (this.subtreeDirtyPaint)
13586
+ return;
13587
+ this.subtreeDirtyPaint = true;
13588
+ if (this.parent && (force || this._visible)) {
13589
+ this.parent.markSubtreeDirtyPaint(force);
13590
+ }
13591
+ }
13592
+ markSubtreeDirtyLayout(force = false) {
13593
+ if (this.subtreeDirtyLayout)
13594
+ return;
13595
+ this.subtreeDirtyLayout = true;
13596
+ if (this.parent && (force || this._visible)) {
13597
+ this.parent.markSubtreeDirtyLayout(force);
13598
+ }
13599
+ }
13600
+ markSubtreeDirtyStructure(force = false) {
13601
+ if (this.subtreeDirtyStructure)
13602
+ return;
13603
+ this.subtreeDirtyStructure = true;
13604
+ if (this.parent && (force || this._visible)) {
13605
+ this.parent.markSubtreeDirtyStructure(force);
13606
+ }
13607
+ }
13608
+ markDirtyPaint() {
13609
+ this.dirtyPaint = true;
13610
+ this.markSubtreeDirtyPaint();
13611
+ }
13612
+ markDirtyLayout() {
13613
+ this.dirtyLayout = true;
13614
+ this.markSubtreeDirtyLayout();
13615
+ }
13616
+ markDirtyStructure() {
13617
+ this.dirtyStructure = true;
13618
+ this.markSubtreeDirtyStructure(true);
13619
+ this.markDirtyLayout();
13620
+ }
13531
13621
  requestRender() {
13532
13622
  this.markDirty();
13623
+ this.markDirtyPaint();
13624
+ if (this.renderRequestDepth > 0) {
13625
+ this.renderRequestPending = true;
13626
+ return;
13627
+ }
13533
13628
  this._ctx.requestRender();
13534
13629
  }
13630
+ beginRenderBatch() {
13631
+ this.renderRequestDepth += 1;
13632
+ }
13633
+ endRenderBatch() {
13634
+ if (this.renderRequestDepth === 0)
13635
+ return;
13636
+ this.renderRequestDepth -= 1;
13637
+ if (this.renderRequestDepth === 0 && this.renderRequestPending) {
13638
+ this.renderRequestPending = false;
13639
+ this._ctx.requestRender();
13640
+ }
13641
+ }
13535
13642
  get translateX() {
13536
13643
  return this._translateX;
13537
13644
  }
@@ -13541,6 +13648,7 @@ class Renderable extends BaseRenderable {
13541
13648
  this._translateX = value;
13542
13649
  if (this.parent)
13543
13650
  this.parent.childrenPrimarySortDirty = true;
13651
+ this.markDirtyStructure();
13544
13652
  this.requestRender();
13545
13653
  }
13546
13654
  get translateY() {
@@ -13552,6 +13660,7 @@ class Renderable extends BaseRenderable {
13552
13660
  this._translateY = value;
13553
13661
  if (this.parent)
13554
13662
  this.parent.childrenPrimarySortDirty = true;
13663
+ this.markDirtyStructure();
13555
13664
  this.requestRender();
13556
13665
  }
13557
13666
  get x() {
@@ -14005,6 +14114,7 @@ class Renderable extends BaseRenderable {
14005
14114
  }
14006
14115
  onLayoutResize(width, height) {
14007
14116
  if (this._visible) {
14117
+ this.markDirtyLayout();
14008
14118
  this.handleFrameBufferResize(width, height);
14009
14119
  this.onResize(width, height);
14010
14120
  this.requestRender();
@@ -14046,10 +14156,30 @@ class Renderable extends BaseRenderable {
14046
14156
  }
14047
14157
  obj.parent = this;
14048
14158
  }
14159
+ assertCanAdoptChild(renderable) {
14160
+ if (renderable === this) {
14161
+ throw new Error(`Invalid render tree operation: '${this.id}' cannot contain itself`);
14162
+ }
14163
+ let current = this;
14164
+ const visited = new Set;
14165
+ while (current) {
14166
+ if (visited.has(current)) {
14167
+ throw new Error(`Invalid render tree operation: parent chain cycle detected near '${current.id}'`);
14168
+ }
14169
+ visited.add(current);
14170
+ if (current === renderable) {
14171
+ throw new Error(`Invalid render tree operation: adding '${renderable.id}' to '${this.id}' would create a cycle`);
14172
+ }
14173
+ current = current.parent;
14174
+ }
14175
+ }
14049
14176
  add(obj, index) {
14050
14177
  if (!obj) {
14051
14178
  return -1;
14052
14179
  }
14180
+ const trace = this._ctx.trace;
14181
+ const traceEnabled = trace?.enabled === true;
14182
+ const traceStart = traceEnabled ? trace.now() : 0;
14053
14183
  const renderable = maybeMakeRenderable(this._ctx, obj);
14054
14184
  if (!renderable) {
14055
14185
  return -1;
@@ -14060,6 +14190,7 @@ class Renderable extends BaseRenderable {
14060
14190
  }
14061
14191
  return -1;
14062
14192
  }
14193
+ this.assertCanAdoptChild(renderable);
14063
14194
  const anchorRenderable = index !== undefined ? this._childrenInLayoutOrder[index] : undefined;
14064
14195
  if (anchorRenderable) {
14065
14196
  return this.insertBefore(renderable, anchorRenderable);
@@ -14085,7 +14216,11 @@ class Renderable extends BaseRenderable {
14085
14216
  this.yogaNode.insertChild(childLayoutNode, insertedIndex);
14086
14217
  this.childrenPrimarySortDirty = true;
14087
14218
  this._shouldUpdateBefore.add(renderable);
14219
+ this.markDirtyStructure();
14088
14220
  this.requestRender();
14221
+ if (traceEnabled) {
14222
+ trace.write(`trace.container.add id=${this.id} child=${renderable.id} index=${insertedIndex} ms=${(trace.now() - traceStart).toFixed(3)}`);
14223
+ }
14089
14224
  return insertedIndex;
14090
14225
  }
14091
14226
  insertBefore(obj, anchor) {
@@ -14095,6 +14230,9 @@ class Renderable extends BaseRenderable {
14095
14230
  if (!obj) {
14096
14231
  return -1;
14097
14232
  }
14233
+ const trace = this._ctx.trace;
14234
+ const traceEnabled = trace?.enabled === true;
14235
+ const traceStart = traceEnabled ? trace.now() : 0;
14098
14236
  const renderable = maybeMakeRenderable(this._ctx, obj);
14099
14237
  if (!renderable) {
14100
14238
  return -1;
@@ -14105,6 +14243,7 @@ class Renderable extends BaseRenderable {
14105
14243
  }
14106
14244
  return -1;
14107
14245
  }
14246
+ this.assertCanAdoptChild(renderable);
14108
14247
  if (!isRenderable(anchor)) {
14109
14248
  throw new Error("Anchor must be a Renderable");
14110
14249
  }
@@ -14147,9 +14286,120 @@ class Renderable extends BaseRenderable {
14147
14286
  this._childrenInLayoutOrder.splice(insertedIndex, 0, renderable);
14148
14287
  this.yogaNode.insertChild(renderable.getLayoutNode(), insertedIndex);
14149
14288
  this._shouldUpdateBefore.add(renderable);
14289
+ this.markDirtyStructure();
14150
14290
  this.requestRender();
14291
+ if (traceEnabled) {
14292
+ trace.write(`trace.container.insertBefore id=${this.id} child=${renderable.id} index=${insertedIndex} ms=${(trace.now() - traceStart).toFixed(3)}`);
14293
+ }
14151
14294
  return insertedIndex;
14152
14295
  }
14296
+ replaceChildren(nextChildren, options) {
14297
+ const trace = this._ctx.trace;
14298
+ const traceEnabled = trace?.enabled === true;
14299
+ const traceStart = traceEnabled ? trace.now() : 0;
14300
+ const destroyRemoved = options?.destroyRemoved ?? false;
14301
+ this.beginRenderBatch();
14302
+ let removedCount = 0;
14303
+ let addedCount = 0;
14304
+ let orderChanged = false;
14305
+ try {
14306
+ const nextRenderables = [];
14307
+ const nextIds = new Set;
14308
+ const added = [];
14309
+ for (const child of nextChildren ?? []) {
14310
+ if (!child)
14311
+ continue;
14312
+ const renderable = maybeMakeRenderable(this._ctx, child);
14313
+ if (!renderable)
14314
+ continue;
14315
+ if (renderable.isDestroyed)
14316
+ continue;
14317
+ if (nextIds.has(renderable.id))
14318
+ continue;
14319
+ this.assertCanAdoptChild(renderable);
14320
+ if (renderable.parent !== this) {
14321
+ this.replaceParent(renderable);
14322
+ this.needsZIndexSort = true;
14323
+ this.renderableMapById.set(renderable.id, renderable);
14324
+ this._childrenInZIndexOrder.push(renderable);
14325
+ if (typeof renderable.onLifecyclePass === "function") {
14326
+ this._ctx.registerLifecyclePass(renderable);
14327
+ }
14328
+ if (renderable._liveCount > 0) {
14329
+ this.propagateLiveCount(renderable._liveCount);
14330
+ }
14331
+ added.push(renderable);
14332
+ }
14333
+ nextRenderables.push(renderable);
14334
+ nextIds.add(renderable.id);
14335
+ }
14336
+ const previousChildren = this._childrenInLayoutOrder;
14337
+ const removed = [];
14338
+ if (previousChildren.length > 0) {
14339
+ for (const child of previousChildren) {
14340
+ if (!nextIds.has(child.id)) {
14341
+ removed.push(child);
14342
+ }
14343
+ }
14344
+ }
14345
+ removedCount = removed.length;
14346
+ addedCount = added.length;
14347
+ if (removed.length > 0) {
14348
+ for (const child of removed) {
14349
+ if (child._liveCount > 0) {
14350
+ this.propagateLiveCount(-child._liveCount);
14351
+ }
14352
+ child.onRemove();
14353
+ child.parent = null;
14354
+ this._ctx.unregisterLifecyclePass(child);
14355
+ this.renderableMapById.delete(child.id);
14356
+ }
14357
+ this._childrenInZIndexOrder = this._childrenInZIndexOrder.filter((child) => nextIds.has(child.id));
14358
+ }
14359
+ if (previousChildren.length !== nextRenderables.length) {
14360
+ orderChanged = true;
14361
+ } else {
14362
+ for (let i = 0;i < previousChildren.length; i += 1) {
14363
+ if (previousChildren[i] !== nextRenderables[i]) {
14364
+ orderChanged = true;
14365
+ break;
14366
+ }
14367
+ }
14368
+ }
14369
+ if (orderChanged) {
14370
+ for (const child of previousChildren) {
14371
+ this.yogaNode.removeChild(child.getLayoutNode());
14372
+ }
14373
+ for (let i = 0;i < nextRenderables.length; i += 1) {
14374
+ this.yogaNode.insertChild(nextRenderables[i].getLayoutNode(), i);
14375
+ }
14376
+ }
14377
+ this._childrenInLayoutOrder = nextRenderables;
14378
+ if (added.length > 0) {
14379
+ for (const child of added) {
14380
+ this._shouldUpdateBefore.add(child);
14381
+ }
14382
+ }
14383
+ if (orderChanged || removed.length > 0 || added.length > 0) {
14384
+ this.childrenPrimarySortDirty = true;
14385
+ this.markDirtyStructure();
14386
+ this.requestRender();
14387
+ }
14388
+ if (destroyRemoved) {
14389
+ for (const child of removed) {
14390
+ child.destroyRecursively();
14391
+ }
14392
+ }
14393
+ } finally {
14394
+ this.endRenderBatch();
14395
+ }
14396
+ if (traceEnabled) {
14397
+ trace.write(`trace.container.replaceChildren id=${this.id} next=${this._childrenInLayoutOrder.length} added=${addedCount} removed=${removedCount} reordered=${orderChanged} ms=${(trace.now() - traceStart).toFixed(3)}`);
14398
+ }
14399
+ }
14400
+ setChildren(nextChildren) {
14401
+ this.replaceChildren(nextChildren);
14402
+ }
14153
14403
  getRenderable(id) {
14154
14404
  return this.renderableMapById.get(id);
14155
14405
  }
@@ -14157,6 +14407,9 @@ class Renderable extends BaseRenderable {
14157
14407
  if (!id) {
14158
14408
  return;
14159
14409
  }
14410
+ const trace = this._ctx.trace;
14411
+ const traceEnabled = trace?.enabled === true;
14412
+ const traceStart = traceEnabled ? trace.now() : 0;
14160
14413
  if (this.renderableMapById.has(id)) {
14161
14414
  const obj = this.renderableMapById.get(id);
14162
14415
  if (obj) {
@@ -14165,6 +14418,7 @@ class Renderable extends BaseRenderable {
14165
14418
  }
14166
14419
  const childLayoutNode = obj.getLayoutNode();
14167
14420
  this.yogaNode.removeChild(childLayoutNode);
14421
+ this.markDirtyStructure();
14168
14422
  this.requestRender();
14169
14423
  obj.onRemove();
14170
14424
  obj.parent = null;
@@ -14181,6 +14435,9 @@ class Renderable extends BaseRenderable {
14181
14435
  this.childrenPrimarySortDirty = true;
14182
14436
  }
14183
14437
  }
14438
+ if (traceEnabled) {
14439
+ trace.write(`trace.container.remove id=${this.id} child=${id} ms=${(trace.now() - traceStart).toFixed(3)}`);
14440
+ }
14184
14441
  }
14185
14442
  onRemove() {}
14186
14443
  getChildren() {
@@ -14195,11 +14452,16 @@ class Renderable extends BaseRenderable {
14195
14452
  this.onUpdate(deltaTime);
14196
14453
  if (this._isDestroyed)
14197
14454
  return;
14198
- this.updateFromLayout();
14455
+ const layoutVersion = this._ctx.layoutVersion;
14456
+ if (this.lastLayoutVersion !== layoutVersion) {
14457
+ this.updateFromLayout();
14458
+ this.lastLayoutVersion = layoutVersion;
14459
+ }
14199
14460
  if (this._shouldUpdateBefore.size > 0) {
14200
14461
  for (const child of this._shouldUpdateBefore) {
14201
14462
  if (!child.isDestroyed) {
14202
14463
  child.updateFromLayout();
14464
+ child.lastLayoutVersion = layoutVersion;
14203
14465
  }
14204
14466
  }
14205
14467
  this._shouldUpdateBefore.clear();
@@ -14226,13 +14488,39 @@ class Renderable extends BaseRenderable {
14226
14488
  });
14227
14489
  }
14228
14490
  const visibleChildren = this._getVisibleChildren();
14229
- for (const child of this._childrenInZIndexOrder) {
14230
- if (!visibleChildren.includes(child.num)) {
14231
- child.updateFromLayout();
14232
- continue;
14491
+ if (visibleChildren.length === this._childrenInZIndexOrder.length) {
14492
+ for (const child of this._childrenInZIndexOrder) {
14493
+ child.updateLayout(deltaTime, renderList);
14494
+ }
14495
+ } else {
14496
+ const visibleSet = new Set(visibleChildren);
14497
+ for (const child of this._childrenInZIndexOrder) {
14498
+ if (!visibleSet.has(child.num)) {
14499
+ if (child.lastLayoutVersion !== layoutVersion) {
14500
+ child.updateFromLayout();
14501
+ child.lastLayoutVersion = layoutVersion;
14502
+ }
14503
+ continue;
14504
+ }
14505
+ child.updateLayout(deltaTime, renderList);
14233
14506
  }
14234
- child.updateLayout(deltaTime, renderList);
14235
14507
  }
14508
+ let hasDirtyStructure = this.dirtyStructure;
14509
+ let hasDirtyLayout = this.dirtyLayout;
14510
+ let hasDirtyPaint = this.dirtyPaint;
14511
+ for (const child of this._childrenInZIndexOrder) {
14512
+ if (child.subtreeDirtyStructure)
14513
+ hasDirtyStructure = true;
14514
+ if (child.subtreeDirtyLayout)
14515
+ hasDirtyLayout = true;
14516
+ if (child.subtreeDirtyPaint)
14517
+ hasDirtyPaint = true;
14518
+ }
14519
+ this.dirtyStructure = false;
14520
+ this.dirtyLayout = false;
14521
+ this.subtreeDirtyStructure = hasDirtyStructure;
14522
+ this.subtreeDirtyLayout = hasDirtyLayout;
14523
+ this.subtreeDirtyPaint = hasDirtyPaint;
14236
14524
  if (shouldPushScissor) {
14237
14525
  renderList.push({ action: "popScissorRect" });
14238
14526
  }
@@ -14286,7 +14574,10 @@ class Renderable extends BaseRenderable {
14286
14574
  this.frameBuffer.destroy();
14287
14575
  this.frameBuffer = null;
14288
14576
  }
14289
- for (const child of this._childrenInLayoutOrder) {
14577
+ while (this._childrenInLayoutOrder.length > 0) {
14578
+ const child = this._childrenInLayoutOrder[0];
14579
+ if (!child)
14580
+ break;
14290
14581
  this.remove(child.id);
14291
14582
  }
14292
14583
  this._childrenInLayoutOrder = [];
@@ -14308,6 +14599,9 @@ class Renderable extends BaseRenderable {
14308
14599
  }
14309
14600
  destroySelf() {}
14310
14601
  processMouseEvent(event) {
14602
+ if (!event.tryVisit(this)) {
14603
+ return;
14604
+ }
14311
14605
  this._mouseListener?.call(this, event);
14312
14606
  this._mouseListeners[event.type]?.call(this, event);
14313
14607
  this.onMouseEvent(event);
@@ -14416,6 +14710,8 @@ class Renderable extends BaseRenderable {
14416
14710
 
14417
14711
  class RootRenderable extends Renderable {
14418
14712
  renderList = [];
14713
+ renderListDirty = true;
14714
+ lastRenderListLayoutVersion = -1;
14419
14715
  constructor(ctx) {
14420
14716
  super(ctx, { id: "__root__", zIndex: 0, visible: true, width: ctx.width, height: ctx.height, enableLayout: true });
14421
14717
  if (this.yogaNode) {
@@ -14430,14 +14726,38 @@ class RootRenderable extends Renderable {
14430
14726
  render(buffer, deltaTime) {
14431
14727
  if (!this.visible)
14432
14728
  return;
14729
+ const trace = this._ctx.trace;
14730
+ const traceEnabled = trace?.enabled === true;
14433
14731
  for (const renderable of this._ctx.getLifecyclePasses()) {
14434
14732
  renderable.onLifecyclePass?.call(renderable);
14435
14733
  }
14734
+ const layoutStart = traceEnabled ? trace.now() : 0;
14436
14735
  if (this.yogaNode.isDirty()) {
14437
14736
  this.calculateLayout();
14438
14737
  }
14439
- this.renderList.length = 0;
14440
- this.updateLayout(deltaTime, this.renderList);
14738
+ const layoutMs = traceEnabled ? trace.now() - layoutStart : 0;
14739
+ const layoutVersion = this._ctx.layoutVersion;
14740
+ if (this.renderList.length === 0) {
14741
+ this.renderListDirty = true;
14742
+ }
14743
+ if (this.subtreeDirtyStructure || this.subtreeDirtyLayout) {
14744
+ this.renderListDirty = true;
14745
+ }
14746
+ if (layoutVersion !== this.lastRenderListLayoutVersion) {
14747
+ this.renderListDirty = true;
14748
+ }
14749
+ let updateMs = 0;
14750
+ let renderListBuildMs = 0;
14751
+ if (this.renderListDirty) {
14752
+ const updateStart = traceEnabled ? trace.now() : 0;
14753
+ this.renderList.length = 0;
14754
+ this.updateLayout(deltaTime, this.renderList);
14755
+ updateMs = traceEnabled ? trace.now() - updateStart : 0;
14756
+ renderListBuildMs = updateMs;
14757
+ this.renderListDirty = false;
14758
+ this.lastRenderListLayoutVersion = layoutVersion;
14759
+ }
14760
+ const renderStart = traceEnabled ? trace.now() : 0;
14441
14761
  this._ctx.clearHitGridScissorRects();
14442
14762
  for (let i = 1;i < this.renderList.length; i++) {
14443
14763
  const command = this.renderList[i];
@@ -14463,6 +14783,13 @@ class RootRenderable extends Renderable {
14463
14783
  break;
14464
14784
  }
14465
14785
  }
14786
+ if (traceEnabled) {
14787
+ const drawMs = trace.now() - renderStart;
14788
+ trace.write(`trace.render.layout ms=${layoutMs.toFixed(3)}`);
14789
+ trace.write(`trace.render.updateTraversal ms=${updateMs.toFixed(3)}`);
14790
+ trace.write(`trace.render.renderListBuild ms=${renderListBuildMs.toFixed(3)} count=${this.renderList.length}`);
14791
+ trace.write(`trace.render.draw ms=${drawMs.toFixed(3)}`);
14792
+ }
14466
14793
  }
14467
14794
  propagateLiveCount(delta) {
14468
14795
  const oldCount = this._liveCount;
@@ -14475,6 +14802,7 @@ class RootRenderable extends Renderable {
14475
14802
  }
14476
14803
  calculateLayout() {
14477
14804
  this.yogaNode.calculateLayout(this.width, this.height, Direction.LTR);
14805
+ this._ctx.layoutVersion += 1;
14478
14806
  this.emit("layout-changed" /* LAYOUT_CHANGED */);
14479
14807
  }
14480
14808
  resize(width, height) {
@@ -16024,6 +16352,12 @@ registerEnvVar({
16024
16352
  type: "boolean",
16025
16353
  default: false
16026
16354
  });
16355
+ registerEnvVar({
16356
+ name: "CASCADE_LOG_CRASH_REPORTS",
16357
+ description: "Log detailed crash reports to the console when a crash is captured.",
16358
+ type: "boolean",
16359
+ default: false
16360
+ });
16027
16361
  var DEFAULT_FORWARDED_ENV_KEYS = [
16028
16362
  "TMUX",
16029
16363
  "TERM",
@@ -16080,8 +16414,10 @@ class MouseEvent {
16080
16414
  scroll;
16081
16415
  target;
16082
16416
  isDragging;
16417
+ clickCount;
16083
16418
  _propagationStopped = false;
16084
16419
  _defaultPrevented = false;
16420
+ _visitedRenderableNums = new Set;
16085
16421
  get propagationStopped() {
16086
16422
  return this._propagationStopped;
16087
16423
  }
@@ -16098,6 +16434,7 @@ class MouseEvent {
16098
16434
  this.scroll = attributes.scroll;
16099
16435
  this.source = attributes.source;
16100
16436
  this.isDragging = attributes.isDragging;
16437
+ this.clickCount = attributes.clickCount ?? 1;
16101
16438
  }
16102
16439
  stopPropagation() {
16103
16440
  this._propagationStopped = true;
@@ -16105,6 +16442,13 @@ class MouseEvent {
16105
16442
  preventDefault() {
16106
16443
  this._defaultPrevented = true;
16107
16444
  }
16445
+ tryVisit(renderable) {
16446
+ if (this._visitedRenderableNums.has(renderable.num)) {
16447
+ return false;
16448
+ }
16449
+ this._visitedRenderableNums.add(renderable.num);
16450
+ return true;
16451
+ }
16108
16452
  }
16109
16453
  var MouseButton;
16110
16454
  ((MouseButton2) => {
@@ -16169,6 +16513,7 @@ var CliRenderEvents;
16169
16513
  ((CliRenderEvents2) => {
16170
16514
  CliRenderEvents2["DEBUG_OVERLAY_TOGGLE"] = "debugOverlay:toggle";
16171
16515
  CliRenderEvents2["DESTROY"] = "destroy";
16516
+ CliRenderEvents2["CRASH"] = "crash";
16172
16517
  })(CliRenderEvents ||= {});
16173
16518
  var RendererControlState;
16174
16519
  ((RendererControlState2) => {
@@ -16206,6 +16551,8 @@ class CliRenderer extends EventEmitter9 {
16206
16551
  arrayBuffers: 0
16207
16552
  };
16208
16553
  root;
16554
+ trace;
16555
+ layoutVersion = 0;
16209
16556
  width;
16210
16557
  height;
16211
16558
  _useThread = false;
@@ -16282,9 +16629,12 @@ class CliRenderer extends EventEmitter9 {
16282
16629
  _hasPointer = false;
16283
16630
  _lastPointerModifiers = { shift: false, alt: false, ctrl: false };
16284
16631
  _currentMousePointerStyle = undefined;
16632
+ _lastClick = null;
16633
+ clickCountThresholdMs = 400;
16285
16634
  _currentFocusedRenderable = null;
16286
16635
  lifecyclePasses = new Set;
16287
16636
  _openConsoleOnError = true;
16637
+ _logCrashReportsToConsole = env.CASCADE_LOG_CRASH_REPORTS;
16288
16638
  _paletteDetector = null;
16289
16639
  _cachedPalette = null;
16290
16640
  _paletteDetectionPromise = null;
@@ -16295,11 +16645,23 @@ class CliRenderer extends EventEmitter9 {
16295
16645
  idleResolvers = [];
16296
16646
  _debugInputs = [];
16297
16647
  _debugModeEnabled = env.CASCADE_DEBUG;
16298
- handleError = ((error) => {
16299
- console.error(error);
16648
+ recentRuntimeEvents = [];
16649
+ crashReports = [];
16650
+ maxRecentRuntimeEvents = 200;
16651
+ maxCrashReports = 20;
16652
+ handleProcessCrash = (reason, source) => {
16653
+ const report = this.reportCrash(reason, source);
16654
+ console.error(reason);
16300
16655
  if (this._openConsoleOnError) {
16301
16656
  this.console.show();
16657
+ console.log(`[crash:${report.source}] ${report.message}`);
16302
16658
  }
16659
+ };
16660
+ uncaughtExceptionHandler = ((error) => {
16661
+ this.handleProcessCrash(error, "uncaughtException");
16662
+ }).bind(this);
16663
+ unhandledRejectionHandler = ((reason) => {
16664
+ this.handleProcessCrash(reason, "unhandledRejection");
16303
16665
  }).bind(this);
16304
16666
  dumpOutputCache(optionalMessage = "") {
16305
16667
  const cachedLogs = this.console.getCachedLogs();
@@ -16391,6 +16753,17 @@ Captured output:
16391
16753
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
16392
16754
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
16393
16755
  this.postProcessFns = config.postProcessFns || [];
16756
+ if (config.trace) {
16757
+ const writer = config.traceWriter ?? ((line) => {
16758
+ this.realStdoutWrite.call(this.stdout, `${line}
16759
+ `);
16760
+ });
16761
+ this.trace = {
16762
+ enabled: true,
16763
+ now: () => performance.now(),
16764
+ write: writer
16765
+ };
16766
+ }
16394
16767
  this.prependedInputHandlers = config.prependInputHandlers || [];
16395
16768
  this.root = new RootRenderable(this);
16396
16769
  if (this.memorySnapshotInterval > 0) {
@@ -16401,8 +16774,8 @@ Captured output:
16401
16774
  }
16402
16775
  process.on("SIGWINCH", this.sigwinchHandler);
16403
16776
  process.on("warning", this.warningHandler);
16404
- process.on("uncaughtException", this.handleError);
16405
- process.on("unhandledRejection", this.handleError);
16777
+ process.on("uncaughtException", this.uncaughtExceptionHandler);
16778
+ process.on("unhandledRejection", this.unhandledRejectionHandler);
16406
16779
  process.on("beforeExit", this.exitHandler);
16407
16780
  const kittyConfig = config.useKittyKeyboard ?? {};
16408
16781
  const useKittyForParsing = kittyConfig !== null;
@@ -16432,6 +16805,7 @@ Captured output:
16432
16805
  this._console = new TerminalConsole(this, consoleOptions);
16433
16806
  this.useConsole = config.useConsole ?? true;
16434
16807
  this._openConsoleOnError = config.openConsoleOnError ?? true;
16808
+ this._logCrashReportsToConsole = config.logCrashReportsToConsole ?? env.CASCADE_LOG_CRASH_REPORTS;
16435
16809
  this._onDestroy = config.onDestroy;
16436
16810
  global.requestAnimationFrame = (callback) => {
16437
16811
  const id = CliRenderer.animationFrameId++;
@@ -16773,10 +17147,11 @@ Captured output:
16773
17147
  this.queryPixelResolution();
16774
17148
  }
16775
17149
  stdinListener = ((data) => {
16776
- if (this._useMouse && this.handleMouseData(data)) {
17150
+ const chunk = this.normalizeInputChunk(data);
17151
+ if (this._useMouse && this.handleMouseData(chunk)) {
16777
17152
  return;
16778
17153
  }
16779
- this._stdinBuffer.process(data);
17154
+ this._stdinBuffer.process(chunk);
16780
17155
  }).bind(this);
16781
17156
  addInputHandler(handler) {
16782
17157
  this.inputHandlers.push(handler);
@@ -16853,6 +17228,10 @@ Captured output:
16853
17228
  this.stdin.setEncoding("utf8");
16854
17229
  this.stdin.on("data", this.stdinListener);
16855
17230
  this._stdinBuffer.on("data", (sequence) => {
17231
+ this.recordRuntimeEvent("input:data", {
17232
+ sequence: sequence.length > 120 ? `${sequence.slice(0, 120)}...` : sequence,
17233
+ length: sequence.length
17234
+ });
16856
17235
  if (this._debugModeEnabled) {
16857
17236
  this._debugInputs.push({
16858
17237
  timestamp: new Date().toISOString(),
@@ -16866,6 +17245,9 @@ Captured output:
16866
17245
  }
16867
17246
  });
16868
17247
  this._stdinBuffer.on("paste", (data) => {
17248
+ this.recordRuntimeEvent("input:paste", {
17249
+ length: data.length
17250
+ });
16869
17251
  this._keyHandler.processPaste(data);
16870
17252
  });
16871
17253
  }
@@ -16874,7 +17256,12 @@ Captured output:
16874
17256
  target.processMouseEvent(event);
16875
17257
  if (this.autoFocus && event.type === "down" && event.button === 0 /* LEFT */ && !event.defaultPrevented) {
16876
17258
  let current = target;
17259
+ const visited = new Set;
16877
17260
  while (current) {
17261
+ if (visited.has(current)) {
17262
+ break;
17263
+ }
17264
+ visited.add(current);
16878
17265
  if (current.focusable) {
16879
17266
  current.focus();
16880
17267
  break;
@@ -16903,6 +17290,18 @@ Captured output:
16903
17290
  }
16904
17291
  mouseEvent.y -= this.renderOffset;
16905
17292
  }
17293
+ const clickCount = this.resolveClickCount(mouseEvent);
17294
+ const mouseEventWithClickCount = { ...mouseEvent, clickCount };
17295
+ this.recordRuntimeEvent("input:mouse", {
17296
+ type: mouseEvent.type,
17297
+ x: mouseEvent.x,
17298
+ y: mouseEvent.y,
17299
+ button: mouseEvent.button,
17300
+ clickCount,
17301
+ ctrl: mouseEvent.modifiers.ctrl,
17302
+ alt: mouseEvent.modifiers.alt,
17303
+ shift: mouseEvent.modifiers.shift
17304
+ });
16906
17305
  this._latestPointer.x = mouseEvent.x;
16907
17306
  this._latestPointer.y = mouseEvent.y;
16908
17307
  this._hasPointer = true;
@@ -16910,7 +17309,7 @@ Captured output:
16910
17309
  if (this._console.visible) {
16911
17310
  const consoleBounds = this._console.bounds;
16912
17311
  if (mouseEvent.x >= consoleBounds.x && mouseEvent.x < consoleBounds.x + consoleBounds.width && mouseEvent.y >= consoleBounds.y && mouseEvent.y < consoleBounds.y + consoleBounds.height) {
16913
- const event2 = new MouseEvent(null, mouseEvent);
17312
+ const event2 = new MouseEvent(null, mouseEventWithClickCount);
16914
17313
  const handled = this._console.handleMouse(event2);
16915
17314
  if (handled)
16916
17315
  return true;
@@ -16922,7 +17321,7 @@ Captured output:
16922
17321
  const fallbackTarget = this._currentFocusedRenderable && !this._currentFocusedRenderable.isDestroyed && this._currentFocusedRenderable.focused ? this._currentFocusedRenderable : null;
16923
17322
  const scrollTarget = maybeRenderable2 ?? fallbackTarget;
16924
17323
  if (scrollTarget) {
16925
- const event2 = new MouseEvent(scrollTarget, mouseEvent);
17324
+ const event2 = new MouseEvent(scrollTarget, mouseEventWithClickCount);
16926
17325
  scrollTarget.processMouseEvent(event2);
16927
17326
  }
16928
17327
  return true;
@@ -16935,21 +17334,21 @@ Captured output:
16935
17334
  const canStartSelection = Boolean(maybeRenderable && maybeRenderable.selectable && !maybeRenderable.isDestroyed && maybeRenderable.shouldStartSelection(mouseEvent.x, mouseEvent.y));
16936
17335
  if (canStartSelection && maybeRenderable) {
16937
17336
  this.startSelection(maybeRenderable, mouseEvent.x, mouseEvent.y);
16938
- this.dispatchMouseEvent(maybeRenderable, mouseEvent);
17337
+ this.dispatchMouseEvent(maybeRenderable, mouseEventWithClickCount);
16939
17338
  return true;
16940
17339
  }
16941
17340
  }
16942
17341
  if (mouseEvent.type === "drag" && this.currentSelection?.isDragging) {
16943
17342
  this.updateSelection(maybeRenderable, mouseEvent.x, mouseEvent.y);
16944
17343
  if (maybeRenderable) {
16945
- const event2 = new MouseEvent(maybeRenderable, { ...mouseEvent, isDragging: true });
17344
+ const event2 = new MouseEvent(maybeRenderable, { ...mouseEventWithClickCount, isDragging: true });
16946
17345
  maybeRenderable.processMouseEvent(event2);
16947
17346
  }
16948
17347
  return true;
16949
17348
  }
16950
17349
  if (mouseEvent.type === "up" && this.currentSelection?.isDragging) {
16951
17350
  if (maybeRenderable) {
16952
- const event2 = new MouseEvent(maybeRenderable, { ...mouseEvent, isDragging: true });
17351
+ const event2 = new MouseEvent(maybeRenderable, { ...mouseEventWithClickCount, isDragging: true });
16953
17352
  maybeRenderable.processMouseEvent(event2);
16954
17353
  }
16955
17354
  this.finishSelection();
@@ -16964,13 +17363,13 @@ Captured output:
16964
17363
  }
16965
17364
  if (!sameElement && (mouseEvent.type === "drag" || mouseEvent.type === "move")) {
16966
17365
  if (this.lastOverRenderable && this.lastOverRenderable !== this.capturedRenderable) {
16967
- const event2 = new MouseEvent(this.lastOverRenderable, { ...mouseEvent, type: "out" });
17366
+ const event2 = new MouseEvent(this.lastOverRenderable, { ...mouseEventWithClickCount, type: "out" });
16968
17367
  this.lastOverRenderable.processMouseEvent(event2);
16969
17368
  }
16970
17369
  this.lastOverRenderable = maybeRenderable;
16971
17370
  if (maybeRenderable) {
16972
17371
  const event2 = new MouseEvent(maybeRenderable, {
16973
- ...mouseEvent,
17372
+ ...mouseEventWithClickCount,
16974
17373
  type: "over",
16975
17374
  source: this.capturedRenderable
16976
17375
  });
@@ -16978,17 +17377,17 @@ Captured output:
16978
17377
  }
16979
17378
  }
16980
17379
  if (this.capturedRenderable && mouseEvent.type !== "up") {
16981
- const event2 = new MouseEvent(this.capturedRenderable, mouseEvent);
17380
+ const event2 = new MouseEvent(this.capturedRenderable, mouseEventWithClickCount);
16982
17381
  this.capturedRenderable.processMouseEvent(event2);
16983
17382
  return true;
16984
17383
  }
16985
17384
  if (this.capturedRenderable && mouseEvent.type === "up") {
16986
- const event2 = new MouseEvent(this.capturedRenderable, { ...mouseEvent, type: "drag-end" });
17385
+ const event2 = new MouseEvent(this.capturedRenderable, { ...mouseEventWithClickCount, type: "drag-end" });
16987
17386
  this.capturedRenderable.processMouseEvent(event2);
16988
- this.capturedRenderable.processMouseEvent(new MouseEvent(this.capturedRenderable, mouseEvent));
17387
+ this.capturedRenderable.processMouseEvent(new MouseEvent(this.capturedRenderable, mouseEventWithClickCount));
16989
17388
  if (maybeRenderable) {
16990
17389
  const event3 = new MouseEvent(maybeRenderable, {
16991
- ...mouseEvent,
17390
+ ...mouseEventWithClickCount,
16992
17391
  type: "drop",
16993
17392
  source: this.capturedRenderable
16994
17393
  });
@@ -17006,7 +17405,7 @@ Captured output:
17006
17405
  } else {
17007
17406
  this.setCapturedRenderable(undefined);
17008
17407
  }
17009
- event = this.dispatchMouseEvent(maybeRenderable, mouseEvent);
17408
+ event = this.dispatchMouseEvent(maybeRenderable, mouseEventWithClickCount);
17010
17409
  } else {
17011
17410
  this.setCapturedRenderable(undefined);
17012
17411
  this.lastOverRenderable = undefined;
@@ -17176,6 +17575,115 @@ Captured output:
17176
17575
  copyToClipboardOSC52(text, target) {
17177
17576
  return this.clipboard.copyToClipboardOSC52(text, target);
17178
17577
  }
17578
+ normalizeInputChunk(data) {
17579
+ if (typeof data === "string") {
17580
+ return data;
17581
+ }
17582
+ if (Buffer.isBuffer(data)) {
17583
+ return data;
17584
+ }
17585
+ if (data instanceof Uint8Array) {
17586
+ return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
17587
+ }
17588
+ return Buffer.from(data);
17589
+ }
17590
+ resolveClickCount(mouseEvent) {
17591
+ if (mouseEvent.type !== "down" || mouseEvent.button > 2 /* RIGHT */) {
17592
+ return 1;
17593
+ }
17594
+ const now = Date.now();
17595
+ const previous = this._lastClick;
17596
+ const isConsecutive = previous && previous.button === mouseEvent.button && previous.x === mouseEvent.x && previous.y === mouseEvent.y && now - previous.timestamp <= this.clickCountThresholdMs;
17597
+ const count = isConsecutive ? previous.count + 1 : 1;
17598
+ this._lastClick = {
17599
+ x: mouseEvent.x,
17600
+ y: mouseEvent.y,
17601
+ button: mouseEvent.button,
17602
+ timestamp: now,
17603
+ count
17604
+ };
17605
+ return count;
17606
+ }
17607
+ normalizeCrashError(reason) {
17608
+ if (reason instanceof Error) {
17609
+ return {
17610
+ message: reason.message || reason.name || "Unknown error",
17611
+ stack: reason.stack
17612
+ };
17613
+ }
17614
+ if (typeof reason === "string") {
17615
+ return { message: reason };
17616
+ }
17617
+ try {
17618
+ return { message: JSON.stringify(reason) };
17619
+ } catch {
17620
+ return { message: String(reason) };
17621
+ }
17622
+ }
17623
+ recordRuntimeEvent(type, payload) {
17624
+ this.recentRuntimeEvents.push({
17625
+ timestamp: new Date().toISOString(),
17626
+ type,
17627
+ payload
17628
+ });
17629
+ if (this.recentRuntimeEvents.length > this.maxRecentRuntimeEvents) {
17630
+ this.recentRuntimeEvents.shift();
17631
+ }
17632
+ }
17633
+ reportCrash(reason, source = "manual", extra) {
17634
+ const error = this.normalizeCrashError(reason);
17635
+ const report = {
17636
+ timestamp: new Date().toISOString(),
17637
+ source,
17638
+ message: error.message,
17639
+ stack: error.stack,
17640
+ recentEvents: [...this.recentRuntimeEvents]
17641
+ };
17642
+ if (extra && Object.keys(extra).length > 0) {
17643
+ report.recentEvents.push({
17644
+ timestamp: report.timestamp,
17645
+ type: "crash:context",
17646
+ payload: extra
17647
+ });
17648
+ }
17649
+ this.crashReports.push(report);
17650
+ if (this.crashReports.length > this.maxCrashReports) {
17651
+ this.crashReports.shift();
17652
+ }
17653
+ if (this._logCrashReportsToConsole) {
17654
+ console.error(this.formatCrashReportForConsole(report));
17655
+ }
17656
+ this.emit("crash" /* CRASH */, report);
17657
+ return report;
17658
+ }
17659
+ formatCrashReportForConsole(report) {
17660
+ const lines = [];
17661
+ lines.push("[Cascade Crash Report]");
17662
+ lines.push(`timestamp=${report.timestamp}`);
17663
+ lines.push(`source=${report.source}`);
17664
+ lines.push(`message=${report.message}`);
17665
+ if (report.stack) {
17666
+ lines.push("stack:");
17667
+ lines.push(report.stack);
17668
+ }
17669
+ if (report.recentEvents.length > 0) {
17670
+ lines.push(`recentEvents(${report.recentEvents.length}):`);
17671
+ for (const entry of report.recentEvents) {
17672
+ lines.push(`${entry.timestamp} ${entry.type} ${JSON.stringify(entry.payload)}`);
17673
+ }
17674
+ }
17675
+ return lines.join(`
17676
+ `);
17677
+ }
17678
+ getCrashReports() {
17679
+ return [...this.crashReports];
17680
+ }
17681
+ getRecentRuntimeEvents() {
17682
+ return [...this.recentRuntimeEvents];
17683
+ }
17684
+ clearCrashReports() {
17685
+ this.crashReports = [];
17686
+ }
17179
17687
  copyToSystemClipboard(text) {
17180
17688
  return this.clipboard.copyToSystemClipboard(text);
17181
17689
  }
@@ -17379,8 +17887,8 @@ Captured output:
17379
17887
  this._destroyFinalized = true;
17380
17888
  this._destroyPending = false;
17381
17889
  process.removeListener("SIGWINCH", this.sigwinchHandler);
17382
- process.removeListener("uncaughtException", this.handleError);
17383
- process.removeListener("unhandledRejection", this.handleError);
17890
+ process.removeListener("uncaughtException", this.uncaughtExceptionHandler);
17891
+ process.removeListener("unhandledRejection", this.unhandledRejectionHandler);
17384
17892
  process.removeListener("warning", this.warningHandler);
17385
17893
  process.removeListener("beforeExit", this.exitHandler);
17386
17894
  capture.removeListener("write", this.captureCallback);
@@ -17468,6 +17976,8 @@ Captured output:
17468
17976
  this.renderStats.frameCount++;
17469
17977
  this.renderStats.fps = this.currentFps;
17470
17978
  const overallStart = performance.now();
17979
+ const trace = this.trace;
17980
+ const traceEnabled = trace?.enabled === true;
17471
17981
  const frameRequests = Array.from(this.animationRequest.values());
17472
17982
  this.animationRequest.clear();
17473
17983
  const animationRequestStart = performance.now();
@@ -17482,22 +17992,33 @@ Captured output:
17482
17992
  try {
17483
17993
  await frameCallback(deltaTime);
17484
17994
  } catch (error) {
17995
+ this.reportCrash(error, "frame-callback");
17485
17996
  console.error("Error in frame callback:", error);
17486
17997
  }
17487
17998
  }
17488
17999
  const end = performance.now();
17489
18000
  this.renderStats.frameCallbackTime = end - start;
18001
+ const renderTreeStart = traceEnabled ? trace.now() : 0;
17490
18002
  this.root.render(this.nextRenderBuffer, deltaTime);
18003
+ const renderTreeMs = traceEnabled ? trace.now() - renderTreeStart : 0;
18004
+ const postProcessStart = traceEnabled ? trace.now() : 0;
17491
18005
  for (const postProcessFn of this.postProcessFns) {
17492
18006
  postProcessFn(this.nextRenderBuffer, deltaTime);
17493
18007
  }
17494
18008
  this._console.renderToBuffer(this.nextRenderBuffer);
18009
+ const postProcessMs = traceEnabled ? trace.now() - postProcessStart : 0;
17495
18010
  if (!this._isDestroyed) {
18011
+ const nativeStart = traceEnabled ? trace.now() : 0;
17496
18012
  this.renderNative();
18013
+ const nativeMs = traceEnabled ? trace.now() - nativeStart : 0;
17497
18014
  if (this._useMouse && this.lib.getHitGridDirty(this.rendererPtr)) {
17498
18015
  this.recheckHoverState();
17499
18016
  }
17500
18017
  const overallFrameTime = performance.now() - overallStart;
18018
+ if (traceEnabled) {
18019
+ trace.write(`trace.render.native ms=${nativeMs.toFixed(3)}`);
18020
+ trace.write(`trace.render.pipeline treeMs=${renderTreeMs.toFixed(3)} postMs=${postProcessMs.toFixed(3)} nativeMs=${nativeMs.toFixed(3)} frameMs=${overallFrameTime.toFixed(3)} animMs=${animationRequestTime.toFixed(3)}`);
18021
+ }
17501
18022
  this.lib.updateStats(this.rendererPtr, overallFrameTime, this.renderStats.fps, this.renderStats.frameCallbackTime);
17502
18023
  if (this.gatherStats) {
17503
18024
  this.collectStatSample(overallFrameTime);
@@ -17515,6 +18036,9 @@ Captured output:
17515
18036
  this.renderTimeout = null;
17516
18037
  }
17517
18038
  }
18039
+ } catch (error) {
18040
+ this.reportCrash(error, "render-loop");
18041
+ console.error("Error in render loop:", error);
17518
18042
  } finally {
17519
18043
  this.rendering = false;
17520
18044
  if (this._destroyPending) {
@@ -17591,6 +18115,30 @@ Captured output:
17591
18115
  }
17592
18116
  this.selectionContainers = [];
17593
18117
  }
18118
+ selectWord(x, y) {
18119
+ let current = this.resolveSelectionMethodTarget(x, y);
18120
+ while (current) {
18121
+ if (current.selectWord(x, y))
18122
+ return;
18123
+ current = current.parent;
18124
+ }
18125
+ }
18126
+ selectLine(x, y) {
18127
+ let current = this.resolveSelectionMethodTarget(x, y);
18128
+ while (current) {
18129
+ if (current.selectLine(x, y))
18130
+ return;
18131
+ current = current.parent;
18132
+ }
18133
+ }
18134
+ updateSelectionWordSnap(x, y) {
18135
+ let current = this.resolveSelectionMethodTarget(x, y);
18136
+ while (current) {
18137
+ if (current.updateSelectionWordSnap(x, y))
18138
+ return;
18139
+ current = current.parent;
18140
+ }
18141
+ }
17594
18142
  startSelection(renderable, x, y) {
17595
18143
  if (!renderable.selectable)
17596
18144
  return;
@@ -17636,7 +18184,11 @@ Captured output:
17636
18184
  }
17637
18185
  isWithinContainer(renderable, container) {
17638
18186
  let current = renderable;
18187
+ const visited = new Set;
17639
18188
  while (current) {
18189
+ if (visited.has(current))
18190
+ return false;
18191
+ visited.add(current);
17640
18192
  if (current === container)
17641
18193
  return true;
17642
18194
  current = current.parent;
@@ -17650,6 +18202,24 @@ Captured output:
17650
18202
  this.notifySelectablesOfSelectionChange();
17651
18203
  }
17652
18204
  }
18205
+ resolveSelectionMethodTarget(x, y) {
18206
+ const maybeRenderableId = this.hitTest(x, y);
18207
+ let current = Renderable.renderablesByNumber.get(maybeRenderableId) ?? null;
18208
+ while (current) {
18209
+ if (current.selectable && !current.isDestroyed) {
18210
+ return current;
18211
+ }
18212
+ current = current.parent;
18213
+ }
18214
+ const candidates = [...Renderable.renderablesByNumber.values()];
18215
+ for (let i = candidates.length - 1;i >= 0; i -= 1) {
18216
+ const candidate = candidates[i];
18217
+ if (candidate.selectable && !candidate.isDestroyed && x >= candidate.x && x < candidate.x + candidate.width && y >= candidate.y && y < candidate.y + candidate.height) {
18218
+ return candidate;
18219
+ }
18220
+ }
18221
+ return null;
18222
+ }
17653
18223
  notifySelectablesOfSelectionChange() {
17654
18224
  const selectedRenderables = [];
17655
18225
  const touchedRenderables = [];
@@ -17719,5 +18289,5 @@ Captured output:
17719
18289
 
17720
18290
  export { __toESM, __commonJS, __export, __require, Edge, Gutter, MeasureMode, exports_src, isValidBorderStyle, parseBorderStyle, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, nonAlphanumericKeys, parseKeypress, KeyEvent, PasteEvent, KeyHandler, InternalKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, ATTRIBUTE_BASE_BITS, ATTRIBUTE_BASE_MASK, getBaseAttributes, DebugOverlayCorner, createTextAttributes, attributesWithLink, getLinkId, visualizeRenderableTree, isStyledText, 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, link, t, hastToStyledText, LinearScrollAccel, MacOSScrollAccel, StdinBuffer, parseAlign, parseAlignItems, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, convertGlobalToLocalSelection, ASCIIFontSelectionHelper, envRegistry, registerEnvVar, clearEnvCache, generateEnvMarkdown, generateEnvColored, env, treeSitterToTextChunks, treeSitterToStyledText, addDefaultParsers, TreeSitterClient, DataPathsManager, getDataPaths, extToFiletype, pathToFiletype, main, getTreeSitterClient, ExtmarksController, createExtmarksController, TerminalPalette, createTerminalPalette, TextBuffer, SpanInfoStruct, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, maybeMakeRenderable, wrapWithDelegates, instantiate, delegate, isValidPercentage, LayoutEvents, RenderableEvents, isRenderable, BaseRenderable, Renderable, RootRenderable, ANSI, defaultKeyAliases, mergeKeyAliases, mergeKeyBindings, getKeyBindingKey, buildKeyBindingsMap, capture, ConsolePosition, TerminalConsole, getObjectsInViewport, buildKittyKeyboardFlags, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, RendererControlState, CliRenderer };
17721
18291
 
17722
- //# debugId=0377C87661B220C664756E2164756E21
17723
- //# sourceMappingURL=index-jx194wn1.js.map
18292
+ //# debugId=A43FA725E26A0ABA64756E2164756E21
18293
+ //# sourceMappingURL=index-r7vqrd61.js.map