pi-cc-extensions 1.0.1 → 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 (28) hide show
  1. package/extensions/feature/compact-thinking.ts +1 -1
  2. package/extensions/feature/reference/subagent.ts +2 -1
  3. package/extensions/feature/shell/footer.ts +9 -6
  4. package/extensions/feature/shell/working-message.ts +2 -2
  5. package/extensions/renderer/compact-mode.ts +38 -28
  6. package/extensions/renderer/default-mode.ts +23 -463
  7. package/extensions/renderer/index.ts +3 -3
  8. package/extensions/renderer/markdown-enhance.ts +63 -22
  9. package/extensions/renderer/mouse/hover.ts +1 -1
  10. package/extensions/renderer/mouse/interaction.ts +2 -2
  11. package/extensions/renderer/mouse/packets.ts +1 -1
  12. package/extensions/renderer/mouse/sticky-prompt.ts +61 -9
  13. package/extensions/renderer/tool/ask-user-question.ts +1 -1
  14. package/extensions/renderer/tool/ccstyle-tool.ts +467 -0
  15. package/extensions/renderer/tool/codemode.ts +1 -1
  16. package/extensions/renderer/tool/diff/diff-edit-render.ts +1 -1
  17. package/extensions/renderer/tool/diff/diff-limits.ts +1 -1
  18. package/extensions/renderer/tool/diff/diff-write-render.ts +1 -1
  19. package/extensions/renderer/tool/diff/line-width-safety.ts +1 -1
  20. package/extensions/renderer/tool/grouping.ts +53 -34
  21. package/extensions/renderer/tool/message-display.ts +1 -1
  22. package/extensions/renderer/tool/names.ts +41 -1
  23. package/extensions/renderer/tool/result.ts +2 -862
  24. package/extensions/renderer/tool/tool-io.ts +735 -0
  25. package/extensions/renderer/tool/tool-state.ts +138 -0
  26. package/package.json +1 -1
  27. package/themes/cc-light.json +1 -1
  28. package/extensions/renderer/tool/show-more-hint.ts +0 -48
@@ -20,7 +20,7 @@ import {
20
20
  import { Box, Markdown, Spacer, Text, type Component } from "@earendil-works/pi-tui";
21
21
  // 展开卡背景是 ccstyle 的渲染约定,不随上游 CompactThinkingConfig 走,直接读全局配置。
22
22
  import { config as styleConfig } from "../config/config.ts";
23
- import { isToolTuiFullscreen } from "../renderer/tool/show-more-hint.ts";
23
+ import { isToolTuiFullscreen } from "../renderer/tool/names.ts";
24
24
  import {
25
25
  animateCompactThinkingText,
26
26
  formatThoughtDuration,
@@ -26,8 +26,9 @@ const MENTION_PATTERN = /(?:^|[\t ])@([^\s@]*)$/;
26
26
  const SUBAGENT_REFERENCE_PATTERN = /(?:^|\s)@subagent:\[([^\]]+)](?![\w-])/g;
27
27
 
28
28
  function getAgentDir(): string {
29
+ // PI_CODING_AGENT_DIR 本身就是 agent 目录(默认 ~/.pi/agent),不是家目录。
29
30
  const override = process.env.PI_CODING_AGENT_DIR;
30
- return join(override ? override : homedir(), ".pi", "agent", "agents");
31
+ return join(override ? override : join(homedir(), ".pi", "agent"), "agents");
31
32
  }
32
33
 
33
34
  function parseFrontmatter(content: string): Record<string, string> {
@@ -53,13 +53,16 @@ export function footerGlyphs(nerdIcons: boolean): { git: string; cache: string;
53
53
  : { git: "", cache: "", mcp: "" };
54
54
  }
55
55
 
56
- /** 思考等级的填充圆标:等级越高填得越满。 */
56
+ /**
57
+ * 思考等级的填充圆标:等级越高填得越满。
58
+ * 全部取自同一圆径的几何圆,避开占位虚线圆和偏小的半圆。
59
+ */
57
60
  const EFFORT_SYMBOL: Record<string, string> = {
58
- minimal: "\u25cc",
59
- low: "\u25cb",
61
+ minimal: "\u25cb",
62
+ low: "\u25ce",
60
63
  medium: "\u25d4",
61
- high: "\u25d1",
62
- xhigh: "\u25d5",
64
+ high: "\u25d5",
65
+ xhigh: "\u25c9",
63
66
  max: "\u25cf",
64
67
  };
65
68
 
@@ -72,7 +75,7 @@ export function modelChip(
72
75
  ): string {
73
76
  const symbol = EFFORT_SYMBOL[level];
74
77
  if (!symbol) return theme.fg("accent", modelLabel);
75
- return `${theme.fg("accent", modelLabel)} ${thinkingColor(symbol)} ${theme.fg("dim", level)}`;
78
+ return `${theme.fg("accent", modelLabel)} ${thinkingColor(`${symbol} ${level}`)}`;
76
79
  }
77
80
 
78
81
  /** git 状态符号按职能分色:冲突/落后 error,修改 warning,未跟踪 muted,领先 success。 */
@@ -186,9 +186,9 @@ export default function (pi: ExtensionAPI): void {
186
186
  evt.type === "text_start" ||
187
187
  evt.type === "toolcall_start"
188
188
  ) {
189
- // TTFT 的首块含 toolcall_start:模型直接发起 tool call(thinking/text 省略)时也算首 token
189
+ // TTFT 的首块含 toolcall_start:模型直接发起 tool call(thinking/text 省略)时也算首 token。
190
+ // 分段锚点只在 turn_start 重置。这里再重置会把累计 usage.output 当成新的首报。
190
191
  markFirstToken();
191
- startMessageSegment();
192
192
  updateProviderUsage(evt.partial);
193
193
  } else if (evt.type === "thinking_delta" || evt.type === "text_delta") {
194
194
  if (typeof evt.delta === "string") sampleTokenDelta(evt.delta);
@@ -42,7 +42,7 @@ import { toolLoadingIcon } from "../utils/tool-loading-icon.ts";
42
42
  import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
43
43
  import { refreshTranscriptComponent } from "./transcript-refresh.ts";
44
44
  import { getMessageDisplayTheme } from "./tool/message-display.ts";
45
- import { showMoreHintText } from "./tool/show-more-hint.ts";
45
+ import { showMoreHintText } from "./tool/names.ts";
46
46
  import {
47
47
  countEditDiffStats,
48
48
  countWriteDiffStats,
@@ -53,13 +53,11 @@ import { displayConfigCacheKey } from "./tool/diff/diff-component.ts";
53
53
  import type { WriteExecutionMetadataStore } from "./tool/diff/write-execution.ts";
54
54
  import { isToolCallHovered } from "./mouse/hover.ts";
55
55
  import { getToolMouseTui } from "./mouse/scroll.ts";
56
- import {
57
- insetComponent,
58
- isLiveToolCall,
59
- renderExpandedToolResult,
60
- scheduleAnimation,
61
- } from "./tool/result.ts";
56
+ import { insetComponent } from "./tool/result.ts";
57
+ import { isLiveToolCall, scheduleAnimation } from "./tool/tool-state.ts";
58
+ import { renderExpandedToolResult } from "./tool/tool-io.ts";
62
59
  import { paddedBackgroundRow } from "./tool/grouping.ts";
60
+ import { styleChangeCounts } from "./tool/ccstyle-tool.ts";
63
61
  import { formatDisplayPath } from "./tool/names.ts";
64
62
  import {
65
63
  hasVisibleText,
@@ -663,31 +661,37 @@ export function markCompactRoundToolExpanded(tool: any): void {
663
661
  roundToolExpansion?.markUserExpanded(tool);
664
662
  }
665
663
 
666
- /** 面板展开前的视口位置:收起时还原,避免面板撑高后收起导致视口错位。 */
667
- const panelViewportBeforeExpand = new WeakMap<
668
- object,
669
- { scrollTop: number; followingEnd: boolean }
670
- >();
664
+ /**
665
+ * 展开面板前的分隔空行与面板绑成一体:挂在同一尾行上,才不会被拆到
666
+ * anchor 正文之后(面板挂回合尾行,空行却留在消息里)。
667
+ * 带 childAtRow 返回 null:拆分判据认它为面板成员,行命中落空时归外层面板。
668
+ */
669
+ function panelBoundSpacer(): any {
670
+ return {
671
+ render: () => [""],
672
+ childAtRow: () => null,
673
+ invalidate() {},
674
+ };
675
+ }
676
+
677
+ /** 面板展开前的视口标记:收起时需要重钉视口的卡。 */
678
+ const panelViewportBeforeExpand = new WeakSet<object>();
671
679
 
672
680
  function capturePanelViewport(card: any): void {
673
- if (!card || panelViewportBeforeExpand.has(card)) return;
674
- const view = getToolMouseTui()?.getPrimaryScrollView?.();
675
- if (!view || typeof view.scrollTop !== "number" || typeof view.scrollTo !== "function") return;
676
- panelViewportBeforeExpand.set(card, {
677
- scrollTop: view.scrollTop,
678
- followingEnd: view.isFollowingEnd !== false,
679
- });
681
+ if (!card) return;
682
+ panelViewportBeforeExpand.add(card);
680
683
  }
681
684
 
682
685
  function restorePanelViewport(card: any): void {
683
- const snapshot = panelViewportBeforeExpand.get(card);
684
- if (!snapshot) return;
685
- panelViewportBeforeExpand.delete(card);
686
+ if (!panelViewportBeforeExpand.delete(card)) return;
686
687
  const view = getToolMouseTui()?.getPrimaryScrollView?.();
687
688
  if (!view) return;
688
- // 展开前就在底部:交回官方 follow;否则回到展开前的滚动偏移。
689
- if (snapshot.followingEnd && typeof view.scrollToEnd === "function") view.scrollToEnd();
690
- else if (typeof view.scrollTo === "function") view.scrollTo(snapshot.scrollTop);
689
+ // 不用展开前的快照:展开期间用户可能滚进面板里,回跳旧位置才是"跳"。
690
+ // 面板在摘要行之下,收起只删它以下的行,保持当前 scrollTop 摘要行原地不动;
691
+ // clamp 后落在新底时 scrollTo 会自动恢复 followingEnd(并清 followSuppressedAtEnd)。
692
+ if (typeof view.scrollTo === "function" && typeof view.scrollTop === "number") {
693
+ view.scrollTo(view.scrollTop);
694
+ }
691
695
  }
692
696
 
693
697
  function compactEditWriteLine(
@@ -724,8 +728,13 @@ function compactEditWriteLine(
724
728
  )
725
729
  : undefined;
726
730
  if (stats) {
727
- statsText = ` (+${stats.added} -${stats.removed})`;
728
- statsStyled = ` ${theme.fg("dim", "(")}${theme.fg("success", `+${stats.added}`)} ${theme.fg("error", `-${stats.removed}`)}${theme.fg("dim", ")")}`;
731
+ const counts = styleChangeCounts(
732
+ (color, text) => theme.fg(color, text),
733
+ stats.added,
734
+ stats.removed,
735
+ );
736
+ statsText = counts.text;
737
+ statsStyled = counts.styled;
729
738
  }
730
739
  }
731
740
  // 展开卡 Box(1,1) 已 pad;折叠行自己留 1 格前导空格
@@ -1828,7 +1837,8 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
1828
1837
  } else {
1829
1838
  // 面板上沿留 1 行卡外空行:面板底色与 userMessage 同色,
1830
1839
  // 贴在一起会看成一个块;卡内顶部再加 1 行 padding 保持上下对称。
1831
- anchorChildren.push(new Spacer(1));
1840
+ // 空行必须与面板同进退(childAtRow 参与面板拆分),用 panelBoundSpacer。
1841
+ anchorChildren.push(panelBoundSpacer());
1832
1842
  anchorChildren.push(
1833
1843
  compactRoundCard(
1834
1844
  cardItems,