pi-cc-extensions 0.9.11 → 1.0.0
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 +5 -3
- package/README.md +5 -3
- package/extensions/config/config.ts +28 -10
- package/extensions/config/panel.ts +60 -20
- package/extensions/feature/shell/footer.ts +35 -49
- package/extensions/feature/shell/git-stats.ts +162 -0
- package/extensions/renderer/compact-mode.ts +121 -28
- package/extensions/renderer/default-mode.ts +152 -24
- package/extensions/renderer/index.ts +30 -1
- package/extensions/renderer/mouse/interaction.ts +22 -4
- package/extensions/renderer/mouse/sticky-prompt.ts +398 -0
- package/extensions/renderer/tool/ask-user-question.ts +173 -0
- package/extensions/renderer/tool/codemode.ts +4 -8
- package/extensions/renderer/tool/diff/diff-edit-render.ts +64 -11
- package/extensions/renderer/tool/diff/diff-presentation.ts +41 -0
- package/extensions/renderer/tool/diff/diff-renderer.ts +5 -1
- package/extensions/renderer/tool/diff/diff-write-render.ts +35 -9
- package/extensions/renderer/tool/grouping.ts +115 -24
- package/extensions/renderer/tool/names.ts +3 -3
- package/extensions/renderer/tool/result.ts +106 -15
- package/extensions/utils/patch-keys.ts +5 -0
- package/package.json +1 -4
- package/themes/cc-dark.json +1 -1
- package/themes/cc-light.json +1 -1
package/README.en.md
CHANGED
|
@@ -61,14 +61,15 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
|
|
|
61
61
|
"expandedOutputMaxLines": 10, // expanded tool Output lines; overflow shows a footer hint
|
|
62
62
|
"expandedPreviewMaxLines": 40, // max lines for expanded TaskList bodies (expanded diffs always show all)
|
|
63
63
|
"expandedCardBackground": "userMessageBg", // expanded card background slot: userMessageBg (same as user prompts) / toolPendingBg (neutral) etc.
|
|
64
|
-
"
|
|
64
|
+
"toolLabelClip": 0, // tool summary path/command clip length; 0 = fit width
|
|
65
65
|
"showStartupHeader": true, // startup header (logo + tips) toggle
|
|
66
|
+
"stickyUserMessage": true, // fullscreen: pin a user message's first line once it scrolls off the top edge, until the next one takes over
|
|
66
67
|
|
|
67
68
|
// diff
|
|
68
69
|
"diffViewMode": "auto", // layout: auto / split / unified
|
|
69
70
|
"diffIndicatorMode": "bars", // change indicators: bars / classic / none
|
|
70
71
|
"diffSplitMinWidth": 120, // min terminal width for side-by-side columns
|
|
71
|
-
"editDiffCollapsedLines": 24, //
|
|
72
|
+
"editDiffCollapsedLines": 24, // edit collapse lines; 0 = change-count summary only
|
|
72
73
|
"writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
|
|
73
74
|
"diffWordWrap": true, // wrap long diff lines
|
|
74
75
|
|
|
@@ -83,6 +84,7 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
|
|
|
83
84
|
"footerCurrency": "USD", // display currency for the footer cost
|
|
84
85
|
"footerCurrencyRate": null, // display units per 1 cost unit; null changes the symbol only
|
|
85
86
|
"footerNerdIcons": true, // Nerd Font glyphs for git/cache; false = plain text
|
|
87
|
+
"footerGitStatsMode": "working", // working (default): uncommitted; branch: cumulative net changes
|
|
86
88
|
"footerHiddenKeys": [], // hidden plugin chip keys
|
|
87
89
|
"footerLine1Keys": ["pi-usage"], // line1 plugin chip order; pi-usage is shown by default, data from @narumitw/pi-usage
|
|
88
90
|
"footerLine2Keys": [], // line2 plugin chip order (after cwd/git)
|
|
@@ -91,7 +93,7 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
|
|
|
91
93
|
```
|
|
92
94
|
|
|
93
95
|
> [!TIP]
|
|
94
|
-
> **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
|
|
96
|
+
> **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.
|
|
95
97
|
|
|
96
98
|
> [!NOTE]
|
|
97
99
|
> **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.
|
package/README.md
CHANGED
|
@@ -61,14 +61,15 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
61
61
|
"expandedOutputMaxLines": 10, // 展开工具卡 Output 可见行数,超出在末行显示展开提示
|
|
62
62
|
"expandedPreviewMaxLines": 40, // 展开 TaskList 正文最大行数(展开 diff 始终全量)
|
|
63
63
|
"expandedCardBackground": "userMessageBg", // 展开卡背景槽位;userMessageBg 与用户消息同色,toolPendingBg 为中性灰
|
|
64
|
-
"
|
|
64
|
+
"toolLabelClip": 0, // 工具摘要 path/command 折叠字符数;0 = 按可用宽度
|
|
65
65
|
"showStartupHeader": true, // 启动头(logo + tips)开关
|
|
66
|
+
"stickyUserMessage": true, // fullscreen:userMessage 正文首行滚出视口上沿后钉住,直到下一条指令接手
|
|
66
67
|
|
|
67
68
|
// diff
|
|
68
69
|
"diffViewMode": "auto", // 布局:auto / split / unified
|
|
69
70
|
"diffIndicatorMode": "bars", // 变更指示:bars / classic / none
|
|
70
71
|
"diffSplitMinWidth": 120, // 左右分栏的最小终端宽度
|
|
71
|
-
"editDiffCollapsedLines": 24, //
|
|
72
|
+
"editDiffCollapsedLines": 24, // edit 折叠行数;0 仅显示变更统计摘要
|
|
72
73
|
"writeDiffCollapsedLines": 0, // write 折叠行数,0 仅显示创建摘要
|
|
73
74
|
"diffWordWrap": true, // 长 diff 行换行
|
|
74
75
|
|
|
@@ -83,6 +84,7 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
83
84
|
"footerCurrency": "USD", // 底栏费用的显示币种
|
|
84
85
|
"footerCurrencyRate": null, // 1 单位费用换成多少显示币种;null 只换符号、不乘倍率
|
|
85
86
|
"footerNerdIcons": true, // git/缓存用 Nerd Font 图标;false 为纯文本
|
|
87
|
+
"footerGitStatsMode": "working", // working(默认):未提交改动;branch:分支累计净改动
|
|
86
88
|
"footerHiddenKeys": [], // 隐藏的插件芯片 key
|
|
87
89
|
"footerLine1Keys": ["pi-usage"], // line1 插件芯片顺序;pi-usage 默认显示,数据来自 @narumitw/pi-usage
|
|
88
90
|
"footerLine2Keys": [], // line2 插件芯片顺序(接在 cwd/git 后)
|
|
@@ -91,7 +93,7 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
91
93
|
```
|
|
92
94
|
|
|
93
95
|
> [!TIP]
|
|
94
|
-
> **全屏模式**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要;工具卡展开态 diff 全量显示,Input/Output 超行时末行 `… +N more lines · click to show more`
|
|
96
|
+
> **全屏模式**:单击 `click to show more` 展开工具卡、思考、Skill 和 compact 摘要;工具卡展开态 diff 全量显示,Input/Output 超行时末行 `… +N more lines · click to show more` 打开全量预览,单击收起。
|
|
95
97
|
|
|
96
98
|
> [!NOTE]
|
|
97
99
|
> **Mermaid 渲染**:建议把 `markdown.mermaid` 设为 `final`(`~/.pi/agent/settings.json` 或 `/settings` 面板的 Mermaid diagrams 选项)。默认 `streaming` 逐帧重绘,`final` 渲染最终版更稳定。
|
|
@@ -13,11 +13,13 @@ export type CompactStyleMode = "on" | "compact" | "off";
|
|
|
13
13
|
|
|
14
14
|
export type DiffViewMode = "auto" | "split" | "unified";
|
|
15
15
|
export type DiffIndicatorMode = "bars" | "classic" | "none";
|
|
16
|
+
export type FooterGitStatsMode = "working" | "branch";
|
|
16
17
|
|
|
17
18
|
export interface ToolDisplayConfig {
|
|
18
19
|
diffViewMode: DiffViewMode;
|
|
19
20
|
diffIndicatorMode: DiffIndicatorMode;
|
|
20
21
|
diffSplitMinWidth: number;
|
|
22
|
+
/** Edit-only collapsed body lines. 0 = change counts and an expand hint. */
|
|
21
23
|
editDiffCollapsedLines: number;
|
|
22
24
|
/** Write-only collapsed body lines. 0 = `↳ created · click to show more`. */
|
|
23
25
|
writeDiffCollapsedLines: number;
|
|
@@ -57,12 +59,14 @@ export type Config = {
|
|
|
57
59
|
expandedCardBackground: ThemeBg;
|
|
58
60
|
expandedInputMaxLines: number;
|
|
59
61
|
expandedOutputMaxLines: number;
|
|
60
|
-
|
|
62
|
+
toolLabelClip: number;
|
|
61
63
|
useSummaryTitlesAsThinkingTitle: boolean;
|
|
62
64
|
previewLines: number;
|
|
63
65
|
animationIntervalMs: number;
|
|
64
66
|
dimThinkingText: boolean;
|
|
65
67
|
showStartupHeader: boolean;
|
|
68
|
+
/** fullscreen:userMessage 正文首行滚出视口上沿后钉住,直到下一条指令接手。 */
|
|
69
|
+
stickyUserMessage: boolean;
|
|
66
70
|
enableSessionReference: boolean;
|
|
67
71
|
enableSubagentAutocomplete: boolean;
|
|
68
72
|
enableContextCommand: boolean;
|
|
@@ -75,6 +79,7 @@ export type Config = {
|
|
|
75
79
|
/** 1 单位费用对应的显示币种数量。null 只换符号、不乘倍率。 */
|
|
76
80
|
footerCurrencyRate: number | null;
|
|
77
81
|
footerNerdIcons: boolean;
|
|
82
|
+
footerGitStatsMode: FooterGitStatsMode;
|
|
78
83
|
footerHiddenKeys: string[];
|
|
79
84
|
footerLine1Keys: string[];
|
|
80
85
|
footerLine2Keys: string[];
|
|
@@ -87,8 +92,9 @@ const LEGACY_CONFIG_PATH = join(AGENT_DIR, "claude-code-style.json");
|
|
|
87
92
|
|
|
88
93
|
export const DIFF_VIEW_MODES: DiffViewMode[] = ["auto", "split", "unified"];
|
|
89
94
|
export const DIFF_INDICATOR_MODES: DiffIndicatorMode[] = ["bars", "classic", "none"];
|
|
95
|
+
export const FOOTER_GIT_STATS_MODES: FooterGitStatsMode[] = ["working", "branch"];
|
|
90
96
|
export const DIFF_SPLIT_MIN_WIDTH_VALUES = ["80", "100", "120", "140", "160", "180"];
|
|
91
|
-
export const DIFF_COLLAPSED_LINES_VALUES = ["12", "24", "36", "48", "80", "120"];
|
|
97
|
+
export const DIFF_COLLAPSED_LINES_VALUES = ["0", "12", "24", "36", "48", "80", "120"];
|
|
92
98
|
/** Write collapsed presets. 0 = stats only (`+N -0` + expand hint). */
|
|
93
99
|
export const WRITE_DIFF_COLLAPSED_LINES_VALUES = ["0", "4", "8", "12", "24", "36"];
|
|
94
100
|
/** Presets for expanded body height — keep low options first so cycling stays TUI-friendly. */
|
|
@@ -112,7 +118,7 @@ export const EXPANDED_INPUT_MAX_LINES_VALUES = ["5", "10", "20", "40", "80"];
|
|
|
112
118
|
/** 展开工具卡 Output 可见行数预设。 */
|
|
113
119
|
export const EXPANDED_OUTPUT_MAX_LINES_VALUES = ["10", "20", "40", "80", "120"];
|
|
114
120
|
/** 工具摘要里 path/command 等输入的折叠字符数;0 = 只按可用宽度截断。 */
|
|
115
|
-
export const
|
|
121
|
+
export const TOOL_LABEL_CLIP_VALUES = ["0", "40", "60", "80", "100", "120", "160"];
|
|
116
122
|
export const THINKING_PREVIEW_LINES_VALUES = ["0", "1", "3", "5", "10"];
|
|
117
123
|
export const THINKING_ANIMATION_INTERVAL_VALUES = ["40", "60", "90", "120", "180"];
|
|
118
124
|
/** Tools commonly toggled in excludeRenderers via the settings panel. */
|
|
@@ -143,12 +149,13 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
143
149
|
expandedCardBackground: "userMessageBg",
|
|
144
150
|
expandedInputMaxLines: 5,
|
|
145
151
|
expandedOutputMaxLines: 10,
|
|
146
|
-
|
|
152
|
+
toolLabelClip: 0,
|
|
147
153
|
useSummaryTitlesAsThinkingTitle: true,
|
|
148
154
|
previewLines: 3,
|
|
149
155
|
animationIntervalMs: 90,
|
|
150
156
|
dimThinkingText: false,
|
|
151
157
|
showStartupHeader: true,
|
|
158
|
+
stickyUserMessage: true,
|
|
152
159
|
enableSessionReference: true,
|
|
153
160
|
enableSubagentAutocomplete: true,
|
|
154
161
|
enableContextCommand: true,
|
|
@@ -159,6 +166,7 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
159
166
|
footerCurrency: "USD",
|
|
160
167
|
footerCurrencyRate: null,
|
|
161
168
|
footerNerdIcons: true,
|
|
169
|
+
footerGitStatsMode: "working",
|
|
162
170
|
...DEFAULT_FOOTER_CHIP_LAYOUT,
|
|
163
171
|
};
|
|
164
172
|
|
|
@@ -174,10 +182,10 @@ export function pickPositiveInt(value: unknown, fallback: number, min = 1, max =
|
|
|
174
182
|
return Math.min(max, Math.max(min, Math.floor(n)));
|
|
175
183
|
}
|
|
176
184
|
|
|
177
|
-
/**
|
|
178
|
-
export function
|
|
185
|
+
/** toolLabelClip:0 表示按宽度自适应,其余夹到 8..500。 */
|
|
186
|
+
export function pickToolLabelClip(value: unknown): number {
|
|
179
187
|
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
180
|
-
return n === 0 ? 0 : pickPositiveInt(value, DEFAULT_CONFIG.
|
|
188
|
+
return n === 0 ? 0 : pickPositiveInt(value, DEFAULT_CONFIG.toolLabelClip, 8, 500);
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
export function pickPositiveNumber(value: unknown, fallback: number, min = 1): number {
|
|
@@ -228,7 +236,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
228
236
|
editDiffCollapsedLines: pickPositiveInt(
|
|
229
237
|
source.editDiffCollapsedLines,
|
|
230
238
|
DEFAULT_CONFIG.editDiffCollapsedLines,
|
|
231
|
-
|
|
239
|
+
0,
|
|
232
240
|
500,
|
|
233
241
|
),
|
|
234
242
|
writeDiffCollapsedLines: pickPositiveInt(
|
|
@@ -261,7 +269,9 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
261
269
|
1,
|
|
262
270
|
5_000,
|
|
263
271
|
),
|
|
264
|
-
|
|
272
|
+
toolLabelClip: pickToolLabelClip(
|
|
273
|
+
source.toolLabelClip ?? source.inputClip, // 兼容旧配置键
|
|
274
|
+
),
|
|
265
275
|
useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle !== false,
|
|
266
276
|
previewLines: pickPositiveInt(
|
|
267
277
|
source.previewLines,
|
|
@@ -275,6 +285,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
275
285
|
),
|
|
276
286
|
dimThinkingText: source.dimThinkingText === true,
|
|
277
287
|
showStartupHeader: source.showStartupHeader !== false,
|
|
288
|
+
stickyUserMessage: source.stickyUserMessage !== false,
|
|
278
289
|
enableSessionReference: source.enableSessionReference !== false,
|
|
279
290
|
enableSubagentAutocomplete: source.enableSubagentAutocomplete !== false,
|
|
280
291
|
enableContextCommand: source.enableContextCommand !== false,
|
|
@@ -285,6 +296,11 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
285
296
|
footerCurrency: normalizeCurrency(source.footerCurrency),
|
|
286
297
|
footerCurrencyRate: normalizeFooterCurrencyRate(source.footerCurrencyRate),
|
|
287
298
|
footerNerdIcons: source.footerNerdIcons !== false,
|
|
299
|
+
footerGitStatsMode: pickEnum(
|
|
300
|
+
source.footerGitStatsMode,
|
|
301
|
+
FOOTER_GIT_STATS_MODES,
|
|
302
|
+
DEFAULT_CONFIG.footerGitStatsMode,
|
|
303
|
+
),
|
|
288
304
|
...normalizeFooterChipLayout(source),
|
|
289
305
|
};
|
|
290
306
|
}
|
|
@@ -326,12 +342,13 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
326
342
|
`expandedMax=${source.expandedPreviewMaxLines}`,
|
|
327
343
|
`expandedInput=${source.expandedInputMaxLines}`,
|
|
328
344
|
`expandedOutput=${source.expandedOutputMaxLines}`,
|
|
329
|
-
`
|
|
345
|
+
`toolLabelClip=${source.toolLabelClip}`,
|
|
330
346
|
`thinkingTitle=${source.useSummaryTitlesAsThinkingTitle ? "summary" : "default"}`,
|
|
331
347
|
`thinkingPreview=${source.previewLines}`,
|
|
332
348
|
`thinkingAnimation=${source.animationIntervalMs}ms`,
|
|
333
349
|
`thinkingDim=${source.dimThinkingText ? "on" : "off"}`,
|
|
334
350
|
`startupHeader=${source.showStartupHeader ? "on" : "off"}`,
|
|
351
|
+
`stickyUserMessage=${source.stickyUserMessage ? "on" : "off"}`,
|
|
335
352
|
`sessionRef=${source.enableSessionReference ? "on" : "off"}`,
|
|
336
353
|
`subagentAuto=${source.enableSubagentAutocomplete ? "on" : "off"}`,
|
|
337
354
|
`context=${source.enableContextCommand ? "on" : "off"}`,
|
|
@@ -342,6 +359,7 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
342
359
|
`footerCurrency=${source.footerCurrency}`,
|
|
343
360
|
`footerRate=${source.footerCurrencyRate ?? "-"}`,
|
|
344
361
|
`footerIcons=${source.footerNerdIcons ? "nerd" : "plain"}`,
|
|
362
|
+
`footerGitStats=${source.footerGitStatsMode}`,
|
|
345
363
|
formatFooterChipSummary(source),
|
|
346
364
|
].join(" · ");
|
|
347
365
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
applyCustomFooter,
|
|
12
12
|
clearCustomFooter,
|
|
13
13
|
getFooterStatusSnapshot,
|
|
14
|
+
refreshFooterGitStats,
|
|
14
15
|
} from "../feature/shell/footer.ts";
|
|
15
16
|
import {
|
|
16
17
|
FOOTER_CURRENCY_PRESETS,
|
|
@@ -41,20 +42,22 @@ import {
|
|
|
41
42
|
EXPANDED_INPUT_MAX_LINES_VALUES,
|
|
42
43
|
EXPANDED_OUTPUT_MAX_LINES_VALUES,
|
|
43
44
|
EXPANDED_PREVIEW_MAX_LINES_VALUES,
|
|
45
|
+
FOOTER_GIT_STATS_MODES,
|
|
44
46
|
formatExcludeRenderers,
|
|
45
47
|
getCompactThinkingConfig,
|
|
46
|
-
pickInputClip,
|
|
47
48
|
pickPositiveInt,
|
|
48
49
|
pickPositiveNumber,
|
|
50
|
+
pickToolLabelClip,
|
|
49
51
|
THINKING_ANIMATION_INTERVAL_VALUES,
|
|
50
52
|
THINKING_PREVIEW_LINES_VALUES,
|
|
51
|
-
|
|
53
|
+
TOOL_LABEL_CLIP_VALUES,
|
|
52
54
|
WRITE_DIFF_COLLAPSED_LINES_VALUES,
|
|
53
55
|
updateConfig,
|
|
54
56
|
type CompactStyleMode,
|
|
55
57
|
type Config,
|
|
56
58
|
type DiffIndicatorMode,
|
|
57
59
|
type DiffViewMode,
|
|
60
|
+
type FooterGitStatsMode,
|
|
58
61
|
} from "./config.ts";
|
|
59
62
|
|
|
60
63
|
/** renderer 注入的渲染副作用,面板自身不触碰渲染状态。 */
|
|
@@ -65,7 +68,7 @@ export type CcstylePanelHooks = {
|
|
|
65
68
|
|
|
66
69
|
function modeSettingDescription(mode: CompactStyleMode): string {
|
|
67
70
|
if (mode === "compact") {
|
|
68
|
-
return "
|
|
71
|
+
return "One summary line per assistant round; edit/write reuse the same Diff preview settings as on.";
|
|
69
72
|
}
|
|
70
73
|
if (mode === "off") {
|
|
71
74
|
return "Pi native tool rendering. Diff options below still apply independently.";
|
|
@@ -84,6 +87,12 @@ function customFooterDescription(enabled: boolean): string {
|
|
|
84
87
|
return "Custom status bar with model, context, cache, cost, git, and plugin chips.";
|
|
85
88
|
}
|
|
86
89
|
|
|
90
|
+
function footerGitStatsDescription(mode: FooterGitStatsMode): string {
|
|
91
|
+
return mode === "branch"
|
|
92
|
+
? "Net changes since the merge base, including uncommitted edits. Base: origin/HEAD, then local main/master. Untracked files excluded; unavailable stats hidden."
|
|
93
|
+
: "Uncommitted net changes relative to HEAD (default). Includes staged and unstaged edits; excludes untracked files.";
|
|
94
|
+
}
|
|
95
|
+
|
|
87
96
|
function pluginChipsDescription(): string {
|
|
88
97
|
return `Arrange plugin chips: ${formatFooterChipSummary(config)}. Enter to edit.`;
|
|
89
98
|
}
|
|
@@ -413,8 +422,8 @@ export async function showCcstylePanel(
|
|
|
413
422
|
id: "mode",
|
|
414
423
|
label: "Mode",
|
|
415
424
|
description: modeSettingDescription(config.mode),
|
|
416
|
-
currentValue: config.mode
|
|
417
|
-
values: ["on", "compact
|
|
425
|
+
currentValue: config.mode,
|
|
426
|
+
values: ["on", "compact", "off"],
|
|
418
427
|
};
|
|
419
428
|
// Tracks whether a nested submenu is open so Tab switches sections
|
|
420
429
|
// only at the top level (mirrors Zentui settings: Tab = switch sections).
|
|
@@ -444,15 +453,15 @@ export async function showCcstylePanel(
|
|
|
444
453
|
);
|
|
445
454
|
},
|
|
446
455
|
};
|
|
447
|
-
const
|
|
448
|
-
id: "
|
|
449
|
-
label: "
|
|
456
|
+
const toolLabelClipSetting = {
|
|
457
|
+
id: "toolLabelClip",
|
|
458
|
+
label: "Tool label clip",
|
|
450
459
|
description:
|
|
451
460
|
"Max characters for path/command/name in single and grouped tool summaries; 0 fits available width. Enter to type a custom value.",
|
|
452
|
-
currentValue: String(config.
|
|
453
|
-
values: [...
|
|
461
|
+
currentValue: String(config.toolLabelClip),
|
|
462
|
+
values: [...TOOL_LABEL_CLIP_VALUES],
|
|
454
463
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
455
|
-
buildNumberInputSubmenu(theme,
|
|
464
|
+
buildNumberInputSubmenu(theme, toolLabelClipSetting, closeSubmenu),
|
|
456
465
|
};
|
|
457
466
|
const diffViewSetting = {
|
|
458
467
|
id: "diffViewMode",
|
|
@@ -482,7 +491,7 @@ export async function showCcstylePanel(
|
|
|
482
491
|
id: "editDiffCollapsedLines",
|
|
483
492
|
label: "Edit collapsed lines",
|
|
484
493
|
description:
|
|
485
|
-
"
|
|
494
|
+
"Edit diff body lines before the expand hint (Ctrl+O / click). 0 shows only change counts and the hint.",
|
|
486
495
|
currentValue: String(config.editDiffCollapsedLines),
|
|
487
496
|
values: [...DIFF_COLLAPSED_LINES_VALUES],
|
|
488
497
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
@@ -586,6 +595,15 @@ export async function showCcstylePanel(
|
|
|
586
595
|
currentValue: config.showStartupHeader ? "on" : "off",
|
|
587
596
|
values: ["on", "off"],
|
|
588
597
|
};
|
|
598
|
+
const stickyUserMessageSetting = {
|
|
599
|
+
id: "stickyUserMessage",
|
|
600
|
+
label: "Sticky user message",
|
|
601
|
+
description: config.stickyUserMessage
|
|
602
|
+
? "Fullscreen: pin a user message's first line once it scrolls off the top edge, until the next one takes over."
|
|
603
|
+
: "User messages scroll normally.",
|
|
604
|
+
currentValue: config.stickyUserMessage ? "on" : "off",
|
|
605
|
+
values: ["on", "off"],
|
|
606
|
+
};
|
|
589
607
|
|
|
590
608
|
// 额外功能开关:注册于扩展加载期,切换后需重启(/reload)生效。
|
|
591
609
|
const sessionReferenceToggle = featureToggleSetting(
|
|
@@ -705,6 +723,13 @@ export async function showCcstylePanel(
|
|
|
705
723
|
currentValue: config.enableCustomFooter ? "on" : "off",
|
|
706
724
|
values: ["on", "off"],
|
|
707
725
|
};
|
|
726
|
+
const footerGitStatsSetting = {
|
|
727
|
+
id: "footerGitStatsMode",
|
|
728
|
+
label: "Git changes",
|
|
729
|
+
description: footerGitStatsDescription(config.footerGitStatsMode),
|
|
730
|
+
currentValue: config.footerGitStatsMode,
|
|
731
|
+
values: FOOTER_GIT_STATS_MODES,
|
|
732
|
+
};
|
|
708
733
|
const pluginChipsSetting = {
|
|
709
734
|
id: "footerPluginChips",
|
|
710
735
|
label: "Plugin chips",
|
|
@@ -769,6 +794,14 @@ export async function showCcstylePanel(
|
|
|
769
794
|
ctx.ui.notify(`Updated ${id}: ${footerCurrencyRateSetting.currentValue}`, "info");
|
|
770
795
|
return;
|
|
771
796
|
}
|
|
797
|
+
if (id === "footerGitStatsMode") {
|
|
798
|
+
updateConfig({ footerGitStatsMode: value as FooterGitStatsMode });
|
|
799
|
+
footerGitStatsSetting.currentValue = config.footerGitStatsMode;
|
|
800
|
+
footerGitStatsSetting.description = footerGitStatsDescription(config.footerGitStatsMode);
|
|
801
|
+
refreshFooterGitStats();
|
|
802
|
+
ctx.ui.notify(`Updated ${id}: ${value}`, "info");
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
772
805
|
if (id === "footerNerdIcons") {
|
|
773
806
|
const enabled = value === "on";
|
|
774
807
|
updateConfig({ footerNerdIcons: enabled });
|
|
@@ -788,16 +821,14 @@ export async function showCcstylePanel(
|
|
|
788
821
|
return;
|
|
789
822
|
}
|
|
790
823
|
switch (id) {
|
|
791
|
-
case "
|
|
824
|
+
case "toolLabelClip":
|
|
792
825
|
updateConfig({
|
|
793
|
-
|
|
826
|
+
toolLabelClip: pickToolLabelClip(value),
|
|
794
827
|
});
|
|
795
|
-
|
|
828
|
+
toolLabelClipSetting.currentValue = String(config.toolLabelClip);
|
|
796
829
|
break;
|
|
797
830
|
case "mode": {
|
|
798
|
-
|
|
799
|
-
const mode: CompactStyleMode =
|
|
800
|
-
value === "compact (Experimental)" ? "compact" : (value as CompactStyleMode);
|
|
831
|
+
const mode = value as CompactStyleMode;
|
|
801
832
|
modeSetting.description = modeSettingDescription(mode);
|
|
802
833
|
hooks.applyStyleMode(mode, ctx, toolGrouping);
|
|
803
834
|
return;
|
|
@@ -825,7 +856,7 @@ export async function showCcstylePanel(
|
|
|
825
856
|
editDiffCollapsedLines: pickPositiveInt(
|
|
826
857
|
value,
|
|
827
858
|
DEFAULT_CONFIG.editDiffCollapsedLines,
|
|
828
|
-
|
|
859
|
+
0,
|
|
829
860
|
500,
|
|
830
861
|
),
|
|
831
862
|
});
|
|
@@ -906,6 +937,13 @@ export async function showCcstylePanel(
|
|
|
906
937
|
? "Thinking text uses the theme's dim color."
|
|
907
938
|
: "Keep the default thinking text color.";
|
|
908
939
|
break;
|
|
940
|
+
case "stickyUserMessage": {
|
|
941
|
+
updateConfig({ stickyUserMessage: value === "on" });
|
|
942
|
+
stickyUserMessageSetting.description = config.stickyUserMessage
|
|
943
|
+
? "Fullscreen: pin a user message's first line once it scrolls off the top edge, until the next one takes over."
|
|
944
|
+
: "User messages scroll normally.";
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
909
947
|
case "showStartupHeader": {
|
|
910
948
|
const enabled = value === "on";
|
|
911
949
|
updateConfig({ showStartupHeader: enabled });
|
|
@@ -950,7 +988,8 @@ export async function showCcstylePanel(
|
|
|
950
988
|
expandedOutputSetting,
|
|
951
989
|
expandedMaxSetting,
|
|
952
990
|
expandedCardBgSetting,
|
|
953
|
-
|
|
991
|
+
toolLabelClipSetting,
|
|
992
|
+
stickyUserMessageSetting,
|
|
954
993
|
startupHeaderSetting,
|
|
955
994
|
],
|
|
956
995
|
},
|
|
@@ -984,6 +1023,7 @@ export async function showCcstylePanel(
|
|
|
984
1023
|
footerCurrencySetting,
|
|
985
1024
|
footerCurrencyRateSetting,
|
|
986
1025
|
footerNerdIconsSetting,
|
|
1026
|
+
footerGitStatsSetting,
|
|
987
1027
|
pluginChipsSetting,
|
|
988
1028
|
],
|
|
989
1029
|
},
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
16
16
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
17
|
-
import { execFile } from "node:child_process";
|
|
18
17
|
import { existsSync } from "node:fs";
|
|
19
18
|
import { homedir } from "node:os";
|
|
20
19
|
import { join } from "node:path";
|
|
@@ -29,8 +28,11 @@ import {
|
|
|
29
28
|
visibleFooterPluginTexts,
|
|
30
29
|
type FooterChipLayout,
|
|
31
30
|
} from "./footer-layout.ts";
|
|
31
|
+
import { createGitStatsRefresher, readGitStats, type GitStats } from "./git-stats.ts";
|
|
32
32
|
import { MCP_STATUS_KEY, buildMcpChip } from "./mcp-chip.ts";
|
|
33
33
|
|
|
34
|
+
export { parseGitStats } from "./git-stats.ts";
|
|
35
|
+
|
|
34
36
|
const GIT_REFRESH_INTERVAL_MS = 10_000;
|
|
35
37
|
const USAGE_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
36
38
|
// 后台连接 MCP 服务器不会触发 footer 重绘,靠这个兜底轮询把芯片刷出来
|
|
@@ -49,8 +51,6 @@ export function footerGlyphs(nerdIcons: boolean): { git: string; cache: string;
|
|
|
49
51
|
: { git: "", cache: "", mcp: "" };
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
type GitStats = { add: number; del: number };
|
|
53
|
-
|
|
54
54
|
type XaiFooterReport = {
|
|
55
55
|
buckets?: Array<{
|
|
56
56
|
id?: string;
|
|
@@ -62,19 +62,6 @@ type XaiFooterReport = {
|
|
|
62
62
|
metrics?: Array<{ id?: string; value?: unknown }>;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
export function parseGitStats(stdout: string): GitStats {
|
|
66
|
-
let add = 0;
|
|
67
|
-
let del = 0;
|
|
68
|
-
for (const line of stdout.split("\n")) {
|
|
69
|
-
const match = line.match(/^(\d+)\s+(\d+)/);
|
|
70
|
-
if (match) {
|
|
71
|
-
add += Number(match[1]);
|
|
72
|
-
del += Number(match[2]);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return { add, del };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
65
|
export function formatXaiFooterChip(report: XaiFooterReport): string | undefined {
|
|
79
66
|
const included = report.buckets?.find((b) => b.id === "included-allowance");
|
|
80
67
|
if (included?.unit === "percent" && typeof included.used === "number") {
|
|
@@ -181,7 +168,12 @@ function colorUsageChip(theme: any, text: string): string {
|
|
|
181
168
|
}
|
|
182
169
|
|
|
183
170
|
let currentTui: any = undefined;
|
|
184
|
-
let refreshCurrentGitStats: (() => void) | undefined;
|
|
171
|
+
let refreshCurrentGitStats: ((reset?: boolean) => void) | undefined;
|
|
172
|
+
|
|
173
|
+
/** Apply a /ccstyle Git mode change immediately, without recreating the footer. */
|
|
174
|
+
export function refreshFooterGitStats(): void {
|
|
175
|
+
refreshCurrentGitStats?.(true);
|
|
176
|
+
}
|
|
185
177
|
let refreshCurrentUsage: (() => void) | undefined;
|
|
186
178
|
let refreshCurrentMcpChip: (() => void) | undefined;
|
|
187
179
|
/** 工具/命令列表在 pi API 上,而 footer 工厂只拿得到 ctx,加载时记下来。 */
|
|
@@ -193,33 +185,23 @@ const createCustomFooterFactory =
|
|
|
193
185
|
const sep = theme.fg("muted", " · ");
|
|
194
186
|
const joinChips = (parts: string[]) => parts.filter(Boolean).join(sep);
|
|
195
187
|
let gitStats: GitStats | undefined;
|
|
196
|
-
let gitRefreshRunning = false;
|
|
197
188
|
let localUsageChip = "";
|
|
198
189
|
let usageGeneration = 0;
|
|
199
190
|
let usageAbort: AbortController | undefined;
|
|
200
191
|
let disposed = false;
|
|
201
192
|
|
|
202
|
-
// 查询在 render
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (gitStats?.add === next.add && gitStats.del === next.del) return;
|
|
215
|
-
gitStats = next;
|
|
216
|
-
tui.requestRender();
|
|
217
|
-
},
|
|
218
|
-
);
|
|
219
|
-
};
|
|
220
|
-
refreshCurrentGitStats = refreshGitStats;
|
|
221
|
-
refreshGitStats();
|
|
222
|
-
const gitRefreshTimer = setInterval(refreshGitStats, GIT_REFRESH_INTERVAL_MS);
|
|
193
|
+
// 查询在 render 外执行;模式/分支变化清旧值,普通轮询只在统计变化时重绘。
|
|
194
|
+
const gitRefresher = createGitStatsRefresher({
|
|
195
|
+
getMode: () => config.footerGitStatsMode,
|
|
196
|
+
query: (mode, signal) => readGitStats(ctx.cwd, mode, signal),
|
|
197
|
+
onChange: (next) => {
|
|
198
|
+
gitStats = next;
|
|
199
|
+
tui.requestRender();
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
refreshCurrentGitStats = gitRefresher.refresh;
|
|
203
|
+
gitRefresher.refresh();
|
|
204
|
+
const gitRefreshTimer = setInterval(() => gitRefresher.refresh(), GIT_REFRESH_INTERVAL_MS);
|
|
223
205
|
gitRefreshTimer.unref?.();
|
|
224
206
|
|
|
225
207
|
const refreshUsage = () => {
|
|
@@ -315,9 +297,9 @@ const createCustomFooterFactory =
|
|
|
315
297
|
const mcpRefreshTimer = setInterval(refreshMcp, MCP_REFRESH_INTERVAL_MS);
|
|
316
298
|
mcpRefreshTimer.unref?.();
|
|
317
299
|
|
|
318
|
-
//
|
|
300
|
+
// 分支变化时重新选择基准,并拒绝旧分支的异步查询结果。
|
|
319
301
|
const unsubBranch = footerData.onBranchChange(() => {
|
|
320
|
-
|
|
302
|
+
gitRefresher.refresh(true);
|
|
321
303
|
tui.requestRender();
|
|
322
304
|
});
|
|
323
305
|
|
|
@@ -442,14 +424,17 @@ const createCustomFooterFactory =
|
|
|
442
424
|
}
|
|
443
425
|
const branch = footerData.getGitBranch();
|
|
444
426
|
if (branch) {
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
427
|
+
const s = gitStats;
|
|
428
|
+
let stats = "";
|
|
429
|
+
if (s && s.status)
|
|
430
|
+
stats = theme.fg("dim", " [") + gitColor(s.status) + theme.fg("dim", "]");
|
|
431
|
+
if (s && (s.add || s.del))
|
|
432
|
+
stats +=
|
|
433
|
+
theme.fg("dim", "(") +
|
|
434
|
+
theme.fg("success", `+${s.add}`) +
|
|
435
|
+
" " +
|
|
436
|
+
theme.fg("error", `−${s.del}`) +
|
|
437
|
+
theme.fg("dim", ")");
|
|
453
438
|
const gitLabel = glyphs.git ? `${glyphs.git} ${branch}` : branch;
|
|
454
439
|
place += theme.fg("muted", " on ") + gitColor(gitLabel) + stats;
|
|
455
440
|
}
|
|
@@ -470,6 +455,7 @@ const createCustomFooterFactory =
|
|
|
470
455
|
},
|
|
471
456
|
dispose(): void {
|
|
472
457
|
disposed = true;
|
|
458
|
+
gitRefresher.dispose();
|
|
473
459
|
usageAbort?.abort();
|
|
474
460
|
usageAbort = undefined;
|
|
475
461
|
clearInterval(gitRefreshTimer);
|