pi-zh-cn 0.2.2 → 0.2.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GoetheDady
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,40 +1,58 @@
1
1
  # pi-zh-cn
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/pi-zh-cn.svg)](https://www.npmjs.com/package/pi-zh-cn)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
3
6
  把 [pi](https://github.com/earendil-works/pi) 编码代理的界面变成简体中文。装上重启即可,不想要了随时卸载。
4
7
 
5
- 原理上它只是一个普通扩展:调用官方扩展 API 替换文案,不打补丁、不改安装目录里的任何文件,所以 pi 正常升级后一般也能直接用。
8
+ ## 特性
9
+
10
+ - **零侵入**:只是普通扩展,调用官方扩展 API 替换文案,不打补丁、不改安装目录里的任何文件,pi 正常升级后一般也能直接用
11
+ - **不改模型输入**:发给模型的内容保持不变,工具 description 保留英文原文,避免影响模型行为
12
+ - **按需开启**:`grep`、`find`、`ls` 或 `powershell` 没开就不会被偷偷打开
13
+ - **键位跟随**:快捷键提示跟随你自己的键位配置
6
14
 
7
- ## 会变成中文的部分
15
+ ## 汉化范围
8
16
 
9
- - 启动画面:标题、快捷键提示、引导语(快捷键显示跟随你自己的键位配置)
17
+ - 启动画面:标题、快捷键提示、引导语
10
18
  - 底部状态栏:当前目录 / 分支 / 会话名、token 与缓存用量、费用、上下文占比、模型和思考等级
11
19
  - 加载提示:「正在处理…」;thinking 折叠块的标签
12
20
  - 八个内置工具的执行结果:读取、执行(bash)、编辑、写入、搜索内容、查找文件、列出目录、PowerShell——状态行都是中文,比如「完成」「退出码 N」「输出过长已截断」
21
+ - 编辑与写入的改动展示沿用 pi 内置渲染器(只有标题和展开提示是中文):`edit` 显示行号 + 红绿 diff,`write` 预览文件内容(折叠 10 行,`ctrl+o` 展开全量)
13
22
  - 项目信任弹窗:信任/不信任选择,含「信任父文件夹」变体
23
+ - 内置斜杠命令描述:core 全部 23 个命令
14
24
 
15
- 发给模型的内容不变。工具的 description 保持英文原文,避免影响模型行为。
16
-
17
- ## 保持原样的部分
18
-
19
- - 内置菜单和其他硬编码在 pi 源码里的英文(扩展 API 够不到,除非 fork 上游重编译,不值得)
20
- - 工具的启用范围:`grep`、`find`、`ls` 或 `powershell` 没开就不会被这个插件偷偷打开
21
-
22
- ## 使用
25
+ ## 安装
23
26
 
24
27
  ```bash
25
- # 从 npm 安装
26
28
  pi install npm:pi-zh-cn
29
+ ```
30
+
31
+ 重启 pi 即生效。查看已安装的包:
27
32
 
28
- # 查看/卸载用这里显示的 source 名
33
+ ```bash
29
34
  pi list
30
35
  ```
31
36
 
32
- 觉得中文状态栏不如原生好读,可以临时切回英文:
37
+ ### 卸载
38
+
39
+ ```bash
40
+ pi remove npm:pi-zh-cn
41
+ ```
42
+
43
+ ### 临时切回英文
44
+
45
+ 觉得中文状态栏不如原生好读,可以在会话里执行:
33
46
 
34
47
  ```text
35
48
  /footer
36
49
  ```
37
50
 
51
+ ## 已知局限
52
+
53
+ - 内置菜单和其他硬编码在 pi 源码里的英文保持原样——扩展 API 够不到,除非 fork 上游重编译,不值得
54
+ - 实验性的全屏模式(`tuiMode: "fullscreen"`)文案写死在 pi-tui 里,同样够不到,例如 pi 0.85.0 新增的 `↓ Jump to latest message · <键位>`,以及搜索框的 `Find in transcript` / `No matches`
55
+
38
56
  ## 开发
39
57
 
40
58
  ```bash
@@ -47,4 +65,27 @@ pi --no-session -e ./extensions/index.ts -p "hi"
47
65
 
48
66
  所有文案都集中在 [`extensions/zh.ts`](extensions/zh.ts),想改词只动这一个文件就够了。
49
67
 
50
- 改动记录见 [CHANGELOG.md](CHANGELOG.md)。pi 大版本升级后如果出现错位或残留英文,欢迎提 issue。
68
+ ### 上游兼容性测试(契约测试)
69
+
70
+ 汉化依赖 pi 内部结构(footer 统计行、工具 details 字段、trust.json 语义、
71
+ 斜杠命令清单等),上游一变就可能静默失效。`test/` 下的契约测试把这些
72
+ 依赖点逐条断言:实际执行内置工具、实例化 `ProjectTrustStore`、探查内置
73
+ footer 的 dist 源码,任一结构变化都会失败并指明该核对哪个文案。
74
+
75
+ ```bash
76
+ npm test # tsc 类型检查 + 全部测试
77
+ ```
78
+
79
+ CI 在每次 push/PR 时运行,并每两天定时跑一次「上游金丝雀」:因为 peer
80
+ 依赖是 `*`,定时任务会拉到最新版 pi,上游发布后两天内即可自动暴露兼容
81
+ 性问题,不用等用户反馈。
82
+
83
+ ## 反馈
84
+
85
+ pi 大版本升级后如果出现错位或残留英文,欢迎[提 issue](https://github.com/GoetheDady/pi-zh-cn/issues)。
86
+
87
+ 改动记录见 [CHANGELOG.md](CHANGELOG.md)。
88
+
89
+ ## License
90
+
91
+ [MIT](LICENSE)
@@ -13,20 +13,24 @@ import {
13
13
  createPowerShellTool,
14
14
  createReadTool,
15
15
  createWriteTool,
16
+ getLanguageFromPath,
17
+ highlightCode,
18
+ keyHint,
16
19
  type BashToolDetails,
17
- type EditToolDetails,
18
20
  type ReadToolDetails,
19
21
  } from "@earendil-works/pi-coding-agent";
20
22
  import { Text } from "@earendil-works/pi-tui";
21
23
  import { zh } from "./zh.ts";
22
24
  import {
23
25
  countResultRender,
24
- errorFirstLine,
25
26
  notePartial,
26
27
  shellRenders,
27
28
  withExpanded,
28
29
  } from "./tool-renders.ts";
29
30
 
31
+ /** write 工具折叠态预览的行数(与内置 write 渲染器一致)。 */
32
+ const WRITE_PREVIEW_LINES = 10;
33
+
30
34
  /**
31
35
  * 重注册所有内置工具的中文渲染。
32
36
  *
@@ -118,38 +122,7 @@ export function registerLocalizedTools(pi: ExtensionAPI, cwd: string) {
118
122
  return new Text(text, 0, 0);
119
123
  },
120
124
 
121
- renderResult(
122
- result: any,
123
- { expanded, isPartial }: { expanded: boolean; isPartial: boolean },
124
- theme: any,
125
- ) {
126
- if (isPartial) return notePartial(zh.tools.edit.editing, theme);
127
-
128
- const err = errorFirstLine(result, theme);
129
- if (err) return err;
130
-
131
- const details = result.details as EditToolDetails | undefined;
132
- if (!details?.diff) {
133
- return new Text(theme.fg("success", zh.tools.edit.applied), 0, 0);
134
- }
135
-
136
- let additions = 0,
137
- removals = 0;
138
- for (const line of details.diff.split("\n")) {
139
- if (line.startsWith("+") && !line.startsWith("+++")) additions++;
140
- if (line.startsWith("-") && !line.startsWith("---")) removals++;
141
- }
142
-
143
- let text = theme.fg("success", zh.tools.edit.additions(additions));
144
- text += theme.fg("dim", " / ");
145
- text += theme.fg("error", zh.tools.edit.removals(removals));
146
-
147
- return new Text(
148
- withExpanded(expanded ? details.diff : "", text, theme),
149
- 0,
150
- 0,
151
- );
152
- },
125
+ // 不覆盖 renderResult:沿用内置渲染器(行号 + 红绿 + 词级高亮的 diff)。
153
126
  });
154
127
 
155
128
  // write
@@ -157,20 +130,38 @@ export function registerLocalizedTools(pi: ExtensionAPI, cwd: string) {
157
130
  ...createWriteTool(cwd),
158
131
  label: zh.tools.write.label,
159
132
 
160
- renderCall(args: any, theme: any) {
133
+ renderCall(args: any, theme: any, context: any) {
134
+ const rawLines = String(args.content ?? "")
135
+ .replace(/\r/g, "")
136
+ .replace(/\t/g, " ")
137
+ .split("\n");
138
+ let end = rawLines.length;
139
+ while (end > 0 && rawLines[end - 1] === "") end--;
140
+
161
141
  let text = theme.fg("toolTitle", theme.bold(zh.tools.write.title));
162
142
  text += theme.fg("accent", args.path);
163
- const lineCount = String(args.content).split("\n").length;
164
- text += theme.fg("dim", ` (${zh.tools.write.lines(lineCount)})`);
143
+ text += theme.fg("dim", ` (${zh.tools.write.lines(end)})`);
144
+
145
+ if (end > 0) {
146
+ const maxLines = context.expanded ? end : WRITE_PREVIEW_LINES;
147
+ const lang = args.path ? getLanguageFromPath(args.path) : undefined;
148
+ // ponytail: 只高亮预览到的行,大文件折叠态不跑全量高亮;跨行语法
149
+ // (块注释等)在预览边界可能染色偏移,需要精确时改全量高亮 + 增量缓存
150
+ // (上游 write.js 的做法)。
151
+ text += `\n\n${highlightCode(rawLines.slice(0, maxLines).join("\n"), lang).join("\n")}`;
152
+ if (end > maxLines) {
153
+ text += theme.fg(
154
+ "muted",
155
+ `\n${zh.tools.write.moreLines(end - maxLines, end)}`,
156
+ );
157
+ text += keyHint("app.tools.expand", zh.tools.write.expand);
158
+ text += theme.fg("muted", ")");
159
+ }
160
+ }
165
161
  return new Text(text, 0, 0);
166
162
  },
167
163
 
168
- renderResult(result: any, { isPartial }: { isPartial: boolean }, theme: any) {
169
- if (isPartial) return notePartial(zh.tools.write.writing, theme);
170
- const err = errorFirstLine(result, theme);
171
- if (err) return err;
172
- return new Text(theme.fg("success", zh.tools.write.written), 0, 0);
173
- },
164
+ // 不覆盖 renderResult:沿用内置渲染器(成功时不追加内容,出错时显示完整错误)。
174
165
  });
175
166
 
176
167
  // grep / find / ls:调用渲染各自略有差异,结果渲染走同一个计数模板。
@@ -22,15 +22,6 @@ export const countNonEmpty = (s: string): number =>
22
22
  export const notePartial = (msg: string, theme: any): Text =>
23
23
  new Text(theme.fg("warning", msg), 0, 0);
24
24
 
25
- // 错误结果:内容以 "Error" 开头时只展示首行(edit/write 共用)。
26
- /** 展示错误首行(红色);结果不是错误时返回 undefined。 */
27
- export const errorFirstLine = (result: any, theme: any): Text | undefined => {
28
- const out = textOf(result);
29
- return out.startsWith("Error")
30
- ? new Text(theme.fg("error", out.split("\n")[0]), 0, 0)
31
- : undefined;
32
- };
33
-
34
25
  // 展开态:在摘要后追加逐行弱化的原始输出。
35
26
  /**
36
27
  * 组装展开态文本:raw 为空时返回 summary,否则在 summary 之后
package/extensions/zh.ts CHANGED
@@ -95,6 +95,9 @@ export const zh = {
95
95
  writing: "写入中…",
96
96
  written: "已写入",
97
97
  lines: (n: number) => `${n} 行`,
98
+ moreLines: (remaining: number, total: number) =>
99
+ `…(还有 ${remaining} 行,共 ${total} 行,`,
100
+ expand: "展开",
98
101
  },
99
102
  grep: {
100
103
  label: "搜索内容",
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "pi-zh-cn",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "pi 编码代理的简体中文界面汉化插件",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/GoetheDady/pi-zh-cn.git"
8
8
  },
9
+ "license": "MIT",
9
10
  "type": "module",
11
+ "scripts": {
12
+ "test": "tsc -p . && node --test \"test/*.test.ts\""
13
+ },
10
14
  "keywords": [
11
15
  "pi-package",
12
16
  "i18n",