@code-yeongyu/senpi-tui 2026.9.12-3 → 2026.9.12

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.
Files changed (94) hide show
  1. package/README.md +4 -53
  2. package/dist/alt-screen-search.d.ts +1 -21
  3. package/dist/alt-screen-search.d.ts.map +1 -1
  4. package/dist/alt-screen-search.js +42 -199
  5. package/dist/alt-screen-search.js.map +1 -1
  6. package/dist/components/box.d.ts +1 -3
  7. package/dist/components/box.d.ts.map +1 -1
  8. package/dist/components/box.js +0 -28
  9. package/dist/components/box.js.map +1 -1
  10. package/dist/components/editor.d.ts +1 -6
  11. package/dist/components/editor.d.ts.map +1 -1
  12. package/dist/components/editor.js +18 -98
  13. package/dist/components/editor.js.map +1 -1
  14. package/dist/components/input.d.ts +1 -12
  15. package/dist/components/input.d.ts.map +1 -1
  16. package/dist/components/input.js +6 -40
  17. package/dist/components/input.js.map +1 -1
  18. package/dist/components/latex.d.ts.map +1 -1
  19. package/dist/components/latex.js +0 -7
  20. package/dist/components/latex.js.map +1 -1
  21. package/dist/components/loader.d.ts +0 -3
  22. package/dist/components/loader.d.ts.map +1 -1
  23. package/dist/components/loader.js +8 -20
  24. package/dist/components/loader.js.map +1 -1
  25. package/dist/components/scroll-view.d.ts +3 -6
  26. package/dist/components/scroll-view.d.ts.map +1 -1
  27. package/dist/components/scroll-view.js +1 -6
  28. package/dist/components/scroll-view.js.map +1 -1
  29. package/dist/components/select-list.d.ts +1 -4
  30. package/dist/components/select-list.d.ts.map +1 -1
  31. package/dist/components/select-list.js +2 -48
  32. package/dist/components/select-list.js.map +1 -1
  33. package/dist/components/settings-list.d.ts +1 -5
  34. package/dist/components/settings-list.d.ts.map +1 -1
  35. package/dist/components/settings-list.js +5 -54
  36. package/dist/components/settings-list.js.map +1 -1
  37. package/dist/index.d.ts +1 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +0 -3
  40. package/dist/index.js.map +1 -1
  41. package/dist/layout.d.ts +1 -3
  42. package/dist/layout.d.ts.map +1 -1
  43. package/dist/layout.js +9 -32
  44. package/dist/layout.js.map +1 -1
  45. package/dist/native-modifiers.d.ts +1 -2
  46. package/dist/native-modifiers.d.ts.map +1 -1
  47. package/dist/native-modifiers.js +44 -3
  48. package/dist/native-modifiers.js.map +1 -1
  49. package/dist/terminal.d.ts.map +1 -1
  50. package/dist/terminal.js +20 -2
  51. package/dist/terminal.js.map +1 -1
  52. package/dist/tui-alt-screen.d.ts +1 -31
  53. package/dist/tui-alt-screen.d.ts.map +1 -1
  54. package/dist/tui-alt-screen.js +41 -335
  55. package/dist/tui-alt-screen.js.map +1 -1
  56. package/dist/tui.d.ts +5 -83
  57. package/dist/tui.d.ts.map +1 -1
  58. package/dist/tui.js +4 -118
  59. package/dist/tui.js.map +1 -1
  60. package/dist/utils.d.ts +0 -2
  61. package/dist/utils.d.ts.map +1 -1
  62. package/dist/utils.js +0 -9
  63. package/dist/utils.js.map +1 -1
  64. package/native/darwin/README.md +6 -8
  65. package/native/darwin/build.sh +5 -12
  66. package/native/darwin/prebuilds/darwin-arm64/{darwin-platform.node → darwin-modifiers.node} +0 -0
  67. package/native/darwin/prebuilds/darwin-x64/{darwin-platform.node → darwin-modifiers.node} +0 -0
  68. package/native/darwin/src/darwin-modifiers.c +70 -0
  69. package/native/win32/README.md +7 -18
  70. package/native/win32/build.mjs +4 -7
  71. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  72. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  73. package/native/win32/src/win32-console-mode.c +119 -0
  74. package/package.json +3 -10
  75. package/dist/components/mouse-region.d.ts +0 -12
  76. package/dist/components/mouse-region.d.ts.map +0 -1
  77. package/dist/components/mouse-region.js +0 -19
  78. package/dist/components/mouse-region.js.map +0 -1
  79. package/dist/native-platform.d.ts +0 -18
  80. package/dist/native-platform.d.ts.map +0 -1
  81. package/dist/native-platform.js +0 -42
  82. package/dist/native-platform.js.map +0 -1
  83. package/native/clipboard.h +0 -170
  84. package/native/darwin/src/darwin-platform.m +0 -93
  85. package/native/linux/README.md +0 -23
  86. package/native/linux/build.sh +0 -65
  87. package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  88. package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  89. package/native/linux/src/clipboard-worker.h +0 -70
  90. package/native/linux/src/linux-platform-x11.c +0 -274
  91. package/native/napi.h +0 -89
  92. package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  93. package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  94. package/native/win32/src/win32-platform.c +0 -241
@@ -1,14 +1,13 @@
1
1
  var _a;
2
- import { AltScreenSearchComponent, AltScreenSearchIndex, getAltScreenSearchMatchKey, } from "./alt-screen-search.js";
2
+ import { AltScreenSearchComponent, findAltScreenSearchMatches, getAltScreenSearchMatchKey, } from "./alt-screen-search.js";
3
3
  import { AltScreenFlashContainer } from "./components/alt-screen-flash.js";
4
4
  import { ScrollView } from "./components/scroll-view.js";
5
5
  import { getKeybindings } from "./keybindings.js";
6
6
  import { isKeyRelease } from "./keys.js";
7
- import { getLayoutBoxesAt, getScrollbarGeometry, getScrollViewBox, getScrollViewsAt, renderLayoutFrame, } from "./layout.js";
8
- import { getLayoutNode } from "./layout-node.js";
7
+ import { getScrollbarGeometry, getScrollViewBox, getScrollViewsAt, renderLayoutFrame, } from "./layout.js";
9
8
  import { deleteAllKittyImages, deleteAllKittyPlacements, deleteKittyImage, getCapabilities, getKittyImagePlacement, isImageLine, setCapabilities, } from "./terminal-image.js";
10
- import { Container, CURSOR_MARKER, compositeTuiLine, dispatchMouseEvent, retargetMouseEvent, TuiBase, VIEWPORT_TUI, } from "./tui.js";
11
- import { extractAnsiCode, getGraphemeCellRange, getOsc8LinkAtColumn, getWordSegmenter, sliceByColumn, stripTerminalSequences, truncateToWidth, visibleWidth, } from "./utils.js";
9
+ import { CURSOR_MARKER, compositeTuiLine, TuiBase, VIEWPORT_TUI, } from "./tui.js";
10
+ import { extractAnsiCode, getGraphemeCellRange, getOsc8LinkAtColumn, getWordSegmenter, sliceByColumn, stripTerminalSequences, visibleWidth, } from "./utils.js";
12
11
  const ENTER_ALT_SCREEN = "\x1b[?1049h";
13
12
  const EXIT_ALT_SCREEN = "\x1b[?1049l";
14
13
  const DISABLE_AUTOWRAP = "\x1b[?7l";
@@ -23,7 +22,6 @@ const END_SYNCHRONIZED_OUTPUT = "\x1b[?2026l";
23
22
  const OSC133_ZONE_PREFIX = /^(?:\x1b\]133;[ABC](?:\x07|\x1b\\))+/;
24
23
  const OSC133_PROMPT_START = /^\x1b\]133;A(?:\x07|\x1b\\)/;
25
24
  const PAGE_SCROLL_OVERLAP = 4;
26
- const ALT_WHEEL_SCROLL_MULTIPLIER = 5;
27
25
  const MAX_CACHED_OFFSCREEN_KITTY_IMAGES = 16;
28
26
  const MAX_CACHED_OFFSCREEN_KITTY_TRANSMISSION_BYTES = 32 * 1024 * 1024;
29
27
  const MAX_CACHED_OFFSCREEN_KITTY_DECODED_BYTES = 64 * 1024 * 1024;
@@ -50,10 +48,8 @@ export class TuiAltScreen extends TuiBase {
50
48
  this.selectionAutoScrollDirection = 0;
51
49
  this.selectionPressActive = false;
52
50
  this.selectionDragged = false;
53
- this.mousePressMoved = false;
54
51
  this.implicitDocument = {
55
52
  render: (width) => super.render(width),
56
- handleMouse: (event) => super.handleMouse(event),
57
53
  invalidate: () => {
58
54
  for (const child of this.children)
59
55
  child.invalidate();
@@ -65,8 +61,6 @@ export class TuiAltScreen extends TuiBase {
65
61
  this.mouseEnabled = options.mouse ?? true;
66
62
  this.searchMatchStyle = options.searchMatchStyle ?? ((text) => `\x1b[4m${text}\x1b[24m`);
67
63
  this.searchCurrentMatchStyle = options.searchCurrentMatchStyle ?? ((text) => `\x1b[1;7m${text}\x1b[22;27m`);
68
- this.searchNavigationButtonStyle = options.searchNavigationButtonStyle ?? ((text) => text);
69
- this.scrollToEndIndicator = options.scrollToEndIndicator;
70
64
  this.openUrl = options.openUrl;
71
65
  this.onRightClickPaste = options.onRightClickPaste;
72
66
  this.copyOnSelect = options.copyOnSelect ?? true;
@@ -135,8 +129,6 @@ export class TuiAltScreen extends TuiBase {
135
129
  this.lastClick = undefined;
136
130
  this.pressedUrl = undefined;
137
131
  this.selectionDragged = false;
138
- this.clearComponentMouseGesture();
139
- this.lastComponentClick = undefined;
140
132
  this.resetRenderState();
141
133
  const term = process.env.TERM?.toLowerCase() ?? "";
142
134
  // Multiplexers can lag when every pointer movement is forwarded. Button-motion
@@ -156,7 +148,6 @@ export class TuiAltScreen extends TuiBase {
156
148
  this.selectionPressActive = false;
157
149
  this.stopScrollbarHover();
158
150
  this.stopScrollbarDrag();
159
- this.clearComponentMouseGesture();
160
151
  this.flashes.dispose();
161
152
  if (!this.altScreenActive)
162
153
  return;
@@ -271,15 +262,14 @@ export class TuiAltScreen extends TuiBase {
271
262
  return;
272
263
  }
273
264
  }
274
- toggleSearch() {
265
+ openSearch() {
275
266
  if (this.activeSearch) {
276
- this.closeSearch();
267
+ this.activeSearch.overlay?.focus();
277
268
  return;
278
269
  }
279
- const component = new AltScreenSearchComponent((query) => this.updateSearchQuery(query), this.searchNavigationButtonStyle);
270
+ const component = new AltScreenSearchComponent((query) => this.updateSearchQuery(query));
280
271
  const search = {
281
272
  component,
282
- index: new AltScreenSearchIndex(),
283
273
  query: "",
284
274
  matches: [],
285
275
  selectedIndex: -1,
@@ -290,7 +280,7 @@ export class TuiAltScreen extends TuiBase {
290
280
  search.overlay = this.showOverlay(component, {
291
281
  anchor: "top-right",
292
282
  width: "40%",
293
- minWidth: 32,
283
+ minWidth: 24,
294
284
  margin: 1,
295
285
  });
296
286
  }
@@ -320,29 +310,6 @@ export class TuiAltScreen extends TuiBase {
320
310
  search.selectionMode = direction < 0 ? "previous" : "next";
321
311
  this.requestRender();
322
312
  }
323
- getSearchNavigationDirectionAt(x, y) {
324
- const search = this.activeSearch;
325
- const bounds = search?.overlay?.getBounds();
326
- if (!search || !bounds)
327
- return undefined;
328
- if (x < bounds.col || x >= bounds.col + bounds.width || y < bounds.row || y >= bounds.row + bounds.height) {
329
- return undefined;
330
- }
331
- return search.component.getNavigationDirectionAt(y - bounds.row, x - bounds.col);
332
- }
333
- handleSearchMouseEvent(event) {
334
- const search = this.activeSearch;
335
- if (!search)
336
- return false;
337
- const direction = this.getSearchNavigationDirectionAt(event.x, event.y);
338
- if (search.component.setHoveredNavigationDirection(direction))
339
- this.requestRender();
340
- if (direction === undefined || event.release || (event.button & 32) !== 0 || (event.button & 3) !== 0) {
341
- return false;
342
- }
343
- this.navigateSearch(direction);
344
- return true;
345
- }
346
313
  refreshSearch(layout) {
347
314
  const search = this.activeSearch;
348
315
  if (!search)
@@ -359,29 +326,16 @@ export class TuiAltScreen extends TuiBase {
359
326
  return false;
360
327
  }
361
328
  const shouldRevealSelection = search.selectionMode !== "retain";
362
- const result = search.index.search(lines, search.query);
363
- const matches = result.matches;
364
- search.matches = matches;
365
- if (!result.changed && search.selectionMode === "retain")
366
- return false;
367
- const exactIndex = result.changed
368
- ? search.selectedKey
369
- ? matches.findIndex((match) => getAltScreenSearchMatchKey(match) === search.selectedKey)
370
- : -1
371
- : search.selectedIndex;
329
+ const matches = findAltScreenSearchMatches(lines, search.query);
330
+ const exactIndex = search.selectedKey
331
+ ? matches.findIndex((match) => getAltScreenSearchMatchKey(match) === search.selectedKey)
332
+ : -1;
372
333
  let selectedIndex = -1;
373
334
  if (matches.length > 0) {
374
335
  if (search.selectionMode === "query") {
375
- let low = 0;
376
- let high = matches.length;
377
- while (low < high) {
378
- const middle = low + Math.floor((high - low) / 2);
379
- if ((matches[middle].segments[0]?.row ?? 0) < search.anchorRow)
380
- low = middle + 1;
381
- else
382
- high = middle;
383
- }
384
- selectedIndex = low < matches.length ? low : 0;
336
+ selectedIndex = matches.findIndex((match) => (match.segments[0]?.row ?? 0) >= search.anchorRow);
337
+ if (selectedIndex < 0)
338
+ selectedIndex = 0;
385
339
  }
386
340
  else if (search.selectionMode === "next") {
387
341
  const baseIndex = exactIndex >= 0 ? exactIndex : Math.min(search.selectedIndex, matches.length - 1);
@@ -396,6 +350,7 @@ export class TuiAltScreen extends TuiBase {
396
350
  exactIndex >= 0 ? exactIndex : Math.min(Math.max(0, search.selectedIndex), matches.length - 1);
397
351
  }
398
352
  }
353
+ search.matches = matches;
399
354
  search.selectedIndex = selectedIndex;
400
355
  search.selectedKey = selectedIndex >= 0 ? getAltScreenSearchMatchKey(matches[selectedIndex]) : undefined;
401
356
  search.selectionMode = "retain";
@@ -423,12 +378,6 @@ export class TuiAltScreen extends TuiBase {
423
378
  shouldDeferViewportInputToOverlay() {
424
379
  return this.isOverlayFocused() && this.activeSearch?.overlay?.isFocused() !== true;
425
380
  }
426
- clearComponentMouseGesture() {
427
- this.mouseCapture = undefined;
428
- this.mousePressTarget = undefined;
429
- this.mousePressPoint = undefined;
430
- this.mousePressMoved = false;
431
- }
432
381
  handleViewportInput(data) {
433
382
  if (data === FOCUS_OUT) {
434
383
  const hadActiveSelection = this.selectionPressActive;
@@ -436,13 +385,9 @@ export class TuiAltScreen extends TuiBase {
436
385
  this.selectionPressActive = false;
437
386
  this.stopSelectionAutoScroll();
438
387
  this.stopScrollbarHover();
439
- if (this.activeSearch?.component.setHoveredNavigationDirection(undefined))
440
- this.requestRender();
441
388
  this.stopScrollbarDrag();
442
389
  this.pressedUrl = undefined;
443
390
  this.selectionDragged = false;
444
- this.clearComponentMouseGesture();
445
- this.lastComponentClick = undefined;
446
391
  if (hadActiveSelection) {
447
392
  this.selectionAnchor = undefined;
448
393
  this.selectionFocus = undefined;
@@ -458,16 +403,6 @@ export class TuiAltScreen extends TuiBase {
458
403
  return { consume: true };
459
404
  const wheelEvent = this.parseWheelEvent(data);
460
405
  if (wheelEvent) {
461
- const event = this.createMouseEvent("wheel", wheelEvent.button, wheelEvent.x, wheelEvent.y, {
462
- wheelDelta: wheelEvent.direction * this.getWheelScrollLines(wheelEvent.button),
463
- });
464
- const overlay = this.dispatchMouseToOverlay(event);
465
- const result = overlay.result ?? (overlay.hit ? undefined : this.dispatchMouseToLayout(event));
466
- if (result) {
467
- if (this.applyMouseDispatchResult(event, result))
468
- this.requestRender();
469
- return { consume: true };
470
- }
471
406
  if (this.shouldDeferViewportInputToOverlay())
472
407
  return undefined;
473
408
  this.routeWheel(wheelEvent);
@@ -475,7 +410,13 @@ export class TuiAltScreen extends TuiBase {
475
410
  }
476
411
  const mouseEvent = this.parseSgrMouseEvent(data);
477
412
  if (mouseEvent) {
478
- this.handleMouseEvent(mouseEvent);
413
+ if (this.handleRightClickPaste(mouseEvent))
414
+ return { consume: true };
415
+ const handled = this.handleScrollbarMouseEvent(mouseEvent);
416
+ if (!this.scrollbarDrag)
417
+ this.updateScrollbarHover(mouseEvent.x, mouseEvent.y);
418
+ if (!handled)
419
+ this.handleSelectionMouseEvent(mouseEvent);
479
420
  return { consume: true };
480
421
  }
481
422
  if (this.isMouseSequence(data))
@@ -484,7 +425,7 @@ export class TuiAltScreen extends TuiBase {
484
425
  const isRelease = isKeyRelease(data);
485
426
  if (keybindings.matches(data, "tui.altScreen.search")) {
486
427
  if (!isRelease)
487
- this.toggleSearch();
428
+ this.openSearch();
488
429
  return { consume: true };
489
430
  }
490
431
  if (this.activeSearch?.overlay?.isFocused()) {
@@ -560,165 +501,6 @@ export class TuiAltScreen extends TuiBase {
560
501
  }
561
502
  return undefined;
562
503
  }
563
- decodeMouseButton(button) {
564
- switch (button & 3) {
565
- case 0:
566
- return "left";
567
- case 1:
568
- return "middle";
569
- case 2:
570
- return "right";
571
- default:
572
- return "none";
573
- }
574
- }
575
- createMouseEvent(type, button, x, y, extra = {}) {
576
- return {
577
- type,
578
- button: type === "wheel" ? "none" : this.decodeMouseButton(button),
579
- x,
580
- y,
581
- screenX: x,
582
- screenY: y,
583
- width: Math.max(1, this.terminal.columns),
584
- height: Math.max(1, this.terminal.rows),
585
- shift: (button & 4) !== 0,
586
- alt: (button & 8) !== 0,
587
- ctrl: (button & 16) !== 0,
588
- ...(extra.wheelDelta === undefined ? {} : { wheelDelta: extra.wheelDelta }),
589
- ...(extra.clickCount === undefined ? {} : { clickCount: extra.clickCount }),
590
- };
591
- }
592
- dispatchMouseToLayout(event) {
593
- if (!this.currentLayout)
594
- return undefined;
595
- const visited = new Set();
596
- const boxes = getLayoutBoxesAt(this.currentLayout, event.screenX, event.screenY);
597
- for (const box of boxes) {
598
- if (visited.has(box.component))
599
- continue;
600
- if (getLayoutNode(box.component) && box.component.handleMouse === Container.prototype.handleMouse)
601
- continue;
602
- visited.add(box.component);
603
- const result = dispatchMouseEvent(box.component, {
604
- ...event,
605
- x: event.screenX - box.rect.x,
606
- y: event.screenY - box.rect.y,
607
- width: box.rect.width,
608
- height: box.rect.height,
609
- });
610
- if (result)
611
- return result;
612
- }
613
- return undefined;
614
- }
615
- applyMouseDispatchResult(event, result) {
616
- const focusTarget = this.resolveMouseFocusTarget(result.focusTarget ?? result.target.component);
617
- const focusChanged = result.focus === true && this.getFocusedComponent() !== focusTarget;
618
- if (result.focus)
619
- this.setFocus(focusTarget);
620
- if (result.capture)
621
- this.mouseCapture = result.target;
622
- return (result.render ??
623
- (focusChanged ||
624
- event.type === "press" ||
625
- event.type === "click" ||
626
- event.type === "drag" ||
627
- event.type === "wheel"));
628
- }
629
- dispatchMouseToTarget(event, target) {
630
- return dispatchMouseEvent(target.component, retargetMouseEvent(event, target));
631
- }
632
- getComponentClickCount(target, x, y) {
633
- const now = Date.now();
634
- const previous = this.lastComponentClick;
635
- const count = previous &&
636
- now - previous.timestamp <= DOUBLE_CLICK_INTERVAL_MS &&
637
- previous.component === target.component &&
638
- previous.x === x &&
639
- previous.y === y
640
- ? (previous.count % 3) + 1
641
- : 1;
642
- this.lastComponentClick = { timestamp: now, count, component: target.component, x, y };
643
- return count;
644
- }
645
- clearTextSelection() {
646
- this.stopSelectionAutoScroll();
647
- this.selectionPressActive = false;
648
- this.selectionAnchor = undefined;
649
- this.selectionFocus = undefined;
650
- this.selectionGranularity = "character";
651
- this.selectionInitialRange = undefined;
652
- this.pressedUrl = undefined;
653
- this.selectionDragged = false;
654
- }
655
- handleMouseEvent(raw) {
656
- const isMotion = (raw.button & 32) !== 0;
657
- const type = raw.release
658
- ? "release"
659
- : isMotion
660
- ? this.decodeMouseButton(raw.button) === "none"
661
- ? "move"
662
- : "drag"
663
- : "press";
664
- const event = this.createMouseEvent(type, raw.button, raw.x, raw.y);
665
- if (this.mouseCapture || this.mousePressTarget) {
666
- const target = this.mouseCapture ?? this.mousePressTarget;
667
- if (this.mousePressPoint && (raw.x !== this.mousePressPoint.x || raw.y !== this.mousePressPoint.y)) {
668
- this.mousePressMoved = true;
669
- this.lastComponentClick = undefined;
670
- }
671
- let render = false;
672
- const targetResult = this.dispatchMouseToTarget(event, target);
673
- if (targetResult)
674
- render = this.applyMouseDispatchResult(event, targetResult);
675
- if (raw.release) {
676
- if (!this.mousePressMoved && this.mousePressPoint?.x === raw.x && this.mousePressPoint.y === raw.y) {
677
- const clickEvent = this.createMouseEvent("click", raw.button, raw.x, raw.y, {
678
- clickCount: this.getComponentClickCount(target, raw.x, raw.y),
679
- });
680
- const clickResult = this.dispatchMouseToTarget(clickEvent, target);
681
- if (clickResult)
682
- render = this.applyMouseDispatchResult(clickEvent, clickResult) || render;
683
- }
684
- this.clearComponentMouseGesture();
685
- }
686
- if (render)
687
- this.requestRender();
688
- return;
689
- }
690
- if (this.handleSearchMouseEvent(raw))
691
- return;
692
- const overlay = this.dispatchMouseToOverlay(event);
693
- if (!overlay.hit) {
694
- if (this.handleScrollToEndIndicatorMouseEvent(raw))
695
- return;
696
- const scrollbarHandled = this.handleScrollbarMouseEvent(raw);
697
- if (!this.scrollbarDrag)
698
- this.updateScrollbarHover(raw.x, raw.y);
699
- if (scrollbarHandled)
700
- return;
701
- }
702
- else {
703
- this.stopScrollbarHover();
704
- }
705
- const result = overlay.result ?? (overlay.hit ? undefined : this.dispatchMouseToLayout(event));
706
- if (result) {
707
- const render = this.applyMouseDispatchResult(event, result);
708
- if (type === "press") {
709
- this.clearTextSelection();
710
- this.mousePressTarget = result.target;
711
- this.mousePressPoint = { x: raw.x, y: raw.y };
712
- this.mousePressMoved = false;
713
- }
714
- if (render)
715
- this.requestRender();
716
- return;
717
- }
718
- if (this.handleRightClickPaste(raw))
719
- return;
720
- this.handleSelectionMouseEvent(raw);
721
- }
722
504
  parseWheelEvent(data) {
723
505
  const sgr = /^\x1b\[<(\d+);(\d+);(\d+)[Mm]$/.exec(data);
724
506
  if (sgr) {
@@ -732,7 +514,6 @@ export class TuiAltScreen extends TuiBase {
732
514
  direction: direction === 0 ? -1 : 1,
733
515
  x: Number.parseInt(sgr[2], 10) - 1,
734
516
  y: Number.parseInt(sgr[3], 10) - 1,
735
- button,
736
517
  };
737
518
  }
738
519
  if (data.length === 6 && data.startsWith("\x1b[M")) {
@@ -746,17 +527,12 @@ export class TuiAltScreen extends TuiBase {
746
527
  direction: direction === 0 ? -1 : 1,
747
528
  x: data.charCodeAt(4) - 33,
748
529
  y: data.charCodeAt(5) - 33,
749
- button,
750
530
  };
751
531
  }
752
532
  return undefined;
753
533
  }
754
- getWheelScrollLines(button) {
755
- // SGR mouse button codes use bit 3 (value 8) for the Alt modifier.
756
- return (button & 8) !== 0 ? this.wheelScrollLines * ALT_WHEEL_SCROLL_MULTIPLIER : this.wheelScrollLines;
757
- }
758
534
  routeWheel(event) {
759
- let remaining = event.direction * this.getWheelScrollLines(event.button);
535
+ let remaining = event.direction * this.wheelScrollLines;
760
536
  const seen = new Set();
761
537
  for (const scrollView of this.currentLayout ? getScrollViewsAt(this.currentLayout, event.x, event.y) : []) {
762
538
  seen.add(scrollView);
@@ -797,25 +573,16 @@ export class TuiAltScreen extends TuiBase {
797
573
  }
798
574
  return true;
799
575
  }
800
- handleScrollToEndIndicatorMouseEvent(event) {
801
- const rect = this.scrollToEndIndicatorRect;
802
- if (!rect || event.release || (event.button & 32) !== 0 || (event.button & 3) !== 0)
803
- return false;
804
- if (event.y !== rect.row || event.x < rect.column || event.x >= rect.column + rect.width)
805
- return false;
806
- this.scrollToBottom();
807
- return true;
808
- }
809
- getScrollbarTargetAt(x, y, includeHiddenAuto = false) {
576
+ getScrollbarTargetAt(x, y) {
810
577
  if (this.hasOverlay() || !this.currentLayout)
811
578
  return undefined;
812
579
  for (const scrollView of getScrollViewsAt(this.currentLayout, x, y)) {
813
580
  const box = getScrollViewBox(this.currentLayout, scrollView);
814
- const geometry = box ? getScrollbarGeometry(box, includeHiddenAuto) : undefined;
581
+ const geometry = box ? getScrollbarGeometry(box) : undefined;
815
582
  if (geometry &&
816
583
  x === geometry.column &&
817
- y >= geometry.trackTop &&
818
- y < geometry.trackTop + geometry.trackHeight) {
584
+ y >= geometry.thumbTop &&
585
+ y < geometry.thumbTop + geometry.thumbHeight) {
819
586
  return { scrollView, geometry };
820
587
  }
821
588
  }
@@ -829,17 +596,11 @@ export class TuiAltScreen extends TuiBase {
829
596
  this.scrollbarHover?.setScrollbarActive(true);
830
597
  }
831
598
  updateScrollbarHover(x, y) {
832
- this.setScrollbarHover(this.getScrollbarTargetAt(x, y, true)?.scrollView);
599
+ this.setScrollbarHover(this.getScrollbarTargetAt(x, y)?.scrollView);
833
600
  }
834
601
  stopScrollbarHover() {
835
602
  this.setScrollbarHover(undefined);
836
603
  }
837
- scrollScrollbarToPointer(scrollView, geometry, pointerY, grabOffset) {
838
- const maxThumbOffset = geometry.trackHeight - geometry.thumbHeight;
839
- const thumbOffset = Math.max(0, Math.min(maxThumbOffset, pointerY - geometry.trackTop - grabOffset));
840
- const scrollTop = maxThumbOffset === 0 ? 0 : Math.round((thumbOffset / maxThumbOffset) * geometry.maxScrollTop);
841
- scrollView.scrollTo(scrollTop);
842
- }
843
604
  handleScrollbarMouseEvent(event) {
844
605
  if (this.scrollbarDrag) {
845
606
  if (event.release) {
@@ -851,7 +612,10 @@ export class TuiAltScreen extends TuiBase {
851
612
  : undefined;
852
613
  const geometry = box ? getScrollbarGeometry(box) : undefined;
853
614
  if (geometry) {
854
- this.scrollScrollbarToPointer(this.scrollbarDrag.scrollView, geometry, event.y, this.scrollbarDrag.grabOffset);
615
+ const maxThumbOffset = geometry.trackHeight - geometry.thumbHeight;
616
+ const thumbOffset = Math.max(0, Math.min(maxThumbOffset, event.y - geometry.trackTop - this.scrollbarDrag.grabOffset));
617
+ const scrollTop = maxThumbOffset === 0 ? 0 : Math.round((thumbOffset / maxThumbOffset) * geometry.maxScrollTop);
618
+ this.scrollbarDrag.scrollView.scrollTo(scrollTop);
855
619
  }
856
620
  return true;
857
621
  }
@@ -870,13 +634,9 @@ export class TuiAltScreen extends TuiBase {
870
634
  this.pressedUrl = undefined;
871
635
  this.selectionDragged = false;
872
636
  this.setScrollbarHover(target.scrollView);
873
- const onThumb = event.y >= target.geometry.thumbTop && event.y < target.geometry.thumbTop + target.geometry.thumbHeight;
874
- const grabOffset = onThumb ? event.y - target.geometry.thumbTop : Math.floor(target.geometry.thumbHeight / 2);
875
- if (!onThumb)
876
- this.scrollScrollbarToPointer(target.scrollView, target.geometry, event.y, grabOffset);
877
637
  this.scrollbarDrag = {
878
638
  scrollView: target.scrollView,
879
- grabOffset,
639
+ grabOffset: event.y - target.geometry.thumbTop,
880
640
  };
881
641
  return true;
882
642
  }
@@ -1061,11 +821,12 @@ export class TuiAltScreen extends TuiBase {
1061
821
  if (!this.selectionAnchor)
1062
822
  return;
1063
823
  this.updateSelectionFocus(point);
1064
- const isClick = !this.selectionDragged &&
824
+ const clickedUrl = !this.selectionDragged &&
1065
825
  this.selectionAnchor.scrollView === point.scrollView &&
1066
826
  this.selectionAnchor.row === point.row &&
1067
- this.selectionAnchor.col === point.col;
1068
- const clickedUrl = isClick ? this.pressedUrl : undefined;
827
+ this.selectionAnchor.col === point.col
828
+ ? this.pressedUrl
829
+ : undefined;
1069
830
  this.pressedUrl = undefined;
1070
831
  if (clickedUrl && this.openUrl) {
1071
832
  this.selectionAnchor = undefined;
@@ -1079,20 +840,6 @@ export class TuiAltScreen extends TuiBase {
1079
840
  this.requestRender();
1080
841
  return;
1081
842
  }
1082
- if (isClick) {
1083
- const clickEvent = this.createMouseEvent("click", event.button, event.x, event.y, {
1084
- clickCount: this.lastClick?.count ?? 1,
1085
- });
1086
- const overlay = this.dispatchMouseToOverlay(clickEvent);
1087
- const result = overlay.result ?? (overlay.hit ? undefined : this.dispatchMouseToLayout(clickEvent));
1088
- if (result) {
1089
- const render = this.applyMouseDispatchResult(clickEvent, result);
1090
- this.clearTextSelection();
1091
- if (render)
1092
- this.requestRender();
1093
- return;
1094
- }
1095
- }
1096
843
  if (this.copyOnSelect)
1097
844
  void this.copySelectionToClipboard();
1098
845
  this.requestRender();
@@ -1234,24 +981,8 @@ export class TuiAltScreen extends TuiBase {
1234
981
  const maxRow = Math.min(screen.length, box.rect.y + box.rect.height, box.clip.y + box.clip.height);
1235
982
  const minColumn = Math.max(0, box.rect.x, box.clip.x);
1236
983
  const maxColumn = Math.min(this.terminal.columns, box.rect.x + box.rect.width, box.clip.x + box.clip.width, scrollbarColumn ?? Number.POSITIVE_INFINITY);
1237
- const minContentRow = scrollView.scrollTop + minRow - box.rect.y;
1238
- const maxContentRow = scrollView.scrollTop + maxRow - box.rect.y - 1;
1239
- let low = 0;
1240
- let high = search.matches.length;
1241
- while (low < high) {
1242
- const middle = low + Math.floor((high - low) / 2);
1243
- const match = search.matches[middle];
1244
- const lastRow = match.segments[match.segments.length - 1]?.row ?? -1;
1245
- if (lastRow < minContentRow)
1246
- low = middle + 1;
1247
- else
1248
- high = middle;
1249
- }
1250
- for (let matchIndex = low; matchIndex < search.matches.length; matchIndex++) {
1251
- const match = search.matches[matchIndex];
1252
- if ((match.segments[0]?.row ?? 0) > maxContentRow)
1253
- break;
1254
- for (const segment of match.segments) {
984
+ for (let matchIndex = 0; matchIndex < search.matches.length; matchIndex++) {
985
+ for (const segment of search.matches[matchIndex].segments) {
1255
986
  const row = box.rect.y + segment.row - scrollView.scrollTop;
1256
987
  if (row < minRow || row >= maxRow)
1257
988
  continue;
@@ -1354,30 +1085,6 @@ export class TuiAltScreen extends TuiBase {
1354
1085
  isMouseSequence(data) {
1355
1086
  return /^\x1b\[<\d+;\d+;\d+[Mm]$/.test(data) || (data.length === 6 && data.startsWith("\x1b[M"));
1356
1087
  }
1357
- compositeScrollToEndIndicator(screen, layout, width) {
1358
- this.scrollToEndIndicatorRect = undefined;
1359
- const scrollView = layout.primaryScrollView ?? this.implicitScrollView;
1360
- if (!this.scrollToEndIndicator || !scrollView.followEnd || scrollView.isFollowingEnd)
1361
- return screen;
1362
- const box = getScrollViewBox(layout, scrollView);
1363
- const clip = box?.clip;
1364
- if (!clip || clip.width <= 0 || clip.height <= 0)
1365
- return screen;
1366
- const row = clip.y + clip.height - 1;
1367
- if (row >= screen.length || isImageLine(screen[row] ?? ""))
1368
- return screen;
1369
- const scrollbarColumn = box ? getScrollbarGeometry(box)?.column : undefined;
1370
- const availableWidth = Math.max(0, (scrollbarColumn ?? clip.x + clip.width) - clip.x);
1371
- const text = truncateToWidth(this.scrollToEndIndicator(), availableWidth, "");
1372
- const textWidth = visibleWidth(text);
1373
- if (textWidth === 0)
1374
- return screen;
1375
- const column = clip.x + Math.floor((availableWidth - textWidth) / 2);
1376
- const result = [...screen];
1377
- result[row] = compositeTuiLine(result[row] ?? "", text, column, textWidth, width);
1378
- this.scrollToEndIndicatorRect = { row, column, width: textWidth };
1379
- return result;
1380
- }
1381
1088
  compositeFlashes(screen, width, height) {
1382
1089
  const flashLines = this.flashes.render(width).slice(-height);
1383
1090
  if (flashLines.length === 0)
@@ -1406,7 +1113,6 @@ export class TuiAltScreen extends TuiBase {
1406
1113
  }
1407
1114
  let screen = nextLayout.lines.map((line) => line.replace(OSC133_ZONE_PREFIX, ""));
1408
1115
  screen = this.applySearchHighlights(screen, nextLayout);
1409
- screen = this.compositeScrollToEndIndicator(screen, nextLayout, width);
1410
1116
  screen = this.compositeOverlays(screen, width, height);
1411
1117
  if (screen.length > height)
1412
1118
  screen = screen.slice(screen.length - height);