pi-cc-extensions 0.8.47 → 0.8.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +29 -54
- package/README.md +26 -32
- package/assets/readme/welcome.webp +0 -0
- package/extensions/config/config.ts +245 -0
- package/extensions/config/panel.ts +469 -0
- package/extensions/{compact-thinking.ts → feature/compact-thinking.ts} +43 -6
- package/extensions/{markdown-enhance.ts → feature/markdown-enhance.ts} +88 -62
- package/extensions/{pi-startup-header.ts → feature/pi-startup-header.ts} +129 -56
- package/extensions/index.ts +9 -9
- package/extensions/renderer/index.ts +890 -0
- package/extensions/renderer/mouse-interaction.ts +1344 -0
- package/extensions/renderer/show-more-hint.ts +40 -0
- package/extensions/{tool-diff → renderer/tool-diff}/diff-renderer.ts +3 -2
- package/extensions/{tool-diff → renderer/tool-diff}/line-width-safety.ts +2 -1
- package/extensions/{tool-grouping.ts → renderer/tool-grouping.ts} +71 -10
- package/extensions/renderer/tool-result.ts +717 -0
- package/package.json +1 -2
- package/themes/cc-dark.json +2 -2
- package/themes/cc-light.json +2 -2
- package/extensions/claude-code-style.ts +0 -3348
- /package/extensions/{agent-autocomplete.ts → feature/agent-autocomplete.ts} +0 -0
- /package/extensions/{context.ts → feature/context.ts} +0 -0
- /package/extensions/{pi-aliases.ts → feature/pi-aliases.ts} +0 -0
- /package/extensions/{session-reference-core.ts → feature/session-reference-core.ts} +0 -0
- /package/extensions/{session-reference.ts → feature/session-reference.ts} +0 -0
- /package/extensions/{working-message.ts → feature/working-message.ts} +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/ATTRIBUTION.md +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/ansi-utils.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/diff-presentation.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/index.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/render-utils.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/shiki-highlight.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/types.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/write-display-utils.ts +0 -0
- /package/extensions/{tool-diff → renderer/tool-diff}/write-execution.ts +0 -0
- /package/extensions/{fullscreen-detect.ts → utils/fullscreen-detect.ts} +0 -0
- /package/extensions/{tool-loading-icon.ts → utils/tool-loading-icon.ts} +0 -0
- /package/extensions/{tool-result-sanitize.ts → utils/tool-result-sanitize.ts} +0 -0
package/README.en.md
CHANGED
|
@@ -29,76 +29,53 @@
|
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
pi install npm:pi-cc-extensions
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Run `/reload` after installation, then try:
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/context # Inspect context usage
|
|
39
|
-
@ # Complete Agents, Sessions, and files
|
|
33
|
+
# GitHub
|
|
34
|
+
pi install git:github.com/minuque/pi-cc-extensions
|
|
40
35
|
```
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
Run `/reload` after installation.
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------- |
|
|
47
|
-
| Claude Code-style output | Tool summaries, expand/collapse, rich edit/write diffs, and`on` / `off` modes | `/ccstyle` |
|
|
48
|
-
| Fullscreen mouse interaction | Tool card/group click-to-toggle, `[show more]` previews, and a back-to-bottom button | `/ccstyle` |
|
|
49
|
-
| Context inspection | Usage breakdown and previews for the system prompt, tools, skills, and messages | `/context` |
|
|
50
|
-
| Session references | Search and inject effective context from previous Sessions or existing SubAgents | `@session:` |
|
|
51
|
-
| Theme | Bundled GitHub Dark Default, CC Dark, and CC Light themes | `/theme` |
|
|
39
|
+
## Features
|
|
52
40
|
|
|
53
|
-
### Output modes
|
|
54
41
|
|
|
42
|
+
| Feature | Description | Entry point |
|
|
43
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
44
|
+
| Claude Code-style output | Tool summaries, expand/collapse, rich edit/write diffs, and`on` / `off` modes | `/ccstyle` |
|
|
45
|
+
| Fullscreen mouse interaction | Tool card/group click-to-toggle,`[show more]` previews, hover highlight, and a back-to-bottom button (motion reporting auto-enabled under tmux/zellij/screen) | `/ccstyle` |
|
|
46
|
+
| Settings panel | `Style / Diff / Thinking / Feature` tabs: startup header toggle and wheel step | `/ccstyle` |
|
|
47
|
+
| Context inspection | Usage breakdown and previews for the system prompt, tools, skills, and messages | `/context` |
|
|
48
|
+
| Session references | Search and inject effective context from previous Sessions or existing SubAgents | `@session:` |
|
|
49
|
+
| Theme | Bundled CC Dark and CC Light themes | `/theme` |
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
| ----------- | ----------------------------------------------------------------------- |
|
|
58
|
-
| `on` | Claude Code-style tool output with rich diffs for`edit` and `write` |
|
|
59
|
-
| `off` | Native Pi renderers |
|
|
51
|
+
## Configuration
|
|
60
52
|
|
|
61
|
-
|
|
62
|
-
/ccstyle on
|
|
63
|
-
/ccstyle off
|
|
64
|
-
/ccstyle status
|
|
65
|
-
```
|
|
53
|
+
`/ccstyle` behavior is configured through `~/.pi/agent/claude-code-style.json`:
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```json
|
|
55
|
+
```js
|
|
70
56
|
{
|
|
71
|
-
"mode": "on",
|
|
72
|
-
"excludeRenderers": []
|
|
57
|
+
"mode": "on", // on: Claude Code-style output; off: Pi native rendering
|
|
58
|
+
"excludeRenderers": [], // exact tool names that keep their native renderer; Agent always keeps its dedicated renderer
|
|
59
|
+
"diffViewMode": "auto", // diff layout: auto / split / unified
|
|
60
|
+
"diffIndicatorMode": "bars", // diff change indicators: bars / classic / none
|
|
61
|
+
"diffSplitMinWidth": 120, // min terminal width before auto layout uses side-by-side columns
|
|
62
|
+
"diffCollapsedLines": 24, // diff body lines shown when collapsed; beyond that shows the expand hint (Ctrl+O / click)
|
|
63
|
+
"diffWordWrap": true, // whether long diff lines wrap (otherwise truncated)
|
|
64
|
+
"expandedPreviewMaxLines": 40, // max body lines for expanded output/diff
|
|
65
|
+
"useSummaryTitlesAsThinkingTitle": true, // use the latest provider summary as the active thinking title
|
|
66
|
+
"previewLines": 3, // thinking preview lines; 0 hides the preview body
|
|
67
|
+
"animationIntervalMs": 90, // thinking title animation interval in ms
|
|
68
|
+
"showStartupHeader": true, // custom startup header (logo + tips) toggle
|
|
69
|
+
"scrollStepLines": 3 // fullscreen mouse wheel scroll lines
|
|
73
70
|
}
|
|
74
71
|
```
|
|
75
72
|
|
|
76
|
-
- `excludeRenderers` uses exact tool names. `Agent` always keeps its dedicated renderer.
|
|
77
|
-
- Run `/reload` after editing the file manually.
|
|
78
|
-
|
|
79
|
-
### `@` references
|
|
80
|
-
|
|
81
|
-
- Enter `@session:` to reference a previous Session.
|
|
82
|
-
- Enter `@agent-name` to delegate to a custom Agent.
|
|
83
|
-
- Compatible with `@bacnh85/pi-fff` and `@tintinweb/pi-subagents`.
|
|
84
|
-
- Session context is deduplicated and size-limited before injection.
|
|
85
|
-
|
|
86
|
-
## Other installation methods
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
# GitHub
|
|
90
|
-
pi install git:github.com/minuque/pi-cc-extensions
|
|
91
|
-
|
|
92
|
-
# Local repository
|
|
93
|
-
pi install /absolute/path/to/pi-cc-extensions
|
|
94
|
-
```
|
|
95
|
-
|
|
96
73
|
## Local development
|
|
97
74
|
|
|
98
75
|
```bash
|
|
99
76
|
npm test
|
|
100
77
|
npm run typecheck
|
|
101
|
-
pi -e .
|
|
78
|
+
./test.bat # or pi -e .
|
|
102
79
|
```
|
|
103
80
|
|
|
104
81
|
Run `/reload` after changing extensions.
|
|
@@ -127,5 +104,3 @@ Run `/reload` after changing extensions.
|
|
|
127
104
|
## Credits
|
|
128
105
|
|
|
129
106
|
- Rich diffs are adapted from [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display) (MIT). See [`extensions/tool-diff/ATTRIBUTION.md`](./extensions/tool-diff/ATTRIBUTION.md).
|
|
130
|
-
- Startup header based on [`EnderLiquid/pi-startup-header`](https://github.com/EnderLiquid/pi-startup-header) (MIT).
|
|
131
|
-
- Command aliases based on [`xRyul/pi-aliases`](https://github.com/xRyul/pi-aliases) (MIT).
|
package/README.md
CHANGED
|
@@ -39,47 +39,43 @@ pi install git:github.com/minuque/pi-cc-extensions
|
|
|
39
39
|
## 功能
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
| 功能 | 说明
|
|
43
|
-
| ---------------------- |
|
|
44
|
-
| Claude Code 风格输出 | 工具摘要、折叠展开、rich edit/write diff,以及`on` / `off` 两种模式
|
|
45
|
-
| Fullscreen 鼠标交互 | 工具卡/group 点击展开与收起、`[show more]`
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| 模式 | 行为 |
|
|
54
|
-
| ------- | --------------------------------------------------------- |
|
|
55
|
-
| `on` | Claude Code 风格工具输出,`edit` / `write` 带 rich diff |
|
|
56
|
-
| `off` | Pi 原生渲染 |
|
|
57
|
-
|
|
58
|
-
```text
|
|
59
|
-
/ccstyle on
|
|
60
|
-
/ccstyle off
|
|
61
|
-
/ccstyle status
|
|
62
|
-
/ccstyle panel # 交互式设置面板(模式、Diff、thinking 选项)
|
|
63
|
-
```
|
|
42
|
+
| 功能 | 说明 | 入口 |
|
|
43
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------- |
|
|
44
|
+
| Claude Code 风格输出 | 工具摘要、折叠展开、rich edit/write diff,以及`on` / `off` 两种模式 | `/ccstyle` |
|
|
45
|
+
| Fullscreen 鼠标交互 | 工具卡/group 点击展开与收起、`[show more]` 预览、hover 高亮、回到底部按钮(tmux/zellij/screen 下自动补开 motion 上报) | `/ccstyle` |
|
|
46
|
+
| 配置面板 | `Style / Diff / Thinking / Feature` 四页签,含启动头开关与滚轮步进 | `/ccstyle` |
|
|
47
|
+
| 上下文检查 | 查看上下文占用,并预览 System prompt、Tools、Skills 和消息内容 | `/context` |
|
|
48
|
+
| Session 引用 | 搜索并注入历史 Session 或现有 SubAgent 的有效上下文 | `@session:` |
|
|
49
|
+
| 主题 | 随包提供内置 CC Dark、CC Light 主题 | `/theme` |
|
|
50
|
+
|
|
51
|
+
## 配置
|
|
64
52
|
|
|
65
|
-
|
|
53
|
+
`/ccstyle` 的行为由 `~/.pi/agent/claude-code-style.json` 配置:
|
|
66
54
|
|
|
67
|
-
```
|
|
55
|
+
```js
|
|
68
56
|
{
|
|
69
|
-
"mode": "on",
|
|
70
|
-
"excludeRenderers": []
|
|
57
|
+
"mode": "on", // on:Claude Code 风格输出;off:Pi 原生渲染
|
|
58
|
+
"excludeRenderers": [], // 走原生渲染的工具名列表(精确匹配),Agent 始终保留专用渲染器
|
|
59
|
+
"diffViewMode": "auto", // diff 布局:auto / split / unified
|
|
60
|
+
"diffIndicatorMode": "bars", // diff 变更指示:bars / classic / none
|
|
61
|
+
"diffSplitMinWidth": 120, // 自动布局下使用左右分栏的最小终端宽度
|
|
62
|
+
"diffCollapsedLines": 24, // 折叠时展示的 diff 行数,超出显示展开提示(Ctrl+O / 点击)
|
|
63
|
+
"diffWordWrap": true, // 长 diff 行是否换行(否则截断)
|
|
64
|
+
"expandedPreviewMaxLines": 40, // 展开后输出/diff 正文的最大行数
|
|
65
|
+
"useSummaryTitlesAsThinkingTitle": true, // 用最新 provider 摘要作为思考标题
|
|
66
|
+
"previewLines": 3, // thinking 预览行数,0 隐藏预览正文
|
|
67
|
+
"animationIntervalMs": 90, // thinking 标题动画间隔(毫秒)
|
|
68
|
+
"showStartupHeader": true, // 自定义启动头(logo + tips)开关
|
|
69
|
+
"scrollStepLines": 3 // fullscreen 滚轮滚动步进行数
|
|
71
70
|
}
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
- `excludeRenderers` 使用精确工具名;`Agent` 始终保留其专用渲染器。
|
|
75
|
-
- 手动修改配置后执行 `/reload`。
|
|
76
|
-
|
|
77
73
|
## 本地开发
|
|
78
74
|
|
|
79
75
|
```bash
|
|
80
76
|
npm test
|
|
81
77
|
npm run typecheck
|
|
82
|
-
pi -e .
|
|
78
|
+
./test.bat # or pi -e .
|
|
83
79
|
```
|
|
84
80
|
|
|
85
81
|
修改扩展后执行 `/reload`。
|
|
@@ -108,5 +104,3 @@ pi -e .
|
|
|
108
104
|
## 致谢
|
|
109
105
|
|
|
110
106
|
- Rich diff 改编自 [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display)(MIT);详见 [`extensions/tool-diff/ATTRIBUTION.md`](./extensions/tool-diff/ATTRIBUTION.md)。
|
|
111
|
-
- 启动头基于 [`EnderLiquid/pi-startup-header`](https://github.com/EnderLiquid/pi-startup-header)(MIT)。
|
|
112
|
-
- 命令别名基于 [`xRyul/pi-aliases`](https://github.com/xRyul/pi-aliases)(MIT)。
|
|
Binary file
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type { CompactThinkingConfig } from "../feature/compact-thinking.ts";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_TOOL_DISPLAY_CONFIG,
|
|
4
|
+
type DiffIndicatorMode,
|
|
5
|
+
type DiffViewMode,
|
|
6
|
+
type ToolDisplayConfig,
|
|
7
|
+
} from "../renderer/tool-diff/index.ts";
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
export type CompactStyleMode = "on" | "off";
|
|
13
|
+
|
|
14
|
+
export type Config = {
|
|
15
|
+
mode: CompactStyleMode;
|
|
16
|
+
excludeRenderers: string[];
|
|
17
|
+
diffViewMode: DiffViewMode;
|
|
18
|
+
diffIndicatorMode: DiffIndicatorMode;
|
|
19
|
+
diffSplitMinWidth: number;
|
|
20
|
+
diffCollapsedLines: number;
|
|
21
|
+
diffWordWrap: boolean;
|
|
22
|
+
expandedPreviewMaxLines: number;
|
|
23
|
+
useSummaryTitlesAsThinkingTitle: boolean;
|
|
24
|
+
previewLines: number;
|
|
25
|
+
animationIntervalMs: number;
|
|
26
|
+
showStartupHeader: boolean;
|
|
27
|
+
scrollStepLines: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const AGENT_DIR = process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
|
|
31
|
+
const CONFIG_PATH = join(AGENT_DIR, "claude-code-style.json");
|
|
32
|
+
|
|
33
|
+
export const DIFF_VIEW_MODES: DiffViewMode[] = ["auto", "split", "unified"];
|
|
34
|
+
export const DIFF_INDICATOR_MODES: DiffIndicatorMode[] = ["bars", "classic", "none"];
|
|
35
|
+
export const DIFF_SPLIT_MIN_WIDTH_VALUES = ["80", "100", "120", "140", "160", "180"];
|
|
36
|
+
export const DIFF_COLLAPSED_LINES_VALUES = ["12", "24", "36", "48", "80", "120"];
|
|
37
|
+
/** Presets for expanded body height — keep low options first so cycling stays TUI-friendly. */
|
|
38
|
+
export const EXPANDED_PREVIEW_MAX_LINES_VALUES = ["40", "60", "80", "120", "200", "500", "2000"];
|
|
39
|
+
export const THINKING_PREVIEW_LINES_VALUES = ["0", "1", "3", "5", "10"];
|
|
40
|
+
export const THINKING_ANIMATION_INTERVAL_VALUES = ["40", "60", "90", "120", "180"];
|
|
41
|
+
/** fullscreen 滚轮步进行数预设。 */
|
|
42
|
+
export const SCROLL_STEP_LINES_VALUES = ["1", "2", "3", "5", "10"];
|
|
43
|
+
/** Tools commonly toggled in excludeRenderers via the settings panel. */
|
|
44
|
+
export const EXCLUDE_RENDERER_CANDIDATES = [
|
|
45
|
+
"bash",
|
|
46
|
+
"read",
|
|
47
|
+
"edit",
|
|
48
|
+
"write",
|
|
49
|
+
"grep",
|
|
50
|
+
"find",
|
|
51
|
+
"ls",
|
|
52
|
+
"webfetch",
|
|
53
|
+
"wait",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export const DEFAULT_CONFIG: Config = {
|
|
57
|
+
mode: "on",
|
|
58
|
+
excludeRenderers: [],
|
|
59
|
+
diffViewMode: DEFAULT_TOOL_DISPLAY_CONFIG.diffViewMode,
|
|
60
|
+
diffIndicatorMode: DEFAULT_TOOL_DISPLAY_CONFIG.diffIndicatorMode,
|
|
61
|
+
diffSplitMinWidth: DEFAULT_TOOL_DISPLAY_CONFIG.diffSplitMinWidth,
|
|
62
|
+
diffCollapsedLines: DEFAULT_TOOL_DISPLAY_CONFIG.diffCollapsedLines,
|
|
63
|
+
diffWordWrap: DEFAULT_TOOL_DISPLAY_CONFIG.diffWordWrap,
|
|
64
|
+
expandedPreviewMaxLines: DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
65
|
+
useSummaryTitlesAsThinkingTitle: true,
|
|
66
|
+
previewLines: 3,
|
|
67
|
+
animationIntervalMs: 90,
|
|
68
|
+
showStartupHeader: true,
|
|
69
|
+
scrollStepLines: 3,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function pickEnum<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T {
|
|
73
|
+
return typeof value === "string" && (allowed as readonly string[]).includes(value)
|
|
74
|
+
? (value as T)
|
|
75
|
+
: fallback;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function pickPositiveInt(value: unknown, fallback: number, min = 1, max = 100_000): number {
|
|
79
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
80
|
+
if (!Number.isFinite(n)) return fallback;
|
|
81
|
+
return Math.min(max, Math.max(min, Math.floor(n)));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function pickPositiveNumber(value: unknown, fallback: number, min = 1): number {
|
|
85
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
86
|
+
return Number.isFinite(n) ? Math.max(min, n) : fallback;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function nearestPreset(value: number, presets: readonly string[]): string {
|
|
90
|
+
const numeric = presets.map((p) => Number(p));
|
|
91
|
+
let best = presets[0] ?? String(value);
|
|
92
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
93
|
+
for (let i = 0; i < numeric.length; i++) {
|
|
94
|
+
const dist = Math.abs((numeric[i] ?? 0) - value);
|
|
95
|
+
if (dist < bestDist) {
|
|
96
|
+
bestDist = dist;
|
|
97
|
+
best = presets[i] ?? best;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Prefer exact match when value is already a preset.
|
|
101
|
+
const exact = presets.find((p) => Number(p) === value);
|
|
102
|
+
return exact ?? best;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function normalizeConfig(input: unknown): Config {
|
|
106
|
+
const source = (input && typeof input === "object" ? input : {}) as Record<string, unknown>;
|
|
107
|
+
const mode = source.mode;
|
|
108
|
+
// mode=compact 已日落:旧配置回退到 on(Claude Code 风格)。
|
|
109
|
+
const migratedMode: CompactStyleMode =
|
|
110
|
+
mode === "on" || mode === "off"
|
|
111
|
+
? mode
|
|
112
|
+
: typeof source.enabled === "boolean"
|
|
113
|
+
? source.enabled
|
|
114
|
+
? "on"
|
|
115
|
+
: "off"
|
|
116
|
+
: "on";
|
|
117
|
+
const excludeRenderers = Array.isArray(source.excludeRenderers)
|
|
118
|
+
? [
|
|
119
|
+
...new Set(
|
|
120
|
+
source.excludeRenderers.filter(
|
|
121
|
+
(name): name is string => typeof name === "string" && name.length > 0,
|
|
122
|
+
),
|
|
123
|
+
),
|
|
124
|
+
]
|
|
125
|
+
: [];
|
|
126
|
+
return {
|
|
127
|
+
mode: migratedMode,
|
|
128
|
+
excludeRenderers,
|
|
129
|
+
diffViewMode: pickEnum(source.diffViewMode, DIFF_VIEW_MODES, DEFAULT_CONFIG.diffViewMode),
|
|
130
|
+
diffIndicatorMode: pickEnum(
|
|
131
|
+
source.diffIndicatorMode,
|
|
132
|
+
DIFF_INDICATOR_MODES,
|
|
133
|
+
DEFAULT_CONFIG.diffIndicatorMode,
|
|
134
|
+
),
|
|
135
|
+
diffSplitMinWidth: pickPositiveInt(
|
|
136
|
+
source.diffSplitMinWidth,
|
|
137
|
+
DEFAULT_CONFIG.diffSplitMinWidth,
|
|
138
|
+
40,
|
|
139
|
+
300,
|
|
140
|
+
),
|
|
141
|
+
diffCollapsedLines: pickPositiveInt(
|
|
142
|
+
source.diffCollapsedLines,
|
|
143
|
+
DEFAULT_CONFIG.diffCollapsedLines,
|
|
144
|
+
1,
|
|
145
|
+
500,
|
|
146
|
+
),
|
|
147
|
+
diffWordWrap: source.diffWordWrap !== false,
|
|
148
|
+
expandedPreviewMaxLines: pickPositiveInt(
|
|
149
|
+
source.expandedPreviewMaxLines,
|
|
150
|
+
DEFAULT_CONFIG.expandedPreviewMaxLines,
|
|
151
|
+
10,
|
|
152
|
+
50_000,
|
|
153
|
+
),
|
|
154
|
+
useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle !== false,
|
|
155
|
+
previewLines: pickPositiveInt(
|
|
156
|
+
source.previewLines,
|
|
157
|
+
DEFAULT_CONFIG.previewLines,
|
|
158
|
+
0,
|
|
159
|
+
Number.MAX_SAFE_INTEGER,
|
|
160
|
+
),
|
|
161
|
+
animationIntervalMs: pickPositiveNumber(
|
|
162
|
+
source.animationIntervalMs,
|
|
163
|
+
DEFAULT_CONFIG.animationIntervalMs,
|
|
164
|
+
),
|
|
165
|
+
showStartupHeader: source.showStartupHeader !== false,
|
|
166
|
+
scrollStepLines: pickPositiveInt(
|
|
167
|
+
source.scrollStepLines,
|
|
168
|
+
DEFAULT_CONFIG.scrollStepLines,
|
|
169
|
+
1,
|
|
170
|
+
50,
|
|
171
|
+
),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function getCompactThinkingConfig(source: Config = config): CompactThinkingConfig {
|
|
176
|
+
return {
|
|
177
|
+
useSummaryTitlesAsThinkingTitle: source.useSummaryTitlesAsThinkingTitle,
|
|
178
|
+
previewLines: source.previewLines,
|
|
179
|
+
animationIntervalMs: source.animationIntervalMs,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function getToolDisplayConfig(source: Config = config): ToolDisplayConfig {
|
|
184
|
+
return {
|
|
185
|
+
diffViewMode: source.diffViewMode,
|
|
186
|
+
diffIndicatorMode: source.diffIndicatorMode,
|
|
187
|
+
diffSplitMinWidth: source.diffSplitMinWidth,
|
|
188
|
+
diffCollapsedLines: source.diffCollapsedLines,
|
|
189
|
+
diffWordWrap: source.diffWordWrap,
|
|
190
|
+
expandedPreviewMaxLines: source.expandedPreviewMaxLines,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function formatExcludeRenderers(names: readonly string[]): string {
|
|
195
|
+
return names.length === 0 ? "none" : names.join(", ");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function formatConfigStatus(source: Config = config): string {
|
|
199
|
+
return [
|
|
200
|
+
`mode=${source.mode}`,
|
|
201
|
+
`exclude=[${source.excludeRenderers.join(", ") || "none"}]`,
|
|
202
|
+
`diffView=${source.diffViewMode}`,
|
|
203
|
+
`diffIndicator=${source.diffIndicatorMode}`,
|
|
204
|
+
`diffSplitMin=${source.diffSplitMinWidth}`,
|
|
205
|
+
`diffCollapsed=${source.diffCollapsedLines}`,
|
|
206
|
+
`diffWordWrap=${source.diffWordWrap ? "on" : "off"}`,
|
|
207
|
+
`expandedMax=${source.expandedPreviewMaxLines}`,
|
|
208
|
+
`thinkingTitle=${source.useSummaryTitlesAsThinkingTitle ? "summary" : "default"}`,
|
|
209
|
+
`thinkingPreview=${source.previewLines}`,
|
|
210
|
+
`thinkingAnimation=${source.animationIntervalMs}ms`,
|
|
211
|
+
`startupHeader=${source.showStartupHeader ? "on" : "off"}`,
|
|
212
|
+
`scrollStep=${source.scrollStepLines}`,
|
|
213
|
+
].join(" · ");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export let config: Config = loadConfig();
|
|
217
|
+
|
|
218
|
+
function loadConfig(): Config {
|
|
219
|
+
try {
|
|
220
|
+
const source = existsSync(CONFIG_PATH)
|
|
221
|
+
? (JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as Record<string, unknown>)
|
|
222
|
+
: {};
|
|
223
|
+
const normalized = normalizeConfig(source);
|
|
224
|
+
if (typeof source.enabled === "boolean" && source.mode !== "on" && source.mode !== "off") {
|
|
225
|
+
try {
|
|
226
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(normalized, null, 2));
|
|
227
|
+
} catch {
|
|
228
|
+
// A read-only config still uses the migrated in-memory value.
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return normalized;
|
|
232
|
+
} catch {
|
|
233
|
+
// Ignore bad config and fall back to defaults.
|
|
234
|
+
}
|
|
235
|
+
return { ...DEFAULT_CONFIG };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function saveConfig() {
|
|
239
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** 整体替换配置(default export 的 configOverride 注入路径;导入绑定不可重新赋值)。 */
|
|
243
|
+
export function setConfig(next: Config): void {
|
|
244
|
+
config = next;
|
|
245
|
+
}
|