pi-cc-extensions 0.8.53 → 0.8.55
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 +12 -17
- package/README.md +18 -17
- package/assets/readme/hero.en.svg +101 -0
- package/extensions/config/config.ts +25 -17
- package/extensions/config/panel.ts +100 -4
- package/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +39 -47
- package/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
- package/extensions/feature/compact-thinking.ts +8 -3
- package/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
- package/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +0 -1
- package/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +2 -42
- package/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -11
- package/extensions/index.ts +28 -38
- package/extensions/renderer/compact-mode.ts +394 -101
- package/extensions/renderer/default-mode.ts +747 -0
- package/extensions/renderer/index.ts +58 -775
- package/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +8 -6
- package/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +37 -36
- package/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
- package/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
- package/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +49 -36
- package/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +10 -45
- package/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
- package/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
- package/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +20 -16
- package/extensions/renderer/{tool-result.ts → tool/result.ts} +35 -27
- package/extensions/utils/ansi-color.ts +44 -0
- package/extensions/utils/format.ts +21 -0
- package/extensions/utils/fullscreen-detect.ts +0 -27
- package/package.json +4 -2
- package/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
- /package/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
- /package/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
- /package/extensions/{feature → renderer}/markdown-enhance.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
- /package/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compact mode:每条含 toolCall 的 assistant message 折叠为一条逐步累加的摘要行
|
|
3
|
-
* (`
|
|
3
|
+
* (`Ran for 8s, bash×2, read×2`),edit/write 独立单行(`✓ write <path> (+25 -0)`),
|
|
4
4
|
* 普通工具折叠时不显示独立行;展开(Ctrl+O / fullscreen 点击)在单个工具卡中恢复
|
|
5
5
|
* compact-thinking/Pi 原生或专用 renderer。edit/write 折叠时显示统计,展开时显示 rich diff。
|
|
6
|
+
* Agent/Task 族:调用只进摘要计数,tool 卡始终折叠(避免 pending→完成高度闪动)。
|
|
7
|
+
* 底部 Agents/Tasks 面板由 pi-subagents/pi-tasks 独立 widget 负责,不经 tool 卡外置。
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 工具计数:read 按非空路径去重、其余按调用计数(首次出现顺序);edit/write 不进摘要。
|
|
10
|
+
* 时长 = 回合流逝挂钟;进行中 Running...,结束 Ran for。
|
|
11
|
+
* abort/error/length 状态行挂在摘要外层,避免被折叠吞掉。
|
|
12
|
+
* 最终 agent 回合摘要由 feature/agent-summary 独占(bash/read/edit/write/other)。
|
|
11
13
|
*
|
|
12
14
|
* 补丁生命周期遵循仓库既有模式:Symbol 所有权、dispose 仅恢复仍由本安装持有的
|
|
13
15
|
* 方法、重入守卫防止 /reload 后残留闭包递归。
|
|
@@ -17,8 +19,7 @@ import {
|
|
|
17
19
|
ToolExecutionComponent,
|
|
18
20
|
type Theme,
|
|
19
21
|
} from "@earendil-works/pi-coding-agent";
|
|
20
|
-
import
|
|
21
|
-
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
22
|
+
import { truncateToWidth, visibleWidth, Box } from "@earendil-works/pi-tui";
|
|
22
23
|
import { config, getToolDisplayConfig } from "../config/config.ts";
|
|
23
24
|
import { toolLoadingIcon } from "../utils/tool-loading-icon.ts";
|
|
24
25
|
import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
|
|
@@ -29,15 +30,12 @@ import {
|
|
|
29
30
|
countEditDiffStats,
|
|
30
31
|
countWriteDiffStats,
|
|
31
32
|
isRichDiffComponent,
|
|
32
|
-
} from "./tool
|
|
33
|
-
import { renderRichToolResult } from "./tool
|
|
34
|
-
import type { WriteExecutionMetadataStore } from "./tool
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
renderExpandedToolResult,
|
|
39
|
-
scheduleAnimation,
|
|
40
|
-
} from "./tool-result.ts";
|
|
33
|
+
} from "./tool/diff/diff-renderer.ts";
|
|
34
|
+
import { renderRichToolResult } from "./tool/diff/index.ts";
|
|
35
|
+
import type { WriteExecutionMetadataStore } from "./tool/diff/write-execution.ts";
|
|
36
|
+
import { insetComponent, renderExpandedToolResult, scheduleAnimation } from "./tool/result.ts";
|
|
37
|
+
import { oneLine } from "../utils/format.ts";
|
|
38
|
+
import { paddedBackgroundRow, stripBackgroundAnsi } from "./tool/grouping.ts";
|
|
41
39
|
|
|
42
40
|
/** compact 渲染层对 compact-thinking 的只读查询面(不建第二套计时器)。 */
|
|
43
41
|
export type CompactThinkingQuery = {
|
|
@@ -57,7 +55,6 @@ const ASSISTANT_SET_EXPANDED_KEY = Symbol.for(ASSISTANT_SET_EXPANDED_DESCRIPTION
|
|
|
57
55
|
const ASSISTANT_TOGGLE_ROUND_KEY = Symbol.for(ASSISTANT_TOGGLE_ROUND_DESCRIPTION);
|
|
58
56
|
const ASSISTANT_REENTRY_KEY = Symbol.for(ASSISTANT_REENTRY_DESCRIPTION);
|
|
59
57
|
|
|
60
|
-
const Box = (PiTui as any).Box;
|
|
61
58
|
const EDIT_WRITE_TOOLS = new Set(["edit", "write"]);
|
|
62
59
|
|
|
63
60
|
type CompactThinkingTheme = Pick<Theme, "fg" | "italic" | "bold">;
|
|
@@ -124,26 +121,52 @@ function formatThoughtDuration(durationMs: number) {
|
|
|
124
121
|
|
|
125
122
|
export { formatThoughtDuration };
|
|
126
123
|
|
|
124
|
+
/** assistant stopReason → 外层状态文案(与 Pi 原生口径对齐)。 */
|
|
125
|
+
export function messageStopStatus(message: any): string | undefined {
|
|
126
|
+
const reason = message?.stopReason;
|
|
127
|
+
if (reason === "aborted") {
|
|
128
|
+
return message.errorMessage && message.errorMessage !== "Request was aborted"
|
|
129
|
+
? String(message.errorMessage)
|
|
130
|
+
: "Operation aborted";
|
|
131
|
+
}
|
|
132
|
+
if (reason === "error") {
|
|
133
|
+
return `Error: ${message.errorMessage || "Unknown error"}`;
|
|
134
|
+
}
|
|
135
|
+
if (reason === "length") {
|
|
136
|
+
return "Response was truncated before completion.";
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function roundStopStatus(messages: Iterable<any>): string | undefined {
|
|
142
|
+
for (const message of messages) {
|
|
143
|
+
const status = messageStopStatus(message);
|
|
144
|
+
if (status) return status;
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
127
149
|
/**
|
|
128
150
|
* 逐条 assistant message 的摘要文本(无工具计数时可为空串):
|
|
129
|
-
*
|
|
151
|
+
* 时长 = max(thinking query, durationFloorMs 挂钟);工具首次出现顺序;read 路径去重。
|
|
130
152
|
*/
|
|
131
153
|
function buildMessagesSummary(
|
|
132
154
|
messages: Iterable<any>,
|
|
133
155
|
query?: CompactThinkingQuery,
|
|
134
|
-
|
|
156
|
+
runningActiveOverride?: boolean,
|
|
157
|
+
durationFloorMs?: number,
|
|
135
158
|
): string {
|
|
136
159
|
const parts: string[] = [];
|
|
137
160
|
const counts = new Map<string, number>();
|
|
138
161
|
const readPaths = new Set<string>();
|
|
139
162
|
const durationTimestamps = new Set<number>();
|
|
140
163
|
let durationMs = 0;
|
|
141
|
-
let
|
|
164
|
+
let runningActive = false;
|
|
142
165
|
|
|
143
166
|
for (const message of messages) {
|
|
144
167
|
if (typeof message?.timestamp === "number" && !durationTimestamps.has(message.timestamp)) {
|
|
145
168
|
durationTimestamps.add(message.timestamp);
|
|
146
|
-
if (query?.isMessageThinkingActive?.(message.timestamp))
|
|
169
|
+
if (query?.isMessageThinkingActive?.(message.timestamp)) runningActive = true;
|
|
147
170
|
const value = query?.getMessageThinkingDurationMs(message.timestamp);
|
|
148
171
|
if (typeof value === "number" && Number.isFinite(value) && value > 0) durationMs += value;
|
|
149
172
|
}
|
|
@@ -165,18 +188,28 @@ function buildMessagesSummary(
|
|
|
165
188
|
}
|
|
166
189
|
}
|
|
167
190
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
191
|
+
if (
|
|
192
|
+
typeof durationFloorMs === "number" &&
|
|
193
|
+
Number.isFinite(durationFloorMs) &&
|
|
194
|
+
durationFloorMs > durationMs
|
|
195
|
+
) {
|
|
196
|
+
durationMs = durationFloorMs;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
runningActive = runningActiveOverride ?? runningActive;
|
|
200
|
+
if (runningActive) {
|
|
201
|
+
parts.push(durationMs > 0 ? `Running... · ${formatThoughtDuration(durationMs)}` : "Running...");
|
|
202
|
+
} else if (durationMs > 0) parts.push(`Ran for ${formatThoughtDuration(durationMs)}`);
|
|
174
203
|
for (const [name, count] of counts) parts.push(`${name}×${count}`);
|
|
175
204
|
return parts.join(", ");
|
|
176
205
|
}
|
|
177
206
|
|
|
178
|
-
export function buildMessageSummary(
|
|
179
|
-
|
|
207
|
+
export function buildMessageSummary(
|
|
208
|
+
message: any,
|
|
209
|
+
query?: CompactThinkingQuery,
|
|
210
|
+
durationFloorMs?: number,
|
|
211
|
+
): string {
|
|
212
|
+
return buildMessagesSummary([message], query, undefined, durationFloorMs);
|
|
180
213
|
}
|
|
181
214
|
|
|
182
215
|
function fallbackTheme(): any {
|
|
@@ -191,7 +224,101 @@ function themeOf(): any {
|
|
|
191
224
|
return getMessageDisplayTheme() ?? fallbackTheme();
|
|
192
225
|
}
|
|
193
226
|
|
|
194
|
-
|
|
227
|
+
/** RGB → HSL(h∈[0,1), l/s∈[0,1])。 */
|
|
228
|
+
function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
|
|
229
|
+
r /= 255;
|
|
230
|
+
g /= 255;
|
|
231
|
+
b /= 255;
|
|
232
|
+
const max = Math.max(r, g, b);
|
|
233
|
+
const min = Math.min(r, g, b);
|
|
234
|
+
const l = (max + min) / 2;
|
|
235
|
+
if (max === min) return [0, 0, l];
|
|
236
|
+
const d = max - min;
|
|
237
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
238
|
+
let h: number;
|
|
239
|
+
switch (max) {
|
|
240
|
+
case r:
|
|
241
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
242
|
+
break;
|
|
243
|
+
case g:
|
|
244
|
+
h = (b - r) / d + 2;
|
|
245
|
+
break;
|
|
246
|
+
default:
|
|
247
|
+
h = (r - g) / d + 4;
|
|
248
|
+
}
|
|
249
|
+
return [h / 6, l, s];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** HSL → RGB(h∈[0,1),l/s∈[0,1])。 */
|
|
253
|
+
function hslToRgb(h: number, l: number, s: number): [number, number, number] {
|
|
254
|
+
if (s === 0) {
|
|
255
|
+
const v = Math.round(l * 255);
|
|
256
|
+
return [v, v, v];
|
|
257
|
+
}
|
|
258
|
+
const hue2rgb = (p: number, q: number, t: number): number => {
|
|
259
|
+
if (t < 0) t += 1;
|
|
260
|
+
if (t > 1) t -= 1;
|
|
261
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
262
|
+
if (t < 1 / 2) return q;
|
|
263
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
264
|
+
return p;
|
|
265
|
+
};
|
|
266
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
267
|
+
const p = 2 * l - q;
|
|
268
|
+
return [
|
|
269
|
+
Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
|
|
270
|
+
Math.round(hue2rgb(p, q, h) * 255),
|
|
271
|
+
Math.round(hue2rgb(p, q, h - 1 / 3) * 255),
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** 内部 tool call card 背景:在 userMessageBg 基础上按 HSL 变暗(l×0.7),
|
|
276
|
+
* 与外卡片形成嵌套层次;非 RGB 色或已足够暗时原样返回。 */
|
|
277
|
+
function darkenBgAnsi(theme: any, slot: string): string {
|
|
278
|
+
const prefix = typeof theme?.getBgAnsi === "function" ? String(theme.getBgAnsi(slot)) : "";
|
|
279
|
+
const m = prefix.match(/48;2;(\d+);(\d+);(\d+)/);
|
|
280
|
+
if (!m) return prefix;
|
|
281
|
+
const [h, l, s] = rgbToHsl(Number(m[1]), Number(m[2]), Number(m[3]));
|
|
282
|
+
if (l <= 0.05) return prefix; // 已足够暗,不再变暗
|
|
283
|
+
const [r, g, b] = hslToRgb(h, l * 0.7, s);
|
|
284
|
+
return `\x1b[48;2;${r};${g};${b}m`;
|
|
285
|
+
}
|
|
286
|
+
|
|
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
|
+
/** 工具卡深色行:左右内缩(左 2 右 3 格,含工具卡自身 padding),背景只到内容区,
|
|
298
|
+
* 修复行尾 reset 截断。 */
|
|
299
|
+
function toolCardBgRow(
|
|
300
|
+
theme: any,
|
|
301
|
+
slot: string,
|
|
302
|
+
bgAnsi: string,
|
|
303
|
+
line: string,
|
|
304
|
+
width: number,
|
|
305
|
+
): string {
|
|
306
|
+
const leftInset = 2;
|
|
307
|
+
const rightInset = 3;
|
|
308
|
+
const contentWidth = Math.max(0, width - leftInset - rightInset);
|
|
309
|
+
// 深色块内左右各 1 格内部 padding;去掉行首原有空格后重新对齐
|
|
310
|
+
const text = stripBackgroundAnsi(line).replace(/^ +/, "");
|
|
311
|
+
const innerPad = 2;
|
|
312
|
+
const clipped = truncateToWidth(text, Math.max(0, contentWidth - innerPad), "");
|
|
313
|
+
const pad = Math.max(0, contentWidth - innerPad - visibleWidth(clipped));
|
|
314
|
+
const body = ` ${clipped}${" ".repeat(pad)} `;
|
|
315
|
+
const stable = body.replace(/\x1b\[(?:0)?m/g, (reset) => reset + bgAnsi);
|
|
316
|
+
const outerBg = typeof theme?.getBgAnsi === "function" ? String(theme.getBgAnsi(slot)) : "";
|
|
317
|
+
const inset = (n) => (outerBg ? `${outerBg}${" ".repeat(n)}\x1b[49m` : " ".repeat(n));
|
|
318
|
+
return `${inset(leftInset)}${bgAnsi}${stable}\x1b[49m${inset(rightInset)}`;
|
|
319
|
+
}
|
|
320
|
+
/** edit/write 展开卡:保持原样式(userMessageBg Box),不应用工具卡深色/间距改动。 */
|
|
321
|
+
function editWriteExpandedCard(theme: any): any {
|
|
195
322
|
return new Box(
|
|
196
323
|
1,
|
|
197
324
|
1,
|
|
@@ -199,6 +326,73 @@ function expandedToolCard(theme: any): any {
|
|
|
199
326
|
);
|
|
200
327
|
}
|
|
201
328
|
|
|
329
|
+
/** compact 展开卡:外卡片保持 userMessageBg 原色;内部 tool call card(addToolChild)
|
|
330
|
+
* 背景更深一层且只覆盖内容区(左右内缩、上下限首尾文本行),形成嵌套层次。
|
|
331
|
+
* 逐行重建背景同时修复嵌套 Box 的右侧截断。 */
|
|
332
|
+
function expandedToolCard(theme: any): any {
|
|
333
|
+
const slot = "userMessageBg";
|
|
334
|
+
const toolBgAnsi = darkenBgAnsi(theme, slot);
|
|
335
|
+
const children: any[] = [];
|
|
336
|
+
return {
|
|
337
|
+
children,
|
|
338
|
+
addChild(child: any) {
|
|
339
|
+
children.push(child);
|
|
340
|
+
},
|
|
341
|
+
addToolChild(child: any) {
|
|
342
|
+
child.__ccToolCard = true;
|
|
343
|
+
children.push(child);
|
|
344
|
+
},
|
|
345
|
+
render(width: number): string[] {
|
|
346
|
+
const innerWidth = Math.max(0, width - 2);
|
|
347
|
+
const lines: string[] = [];
|
|
348
|
+
lines.push(paddedBackgroundRow(theme, slot, "", width));
|
|
349
|
+
// 展开不再显示摘要行:跳过第一个 child 的首行空白,避免白占一行
|
|
350
|
+
let skipLeadingBlank = true;
|
|
351
|
+
for (const child of children) {
|
|
352
|
+
const childLines = child.render(innerWidth);
|
|
353
|
+
if (!child.__ccToolCard) {
|
|
354
|
+
let start = 0;
|
|
355
|
+
if (skipLeadingBlank) {
|
|
356
|
+
while (start < childLines.length && !hasVisibleText(childLines[start])) start++;
|
|
357
|
+
skipLeadingBlank = false;
|
|
358
|
+
}
|
|
359
|
+
for (let i = start; i < childLines.length; i++) {
|
|
360
|
+
lines.push(paddedBackgroundRow(theme, slot, childLines[i], width));
|
|
361
|
+
}
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
// 工具卡:前导 1 空行 + 首尾文本行之间的内容区(深色);不保留尾随,
|
|
365
|
+
// 相邻工具卡之间正好 1 空行,底部由卡片 padding 收尾。
|
|
366
|
+
skipLeadingBlank = false;
|
|
367
|
+
let first = -1;
|
|
368
|
+
let last = -1;
|
|
369
|
+
for (let i = 0; i < childLines.length; i++) {
|
|
370
|
+
if (hasVisibleText(childLines[i])) {
|
|
371
|
+
if (first < 0) first = i;
|
|
372
|
+
last = i;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (first < 0) {
|
|
376
|
+
for (const line of childLines) {
|
|
377
|
+
lines.push(paddedBackgroundRow(theme, slot, line, width));
|
|
378
|
+
}
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
lines.push(paddedBackgroundRow(theme, slot, "", width));
|
|
382
|
+
// 子卡片内部上下各 1 行深色 padding
|
|
383
|
+
lines.push(toolCardBgRow(theme, slot, toolBgAnsi, "", width));
|
|
384
|
+
for (let i = first; i <= last; i++) {
|
|
385
|
+
lines.push(toolCardBgRow(theme, slot, toolBgAnsi, childLines[i], width));
|
|
386
|
+
}
|
|
387
|
+
lines.push(toolCardBgRow(theme, slot, toolBgAnsi, "", width));
|
|
388
|
+
}
|
|
389
|
+
lines.push(paddedBackgroundRow(theme, slot, "", width));
|
|
390
|
+
return lines;
|
|
391
|
+
},
|
|
392
|
+
invalidate() {},
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
202
396
|
function isAssistantComponent(value: any): boolean {
|
|
203
397
|
return value instanceof AssistantMessageComponent;
|
|
204
398
|
}
|
|
@@ -249,7 +443,7 @@ export type CompactModeHooks = {
|
|
|
249
443
|
refresh(): void;
|
|
250
444
|
/** compact 模式下重新认领 assistant patch(位于 compact-thinking 之上)。 */
|
|
251
445
|
assertOwnership(): void;
|
|
252
|
-
/** 重新渲染包含指定 toolCallId 的 assistant
|
|
446
|
+
/** 重新渲染包含指定 toolCallId 的 assistant 消息(思考收尾时刷新)。 */
|
|
253
447
|
refreshToolCallMessage(toolCallId: string | undefined): void;
|
|
254
448
|
shutdown(): void;
|
|
255
449
|
};
|
|
@@ -383,7 +577,7 @@ function compactEditWriteExpandedLines(
|
|
|
383
577
|
component.resultRendererComponent = detail;
|
|
384
578
|
}
|
|
385
579
|
|
|
386
|
-
const box =
|
|
580
|
+
const box = editWriteExpandedCard(theme);
|
|
387
581
|
box.addChild({
|
|
388
582
|
render(innerWidth: number): string[] {
|
|
389
583
|
return compactEditWriteLine(component, innerWidth, writeMetadata).slice(1);
|
|
@@ -396,7 +590,8 @@ function compactEditWriteExpandedLines(
|
|
|
396
590
|
|
|
397
591
|
function compactAssistantLineComponent(
|
|
398
592
|
component: any,
|
|
399
|
-
|
|
593
|
+
/** 静态串或每次 render 重算(Running 时长需逐步跳动)。 */
|
|
594
|
+
summary: string | (() => string),
|
|
400
595
|
query?: CompactThinkingQuery,
|
|
401
596
|
options: { hint?: boolean; leadingBlank?: boolean; pad?: number } = {},
|
|
402
597
|
): any {
|
|
@@ -408,17 +603,15 @@ function compactAssistantLineComponent(
|
|
|
408
603
|
const available = Math.max(0, width - pad);
|
|
409
604
|
const hintText = options.hint === false ? "" : ` • ${showMoreHintText()}`;
|
|
410
605
|
const summaryWidth = Math.max(0, available - visibleWidth(hintText));
|
|
411
|
-
const
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
: summary;
|
|
415
|
-
const plainText = truncateToWidth(displaySummary, summaryWidth, "…");
|
|
606
|
+
const resolved = typeof summary === "function" ? summary() : summary;
|
|
607
|
+
const runningActive = resolved.startsWith("Running...");
|
|
608
|
+
const plainText = truncateToWidth(resolved, summaryWidth, "…");
|
|
416
609
|
let text = theme.fg("muted", plainText);
|
|
417
|
-
if (
|
|
610
|
+
if (runningActive || plainText.startsWith("Ran for ")) {
|
|
418
611
|
const separator = plainText.indexOf(", ");
|
|
419
612
|
const heading = separator < 0 ? plainText : plainText.slice(0, separator);
|
|
420
613
|
const tools = separator < 0 ? "" : plainText.slice(separator);
|
|
421
|
-
if (
|
|
614
|
+
if (runningActive) {
|
|
422
615
|
const durationSeparator = heading.indexOf(" · ");
|
|
423
616
|
const label = durationSeparator < 0 ? heading : heading.slice(0, durationSeparator);
|
|
424
617
|
const duration = durationSeparator < 0 ? "" : heading.slice(durationSeparator);
|
|
@@ -440,11 +633,35 @@ function compactAssistantLineComponent(
|
|
|
440
633
|
};
|
|
441
634
|
}
|
|
442
635
|
|
|
443
|
-
function
|
|
444
|
-
|
|
636
|
+
function compactStopStatusLine(status: string, pad = 0): any {
|
|
637
|
+
return {
|
|
638
|
+
render(width: number): string[] {
|
|
639
|
+
const theme = themeOf();
|
|
640
|
+
const prefix = " ".repeat(Math.max(0, pad));
|
|
641
|
+
const line = `${prefix}${theme.fg("error", status)}`;
|
|
642
|
+
return ["", truncateToWidth(line, width, "")];
|
|
643
|
+
},
|
|
644
|
+
invalidate() {},
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function compactAssistantLine(
|
|
649
|
+
component: any,
|
|
650
|
+
summary: string | (() => string),
|
|
651
|
+
query?: CompactThinkingQuery,
|
|
652
|
+
): void {
|
|
653
|
+
// 空静态串跳过;getter 留给 render 判断(Running 可能稍后才有时长)。
|
|
654
|
+
if (typeof summary === "string" && !summary) return;
|
|
445
655
|
component.contentContainer.addChild(compactAssistantLineComponent(component, summary, query));
|
|
446
656
|
}
|
|
447
657
|
|
|
658
|
+
function appendStopStatus(component: any, status: string | undefined): void {
|
|
659
|
+
if (!status || !component?.contentContainer?.addChild) return;
|
|
660
|
+
component.contentContainer.addChild(
|
|
661
|
+
compactStopStatusLine(status, Number(component.outputPad) || 0),
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
448
665
|
function ensureAssistantSetExpanded(component: any): void {
|
|
449
666
|
if (
|
|
450
667
|
typeof component[ASSISTANT_SET_EXPANDED_KEY] === "function" &&
|
|
@@ -508,10 +725,17 @@ function collectMountedComponents(root: any): void {
|
|
|
508
725
|
}
|
|
509
726
|
};
|
|
510
727
|
visit(root);
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
728
|
+
// 扫到组件才替换跟踪表。面板/custom UI 打开时 root 往往扫不到 transcript,
|
|
729
|
+
// 若此时清空会丢掉 live updateContent/updateDisplay 已登记的实例,
|
|
730
|
+
// /ccstyle 切换就只能靠 /reload 重建。
|
|
731
|
+
if (assistants.size > 0 || tools.size > 0) {
|
|
732
|
+
trackedAssistantComponents.clear();
|
|
733
|
+
trackedToolComponents.clear();
|
|
734
|
+
for (const component of assistants) trackedAssistantComponents.add(component);
|
|
735
|
+
for (const component of tools) trackedToolComponents.add(component);
|
|
736
|
+
} else if (config.mode === "compact") {
|
|
737
|
+
for (const component of trackedAssistantComponents) ensureAssistantSetExpanded(component);
|
|
738
|
+
}
|
|
515
739
|
}
|
|
516
740
|
|
|
517
741
|
export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHooks {
|
|
@@ -535,13 +759,13 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
535
759
|
dispose: () => {},
|
|
536
760
|
};
|
|
537
761
|
|
|
538
|
-
const passThroughAssistant = (component: any, message: any): any => {
|
|
762
|
+
const passThroughAssistant = (component: any, message: any, isStreaming?: boolean): any => {
|
|
539
763
|
const self = component as any;
|
|
540
764
|
if (self[ASSISTANT_REENTRY_KEY] === patch)
|
|
541
|
-
return patch.assistantNative.call(component, message);
|
|
765
|
+
return patch.assistantNative.call(component, message, isStreaming);
|
|
542
766
|
self[ASSISTANT_REENTRY_KEY] = patch;
|
|
543
767
|
try {
|
|
544
|
-
return patch.assistantOriginal.call(component, message);
|
|
768
|
+
return patch.assistantOriginal.call(component, message, isStreaming);
|
|
545
769
|
} finally {
|
|
546
770
|
delete self[ASSISTANT_REENTRY_KEY];
|
|
547
771
|
}
|
|
@@ -553,17 +777,77 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
553
777
|
detachedMessages: any[];
|
|
554
778
|
active: boolean;
|
|
555
779
|
suppressedToolIds: Set<string>;
|
|
780
|
+
/** 回合挂钟起点,保证 Running 时长连续递增。 */
|
|
781
|
+
startedAt: number;
|
|
782
|
+
endedAt?: number;
|
|
556
783
|
};
|
|
557
784
|
let activeRound: CompactRound | undefined;
|
|
558
785
|
let roundByComponent = new WeakMap<object, CompactRound>();
|
|
559
786
|
const expandedRoundToolIds = new Set<string>();
|
|
787
|
+
let uiRef: { requestRender?: (force?: boolean) => void } | undefined;
|
|
788
|
+
let roundTickTimer: ReturnType<typeof setInterval> | undefined;
|
|
560
789
|
|
|
561
|
-
const
|
|
790
|
+
const roundWallMs = (round: CompactRound): number => {
|
|
791
|
+
const end = round.active ? Date.now() : (round.endedAt ?? Date.now());
|
|
792
|
+
return Math.max(1, end - round.startedAt);
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
const summarize = (messages: Iterable<any>, runningActive?: boolean, round?: CompactRound) =>
|
|
796
|
+
buildMessagesSummary(
|
|
797
|
+
messages,
|
|
798
|
+
deps.query,
|
|
799
|
+
runningActive,
|
|
800
|
+
round ? roundWallMs(round) : undefined,
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
const stopRoundTick = (): void => {
|
|
804
|
+
if (!roundTickTimer) return;
|
|
805
|
+
clearInterval(roundTickTimer);
|
|
806
|
+
roundTickTimer = undefined;
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// 自备 tick:getter 在 render 重算挂钟;兼驱动 Running 扫光(setCompactSummaryActive)。
|
|
810
|
+
const ensureRoundTick = (): void => {
|
|
811
|
+
if (roundTickTimer) return;
|
|
812
|
+
roundTickTimer = setInterval(() => {
|
|
813
|
+
if (!patch.active || !activeRound?.active) {
|
|
814
|
+
stopRoundTick();
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
uiRef?.requestRender?.(true);
|
|
819
|
+
} catch {
|
|
820
|
+
/* 无 UI */
|
|
821
|
+
}
|
|
822
|
+
}, 250);
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
/** 结束回合活动态;可选立即重绘。 */
|
|
826
|
+
const endRound = (round: CompactRound, render = false): void => {
|
|
827
|
+
round.active = false;
|
|
828
|
+
if (round.endedAt === undefined) round.endedAt = Date.now();
|
|
829
|
+
if (activeRound === round) {
|
|
830
|
+
activeRound = undefined;
|
|
831
|
+
deps.query?.setCompactSummaryActive?.(false);
|
|
832
|
+
stopRoundTick();
|
|
833
|
+
}
|
|
834
|
+
if (render) renderRound(round);
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
const renderAssistantWithoutThinking = (
|
|
838
|
+
component: any,
|
|
839
|
+
message: any,
|
|
840
|
+
isStreaming?: boolean,
|
|
841
|
+
): any => {
|
|
562
842
|
const content = Array.isArray(message?.content) ? message.content : [];
|
|
563
|
-
const result = passThroughAssistant(
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
843
|
+
const result = passThroughAssistant(
|
|
844
|
+
component,
|
|
845
|
+
{
|
|
846
|
+
...message,
|
|
847
|
+
content: content.filter((item: any) => item?.type !== "thinking"),
|
|
848
|
+
},
|
|
849
|
+
isStreaming,
|
|
850
|
+
);
|
|
567
851
|
component.lastMessage = message;
|
|
568
852
|
return result;
|
|
569
853
|
};
|
|
@@ -584,7 +868,12 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
584
868
|
};
|
|
585
869
|
|
|
586
870
|
const renderRound = (round: CompactRound): void => {
|
|
587
|
-
const
|
|
871
|
+
const messages = roundMessages(round);
|
|
872
|
+
const stopStatus = roundStopStatus(messages);
|
|
873
|
+
if (stopStatus) endRound(round);
|
|
874
|
+
// Running 时每次 render 重算时长(含挂钟下限);结束后固定。
|
|
875
|
+
const getSummary = () => summarize(roundMessages(round), round.active, round);
|
|
876
|
+
const summary = getSummary();
|
|
588
877
|
for (const id of round.suppressedToolIds) expandedRoundToolIds.delete(id);
|
|
589
878
|
round.suppressedToolIds.clear();
|
|
590
879
|
|
|
@@ -634,20 +923,13 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
634
923
|
render(width: number): string[] {
|
|
635
924
|
const theme = themeOf();
|
|
636
925
|
const box = expandedToolCard(theme);
|
|
637
|
-
box.addChild(
|
|
638
|
-
compactAssistantLineComponent(round.anchor, summary, deps.query, {
|
|
639
|
-
hint: false,
|
|
640
|
-
leadingBlank: false,
|
|
641
|
-
pad: 0,
|
|
642
|
-
}),
|
|
643
|
-
);
|
|
644
926
|
for (const child of assistantChildren) box.addChild(child);
|
|
645
927
|
const ids = roundToolCallIds(round);
|
|
646
928
|
for (const tool of trackedToolComponents) {
|
|
647
929
|
if (!ids.has(tool.toolCallId) || EDIT_WRITE_TOOLS.has(String(tool.toolName ?? ""))) {
|
|
648
930
|
continue;
|
|
649
931
|
}
|
|
650
|
-
box.
|
|
932
|
+
box.addToolChild({
|
|
651
933
|
render: (innerWidth: number) => patch.toolOriginalRender.call(tool, innerWidth),
|
|
652
934
|
invalidate: () => tool.invalidate?.(),
|
|
653
935
|
});
|
|
@@ -658,13 +940,18 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
658
940
|
for (const child of assistantChildren) child.invalidate?.();
|
|
659
941
|
},
|
|
660
942
|
});
|
|
943
|
+
// 展开卡内工具会显示 error,外层仍挂 abort/length,避免只藏在折叠工具里。
|
|
944
|
+
appendStopStatus(round.anchor, stopStatus);
|
|
661
945
|
return;
|
|
662
946
|
}
|
|
663
947
|
|
|
664
948
|
for (const [component, message] of round.messages) {
|
|
665
949
|
if (component === round.anchor) {
|
|
666
950
|
renderAssistantWithoutThinking(component, message);
|
|
667
|
-
|
|
951
|
+
// 空摘要不挂行(getter 在 Running 启动瞬间也可能短暂为空)
|
|
952
|
+
if (summary || round.active) compactAssistantLine(component, getSummary, deps.query);
|
|
953
|
+
// 折叠时工具行被隐藏:abort/error/length 必须挂在摘要外层。
|
|
954
|
+
appendStopStatus(component, stopStatus);
|
|
668
955
|
} else {
|
|
669
956
|
component.contentContainer?.clear?.();
|
|
670
957
|
}
|
|
@@ -673,38 +960,37 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
673
960
|
|
|
674
961
|
const activateRound = (round: CompactRound): void => {
|
|
675
962
|
round.active = true;
|
|
963
|
+
if (!round.startedAt) round.startedAt = Date.now();
|
|
964
|
+
delete round.endedAt;
|
|
676
965
|
activeRound = round;
|
|
677
966
|
deps.query?.setCompactSummaryActive?.(true);
|
|
967
|
+
ensureRoundTick();
|
|
678
968
|
};
|
|
679
969
|
|
|
680
|
-
const finishRound = (round: CompactRound): void =>
|
|
681
|
-
round.active = false;
|
|
682
|
-
renderRound(round);
|
|
683
|
-
if (activeRound === round) {
|
|
684
|
-
activeRound = undefined;
|
|
685
|
-
deps.query?.setCompactSummaryActive?.(false);
|
|
686
|
-
}
|
|
687
|
-
};
|
|
970
|
+
const finishRound = (round: CompactRound): void => endRound(round, true);
|
|
688
971
|
|
|
689
972
|
const resetRounds = (): void => {
|
|
690
973
|
activeRound = undefined;
|
|
691
974
|
roundByComponent = new WeakMap();
|
|
692
975
|
expandedRoundToolIds.clear();
|
|
693
976
|
deps.query?.setCompactSummaryActive?.(false);
|
|
977
|
+
stopRoundTick();
|
|
694
978
|
};
|
|
695
979
|
|
|
696
|
-
patch.assistantInstalled = function (this: any, message: any) {
|
|
980
|
+
patch.assistantInstalled = function (this: any, message: any, isStreaming?: boolean) {
|
|
697
981
|
const self = this as any;
|
|
982
|
+
// 同 compact-thinking:isStreaming 丢失 → mermaid 流式误渲染来回闪。
|
|
983
|
+
if (isStreaming !== undefined) self.isStreaming = isStreaming;
|
|
698
984
|
if (self[ASSISTANT_REENTRY_KEY] === patch) {
|
|
699
|
-
return patch.assistantNative.call(this, message);
|
|
985
|
+
return patch.assistantNative.call(this, message, isStreaming);
|
|
700
986
|
}
|
|
701
987
|
self.lastMessage = message;
|
|
702
988
|
trackedAssistantComponents.add(this);
|
|
703
989
|
if (!patch.active || config.mode !== "compact") {
|
|
704
|
-
return passThroughAssistant(this, message);
|
|
990
|
+
return passThroughAssistant(this, message, isStreaming);
|
|
705
991
|
}
|
|
706
992
|
if (!self.contentContainer || typeof self.contentContainer.clear !== "function") {
|
|
707
|
-
return passThroughAssistant(this, message);
|
|
993
|
+
return passThroughAssistant(this, message, isStreaming);
|
|
708
994
|
}
|
|
709
995
|
|
|
710
996
|
const content = Array.isArray(message?.content) ? message.content : [];
|
|
@@ -725,6 +1011,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
725
1011
|
detachedMessages: [],
|
|
726
1012
|
active: true,
|
|
727
1013
|
suppressedToolIds: new Set(),
|
|
1014
|
+
startedAt: Date.now(),
|
|
728
1015
|
};
|
|
729
1016
|
roundByComponent.set(this, round);
|
|
730
1017
|
activateRound(round);
|
|
@@ -735,6 +1022,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
735
1022
|
detachedMessages: [],
|
|
736
1023
|
active: true,
|
|
737
1024
|
suppressedToolIds: new Set(),
|
|
1025
|
+
startedAt: Date.now(),
|
|
738
1026
|
};
|
|
739
1027
|
roundByComponent.set(this, round);
|
|
740
1028
|
if (!activeRound) activateRound(round);
|
|
@@ -757,12 +1045,8 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
757
1045
|
return renderAssistantWithoutThinking(this, message);
|
|
758
1046
|
}
|
|
759
1047
|
if (round) {
|
|
760
|
-
round
|
|
1048
|
+
endRound(round);
|
|
761
1049
|
roundByComponent.delete(this);
|
|
762
|
-
if (activeRound === round) {
|
|
763
|
-
activeRound = undefined;
|
|
764
|
-
deps.query?.setCompactSummaryActive?.(false);
|
|
765
|
-
}
|
|
766
1050
|
return passThroughAssistant(this, message);
|
|
767
1051
|
}
|
|
768
1052
|
if (activeRound) finishRound(activeRound);
|
|
@@ -779,6 +1063,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
779
1063
|
detachedMessages: [],
|
|
780
1064
|
active: true,
|
|
781
1065
|
suppressedToolIds: new Set(),
|
|
1066
|
+
startedAt: Date.now(),
|
|
782
1067
|
};
|
|
783
1068
|
roundByComponent.set(this, round);
|
|
784
1069
|
if (!activeRound) activateRound(round);
|
|
@@ -788,6 +1073,15 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
788
1073
|
return undefined;
|
|
789
1074
|
}
|
|
790
1075
|
|
|
1076
|
+
// 无可见内容的 abort/error/length:直接外层状态行,并结束进行中的回合。
|
|
1077
|
+
const loneStatus = messageStopStatus(message);
|
|
1078
|
+
if (loneStatus) {
|
|
1079
|
+
if (activeRound) finishRound(activeRound);
|
|
1080
|
+
self.contentContainer.clear();
|
|
1081
|
+
appendStopStatus(this, loneStatus);
|
|
1082
|
+
return undefined;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
791
1085
|
self.contentContainer.clear();
|
|
792
1086
|
return undefined;
|
|
793
1087
|
};
|
|
@@ -803,6 +1097,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
803
1097
|
if (this.expanded !== true) return lines;
|
|
804
1098
|
return compactEditWriteExpandedLines(this, width, deps.writeMetadata);
|
|
805
1099
|
}
|
|
1100
|
+
// Agent/Task 等同普通工具:折叠不外置(live 面板走独立 widget)。
|
|
806
1101
|
if (expandedRoundToolIds.has(String(this.toolCallId ?? ""))) return [];
|
|
807
1102
|
// 普通工具折叠时不显示独立行(摘要行已统计),独立展开走原 renderer。
|
|
808
1103
|
if (this.expanded === true) {
|
|
@@ -826,12 +1121,18 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
826
1121
|
};
|
|
827
1122
|
|
|
828
1123
|
patch.assertAssistantOwnership = () => {
|
|
829
|
-
//
|
|
1124
|
+
// compact 必须在外层:从 compact-thinking 包装器认领,或收回 mode=on/off
|
|
1125
|
+
// 时主动释放给 original/native 的所有权。未知外部包装器不抢,避免递归。
|
|
830
1126
|
if (!patch.active || assistantPrototype.updateContent === patch.assistantInstalled) return;
|
|
831
1127
|
const current = assistantPrototype.updateContent;
|
|
832
|
-
if ((current as any)[COMPACT_THINKING_PATCH_KEY]
|
|
833
|
-
|
|
834
|
-
|
|
1128
|
+
if ((current as any)[COMPACT_THINKING_PATCH_KEY] === true) {
|
|
1129
|
+
patch.assistantOriginal = current;
|
|
1130
|
+
assistantPrototype.updateContent = patch.assistantInstalled;
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
if (current === patch.assistantOriginal || current === patch.assistantNative) {
|
|
1134
|
+
assistantPrototype.updateContent = patch.assistantInstalled;
|
|
1135
|
+
}
|
|
835
1136
|
};
|
|
836
1137
|
|
|
837
1138
|
(patch.assistantInstalled as any)[PROTOTYPE_ORIGINAL_KEY] = patch.assistantNative;
|
|
@@ -872,31 +1173,23 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
|
|
|
872
1173
|
for (const component of trackedToolComponents) component.expanded = globalExpanded;
|
|
873
1174
|
};
|
|
874
1175
|
|
|
875
|
-
const releaseAssistantOwnership = (): void => {
|
|
876
|
-
if (assistantPrototype.updateContent === patch.assistantInstalled) {
|
|
877
|
-
assistantPrototype.updateContent = patch.assistantOriginal;
|
|
878
|
-
}
|
|
879
|
-
};
|
|
880
|
-
|
|
881
1176
|
return {
|
|
882
1177
|
sync(ctx: any) {
|
|
883
1178
|
if (!patch.active) return;
|
|
1179
|
+
uiRef = ctx?.ui;
|
|
884
1180
|
resetRounds();
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
}
|
|
1181
|
+
// 始终保持补丁在链上:mode=on/off 走 passThrough,仍写入 lastMessage
|
|
1182
|
+
// 与 tracked 集合,这样 /ccstyle 切回 compact 时不必 /reload。
|
|
1183
|
+
patch.assertAssistantOwnership();
|
|
1184
|
+
if (config.mode === "compact") syncGlobalExpanded(ctx);
|
|
1185
|
+
else hoveredAssistantComponent = undefined;
|
|
891
1186
|
refreshTrackedComponents();
|
|
892
1187
|
},
|
|
893
1188
|
refresh() {
|
|
894
1189
|
if (!patch.active) return;
|
|
895
1190
|
resetRounds();
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
hoveredAssistantComponent = undefined;
|
|
899
|
-
}
|
|
1191
|
+
patch.assertAssistantOwnership();
|
|
1192
|
+
if (config.mode !== "compact") hoveredAssistantComponent = undefined;
|
|
900
1193
|
refreshTrackedComponents();
|
|
901
1194
|
},
|
|
902
1195
|
assertOwnership() {
|