pi-cc-extensions 0.6.0 → 0.7.2
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/README.md +1 -1
- package/extensions/claude-code-style.ts +87 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,10 +9,11 @@ import {
|
|
|
9
9
|
createWriteToolDefinition,
|
|
10
10
|
generateDiffString,
|
|
11
11
|
keyHint,
|
|
12
|
+
getSettingsListTheme,
|
|
12
13
|
renderDiff,
|
|
13
14
|
ToolExecutionComponent,
|
|
14
15
|
} from "@earendil-works/pi-coding-agent";
|
|
15
|
-
import { Text, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
16
|
+
import { Container, SettingsList, Text, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
16
17
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
18
|
import { readFile, stat } from "node:fs/promises";
|
|
18
19
|
import { homedir } from "node:os";
|
|
@@ -26,7 +27,7 @@ import { join, resolve } from "node:path";
|
|
|
26
27
|
* expand only that tool.
|
|
27
28
|
*
|
|
28
29
|
* Dynamic commands:
|
|
29
|
-
* /ccstyle
|
|
30
|
+
* /ccstyle open interactive settings
|
|
30
31
|
* /ccstyle on enable
|
|
31
32
|
* /ccstyle off disable
|
|
32
33
|
* /ccstyle status show current state
|
|
@@ -325,6 +326,7 @@ let scrollButtonWidget: any = null;
|
|
|
325
326
|
let pendingScrollMessages = 0;
|
|
326
327
|
let assistantMessageActive = false;
|
|
327
328
|
let scrollButtonSyncScheduled = false;
|
|
329
|
+
let sessionRenderTimer: ReturnType<typeof setTimeout> | null = null;
|
|
328
330
|
|
|
329
331
|
function parseSgrMousePackets(data: string): SgrMousePacket[] | null {
|
|
330
332
|
const pattern = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
@@ -586,15 +588,14 @@ function hideScrollButton(tui: any): void {
|
|
|
586
588
|
}
|
|
587
589
|
|
|
588
590
|
function scheduleScrollButtonSync(tui: any, data: string): void {
|
|
589
|
-
if (!
|
|
591
|
+
if (!isScrollNavigationInput(data) || scrollButtonSyncScheduled) return;
|
|
590
592
|
scrollButtonSyncScheduled = true;
|
|
591
593
|
const previousLines = tui.previousLines;
|
|
592
594
|
const check = (attempt: number) => {
|
|
593
595
|
scrollButtonSyncScheduled = false;
|
|
594
|
-
if (toolMouseTui !== tui
|
|
595
|
-
// Pi renders on its own frame timer.
|
|
596
|
-
//
|
|
597
|
-
// immediately hide the button it just requested.
|
|
596
|
+
if (toolMouseTui !== tui) return;
|
|
597
|
+
// Pi renders on its own frame timer. Inspect the resulting viewport before
|
|
598
|
+
// showing the button so empty or non-scrollable transcripts never flash it.
|
|
598
599
|
const rendered = tui.previousLines !== previousLines;
|
|
599
600
|
if (!rendered && attempt < 4) {
|
|
600
601
|
scrollButtonSyncScheduled = true;
|
|
@@ -604,35 +605,20 @@ function scheduleScrollButtonSync(tui: any, data: string): void {
|
|
|
604
605
|
}
|
|
605
606
|
return;
|
|
606
607
|
}
|
|
607
|
-
|
|
608
|
+
const nextVisible = !isFixedEditorAtBottom(tui);
|
|
609
|
+
if (!nextVisible) pendingScrollMessages = 0;
|
|
610
|
+
if (nextVisible !== scrollButtonVisible) {
|
|
611
|
+
scrollButtonVisible = nextVisible;
|
|
612
|
+
tui.requestRender?.();
|
|
613
|
+
}
|
|
608
614
|
};
|
|
609
615
|
process.nextTick(() => check(0));
|
|
610
616
|
}
|
|
611
617
|
|
|
612
618
|
function updateScrollButtonFromInput(tui: any, data: string): void {
|
|
613
619
|
if (!isFixedEditorTui(tui)) return;
|
|
614
|
-
|
|
615
|
-
let movedUp = matchesKey(data, "pageUp") || ZENTUI_PAGE_UP_INPUT.test(data);
|
|
616
|
-
const packets = parseSgrMousePackets(data);
|
|
617
|
-
if (packets) {
|
|
618
|
-
movedUp = packets.some((packet) => {
|
|
619
|
-
const baseButton = packet.code & ~(4 | 8 | 16 | 32);
|
|
620
|
-
return packet.final === "M" && baseButton === 64;
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const jumpedBottom =
|
|
625
|
-
matchesKey(data, "enter")
|
|
626
|
-
|| matchesKey(data, "return")
|
|
627
|
-
|| isScrollBottomInput(data);
|
|
628
|
-
if (jumpedBottom) {
|
|
620
|
+
if (matchesKey(data, "enter") || matchesKey(data, "return") || isScrollBottomInput(data)) {
|
|
629
621
|
hideScrollButton(tui);
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
const nextVisible = movedUp ? true : scrollButtonVisible;
|
|
633
|
-
if (nextVisible !== scrollButtonVisible) {
|
|
634
|
-
scrollButtonVisible = nextVisible;
|
|
635
|
-
tui.requestRender?.();
|
|
636
622
|
}
|
|
637
623
|
}
|
|
638
624
|
|
|
@@ -836,6 +822,8 @@ function patchToolMouseInputCapture(tui: any): void {
|
|
|
836
822
|
const data = args[0];
|
|
837
823
|
if (typeof data === "string") {
|
|
838
824
|
updateScrollButtonFromInput(this, data);
|
|
825
|
+
// Capture the current viewport before Pi/Zentui applies the scroll input.
|
|
826
|
+
scheduleScrollButtonSync(this, data);
|
|
839
827
|
if (isFixedEditorTui(this) && isScrollBottomInput(data) && jumpToBottomWithoutSubmit(this)) return;
|
|
840
828
|
const packets = parseSgrMousePackets(data);
|
|
841
829
|
if (packets) {
|
|
@@ -900,6 +888,10 @@ function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
|
900
888
|
}
|
|
901
889
|
|
|
902
890
|
function teardownToolMouseInteraction(): void {
|
|
891
|
+
if (sessionRenderTimer) {
|
|
892
|
+
clearTimeout(sessionRenderTimer);
|
|
893
|
+
sessionRenderTimer = null;
|
|
894
|
+
}
|
|
903
895
|
toolMouseInputUnsubscribe?.();
|
|
904
896
|
toolMouseInputUnsubscribe = null;
|
|
905
897
|
try {
|
|
@@ -942,6 +934,19 @@ function installToolMouseInteraction(ctx: any): void {
|
|
|
942
934
|
toolMouseInputUnsubscribe = ctx.ui.onTerminalInput(handleToolMouseInput);
|
|
943
935
|
}
|
|
944
936
|
|
|
937
|
+
function scheduleSessionRender(): void {
|
|
938
|
+
const tui = toolMouseTui;
|
|
939
|
+
if (!tui || typeof tui.requestRender !== "function") return;
|
|
940
|
+
if (sessionRenderTimer) clearTimeout(sessionRenderTimer);
|
|
941
|
+
// Restored transcripts are populated at different points for startup, reload,
|
|
942
|
+
// and session replacement. Repaint after session_start and the surrounding UI
|
|
943
|
+
// rebuild finish so messages are not left hidden until the next terminal input.
|
|
944
|
+
sessionRenderTimer = setTimeout(() => {
|
|
945
|
+
sessionRenderTimer = null;
|
|
946
|
+
if (toolMouseTui === tui) tui.requestRender(true);
|
|
947
|
+
}, 0);
|
|
948
|
+
}
|
|
949
|
+
|
|
945
950
|
// Bright green for success icon (truecolor ANSI escape)
|
|
946
951
|
const BRIGHT_GREEN = "\x1b[38;2;80;220;100m";
|
|
947
952
|
const ANSI_RESET = "\x1b[0m";
|
|
@@ -1090,12 +1095,49 @@ function renderFileDiffPreview(_toolName: string, _args: any, theme: any, contex
|
|
|
1090
1095
|
return renderDiffPreview(state.ccstyleDiffPreview, theme, isToolExpanded(undefined, context));
|
|
1091
1096
|
}
|
|
1092
1097
|
|
|
1093
|
-
function
|
|
1094
|
-
|
|
1098
|
+
function applyEnabledState(enabled: boolean, ctx: any): void {
|
|
1099
|
+
config.enabled = enabled;
|
|
1100
|
+
saveConfig();
|
|
1101
|
+
ctx.ui.notify(`Claude Code style: ${enabled ? "on" : "off"}`, "info");
|
|
1095
1102
|
}
|
|
1096
1103
|
|
|
1097
|
-
function
|
|
1098
|
-
ctx.ui
|
|
1104
|
+
async function showCcstylePanel(ctx: any): Promise<void> {
|
|
1105
|
+
if (ctx?.mode !== "tui" || !ctx?.hasUI || typeof ctx.ui?.custom !== "function") {
|
|
1106
|
+
ctx.ui?.notify?.("/ccstyle requires TUI mode", "warning");
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
await ctx.ui.custom((tui: any, theme: any, _keybindings: any, done: (value?: void) => void) => {
|
|
1111
|
+
const container = new Container();
|
|
1112
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("Claude Code Style")), 1, 1));
|
|
1113
|
+
const settingsList = new SettingsList(
|
|
1114
|
+
[{
|
|
1115
|
+
id: "enabled",
|
|
1116
|
+
label: "Output style",
|
|
1117
|
+
description: "Use compact Claude Code-like tool rendering",
|
|
1118
|
+
currentValue: config.enabled ? "on" : "off",
|
|
1119
|
+
values: ["on", "off"],
|
|
1120
|
+
}],
|
|
1121
|
+
3,
|
|
1122
|
+
getSettingsListTheme(),
|
|
1123
|
+
(id: string, newValue: string) => {
|
|
1124
|
+
if (id !== "enabled" || (newValue !== "on" && newValue !== "off")) return;
|
|
1125
|
+
applyEnabledState(newValue === "on", ctx);
|
|
1126
|
+
settingsList.updateValue(id, newValue);
|
|
1127
|
+
tui.requestRender();
|
|
1128
|
+
},
|
|
1129
|
+
() => done(),
|
|
1130
|
+
);
|
|
1131
|
+
container.addChild(settingsList);
|
|
1132
|
+
return {
|
|
1133
|
+
render: (width: number) => container.render(width),
|
|
1134
|
+
invalidate: () => container.invalidate(),
|
|
1135
|
+
handleInput(data: string) {
|
|
1136
|
+
settingsList.handleInput?.(data);
|
|
1137
|
+
tui.requestRender();
|
|
1138
|
+
},
|
|
1139
|
+
};
|
|
1140
|
+
});
|
|
1099
1141
|
}
|
|
1100
1142
|
|
|
1101
1143
|
function renderDefault(tool: any, slot: "renderCall" | "renderResult", args: any[], fallback = "") {
|
|
@@ -1502,21 +1544,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
1502
1544
|
},
|
|
1503
1545
|
handler: async (args, ctx) => {
|
|
1504
1546
|
const arg = args.trim().toLowerCase();
|
|
1505
|
-
if (arg
|
|
1506
|
-
|
|
1507
|
-
else if (arg === "status") {
|
|
1508
|
-
updateStatus(ctx);
|
|
1509
|
-
ctx.ui.notify(`Claude Code style: ${config.enabled ? "on" : "off"}`, "info");
|
|
1547
|
+
if (!arg) {
|
|
1548
|
+
await showCcstylePanel(ctx);
|
|
1510
1549
|
return;
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
ctx.ui.notify("Usage: /ccstyle [on|off|status]", "warning");
|
|
1550
|
+
}
|
|
1551
|
+
if (arg === "on" || arg === "off") {
|
|
1552
|
+
applyEnabledState(arg === "on", ctx);
|
|
1515
1553
|
return;
|
|
1516
1554
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1555
|
+
if (arg === "status") {
|
|
1556
|
+
ctx.ui.notify(`Claude Code style: ${config.enabled ? "on" : "off"}`, "info");
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
ctx.ui.notify("Usage: /ccstyle [on|off|status]", "warning");
|
|
1520
1560
|
},
|
|
1521
1561
|
});
|
|
1522
1562
|
|
|
@@ -1525,7 +1565,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
1525
1565
|
handler: async (ctx) => {
|
|
1526
1566
|
config.enabled = !config.enabled;
|
|
1527
1567
|
saveConfig();
|
|
1528
|
-
updateStatus(ctx);
|
|
1529
1568
|
ctx.ui.notify(`Claude Code style: ${config.enabled ? "on" : "off"}`, "info");
|
|
1530
1569
|
},
|
|
1531
1570
|
});
|
|
@@ -1534,8 +1573,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
1534
1573
|
diffPreviewByCall.clear();
|
|
1535
1574
|
pendingScrollMessages = 0;
|
|
1536
1575
|
assistantMessageActive = false;
|
|
1537
|
-
|
|
1576
|
+
ctx.ui.setStatus("ccstyle", undefined);
|
|
1538
1577
|
installToolMouseInteraction(ctx);
|
|
1578
|
+
scheduleSessionRender();
|
|
1539
1579
|
});
|
|
1540
1580
|
|
|
1541
1581
|
pi.on("message_start", async (event) => {
|