dsh-recall-plugin 2.4.6 → 2.4.7
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/CHANGELOG.md +28 -0
- package/README.en.md +12 -2
- package/README.md +12 -2
- package/lib/client.js +770 -199
- package/lib/config.js +11 -4
- package/lib/errors.js +3 -0
- package/lib/exclude-patterns.js +2 -1
- package/lib/index.js +31 -4
- package/lib/intent-journal.js +108 -0
- package/lib/maintenance.js +9 -4
- package/lib/routes-core.js +23 -6
- package/lib/routes-manage.js +24 -6
- package/lib/scripts.posix.js +4 -0
- package/lib/scripts.pwsh.js +4 -0
- package/lib/snapshots.js +74 -4
- package/lib/store.js +7 -5
- package/package.json +8 -2
package/lib/client.js
CHANGED
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
// 变色、不套 ring。替换 V2-5 的 border-l3 环方案(当时核验的是旧版写法)。
|
|
78
78
|
".dsh-recall-btn:focus-visible,.dsh-recall-ex-chip:focus-visible,.dsh-recall-tree-toggle:focus-visible,.dsh-recall-cfg-switch:focus-visible,.dsh-recall-icon-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}",
|
|
79
79
|
".dsh-recall-cardbtn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}",
|
|
80
|
-
".dsh-recall-ex-input:focus-visible,.dsh-recall-cfg-input:focus-visible,.dsh-recall-ex-area:focus-visible,.dsh-recall-cfg-area:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}",
|
|
80
|
+
".dsh-recall-ex-input:focus-visible,.dsh-recall-cfg-input:focus-visible,.dsh-recall-cfg-select:focus-visible,.dsh-recall-ex-area:focus-visible,.dsh-recall-cfg-area:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}",
|
|
81
81
|
// 输入类 hover 中间态:rest l4 → hover l3 → focus brand 三级递进,与按钮的
|
|
82
82
|
// 描边 hover 逻辑同语言;:not(:focus-visible) 收口是防 hover 伪类(特异性更
|
|
83
83
|
// 高)把 focus 的品牌色描边刷回 l3
|
|
84
|
-
".dsh-recall-ex-input:hover:not(:disabled):not(:focus-visible),.dsh-recall-cfg-input:hover:not(:disabled):not(:focus-visible),.dsh-recall-ex-area:hover:not(:disabled):not(:focus-visible),.dsh-recall-cfg-area:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-border-l3)}",
|
|
84
|
+
".dsh-recall-ex-input:hover:not(:disabled):not(:focus-visible),.dsh-recall-cfg-input:hover:not(:disabled):not(:focus-visible),.dsh-recall-cfg-select:hover:not(:disabled):not(:focus-visible),.dsh-recall-ex-area:hover:not(:disabled):not(:focus-visible),.dsh-recall-cfg-area:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-border-l3)}",
|
|
85
85
|
".dsh-recall-toast{position:fixed;top:18px;left:50%;transform:translateX(-50%);z-index:10000;max-width:min(560px,86vw);box-sizing:border-box;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:10px 16px;font-size:13px;line-height:1.5;box-shadow:0 8px 28px rgba(0,0,0,.22);display:flex;align-items:baseline;gap:8px;opacity:0;transition:opacity .25s ease;pointer-events:auto}",
|
|
86
86
|
".dsh-recall-toast.dsh-recall-toast-in{opacity:1}",
|
|
87
87
|
".dsh-recall-toast-tag{flex:none;font-weight:600;color:var(--dsw-alias-state-error-primary)}",
|
|
@@ -244,13 +244,17 @@
|
|
|
244
244
|
".dsh-recall-cfg-input{flex:none;width:var(--dsh-recall-input-w);box-sizing:border-box;height:34px;font:inherit;font-size:13px;text-align:right;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;padding:0 12px}",
|
|
245
245
|
// disabled 随官方 input:文字降 tertiary,不动透明度(整框变淡会让「框还在
|
|
246
246
|
// 只是不可写」的语义变含糊)
|
|
247
|
-
".dsh-recall-cfg-input:disabled,.dsh-recall-cfg-area:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}",
|
|
247
|
+
".dsh-recall-cfg-input:disabled,.dsh-recall-cfg-select:disabled,.dsh-recall-cfg-area:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}",
|
|
248
248
|
// 数字输入框隐藏原生加减微调按钮(spinner):34px 高的定宽框里 spinner 挤占
|
|
249
249
|
// 右侧数字区、跨引擎渲染不一(Chromium 上下箭头 / Firefox 无),与「右对齐
|
|
250
250
|
// 数字」的纵向扫描相冲;隐藏后键盘 ↑↓ 与直接输入仍可用,步进语义不丢。
|
|
251
251
|
// scope 到 cfg-input,不外溢影响宿主或其他插件的 number 输入。
|
|
252
252
|
".dsh-recall-cfg-input::-webkit-inner-spin-button,.dsh-recall-cfg-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}",
|
|
253
253
|
".dsh-recall-cfg-input{-moz-appearance:textfield;appearance:textfield}",
|
|
254
|
+
// 语言下拉(A4):与数字输入框同一套描边/底色/高度配方,差异只在文本左对齐
|
|
255
|
+
// 与宽度自适应——select 的选项文本长度不可控(Follow system (auto) 最长),
|
|
256
|
+
// 定宽会截断;min-width 与输入框同宽保住「控件列」的视觉对齐基线
|
|
257
|
+
".dsh-recall-cfg-select{flex:none;box-sizing:border-box;height:34px;min-width:var(--dsh-recall-input-w);font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;padding:0 8px 0 10px}",
|
|
254
258
|
".dsh-recall-cfg-area{font-family:inherit;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;padding:6px 8px;min-height:64px;box-sizing:border-box;width:100%;grid-column:2}",
|
|
255
259
|
// 布尔开关:官方设置表单的布尔字段用 role=switch 滑钮而非原生 checkbox
|
|
256
260
|
//(vCGm7G_switch 逐字配方:36×20 轨道 border-l3 底、开=brand-primary、
|
|
@@ -303,6 +307,460 @@
|
|
|
303
307
|
"@keyframes dsh-recall-pulse{0%,100%{opacity:1}50%{opacity:.45}}"
|
|
304
308
|
].join("");
|
|
305
309
|
|
|
310
|
+
// src/client/locales/zh.ts
|
|
311
|
+
var zh = {
|
|
312
|
+
// ---- 通用(跨模块复用的动作与连接词)----
|
|
313
|
+
"common.close": "\u5173\u95ED",
|
|
314
|
+
"common.cancel": "\u53D6\u6D88",
|
|
315
|
+
"common.confirm": "\u786E\u8BA4",
|
|
316
|
+
"common.save": "\u4FDD\u5B58",
|
|
317
|
+
"common.discard": "\u653E\u5F03\u4FEE\u6539",
|
|
318
|
+
"common.retry": "\u91CD\u8BD5",
|
|
319
|
+
"common.errorPrefix": "\u9519\u8BEF\uFF1A",
|
|
320
|
+
"common.unknownReason": "\u672A\u77E5\u539F\u56E0",
|
|
321
|
+
"common.opFailed": "\u64CD\u4F5C\u5931\u8D25",
|
|
322
|
+
// 列表连接符:中文顿号 / 英文逗号+空格(分隔符本身也是语言事实)
|
|
323
|
+
"common.listSep": "\u3001",
|
|
324
|
+
// 括号包裹模板:中英标点族不同((x) / (x),英文含前导空格),不写死在调用点
|
|
325
|
+
"common.paren": "\uFF08{text}\uFF09",
|
|
326
|
+
// ---- 变更类型(KIND_INFO 单表引用)----
|
|
327
|
+
"kind.modified": "\u4FEE\u6539",
|
|
328
|
+
"kind.restored": "\u6062\u590D",
|
|
329
|
+
"kind.added": "\u5220\u9664",
|
|
330
|
+
// ---- 用户消息内的文件卡片 ----
|
|
331
|
+
"file.unnamed": "\u672A\u547D\u540D\u6587\u4EF6",
|
|
332
|
+
// ---- 快照管理树的节点文案 ----
|
|
333
|
+
"tree.unknownWorkspace": "\u672A\u77E5\u5DE5\u4F5C\u533A",
|
|
334
|
+
"tree.deletedSession": "\uFF08\u5DF2\u5220\u9664\u4F1A\u8BDD\uFF09",
|
|
335
|
+
"tree.expand": "\u5C55\u5F00\uFF1A{label}",
|
|
336
|
+
"tree.collapse": "\u6536\u8D77\uFF1A{label}",
|
|
337
|
+
"tree.snapCount": "{n} \u6761",
|
|
338
|
+
"tree.wsCount": "{n} \u4F1A\u8BDD / {m} \u5FEB\u7167",
|
|
339
|
+
"tree.family.title": "\u7248\u672C\u5BB6\u65CF\uFF1A{chain}",
|
|
340
|
+
"tree.switch": "\u5207\u6362",
|
|
341
|
+
"tree.switch.title": "\u5207\u6362\u5230\u8BE5\u7248\u672C\u4F1A\u8BDD",
|
|
342
|
+
"tree.delete.snapshot.title": "\u5220\u9664\u8BE5\u5FEB\u7167\uFF08tag \u4E0E\u7D22\u5F15\u6761\u76EE\uFF09",
|
|
343
|
+
"tree.delete.snapshot.confirm": "\u786E\u8BA4\u5220\u9664\u8BE5\u5FEB\u7167\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
|
|
344
|
+
"tree.delete.session.title": "\u5220\u9664\u8BE5\u4F1A\u8BDD\u5168\u90E8\u5FEB\u7167",
|
|
345
|
+
"tree.delete.session.confirm": "\u786E\u8BA4\u5220\u9664\u8BE5\u4F1A\u8BDD\u5168\u90E8\u5FEB\u7167\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
|
|
346
|
+
"tree.delete.workspace.title": "\u5220\u9664\u8BE5\u5DE5\u4F5C\u533A\u5168\u90E8\u5FEB\u7167",
|
|
347
|
+
"tree.delete.workspace.confirm": "\u786E\u8BA4\u5220\u9664\u8BE5\u5DE5\u4F5C\u533A\u5168\u90E8\u5FEB\u7167\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
|
|
348
|
+
// ---- 撤回确认面板 ----
|
|
349
|
+
"recall.loading": "\u6B63\u5728\u8BA1\u7B97\u53D8\u66F4\u2026",
|
|
350
|
+
"recall.error.title": "\u65E0\u6CD5\u56DE\u9000",
|
|
351
|
+
"recall.confirm.title": "\u6574\u6BB5\u56DE\u9000",
|
|
352
|
+
"recall.truncated.files": "\u2026\u4EC5\u663E\u793A\u524D {shown} \u6761\uFF0C\u5171 {total} \u4E2A\u6587\u4EF6\u5C06\u53D8\u66F4",
|
|
353
|
+
"recall.truncated.chat": "\u2026\u4EC5\u663E\u793A\u524D {shown} \u6761\uFF0C\u5171 {total} \u5904\u5DEE\u5F02",
|
|
354
|
+
"recall.confirm.sessionOnly": "\u9879\u76EE\u6587\u4EF6\u4FDD\u6301\u5F53\u524D\u72B6\u6001\uFF0C\u4E0D\u4F1A\u88AB\u56DE\u9000\u6216\u5220\u9664\uFF1B\u5BF9\u8BDD\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\u3002",
|
|
355
|
+
"recall.confirm.rollbackAt": "\u5C06\u9879\u76EE\u6062\u590D\u5230 {time} \u53D1\u9001\u8BE5\u6D88\u606F\u65F6\u7684\u72B6\u6001\u3002",
|
|
356
|
+
"recall.confirm.rollbackNoTime": "\u5C06\u9879\u76EE\u6062\u590D\u5230\u53D1\u9001\u8BE5\u6D88\u606F\u65F6\u7684\u72B6\u6001\u3002",
|
|
357
|
+
"recall.confirm.files": "\u5171 {total} \u4E2A\u6587\u4EF6\u5C06\u53D8\u66F4{summary}\u3002\u6B64\u64CD\u4F5C\u4F1A\u8986\u76D6\u5F53\u524D\u6587\u4EF6\u5185\u5BB9\uFF1B\u56DE\u9000\u524D\u4F1A\u81EA\u52A8\u4FDD\u5B58\u4E00\u4EFD\u5F53\u524D\u72B6\u6001\u7684\u5B89\u5168\u5FEB\u7167\uFF08\u4E0D\u542B\u5728\u4E0B\u65B9\u6E05\u5355\u5185\uFF09\u3002",
|
|
358
|
+
"recall.confirm.chatBoth": "\u5BF9\u8BDD\u5C06\u4E00\u5E76\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\uFF1A\u8BE5\u6D88\u606F\u53CA\u4E4B\u540E\u7684\u5168\u90E8\u5BF9\u8BDD\u4F1A\u4ECE\u5F53\u524D\u89C6\u56FE\u79FB\u9664\uFF0C\u539F\u4F1A\u8BDD\u5F52\u6863\u4FDD\u5B58\uFF08\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF09\u3002",
|
|
359
|
+
"recall.confirm.chatFirst": "\u8BE5\u6D88\u606F\u662F\u672C\u4F1A\u8BDD\u4E2D\u7B2C\u4E00\u6761\u7528\u6237\u6D88\u606F\uFF0C\u65E0\u6CD5\u56DE\u9000\u5BF9\u8BDD\uFF1B\u786E\u8BA4\u540E\u4EC5\u56DE\u9000\u9879\u76EE\u6587\u4EF6\u3002",
|
|
360
|
+
"recall.confirm.refNote": "\u4EE5\u4E0B\u5DEE\u5F02\u4EC5\u4F5C\u53C2\u8003\uFF0C\u6240\u9009\u6A21\u5F0F\u4E0D\u4F1A\u6539\u52A8\u6587\u4EF6\u3002",
|
|
361
|
+
"recall.scope.aria": "\u64A4\u56DE\u8303\u56F4",
|
|
362
|
+
"recall.scope.both": "\u56DE\u9000\u6587\u4EF6\u4E0E\u5BF9\u8BDD",
|
|
363
|
+
"recall.scope.sessionOnly": "\u4EC5\u64A4\u56DE\u5BF9\u8BDD",
|
|
364
|
+
"recall.confirm.submit.both": "\u786E\u8BA4\u56DE\u9000",
|
|
365
|
+
"recall.confirm.submit.sessionOnly": "\u786E\u8BA4\u64A4\u56DE\u5BF9\u8BDD",
|
|
366
|
+
"recall.executing.both": "\u6B63\u5728\u56DE\u9000\u2026",
|
|
367
|
+
"recall.executing.sessionOnly": "\u6B63\u5728\u64A4\u56DE\u5BF9\u8BDD\u2026",
|
|
368
|
+
"recall.done.title": "\u56DE\u9000\u5B8C\u6210",
|
|
369
|
+
"recall.done.both.ok": "\u9879\u76EE\u6587\u4EF6\u4E0E\u5BF9\u8BDD\u5DF2\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\u3002\u65B0\u4F1A\u8BDD\u5DF2\u6253\u5F00\uFF0C\u539F\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF08\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF09\u3002",
|
|
370
|
+
"recall.done.both.partial": "\u9879\u76EE\u5DF2\u6062\u590D\u5230\u53D1\u9001\u8BE5\u6D88\u606F\u65F6\u7684\u72B6\u6001\u3002",
|
|
371
|
+
"recall.done.both.failChat": " \u5BF9\u8BDD\u56DE\u9000\u5931\u8D25\uFF1A{error}",
|
|
372
|
+
"recall.done.sessionOnly.ok": "\u5BF9\u8BDD\u5DF2\u56DE\u9000\u5230\u8BE5\u6D88\u606F\u4E4B\u524D\uFF0C\u9879\u76EE\u6587\u4EF6\u4FDD\u6301\u5F53\u524D\u72B6\u6001\u3002\u65B0\u4F1A\u8BDD\u5DF2\u6253\u5F00\uFF0C\u539F\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF08\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF09\u3002",
|
|
373
|
+
"recall.done.sessionOnly.fail": "\u5BF9\u8BDD\u56DE\u9000\u5931\u8D25\uFF1A{error}\u3002\u9879\u76EE\u6587\u4EF6\u672A\u505A\u4EFB\u4F55\u6539\u52A8\u3002",
|
|
374
|
+
"recall.chat.noChild": "\u672A\u8FD4\u56DE\u65B0\u4F1A\u8BDD",
|
|
375
|
+
// ---- 用户消息气泡的动作按钮 ----
|
|
376
|
+
"action.copy": "\u590D\u5236",
|
|
377
|
+
"action.copied": "\u5DF2\u590D\u5236",
|
|
378
|
+
"action.recall": "\u64A4\u56DE",
|
|
379
|
+
"action.recall.title": "\u6574\u6BB5\u56DE\u9000\uFF1A\u6587\u4EF6\u4E0E\u5BF9\u8BDD\u4E00\u5E76\u56DE\u5230\u8BE5\u6D88\u606F\u4E4B\u524D",
|
|
380
|
+
// ---- toast(降级提示 / 快照反馈)----
|
|
381
|
+
"toast.tag": "\u64A4\u56DE\u63D2\u4EF6",
|
|
382
|
+
"toast.skipped": "\u5FEB\u7167\u5DF2\u8DF3\u8FC7\u672A\u7EB3\u5165\u7684\u8DEF\u5F84\uFF1A{names}\uFF08\u64A4\u56DE\u4E0D\u4F1A\u6062\u590D\u6216\u5220\u9664\u8FD9\u4E9B\u8DEF\u5F84\uFF09",
|
|
383
|
+
"toast.skippedMore": " \u7B49 {n} \u9879",
|
|
384
|
+
"toast.snapshotFailed": "\u5FEB\u7167\u5931\u8D25\uFF1A{error}",
|
|
385
|
+
"toast.staleQueue": "\u64A4\u56DE\u524D\u7684\u4E00\u6761\u6392\u961F\u6D88\u606F\u672A\u88AB\u81EA\u52A8\u6E05\u7406\uFF0C\u53EF\u70B9\u51FB\u8BE5\u5361\u7247\u53F3\u4E0A\u89D2\u7684\u5220\u9664\u6309\u94AE\u624B\u52A8\u79FB\u9664",
|
|
386
|
+
"toast.fileAttachRefill": "\u88AB\u64A4\u56DE\u6D88\u606F\u91CC\u7684\u6587\u4EF6\u9644\u4EF6\u65E0\u6CD5\u81EA\u52A8\u56DE\u586B\uFF08\u5B98\u65B9\u63A5\u53E3\u53EA\u652F\u6301\u56FE\u7247\u56DE\u8BFB\uFF09\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u6587\u4EF6",
|
|
387
|
+
// ---- init / snapshot-info 下发的一次性说明 ----
|
|
388
|
+
"notice.unsupported": "\u64A4\u56DE\u63D2\u4EF6\u4EC5\u652F\u6301 Windows / Linux / macOS\uFF0C\u5F53\u524D\u5E73\u53F0\u7684\u5FEB\u7167\u4E0D\u53EF\u7528\u3002",
|
|
389
|
+
"notice.gitMissing": "\u672A\u68C0\u6D4B\u5230 git CLI\uFF0C\u64A4\u56DE\u529F\u80FD\u4E0D\u53EF\u7528\uFF08\u5FEB\u7167\u5F15\u64CE\u4F9D\u8D56 git\uFF09\u3002\u5B89\u88C5 git \u5E76\u91CD\u542F DSH \u540E\u5373\u53EF\u4F7F\u7528\u3002",
|
|
390
|
+
"notice.homeFallback": "home \u76EE\u5F55\u4E0D\u53EF\u5199\uFF0C\u5FEB\u7167\u5DF2\u964D\u7EA7\u5B58\u50A8\u5230\u9879\u76EE\u5185 .dsh-recall-snapshots \u76EE\u5F55\u3002",
|
|
391
|
+
"notice.buildRoot": "\u5F53\u524D\u5DE5\u4F5C\u533A\u4F4D\u4E8E\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF08\u8DEF\u5F84\u6BB5 {seg}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7\u9879\u76EE\u5FEB\u7167\uFF1B\u5982\u9700\u5728\u6B64\u76EE\u5F55\u4F7F\u7528\u64A4\u56DE\uFF0C\u8BF7\u5728\u63D2\u4EF6\u8BBE\u7F6E\u91CC\u4ECE\u300C\u57FA\u7840\u6392\u9664\u8868\u300D\u79FB\u9664\u8BE5\u9879\u3002",
|
|
392
|
+
// ---- 错误码文案(host 静态 message 的本地化副本;动态细节码不在表内)----
|
|
393
|
+
"err.STALE": "\u9884\u89C8\u540E\u9879\u76EE\u6587\u4EF6\u53D1\u751F\u4E86\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u9884\u89C8\u786E\u8BA4",
|
|
394
|
+
"err.AGENT_BUSY": "Agent \u6B63\u5728\u8FD0\u884C\u4E2D\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u64A4\u56DE",
|
|
395
|
+
"err.NO_SNAPSHOT": "\u8BE5\u6D88\u606F\u6CA1\u6709\u53EF\u7528\u7684\u9879\u76EE\u5FEB\u7167",
|
|
396
|
+
"err.NO_STORE": "\u5FEB\u7167\u5B58\u50A8\u4E0D\u53EF\u7528",
|
|
397
|
+
"err.FORMAT_BLOCKED": "\u78C1\u76D8\u683C\u5F0F\u4E0D\u53D7\u652F\u6301\uFF0C\u5199\u5165\u5DF2\u6682\u505C\uFF08\u8BE6\u89C1\u6700\u8FD1\u9519\u8BEF\uFF09",
|
|
398
|
+
"err.UNKNOWN_PATH": "\u672A\u77E5\u7684\u6392\u9664\u6587\u4EF6\u8DEF\u5F84",
|
|
399
|
+
"err.EMPTY_PATCH": "\u6CA1\u6709\u53EF\u5199\u5165\u7684\u914D\u7F6E\u5B57\u6BB5",
|
|
400
|
+
"err.SETTINGS_UNAVAILABLE": "\u8BBE\u7F6E\u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u8BF7\u5728 profile \u7684 cordis.patch.yml \u6309 id: recall \u8986\u76D6\u914D\u7F6E",
|
|
401
|
+
"err.BODY_TOO_LARGE": "\u8BF7\u6C42\u4F53\u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650",
|
|
402
|
+
"err.NO_ROOT": "\u65E0\u6CD5\u89E3\u6790\u5DE5\u4F5C\u533A",
|
|
403
|
+
"err.NO_SESSION": "\u7F3A\u5C11\u4F1A\u8BDD ID",
|
|
404
|
+
"err.UNKNOWN_OP": "\u672A\u77E5\u7684\u7BA1\u7406\u64CD\u4F5C",
|
|
405
|
+
"err.UNKNOWN_ENDPOINT": "\u672A\u77E5\u7684 API \u7AEF\u70B9",
|
|
406
|
+
"err.INDEX_CORRUPT": "\u5FEB\u7167\u7D22\u5F15\u635F\u574F",
|
|
407
|
+
// ---- 面板内的兜底文案(res 缺失时的最后一级回落)----
|
|
408
|
+
"fallback.preview": "\u65E0\u6CD5\u83B7\u53D6\u5FEB\u7167",
|
|
409
|
+
"fallback.rollback": "\u56DE\u9000\u5931\u8D25",
|
|
410
|
+
// ---- 设置页:快照管理的错误区(按 host kind 键控渲染)----
|
|
411
|
+
"errorKind.git": "\u672A\u68C0\u6D4B\u5230 git CLI \u6216\u7248\u672C\u8FC7\u65E7\uFF1A\u8BF7\u5B89\u88C5\u6216\u5347\u7EA7 git\uFF0C\u5B8C\u6210\u540E\u81EA\u52A8\u6062\u590D",
|
|
412
|
+
"errorKind.space": "\u78C1\u76D8\u7A7A\u95F4\u5DF2\u6EE1\uFF0C\u5FEB\u7167\u5199\u5165\u5931\u8D25\uFF1A\u6E05\u7406\u78C1\u76D8\u7A7A\u95F4\u540E\u81EA\u52A8\u6062\u590D",
|
|
413
|
+
"errorKind.permission": "\u5FEB\u7167\u76EE\u5F55\u65E0\u5199\u5165\u6743\u9650\uFF1A\u8BF7\u68C0\u67E5\u76EE\u5F55\u6743\u9650\u540E\u91CD\u8BD5",
|
|
414
|
+
"errorKind.lock": "\u7591\u4F3C\u591A\u4E2A DSH \u5B9E\u4F8B\u5E76\u53D1\u4F7F\u7528\u540C\u4E00\u5FEB\u7167\u5E93\uFF1A\u8BF7\u786E\u8BA4\u53EA\u542F\u52A8\u4E86\u4E00\u4E2A\uFF1B\u786E\u8BA4\u540E\u4ECD\u5931\u8D25\u65F6\uFF0C\u6309\u300C\u8BBE\u7F6E \xB7 \u63D2\u4EF6\u914D\u7F6E \xB7 \u6700\u8FD1\u9519\u8BEF\u300D\u4E2D\u7684\u8DEF\u5F84\u5220\u9664\u9501\u6587\u4EF6",
|
|
415
|
+
"errorKind.mkdir": "\u5FEB\u7167\u5B58\u50A8\u76EE\u5F55\u88AB\u540C\u540D\u6587\u4EF6\u5360\u7528\uFF1A\u5904\u7406\u540E\u81EA\u52A8\u6062\u590D",
|
|
416
|
+
// ---- 设置页:快照管理卡片 ----
|
|
417
|
+
"manage.busy": "\u6267\u884C\u4E2D\u2026",
|
|
418
|
+
"manage.deletedCount": "\u5DF2\u5220\u9664 {deleted} \u6761\u5FEB\u7167",
|
|
419
|
+
"manage.done.delete": "\u5DF2\u5220\u9664",
|
|
420
|
+
"manage.done.deleteAll": "\u5DF2\u6E05\u7A7A\u5168\u90E8\u5FEB\u7167",
|
|
421
|
+
"manage.done.gc": "gc \u5B8C\u6210",
|
|
422
|
+
"manage.confirm.all": "\u786E\u8BA4\u5220\u9664\u6240\u6709\u5DE5\u4F5C\u533A\u7684\u5168\u90E8\u5FEB\u7167\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
|
|
423
|
+
"manage.countLoading": "\u5171 \u2026 \u6761\u5FEB\u7167",
|
|
424
|
+
"manage.countLoaded": "\u5171 {total} \u6761\u5FEB\u7167",
|
|
425
|
+
"manage.countShown": "\uFF08\u5F53\u524D\u663E\u793A\u6700\u65B0 {shown} \u6761\uFF09",
|
|
426
|
+
"manage.countEnd": "\u3002",
|
|
427
|
+
"manage.usageSuffix": "\uFF0C\u5168\u90E8\u5DE5\u4F5C\u533A\u5FEB\u7167\u5B58\u50A8\u5360\u7528 {size}\u3002",
|
|
428
|
+
"manage.health.gitTitle": "\u5FEB\u7167\u5F15\u64CE\u4F9D\u8D56 git",
|
|
429
|
+
"manage.health.gitOk": "git \u53EF\u7528",
|
|
430
|
+
"manage.health.gitBad": "git \u4E0D\u53EF\u7528\uFF08\u5FEB\u7167\u5F15\u64CE\u4F9D\u8D56 git\uFF09",
|
|
431
|
+
"manage.health.stores": " \xB7 \u5FEB\u7167\u5B58\u50A8\uFF1Ahome {n} \u4E2A\u5DE5\u4F5C\u533A",
|
|
432
|
+
"manage.health.storesFallback": "\uFF0C\u964D\u7EA7 {n} \u4E2A",
|
|
433
|
+
"manage.search.placeholder": "\u641C\u7D22\u5DE5\u4F5C\u533A / \u4F1A\u8BDD\u6807\u9898 / \u6D88\u606F\u5185\u5BB9 / ID",
|
|
434
|
+
"manage.search.aria": "\u641C\u7D22\u5FEB\u7167",
|
|
435
|
+
"manage.empty": "\u5728\u4EFB\u610F\u5DE5\u4F5C\u533A\u53D1\u9001\u4E00\u6761\u6D88\u606F\u540E\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u5FEB\u7167\u3002",
|
|
436
|
+
"manage.emptyFiltered": "\u65E0\u5339\u914D\u5FEB\u7167",
|
|
437
|
+
"manage.errors.title": "\u6700\u8FD1\u9519\u8BEF ({n})",
|
|
438
|
+
"manage.errors.collapse": "\u6536\u8D77",
|
|
439
|
+
"manage.errors.expand": "\u5C55\u5F00\u5168\u90E8 ({n})",
|
|
440
|
+
"manage.errors.clear": "\u6E05\u7A7A",
|
|
441
|
+
"manage.errors.dup": "\uFF08\xD7{n}\uFF09",
|
|
442
|
+
"manage.loadMore": "\u52A0\u8F7D\u66F4\u591A",
|
|
443
|
+
"manage.refresh": "\u5237\u65B0",
|
|
444
|
+
"manage.gc": "\u7ACB\u5373 gc",
|
|
445
|
+
"manage.gc.title": "\u7ACB\u5373\u5BF9\u5168\u90E8\u5DE5\u4F5C\u533A\u6267\u884C\u4E00\u6B21 git gc\uFF08\u538B\u7F29\u5BF9\u8C61\u5E93\u91CA\u653E\u7A7A\u95F4\uFF09",
|
|
446
|
+
"manage.deleteAll": "\u5168\u90E8\u5220\u9664",
|
|
447
|
+
"manage.deleteAll.title": "\u5220\u9664\u5168\u90E8\u5DE5\u4F5C\u533A\u7684\u6240\u6709\u5FEB\u7167\uFF1B\u4F1A\u76F4\u63A5\u6838\u5BF9\u5E76\u5220\u9664 git tag\uFF08\u5373\u4F7F\u5217\u8868\u4E3A\u7A7A\u4E5F\u53EF\u6E05\u7406\u6B8B\u7559\uFF09",
|
|
448
|
+
// ---- 设置页:插件配置表单 ----
|
|
449
|
+
"config.group.snapshot": "\u5FEB\u7167\u884C\u4E3A",
|
|
450
|
+
"config.group.auto": "\u81EA\u52A8\u6CBB\u7406",
|
|
451
|
+
"config.group.ui": "\u754C\u9762",
|
|
452
|
+
"config.snapshotEnabled.label": "\u542F\u7528\u5FEB\u7167",
|
|
453
|
+
"config.snapshotEnabled.hint": "\u5173\u95ED\u540E\u4E0D\u518D\u65B0\u5EFA\u5FEB\u7167\uFF1B\u5DF2\u6709\u5FEB\u7167\u4ECD\u53EF\u64A4\u56DE",
|
|
454
|
+
"config.refillDraft.label": "\u56DE\u586B\u8F93\u5165\u6846",
|
|
455
|
+
"config.refillDraft.hint": "\u64A4\u56DE\u540E\u628A\u6D88\u606F\u6587\u672C\u56DE\u586B\u8F93\u5165\u6846\uFF0C\u4FBF\u4E8E\u6539\u5B8C\u91CD\u53D1",
|
|
456
|
+
"config.archiveOriginal.label": "\u5F52\u6863\u539F\u4F1A\u8BDD",
|
|
457
|
+
"config.archiveOriginal.hint": "\u539F\u4F1A\u8BDD\u5F52\u6863\u9690\u85CF\uFF0C\u53EF\u4ECE\u5F52\u6863\u627E\u56DE\uFF1B\u5173\u95ED\u5219\u7559\u5728\u5217\u8868\u4FBF\u4E8E\u5BF9\u7167",
|
|
458
|
+
"config.gcSnaps.label": "gc \u89E6\u53D1\u6761\u6570",
|
|
459
|
+
"config.gcSnaps.hint": "\u6BCF\u79EF\u7D2F\u591A\u5C11\u6761\u5FEB\u7167\u89E6\u53D1\u4E00\u6B21 gc",
|
|
460
|
+
"config.gcSnaps.suffix": "\u6761",
|
|
461
|
+
"config.gcHours.label": "gc \u89E6\u53D1\u5C0F\u65F6",
|
|
462
|
+
"config.gcHours.hint": "\u8DDD\u4E0A\u6B21 gc \u8D85\u8FC7\u591A\u5C11\u5C0F\u65F6\u89E6\u53D1\uFF0C\u4E0E\u6761\u6570\u5148\u5230\u5148\u89E6\u53D1",
|
|
463
|
+
"config.gcHours.suffix": "\u5C0F\u65F6",
|
|
464
|
+
"config.maxFileBytes.label": "\u6587\u4EF6\u5927\u5C0F\u4E0A\u9650",
|
|
465
|
+
"config.maxFileBytes.hint": "\u8D85\u8FC7\u8BE5\u5927\u5C0F\u7684\u6587\u4EF6\u4E0D\u8FDB\u5FEB\u7167\u3001\u4E0D\u88AB\u56DE\u9000\u89E6\u78B0",
|
|
466
|
+
"config.maxFileBytes.suffix": "MB",
|
|
467
|
+
"config.maxSnapshotsPerWorkspace.label": "\u5FEB\u7167\u603B\u91CF\u4E0A\u9650",
|
|
468
|
+
"config.maxSnapshotsPerWorkspace.hint": "\u6BCF\u5DE5\u4F5C\u533A\u4FDD\u7559\u7684\u6700\u5927\u5FEB\u7167\u6570\uFF0C\u8D85\u9650\u5220\u9664\u6700\u65E7\u7684\uFF1B0 \u8868\u793A\u4E0D\u9650\u5236",
|
|
469
|
+
"config.maxSnapshotsPerWorkspace.suffix": "\u6761",
|
|
470
|
+
"config.retentionDays.label": "\u5FEB\u7167\u4FDD\u7559\u5929\u6570",
|
|
471
|
+
"config.retentionDays.hint": "\u8D85\u671F\u5FEB\u7167\u81EA\u52A8\u5220\u9664\u6700\u65E7\u7684\uFF1B0 \u8868\u793A\u4E0D\u542F\u7528",
|
|
472
|
+
"config.retentionDays.suffix": "\u5929",
|
|
473
|
+
"config.locale.label": "\u754C\u9762\u8BED\u8A00",
|
|
474
|
+
"config.locale.hint": "auto \u8DDF\u968F\u7CFB\u7EDF\u8BED\u8A00\uFF1B\u4FDD\u5B58\u540E\u751F\u6548\uFF08\u5BBF\u4E3B\u4FA7\u300C\u63D2\u4EF6\u914D\u7F6E\u300D\u81EA\u5E26\u7684\u5B57\u6BB5\u8BF4\u660E\u4ECD\u4E3A\u4E2D\u6587\uFF09",
|
|
475
|
+
"config.locale.auto": "\u8DDF\u968F\u7CFB\u7EDF (auto)",
|
|
476
|
+
"config.tag.modified": "\u5DF2\u4FEE\u6539",
|
|
477
|
+
"config.tag.overridden": "\u5DF2\u8986\u76D6",
|
|
478
|
+
"config.tag.locked": "\u73AF\u5883\u53D8\u91CF\u9501\u5B9A",
|
|
479
|
+
"config.tag.readonly": "\u53EA\u8BFB\u8BBE\u7F6E\u6E90",
|
|
480
|
+
"config.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
481
|
+
"config.reset.title": "\u628A\u6240\u6709\u5B57\u6BB5\u6062\u590D\u5230\u63D2\u4EF6\u51FA\u5382\u9ED8\u8BA4\u503C",
|
|
482
|
+
"config.advanced.title": "\u9AD8\u7EA7\uFF1A\u57FA\u7840\u6392\u9664\u8868",
|
|
483
|
+
"config.baseExcludes.label": "\u57FA\u7840\u6392\u9664\u8868",
|
|
484
|
+
"config.baseExcludes.hint": "\u5404\u5DE5\u4F5C\u533A\u5171\u4EAB\u7684\u5185\u7F6E\u89C4\u5219\uFF1Bgitignore \u8BED\u6CD5\uFF0C\u6BCF\u884C\u4E00\u6761\uFF1B\u4F18\u5148\u7EA7\u4F4E\u4E8E\u300C\u6392\u9664\u914D\u7F6E\u300D\u7684 exclude.txt",
|
|
485
|
+
"config.msg.loading": "\u6B63\u5728\u8BFB\u53D6\u914D\u7F6E\u2026",
|
|
486
|
+
"config.msg.loadFailed": "\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E",
|
|
487
|
+
"config.msg.noChange": "\u6CA1\u6709\u4FEE\u6539",
|
|
488
|
+
"config.msg.saving": "\u4FDD\u5B58\u4E2D\u2026",
|
|
489
|
+
"config.msg.saved": "\u5DF2\u4FDD\u5B58\u5E76\u5373\u65F6\u751F\u6548",
|
|
490
|
+
"config.msg.saveFailed": "\u4FDD\u5B58\u5931\u8D25",
|
|
491
|
+
"config.msg.resetting": "\u6062\u590D\u9ED8\u8BA4\u4E2D\u2026",
|
|
492
|
+
"config.msg.resetDone": "\u5DF2\u6062\u590D\u9ED8\u8BA4\u503C",
|
|
493
|
+
"config.msg.resetFailed": "\u6062\u590D\u9ED8\u8BA4\u5931\u8D25",
|
|
494
|
+
"config.err.gcSnaps": "\u5FEB\u7167\u6761\u6570\u9608\u503C\u5FC5\u987B\u662F >= 1 \u7684\u6574\u6570",
|
|
495
|
+
"config.err.gcHours": "gc \u5C0F\u65F6\u9608\u503C\u5FC5\u987B\u662F >= 1 \u7684\u6574\u6570",
|
|
496
|
+
"config.err.maxFileBytes": "\u6587\u4EF6\u5927\u5C0F\u4E0A\u9650\u81F3\u5C11 0.01 MB",
|
|
497
|
+
"config.err.maxSnapshots": "\u5FEB\u7167\u603B\u91CF\u4E0A\u9650\u5FC5\u987B\u662F >= 0 \u7684\u6574\u6570\uFF080 \u8868\u793A\u4E0D\u9650\u5236\uFF09",
|
|
498
|
+
"config.err.retentionDays": "\u4FDD\u7559\u5929\u6570\u5FC5\u987B\u662F >= 0 \u7684\u6574\u6570\uFF080 \u8868\u793A\u4E0D\u542F\u7528\uFF09",
|
|
499
|
+
"config.err.locale": "\u754C\u9762\u8BED\u8A00\u5FC5\u987B\u662F auto / zh / en \u4E4B\u4E00",
|
|
500
|
+
// ---- 设置页:排除配置卡片 ----
|
|
501
|
+
"exclude.note.home": "\u6B64\u914D\u7F6E\u5168\u5C40\u5171\u4EAB\uFF0C\u5BF9\u6240\u6709\u5DE5\u4F5C\u533A\u7684\u5FEB\u7167\u751F\u6548\u3002",
|
|
502
|
+
"exclude.note.fallback": "home \u76EE\u5F55\u4E0D\u53EF\u5199\u65F6\u6B64\u5DE5\u4F5C\u533A\u964D\u7EA7\u5B58\u50A8\uFF0C\u6392\u9664\u914D\u7F6E\u72EC\u7ACB\u751F\u6548\u3002",
|
|
503
|
+
"exclude.path": "\u5B58\u50A8\u4F4D\u7F6E\uFF1A{path}",
|
|
504
|
+
"exclude.syntax": "gitignore \u8BED\u6CD5\uFF0C\u4E00\u884C\u4E00\u6761\uFF0C\u652F\u6301 # \u6CE8\u91CA\uFF1B\u547D\u4E2D\u9879\u4E0D\u8FDB\u5FEB\u7167\u3001\u4E0D\u88AB\u56DE\u9000\u89E6\u78B0\u3002",
|
|
505
|
+
"exclude.area.aria": "\u5FEB\u7167\u6392\u9664\u6A21\u5F0F\u5217\u8868\uFF08gitignore \u8BED\u6CD5\uFF0C\u4E00\u884C\u4E00\u6761\uFF09",
|
|
506
|
+
"exclude.quick.placeholder": "\u8F93\u5165\u8DEF\u5F84\u6216\u6A21\u5F0F\uFF0C\u56DE\u8F66\u5FEB\u901F\u6DFB\u52A0",
|
|
507
|
+
"exclude.quick.aria": "\u5FEB\u901F\u6DFB\u52A0\u6392\u9664\u6A21\u5F0F",
|
|
508
|
+
"exclude.add": "\u6DFB\u52A0",
|
|
509
|
+
"exclude.chip.title": "\u70B9\u51FB\u8FFD\u52A0 {pattern}",
|
|
510
|
+
"exclude.msg.saving": "\u4FDD\u5B58\u4E2D\u2026",
|
|
511
|
+
"exclude.msg.saved": "\u5DF2\u4FDD\u5B58\uFF0C\u4E0B\u4E00\u6B21\u5FEB\u7167 / \u9884\u89C8 / \u56DE\u9000\u65F6\u751F\u6548",
|
|
512
|
+
"exclude.msg.saveFailed": "\u4FDD\u5B58\u5931\u8D25",
|
|
513
|
+
"exclude.loadFailed": "\u65E0\u6CD5\u8BFB\u53D6\u6392\u9664\u914D\u7F6E",
|
|
514
|
+
"exclude.unsupported": "\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u5FEB\u7167\u529F\u80FD\uFF0C\u6392\u9664\u914D\u7F6E\u4E0D\u53EF\u7528\u3002",
|
|
515
|
+
"exclude.loading": "\u6B63\u5728\u52A0\u8F7D\u6392\u9664\u914D\u7F6E\u2026",
|
|
516
|
+
"exclude.empty": "\u5C1A\u672A\u521B\u5EFA\u4EFB\u4F55\u5FEB\u7167\u5B58\u50A8\uFF1A\u5728\u4EFB\u610F\u5DE5\u4F5C\u533A\u53D1\u9001\u4E00\u6761\u6D88\u606F\u540E\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u53EF\u7F16\u8F91\u7684\u6392\u9664\u914D\u7F6E\u3002",
|
|
517
|
+
// ---- 设置页:分区折叠头 ----
|
|
518
|
+
"section.exclude": "\u6392\u9664\u914D\u7F6E\uFF08exclude.txt\uFF09",
|
|
519
|
+
"section.manage": "\u5FEB\u7167\u7BA1\u7406"
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
// src/client/locales/en.ts
|
|
523
|
+
var en = {
|
|
524
|
+
// ---- 通用 ----
|
|
525
|
+
"common.close": "Close",
|
|
526
|
+
"common.cancel": "Cancel",
|
|
527
|
+
"common.confirm": "Confirm",
|
|
528
|
+
"common.save": "Save",
|
|
529
|
+
"common.discard": "Discard changes",
|
|
530
|
+
"common.retry": "Retry",
|
|
531
|
+
"common.errorPrefix": "Error: ",
|
|
532
|
+
"common.unknownReason": "unknown reason",
|
|
533
|
+
"common.opFailed": "Operation failed",
|
|
534
|
+
"common.listSep": ", ",
|
|
535
|
+
"common.paren": " ({text})",
|
|
536
|
+
// ---- 变更类型 ----
|
|
537
|
+
"kind.modified": "modified",
|
|
538
|
+
"kind.restored": "restored",
|
|
539
|
+
"kind.added": "deleted",
|
|
540
|
+
// ---- 用户消息内的文件卡片 ----
|
|
541
|
+
"file.unnamed": "unnamed file",
|
|
542
|
+
// ---- 快照管理树的节点文案 ----
|
|
543
|
+
"tree.unknownWorkspace": "Unknown workspace",
|
|
544
|
+
"tree.deletedSession": "(deleted session)",
|
|
545
|
+
"tree.expand": "Expand: {label}",
|
|
546
|
+
"tree.collapse": "Collapse: {label}",
|
|
547
|
+
"tree.snapCount": "{n} snapshots",
|
|
548
|
+
"tree.wsCount": "{n} sessions / {m} snapshots",
|
|
549
|
+
"tree.family.title": "Version family: {chain}",
|
|
550
|
+
"tree.switch": "Switch",
|
|
551
|
+
"tree.switch.title": "Switch to this version session",
|
|
552
|
+
"tree.delete.snapshot.title": "Delete this snapshot (tag and index entry)",
|
|
553
|
+
"tree.delete.snapshot.confirm": "Delete this snapshot? This cannot be undone.",
|
|
554
|
+
"tree.delete.session.title": "Delete all snapshots of this session",
|
|
555
|
+
"tree.delete.session.confirm": "Delete all snapshots of this session? This cannot be undone.",
|
|
556
|
+
"tree.delete.workspace.title": "Delete all snapshots of this workspace",
|
|
557
|
+
"tree.delete.workspace.confirm": "Delete all snapshots of this workspace? This cannot be undone.",
|
|
558
|
+
// ---- 撤回确认面板 ----
|
|
559
|
+
"recall.loading": "Computing changes\u2026",
|
|
560
|
+
"recall.error.title": "Cannot roll back",
|
|
561
|
+
"recall.confirm.title": "Roll back",
|
|
562
|
+
"recall.truncated.files": "\u2026showing the first {shown} of {total} files to change",
|
|
563
|
+
"recall.truncated.chat": "\u2026showing the first {shown} of {total} differences",
|
|
564
|
+
"recall.confirm.sessionOnly": "Project files stay as they are \u2014 nothing is reverted or deleted; only the chat rewinds to before this message.",
|
|
565
|
+
"recall.confirm.rollbackAt": "Restore the project to its state at {time}, when this message was sent.",
|
|
566
|
+
"recall.confirm.rollbackNoTime": "Restore the project to its state when this message was sent.",
|
|
567
|
+
// 句首空格是有意的:本句与 recall.confirm.rollbackAt 拼接成一段(前句以句点
|
|
568
|
+
// 收尾),英文标点族需要空格(与 common.paren / tellChat 同类;中文版不需要)
|
|
569
|
+
"recall.confirm.files": " {total} files will change{summary}. This overwrites current file contents; a safety snapshot of the current state is saved automatically before the rollback (not listed below).",
|
|
570
|
+
"recall.confirm.chatBoth": "The chat also rewinds to before this message: this message and everything after it leaves the current view, and the original session is archived (recoverable from Archive).",
|
|
571
|
+
"recall.confirm.chatFirst": "This is the first user message in this session, so the chat cannot rewind; only project files will be restored.",
|
|
572
|
+
"recall.confirm.refNote": "The differences below are for reference only; the selected mode will not touch any file.",
|
|
573
|
+
"recall.scope.aria": "Recall scope",
|
|
574
|
+
"recall.scope.both": "Files and chat",
|
|
575
|
+
"recall.scope.sessionOnly": "Chat only",
|
|
576
|
+
"recall.confirm.submit.both": "Roll back",
|
|
577
|
+
"recall.confirm.submit.sessionOnly": "Rewind chat",
|
|
578
|
+
"recall.executing.both": "Rolling back\u2026",
|
|
579
|
+
"recall.executing.sessionOnly": "Rewinding chat\u2026",
|
|
580
|
+
"recall.done.title": "Rollback complete",
|
|
581
|
+
"recall.done.both.ok": "Project files and the chat have been rewound to before this message. A new session is open and the original one is archived (recoverable from Archive).",
|
|
582
|
+
"recall.done.both.partial": "The project has been restored to its state when this message was sent.",
|
|
583
|
+
"recall.done.both.failChat": " Chat rewind failed: {error}",
|
|
584
|
+
"recall.done.sessionOnly.ok": "The chat has been rewound to before this message; project files stay as they are. A new session is open and the original one is archived (recoverable from Archive).",
|
|
585
|
+
"recall.done.sessionOnly.fail": "Chat rewind failed: {error}. No project files were touched.",
|
|
586
|
+
"recall.chat.noChild": "no new session returned",
|
|
587
|
+
// ---- 用户消息气泡的动作按钮 ----
|
|
588
|
+
"action.copy": "Copy",
|
|
589
|
+
"action.copied": "Copied",
|
|
590
|
+
"action.recall": "Recall",
|
|
591
|
+
"action.recall.title": "Roll back: files and chat return to before this message",
|
|
592
|
+
// ---- toast ----
|
|
593
|
+
"toast.tag": "Recall plugin",
|
|
594
|
+
"toast.skipped": "Snapshot skipped unlisted paths: {names} (rollback will not restore or delete them)",
|
|
595
|
+
"toast.skippedMore": " and {n} more",
|
|
596
|
+
"toast.snapshotFailed": "Snapshot failed: {error}",
|
|
597
|
+
"toast.staleQueue": "A queued message from before the recall could not be cleaned up automatically; remove it with the delete button on that card",
|
|
598
|
+
"toast.fileAttachRefill": "File attachments from the recalled message cannot be refilled automatically (the official API only reads images back); please re-select the files",
|
|
599
|
+
// ---- init / snapshot-info 说明 ----
|
|
600
|
+
"notice.unsupported": "The recall plugin supports Windows / Linux / macOS only; snapshots are unavailable on this platform.",
|
|
601
|
+
"notice.gitMissing": "git CLI not found, so recall is unavailable (the snapshot engine depends on git). Install git and restart DSH to enable it.",
|
|
602
|
+
"notice.homeFallback": "The home directory is not writable; snapshots fall back to .dsh-recall-snapshots inside the project.",
|
|
603
|
+
"notice.buildRoot": 'This workspace is a build-artifact directory (path segment {seg}), so project snapshots are skipped; to use recall here, remove that entry from "Base excludes" in the plugin settings.',
|
|
604
|
+
// ---- 错误码文案 ----
|
|
605
|
+
"err.STALE": "Project files changed after the preview; preview again to confirm",
|
|
606
|
+
"err.AGENT_BUSY": "The agent is running; stop it before recalling",
|
|
607
|
+
"err.NO_SNAPSHOT": "No project snapshot is available for this message",
|
|
608
|
+
"err.NO_STORE": "Snapshot storage is unavailable",
|
|
609
|
+
"err.FORMAT_BLOCKED": "Unsupported disk format; writes are paused (see Recent errors)",
|
|
610
|
+
"err.UNKNOWN_PATH": "Unknown exclude file path",
|
|
611
|
+
"err.EMPTY_PATCH": "No config field to write",
|
|
612
|
+
"err.SETTINGS_UNAVAILABLE": "Settings service unavailable: override the config by id: recall in the profile cordis.patch.yml",
|
|
613
|
+
"err.BODY_TOO_LARGE": "Request body exceeds the size limit",
|
|
614
|
+
"err.NO_ROOT": "Cannot resolve the workspace",
|
|
615
|
+
"err.NO_SESSION": "Missing session ID",
|
|
616
|
+
"err.UNKNOWN_OP": "Unknown manage operation",
|
|
617
|
+
"err.UNKNOWN_ENDPOINT": "Unknown API endpoint",
|
|
618
|
+
"err.INDEX_CORRUPT": "Snapshot index is corrupted",
|
|
619
|
+
// ---- 面板兜底 ----
|
|
620
|
+
"fallback.preview": "Failed to load the snapshot",
|
|
621
|
+
"fallback.rollback": "Rollback failed",
|
|
622
|
+
// ---- 设置页:最近错误(按 host kind 键控)----
|
|
623
|
+
"errorKind.git": "git CLI not found or too old: install or upgrade git; snapshots resume automatically afterwards",
|
|
624
|
+
"errorKind.space": "Disk is full, snapshot writes failed: free up disk space to resume automatically",
|
|
625
|
+
"errorKind.permission": "No write permission on the snapshot directory: check directory permissions and retry",
|
|
626
|
+
"errorKind.lock": "Possibly multiple DSH instances sharing one snapshot store: make sure only one is running; if it still fails, delete the lock file at the path shown in Settings \xB7 Plugin config \xB7 Recent errors",
|
|
627
|
+
"errorKind.mkdir": "The snapshot directory is occupied by a file of the same name: resolve it to resume automatically",
|
|
628
|
+
// ---- 设置页:快照管理卡片 ----
|
|
629
|
+
"manage.busy": "Working\u2026",
|
|
630
|
+
"manage.deletedCount": "Deleted {deleted} snapshots",
|
|
631
|
+
"manage.done.delete": "Deleted",
|
|
632
|
+
"manage.done.deleteAll": "All snapshots cleared",
|
|
633
|
+
"manage.done.gc": "gc finished",
|
|
634
|
+
"manage.confirm.all": "Delete all snapshots in every workspace? This cannot be undone.",
|
|
635
|
+
"manage.countLoading": "Loading snapshot count\u2026",
|
|
636
|
+
"manage.countLoaded": "{total} snapshots in total",
|
|
637
|
+
"manage.countShown": " (showing the newest {shown})",
|
|
638
|
+
"manage.countEnd": ".",
|
|
639
|
+
"manage.usageSuffix": ", using {size} across all workspaces.",
|
|
640
|
+
"manage.health.gitTitle": "The snapshot engine depends on git",
|
|
641
|
+
"manage.health.gitOk": "git available",
|
|
642
|
+
"manage.health.gitBad": "git unavailable (the snapshot engine depends on git)",
|
|
643
|
+
"manage.health.stores": " \xB7 Snapshot storage: {n} workspaces in home",
|
|
644
|
+
"manage.health.storesFallback": ", {n} in fallback",
|
|
645
|
+
"manage.search.placeholder": "Search workspace / session title / message / ID",
|
|
646
|
+
"manage.search.aria": "Search snapshots",
|
|
647
|
+
"manage.empty": "Send a message in any workspace and its snapshot will appear here.",
|
|
648
|
+
"manage.emptyFiltered": "No matching snapshots",
|
|
649
|
+
"manage.errors.title": "Recent errors ({n})",
|
|
650
|
+
"manage.errors.collapse": "Collapse",
|
|
651
|
+
"manage.errors.expand": "Show all ({n})",
|
|
652
|
+
"manage.errors.clear": "Clear",
|
|
653
|
+
"manage.errors.dup": " (\xD7{n})",
|
|
654
|
+
"manage.loadMore": "Load more",
|
|
655
|
+
"manage.refresh": "Refresh",
|
|
656
|
+
"manage.gc": "Run gc",
|
|
657
|
+
"manage.gc.title": "Run git gc once for every workspace (compacts the object store to free space)",
|
|
658
|
+
"manage.deleteAll": "Delete all",
|
|
659
|
+
"manage.deleteAll.title": "Delete all snapshots in every workspace; git tags are checked and removed directly (leftovers can be cleaned even when the list is empty)",
|
|
660
|
+
// ---- 设置页:插件配置表单 ----
|
|
661
|
+
"config.group.snapshot": "Snapshot behavior",
|
|
662
|
+
"config.group.auto": "Automatic maintenance",
|
|
663
|
+
"config.group.ui": "Interface",
|
|
664
|
+
"config.snapshotEnabled.label": "Enable snapshots",
|
|
665
|
+
"config.snapshotEnabled.hint": "When off, no new snapshots are created; existing ones can still be recalled",
|
|
666
|
+
"config.refillDraft.label": "Refill input box",
|
|
667
|
+
"config.refillDraft.hint": "Put the message text back into the input box after a recall, so you can edit and resend",
|
|
668
|
+
"config.archiveOriginal.label": "Archive original session",
|
|
669
|
+
"config.archiveOriginal.hint": "Archive and hide the original session (recoverable from Archive); when off it stays in the list for comparison",
|
|
670
|
+
"config.gcSnaps.label": "gc trigger count",
|
|
671
|
+
"config.gcSnaps.hint": "Run gc after this many snapshots accumulate",
|
|
672
|
+
"config.gcSnaps.suffix": "snapshots",
|
|
673
|
+
"config.gcHours.label": "gc trigger hours",
|
|
674
|
+
"config.gcHours.hint": "Run gc when this many hours passed since the last one; whichever comes first",
|
|
675
|
+
"config.gcHours.suffix": "hours",
|
|
676
|
+
"config.maxFileBytes.label": "File size limit",
|
|
677
|
+
"config.maxFileBytes.hint": "Files larger than this are not snapshotted and never touched by rollback",
|
|
678
|
+
"config.maxFileBytes.suffix": "MB",
|
|
679
|
+
"config.maxSnapshotsPerWorkspace.label": "Snapshot count limit",
|
|
680
|
+
"config.maxSnapshotsPerWorkspace.hint": "Maximum snapshots kept per workspace; the oldest are deleted beyond it; 0 means unlimited",
|
|
681
|
+
"config.maxSnapshotsPerWorkspace.suffix": "snapshots",
|
|
682
|
+
"config.retentionDays.label": "Snapshot retention days",
|
|
683
|
+
"config.retentionDays.hint": "Delete snapshots older than this; the oldest first; 0 disables it",
|
|
684
|
+
"config.retentionDays.suffix": "days",
|
|
685
|
+
"config.locale.label": "Language",
|
|
686
|
+
"config.locale.hint": "auto follows the system language; takes effect after saving (field descriptions rendered by the host-side plugin config form stay in Chinese)",
|
|
687
|
+
"config.locale.auto": "Follow system (auto)",
|
|
688
|
+
"config.tag.modified": "Modified",
|
|
689
|
+
"config.tag.overridden": "Overridden",
|
|
690
|
+
"config.tag.locked": "Locked by env var",
|
|
691
|
+
"config.tag.readonly": "Read-only settings source",
|
|
692
|
+
"config.reset": "Restore defaults",
|
|
693
|
+
"config.reset.title": "Restore every field to the plugin factory defaults",
|
|
694
|
+
"config.advanced.title": "Advanced: base excludes",
|
|
695
|
+
"config.baseExcludes.label": "Base excludes",
|
|
696
|
+
"config.baseExcludes.hint": 'Built-in rules shared by all workspaces; gitignore syntax, one per line; lower priority than exclude.txt in "Exclude config"',
|
|
697
|
+
"config.msg.loading": "Loading configuration\u2026",
|
|
698
|
+
"config.msg.loadFailed": "Failed to load configuration",
|
|
699
|
+
"config.msg.noChange": "No changes",
|
|
700
|
+
"config.msg.saving": "Saving\u2026",
|
|
701
|
+
"config.msg.saved": "Saved and applied immediately",
|
|
702
|
+
"config.msg.saveFailed": "Failed to save",
|
|
703
|
+
"config.msg.resetting": "Restoring defaults\u2026",
|
|
704
|
+
"config.msg.resetDone": "Defaults restored",
|
|
705
|
+
"config.msg.resetFailed": "Failed to restore defaults",
|
|
706
|
+
"config.err.gcSnaps": "The snapshot count threshold must be an integer >= 1",
|
|
707
|
+
"config.err.gcHours": "The gc hour threshold must be an integer >= 1",
|
|
708
|
+
"config.err.maxFileBytes": "The file size limit must be at least 0.01 MB",
|
|
709
|
+
"config.err.maxSnapshots": "The snapshot count limit must be an integer >= 0 (0 means unlimited)",
|
|
710
|
+
"config.err.retentionDays": "Retention days must be an integer >= 0 (0 disables it)",
|
|
711
|
+
"config.err.locale": "Language must be one of auto / zh / en",
|
|
712
|
+
// ---- 设置页:排除配置卡片 ----
|
|
713
|
+
"exclude.note.home": "This configuration is shared globally and applies to snapshots in every workspace.",
|
|
714
|
+
"exclude.note.fallback": "This workspace stores snapshots in the fallback location (home is not writable), so its exclude config is separate.",
|
|
715
|
+
"exclude.path": "Location: {path}",
|
|
716
|
+
"exclude.syntax": "gitignore syntax, one per line, # comments supported; matched paths are not snapshotted and never touched by rollback.",
|
|
717
|
+
"exclude.area.aria": "Snapshot exclude patterns (gitignore syntax, one per line)",
|
|
718
|
+
"exclude.quick.placeholder": "Type a path or pattern, press Enter to add",
|
|
719
|
+
"exclude.quick.aria": "Quickly add an exclude pattern",
|
|
720
|
+
"exclude.add": "Add",
|
|
721
|
+
"exclude.chip.title": "Click to append {pattern}",
|
|
722
|
+
"exclude.msg.saving": "Saving\u2026",
|
|
723
|
+
"exclude.msg.saved": "Saved; takes effect on the next snapshot / preview / rollback",
|
|
724
|
+
"exclude.msg.saveFailed": "Failed to save",
|
|
725
|
+
"exclude.loadFailed": "Failed to load the exclude configuration",
|
|
726
|
+
"exclude.unsupported": "This platform does not support snapshots, so exclude configuration is unavailable.",
|
|
727
|
+
"exclude.loading": "Loading exclude configuration\u2026",
|
|
728
|
+
"exclude.empty": "No snapshot store exists yet: send a message in any workspace and its editable exclude configuration will appear here.",
|
|
729
|
+
// ---- 设置页:分区折叠头 ----
|
|
730
|
+
"section.exclude": "Exclude config (exclude.txt)",
|
|
731
|
+
"section.manage": "Snapshot manager"
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// src/client/locales/index.ts
|
|
735
|
+
var DICTS = { zh, en };
|
|
736
|
+
function resolveLocale(pref) {
|
|
737
|
+
if (pref === "zh" || pref === "en") return pref;
|
|
738
|
+
try {
|
|
739
|
+
const nav = typeof navigator === "undefined" ? null : navigator;
|
|
740
|
+
const lang = nav && typeof nav.language === "string" ? nav.language : "";
|
|
741
|
+
if (!lang) return "zh";
|
|
742
|
+
return lang.toLowerCase().indexOf("zh") === 0 ? "zh" : "en";
|
|
743
|
+
} catch (error) {
|
|
744
|
+
return "zh";
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function interpolate(template, params) {
|
|
748
|
+
if (!params) return template;
|
|
749
|
+
return template.replace(/\{(\w+)\}/g, (raw, name) => {
|
|
750
|
+
const value = params[name];
|
|
751
|
+
return value === void 0 ? raw : String(value);
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
function translate(locale, key, params) {
|
|
755
|
+
const dict = DICTS[locale];
|
|
756
|
+
const hit = dict[key] !== void 0 ? dict[key] : zh[key];
|
|
757
|
+
return interpolate(hit !== void 0 ? hit : key, params);
|
|
758
|
+
}
|
|
759
|
+
var zhTranslate = (key, params) => translate("zh", key, params);
|
|
760
|
+
function hasTranslation(key) {
|
|
761
|
+
return zh[key] !== void 0;
|
|
762
|
+
}
|
|
763
|
+
|
|
306
764
|
// src/client/util.ts
|
|
307
765
|
function clockText(ms) {
|
|
308
766
|
try {
|
|
@@ -329,15 +787,19 @@
|
|
|
329
787
|
if (!Number.isFinite(n) || n <= 0) return "";
|
|
330
788
|
return String(Math.round(n / 1048576 * 100) / 100);
|
|
331
789
|
}
|
|
332
|
-
function buildTree(list) {
|
|
790
|
+
function buildTree(list, t = zhTranslate) {
|
|
333
791
|
const workspaces = /* @__PURE__ */ new Map();
|
|
334
792
|
for (const it of list || []) {
|
|
335
793
|
const rootKey = it.root || "unknown-root";
|
|
336
|
-
|
|
337
|
-
|
|
794
|
+
let ws = workspaces.get(rootKey);
|
|
795
|
+
if (!ws) {
|
|
796
|
+
ws = { root: it.root || null, name: it.workspace || t("tree.unknownWorkspace"), sessions: /* @__PURE__ */ new Map() };
|
|
797
|
+
workspaces.set(rootKey, ws);
|
|
798
|
+
}
|
|
338
799
|
const sidKey = it.sessionId || "unknown-session";
|
|
339
800
|
if (!ws.sessions.has(sidKey)) ws.sessions.set(sidKey, { root: ws.root, sessionId: it.sessionId || null, title: it.sessionTitle || null, items: [] });
|
|
340
|
-
ws.sessions.get(sidKey)
|
|
801
|
+
const session = ws.sessions.get(sidKey);
|
|
802
|
+
if (session) session.items.push(it);
|
|
341
803
|
}
|
|
342
804
|
const wsList = Array.from(workspaces.values()).map((ws) => ({ root: ws.root, name: ws.name, sessions: Array.from(ws.sessions.values()) }));
|
|
343
805
|
wsList.sort((a, b) => (a.name || "").localeCompare(b.name || ""));
|
|
@@ -368,6 +830,13 @@
|
|
|
368
830
|
}, [state.message, state.error, state.busy]);
|
|
369
831
|
}
|
|
370
832
|
function buildUtil() {
|
|
833
|
+
let locale = resolveLocale("auto");
|
|
834
|
+
function t(key, params) {
|
|
835
|
+
return translate(locale, key, params);
|
|
836
|
+
}
|
|
837
|
+
function setLocalePref(pref) {
|
|
838
|
+
locale = resolveLocale(pref);
|
|
839
|
+
}
|
|
371
840
|
function api(name, args) {
|
|
372
841
|
return fetch(recallApiUrl(name), {
|
|
373
842
|
method: "POST",
|
|
@@ -375,17 +844,11 @@
|
|
|
375
844
|
body: JSON.stringify(args || {})
|
|
376
845
|
}).then((r) => r.json());
|
|
377
846
|
}
|
|
378
|
-
const CODE_TEXT = {
|
|
379
|
-
STALE: "\u9884\u89C8\u540E\u9879\u76EE\u6587\u4EF6\u53D1\u751F\u4E86\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u9884\u89C8\u786E\u8BA4",
|
|
380
|
-
AGENT_BUSY: "Agent \u6B63\u5728\u8FD0\u884C\u4E2D\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u64A4\u56DE",
|
|
381
|
-
NO_SNAPSHOT: "\u8BE5\u6D88\u606F\u6CA1\u6709\u53EF\u7528\u7684\u9879\u76EE\u5FEB\u7167",
|
|
382
|
-
NO_STORE: "\u5FEB\u7167\u5B58\u50A8\u4E0D\u53EF\u7528",
|
|
383
|
-
ROLLBACK_FAILED: "\u56DE\u9000\u5931\u8D25"
|
|
384
|
-
};
|
|
385
847
|
function messageFor(res, fallback) {
|
|
386
848
|
if (!res) return fallback;
|
|
387
849
|
const code = res.code;
|
|
388
|
-
|
|
850
|
+
const key = code ? "err." + String(code) : "";
|
|
851
|
+
if (key && hasTranslation(key)) return t(key);
|
|
389
852
|
const m = res.message;
|
|
390
853
|
const e = res.error;
|
|
391
854
|
return String(m || e || fallback);
|
|
@@ -406,7 +869,7 @@
|
|
|
406
869
|
el.className = "dsh-recall-toast";
|
|
407
870
|
const tag = document.createElement("span");
|
|
408
871
|
tag.className = "dsh-recall-toast-tag";
|
|
409
|
-
tag.textContent = "
|
|
872
|
+
tag.textContent = t("toast.tag");
|
|
410
873
|
const body = document.createElement("span");
|
|
411
874
|
body.textContent = text;
|
|
412
875
|
el.appendChild(tag);
|
|
@@ -444,18 +907,21 @@
|
|
|
444
907
|
const cfg = res.config;
|
|
445
908
|
if (typeof cfg.refillDraft === "boolean") pluginConfig.refillDraft = cfg.refillDraft;
|
|
446
909
|
if (typeof cfg.archiveOriginal === "boolean") pluginConfig.archiveOriginal = cfg.archiveOriginal;
|
|
910
|
+
setLocalePref(cfg.locale);
|
|
447
911
|
}
|
|
448
912
|
const notice = res && res.notice;
|
|
449
913
|
if (notice && notice.unsupported) {
|
|
450
|
-
showNotice("unsupported", "
|
|
914
|
+
showNotice("unsupported", t("notice.unsupported"));
|
|
451
915
|
}
|
|
452
916
|
if (notice && notice.gitMissing) {
|
|
453
|
-
showNotice("git", "
|
|
917
|
+
showNotice("git", t("notice.gitMissing"));
|
|
454
918
|
}
|
|
455
919
|
if (notice && notice.homeFallback) {
|
|
456
|
-
showNotice("home", "
|
|
920
|
+
showNotice("home", t("notice.homeFallback"));
|
|
457
921
|
}
|
|
458
|
-
if (notice && notice.
|
|
922
|
+
if (notice && typeof notice.buildRootArtifactSeg === "string" && notice.buildRootArtifactSeg) {
|
|
923
|
+
showNotice("buildRoot", t("notice.buildRoot", { seg: notice.buildRootArtifactSeg }));
|
|
924
|
+
} else if (notice && notice.buildRootNotice) {
|
|
459
925
|
showNotice("buildRoot", String(notice.buildRootNotice).slice(0, 140));
|
|
460
926
|
}
|
|
461
927
|
}).catch(() => {
|
|
@@ -490,8 +956,45 @@
|
|
|
490
956
|
}
|
|
491
957
|
return Promise.resolve(false);
|
|
492
958
|
}
|
|
493
|
-
return { api, messageFor, showNotice, showThrottledToast, ensureInit, clockText, writeClipboard, sizeText, bytesToMb, buildTree, pluginConfig };
|
|
959
|
+
return { api, messageFor, showNotice, showThrottledToast, ensureInit, clockText, writeClipboard, sizeText, bytesToMb, buildTree, t, setLocalePref, pluginConfig };
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// src/client/log.ts
|
|
963
|
+
function debugFlag() {
|
|
964
|
+
try {
|
|
965
|
+
return String(localStorage.getItem("dsh-recall.debug") || "");
|
|
966
|
+
} catch (error) {
|
|
967
|
+
return "";
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function debugEnabled(ns) {
|
|
971
|
+
const raw = debugFlag();
|
|
972
|
+
if (!raw) return false;
|
|
973
|
+
for (const part of raw.split(",")) {
|
|
974
|
+
const t = part.trim();
|
|
975
|
+
if (t === "*" || t === ns) return true;
|
|
976
|
+
}
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
function createLogger(ns) {
|
|
980
|
+
const prefix = "[dsh-recall:" + ns + "]";
|
|
981
|
+
return {
|
|
982
|
+
error: (...args) => {
|
|
983
|
+
console.error(prefix, ...args);
|
|
984
|
+
},
|
|
985
|
+
warn: (...args) => {
|
|
986
|
+
console.warn(prefix, ...args);
|
|
987
|
+
},
|
|
988
|
+
info: (...args) => {
|
|
989
|
+
if (debugEnabled(ns)) console.info(prefix, ...args);
|
|
990
|
+
},
|
|
991
|
+
debug: (...args) => {
|
|
992
|
+
if (debugEnabled(ns)) console.debug(prefix, ...args);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
494
995
|
}
|
|
996
|
+
var appLog = createLogger("app");
|
|
997
|
+
var recallNodeLog = createLogger("recall-node");
|
|
495
998
|
|
|
496
999
|
// src/client/recall-node.ts
|
|
497
1000
|
function attachmentRefsFromBlocks(blocks) {
|
|
@@ -526,26 +1029,29 @@
|
|
|
526
1029
|
if (n < 1073741824) return (n / 1048576).toFixed(1) + "MB";
|
|
527
1030
|
return (n / 1073741824).toFixed(1) + "GB";
|
|
528
1031
|
}
|
|
529
|
-
function fileCardInfo(block) {
|
|
1032
|
+
function fileCardInfo(block, t = zhTranslate) {
|
|
530
1033
|
if (!block || block.type !== "file") return null;
|
|
531
1034
|
const ref = block.attachment;
|
|
532
|
-
const name = ref && typeof ref === "object" && typeof ref.name === "string" && ref.name !== "" ? ref.name : "
|
|
1035
|
+
const name = ref && typeof ref === "object" && typeof ref.name === "string" && ref.name !== "" ? ref.name : t("file.unnamed");
|
|
533
1036
|
return { name, ext: fileExtOf(name), size: fileCardSizeText(ref && typeof ref === "object" ? ref.bytes : void 0) };
|
|
534
1037
|
}
|
|
535
1038
|
var KIND_INFO = {
|
|
536
|
-
modified: {
|
|
537
|
-
restored: {
|
|
538
|
-
added: {
|
|
1039
|
+
modified: { labelKey: "kind.modified", cls: "modified" },
|
|
1040
|
+
restored: { labelKey: "kind.restored", cls: "restored" },
|
|
1041
|
+
added: { labelKey: "kind.added", cls: "added" }
|
|
539
1042
|
};
|
|
540
|
-
function
|
|
1043
|
+
function isChangeKind(kind) {
|
|
1044
|
+
return typeof kind === "string" && Object.prototype.hasOwnProperty.call(KIND_INFO, kind);
|
|
1045
|
+
}
|
|
1046
|
+
function summaryText(counts, t = zhTranslate) {
|
|
541
1047
|
const parts = [];
|
|
542
1048
|
for (const kind of Object.keys(KIND_INFO)) {
|
|
543
|
-
if (counts[kind] > 0) parts.push(KIND_INFO[kind].
|
|
1049
|
+
if (counts[kind] > 0) parts.push(t(KIND_INFO[kind].labelKey) + " " + counts[kind]);
|
|
544
1050
|
}
|
|
545
1051
|
return parts.join(" \xB7 ");
|
|
546
1052
|
}
|
|
547
1053
|
function buildRecallNode(React, util, ctx, sessionsSvc, workspacesSvc, uiWorkspaceSvc) {
|
|
548
|
-
const { api, ensureInit, showThrottledToast, writeClipboard, clockText: clockText2, pluginConfig, messageFor } = util;
|
|
1054
|
+
const { api, ensureInit, showThrottledToast, writeClipboard, clockText: clockText2, pluginConfig, messageFor, t } = util;
|
|
549
1055
|
function CopyIcon() {
|
|
550
1056
|
return React.createElement(
|
|
551
1057
|
"svg",
|
|
@@ -578,19 +1084,19 @@
|
|
|
578
1084
|
return React.createElement(
|
|
579
1085
|
"div",
|
|
580
1086
|
{ className: "dsh-recall-panel" },
|
|
581
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1087
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.loading"))
|
|
582
1088
|
);
|
|
583
1089
|
}
|
|
584
1090
|
if (recall.stage === "error") {
|
|
585
1091
|
return React.createElement(
|
|
586
1092
|
"div",
|
|
587
1093
|
{ className: "dsh-recall-panel" },
|
|
588
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1094
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.error.title")),
|
|
589
1095
|
React.createElement("div", { className: "dsh-recall-panel-note" }, recall.message || ""),
|
|
590
1096
|
React.createElement(
|
|
591
1097
|
"div",
|
|
592
1098
|
{ className: "dsh-recall-panel-actions" },
|
|
593
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, "
|
|
1099
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, t("common.close"))
|
|
594
1100
|
)
|
|
595
1101
|
);
|
|
596
1102
|
}
|
|
@@ -599,14 +1105,14 @@
|
|
|
599
1105
|
const total = typeof recall.total === "number" ? recall.total : changes.length;
|
|
600
1106
|
const counts = { modified: 0, restored: 0, added: 0 };
|
|
601
1107
|
for (const c of changes) {
|
|
602
|
-
if (c &&
|
|
1108
|
+
if (c && isChangeKind(c.kind)) counts[c.kind]++;
|
|
603
1109
|
}
|
|
604
1110
|
const rows = changes.map((c, i) => {
|
|
605
1111
|
const info = KIND_INFO[c.kind];
|
|
606
1112
|
return React.createElement(
|
|
607
1113
|
"div",
|
|
608
1114
|
{ className: "dsh-recall-file", key: i },
|
|
609
|
-
React.createElement("span", { className: "dsh-recall-badge dsh-recall-badge-" + (info ? info.cls : "") }, info ? info.
|
|
1115
|
+
React.createElement("span", { className: "dsh-recall-badge dsh-recall-badge-" + (info ? info.cls : "") }, info ? t(info.labelKey) : c.kind || ""),
|
|
610
1116
|
React.createElement("span", { className: "dsh-recall-rel" }, c.rel || "")
|
|
611
1117
|
);
|
|
612
1118
|
});
|
|
@@ -616,30 +1122,36 @@
|
|
|
616
1122
|
rows.push(React.createElement(
|
|
617
1123
|
"div",
|
|
618
1124
|
{ className: "dsh-recall-panel-note", key: "truncated" },
|
|
619
|
-
sessionOnly ? "
|
|
1125
|
+
t(sessionOnly ? "recall.truncated.chat" : "recall.truncated.files", { shown: changes.length, total })
|
|
620
1126
|
));
|
|
621
1127
|
}
|
|
1128
|
+
const summary = summaryText(counts, t);
|
|
1129
|
+
const rollbackLead = recall.time ? t("recall.confirm.rollbackAt", { time: clockText2(recall.time) }) : t("recall.confirm.rollbackNoTime");
|
|
1130
|
+
const filesLead = t("recall.confirm.files", {
|
|
1131
|
+
total,
|
|
1132
|
+
summary: summary ? t("common.paren", { text: summary }) : ""
|
|
1133
|
+
});
|
|
622
1134
|
return React.createElement(
|
|
623
1135
|
"div",
|
|
624
1136
|
{ className: "dsh-recall-panel" },
|
|
625
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1137
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.confirm.title")),
|
|
626
1138
|
sessionOnly ? (
|
|
627
1139
|
// session-only:零文件改动即无不可逆操作缺口——安全快照预告随之隐藏
|
|
628
1140
|
//(不打安全快照),主说明换成模式语义
|
|
629
1141
|
React.createElement(
|
|
630
1142
|
"div",
|
|
631
1143
|
{ className: "dsh-recall-panel-note" },
|
|
632
|
-
"
|
|
1144
|
+
t("recall.confirm.sessionOnly")
|
|
633
1145
|
)
|
|
634
1146
|
) : React.createElement(
|
|
635
1147
|
"div",
|
|
636
1148
|
{ className: "dsh-recall-panel-note" },
|
|
637
|
-
|
|
1149
|
+
rollbackLead + filesLead
|
|
638
1150
|
),
|
|
639
1151
|
sessionOnly ? null : React.createElement(
|
|
640
1152
|
"div",
|
|
641
1153
|
{ className: "dsh-recall-panel-note" },
|
|
642
|
-
canRevertChat ? "
|
|
1154
|
+
t(canRevertChat ? "recall.confirm.chatBoth" : "recall.confirm.chatFirst")
|
|
643
1155
|
),
|
|
644
1156
|
sessionOnly && changes.length > 0 ? (
|
|
645
1157
|
// 文件清单保留展示但降级为参考语义:清单照常算(Host preview 链路
|
|
@@ -647,7 +1159,7 @@
|
|
|
647
1159
|
React.createElement(
|
|
648
1160
|
"div",
|
|
649
1161
|
{ className: "dsh-recall-panel-note", key: "ref-note" },
|
|
650
|
-
"
|
|
1162
|
+
t("recall.confirm.refNote")
|
|
651
1163
|
)
|
|
652
1164
|
) : null,
|
|
653
1165
|
changes.length > 0 ? React.createElement("div", { className: "dsh-recall-list" }, ...rows) : null,
|
|
@@ -656,26 +1168,26 @@
|
|
|
656
1168
|
// 默认 both 与现状一致,面板每次打开都复位到默认起点
|
|
657
1169
|
React.createElement(
|
|
658
1170
|
"div",
|
|
659
|
-
{ className: "dsh-recall-scope", role: "radiogroup", "aria-label": "
|
|
1171
|
+
{ className: "dsh-recall-scope", role: "radiogroup", "aria-label": t("recall.scope.aria") },
|
|
660
1172
|
React.createElement(
|
|
661
1173
|
"label",
|
|
662
1174
|
{ className: "dsh-recall-scope-item" },
|
|
663
1175
|
React.createElement("input", { type: "radio", name: "dsh-recall-scope", checked: scope === "both", onChange: () => onScopeChange("both") }),
|
|
664
|
-
React.createElement("span", { className: "dsh-recall-scope-label" }, "
|
|
1176
|
+
React.createElement("span", { className: "dsh-recall-scope-label" }, t("recall.scope.both"))
|
|
665
1177
|
),
|
|
666
1178
|
React.createElement(
|
|
667
1179
|
"label",
|
|
668
1180
|
{ className: "dsh-recall-scope-item" },
|
|
669
1181
|
React.createElement("input", { type: "radio", name: "dsh-recall-scope", checked: scope === "session-only", onChange: () => onScopeChange("session-only") }),
|
|
670
|
-
React.createElement("span", { className: "dsh-recall-scope-label" }, "
|
|
1182
|
+
React.createElement("span", { className: "dsh-recall-scope-label" }, t("recall.scope.sessionOnly"))
|
|
671
1183
|
)
|
|
672
1184
|
)
|
|
673
1185
|
) : null,
|
|
674
1186
|
React.createElement(
|
|
675
1187
|
"div",
|
|
676
1188
|
{ className: "dsh-recall-panel-actions" },
|
|
677
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, "
|
|
678
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-danger", onClick: executeRecall }, sessionOnly ? "
|
|
1189
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, t("common.cancel")),
|
|
1190
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-danger", onClick: executeRecall }, t(sessionOnly ? "recall.confirm.submit.sessionOnly" : "recall.confirm.submit.both"))
|
|
679
1191
|
)
|
|
680
1192
|
);
|
|
681
1193
|
}
|
|
@@ -683,26 +1195,26 @@
|
|
|
683
1195
|
return React.createElement(
|
|
684
1196
|
"div",
|
|
685
1197
|
{ className: "dsh-recall-panel" },
|
|
686
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, scope === "session-only" ? "
|
|
1198
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t(scope === "session-only" ? "recall.executing.sessionOnly" : "recall.executing.both"))
|
|
687
1199
|
);
|
|
688
1200
|
}
|
|
689
1201
|
if (recall.stage === "done") {
|
|
690
1202
|
return React.createElement(
|
|
691
1203
|
"div",
|
|
692
1204
|
{ className: "dsh-recall-panel" },
|
|
693
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1205
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.done.title")),
|
|
694
1206
|
React.createElement(
|
|
695
1207
|
"div",
|
|
696
1208
|
{ className: "dsh-recall-panel-note" },
|
|
697
1209
|
scope === "session-only" ? (
|
|
698
1210
|
// session-only 文案矩阵:文件侧零改动是确定事实,失败时也只描述对话侧
|
|
699
|
-
recall.chatReverted ? "
|
|
700
|
-
) : recall.chatReverted ? "
|
|
1211
|
+
recall.chatReverted ? t("recall.done.sessionOnly.ok") : t("recall.done.sessionOnly.fail", { error: recall.chatError || t("common.unknownReason") })
|
|
1212
|
+
) : recall.chatReverted ? t("recall.done.both.ok") : t("recall.done.both.partial") + (recall.chatError ? t("recall.done.both.failChat", { error: recall.chatError }) : "")
|
|
701
1213
|
),
|
|
702
1214
|
React.createElement(
|
|
703
1215
|
"div",
|
|
704
1216
|
{ className: "dsh-recall-panel-actions" },
|
|
705
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, "
|
|
1217
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, t("common.close"))
|
|
706
1218
|
)
|
|
707
1219
|
);
|
|
708
1220
|
}
|
|
@@ -809,8 +1321,8 @@
|
|
|
809
1321
|
} catch (error) {
|
|
810
1322
|
}
|
|
811
1323
|
if (Date.now() >= deadline) {
|
|
812
|
-
|
|
813
|
-
showThrottledToast("
|
|
1324
|
+
recallNodeLog.warn("\u6B8B\u7559\u6392\u961F\u6D88\u606F\u672A\u80FD\u81EA\u52A8\u6E05\u7406\uFF08\u4F1A\u8BDD\u9762\u672A\u5C31\u7EEA\uFF09\uFF1A", itemIds);
|
|
1325
|
+
showThrottledToast(t("toast.staleQueue"));
|
|
814
1326
|
return;
|
|
815
1327
|
}
|
|
816
1328
|
setTimeout(attempt, 250);
|
|
@@ -849,18 +1361,21 @@
|
|
|
849
1361
|
if (!alive) return;
|
|
850
1362
|
if (res && res.has) {
|
|
851
1363
|
if (recent && Array.isArray(res.skipped) && res.skipped.length) {
|
|
852
|
-
const names = res.skipped.slice(0, 5).join("
|
|
853
|
-
showThrottledToast("
|
|
1364
|
+
const names = res.skipped.slice(0, 5).join(t("common.listSep")) + (res.skipped.length > 5 ? t("toast.skippedMore", { n: res.skipped.length }) : "");
|
|
1365
|
+
showThrottledToast(t("toast.skipped", { names }));
|
|
854
1366
|
}
|
|
855
1367
|
setHasSnapshot(true);
|
|
856
1368
|
return;
|
|
857
1369
|
}
|
|
858
1370
|
if (res && res.failed) {
|
|
859
|
-
if (recent) showThrottledToast("
|
|
1371
|
+
if (recent) showThrottledToast(t("toast.snapshotFailed", { error: String(res.error || t("common.unknownReason")).slice(0, 140) }));
|
|
860
1372
|
return;
|
|
861
1373
|
}
|
|
862
|
-
if (res && res.notice) {
|
|
863
|
-
if (recent)
|
|
1374
|
+
if (res && (res.artifactSeg || res.notice)) {
|
|
1375
|
+
if (recent) {
|
|
1376
|
+
const text2 = res.artifactSeg ? t("notice.buildRoot", { seg: res.artifactSeg }) : String(res.notice).slice(0, 140);
|
|
1377
|
+
showThrottledToast(text2);
|
|
1378
|
+
}
|
|
864
1379
|
return;
|
|
865
1380
|
}
|
|
866
1381
|
if (recent && attempts < MAX_ATTEMPTS) timer = setTimeout(schedule, RETRY_MS);
|
|
@@ -897,7 +1412,7 @@
|
|
|
897
1412
|
setRecall({ stage: "loading" });
|
|
898
1413
|
api("preview", { messageId, sessionId }).then((res) => {
|
|
899
1414
|
if (!res || !res.ok) {
|
|
900
|
-
setRecall({ stage: "error", message: messageFor(res, "
|
|
1415
|
+
setRecall({ stage: "error", message: messageFor(res, t("fallback.preview")) });
|
|
901
1416
|
return;
|
|
902
1417
|
}
|
|
903
1418
|
setRecall({
|
|
@@ -926,7 +1441,7 @@
|
|
|
926
1441
|
setRecall({ stage: "loading" });
|
|
927
1442
|
api("preview", { messageId, sessionId }).then((res2) => {
|
|
928
1443
|
if (!res2 || !res2.ok) {
|
|
929
|
-
setRecall({ stage: "error", message: messageFor(res2, "
|
|
1444
|
+
setRecall({ stage: "error", message: messageFor(res2, t("fallback.preview")) });
|
|
930
1445
|
return;
|
|
931
1446
|
}
|
|
932
1447
|
setRecall({
|
|
@@ -943,7 +1458,7 @@
|
|
|
943
1458
|
});
|
|
944
1459
|
return;
|
|
945
1460
|
}
|
|
946
|
-
setRecall({ stage: "error", message: messageFor(res, "
|
|
1461
|
+
setRecall({ stage: "error", message: messageFor(res, t("fallback.rollback")) });
|
|
947
1462
|
return;
|
|
948
1463
|
}
|
|
949
1464
|
const cutSeq = typeof res.cutSeq === "number" ? res.cutSeq : previewCut;
|
|
@@ -963,11 +1478,11 @@
|
|
|
963
1478
|
});
|
|
964
1479
|
if (pluginConfig.archiveOriginal && workspacesSvc && typeof workspacesSvc.archiveSession === "function") {
|
|
965
1480
|
workspacesSvc.archiveSession(sessionId, { stopActivity: true }).catch((error) => {
|
|
966
|
-
|
|
1481
|
+
recallNodeLog.warn("archive original session failed:", error);
|
|
967
1482
|
});
|
|
968
1483
|
}
|
|
969
1484
|
} else {
|
|
970
|
-
chatError = "
|
|
1485
|
+
chatError = t("recall.chat.noChild");
|
|
971
1486
|
}
|
|
972
1487
|
} catch (error) {
|
|
973
1488
|
chatError = String(error);
|
|
@@ -975,7 +1490,7 @@
|
|
|
975
1490
|
}
|
|
976
1491
|
if (pluginConfig.refillDraft) {
|
|
977
1492
|
fillDraft(fillTarget, text, attachmentFiles);
|
|
978
|
-
if (fileBlocks.length > 0) showThrottledToast("
|
|
1493
|
+
if (fileBlocks.length > 0) showThrottledToast(t("toast.fileAttachRefill"));
|
|
979
1494
|
}
|
|
980
1495
|
setHasSnapshot(false);
|
|
981
1496
|
setRecall({ stage: "done", count: typeof res.count === "number" ? res.count : changes.length, chatReverted, chatError });
|
|
@@ -997,7 +1512,7 @@
|
|
|
997
1512
|
));
|
|
998
1513
|
}
|
|
999
1514
|
for (let i = 0; i < fileBlocks.length; i++) {
|
|
1000
|
-
const info = fileCardInfo(fileBlocks[i]);
|
|
1515
|
+
const info = fileCardInfo(fileBlocks[i], t);
|
|
1001
1516
|
if (!info) continue;
|
|
1002
1517
|
bubbleChildren.push(React.createElement(
|
|
1003
1518
|
"div",
|
|
@@ -1021,8 +1536,8 @@
|
|
|
1021
1536
|
key: "copy",
|
|
1022
1537
|
type: "button",
|
|
1023
1538
|
className: "dsh-recall-action",
|
|
1024
|
-
"aria-label": copied ? "
|
|
1025
|
-
title: copied ? "
|
|
1539
|
+
"aria-label": copied ? t("action.copied") : t("action.copy"),
|
|
1540
|
+
title: copied ? t("action.copied") : t("action.copy"),
|
|
1026
1541
|
onClick: onCopy
|
|
1027
1542
|
}, copied ? React.createElement(CheckIcon, {}) : React.createElement(CopyIcon, {})));
|
|
1028
1543
|
if (hasSnapshot) {
|
|
@@ -1030,8 +1545,8 @@
|
|
|
1030
1545
|
key: "recall",
|
|
1031
1546
|
type: "button",
|
|
1032
1547
|
className: "dsh-recall-action",
|
|
1033
|
-
"aria-label": "
|
|
1034
|
-
title: "
|
|
1548
|
+
"aria-label": t("action.recall"),
|
|
1549
|
+
title: t("action.recall.title"),
|
|
1035
1550
|
onClick: openPreview
|
|
1036
1551
|
}, React.createElement(UndoIcon, {})));
|
|
1037
1552
|
}
|
|
@@ -1048,8 +1563,8 @@
|
|
|
1048
1563
|
|
|
1049
1564
|
// src/client/config-card.ts
|
|
1050
1565
|
function buildConfigForm(React, util, SectionToggle) {
|
|
1051
|
-
const { api, bytesToMb: bytesToMb2 } = util;
|
|
1052
|
-
function ConfigForm() {
|
|
1566
|
+
const { api, bytesToMb: bytesToMb2, t, setLocalePref } = util;
|
|
1567
|
+
function ConfigForm(props) {
|
|
1053
1568
|
const [baseline, setBaseline] = React.useState(null);
|
|
1054
1569
|
const [draft, setDraft] = React.useState(null);
|
|
1055
1570
|
const [envLocks, setEnvLocks] = React.useState({});
|
|
@@ -1071,15 +1586,18 @@
|
|
|
1071
1586
|
refillDraft: v.refillDraft !== false,
|
|
1072
1587
|
snapshotEnabled: v.snapshotEnabled !== false,
|
|
1073
1588
|
archiveOriginal: v.archiveOriginal !== false,
|
|
1074
|
-
retentionDays: String(v.retentionDays == null ? "" : v.retentionDays)
|
|
1589
|
+
retentionDays: String(v.retentionDays == null ? "" : v.retentionDays),
|
|
1590
|
+
locale: typeof v.locale === "string" && v.locale ? v.locale : "auto"
|
|
1075
1591
|
};
|
|
1592
|
+
setLocalePref(next.locale);
|
|
1593
|
+
if (props.onLocaleApplied) props.onLocaleApplied();
|
|
1076
1594
|
setDraft(next);
|
|
1077
1595
|
setBaseline(next);
|
|
1078
1596
|
setEnvLocks(res.envLocks || {});
|
|
1079
1597
|
setOverridden(res.overridden || {});
|
|
1080
1598
|
setWritable(res.writable !== false);
|
|
1081
1599
|
} else {
|
|
1082
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1600
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("config.msg.loadFailed"), error: true });
|
|
1083
1601
|
}
|
|
1084
1602
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1085
1603
|
}
|
|
@@ -1092,18 +1610,19 @@
|
|
|
1092
1610
|
function save() {
|
|
1093
1611
|
if (state.busy || !draft || !baseline) return;
|
|
1094
1612
|
const patch = {};
|
|
1095
|
-
for (const key of ["gcSnaps", "gcHours", "maxFileBytes", "maxSnapshotsPerWorkspace", "baseExcludes", "refillDraft", "snapshotEnabled", "archiveOriginal", "retentionDays"]) {
|
|
1096
|
-
|
|
1613
|
+
for (const key of ["gcSnaps", "gcHours", "maxFileBytes", "maxSnapshotsPerWorkspace", "baseExcludes", "refillDraft", "snapshotEnabled", "archiveOriginal", "retentionDays", "locale"]) {
|
|
1614
|
+
const value = draft[key];
|
|
1615
|
+
if (value !== void 0 && value !== baseline[key]) patch[key] = value;
|
|
1097
1616
|
}
|
|
1098
1617
|
if (!Object.keys(patch).length) {
|
|
1099
|
-
setState({ busy: false, message: "
|
|
1618
|
+
setState({ busy: false, message: t("config.msg.noChange"), error: false });
|
|
1100
1619
|
return;
|
|
1101
1620
|
}
|
|
1102
1621
|
const clean = {};
|
|
1103
1622
|
if (patch.gcSnaps !== void 0) {
|
|
1104
1623
|
const n = parseInt(String(patch.gcSnaps), 10);
|
|
1105
1624
|
if (!Number.isFinite(n) || n < 1) {
|
|
1106
|
-
setState({ busy: false, message: "
|
|
1625
|
+
setState({ busy: false, message: t("config.err.gcSnaps"), error: true });
|
|
1107
1626
|
return;
|
|
1108
1627
|
}
|
|
1109
1628
|
clean.gcSnaps = n;
|
|
@@ -1111,7 +1630,7 @@
|
|
|
1111
1630
|
if (patch.gcHours !== void 0) {
|
|
1112
1631
|
const n = parseInt(String(patch.gcHours), 10);
|
|
1113
1632
|
if (!Number.isFinite(n) || n < 1) {
|
|
1114
|
-
setState({ busy: false, message: "
|
|
1633
|
+
setState({ busy: false, message: t("config.err.gcHours"), error: true });
|
|
1115
1634
|
return;
|
|
1116
1635
|
}
|
|
1117
1636
|
clean.gcHours = n;
|
|
@@ -1119,7 +1638,7 @@
|
|
|
1119
1638
|
if (patch.maxFileBytes !== void 0) {
|
|
1120
1639
|
const mb = Number(patch.maxFileBytes);
|
|
1121
1640
|
if (!Number.isFinite(mb) || mb < 0.01) {
|
|
1122
|
-
setState({ busy: false, message: "
|
|
1641
|
+
setState({ busy: false, message: t("config.err.maxFileBytes"), error: true });
|
|
1123
1642
|
return;
|
|
1124
1643
|
}
|
|
1125
1644
|
clean.maxFileBytes = Math.round(mb * 1048576);
|
|
@@ -1127,7 +1646,7 @@
|
|
|
1127
1646
|
if (patch.maxSnapshotsPerWorkspace !== void 0) {
|
|
1128
1647
|
const n = parseInt(String(patch.maxSnapshotsPerWorkspace), 10);
|
|
1129
1648
|
if (!Number.isFinite(n) || n < 0) {
|
|
1130
|
-
setState({ busy: false, message: "
|
|
1649
|
+
setState({ busy: false, message: t("config.err.maxSnapshots"), error: true });
|
|
1131
1650
|
return;
|
|
1132
1651
|
}
|
|
1133
1652
|
clean.maxSnapshotsPerWorkspace = n;
|
|
@@ -1138,21 +1657,30 @@
|
|
|
1138
1657
|
if (patch.retentionDays !== void 0) {
|
|
1139
1658
|
const n = parseInt(String(patch.retentionDays), 10);
|
|
1140
1659
|
if (!Number.isFinite(n) || n < 0) {
|
|
1141
|
-
setState({ busy: false, message: "
|
|
1660
|
+
setState({ busy: false, message: t("config.err.retentionDays"), error: true });
|
|
1142
1661
|
return;
|
|
1143
1662
|
}
|
|
1144
1663
|
clean.retentionDays = n;
|
|
1145
1664
|
}
|
|
1665
|
+
if (patch.locale !== void 0) {
|
|
1666
|
+
const v = String(patch.locale);
|
|
1667
|
+
if (v !== "auto" && v !== "zh" && v !== "en") {
|
|
1668
|
+
setState({ busy: false, message: t("config.err.locale"), error: true });
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
clean.locale = v;
|
|
1672
|
+
}
|
|
1146
1673
|
if (patch.baseExcludes !== void 0) {
|
|
1147
1674
|
clean.baseExcludes = String(patch.baseExcludes).split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1148
1675
|
}
|
|
1149
|
-
setState({ busy: true, message: "
|
|
1676
|
+
setState({ busy: true, message: t("config.msg.saving"), error: false });
|
|
1150
1677
|
api("config-set", { patch: clean }).then((res) => {
|
|
1151
1678
|
if (res && res.ok) {
|
|
1152
|
-
|
|
1679
|
+
if (clean.locale !== void 0) setLocalePref(clean.locale);
|
|
1680
|
+
setState({ busy: false, message: t("config.msg.saved"), error: false });
|
|
1153
1681
|
load();
|
|
1154
1682
|
} else {
|
|
1155
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1683
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("config.msg.saveFailed"), error: true });
|
|
1156
1684
|
}
|
|
1157
1685
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1158
1686
|
}
|
|
@@ -1184,10 +1712,10 @@
|
|
|
1184
1712
|
step: opts && opts.step,
|
|
1185
1713
|
onChange: (e) => edit(key, e.target.value)
|
|
1186
1714
|
}),
|
|
1187
|
-
opts && opts.
|
|
1188
|
-
changed && !locked ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, "
|
|
1189
|
-
overridden && overridden[key] !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, "
|
|
1190
|
-
locked ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-locked" }, "
|
|
1715
|
+
opts && opts.suffixKey ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t(opts.suffixKey)) : null,
|
|
1716
|
+
changed && !locked ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1717
|
+
overridden && overridden[key] !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null,
|
|
1718
|
+
locked ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-locked" }, t("config.tag.locked")) : null
|
|
1191
1719
|
),
|
|
1192
1720
|
React.createElement("div", { className: "dsh-recall-cfg-hint" }, hint)
|
|
1193
1721
|
);
|
|
@@ -1216,26 +1744,55 @@
|
|
|
1216
1744
|
disabled: !writable,
|
|
1217
1745
|
onClick: () => edit(key, !on)
|
|
1218
1746
|
}, React.createElement("span", { className: "dsh-recall-cfg-switch-thumb" })),
|
|
1219
|
-
changed ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, "
|
|
1220
|
-
overridden && overridden[key] !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, "
|
|
1747
|
+
changed ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1748
|
+
overridden && overridden[key] !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null
|
|
1221
1749
|
),
|
|
1222
1750
|
React.createElement("div", { className: "dsh-recall-cfg-hint" }, hint)
|
|
1223
1751
|
);
|
|
1224
1752
|
}
|
|
1753
|
+
function localeRow() {
|
|
1754
|
+
const changed = Boolean(draft && baseline && draft.locale !== baseline.locale);
|
|
1755
|
+
return React.createElement(
|
|
1756
|
+
"div",
|
|
1757
|
+
{ className: "dsh-recall-cfg-row", key: "locale" },
|
|
1758
|
+
React.createElement("span", { className: "dsh-recall-cfg-label", id: "dsh-recall-cfg-label-locale" }, t("config.locale.label")),
|
|
1759
|
+
React.createElement(
|
|
1760
|
+
"div",
|
|
1761
|
+
{ className: "dsh-recall-cfg-line" },
|
|
1762
|
+
React.createElement(
|
|
1763
|
+
"select",
|
|
1764
|
+
{
|
|
1765
|
+
id: "dsh-recall-cfg-locale",
|
|
1766
|
+
"aria-labelledby": "dsh-recall-cfg-label-locale",
|
|
1767
|
+
className: "dsh-recall-cfg-select",
|
|
1768
|
+
value: draft ? draft.locale : "auto",
|
|
1769
|
+
disabled: !writable,
|
|
1770
|
+
onChange: (e) => edit("locale", e.target.value)
|
|
1771
|
+
},
|
|
1772
|
+
React.createElement("option", { value: "auto" }, t("config.locale.auto")),
|
|
1773
|
+
React.createElement("option", { value: "zh" }, "\u4E2D\u6587"),
|
|
1774
|
+
React.createElement("option", { value: "en" }, "English")
|
|
1775
|
+
),
|
|
1776
|
+
changed ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1777
|
+
overridden && overridden.locale !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null
|
|
1778
|
+
),
|
|
1779
|
+
React.createElement("div", { className: "dsh-recall-cfg-hint" }, t("config.locale.hint"))
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1225
1782
|
function resetDefaults() {
|
|
1226
1783
|
if (state.busy || !writable) return;
|
|
1227
|
-
setState({ busy: true, message: "
|
|
1784
|
+
setState({ busy: true, message: t("config.msg.resetting"), error: false });
|
|
1228
1785
|
api("config-reset", {}).then((res) => {
|
|
1229
1786
|
if (res && res.ok) {
|
|
1230
1787
|
load();
|
|
1231
|
-
setState({ busy: false, message: "
|
|
1788
|
+
setState({ busy: false, message: t("config.msg.resetDone"), error: false });
|
|
1232
1789
|
} else {
|
|
1233
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1790
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("config.msg.resetFailed"), error: true });
|
|
1234
1791
|
}
|
|
1235
1792
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1236
1793
|
}
|
|
1237
1794
|
if (!draft || !baseline) {
|
|
1238
|
-
return React.createElement("div", { className: "dsh-recall-ex-note" }, state.message || "
|
|
1795
|
+
return React.createElement("div", { className: "dsh-recall-ex-note" }, state.message || t("config.msg.loading"));
|
|
1239
1796
|
}
|
|
1240
1797
|
return React.createElement(
|
|
1241
1798
|
"div",
|
|
@@ -1247,52 +1804,56 @@
|
|
|
1247
1804
|
React.createElement(
|
|
1248
1805
|
"div",
|
|
1249
1806
|
{ className: "dsh-recall-cfg-grid" },
|
|
1250
|
-
// V5 表单分组:9 字段平铺 → 「快照行为 /
|
|
1251
|
-
//
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
boolRow("
|
|
1255
|
-
boolRow("
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
numRow("
|
|
1261
|
-
numRow("
|
|
1262
|
-
numRow("
|
|
1807
|
+
// V5 表单分组:9 字段平铺 → 「快照行为 / 自动治理 / 界面」三组语义分组
|
|
1808
|
+
// 小标题,降低认知负担;「高级:基础排除表」沿用 SectionToggle 折叠,
|
|
1809
|
+
// 不重复加标题。
|
|
1810
|
+
React.createElement("div", { className: "dsh-recall-cfg-group" }, t("config.group.snapshot")),
|
|
1811
|
+
boolRow("snapshotEnabled", t("config.snapshotEnabled.label"), t("config.snapshotEnabled.hint")),
|
|
1812
|
+
boolRow("refillDraft", t("config.refillDraft.label"), t("config.refillDraft.hint")),
|
|
1813
|
+
boolRow("archiveOriginal", t("config.archiveOriginal.label"), t("config.archiveOriginal.hint")),
|
|
1814
|
+
React.createElement("div", { className: "dsh-recall-cfg-group" }, t("config.group.auto")),
|
|
1815
|
+
// 单位后缀统一挂输入框右侧(与状态标签同基线),不再只藏在说明文字里;
|
|
1816
|
+
// 传词表键而非字面量——单位词也是语言事实(条/小时 vs snapshots/hours)
|
|
1817
|
+
numRow("gcSnaps", t("config.gcSnaps.label"), t("config.gcSnaps.hint"), { suffixKey: "config.gcSnaps.suffix", min: 1, step: 1 }),
|
|
1818
|
+
numRow("gcHours", t("config.gcHours.label"), t("config.gcHours.hint"), { suffixKey: "config.gcHours.suffix", min: 1, step: 1 }),
|
|
1819
|
+
numRow("maxFileBytes", t("config.maxFileBytes.label"), t("config.maxFileBytes.hint"), { suffixKey: "config.maxFileBytes.suffix", min: 0.01, step: 0.5 }),
|
|
1820
|
+
numRow("maxSnapshotsPerWorkspace", t("config.maxSnapshotsPerWorkspace.label"), t("config.maxSnapshotsPerWorkspace.hint"), { suffixKey: "config.maxSnapshotsPerWorkspace.suffix", min: 0, step: 1 }),
|
|
1821
|
+
numRow("retentionDays", t("config.retentionDays.label"), t("config.retentionDays.hint"), { suffixKey: "config.retentionDays.suffix", min: 0, step: 1 }),
|
|
1822
|
+
React.createElement("div", { className: "dsh-recall-cfg-group" }, t("config.group.ui")),
|
|
1823
|
+
localeRow(),
|
|
1263
1824
|
// 操作区在「基础排除表」折叠头之前:按钮服务整个表单(含折叠区之外的字段),
|
|
1264
1825
|
// 排在折叠头之后会被误读为折叠区内容、折叠时像漏收起(用户实测反馈);
|
|
1265
1826
|
// 因此也不把按钮藏进折叠分支——否则折叠基础排除表后将无法保存。
|
|
1266
1827
|
React.createElement(
|
|
1267
1828
|
"div",
|
|
1268
1829
|
{ className: "dsh-recall-panel-actions" },
|
|
1269
|
-
state.message ? React.createElement("span", { role: "status", "aria-live": "polite", className: "dsh-recall-ex-status" + (state.error ? " dsh-recall-ex-status-error" : " dsh-recall-ex-status-success") }, (state.error ? "
|
|
1270
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy || !writable, onClick: () => setDraft(baseline ? Object.assign({}, baseline) : null) }, "
|
|
1830
|
+
state.message ? React.createElement("span", { role: "status", "aria-live": "polite", className: "dsh-recall-ex-status" + (state.error ? " dsh-recall-ex-status-error" : " dsh-recall-ex-status-success") }, (state.error ? t("common.errorPrefix") : "") + state.message) : null,
|
|
1831
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy || !writable, onClick: () => setDraft(baseline ? Object.assign({}, baseline) : null) }, t("common.discard")),
|
|
1271
1832
|
React.createElement("button", {
|
|
1272
1833
|
type: "button",
|
|
1273
1834
|
className: "dsh-recall-btn",
|
|
1274
1835
|
disabled: state.busy || !writable,
|
|
1275
|
-
title: "
|
|
1836
|
+
title: t("config.reset.title"),
|
|
1276
1837
|
onClick: resetDefaults
|
|
1277
|
-
}, "
|
|
1838
|
+
}, t("config.reset")),
|
|
1278
1839
|
// 「保存」是表单唯一主动作,升主色实心按钮;放弃修改/恢复默认维持次级
|
|
1279
1840
|
// 灰底(同排按钮只有一个视觉焦点,配色令牌经官方主题产物核验,见 css.ts)
|
|
1280
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: state.busy || !writable, onClick: save }, "
|
|
1281
|
-
!writable ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, "
|
|
1841
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: state.busy || !writable, onClick: save }, t("common.save")),
|
|
1842
|
+
!writable ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.readonly")) : null
|
|
1282
1843
|
),
|
|
1283
1844
|
// divider:本条是折叠分区列表的首项,上方分界线把「字段表单」与「折叠
|
|
1284
1845
|
// 分区」分开(否则保存按钮行紧贴折叠头,读起来像同一组字段)
|
|
1285
|
-
React.createElement(SectionToggle, { title: "
|
|
1846
|
+
React.createElement(SectionToggle, { title: t("config.advanced.title"), open: showAdvanced, onToggle: () => setShowAdvanced((v) => !v), divider: true }),
|
|
1286
1847
|
showAdvanced ? React.createElement(
|
|
1287
1848
|
"div",
|
|
1288
1849
|
{ className: "dsh-recall-cfg-row", key: "baseExcludes" },
|
|
1289
1850
|
// V4:标签上提(与 numRow 同法);cfg-line 只剩 tags,textarea/hint 通栏
|
|
1290
|
-
React.createElement("span", { className: "dsh-recall-cfg-label", id: "dsh-recall-cfg-label-baseExcludes" }, "
|
|
1851
|
+
React.createElement("span", { className: "dsh-recall-cfg-label", id: "dsh-recall-cfg-label-baseExcludes" }, t("config.baseExcludes.label")),
|
|
1291
1852
|
React.createElement(
|
|
1292
1853
|
"div",
|
|
1293
1854
|
{ className: "dsh-recall-cfg-line" },
|
|
1294
|
-
draft.baseExcludes !== baseline.baseExcludes ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, "
|
|
1295
|
-
overridden && overridden.baseExcludes !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, "
|
|
1855
|
+
draft.baseExcludes !== baseline.baseExcludes ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1856
|
+
overridden && overridden.baseExcludes !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null
|
|
1296
1857
|
),
|
|
1297
1858
|
// textarea/hint 加 cfg-span 通栏:折叠区在共享 grid 内展开时,内容若只占
|
|
1298
1859
|
// 第二列,左侧标签列会成为竖直死区(实测 textarea 被挤窄);通栏后与
|
|
@@ -1307,7 +1868,7 @@
|
|
|
1307
1868
|
disabled: !writable,
|
|
1308
1869
|
onChange: (e) => edit("baseExcludes", e.target.value)
|
|
1309
1870
|
}),
|
|
1310
|
-
React.createElement("div", { className: "dsh-recall-cfg-hint dsh-recall-cfg-span" }, "
|
|
1871
|
+
React.createElement("div", { className: "dsh-recall-cfg-hint dsh-recall-cfg-span" }, t("config.baseExcludes.hint"))
|
|
1311
1872
|
) : null
|
|
1312
1873
|
)
|
|
1313
1874
|
);
|
|
@@ -1318,7 +1879,7 @@
|
|
|
1318
1879
|
// src/client/exclude-card.ts
|
|
1319
1880
|
var EXCLUDE_SUGGESTIONS = ["dist/", "build/", "out/", "coverage/", "*.log", ".env"];
|
|
1320
1881
|
function buildExcludeCards(React, util) {
|
|
1321
|
-
const { api } = util;
|
|
1882
|
+
const { api, t } = util;
|
|
1322
1883
|
function ExcludeCard(props) {
|
|
1323
1884
|
const file = props.file;
|
|
1324
1885
|
const [draft, setDraft] = React.useState(file.content || "");
|
|
@@ -1331,20 +1892,20 @@
|
|
|
1331
1892
|
setDraft((d) => (d && !d.endsWith("\n") ? d + "\n" : d) + pattern + "\n");
|
|
1332
1893
|
}
|
|
1333
1894
|
function addQuick() {
|
|
1334
|
-
const
|
|
1335
|
-
if (!
|
|
1336
|
-
appendPattern(
|
|
1895
|
+
const pattern = quick.trim();
|
|
1896
|
+
if (!pattern) return;
|
|
1897
|
+
appendPattern(pattern);
|
|
1337
1898
|
setQuick("");
|
|
1338
1899
|
}
|
|
1339
1900
|
function save() {
|
|
1340
1901
|
if (state.busy || !dirty) return;
|
|
1341
|
-
setState({ busy: true, message: "
|
|
1902
|
+
setState({ busy: true, message: t("exclude.msg.saving"), error: false });
|
|
1342
1903
|
api("exclude-set", { path: file.path, content: draft }).then((res) => {
|
|
1343
1904
|
if (res && res.ok) {
|
|
1344
1905
|
setBaseline(draft);
|
|
1345
|
-
setState({ busy: false, message: "
|
|
1906
|
+
setState({ busy: false, message: t("exclude.msg.saved"), error: false });
|
|
1346
1907
|
} else {
|
|
1347
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1908
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("exclude.msg.saveFailed"), error: true });
|
|
1348
1909
|
}
|
|
1349
1910
|
}).catch((error) => {
|
|
1350
1911
|
setState({ busy: false, message: String(error), error: true });
|
|
@@ -1367,13 +1928,13 @@
|
|
|
1367
1928
|
React.createElement(
|
|
1368
1929
|
"div",
|
|
1369
1930
|
{ className: "dsh-recall-ex-note" },
|
|
1370
|
-
file.home ? "
|
|
1931
|
+
file.home ? t("exclude.note.home") : t("exclude.note.fallback")
|
|
1371
1932
|
),
|
|
1372
|
-
React.createElement("div", { className: "dsh-recall-ex-path" }, "
|
|
1373
|
-
React.createElement("div", { className: "dsh-recall-ex-note" }, "
|
|
1933
|
+
React.createElement("div", { className: "dsh-recall-ex-path" }, t("exclude.path", { path: file.path })),
|
|
1934
|
+
React.createElement("div", { className: "dsh-recall-ex-note" }, t("exclude.syntax")),
|
|
1374
1935
|
React.createElement("textarea", {
|
|
1375
1936
|
className: "dsh-recall-ex-area",
|
|
1376
|
-
"aria-label": "
|
|
1937
|
+
"aria-label": t("exclude.area.aria"),
|
|
1377
1938
|
value: draft,
|
|
1378
1939
|
spellCheck: false,
|
|
1379
1940
|
onChange: (e) => setDraft(e.target.value)
|
|
@@ -1384,8 +1945,8 @@
|
|
|
1384
1945
|
React.createElement("input", {
|
|
1385
1946
|
className: "dsh-recall-ex-input",
|
|
1386
1947
|
value: quick,
|
|
1387
|
-
placeholder: "
|
|
1388
|
-
"aria-label": "
|
|
1948
|
+
placeholder: t("exclude.quick.placeholder"),
|
|
1949
|
+
"aria-label": t("exclude.quick.aria"),
|
|
1389
1950
|
onChange: (e) => setQuick(e.target.value),
|
|
1390
1951
|
onKeyDown: (e) => {
|
|
1391
1952
|
if (e.key === "Enter") {
|
|
@@ -1394,23 +1955,23 @@
|
|
|
1394
1955
|
}
|
|
1395
1956
|
}
|
|
1396
1957
|
}),
|
|
1397
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: addQuick }, "
|
|
1958
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: addQuick }, t("exclude.add")),
|
|
1398
1959
|
...suggestions.map((s) => React.createElement("button", {
|
|
1399
1960
|
key: "chip-" + s,
|
|
1400
1961
|
type: "button",
|
|
1401
1962
|
className: "dsh-recall-ex-chip",
|
|
1402
|
-
title: "
|
|
1963
|
+
title: t("exclude.chip.title", { pattern: s }),
|
|
1403
1964
|
onClick: () => appendPattern(s)
|
|
1404
1965
|
}, s))
|
|
1405
1966
|
),
|
|
1406
1967
|
React.createElement(
|
|
1407
1968
|
"div",
|
|
1408
1969
|
{ className: "dsh-recall-panel-actions" },
|
|
1409
|
-
state.message ? React.createElement("span", { role: "status", "aria-live": "polite", className: "dsh-recall-ex-status" + (state.error ? " dsh-recall-ex-status-error" : " dsh-recall-ex-status-success") }, (state.error ? "
|
|
1410
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: !dirty || state.busy, onClick: discard }, "
|
|
1970
|
+
state.message ? React.createElement("span", { role: "status", "aria-live": "polite", className: "dsh-recall-ex-status" + (state.error ? " dsh-recall-ex-status-error" : " dsh-recall-ex-status-success") }, (state.error ? t("common.errorPrefix") : "") + state.message) : null,
|
|
1971
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: !dirty || state.busy, onClick: discard }, t("common.discard")),
|
|
1411
1972
|
// 保存升主色实心(与配置表单同一约定:每卡唯一主动作,对齐官方插件卡
|
|
1412
1973
|
// footer 的 discard 幽灵 + save 实心组合)
|
|
1413
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: !dirty || state.busy, onClick: save }, "
|
|
1974
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: !dirty || state.busy, onClick: save }, t("common.save"))
|
|
1414
1975
|
)
|
|
1415
1976
|
);
|
|
1416
1977
|
}
|
|
@@ -1425,10 +1986,10 @@
|
|
|
1425
1986
|
return;
|
|
1426
1987
|
}
|
|
1427
1988
|
if (res && res.unsupported) {
|
|
1428
|
-
setError("
|
|
1989
|
+
setError(t("exclude.unsupported"));
|
|
1429
1990
|
return;
|
|
1430
1991
|
}
|
|
1431
|
-
setError(res && (res.message || res.error) || "
|
|
1992
|
+
setError(res && (res.message || res.error) || t("exclude.loadFailed"));
|
|
1432
1993
|
}).catch((e) => setError(String(e)));
|
|
1433
1994
|
}
|
|
1434
1995
|
React.useEffect(() => {
|
|
@@ -1442,15 +2003,15 @@
|
|
|
1442
2003
|
React.createElement(
|
|
1443
2004
|
"div",
|
|
1444
2005
|
{ className: "dsh-recall-panel-actions" },
|
|
1445
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: load }, "
|
|
2006
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: load }, t("common.retry"))
|
|
1446
2007
|
)
|
|
1447
2008
|
);
|
|
1448
2009
|
}
|
|
1449
2010
|
if (files === null) {
|
|
1450
|
-
return React.createElement("div", { className: "dsh-recall-ex-note" }, "
|
|
2011
|
+
return React.createElement("div", { className: "dsh-recall-ex-note" }, t("exclude.loading"));
|
|
1451
2012
|
}
|
|
1452
2013
|
if (!files.length) {
|
|
1453
|
-
return React.createElement("div", { className: "dsh-recall-ex-note" }, "
|
|
2014
|
+
return React.createElement("div", { className: "dsh-recall-ex-note" }, t("exclude.empty"));
|
|
1454
2015
|
}
|
|
1455
2016
|
return React.createElement(
|
|
1456
2017
|
"div",
|
|
@@ -1502,7 +2063,7 @@
|
|
|
1502
2063
|
return result;
|
|
1503
2064
|
}
|
|
1504
2065
|
function buildSnapshotManager(React, util, sessionsSvc, workspacesSvc, uiWorkspaceSvc) {
|
|
1505
|
-
const { api, clockText: clockText2, sizeText: sizeText2, buildTree: buildTree2 } = util;
|
|
2066
|
+
const { api, clockText: clockText2, sizeText: sizeText2, buildTree: buildTree2, t } = util;
|
|
1506
2067
|
function DeleteButton(props) {
|
|
1507
2068
|
return React.createElement("button", {
|
|
1508
2069
|
type: "button",
|
|
@@ -1637,16 +2198,16 @@
|
|
|
1637
2198
|
api("status", { op: "clear" }).catch(() => {
|
|
1638
2199
|
});
|
|
1639
2200
|
}
|
|
1640
|
-
function run(op, extra,
|
|
2201
|
+
function run(op, extra, doneKey) {
|
|
1641
2202
|
if (state.busy) return;
|
|
1642
|
-
setState({ busy: true, message: "
|
|
2203
|
+
setState({ busy: true, message: t("manage.busy"), error: false });
|
|
1643
2204
|
api("manage", Object.assign({ op }, extra || {})).then((res) => {
|
|
1644
2205
|
if (res && res.ok) {
|
|
1645
2206
|
const deleted = res.deleted;
|
|
1646
|
-
setState({ busy: false, message: typeof deleted === "number" ? "
|
|
2207
|
+
setState({ busy: false, message: typeof deleted === "number" ? t("manage.deletedCount", { deleted }) : t(doneKey), error: false });
|
|
1647
2208
|
refresh();
|
|
1648
2209
|
} else {
|
|
1649
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
2210
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("common.opFailed"), error: true });
|
|
1650
2211
|
}
|
|
1651
2212
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1652
2213
|
}
|
|
@@ -1657,17 +2218,17 @@
|
|
|
1657
2218
|
"div",
|
|
1658
2219
|
{ className: "dsh-recall-tree-confirm" },
|
|
1659
2220
|
props.text,
|
|
1660
|
-
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip dsh-recall-ex-chip-danger", onClick: props.onConfirm }, "
|
|
1661
|
-
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: props.onCancel }, "
|
|
2221
|
+
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip dsh-recall-ex-chip-danger", onClick: props.onConfirm }, t("common.confirm")),
|
|
2222
|
+
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: props.onCancel }, t("common.cancel"))
|
|
1662
2223
|
);
|
|
1663
2224
|
}
|
|
1664
2225
|
function renderDeleteAllConfirm() {
|
|
1665
2226
|
if (!confirming || confirming.kind !== "all") return null;
|
|
1666
2227
|
return React.createElement(ConfirmRow, {
|
|
1667
|
-
text: "
|
|
2228
|
+
text: t("manage.confirm.all"),
|
|
1668
2229
|
onConfirm: () => {
|
|
1669
2230
|
setConfirming(null);
|
|
1670
|
-
run("deleteAll", {}, "
|
|
2231
|
+
run("deleteAll", {}, "manage.done.deleteAll");
|
|
1671
2232
|
},
|
|
1672
2233
|
onCancel: () => setConfirming(null)
|
|
1673
2234
|
});
|
|
@@ -1684,7 +2245,7 @@
|
|
|
1684
2245
|
const filteredItems = q ? (items || []).filter(
|
|
1685
2246
|
(it) => (it.workspace || "").toLowerCase().indexOf(q) >= 0 || (it.sessionTitle || "").toLowerCase().indexOf(q) >= 0 || (it.messageText || "").toLowerCase().indexOf(q) >= 0 || String(it.id || "").toLowerCase().indexOf(q) >= 0
|
|
1686
2247
|
) : items;
|
|
1687
|
-
const tree = buildTree2(filteredItems);
|
|
2248
|
+
const tree = buildTree2(filteredItems, t);
|
|
1688
2249
|
const allSessionIds = Array.from(new Set((items || []).map((it) => it.sessionId).filter(Boolean)));
|
|
1689
2250
|
const versionMap = groupByLineage(allSessionIds, lineage);
|
|
1690
2251
|
let listById = null;
|
|
@@ -1703,17 +2264,17 @@
|
|
|
1703
2264
|
} catch (e) {
|
|
1704
2265
|
archivedIds = null;
|
|
1705
2266
|
}
|
|
1706
|
-
function confirmDelete(kind, key, extra,
|
|
1707
|
-
setConfirming({ kind, key, extra, text });
|
|
2267
|
+
function confirmDelete(kind, key, extra, textKey) {
|
|
2268
|
+
setConfirming({ kind, key, extra, text: textKey });
|
|
1708
2269
|
}
|
|
1709
|
-
function renderConfirm(kind, key, extra,
|
|
2270
|
+
function renderConfirm(kind, key, extra, textKey) {
|
|
1710
2271
|
if (!confirming || confirming.kind !== kind || confirming.key !== key) return null;
|
|
1711
2272
|
return React.createElement(ConfirmRow, {
|
|
1712
|
-
text,
|
|
2273
|
+
text: t(textKey),
|
|
1713
2274
|
onConfirm: () => {
|
|
1714
2275
|
const c = confirming;
|
|
1715
2276
|
setConfirming(null);
|
|
1716
|
-
run("delete", c.extra, "
|
|
2277
|
+
run("delete", c.extra, "manage.done.delete");
|
|
1717
2278
|
},
|
|
1718
2279
|
onCancel: () => setConfirming(null)
|
|
1719
2280
|
});
|
|
@@ -1737,18 +2298,18 @@
|
|
|
1737
2298
|
// 删除紧贴本条消息摘要(而非行尾):按行扫读时动作落在「看的那一行」,
|
|
1738
2299
|
// 不必横跳到右侧同列按钮再回读行号
|
|
1739
2300
|
React.createElement(DeleteButton, {
|
|
1740
|
-
title: "
|
|
1741
|
-
onClick: () => confirmDelete("snapshot", key, { messageId: it.id, root: it.root || null }, "
|
|
2301
|
+
title: t("tree.delete.snapshot.title"),
|
|
2302
|
+
onClick: () => confirmDelete("snapshot", key, { messageId: it.id, root: it.root || null }, "tree.delete.snapshot.confirm")
|
|
1742
2303
|
})
|
|
1743
2304
|
)
|
|
1744
2305
|
),
|
|
1745
|
-
renderConfirm("snapshot", key, { messageId: it.id, root: it.root || null }, "
|
|
2306
|
+
renderConfirm("snapshot", key, { messageId: it.id, root: it.root || null }, "tree.delete.snapshot.confirm")
|
|
1746
2307
|
);
|
|
1747
2308
|
}
|
|
1748
2309
|
function renderSession(s) {
|
|
1749
2310
|
const key = "session-" + (s.root || "") + "-" + s.sessionId;
|
|
1750
2311
|
const open = expanded.has(key);
|
|
1751
|
-
const label = s.title || (titlesPending && s.sessionId ? "\u2026" : "
|
|
2312
|
+
const label = s.title || (titlesPending && s.sessionId ? "\u2026" : t("tree.deletedSession"));
|
|
1752
2313
|
const version = s.sessionId ? versionMap.get(String(s.sessionId)) : null;
|
|
1753
2314
|
const switchable = Boolean(s.sessionId && listById && listById[s.sessionId] && !(archivedIds && archivedIds.has(String(s.sessionId))));
|
|
1754
2315
|
return React.createElement(
|
|
@@ -1766,7 +2327,7 @@
|
|
|
1766
2327
|
type: "button",
|
|
1767
2328
|
className: "dsh-recall-tree-toggle",
|
|
1768
2329
|
"aria-expanded": open,
|
|
1769
|
-
"aria-label": (open ? "
|
|
2330
|
+
"aria-label": t(open ? "tree.collapse" : "tree.expand", { label }),
|
|
1770
2331
|
onClick: (e) => {
|
|
1771
2332
|
e.stopPropagation();
|
|
1772
2333
|
toggle(key);
|
|
@@ -1776,11 +2337,11 @@
|
|
|
1776
2337
|
"span",
|
|
1777
2338
|
{ className: "dsh-recall-tree-label", title: s.sessionId || "" },
|
|
1778
2339
|
React.createElement("span", { className: "dsh-recall-tree-title" }, label),
|
|
1779
|
-
version ? React.createElement("span", { className: "dsh-recall-tree-meta", title: "
|
|
1780
|
-
React.createElement("span", { className: "dsh-recall-tree-meta" }, s.items.length
|
|
2340
|
+
version ? React.createElement("span", { className: "dsh-recall-tree-meta", title: t("tree.family.title", { chain: version.family.join(" \u2192 ") }) }, "v" + version.index + "/" + version.family.length) : null,
|
|
2341
|
+
React.createElement("span", { className: "dsh-recall-tree-meta" }, t("tree.snapCount", { n: s.items.length })),
|
|
1781
2342
|
s.sessionId ? React.createElement(DeleteButton, {
|
|
1782
|
-
title: "
|
|
1783
|
-
onClick: () => confirmDelete("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "
|
|
2343
|
+
title: t("tree.delete.session.title"),
|
|
2344
|
+
onClick: () => confirmDelete("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "tree.delete.session.confirm")
|
|
1784
2345
|
}) : null
|
|
1785
2346
|
),
|
|
1786
2347
|
// 「切换」留在行尾单占右侧:它是导航动作(离开当前视图),与行内
|
|
@@ -1788,7 +2349,7 @@
|
|
|
1788
2349
|
switchable ? React.createElement("button", {
|
|
1789
2350
|
type: "button",
|
|
1790
2351
|
className: "dsh-recall-ex-chip",
|
|
1791
|
-
title: "
|
|
2352
|
+
title: t("tree.switch.title"),
|
|
1792
2353
|
onClick: (ev) => {
|
|
1793
2354
|
ev.stopPropagation();
|
|
1794
2355
|
try {
|
|
@@ -1797,10 +2358,10 @@
|
|
|
1797
2358
|
} catch (e) {
|
|
1798
2359
|
}
|
|
1799
2360
|
}
|
|
1800
|
-
}, "
|
|
2361
|
+
}, t("tree.switch")) : null
|
|
1801
2362
|
),
|
|
1802
2363
|
open ? React.createElement("div", { className: "dsh-recall-tree-children" }, ...s.items.map(renderLeaf)) : null,
|
|
1803
|
-
s.sessionId ? renderConfirm("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "
|
|
2364
|
+
s.sessionId ? renderConfirm("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "tree.delete.session.confirm") : null
|
|
1804
2365
|
);
|
|
1805
2366
|
}
|
|
1806
2367
|
function renderWorkspace(ws) {
|
|
@@ -1820,7 +2381,7 @@
|
|
|
1820
2381
|
type: "button",
|
|
1821
2382
|
className: "dsh-recall-tree-toggle",
|
|
1822
2383
|
"aria-expanded": open,
|
|
1823
|
-
"aria-label": (open ? "
|
|
2384
|
+
"aria-label": t(open ? "tree.collapse" : "tree.expand", { label: ws.name }),
|
|
1824
2385
|
onClick: (e) => {
|
|
1825
2386
|
e.stopPropagation();
|
|
1826
2387
|
toggle(key);
|
|
@@ -1830,20 +2391,26 @@
|
|
|
1830
2391
|
"span",
|
|
1831
2392
|
{ className: "dsh-recall-tree-label", title: ws.root || "" },
|
|
1832
2393
|
React.createElement("span", { className: "dsh-recall-tree-name" }, ws.name),
|
|
1833
|
-
React.createElement("span", { className: "dsh-recall-tree-meta" },
|
|
2394
|
+
React.createElement("span", { className: "dsh-recall-tree-meta" }, t("tree.wsCount", { n: sessionCount, m: snapCount })),
|
|
1834
2395
|
ws.root ? React.createElement(DeleteButton, {
|
|
1835
|
-
title: "
|
|
1836
|
-
onClick: () => confirmDelete("workspace", key, { scope: "workspace", root: ws.root }, "
|
|
2396
|
+
title: t("tree.delete.workspace.title"),
|
|
2397
|
+
onClick: () => confirmDelete("workspace", key, { scope: "workspace", root: ws.root }, "tree.delete.workspace.confirm")
|
|
1837
2398
|
}) : null
|
|
1838
2399
|
)
|
|
1839
2400
|
),
|
|
1840
2401
|
open ? React.createElement("div", { className: "dsh-recall-tree-children" }, ...ws.sessions.map(renderSession)) : null,
|
|
1841
|
-
ws.root ? renderConfirm("workspace", key, { scope: "workspace", root: ws.root }, "
|
|
2402
|
+
ws.root ? renderConfirm("workspace", key, { scope: "workspace", root: ws.root }, "tree.delete.workspace.confirm") : null
|
|
1842
2403
|
);
|
|
1843
2404
|
}
|
|
1844
2405
|
const treeNodes = tree.map(renderWorkspace);
|
|
1845
2406
|
const loaded = items ? items.length : null;
|
|
1846
|
-
const countText = loaded === null ? "
|
|
2407
|
+
const countText = loaded === null ? t("manage.countLoading") : t("manage.countLoaded", { total }) + (limit < total ? t("manage.countShown", { shown: loaded }) : "");
|
|
2408
|
+
function errorLine(e, key) {
|
|
2409
|
+
const kindKey = e.kind ? "errorKind." + e.kind : "";
|
|
2410
|
+
const localized = kindKey !== "" && hasTranslation(kindKey);
|
|
2411
|
+
const base = localized ? t(kindKey) + (typeof e.count === "number" && e.count > 1 ? t("manage.errors.dup", { n: e.count }) : "") : String(e.hint || e.message || "");
|
|
2412
|
+
return React.createElement("div", { className: "dsh-recall-ex-note", key, title: e.message || "" }, clockText2(e.time) + " " + base);
|
|
2413
|
+
}
|
|
1847
2414
|
function loadMore() {
|
|
1848
2415
|
const next = Math.min(Math.max(total, limit), 2e3);
|
|
1849
2416
|
if (next <= limit) return;
|
|
@@ -1859,7 +2426,7 @@
|
|
|
1859
2426
|
React.createElement(
|
|
1860
2427
|
"div",
|
|
1861
2428
|
{ className: "dsh-recall-ex-note" },
|
|
1862
|
-
usage === null ? countText + "
|
|
2429
|
+
usage === null ? countText + t("manage.countEnd") : countText + t("manage.usageSuffix", { size: sizeText2(usage) })
|
|
1863
2430
|
),
|
|
1864
2431
|
// V6 健康行徽章化:git 状态用彩色 pill(成功/失败,官方状态行配对),
|
|
1865
2432
|
// 从普通 note 提升为卡片顶部横幅(渲染在搜索框与树之前);存储计数
|
|
@@ -1869,9 +2436,9 @@
|
|
|
1869
2436
|
{ className: "dsh-recall-ex-note", key: "health" },
|
|
1870
2437
|
React.createElement("span", {
|
|
1871
2438
|
className: "dsh-recall-health-pill " + (health.gitAvailable ? "dsh-recall-health-pill-ok" : "dsh-recall-health-pill-bad"),
|
|
1872
|
-
title: "
|
|
1873
|
-
}, health.gitAvailable ? "
|
|
1874
|
-
"
|
|
2439
|
+
title: t("manage.health.gitTitle")
|
|
2440
|
+
}, t(health.gitAvailable ? "manage.health.gitOk" : "manage.health.gitBad")),
|
|
2441
|
+
t("manage.health.stores", { n: health.homeStores }) + (health.fallbackStores ? t("manage.health.storesFallback", { n: health.fallbackStores }) : "")
|
|
1875
2442
|
) : null,
|
|
1876
2443
|
// 搜索行:图标绝对定位在框内左侧(pointer-events:none 不挡点击),输入框
|
|
1877
2444
|
// 靠 padding-left 让出图标位;高度由 CSS 的 .dsh-recall-search 覆写加高 20%
|
|
@@ -1898,8 +2465,8 @@
|
|
|
1898
2465
|
),
|
|
1899
2466
|
React.createElement("input", {
|
|
1900
2467
|
className: "dsh-recall-ex-input",
|
|
1901
|
-
placeholder: "
|
|
1902
|
-
"aria-label": "
|
|
2468
|
+
placeholder: t("manage.search.placeholder"),
|
|
2469
|
+
"aria-label": t("manage.search.aria"),
|
|
1903
2470
|
value: query,
|
|
1904
2471
|
spellCheck: false,
|
|
1905
2472
|
onChange: (e) => setQuery(e.target.value)
|
|
@@ -1913,41 +2480,41 @@
|
|
|
1913
2480
|
...[1, 2, 3, 4, 5].map((n) => React.createElement("div", { key: "sk-" + n, className: "dsh-recall-tree-skeleton-row" }))
|
|
1914
2481
|
) : null,
|
|
1915
2482
|
treeNodes.length > 0 ? React.createElement("div", { className: "dsh-recall-tree" }, ...treeNodes) : null,
|
|
1916
|
-
items && items.length === 0 && !q ? React.createElement("div", { className: "dsh-recall-empty", key: "empty" }, "
|
|
1917
|
-
q && filteredItems && filteredItems.length === 0 ? React.createElement("div", { className: "dsh-recall-empty", key: "no-match" }, "
|
|
2483
|
+
items && items.length === 0 && !q ? React.createElement("div", { className: "dsh-recall-empty", key: "empty" }, t("manage.empty")) : null,
|
|
2484
|
+
q && filteredItems && filteredItems.length === 0 ? React.createElement("div", { className: "dsh-recall-empty", key: "no-match" }, t("manage.emptyFiltered")) : null,
|
|
1918
2485
|
renderDeleteAllConfirm(),
|
|
1919
2486
|
// V6:错误区从卡片最底上移到操作区上方(fail-loud 可见性);标题
|
|
1920
2487
|
// error 色带条数徽章,时间戳格式维持原样
|
|
1921
2488
|
errors && errors.length > 0 ? React.createElement(
|
|
1922
2489
|
"div",
|
|
1923
2490
|
{ key: "errors" },
|
|
1924
|
-
React.createElement("div", { className: "dsh-recall-errors-title" },
|
|
1925
|
-
(showAllErrors ? errors : errors.slice(0, 5)).map(
|
|
2491
|
+
React.createElement("div", { className: "dsh-recall-errors-title" }, t("manage.errors.title", { n: errors.length })),
|
|
2492
|
+
(showAllErrors ? errors : errors.slice(0, 5)).map(errorLine),
|
|
1926
2493
|
React.createElement(
|
|
1927
2494
|
"div",
|
|
1928
2495
|
{ className: "dsh-recall-panel-actions" },
|
|
1929
|
-
errors.length > 5 ? React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: () => setShowAllErrors((v) => !v) }, showAllErrors ? "
|
|
1930
|
-
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: clearErrors }, "
|
|
2496
|
+
errors.length > 5 ? React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: () => setShowAllErrors((v) => !v) }, showAllErrors ? t("manage.errors.collapse") : t("manage.errors.expand", { n: errors.length })) : null,
|
|
2497
|
+
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: clearErrors }, t("manage.errors.clear"))
|
|
1931
2498
|
)
|
|
1932
2499
|
) : null,
|
|
1933
2500
|
React.createElement(
|
|
1934
2501
|
"div",
|
|
1935
2502
|
{ className: "dsh-recall-panel-actions" },
|
|
1936
|
-
state.message ? React.createElement("span", { role: "status", "aria-live": "polite", className: "dsh-recall-ex-status" + (state.error ? " dsh-recall-ex-status-error" : " dsh-recall-ex-status-success") }, (state.error ? "
|
|
2503
|
+
state.message ? React.createElement("span", { role: "status", "aria-live": "polite", className: "dsh-recall-ex-status" + (state.error ? " dsh-recall-ex-status-error" : " dsh-recall-ex-status-success") }, (state.error ? t("common.errorPrefix") : "") + state.message) : null,
|
|
1937
2504
|
limit < total ? React.createElement("button", {
|
|
1938
2505
|
type: "button",
|
|
1939
2506
|
className: "dsh-recall-btn",
|
|
1940
2507
|
disabled: state.busy,
|
|
1941
2508
|
onClick: loadMore
|
|
1942
|
-
}, "
|
|
1943
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy, onClick: refresh }, "
|
|
2509
|
+
}, t("manage.loadMore")) : null,
|
|
2510
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy, onClick: () => refresh() }, t("manage.refresh")),
|
|
1944
2511
|
React.createElement("button", {
|
|
1945
2512
|
type: "button",
|
|
1946
2513
|
className: "dsh-recall-btn",
|
|
1947
2514
|
disabled: state.busy,
|
|
1948
|
-
title: "
|
|
1949
|
-
onClick: () => run("gc", {}, "gc
|
|
1950
|
-
}, "
|
|
2515
|
+
title: t("manage.gc.title"),
|
|
2516
|
+
onClick: () => run("gc", {}, "manage.done.gc")
|
|
2517
|
+
}, t("manage.gc")),
|
|
1951
2518
|
// V5:全部删除固定为操作区最后一个按钮(排在立即 gc 之后)——即使
|
|
1952
2519
|
// 「加载更多」出现/消失也不漂移;danger 与普通按钮间在 panel-actions
|
|
1953
2520
|
// 统一 gap:8px 之上再加 btn-gap 的 8px 物理间隔,危险按钮与常规按钮
|
|
@@ -1956,9 +2523,9 @@
|
|
|
1956
2523
|
type: "button",
|
|
1957
2524
|
className: "dsh-recall-btn dsh-recall-btn-danger dsh-recall-btn-gap",
|
|
1958
2525
|
disabled: state.busy,
|
|
1959
|
-
title: "
|
|
2526
|
+
title: t("manage.deleteAll.title"),
|
|
1960
2527
|
onClick: () => setConfirming({ kind: "all" })
|
|
1961
|
-
}, "
|
|
2528
|
+
}, t("manage.deleteAll"))
|
|
1962
2529
|
)
|
|
1963
2530
|
);
|
|
1964
2531
|
}
|
|
@@ -2000,8 +2567,11 @@
|
|
|
2000
2567
|
const { ConfigForm } = buildConfigForm(React, util, SectionToggle);
|
|
2001
2568
|
const { ExcludeFilesSection } = buildExcludeCards(React, util);
|
|
2002
2569
|
const { ManageCard } = buildSnapshotManager(React, util, sessionsSvc, workspacesSvc, uiWorkspaceSvc);
|
|
2570
|
+
const { t } = util;
|
|
2003
2571
|
function RecallSettingsCard() {
|
|
2004
2572
|
const [sections, setSections] = React.useState({ exclude: false, manage: false });
|
|
2573
|
+
const [, setLocaleTick] = React.useState(0);
|
|
2574
|
+
const notifyLocale = React.useCallback(() => setLocaleTick((n) => n + 1), []);
|
|
2005
2575
|
function toggle(key) {
|
|
2006
2576
|
setSections((prev) => Object.assign({}, prev, { [key]: !prev[key] }));
|
|
2007
2577
|
}
|
|
@@ -2011,14 +2581,14 @@
|
|
|
2011
2581
|
React.createElement(
|
|
2012
2582
|
"div",
|
|
2013
2583
|
{ className: "dsh-recall-settings-body" },
|
|
2014
|
-
React.createElement(ConfigForm),
|
|
2015
|
-
React.createElement(SectionToggle, { title: "
|
|
2584
|
+
React.createElement(ConfigForm, { onLocaleApplied: notifyLocale }),
|
|
2585
|
+
React.createElement(SectionToggle, { title: t("section.exclude"), open: sections.exclude, onToggle: () => toggle("exclude") }),
|
|
2016
2586
|
// section-body 只做纵向间距与展开入场动画,不带描边/底色:外层卡片框
|
|
2017
2587
|
// 去掉后内容区再套一层框等于把「去包裹」加回来(用户实测反馈)。「高级:
|
|
2018
2588
|
// 基础排除表」不包——它在 cfg-grid 内本就走通栏,且 label 列对齐依赖
|
|
2019
2589
|
// grid item 身份,包容器会破坏跨行对齐。
|
|
2020
2590
|
sections.exclude ? React.createElement("div", { className: "dsh-recall-section-body" }, React.createElement(ExcludeFilesSection)) : null,
|
|
2021
|
-
React.createElement(SectionToggle, { title: "
|
|
2591
|
+
React.createElement(SectionToggle, { title: t("section.manage"), open: sections.manage, onToggle: () => toggle("manage") }),
|
|
2022
2592
|
sections.manage ? React.createElement("div", { className: "dsh-recall-section-body" }, React.createElement(ManageCard)) : null
|
|
2023
2593
|
)
|
|
2024
2594
|
);
|
|
@@ -2066,7 +2636,7 @@
|
|
|
2066
2636
|
);
|
|
2067
2637
|
});
|
|
2068
2638
|
} catch (error) {
|
|
2069
|
-
|
|
2639
|
+
appLog.error("slot register failed (" + slotKey + "):", error);
|
|
2070
2640
|
}
|
|
2071
2641
|
}
|
|
2072
2642
|
try {
|
|
@@ -2075,7 +2645,7 @@
|
|
|
2075
2645
|
RecallSettingsCard
|
|
2076
2646
|
));
|
|
2077
2647
|
} catch (error) {
|
|
2078
|
-
|
|
2648
|
+
appLog.error("settings card register failed:", error);
|
|
2079
2649
|
}
|
|
2080
2650
|
try {
|
|
2081
2651
|
slots.inject("plugins.bundle.config", () => slots.register(
|
|
@@ -2083,8 +2653,9 @@
|
|
|
2083
2653
|
RecallSettingsCard
|
|
2084
2654
|
));
|
|
2085
2655
|
} catch (error) {
|
|
2086
|
-
|
|
2656
|
+
appLog.error("plugin manager config register failed:", error);
|
|
2087
2657
|
}
|
|
2658
|
+
appLog.info("client applied");
|
|
2088
2659
|
};
|
|
2089
2660
|
}
|
|
2090
2661
|
|