pi-cc-extensions 0.5.3 → 0.5.5
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 +293 -7
- 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
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,86 @@ function collectToolRenderHits(
|
|
|
409
412
|
return start + count;
|
|
410
413
|
}
|
|
411
414
|
|
|
415
|
+
function collectToolComponents(component: any, tools: any[], seen = new Set<any>()): void {
|
|
416
|
+
if (!component || typeof component !== "object" || seen.has(component)) return;
|
|
417
|
+
seen.add(component);
|
|
418
|
+
if (isToolExecutionComponent(component)) {
|
|
419
|
+
tools.push(component);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (!Array.isArray(component.children)) return;
|
|
423
|
+
for (const child of component.children) collectToolComponents(child, tools, seen);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function fixedEditorLineMatch(rendered: string, visible: string): boolean {
|
|
427
|
+
return rendered === visible
|
|
428
|
+
|| (visible.length >= 8 && (rendered.includes(visible) || visible.includes(rendered)));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function fixedEditorContextScore(
|
|
432
|
+
renderedLines: string[],
|
|
433
|
+
renderedRow: number,
|
|
434
|
+
visibleLines: string[],
|
|
435
|
+
visibleRow: number,
|
|
436
|
+
): number {
|
|
437
|
+
let score = renderedLines[renderedRow] === visibleLines[visibleRow] ? 100 : 50;
|
|
438
|
+
for (const direction of [-1, 1]) {
|
|
439
|
+
for (let distance = 1; distance <= 4; distance++) {
|
|
440
|
+
const candidate = renderedLines[renderedRow + direction * distance];
|
|
441
|
+
const visible = visibleLines[visibleRow + direction * distance];
|
|
442
|
+
if (candidate === undefined || visible === undefined || candidate !== visible) break;
|
|
443
|
+
score += 5 - distance;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return score;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function findToolAtFixedEditorRow(
|
|
450
|
+
tui: any,
|
|
451
|
+
visibleRow: number,
|
|
452
|
+
previousLines: string[],
|
|
453
|
+
width: number,
|
|
454
|
+
): ToolRenderHit | null {
|
|
455
|
+
if (visibleRow < 0 || visibleRow >= previousLines.length) return null;
|
|
456
|
+
const visibleLines = previousLines.map((line) => stripTerminalSequences(String(line)));
|
|
457
|
+
const clickedLine = visibleLines[visibleRow] ?? "";
|
|
458
|
+
if (!clickedLine) return null;
|
|
459
|
+
|
|
460
|
+
const tools: any[] = [];
|
|
461
|
+
collectToolComponents(tui, tools);
|
|
462
|
+
let best: { hit: ToolRenderHit; score: number } | null = null;
|
|
463
|
+
for (const component of tools) {
|
|
464
|
+
const expanded = Boolean(component.expanded);
|
|
465
|
+
if (!expanded && !/(?:expand|\/ click)/i.test(clickedLine)) continue;
|
|
466
|
+
const renderedLines = renderComponentTree(component, width)
|
|
467
|
+
.map((line) => stripTerminalSequences(String(line)));
|
|
468
|
+
for (let renderedRow = 0; renderedRow < renderedLines.length; renderedRow++) {
|
|
469
|
+
if (!fixedEditorLineMatch(renderedLines[renderedRow] ?? "", clickedLine)) continue;
|
|
470
|
+
const score = fixedEditorContextScore(renderedLines, renderedRow, visibleLines, visibleRow);
|
|
471
|
+
if (!best || score > best.score) {
|
|
472
|
+
best = {
|
|
473
|
+
hit: { component, start: visibleRow, end: visibleRow + renderedLines.length },
|
|
474
|
+
score,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return best?.hit ?? null;
|
|
480
|
+
}
|
|
481
|
+
|
|
412
482
|
function findToolAtScreenRow(tui: any, screenRow: number): ToolRenderHit | null {
|
|
413
483
|
const previousLines = Array.isArray(tui?.previousLines) ? tui.previousLines : [];
|
|
484
|
+
const width = Math.max(1, Number(tui?.terminal?.columns) || 80);
|
|
485
|
+
if (isFixedEditorTui(tui)) {
|
|
486
|
+
// Zentui replaces Pi's root render with the already-sliced visible
|
|
487
|
+
// transcript. previousViewportTop remains cursor bookkeeping and must not
|
|
488
|
+
// be added to a physical mouse row here.
|
|
489
|
+
return findToolAtFixedEditorRow(tui, screenRow - 1, previousLines, width);
|
|
490
|
+
}
|
|
414
491
|
const viewportTop = Number.isFinite(tui?.previousViewportTop) ? tui.previousViewportTop : 0;
|
|
415
492
|
const bufferRow = viewportTop + screenRow - 1;
|
|
416
493
|
if (bufferRow < 0 || bufferRow >= previousLines.length) return null;
|
|
417
494
|
|
|
418
|
-
const width = Math.max(1, Number(tui?.terminal?.columns) || 80);
|
|
419
495
|
const hits: ToolRenderHit[] = [];
|
|
420
496
|
const cache = new WeakMap<object, number>();
|
|
421
497
|
collectToolRenderHits(tui, 0, width, hits, cache, new Set<object>());
|
|
@@ -433,6 +509,199 @@ function findToolAtScreenRow(tui: any, screenRow: number): ToolRenderHit | null
|
|
|
433
509
|
return null;
|
|
434
510
|
}
|
|
435
511
|
|
|
512
|
+
function isFixedEditorTui(tui: any): boolean {
|
|
513
|
+
const terminal = tui?.terminal;
|
|
514
|
+
if (!terminal) return false;
|
|
515
|
+
const ownRows = Object.getOwnPropertyDescriptor(terminal, "rows");
|
|
516
|
+
const prototype = Object.getPrototypeOf(terminal);
|
|
517
|
+
const inheritedRows = prototype
|
|
518
|
+
? Object.getOwnPropertyDescriptor(prototype, "rows")
|
|
519
|
+
: undefined;
|
|
520
|
+
return typeof ownRows?.get === "function" && ownRows.get !== inheritedRows?.get;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function updateScrollButtonFromInput(tui: any, data: string): void {
|
|
524
|
+
if (!isFixedEditorTui(tui)) return;
|
|
525
|
+
|
|
526
|
+
let movedUp = matchesKey(data, "pageUp") || ZENTUI_PAGE_UP_INPUT.test(data);
|
|
527
|
+
const packets = parseSgrMousePackets(data);
|
|
528
|
+
if (packets) {
|
|
529
|
+
movedUp = packets.some((packet) => {
|
|
530
|
+
const baseButton = packet.code & ~(4 | 8 | 16 | 32);
|
|
531
|
+
return packet.final === "M" && baseButton === 64;
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const jumpedBottom = matchesKey(data, "enter") || matchesKey(data, "return");
|
|
536
|
+
const nextVisible = jumpedBottom ? false : movedUp ? true : scrollButtonVisible;
|
|
537
|
+
if (nextVisible !== scrollButtonVisible) {
|
|
538
|
+
scrollButtonVisible = nextVisible;
|
|
539
|
+
tui.requestRender?.();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function renderComponentTree(component: any, width: number): string[] {
|
|
544
|
+
if (!component || typeof component !== "object") return [];
|
|
545
|
+
try {
|
|
546
|
+
const lines = component.render?.(width);
|
|
547
|
+
if (Array.isArray(lines) && lines.length > 0) return lines;
|
|
548
|
+
} catch {
|
|
549
|
+
// Fall through to children for hidden container renderers.
|
|
550
|
+
}
|
|
551
|
+
if (!Array.isArray(component.children)) return [];
|
|
552
|
+
return component.children.flatMap((child: any) => renderComponentTree(child, width));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function renderTreeWithTarget(
|
|
556
|
+
component: any,
|
|
557
|
+
target: any,
|
|
558
|
+
width: number,
|
|
559
|
+
seen = new Set<any>(),
|
|
560
|
+
): { lines: string[]; targetStart: number | null } {
|
|
561
|
+
if (!component || typeof component !== "object" || seen.has(component)) {
|
|
562
|
+
return { lines: [], targetStart: null };
|
|
563
|
+
}
|
|
564
|
+
seen.add(component);
|
|
565
|
+
if (component === target) {
|
|
566
|
+
return { lines: renderComponentTree(component, width), targetStart: 0 };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (Array.isArray(component.children)) {
|
|
570
|
+
const lines: string[] = [];
|
|
571
|
+
let targetStart: number | null = null;
|
|
572
|
+
for (const child of component.children) {
|
|
573
|
+
const result = renderTreeWithTarget(child, target, width, seen);
|
|
574
|
+
if (result.targetStart !== null) targetStart = lines.length + result.targetStart;
|
|
575
|
+
lines.push(...result.lines);
|
|
576
|
+
}
|
|
577
|
+
if (targetStart !== null) return { lines, targetStart };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return { lines: renderComponentTree(component, width), targetStart: null };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function normalizedClusterLines(component: any, width: number): string[] {
|
|
584
|
+
if (!component) return [];
|
|
585
|
+
const lines = renderComponentTree(component, width);
|
|
586
|
+
let end = lines.length;
|
|
587
|
+
while (end > 0 && visibleWidth(lines[end - 1] ?? "") === 0) end--;
|
|
588
|
+
return lines.slice(0, Math.max(end, 1));
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function rawTerminalRows(tui: any): number {
|
|
592
|
+
const terminal = tui?.terminal;
|
|
593
|
+
if (!terminal) return 0;
|
|
594
|
+
const prototype = Object.getPrototypeOf(terminal);
|
|
595
|
+
const rows = prototype ? Object.getOwnPropertyDescriptor(prototype, "rows") : undefined;
|
|
596
|
+
if (typeof rows?.get === "function") {
|
|
597
|
+
try {
|
|
598
|
+
const value = rows.get.call(terminal);
|
|
599
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
600
|
+
} catch {
|
|
601
|
+
// Fall through to the current terminal value.
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return typeof terminal.rows === "number" && Number.isFinite(terminal.rows) ? terminal.rows : 0;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function containsEditorLike(component: any, focused: any, seen = new Set<any>()): boolean {
|
|
608
|
+
if (!component || typeof component !== "object" || seen.has(component)) return false;
|
|
609
|
+
seen.add(component);
|
|
610
|
+
if (component === focused) return true;
|
|
611
|
+
if (
|
|
612
|
+
typeof component.getText === "function"
|
|
613
|
+
&& typeof component.setText === "function"
|
|
614
|
+
&& typeof component.handleInput === "function"
|
|
615
|
+
) return true;
|
|
616
|
+
return Array.isArray(component.children)
|
|
617
|
+
&& component.children.some((child: any) => containsEditorLike(child, focused, seen));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function scrollButtonScreenRow(tui: any, width: number): number | null {
|
|
621
|
+
if (!scrollButtonVisible || !isFixedEditorTui(tui) || !scrollButtonWidget) return null;
|
|
622
|
+
const children = Array.isArray(tui?.children) ? tui.children : [];
|
|
623
|
+
const editorIndex = children.findIndex((child: any) =>
|
|
624
|
+
containsEditorLike(child, tui.focusedComponent),
|
|
625
|
+
);
|
|
626
|
+
if (editorIndex < 2 || editorIndex + 2 >= children.length) return null;
|
|
627
|
+
|
|
628
|
+
const above = children[editorIndex - 1];
|
|
629
|
+
const widthValue = Math.max(1, width || Number(tui?.terminal?.columns) || 80);
|
|
630
|
+
const target = renderTreeWithTarget(above, scrollButtonWidget, widthValue);
|
|
631
|
+
if (target.targetStart === null) return null;
|
|
632
|
+
|
|
633
|
+
const rawRows = rawTerminalRows(tui);
|
|
634
|
+
if (rawRows <= 0) return null;
|
|
635
|
+
const maxRows = Math.max(1, rawRows - 1);
|
|
636
|
+
const status = normalizedClusterLines(children[editorIndex - 2], widthValue);
|
|
637
|
+
const editor = normalizedClusterLines(children[editorIndex], widthValue);
|
|
638
|
+
const below = normalizedClusterLines(children[editorIndex + 1], widthValue);
|
|
639
|
+
const footer = normalizedClusterLines(children[editorIndex + 2], widthValue);
|
|
640
|
+
const aboveLines = target.lines.length > 0 ? target.lines : normalizedClusterLines(above, widthValue);
|
|
641
|
+
|
|
642
|
+
const takeLast = (lines: string[], count: number): string[] =>
|
|
643
|
+
count > 0 ? lines.slice(-count) : [];
|
|
644
|
+
const editorVisible = takeLast(editor, Math.min(editor.length, maxRows));
|
|
645
|
+
let remaining = Math.max(0, maxRows - editorVisible.length);
|
|
646
|
+
const footerVisible = takeLast(footer, remaining);
|
|
647
|
+
remaining -= footerVisible.length;
|
|
648
|
+
const belowVisible = takeLast(below, remaining);
|
|
649
|
+
remaining -= belowVisible.length;
|
|
650
|
+
const aboveVisible = takeLast(aboveLines, remaining);
|
|
651
|
+
const statusVisible = takeLast(status, Math.max(0, remaining - aboveVisible.length));
|
|
652
|
+
const aboveStart = aboveLines.length - aboveVisible.length;
|
|
653
|
+
const targetRow = target.targetStart - aboveStart;
|
|
654
|
+
if (targetRow < 0 || targetRow >= aboveVisible.length) return null;
|
|
655
|
+
|
|
656
|
+
const allLines = [...statusVisible, ...aboveVisible, ...editorVisible, ...belowVisible, ...footerVisible];
|
|
657
|
+
let leadingBlank = 0;
|
|
658
|
+
while (leadingBlank < allLines.length - 1 && visibleWidth(allLines[leadingBlank] ?? "") === 0) {
|
|
659
|
+
leadingBlank++;
|
|
660
|
+
}
|
|
661
|
+
const clusterRow = statusVisible.length + targetRow - leadingBlank;
|
|
662
|
+
if (clusterRow < 0 || clusterRow >= allLines.length - leadingBlank) return null;
|
|
663
|
+
return rawRows - allLines.length + clusterRow + 1;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function isScrollButtonAtScreenRow(tui: any, packet: SgrMousePacket): boolean {
|
|
667
|
+
const width = Math.max(1, Number(tui?.terminal?.columns) || 80);
|
|
668
|
+
if (scrollButtonScreenRow(tui, width) !== packet.row || !scrollButtonWidget) return false;
|
|
669
|
+
const rendered = scrollButtonWidget.render?.(width)?.[0];
|
|
670
|
+
if (typeof rendered !== "string") return false;
|
|
671
|
+
const plain = rendered
|
|
672
|
+
.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "")
|
|
673
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
|
|
674
|
+
const leading = plain.length - plain.trimStart().length;
|
|
675
|
+
const end = visibleWidth(plain.trimEnd());
|
|
676
|
+
return packet.col >= leading + 1 && packet.col <= end;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function jumpToBottomWithoutSubmit(tui: any): boolean {
|
|
680
|
+
const originalHandle = toolMouseInputPatchTui === tui
|
|
681
|
+
? toolMouseInputPatchOriginalHandle
|
|
682
|
+
: null;
|
|
683
|
+
if (!originalHandle) return false;
|
|
684
|
+
|
|
685
|
+
// Route Enter through Pi's normal listener chain so pi-zentui can update its
|
|
686
|
+
// private scroll offset, but suppress the focused editor for this synthetic
|
|
687
|
+
// dispatch so clicking the button never submits the current input.
|
|
688
|
+
const focused = tui.focusedComponent;
|
|
689
|
+
try {
|
|
690
|
+
tui.focusedComponent = null;
|
|
691
|
+
Reflect.apply(originalHandle, tui, ["\r"]);
|
|
692
|
+
} finally {
|
|
693
|
+
tui.focusedComponent = focused;
|
|
694
|
+
}
|
|
695
|
+
scrollButtonVisible = false;
|
|
696
|
+
tui.requestRender?.();
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function handleScrollButtonClick(tui: any, packet: SgrMousePacket): boolean {
|
|
701
|
+
if (!isScrollButtonAtScreenRow(tui, packet)) return false;
|
|
702
|
+
return jumpToBottomWithoutSubmit(tui);
|
|
703
|
+
}
|
|
704
|
+
|
|
436
705
|
function toggleToolAtMouseClick(tui: any, packet: SgrMousePacket): boolean {
|
|
437
706
|
const hit = findToolAtScreenRow(tui, packet.row);
|
|
438
707
|
if (!hit) return false;
|
|
@@ -444,6 +713,13 @@ function toggleToolAtMouseClick(tui: any, packet: SgrMousePacket): boolean {
|
|
|
444
713
|
return true;
|
|
445
714
|
}
|
|
446
715
|
|
|
716
|
+
function renderScrollButton(width: number, theme: any): string[] {
|
|
717
|
+
if (!scrollButtonVisible || !isFixedEditorTui(toolMouseTui)) return [];
|
|
718
|
+
const label = theme.fg("accent", "[ ↓ Back to bottom · click ]");
|
|
719
|
+
const leftPad = Math.max(0, Math.floor((width - visibleWidth(label)) / 2));
|
|
720
|
+
return [`${" ".repeat(leftPad)}${truncateToWidth(label, width, "…")}`];
|
|
721
|
+
}
|
|
722
|
+
|
|
447
723
|
/**
|
|
448
724
|
* pi-zentui consumes left-button presses for text selection. Intercept only a
|
|
449
725
|
* tool-row click at the TUI input boundary, before extension listeners run.
|
|
@@ -460,10 +736,12 @@ function patchToolMouseInputCapture(tui: any): void {
|
|
|
460
736
|
const wrapper = function (this: any, ...args: any[]): any {
|
|
461
737
|
const data = args[0];
|
|
462
738
|
if (typeof data === "string") {
|
|
739
|
+
updateScrollButtonFromInput(this, data);
|
|
463
740
|
const packets = parseSgrMousePackets(data);
|
|
464
741
|
if (packets) {
|
|
465
742
|
for (const packet of packets) {
|
|
466
|
-
if (isSgrLeftPress(packet)
|
|
743
|
+
if (!isSgrLeftPress(packet)) continue;
|
|
744
|
+
if (handleScrollButtonClick(this, packet) || toggleToolAtMouseClick(this, packet)) return;
|
|
467
745
|
}
|
|
468
746
|
}
|
|
469
747
|
}
|
|
@@ -495,12 +773,14 @@ function restoreToolMouseInputCapture(): void {
|
|
|
495
773
|
|
|
496
774
|
function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
497
775
|
if (!toolMouseTui) return undefined;
|
|
776
|
+
updateScrollButtonFromInput(toolMouseTui, data);
|
|
498
777
|
const packets = parseSgrMousePackets(data);
|
|
499
778
|
if (!packets) return undefined;
|
|
500
779
|
|
|
501
780
|
let consumed = false;
|
|
502
781
|
for (const packet of packets) {
|
|
503
|
-
if (isSgrLeftPress(packet)
|
|
782
|
+
if (!isSgrLeftPress(packet)) continue;
|
|
783
|
+
if (handleScrollButtonClick(toolMouseTui, packet) || toggleToolAtMouseClick(toolMouseTui, packet)) {
|
|
504
784
|
consumed = true;
|
|
505
785
|
}
|
|
506
786
|
}
|
|
@@ -524,6 +804,8 @@ function teardownToolMouseInteraction(): void {
|
|
|
524
804
|
// The UI context may already have been reset during /reload.
|
|
525
805
|
}
|
|
526
806
|
restoreToolMouseInputCapture();
|
|
807
|
+
scrollButtonVisible = false;
|
|
808
|
+
scrollButtonWidget = null;
|
|
527
809
|
toolMouseTui = null;
|
|
528
810
|
toolMouseUi = null;
|
|
529
811
|
}
|
|
@@ -534,12 +816,16 @@ function installToolMouseInteraction(ctx: any): void {
|
|
|
534
816
|
if (typeof ctx.ui?.onTerminalInput !== "function" || typeof ctx.ui?.setWidget !== "function") return;
|
|
535
817
|
|
|
536
818
|
toolMouseUi = ctx.ui;
|
|
537
|
-
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any) => {
|
|
819
|
+
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any, theme: any) => {
|
|
538
820
|
toolMouseTui = tui;
|
|
539
821
|
patchToolMouseInputCapture(tui);
|
|
540
822
|
tui?.terminal?.write?.(TOOL_MOUSE_ENABLE);
|
|
541
|
-
|
|
542
|
-
|
|
823
|
+
const widget = {
|
|
824
|
+
render: (width: number) => renderScrollButton(width, theme),
|
|
825
|
+
invalidate() {},
|
|
826
|
+
};
|
|
827
|
+
scrollButtonWidget = widget;
|
|
828
|
+
return widget;
|
|
543
829
|
});
|
|
544
830
|
toolMouseInputUnsubscribe = ctx.ui.onTerminalInput(handleToolMouseInput);
|
|
545
831
|
}
|