pi-cc-extensions 0.8.18 → 0.8.20
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.
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { bottomDialogBounds, isDialogCloseClick } from "../../closable-dialog.ts";
|
|
3
2
|
import { getKeybindings, type Input } from "@earendil-works/pi-tui";
|
|
4
3
|
import type { QuestionData, QuestionnaireResult, QuestionParams } from "../tool/types.js";
|
|
5
4
|
import type { WrappingSelectItem } from "../view/components/wrapping-select.js";
|
|
@@ -94,26 +93,10 @@ export class QuestionnaireSession {
|
|
|
94
93
|
// so the transcript regains space while Ctrl+] can still expand the questionnaire.
|
|
95
94
|
const collapsedRender = (_width: number): string[] => [theme.fg("dim", ` ${COLLAPSED_HINT} `)];
|
|
96
95
|
|
|
97
|
-
let lastWidth = 0;
|
|
98
|
-
let lastHeight = 0;
|
|
99
96
|
this.component = {
|
|
100
|
-
render: (width) =>
|
|
101
|
-
const lines = this.state.collapsed ? collapsedRender(width) : built.render(width);
|
|
102
|
-
lastWidth = width;
|
|
103
|
-
lastHeight = lines.length;
|
|
104
|
-
return lines;
|
|
105
|
-
},
|
|
97
|
+
render: (width) => (this.state.collapsed ? collapsedRender(width) : built.render(width)),
|
|
106
98
|
invalidate: built.invalidate,
|
|
107
|
-
handleInput: (data) =>
|
|
108
|
-
if (
|
|
109
|
-
!this.state.collapsed &&
|
|
110
|
-
isDialogCloseClick(data, bottomDialogBounds(this.tui.terminal, lastWidth, lastHeight))
|
|
111
|
-
) {
|
|
112
|
-
this.dispatch("\x1b");
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
this.dispatch(data);
|
|
116
|
-
},
|
|
99
|
+
handleInput: (data) => this.dispatch(data),
|
|
117
100
|
};
|
|
118
101
|
|
|
119
102
|
this.viewAdapter.apply(this.state);
|
|
@@ -793,8 +793,9 @@ type ToolRenderHit = {
|
|
|
793
793
|
};
|
|
794
794
|
|
|
795
795
|
const TOOL_MOUSE_WIDGET_KEY = "ccstyle-tool-mouse";
|
|
796
|
-
const TOOL_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1006h";
|
|
797
|
-
const
|
|
796
|
+
const TOOL_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1003h\x1b[?1006h";
|
|
797
|
+
const TOOL_MOUSE_HOVER_ENABLE = "\x1b[?1003h\x1b[?1006h";
|
|
798
|
+
const TOOL_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1003l\x1b[?1000l";
|
|
798
799
|
const ZENTUI_PAGE_UP_INPUT = /^\x1b\[5;9(?::[12])?~$|^\x1b\[57421;9(?::[12])?u$|^\x1b\[1;6A$/;
|
|
799
800
|
const ZENTUI_PAGE_DOWN_INPUT = /^\x1b\[6;9(?::[12])?~$|^\x1b\[57422;9(?::[12])?u$|^\x1b\[1;6B$/;
|
|
800
801
|
const SCROLL_BOTTOM_SHORTCUT = "ctrl+end";
|
|
@@ -816,6 +817,8 @@ let pendingScrollMessages = 0;
|
|
|
816
817
|
let assistantMessageActive = false;
|
|
817
818
|
let scrollButtonSyncScheduled = false;
|
|
818
819
|
let sessionRenderTimer: ReturnType<typeof setTimeout> | null = null;
|
|
820
|
+
let hoveredToolSummaryRow: number | null = null;
|
|
821
|
+
let hoveredToolCallId: string | null = null;
|
|
819
822
|
|
|
820
823
|
function parseSgrMousePackets(data: string): SgrMousePacket[] | null {
|
|
821
824
|
const pattern = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
@@ -938,8 +941,8 @@ function fixedEditorContextScore(
|
|
|
938
941
|
return score;
|
|
939
942
|
}
|
|
940
943
|
|
|
941
|
-
/**
|
|
942
|
-
const
|
|
944
|
+
/** Summary markers used by Pi and ccstyle; unlike the trailing hint, these survive truncation. */
|
|
945
|
+
const COLLAPSED_TOOL_SUMMARY = /^\s*(?:↳|└|⎿)/;
|
|
943
946
|
/** fixedEditorContextScore max ≈ 100 + 2*(5+4+3+2); stop once we hit a near-perfect match. */
|
|
944
947
|
const TOOL_CLICK_EARLY_EXIT_SCORE = 120;
|
|
945
948
|
|
|
@@ -956,12 +959,11 @@ function findToolAtFixedEditorRow(
|
|
|
956
959
|
|
|
957
960
|
const tools: any[] = [];
|
|
958
961
|
collectToolComponents(tui, tools);
|
|
959
|
-
|
|
960
|
-
// Skips re-rendering the opposite half of the transcript tools.
|
|
961
|
-
const wantExpanded = !COLLAPSED_TOOL_CLICK_HINT.test(clickedLine);
|
|
962
|
+
const clickedCollapsedSummary = COLLAPSED_TOOL_SUMMARY.test(clickedLine);
|
|
962
963
|
let best: { hit: ToolRenderHit; score: number } | null = null;
|
|
963
964
|
for (const component of tools) {
|
|
964
|
-
|
|
965
|
+
// Collapsed tools open only from their summary row; expanded tools collapse from any body row.
|
|
966
|
+
if (!component.expanded && !clickedCollapsedSummary) continue;
|
|
965
967
|
const renderedLines = renderComponentTree(component, width).map((line) =>
|
|
966
968
|
stripTerminalSequences(String(line)),
|
|
967
969
|
);
|
|
@@ -998,12 +1000,11 @@ function findToolAtScreenRow(tui: any, screenRow: number): ToolRenderHit | null
|
|
|
998
1000
|
collectToolRenderHits(tui, 0, width, hits, cache, new Set<object>());
|
|
999
1001
|
|
|
1000
1002
|
const clickedLine = stripTerminalSequences(String(previousLines[bufferRow] ?? ""));
|
|
1001
|
-
const wantExpanded = !COLLAPSED_TOOL_CLICK_HINT.test(clickedLine);
|
|
1002
1003
|
for (const hit of hits) {
|
|
1003
1004
|
if (bufferRow < hit.start || bufferRow >= hit.end) continue;
|
|
1004
|
-
//
|
|
1005
|
-
//
|
|
1006
|
-
if (
|
|
1005
|
+
// The stable summary marker keeps the whole row clickable even when the
|
|
1006
|
+
// trailing keyboard/click hint was truncated (common for subagent output).
|
|
1007
|
+
if (!hit.component.expanded && !COLLAPSED_TOOL_SUMMARY.test(clickedLine)) continue;
|
|
1007
1008
|
return hit;
|
|
1008
1009
|
}
|
|
1009
1010
|
return null;
|
|
@@ -1445,6 +1446,24 @@ function tryOpenToolIoShowMore(tui: any, packet: SgrMousePacket, hit: ToolRender
|
|
|
1445
1446
|
return true;
|
|
1446
1447
|
}
|
|
1447
1448
|
|
|
1449
|
+
function updateToolSummaryHover(tui: any, packet: SgrMousePacket): void {
|
|
1450
|
+
if ((packet.code & 32) === 0 || packet.final !== "M") return;
|
|
1451
|
+
const previousLines = Array.isArray(tui?.previousLines) ? tui.previousLines : [];
|
|
1452
|
+
const lineIndex = useFixedEditorFeatures(tui)
|
|
1453
|
+
? packet.row - 1
|
|
1454
|
+
: (Number.isFinite(tui?.previousViewportTop) ? tui.previousViewportTop : 0) + packet.row - 1;
|
|
1455
|
+
const line = stripTerminalSequences(String(previousLines[lineIndex] ?? ""));
|
|
1456
|
+
const hit = COLLAPSED_TOOL_SUMMARY.test(line) ? findToolAtScreenRow(tui, packet.row) : null;
|
|
1457
|
+
const nextRow = hit && !hit.component.expanded ? packet.row : null;
|
|
1458
|
+
const nextToolCallId = nextRow ? (hit?.component?.toolCallId ?? null) : null;
|
|
1459
|
+
if (nextRow === hoveredToolSummaryRow && nextToolCallId === hoveredToolCallId) return;
|
|
1460
|
+
hoveredToolSummaryRow = nextRow;
|
|
1461
|
+
hoveredToolCallId = nextToolCallId;
|
|
1462
|
+
// Collapsed result components read hoveredToolCallId during render. Keeping
|
|
1463
|
+
// hover in the render path avoids an async TUI repaint overwriting raw ANSI.
|
|
1464
|
+
tui.requestRender?.();
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1448
1467
|
function toggleToolAtMouseClick(tui: any, packet: SgrMousePacket): boolean {
|
|
1449
1468
|
const hit = findToolAtScreenRow(tui, packet.row);
|
|
1450
1469
|
if (!hit) return false;
|
|
@@ -1504,6 +1523,9 @@ function patchToolMouseInputCapture(tui: any): void {
|
|
|
1504
1523
|
const packets = parseSgrMousePackets(data);
|
|
1505
1524
|
if (packets) {
|
|
1506
1525
|
for (const packet of packets) {
|
|
1526
|
+
// Fixed-editor owners may consume motion before extension listeners run,
|
|
1527
|
+
// so hover must be handled at the root input boundary too.
|
|
1528
|
+
updateToolSummaryHover(this, packet);
|
|
1507
1529
|
if (!isSgrLeftPress(packet)) continue;
|
|
1508
1530
|
if (handleScrollButtonClick(this, packet) || toggleToolAtMouseClick(this, packet)) return;
|
|
1509
1531
|
}
|
|
@@ -1567,6 +1589,7 @@ function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
|
1567
1589
|
|
|
1568
1590
|
let consumed = false;
|
|
1569
1591
|
for (const packet of packets) {
|
|
1592
|
+
updateToolSummaryHover(toolMouseTui, packet);
|
|
1570
1593
|
if (!isSgrLeftPress(packet)) continue;
|
|
1571
1594
|
if (
|
|
1572
1595
|
handleScrollButtonClick(toolMouseTui, packet) ||
|
|
@@ -1589,6 +1612,8 @@ function teardownToolMouseInteraction(): void {
|
|
|
1589
1612
|
}
|
|
1590
1613
|
toolMouseInputUnsubscribe?.();
|
|
1591
1614
|
toolMouseInputUnsubscribe = null;
|
|
1615
|
+
hoveredToolSummaryRow = null;
|
|
1616
|
+
hoveredToolCallId = null;
|
|
1592
1617
|
try {
|
|
1593
1618
|
toolMouseTui?.terminal?.write?.(TOOL_MOUSE_DISABLE);
|
|
1594
1619
|
} catch {
|
|
@@ -1626,10 +1651,9 @@ export function installToolMouseInteraction(
|
|
|
1626
1651
|
toolMouseFixedFeaturesEnabled = fixedEditorFeatures;
|
|
1627
1652
|
ctx.ui.setWidget(TOOL_MOUSE_WIDGET_KEY, (tui: any, theme: any) => {
|
|
1628
1653
|
toolMouseTui = tui;
|
|
1629
|
-
if (fixedEditorFeatures)
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
}
|
|
1654
|
+
if (fixedEditorFeatures) patchToolMouseInputCapture(tui);
|
|
1655
|
+
// Motion reporting is required for hover in both native and fixed-editor layouts.
|
|
1656
|
+
tui?.terminal?.write?.(fixedEditorFeatures ? TOOL_MOUSE_ENABLE : TOOL_MOUSE_HOVER_ENABLE);
|
|
1633
1657
|
const widget = {
|
|
1634
1658
|
render: (width: number) => renderScrollButton(width, theme),
|
|
1635
1659
|
invalidate() {},
|
|
@@ -2164,7 +2188,9 @@ function createCcstyleTool(
|
|
|
2164
2188
|
: truncateToWidth(argument.value, argumentWidth, "…")
|
|
2165
2189
|
: undefined;
|
|
2166
2190
|
const call = shown === undefined ? label : `${label}(${shown})`;
|
|
2167
|
-
|
|
2191
|
+
// Leave one cell unused so the host shell never performs a second,
|
|
2192
|
+
// unthemed truncation after this renderer has applied toolTitle.
|
|
2193
|
+
const callWidth = Math.max(0, width - visibleWidth(icon) - 2);
|
|
2168
2194
|
return [`${icon} ${theme.fg("toolTitle", truncateToWidth(call, callWidth, "…"))}`];
|
|
2169
2195
|
},
|
|
2170
2196
|
invalidate() {},
|
|
@@ -2218,9 +2244,21 @@ function createCcstyleTool(
|
|
|
2218
2244
|
);
|
|
2219
2245
|
}
|
|
2220
2246
|
if (context?.state) context.state.ccstyleIoView = undefined;
|
|
2221
|
-
|
|
2222
|
-
|
|
2247
|
+
const toolCallId =
|
|
2248
|
+
context?.toolCallId ?? context?.id ?? context?.state?.toolCallId ?? context?.state?.id;
|
|
2249
|
+
const summary = theme.fg(
|
|
2250
|
+
isError ? "error" : "muted",
|
|
2251
|
+
renderCollapsedToolResult(rendered, hint),
|
|
2223
2252
|
);
|
|
2253
|
+
return {
|
|
2254
|
+
render(width: number) {
|
|
2255
|
+
const line = truncateToWidth(summary, width, "…");
|
|
2256
|
+
if (!toolCallId || hoveredToolCallId !== toolCallId) return [line];
|
|
2257
|
+
const padding = " ".repeat(Math.max(0, width - visibleWidth(line)));
|
|
2258
|
+
return [theme.bg("selectedBg", `${line}${padding}`)];
|
|
2259
|
+
},
|
|
2260
|
+
invalidate() {},
|
|
2261
|
+
};
|
|
2224
2262
|
},
|
|
2225
2263
|
};
|
|
2226
2264
|
}
|
|
@@ -14,14 +14,17 @@ export function renderDialogHeader(
|
|
|
14
14
|
color: (text: string) => string,
|
|
15
15
|
titleColor: (text: string) => string,
|
|
16
16
|
closeColor: (text: string) => string,
|
|
17
|
+
showClose = true,
|
|
17
18
|
): string {
|
|
18
19
|
const safeWidth = Math.max(8, Math.floor(width));
|
|
19
20
|
const innerWidth = safeWidth - 2;
|
|
20
|
-
const button = "[ × ]";
|
|
21
|
-
const titleWidth = Math.max(0, innerWidth - visibleWidth(button) - 2);
|
|
21
|
+
const button = showClose ? "[ × ]" : "";
|
|
22
|
+
const titleWidth = Math.max(0, innerWidth - visibleWidth(button) - (showClose ? 2 : 0));
|
|
22
23
|
const shownTitle = truncateToWidth(title, titleWidth, "…");
|
|
23
|
-
const gap = " ".repeat(
|
|
24
|
-
|
|
24
|
+
const gap = " ".repeat(
|
|
25
|
+
Math.max(showClose ? 1 : 0, innerWidth - visibleWidth(shownTitle) - visibleWidth(button)),
|
|
26
|
+
);
|
|
27
|
+
return `${color("│")}${titleColor(shownTitle)}${gap}${showClose ? closeColor(button) : ""}${color("│")}`;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
/** Match a left-button press anywhere on the [ × ] target. */
|
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-cc-extensions",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "A Pi productivity suite with Claude Code-style UI, structured questionnaires, context inspection, and agent/session references",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"pi-package",
|
|
8
|
-
"pi-extension",
|
|
9
|
-
"pi-coding-agent",
|
|
10
|
-
"claude-code",
|
|
11
|
-
"questionnaire"
|
|
12
|
-
],
|
|
13
|
-
"homepage": "https://github.com/minuque/pi-cc-extensions#readme",
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/minuque/pi-cc-extensions.git"
|
|
17
|
-
},
|
|
18
|
-
"bugs": {
|
|
19
|
-
"url": "https://github.com/minuque/pi-cc-extensions/issues"
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"extensions",
|
|
23
|
-
"assets/readme",
|
|
24
|
-
"themes",
|
|
25
|
-
"README.md",
|
|
26
|
-
"README.en.md"
|
|
27
|
-
],
|
|
28
|
-
"pi": {
|
|
29
|
-
"extensions": [
|
|
30
|
-
"./extensions/pi-aliases.ts",
|
|
31
|
-
"./extensions/pi-startup-header.ts",
|
|
32
|
-
"./extensions/claude-code-style.ts",
|
|
33
|
-
"./extensions/context.ts",
|
|
34
|
-
"./extensions/session-reference.ts",
|
|
35
|
-
"./extensions/agent-autocomplete.ts",
|
|
36
|
-
"./extensions/ask-user-question/index.ts"
|
|
37
|
-
],
|
|
38
|
-
"themes": [
|
|
39
|
-
"./themes/github-dark-default.json"
|
|
40
|
-
],
|
|
41
|
-
"image": "https://raw.githubusercontent.com/minuque/pi-cc-extensions/main/assets/readme/welcome.webp"
|
|
42
|
-
},
|
|
43
|
-
"scripts": {
|
|
44
|
-
"test": "node --test tests/**/*.test.ts",
|
|
45
|
-
"typecheck": "node scripts/typecheck.mjs",
|
|
46
|
-
"format": "biome format --write .",
|
|
47
|
-
"format:check": "biome format .",
|
|
48
|
-
"preversion": "npm run format:check && npm run typecheck && npm test",
|
|
49
|
-
"release": "npm version patch && npm publish",
|
|
50
|
-
"release:minor": "npm version minor && npm publish",
|
|
51
|
-
"release:major": "npm version major && npm publish"
|
|
52
|
-
},
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"@juicesharp/rpiv-config": "^2.1.0",
|
|
55
|
-
"typebox": "^1.1.24"
|
|
56
|
-
},
|
|
57
|
-
"peerDependencies": {
|
|
58
|
-
"@earendil-works/pi-ai": "*",
|
|
59
|
-
"@earendil-works/pi-coding-agent": "*",
|
|
60
|
-
"@earendil-works/pi-tui": "*"
|
|
61
|
-
},
|
|
62
|
-
"engines": {
|
|
63
|
-
"node": ">=22.19.0"
|
|
64
|
-
},
|
|
65
|
-
"devDependencies": {
|
|
66
|
-
"@biomejs/biome": "2.5.5",
|
|
67
|
-
"@types/node": "^26.1.1"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-cc-extensions",
|
|
3
|
+
"version": "0.8.20",
|
|
4
|
+
"description": "A Pi productivity suite with Claude Code-style UI, structured questionnaires, context inspection, and agent/session references",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"questionnaire"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/minuque/pi-cc-extensions#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/minuque/pi-cc-extensions.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/minuque/pi-cc-extensions/issues"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"extensions",
|
|
23
|
+
"assets/readme",
|
|
24
|
+
"themes",
|
|
25
|
+
"README.md",
|
|
26
|
+
"README.en.md"
|
|
27
|
+
],
|
|
28
|
+
"pi": {
|
|
29
|
+
"extensions": [
|
|
30
|
+
"./extensions/pi-aliases.ts",
|
|
31
|
+
"./extensions/pi-startup-header.ts",
|
|
32
|
+
"./extensions/claude-code-style.ts",
|
|
33
|
+
"./extensions/context.ts",
|
|
34
|
+
"./extensions/session-reference.ts",
|
|
35
|
+
"./extensions/agent-autocomplete.ts",
|
|
36
|
+
"./extensions/ask-user-question/index.ts"
|
|
37
|
+
],
|
|
38
|
+
"themes": [
|
|
39
|
+
"./themes/github-dark-default.json"
|
|
40
|
+
],
|
|
41
|
+
"image": "https://raw.githubusercontent.com/minuque/pi-cc-extensions/main/assets/readme/welcome.webp"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"test": "node --test tests/**/*.test.ts",
|
|
45
|
+
"typecheck": "node scripts/typecheck.mjs",
|
|
46
|
+
"format": "biome format --write .",
|
|
47
|
+
"format:check": "biome format .",
|
|
48
|
+
"preversion": "npm run format:check && npm run typecheck && npm test",
|
|
49
|
+
"release": "npm version patch && npm publish",
|
|
50
|
+
"release:minor": "npm version minor && npm publish",
|
|
51
|
+
"release:major": "npm version major && npm publish"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@juicesharp/rpiv-config": "^2.1.0",
|
|
55
|
+
"typebox": "^1.1.24"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@earendil-works/pi-ai": "*",
|
|
59
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
60
|
+
"@earendil-works/pi-tui": "*"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22.19.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@biomejs/biome": "2.5.5",
|
|
67
|
+
"@types/node": "^26.1.1"
|
|
68
|
+
}
|
|
69
|
+
}
|