pi-cc-extensions 0.9.10 → 0.9.12
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 +8 -8
- package/README.md +8 -8
- package/extensions/config/config.ts +46 -16
- package/extensions/config/panel.ts +173 -43
- package/extensions/feature/compact-thinking.ts +12 -12
- package/extensions/feature/shell/footer-currency.ts +29 -0
- package/extensions/feature/shell/footer.ts +40 -51
- package/extensions/feature/shell/git-stats.ts +162 -0
- package/extensions/renderer/compact-mode.ts +35 -6
- package/extensions/renderer/default-mode.ts +15 -9
- package/extensions/renderer/mouse/interaction.ts +27 -25
- package/extensions/renderer/tool/codemode.ts +4 -1
- package/extensions/renderer/tool/diff/diff-edit-render.ts +64 -11
- package/extensions/renderer/tool/names.ts +2 -2
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -61,15 +61,14 @@ 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
|
-
"scrollStepLines": 3, // fullscreen wheel scroll step
|
|
67
66
|
|
|
68
67
|
// diff
|
|
69
68
|
"diffViewMode": "auto", // layout: auto / split / unified
|
|
70
69
|
"diffIndicatorMode": "bars", // change indicators: bars / classic / none
|
|
71
70
|
"diffSplitMinWidth": 120, // min terminal width for side-by-side columns
|
|
72
|
-
"editDiffCollapsedLines": 24, //
|
|
71
|
+
"editDiffCollapsedLines": 24, // edit collapse lines; 0 = change-count summary only
|
|
73
72
|
"writeDiffCollapsedLines": 0, // write collapse lines; 0 = creation summary only
|
|
74
73
|
"diffWordWrap": true, // wrap long diff lines
|
|
75
74
|
|
|
@@ -81,7 +80,10 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
|
|
|
81
80
|
|
|
82
81
|
// footer
|
|
83
82
|
"enableCustomFooter": true, // custom status bar
|
|
83
|
+
"footerCurrency": "USD", // display currency for the footer cost
|
|
84
|
+
"footerCurrencyRate": null, // display units per 1 cost unit; null changes the symbol only
|
|
84
85
|
"footerNerdIcons": true, // Nerd Font glyphs for git/cache; false = plain text
|
|
86
|
+
"footerGitStatsMode": "working", // working (default): uncommitted; branch: cumulative net changes
|
|
85
87
|
"footerHiddenKeys": [], // hidden plugin chip keys
|
|
86
88
|
"footerLine1Keys": ["pi-usage"], // line1 plugin chip order; pi-usage is shown by default, data from @narumitw/pi-usage
|
|
87
89
|
"footerLine2Keys": [], // line2 plugin chip order (after cwd/git)
|
|
@@ -98,11 +100,9 @@ Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-exam
|
|
|
98
100
|
## Local development
|
|
99
101
|
|
|
100
102
|
```bash
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
test.bat # Windows: same
|
|
105
|
-
# one-off run without touching settings: pi -e .
|
|
103
|
+
./test.sh # macOS/Linux; needs node and pi
|
|
104
|
+
./test.bat # Windows; runs test.sh through Git Bash
|
|
105
|
+
pi -e . # one run, does not edit settings.json
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
## Compatibility
|
package/README.md
CHANGED
|
@@ -61,15 +61,14 @@ 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
|
-
"scrollStepLines": 3, // fullscreen 滚轮步进
|
|
67
66
|
|
|
68
67
|
// diff
|
|
69
68
|
"diffViewMode": "auto", // 布局:auto / split / unified
|
|
70
69
|
"diffIndicatorMode": "bars", // 变更指示:bars / classic / none
|
|
71
70
|
"diffSplitMinWidth": 120, // 左右分栏的最小终端宽度
|
|
72
|
-
"editDiffCollapsedLines": 24, //
|
|
71
|
+
"editDiffCollapsedLines": 24, // edit 折叠行数;0 仅显示变更统计摘要
|
|
73
72
|
"writeDiffCollapsedLines": 0, // write 折叠行数,0 仅显示创建摘要
|
|
74
73
|
"diffWordWrap": true, // 长 diff 行换行
|
|
75
74
|
|
|
@@ -81,7 +80,10 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
81
80
|
|
|
82
81
|
// footer
|
|
83
82
|
"enableCustomFooter": true, // 自定义状态栏
|
|
83
|
+
"footerCurrency": "USD", // 底栏费用的显示币种
|
|
84
|
+
"footerCurrencyRate": null, // 1 单位费用换成多少显示币种;null 只换符号、不乘倍率
|
|
84
85
|
"footerNerdIcons": true, // git/缓存用 Nerd Font 图标;false 为纯文本
|
|
86
|
+
"footerGitStatsMode": "working", // working(默认):未提交改动;branch:分支累计净改动
|
|
85
87
|
"footerHiddenKeys": [], // 隐藏的插件芯片 key
|
|
86
88
|
"footerLine1Keys": ["pi-usage"], // line1 插件芯片顺序;pi-usage 默认显示,数据来自 @narumitw/pi-usage
|
|
87
89
|
"footerLine2Keys": [], // line2 插件芯片顺序(接在 cwd/git 后)
|
|
@@ -98,11 +100,9 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
98
100
|
## 本地开发
|
|
99
101
|
|
|
100
102
|
```bash
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
test.bat # Windows:同上
|
|
105
|
-
# 只想跑一次、不改配置:pi -e .
|
|
103
|
+
./test.sh # macOS/Linux,需要 node 和 pi
|
|
104
|
+
./test.bat # Windows,经 Git Bash 运行 test.sh
|
|
105
|
+
pi -e . # 只跑一次,不改 settings.json
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
## 兼容性
|
|
@@ -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,13 +59,12 @@ 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;
|
|
66
|
-
scrollStepLines: number;
|
|
67
68
|
enableSessionReference: boolean;
|
|
68
69
|
enableSubagentAutocomplete: boolean;
|
|
69
70
|
enableContextCommand: boolean;
|
|
@@ -71,7 +72,12 @@ export type Config = {
|
|
|
71
72
|
enableWorkingMessage: boolean;
|
|
72
73
|
enableAliases: boolean;
|
|
73
74
|
enableCustomFooter: boolean;
|
|
75
|
+
/** ISO 4217 显示币种。USD 时不换算。 */
|
|
76
|
+
footerCurrency: string;
|
|
77
|
+
/** 1 单位费用对应的显示币种数量。null 只换符号、不乘倍率。 */
|
|
78
|
+
footerCurrencyRate: number | null;
|
|
74
79
|
footerNerdIcons: boolean;
|
|
80
|
+
footerGitStatsMode: FooterGitStatsMode;
|
|
75
81
|
footerHiddenKeys: string[];
|
|
76
82
|
footerLine1Keys: string[];
|
|
77
83
|
footerLine2Keys: string[];
|
|
@@ -84,8 +90,9 @@ const LEGACY_CONFIG_PATH = join(AGENT_DIR, "claude-code-style.json");
|
|
|
84
90
|
|
|
85
91
|
export const DIFF_VIEW_MODES: DiffViewMode[] = ["auto", "split", "unified"];
|
|
86
92
|
export const DIFF_INDICATOR_MODES: DiffIndicatorMode[] = ["bars", "classic", "none"];
|
|
93
|
+
export const FOOTER_GIT_STATS_MODES: FooterGitStatsMode[] = ["working", "branch"];
|
|
87
94
|
export const DIFF_SPLIT_MIN_WIDTH_VALUES = ["80", "100", "120", "140", "160", "180"];
|
|
88
|
-
export const DIFF_COLLAPSED_LINES_VALUES = ["12", "24", "36", "48", "80", "120"];
|
|
95
|
+
export const DIFF_COLLAPSED_LINES_VALUES = ["0", "12", "24", "36", "48", "80", "120"];
|
|
89
96
|
/** Write collapsed presets. 0 = stats only (`+N -0` + expand hint). */
|
|
90
97
|
export const WRITE_DIFF_COLLAPSED_LINES_VALUES = ["0", "4", "8", "12", "24", "36"];
|
|
91
98
|
/** Presets for expanded body height — keep low options first so cycling stays TUI-friendly. */
|
|
@@ -109,11 +116,9 @@ export const EXPANDED_INPUT_MAX_LINES_VALUES = ["5", "10", "20", "40", "80"];
|
|
|
109
116
|
/** 展开工具卡 Output 可见行数预设。 */
|
|
110
117
|
export const EXPANDED_OUTPUT_MAX_LINES_VALUES = ["10", "20", "40", "80", "120"];
|
|
111
118
|
/** 工具摘要里 path/command 等输入的折叠字符数;0 = 只按可用宽度截断。 */
|
|
112
|
-
export const
|
|
119
|
+
export const TOOL_LABEL_CLIP_VALUES = ["0", "40", "60", "80", "100", "120", "160"];
|
|
113
120
|
export const THINKING_PREVIEW_LINES_VALUES = ["0", "1", "3", "5", "10"];
|
|
114
121
|
export const THINKING_ANIMATION_INTERVAL_VALUES = ["40", "60", "90", "120", "180"];
|
|
115
|
-
/** fullscreen 滚轮步进行数预设。 */
|
|
116
|
-
export const SCROLL_STEP_LINES_VALUES = ["1", "2", "3", "5", "10"];
|
|
117
122
|
/** Tools commonly toggled in excludeRenderers via the settings panel. */
|
|
118
123
|
export const EXCLUDE_RENDERER_CANDIDATES = [
|
|
119
124
|
"bash",
|
|
@@ -142,13 +147,12 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
142
147
|
expandedCardBackground: "userMessageBg",
|
|
143
148
|
expandedInputMaxLines: 5,
|
|
144
149
|
expandedOutputMaxLines: 10,
|
|
145
|
-
|
|
150
|
+
toolLabelClip: 0,
|
|
146
151
|
useSummaryTitlesAsThinkingTitle: true,
|
|
147
152
|
previewLines: 3,
|
|
148
153
|
animationIntervalMs: 90,
|
|
149
154
|
dimThinkingText: false,
|
|
150
155
|
showStartupHeader: true,
|
|
151
|
-
scrollStepLines: 3,
|
|
152
156
|
enableSessionReference: true,
|
|
153
157
|
enableSubagentAutocomplete: true,
|
|
154
158
|
enableContextCommand: true,
|
|
@@ -156,7 +160,10 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
156
160
|
enableWorkingMessage: true,
|
|
157
161
|
enableAliases: true,
|
|
158
162
|
enableCustomFooter: true,
|
|
163
|
+
footerCurrency: "USD",
|
|
164
|
+
footerCurrencyRate: null,
|
|
159
165
|
footerNerdIcons: true,
|
|
166
|
+
footerGitStatsMode: "working",
|
|
160
167
|
...DEFAULT_FOOTER_CHIP_LAYOUT,
|
|
161
168
|
};
|
|
162
169
|
|
|
@@ -172,10 +179,10 @@ export function pickPositiveInt(value: unknown, fallback: number, min = 1, max =
|
|
|
172
179
|
return Math.min(max, Math.max(min, Math.floor(n)));
|
|
173
180
|
}
|
|
174
181
|
|
|
175
|
-
/**
|
|
176
|
-
export function
|
|
182
|
+
/** toolLabelClip:0 表示按宽度自适应,其余夹到 8..500。 */
|
|
183
|
+
export function pickToolLabelClip(value: unknown): number {
|
|
177
184
|
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
178
|
-
return n === 0 ? 0 : pickPositiveInt(value, DEFAULT_CONFIG.
|
|
185
|
+
return n === 0 ? 0 : pickPositiveInt(value, DEFAULT_CONFIG.toolLabelClip, 8, 500);
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
export function pickPositiveNumber(value: unknown, fallback: number, min = 1): number {
|
|
@@ -183,6 +190,19 @@ export function pickPositiveNumber(value: unknown, fallback: number, min = 1): n
|
|
|
183
190
|
return Number.isFinite(n) ? Math.max(min, n) : fallback;
|
|
184
191
|
}
|
|
185
192
|
|
|
193
|
+
function normalizeCurrency(value: unknown): string {
|
|
194
|
+
return typeof value === "string" && /^[A-Za-z]{3}$/.test(value.trim())
|
|
195
|
+
? value.trim().toUpperCase()
|
|
196
|
+
: "USD";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** 缺失、空串、非正数都视为不换算。 */
|
|
200
|
+
function normalizeFooterCurrencyRate(value: unknown): number | null {
|
|
201
|
+
if (value === undefined || value === null || value === "") return null;
|
|
202
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
203
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
204
|
+
}
|
|
205
|
+
|
|
186
206
|
export function normalizeConfig(input: unknown): Config {
|
|
187
207
|
const source = (input && typeof input === "object" ? input : {}) as Record<string, unknown>;
|
|
188
208
|
const mode = pickEnum(source.mode, ["on", "compact", "off"], DEFAULT_CONFIG.mode);
|
|
@@ -213,7 +233,7 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
213
233
|
editDiffCollapsedLines: pickPositiveInt(
|
|
214
234
|
source.editDiffCollapsedLines,
|
|
215
235
|
DEFAULT_CONFIG.editDiffCollapsedLines,
|
|
216
|
-
|
|
236
|
+
0,
|
|
217
237
|
500,
|
|
218
238
|
),
|
|
219
239
|
writeDiffCollapsedLines: pickPositiveInt(
|
|
@@ -246,7 +266,9 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
246
266
|
1,
|
|
247
267
|
5_000,
|
|
248
268
|
),
|
|
249
|
-
|
|
269
|
+
toolLabelClip: pickToolLabelClip(
|
|
270
|
+
source.toolLabelClip ?? source.inputClip, // 兼容旧配置键
|
|
271
|
+
),
|
|
250
272
|
useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle !== false,
|
|
251
273
|
previewLines: pickPositiveInt(
|
|
252
274
|
source.previewLines,
|
|
@@ -260,7 +282,6 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
260
282
|
),
|
|
261
283
|
dimThinkingText: source.dimThinkingText === true,
|
|
262
284
|
showStartupHeader: source.showStartupHeader !== false,
|
|
263
|
-
scrollStepLines: pickPositiveInt(source.scrollStepLines, DEFAULT_CONFIG.scrollStepLines, 1, 50),
|
|
264
285
|
enableSessionReference: source.enableSessionReference !== false,
|
|
265
286
|
enableSubagentAutocomplete: source.enableSubagentAutocomplete !== false,
|
|
266
287
|
enableContextCommand: source.enableContextCommand !== false,
|
|
@@ -268,7 +289,14 @@ export function normalizeConfig(input: unknown): Config {
|
|
|
268
289
|
enableWorkingMessage: source.enableWorkingMessage !== false,
|
|
269
290
|
enableAliases: source.enableAliases !== false,
|
|
270
291
|
enableCustomFooter: source.enableCustomFooter !== false,
|
|
292
|
+
footerCurrency: normalizeCurrency(source.footerCurrency),
|
|
293
|
+
footerCurrencyRate: normalizeFooterCurrencyRate(source.footerCurrencyRate),
|
|
271
294
|
footerNerdIcons: source.footerNerdIcons !== false,
|
|
295
|
+
footerGitStatsMode: pickEnum(
|
|
296
|
+
source.footerGitStatsMode,
|
|
297
|
+
FOOTER_GIT_STATS_MODES,
|
|
298
|
+
DEFAULT_CONFIG.footerGitStatsMode,
|
|
299
|
+
),
|
|
272
300
|
...normalizeFooterChipLayout(source),
|
|
273
301
|
};
|
|
274
302
|
}
|
|
@@ -310,13 +338,12 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
310
338
|
`expandedMax=${source.expandedPreviewMaxLines}`,
|
|
311
339
|
`expandedInput=${source.expandedInputMaxLines}`,
|
|
312
340
|
`expandedOutput=${source.expandedOutputMaxLines}`,
|
|
313
|
-
`
|
|
341
|
+
`toolLabelClip=${source.toolLabelClip}`,
|
|
314
342
|
`thinkingTitle=${source.useSummaryTitlesAsThinkingTitle ? "summary" : "default"}`,
|
|
315
343
|
`thinkingPreview=${source.previewLines}`,
|
|
316
344
|
`thinkingAnimation=${source.animationIntervalMs}ms`,
|
|
317
345
|
`thinkingDim=${source.dimThinkingText ? "on" : "off"}`,
|
|
318
346
|
`startupHeader=${source.showStartupHeader ? "on" : "off"}`,
|
|
319
|
-
`scrollStep=${source.scrollStepLines}`,
|
|
320
347
|
`sessionRef=${source.enableSessionReference ? "on" : "off"}`,
|
|
321
348
|
`subagentAuto=${source.enableSubagentAutocomplete ? "on" : "off"}`,
|
|
322
349
|
`context=${source.enableContextCommand ? "on" : "off"}`,
|
|
@@ -324,7 +351,10 @@ export function formatConfigStatus(source: Config = config): string {
|
|
|
324
351
|
`workingMsg=${source.enableWorkingMessage ? "on" : "off"}`,
|
|
325
352
|
`aliases=${source.enableAliases ? "on" : "off"}`,
|
|
326
353
|
`footer=${source.enableCustomFooter ? "on" : "off"}`,
|
|
354
|
+
`footerCurrency=${source.footerCurrency}`,
|
|
355
|
+
`footerRate=${source.footerCurrencyRate ?? "-"}`,
|
|
327
356
|
`footerIcons=${source.footerNerdIcons ? "nerd" : "plain"}`,
|
|
357
|
+
`footerGitStats=${source.footerGitStatsMode}`,
|
|
328
358
|
formatFooterChipSummary(source),
|
|
329
359
|
].join(" · ");
|
|
330
360
|
}
|
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
applyCustomFooter,
|
|
12
12
|
clearCustomFooter,
|
|
13
13
|
getFooterStatusSnapshot,
|
|
14
|
+
refreshFooterGitStats,
|
|
14
15
|
} from "../feature/shell/footer.ts";
|
|
16
|
+
import {
|
|
17
|
+
FOOTER_CURRENCY_PRESETS,
|
|
18
|
+
presetFooterCurrencyRate,
|
|
19
|
+
} from "../feature/shell/footer-currency.ts";
|
|
15
20
|
import {
|
|
16
21
|
footerChipDescription,
|
|
17
22
|
footerLineOfKey,
|
|
@@ -37,21 +42,22 @@ import {
|
|
|
37
42
|
EXPANDED_INPUT_MAX_LINES_VALUES,
|
|
38
43
|
EXPANDED_OUTPUT_MAX_LINES_VALUES,
|
|
39
44
|
EXPANDED_PREVIEW_MAX_LINES_VALUES,
|
|
45
|
+
FOOTER_GIT_STATS_MODES,
|
|
40
46
|
formatExcludeRenderers,
|
|
41
47
|
getCompactThinkingConfig,
|
|
42
|
-
pickInputClip,
|
|
43
48
|
pickPositiveInt,
|
|
44
49
|
pickPositiveNumber,
|
|
45
|
-
|
|
50
|
+
pickToolLabelClip,
|
|
46
51
|
THINKING_ANIMATION_INTERVAL_VALUES,
|
|
47
52
|
THINKING_PREVIEW_LINES_VALUES,
|
|
48
|
-
|
|
53
|
+
TOOL_LABEL_CLIP_VALUES,
|
|
49
54
|
WRITE_DIFF_COLLAPSED_LINES_VALUES,
|
|
50
55
|
updateConfig,
|
|
51
56
|
type CompactStyleMode,
|
|
52
57
|
type Config,
|
|
53
58
|
type DiffIndicatorMode,
|
|
54
59
|
type DiffViewMode,
|
|
60
|
+
type FooterGitStatsMode,
|
|
55
61
|
} from "./config.ts";
|
|
56
62
|
|
|
57
63
|
/** renderer 注入的渲染副作用,面板自身不触碰渲染状态。 */
|
|
@@ -81,6 +87,12 @@ function customFooterDescription(enabled: boolean): string {
|
|
|
81
87
|
return "Custom status bar with model, context, cache, cost, git, and plugin chips.";
|
|
82
88
|
}
|
|
83
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
|
+
|
|
84
96
|
function pluginChipsDescription(): string {
|
|
85
97
|
return `Arrange plugin chips: ${formatFooterChipSummary(config)}. Enter to edit.`;
|
|
86
98
|
}
|
|
@@ -281,10 +293,22 @@ function buildExcludeRenderersSubmenu(
|
|
|
281
293
|
};
|
|
282
294
|
}
|
|
283
295
|
|
|
284
|
-
|
|
285
|
-
|
|
296
|
+
function footerCurrencyRateLabel(rate: number | null): string {
|
|
297
|
+
return rate === null ? "-" : String(rate);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 手动输入子面板。accept 返回字符串则提交,undefined 取消,null 用 invalid 提示。
|
|
302
|
+
*/
|
|
303
|
+
function buildValueInputSubmenu(
|
|
286
304
|
theme: any,
|
|
287
|
-
|
|
305
|
+
options: {
|
|
306
|
+
prompt: string;
|
|
307
|
+
initial: string;
|
|
308
|
+
hint: string;
|
|
309
|
+
accept: (raw: string) => string | null | undefined;
|
|
310
|
+
invalid?: (raw: string) => string;
|
|
311
|
+
},
|
|
288
312
|
closeSubmenu: (selected?: string) => void,
|
|
289
313
|
): {
|
|
290
314
|
render: (width: number) => string[];
|
|
@@ -293,27 +317,28 @@ function buildNumberInputSubmenu(
|
|
|
293
317
|
} {
|
|
294
318
|
const input = new Input();
|
|
295
319
|
let error = "";
|
|
296
|
-
input.setValue(
|
|
320
|
+
input.setValue(options.initial);
|
|
297
321
|
input.onSubmit = (value: string) => {
|
|
298
322
|
const raw = value.trim();
|
|
299
|
-
|
|
300
|
-
|
|
323
|
+
const next = options.accept(raw);
|
|
324
|
+
if (next === undefined) {
|
|
325
|
+
closeSubmenu();
|
|
301
326
|
return;
|
|
302
327
|
}
|
|
303
|
-
if (
|
|
304
|
-
error =
|
|
328
|
+
if (next === null) {
|
|
329
|
+
error = options.invalid?.(raw) ?? "Invalid value";
|
|
305
330
|
return;
|
|
306
331
|
}
|
|
307
|
-
closeSubmenu(
|
|
332
|
+
closeSubmenu(next);
|
|
308
333
|
};
|
|
309
334
|
input.onEscape = () => closeSubmenu();
|
|
310
335
|
return {
|
|
311
336
|
render: (width: number) => {
|
|
312
337
|
const safe = Math.max(0, Math.floor(width));
|
|
313
338
|
const lines = [
|
|
314
|
-
theme.fg("dim",
|
|
339
|
+
theme.fg("dim", options.prompt),
|
|
315
340
|
...input.render(safe),
|
|
316
|
-
truncateToWidth(theme.fg("dim",
|
|
341
|
+
truncateToWidth(theme.fg("dim", options.hint), safe),
|
|
317
342
|
];
|
|
318
343
|
if (error !== "") lines.push(theme.fg("dim", ` ${error}`));
|
|
319
344
|
return lines;
|
|
@@ -323,6 +348,25 @@ function buildNumberInputSubmenu(
|
|
|
323
348
|
};
|
|
324
349
|
}
|
|
325
350
|
|
|
351
|
+
/** 数值项:预填当前值,空输入取消,非数字留下错误。 */
|
|
352
|
+
function buildNumberInputSubmenu(
|
|
353
|
+
theme: any,
|
|
354
|
+
setting: { label: string; currentValue: string },
|
|
355
|
+
closeSubmenu: (selected?: string) => void,
|
|
356
|
+
) {
|
|
357
|
+
return buildValueInputSubmenu(
|
|
358
|
+
theme,
|
|
359
|
+
{
|
|
360
|
+
prompt: ` ${setting.label} — custom value:`,
|
|
361
|
+
initial: setting.currentValue,
|
|
362
|
+
hint: " Enter to apply · Esc to go back",
|
|
363
|
+
accept: (raw) => (raw === "" ? undefined : Number.isFinite(Number(raw)) ? raw : null),
|
|
364
|
+
invalid: (raw) => `Invalid number: "${raw}"`,
|
|
365
|
+
},
|
|
366
|
+
closeSubmenu,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
326
370
|
/** Section tabs for /ccstyle — matches Zentui-style "A / B / C" headers. */
|
|
327
371
|
type CcstyleSection = {
|
|
328
372
|
id: "style" | "diff" | "thinking" | "ui" | "feature" | "footer";
|
|
@@ -409,15 +453,15 @@ export async function showCcstylePanel(
|
|
|
409
453
|
);
|
|
410
454
|
},
|
|
411
455
|
};
|
|
412
|
-
const
|
|
413
|
-
id: "
|
|
414
|
-
label: "
|
|
456
|
+
const toolLabelClipSetting = {
|
|
457
|
+
id: "toolLabelClip",
|
|
458
|
+
label: "Tool label clip",
|
|
415
459
|
description:
|
|
416
460
|
"Max characters for path/command/name in single and grouped tool summaries; 0 fits available width. Enter to type a custom value.",
|
|
417
|
-
currentValue: String(config.
|
|
418
|
-
values: [...
|
|
461
|
+
currentValue: String(config.toolLabelClip),
|
|
462
|
+
values: [...TOOL_LABEL_CLIP_VALUES],
|
|
419
463
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
420
|
-
buildNumberInputSubmenu(theme,
|
|
464
|
+
buildNumberInputSubmenu(theme, toolLabelClipSetting, closeSubmenu),
|
|
421
465
|
};
|
|
422
466
|
const diffViewSetting = {
|
|
423
467
|
id: "diffViewMode",
|
|
@@ -447,7 +491,7 @@ export async function showCcstylePanel(
|
|
|
447
491
|
id: "editDiffCollapsedLines",
|
|
448
492
|
label: "Edit collapsed lines",
|
|
449
493
|
description:
|
|
450
|
-
"
|
|
494
|
+
"Edit diff body lines before the expand hint (Ctrl+O / click). 0 shows only change counts and the hint.",
|
|
451
495
|
currentValue: String(config.editDiffCollapsedLines),
|
|
452
496
|
values: [...DIFF_COLLAPSED_LINES_VALUES],
|
|
453
497
|
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
@@ -551,15 +595,6 @@ export async function showCcstylePanel(
|
|
|
551
595
|
currentValue: config.showStartupHeader ? "on" : "off",
|
|
552
596
|
values: ["on", "off"],
|
|
553
597
|
};
|
|
554
|
-
const scrollStepSetting = {
|
|
555
|
-
id: "scrollStepLines",
|
|
556
|
-
label: "Scroll step",
|
|
557
|
-
description: "Mouse wheel scroll lines in fullscreen mode.",
|
|
558
|
-
currentValue: String(config.scrollStepLines),
|
|
559
|
-
values: [...SCROLL_STEP_LINES_VALUES],
|
|
560
|
-
submenu: (_current: string, closeSubmenu: (selected?: string) => void) =>
|
|
561
|
-
buildNumberInputSubmenu(theme, scrollStepSetting, closeSubmenu),
|
|
562
|
-
};
|
|
563
598
|
|
|
564
599
|
// 额外功能开关:注册于扩展加载期,切换后需重启(/reload)生效。
|
|
565
600
|
const sessionReferenceToggle = featureToggleSetting(
|
|
@@ -604,6 +639,65 @@ export async function showCcstylePanel(
|
|
|
604
639
|
"Aliases disabled.",
|
|
605
640
|
config.enableAliases,
|
|
606
641
|
);
|
|
642
|
+
const footerCurrencySetting = {
|
|
643
|
+
id: "footerCurrency",
|
|
644
|
+
label: "Cost currency",
|
|
645
|
+
description:
|
|
646
|
+
"ISO code for the status-bar cost. Switching currency fills its preset rate. USD clears the rate.",
|
|
647
|
+
currentValue: config.footerCurrency,
|
|
648
|
+
values: [...FOOTER_CURRENCY_PRESETS],
|
|
649
|
+
submenu: (_current: string, closeSubmenu: (selected?: string) => void) => {
|
|
650
|
+
nestedSubmenuOpen = true;
|
|
651
|
+
nestedHint = " Enter to apply · Esc back to Footer";
|
|
652
|
+
return buildValueInputSubmenu(
|
|
653
|
+
theme,
|
|
654
|
+
{
|
|
655
|
+
prompt: " Cost currency — ISO code:",
|
|
656
|
+
initial: footerCurrencySetting.currentValue,
|
|
657
|
+
hint: " Enter to apply · Esc to go back",
|
|
658
|
+
accept: (raw) => (raw === "" ? undefined : raw),
|
|
659
|
+
},
|
|
660
|
+
(selected) => {
|
|
661
|
+
nestedSubmenuOpen = false;
|
|
662
|
+
nestedHint = "";
|
|
663
|
+
closeSubmenu(selected);
|
|
664
|
+
},
|
|
665
|
+
);
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
const footerCurrencyRateSetting = {
|
|
669
|
+
id: "footerCurrencyRate",
|
|
670
|
+
label: "Cost rate",
|
|
671
|
+
description:
|
|
672
|
+
"Display units per 1 USD of cost. Switching currency replaces this. Empty keeps the number and only changes the symbol.",
|
|
673
|
+
currentValue: footerCurrencyRateLabel(config.footerCurrencyRate),
|
|
674
|
+
submenu: (_current: string, closeSubmenu: (selected?: string) => void) => {
|
|
675
|
+
nestedSubmenuOpen = true;
|
|
676
|
+
nestedHint = " Enter to apply · empty clears · Esc back to Footer";
|
|
677
|
+
return buildValueInputSubmenu(
|
|
678
|
+
theme,
|
|
679
|
+
{
|
|
680
|
+
prompt: " Cost rate — display units per 1 cost unit:",
|
|
681
|
+
initial:
|
|
682
|
+
footerCurrencyRateSetting.currentValue === "-"
|
|
683
|
+
? ""
|
|
684
|
+
: footerCurrencyRateSetting.currentValue,
|
|
685
|
+
hint: " Enter to apply · empty clears conversion · Esc to go back",
|
|
686
|
+
accept: (raw) => {
|
|
687
|
+
if (raw === "" || raw === "-") return "-";
|
|
688
|
+
const n = Number(raw);
|
|
689
|
+
return Number.isFinite(n) && n > 0 ? raw : null;
|
|
690
|
+
},
|
|
691
|
+
invalid: () => "Rate must be a positive number",
|
|
692
|
+
},
|
|
693
|
+
(selected) => {
|
|
694
|
+
nestedSubmenuOpen = false;
|
|
695
|
+
nestedHint = "";
|
|
696
|
+
closeSubmenu(selected);
|
|
697
|
+
},
|
|
698
|
+
);
|
|
699
|
+
},
|
|
700
|
+
};
|
|
607
701
|
const footerNerdIconsSetting = {
|
|
608
702
|
id: "footerNerdIcons",
|
|
609
703
|
label: "Nerd Font icons",
|
|
@@ -620,6 +714,13 @@ export async function showCcstylePanel(
|
|
|
620
714
|
currentValue: config.enableCustomFooter ? "on" : "off",
|
|
621
715
|
values: ["on", "off"],
|
|
622
716
|
};
|
|
717
|
+
const footerGitStatsSetting = {
|
|
718
|
+
id: "footerGitStatsMode",
|
|
719
|
+
label: "Git changes",
|
|
720
|
+
description: footerGitStatsDescription(config.footerGitStatsMode),
|
|
721
|
+
currentValue: config.footerGitStatsMode,
|
|
722
|
+
values: FOOTER_GIT_STATS_MODES,
|
|
723
|
+
};
|
|
623
724
|
const pluginChipsSetting = {
|
|
624
725
|
id: "footerPluginChips",
|
|
625
726
|
label: "Plugin chips",
|
|
@@ -663,6 +764,35 @@ export async function showCcstylePanel(
|
|
|
663
764
|
ctx.ui.notify(`Updated ${id}: ${value}`, "info");
|
|
664
765
|
return;
|
|
665
766
|
}
|
|
767
|
+
if (id === "footerCurrency") {
|
|
768
|
+
updateConfig({
|
|
769
|
+
footerCurrency: value,
|
|
770
|
+
footerCurrencyRate: presetFooterCurrencyRate(value),
|
|
771
|
+
});
|
|
772
|
+
footerCurrencySetting.currentValue = config.footerCurrency;
|
|
773
|
+
footerCurrencyRateSetting.currentValue = footerCurrencyRateLabel(config.footerCurrencyRate);
|
|
774
|
+
if (config.enableCustomFooter) applyCustomFooter(ctx);
|
|
775
|
+
ctx.ui.notify(
|
|
776
|
+
`Updated ${id}: ${config.footerCurrency} · ${footerCurrencyRateSetting.currentValue}`,
|
|
777
|
+
"info",
|
|
778
|
+
);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (id === "footerCurrencyRate") {
|
|
782
|
+
updateConfig({ footerCurrencyRate: value === "-" ? null : Number(value) });
|
|
783
|
+
footerCurrencyRateSetting.currentValue = footerCurrencyRateLabel(config.footerCurrencyRate);
|
|
784
|
+
if (config.enableCustomFooter) applyCustomFooter(ctx);
|
|
785
|
+
ctx.ui.notify(`Updated ${id}: ${footerCurrencyRateSetting.currentValue}`, "info");
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (id === "footerGitStatsMode") {
|
|
789
|
+
updateConfig({ footerGitStatsMode: value as FooterGitStatsMode });
|
|
790
|
+
footerGitStatsSetting.currentValue = config.footerGitStatsMode;
|
|
791
|
+
footerGitStatsSetting.description = footerGitStatsDescription(config.footerGitStatsMode);
|
|
792
|
+
refreshFooterGitStats();
|
|
793
|
+
ctx.ui.notify(`Updated ${id}: ${value}`, "info");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
666
796
|
if (id === "footerNerdIcons") {
|
|
667
797
|
const enabled = value === "on";
|
|
668
798
|
updateConfig({ footerNerdIcons: enabled });
|
|
@@ -682,11 +812,11 @@ export async function showCcstylePanel(
|
|
|
682
812
|
return;
|
|
683
813
|
}
|
|
684
814
|
switch (id) {
|
|
685
|
-
case "
|
|
815
|
+
case "toolLabelClip":
|
|
686
816
|
updateConfig({
|
|
687
|
-
|
|
817
|
+
toolLabelClip: pickToolLabelClip(value),
|
|
688
818
|
});
|
|
689
|
-
|
|
819
|
+
toolLabelClipSetting.currentValue = String(config.toolLabelClip);
|
|
690
820
|
break;
|
|
691
821
|
case "mode": {
|
|
692
822
|
// 选项值带 Experimental 标记,选择后还原为真实 mode 值。
|
|
@@ -719,7 +849,7 @@ export async function showCcstylePanel(
|
|
|
719
849
|
editDiffCollapsedLines: pickPositiveInt(
|
|
720
850
|
value,
|
|
721
851
|
DEFAULT_CONFIG.editDiffCollapsedLines,
|
|
722
|
-
|
|
852
|
+
0,
|
|
723
853
|
500,
|
|
724
854
|
),
|
|
725
855
|
});
|
|
@@ -810,12 +940,6 @@ export async function showCcstylePanel(
|
|
|
810
940
|
else clearStartupHeader(ctx);
|
|
811
941
|
break;
|
|
812
942
|
}
|
|
813
|
-
case "scrollStepLines":
|
|
814
|
-
updateConfig({
|
|
815
|
-
scrollStepLines: pickPositiveInt(value, DEFAULT_CONFIG.scrollStepLines, 1, 50),
|
|
816
|
-
});
|
|
817
|
-
scrollStepSetting.currentValue = String(config.scrollStepLines);
|
|
818
|
-
break;
|
|
819
943
|
default:
|
|
820
944
|
return;
|
|
821
945
|
}
|
|
@@ -850,9 +974,8 @@ export async function showCcstylePanel(
|
|
|
850
974
|
expandedOutputSetting,
|
|
851
975
|
expandedMaxSetting,
|
|
852
976
|
expandedCardBgSetting,
|
|
853
|
-
|
|
977
|
+
toolLabelClipSetting,
|
|
854
978
|
startupHeaderSetting,
|
|
855
|
-
scrollStepSetting,
|
|
856
979
|
],
|
|
857
980
|
},
|
|
858
981
|
{
|
|
@@ -880,7 +1003,14 @@ export async function showCcstylePanel(
|
|
|
880
1003
|
{
|
|
881
1004
|
id: "footer",
|
|
882
1005
|
label: "Footer",
|
|
883
|
-
items: [
|
|
1006
|
+
items: [
|
|
1007
|
+
customFooterSetting,
|
|
1008
|
+
footerCurrencySetting,
|
|
1009
|
+
footerCurrencyRateSetting,
|
|
1010
|
+
footerNerdIconsSetting,
|
|
1011
|
+
footerGitStatsSetting,
|
|
1012
|
+
pluginChipsSetting,
|
|
1013
|
+
],
|
|
884
1014
|
},
|
|
885
1015
|
];
|
|
886
1016
|
|