pi-cc-extensions 0.8.56 → 0.8.59
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 +1 -4
- package/README.md +1 -4
- package/extensions/config/config.ts +40 -9
- package/extensions/config/panel.ts +32 -36
- package/extensions/feature/compact-thinking.ts +12 -12
- package/extensions/feature/context.ts +10 -56
- package/extensions/feature/reference/index.ts +17 -7
- package/extensions/feature/shell/startup-header.ts +1 -1
- package/extensions/feature/shell/working-message.ts +28 -21
- package/extensions/renderer/compact-mode.ts +23 -50
- package/extensions/renderer/default-mode.ts +36 -152
- package/extensions/renderer/index.ts +5 -9
- package/extensions/renderer/markdown-enhance.ts +15 -6
- package/extensions/renderer/mouse/hover.ts +2 -4
- package/extensions/renderer/mouse/interaction.ts +6 -6
- package/extensions/renderer/mouse/packets.ts +16 -66
- package/extensions/renderer/mouse/scroll.ts +13 -7
- package/extensions/renderer/tool/diff/diff-component.ts +107 -0
- package/extensions/renderer/tool/diff/diff-edit-render.ts +198 -0
- package/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
- package/extensions/renderer/tool/diff/diff-inline.ts +312 -0
- package/extensions/renderer/tool/diff/diff-layout.ts +730 -0
- package/extensions/renderer/tool/diff/diff-limits.ts +145 -0
- package/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/extensions/renderer/tool/diff/diff-parse.ts +512 -0
- package/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
- package/extensions/renderer/tool/diff/diff-renderer.ts +21 -3046
- package/extensions/renderer/tool/diff/diff-text.ts +45 -0
- package/extensions/renderer/tool/diff/diff-write-render.ts +498 -0
- package/extensions/renderer/tool/diff/index.ts +2 -2
- package/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
- package/extensions/renderer/tool/grouping.ts +19 -143
- package/extensions/renderer/{message-display.ts → tool/message-display.ts} +9 -30
- package/extensions/renderer/tool/names.ts +168 -0
- package/extensions/renderer/tool/result.ts +3 -6
- package/extensions/renderer/transcript-refresh.ts +8 -26
- package/extensions/utils/ansi-text.ts +47 -0
- package/extensions/utils/component-tree.ts +34 -0
- package/extensions/utils/format.ts +7 -0
- package/extensions/utils/patch-keys.ts +121 -0
- package/extensions/utils/sgr-mouse.ts +59 -0
- package/package.json +3 -2
- package/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/extensions/renderer/tool/diff/types.ts +0 -26
- package/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- /package/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +0 -0
|
@@ -24,8 +24,8 @@ import { config, getToolDisplayConfig } from "../config/config.ts";
|
|
|
24
24
|
import { toolLoadingIcon } from "../utils/tool-loading-icon.ts";
|
|
25
25
|
import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
|
|
26
26
|
import { refreshTranscriptComponent } from "./transcript-refresh.ts";
|
|
27
|
-
import { getMessageDisplayTheme } from "./message-display.ts";
|
|
28
|
-
import { showMoreHintText } from "./show-more-hint.ts";
|
|
27
|
+
import { getMessageDisplayTheme } from "./tool/message-display.ts";
|
|
28
|
+
import { showMoreHintText } from "./tool/show-more-hint.ts";
|
|
29
29
|
import {
|
|
30
30
|
countEditDiffStats,
|
|
31
31
|
countWriteDiffStats,
|
|
@@ -35,7 +35,21 @@ import { renderRichToolResult } from "./tool/diff/index.ts";
|
|
|
35
35
|
import type { WriteExecutionMetadataStore } from "./tool/diff/write-execution.ts";
|
|
36
36
|
import { insetComponent, renderExpandedToolResult, scheduleAnimation } from "./tool/result.ts";
|
|
37
37
|
import { oneLine } from "../utils/format.ts";
|
|
38
|
-
import { paddedBackgroundRow
|
|
38
|
+
import { paddedBackgroundRow } from "./tool/grouping.ts";
|
|
39
|
+
import { hasVisibleText, stripBackgroundAnsi } from "../utils/ansi-text.ts";
|
|
40
|
+
import { walkComponentTree } from "../utils/component-tree.ts";
|
|
41
|
+
import {
|
|
42
|
+
ASSISTANT_REENTRY_DESCRIPTION,
|
|
43
|
+
ASSISTANT_REENTRY_KEY,
|
|
44
|
+
ASSISTANT_SET_EXPANDED_DESCRIPTION,
|
|
45
|
+
ASSISTANT_SET_EXPANDED_KEY,
|
|
46
|
+
ASSISTANT_TOGGLE_ROUND_DESCRIPTION,
|
|
47
|
+
ASSISTANT_TOGGLE_ROUND_KEY,
|
|
48
|
+
COMPACT_MODE_PATCH_KEY,
|
|
49
|
+
COMPACT_THINKING_PATCH_KEY,
|
|
50
|
+
patchRegistry,
|
|
51
|
+
PROTOTYPE_ORIGINAL_KEY,
|
|
52
|
+
} from "../utils/patch-keys.ts";
|
|
39
53
|
|
|
40
54
|
/** compact 渲染层对 compact-thinking 的只读查询面(不建第二套计时器)。 */
|
|
41
55
|
export type CompactThinkingQuery = {
|
|
@@ -45,16 +59,6 @@ export type CompactThinkingQuery = {
|
|
|
45
59
|
setCompactSummaryActive?(active: boolean): void;
|
|
46
60
|
};
|
|
47
61
|
|
|
48
|
-
const COMPACT_MODE_PATCH_KEY = Symbol.for("pi.ccstyle.compact-mode-patch");
|
|
49
|
-
const COMPACT_THINKING_PATCH_KEY = Symbol.for("pi.ccstyle.compact-thinking-update");
|
|
50
|
-
const PROTOTYPE_ORIGINAL_KEY = Symbol.for("pi.ccstyle.prototype-original");
|
|
51
|
-
const ASSISTANT_SET_EXPANDED_DESCRIPTION = "pi.ccstyle.compact-assistant-set-expanded";
|
|
52
|
-
const ASSISTANT_TOGGLE_ROUND_DESCRIPTION = "pi.ccstyle.compact-assistant-toggle-round";
|
|
53
|
-
const ASSISTANT_REENTRY_DESCRIPTION = "pi.ccstyle.compact-assistant-reentry";
|
|
54
|
-
const ASSISTANT_SET_EXPANDED_KEY = Symbol.for(ASSISTANT_SET_EXPANDED_DESCRIPTION);
|
|
55
|
-
const ASSISTANT_TOGGLE_ROUND_KEY = Symbol.for(ASSISTANT_TOGGLE_ROUND_DESCRIPTION);
|
|
56
|
-
const ASSISTANT_REENTRY_KEY = Symbol.for(ASSISTANT_REENTRY_DESCRIPTION);
|
|
57
|
-
|
|
58
62
|
const EDIT_WRITE_TOOLS = new Set(["edit", "write"]);
|
|
59
63
|
|
|
60
64
|
type CompactThinkingTheme = Pick<Theme, "fg" | "italic" | "bold">;
|
|
@@ -284,16 +288,6 @@ function darkenBgAnsi(theme: any, slot: string): string {
|
|
|
284
288
|
return `\x1b[48;2;${r};${g};${b}m`;
|
|
285
289
|
}
|
|
286
290
|
|
|
287
|
-
/** 去掉行内所有 CSI/OSC 序列后是否有可见文本(判断工具卡首尾内容行)。 */
|
|
288
|
-
function hasVisibleText(line: string): boolean {
|
|
289
|
-
return (
|
|
290
|
-
line
|
|
291
|
-
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
|
|
292
|
-
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
|
|
293
|
-
.trim().length > 0
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
291
|
/** 工具卡深色行:左右内缩(左 2 右 3 格,含工具卡自身 padding),背景只到内容区,
|
|
298
292
|
* 修复行尾 reset 截断。 */
|
|
299
293
|
function toolCardBgRow(
|
|
@@ -314,7 +308,7 @@ function toolCardBgRow(
|
|
|
314
308
|
const body = ` ${clipped}${" ".repeat(pad)} `;
|
|
315
309
|
const stable = body.replace(/\x1b\[(?:0)?m/g, (reset) => reset + bgAnsi);
|
|
316
310
|
const outerBg = typeof theme?.getBgAnsi === "function" ? String(theme.getBgAnsi(slot)) : "";
|
|
317
|
-
const inset = (n) => (outerBg ? `${outerBg}${" ".repeat(n)}\x1b[49m` : " ".repeat(n));
|
|
311
|
+
const inset = (n: number) => (outerBg ? `${outerBg}${" ".repeat(n)}\x1b[49m` : " ".repeat(n));
|
|
318
312
|
return `${inset(leftInset)}${bgAnsi}${stable}\x1b[49m${inset(rightInset)}`;
|
|
319
313
|
}
|
|
320
314
|
/** edit/write 展开卡:保持原样式(userMessageBg Box),不应用工具卡深色/间距改动。 */
|
|
@@ -694,16 +688,9 @@ function ensureAssistantSetExpanded(component: any): void {
|
|
|
694
688
|
|
|
695
689
|
function collectMountedComponents(root: any): void {
|
|
696
690
|
if (!root || typeof root !== "object") return;
|
|
697
|
-
const seen = new Set<any>();
|
|
698
691
|
const assistants = new Set<any>();
|
|
699
692
|
const tools = new Set<any>();
|
|
700
|
-
|
|
701
|
-
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
702
|
-
seen.add(value);
|
|
703
|
-
if (Array.isArray(value)) {
|
|
704
|
-
for (const child of value) visit(child);
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
693
|
+
walkComponentTree(root, (value: any) => {
|
|
707
694
|
if (isAssistantComponent(value)) {
|
|
708
695
|
assistants.add(value);
|
|
709
696
|
// 仅在 compact 模式给实例装 setExpanded,避免 on/off 下 Ctrl+O/mouse 回归。
|
|
@@ -711,20 +698,7 @@ function collectMountedComponents(root: any): void {
|
|
|
711
698
|
} else if (isToolComponent(value)) {
|
|
712
699
|
tools.add(value);
|
|
713
700
|
}
|
|
714
|
-
|
|
715
|
-
if (Array.isArray(children)) {
|
|
716
|
-
for (const child of children) visit(child);
|
|
717
|
-
}
|
|
718
|
-
try {
|
|
719
|
-
const mounted = value.getMountedRoots?.();
|
|
720
|
-
if (Array.isArray(mounted)) {
|
|
721
|
-
for (const root2 of mounted) visit(root2);
|
|
722
|
-
}
|
|
723
|
-
} catch {
|
|
724
|
-
// renderer 切换中的惰性 Proxy 可能暂时没有 mounted roots
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
visit(root);
|
|
701
|
+
});
|
|
728
702
|
// 扫到组件才替换跟踪表。面板/custom UI 打开时 root 往往扫不到 transcript,
|
|
729
703
|
// 若此时清空会丢掉 live updateContent/updateDisplay 已登记的实例,
|
|
730
704
|
// /ccstyle 切换就只能靠 /reload 重建。
|
|
@@ -739,8 +713,7 @@ function collectMountedComponents(root: any): void {
|
|
|
739
713
|
}
|
|
740
714
|
|
|
741
715
|
export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHooks {
|
|
742
|
-
const
|
|
743
|
-
const previous = host[COMPACT_MODE_PATCH_KEY] as CompactModePatch | undefined;
|
|
716
|
+
const previous = patchRegistry.get<CompactModePatch>(COMPACT_MODE_PATCH_KEY);
|
|
744
717
|
if (previous) previous.dispose();
|
|
745
718
|
|
|
746
719
|
const assistantPrototype = AssistantMessageComponent.prototype as any;
|
|
@@ -1149,7 +1122,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1149
1122
|
if (toolPrototype.updateDisplay === patch.toolInstalledUpdateDisplay) {
|
|
1150
1123
|
toolPrototype.updateDisplay = patch.toolOriginalUpdateDisplay;
|
|
1151
1124
|
}
|
|
1152
|
-
|
|
1125
|
+
patchRegistry.dispose(COMPACT_MODE_PATCH_KEY, patch);
|
|
1153
1126
|
for (const component of trackedAssistantComponents) detachAssistantExpansion(component);
|
|
1154
1127
|
trackedAssistantComponents.clear();
|
|
1155
1128
|
trackedToolComponents.clear();
|
|
@@ -1160,7 +1133,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
1160
1133
|
assistantPrototype.updateContent = patch.assistantInstalled;
|
|
1161
1134
|
toolPrototype.render = patch.toolInstalledRender;
|
|
1162
1135
|
toolPrototype.updateDisplay = patch.toolInstalledUpdateDisplay;
|
|
1163
|
-
|
|
1136
|
+
patchRegistry.install(COMPACT_MODE_PATCH_KEY, patch);
|
|
1164
1137
|
|
|
1165
1138
|
const syncGlobalExpanded = (ctx: any): void => {
|
|
1166
1139
|
let globalExpanded = false;
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
import { ToolExecutionComponent } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Text, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
9
9
|
import { config, getToolDisplayConfig, type CompactStyleMode } from "../config/config.ts";
|
|
10
|
+
import {
|
|
11
|
+
COMPONENT_TOOL_RENDER_MODE,
|
|
12
|
+
GLOBAL_TOOL_RENDER_PATCH,
|
|
13
|
+
patchRegistry,
|
|
14
|
+
TOOL_EXPANDED_BACKGROUND_PATCH,
|
|
15
|
+
} from "../utils/patch-keys.ts";
|
|
10
16
|
import { isToolCallHovered } from "./mouse/hover.ts";
|
|
11
17
|
import {
|
|
12
18
|
countLines,
|
|
@@ -27,25 +33,15 @@ import {
|
|
|
27
33
|
toolViewportWidth,
|
|
28
34
|
} from "./tool/result.ts";
|
|
29
35
|
import { oneLine } from "../utils/format.ts";
|
|
30
|
-
import { showMoreHintText } from "./show-more-hint.ts";
|
|
36
|
+
import { showMoreHintText } from "./tool/show-more-hint.ts";
|
|
31
37
|
import { renderRichToolResult, type WriteExecutionMetadataStore } from "./tool/diff/index.ts";
|
|
32
|
-
import { getMessageDisplayTheme } from "./message-display.ts";
|
|
38
|
+
import { getMessageDisplayTheme } from "./tool/message-display.ts";
|
|
39
|
+
import { humanizeToolLabel, toolCallSummary } from "./tool/names.ts";
|
|
33
40
|
|
|
34
41
|
// 成功勾:亮绿 truecolor(与 message-display 一致)
|
|
35
42
|
const BRIGHT_GREEN = "\x1b[38;2;80;220;100m";
|
|
36
43
|
const ANSI_FG_RESET = "\x1b[39m";
|
|
37
44
|
|
|
38
|
-
const GLOBAL_TOOL_RENDER_PATCH = Symbol.for("pi.ccstyle.global-tool-render-patch");
|
|
39
|
-
const COMPONENT_TOOL_RENDER_MODE = Symbol.for("pi.ccstyle.component-tool-render-mode");
|
|
40
|
-
const COMPONENT_TOOL_SELF_SHELL_MODE = Symbol.for("pi.ccstyle.component-tool-self-shell-mode");
|
|
41
|
-
const TOOL_EXPANDED_BACKGROUND_PATCH = Symbol.for("pi.ccstyle.tool-expanded-background-patch");
|
|
42
|
-
|
|
43
|
-
const AGENT_FAMILY_TOOL_NAMES = new Set([
|
|
44
|
-
"Agent",
|
|
45
|
-
"Agents",
|
|
46
|
-
"get_subagent_result",
|
|
47
|
-
"steer_subagent",
|
|
48
|
-
]);
|
|
49
45
|
// pi-subagents 等扩展为 Agent 提供专用渲染器,ccstyle 必须保留。
|
|
50
46
|
const DEDICATED_RENDERER_TOOLS = new Set(["Agent"]);
|
|
51
47
|
|
|
@@ -141,94 +137,6 @@ function renderDefault(tool: any, slot: "renderCall" | "renderResult", args: any
|
|
|
141
137
|
return new Text(fallback, 0, 0);
|
|
142
138
|
}
|
|
143
139
|
|
|
144
|
-
function humanizeToolLabel(label: string): string {
|
|
145
|
-
return label
|
|
146
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
147
|
-
.replace(/[_-]+/g, " ")
|
|
148
|
-
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function singleToolCallSummary(
|
|
152
|
-
toolName: string,
|
|
153
|
-
label: string,
|
|
154
|
-
args: any,
|
|
155
|
-
): { main: string; detail: string } {
|
|
156
|
-
const title = label === toolName ? humanizeToolLabel(label) : label;
|
|
157
|
-
if (!args || typeof args !== "object") return { main: title, detail: "" };
|
|
158
|
-
const name = toolName.toLowerCase();
|
|
159
|
-
const value = (fallback: string, ...keys: string[]) => {
|
|
160
|
-
const found = keys.map((key) => args[key]).find((item) => typeof item === "string" && item);
|
|
161
|
-
// 与多 tool 摘要对齐:从头截断,上限 96 字符(tool-grouping oneLine 默认值)。
|
|
162
|
-
return `${title} ${oneLine(found || fallback, 96)}`;
|
|
163
|
-
};
|
|
164
|
-
if (AGENT_FAMILY_TOOL_NAMES.has(toolName) && args.agent_id) {
|
|
165
|
-
return { main: `${title} ${oneLine(args.agent_id, 96)}`, detail: "" };
|
|
166
|
-
}
|
|
167
|
-
// Agents 走 ccstyle wrapper;Agent 保留专用渲染器。
|
|
168
|
-
if (name === "agents") {
|
|
169
|
-
return {
|
|
170
|
-
main: value("launch agents", "description", "prompt"),
|
|
171
|
-
detail: "",
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (name === "skill") return { main: value("run skill", "name"), detail: "" };
|
|
175
|
-
if (name === "enterplanmode" || name === "enter_plan_mode") {
|
|
176
|
-
return { main: `${title} enable read-only planning`, detail: "" };
|
|
177
|
-
}
|
|
178
|
-
if (name === "exitplanmode" || name === "exit_plan_mode") {
|
|
179
|
-
return { main: `${title} present plan`, detail: "" };
|
|
180
|
-
}
|
|
181
|
-
if (name === "taskcreate") return { main: value("create task", "subject"), detail: "" };
|
|
182
|
-
if (name === "tasklist") return { main: `${title} task list`, detail: "" };
|
|
183
|
-
if (name === "taskget" || name === "taskupdate") {
|
|
184
|
-
return { main: value("task", "taskId", "task_id"), detail: "" };
|
|
185
|
-
}
|
|
186
|
-
if (name === "taskoutput" || name === "taskstop") {
|
|
187
|
-
return { main: value("background task", "task_id", "taskId"), detail: "" };
|
|
188
|
-
}
|
|
189
|
-
if (name === "taskexecute") {
|
|
190
|
-
const ids = Array.isArray(args.task_ids)
|
|
191
|
-
? args.task_ids
|
|
192
|
-
: Array.isArray(args.taskIds)
|
|
193
|
-
? args.taskIds
|
|
194
|
-
: [];
|
|
195
|
-
const summary = ids.length
|
|
196
|
-
? `${ids[0]}${ids.length > 1 ? ` (+${ids.length - 1} tasks)` : ""}`
|
|
197
|
-
: "start tasks";
|
|
198
|
-
return { main: `${title} ${summary}`, detail: "" };
|
|
199
|
-
}
|
|
200
|
-
if (toolName === "read") {
|
|
201
|
-
const details = [
|
|
202
|
-
args.offset !== undefined ? `offset=${args.offset}` : "",
|
|
203
|
-
args.limit !== undefined ? `limit=${args.limit}` : "",
|
|
204
|
-
].filter(Boolean);
|
|
205
|
-
return {
|
|
206
|
-
main: `${title}${args.path ? ` ${oneLine(args.path, 96)}` : ""}`,
|
|
207
|
-
detail: details.length ? ` (${details.join(", ")})` : "",
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
const preferred =
|
|
211
|
-
args.path ??
|
|
212
|
-
args.file_path ??
|
|
213
|
-
args.command ??
|
|
214
|
-
args.query ??
|
|
215
|
-
args.question ??
|
|
216
|
-
args.pattern ??
|
|
217
|
-
args.url ??
|
|
218
|
-
args.name ??
|
|
219
|
-
args.tool_use_id ??
|
|
220
|
-
args.toolCallId ??
|
|
221
|
-
args.id ??
|
|
222
|
-
args.message;
|
|
223
|
-
return {
|
|
224
|
-
main:
|
|
225
|
-
preferred !== undefined && preferred !== null && typeof preferred !== "object"
|
|
226
|
-
? `${title} ${oneLine(preferred, 96)}`
|
|
227
|
-
: title,
|
|
228
|
-
detail: "",
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
140
|
type ParsedTask = { id: string; status: string; subject: string };
|
|
233
141
|
|
|
234
142
|
function parseTaskList(text: string): ParsedTask[] {
|
|
@@ -330,7 +238,10 @@ function createCcstyleTool(
|
|
|
330
238
|
visualState === "success"
|
|
331
239
|
? `${BRIGHT_GREEN}${rawIcon}${ANSI_FG_RESET}`
|
|
332
240
|
: theme.fg(toolIconColor(context), rawIcon);
|
|
333
|
-
const summary =
|
|
241
|
+
const summary = toolCallSummary(toolName, args, {
|
|
242
|
+
title: label === toolName ? humanizeToolLabel(label) : label,
|
|
243
|
+
variant: "default",
|
|
244
|
+
});
|
|
334
245
|
let cachedWidth: number | undefined;
|
|
335
246
|
let cachedLine: string | undefined;
|
|
336
247
|
const expanded = Boolean(context?.expanded);
|
|
@@ -485,11 +396,6 @@ function shouldGloballyStyleTool(component: any, patch: GlobalToolRenderPatch):
|
|
|
485
396
|
return useCcstyle;
|
|
486
397
|
}
|
|
487
398
|
|
|
488
|
-
function shouldUseSelfShell(component: any, _patch: GlobalToolRenderPatch): boolean {
|
|
489
|
-
component[COMPONENT_TOOL_SELF_SHELL_MODE] = false;
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
399
|
function getGloballyStyledTool(component: any, patch: GlobalToolRenderPatch): any {
|
|
494
400
|
const definition = component.toolDefinition ?? component.builtInToolDefinition;
|
|
495
401
|
if (definition && typeof definition === "object") {
|
|
@@ -527,21 +433,6 @@ function isOwnershipAwarePatch(value: any): value is GlobalToolRenderPatch {
|
|
|
527
433
|
);
|
|
528
434
|
}
|
|
529
435
|
|
|
530
|
-
function isLegacyInstalledWrapper(method: unknown, downstreamField: string): boolean {
|
|
531
|
-
if (typeof method !== "function") return false;
|
|
532
|
-
try {
|
|
533
|
-
const source = Function.prototype.toString.call(method);
|
|
534
|
-
return (
|
|
535
|
-
source.includes(downstreamField) &&
|
|
536
|
-
(source.includes("shouldGloballyStyleTool") ||
|
|
537
|
-
source.includes("shouldUseSelfShell") ||
|
|
538
|
-
source.includes("getGloballyStyledTool"))
|
|
539
|
-
);
|
|
540
|
-
} catch {
|
|
541
|
-
return false;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
436
|
function downstreamForGlobalToolInstall(prototype: any, previous: any): ToolRenderMethods {
|
|
546
437
|
const current = prototypeToolRenderMethods(prototype);
|
|
547
438
|
if (!previous || previous.prototype !== prototype) return current;
|
|
@@ -566,27 +457,26 @@ function downstreamForGlobalToolInstall(prototype: any, previous: any): ToolRend
|
|
|
566
457
|
};
|
|
567
458
|
}
|
|
568
459
|
|
|
569
|
-
// pre-v2 Symbol
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
};
|
|
460
|
+
// pre-v2 Symbol:originalX 显式记录真实下游(native 原型方法),无需源码嗅探。
|
|
461
|
+
// 缺失字段回退到当前原型方法(视作 external)。
|
|
462
|
+
const originalOrCurrent = (field: string, method: Function): Function =>
|
|
463
|
+
typeof previous[field] === "function" ? previous[field] : method;
|
|
574
464
|
return {
|
|
575
|
-
hasRendererDefinition:
|
|
576
|
-
current.hasRendererDefinition,
|
|
465
|
+
hasRendererDefinition: originalOrCurrent(
|
|
577
466
|
"originalHasRendererDefinition",
|
|
467
|
+
current.hasRendererDefinition,
|
|
578
468
|
) as ToolRenderMethods["hasRendererDefinition"],
|
|
579
|
-
getRenderShell:
|
|
580
|
-
current.getRenderShell,
|
|
469
|
+
getRenderShell: originalOrCurrent(
|
|
581
470
|
"originalGetRenderShell",
|
|
471
|
+
current.getRenderShell,
|
|
582
472
|
) as ToolRenderMethods["getRenderShell"],
|
|
583
|
-
getCallRenderer:
|
|
584
|
-
current.getCallRenderer,
|
|
473
|
+
getCallRenderer: originalOrCurrent(
|
|
585
474
|
"originalGetCallRenderer",
|
|
475
|
+
current.getCallRenderer,
|
|
586
476
|
) as ToolRenderMethods["getCallRenderer"],
|
|
587
|
-
getResultRenderer:
|
|
588
|
-
current.getResultRenderer,
|
|
477
|
+
getResultRenderer: originalOrCurrent(
|
|
589
478
|
"originalGetResultRenderer",
|
|
479
|
+
current.getResultRenderer,
|
|
590
480
|
) as ToolRenderMethods["getResultRenderer"],
|
|
591
481
|
};
|
|
592
482
|
}
|
|
@@ -606,8 +496,7 @@ function installGlobalToolRendering(
|
|
|
606
496
|
writeExecutionMetadata: WriteExecutionMetadataStore,
|
|
607
497
|
): GlobalToolRenderPatch {
|
|
608
498
|
const prototype = (ToolExecutionComponent as any).prototype;
|
|
609
|
-
const
|
|
610
|
-
const previous = host[GLOBAL_TOOL_RENDER_PATCH];
|
|
499
|
+
const previous = patchRegistry.get<GlobalToolRenderPatch>(GLOBAL_TOOL_RENDER_PATCH);
|
|
611
500
|
const downstream = downstreamForGlobalToolInstall(prototype, previous);
|
|
612
501
|
// 外部仍持有的旧 wrapper 先变为无回调 pass-through,再挂新安装。
|
|
613
502
|
disconnectGlobalToolRenderPatch(previous);
|
|
@@ -637,14 +526,12 @@ function installGlobalToolRendering(
|
|
|
637
526
|
},
|
|
638
527
|
getRenderShell: function (this: any, ...args: any[]) {
|
|
639
528
|
if (!patch.active) return patch.downstream.getRenderShell.apply(this, args);
|
|
640
|
-
const useSelfShell = shouldUseSelfShell(this, patch);
|
|
641
529
|
const useCcstyle = shouldGloballyStyleTool(this, patch);
|
|
642
|
-
const shell =
|
|
643
|
-
|
|
644
|
-
? "
|
|
645
|
-
:
|
|
646
|
-
|
|
647
|
-
: patch.downstream.getRenderShell.apply(this, args);
|
|
530
|
+
const shell = useCcstyle
|
|
531
|
+
? this.expanded
|
|
532
|
+
? "default"
|
|
533
|
+
: "self"
|
|
534
|
+
: patch.downstream.getRenderShell.apply(this, args);
|
|
648
535
|
syncToolShell(this, shell);
|
|
649
536
|
return shell;
|
|
650
537
|
},
|
|
@@ -666,7 +553,7 @@ function installGlobalToolRendering(
|
|
|
666
553
|
prototype.getRenderShell = patch.installed.getRenderShell;
|
|
667
554
|
prototype.getCallRenderer = patch.installed.getCallRenderer;
|
|
668
555
|
prototype.getResultRenderer = patch.installed.getResultRenderer;
|
|
669
|
-
|
|
556
|
+
patchRegistry.install(GLOBAL_TOOL_RENDER_PATCH, patch);
|
|
670
557
|
return patch;
|
|
671
558
|
}
|
|
672
559
|
|
|
@@ -691,8 +578,7 @@ function deactivateGlobalToolRendering(patch: GlobalToolRenderPatch): void {
|
|
|
691
578
|
/** 展开面板背景统一为 user message 背景色;折叠行保持原生状态色。
|
|
692
579
|
* 必须在 compact-mode 之后安装,shutdown 时先于 compact-mode 释放。 */
|
|
693
580
|
export function installToolExpandedBackground(): () => void {
|
|
694
|
-
const
|
|
695
|
-
const previous = host[TOOL_EXPANDED_BACKGROUND_PATCH] as ToolExpandedBackgroundPatch | undefined;
|
|
581
|
+
const previous = patchRegistry.get<ToolExpandedBackgroundPatch>(TOOL_EXPANDED_BACKGROUND_PATCH);
|
|
696
582
|
if (previous) previous.dispose();
|
|
697
583
|
const prototype = ToolExecutionComponent.prototype as unknown as { updateDisplay: () => void };
|
|
698
584
|
const original = prototype.updateDisplay;
|
|
@@ -718,13 +604,11 @@ export function installToolExpandedBackground(): () => void {
|
|
|
718
604
|
if (prototype.updateDisplay === patch.installed) {
|
|
719
605
|
prototype.updateDisplay = original;
|
|
720
606
|
}
|
|
721
|
-
|
|
722
|
-
delete host[TOOL_EXPANDED_BACKGROUND_PATCH];
|
|
723
|
-
}
|
|
607
|
+
patchRegistry.dispose(TOOL_EXPANDED_BACKGROUND_PATCH, patch);
|
|
724
608
|
},
|
|
725
609
|
};
|
|
726
610
|
prototype.updateDisplay = patch.installed;
|
|
727
|
-
|
|
611
|
+
patchRegistry.install(TOOL_EXPANDED_BACKGROUND_PATCH, patch);
|
|
728
612
|
return patch.dispose;
|
|
729
613
|
}
|
|
730
614
|
|
|
@@ -736,7 +620,7 @@ export function installDefaultMode(
|
|
|
736
620
|
return {
|
|
737
621
|
isOwner() {
|
|
738
622
|
return (
|
|
739
|
-
(
|
|
623
|
+
patchRegistry.owns(GLOBAL_TOOL_RENDER_PATCH, globalToolRendering) &&
|
|
740
624
|
globalToolRendering.active
|
|
741
625
|
);
|
|
742
626
|
},
|
|
@@ -14,12 +14,13 @@ 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";
|
|
17
18
|
import {
|
|
18
19
|
config,
|
|
19
20
|
formatConfigStatus,
|
|
20
21
|
normalizeConfig,
|
|
21
|
-
saveConfig,
|
|
22
22
|
setConfig,
|
|
23
|
+
updateConfig,
|
|
23
24
|
type CompactStyleMode,
|
|
24
25
|
type Config,
|
|
25
26
|
} from "../config/config.ts";
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
installMessageDisplayRendering,
|
|
39
40
|
refreshMessageDisplays,
|
|
40
41
|
setMessageDisplayTheme,
|
|
41
|
-
} from "./message-display.ts";
|
|
42
|
+
} from "./tool/message-display.ts";
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Claude Code Style for pi — 装配入口。
|
|
@@ -48,8 +49,6 @@ import {
|
|
|
48
49
|
* 共用 → tool-grouping / message-display / mouse / write override
|
|
49
50
|
*/
|
|
50
51
|
|
|
51
|
-
const GLOBAL_COMPACTION_RENDER_PATCH = Symbol.for("pi.ccstyle.compaction-render-patch");
|
|
52
|
-
|
|
53
52
|
let compactModeHooks: CompactModeHooks | undefined;
|
|
54
53
|
|
|
55
54
|
function refreshCurrentTranscript(ctx?: any, toolGrouping?: ToolGroupingHooks): void {
|
|
@@ -68,8 +67,7 @@ function syncCompactMode(ctx: any): void {
|
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
function applyStyleMode(mode: CompactStyleMode, ctx: any, toolGrouping?: ToolGroupingHooks): void {
|
|
71
|
-
|
|
72
|
-
saveConfig();
|
|
70
|
+
updateConfig({ mode });
|
|
73
71
|
if (mode === "off") {
|
|
74
72
|
// Native rendering:清 hover/click,关闭鼠标上报以恢复终端默认滚轮。
|
|
75
73
|
resetToolHoverState();
|
|
@@ -101,9 +99,7 @@ type LegacyCompactionRenderPatch = {
|
|
|
101
99
|
|
|
102
100
|
/** Disable the pre-native compaction monkey patch left alive by /reload. */
|
|
103
101
|
function deactivateLegacyCompactionRendering() {
|
|
104
|
-
const patch = (
|
|
105
|
-
| LegacyCompactionRenderPatch
|
|
106
|
-
| undefined;
|
|
102
|
+
const patch = patchRegistry.get<LegacyCompactionRenderPatch>(GLOBAL_COMPACTION_RENDER_PATCH);
|
|
107
103
|
if (patch) patch.enabled = () => false;
|
|
108
104
|
}
|
|
109
105
|
|
|
@@ -71,7 +71,7 @@ function renderAdmonition(lines: string[], i: number): { output: string[]; next:
|
|
|
71
71
|
while (body.length > 0 && body[body.length - 1].trim() === "") body.pop();
|
|
72
72
|
// 内容合并为一行,作为引用块正文
|
|
73
73
|
const content = body.join(" ").trim();
|
|
74
|
-
//
|
|
74
|
+
// 保留引用块形态(pi 渲染为左侧竖线),标签加粗
|
|
75
75
|
return {
|
|
76
76
|
output: [`> **${style.icon} ${style.label}**${content ? ` ${content}` : ""}`, ""],
|
|
77
77
|
next: j,
|
|
@@ -82,14 +82,23 @@ function renderAdmonition(lines: string[], i: number): { output: string[]; next:
|
|
|
82
82
|
// 裸 URL 转可点击超链接
|
|
83
83
|
// ============================================================================
|
|
84
84
|
|
|
85
|
-
const URL_RE = /(?<!<)(?<!\]\()https?:\/\/[^\s<>'"
|
|
86
|
-
const TRIM_URL_RE = /[.,;:!?】」』"'
|
|
85
|
+
const URL_RE = /(?<!<)(?<!\]\()https?:\/\/[^\s<>'"|,。;:!?、」』】()【】《》『』「」]+/g;
|
|
86
|
+
const TRIM_URL_RE = /[.,;:!?】」』"'》)}]+$/;
|
|
87
87
|
|
|
88
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* 去掉 URL 尾部标点;括号按平衡保留(如 Wikipedia 链接含括号、
|
|
90
|
+
* IPv6 地址含 [::1]),尾部不成对的 ] 与 ) 会截掉。
|
|
91
|
+
*/
|
|
89
92
|
function trimUrl(url: string): string {
|
|
90
93
|
let t = url.replace(TRIM_URL_RE, "");
|
|
91
|
-
|
|
92
|
-
t
|
|
94
|
+
for (;;) {
|
|
95
|
+
if (t.endsWith(")") && (t.match(/\(/g)?.length ?? 0) < (t.match(/\)/g)?.length ?? 0)) {
|
|
96
|
+
t = t.slice(0, -1);
|
|
97
|
+
} else if (t.endsWith("]") && (t.match(/\[/g)?.length ?? 0) < (t.match(/\]/g)?.length ?? 0)) {
|
|
98
|
+
t = t.slice(0, -1);
|
|
99
|
+
} else {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
93
102
|
}
|
|
94
103
|
return t;
|
|
95
104
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { setHoveredCompactAssistant } from "../compact-mode.ts";
|
|
2
|
+
import { patchRegistry, TOOL_HOVER_STATE_KEY } from "../../utils/patch-keys.ts";
|
|
2
3
|
import {
|
|
3
4
|
isExpandedToolIoView,
|
|
4
5
|
invalidateIoView,
|
|
@@ -9,8 +10,6 @@ import type { ToolGroupComponent } from "../tool/grouping.ts";
|
|
|
9
10
|
import { componentAtLocalRow, type ComponentRowHit } from "./layout.ts";
|
|
10
11
|
import { setScrollButtonHovered } from "./scroll.ts";
|
|
11
12
|
|
|
12
|
-
const TOOL_HOVER_STATE_KEY = Symbol.for("pi.ccstyle.tool-hover-state");
|
|
13
|
-
|
|
14
13
|
type SharedToolHoverState = { toolCallId: string | null };
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -18,8 +17,7 @@ type SharedToolHoverState = { toolCallId: string | null };
|
|
|
18
17
|
* 测试依赖该跨实例语义。
|
|
19
18
|
*/
|
|
20
19
|
export function sharedToolHoverState(): SharedToolHoverState {
|
|
21
|
-
|
|
22
|
-
return (host[TOOL_HOVER_STATE_KEY] ??= { toolCallId: null });
|
|
20
|
+
return patchRegistry.ensure(TOOL_HOVER_STATE_KEY, () => ({ toolCallId: null }));
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
// 状态单源在 globalThis 槽;hoveredToolCallId 变量仅为兼容旧 deep import 保留
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { hasActiveTextPreview, showTextPreview } from "../../feature/context.ts";
|
|
2
|
+
import { patchRegistry, TOOL_MOUSE_OWNER_KEY } from "../../utils/patch-keys.ts";
|
|
2
3
|
import { ToolGroupComponent } from "../tool/grouping.ts";
|
|
3
4
|
import { isCompactAssistantComponent, setHoveredCompactAssistant } from "../compact-mode.ts";
|
|
4
5
|
import { config } from "../../config/config.ts";
|
|
5
6
|
import { isLazyProxyTui } from "../../utils/fullscreen-detect.ts";
|
|
6
|
-
import { setToolTuiFullscreen } from "../show-more-hint.ts";
|
|
7
|
+
import { setToolTuiFullscreen } from "../tool/show-more-hint.ts";
|
|
7
8
|
import {
|
|
8
9
|
type ExpandedToolIoView,
|
|
9
10
|
getActiveIoViewFrame,
|
|
@@ -81,7 +82,6 @@ const TOOL_MOUSE_WIDGET_KEY = "ccstyle-tool-mouse";
|
|
|
81
82
|
const TOOL_MOUSE_MOTION_ENABLE = "\x1b[?1003h\x1b[?1006h";
|
|
82
83
|
const TOOL_MOUSE_MOTION_DISABLE = "\x1b[?1003l";
|
|
83
84
|
const FULLSCREEN_MOTION_ENABLED = Symbol("ccstyle.fullscreen-motion-enabled");
|
|
84
|
-
const TOOL_MOUSE_OWNER_KEY = Symbol.for("pi.ccstyle.tool-mouse-owner");
|
|
85
85
|
const DEFAULT_TOOL_MOUSE_OWNER = {};
|
|
86
86
|
export const TOOL_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1003l\x1b[?1000l";
|
|
87
87
|
|
|
@@ -768,8 +768,8 @@ function handleToolMouseInput(data: string): { consume: true } | undefined {
|
|
|
768
768
|
export function teardownToolMouseInteraction(
|
|
769
769
|
owner: object = toolMouseInstallationOwner ?? DEFAULT_TOOL_MOUSE_OWNER,
|
|
770
770
|
): void {
|
|
771
|
-
const
|
|
772
|
-
if (
|
|
771
|
+
const current = patchRegistry.get<object>(TOOL_MOUSE_OWNER_KEY);
|
|
772
|
+
if (current && current !== owner) return;
|
|
773
773
|
if (sessionRenderTimer) {
|
|
774
774
|
clearTimeout(sessionRenderTimer);
|
|
775
775
|
sessionRenderTimer = null;
|
|
@@ -796,7 +796,7 @@ export function teardownToolMouseInteraction(
|
|
|
796
796
|
resetScrollButtonState();
|
|
797
797
|
setToolMouseTui(null);
|
|
798
798
|
toolMouseUi = null;
|
|
799
|
-
|
|
799
|
+
patchRegistry.dispose(TOOL_MOUSE_OWNER_KEY, owner);
|
|
800
800
|
if (toolMouseInstallationOwner === owner) toolMouseInstallationOwner = null;
|
|
801
801
|
}
|
|
802
802
|
|
|
@@ -819,7 +819,7 @@ export function installToolMouseInteraction(
|
|
|
819
819
|
return;
|
|
820
820
|
|
|
821
821
|
toolMouseInstallationOwner = owner;
|
|
822
|
-
(
|
|
822
|
+
patchRegistry.install(TOOL_MOUSE_OWNER_KEY, owner);
|
|
823
823
|
setHoveredToolCallId(null);
|
|
824
824
|
toolMouseUi = ctx.ui;
|
|
825
825
|
// 0.84+ 的 tui 是惰性 Proxy:regular 保留原生 scrollback;fullscreen
|