pi-cc-extensions 0.8.48 → 0.8.49

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 (38) hide show
  1. package/README.en.md +17 -36
  2. package/README.md +13 -12
  3. package/assets/readme/welcome.webp +0 -0
  4. package/extensions/config/config.ts +245 -0
  5. package/extensions/config/panel.ts +469 -0
  6. package/extensions/{compact-thinking.ts → feature/compact-thinking.ts} +43 -6
  7. package/extensions/{markdown-enhance.ts → feature/markdown-enhance.ts} +88 -62
  8. package/extensions/{pi-startup-header.ts → feature/pi-startup-header.ts} +129 -56
  9. package/extensions/index.ts +9 -9
  10. package/extensions/renderer/index.ts +890 -0
  11. package/extensions/renderer/mouse-interaction.ts +1344 -0
  12. package/extensions/renderer/show-more-hint.ts +40 -0
  13. package/extensions/{tool-diff → renderer/tool-diff}/diff-renderer.ts +3 -2
  14. package/extensions/{tool-diff → renderer/tool-diff}/line-width-safety.ts +2 -1
  15. package/extensions/{tool-grouping.ts → renderer/tool-grouping.ts} +71 -10
  16. package/extensions/renderer/tool-result.ts +717 -0
  17. package/package.json +1 -1
  18. package/themes/cc-dark.json +2 -2
  19. package/themes/cc-light.json +2 -2
  20. package/extensions/claude-code-style.ts +0 -3348
  21. /package/extensions/{agent-autocomplete.ts → feature/agent-autocomplete.ts} +0 -0
  22. /package/extensions/{context.ts → feature/context.ts} +0 -0
  23. /package/extensions/{pi-aliases.ts → feature/pi-aliases.ts} +0 -0
  24. /package/extensions/{session-reference-core.ts → feature/session-reference-core.ts} +0 -0
  25. /package/extensions/{session-reference.ts → feature/session-reference.ts} +0 -0
  26. /package/extensions/{working-message.ts → feature/working-message.ts} +0 -0
  27. /package/extensions/{tool-diff → renderer/tool-diff}/ATTRIBUTION.md +0 -0
  28. /package/extensions/{tool-diff → renderer/tool-diff}/ansi-utils.ts +0 -0
  29. /package/extensions/{tool-diff → renderer/tool-diff}/diff-presentation.ts +0 -0
  30. /package/extensions/{tool-diff → renderer/tool-diff}/index.ts +0 -0
  31. /package/extensions/{tool-diff → renderer/tool-diff}/render-utils.ts +0 -0
  32. /package/extensions/{tool-diff → renderer/tool-diff}/shiki-highlight.ts +0 -0
  33. /package/extensions/{tool-diff → renderer/tool-diff}/types.ts +0 -0
  34. /package/extensions/{tool-diff → renderer/tool-diff}/write-display-utils.ts +0 -0
  35. /package/extensions/{tool-diff → renderer/tool-diff}/write-execution.ts +0 -0
  36. /package/extensions/{fullscreen-detect.ts → utils/fullscreen-detect.ts} +0 -0
  37. /package/extensions/{tool-loading-icon.ts → utils/tool-loading-icon.ts} +0 -0
  38. /package/extensions/{tool-result-sanitize.ts → utils/tool-result-sanitize.ts} +0 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * show-more 提示文本:fullscreen 下鼠标 hover/click 可用,提示 "click to show more";
3
+ * 其他模式(regular 保留终端原生 scrollback、不启用鼠标上报)提示默认展开快捷键。
4
+ *
5
+ * tui 模式由 mouse-interaction 在 renderer 回调中维护;未安装时按 fullscreen 处理,
6
+ * 保证纯渲染测试与既有行为不变。
7
+ */
8
+ import { getKeybindings } from "@earendil-works/pi-tui";
9
+
10
+ let toolTuiFullscreen: boolean | undefined;
11
+
12
+ export function setToolTuiFullscreen(value: boolean): void {
13
+ toolTuiFullscreen = value;
14
+ }
15
+
16
+ export function isToolTuiFullscreen(): boolean {
17
+ return toolTuiFullscreen !== false;
18
+ }
19
+
20
+ function formatKeyPart(part: string): string {
21
+ // 与 pi 内置 keybinding-hints 一致:macOS 上 alt 显示为 option
22
+ return process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part;
23
+ }
24
+
25
+ /** 默认展开快捷键(app.tools.expand)显示文本;未配置时回退 ctrl+o。 */
26
+ export function expandShortcutText(): string {
27
+ const keys = getKeybindings().getKeys("app.tools.expand");
28
+ if (keys.length === 0) return "ctrl+o";
29
+ return keys
30
+ .join("/")
31
+ .split("/")
32
+ .map((part) => part.split("+").map(formatKeyPart).join("+"))
33
+ .join("/");
34
+ }
35
+
36
+ export function showMoreHintText(): string {
37
+ return isToolTuiFullscreen()
38
+ ? "click to show more"
39
+ : `${expandShortcutText()} to show more`;
40
+ }
@@ -30,10 +30,11 @@ import {
30
30
  resolveDiffPresentationMode,
31
31
  type DiffPresentationMode,
32
32
  } from "./diff-presentation.ts";
33
- import { sanitizeToolResultText } from "../tool-result-sanitize.ts";
33
+ import { sanitizeToolResultText } from "../../utils/tool-result-sanitize.ts";
34
34
  import { pluralize, sanitizeAnsiForThemedOutput } from "./render-utils.ts";
35
35
  import { splitWriteContentLines } from "./write-display-utils.ts";
36
36
  import { MAX_HL_CHARS, shikiHighlightCache } from "./shiki-highlight.ts";
37
+ import { showMoreHintText } from "../show-more-hint.ts";
37
38
  import {
38
39
  DEFAULT_TOOL_DISPLAY_CONFIG,
39
40
  type DiffIndicatorMode,
@@ -2319,7 +2320,7 @@ function applyLineLimit(
2319
2320
  DIFF_WIDTH_OPS,
2320
2321
  );
2321
2322
 
2322
- const clickLabel = "click to show more";
2323
+ const clickLabel = showMoreHintText();
2323
2324
  const clickIndex = hintText.lastIndexOf(clickLabel);
2324
2325
  const styledHint =
2325
2326
  hovered && !expanded && clickIndex >= 0
@@ -1,5 +1,6 @@
1
1
  import { normalizeDiffRenderWidth } from "./diff-presentation.ts";
2
2
  import { pluralize } from "./render-utils.ts";
3
+ import { showMoreHintText } from "../show-more-hint.ts";
3
4
 
4
5
  export interface WidthMeasurementOps {
5
6
  measure(text: string): number;
@@ -71,7 +72,7 @@ export function buildCollapsedDiffHintText(
71
72
  : undefined;
72
73
 
73
74
  const candidates = [
74
- `… (${[remainingText, hiddenHunksText, "click to show more"].filter(Boolean).join(" • ")})`,
75
+ `… (${[remainingText, hiddenHunksText, showMoreHintText()].filter(Boolean).join(" • ")})`,
75
76
  `… (${[remainingText, hiddenHunksText].filter(Boolean).join(" • ")})`,
76
77
  `… (${[shortRemainingText, shortHiddenHunksText].filter(Boolean).join(" • ")})`,
77
78
  options.hiddenHunks > 0
@@ -1,7 +1,8 @@
1
1
  import { AssistantMessageComponent, ToolExecutionComponent } from "@earendil-works/pi-coding-agent";
2
2
  import { Container, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
- import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "./tool-loading-icon.ts";
4
- import { sanitizeToolResultText } from "./tool-result-sanitize.ts";
3
+ import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../utils/tool-loading-icon.ts";
4
+ import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
5
+ import { showMoreHintText } from "./show-more-hint.ts";
5
6
 
6
7
  const PATCH_KEY = Symbol.for("pi.ccstyle.tool-grouping-patch");
7
8
  const PARENT_KEY = Symbol.for("pi.ccstyle.tool-grouping-parent");
@@ -250,7 +251,11 @@ let nextGroupId = 1;
250
251
  export class ToolGroupComponent extends Container {
251
252
  readonly toolCallId = `ccstyle-tool-group-${nextGroupId++}`;
252
253
  readonly toolName = "Tool group";
253
- private expanded = false;
254
+ private _expanded = false;
255
+ /** 分组是否展开(只读;测试与外部读状态用)。 */
256
+ get expanded(): boolean {
257
+ return this._expanded;
258
+ }
254
259
  private hintHovered = false;
255
260
  private readonly patch: Patch;
256
261
 
@@ -279,7 +284,7 @@ export class ToolGroupComponent extends Container {
279
284
  }
280
285
 
281
286
  setExpanded(expanded: boolean): void {
282
- this.expanded = expanded;
287
+ this._expanded = expanded;
283
288
  for (const tool of this.children) tool.setExpanded?.(expanded);
284
289
  }
285
290
 
@@ -287,6 +292,30 @@ export class ToolGroupComponent extends Container {
287
292
  this.hintHovered = hovered;
288
293
  }
289
294
 
295
+ /**
296
+ * 展开时按局部行定位内部组件(null = 行属于 group 自身:空行/头行/尾行)。
297
+ * 行数计算与 render 保持一致:宽度 width-2 + 空行过滤。
298
+ */
299
+ childAtRow(localRow: number, width: number): { component: any; row: number } | null {
300
+ if (!this._expanded || localRow < 2) return null;
301
+ let offset = 2;
302
+ for (const tool of this.children) {
303
+ let lines: string[] = [];
304
+ try {
305
+ const rendered = tool.render?.(Math.max(1, width - 2));
306
+ if (Array.isArray(rendered)) lines = visibleLines(rendered.map((line) => String(line)));
307
+ } catch {
308
+ lines = [];
309
+ }
310
+ const lineCount = Math.max(1, lines.length);
311
+ if (localRow < offset + lineCount) {
312
+ return { component: tool, row: localRow - offset };
313
+ }
314
+ offset += lineCount;
315
+ }
316
+ return null;
317
+ }
318
+
290
319
  invalidate(): void {
291
320
  for (const tool of this.children) tool.invalidate?.();
292
321
  }
@@ -312,7 +341,7 @@ export class ToolGroupComponent extends Container {
312
341
  const overallColor = overall === "pending" ? "accent" : overall;
313
342
  const nameList = names.size > 1 ? ` ${fg("dim", `• ${toolNameList(this.children)}`)}` : "";
314
343
  // 圆点保持 dim;hover 只高亮可点击文字。
315
- const hint = `${fg("dim", "•")} ${fg(this.hintHovered ? "text" : "dim", "click to show more")}`;
344
+ const hint = `${fg("dim", "•")} ${fg(this.hintHovered ? "text" : "dim", showMoreHintText())}`;
316
345
  const lines = [
317
346
  "",
318
347
  truncateToWidth(
@@ -329,7 +358,7 @@ export class ToolGroupComponent extends Container {
329
358
  const color = toolStatus === "pending" ? "accent" : toolStatus;
330
359
  const branch = index === total - 1 ? "└" : "├";
331
360
  const continuation = index === total - 1 ? " " : "│ ";
332
- if (!this.expanded) {
361
+ if (!this._expanded) {
333
362
  const summary = toolSummary(tool);
334
363
  lines.push(
335
364
  truncateToWidth(
@@ -357,7 +386,7 @@ export class ToolGroupComponent extends Container {
357
386
  expandedLines.push(prefix + content);
358
387
  }
359
388
  }
360
- if (this.expanded) {
389
+ if (this._expanded) {
361
390
  const backgroundSlot =
362
391
  overall === "error"
363
392
  ? "toolErrorBg"
@@ -438,9 +467,40 @@ function maybeGroup(patch: Patch, parent: any, component: any): void {
438
467
  children.splice(index, 1);
439
468
  }
440
469
 
470
+ /** /reload 不会重新 addChild;扫描当前 mounted roots,把已有工具重新送入同一分组规则。 */
471
+ function regroup(patch: Patch, root: any): void {
472
+ if (!patch.active || !patch.enabled() || !root) return;
473
+ const seen = new Set<any>();
474
+ const visit = (value: any): void => {
475
+ if (!value || typeof value !== "object" || seen.has(value)) return;
476
+ seen.add(value);
477
+ if (Array.isArray(value)) {
478
+ for (const child of value) visit(child);
479
+ return;
480
+ }
481
+ const children = value.children;
482
+ if (Array.isArray(children)) {
483
+ for (const child of [...children]) {
484
+ if (child && typeof child === "object") child[PARENT_KEY] = value;
485
+ maybeGroup(patch, value, child);
486
+ }
487
+ for (const child of [...children]) {
488
+ if (!(child instanceof ToolGroupComponent) && !isGroupable(child)) visit(child);
489
+ }
490
+ }
491
+ try {
492
+ const mounted = value.getMountedRoots?.();
493
+ if (Array.isArray(mounted)) visit(mounted);
494
+ } catch {
495
+ // renderer 切换中的惰性 Proxy 可能暂时没有 mounted roots。
496
+ }
497
+ };
498
+ visit(root);
499
+ }
500
+
441
501
  export type ToolGroupingHooks = {
442
502
  setTheme(theme: any): void;
443
- refresh(): void;
503
+ refresh(root?: any): void;
444
504
  shutdown(): void;
445
505
  };
446
506
 
@@ -522,13 +582,14 @@ export function installToolGrouping(getEnabled: () => boolean): ToolGroupingHook
522
582
  setTheme(theme: any) {
523
583
  patch.theme = theme;
524
584
  },
525
- refresh() {
585
+ refresh(root?: any) {
526
586
  const enabled = patch.enabled();
527
587
  if (enabled !== patch.lastEnabled) {
528
588
  patch.lastEnabled = enabled;
529
589
  if (enabled) patch.generation++;
530
590
  }
531
- if (!enabled) ungroup(patch);
591
+ if (enabled) regroup(patch, root);
592
+ else ungroup(patch);
532
593
  },
533
594
  shutdown() {
534
595
  if (!patch.active) return;