@ashx-j/lunr-tui 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.js CHANGED
@@ -104,6 +104,20 @@ export class Container {
104
104
  }
105
105
  return lines;
106
106
  }
107
+ handleClick(localY, width) {
108
+ let y = 0;
109
+ for (const child of this.children) {
110
+ const h = child.render(width).length;
111
+ if (localY >= y && localY < y + h) {
112
+ if (typeof child.handleClick === "function") {
113
+ return child.handleClick(localY - y, width);
114
+ }
115
+ return false;
116
+ }
117
+ y += h;
118
+ }
119
+ return false;
120
+ }
107
121
  }
108
122
  /**
109
123
  * TUI - Main class for managing terminal UI with differential rendering
@@ -157,6 +171,9 @@ export class TUI extends Container {
157
171
  static WHEEL_LINES = 3;
158
172
  chatScrollbar;
159
173
  scrollbarDrag;
174
+ pendingClick;
175
+ lastChatStart = 0;
176
+ lastChatSliceHeight = 0;
160
177
  constructor(terminal, showHardwareCursor) {
161
178
  super();
162
179
  this.terminal = terminal;
@@ -386,27 +403,29 @@ export class TUI extends Container {
386
403
  return -1;
387
404
  return this.children.indexOf(this.pinComponent);
388
405
  }
389
- collectChildLines(from, to, width) {
406
+ collectChildLines(from, to, width, ranges) {
390
407
  const lines = [];
391
408
  for (let i = from; i < to; i++) {
392
409
  const child = this.children[i];
393
410
  if (!child)
394
411
  continue;
395
- this.appendRenderedChild(lines, child, width);
412
+ this.appendRenderedChild(lines, child, width, ranges);
396
413
  }
397
414
  return lines;
398
415
  }
399
- appendRenderedChild(lines, child, width) {
416
+ appendRenderedChild(lines, child, width, ranges) {
400
417
  const nested = "children" in child && Array.isArray(child.children);
401
418
  if (nested && child.children.length > 0 && child.render === Container.prototype.render) {
402
419
  for (const nestedChild of child.children) {
403
- this.appendRenderedChild(lines, nestedChild, width);
420
+ this.appendRenderedChild(lines, nestedChild, width, ranges);
404
421
  }
405
422
  return;
406
423
  }
424
+ const start = lines.length;
407
425
  for (const line of child.render(width)) {
408
426
  lines.push(line);
409
427
  }
428
+ ranges?.push({ component: child, start, end: lines.length });
410
429
  }
411
430
  snapStartToKittyImageHeader(lines, start) {
412
431
  if (start <= 0 || start >= lines.length)
@@ -448,27 +467,32 @@ export class TUI extends Container {
448
467
  this.chatLaidOut = true;
449
468
  let chatWidth = this.chatUsesGutter ? Math.max(1, width - 1) : width;
450
469
  let scrollLines;
470
+ let ranges;
451
471
  let fromCache = false;
452
472
  const cache = this.chatLayoutCache;
453
473
  if (cache && cache.epoch === this.contentEpoch && cache.chatWidth === chatWidth) {
454
474
  scrollLines = cache.scrollLines;
475
+ ranges = cache.ranges;
455
476
  fromCache = true;
456
477
  }
457
478
  else {
458
- scrollLines = this.collectChildLines(0, pinIndex, chatWidth);
479
+ ranges = [];
480
+ scrollLines = this.collectChildLines(0, pinIndex, chatWidth, ranges);
459
481
  }
460
482
  if (this.chatUsesGutter) {
461
483
  if (scrollLines.length <= viewH) {
462
484
  this.chatUsesGutter = false;
463
485
  chatWidth = width;
464
- scrollLines = this.collectChildLines(0, pinIndex, chatWidth);
486
+ ranges = [];
487
+ scrollLines = this.collectChildLines(0, pinIndex, chatWidth, ranges);
465
488
  fromCache = false;
466
489
  }
467
490
  }
468
491
  else if (scrollLines.length > viewH) {
469
492
  this.chatUsesGutter = true;
470
493
  chatWidth = Math.max(1, width - 1);
471
- scrollLines = this.collectChildLines(0, pinIndex, chatWidth);
494
+ ranges = [];
495
+ scrollLines = this.collectChildLines(0, pinIndex, chatWidth, ranges);
472
496
  fromCache = false;
473
497
  }
474
498
  if (!fromCache) {
@@ -476,6 +500,7 @@ export class TUI extends Container {
476
500
  epoch: this.contentEpoch,
477
501
  chatWidth,
478
502
  scrollLines,
503
+ ranges,
479
504
  };
480
505
  }
481
506
  let chatSlice;
@@ -487,6 +512,7 @@ export class TUI extends Container {
487
512
  this.chatScrollbar = undefined;
488
513
  this.scrollbarDrag = undefined;
489
514
  chatSlice = scrollLines.slice();
515
+ this.lastChatStart = 0;
490
516
  }
491
517
  else {
492
518
  this.lastChatScrollMax = scrollLines.length - viewH;
@@ -494,6 +520,7 @@ export class TUI extends Container {
494
520
  start = scrollLines.length - viewH - this.chatScrollOffset;
495
521
  start = this.snapStartToKittyImageHeader(scrollLines, start);
496
522
  start = Math.max(0, start);
523
+ this.lastChatStart = start;
497
524
  chatSlice = scrollLines.slice(start, start + viewH);
498
525
  while (chatSlice.length < viewH) {
499
526
  chatSlice.push("");
@@ -521,6 +548,7 @@ export class TUI extends Container {
521
548
  chatSlice[i] = `${padToWidth(chatSlice[i] ?? "", chatWidth)}${glyph}`;
522
549
  }
523
550
  }
551
+ this.lastChatSliceHeight = chatSlice.length;
524
552
  return [...chatSlice, ...dockLines];
525
553
  }
526
554
  /**
@@ -860,8 +888,67 @@ export class TUI extends Container {
860
888
  this.setChatScroll(Math.round((1 - rel) * sb.scrollMax));
861
889
  }
862
890
  this.scrollbarDrag = { grabY: y, startOffset: this.chatScrollOffset };
891
+ this.pendingClick = undefined;
892
+ return;
893
+ }
894
+ if (mouse.kind === "button" && mouse.button === 0 && !mouse.release) {
895
+ this.pendingClick = { x: mouse.x, y: mouse.y };
863
896
  return;
864
897
  }
898
+ if (mouse.kind === "move" && this.pendingClick) {
899
+ const dx = Math.abs(mouse.x - this.pendingClick.x);
900
+ const dy = Math.abs(mouse.y - this.pendingClick.y);
901
+ if (dx + dy > 1) {
902
+ this.pendingClick = undefined;
903
+ }
904
+ return;
905
+ }
906
+ if (mouse.kind === "button" && mouse.button === 0 && mouse.release) {
907
+ const pending = this.pendingClick;
908
+ this.pendingClick = undefined;
909
+ if (!pending)
910
+ return;
911
+ if (this.dispatchChatClick(pending.y) || this.dispatchDockClick(pending.y)) {
912
+ this.requestRender();
913
+ }
914
+ }
915
+ }
916
+ dispatchChatClick(mouseY) {
917
+ const cache = this.chatLayoutCache;
918
+ if (!cache || mouseY < 1 || mouseY > this.lastChatSliceHeight)
919
+ return false;
920
+ const lineIndex = this.lastChatStart + (mouseY - 1);
921
+ const hit = cache.ranges.find((range) => lineIndex >= range.start && lineIndex < range.end);
922
+ if (!hit || typeof hit.component.handleClick !== "function")
923
+ return false;
924
+ return hit.component.handleClick(lineIndex - hit.start, cache.chatWidth) === true;
925
+ }
926
+ dispatchDockClick(mouseY) {
927
+ if (mouseY <= this.lastChatSliceHeight)
928
+ return false;
929
+ const pin = this.getPinIndex();
930
+ if (pin < 0)
931
+ return false;
932
+ const width = this.terminal.columns;
933
+ let y = this.lastChatSliceHeight;
934
+ const local = mouseY - 1;
935
+ for (let i = pin; i < this.children.length; i++) {
936
+ const child = this.children[i];
937
+ if (!child)
938
+ continue;
939
+ const h = child.render(width).length;
940
+ if (local >= y && local < y + h) {
941
+ if (typeof child.handleClick === "function") {
942
+ return child.handleClick(local - y, width);
943
+ }
944
+ if (child instanceof Container) {
945
+ return child.handleClick(local - y, width);
946
+ }
947
+ return false;
948
+ }
949
+ y += h;
950
+ }
951
+ return false;
865
952
  }
866
953
  dragScrollbarTo(mouseY) {
867
954
  if (!this.scrollbarDrag || !this.chatScrollbar)