pi-cc-extensions 0.9.8 → 0.9.10

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 CHANGED
@@ -25,16 +25,16 @@ Run `/reload` after installation.
25
25
 
26
26
  ## Features
27
27
 
28
- | Feature | Description | Entry point |
29
- | --------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------- |
30
- | Claude Code UI | Tool summaries, expand/collapse, rich edit/write diffs, and `on` / `compact` / `off` modes | `/ccstyle` |
31
- | Markdown enhancements | Mermaid diagrams, admonitions, URL linking, and more | Automatic |
32
- | Fullscreen mode | Tool card/group expand and collapse on click, previews, hover highlight, and a back-to-bottom button | `TUIMODE=fullscreen` or `--tui-mode fullscreen` |
33
- | Settings panel | `Style / Features / UI / Diff / Thinking / Footer` tabs | `/ccstyle` |
34
- | Context inspection | Usage breakdown and previews for the system prompt, memory, skills, tools definition, and messages | `/context` |
35
- | Session/Subagent references | Search and inject effective context from previous Sessions or existing SubAgents | `@` |
28
+ | Feature | Description | Entry point |
29
+ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
30
+ | Claude Code UI | Tool summaries, expand/collapse, rich edit/write diffs, and `on` / `compact` / `off` modes | `/ccstyle` |
31
+ | Markdown enhancements | Mermaid diagrams, admonitions, URL linking, and more | Automatic |
32
+ | Fullscreen mode | Tool card/group expand and collapse on click, previews, hover highlight, and a back-to-bottom button | `TUIMODE=fullscreen` or `--tui-mode fullscreen` |
33
+ | Settings panel | `Style / Features / UI / Diff / Thinking / Footer` tabs | `/ccstyle` |
34
+ | Context inspection | Usage breakdown and previews for the system prompt, memory, skills, tools definition, and messages | `/context` |
35
+ | Session/Subagent references | Search and inject effective context from previous Sessions or existing SubAgents | `@` |
36
36
  | Status bar | Shows model, context, cache, cost, and git; also works with `@narumitw/pi-usage` for live quota. Git/cache Nerd Font icons can be turned off | `/ccstyle` |
37
- | Theme | Bundled CC Dark and CC Light themes | `/theme` |
37
+ | Theme | Bundled CC Dark and CC Light themes | `/theme` |
38
38
 
39
39
  Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-examples-default.md) · [compact](./docs/tool-render-examples-compact.md)
40
40
 
@@ -45,51 +45,52 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
45
45
  ```jsonc
46
46
  {
47
47
  // style
48
- "mode": "on", // on / compact / off
49
- "excludeRenderers": [], // tools keeping the native renderer; Agent always keeps its dedicated renderer
48
+ "mode": "on", // on / compact / off
49
+ "excludeRenderers": [], // tools keeping the native renderer; Agent always keeps its dedicated renderer
50
50
 
51
51
  // features
52
- "enableSessionReference": true, // @ session references
53
- "enableSubagentAutocomplete": true, // @subagent:[name] completion and delegation hints
54
- "enableContextCommand": true, // /context usage check
55
- "enableAgentSummary": true, // per-turn tool summary
56
- "enableWorkingMessage": true, // Working... bottom token/elapsed
57
- "enableAliases": true, // /clear, /exit aliases
52
+ "enableSessionReference": true, // @ session references
53
+ "enableSubagentAutocomplete": true, // @subagent:[name] completion and delegation hints
54
+ "enableContextCommand": true, // /context usage check
55
+ "enableAgentSummary": true, // per-turn tool summary
56
+ "enableWorkingMessage": true, // Working... bottom token/elapsed
57
+ "enableAliases": true, // /clear, /exit aliases
58
58
 
59
59
  // ui
60
- "expandedInputMaxLines": 5, // expanded tool Input lines; overflow shows a footer hint
61
- "expandedOutputMaxLines": 10, // expanded tool Output lines; overflow shows a footer hint
62
- "expandedPreviewMaxLines": 40, // max lines for expanded TaskList bodies (expanded diffs always show all)
63
- "inputClip": 100, // tool summary path/command clip length
64
- "showStartupHeader": true, // startup header (logo + tips) toggle
65
- "scrollStepLines": 3, // fullscreen wheel scroll step
60
+ "expandedInputMaxLines": 5, // expanded tool Input lines; overflow shows a footer hint
61
+ "expandedOutputMaxLines": 10, // expanded tool Output lines; overflow shows a footer hint
62
+ "expandedPreviewMaxLines": 40, // max lines for expanded TaskList bodies (expanded diffs always show all)
63
+ "expandedCardBackground": "userMessageBg", // expanded card background slot: userMessageBg (same as user prompts) / toolPendingBg (neutral) etc.
64
+ "inputClip": 0, // tool summary path/command clip length; 0 = fit width
65
+ "showStartupHeader": true, // startup header (logo + tips) toggle
66
+ "scrollStepLines": 3, // fullscreen wheel scroll step
66
67
 
67
68
  // diff
68
- "diffViewMode": "auto", // layout: auto / split / unified
69
- "diffIndicatorMode": "bars", // change indicators: bars / classic / none
70
- "diffSplitMinWidth": 120, // min terminal width for side-by-side columns
71
- "editDiffCollapsedLines": 24, // Edit collapse lines; beyond that shows the expand hint
72
- "writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
73
- "diffWordWrap": true, // wrap long diff lines
69
+ "diffViewMode": "auto", // layout: auto / split / unified
70
+ "diffIndicatorMode": "bars", // change indicators: bars / classic / none
71
+ "diffSplitMinWidth": 120, // min terminal width for side-by-side columns
72
+ "editDiffCollapsedLines": 24, // Edit collapse lines; beyond that shows the expand hint
73
+ "writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
74
+ "diffWordWrap": true, // wrap long diff lines
74
75
 
75
76
  // thinking
76
77
  "useSummaryTitlesAsThinkingTitle": true, // use latest summary as thinking title
77
- "previewLines": 3, // preview lines; 0 hides
78
- "animationIntervalMs": 90, // title animation interval (ms)
79
- "dimThinkingText": false, // dim thinking body text
78
+ "previewLines": 3, // preview lines; 0 hides
79
+ "animationIntervalMs": 90, // title animation interval (ms)
80
+ "dimThinkingText": false, // dim thinking body text
80
81
 
81
82
  // footer
82
- "enableCustomFooter": true, // custom status bar
83
- "footerNerdIcons": true, // Nerd Font glyphs for git/cache; false = plain text
84
- "footerHiddenKeys": [], // hidden plugin chip keys
85
- "footerLine1Keys": ["pi-usage"], // line1 plugin chip order; pi-usage is shown by default, data from @narumitw/pi-usage
86
- "footerLine2Keys": [], // line2 plugin chip order (after cwd/git)
87
- "footerLine3Keys": [] // line3 overflow slot; painted only when a chip is visible
83
+ "enableCustomFooter": true, // custom status bar
84
+ "footerNerdIcons": true, // Nerd Font glyphs for git/cache; false = plain text
85
+ "footerHiddenKeys": [], // hidden plugin chip keys
86
+ "footerLine1Keys": ["pi-usage"], // line1 plugin chip order; pi-usage is shown by default, data from @narumitw/pi-usage
87
+ "footerLine2Keys": [], // line2 plugin chip order (after cwd/git)
88
+ "footerLine3Keys": [], // line3 overflow slot; painted only when a chip is visible
88
89
  }
89
90
  ```
90
91
 
91
92
  > [!TIP]
92
- > **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. An expanded diff always shows every line; when expanded Input/Output exceeds the line cap, the footer `… +N more lines • click to show more` opens a full preview. Click to collapse (dragging inside the card selects text).
93
+ > **Fullscreen**: click `click to show more` to expand tool cards, thinking, Skill, and compact summaries. An expanded diff always shows every line; when expanded Input/Output exceeds the line cap, the footer `… +N more lines · click to show more` opens a full preview. Click to collapse (dragging inside the card selects text).
93
94
 
94
95
  > [!NOTE]
95
96
  > **Mermaid rendering**: set `markdown.mermaid` to `final` via `~/.pi/agent/settings.json` or the Mermaid diagrams option in `/settings`. Default `streaming` redraws per frame; `final` renders once at completion.
@@ -99,7 +100,9 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
99
100
  ```bash
100
101
  npm test
101
102
  npm run typecheck
102
- ./test.bat # or pi -e .
103
+ ./test.sh # macOS/Linux: load the local checkout for one run, then restore
104
+ test.bat # Windows: same
105
+ # one-off run without touching settings: pi -e .
103
106
  ```
104
107
 
105
108
  ## Compatibility
@@ -108,14 +111,11 @@ npm run typecheck
108
111
 
109
112
  ## Recommended companions
110
113
 
111
- | Extension | Purpose |
112
- | ---------------------------------------- | ------------------------------------------------------------ |
113
- | `npm:@tintinweb/pi-subagents` | Parallel SubAgents, background tasks, and worktree isolation |
114
- | `npm:@tintinweb/pi-tasks` | Claude Code-style task tracking and coordination |
115
- | `npm:pi-mcp-adapter` | On-demand MCP tool discovery with lower context usage |
116
- | `npm:@ff-labs/pi-fff` | FFF-powered fuzzy file and content search (fffind / ffgrep) |
117
- | `npm:pi-web-access` | Web search, URL fetching, GitHub cloning, PDF/video parsing |
118
- | `npm:@narumitw/pi-usage` | Current-account usage for Codex / Copilot / OpenRouter |
114
+ | Extension | Purpose |
115
+ | ------------------------ | ----------------------------------------------------------- |
116
+ | `npm:@ff-labs/pi-fff` | FFF-powered fuzzy file and content search (fffind / ffgrep) |
117
+ | `npm:pi-web-access` | Web search, URL fetching, GitHub cloning, PDF/video parsing |
118
+ | `npm:@narumitw/pi-usage` | Current-account usage for Codex / Copilot / OpenRouter |
119
119
 
120
120
  ## Credits
121
121
 
package/README.md CHANGED
@@ -60,7 +60,8 @@ pi install git:github.com/minuque/pi-cc-extensions
60
60
  "expandedInputMaxLines": 5, // 展开工具卡 Input 可见行数,超出在末行显示展开提示
61
61
  "expandedOutputMaxLines": 10, // 展开工具卡 Output 可见行数,超出在末行显示展开提示
62
62
  "expandedPreviewMaxLines": 40, // 展开 TaskList 正文最大行数(展开 diff 始终全量)
63
- "inputClip": 100, // 工具摘要 path/command 折叠字符数
63
+ "expandedCardBackground": "userMessageBg", // 展开卡背景槽位;userMessageBg 与用户消息同色,toolPendingBg 为中性灰
64
+ "inputClip": 0, // 工具摘要 path/command 折叠字符数;0 = 按可用宽度
64
65
  "showStartupHeader": true, // 启动头(logo + tips)开关
65
66
  "scrollStepLines": 3, // fullscreen 滚轮步进
66
67
 
@@ -89,7 +90,7 @@ pi install git:github.com/minuque/pi-cc-extensions
89
90
  ```
90
91
 
91
92
  > [!TIP]
92
- > **全屏模式**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要;工具卡展开态 diff 全量显示,Input/Output 超行时末行 `… +N more lines • click to show more` 打开全量预览,单击收起(卡内拖动为选中文本)。
93
+ > **全屏模式**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要;工具卡展开态 diff 全量显示,Input/Output 超行时末行 `… +N more lines · click to show more` 打开全量预览,单击收起(卡内拖动为选中文本)。
93
94
 
94
95
  > [!NOTE]
95
96
  > **Mermaid 渲染**:建议把 `markdown.mermaid` 设为 `final`(`~/.pi/agent/settings.json` 或 `/settings` 面板的 Mermaid diagrams 选项)。默认 `streaming` 逐帧重绘,`final` 渲染最终版更稳定。
@@ -99,7 +100,9 @@ pi install git:github.com/minuque/pi-cc-extensions
99
100
  ```bash
100
101
  npm test
101
102
  npm run typecheck
102
- ./test.bat # or pi -e .
103
+ ./test.sh # macOS/Linux:临时加载本地检出,退出后还原
104
+ test.bat # Windows:同上
105
+ # 只想跑一次、不改配置:pi -e .
103
106
  ```
104
107
 
105
108
  ## 兼容性
@@ -110,9 +113,6 @@ npm run typecheck
110
113
 
111
114
  | 扩展 | 用途 |
112
115
  | ---------------------------------------- | ------------------------------------------------ |
113
- | `npm:@tintinweb/pi-subagents` | 并行 SubAgent、后台任务与工作树隔离 |
114
- | `npm:@tintinweb/pi-tasks` | Claude Code 风格任务跟踪与协调 |
115
- | `npm:pi-mcp-adapter` | 按需发现 MCP 工具,减少上下文占用 |
116
116
  | `npm:@ff-labs/pi-fff` | 模糊文件与内容检索(fffind / ffgrep) |
117
117
  | `npm:pi-web-access` | 网页搜索、URL 抓取、GitHub 克隆、PDF/视频解析 |
118
118
  | `npm:@narumitw/pi-usage` | 查看当前账号用量(Codex / Copilot / OpenRouter) |
@@ -1,3 +1,4 @@
1
+ import type { ThemeBg } from "@earendil-works/pi-coding-agent";
1
2
  import type { CompactThinkingConfig } from "../feature/compact-thinking.ts";
2
3
  import {
3
4
  DEFAULT_FOOTER_CHIP_LAYOUT,
@@ -18,7 +19,7 @@ export interface ToolDisplayConfig {
18
19
  diffIndicatorMode: DiffIndicatorMode;
19
20
  diffSplitMinWidth: number;
20
21
  editDiffCollapsedLines: number;
21
- /** Write-only collapsed body lines. 0 = `↳ created • click to show more`. */
22
+ /** Write-only collapsed body lines. 0 = `↳ created · click to show more`. */
22
23
  writeDiffCollapsedLines: number;
23
24
  diffWordWrap: boolean;
24
25
  expandedPreviewMaxLines: number;
@@ -32,7 +33,7 @@ export const DEFAULT_TOOL_DISPLAY_CONFIG: ToolDisplayConfig = {
32
33
  editDiffCollapsedLines: 24,
33
34
  /**
34
35
  * Write create/overwrite collapsed body.
35
- * 0 = `↳ created • click to show more` (stats stay on the title).
36
+ * 0 = `↳ created · click to show more` (stats stay on the title).
36
37
  */
37
38
  writeDiffCollapsedLines: 0,
38
39
  diffWordWrap: true,
@@ -53,6 +54,7 @@ export type Config = {
53
54
  writeDiffCollapsedLines: number;
54
55
  diffWordWrap: boolean;
55
56
  expandedPreviewMaxLines: number;
57
+ expandedCardBackground: ThemeBg;
56
58
  expandedInputMaxLines: number;
57
59
  expandedOutputMaxLines: number;
58
60
  inputClip: number;
@@ -88,12 +90,26 @@ export const DIFF_COLLAPSED_LINES_VALUES = ["12", "24", "36", "48", "80", "120"]
88
90
  export const WRITE_DIFF_COLLAPSED_LINES_VALUES = ["0", "4", "8", "12", "24", "36"];
89
91
  /** Presets for expanded body height — keep low options first so cycling stays TUI-friendly. */
90
92
  export const EXPANDED_PREVIEW_MAX_LINES_VALUES = ["40", "60", "80", "120", "200", "500", "2000"];
93
+ /**
94
+ * 展开卡可用的主题背景槽位。theme.bg/getBgAnsi 只认这些 token,
95
+ * 别的名字会抛 Unknown theme color,所以按白名单校验;
96
+ * 面板顺序把 userMessageBg(默认)和 toolPendingBg(内置主题的中性灰)放最前。
97
+ */
98
+ export const EXPANDED_CARD_BACKGROUND_SLOTS: readonly ThemeBg[] = [
99
+ "userMessageBg",
100
+ "toolPendingBg",
101
+ "customMessageBg",
102
+ "selectedBg",
103
+ "searchMatchBg",
104
+ "toolSuccessBg",
105
+ "toolErrorBg",
106
+ ];
91
107
  /** 展开工具卡 Input 可见行数预设。 */
92
108
  export const EXPANDED_INPUT_MAX_LINES_VALUES = ["5", "10", "20", "40", "80"];
93
109
  /** 展开工具卡 Output 可见行数预设。 */
94
110
  export const EXPANDED_OUTPUT_MAX_LINES_VALUES = ["10", "20", "40", "80", "120"];
95
- /** 工具摘要里 path/command 等输入的折叠字符数。 */
96
- export const INPUT_CLIP_VALUES = ["40", "60", "80", "100", "120", "160"];
111
+ /** 工具摘要里 path/command 等输入的折叠字符数;0 = 只按可用宽度截断。 */
112
+ export const INPUT_CLIP_VALUES = ["0", "40", "60", "80", "100", "120", "160"];
97
113
  export const THINKING_PREVIEW_LINES_VALUES = ["0", "1", "3", "5", "10"];
98
114
  export const THINKING_ANIMATION_INTERVAL_VALUES = ["40", "60", "90", "120", "180"];
99
115
  /** fullscreen 滚轮步进行数预设。 */
@@ -122,9 +138,11 @@ export const DEFAULT_CONFIG: Config = {
122
138
  writeDiffCollapsedLines: DEFAULT_TOOL_DISPLAY_CONFIG.writeDiffCollapsedLines,
123
139
  diffWordWrap: DEFAULT_TOOL_DISPLAY_CONFIG.diffWordWrap,
124
140
  expandedPreviewMaxLines: DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
141
+ /** 展开卡背景。userMessageBg 与用户消息同色(ccstyle 原约定)。 */
142
+ expandedCardBackground: "userMessageBg",
125
143
  expandedInputMaxLines: 5,
126
144
  expandedOutputMaxLines: 10,
127
- inputClip: 100,
145
+ inputClip: 0,
128
146
  useSummaryTitlesAsThinkingTitle: true,
129
147
  previewLines: 3,
130
148
  animationIntervalMs: 90,
@@ -154,6 +172,12 @@ export function pickPositiveInt(value: unknown, fallback: number, min = 1, max =
154
172
  return Math.min(max, Math.max(min, Math.floor(n)));
155
173
  }
156
174
 
175
+ /** inputClip:0 表示按宽度自适应,其余夹到 8..500。 */
176
+ export function pickInputClip(value: unknown): number {
177
+ const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
178
+ return n === 0 ? 0 : pickPositiveInt(value, DEFAULT_CONFIG.inputClip, 8, 500);
179
+ }
180
+
157
181
  export function pickPositiveNumber(value: unknown, fallback: number, min = 1): number {
158
182
  const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
159
183
  return Number.isFinite(n) ? Math.max(min, n) : fallback;
@@ -205,6 +229,11 @@ export function normalizeConfig(input: unknown): Config {
205
229
  10,
206
230
  50_000,
207
231
  ),
232
+ expandedCardBackground: pickEnum(
233
+ source.expandedCardBackground,
234
+ EXPANDED_CARD_BACKGROUND_SLOTS,
235
+ DEFAULT_CONFIG.expandedCardBackground,
236
+ ),
208
237
  expandedInputMaxLines: pickPositiveInt(
209
238
  source.expandedInputMaxLines,
210
239
  DEFAULT_CONFIG.expandedInputMaxLines,
@@ -217,7 +246,7 @@ export function normalizeConfig(input: unknown): Config {
217
246
  1,
218
247
  5_000,
219
248
  ),
220
- inputClip: pickPositiveInt(source.inputClip, DEFAULT_CONFIG.inputClip, 8, 500),
249
+ inputClip: pickInputClip(source.inputClip),
221
250
  useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle !== false,
222
251
  previewLines: pickPositiveInt(
223
252
  source.previewLines,
@@ -4,7 +4,7 @@
4
4
  * 渲染副作用(applyStyleMode / refreshCurrentTranscript)由 renderer 经
5
5
  * CcstylePanelHooks 注入,避免 config → renderer 循环依赖。
6
6
  */
7
- import { getSettingsListTheme } from "@earendil-works/pi-coding-agent";
7
+ import { getSettingsListTheme, type ThemeBg } from "@earendil-works/pi-coding-agent";
8
8
  import { Input, SettingsList, matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
9
9
  import type { CompactThinkingController } from "../feature/compact-thinking.ts";
10
10
  import {
@@ -33,11 +33,13 @@ import {
33
33
  DIFF_SPLIT_MIN_WIDTH_VALUES,
34
34
  DIFF_VIEW_MODES,
35
35
  EXCLUDE_RENDERER_CANDIDATES,
36
+ EXPANDED_CARD_BACKGROUND_SLOTS,
36
37
  EXPANDED_INPUT_MAX_LINES_VALUES,
37
38
  EXPANDED_OUTPUT_MAX_LINES_VALUES,
38
39
  EXPANDED_PREVIEW_MAX_LINES_VALUES,
39
40
  formatExcludeRenderers,
40
41
  getCompactThinkingConfig,
42
+ pickInputClip,
41
43
  pickPositiveInt,
42
44
  pickPositiveNumber,
43
45
  SCROLL_STEP_LINES_VALUES,
@@ -200,6 +202,14 @@ function diffIndicatorDescription(mode: DiffIndicatorMode): string {
200
202
  return "Vertical bar indicators on changed lines (default).";
201
203
  }
202
204
 
205
+ function expandedCardBackgroundDescription(slot: ThemeBg): string {
206
+ if (slot === "userMessageBg") {
207
+ return "Expanded cards share the user message background. Pick another slot to tell them apart.";
208
+ }
209
+ if (slot === "toolPendingBg") return "Expanded cards use the theme's neutral tool background.";
210
+ return `Expanded cards use the theme's ${slot} background.`;
211
+ }
212
+
203
213
  /** 额外功能开关项:on/off 二值,描述随状态切换;切换后需重启生效。 */
204
214
  function featureToggleSetting(
205
215
  id: string,
@@ -403,7 +413,7 @@ export async function showCcstylePanel(
403
413
  id: "inputClip",
404
414
  label: "Input clip",
405
415
  description:
406
- "Max characters for path/command/name in single and grouped tool summaries. Enter to type a custom value.",
416
+ "Max characters for path/command/name in single and grouped tool summaries; 0 fits available width. Enter to type a custom value.",
407
417
  currentValue: String(config.inputClip),
408
418
  values: [...INPUT_CLIP_VALUES],
409
419
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
@@ -491,6 +501,13 @@ export async function showCcstylePanel(
491
501
  submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
492
502
  buildNumberInputSubmenu(theme, expandedMaxSetting, closeSubmenu),
493
503
  };
504
+ const expandedCardBgSetting = {
505
+ id: "expandedCardBackground",
506
+ label: "Expanded card bg",
507
+ description: expandedCardBackgroundDescription(config.expandedCardBackground),
508
+ currentValue: config.expandedCardBackground,
509
+ values: [...EXPANDED_CARD_BACKGROUND_SLOTS],
510
+ };
494
511
  const thinkingTitleSetting = {
495
512
  id: "useSummaryTitlesAsThinkingTitle",
496
513
  label: "Summary title",
@@ -667,7 +684,7 @@ export async function showCcstylePanel(
667
684
  switch (id) {
668
685
  case "inputClip":
669
686
  updateConfig({
670
- inputClip: pickPositiveInt(value, DEFAULT_CONFIG.inputClip, 8, 500),
687
+ inputClip: pickInputClip(value),
671
688
  });
672
689
  inputClipSetting.currentValue = String(config.inputClip);
673
690
  break;
@@ -758,6 +775,12 @@ export async function showCcstylePanel(
758
775
  });
759
776
  expandedMaxSetting.currentValue = String(config.expandedPreviewMaxLines);
760
777
  break;
778
+ case "expandedCardBackground":
779
+ updateConfig({ expandedCardBackground: value as ThemeBg });
780
+ expandedCardBgSetting.description = expandedCardBackgroundDescription(
781
+ config.expandedCardBackground,
782
+ );
783
+ break;
761
784
  case "useSummaryTitlesAsThinkingTitle":
762
785
  updateConfig({ useSummaryTitlesAsThinkingTitle: value === "on" });
763
786
  break;
@@ -826,6 +849,7 @@ export async function showCcstylePanel(
826
849
  expandedInputSetting,
827
850
  expandedOutputSetting,
828
851
  expandedMaxSetting,
852
+ expandedCardBgSetting,
829
853
  inputClipSetting,
830
854
  startupHeaderSetting,
831
855
  scrollStepSetting,
@@ -18,6 +18,8 @@ import {
18
18
  wrapTextWithAnsi,
19
19
  } from "@earendil-works/pi-tui";
20
20
  import { Box, Markdown, Spacer, Text, type Component } from "@earendil-works/pi-tui";
21
+ // 展开卡背景是 ccstyle 的渲染约定,不随上游 CompactThinkingConfig 走,直接读全局配置。
22
+ import { config as styleConfig } from "../config/config.ts";
21
23
  import { isToolTuiFullscreen } from "../renderer/tool/show-more-hint.ts";
22
24
  import {
23
25
  animateCompactThinkingText,
@@ -309,14 +311,15 @@ function hiddenPreviewHint(
309
311
  const action = thinkingExpandAction() ?? "";
310
312
  if (hiddenLines > 0) {
311
313
  const noun = hiddenLines === 1 ? "line" : "lines";
314
+ // 与其它提示同一套写法:小圆点连接,不用逗号和括号
312
315
  return {
313
- prefix: ` • (${hiddenLines} more ${noun}${action ? ", " : ""}`,
316
+ prefix: ` · ${hiddenLines} more ${noun}${action ? " · " : ""}`,
314
317
  action,
315
- suffix: ")",
318
+ suffix: "",
316
319
  };
317
320
  }
318
321
  if (forceExpandHint && isToolTuiFullscreen()) {
319
- return { prefix: " • ", action: "click to show more", suffix: "" };
322
+ return { prefix: " · ", action: "click to show more", suffix: "" };
320
323
  }
321
324
  return undefined;
322
325
  }
@@ -432,7 +435,7 @@ export class ThinkingPreviewBlock implements Component {
432
435
  : heading;
433
436
  const bgFn =
434
437
  this.theme && typeof this.theme.bg === "function"
435
- ? (text: string) => this.theme!.bg("userMessageBg" as never, text)
438
+ ? (text: string) => this.theme!.bg(styleConfig.expandedCardBackground, text)
436
439
  : undefined;
437
440
  const box = new Box(1, 1, bgFn);
438
441
  box.addChild({
@@ -28,18 +28,24 @@ import {
28
28
  visibleFooterPluginTexts,
29
29
  type FooterChipLayout,
30
30
  } from "./footer-layout.ts";
31
+ import { MCP_STATUS_KEY, buildMcpChip } from "./mcp-chip.ts";
31
32
 
32
33
  const GIT_REFRESH_INTERVAL_MS = 10_000;
33
34
  const USAGE_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
35
+ // 后台连接 MCP 服务器不会触发 footer 重绘,靠这个兜底轮询把芯片刷出来
36
+ const MCP_REFRESH_INTERVAL_MS = 5_000;
34
37
  const USAGE_TIMEOUT_MS = 15_000;
35
38
  // zentui NERD_DEFAULT_ICONS:git / cacheHit。无 Nerd Font 时留空,只保留文字。
36
39
  export const FOOTER_NERD_ICON_GIT = "";
37
40
  export const FOOTER_NERD_ICON_CACHE = "󰆼";
38
41
 
39
- export function footerGlyphs(nerdIcons: boolean): { git: string; cache: string } {
42
+ // md power-plug:MCP 服务器连接数
43
+ export const FOOTER_NERD_ICON_MCP = "\u{f06a5}";
44
+
45
+ export function footerGlyphs(nerdIcons: boolean): { git: string; cache: string; mcp: string } {
40
46
  return nerdIcons
41
- ? { git: FOOTER_NERD_ICON_GIT, cache: FOOTER_NERD_ICON_CACHE }
42
- : { git: "", cache: "" };
47
+ ? { git: FOOTER_NERD_ICON_GIT, cache: FOOTER_NERD_ICON_CACHE, mcp: FOOTER_NERD_ICON_MCP }
48
+ : { git: "", cache: "", mcp: "" };
43
49
  }
44
50
 
45
51
  type GitStats = { add: number; del: number };
@@ -97,8 +103,15 @@ export function formatXaiFooterChip(report: XaiFooterReport): string | undefined
97
103
 
98
104
  const cachedExtensionStatuses = new Map<string, string>();
99
105
  let cachedLocalUsageText = "";
106
+ /** 本包推算的内置 MCP 芯片(含 nerd 图标);空字符串表示不显示。 */
107
+ let cachedMcpChip = "";
108
+
109
+ /** 别的扩展(pi-mcp-adapter)自己写了 mcp 状态时不画,避免两个芯片。 */
110
+ function ownMcpChip(): string {
111
+ return cachedMcpChip && !cachedExtensionStatuses.has(MCP_STATUS_KEY) ? cachedMcpChip : "";
112
+ }
100
113
 
101
- /** 面板用:当前 setStatus 文案 + 本包 pi-usage(无文案时为空字符串)。 */
114
+ /** 面板用:当前 setStatus 文案 + 本包 pi-usage / MCP(无文案时为空字符串)。 */
102
115
  export function getFooterStatusSnapshot(): Map<string, string> {
103
116
  const out = new Map<string, string>();
104
117
  for (const [key, text] of cachedExtensionStatuses) {
@@ -106,6 +119,8 @@ export function getFooterStatusSnapshot(): Map<string, string> {
106
119
  out.set(key, text);
107
120
  }
108
121
  out.set(PI_USAGE_KEY, cachedLocalUsageText);
122
+ const mcpChip = ownMcpChip();
123
+ if (mcpChip) out.set(MCP_STATUS_KEY, mcpChip);
109
124
  return out;
110
125
  }
111
126
 
@@ -167,6 +182,9 @@ function colorUsageChip(theme: any, text: string): string {
167
182
  let currentTui: any = undefined;
168
183
  let refreshCurrentGitStats: (() => void) | undefined;
169
184
  let refreshCurrentUsage: (() => void) | undefined;
185
+ let refreshCurrentMcpChip: (() => void) | undefined;
186
+ /** 工具/命令列表在 pi API 上,而 footer 工厂只拿得到 ctx,加载时记下来。 */
187
+ let extensionApi: Pick<ExtensionAPI, "getAllTools" | "getCommands"> | undefined;
170
188
 
171
189
  const createCustomFooterFactory =
172
190
  (ctx: ExtensionContext) => (tui: any, theme: any, footerData: any) => {
@@ -281,6 +299,21 @@ const createCustomFooterFactory =
281
299
  const usageRefreshTimer = setInterval(refreshUsage, USAGE_REFRESH_INTERVAL_MS);
282
300
  usageRefreshTimer.unref?.();
283
301
 
302
+ // 内置 MCP 的状态不经 setStatus 暴露,只能轮询 pi API 与 mcp.json 推算
303
+ const refreshMcp = () => {
304
+ if (disposed) return;
305
+ const chip = buildMcpChip(ctx, extensionApi);
306
+ const glyph = footerGlyphs(config.footerNerdIcons).mcp;
307
+ const next = chip ? `${glyph ? `${glyph} ` : ""}${chip}` : "";
308
+ if (cachedMcpChip === next) return;
309
+ cachedMcpChip = next;
310
+ tui.requestRender();
311
+ };
312
+ refreshCurrentMcpChip = refreshMcp;
313
+ refreshMcp();
314
+ const mcpRefreshTimer = setInterval(refreshMcp, MCP_REFRESH_INTERVAL_MS);
315
+ mcpRefreshTimer.unref?.();
316
+
284
317
  // 分支变化时同步更新分支名和相对 HEAD 的统计。
285
318
  const unsubBranch = footerData.onBranchChange(() => {
286
319
  refreshGitStats();
@@ -359,6 +392,8 @@ const createCustomFooterFactory =
359
392
  footerData.getExtensionStatuses().entries() as Iterable<[string, string]>,
360
393
  );
361
394
  const pluginTexts = pluginTextsForRender(localUsageChip);
395
+ const mcpChip = ownMcpChip();
396
+ if (mcpChip) pluginTexts.set(MCP_STATUS_KEY, mcpChip);
362
397
  const layout = resolveFooterChipLayout(layoutFromConfig(), [...pluginTexts.keys()]);
363
398
  const dimPlugin = (text: string) => colorUsageChip(theme, text);
364
399
  const line1Plugins = visibleFooterPluginTexts(
@@ -436,11 +471,13 @@ const createCustomFooterFactory =
436
471
  usageAbort = undefined;
437
472
  clearInterval(gitRefreshTimer);
438
473
  clearInterval(usageRefreshTimer);
474
+ clearInterval(mcpRefreshTimer);
439
475
  unsubBranch();
440
476
  if (currentTui === tui) {
441
477
  currentTui = undefined;
442
478
  refreshCurrentGitStats = undefined;
443
479
  refreshCurrentUsage = undefined;
480
+ refreshCurrentMcpChip = undefined;
444
481
  }
445
482
  },
446
483
  };
@@ -463,6 +500,7 @@ export function clearCustomFooter(ctx: ExtensionContext): void {
463
500
  }
464
501
 
465
502
  export default function (pi: ExtensionAPI) {
503
+ extensionApi = pi;
466
504
  // 模型/思考级别变化时强制重渲染(自定义 footer 不会被内置 invalidate() 触达)
467
505
  pi.on("model_select", () => {
468
506
  currentTui?.requestRender();
@@ -470,7 +508,11 @@ export default function (pi: ExtensionAPI) {
470
508
  });
471
509
  pi.on("thinking_level_select", () => currentTui?.requestRender());
472
510
  // 工具执行完成后立即刷新;定时器只负责兜底捕获外部文件变化。
473
- pi.on("tool_execution_end", () => refreshCurrentGitStats?.());
511
+ pi.on("tool_execution_end", () => {
512
+ refreshCurrentGitStats?.();
513
+ // 懒连接的 MCP 服务器可能刚被调用过
514
+ refreshCurrentMcpChip?.();
515
+ });
474
516
 
475
517
  // 启动 / /reload / 新建会话 时按配置恢复
476
518
  pi.on("session_start", (_event, ctx) => {