u1s1-cli 1.11.8 → 1.12.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/dist/agent-setup.d.ts +5 -0
- package/dist/agent-setup.js +31 -7
- package/dist/api.js +42 -34
- package/dist/bench-report.js +12 -9
- package/dist/bench.js +34 -31
- package/dist/brand.d.ts +2 -1
- package/dist/brand.js +38 -12
- package/dist/config.d.ts +11 -1
- package/dist/config.js +25 -8
- package/dist/context-usage.js +12 -9
- package/dist/deploy.js +49 -45
- package/dist/device-auth.js +7 -5
- package/dist/error-humanize.d.ts +1 -0
- package/dist/error-humanize.js +18 -6
- package/dist/feedback.js +48 -31
- package/dist/i18n.d.ts +21 -0
- package/dist/i18n.js +46 -0
- package/dist/import/index.js +64 -28
- package/dist/import/skills.js +72 -41
- package/dist/import/util.js +3 -2
- package/dist/import/write.js +2 -1
- package/dist/index.js +139 -48
- package/dist/login.js +39 -28
- package/dist/loop.js +18 -12
- package/dist/mcp/client.js +15 -14
- package/dist/mcp/command.js +76 -48
- package/dist/mcp/extension.js +5 -3
- package/dist/mcp/tools.js +6 -5
- package/dist/model.js +11 -10
- package/dist/nudges.d.ts +8 -2
- package/dist/nudges.js +22 -3
- package/dist/search-tools.js +7 -6
- package/dist/shell-doctor.js +9 -8
- package/dist/subagent.js +5 -4
- package/dist/tools.js +53 -37
- package/dist/update.js +29 -28
- package/dist/usage.d.ts +1 -1
- package/dist/usage.js +96 -43
- package/dist/web.js +2 -1
- package/dist/workflow/runner.js +33 -29
- package/dist/workflow/templates.js +70 -45
- package/dist/workflow/tool.js +15 -12
- package/package.json +2 -2
- package/scripts/patch-pi.js +243 -278
- package/scripts/render-regression.mjs +55 -4
package/scripts/patch-pi.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The repository applies patches/*.patch through pnpm for development, while
|
|
5
5
|
* global `npm i -g u1s1-cli` installations do not understand pnpm patches.
|
|
6
|
-
* This script applies the same compact-UI
|
|
7
|
-
* dependencies so both installation paths behave identically.
|
|
6
|
+
* This script applies the same compact-UI, Windows TUI and UI-text (zh/en by
|
|
7
|
+
* U1S1_LANG) fixes to installed dependencies so both installation paths behave identically.
|
|
8
8
|
*
|
|
9
9
|
* Properties:
|
|
10
10
|
* - Pure Node implementation with no patch(1) dependency, including Windows
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
|
16
|
-
import { join, dirname } from "node:path";
|
|
16
|
+
import { join, dirname, resolve, sep } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { createRequire } from "node:module";
|
|
19
19
|
|
|
@@ -30,6 +30,9 @@ function addPackageDir(found, candidate) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// Tests set U1S1_PATCH_PI_SCAN_ROOT so fixtures under the OS temp dir never touch stray installs above them.
|
|
34
|
+
const scanRoot = process.env.U1S1_PATCH_PI_SCAN_ROOT ? realpathSync(resolve(process.env.U1S1_PATCH_PI_SCAN_ROOT)) : undefined;
|
|
35
|
+
|
|
33
36
|
function findPackageDirs(packageName) {
|
|
34
37
|
const found = new Set();
|
|
35
38
|
try {
|
|
@@ -43,10 +46,10 @@ function findPackageDirs(packageName) {
|
|
|
43
46
|
while (true) {
|
|
44
47
|
addPackageDir(found, join(dir, "node_modules", scope, name));
|
|
45
48
|
const parent = dirname(dir);
|
|
46
|
-
if (parent === dir) break;
|
|
49
|
+
if (parent === dir || resolve(dir) === scanRoot) break;
|
|
47
50
|
dir = parent;
|
|
48
51
|
}
|
|
49
|
-
return [...found];
|
|
52
|
+
return [...found].filter((d) => !scanRoot || d === scanRoot || d.startsWith(scanRoot + sep));
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
function findTuiDirs() {
|
|
@@ -71,168 +74,6 @@ const COMPACT_UI_REPLACEMENTS = [
|
|
|
71
74
|
],
|
|
72
75
|
];
|
|
73
76
|
|
|
74
|
-
// 内置斜杠命令描述汉化(dist/core/slash-commands.js),与 pnpm coding-agent patch 同步。
|
|
75
|
-
const SLASH_COMMANDS_REPLACEMENTS = [
|
|
76
|
-
['{ name: "settings", description: "Open settings menu" },', '{ name: "settings", description: "打开设置菜单" },'],
|
|
77
|
-
['{ name: "model", description: "Select model (opens selector UI)", argumentHint: "<provider/model>" },', '{ name: "model", description: "切换模型(列表里有免费/价格提示)", argumentHint: "<provider/model>" },'],
|
|
78
|
-
['{ name: "tree", description: "Navigate session tree (switch branches)" },', '{ name: "tree", description: "查看会话分支树,切换分支" },'],
|
|
79
|
-
['{ name: "thinking", description: "Set thinking level", argumentHint: "<level>" },', '{ name: "thinking", description: "调整思考强度(想得越深越费额度)", argumentHint: "<level>" },'],
|
|
80
|
-
['{ name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },', '{ name: "scoped-models", description: "设置 Ctrl+P 轮换的模型范围" },'],
|
|
81
|
-
['{ name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },', '{ name: "export", description: "导出会话(默认 HTML,也可指定 .html/.jsonl 路径)" },'],
|
|
82
|
-
['{ name: "import", description: "Import and resume a session from a JSONL file" },', '{ name: "import", description: "从 JSONL 文件导入并继续会话" },'],
|
|
83
|
-
['{ name: "share", description: "Share session as a secret GitHub gist" },', '{ name: "share", description: "把会话分享为 GitHub 私密 gist" },'],
|
|
84
|
-
// /bug 会把诊断信息上传给 Pi 上游开发者,u1s1 用户的问题应走 /feedback 到我们这边,从候选列表里去掉
|
|
85
|
-
['{ name: "bug", description: "Report a bug to the Pi developers", argumentHint: "<description>" },', ''],
|
|
86
|
-
['{ name: "copy", description: "Copy last agent message to clipboard" },', '{ name: "copy", description: "复制上一条回复到剪贴板" },'],
|
|
87
|
-
['{ name: "name", description: "Set session display name" },', '{ name: "name", description: "给当前会话起个名字" },'],
|
|
88
|
-
['{ name: "session", description: "Show session info and stats" },', '{ name: "session", description: "查看会话信息与统计" },'],
|
|
89
|
-
['{ name: "changelog", description: "Show changelog entries" },', '{ name: "changelog", description: "查看引擎更新日志(英文)" },'],
|
|
90
|
-
['{ name: "hotkeys", description: "Show all keyboard shortcuts" },', '{ name: "hotkeys", description: "查看全部快捷键" },'],
|
|
91
|
-
['{ name: "fork", description: "Create a new fork from a previous user message" },', '{ name: "fork", description: "从之前某条消息分叉出一个新会话" },'],
|
|
92
|
-
['{ name: "clone", description: "Duplicate the current session at the current position" },', '{ name: "clone", description: "复制当前会话再继续" },'],
|
|
93
|
-
['{ name: "trust", description: "Save project trust decision for future sessions" },', '{ name: "trust", description: "记住对这个项目目录的信任选择" },'],
|
|
94
|
-
['{ name: "login", description: "Configure provider authentication", argumentHint: "<provider>" },', '{ name: "login", description: "换 u1s1 账号请先 /exit,再运行 u1s1 login", argumentHint: "<provider>" },'],
|
|
95
|
-
['{ name: "logout", description: "Remove provider authentication" },', '{ name: "logout", description: "退出登录请先 /exit,再运行 u1s1 logout" },'],
|
|
96
|
-
['{ name: "new", description: "Start a new session" },', '{ name: "new", description: "开始新会话" },'],
|
|
97
|
-
['{ name: "compact", description: "Manually compact the session context" },', '{ name: "compact", description: "压缩会话上下文,腾出空间继续聊" },'],
|
|
98
|
-
['{ name: "resume", description: "Resume a different session" },', '{ name: "resume", description: "恢复或切换到其他会话" },'],
|
|
99
|
-
['{ name: "reload", description: "Reload keybindings, extensions, skills, prompts, themes, and context files" },', '{ name: "reload", description: "重新加载扩展、技能、主题等资源" },'],
|
|
100
|
-
['{ name: "quit", description: `Quit ${APP_NAME}` },', '{ name: "quit", description: `退出 ${APP_NAME}` },'],
|
|
101
|
-
];
|
|
102
|
-
|
|
103
|
-
// 扩展报错文案汉化 + 默认不刷堆栈(dist/modes/interactive/interactive-mode.js),与 pnpm coding-agent patch 同步。
|
|
104
|
-
// 小白看到一屏英文 JS 堆栈只会以为程序坏了;一句中文 + 下一步(重启 / u1s1 feedback)就够。
|
|
105
|
-
const EXTENSION_ERROR_REPLACEMENTS = [
|
|
106
|
-
['const errorMsg = `Extension "${extensionPath}" error: ${error}`;', 'const errorMsg = `扩展 ${String(extensionPath).split(/[\\\\/]/).pop()} 出错(重启 u1s1 一般可恢复;反复出现请运行 u1s1 feedback 反馈):${error}`;'],
|
|
107
|
-
['if (stack) {', 'if (stack && process.env.U1S1_DEBUG_EXTENSIONS === "1") {'],
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
function patchExtensionError() {
|
|
111
|
-
for (const piDir of findPackageDirs("@earendil-works/pi-coding-agent")) {
|
|
112
|
-
const target = join(piDir, "dist", "modes", "interactive", "interactive-mode.js");
|
|
113
|
-
try {
|
|
114
|
-
let text = readFileSync(target, "utf8");
|
|
115
|
-
let applied = 0;
|
|
116
|
-
for (const [from, to] of EXTENSION_ERROR_REPLACEMENTS) {
|
|
117
|
-
if (text.includes(to)) {
|
|
118
|
-
applied++;
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (text.split(from).length !== 2) continue;
|
|
122
|
-
text = text.replace(from, to);
|
|
123
|
-
applied++;
|
|
124
|
-
}
|
|
125
|
-
if (applied < EXTENSION_ERROR_REPLACEMENTS.length) {
|
|
126
|
-
console.log(
|
|
127
|
-
`[u1s1] 提示: pi 版本可能已更新,扩展报错文案补丁只应用了 ${applied}/${EXTENSION_ERROR_REPLACEMENTS.length} 处(不影响使用)`,
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
writeFileSync(target, text);
|
|
131
|
-
} catch {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// /hotkeys 快捷键表汉化(dist/modes/interactive/interactive-mode.js),与 pnpm coding-agent patch 同步。
|
|
138
|
-
// 表格由 pi 硬编码英文拼出来,启动横幅/help 都在引导新手输入 /hotkeys,整页英文等于没给。
|
|
139
|
-
// 只译说明列,按键显示(`${cursorUp}` 等)原样保留;表头出现 4 次,用 expected 计数整体替换。
|
|
140
|
-
const HOTKEYS_REPLACEMENTS = [
|
|
141
|
-
['"Keyboard Shortcuts"', '"快捷键一览"'],
|
|
142
|
-
["**Navigation**", "**移动**"],
|
|
143
|
-
["**Editing**", "**编辑**"],
|
|
144
|
-
["**Other**", "**其他**"],
|
|
145
|
-
["**Extensions**", "**扩展**"],
|
|
146
|
-
{ from: "| Key | Action |", to: "| 按键 | 操作 |", expected: 4 },
|
|
147
|
-
["| Move cursor / browse history |", "| 移动光标 / 翻看输入历史 |"],
|
|
148
|
-
["| Move by word |", "| 按词移动 |"],
|
|
149
|
-
["| Start of line |", "| 跳到行首 |"],
|
|
150
|
-
["| End of line |", "| 跳到行尾 |"],
|
|
151
|
-
["| Jump forward to character |", "| 向后跳到指定字符 |"],
|
|
152
|
-
["| Jump backward to character |", "| 向前跳到指定字符 |"],
|
|
153
|
-
["| Scroll by page |", "| 整页滚动 |"],
|
|
154
|
-
["| Send message |", "| 发送消息 |"],
|
|
155
|
-
['| New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""} |', '| 换行${process.platform === "win32" ? "(Windows Terminal 用 Ctrl+Enter)" : ""} |'],
|
|
156
|
-
["| Delete word backwards |", "| 删除光标前一个词 |"],
|
|
157
|
-
["| Delete word forwards |", "| 删除光标后一个词 |"],
|
|
158
|
-
["| Delete to start of line |", "| 删到行首 |"],
|
|
159
|
-
["| Delete to end of line |", "| 删到行尾 |"],
|
|
160
|
-
["| Paste the most-recently-deleted text |", "| 粘贴最近删除的文本 |"],
|
|
161
|
-
["| Cycle through the deleted text after pasting |", "| 粘贴后在更早删除的文本间循环 |"],
|
|
162
|
-
["| Undo |", "| 撤销 |"],
|
|
163
|
-
["| Path completion / accept autocomplete |", "| 路径补全 / 接受自动补全 |"],
|
|
164
|
-
["| Cancel autocomplete / abort streaming |", "| 取消补全 / 中断模型回复 |"],
|
|
165
|
-
["| Clear editor (first) / exit (second) |", "| 清空输入框(再按一次退出) |"],
|
|
166
|
-
["| Exit (when editor is empty) |", "| 退出(输入框为空时) |"],
|
|
167
|
-
["| Suspend to background |", "| 挂起到后台 |"],
|
|
168
|
-
["| Cycle thinking level |", "| 切换思考深度 |"],
|
|
169
|
-
["| Cycle models |", "| 轮换模型 |"],
|
|
170
|
-
["| Open model selector |", "| 打开模型选择器 |"],
|
|
171
|
-
["| Toggle tool output expansion |", "| 展开 / 收起工具输出 |"],
|
|
172
|
-
["| Toggle thinking block visibility |", "| 显示 / 隐藏思考过程 |"],
|
|
173
|
-
["| Edit message in external editor |", "| 用外部编辑器写消息 |"],
|
|
174
|
-
["| Copy selection or last assistant message |", "| 复制选中内容或最近一条回复 |"],
|
|
175
|
-
["| Queue follow-up message |", "| 把消息排到当前回复之后 |"],
|
|
176
|
-
["| Restore queued messages |", "| 取回排队中的消息 |"],
|
|
177
|
-
["| Paste image or text from clipboard |", "| 从剪贴板粘贴图片或文本 |"],
|
|
178
|
-
["| Slash commands |", "| 斜杠命令(/help 看常用操作) |"],
|
|
179
|
-
["| Run bash command |", "| 运行终端命令 |"],
|
|
180
|
-
["| Run bash command (excluded from context) |", "| 运行终端命令(结果不进上下文) |"],
|
|
181
|
-
];
|
|
182
|
-
|
|
183
|
-
function patchHotkeys() {
|
|
184
|
-
for (const piDir of findPackageDirs("@earendil-works/pi-coding-agent")) {
|
|
185
|
-
const target = join(piDir, "dist", "modes", "interactive", "interactive-mode.js");
|
|
186
|
-
try {
|
|
187
|
-
let text = readFileSync(target, "utf8");
|
|
188
|
-
let applied = 0;
|
|
189
|
-
for (const rule of HOTKEYS_REPLACEMENTS) {
|
|
190
|
-
const { from, to, expected } = Array.isArray(rule) ? { from: rule[0], to: rule[1], expected: 1 } : rule;
|
|
191
|
-
if (text.includes(to)) {
|
|
192
|
-
applied++;
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
if (text.split(from).length !== expected + 1) continue;
|
|
196
|
-
text = text.split(from).join(to);
|
|
197
|
-
applied++;
|
|
198
|
-
}
|
|
199
|
-
if (applied < HOTKEYS_REPLACEMENTS.length) {
|
|
200
|
-
console.log(
|
|
201
|
-
`[u1s1] 提示: pi 版本可能已更新,/hotkeys 汉化补丁只应用了 ${applied}/${HOTKEYS_REPLACEMENTS.length} 处(不影响使用)`,
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
writeFileSync(target, text);
|
|
205
|
-
} catch {
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function patchSlashCommands() {
|
|
212
|
-
for (const piDir of findPackageDirs("@earendil-works/pi-coding-agent")) {
|
|
213
|
-
const target = join(piDir, "dist", "core", "slash-commands.js");
|
|
214
|
-
try {
|
|
215
|
-
let text = readFileSync(target, "utf8");
|
|
216
|
-
if (text.includes("打开设置菜单")) continue; // pnpm patch 或此前运行已应用
|
|
217
|
-
|
|
218
|
-
let applied = 0;
|
|
219
|
-
for (const [from, to] of SLASH_COMMANDS_REPLACEMENTS) {
|
|
220
|
-
if (!text.includes(from)) continue;
|
|
221
|
-
text = text.split(from).join(to);
|
|
222
|
-
applied++;
|
|
223
|
-
}
|
|
224
|
-
if (applied < SLASH_COMMANDS_REPLACEMENTS.length) {
|
|
225
|
-
console.log(
|
|
226
|
-
`[u1s1] 提示: pi 版本可能已更新,命令描述汉化只应用了 ${applied}/${SLASH_COMMANDS_REPLACEMENTS.length} 处(不影响使用)`,
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
writeFileSync(target, text);
|
|
230
|
-
} catch {
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
77
|
function patchCompactUi() {
|
|
237
78
|
const piDirs = findPackageDirs("@earendil-works/pi-coding-agent");
|
|
238
79
|
if (piDirs.length === 0) {
|
|
@@ -268,130 +109,227 @@ function patchCompactUi() {
|
|
|
268
109
|
}
|
|
269
110
|
}
|
|
270
111
|
|
|
271
|
-
// ---- TUI
|
|
112
|
+
// ---- TUI 文案(与 pnpm coding-agent patch 同步,patch-pi.test.ts 校验双轨一致) ----
|
|
113
|
+
// 中英双语:u1s1 CLI 拉起 pi 前写 process.env.U1S1_LANG(国际站 "en",中国站 "zh"),
|
|
114
|
+
// 补丁注入的文案一律写成 `process.env.U1S1_LANG === "en" ? "English" : "中文"`,
|
|
115
|
+
// 英文优先用 pi 上游原文;非 "en" 时中文与之前逐字节一致。
|
|
116
|
+
// 不抽成文件内 helper:规则逐条独立应用,helper 那条没打上而用到它的打上了会直接 ReferenceError。
|
|
117
|
+
// 旧版安装打的是纯中文替换,zhOnly(to) 能还原出那个旧结果,applyTextRules 会把它升级成双语版。
|
|
118
|
+
|
|
119
|
+
// 内置斜杠命令描述(dist/core/slash-commands.js)。模块加载时求值:CLI 在动态 import pi 之前就设好了 U1S1_LANG。
|
|
120
|
+
const SLASH_COMMANDS_REPLACEMENTS = [
|
|
121
|
+
['{ name: "settings", description: "Open settings menu" },', '{ name: "settings", description: process.env.U1S1_LANG === "en" ? "Open settings menu" : "打开设置菜单" },'],
|
|
122
|
+
['{ name: "model", description: "Select model (opens selector UI)", argumentHint: "<provider/model>" },', '{ name: "model", description: process.env.U1S1_LANG === "en" ? "Select model (opens selector UI)" : "切换模型(列表里有免费/价格提示)", argumentHint: "<provider/model>" },'],
|
|
123
|
+
['{ name: "tree", description: "Navigate session tree (switch branches)" },', '{ name: "tree", description: process.env.U1S1_LANG === "en" ? "Navigate session tree (switch branches)" : "查看会话分支树,切换分支" },'],
|
|
124
|
+
['{ name: "thinking", description: "Set thinking level", argumentHint: "<level>" },', '{ name: "thinking", description: process.env.U1S1_LANG === "en" ? "Set thinking level" : "调整思考强度(想得越深越费额度)", argumentHint: "<level>" },'],
|
|
125
|
+
['{ name: "scoped-models", description: "Enable/disable models for Ctrl+P cycling" },', '{ name: "scoped-models", description: process.env.U1S1_LANG === "en" ? "Enable/disable models for Ctrl+P cycling" : "设置 Ctrl+P 轮换的模型范围" },'],
|
|
126
|
+
['{ name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },', '{ name: "export", description: process.env.U1S1_LANG === "en" ? "Export session (HTML default, or specify path: .html/.jsonl)" : "导出会话(默认 HTML,也可指定 .html/.jsonl 路径)" },'],
|
|
127
|
+
['{ name: "import", description: "Import and resume a session from a JSONL file" },', '{ name: "import", description: process.env.U1S1_LANG === "en" ? "Import and resume a session from a JSONL file" : "从 JSONL 文件导入并继续会话" },'],
|
|
128
|
+
['{ name: "share", description: "Share session as a secret GitHub gist" },', '{ name: "share", description: process.env.U1S1_LANG === "en" ? "Share session as a secret GitHub gist" : "把会话分享为 GitHub 私密 gist" },'],
|
|
129
|
+
// /bug 会把诊断信息上传给 Pi 上游开发者,u1s1 用户的问题应走 /feedback 到我们这边,从候选列表里去掉
|
|
130
|
+
['{ name: "bug", description: "Report a bug to the Pi developers", argumentHint: "<description>" },', ''],
|
|
131
|
+
['{ name: "copy", description: "Copy last agent message to clipboard" },', '{ name: "copy", description: process.env.U1S1_LANG === "en" ? "Copy last agent message to clipboard" : "复制上一条回复到剪贴板" },'],
|
|
132
|
+
['{ name: "name", description: "Set session display name" },', '{ name: "name", description: process.env.U1S1_LANG === "en" ? "Set session display name" : "给当前会话起个名字" },'],
|
|
133
|
+
['{ name: "session", description: "Show session info and stats" },', '{ name: "session", description: process.env.U1S1_LANG === "en" ? "Show session info and stats" : "查看会话信息与统计" },'],
|
|
134
|
+
['{ name: "changelog", description: "Show changelog entries" },', '{ name: "changelog", description: process.env.U1S1_LANG === "en" ? "Show changelog entries" : "查看引擎更新日志(英文)" },'],
|
|
135
|
+
['{ name: "hotkeys", description: "Show all keyboard shortcuts" },', '{ name: "hotkeys", description: process.env.U1S1_LANG === "en" ? "Show all keyboard shortcuts" : "查看全部快捷键" },'],
|
|
136
|
+
['{ name: "fork", description: "Create a new fork from a previous user message" },', '{ name: "fork", description: process.env.U1S1_LANG === "en" ? "Create a new fork from a previous user message" : "从之前某条消息分叉出一个新会话" },'],
|
|
137
|
+
['{ name: "clone", description: "Duplicate the current session at the current position" },', '{ name: "clone", description: process.env.U1S1_LANG === "en" ? "Duplicate the current session at the current position" : "复制当前会话再继续" },'],
|
|
138
|
+
['{ name: "trust", description: "Save project trust decision for future sessions" },', '{ name: "trust", description: process.env.U1S1_LANG === "en" ? "Save project trust decision for future sessions" : "记住对这个项目目录的信任选择" },'],
|
|
139
|
+
// /login /logout 管的是 pi 自己的渠道登录,u1s1 账号要退出 TUI 用 u1s1 login/logout,英文也照这个意思写
|
|
140
|
+
['{ name: "login", description: "Configure provider authentication", argumentHint: "<provider>" },', '{ name: "login", description: process.env.U1S1_LANG === "en" ? "To switch u1s1 accounts, /exit and run u1s1 login" : "换 u1s1 账号请先 /exit,再运行 u1s1 login", argumentHint: "<provider>" },'],
|
|
141
|
+
['{ name: "logout", description: "Remove provider authentication" },', '{ name: "logout", description: process.env.U1S1_LANG === "en" ? "To sign out, /exit and run u1s1 logout" : "退出登录请先 /exit,再运行 u1s1 logout" },'],
|
|
142
|
+
['{ name: "new", description: "Start a new session" },', '{ name: "new", description: process.env.U1S1_LANG === "en" ? "Start a new session" : "开始新会话" },'],
|
|
143
|
+
['{ name: "compact", description: "Manually compact the session context" },', '{ name: "compact", description: process.env.U1S1_LANG === "en" ? "Manually compact the session context" : "压缩会话上下文,腾出空间继续聊" },'],
|
|
144
|
+
['{ name: "resume", description: "Resume a different session" },', '{ name: "resume", description: process.env.U1S1_LANG === "en" ? "Resume a different session" : "恢复或切换到其他会话" },'],
|
|
145
|
+
['{ name: "reload", description: "Reload keybindings, extensions, skills, prompts, themes, and context files" },', '{ name: "reload", description: process.env.U1S1_LANG === "en" ? "Reload keybindings, extensions, skills, prompts, themes, and context files" : "重新加载扩展、技能、主题等资源" },'],
|
|
146
|
+
['{ name: "quit", description: `Quit ${APP_NAME}` },', '{ name: "quit", description: process.env.U1S1_LANG === "en" ? `Quit ${APP_NAME}` : `退出 ${APP_NAME}` },'],
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
// 扩展报错一句话 + 默认不刷堆栈(dist/modes/interactive/interactive-mode.js)。
|
|
150
|
+
// 小白看到一屏 JS 堆栈只会以为程序坏了;一句话 + 下一步(重启 / u1s1 feedback)就够。
|
|
151
|
+
const EXTENSION_ERROR_REPLACEMENTS = [
|
|
152
|
+
['const errorMsg = `Extension "${extensionPath}" error: ${error}`;', 'const errorMsg = process.env.U1S1_LANG === "en" ? `Extension ${String(extensionPath).split(/[\\\\/]/).pop()} failed (restarting u1s1 usually fixes it; if it keeps happening, run u1s1 feedback): ${error}` : `扩展 ${String(extensionPath).split(/[\\\\/]/).pop()} 出错(重启 u1s1 一般可恢复;反复出现请运行 u1s1 feedback 反馈):${error}`;'],
|
|
153
|
+
['if (stack) {', 'if (stack && process.env.U1S1_DEBUG_EXTENSIONS === "1") {'],
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
// /hotkeys 快捷键表(dist/modes/interactive/interactive-mode.js)。
|
|
157
|
+
// 表格由 pi 硬编码拼出来,启动横幅/help 都在引导新手输入 /hotkeys,整页英文等于没给。
|
|
158
|
+
// 只换说明列,按键显示(`${cursorUp}` 等)原样保留;表头出现 4 次,用 expected 计数整体替换。
|
|
159
|
+
const HOTKEYS_REPLACEMENTS = [
|
|
160
|
+
['"Keyboard Shortcuts"', 'process.env.U1S1_LANG === "en" ? "Keyboard Shortcuts" : "快捷键一览"'],
|
|
161
|
+
["**Navigation**", '${process.env.U1S1_LANG === "en" ? "**Navigation**" : "**移动**"}'],
|
|
162
|
+
["**Editing**", '${process.env.U1S1_LANG === "en" ? "**Editing**" : "**编辑**"}'],
|
|
163
|
+
["**Other**", '${process.env.U1S1_LANG === "en" ? "**Other**" : "**其他**"}'],
|
|
164
|
+
["**Extensions**", '${process.env.U1S1_LANG === "en" ? "**Extensions**" : "**扩展**"}'],
|
|
165
|
+
{ from: "| Key | Action |", to: '| ${process.env.U1S1_LANG === "en" ? "Key" : "按键"} | ${process.env.U1S1_LANG === "en" ? "Action" : "操作"} |', expected: 4 },
|
|
166
|
+
["| Move cursor / browse history |", '| ${process.env.U1S1_LANG === "en" ? "Move cursor / browse history" : "移动光标 / 翻看输入历史"} |'],
|
|
167
|
+
["| Move by word |", '| ${process.env.U1S1_LANG === "en" ? "Move by word" : "按词移动"} |'],
|
|
168
|
+
["| Start of line |", '| ${process.env.U1S1_LANG === "en" ? "Start of line" : "跳到行首"} |'],
|
|
169
|
+
["| End of line |", '| ${process.env.U1S1_LANG === "en" ? "End of line" : "跳到行尾"} |'],
|
|
170
|
+
["| Jump forward to character |", '| ${process.env.U1S1_LANG === "en" ? "Jump forward to character" : "向后跳到指定字符"} |'],
|
|
171
|
+
["| Jump backward to character |", '| ${process.env.U1S1_LANG === "en" ? "Jump backward to character" : "向前跳到指定字符"} |'],
|
|
172
|
+
["| Scroll by page |", '| ${process.env.U1S1_LANG === "en" ? "Scroll by page" : "整页滚动"} |'],
|
|
173
|
+
["| Send message |", '| ${process.env.U1S1_LANG === "en" ? "Send message" : "发送消息"} |'],
|
|
174
|
+
['| New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""} |', '| ${process.env.U1S1_LANG === "en" ? "New line" : "换行"}${process.platform === "win32" ? (process.env.U1S1_LANG === "en" ? " (Ctrl+Enter on Windows Terminal)" : "(Windows Terminal 用 Ctrl+Enter)") : ""} |'],
|
|
175
|
+
["| Delete word backwards |", '| ${process.env.U1S1_LANG === "en" ? "Delete word backwards" : "删除光标前一个词"} |'],
|
|
176
|
+
["| Delete word forwards |", '| ${process.env.U1S1_LANG === "en" ? "Delete word forwards" : "删除光标后一个词"} |'],
|
|
177
|
+
["| Delete to start of line |", '| ${process.env.U1S1_LANG === "en" ? "Delete to start of line" : "删到行首"} |'],
|
|
178
|
+
["| Delete to end of line |", '| ${process.env.U1S1_LANG === "en" ? "Delete to end of line" : "删到行尾"} |'],
|
|
179
|
+
["| Paste the most-recently-deleted text |", '| ${process.env.U1S1_LANG === "en" ? "Paste the most-recently-deleted text" : "粘贴最近删除的文本"} |'],
|
|
180
|
+
["| Cycle through the deleted text after pasting |", '| ${process.env.U1S1_LANG === "en" ? "Cycle through the deleted text after pasting" : "粘贴后在更早删除的文本间循环"} |'],
|
|
181
|
+
["| Undo |", '| ${process.env.U1S1_LANG === "en" ? "Undo" : "撤销"} |'],
|
|
182
|
+
["| Path completion / accept autocomplete |", '| ${process.env.U1S1_LANG === "en" ? "Path completion / accept autocomplete" : "路径补全 / 接受自动补全"} |'],
|
|
183
|
+
["| Cancel autocomplete / abort streaming |", '| ${process.env.U1S1_LANG === "en" ? "Cancel autocomplete / abort streaming" : "取消补全 / 中断模型回复"} |'],
|
|
184
|
+
["| Clear editor (first) / exit (second) |", '| ${process.env.U1S1_LANG === "en" ? "Clear editor (first) / exit (second)" : "清空输入框(再按一次退出)"} |'],
|
|
185
|
+
["| Exit (when editor is empty) |", '| ${process.env.U1S1_LANG === "en" ? "Exit (when editor is empty)" : "退出(输入框为空时)"} |'],
|
|
186
|
+
["| Suspend to background |", '| ${process.env.U1S1_LANG === "en" ? "Suspend to background" : "挂起到后台"} |'],
|
|
187
|
+
["| Cycle thinking level |", '| ${process.env.U1S1_LANG === "en" ? "Cycle thinking level" : "切换思考深度"} |'],
|
|
188
|
+
["| Cycle models |", '| ${process.env.U1S1_LANG === "en" ? "Cycle models" : "轮换模型"} |'],
|
|
189
|
+
["| Open model selector |", '| ${process.env.U1S1_LANG === "en" ? "Open model selector" : "打开模型选择器"} |'],
|
|
190
|
+
["| Toggle tool output expansion |", '| ${process.env.U1S1_LANG === "en" ? "Toggle tool output expansion" : "展开 / 收起工具输出"} |'],
|
|
191
|
+
["| Toggle thinking block visibility |", '| ${process.env.U1S1_LANG === "en" ? "Toggle thinking block visibility" : "显示 / 隐藏思考过程"} |'],
|
|
192
|
+
["| Edit message in external editor |", '| ${process.env.U1S1_LANG === "en" ? "Edit message in external editor" : "用外部编辑器写消息"} |'],
|
|
193
|
+
["| Copy selection or last assistant message |", '| ${process.env.U1S1_LANG === "en" ? "Copy selection or last assistant message" : "复制选中内容或最近一条回复"} |'],
|
|
194
|
+
["| Queue follow-up message |", '| ${process.env.U1S1_LANG === "en" ? "Queue follow-up message" : "把消息排到当前回复之后"} |'],
|
|
195
|
+
["| Restore queued messages |", '| ${process.env.U1S1_LANG === "en" ? "Restore queued messages" : "取回排队中的消息"} |'],
|
|
196
|
+
["| Paste image or text from clipboard |", '| ${process.env.U1S1_LANG === "en" ? "Paste image or text from clipboard" : "从剪贴板粘贴图片或文本"} |'],
|
|
197
|
+
["| Slash commands |", '| ${process.env.U1S1_LANG === "en" ? "Slash commands (/help for common actions)" : "斜杠命令(/help 看常用操作)"} |'],
|
|
198
|
+
["| Run bash command |", '| ${process.env.U1S1_LANG === "en" ? "Run bash command" : "运行终端命令"} |'],
|
|
199
|
+
["| Run bash command (excluded from context) |", '| ${process.env.U1S1_LANG === "en" ? "Run bash command (excluded from context)" : "运行终端命令(结果不进上下文)"} |'],
|
|
200
|
+
];
|
|
201
|
+
|
|
272
202
|
// 新手最常撞见的几句:Esc 中断后的 "Operation aborted"、每条报错前缀 "Error:"、
|
|
273
203
|
// 每轮都在转的 "Working..."、选择器脚注的 "navigate/select/cancel"、/model 列表只显示 id
|
|
274
204
|
// 看不到免费/价格提示、/settings 整页英文。行为不变,只换文案;选择器改为渲染 name(带提示)。
|
|
275
205
|
const ASSISTANT_TEXT_REPLACEMENTS = [
|
|
276
|
-
['theme.fg("error", "Response was truncated before completion.")', 'theme.fg("error", "回复超出单次长度上限被截断了,输入「继续」可以接着写。")'],
|
|
277
|
-
[': "Operation aborted";', ': "已中断";'],
|
|
278
|
-
['const errorMsg = message.errorMessage || "Unknown error";', 'const errorMsg = message.errorMessage || "未知错误(反复出现可运行 u1s1 feedback 反馈)";'],
|
|
279
|
-
['theme.fg("error", `Error: ${errorMsg}`)', 'theme.fg("error", `出错:${errorMsg}`)'],
|
|
206
|
+
['theme.fg("error", "Response was truncated before completion.")', 'theme.fg("error", process.env.U1S1_LANG === "en" ? "Response was truncated before completion. Type \\"continue\\" to pick up where it left off." : "回复超出单次长度上限被截断了,输入「继续」可以接着写。")'],
|
|
207
|
+
[': "Operation aborted";', ': (process.env.U1S1_LANG === "en" ? "Operation aborted" : "已中断");'],
|
|
208
|
+
['const errorMsg = message.errorMessage || "Unknown error";', 'const errorMsg = message.errorMessage || (process.env.U1S1_LANG === "en" ? "Unknown error (if it keeps happening, run u1s1 feedback)" : "未知错误(反复出现可运行 u1s1 feedback 反馈)");'],
|
|
209
|
+
['theme.fg("error", `Error: ${errorMsg}`)', 'theme.fg("error", process.env.U1S1_LANG === "en" ? `Error: ${errorMsg}` : `出错:${errorMsg}`)'],
|
|
280
210
|
];
|
|
281
211
|
const WORKING_TEXT_REPLACEMENTS = [
|
|
282
|
-
['defaultWorkingMessage = "Working";', 'defaultWorkingMessage = "思考中…";'],
|
|
283
|
-
['defaultHiddenThinkingLabel = "Thinking...";', 'defaultHiddenThinkingLabel = "思考中…";'],
|
|
212
|
+
['defaultWorkingMessage = "Working";', 'defaultWorkingMessage = process.env.U1S1_LANG === "en" ? "Working" : "思考中…";'],
|
|
213
|
+
['defaultHiddenThinkingLabel = "Thinking...";', 'defaultHiddenThinkingLabel = process.env.U1S1_LANG === "en" ? "Thinking..." : "思考中…";'],
|
|
284
214
|
];
|
|
285
215
|
// 脚注提示(↑↓ navigate · enter select · esc cancel …)在十几个组件里各写各的,
|
|
286
|
-
// 在 keyHint/rawKeyHint
|
|
216
|
+
// 在 keyHint/rawKeyHint 出口处统一查表翻译,新组件加进来也能覆盖到;英文直接用原文。
|
|
287
217
|
const KEY_HINT_REPLACEMENTS = [
|
|
288
|
-
|
|
289
|
-
'export function keyHint(keybinding, description) {',
|
|
290
|
-
'const U1S1_HINTS = { cancel: "取消", "to cancel": "取消", "to cancel,": "取消,", navigate: "上下选择", submit: "提交", "to submit": "提交", save: "保存", select: "选择", confirm: "确认", scope: "切换范围", "to expand": "展开", "to collapse": "收起", "to run bash": "直接跑命令", "to run bash (no context)": "跑命令(不进上下文)", toggle: "切换", "cycle inherit/+/-": "循环 继承/+/-", "for commands": "输入命令", commands: "命令", bash: "命令", close: "关闭", "to close": "关闭", "to attach": "拖入文件即附加", "skip setup": "跳过设置", "switch mode": "切换模式", newline: "换行", "to delete to end": "删到行尾", sort: "排序", named: "只看已命名", rename: "重命名", delete: "删除", "to exit": "退出", "to cycle models": "切换模型", "external editor": "外部编辑器", "clear/exit": "清空/退出" };\nconst u1s1Hint = (d) => U1S1_HINTS[d] ?? d;\nexport function keyHint(keybinding, description) {',
|
|
291
|
-
|
|
218
|
+
{
|
|
219
|
+
from: 'export function keyHint(keybinding, description) {',
|
|
220
|
+
to: 'const U1S1_HINTS = { cancel: "取消", "to cancel": "取消", "to cancel,": "取消,", navigate: "上下选择", submit: "提交", "to submit": "提交", save: "保存", select: "选择", confirm: "确认", scope: "切换范围", "to expand": "展开", "to collapse": "收起", "to run bash": "直接跑命令", "to run bash (no context)": "跑命令(不进上下文)", toggle: "切换", "cycle inherit/+/-": "循环 继承/+/-", "for commands": "输入命令", commands: "命令", bash: "命令", close: "关闭", "to close": "关闭", "to attach": "拖入文件即附加", "skip setup": "跳过设置", "switch mode": "切换模式", newline: "换行", "to delete to end": "删到行尾", sort: "排序", named: "只看已命名", rename: "重命名", delete: "删除", "to exit": "退出", "to cycle models": "切换模型", "external editor": "外部编辑器", "clear/exit": "清空/退出" };\nconst u1s1Hint = (d) => (process.env.U1S1_LANG === "en" ? d : U1S1_HINTS[d] ?? d);\nexport function keyHint(keybinding, description) {',
|
|
221
|
+
legacy: "const u1s1Hint = (d) => U1S1_HINTS[d] ?? d;",
|
|
222
|
+
legacyTo: 'const u1s1Hint = (d) => (process.env.U1S1_LANG === "en" ? d : U1S1_HINTS[d] ?? d);',
|
|
223
|
+
},
|
|
292
224
|
{ from: 'theme.fg("muted", ` ${description}`)', to: 'theme.fg("muted", ` ${u1s1Hint(description)}`)', expected: 2 },
|
|
293
225
|
];
|
|
294
226
|
const MODEL_SELECTOR_REPLACEMENTS = [
|
|
295
227
|
['const modelText = isSelected ? theme.fg("accent", item.id) : item.id;', 'const modelText = isSelected ? theme.fg("accent", item.model.name || item.id) : (item.model.name || item.id);'],
|
|
296
|
-
['Model Name: ${selected.model.name}`)', '模型 ID: ${selected.model.id}`)'],
|
|
297
|
-
['No matching models")', '没有匹配的模型")'],
|
|
298
|
-
['theme.fg("muted", " · default")', 'theme.fg("muted", " · 默认")'],
|
|
299
|
-
['const allText = this.scope === "all" ? theme.fg("accent", "all") : theme.fg("muted", "all");', 'const allText = this.scope === "all" ? theme.fg("accent", "全部") : theme.fg("muted", "全部");'],
|
|
300
|
-
['const scopedText = this.scope === "scoped" ? theme.fg("accent", "scoped") : theme.fg("muted", "scoped");', 'const scopedText = this.scope === "scoped" ? theme.fg("accent", "已配置") : theme.fg("muted", "已配置");'],
|
|
301
|
-
['theme.fg("muted", "Scope: ")', 'theme.fg("muted", "范围: ")'],
|
|
302
|
-
['theme.fg("muted", " (all/scoped)")', 'theme.fg("muted", " (全部/已配置)")'],
|
|
303
|
-
['refreshStatusMessage = "Refreshing model catalogs…";', 'refreshStatusMessage = "正在刷新模型列表…";'],
|
|
304
|
-
['"Model catalogs refreshed."', '"模型列表已刷新。"'],
|
|
305
|
-
|
|
228
|
+
['theme.fg("muted", ` Model Name: ${selected.model.name}`)', 'theme.fg("muted", ` ${process.env.U1S1_LANG === "en" ? "Model ID" : "模型 ID"}: ${selected.model.id}`)'],
|
|
229
|
+
['theme.fg("muted", " No matching models")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? " No matching models" : " 没有匹配的模型")'],
|
|
230
|
+
['theme.fg("muted", " · default")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? " · default" : " · 默认")'],
|
|
231
|
+
['const allText = this.scope === "all" ? theme.fg("accent", "all") : theme.fg("muted", "all");', 'const allText = this.scope === "all" ? theme.fg("accent", process.env.U1S1_LANG === "en" ? "all" : "全部") : theme.fg("muted", process.env.U1S1_LANG === "en" ? "all" : "全部");'],
|
|
232
|
+
['const scopedText = this.scope === "scoped" ? theme.fg("accent", "scoped") : theme.fg("muted", "scoped");', 'const scopedText = this.scope === "scoped" ? theme.fg("accent", process.env.U1S1_LANG === "en" ? "scoped" : "已配置") : theme.fg("muted", process.env.U1S1_LANG === "en" ? "scoped" : "已配置");'],
|
|
233
|
+
['theme.fg("muted", "Scope: ")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? "Scope: " : "范围: ")'],
|
|
234
|
+
['theme.fg("muted", " (all/scoped)")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? " (all/scoped)" : " (全部/已配置)")'],
|
|
235
|
+
['refreshStatusMessage = "Refreshing model catalogs…";', 'refreshStatusMessage = process.env.U1S1_LANG === "en" ? "Refreshing model catalogs…" : "正在刷新模型列表…";'],
|
|
236
|
+
['"Model catalogs refreshed."', '(process.env.U1S1_LANG === "en" ? "Model catalogs refreshed." : "模型列表已刷新。")'],
|
|
237
|
+
// 去掉 "Use /login":u1s1 的模型由账号决定,不走 pi 的渠道登录
|
|
238
|
+
['const hintText = "Only showing models from configured providers. Use /login to add providers.";', 'const hintText = process.env.U1S1_LANG === "en" ? "Only showing models from configured providers." : "只显示已配置渠道的模型。";'],
|
|
306
239
|
];
|
|
240
|
+
// THINKING_DESCRIPTIONS / DEFAULT_PROJECT_TRUST_LABELS 是模块顶层常量,加载时求值(同斜杠命令,见上)。
|
|
307
241
|
const SETTINGS_REPLACEMENTS = [
|
|
308
|
-
['off: "No reasoning",', 'off: "不推理",'],
|
|
309
|
-
['minimal: "Very brief reasoning (~1k tokens)",', 'minimal: "极简推理(约 1k token)",'],
|
|
310
|
-
['low: "Light reasoning (~2k tokens)",', 'low: "轻度推理(约 2k token)",'],
|
|
311
|
-
['medium: "Moderate reasoning (~8k tokens)",', 'medium: "中等推理(约 8k token)",'],
|
|
312
|
-
['high: "Deep reasoning (~16k tokens)",', 'high: "深度推理(约 16k token)",'],
|
|
313
|
-
['xhigh: "Extra-high reasoning (~32k tokens)",', 'xhigh: "超深推理(约 32k token)",'],
|
|
314
|
-
['max: "Maximum reasoning",', 'max: "最大推理",'],
|
|
315
|
-
['ask: "Ask",', 'ask: "每次询问",'],
|
|
316
|
-
['always: "Always trust",', 'always: "始终信任",'],
|
|
317
|
-
['never: "Never trust",', 'never: "从不信任",'],
|
|
318
|
-
['new SelectSubmenu("Theme", "Select a theme, or choose Automatic to follow terminal appearance."', 'new SelectSubmenu("主题", "选择一个主题,或选「自动」跟随终端明暗。"'],
|
|
319
|
-
['label: " Automatic",', 'label: " 自动",'],
|
|
320
|
-
['description: "Use separate themes for light and dark terminal appearance",', 'description: "终端明暗各用一套主题",'],
|
|
321
|
-
['theme.fg("accent", "Automatic Theme")', 'theme.fg("accent", "自动主题")'],
|
|
322
|
-
['theme.fg("muted", "Choose themes for terminal light and dark appearance.")', 'theme.fg("muted", "分别为终端的浅色/深色外观选主题。")'],
|
|
323
|
-
['theme.fg("muted", "Light/dark detection requires terminal support.")', 'theme.fg("muted", "明暗检测需要终端支持。")'],
|
|
324
|
-
['label: "Light theme",', 'label: "浅色主题",'],
|
|
325
|
-
['description: "Theme to use in automatic mode when the terminal is light",', 'description: "自动模式下终端为浅色时使用的主题",'],
|
|
326
|
-
['label: "Dark theme",', 'label: "深色主题",'],
|
|
327
|
-
['description: "Theme to use in automatic mode when the terminal is dark",', 'description: "自动模式下终端为深色时使用的主题",'],
|
|
328
|
-
['label: "Apply",', 'label: "应用",'],
|
|
329
|
-
['description: "Save and go back",', 'description: "保存并返回",'],
|
|
330
|
-
['label: "Change mode",', 'label: "切换模式",'],
|
|
331
|
-
['description: "Switch to one theme for light and dark",', 'description: "明暗共用一个主题",'],
|
|
332
|
-
['label: "Auto-compact",', 'label: "自动压缩上下文",'],
|
|
333
|
-
['description: "Automatically compact context when it gets too large",', 'description: "上下文过长时自动压缩",'],
|
|
334
|
-
['label: "Steering mode",', 'label: "插话模式",'],
|
|
335
|
-
['description: "Enter while streaming queues steering messages. \'one-at-a-time\': deliver one, wait for response. \'all\': deliver all at once.",', 'description: "回复进行中按 Enter 发送插话。one-at-a-time:发一条等回复;all:一次全发。",'],
|
|
336
|
-
['label: "Follow-up mode",', 'label: "追问模式",'],
|
|
337
|
-
['description: `${followUpKey} queues follow-up messages until agent stops. \'one-at-a-time\': deliver one, wait for response. \'all\': deliver all at once.`,', 'description: `${followUpKey} 把追问排队到本轮结束再发。one-at-a-time:发一条等回复;all:一次全发。`,'],
|
|
338
|
-
['label: "Transport",', 'label: "传输方式",'],
|
|
339
|
-
['description: "Preferred transport for providers that support multiple transports",', 'description: "支持多种传输方式的渠道优先用哪种",'],
|
|
340
|
-
['label: "Cache warming",', 'label: "缓存预热",'],
|
|
341
|
-
['description: "off; streaming while the agent runs; idle also between runs while continuation stays profitable",', 'description: "off:不预热;streaming:回复进行中保温;idle:两轮之间只要续聊划算也保温",'],
|
|
342
|
-
['label: "HTTP idle timeout",', 'label: "HTTP 空闲超时",'],
|
|
343
|
-
['description: "Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.",', 'description: "等待响应头或数据块的最长空闲间隔;本地模型停顿超过五分钟可关闭。",'],
|
|
344
|
-
['label: "Hide thinking",', 'label: "隐藏思考过程",'],
|
|
345
|
-
['description: "Hide thinking blocks in assistant responses",', 'description: "不显示回复里的思考块",'],
|
|
346
|
-
['label: "Mermaid diagrams",', 'label: "Mermaid 图表",'],
|
|
347
|
-
['description: "Render Mermaid code blocks as Unicode diagrams",', 'description: "把 Mermaid 代码块渲染成字符图",'],
|
|
348
|
-
['label: "Cache miss notices",', 'label: "缓存未命中提示",'],
|
|
349
|
-
['description: "Show transcript notices for cache costs and provider recovery diagnostics",', 'description: "提示缓存开销与渠道恢复诊断",'],
|
|
350
|
-
['label: "Collapse changelog",', 'label: "折叠更新日志",'],
|
|
351
|
-
['description: "Show condensed changelog after updates",', 'description: "更新后只显示精简的更新日志",'],
|
|
352
|
-
['label: "Quiet startup",', 'label: "安静启动",'],
|
|
353
|
-
['description: "Disable verbose printing at startup",', 'description: "启动时不打印冗长信息",'],
|
|
354
|
-
['label: "Install telemetry",', 'label: "安装统计",'],
|
|
355
|
-
['description: "Send an anonymous version/update ping after changelog-detected updates",', 'description: "更新后匿名上报一次版本信息",'],
|
|
356
|
-
['label: "Default project trust",', 'label: "默认项目信任",'],
|
|
357
|
-
['description: "Fallback behavior when no extension or saved trust decision decides project trust",', 'description: "没有扩展或已保存决定时,项目信任的兜底行为",'],
|
|
358
|
-
['label: "Double-escape action",', 'label: "双击 Esc 动作",'],
|
|
359
|
-
['description: "Action when pressing Escape twice with empty editor",', 'description: "输入框为空时连按两次 Esc 的动作",'],
|
|
360
|
-
['label: "Tree filter mode",', 'label: "会话树筛选",'],
|
|
361
|
-
['description: "Default filter when opening /tree",', 'description: "打开 /tree 时的默认筛选",'],
|
|
362
|
-
['label: "Warnings",', 'label: "警告提示",'],
|
|
363
|
-
['description: "Enable or disable individual warnings",', 'description: "逐项开关各类警告",'],
|
|
364
|
-
['label: "Default thinking level per model",', 'label: "各模型默认推理强度",'],
|
|
365
|
-
['description: `Override the default thinking level for specific models. ${cycleThinkingKey} cycles in-session.`,', 'description: `按模型覆盖默认推理强度;会话内用 ${cycleThinkingKey} 切换。`,'],
|
|
366
|
-
['description: "Select a model to configure",', 'description: "选择要设置的模型",'],
|
|
367
|
-
['label: "No models available",', 'label: "没有可用模型",'],
|
|
368
|
-
|
|
369
|
-
['description: "
|
|
370
|
-
['
|
|
371
|
-
['
|
|
372
|
-
['
|
|
373
|
-
['
|
|
374
|
-
['
|
|
375
|
-
['
|
|
376
|
-
['
|
|
377
|
-
['
|
|
378
|
-
['
|
|
379
|
-
['
|
|
380
|
-
['
|
|
381
|
-
['
|
|
242
|
+
['off: "No reasoning",', 'off: process.env.U1S1_LANG === "en" ? "No reasoning" : "不推理",'],
|
|
243
|
+
['minimal: "Very brief reasoning (~1k tokens)",', 'minimal: process.env.U1S1_LANG === "en" ? "Very brief reasoning (~1k tokens)" : "极简推理(约 1k token)",'],
|
|
244
|
+
['low: "Light reasoning (~2k tokens)",', 'low: process.env.U1S1_LANG === "en" ? "Light reasoning (~2k tokens)" : "轻度推理(约 2k token)",'],
|
|
245
|
+
['medium: "Moderate reasoning (~8k tokens)",', 'medium: process.env.U1S1_LANG === "en" ? "Moderate reasoning (~8k tokens)" : "中等推理(约 8k token)",'],
|
|
246
|
+
['high: "Deep reasoning (~16k tokens)",', 'high: process.env.U1S1_LANG === "en" ? "Deep reasoning (~16k tokens)" : "深度推理(约 16k token)",'],
|
|
247
|
+
['xhigh: "Extra-high reasoning (~32k tokens)",', 'xhigh: process.env.U1S1_LANG === "en" ? "Extra-high reasoning (~32k tokens)" : "超深推理(约 32k token)",'],
|
|
248
|
+
['max: "Maximum reasoning",', 'max: process.env.U1S1_LANG === "en" ? "Maximum reasoning" : "最大推理",'],
|
|
249
|
+
['ask: "Ask",', 'ask: process.env.U1S1_LANG === "en" ? "Ask" : "每次询问",'],
|
|
250
|
+
['always: "Always trust",', 'always: process.env.U1S1_LANG === "en" ? "Always trust" : "始终信任",'],
|
|
251
|
+
['never: "Never trust",', 'never: process.env.U1S1_LANG === "en" ? "Never trust" : "从不信任",'],
|
|
252
|
+
['new SelectSubmenu("Theme", "Select a theme, or choose Automatic to follow terminal appearance."', 'new SelectSubmenu(process.env.U1S1_LANG === "en" ? "Theme" : "主题", process.env.U1S1_LANG === "en" ? "Select a theme, or choose Automatic to follow terminal appearance." : "选择一个主题,或选「自动」跟随终端明暗。"'],
|
|
253
|
+
['label: " Automatic",', 'label: process.env.U1S1_LANG === "en" ? " Automatic" : " 自动",'],
|
|
254
|
+
['description: "Use separate themes for light and dark terminal appearance",', 'description: process.env.U1S1_LANG === "en" ? "Use separate themes for light and dark terminal appearance" : "终端明暗各用一套主题",'],
|
|
255
|
+
['theme.fg("accent", "Automatic Theme")', 'theme.fg("accent", process.env.U1S1_LANG === "en" ? "Automatic Theme" : "自动主题")'],
|
|
256
|
+
['theme.fg("muted", "Choose themes for terminal light and dark appearance.")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? "Choose themes for terminal light and dark appearance." : "分别为终端的浅色/深色外观选主题。")'],
|
|
257
|
+
['theme.fg("muted", "Light/dark detection requires terminal support.")', 'theme.fg("muted", process.env.U1S1_LANG === "en" ? "Light/dark detection requires terminal support." : "明暗检测需要终端支持。")'],
|
|
258
|
+
['label: "Light theme",', 'label: process.env.U1S1_LANG === "en" ? "Light theme" : "浅色主题",'],
|
|
259
|
+
['description: "Theme to use in automatic mode when the terminal is light",', 'description: process.env.U1S1_LANG === "en" ? "Theme to use in automatic mode when the terminal is light" : "自动模式下终端为浅色时使用的主题",'],
|
|
260
|
+
['label: "Dark theme",', 'label: process.env.U1S1_LANG === "en" ? "Dark theme" : "深色主题",'],
|
|
261
|
+
['description: "Theme to use in automatic mode when the terminal is dark",', 'description: process.env.U1S1_LANG === "en" ? "Theme to use in automatic mode when the terminal is dark" : "自动模式下终端为深色时使用的主题",'],
|
|
262
|
+
['label: "Apply",', 'label: process.env.U1S1_LANG === "en" ? "Apply" : "应用",'],
|
|
263
|
+
['description: "Save and go back",', 'description: process.env.U1S1_LANG === "en" ? "Save and go back" : "保存并返回",'],
|
|
264
|
+
['label: "Change mode",', 'label: process.env.U1S1_LANG === "en" ? "Change mode" : "切换模式",'],
|
|
265
|
+
['description: "Switch to one theme for light and dark",', 'description: process.env.U1S1_LANG === "en" ? "Switch to one theme for light and dark" : "明暗共用一个主题",'],
|
|
266
|
+
['label: "Auto-compact",', 'label: process.env.U1S1_LANG === "en" ? "Auto-compact" : "自动压缩上下文",'],
|
|
267
|
+
['description: "Automatically compact context when it gets too large",', 'description: process.env.U1S1_LANG === "en" ? "Automatically compact context when it gets too large" : "上下文过长时自动压缩",'],
|
|
268
|
+
['label: "Steering mode",', 'label: process.env.U1S1_LANG === "en" ? "Steering mode" : "插话模式",'],
|
|
269
|
+
['description: "Enter while streaming queues steering messages. \'one-at-a-time\': deliver one, wait for response. \'all\': deliver all at once.",', 'description: process.env.U1S1_LANG === "en" ? "Enter while streaming queues steering messages. \'one-at-a-time\': deliver one, wait for response. \'all\': deliver all at once." : "回复进行中按 Enter 发送插话。one-at-a-time:发一条等回复;all:一次全发。",'],
|
|
270
|
+
['label: "Follow-up mode",', 'label: process.env.U1S1_LANG === "en" ? "Follow-up mode" : "追问模式",'],
|
|
271
|
+
['description: `${followUpKey} queues follow-up messages until agent stops. \'one-at-a-time\': deliver one, wait for response. \'all\': deliver all at once.`,', 'description: process.env.U1S1_LANG === "en" ? `${followUpKey} queues follow-up messages until agent stops. \'one-at-a-time\': deliver one, wait for response. \'all\': deliver all at once.` : `${followUpKey} 把追问排队到本轮结束再发。one-at-a-time:发一条等回复;all:一次全发。`,'],
|
|
272
|
+
['label: "Transport",', 'label: process.env.U1S1_LANG === "en" ? "Transport" : "传输方式",'],
|
|
273
|
+
['description: "Preferred transport for providers that support multiple transports",', 'description: process.env.U1S1_LANG === "en" ? "Preferred transport for providers that support multiple transports" : "支持多种传输方式的渠道优先用哪种",'],
|
|
274
|
+
['label: "Cache warming",', 'label: process.env.U1S1_LANG === "en" ? "Cache warming" : "缓存预热",'],
|
|
275
|
+
['description: "off; streaming while the agent runs; idle also between runs while continuation stays profitable",', 'description: process.env.U1S1_LANG === "en" ? "off; streaming while the agent runs; idle also between runs while continuation stays profitable" : "off:不预热;streaming:回复进行中保温;idle:两轮之间只要续聊划算也保温",'],
|
|
276
|
+
['label: "HTTP idle timeout",', 'label: process.env.U1S1_LANG === "en" ? "HTTP idle timeout" : "HTTP 空闲超时",'],
|
|
277
|
+
['description: "Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.",', 'description: process.env.U1S1_LANG === "en" ? "Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes." : "等待响应头或数据块的最长空闲间隔;本地模型停顿超过五分钟可关闭。",'],
|
|
278
|
+
['label: "Hide thinking",', 'label: process.env.U1S1_LANG === "en" ? "Hide thinking" : "隐藏思考过程",'],
|
|
279
|
+
['description: "Hide thinking blocks in assistant responses",', 'description: process.env.U1S1_LANG === "en" ? "Hide thinking blocks in assistant responses" : "不显示回复里的思考块",'],
|
|
280
|
+
['label: "Mermaid diagrams",', 'label: process.env.U1S1_LANG === "en" ? "Mermaid diagrams" : "Mermaid 图表",'],
|
|
281
|
+
['description: "Render Mermaid code blocks as Unicode diagrams",', 'description: process.env.U1S1_LANG === "en" ? "Render Mermaid code blocks as Unicode diagrams" : "把 Mermaid 代码块渲染成字符图",'],
|
|
282
|
+
['label: "Cache miss notices",', 'label: process.env.U1S1_LANG === "en" ? "Cache miss notices" : "缓存未命中提示",'],
|
|
283
|
+
['description: "Show transcript notices for cache costs and provider recovery diagnostics",', 'description: process.env.U1S1_LANG === "en" ? "Show transcript notices for cache costs and provider recovery diagnostics" : "提示缓存开销与渠道恢复诊断",'],
|
|
284
|
+
['label: "Collapse changelog",', 'label: process.env.U1S1_LANG === "en" ? "Collapse changelog" : "折叠更新日志",'],
|
|
285
|
+
['description: "Show condensed changelog after updates",', 'description: process.env.U1S1_LANG === "en" ? "Show condensed changelog after updates" : "更新后只显示精简的更新日志",'],
|
|
286
|
+
['label: "Quiet startup",', 'label: process.env.U1S1_LANG === "en" ? "Quiet startup" : "安静启动",'],
|
|
287
|
+
['description: "Disable verbose printing at startup",', 'description: process.env.U1S1_LANG === "en" ? "Disable verbose printing at startup" : "启动时不打印冗长信息",'],
|
|
288
|
+
['label: "Install telemetry",', 'label: process.env.U1S1_LANG === "en" ? "Install telemetry" : "安装统计",'],
|
|
289
|
+
['description: "Send an anonymous version/update ping after changelog-detected updates",', 'description: process.env.U1S1_LANG === "en" ? "Send an anonymous version/update ping after changelog-detected updates" : "更新后匿名上报一次版本信息",'],
|
|
290
|
+
['label: "Default project trust",', 'label: process.env.U1S1_LANG === "en" ? "Default project trust" : "默认项目信任",'],
|
|
291
|
+
['description: "Fallback behavior when no extension or saved trust decision decides project trust",', 'description: process.env.U1S1_LANG === "en" ? "Fallback behavior when no extension or saved trust decision decides project trust" : "没有扩展或已保存决定时,项目信任的兜底行为",'],
|
|
292
|
+
['label: "Double-escape action",', 'label: process.env.U1S1_LANG === "en" ? "Double-escape action" : "双击 Esc 动作",'],
|
|
293
|
+
['description: "Action when pressing Escape twice with empty editor",', 'description: process.env.U1S1_LANG === "en" ? "Action when pressing Escape twice with empty editor" : "输入框为空时连按两次 Esc 的动作",'],
|
|
294
|
+
['label: "Tree filter mode",', 'label: process.env.U1S1_LANG === "en" ? "Tree filter mode" : "会话树筛选",'],
|
|
295
|
+
['description: "Default filter when opening /tree",', 'description: process.env.U1S1_LANG === "en" ? "Default filter when opening /tree" : "打开 /tree 时的默认筛选",'],
|
|
296
|
+
['label: "Warnings",', 'label: process.env.U1S1_LANG === "en" ? "Warnings" : "警告提示",'],
|
|
297
|
+
['description: "Enable or disable individual warnings",', 'description: process.env.U1S1_LANG === "en" ? "Enable or disable individual warnings" : "逐项开关各类警告",'],
|
|
298
|
+
['label: "Default thinking level per model",', 'label: process.env.U1S1_LANG === "en" ? "Default thinking level per model" : "各模型默认推理强度",'],
|
|
299
|
+
['description: `Override the default thinking level for specific models. ${cycleThinkingKey} cycles in-session.`,', 'description: process.env.U1S1_LANG === "en" ? `Override the default thinking level for specific models. ${cycleThinkingKey} cycles in-session.` : `按模型覆盖默认推理强度;会话内用 ${cycleThinkingKey} 切换。`,'],
|
|
300
|
+
['description: "Select a model to configure",', 'description: process.env.U1S1_LANG === "en" ? "Select a model to configure" : "选择要设置的模型",'],
|
|
301
|
+
['label: "No models available",', 'label: process.env.U1S1_LANG === "en" ? "No models available" : "没有可用模型",'],
|
|
302
|
+
// u1s1 的模型来自账号登录,不提 pi 的渠道登录 / API key
|
|
303
|
+
['description: "Log in to a provider or configure an API key first",', 'description: process.env.U1S1_LANG === "en" ? "Sign in first" : "请先登录",'],
|
|
304
|
+
['description: "Select default thinking level for this model",', 'description: process.env.U1S1_LANG === "en" ? "Select default thinking level for this model" : "选择这个模型的默认推理强度",'],
|
|
305
|
+
['label: " (clear override)",', 'label: process.env.U1S1_LANG === "en" ? " (clear override)" : " (清除覆盖)",'],
|
|
306
|
+
['description: `Revert to global default (${config.thinkingLevel})`,', 'description: process.env.U1S1_LANG === "en" ? `Revert to global default (${config.thinkingLevel})` : `恢复为全局默认(${config.thinkingLevel})`,'],
|
|
307
|
+
['label: "TUI mode",', 'label: process.env.U1S1_LANG === "en" ? "TUI mode" : "界面模式",'],
|
|
308
|
+
['description: "Interface layout; fullscreen mode is experimental",', 'description: process.env.U1S1_LANG === "en" ? "Interface layout; fullscreen mode is experimental" : "界面布局;全屏模式为实验功能",'],
|
|
309
|
+
['label: "Fullscreen exit output",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen exit output" : "退出全屏时的输出",'],
|
|
310
|
+
['description: "Print the transcript or only a session resume hint when exiting fullscreen mode",', 'description: process.env.U1S1_LANG === "en" ? "Print the transcript or only a session resume hint when exiting fullscreen mode" : "退出全屏时打印完整对话,还是只留一条恢复会话提示",'],
|
|
311
|
+
['label: "Fullscreen scrollbar",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen scrollbar" : "全屏滚动条",'],
|
|
312
|
+
['description: "Scrollbar behavior in fullscreen mode; has no effect in regular mode",', 'description: process.env.U1S1_LANG === "en" ? "Scrollbar behavior in fullscreen mode; has no effect in regular mode" : "全屏模式下的滚动条行为;普通模式无效",'],
|
|
313
|
+
['label: "Fullscreen copy on select",', 'label: process.env.U1S1_LANG === "en" ? "Fullscreen copy on select" : "全屏选中即复制",'],
|
|
314
|
+
['description: "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X",', 'description: process.env.U1S1_LANG === "en" ? "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X" : "全屏模式下选中文字自动复制;关闭后用 Ctrl+X 复制",'],
|
|
315
|
+
['label: "Theme",', 'label: process.env.U1S1_LANG === "en" ? "Theme" : "主题",'],
|
|
316
|
+
['description: "Color theme for the interface",', 'description: process.env.U1S1_LANG === "en" ? "Color theme for the interface" : "界面配色主题",'],
|
|
382
317
|
];
|
|
383
318
|
|
|
384
319
|
// /bug 入口改指向 u1s1 的 /feedback(dist/modes/interactive/interactive-mode.js):
|
|
385
320
|
// 手动输入 /bug、启动时的上次崩溃提示、崩溃退出说明、报错后的一次性提示,都不再把用户引到 Pi 上游。
|
|
386
321
|
const BUG_REPORT_REPLACEMENTS = [
|
|
387
|
-
["await this.handleBugCommand(hint ? hint : undefined);", 'this.showStatus("反馈问题请用 /feedback(会附带诊断信息建工单)");'],
|
|
388
|
-
["this.showWarning(`${APP_NAME} crashed on ${when} (${crash.message}). Run /bug to report it; the crash details are attached automatically.`);",
|
|
389
|
-
["return `To report this crash: ${resume} run /bug. The crash details are attached automatically.`;", 'return "u1s1 异常退出了。反复出现请运行 u1s1 feedback 反馈。";'],
|
|
390
|
-
["`If this looks like a ${APP_NAME} bug, /bug sends a report to the developers.`", '"如果这看起来是 u1s1 自身的问题,可以用 /feedback 反馈。"'],
|
|
322
|
+
["await this.handleBugCommand(hint ? hint : undefined);", 'this.showStatus(process.env.U1S1_LANG === "en" ? "To report a problem, use /feedback (it attaches diagnostics and opens a support ticket)" : "反馈问题请用 /feedback(会附带诊断信息建工单)");'],
|
|
323
|
+
["this.showWarning(`${APP_NAME} crashed on ${when} (${crash.message}). Run /bug to report it; the crash details are attached automatically.`);", 'this.showWarning(process.env.U1S1_LANG === "en" ? `u1s1 exited unexpectedly last time (${when}: ${crash.message}). If it keeps happening, report it with /feedback and mention when it happened.` : `u1s1 上次异常退出(${when}:${crash.message})。反复出现请用 /feedback 反馈,并写上发生时间。`);'],
|
|
324
|
+
["return `To report this crash: ${resume} run /bug. The crash details are attached automatically.`;", 'return process.env.U1S1_LANG === "en" ? "u1s1 exited unexpectedly. If it keeps happening, run u1s1 feedback to report it." : "u1s1 异常退出了。反复出现请运行 u1s1 feedback 反馈。";'],
|
|
325
|
+
["`If this looks like a ${APP_NAME} bug, /bug sends a report to the developers.`", 'process.env.U1S1_LANG === "en" ? "If this looks like a u1s1 bug, report it with /feedback." : "如果这看起来是 u1s1 自身的问题,可以用 /feedback 反馈。"'],
|
|
391
326
|
];
|
|
392
327
|
|
|
393
328
|
const TUI_TEXT_TARGETS = [
|
|
329
|
+
[["core", "slash-commands.js"], SLASH_COMMANDS_REPLACEMENTS, "命令描述"],
|
|
394
330
|
[["modes", "interactive", "components", "assistant-message.js"], ASSISTANT_TEXT_REPLACEMENTS, "回复状态文案"],
|
|
331
|
+
[["modes", "interactive", "interactive-mode.js"], EXTENSION_ERROR_REPLACEMENTS, "扩展报错"],
|
|
332
|
+
[["modes", "interactive", "interactive-mode.js"], HOTKEYS_REPLACEMENTS, "/hotkeys"],
|
|
395
333
|
[["modes", "interactive", "interactive-mode.js"], WORKING_TEXT_REPLACEMENTS, "工作中提示"],
|
|
396
334
|
[["modes", "interactive", "interactive-mode.js"], BUG_REPORT_REPLACEMENTS, "/bug 入口"],
|
|
397
335
|
[["modes", "interactive", "components", "keybinding-hints.js"], KEY_HINT_REPLACEMENTS, "按键脚注"],
|
|
@@ -399,21 +337,50 @@ const TUI_TEXT_TARGETS = [
|
|
|
399
337
|
[["modes", "interactive", "components", "settings-selector.js"], SETTINGS_REPLACEMENTS, "设置菜单"],
|
|
400
338
|
];
|
|
401
339
|
|
|
340
|
+
/**
|
|
341
|
+
* 双语替换结果还原成「旧版纯中文补丁」的样子:把每个
|
|
342
|
+
* `process.env.U1S1_LANG === "en" ? 英文 : 中文` 收成中文那一支。
|
|
343
|
+
* 三种写法:模板字符串里的 `${…}`(去掉引号直接内联)、加了括号的 `(…)`(不含函数调用括号)、裸三元。
|
|
344
|
+
*/
|
|
345
|
+
function zhOnly(to) {
|
|
346
|
+
const str = '(?:"(?:[^"\\\\\\n]|\\\\.)*"|`(?:[^`\\\\]|\\\\.)*`)';
|
|
347
|
+
const ternary = `process\\.env\\.U1S1_LANG === "en" \\? ${str} : (${str})`;
|
|
348
|
+
return to
|
|
349
|
+
.replace(new RegExp(`\\$\\{${ternary}\\}`, "g"), (_, zh) => zh.slice(1, -1))
|
|
350
|
+
.replace(new RegExp(`(?<![\\w$)\\]])\\(${ternary}\\)`, "g"), (_, zh) => zh)
|
|
351
|
+
.replace(new RegExp(ternary, "g"), (_, zh) => zh);
|
|
352
|
+
}
|
|
353
|
+
|
|
402
354
|
/**
|
|
403
355
|
* 逐条套文案替换规则:已替换过的算应用成功(幂等),出现次数与预期不符的跳过
|
|
404
|
-
* (pi 升级后上下文变了,宁可不改也不误改)
|
|
356
|
+
* (pi 升级后上下文变了,宁可不改也不误改)。旧版装过纯中文补丁的(找得到 zhOnly(to)
|
|
357
|
+
* 或规则显式给的 legacy),原地升级成双语版。to 为空的规则表示删除 from。
|
|
405
358
|
*/
|
|
406
359
|
function applyTextRules(text, rules) {
|
|
407
360
|
let applied = 0;
|
|
361
|
+
const count = (needle) => text.split(needle).length - 1;
|
|
408
362
|
for (const rule of rules) {
|
|
409
|
-
const { from, to, expected } = Array.isArray(rule) ? { from: rule[0], to: rule[1]
|
|
363
|
+
const { from, to, expected = 1, legacy, legacyTo } = Array.isArray(rule) ? { from: rule[0], to: rule[1] } : rule;
|
|
364
|
+
if (to === "") {
|
|
365
|
+
if (count(from) === expected) text = text.split(from).join("");
|
|
366
|
+
if (count(from) === 0) applied++;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
410
369
|
if (text.includes(to)) {
|
|
411
370
|
applied++;
|
|
412
371
|
continue;
|
|
413
372
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
373
|
+
// 先认旧版纯中文结果:有的 to 里含着 from(如按键脚注整行前插),先套 from 会重复插入
|
|
374
|
+
const old = legacy ?? zhOnly(to);
|
|
375
|
+
if (old !== to && count(old) === expected) {
|
|
376
|
+
text = text.split(old).join(legacyTo ?? to);
|
|
377
|
+
applied++;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (count(from) === expected) {
|
|
381
|
+
text = text.split(from).join(to);
|
|
382
|
+
applied++;
|
|
383
|
+
}
|
|
417
384
|
}
|
|
418
385
|
return { text, applied };
|
|
419
386
|
}
|
|
@@ -423,13 +390,14 @@ function patchTuiText() {
|
|
|
423
390
|
for (const [parts, rules, label] of TUI_TEXT_TARGETS) {
|
|
424
391
|
const target = join(piDir, "dist", ...parts);
|
|
425
392
|
try {
|
|
426
|
-
const
|
|
393
|
+
const before = readFileSync(target, "utf8");
|
|
394
|
+
const { text, applied } = applyTextRules(before, rules);
|
|
427
395
|
if (applied < rules.length) {
|
|
428
396
|
console.log(
|
|
429
|
-
`[u1s1] 提示: pi 版本可能已更新,${label}
|
|
397
|
+
`[u1s1] 提示: pi 版本可能已更新,${label}文案补丁只应用了 ${applied}/${rules.length} 处(不影响使用)`,
|
|
430
398
|
);
|
|
431
399
|
}
|
|
432
|
-
writeFileSync(target, text);
|
|
400
|
+
if (text !== before) writeFileSync(target, text);
|
|
433
401
|
} catch {
|
|
434
402
|
continue;
|
|
435
403
|
}
|
|
@@ -492,9 +460,6 @@ const ENABLE_AUTOWRAP = "\\x1b[?7h";`,
|
|
|
492
460
|
|
|
493
461
|
try {
|
|
494
462
|
patchCompactUi();
|
|
495
|
-
patchSlashCommands();
|
|
496
|
-
patchExtensionError();
|
|
497
|
-
patchHotkeys();
|
|
498
463
|
patchTuiText();
|
|
499
464
|
patchMainScreenAutowrap();
|
|
500
465
|
} catch (err) {
|