pi-cc-extensions 0.8.68 → 0.8.69
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.en.md +5 -3
- package/README.md +5 -3
- package/extensions/config/config.ts +26 -34
- package/extensions/config/panel.ts +53 -4
- package/extensions/feature/agent-summary/core.ts +3 -24
- package/extensions/feature/agent-summary/index.ts +1 -1
- package/extensions/feature/compact-thinking.ts +0 -3
- package/extensions/feature/context.ts +1 -0
- package/extensions/feature/reference/index.ts +7 -53
- package/extensions/feature/reference/session.ts +2 -14
- package/extensions/feature/shell/startup-header.ts +1 -6
- package/extensions/renderer/compact-mode.ts +8 -23
- package/extensions/renderer/default-mode.ts +26 -64
- package/extensions/renderer/index.ts +0 -13
- package/extensions/renderer/mouse/hover.ts +0 -5
- package/extensions/renderer/mouse/interaction.ts +57 -27
- package/extensions/renderer/mouse/packets.ts +1 -0
- package/extensions/renderer/mouse/scroll.ts +7 -14
- package/extensions/renderer/tool/diff/diff-limits.ts +2 -10
- package/extensions/renderer/tool/diff/diff-parse.ts +2 -9
- package/extensions/renderer/tool/result.ts +45 -31
- package/extensions/utils/patch-keys.ts +4 -10
- package/extensions/utils/sgr-mouse.ts +6 -0
- package/package.json +3 -3
package/README.en.md
CHANGED
|
@@ -61,8 +61,6 @@ Run `/reload` after installation.
|
|
|
61
61
|
"editDiffCollapsedLines": 24, // Edit collapse lines; beyond that shows the expand hint
|
|
62
62
|
"writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
|
|
63
63
|
"diffWordWrap": true, // wrap long diff lines
|
|
64
|
-
"expandedPreviewMaxLines": 40, // max lines for expanded bodies
|
|
65
|
-
"inputClip": 100, // tool summary path/command clip length
|
|
66
64
|
|
|
67
65
|
// thinking
|
|
68
66
|
"useSummaryTitlesAsThinkingTitle": true, // use latest summary as thinking title
|
|
@@ -71,6 +69,10 @@ Run `/reload` after installation.
|
|
|
71
69
|
"dimThinkingText": false, // dim thinking body text
|
|
72
70
|
|
|
73
71
|
// ui
|
|
72
|
+
"expandedInputMaxLines": 5, // expanded tool Input lines; overflow shows a footer hint
|
|
73
|
+
"expandedOutputMaxLines": 10, // expanded tool Output lines; overflow shows a footer hint
|
|
74
|
+
"expandedPreviewMaxLines": 40, // max lines for expanded diff/TaskList bodies
|
|
75
|
+
"inputClip": 100, // tool summary path/command clip length
|
|
74
76
|
"showStartupHeader": true, // startup header (logo + tips) toggle
|
|
75
77
|
"scrollStepLines": 3, // fullscreen wheel scroll step
|
|
76
78
|
|
|
@@ -84,7 +86,7 @@ Run `/reload` after installation.
|
|
|
84
86
|
}
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
> **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. Double-click an expanded panel to collapse it.
|
|
89
|
+
> **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. When expanded Input/Output exceeds the line cap, the footer `… +N more lines • click to show more` opens a full preview. Double-click an expanded panel to collapse it.
|
|
88
90
|
>
|
|
89
91
|
> **Tip**: set `markdown.mermaid` to `final` via `~/.pi/agent/settings.json` or the Mermaid diagrams option in `/settings`. Default `streaming` redraws per frame; `final` renders once at completion.
|
|
90
92
|
|
package/README.md
CHANGED
|
@@ -61,8 +61,6 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
61
61
|
"editDiffCollapsedLines": 24, // Edit 折叠行数,超出显示展开提示
|
|
62
62
|
"writeDiffCollapsedLines": 0, // write 折叠行数,0 仅显示创建摘要
|
|
63
63
|
"diffWordWrap": true, // 长 diff 行换行
|
|
64
|
-
"expandedPreviewMaxLines": 40, // 展开正文最大行数
|
|
65
|
-
"inputClip": 100, // 工具摘要 path/command 折叠字符数
|
|
66
64
|
|
|
67
65
|
// thinking
|
|
68
66
|
"useSummaryTitlesAsThinkingTitle": true, // 用最新摘要作思考标题
|
|
@@ -71,6 +69,10 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
71
69
|
"dimThinkingText": false, // thinking 正文用 dim 色
|
|
72
70
|
|
|
73
71
|
// ui
|
|
72
|
+
"expandedInputMaxLines": 5, // 展开工具卡 Input 可见行数,超出在末行显示展开提示
|
|
73
|
+
"expandedOutputMaxLines": 10, // 展开工具卡 Output 可见行数,超出在末行显示展开提示
|
|
74
|
+
"expandedPreviewMaxLines": 40, // 展开 diff/TaskList 正文最大行数
|
|
75
|
+
"inputClip": 100, // 工具摘要 path/command 折叠字符数
|
|
74
76
|
"showStartupHeader": true, // 启动头(logo + tips)开关
|
|
75
77
|
"scrollStepLines": 3, // fullscreen 滚轮步进
|
|
76
78
|
|
|
@@ -84,7 +86,7 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
84
86
|
}
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
> **Fullscreen**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact
|
|
89
|
+
> **Fullscreen**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要;展开后 Input/Output 超行时,末行 `… +N more lines • click to show more` 打开全量预览。双击展开面板收起。
|
|
88
90
|
>
|
|
89
91
|
> **建议**:`markdown.mermaid` 设为 `final`(`~/.pi/agent/settings.json` 或 `/settings` 面板的 Mermaid diagrams 选项)。默认 `streaming` 逐帧重绘,`final` 渲染最终版更稳定。
|
|
90
92
|
|
|
@@ -5,9 +5,6 @@ import { join } from "node:path";
|
|
|
5
5
|
|
|
6
6
|
export type CompactStyleMode = "on" | "compact" | "off";
|
|
7
7
|
|
|
8
|
-
// ── diff 显示配置:作为配置 schema 的一部分由 config 层拥有 ──
|
|
9
|
-
// renderer(diff)从 config 导入并 re-export,避免 config → renderer 的反向依赖。
|
|
10
|
-
|
|
11
8
|
export type DiffViewMode = "auto" | "split" | "unified";
|
|
12
9
|
export type DiffIndicatorMode = "bars" | "classic" | "none";
|
|
13
10
|
|
|
@@ -52,6 +49,8 @@ export type Config = {
|
|
|
52
49
|
writeDiffCollapsedLines: number;
|
|
53
50
|
diffWordWrap: boolean;
|
|
54
51
|
expandedPreviewMaxLines: number;
|
|
52
|
+
expandedInputMaxLines: number;
|
|
53
|
+
expandedOutputMaxLines: number;
|
|
55
54
|
inputClip: number;
|
|
56
55
|
useSummaryTitlesAsThinkingTitle: boolean;
|
|
57
56
|
previewLines: number;
|
|
@@ -78,6 +77,10 @@ export const DIFF_COLLAPSED_LINES_VALUES = ["12", "24", "36", "48", "80", "120"]
|
|
|
78
77
|
export const WRITE_DIFF_COLLAPSED_LINES_VALUES = ["0", "4", "8", "12", "24", "36"];
|
|
79
78
|
/** Presets for expanded body height — keep low options first so cycling stays TUI-friendly. */
|
|
80
79
|
export const EXPANDED_PREVIEW_MAX_LINES_VALUES = ["40", "60", "80", "120", "200", "500", "2000"];
|
|
80
|
+
/** 展开工具卡 Input 可见行数预设。 */
|
|
81
|
+
export const EXPANDED_INPUT_MAX_LINES_VALUES = ["5", "10", "20", "40", "80"];
|
|
82
|
+
/** 展开工具卡 Output 可见行数预设。 */
|
|
83
|
+
export const EXPANDED_OUTPUT_MAX_LINES_VALUES = ["10", "20", "40", "80", "120"];
|
|
81
84
|
/** 工具摘要里 path/command 等输入的折叠字符数。 */
|
|
82
85
|
export const INPUT_CLIP_VALUES = ["40", "60", "80", "100", "120", "160"];
|
|
83
86
|
export const THINKING_PREVIEW_LINES_VALUES = ["0", "1", "3", "5", "10"];
|
|
@@ -107,6 +110,8 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
107
110
|
writeDiffCollapsedLines: DEFAULT_TOOL_DISPLAY_CONFIG.writeDiffCollapsedLines,
|
|
108
111
|
diffWordWrap: DEFAULT_TOOL_DISPLAY_CONFIG.diffWordWrap,
|
|
109
112
|
expandedPreviewMaxLines: DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
113
|
+
expandedInputMaxLines: 5,
|
|
114
|
+
expandedOutputMaxLines: 10,
|
|
110
115
|
inputClip: 100,
|
|
111
116
|
useSummaryTitlesAsThinkingTitle: true,
|
|
112
117
|
previewLines: 3,
|
|
@@ -141,16 +146,7 @@ export function pickPositiveNumber(value: unknown, fallback: number, min = 1): n
|
|
|
141
146
|
|
|
142
147
|
export function normalizeConfig(input: unknown): Config {
|
|
143
148
|
const source = (input && typeof input === "object" ? input : {}) as Record<string, unknown>;
|
|
144
|
-
const mode = source.mode;
|
|
145
|
-
// 旧 `enabled: boolean` 配置迁移;compact 已恢复为受支持模式,不再回退 on。
|
|
146
|
-
const migratedMode: CompactStyleMode =
|
|
147
|
-
mode === "on" || mode === "compact" || mode === "off"
|
|
148
|
-
? mode
|
|
149
|
-
: typeof source.enabled === "boolean"
|
|
150
|
-
? source.enabled
|
|
151
|
-
? "on"
|
|
152
|
-
: "off"
|
|
153
|
-
: "on";
|
|
149
|
+
const mode = pickEnum(source.mode, ["on", "compact", "off"], DEFAULT_CONFIG.mode);
|
|
154
150
|
const excludeRenderers = Array.isArray(source.excludeRenderers)
|
|
155
151
|
? [
|
|
156
152
|
...new Set(
|
|
@@ -161,7 +157,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
161
157
|
]
|
|
162
158
|
: [];
|
|
163
159
|
return {
|
|
164
|
-
mode
|
|
160
|
+
mode,
|
|
165
161
|
excludeRenderers,
|
|
166
162
|
diffViewMode: pickEnum(source.diffViewMode, DIFF_VIEW_MODES, DEFAULT_CONFIG.diffViewMode),
|
|
167
163
|
diffIndicatorMode: pickEnum(
|
|
@@ -176,7 +172,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
176
172
|
300,
|
|
177
173
|
),
|
|
178
174
|
editDiffCollapsedLines: pickPositiveInt(
|
|
179
|
-
source.editDiffCollapsedLines
|
|
175
|
+
source.editDiffCollapsedLines,
|
|
180
176
|
DEFAULT_CONFIG.editDiffCollapsedLines,
|
|
181
177
|
1,
|
|
182
178
|
500,
|
|
@@ -194,12 +190,19 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
194
190
|
10,
|
|
195
191
|
50_000,
|
|
196
192
|
),
|
|
197
|
-
|
|
198
|
-
source.
|
|
199
|
-
DEFAULT_CONFIG.
|
|
200
|
-
|
|
201
|
-
|
|
193
|
+
expandedInputMaxLines: pickPositiveInt(
|
|
194
|
+
source.expandedInputMaxLines,
|
|
195
|
+
DEFAULT_CONFIG.expandedInputMaxLines,
|
|
196
|
+
1,
|
|
197
|
+
5_000,
|
|
198
|
+
),
|
|
199
|
+
expandedOutputMaxLines: pickPositiveInt(
|
|
200
|
+
source.expandedOutputMaxLines,
|
|
201
|
+
DEFAULT_CONFIG.expandedOutputMaxLines,
|
|
202
|
+
1,
|
|
203
|
+
5_000,
|
|
202
204
|
),
|
|
205
|
+
inputClip: pickPositiveInt(source.inputClip, DEFAULT_CONFIG.inputClip, 8, 500),
|
|
203
206
|
useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle !== false,
|
|
204
207
|
previewLines: pickPositiveInt(
|
|
205
208
|
source.previewLines,
|
|
@@ -258,6 +261,8 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
258
261
|
`writeCollapsed=${source.writeDiffCollapsedLines}`,
|
|
259
262
|
`diffWordWrap=${source.diffWordWrap ? "on" : "off"}`,
|
|
260
263
|
`expandedMax=${source.expandedPreviewMaxLines}`,
|
|
264
|
+
`expandedInput=${source.expandedInputMaxLines}`,
|
|
265
|
+
`expandedOutput=${source.expandedOutputMaxLines}`,
|
|
261
266
|
`inputClip=${source.inputClip}`,
|
|
262
267
|
`thinkingTitle=${source.useSummaryTitlesAsThinkingTitle ? "summary" : "default"}`,
|
|
263
268
|
`thinkingPreview=${source.previewLines}`,
|
|
@@ -282,20 +287,7 @@ function loadConfig(): Config {
|
|
|
282
287
|
const source = existsSync(CONFIG_PATH)
|
|
283
288
|
? (JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as Record<string, unknown>)
|
|
284
289
|
: {};
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
typeof source.enabled === "boolean" &&
|
|
288
|
-
source.mode !== "on" &&
|
|
289
|
-
source.mode !== "compact" &&
|
|
290
|
-
source.mode !== "off"
|
|
291
|
-
) {
|
|
292
|
-
try {
|
|
293
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(normalized, null, 2));
|
|
294
|
-
} catch {
|
|
295
|
-
// A read-only config still uses the migrated in-memory value.
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
return normalized;
|
|
290
|
+
return normalizeConfig(source);
|
|
299
291
|
} catch {
|
|
300
292
|
// Ignore bad config and fall back to defaults.
|
|
301
293
|
}
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
DIFF_SPLIT_MIN_WIDTH_VALUES,
|
|
18
18
|
DIFF_VIEW_MODES,
|
|
19
19
|
EXCLUDE_RENDERER_CANDIDATES,
|
|
20
|
+
EXPANDED_INPUT_MAX_LINES_VALUES,
|
|
21
|
+
EXPANDED_OUTPUT_MAX_LINES_VALUES,
|
|
20
22
|
EXPANDED_PREVIEW_MAX_LINES_VALUES,
|
|
21
23
|
formatExcludeRenderers,
|
|
22
24
|
getCompactThinkingConfig,
|
|
@@ -327,11 +329,31 @@ export async function showCcstylePanel(
|
|
|
327
329
|
currentValue: config.diffWordWrap ? "on" : "off",
|
|
328
330
|
values: ["on", "off"],
|
|
329
331
|
};
|
|
332
|
+
const expandedInputSetting = {
|
|
333
|
+
id: "expandedInputMaxLines",
|
|
334
|
+
label: "Expanded input lines",
|
|
335
|
+
description:
|
|
336
|
+
"Max Input section lines in an expanded tool card. Overflow shows click to show more. Default 5.",
|
|
337
|
+
currentValue: String(config.expandedInputMaxLines),
|
|
338
|
+
values: [...EXPANDED_INPUT_MAX_LINES_VALUES],
|
|
339
|
+
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
340
|
+
buildNumberInputSubmenu(theme, expandedInputSetting, closeSubmenu),
|
|
341
|
+
};
|
|
342
|
+
const expandedOutputSetting = {
|
|
343
|
+
id: "expandedOutputMaxLines",
|
|
344
|
+
label: "Expanded output lines",
|
|
345
|
+
description:
|
|
346
|
+
"Max Output section lines in an expanded tool card. Overflow shows click to show more. Default 10.",
|
|
347
|
+
currentValue: String(config.expandedOutputMaxLines),
|
|
348
|
+
values: [...EXPANDED_OUTPUT_MAX_LINES_VALUES],
|
|
349
|
+
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
350
|
+
buildNumberInputSubmenu(theme, expandedOutputSetting, closeSubmenu),
|
|
351
|
+
};
|
|
330
352
|
const expandedMaxSetting = {
|
|
331
353
|
id: "expandedPreviewMaxLines",
|
|
332
354
|
label: "Expanded max lines",
|
|
333
355
|
description:
|
|
334
|
-
"Max
|
|
356
|
+
"Max diff/TaskList body lines when expanded. Tool Input/Output use the two settings above.",
|
|
335
357
|
currentValue: String(config.expandedPreviewMaxLines),
|
|
336
358
|
values: [...EXPANDED_PREVIEW_MAX_LINES_VALUES],
|
|
337
359
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
@@ -512,6 +534,28 @@ export async function showCcstylePanel(
|
|
|
512
534
|
? "Long diff lines wrap within the panel width."
|
|
513
535
|
: "Long diff lines are truncated to the panel width.";
|
|
514
536
|
break;
|
|
537
|
+
case "expandedInputMaxLines":
|
|
538
|
+
updateConfig({
|
|
539
|
+
expandedInputMaxLines: pickPositiveInt(
|
|
540
|
+
value,
|
|
541
|
+
DEFAULT_CONFIG.expandedInputMaxLines,
|
|
542
|
+
1,
|
|
543
|
+
5_000,
|
|
544
|
+
),
|
|
545
|
+
});
|
|
546
|
+
expandedInputSetting.currentValue = String(config.expandedInputMaxLines);
|
|
547
|
+
break;
|
|
548
|
+
case "expandedOutputMaxLines":
|
|
549
|
+
updateConfig({
|
|
550
|
+
expandedOutputMaxLines: pickPositiveInt(
|
|
551
|
+
value,
|
|
552
|
+
DEFAULT_CONFIG.expandedOutputMaxLines,
|
|
553
|
+
1,
|
|
554
|
+
5_000,
|
|
555
|
+
),
|
|
556
|
+
});
|
|
557
|
+
expandedOutputSetting.currentValue = String(config.expandedOutputMaxLines);
|
|
558
|
+
break;
|
|
515
559
|
case "expandedPreviewMaxLines":
|
|
516
560
|
updateConfig({
|
|
517
561
|
expandedPreviewMaxLines: pickPositiveInt(
|
|
@@ -580,8 +624,6 @@ export async function showCcstylePanel(
|
|
|
580
624
|
diffCollapsedSetting,
|
|
581
625
|
writeDiffCollapsedSetting,
|
|
582
626
|
diffWordWrapSetting,
|
|
583
|
-
expandedMaxSetting,
|
|
584
|
-
inputClipSetting,
|
|
585
627
|
],
|
|
586
628
|
},
|
|
587
629
|
{
|
|
@@ -597,7 +639,14 @@ export async function showCcstylePanel(
|
|
|
597
639
|
{
|
|
598
640
|
id: "ui",
|
|
599
641
|
label: "UI",
|
|
600
|
-
items: [
|
|
642
|
+
items: [
|
|
643
|
+
expandedInputSetting,
|
|
644
|
+
expandedOutputSetting,
|
|
645
|
+
expandedMaxSetting,
|
|
646
|
+
inputClipSetting,
|
|
647
|
+
startupHeaderSetting,
|
|
648
|
+
scrollStepSetting,
|
|
649
|
+
],
|
|
601
650
|
},
|
|
602
651
|
{
|
|
603
652
|
id: "feature",
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { formatDuration } from "../../utils/format.ts";
|
|
13
|
-
export { formatDuration };
|
|
14
13
|
|
|
15
14
|
/** 工具分类。 */
|
|
16
15
|
export type AgentToolCategory = "bash" | "read" | "edit" | "write" | "other";
|
|
@@ -114,24 +113,8 @@ function summaryParts(data: AgentSummaryData): string[] {
|
|
|
114
113
|
return parts;
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
/** 纯文本摘要行:句首大写 + 可选耗时。 */
|
|
118
|
-
export function summaryLine(data: AgentSummaryData): string {
|
|
119
|
-
const parts = summaryParts(data);
|
|
120
|
-
if (parts.length === 0) return "";
|
|
121
|
-
const text = parts.join(", ");
|
|
122
|
-
const capitalized = text[0].toUpperCase() + text.slice(1);
|
|
123
|
-
const duration = formatDuration(data.durationMs);
|
|
124
|
-
return duration ? `${capitalized} · ${duration}` : capitalized;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Markdown 摘要行。
|
|
129
|
-
* `box` 为 true:引用块 `> *斜体*`;false:整体加粗。
|
|
130
|
-
* `colors`:仅数字染色(success / failed)。
|
|
131
|
-
*/
|
|
132
116
|
export function summaryMarkdown(
|
|
133
117
|
data: AgentSummaryData,
|
|
134
|
-
box = false,
|
|
135
118
|
colors: { success: string; failed: string } = { success: "", failed: "" },
|
|
136
119
|
): string {
|
|
137
120
|
const parts = summaryParts(data);
|
|
@@ -148,7 +131,7 @@ export function summaryMarkdown(
|
|
|
148
131
|
.join(", ");
|
|
149
132
|
const duration = formatDuration(data.durationMs);
|
|
150
133
|
const line = duration ? `${text} · ${duration}` : text;
|
|
151
|
-
return
|
|
134
|
+
return `> *${line}*`;
|
|
152
135
|
}
|
|
153
136
|
|
|
154
137
|
/**
|
|
@@ -160,8 +143,7 @@ export function summaryMarkdown(
|
|
|
160
143
|
export function bindAgentSummary(
|
|
161
144
|
pi: ExtensionAPI,
|
|
162
145
|
onSummary: (data: AgentSummaryData) => void,
|
|
163
|
-
|
|
164
|
-
): () => void {
|
|
146
|
+
): void {
|
|
165
147
|
let summary = new AgentRunSummary();
|
|
166
148
|
pi.on("agent_start", async () => {
|
|
167
149
|
summary = new AgentRunSummary();
|
|
@@ -173,9 +155,6 @@ export function bindAgentSummary(
|
|
|
173
155
|
summary.recordToolResult(event.isError === true);
|
|
174
156
|
});
|
|
175
157
|
pi.on("agent_end", async () => {
|
|
176
|
-
if (summary.toolCount >=
|
|
158
|
+
if (summary.toolCount >= 2) onSummary(summary.snapshot());
|
|
177
159
|
});
|
|
178
|
-
return () => {
|
|
179
|
-
summary = new AgentRunSummary();
|
|
180
|
-
};
|
|
181
160
|
}
|
|
@@ -16,7 +16,7 @@ export const AGENT_SUMMARY_ENTRY_TYPE = "agent-summary";
|
|
|
16
16
|
|
|
17
17
|
export default function (pi: ExtensionAPI): void {
|
|
18
18
|
pi.registerEntryRenderer(AGENT_SUMMARY_ENTRY_TYPE, (entry, _options, theme) => {
|
|
19
|
-
const line = summaryMarkdown(entry.data as AgentSummaryData,
|
|
19
|
+
const line = summaryMarkdown(entry.data as AgentSummaryData, {
|
|
20
20
|
success: theme.getFgAnsi("success"),
|
|
21
21
|
failed: theme.getFgAnsi("error"),
|
|
22
22
|
});
|
|
@@ -31,9 +31,6 @@ import {
|
|
|
31
31
|
patchRegistry,
|
|
32
32
|
PROTOTYPE_ORIGINAL_KEY,
|
|
33
33
|
} from "../utils/patch-keys.ts";
|
|
34
|
-
// 保持导出兼容:渲染函数已并入 renderer/compact-mode.ts,这里 re-export。
|
|
35
|
-
export { animateCompactThinkingText, formatThoughtDuration, styleCompactThinkingText };
|
|
36
|
-
|
|
37
34
|
// pi-tui 类型声明中 TUI 的 re-export 解析失败,本地用最小结构化类型(只用到 requestRender)。
|
|
38
35
|
type RenderTui = { requestRender(force?: boolean): void };
|
|
39
36
|
|
|
@@ -26,8 +26,6 @@ const MAX_SESSION_SUGGESTIONS = 3;
|
|
|
26
26
|
const MAX_FILE_SUGGESTIONS = 7;
|
|
27
27
|
const MAX_REFERENCED_SESSIONS = 5;
|
|
28
28
|
const MENTION_PATTERN = /(?:^|[\t ])@([^\s@]*)$/;
|
|
29
|
-
const SUBAGENT_MANAGER_KEY = Symbol.for("pi-subagents:manager");
|
|
30
|
-
|
|
31
29
|
// ── In-process subagent record tracker ──────────────────────────────
|
|
32
30
|
// pi-subagents does not expose a global manager; we track records
|
|
33
31
|
// ourselves by listening to the events it emits.
|
|
@@ -53,17 +51,6 @@ type SessionReference = {
|
|
|
53
51
|
messages?: unknown[];
|
|
54
52
|
};
|
|
55
53
|
|
|
56
|
-
type SubagentRecord = {
|
|
57
|
-
id: string;
|
|
58
|
-
description?: string;
|
|
59
|
-
startedAt?: number;
|
|
60
|
-
completedAt?: number;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
type SubagentManager = {
|
|
64
|
-
getRecord(id: string): SubagentRecord | undefined;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
54
|
// Local subagent record tracking (pi-subagents does not expose a global manager).
|
|
68
55
|
const liveSubagentRecords = new Map<string, SubagentLiveRecord>();
|
|
69
56
|
/** Cap retained completed/live records so long-lived pi processes cannot grow unbounded. */
|
|
@@ -120,28 +107,6 @@ function trackSubagentFromEvent(data: unknown): void {
|
|
|
120
107
|
pruneLiveSubagentRecords();
|
|
121
108
|
}
|
|
122
109
|
|
|
123
|
-
function getSubagentManager(): SubagentManager | undefined {
|
|
124
|
-
// Try the global manager first (future-proof), fall back to local records.
|
|
125
|
-
const manager = (globalThis as Record<PropertyKey, unknown>)[SUBAGENT_MANAGER_KEY] as
|
|
126
|
-
| SubagentManager
|
|
127
|
-
| undefined;
|
|
128
|
-
if (manager && typeof manager.getRecord === "function") return manager;
|
|
129
|
-
// If no global manager, use our local tracking.
|
|
130
|
-
if (liveSubagentRecords.size === 0) return undefined;
|
|
131
|
-
return {
|
|
132
|
-
getRecord(id: string): SubagentRecord | undefined {
|
|
133
|
-
const record = liveSubagentRecords.get(id);
|
|
134
|
-
if (!record) return undefined;
|
|
135
|
-
return {
|
|
136
|
-
id: record.runId,
|
|
137
|
-
description: record.agent || undefined,
|
|
138
|
-
startedAt: record.startedAt,
|
|
139
|
-
completedAt: record.completedAt,
|
|
140
|
-
};
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
110
|
function extractMentionQuery(textBeforeCursor: string): string | undefined {
|
|
146
111
|
return textBeforeCursor.match(MENTION_PATTERN)?.[1];
|
|
147
112
|
}
|
|
@@ -281,31 +246,20 @@ function liveSubagentReferences(
|
|
|
281
246
|
agentIds: Set<string>,
|
|
282
247
|
currentSessionId: string,
|
|
283
248
|
): SessionReference[] {
|
|
284
|
-
const manager = getSubagentManager();
|
|
285
|
-
if (!manager) return [];
|
|
286
|
-
|
|
287
249
|
const references: SessionReference[] = [];
|
|
288
250
|
for (const agentId of agentIds) {
|
|
289
|
-
const record =
|
|
290
|
-
if (!record) continue;
|
|
291
|
-
|
|
292
|
-
// Try the global manager's live session first (future-proof).
|
|
293
|
-
const liveRecord = liveSubagentRecords.get(agentId);
|
|
294
|
-
const sessionId = liveRecord?.sessionId ?? record.id;
|
|
295
|
-
if (!sessionId || sessionId === currentSessionId) continue;
|
|
296
|
-
|
|
297
|
-
const name = record.description?.trim() || liveRecord?.agent || undefined;
|
|
298
|
-
const modifiedAt = record.completedAt ?? record.startedAt ?? Date.now();
|
|
251
|
+
const record = liveSubagentRecords.get(agentId);
|
|
252
|
+
if (!record || record.sessionId === currentSessionId) continue;
|
|
299
253
|
references.push({
|
|
300
254
|
kind: "subagent",
|
|
301
|
-
referenceIds: [sessionId, agentId],
|
|
255
|
+
referenceIds: [record.sessionId, agentId],
|
|
302
256
|
info: {
|
|
303
|
-
id: sessionId,
|
|
304
|
-
name,
|
|
305
|
-
cwd:
|
|
257
|
+
id: record.sessionId,
|
|
258
|
+
name: record.agent || undefined,
|
|
259
|
+
cwd: record.cwd,
|
|
306
260
|
firstMessage: "",
|
|
307
261
|
messageCount: 0,
|
|
308
|
-
modified: new Date(
|
|
262
|
+
modified: new Date(record.completedAt ?? record.startedAt),
|
|
309
263
|
},
|
|
310
264
|
});
|
|
311
265
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export const SESSION_REFERENCE_CUSTOM_TYPE = "session-reference";
|
|
2
2
|
export const SESSION_REFERENCE_PREFIX = "@session:";
|
|
3
3
|
|
|
4
|
-
const SESSION_REFERENCE_PATTERN =
|
|
5
|
-
/(?:^|\s)@session:(?:\[([^\]]+)\]|([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?))(?![\w.\]-])/g;
|
|
4
|
+
const SESSION_REFERENCE_PATTERN = /(?:^|\s)@session:\[([^\]]+)](?![\w-])/g;
|
|
6
5
|
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
|
|
7
6
|
|
|
8
7
|
export interface ReferenceSessionInfo {
|
|
@@ -31,13 +30,12 @@ export const DEFAULT_REFERENCE_LIMITS: ReferenceLimits = {
|
|
|
31
30
|
maxTotalBytes: 48_000,
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
/** 提取 @session:[name](组 1)或旧格式 @session:id(组 2),返回 name 或 id。 */
|
|
35
33
|
export function extractSessionReferenceIds(text: string): string[] {
|
|
36
34
|
const ids: string[] = [];
|
|
37
35
|
const seen = new Set<string>();
|
|
38
36
|
|
|
39
37
|
for (const match of text.matchAll(SESSION_REFERENCE_PATTERN)) {
|
|
40
|
-
const id =
|
|
38
|
+
const id = match[1]?.trim();
|
|
41
39
|
if (id && !seen.has(id)) {
|
|
42
40
|
seen.add(id);
|
|
43
41
|
ids.push(id);
|
|
@@ -250,13 +248,3 @@ export function buildReferenceContentFromSections(
|
|
|
250
248
|
].join("\n");
|
|
251
249
|
return truncateUtf8([header, ...sections].join("\n\n"), maxTotalBytes);
|
|
252
250
|
}
|
|
253
|
-
|
|
254
|
-
export function buildReferenceContent(
|
|
255
|
-
sources: ReferenceSource[],
|
|
256
|
-
limits: ReferenceLimits = DEFAULT_REFERENCE_LIMITS,
|
|
257
|
-
): string {
|
|
258
|
-
return buildReferenceContentFromSections(
|
|
259
|
-
sources.map((source) => formatReferenceSession(source, limits)),
|
|
260
|
-
limits.maxTotalBytes,
|
|
261
|
-
);
|
|
262
|
-
}
|
|
@@ -3,6 +3,7 @@ import { getKeybindings } from "@earendil-works/pi-tui";
|
|
|
3
3
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { config } from "../../config/config.ts";
|
|
5
5
|
import { ansi16ToRgb, ansi256ToRgb } from "../../utils/ansi-color.ts";
|
|
6
|
+
import { stripAnsi } from "../../utils/ansi-text.ts";
|
|
6
7
|
type Rgb = [number, number, number];
|
|
7
8
|
type StyledPart = {
|
|
8
9
|
raw: string;
|
|
@@ -10,8 +11,6 @@ type StyledPart = {
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
const ANSI_RESET = "\x1b[0m";
|
|
13
|
-
const ANSI_PATTERN =
|
|
14
|
-
/[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
15
14
|
|
|
16
15
|
// 官方 install.sh 静态 logo(4 行原样,短行补尾随空格统一到 8 列)+ 底部空行补到 5 行,
|
|
17
16
|
// 与右侧 tips 行数等高;着色保持现状(accent 渐变)
|
|
@@ -40,10 +39,6 @@ const PALETTE_SPAN = 0.25;
|
|
|
40
39
|
// 行间相位偏移:小步累加 → 整体左上暗→右下亮的对角渐变
|
|
41
40
|
const LOGO_ROW_PHASE_STEP = 0.08;
|
|
42
41
|
|
|
43
|
-
function stripAnsi(text: string): string {
|
|
44
|
-
return text.replace(ANSI_PATTERN, "");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
42
|
function getVisibleLength(text: string): number {
|
|
48
43
|
return [...stripAnsi(text)].length;
|
|
49
44
|
}
|
|
@@ -41,11 +41,8 @@ import { paddedBackgroundRow } from "./tool/grouping.ts";
|
|
|
41
41
|
import { hasVisibleText, stripBackgroundAnsi } from "../utils/ansi-text.ts";
|
|
42
42
|
import { walkComponentTree } from "../utils/component-tree.ts";
|
|
43
43
|
import {
|
|
44
|
-
ASSISTANT_REENTRY_DESCRIPTION,
|
|
45
44
|
ASSISTANT_REENTRY_KEY,
|
|
46
|
-
ASSISTANT_SET_EXPANDED_DESCRIPTION,
|
|
47
45
|
ASSISTANT_SET_EXPANDED_KEY,
|
|
48
|
-
ASSISTANT_TOGGLE_ROUND_DESCRIPTION,
|
|
49
46
|
ASSISTANT_TOGGLE_ROUND_KEY,
|
|
50
47
|
COMPACT_MODE_PATCH_KEY,
|
|
51
48
|
COMPACT_THINKING_PATCH_KEY,
|
|
@@ -433,28 +430,16 @@ function isToolComponent(value: any): boolean {
|
|
|
433
430
|
}
|
|
434
431
|
|
|
435
432
|
function detachAssistantExpansion(component: any): void {
|
|
436
|
-
|
|
437
|
-
typeof component?.[ASSISTANT_SET_EXPANDED_KEY]
|
|
438
|
-
component.setExpanded
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
owned = Object.getOwnPropertySymbols(component).some(
|
|
442
|
-
(symbol) =>
|
|
443
|
-
symbol.description === ASSISTANT_SET_EXPANDED_DESCRIPTION && component[symbol] === true,
|
|
444
|
-
);
|
|
433
|
+
if (
|
|
434
|
+
typeof component?.[ASSISTANT_SET_EXPANDED_KEY] !== "function" ||
|
|
435
|
+
component.setExpanded !== component[ASSISTANT_SET_EXPANDED_KEY]
|
|
436
|
+
) {
|
|
437
|
+
return;
|
|
445
438
|
}
|
|
446
|
-
if (!owned) return;
|
|
447
|
-
|
|
448
439
|
delete component.setExpanded;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
symbol.description === ASSISTANT_TOGGLE_ROUND_DESCRIPTION ||
|
|
453
|
-
symbol.description === ASSISTANT_REENTRY_DESCRIPTION
|
|
454
|
-
) {
|
|
455
|
-
delete component[symbol];
|
|
456
|
-
}
|
|
457
|
-
}
|
|
440
|
+
delete component[ASSISTANT_SET_EXPANDED_KEY];
|
|
441
|
+
delete component[ASSISTANT_TOGGLE_ROUND_KEY];
|
|
442
|
+
delete component[ASSISTANT_REENTRY_KEY];
|
|
458
443
|
}
|
|
459
444
|
|
|
460
445
|
/** 供 mouse-interaction 识别可点击的 compact assistant 行(仅 compact 模式下生效)。 */
|
|
@@ -62,20 +62,13 @@ type ToolRenderMethods = {
|
|
|
62
62
|
type GlobalToolRenderPatch = {
|
|
63
63
|
version: 2;
|
|
64
64
|
prototype: any;
|
|
65
|
-
owner: object;
|
|
66
65
|
active: boolean;
|
|
67
|
-
enabled: () => boolean;
|
|
68
66
|
mode: () => CompactStyleMode;
|
|
69
67
|
wrap: (tool: any) => any;
|
|
70
68
|
byDefinition: WeakMap<object, any>;
|
|
71
69
|
byName: Map<string, any>;
|
|
72
70
|
downstream: ToolRenderMethods;
|
|
73
71
|
installed: ToolRenderMethods;
|
|
74
|
-
// 兼容旧扩展读取 Symbol 上的 legacy 字段。
|
|
75
|
-
originalHasRendererDefinition: ToolRenderMethods["hasRendererDefinition"];
|
|
76
|
-
originalGetRenderShell: ToolRenderMethods["getRenderShell"];
|
|
77
|
-
originalGetCallRenderer: ToolRenderMethods["getCallRenderer"];
|
|
78
|
-
originalGetResultRenderer: ToolRenderMethods["getResultRenderer"];
|
|
79
72
|
};
|
|
80
73
|
|
|
81
74
|
type ToolExpandedBackgroundPatch = {
|
|
@@ -450,63 +443,39 @@ function isOwnershipAwarePatch(value: any): value is GlobalToolRenderPatch {
|
|
|
450
443
|
);
|
|
451
444
|
}
|
|
452
445
|
|
|
453
|
-
function downstreamForGlobalToolInstall(
|
|
446
|
+
function downstreamForGlobalToolInstall(
|
|
447
|
+
prototype: any,
|
|
448
|
+
previous: GlobalToolRenderPatch | undefined,
|
|
449
|
+
): ToolRenderMethods {
|
|
454
450
|
const current = prototypeToolRenderMethods(prototype);
|
|
455
|
-
if (!previous || previous.prototype !== prototype)
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
hasRendererDefinition:
|
|
459
|
-
current.hasRendererDefinition === previous.installed.hasRendererDefinition
|
|
460
|
-
? previous.downstream.hasRendererDefinition
|
|
461
|
-
: current.hasRendererDefinition,
|
|
462
|
-
getRenderShell:
|
|
463
|
-
current.getRenderShell === previous.installed.getRenderShell
|
|
464
|
-
? previous.downstream.getRenderShell
|
|
465
|
-
: current.getRenderShell,
|
|
466
|
-
getCallRenderer:
|
|
467
|
-
current.getCallRenderer === previous.installed.getCallRenderer
|
|
468
|
-
? previous.downstream.getCallRenderer
|
|
469
|
-
: current.getCallRenderer,
|
|
470
|
-
getResultRenderer:
|
|
471
|
-
current.getResultRenderer === previous.installed.getResultRenderer
|
|
472
|
-
? previous.downstream.getResultRenderer
|
|
473
|
-
: current.getResultRenderer,
|
|
474
|
-
};
|
|
451
|
+
if (!previous || previous.prototype !== prototype || !isOwnershipAwarePatch(previous)) {
|
|
452
|
+
return current;
|
|
475
453
|
}
|
|
476
|
-
|
|
477
|
-
// pre-v2 Symbol:originalX 显式记录真实下游(native 原型方法),无需源码嗅探。
|
|
478
|
-
// 缺失字段回退到当前原型方法(视作 external)。
|
|
479
|
-
const originalOrCurrent = (field: string, method: Function): Function =>
|
|
480
|
-
typeof previous[field] === "function" ? previous[field] : method;
|
|
481
454
|
return {
|
|
482
|
-
hasRendererDefinition:
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
getRenderShell:
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
getCallRenderer:
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
getResultRenderer:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
455
|
+
hasRendererDefinition:
|
|
456
|
+
current.hasRendererDefinition === previous.installed.hasRendererDefinition
|
|
457
|
+
? previous.downstream.hasRendererDefinition
|
|
458
|
+
: current.hasRendererDefinition,
|
|
459
|
+
getRenderShell:
|
|
460
|
+
current.getRenderShell === previous.installed.getRenderShell
|
|
461
|
+
? previous.downstream.getRenderShell
|
|
462
|
+
: current.getRenderShell,
|
|
463
|
+
getCallRenderer:
|
|
464
|
+
current.getCallRenderer === previous.installed.getCallRenderer
|
|
465
|
+
? previous.downstream.getCallRenderer
|
|
466
|
+
: current.getCallRenderer,
|
|
467
|
+
getResultRenderer:
|
|
468
|
+
current.getResultRenderer === previous.installed.getResultRenderer
|
|
469
|
+
? previous.downstream.getResultRenderer
|
|
470
|
+
: current.getResultRenderer,
|
|
498
471
|
};
|
|
499
472
|
}
|
|
500
473
|
|
|
501
|
-
function disconnectGlobalToolRenderPatch(patch:
|
|
502
|
-
if (!patch
|
|
474
|
+
function disconnectGlobalToolRenderPatch(patch: GlobalToolRenderPatch | undefined): void {
|
|
475
|
+
if (!patch) return;
|
|
503
476
|
patch.active = false;
|
|
504
|
-
patch.enabled = () => false;
|
|
505
|
-
patch.mode = () => "off";
|
|
506
|
-
patch.wrap = (tool: any) => tool;
|
|
507
477
|
patch.byDefinition = new WeakMap();
|
|
508
|
-
|
|
509
|
-
else patch.byName = new Map();
|
|
478
|
+
patch.byName.clear();
|
|
510
479
|
}
|
|
511
480
|
|
|
512
481
|
function installGlobalToolRendering(
|
|
@@ -515,25 +484,18 @@ function installGlobalToolRendering(
|
|
|
515
484
|
const prototype = (ToolExecutionComponent as any).prototype;
|
|
516
485
|
const previous = patchRegistry.get<GlobalToolRenderPatch>(GLOBAL_TOOL_RENDER_PATCH);
|
|
517
486
|
const downstream = downstreamForGlobalToolInstall(prototype, previous);
|
|
518
|
-
|
|
519
|
-
disconnectGlobalToolRenderPatch(previous);
|
|
487
|
+
if (isOwnershipAwarePatch(previous)) disconnectGlobalToolRenderPatch(previous);
|
|
520
488
|
|
|
521
489
|
const patch: GlobalToolRenderPatch = {
|
|
522
490
|
version: 2,
|
|
523
491
|
prototype,
|
|
524
|
-
owner: {},
|
|
525
492
|
active: true,
|
|
526
|
-
enabled: () => config.mode === "on",
|
|
527
493
|
mode: () => config.mode,
|
|
528
494
|
wrap: (tool: any) => createCcstyleTool(tool, writeExecutionMetadata),
|
|
529
495
|
byDefinition: new WeakMap(),
|
|
530
496
|
byName: new Map(),
|
|
531
497
|
downstream,
|
|
532
498
|
installed: undefined as any,
|
|
533
|
-
originalHasRendererDefinition: downstream.hasRendererDefinition,
|
|
534
|
-
originalGetRenderShell: downstream.getRenderShell,
|
|
535
|
-
originalGetCallRenderer: downstream.getCallRenderer,
|
|
536
|
-
originalGetResultRenderer: downstream.getResultRenderer,
|
|
537
499
|
};
|
|
538
500
|
|
|
539
501
|
patch.installed = {
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
} from "./default-mode.ts";
|
|
15
15
|
import { isLazyProxyTui } from "../utils/fullscreen-detect.ts";
|
|
16
16
|
import { showCcstylePanel } from "../config/panel.ts";
|
|
17
|
-
import { GLOBAL_COMPACTION_RENDER_PATCH, patchRegistry } from "../utils/patch-keys.ts";
|
|
18
17
|
import {
|
|
19
18
|
config,
|
|
20
19
|
formatConfigStatus,
|
|
@@ -93,16 +92,6 @@ function applyStyleMode(mode: CompactStyleMode, ctx: any, toolGrouping?: ToolGro
|
|
|
93
92
|
ctx.ui.notify(`Claude Code style: ${mode}`, "info");
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
type LegacyCompactionRenderPatch = {
|
|
97
|
-
enabled?: () => boolean;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/** Disable the pre-native compaction monkey patch left alive by /reload. */
|
|
101
|
-
function deactivateLegacyCompactionRendering() {
|
|
102
|
-
const patch = patchRegistry.get<LegacyCompactionRenderPatch>(GLOBAL_COMPACTION_RENDER_PATCH);
|
|
103
|
-
if (patch) patch.enabled = () => false;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
95
|
export default function (
|
|
107
96
|
pi: ExtensionAPI,
|
|
108
97
|
configOverride?: Partial<Config>,
|
|
@@ -137,7 +126,6 @@ export default function (
|
|
|
137
126
|
const disposeMessageDisplay = installMessageDisplayRendering();
|
|
138
127
|
// 展开背景必须在 compact-mode 之后装,shutdown 时先于 compact 释放。
|
|
139
128
|
const disposeToolExpandedBackground = installToolExpandedBackground();
|
|
140
|
-
deactivateLegacyCompactionRendering();
|
|
141
129
|
installation = {
|
|
142
130
|
defaultMode,
|
|
143
131
|
toolGrouping,
|
|
@@ -270,7 +258,6 @@ export default function (
|
|
|
270
258
|
current.compactMode.shutdown();
|
|
271
259
|
compactModeHooks = undefined;
|
|
272
260
|
current.disposeMessageDisplay();
|
|
273
|
-
deactivateLegacyCompactionRendering();
|
|
274
261
|
clearAllAnimations();
|
|
275
262
|
installation = undefined;
|
|
276
263
|
});
|
|
@@ -21,13 +21,8 @@ export function sharedToolHoverState(): SharedToolHoverState {
|
|
|
21
21
|
return patchRegistry.ensure(TOOL_HOVER_STATE_KEY, () => ({ toolCallId: null }));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// 状态单源在 globalThis 槽;hoveredToolCallId 变量仅为兼容旧 deep import 保留
|
|
25
|
-
// (外部扩展可能直接从 interaction.ts re-export 读取),内部读取一律走槽。
|
|
26
|
-
export let hoveredToolCallId: string | null = sharedToolHoverState().toolCallId;
|
|
27
|
-
|
|
28
24
|
export function setHoveredToolCallId(toolCallId: string | null): void {
|
|
29
25
|
sharedToolHoverState().toolCallId = toolCallId;
|
|
30
|
-
hoveredToolCallId = toolCallId;
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
export function isToolCallHovered(toolCallId: string | null | undefined): boolean {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
collectToolComponents,
|
|
20
20
|
extractToolFramePlacements,
|
|
21
21
|
isSgrLeftPress,
|
|
22
|
+
isSgrLeftRelease,
|
|
22
23
|
isToolExecutionComponent,
|
|
23
24
|
parseSgrMousePackets,
|
|
24
25
|
stripTerminalSequences,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
isScrollbarColumnAt,
|
|
37
38
|
} from "./layout.ts";
|
|
38
39
|
import {
|
|
40
|
+
disableOfficialScrollToEnd,
|
|
39
41
|
fullscreenLazyTui,
|
|
40
42
|
hideScrollButton,
|
|
41
43
|
isScrollBottomInput,
|
|
@@ -156,7 +158,11 @@ function updateToolSummaryHover(tui: any, packet: SgrMousePacket): void {
|
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
const EXPAND_PANEL_DOUBLE_CLICK_MS = 400;
|
|
159
|
-
|
|
161
|
+
/** 松开后短时间内的重复按下视为同一次单击(终端会在 mouseup 后再打一次 press)。 */
|
|
162
|
+
const EXPAND_PANEL_CLICK_ARM_MS = 50;
|
|
163
|
+
let pendingExpandPress: { id: unknown } | null = null;
|
|
164
|
+
let lastExpandClick: { id: unknown; at: number } | null = null;
|
|
165
|
+
let armExpandClickTimer: ReturnType<typeof setTimeout> | null = null;
|
|
160
166
|
|
|
161
167
|
function expandPanelIdentity(card: any): unknown {
|
|
162
168
|
return card instanceof ThinkingPreviewBlock
|
|
@@ -167,17 +173,48 @@ function expandPanelIdentity(card: any): unknown {
|
|
|
167
173
|
function isExpandPanelDoubleClick(card: any): boolean {
|
|
168
174
|
const now = Date.now();
|
|
169
175
|
const id = expandPanelIdentity(card);
|
|
170
|
-
const prev =
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
const prev = lastExpandClick;
|
|
177
|
+
pendingExpandPress = { id };
|
|
178
|
+
if (prev && prev.id === id && now - prev.at <= EXPAND_PANEL_DOUBLE_CLICK_MS) {
|
|
179
|
+
pendingExpandPress = null;
|
|
180
|
+
lastExpandClick = null;
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function completeExpandPanelClick(): void {
|
|
187
|
+
if (!pendingExpandPress) return;
|
|
188
|
+
const click = { id: pendingExpandPress.id, at: Date.now() };
|
|
189
|
+
pendingExpandPress = null;
|
|
190
|
+
if (armExpandClickTimer) clearTimeout(armExpandClickTimer);
|
|
191
|
+
armExpandClickTimer = setTimeout(() => {
|
|
192
|
+
armExpandClickTimer = null;
|
|
193
|
+
// 50ms 内又按下:仍是同一次单击,不能武装成双击。
|
|
194
|
+
if (pendingExpandPress) return;
|
|
195
|
+
lastExpandClick = click;
|
|
196
|
+
}, EXPAND_PANEL_CLICK_ARM_MS);
|
|
197
|
+
if (
|
|
198
|
+
typeof armExpandClickTimer === "object" &&
|
|
199
|
+
armExpandClickTimer &&
|
|
200
|
+
"unref" in armExpandClickTimer
|
|
201
|
+
) {
|
|
202
|
+
armExpandClickTimer.unref();
|
|
203
|
+
}
|
|
173
204
|
}
|
|
174
205
|
|
|
175
206
|
function clearExpandPanelDoubleClick(): void {
|
|
176
|
-
|
|
207
|
+
pendingExpandPress = null;
|
|
208
|
+
lastExpandClick = null;
|
|
209
|
+
if (armExpandClickTimer) {
|
|
210
|
+
clearTimeout(armExpandClickTimer);
|
|
211
|
+
armExpandClickTimer = null;
|
|
212
|
+
}
|
|
177
213
|
}
|
|
178
214
|
|
|
179
215
|
function collapseExpandedCard(tui: any, card: any): boolean {
|
|
180
|
-
|
|
216
|
+
// 单击也 consume,避免官方链再合成一次 press 把单击当成双击。
|
|
217
|
+
if (!isExpandPanelDoubleClick(card)) return true;
|
|
181
218
|
card.setExpanded(false);
|
|
182
219
|
setHoveredToolCallId(null);
|
|
183
220
|
setHoveredToolGroup(null);
|
|
@@ -333,18 +370,17 @@ function handleFullscreenToolClick(tui: any, packet: SgrMousePacket): boolean {
|
|
|
333
370
|
const plain = stripTerminalSequencesPreservingLayout(line);
|
|
334
371
|
const section = view.matchShowMoreLine(plain);
|
|
335
372
|
if (section) {
|
|
373
|
+
clearExpandPanelDoubleClick();
|
|
336
374
|
const box = view.showMoreHitbox(plain);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
});
|
|
347
|
-
}
|
|
375
|
+
return tryOpenToolIoShowMore({
|
|
376
|
+
kind: "show-more",
|
|
377
|
+
row: 0,
|
|
378
|
+
startCol: box?.startCol ?? 1,
|
|
379
|
+
endCol: box?.endCol ?? 1,
|
|
380
|
+
component,
|
|
381
|
+
view,
|
|
382
|
+
section,
|
|
383
|
+
});
|
|
348
384
|
}
|
|
349
385
|
}
|
|
350
386
|
// 双击收起:内部工具仍归所属 group。单击放行官方(选择/链接)。
|
|
@@ -461,6 +497,7 @@ function patchFullscreenViewportInput(tui: any): void {
|
|
|
461
497
|
if (packets && tui.hasOverlay?.() && hasActiveTextPreview()) return undefined;
|
|
462
498
|
if (packets && !tui.hasOverlay?.()) {
|
|
463
499
|
for (const packet of packets) {
|
|
500
|
+
if (isSgrLeftRelease(packet)) completeExpandPanelClick();
|
|
464
501
|
if (isSgrLeftPress(packet) && handleFullscreenToolClick(tui, packet)) {
|
|
465
502
|
return { consume: true };
|
|
466
503
|
}
|
|
@@ -797,6 +834,7 @@ function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
|
797
834
|
let consumed = false;
|
|
798
835
|
for (const packet of packets) {
|
|
799
836
|
updateToolSummaryHover(getToolMouseTui(), packet);
|
|
837
|
+
if (isSgrLeftRelease(packet)) completeExpandPanelClick();
|
|
800
838
|
if (!isSgrLeftPress(packet)) continue;
|
|
801
839
|
if (toggleToolAtMouseClick(getToolMouseTui(), packet)) {
|
|
802
840
|
consumed = true;
|
|
@@ -879,10 +917,12 @@ export function installToolMouseInteraction(
|
|
|
879
917
|
if (isLazyProxyTui(tui)) {
|
|
880
918
|
patchFullscreenViewportInput(tui);
|
|
881
919
|
ensureFullscreenToolMouseMotion(tui);
|
|
920
|
+
disableOfficialScrollToEnd(tui);
|
|
882
921
|
setScrollButtonWidget({
|
|
883
922
|
render: (width: number) => {
|
|
884
923
|
patchFullscreenViewportInput(tui);
|
|
885
924
|
ensureFullscreenToolMouseMotion(tui);
|
|
925
|
+
disableOfficialScrollToEnd(tui);
|
|
886
926
|
return renderScrollButton(width, theme);
|
|
887
927
|
},
|
|
888
928
|
invalidate() {},
|
|
@@ -924,13 +964,3 @@ export function scheduleSessionRender(refresh?: () => void): void {
|
|
|
924
964
|
tui.requestRender(true);
|
|
925
965
|
}, 0);
|
|
926
966
|
}
|
|
927
|
-
|
|
928
|
-
// toolMouseTui 跨模块读取一律走 getToolMouseTui()(jiti 转译下 let 绑定可能是快照);
|
|
929
|
-
// 此 re-export 仅保留兼容旧导入,新代码请用 getToolMouseTui。
|
|
930
|
-
export { toolMouseTui } from "./scroll.ts";
|
|
931
|
-
export {
|
|
932
|
-
hoveredToolCallId,
|
|
933
|
-
isToolCallHovered,
|
|
934
|
-
setHoveredToolGroup,
|
|
935
|
-
setHoveredToolIo,
|
|
936
|
-
} from "./hover.ts";
|
|
@@ -18,26 +18,14 @@ const ZENTUI_PAGE_UP_INPUT = /^\x1b\[5;9(?::[12])?~$|^\x1b\[57421;9(?::[12])?u$|
|
|
|
18
18
|
const ZENTUI_PAGE_DOWN_INPUT = /^\x1b\[6;9(?::[12])?~$|^\x1b\[57422;9(?::[12])?u$|^\x1b\[1;6B$/;
|
|
19
19
|
const SCROLL_BOTTOM_SHORTCUT = "ctrl+end";
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* 当前安装的 tui 宿主。宿主放本模块(滚动按钮/调度依赖它),
|
|
23
|
-
* 由 mouse-interaction 经 setToolMouseTui 维护;跨模块一律经绑定/setter 访问。
|
|
24
|
-
*
|
|
25
|
-
* 状态镜像到 globalThis(Symbol 槽):jiti 转译下经 re-export 链读取的
|
|
26
|
-
* 模块级 let 绑定是初始值快照(死绑定,实测恒 null),函数调用才是活引用。
|
|
27
|
-
* 跨模块读取一律用 getToolMouseTui(),避免拿到加载时的快照。
|
|
28
|
-
*/
|
|
29
21
|
patchRegistry.ensure(TOOL_MOUSE_TUI_SLOT, () => null);
|
|
30
|
-
export let toolMouseTui: any = null;
|
|
31
22
|
export function getToolMouseTui(): any {
|
|
32
23
|
return patchRegistry.get(TOOL_MOUSE_TUI_SLOT);
|
|
33
24
|
}
|
|
34
25
|
export function setToolMouseTui(tui: any): void {
|
|
35
|
-
toolMouseTui = tui;
|
|
36
26
|
patchRegistry.install(TOOL_MOUSE_TUI_SLOT, tui);
|
|
37
27
|
}
|
|
38
28
|
|
|
39
|
-
// 滚动按钮状态同 toolMouseTui:镜像到 globalThis(Symbol 槽),跨模块读取
|
|
40
|
-
// 一律用 getter(jiti 转译下模块级 let 绑定是初始值快照)。
|
|
41
29
|
type ScrollButtonState = { visible: boolean; hovered: boolean; widget: any };
|
|
42
30
|
function scrollButtonState(): ScrollButtonState {
|
|
43
31
|
return patchRegistry.ensure(SCROLL_BUTTON_STATE_SLOT, () => ({
|
|
@@ -91,6 +79,11 @@ export function fullscreenLazyTui(tui: any): boolean {
|
|
|
91
79
|
return isLazyProxyTui(tui) && tui.mode === "fullscreen";
|
|
92
80
|
}
|
|
93
81
|
|
|
82
|
+
/** 关掉 pi 0.85 Jump to latest overlay,避免和本仓库 dock 按钮叠两层。 */
|
|
83
|
+
export function disableOfficialScrollToEnd(tui: any): void {
|
|
84
|
+
if (typeof tui?.scrollToEndIndicator === "function") tui.scrollToEndIndicator = undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
94
87
|
/** 官方 fullscreen:是否已跟随 transcript 底部(按钮隐藏条件)。 */
|
|
95
88
|
export function isFullscreenAtBottom(tui: any): boolean {
|
|
96
89
|
const following = tui.isFollowingOutput ?? tui.getPrimaryScrollView?.()?.isFollowingEnd ?? true;
|
|
@@ -164,7 +157,7 @@ export function scheduleScrollButtonSync(tui: any, data: string): void {
|
|
|
164
157
|
const previousLines = tui.previousLines;
|
|
165
158
|
const check = (attempt: number) => {
|
|
166
159
|
scrollButtonSyncScheduled = false;
|
|
167
|
-
if (
|
|
160
|
+
if (getToolMouseTui() !== tui) return;
|
|
168
161
|
// Pi renders on its own frame timer. Inspect the resulting viewport before
|
|
169
162
|
// showing the button so empty or non-scrollable transcripts never flash it.
|
|
170
163
|
const rendered = tui.previousLines !== previousLines;
|
|
@@ -192,7 +185,7 @@ export function updateScrollButtonFromInput(tui: any, data: string): void {
|
|
|
192
185
|
}
|
|
193
186
|
|
|
194
187
|
export function renderScrollButton(width: number, theme: any): string[] {
|
|
195
|
-
if (!getScrollButtonVisible() || !fullscreenLazyTui(
|
|
188
|
+
if (!getScrollButtonVisible() || !fullscreenLazyTui(getToolMouseTui())) return [];
|
|
196
189
|
const shortcut = formatShortcut(SCROLL_BOTTOM_SHORTCUT);
|
|
197
190
|
const label = theme.fg(
|
|
198
191
|
getScrollButtonHovered() ? "text" : "accent",
|
|
@@ -45,18 +45,10 @@ export function resolveDiffDisplayLimit(
|
|
|
45
45
|
return expanded ? Math.max(0, expandedLimit) : Math.max(1, collapsedLimit);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/** Write collapsed body limit. Missing/NaN falls back to edit's `editDiffCollapsedLines`. */
|
|
49
48
|
export function resolveWriteCollapsedLimit(
|
|
50
|
-
config: Pick<ToolDisplayConfig, "writeDiffCollapsedLines"
|
|
49
|
+
config: Pick<ToolDisplayConfig, "writeDiffCollapsedLines">,
|
|
51
50
|
): number {
|
|
52
|
-
|
|
53
|
-
if (Number.isFinite(value)) {
|
|
54
|
-
return Math.max(0, value);
|
|
55
|
-
}
|
|
56
|
-
const fallback = Number.isFinite(config.editDiffCollapsedLines)
|
|
57
|
-
? config.editDiffCollapsedLines
|
|
58
|
-
: DEFAULT_TOOL_DISPLAY_CONFIG.writeDiffCollapsedLines;
|
|
59
|
-
return Math.max(0, fallback);
|
|
51
|
+
return Math.max(0, config.writeDiffCollapsedLines);
|
|
60
52
|
}
|
|
61
53
|
|
|
62
54
|
/**
|
|
@@ -40,7 +40,6 @@ export interface DiffStats {
|
|
|
40
40
|
|
|
41
41
|
const CANONICAL_LINE_PATTERN = /^([+\- ])(\s*\d+)\|(.*)$/;
|
|
42
42
|
const HASHLINE_ANCHOR_LINE_PATTERN = /^([+\- ])(\s*\d+)#([A-Za-z0-9]+| {2}):(.*)$/;
|
|
43
|
-
const LEGACY_LINE_PATTERN = /^([+\- ])(\s*\d+)\s(.*)$/;
|
|
44
43
|
const HUNK_HEADER_PATTERN = /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@(.*)$/;
|
|
45
44
|
const MIN_LINE_NUMBER_WIDTH = 2;
|
|
46
45
|
|
|
@@ -81,14 +80,8 @@ function parseCanonicalDiffLine(line: string): {
|
|
|
81
80
|
};
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const matched = canonicalMatch ?? legacyMatch;
|
|
87
|
-
if (!matched) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return toParsedDiffLine(matched[1] ?? " ", matched[2] ?? "", matched[3] ?? "");
|
|
83
|
+
const match = line.match(CANONICAL_LINE_PATTERN);
|
|
84
|
+
return match ? toParsedDiffLine(match[1] ?? " ", match[2] ?? "", match[3] ?? "") : null;
|
|
92
85
|
}
|
|
93
86
|
|
|
94
87
|
function toNumber(value: string | undefined): number | null {
|
|
@@ -204,7 +204,7 @@ export class ExpandedToolResultText {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
/**
|
|
207
|
+
/** 截断体末行 `… +N more lines` 旁的展开提示,点击打开全量预览。 */
|
|
208
208
|
export const SHOW_MORE_LABEL = "• click to show more";
|
|
209
209
|
|
|
210
210
|
export type ToolIoSection = "input" | "output";
|
|
@@ -251,8 +251,8 @@ export class ExpandedToolIoView {
|
|
|
251
251
|
inputBody: string,
|
|
252
252
|
outputBody: string,
|
|
253
253
|
isError: boolean,
|
|
254
|
-
maxOutputLines = config.
|
|
255
|
-
maxInputLines = config.
|
|
254
|
+
maxOutputLines = config.expandedOutputMaxLines,
|
|
255
|
+
maxInputLines = config.expandedInputMaxLines,
|
|
256
256
|
flushLeft = false,
|
|
257
257
|
) {
|
|
258
258
|
this.theme = theme;
|
|
@@ -309,12 +309,12 @@ export class ExpandedToolIoView {
|
|
|
309
309
|
this.invalidate();
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
/** True when the plain
|
|
312
|
+
/** True when the plain truncation footer carries show-more. Input 续行带 │,Output 不带。 */
|
|
313
313
|
matchShowMoreLine(plainLine: string): ToolIoSection | null {
|
|
314
314
|
const line = plainLine.replace(/\x1b\[[0-9;]*m/g, "");
|
|
315
|
-
if (!line.includes(` • ${showMoreHintText()}`)) return null;
|
|
316
|
-
if (
|
|
317
|
-
if (
|
|
315
|
+
if (!line.includes(` • ${showMoreHintText()}`) || !/\+\d+ more lines/.test(line)) return null;
|
|
316
|
+
if (this.truncated.input && line.includes("│")) return "input";
|
|
317
|
+
if (this.truncated.output) return "output";
|
|
318
318
|
return null;
|
|
319
319
|
}
|
|
320
320
|
|
|
@@ -360,24 +360,13 @@ export class ExpandedToolIoView {
|
|
|
360
360
|
this.truncated = { input: false, output: false };
|
|
361
361
|
this.showMoreHeaderRows = {};
|
|
362
362
|
|
|
363
|
-
const pushHeader = (
|
|
364
|
-
corner: "├" | "└",
|
|
365
|
-
label: string,
|
|
366
|
-
section: ToolIoSection,
|
|
367
|
-
showMore: boolean,
|
|
368
|
-
) => {
|
|
363
|
+
const pushHeader = (corner: "├" | "└", label: string) => {
|
|
369
364
|
const mark = theme.fg("dim", `${lead}${corner} `);
|
|
370
365
|
const title = theme.fg(
|
|
371
366
|
"accent",
|
|
372
367
|
typeof theme.bold === "function" ? theme.bold(label) : label,
|
|
373
368
|
);
|
|
374
|
-
|
|
375
|
-
const more = showMore
|
|
376
|
-
? theme.fg("dim", " •") +
|
|
377
|
-
theme.fg(this.hoveredSection === section ? "text" : "dim", ` ${showMoreHintText()}`)
|
|
378
|
-
: "";
|
|
379
|
-
if (showMore) this.showMoreHeaderRows[section] = lines.length;
|
|
380
|
-
lines.push(truncateToWidth(mark + title + more, safeWidth, ""));
|
|
369
|
+
lines.push(truncateToWidth(mark + title, safeWidth, ""));
|
|
381
370
|
};
|
|
382
371
|
|
|
383
372
|
const pushRailLine = (styledContent: string, continued = true) => {
|
|
@@ -400,7 +389,7 @@ export class ExpandedToolIoView {
|
|
|
400
389
|
|
|
401
390
|
const pushBody = (
|
|
402
391
|
body: string,
|
|
403
|
-
opts: { input?: boolean; limit: number; continued?: boolean },
|
|
392
|
+
opts: { input?: boolean; limit: number; continued?: boolean; section: ToolIoSection },
|
|
404
393
|
): boolean /* truncated */ => {
|
|
405
394
|
const raw = body.replace(/\t/g, " ").replace(/\n+$/, "");
|
|
406
395
|
if (!raw.trim()) {
|
|
@@ -423,7 +412,15 @@ export class ExpandedToolIoView {
|
|
|
423
412
|
if (truncated) {
|
|
424
413
|
const hidden = Math.max(0, wrapped.length - visible.length);
|
|
425
414
|
if (hidden > 0) {
|
|
426
|
-
|
|
415
|
+
// hover 只高亮文字,圆点保持 dim(与 group hint 一致)。
|
|
416
|
+
const more =
|
|
417
|
+
theme.fg("dim", " •") +
|
|
418
|
+
theme.fg(
|
|
419
|
+
this.hoveredSection === opts.section ? "text" : "dim",
|
|
420
|
+
` ${showMoreHintText()}`,
|
|
421
|
+
);
|
|
422
|
+
pushRailLine(theme.fg("dim", `… +${hidden} more lines`) + more, opts.continued);
|
|
423
|
+
this.showMoreHeaderRows[opts.section] = lines.length - 1;
|
|
427
424
|
}
|
|
428
425
|
}
|
|
429
426
|
return truncated;
|
|
@@ -447,20 +444,29 @@ export class ExpandedToolIoView {
|
|
|
447
444
|
|
|
448
445
|
if (hasInput) {
|
|
449
446
|
this.truncated.input = inputWouldTruncate;
|
|
450
|
-
pushHeader("├", "Input"
|
|
447
|
+
pushHeader("├", "Input");
|
|
451
448
|
pushBody(this.inputBody, {
|
|
452
449
|
input: true,
|
|
453
450
|
limit: this.maxInputLines,
|
|
454
451
|
continued: true,
|
|
452
|
+
section: "input",
|
|
455
453
|
});
|
|
456
454
|
pushBlankRail();
|
|
457
455
|
this.truncated.output = outputWouldTruncate;
|
|
458
|
-
pushHeader("└", "Output"
|
|
459
|
-
pushBody(outputText, {
|
|
456
|
+
pushHeader("└", "Output");
|
|
457
|
+
pushBody(outputText, {
|
|
458
|
+
limit: this.maxOutputLines,
|
|
459
|
+
continued: false,
|
|
460
|
+
section: "output",
|
|
461
|
+
});
|
|
460
462
|
} else {
|
|
461
463
|
this.truncated.output = outputWouldTruncate;
|
|
462
|
-
pushHeader("└", "Output"
|
|
463
|
-
pushBody(outputText, {
|
|
464
|
+
pushHeader("└", "Output");
|
|
465
|
+
pushBody(outputText, {
|
|
466
|
+
limit: this.maxOutputLines,
|
|
467
|
+
continued: false,
|
|
468
|
+
section: "output",
|
|
469
|
+
});
|
|
464
470
|
}
|
|
465
471
|
|
|
466
472
|
this.cachedWidth = width;
|
|
@@ -663,13 +669,21 @@ export function renderExpandedToolResult(
|
|
|
663
669
|
): ExpandedToolIoView | ExpandedToolResultText | Text {
|
|
664
670
|
const inputBody = formatToolInputArgs(args);
|
|
665
671
|
const outputBody = body;
|
|
666
|
-
const
|
|
672
|
+
const maxOutputLines = config.expandedOutputMaxLines;
|
|
673
|
+
const maxInputLines = config.expandedInputMaxLines;
|
|
667
674
|
|
|
668
675
|
// Prefer structured Input/Output when we have args or non-empty output.
|
|
669
676
|
if (inputBody.trim() || outputBody.trim()) {
|
|
670
677
|
let view: ExpandedToolIoView;
|
|
671
678
|
if (isExpandedToolIoView(lastComponent)) {
|
|
672
|
-
lastComponent.setContent(
|
|
679
|
+
lastComponent.setContent(
|
|
680
|
+
inputBody,
|
|
681
|
+
outputBody,
|
|
682
|
+
isError,
|
|
683
|
+
maxOutputLines,
|
|
684
|
+
maxInputLines,
|
|
685
|
+
flushLeft,
|
|
686
|
+
);
|
|
673
687
|
view = lastComponent;
|
|
674
688
|
} else {
|
|
675
689
|
view = new ExpandedToolIoView(
|
|
@@ -677,8 +691,8 @@ export function renderExpandedToolResult(
|
|
|
677
691
|
inputBody,
|
|
678
692
|
outputBody,
|
|
679
693
|
isError,
|
|
680
|
-
|
|
681
|
-
|
|
694
|
+
maxOutputLines,
|
|
695
|
+
maxInputLines,
|
|
682
696
|
flushLeft,
|
|
683
697
|
);
|
|
684
698
|
}
|
|
@@ -18,13 +18,10 @@ export const COMPACT_THINKING_PATCH_KEY = Symbol.for("pi.ccstyle.compact-thinkin
|
|
|
18
18
|
export const PROTOTYPE_ORIGINAL_KEY = Symbol.for("pi.ccstyle.prototype-original");
|
|
19
19
|
export const COMPACT_THINKING_OWNER = Symbol.for("pi.ccstyle.compact-thinking-owner");
|
|
20
20
|
|
|
21
|
-
// ── assistant
|
|
22
|
-
export const
|
|
23
|
-
export const
|
|
24
|
-
export const
|
|
25
|
-
export const ASSISTANT_SET_EXPANDED_KEY = Symbol.for(ASSISTANT_SET_EXPANDED_DESCRIPTION);
|
|
26
|
-
export const ASSISTANT_TOGGLE_ROUND_KEY = Symbol.for(ASSISTANT_TOGGLE_ROUND_DESCRIPTION);
|
|
27
|
-
export const ASSISTANT_REENTRY_KEY = Symbol.for(ASSISTANT_REENTRY_DESCRIPTION);
|
|
21
|
+
// ── assistant 展开状态(实例标记) ──
|
|
22
|
+
export const ASSISTANT_SET_EXPANDED_KEY = Symbol.for("pi.ccstyle.compact-assistant-set-expanded");
|
|
23
|
+
export const ASSISTANT_TOGGLE_ROUND_KEY = Symbol.for("pi.ccstyle.compact-assistant-toggle-round");
|
|
24
|
+
export const ASSISTANT_REENTRY_KEY = Symbol.for("pi.ccstyle.compact-assistant-reentry");
|
|
28
25
|
|
|
29
26
|
// ── 工具渲染补丁 ──
|
|
30
27
|
export const GLOBAL_TOOL_RENDER_PATCH = Symbol.for("pi.ccstyle.global-tool-render-patch");
|
|
@@ -36,9 +33,6 @@ export const TOOL_EXPANDED_BACKGROUND_PATCH = Symbol.for(
|
|
|
36
33
|
// ── 消息组件补丁 ──
|
|
37
34
|
export const MESSAGE_DISPLAY_PATCH = Symbol.for("pi.ccstyle.message-display-patch");
|
|
38
35
|
|
|
39
|
-
// ── 压缩渲染补丁(/reload 残留 deactivate 用) ──
|
|
40
|
-
export const GLOBAL_COMPACTION_RENDER_PATCH = Symbol.for("pi.ccstyle.compaction-render-patch");
|
|
41
|
-
|
|
42
36
|
// ── 工具分组 ──
|
|
43
37
|
export const TOOL_GROUPING_PATCH_KEY = Symbol.for("pi.ccstyle.tool-grouping-patch");
|
|
44
38
|
export const TOOL_GROUPING_PARENT_KEY = Symbol.for("pi.ccstyle.tool-grouping-parent");
|
|
@@ -53,6 +53,12 @@ export function isSgrLeftPress(packet: SgrMousePacket): boolean {
|
|
|
53
53
|
return packet.final === "M" && baseButton === 0 && (packet.code & 32) === 0;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/** 是否为左键松开。 */
|
|
57
|
+
export function isSgrLeftRelease(packet: SgrMousePacket): boolean {
|
|
58
|
+
const baseButton = mouseBaseButton(packet.code);
|
|
59
|
+
return packet.final === "m" && baseButton === 0 && (packet.code & 32) === 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
/** 剥离修饰键位(4/8/16/32:shift/meta/ctrl/motion),得到基础按键码。 */
|
|
57
63
|
export function mouseBaseButton(code: number): number {
|
|
58
64
|
return code & ~(4 | 8 | 16 | 32);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cc-extensions",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.69",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "A Pi productivity suite with Claude Code-style UI, context inspection, and agent/session references",
|
|
6
6
|
"type": "module",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"test": "node --test tests/**/*.test.ts",
|
|
42
|
-
"typecheck": "
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
43
|
"format": "biome format --write .",
|
|
44
44
|
"format:check": "biome format .",
|
|
45
|
-
"docs:tool-render": "node scripts/generate-tool-render-examples.
|
|
45
|
+
"docs:tool-render": "node --experimental-strip-types scripts/generate-tool-render-examples.ts",
|
|
46
46
|
"lint": "biome check .",
|
|
47
47
|
"preversion": "npm run format && npm run lint && npm run typecheck && npm test && npm run docs:tool-render",
|
|
48
48
|
"release": "npm version patch && npm publish",
|