zcode-pro 0.17.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/dist/inject.js +1368 -216
- package/package.json +1 -1
- package/src/host/helper.mjs +1248 -156
- package/src/host/main.mjs +11 -0
- package/src/host/pickFolder.mjs +48 -3
- package/src/host/setup.mjs +34 -9
- package/src/host/taskIndex.mjs +83 -0
- package/src/host/webdav.mjs +128 -12
- package/src/inject/core.js +190 -42
- package/src/inject/features/file-menu.js +9 -2
- package/src/inject/features/plugin-updates.js +1 -1
- package/src/inject/features/settings-dialog.js +821 -118
- package/src/inject/features/styles.js +15 -1
- package/src/inject/features/title-gen.js +169 -0
- package/src/inject/features/title-lock.js +14 -0
- package/src/inject/features/toolbar-icons.js +38 -4
- package/src/inject/index.js +14 -2
- package/src/inject/provider-fields.js +71 -0
- package/src/inject/ui.js +23 -7
package/src/inject/core.js
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
// 注入脚本公共工具:与 helper 通信、DOM 构建、多语言文案。
|
|
2
2
|
export const BOOT = typeof window !== 'undefined' ? (window.__ZCODEPRO__ || {}) : {};
|
|
3
3
|
export const HELPER_URL = BOOT.helperUrl || 'http://127.0.0.1:47889';
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
// token/helperUrl 必须在每次调用时实时读 window.__ZCODEPRO__:cli.mjs 重启后 helper 换了
|
|
6
|
+
// 随机 token,重新注入会被 index.js 的幂等守卫跳过、页面继续用旧闭包——若在模块求值时
|
|
7
|
+
// 固化成常量,旧闭包会永远拿旧 token 报 unauthorized。重注入的 bootstrap 会先更新
|
|
8
|
+
// window.__ZCODEPRO__,实时读即可对上新一轮 token。
|
|
9
|
+
function liveBoot() {
|
|
10
|
+
return (typeof window !== 'undefined' && window.__ZCODEPRO__) || BOOT;
|
|
11
|
+
}
|
|
5
12
|
|
|
6
13
|
export function rpc(path, options = {}) {
|
|
7
|
-
|
|
14
|
+
const boot = liveBoot();
|
|
15
|
+
return fetch((boot.helperUrl || HELPER_URL) + path, {
|
|
8
16
|
method: options.method || 'GET',
|
|
9
17
|
headers: {
|
|
10
18
|
'Content-Type': 'application/json',
|
|
11
|
-
'X-ZCodePro-Token':
|
|
19
|
+
'X-ZCodePro-Token': boot.token || '',
|
|
12
20
|
},
|
|
13
21
|
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
14
22
|
})
|
|
@@ -35,22 +43,31 @@ const zh = {
|
|
|
35
43
|
tabFeatures: '功能',
|
|
36
44
|
tabStyles: '样式',
|
|
37
45
|
tabProxy: '代理',
|
|
46
|
+
tabBehavior: '行为',
|
|
38
47
|
tabAgents: '全局提示词',
|
|
39
48
|
agentsDesc: '写入 ~/.zcode/AGENTS.md,作为默认指令用于所有项目的每次会话;保存后从新会话起生效,项目内的 AGENTS.md 可补充或覆盖。内容超过 100KB 会被应用截断。',
|
|
40
49
|
agentsPlaceholder: '填写希望所有项目默认遵循的指令;清空并保存即移除全局提示词',
|
|
41
50
|
agentsSave: '保存',
|
|
42
51
|
agentsSaved: '全局提示词已保存',
|
|
43
52
|
agentsLoadFailed: '读取全局提示词失败',
|
|
44
|
-
proxyDesc: '为 ZCode Pro 本地助手发起的网络访问设置 HTTP
|
|
53
|
+
proxyDesc: '为 ZCode Pro 本地助手发起的网络访问设置 HTTP 代理:插件市场的更新与安装、插件依赖软件的安装与升级、WebDAV 同步都可走该代理,用下方开关选择生效范围。不影响 ZCode 应用本身与模型请求,保存后立即生效。',
|
|
45
54
|
proxyPlaceholder: 'http://127.0.0.1:7890',
|
|
46
55
|
proxySave: '保存',
|
|
47
56
|
proxySaved: '代理设置已保存',
|
|
48
57
|
proxyHint: '留空保存即清除代理。',
|
|
49
58
|
proxyTest: '检测',
|
|
50
59
|
proxyTesting: '检测中…',
|
|
60
|
+
proxyTestEmpty: '请先输入代理地址',
|
|
51
61
|
proxyTestFailed: '检测失败',
|
|
62
|
+
proxyScopePlugins: '插件市场更新',
|
|
63
|
+
proxyScopePluginsDesc: '插件市场的检查更新、安装与卸载(zcode CLI 的 git 操作)走代理。',
|
|
64
|
+
proxyScopeDeps: '插件依赖软件',
|
|
65
|
+
proxyScopeDepsDesc: 'headroom 与 rtk 的安装、检查更新与升级(pip 与 GitHub 下载)走代理。',
|
|
66
|
+
proxyScopeSync: '同步',
|
|
67
|
+
proxyScopeSyncDesc: 'WebDAV 上传与下载走代理。默认关闭:坚果云等国内网盘直连即可,代理停了同步会跟着失败。',
|
|
52
68
|
tabSync: '同步',
|
|
53
|
-
|
|
69
|
+
tabDebug: '调试',
|
|
70
|
+
syncDesc: '手动把 ZCode Pro 设置与模型设置备份到 WebDAV,或在新电脑上恢复。两项各自独立成文件,可只上传或只覆盖其中一项;每次上传会额外保留一份带时间戳的历史版本,下载时任选一个版本恢复;仅在点击按钮时传输,不做自动同步。',
|
|
54
71
|
syncUrlPlaceholder: 'https://dav.jianguoyun.com/dav/',
|
|
55
72
|
syncDir: '上传目录',
|
|
56
73
|
syncDirPlaceholder: 'zcode-pro',
|
|
@@ -58,7 +75,7 @@ const zh = {
|
|
|
58
75
|
syncPassword: '密码',
|
|
59
76
|
syncSave: '保存',
|
|
60
77
|
syncSaved: '同步配置已保存',
|
|
61
|
-
syncFormHint: '同步文件存到“上传目录”(默认 zcode-pro
|
|
78
|
+
syncFormHint: '同步文件存到“上传目录”(默认 zcode-pro,可多级,自动创建):zcodepro.json 与 model.json 始终是最新一版,旁边另保留带时间戳的历史版本。',
|
|
62
79
|
syncRecLink: '坚果云 WebDAV',
|
|
63
80
|
syncRecSuffix: ':登录与密码填“应用密码”,不是账号密码。',
|
|
64
81
|
syncIncludeZcp: 'ZCode Pro 设置',
|
|
@@ -68,16 +85,44 @@ const zh = {
|
|
|
68
85
|
syncUpload: '上传到云端',
|
|
69
86
|
syncDownload: '从云端下载',
|
|
70
87
|
syncUploading: '上传中…',
|
|
71
|
-
syncDownloading: '下载中…',
|
|
72
88
|
syncUploaded: '已上传到云端',
|
|
73
89
|
syncDownloaded: '已从云端恢复',
|
|
74
90
|
syncLastPush: '上次上传',
|
|
75
91
|
syncLastPull: '上次下载',
|
|
76
92
|
syncNever: '无',
|
|
77
93
|
syncConfirmTitle: '从云端下载',
|
|
78
|
-
syncConfirmDesc: '
|
|
79
|
-
syncConfirmGo: '
|
|
94
|
+
syncConfirmDesc: '将用所选文件覆盖本机对应的一项(覆盖前会在原文件旁留下 .zcodepro-backup 备份);本机的同步配置与登录凭据不受影响。模型设置恢复后建议重启 ZCode 生效。确认导入?',
|
|
95
|
+
syncConfirmGo: '确认导入',
|
|
96
|
+
syncPickDesc: '固定名文件始终是最新一版,带时间戳的是每次上传留下的历史版本;「导入」用所选文件覆盖本机对应的一项。',
|
|
97
|
+
syncVerDeleteLatestNote: '注意:这是“最新”文件,旧版 zcode-pro 的「从云端下载」依赖它,删除后旧版机器将无法下载;带时间戳的历史版本不受影响。',
|
|
80
98
|
syncHint: '同步登录凭据仅保存在本机 zcodepro.json;模型设置包含自定义供应商的 API Key,只会上传到你自己的 WebDAV。',
|
|
99
|
+
syncTest: '测试',
|
|
100
|
+
syncTesting: '测试中…',
|
|
101
|
+
syncTestEmpty: '请先填写 URL',
|
|
102
|
+
syncTestFailed: '测试失败',
|
|
103
|
+
syncTestStepConnect: '连接',
|
|
104
|
+
syncTestStepDir: '目录',
|
|
105
|
+
syncTestStepList: '文件',
|
|
106
|
+
syncTestFilesFound: '云端已有 {n} 个备份文件',
|
|
107
|
+
syncPickTitle: '请选择一个文件',
|
|
108
|
+
syncPickLoading: '正在读取云端文件列表…',
|
|
109
|
+
syncPickEmpty: '云端还没有备份文件,请先在其他电脑上传',
|
|
110
|
+
syncVerImport: '导入',
|
|
111
|
+
syncVerImporting: '导入中…',
|
|
112
|
+
syncVerDelete: '删除',
|
|
113
|
+
syncVerSave: '保存到磁盘',
|
|
114
|
+
syncVerSaving: '保存中…',
|
|
115
|
+
syncVerLatest: '最新',
|
|
116
|
+
syncVerDeleteTitle: '删除云端文件',
|
|
117
|
+
syncVerDeleteDesc: '将删除云端的这份备份,不可恢复:',
|
|
118
|
+
syncVerDeleted: '已删除云端备份',
|
|
119
|
+
syncVerSaved: '已保存到',
|
|
120
|
+
devMode: '开发模式',
|
|
121
|
+
devModeRowDesc: '开启后出现「重载 ZCode Pro」按钮;也可给启动器设环境变量 ZCODEPRO_DEV=1 启用。此标签页仅在开发模式生效时显示。',
|
|
122
|
+
devModeDesc: '开发调试用:改完 zcode-pro 代码后不必重启 ZCode,点「重载 ZCode Pro」即可加载新代码。流程为自动构建(需在源码目录运行)→ 以新进程重启 ZCode Pro(本地助手与注入脚本都换新)→ 所有 ZCode 窗口自动刷新并重新注入;ZCode 本体与已打开的会话不受影响。',
|
|
123
|
+
devReload: '重载 ZCode Pro',
|
|
124
|
+
devReloading: '重载中…',
|
|
125
|
+
devReloadDesc: '点击后约 2-3 秒内所有 ZCode 窗口自动刷新一次并加载新脚本。',
|
|
81
126
|
rowGapName: '段落间距',
|
|
82
127
|
rowGapDesc: '会话中段落等文本块之间的垂直间距(回合之间、答案内部)。',
|
|
83
128
|
listSpacingName: '列表上下边距',
|
|
@@ -94,6 +139,8 @@ const zh = {
|
|
|
94
139
|
tableCellPaddingDesc: '表格单元格内的留白;左输入框为上下、右为左右。',
|
|
95
140
|
lineHeightName: '回答行高',
|
|
96
141
|
lineHeightDesc: '回答正文的行高(倍数)。',
|
|
142
|
+
thinkingLineHeightName: '思考行高',
|
|
143
|
+
thinkingLineHeightDesc: '「已工作/工作中」过程记录(读取/编辑/查阅/终端条目与思考内容)的行高(倍数)。',
|
|
97
144
|
userLineHeightName: '提问行高',
|
|
98
145
|
userLineHeightDesc: '提问内容的行高(倍数)。',
|
|
99
146
|
contentWidthName: '内容宽度',
|
|
@@ -108,6 +155,30 @@ const zh = {
|
|
|
108
155
|
featureSessionSwitchDesc: 'alt+z 在当前与上次会话间来回切换;按住 alt 再按 x/c 弹出最近会话列表前后选择,松开 alt 切换(类 alt+tab)。',
|
|
109
156
|
featureTitleLock: '重命名会话名锁定',
|
|
110
157
|
featureTitleLockDesc: '手动重命名过的会话不再被自动标题覆盖:会话有新回答后界面可能改显自动标题,重启后又变回,本功能让侧栏始终显示重命名后的名字。',
|
|
158
|
+
tabTitleGen: '任务名提示词',
|
|
159
|
+
titleGenShortDesc: '新会话开始作答时,按所选供应商与提示词生成任务名并按手动重命名保存,官方自动标题不再覆盖。',
|
|
160
|
+
titleGenEnabled: '启用',
|
|
161
|
+
titleGenProvider: '供应商',
|
|
162
|
+
titleGenProviderNone: '不跟随(自填地址)',
|
|
163
|
+
titleGenProviderSession: '跟随当前会话',
|
|
164
|
+
titleGenProviderDeleted: '(已删除)',
|
|
165
|
+
titleGenModel: '模型',
|
|
166
|
+
titleGenModelHint: '从列表选择,或直接填写模型 id',
|
|
167
|
+
titleGenModelEmpty: '(未选择)',
|
|
168
|
+
titleGenModelSessionHint: '从列表选择模型;列表第一项为跟随会话当前模型',
|
|
169
|
+
titleGenModelFollowSession: '跟随当前会话当前模型',
|
|
170
|
+
titleGenModelManualHint: '输入模型 id',
|
|
171
|
+
titleGenBaseUrl: '接口地址',
|
|
172
|
+
titleGenApiKey: 'API Key',
|
|
173
|
+
titleGenFormat: '协议',
|
|
174
|
+
titleGenPrompt: '提示词',
|
|
175
|
+
titleGenPromptPlaceholder: '留空使用内置默认提示词(点击下方按钮可查看)',
|
|
176
|
+
titleGenPromptReset: '使用默认提示词',
|
|
177
|
+
titleGenSample: '测试消息',
|
|
178
|
+
titleGenTest: '测试生成',
|
|
179
|
+
titleGenTesting: '生成中…',
|
|
180
|
+
titleGenTestOk: '生成结果:',
|
|
181
|
+
titleGenTestFailed: '生成失败',
|
|
111
182
|
featureWsRunning: '折叠项目运行提示(实验性)',
|
|
112
183
|
featureWsRunningDesc: '折叠的项目里仍有会话在运行时,项目图标旋转提示;全部结束或展开项目后恢复。期间每 60 秒在后台短暂展开该项目复核运行状态再收起,会话行不显示,仅项目位置可能有约四分之一秒的轻微高度起伏。',
|
|
113
184
|
tabPerformance: '性能',
|
|
@@ -157,7 +228,7 @@ const zh = {
|
|
|
157
228
|
featurePinnedExpand: '置顶会话保持项目折叠(实验性)',
|
|
158
229
|
featurePinnedExpandDesc: '点击折叠项目的置顶会话后将其保持折叠。受限于应用机制,项目会先短暂展开再缩起。',
|
|
159
230
|
featureAutoUpdatePlugins: '自动更新 zcode-plugins 插件',
|
|
160
|
-
featureAutoUpdatePluginsDesc: '
|
|
231
|
+
featureAutoUpdatePluginsDesc: '启动时自动检查并更新已安装的 zcode-plugins 插件(只升级不安装,卸载不会被撤销),更新在新会话生效。',
|
|
161
232
|
pluginsUpdateTitle: '插件更新 · zcode-plugins',
|
|
162
233
|
pluginsCheckNow: '检查并更新',
|
|
163
234
|
pluginsUpdating: '正在更新插件…',
|
|
@@ -169,7 +240,7 @@ const zh = {
|
|
|
169
240
|
pluginsAutoUpdated: 'zcode-plugins 插件已更新',
|
|
170
241
|
tabVision: 'Vision',
|
|
171
242
|
visionDesc: '主模型不支持图片输入时(如 glm-5.3),自动把消息里的图片交给下面的视觉模型识别,把描述带给主模型(zcode-vision 插件;/vision-* 命令编辑同一配置)。改动自动保存。',
|
|
172
|
-
visionEnabled: '
|
|
243
|
+
visionEnabled: '启用',
|
|
173
244
|
visionForceIntercept: '主模型能看图也拦截',
|
|
174
245
|
visionMode: '链模式',
|
|
175
246
|
visionModeFallback: '依次尝试',
|
|
@@ -182,10 +253,6 @@ const zh = {
|
|
|
182
253
|
visionMoveDown: '下移',
|
|
183
254
|
visionName: '名称',
|
|
184
255
|
visionUseProvider: '供应商',
|
|
185
|
-
visionUseProviderHint: '下拉选择或手动输入:session=跟随当前会话供应商,或供应商名/ID;设置后 baseUrl/API Key/格式自动取该供应商',
|
|
186
|
-
visionUseProviderNone: '不跟随(自填地址)',
|
|
187
|
-
visionUseProviderSession: '跟随当前会话(session)',
|
|
188
|
-
visionUseProviderUnknown: '未找到该供应商,识别时会报错;请从下拉选择,或输入 session',
|
|
189
256
|
visionSkipAfterFailures: '连续失败次数',
|
|
190
257
|
visionSkipAfterFailuresHint: '某代理连续失败满次数后暂时跳过它,直接试下一个;0 = 不跳过',
|
|
191
258
|
visionSkipMinutes: '跳过分钟数',
|
|
@@ -194,21 +261,21 @@ const zh = {
|
|
|
194
261
|
visionResetChainConfirm: '将链模式与代理列表恢复为默认两级(glm-session → glm-flash),当前对代理的修改会丢失。继续?',
|
|
195
262
|
visionBaseUrl: '接口地址 baseUrl',
|
|
196
263
|
visionModel: '模型',
|
|
197
|
-
visionFormat: '格式',
|
|
198
264
|
visionApiKey: 'API Key',
|
|
199
265
|
visionApiKeyHint: '留空自动取 GLM 订阅 key',
|
|
200
266
|
visionPrompt: '识别提示词(pipeline 后续步可用 {prev} 引用上一步)',
|
|
201
267
|
visionTest: '测试',
|
|
202
268
|
visionTesting: '测试中…',
|
|
203
269
|
visionTestFailed: '视觉代理测试失败',
|
|
204
|
-
visionTestOutput: '测试输出',
|
|
205
270
|
visionCompressKB: '压缩阈值 KB',
|
|
206
271
|
visionCompressKBHint: '超过该大小的图先压缩再识别(最长边 2000、JPEG);0 = 不压缩',
|
|
207
272
|
visionLoadFailed: '读取视觉代理配置失败',
|
|
208
273
|
tabRtk: 'rtk',
|
|
209
274
|
rtkDesc: 'rtk 插件:把常见开发命令的输出压缩 60-90% 再进入上下文(/rtk-* 命令编辑同一配置)。改动自动保存并立即生效。',
|
|
210
|
-
rtkEnabled: '启用
|
|
211
|
-
rtkNotInstalled: '未安装 rtk
|
|
275
|
+
rtkEnabled: '启用',
|
|
276
|
+
rtkNotInstalled: '未安装 rtk 本体;安装后以下设置生效(会话里执行 /rtk-setup 也可以)',
|
|
277
|
+
rtkInstallBin: '安装 rtk 本体',
|
|
278
|
+
rtkBinInstalled: 'rtk 本体已安装;PATH 需注销重登后对会话生效,插件钩子即时生效',
|
|
212
279
|
rtkWhitelistTitle: '白名单(不压缩直接放行)',
|
|
213
280
|
rtkBuiltinLabel: '内置放行(只读,随插件更新):',
|
|
214
281
|
rtkWhitelistDesc: '每条 name(如 docker)或 git:name(如 git:clone)。输出极小或纯副作用的命令无需压缩,加入后不再压缩;内置的 git add/commit/push、mkdir/cp 等不可移除。删除本文件即恢复默认。',
|
|
@@ -221,12 +288,25 @@ const zh = {
|
|
|
221
288
|
tabHeadroom: 'Headroom',
|
|
222
289
|
headroomDesc: 'Headroom 插件:本地压缩代理驻留 127.0.0.1(默认 8787 端口),压缩发往模型的上下文以省 token(/hr-* 命令编辑同一配置)。压缩设备与省电切换保存后立即生效;切换设备会重启代理,正在处理的请求可能中断一次。',
|
|
223
290
|
headroomBinMissing: '未找到 headroom 程序,代理无法启动;安装 headroom 后以下设置自动生效',
|
|
291
|
+
hrInstallBin: '安装 headroom 本体',
|
|
292
|
+
hrBinInstalled: 'headroom 本体已安装;重新打开会话后代理可用(SessionStart 钩子自动拉起)',
|
|
224
293
|
headroomInstallTitle: '未安装 headroom 插件',
|
|
225
294
|
headroomInstallDesc: '从 zcode-plugins 市场安装后即可在此管理压缩代理;建议安装后重启会话生效。',
|
|
226
295
|
headroomInstall: '安装',
|
|
227
296
|
headroomInstalling: '安装中…',
|
|
228
297
|
headroomInstalled: '已安装 headroom 插件',
|
|
229
298
|
headroomInstallRetry: '安装未生效,请重试或手动在插件市场安装',
|
|
299
|
+
pluginInstallCardTitle: '未安装 {name} 插件',
|
|
300
|
+
pluginInstallCardDesc: '从 zcode-plugins 市场安装后即可在此管理;建议安装后重启会话生效。',
|
|
301
|
+
pluginInstallDone: '已安装 {name} 插件',
|
|
302
|
+
pluginEnable: '启用',
|
|
303
|
+
hrStarting: '正在启动压缩代理…',
|
|
304
|
+
hrStarted: '压缩代理已启动',
|
|
305
|
+
hrStartTimeout: '代理启动超时(90 秒),请检查 ~/.zcode-headroom-proxy.log',
|
|
306
|
+
hrStopping: '正在停止压缩代理…',
|
|
307
|
+
hrRestarting: '正在重启压缩代理…',
|
|
308
|
+
hrStopped: '压缩代理已停止',
|
|
309
|
+
hrUsageNone: '当前没有走 headroom 的供应商,未切换',
|
|
230
310
|
headroomLoadFailed: '读取 Headroom 配置失败',
|
|
231
311
|
headroomStatusTitle: '运行状态',
|
|
232
312
|
headroomStatusLoadFailed: '读取运行状态失败',
|
|
@@ -252,7 +332,7 @@ const zh = {
|
|
|
252
332
|
headroomRestart: '重启',
|
|
253
333
|
headroomStopAction: '停止',
|
|
254
334
|
headroomStopTitle: '停止 Headroom 代理',
|
|
255
|
-
headroomStopDesc: '停止后,ZCode
|
|
335
|
+
headroomStopDesc: '停止后,ZCode 及其他程序中指向该代理(127.0.0.1)的请求都会失败,直至重新启动代理。headroom 是共享的,其他工具可能正在使用。确认停止?',
|
|
256
336
|
headroomStoppedWarn: '代理已停止;指向 127.0.0.1 的供应商将无法连接',
|
|
257
337
|
headroomBackend: '压缩设备',
|
|
258
338
|
headroomBackendAuto: '自动 · 显卡优先',
|
|
@@ -270,8 +350,6 @@ const zh = {
|
|
|
270
350
|
headroomInterval: '监视间隔',
|
|
271
351
|
headroomIntervalDesc: '省电监视器轮询电源状态的间隔(10–3600 秒,默认 60);保存后从下一轮巡检起生效,无需重启。',
|
|
272
352
|
headroomSecUnit: '秒',
|
|
273
|
-
headroomVersionPlugin: '插件',
|
|
274
|
-
headroomVersionBin: '程序',
|
|
275
353
|
upgradeCheck: '检查更新',
|
|
276
354
|
upgradeChecking: '检查中…',
|
|
277
355
|
upgradeUpToDate: '已是最新({v})',
|
|
@@ -283,7 +361,6 @@ const zh = {
|
|
|
283
361
|
upgradeDone: '已更新到 {v}',
|
|
284
362
|
upgradeFinished: '升级完成',
|
|
285
363
|
upgradeCheckFailed: '检查更新失败',
|
|
286
|
-
version: '版本',
|
|
287
364
|
close: '关闭',
|
|
288
365
|
cancel: '取消',
|
|
289
366
|
pathLabel: '路径',
|
|
@@ -328,22 +405,31 @@ const en = {
|
|
|
328
405
|
tabFeatures: 'Features',
|
|
329
406
|
tabStyles: 'Styles',
|
|
330
407
|
tabProxy: 'Proxy',
|
|
408
|
+
tabBehavior: 'Behavior',
|
|
331
409
|
tabAgents: 'Global Prompt',
|
|
332
410
|
agentsDesc: 'Written to ~/.zcode/AGENTS.md and used as default instructions in every session across all projects. Takes effect for new sessions; per-project AGENTS.md can extend or override it. Content over 100 KB is truncated by the app.',
|
|
333
411
|
agentsPlaceholder: 'Instructions followed by all projects by default; save empty to remove the global prompt',
|
|
334
412
|
agentsSave: 'Save',
|
|
335
413
|
agentsSaved: 'Global prompt saved.',
|
|
336
414
|
agentsLoadFailed: 'Failed to load the global prompt',
|
|
337
|
-
proxyDesc: 'Set an HTTP proxy for network access made by the local helper: plugin marketplace updates and installs,
|
|
415
|
+
proxyDesc: 'Set an HTTP proxy for network access made by the local helper: plugin marketplace updates and installs, plugin dependency installs and upgrades, and WebDAV sync can all use it — pick the scopes with the toggles below. Does not affect the ZCode app itself or model requests. Applies immediately.',
|
|
338
416
|
proxyPlaceholder: 'http://127.0.0.1:7890',
|
|
339
417
|
proxySave: 'Save',
|
|
340
418
|
proxySaved: 'Proxy settings saved.',
|
|
341
419
|
proxyHint: 'Save empty to clear the proxy.',
|
|
342
420
|
proxyTest: 'Test',
|
|
343
421
|
proxyTesting: 'Testing…',
|
|
422
|
+
proxyTestEmpty: 'Enter a proxy address first',
|
|
344
423
|
proxyTestFailed: 'Test failed',
|
|
424
|
+
proxyScopePlugins: 'Plugin marketplace',
|
|
425
|
+
proxyScopePluginsDesc: 'Marketplace checks, installs and uninstalls (zcode CLI git operations) go through the proxy.',
|
|
426
|
+
proxyScopeDeps: 'Plugin dependencies',
|
|
427
|
+
proxyScopeDepsDesc: 'headroom and rtk installs, update checks and upgrades (pip and GitHub downloads) go through the proxy.',
|
|
428
|
+
proxyScopeSync: 'Sync',
|
|
429
|
+
proxyScopeSyncDesc: 'WebDAV upload and download go through the proxy. Off by default: domestic WebDAV providers work fine direct, and a stopped proxy breaks sync.',
|
|
345
430
|
tabSync: 'Sync',
|
|
346
|
-
|
|
431
|
+
tabDebug: 'Debug',
|
|
432
|
+
syncDesc: 'Manually back up ZCode Pro settings and model settings to WebDAV, or restore them on a new computer. The two items are stored as separate files, so you can upload or overwrite either one alone; every upload also keeps a timestamped history version and download lets you pick any of them. Data moves only when you click, never automatically.',
|
|
347
433
|
syncUrlPlaceholder: 'https://dav.example.com/dav/',
|
|
348
434
|
syncDir: '上传目录',
|
|
349
435
|
syncDirPlaceholder: 'zcode-pro',
|
|
@@ -351,7 +437,7 @@ const en = {
|
|
|
351
437
|
syncPassword: 'Password',
|
|
352
438
|
syncSave: 'Save',
|
|
353
439
|
syncSaved: 'Sync settings saved.',
|
|
354
|
-
syncFormHint: 'Sync files go
|
|
440
|
+
syncFormHint: 'Sync files go inside the folder (default zcode-pro, multi-level allowed, created automatically): zcodepro.json and model.json are always the latest version; timestamped history versions are kept alongside.',
|
|
355
441
|
syncRecLink: 'Jianguoyun WebDAV',
|
|
356
442
|
syncRecSuffix: ' — use the app password, not your account password.',
|
|
357
443
|
syncIncludeZcp: 'ZCode Pro settings',
|
|
@@ -361,16 +447,44 @@ const en = {
|
|
|
361
447
|
syncUpload: 'Upload',
|
|
362
448
|
syncDownload: 'Download',
|
|
363
449
|
syncUploading: 'Uploading…',
|
|
364
|
-
syncDownloading: 'Downloading…',
|
|
365
450
|
syncUploaded: 'Uploaded to the server.',
|
|
366
451
|
syncDownloaded: 'Restored from the server.',
|
|
367
452
|
syncLastPush: 'Last upload',
|
|
368
453
|
syncLastPull: 'Last download',
|
|
369
454
|
syncNever: 'never',
|
|
370
455
|
syncConfirmTitle: 'Download from the server',
|
|
371
|
-
syncConfirmDesc: '
|
|
372
|
-
syncConfirmGo: '
|
|
456
|
+
syncConfirmDesc: 'The selected file will overwrite the matching local section (a .zcodepro-backup copy is kept next to each file); your local sync config and credentials stay untouched. Restarting ZCode is recommended after restoring model settings. Import now?',
|
|
457
|
+
syncConfirmGo: 'Import',
|
|
458
|
+
syncPickDesc: 'Fixed names are always the latest version; timestamped files are the history each upload leaves behind. Import overwrites the matching local section.',
|
|
459
|
+
syncVerDeleteLatestNote: 'Note: this is the "latest" file that older zcode-pro installs download from; deleting it breaks their download. Timestamped history versions are not affected.',
|
|
373
460
|
syncHint: 'Sync credentials are stored locally in zcodepro.json only; model settings contain the API keys of custom providers and go to your own WebDAV only.',
|
|
461
|
+
syncTest: 'Test',
|
|
462
|
+
syncTesting: 'Testing…',
|
|
463
|
+
syncTestEmpty: 'Enter the WebDAV URL first',
|
|
464
|
+
syncTestFailed: 'Test failed',
|
|
465
|
+
syncTestStepConnect: 'Connect',
|
|
466
|
+
syncTestStepDir: 'Folder',
|
|
467
|
+
syncTestStepList: 'Files',
|
|
468
|
+
syncTestFilesFound: '{n} backup file(s) on the server',
|
|
469
|
+
syncPickTitle: 'Select a file',
|
|
470
|
+
syncPickLoading: 'Loading files from the server…',
|
|
471
|
+
syncPickEmpty: 'No backups on the server yet. Upload from a computer first.',
|
|
472
|
+
syncVerImport: 'Import',
|
|
473
|
+
syncVerImporting: 'Importing…',
|
|
474
|
+
syncVerDelete: 'Delete',
|
|
475
|
+
syncVerSave: 'Save to disk',
|
|
476
|
+
syncVerSaving: 'Saving…',
|
|
477
|
+
syncVerLatest: 'Latest',
|
|
478
|
+
syncVerDeleteTitle: 'Delete server file',
|
|
479
|
+
syncVerDeleteDesc: 'This backup will be deleted from the server and cannot be recovered: ',
|
|
480
|
+
syncVerDeleted: 'Backup deleted from the server.',
|
|
481
|
+
syncVerSaved: 'Saved to',
|
|
482
|
+
devMode: 'Developer mode',
|
|
483
|
+
devModeRowDesc: 'Shows the "Reload ZCode Pro" button; can also be enabled with the ZCODEPRO_DEV=1 environment variable. This tab is visible only while developer mode is active.',
|
|
484
|
+
devModeDesc: 'For development: after changing zcode-pro code, click "Reload ZCode Pro" instead of restarting ZCode. It builds automatically (when running from the source repo), restarts the ZCode Pro launcher process (fresh helper and injected script), and every ZCode window refreshes and re-injects. The ZCode app itself and all sessions stay untouched.',
|
|
485
|
+
devReload: 'Reload ZCode Pro',
|
|
486
|
+
devReloading: 'Reloading…',
|
|
487
|
+
devReloadDesc: 'Every ZCode window refreshes automatically within 2-3 seconds to load the new script.',
|
|
374
488
|
rowGapName: 'Paragraph spacing',
|
|
375
489
|
rowGapDesc: 'Vertical spacing between text blocks (turns, paragraphs inside answers).',
|
|
376
490
|
listSpacingName: 'List spacing',
|
|
@@ -387,6 +501,8 @@ const en = {
|
|
|
387
501
|
tableCellPaddingDesc: 'Padding inside table cells; left input is vertical, right is horizontal.',
|
|
388
502
|
lineHeightName: 'Answer line height',
|
|
389
503
|
lineHeightDesc: 'Line height of answer text (multiplier).',
|
|
504
|
+
thinkingLineHeightName: 'Work-log line height',
|
|
505
|
+
thinkingLineHeightDesc: 'Line height of the “worked for…” history panel (tool entries and reasoning, multiplier).',
|
|
390
506
|
userLineHeightName: 'Question line height',
|
|
391
507
|
userLineHeightDesc: 'Line height of question text (multiplier).',
|
|
392
508
|
contentWidthName: 'Content width',
|
|
@@ -401,6 +517,30 @@ const en = {
|
|
|
401
517
|
featureSessionSwitchDesc: 'alt+z toggles between the current and the last session; hold alt and press x/c to move the highlight across recently used sessions, release alt to switch (like alt+tab).',
|
|
402
518
|
featureTitleLock: 'Keep renamed session titles',
|
|
403
519
|
featureTitleLockDesc: 'Sessions you renamed keep their custom title: after new replies the app may show an auto-generated title instead, which reverts after a restart; this keeps the renamed title in the sidebar.',
|
|
520
|
+
tabTitleGen: 'Task name prompt',
|
|
521
|
+
titleGenShortDesc: 'When a new session starts answering, generates the task name with the chosen provider and prompt and saves it as a manual rename the built-in title never overrides.',
|
|
522
|
+
titleGenEnabled: 'Enable',
|
|
523
|
+
titleGenProvider: 'Provider',
|
|
524
|
+
titleGenProviderNone: 'Custom endpoint (no provider)',
|
|
525
|
+
titleGenProviderSession: 'Follow current session',
|
|
526
|
+
titleGenProviderDeleted: ' (deleted)',
|
|
527
|
+
titleGenModel: 'Model',
|
|
528
|
+
titleGenModelHint: 'Pick from the list, or type a model id',
|
|
529
|
+
titleGenModelEmpty: '(not set)',
|
|
530
|
+
titleGenModelSessionHint: 'Pick from the list; the first entry follows the session model',
|
|
531
|
+
titleGenModelFollowSession: 'Follow the session model',
|
|
532
|
+
titleGenModelManualHint: 'Type a model id',
|
|
533
|
+
titleGenBaseUrl: 'Base URL',
|
|
534
|
+
titleGenApiKey: 'API Key',
|
|
535
|
+
titleGenFormat: 'Protocol',
|
|
536
|
+
titleGenPrompt: 'Prompt',
|
|
537
|
+
titleGenPromptPlaceholder: 'Leave empty for the built-in default prompt (button below shows it)',
|
|
538
|
+
titleGenPromptReset: 'Use default prompt',
|
|
539
|
+
titleGenSample: 'Test message',
|
|
540
|
+
titleGenTest: 'Test',
|
|
541
|
+
titleGenTesting: 'Generating…',
|
|
542
|
+
titleGenTestOk: 'Result: ',
|
|
543
|
+
titleGenTestFailed: 'Generation failed',
|
|
404
544
|
featureWsRunning: 'Running indicator on collapsed projects (experimental)',
|
|
405
545
|
featureWsRunningDesc: 'Spins a collapsed project\'s icon while any of its sessions is still running; stops when they all finish or the project is expanded. While spinning, the project is briefly re-expanded in the background every 60 s to re-check and then collapsed — rows stay hidden, with only a subtle quarter-second height ripple at the project.',
|
|
406
546
|
tabPerformance: 'Performance',
|
|
@@ -450,7 +590,7 @@ const en = {
|
|
|
450
590
|
featurePinnedExpand: 'Keep projects collapsed for pinned sessions (experimental)',
|
|
451
591
|
featurePinnedExpandDesc: 'Keeps the project collapsed after clicking a pinned session. Note: it briefly expands first, then collapses.',
|
|
452
592
|
featureAutoUpdatePlugins: 'Auto-update zcode-plugins plugins',
|
|
453
|
-
featureAutoUpdatePluginsDesc: 'Checks and updates installed zcode-plugins plugins on startup (
|
|
593
|
+
featureAutoUpdatePluginsDesc: 'Checks and updates installed zcode-plugins plugins on startup (updates only — never installs; uninstalled plugins stay uninstalled). Updates apply to new sessions.',
|
|
454
594
|
pluginsUpdateTitle: 'Plugin updates · zcode-plugins',
|
|
455
595
|
pluginsCheckNow: 'Check & update',
|
|
456
596
|
pluginsUpdating: 'Updating plugins…',
|
|
@@ -462,7 +602,7 @@ const en = {
|
|
|
462
602
|
pluginsAutoUpdated: 'zcode-plugins updated',
|
|
463
603
|
tabVision: 'Vision',
|
|
464
604
|
visionDesc: 'When the main model cannot see images (e.g. glm-5.3), images in messages are recognized by the vision models below and passed to the main model as text (zcode-vision plugin; /vision-* commands edit the same file). Changes save automatically.',
|
|
465
|
-
visionEnabled: 'Enable
|
|
605
|
+
visionEnabled: 'Enable',
|
|
466
606
|
visionForceIntercept: 'Intercept even if the main model sees images',
|
|
467
607
|
visionMode: 'Chain mode',
|
|
468
608
|
visionModeFallback: 'Fallback',
|
|
@@ -475,10 +615,6 @@ const en = {
|
|
|
475
615
|
visionMoveDown: 'Down',
|
|
476
616
|
visionName: 'Name',
|
|
477
617
|
visionUseProvider: 'Provider',
|
|
478
|
-
visionUseProviderHint: 'Pick from the list or type: session = follow the current session provider, or a provider name/ID; baseUrl/API key/format follow that provider',
|
|
479
|
-
visionUseProviderNone: 'Not following (enter base URL)',
|
|
480
|
-
visionUseProviderSession: 'Follow session provider (session)',
|
|
481
|
-
visionUseProviderUnknown: 'Unknown provider; recognition will fail. Pick from the list or type session',
|
|
482
618
|
visionSkipAfterFailures: 'Skip after failures',
|
|
483
619
|
visionSkipAfterFailuresHint: 'After this many consecutive failures a proxy is skipped and the next one is tried; 0 = never skip',
|
|
484
620
|
visionSkipMinutes: 'Skip minutes',
|
|
@@ -487,21 +623,21 @@ const en = {
|
|
|
487
623
|
visionResetChainConfirm: 'Reset chain mode and the proxy list to the default 2-level chain (glm-session → glm-flash)? Current proxy edits will be lost. Continue?',
|
|
488
624
|
visionBaseUrl: 'Base URL',
|
|
489
625
|
visionModel: 'Model',
|
|
490
|
-
visionFormat: 'Format',
|
|
491
626
|
visionApiKey: 'API Key',
|
|
492
627
|
visionApiKeyHint: 'Leave empty to reuse the GLM subscription key',
|
|
493
628
|
visionPrompt: 'Recognition prompt ({prev} = previous step in pipeline)',
|
|
494
629
|
visionTest: 'Test',
|
|
495
630
|
visionTesting: 'Testing…',
|
|
496
631
|
visionTestFailed: 'Vision proxy test failed',
|
|
497
|
-
visionTestOutput: 'Test output',
|
|
498
632
|
visionCompressKB: 'Compress threshold KB',
|
|
499
633
|
visionCompressKBHint: 'Images above this size are compressed first (max edge 2000, JPEG); 0 = never',
|
|
500
634
|
visionLoadFailed: 'Failed to load the vision config',
|
|
501
635
|
tabRtk: 'rtk',
|
|
502
636
|
rtkDesc: 'rtk plugin: compresses common dev command output by 60-90% before it reaches the context (/rtk-* commands edit the same files). Changes save automatically and apply immediately.',
|
|
503
|
-
rtkEnabled: 'Enable
|
|
637
|
+
rtkEnabled: 'Enable',
|
|
504
638
|
rtkNotInstalled: 'rtk is not installed. The settings below take effect after installation (/rtk-setup in a session).',
|
|
639
|
+
rtkInstallBin: 'Install rtk binary',
|
|
640
|
+
rtkBinInstalled: 'rtk installed; PATH takes effect for sessions after signing out and back in — the plugin hook works immediately',
|
|
505
641
|
rtkWhitelistTitle: 'Whitelist (pass through without compression)',
|
|
506
642
|
rtkBuiltinLabel: 'Built-in exemptions (read-only, follow plugin updates):',
|
|
507
643
|
rtkWhitelistDesc: 'Each entry is name (e.g. docker) or git:name (e.g. git:clone). Commands with tiny or no output need no compression; adding them skips compression. Built-ins (git add/commit/push, mkdir/cp, …) cannot be removed. Delete the file to reset.',
|
|
@@ -514,12 +650,25 @@ const en = {
|
|
|
514
650
|
tabHeadroom: 'Headroom',
|
|
515
651
|
headroomDesc: 'Headroom plugin: a local compression proxy on 127.0.0.1 (port 8787 by default) that compresses context sent to the model to save tokens (/hr-* commands edit the same file). Device and power-save changes apply immediately; switching the device restarts the proxy, so requests in progress may be interrupted once.',
|
|
516
652
|
headroomBinMissing: 'The headroom program was not found, so the proxy cannot start. The settings below take effect once it is installed.',
|
|
653
|
+
hrInstallBin: 'Install headroom binary',
|
|
654
|
+
hrBinInstalled: 'headroom installed; reopen a session and the hook will start the proxy automatically',
|
|
517
655
|
headroomInstallTitle: 'headroom plugin not installed',
|
|
518
656
|
headroomInstallDesc: 'Install it from the zcode-plugins marketplace to manage the compression proxy here; restarting the session afterwards is recommended.',
|
|
519
657
|
headroomInstall: 'Install',
|
|
520
658
|
headroomInstalling: 'Installing…',
|
|
521
659
|
headroomInstalled: 'headroom plugin installed',
|
|
522
660
|
headroomInstallRetry: 'Installation did not take effect; retry or install it from the marketplace manually',
|
|
661
|
+
pluginInstallCardTitle: '{name} plugin not installed',
|
|
662
|
+
pluginInstallCardDesc: 'Install it from the zcode-plugins marketplace to manage it here; restarting the session afterwards is recommended.',
|
|
663
|
+
pluginInstallDone: '{name} plugin installed',
|
|
664
|
+
pluginEnable: 'Enable',
|
|
665
|
+
hrStarting: 'Starting compression proxy…',
|
|
666
|
+
hrStarted: 'Compression proxy started',
|
|
667
|
+
hrStartTimeout: 'Proxy start timed out (90s); check ~/.zcode-headroom-proxy.log',
|
|
668
|
+
hrStopping: 'Stopping compression proxy…',
|
|
669
|
+
hrRestarting: 'Restarting compression proxy…',
|
|
670
|
+
hrStopped: 'Compression proxy stopped',
|
|
671
|
+
hrUsageNone: 'No providers route through headroom; nothing changed',
|
|
523
672
|
headroomLoadFailed: 'Failed to load the Headroom config',
|
|
524
673
|
headroomStatusTitle: 'Status',
|
|
525
674
|
headroomStatusLoadFailed: 'Failed to load the status',
|
|
@@ -545,7 +694,7 @@ const en = {
|
|
|
545
694
|
headroomRestart: 'Restart',
|
|
546
695
|
headroomStopAction: 'Stop',
|
|
547
696
|
headroomStopTitle: 'Stop the Headroom proxy',
|
|
548
|
-
headroomStopDesc: '
|
|
697
|
+
headroomStopDesc: 'Requests from ZCode and other apps pointing at this proxy (127.0.0.1) will fail until it is started again. headroom is shared — other tools may be using it. Stop it?',
|
|
549
698
|
headroomStoppedWarn: 'Proxy stopped; providers pointing at 127.0.0.1 cannot connect.',
|
|
550
699
|
headroomBackend: 'Compression device',
|
|
551
700
|
headroomBackendAuto: 'Auto · GPU first',
|
|
@@ -563,8 +712,6 @@ const en = {
|
|
|
563
712
|
headroomInterval: 'Watch interval',
|
|
564
713
|
headroomIntervalDesc: 'Seconds between power checks (10–3600, default 60); applies from the next round, no restart needed.',
|
|
565
714
|
headroomSecUnit: 's',
|
|
566
|
-
headroomVersionPlugin: 'plugin',
|
|
567
|
-
headroomVersionBin: 'program',
|
|
568
715
|
upgradeCheck: 'Check update',
|
|
569
716
|
upgradeChecking: 'Checking…',
|
|
570
717
|
upgradeUpToDate: 'Up to date ({v})',
|
|
@@ -576,7 +723,6 @@ const en = {
|
|
|
576
723
|
upgradeDone: 'Updated to {v}',
|
|
577
724
|
upgradeFinished: 'Update finished',
|
|
578
725
|
upgradeCheckFailed: 'Update check failed',
|
|
579
|
-
version: 'Version',
|
|
580
726
|
close: 'Close',
|
|
581
727
|
cancel: 'Cancel',
|
|
582
728
|
pathLabel: 'Path',
|
|
@@ -695,6 +841,8 @@ const errEn = {
|
|
|
695
841
|
'sync-remote-invalid': 'The URL already holds a different file; use another file path',
|
|
696
842
|
'sync-request': 'The WebDAV request failed',
|
|
697
843
|
'sync-run': 'Sync failed',
|
|
844
|
+
'sync-file-invalid': 'Invalid backup file name',
|
|
845
|
+
'sync-export': 'Failed to save the backup to disk',
|
|
698
846
|
};
|
|
699
847
|
|
|
700
848
|
export function errText(res) {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// 触发器/菜单的 React fiber props(OpenSplitButton 场景 target/fileLink/row.path)。
|
|
9
9
|
import { t, closeRadixMenu, itemsOf, itemText, rpc } from '../core.js';
|
|
10
10
|
import { getConfig } from '../core.js';
|
|
11
|
+
import { showToast } from '../ui.js';
|
|
11
12
|
|
|
12
13
|
const COPY_ABS_TEXTS = ['复制绝对路径', 'Copy absolute path'];
|
|
13
14
|
const NO_APPS_TEXTS = ['未找到可用 App', 'No apps found'];
|
|
@@ -126,6 +127,12 @@ export function handleFileMenu(content) {
|
|
|
126
127
|
const path = resolveTargetPath(content);
|
|
127
128
|
if (!path) return;
|
|
128
129
|
const L = t();
|
|
130
|
+
// 打开失败(路径不存在等)弹 toast 报错,不再无声无息
|
|
131
|
+
const fire = (endpoint, label) => {
|
|
132
|
+
rpc(endpoint, { method: 'POST', body: { path } })
|
|
133
|
+
.then((r) => { if (r && r.ok === false) showToast(`${label}失败:${r.error || '未知错误'}`, 'error'); })
|
|
134
|
+
.catch(() => { /* 网络异常时 helper 多半未运行,此时菜单不会注入,静默 */ });
|
|
135
|
+
};
|
|
129
136
|
// “默认应用打开”锚在编辑器列表末尾(无可用编辑器时即“未找到可用 App”禁用项)之上;
|
|
130
137
|
// 找不到该组时退而锚在“复制绝对路径”上,保持“打开方式在前、路径操作在后”的次序
|
|
131
138
|
const noApps = items.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x))
|
|
@@ -135,13 +142,13 @@ export function handleFileMenu(content) {
|
|
|
135
142
|
marker: 'file-open-default',
|
|
136
143
|
label: L.fileOpenDefault,
|
|
137
144
|
icon: ICON_OPEN_DEFAULT,
|
|
138
|
-
onPick: () => {
|
|
145
|
+
onPick: () => { void fire('/open-path', L.fileOpenDefault); },
|
|
139
146
|
});
|
|
140
147
|
appendMenuItem(content, copyAbs, copyAbs, {
|
|
141
148
|
marker: 'file-reveal',
|
|
142
149
|
label: L.fileReveal,
|
|
143
150
|
icon: ICON_REVEAL,
|
|
144
|
-
onPick: () => {
|
|
151
|
+
onPick: () => { void fire('/reveal-path', L.fileReveal); },
|
|
145
152
|
});
|
|
146
153
|
})();
|
|
147
154
|
}
|
|
@@ -14,7 +14,7 @@ export async function startPluginUpdateCheck() {
|
|
|
14
14
|
|
|
15
15
|
const config = await getConfig();
|
|
16
16
|
if ((config.features || {}).autoUpdatePlugins === true) {
|
|
17
|
-
const res = await rpc('/plugins/update', { method: 'POST', body: {
|
|
17
|
+
const res = await rpc('/plugins/update', { method: 'POST', body: {} });
|
|
18
18
|
if (res.ok) showToast(L.pluginsAutoUpdated, 'success');
|
|
19
19
|
else showToast(L.pluginsUpdateFailed + ': ' + (res.error || ''), 'error');
|
|
20
20
|
return;
|