pi-cc-extensions 0.5.2 → 0.5.4
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/extensions/claude-code-style.ts +291 -45
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
renderDiff,
|
|
13
13
|
ToolExecutionComponent,
|
|
14
14
|
} from "@earendil-works/pi-coding-agent";
|
|
15
|
-
import { Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
15
|
+
import { Text, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
16
16
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { readFile, stat } from "node:fs/promises";
|
|
18
18
|
import { homedir } from "node:os";
|
|
@@ -317,12 +317,15 @@ type ToolRenderHit = {
|
|
|
317
317
|
const TOOL_MOUSE_WIDGET_KEY = "ccstyle-tool-mouse";
|
|
318
318
|
const TOOL_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1006h";
|
|
319
319
|
const TOOL_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1000l";
|
|
320
|
+
const ZENTUI_PAGE_UP_INPUT = /^\x1b\[5;9(?::[12])?~$|^\x1b\[57421;9(?::[12])?u$|^\x1b\[1;6A$/;
|
|
320
321
|
let toolMouseTui: any = null;
|
|
321
322
|
let toolMouseUi: any = null;
|
|
322
323
|
let toolMouseInputUnsubscribe: (() => void) | null = null;
|
|
323
324
|
let toolMouseInputPatchTui: any = null;
|
|
324
|
-
let
|
|
325
|
+
let toolMouseInputPatchOriginalHandle: ((...args: any[]) => any) | null = null;
|
|
325
326
|
let toolMouseInputPatchWrapper: ((...args: any[]) => any) | null = null;
|
|
327
|
+
let scrollButtonVisible = false;
|
|
328
|
+
let scrollButtonWidget: any = null;
|
|
326
329
|
|
|
327
330
|
function parseSgrMousePackets(data: string): SgrMousePacket[] | null {
|
|
328
331
|
const pattern = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
@@ -409,13 +412,51 @@ function collectToolRenderHits(
|
|
|
409
412
|
return start + count;
|
|
410
413
|
}
|
|
411
414
|
|
|
415
|
+
function findToolAtFixedEditorRow(
|
|
416
|
+
tui: any,
|
|
417
|
+
bufferRow: number,
|
|
418
|
+
previousLines: string[],
|
|
419
|
+
width: number,
|
|
420
|
+
): ToolRenderHit | null {
|
|
421
|
+
if (bufferRow < 0 || bufferRow >= previousLines.length) return null;
|
|
422
|
+
const clickedLine = stripTerminalSequences(String(previousLines[bufferRow] ?? ""));
|
|
423
|
+
if (!clickedLine) return null;
|
|
424
|
+
|
|
425
|
+
const hits: ToolRenderHit[] = [];
|
|
426
|
+
collectToolRenderHits(tui, 0, width, hits, new WeakMap<object, number>(), new Set<object>());
|
|
427
|
+
const clickedOccurrence = previousLines
|
|
428
|
+
.slice(0, bufferRow + 1)
|
|
429
|
+
.filter((line) => stripTerminalSequences(String(line)) === clickedLine)
|
|
430
|
+
.length;
|
|
431
|
+
let matchingOccurrence = 0;
|
|
432
|
+
|
|
433
|
+
for (const hit of hits) {
|
|
434
|
+
const component = hit.component;
|
|
435
|
+
const expanded = Boolean(component.expanded);
|
|
436
|
+
if (!expanded && !/(?:expand|\/ click)/i.test(clickedLine)) continue;
|
|
437
|
+
const renderedLines = renderComponentTree(component, width);
|
|
438
|
+
const matchingLines = renderedLines.filter((line) => {
|
|
439
|
+
const rendered = stripTerminalSequences(String(line));
|
|
440
|
+
return rendered === clickedLine
|
|
441
|
+
|| (clickedLine.length >= 8 && (rendered.includes(clickedLine) || clickedLine.includes(rendered)));
|
|
442
|
+
}).length;
|
|
443
|
+
if (matchingLines === 0) continue;
|
|
444
|
+
matchingOccurrence += matchingLines;
|
|
445
|
+
if (clickedOccurrence <= matchingOccurrence) return hit;
|
|
446
|
+
}
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
|
|
412
450
|
function findToolAtScreenRow(tui: any, screenRow: number): ToolRenderHit | null {
|
|
413
451
|
const previousLines = Array.isArray(tui?.previousLines) ? tui.previousLines : [];
|
|
414
452
|
const viewportTop = Number.isFinite(tui?.previousViewportTop) ? tui.previousViewportTop : 0;
|
|
415
453
|
const bufferRow = viewportTop + screenRow - 1;
|
|
454
|
+
const width = Math.max(1, Number(tui?.terminal?.columns) || 80);
|
|
455
|
+
if (isFixedEditorTui(tui)) {
|
|
456
|
+
return findToolAtFixedEditorRow(tui, bufferRow, previousLines, width);
|
|
457
|
+
}
|
|
416
458
|
if (bufferRow < 0 || bufferRow >= previousLines.length) return null;
|
|
417
459
|
|
|
418
|
-
const width = Math.max(1, Number(tui?.terminal?.columns) || 80);
|
|
419
460
|
const hits: ToolRenderHit[] = [];
|
|
420
461
|
const cache = new WeakMap<object, number>();
|
|
421
462
|
collectToolRenderHits(tui, 0, width, hits, cache, new Set<object>());
|
|
@@ -433,6 +474,199 @@ function findToolAtScreenRow(tui: any, screenRow: number): ToolRenderHit | null
|
|
|
433
474
|
return null;
|
|
434
475
|
}
|
|
435
476
|
|
|
477
|
+
function isFixedEditorTui(tui: any): boolean {
|
|
478
|
+
const terminal = tui?.terminal;
|
|
479
|
+
if (!terminal) return false;
|
|
480
|
+
const ownRows = Object.getOwnPropertyDescriptor(terminal, "rows");
|
|
481
|
+
const prototype = Object.getPrototypeOf(terminal);
|
|
482
|
+
const inheritedRows = prototype
|
|
483
|
+
? Object.getOwnPropertyDescriptor(prototype, "rows")
|
|
484
|
+
: undefined;
|
|
485
|
+
return typeof ownRows?.get === "function" && ownRows.get !== inheritedRows?.get;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function updateScrollButtonFromInput(tui: any, data: string): void {
|
|
489
|
+
if (!isFixedEditorTui(tui)) return;
|
|
490
|
+
|
|
491
|
+
let movedUp = matchesKey(data, "pageUp") || ZENTUI_PAGE_UP_INPUT.test(data);
|
|
492
|
+
const packets = parseSgrMousePackets(data);
|
|
493
|
+
if (packets) {
|
|
494
|
+
movedUp = packets.some((packet) => {
|
|
495
|
+
const baseButton = packet.code & ~(4 | 8 | 16 | 32);
|
|
496
|
+
return packet.final === "M" && baseButton === 64;
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const jumpedBottom = matchesKey(data, "enter") || matchesKey(data, "return");
|
|
501
|
+
const nextVisible = jumpedBottom ? false : movedUp ? true : scrollButtonVisible;
|
|
502
|
+
if (nextVisible !== scrollButtonVisible) {
|
|
503
|
+
scrollButtonVisible = nextVisible;
|
|
504
|
+
tui.requestRender?.();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function renderComponentTree(component: any, width: number): string[] {
|
|
509
|
+
if (!component || typeof component !== "object") return [];
|
|
510
|
+
try {
|
|
511
|
+
const lines = component.render?.(width);
|
|
512
|
+
if (Array.isArray(lines) && lines.length > 0) return lines;
|
|
513
|
+
} catch {
|
|
514
|
+
// Fall through to children for hidden container renderers.
|
|
515
|
+
}
|
|
516
|
+
if (!Array.isArray(component.children)) return [];
|
|
517
|
+
return component.children.flatMap((child: any) => renderComponentTree(child, width));
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function renderTreeWithTarget(
|
|
521
|
+
component: any,
|
|
522
|
+
target: any,
|
|
523
|
+
width: number,
|
|
524
|
+
seen = new Set<any>(),
|
|
525
|
+
): { lines: string[]; targetStart: number | null } {
|
|
526
|
+
if (!component || typeof component !== "object" || seen.has(component)) {
|
|
527
|
+
return { lines: [], targetStart: null };
|
|
528
|
+
}
|
|
529
|
+
seen.add(component);
|
|
530
|
+
if (component === target) {
|
|
531
|
+
return { lines: renderComponentTree(component, width), targetStart: 0 };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (Array.isArray(component.children)) {
|
|
535
|
+
const lines: string[] = [];
|
|
536
|
+
let targetStart: number | null = null;
|
|
537
|
+
for (const child of component.children) {
|
|
538
|
+
const result = renderTreeWithTarget(child, target, width, seen);
|
|
539
|
+
if (result.targetStart !== null) targetStart = lines.length + result.targetStart;
|
|
540
|
+
lines.push(...result.lines);
|
|
541
|
+
}
|
|
542
|
+
if (targetStart !== null) return { lines, targetStart };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return { lines: renderComponentTree(component, width), targetStart: null };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function normalizedClusterLines(component: any, width: number): string[] {
|
|
549
|
+
if (!component) return [];
|
|
550
|
+
const lines = renderComponentTree(component, width);
|
|
551
|
+
let end = lines.length;
|
|
552
|
+
while (end > 0 && visibleWidth(lines[end - 1] ?? "") === 0) end--;
|
|
553
|
+
return lines.slice(0, Math.max(end, 1));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function rawTerminalRows(tui: any): number {
|
|
557
|
+
const terminal = tui?.terminal;
|
|
558
|
+
if (!terminal) return 0;
|
|
559
|
+
const prototype = Object.getPrototypeOf(terminal);
|
|
560
|
+
const rows = prototype ? Object.getOwnPropertyDescriptor(prototype, "rows") : undefined;
|
|
561
|
+
if (typeof rows?.get === "function") {
|
|
562
|
+
try {
|
|
563
|
+
const value = rows.get.call(terminal);
|
|
564
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
565
|
+
} catch {
|
|
566
|
+
// Fall through to the current terminal value.
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return typeof terminal.rows === "number" && Number.isFinite(terminal.rows) ? terminal.rows : 0;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function containsEditorLike(component: any, focused: any, seen = new Set<any>()): boolean {
|
|
573
|
+
if (!component || typeof component !== "object" || seen.has(component)) return false;
|
|
574
|
+
seen.add(component);
|
|
575
|
+
if (component === focused) return true;
|
|
576
|
+
if (
|
|
577
|
+
typeof component.getText === "function"
|
|
578
|
+
&& typeof component.setText === "function"
|
|
579
|
+
&& typeof component.handleInput === "function"
|
|
580
|
+
) return true;
|
|
581
|
+
return Array.isArray(component.children)
|
|
582
|
+
&& component.children.some((child: any) => containsEditorLike(child, focused, seen));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function scrollButtonScreenRow(tui: any, width: number): number | null {
|
|
586
|
+
if (!scrollButtonVisible || !isFixedEditorTui(tui) || !scrollButtonWidget) return null;
|
|
587
|
+
const children = Array.isArray(tui?.children) ? tui.children : [];
|
|
588
|
+
const editorIndex = children.findIndex((child: any) =>
|
|
589
|
+
containsEditorLike(child, tui.focusedComponent),
|
|
590
|
+
);
|
|
591
|
+
if (editorIndex < 2 || editorIndex + 2 >= children.length) return null;
|
|
592
|
+
|
|
593
|
+
const above = children[editorIndex - 1];
|
|
594
|
+
const widthValue = Math.max(1, width || Number(tui?.terminal?.columns) || 80);
|
|
595
|
+
const target = renderTreeWithTarget(above, scrollButtonWidget, widthValue);
|
|
596
|
+
if (target.targetStart === null) return null;
|
|
597
|
+
|
|
598
|
+
const rawRows = rawTerminalRows(tui);
|
|
599
|
+
if (rawRows <= 0) return null;
|
|
600
|
+
const maxRows = Math.max(1, rawRows - 1);
|
|
601
|
+
const status = normalizedClusterLines(children[editorIndex - 2], widthValue);
|
|
602
|
+
const editor = normalizedClusterLines(children[editorIndex], widthValue);
|
|
603
|
+
const below = normalizedClusterLines(children[editorIndex + 1], widthValue);
|
|
604
|
+
const footer = normalizedClusterLines(children[editorIndex + 2], widthValue);
|
|
605
|
+
const aboveLines = target.lines.length > 0 ? target.lines : normalizedClusterLines(above, widthValue);
|
|
606
|
+
|
|
607
|
+
const takeLast = (lines: string[], count: number): string[] =>
|
|
608
|
+
count > 0 ? lines.slice(-count) : [];
|
|
609
|
+
const editorVisible = takeLast(editor, Math.min(editor.length, maxRows));
|
|
610
|
+
let remaining = Math.max(0, maxRows - editorVisible.length);
|
|
611
|
+
const footerVisible = takeLast(footer, remaining);
|
|
612
|
+
remaining -= footerVisible.length;
|
|
613
|
+
const belowVisible = takeLast(below, remaining);
|
|
614
|
+
remaining -= belowVisible.length;
|
|
615
|
+
const aboveVisible = takeLast(aboveLines, remaining);
|
|
616
|
+
const statusVisible = takeLast(status, Math.max(0, remaining - aboveVisible.length));
|
|
617
|
+
const aboveStart = aboveLines.length - aboveVisible.length;
|
|
618
|
+
const targetRow = target.targetStart - aboveStart;
|
|
619
|
+
if (targetRow < 0 || targetRow >= aboveVisible.length) return null;
|
|
620
|
+
|
|
621
|
+
const allLines = [...statusVisible, ...aboveVisible, ...editorVisible, ...belowVisible, ...footerVisible];
|
|
622
|
+
let leadingBlank = 0;
|
|
623
|
+
while (leadingBlank < allLines.length - 1 && visibleWidth(allLines[leadingBlank] ?? "") === 0) {
|
|
624
|
+
leadingBlank++;
|
|
625
|
+
}
|
|
626
|
+
const clusterRow = statusVisible.length + targetRow - leadingBlank;
|
|
627
|
+
if (clusterRow < 0 || clusterRow >= allLines.length - leadingBlank) return null;
|
|
628
|
+
return rawRows - allLines.length + clusterRow + 1;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function isScrollButtonAtScreenRow(tui: any, packet: SgrMousePacket): boolean {
|
|
632
|
+
const width = Math.max(1, Number(tui?.terminal?.columns) || 80);
|
|
633
|
+
if (scrollButtonScreenRow(tui, width) !== packet.row || !scrollButtonWidget) return false;
|
|
634
|
+
const rendered = scrollButtonWidget.render?.(width)?.[0];
|
|
635
|
+
if (typeof rendered !== "string") return false;
|
|
636
|
+
const plain = rendered
|
|
637
|
+
.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "")
|
|
638
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
|
|
639
|
+
const leading = plain.length - plain.trimStart().length;
|
|
640
|
+
const end = visibleWidth(plain.trimEnd());
|
|
641
|
+
return packet.col >= leading + 1 && packet.col <= end;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function jumpToBottomWithoutSubmit(tui: any): boolean {
|
|
645
|
+
const originalHandle = toolMouseInputPatchTui === tui
|
|
646
|
+
? toolMouseInputPatchOriginalHandle
|
|
647
|
+
: null;
|
|
648
|
+
if (!originalHandle) return false;
|
|
649
|
+
|
|
650
|
+
// Route Enter through Pi's normal listener chain so pi-zentui can update its
|
|
651
|
+
// private scroll offset, but suppress the focused editor for this synthetic
|
|
652
|
+
// dispatch so clicking the button never submits the current input.
|
|
653
|
+
const focused = tui.focusedComponent;
|
|
654
|
+
try {
|
|
655
|
+
tui.focusedComponent = null;
|
|
656
|
+
Reflect.apply(originalHandle, tui, ["\r"]);
|
|
657
|
+
} finally {
|
|
658
|
+
tui.focusedComponent = focused;
|
|
659
|
+
}
|
|
660
|
+
scrollButtonVisible = false;
|
|
661
|
+
tui.requestRender?.();
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function handleScrollButtonClick(tui: any, packet: SgrMousePacket): boolean {
|
|
666
|
+
if (!isScrollButtonAtScreenRow(tui, packet)) return false;
|
|
667
|
+
return jumpToBottomWithoutSubmit(tui);
|
|
668
|
+
}
|
|
669
|
+
|
|
436
670
|
function toggleToolAtMouseClick(tui: any, packet: SgrMousePacket): boolean {
|
|
437
671
|
const hit = findToolAtScreenRow(tui, packet.row);
|
|
438
672
|
if (!hit) return false;
|
|
@@ -444,68 +678,74 @@ function toggleToolAtMouseClick(tui: any, packet: SgrMousePacket): boolean {
|
|
|
444
678
|
return true;
|
|
445
679
|
}
|
|
446
680
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
*/
|
|
453
|
-
function prioritizeToolMouseInput(tui: any): void {
|
|
454
|
-
const listeners = tui?.inputListeners;
|
|
455
|
-
if (!(listeners instanceof Set) || !listeners.has(handleToolMouseInput)) return;
|
|
456
|
-
|
|
457
|
-
const ordered = Array.from(listeners);
|
|
458
|
-
if (ordered[0] === handleToolMouseInput) return;
|
|
459
|
-
listeners.clear();
|
|
460
|
-
listeners.add(handleToolMouseInput);
|
|
461
|
-
for (const listener of ordered) {
|
|
462
|
-
if (listener !== handleToolMouseInput) listeners.add(listener);
|
|
463
|
-
}
|
|
681
|
+
function renderScrollButton(width: number, theme: any): string[] {
|
|
682
|
+
if (!scrollButtonVisible || !isFixedEditorTui(toolMouseTui)) return [];
|
|
683
|
+
const label = theme.fg("accent", "[ ↓ Back to bottom · click ]");
|
|
684
|
+
const leftPad = Math.max(0, Math.floor((width - visibleWidth(label)) / 2));
|
|
685
|
+
return [`${" ".repeat(leftPad)}${truncateToWidth(label, width, "…")}`];
|
|
464
686
|
}
|
|
465
687
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
688
|
+
/**
|
|
689
|
+
* pi-zentui consumes left-button presses for text selection. Intercept only a
|
|
690
|
+
* tool-row click at the TUI input boundary, before extension listeners run.
|
|
691
|
+
* Keyboard, wheel, drag, release, and non-tool clicks continue through Pi's
|
|
692
|
+
* original dispatcher, preserving pi-zentui's scroll-to-bottom behavior.
|
|
693
|
+
*/
|
|
694
|
+
function patchToolMouseInputCapture(tui: any): void {
|
|
695
|
+
if (toolMouseInputPatchTui === tui) return;
|
|
471
696
|
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
if (typeof
|
|
697
|
+
restoreToolMouseInputCapture();
|
|
698
|
+
const originalHandle = tui?.handleInput;
|
|
699
|
+
if (typeof originalHandle !== "function") return;
|
|
475
700
|
|
|
476
701
|
const wrapper = function (this: any, ...args: any[]): any {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
702
|
+
const data = args[0];
|
|
703
|
+
if (typeof data === "string") {
|
|
704
|
+
updateScrollButtonFromInput(this, data);
|
|
705
|
+
const packets = parseSgrMousePackets(data);
|
|
706
|
+
if (packets) {
|
|
707
|
+
for (const packet of packets) {
|
|
708
|
+
if (!isSgrLeftPress(packet)) continue;
|
|
709
|
+
if (handleScrollButtonClick(this, packet) || toggleToolAtMouseClick(this, packet)) return;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return Reflect.apply(originalHandle, this, args);
|
|
480
714
|
};
|
|
481
|
-
|
|
715
|
+
|
|
716
|
+
try {
|
|
717
|
+
tui.handleInput = wrapper;
|
|
718
|
+
} catch {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
482
721
|
toolMouseInputPatchTui = tui;
|
|
483
|
-
|
|
722
|
+
toolMouseInputPatchOriginalHandle = originalHandle;
|
|
484
723
|
toolMouseInputPatchWrapper = wrapper;
|
|
485
|
-
prioritizeToolMouseInput(tui);
|
|
486
724
|
}
|
|
487
725
|
|
|
488
|
-
function
|
|
726
|
+
function restoreToolMouseInputCapture(): void {
|
|
489
727
|
if (
|
|
490
728
|
toolMouseInputPatchTui
|
|
491
|
-
&&
|
|
492
|
-
&& toolMouseInputPatchTui.
|
|
729
|
+
&& toolMouseInputPatchOriginalHandle
|
|
730
|
+
&& toolMouseInputPatchTui.handleInput === toolMouseInputPatchWrapper
|
|
493
731
|
) {
|
|
494
|
-
toolMouseInputPatchTui.
|
|
732
|
+
toolMouseInputPatchTui.handleInput = toolMouseInputPatchOriginalHandle;
|
|
495
733
|
}
|
|
496
734
|
toolMouseInputPatchTui = null;
|
|
497
|
-
|
|
735
|
+
toolMouseInputPatchOriginalHandle = null;
|
|
498
736
|
toolMouseInputPatchWrapper = null;
|
|
499
737
|
}
|
|
500
738
|
|
|
501
739
|
function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
502
740
|
if (!toolMouseTui) return undefined;
|
|
741
|
+
updateScrollButtonFromInput(toolMouseTui, data);
|
|
503
742
|
const packets = parseSgrMousePackets(data);
|
|
504
743
|
if (!packets) return undefined;
|
|
505
744
|
|
|
506
745
|
let consumed = false;
|
|
507
746
|
for (const packet of packets) {
|
|
508
|
-
if (isSgrLeftPress(packet)
|
|
747
|
+
if (!isSgrLeftPress(packet)) continue;
|
|
748
|
+
if (handleScrollButtonClick(toolMouseTui, packet) || toggleToolAtMouseClick(toolMouseTui, packet)) {
|
|
509
749
|
consumed = true;
|
|
510
750
|
}
|
|
511
751
|
}
|
|
@@ -528,7 +768,9 @@ function teardownToolMouseInteraction(): void {
|
|
|
528
768
|
} catch {
|
|
529
769
|
// The UI context may already have been reset during /reload.
|
|
530
770
|
}
|
|
531
|
-
|
|
771
|
+
restoreToolMouseInputCapture();
|
|
772
|
+
scrollButtonVisible = false;
|
|
773
|
+
scrollButtonWidget = null;
|
|
532
774
|
toolMouseTui = null;
|
|
533
775
|
toolMouseUi = null;
|
|
534
776
|
}
|
|
@@ -539,12 +781,16 @@ function installToolMouseInteraction(ctx: any): void {
|
|
|
539
781
|
if (typeof ctx.ui?.onTerminalInput !== "function" || typeof ctx.ui?.setWidget !== "function") return;
|
|
540
782
|
|
|
541
783
|
toolMouseUi = ctx.ui;
|
|
542
|
-
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any) => {
|
|
784
|
+
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any, theme: any) => {
|
|
543
785
|
toolMouseTui = tui;
|
|
544
|
-
|
|
786
|
+
patchToolMouseInputCapture(tui);
|
|
545
787
|
tui?.terminal?.write?.(TOOL_MOUSE_ENABLE);
|
|
546
|
-
|
|
547
|
-
|
|
788
|
+
const widget = {
|
|
789
|
+
render: (width: number) => renderScrollButton(width, theme),
|
|
790
|
+
invalidate() {},
|
|
791
|
+
};
|
|
792
|
+
scrollButtonWidget = widget;
|
|
793
|
+
return widget;
|
|
548
794
|
});
|
|
549
795
|
toolMouseInputUnsubscribe = ctx.ui.onTerminalInput(handleToolMouseInput);
|
|
550
796
|
}
|