pi-cc-extensions 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.en.md +2 -0
  2. package/README.md +3 -1
  3. package/extensions/config/config.ts +16 -0
  4. package/extensions/config/panel.ts +47 -0
  5. package/extensions/feature/compact-thinking.ts +1 -1
  6. package/extensions/feature/reference/subagent.ts +2 -1
  7. package/extensions/feature/shell/footer.ts +57 -10
  8. package/extensions/feature/shell/token-rate.ts +192 -0
  9. package/extensions/feature/shell/working-message.ts +52 -65
  10. package/extensions/renderer/compact-mode.ts +38 -28
  11. package/extensions/renderer/default-mode.ts +23 -463
  12. package/extensions/renderer/index.ts +3 -3
  13. package/extensions/renderer/markdown-enhance.ts +63 -22
  14. package/extensions/renderer/mouse/hover.ts +1 -1
  15. package/extensions/renderer/mouse/interaction.ts +2 -2
  16. package/extensions/renderer/mouse/packets.ts +1 -1
  17. package/extensions/renderer/mouse/sticky-prompt.ts +61 -9
  18. package/extensions/renderer/tool/ask-user-question.ts +1 -1
  19. package/extensions/renderer/tool/ccstyle-tool.ts +467 -0
  20. package/extensions/renderer/tool/codemode.ts +1 -1
  21. package/extensions/renderer/tool/diff/diff-edit-render.ts +1 -1
  22. package/extensions/renderer/tool/diff/diff-limits.ts +1 -1
  23. package/extensions/renderer/tool/diff/diff-write-render.ts +1 -1
  24. package/extensions/renderer/tool/diff/line-width-safety.ts +1 -1
  25. package/extensions/renderer/tool/grouping.ts +56 -50
  26. package/extensions/renderer/tool/message-display.ts +1 -1
  27. package/extensions/renderer/tool/names.ts +41 -1
  28. package/extensions/renderer/tool/result.ts +2 -862
  29. package/extensions/renderer/tool/tool-io.ts +735 -0
  30. package/extensions/renderer/tool/tool-state.ts +138 -0
  31. package/extensions/utils/format.ts +9 -0
  32. package/package.json +1 -1
  33. package/themes/cc-light.json +1 -1
  34. package/extensions/renderer/tool/show-more-hint.ts +0 -48
@@ -22,7 +22,7 @@ import {
22
22
  } from "./codemode.ts";
23
23
  import { getToolMouseTui } from "../mouse/scroll.ts";
24
24
  import { mcpToolTitle } from "./mcp-title.ts";
25
- import { collapseHintText, isToolTuiFullscreen, showMoreHintText } from "./show-more-hint.ts";
25
+ import { collapseHintText, isToolTuiFullscreen, showMoreHintText } from "./names.ts";
26
26
  import { stripAnsi, stripBackgroundAnsi, stripLeadingStatusIcon } from "../../utils/ansi-text.ts";
27
27
  import { walkComponentTree } from "../../utils/component-tree.ts";
28
28
  import {
@@ -33,14 +33,8 @@ import {
33
33
  toolCallSummary,
34
34
  type ToolCallSummary,
35
35
  } from "./names.ts";
36
- import {
37
- collapsedResultSummary,
38
- formatToolDuration,
39
- headTruncateToWidth,
40
- isLiveToolCall,
41
- textFromResult,
42
- toolViewportWidth,
43
- } from "./result.ts";
36
+ import { formatToolDuration, headTruncateToWidth, toolViewportWidth } from "./result.ts";
37
+ import { isLiveToolCall } from "./tool-state.ts";
44
38
  import {
45
39
  patchRegistry,
46
40
  TOOL_GROUPING_GENERATION_KEY as GENERATION_KEY,
@@ -263,8 +257,8 @@ function codemodeResultSummary(tool: any): ToolCallSummary | undefined {
263
257
  }
264
258
 
265
259
  /**
266
- * 组内 shell 行的尾巴:只报耗时(`· 12.4s`)。行数是冗余信息,展开就能看到;
267
- * 失败另报错误首行,那条不该只在展开后可见。
260
+ * 组内 shell 行的尾巴:只报耗时(`· 12.4s`)。输出和错误正文都在展开后看;
261
+ * 失败靠行首的 ✗,组头另有 failed 计数。
268
262
  * 耗时取 pi 记在结果上的 durationMs(单卡渲染上下文里的同一个值),运行中还拿不到就不报。
269
263
  */
270
264
  function shellResultTail(
@@ -273,23 +267,59 @@ function shellResultTail(
273
267
  toolStatus: ToolStatus,
274
268
  ): string {
275
269
  if (!SHELL_TOOL_NAMES.has(toolName(tool)) || toolStatus === "pending" || !tool?.result) return "";
276
- const isError = tool.result?.isError === true;
277
- // 失败报错误首行(不该只在展开后可见);成功只报耗时,行数是冗余信息。
278
- const failure = isError
279
- ? collapsedResultSummary({
280
- result: tool.result,
281
- toolName: toolName(tool),
282
- text: textFromResult(tool.result),
283
- isError: true,
284
- })
285
- : "";
286
270
  const durationMs = tool.result?.durationMs;
287
271
  const duration =
288
272
  typeof durationMs === "number" && Number.isFinite(durationMs) && durationMs >= 0
289
273
  ? formatToolDuration(durationMs)
290
274
  : "";
291
- const tail = [failure, duration].filter(Boolean).join(" · ");
292
- return tail ? ` ${fg("dim", `· ${tail}`)}` : "";
275
+ return duration ? ` ${fg("dim", `· ${duration}`)}` : "";
276
+ }
277
+
278
+ /** 分组折叠态的一行子工具:`├/└` + 状态图标 + 与组内同一套摘要。 */
279
+ function collapsedGroupToolLine(
280
+ tool: any,
281
+ last: boolean,
282
+ fg: (color: string, text: string) => string,
283
+ width: number,
284
+ ): string {
285
+ const toolStatus = status(tool);
286
+ const color = toolStatus === "pending" ? "accent" : toolStatus;
287
+ const summary = askUserResultSummary(tool) ?? codemodeResultSummary(tool) ?? toolSummary(tool);
288
+ const prefix = ` ${fg("dim", last ? "└" : "├")} ${fg(color, statusIcon(toolStatus))} `;
289
+ const detail = fg("dim", summary.detail);
290
+ const result = shellResultTail(tool, fg, toolStatus);
291
+ const rowWidth = toolViewportWidth(width);
292
+ const mainWidth = Math.max(
293
+ 0,
294
+ rowWidth - visibleWidth(prefix) - visibleWidth(detail) - visibleWidth(result),
295
+ );
296
+ return truncateToWidth(
297
+ `${prefix}${renderToolSummary(summary, mainWidth, fg)}${detail}${result}`,
298
+ rowWidth,
299
+ "",
300
+ );
301
+ }
302
+
303
+ type GroupHeaderCounts = { pending: number; success: number; error: number };
304
+
305
+ /** 组头计数:`2 done · 1 failed`。没有计数时返回空串。 */
306
+ function groupCountText(
307
+ counts: GroupHeaderCounts,
308
+ fg: (color: string, text: string) => string,
309
+ ): string {
310
+ return (["pending", "success", "error"] as const)
311
+ .filter((key) => counts[key])
312
+ .map((key) => {
313
+ const label = key === "pending" ? "running" : key === "success" ? "done" : "failed";
314
+ const color = key === "pending" ? "accent" : key;
315
+ return `${fg(color, String(counts[key]))} ${label}`;
316
+ })
317
+ .join(` ${fg("dim", "·")} `);
318
+ }
319
+
320
+ /** 组头圆点色:有失败取 error,否则有运行中取 accent,否则 success。 */
321
+ function groupOverallStatus(counts: GroupHeaderCounts): ToolStatus {
322
+ return counts.error ? "error" : counts.pending ? "pending" : "success";
293
323
  }
294
324
 
295
325
  /**
@@ -464,17 +494,10 @@ export class ToolGroupComponent extends Container {
464
494
  const fg = (color: string, text: string) => theme?.fg?.(color, text) ?? text;
465
495
  const counts = { pending: 0, success: 0, error: 0 };
466
496
  for (const tool of this.children) counts[status(tool)]++;
467
- const countText = (["pending", "success", "error"] as const)
468
- .filter((key) => counts[key])
469
- .map((key) => {
470
- const label = key === "pending" ? "running" : key === "success" ? "done" : "failed";
471
- const color = key === "pending" ? "accent" : key;
472
- return `${fg(color, String(counts[key]))} ${label}`;
473
- })
474
- .join(` ${fg("dim", "·")} `);
497
+ const countText = groupCountText(counts, fg);
475
498
  const names = new Set(this.children.map(toolName));
476
499
  const label = names.size === 1 ? toolTitle(this.children[0]) : "Multiple Tools";
477
- const overall: ToolStatus = counts.error ? "error" : counts.pending ? "pending" : "success";
500
+ const overall = groupOverallStatus(counts);
478
501
  if ((this.children as any[]).some(isAnimatingTool)) scheduleGroupAnimation(this.patch);
479
502
  const overallColor = overall === "pending" ? "accent" : overall;
480
503
  // 圆点保持 dim;hover 只高亮可点击文字。
@@ -519,24 +542,7 @@ export class ToolGroupComponent extends Container {
519
542
  const branch = index === total - 1 ? "└" : "├";
520
543
  const continuation = index === total - 1 ? " " : "│ ";
521
544
  if (!this._expanded) {
522
- const summary =
523
- askUserResultSummary(tool) ?? codemodeResultSummary(tool) ?? toolSummary(tool);
524
- const prefix = ` ${fg("dim", branch)} ${fg(color, statusIcon(toolStatus))} `;
525
- const detail = fg("dim", summary.detail);
526
- const result = shellResultTail(tool, fg, toolStatus);
527
- // 与单工具卡标题同宽,宽屏右侧留白一致
528
- const rowWidth = toolViewportWidth(width);
529
- const mainWidth = Math.max(
530
- 0,
531
- rowWidth - visibleWidth(prefix) - visibleWidth(detail) - visibleWidth(result),
532
- );
533
- lines.push(
534
- truncateToWidth(
535
- `${prefix}${renderToolSummary(summary, mainWidth, fg)}${detail}${result}`,
536
- rowWidth,
537
- "",
538
- ),
539
- );
545
+ lines.push(collapsedGroupToolLine(tool, index === total - 1, fg, width));
540
546
  continue;
541
547
  }
542
548
  const rendered = visibleLines(Array.isArray(childPaints?.[index]) ? childPaints[index] : []);
@@ -6,7 +6,7 @@ import {
6
6
  import { Markdown, Spacer, Text } from "@earendil-works/pi-tui";
7
7
  import { MESSAGE_DISPLAY_PATCH, patchRegistry } from "../../utils/patch-keys.ts";
8
8
  import { config } from "../../config/config.ts";
9
- import { showMoreHintText } from "./show-more-hint.ts";
9
+ import { showMoreHintText } from "./names.ts";
10
10
  import { walkComponentTree } from "../../utils/component-tree.ts";
11
11
 
12
12
  /**
@@ -1,6 +1,6 @@
1
1
  import { posix, win32 } from "node:path";
2
2
  import type { ThemeColor } from "@earendil-works/pi-coding-agent";
3
- import { visibleWidth } from "@earendil-works/pi-tui";
3
+ import { getKeybindings, visibleWidth } from "@earendil-works/pi-tui";
4
4
  import { config } from "../../config/config.ts";
5
5
  import { oneLine } from "../../utils/format.ts";
6
6
  import { headTruncateToWidth } from "./result.ts";
@@ -427,3 +427,43 @@ export function renderToolSummary(
427
427
  ? `${fg("toolTitle", title)}${fg("dim", headTruncateToWidth(` ${summary.payload}`, room))}`
428
428
  : fg("toolTitle", title);
429
429
  }
430
+
431
+ let toolTuiFullscreen: boolean | undefined;
432
+
433
+ export function setToolTuiFullscreen(value: boolean): void {
434
+ toolTuiFullscreen = value;
435
+ }
436
+
437
+ export function isToolTuiFullscreen(): boolean {
438
+ return toolTuiFullscreen !== false;
439
+ }
440
+
441
+ function formatKeyPart(part: string): string {
442
+ // 与 pi 内置 keybinding-hints 一致:macOS 上 alt 显示为 option
443
+ return process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part;
444
+ }
445
+
446
+ /** 默认展开快捷键(app.tools.expand)显示文本;未配置时回退 ctrl+o。 */
447
+ export function expandShortcutText(): string {
448
+ const keys = getKeybindings().getKeys("app.tools.expand");
449
+ if (keys.length === 0) return "ctrl+o";
450
+ return keys
451
+ .join("/")
452
+ .split("/")
453
+ .map((part) => part.split("+").map(formatKeyPart).join("+"))
454
+ .join("/");
455
+ }
456
+
457
+ export function showMoreHintText(): string {
458
+ return isToolTuiFullscreen() ? "click to show more" : `${expandShortcutText()} to show more`;
459
+ }
460
+
461
+ /** 展开卡的收起提示,与折叠态 showMoreHintText 同一位置对称。 */
462
+ export function collapseHintText(): string {
463
+ return isToolTuiFullscreen() ? "↑ Collapse" : `${expandShortcutText()} to collapse`;
464
+ }
465
+
466
+ /** 该行是否带展开态收起提示(用于展开卡的 hint hover 命中)。 */
467
+ export function isCollapseHintLine(plainLine: string): boolean {
468
+ return plainLine.includes(collapseHintText());
469
+ }