dsh-recall-plugin 2.4.6 → 2.4.8
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 +46 -0
- package/README.en.md +126 -35
- package/README.md +129 -40
- package/lib/client.js +824 -199
- package/lib/config.js +11 -4
- package/lib/errors.js +3 -0
- package/lib/exclude-patterns.js +2 -1
- package/lib/index.js +35 -5
- package/lib/intent-journal.js +108 -0
- package/lib/maintenance.js +9 -4
- package/lib/routes-core.js +89 -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 || ""));
|
|
@@ -358,6 +820,33 @@
|
|
|
358
820
|
return "/api/recall/" + name;
|
|
359
821
|
}
|
|
360
822
|
}
|
|
823
|
+
function buildRecallNotify(base, o) {
|
|
824
|
+
if (o.outcome === "complete") {
|
|
825
|
+
return {
|
|
826
|
+
status: "complete",
|
|
827
|
+
sessionId: base.sessionId,
|
|
828
|
+
messageId: base.messageId,
|
|
829
|
+
scope: base.scope,
|
|
830
|
+
cutSeq: o.cutSeq,
|
|
831
|
+
childSessionId: o.childSessionId,
|
|
832
|
+
count: o.count,
|
|
833
|
+
chatReverted: o.chatReverted,
|
|
834
|
+
archiveRequested: o.archiveRequested
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
const stage = o.outcome === "fork-failed" ? "fork" : "execute";
|
|
838
|
+
const code = o.outcome === "execute-rejected" && typeof o.code === "string" && o.code ? o.code : void 0;
|
|
839
|
+
return {
|
|
840
|
+
status: "failed",
|
|
841
|
+
stage,
|
|
842
|
+
sessionId: base.sessionId,
|
|
843
|
+
messageId: base.messageId,
|
|
844
|
+
scope: base.scope,
|
|
845
|
+
cutSeq: o.cutSeq,
|
|
846
|
+
...code ? { code } : {},
|
|
847
|
+
error: typeof o.error === "string" && o.error ? o.error : String(o.error || "recall failed")
|
|
848
|
+
};
|
|
849
|
+
}
|
|
361
850
|
function useAutoDismissMessage(React, state, setState) {
|
|
362
851
|
React.useEffect(() => {
|
|
363
852
|
if (!state.message || state.error || state.busy) return;
|
|
@@ -368,6 +857,13 @@
|
|
|
368
857
|
}, [state.message, state.error, state.busy]);
|
|
369
858
|
}
|
|
370
859
|
function buildUtil() {
|
|
860
|
+
let locale = resolveLocale("auto");
|
|
861
|
+
function t(key, params) {
|
|
862
|
+
return translate(locale, key, params);
|
|
863
|
+
}
|
|
864
|
+
function setLocalePref(pref) {
|
|
865
|
+
locale = resolveLocale(pref);
|
|
866
|
+
}
|
|
371
867
|
function api(name, args) {
|
|
372
868
|
return fetch(recallApiUrl(name), {
|
|
373
869
|
method: "POST",
|
|
@@ -375,17 +871,11 @@
|
|
|
375
871
|
body: JSON.stringify(args || {})
|
|
376
872
|
}).then((r) => r.json());
|
|
377
873
|
}
|
|
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
874
|
function messageFor(res, fallback) {
|
|
386
875
|
if (!res) return fallback;
|
|
387
876
|
const code = res.code;
|
|
388
|
-
|
|
877
|
+
const key = code ? "err." + String(code) : "";
|
|
878
|
+
if (key && hasTranslation(key)) return t(key);
|
|
389
879
|
const m = res.message;
|
|
390
880
|
const e = res.error;
|
|
391
881
|
return String(m || e || fallback);
|
|
@@ -406,7 +896,7 @@
|
|
|
406
896
|
el.className = "dsh-recall-toast";
|
|
407
897
|
const tag = document.createElement("span");
|
|
408
898
|
tag.className = "dsh-recall-toast-tag";
|
|
409
|
-
tag.textContent = "
|
|
899
|
+
tag.textContent = t("toast.tag");
|
|
410
900
|
const body = document.createElement("span");
|
|
411
901
|
body.textContent = text;
|
|
412
902
|
el.appendChild(tag);
|
|
@@ -444,18 +934,21 @@
|
|
|
444
934
|
const cfg = res.config;
|
|
445
935
|
if (typeof cfg.refillDraft === "boolean") pluginConfig.refillDraft = cfg.refillDraft;
|
|
446
936
|
if (typeof cfg.archiveOriginal === "boolean") pluginConfig.archiveOriginal = cfg.archiveOriginal;
|
|
937
|
+
setLocalePref(cfg.locale);
|
|
447
938
|
}
|
|
448
939
|
const notice = res && res.notice;
|
|
449
940
|
if (notice && notice.unsupported) {
|
|
450
|
-
showNotice("unsupported", "
|
|
941
|
+
showNotice("unsupported", t("notice.unsupported"));
|
|
451
942
|
}
|
|
452
943
|
if (notice && notice.gitMissing) {
|
|
453
|
-
showNotice("git", "
|
|
944
|
+
showNotice("git", t("notice.gitMissing"));
|
|
454
945
|
}
|
|
455
946
|
if (notice && notice.homeFallback) {
|
|
456
|
-
showNotice("home", "
|
|
947
|
+
showNotice("home", t("notice.homeFallback"));
|
|
457
948
|
}
|
|
458
|
-
if (notice && notice.
|
|
949
|
+
if (notice && typeof notice.buildRootArtifactSeg === "string" && notice.buildRootArtifactSeg) {
|
|
950
|
+
showNotice("buildRoot", t("notice.buildRoot", { seg: notice.buildRootArtifactSeg }));
|
|
951
|
+
} else if (notice && notice.buildRootNotice) {
|
|
459
952
|
showNotice("buildRoot", String(notice.buildRootNotice).slice(0, 140));
|
|
460
953
|
}
|
|
461
954
|
}).catch(() => {
|
|
@@ -490,9 +983,46 @@
|
|
|
490
983
|
}
|
|
491
984
|
return Promise.resolve(false);
|
|
492
985
|
}
|
|
493
|
-
return { api, messageFor, showNotice, showThrottledToast, ensureInit, clockText, writeClipboard, sizeText, bytesToMb, buildTree, pluginConfig };
|
|
986
|
+
return { api, messageFor, showNotice, showThrottledToast, ensureInit, clockText, writeClipboard, sizeText, bytesToMb, buildTree, t, setLocalePref, pluginConfig };
|
|
494
987
|
}
|
|
495
988
|
|
|
989
|
+
// src/client/log.ts
|
|
990
|
+
function debugFlag() {
|
|
991
|
+
try {
|
|
992
|
+
return String(localStorage.getItem("dsh-recall.debug") || "");
|
|
993
|
+
} catch (error) {
|
|
994
|
+
return "";
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function debugEnabled(ns) {
|
|
998
|
+
const raw = debugFlag();
|
|
999
|
+
if (!raw) return false;
|
|
1000
|
+
for (const part of raw.split(",")) {
|
|
1001
|
+
const t = part.trim();
|
|
1002
|
+
if (t === "*" || t === ns) return true;
|
|
1003
|
+
}
|
|
1004
|
+
return false;
|
|
1005
|
+
}
|
|
1006
|
+
function createLogger(ns) {
|
|
1007
|
+
const prefix = "[dsh-recall:" + ns + "]";
|
|
1008
|
+
return {
|
|
1009
|
+
error: (...args) => {
|
|
1010
|
+
console.error(prefix, ...args);
|
|
1011
|
+
},
|
|
1012
|
+
warn: (...args) => {
|
|
1013
|
+
console.warn(prefix, ...args);
|
|
1014
|
+
},
|
|
1015
|
+
info: (...args) => {
|
|
1016
|
+
if (debugEnabled(ns)) console.info(prefix, ...args);
|
|
1017
|
+
},
|
|
1018
|
+
debug: (...args) => {
|
|
1019
|
+
if (debugEnabled(ns)) console.debug(prefix, ...args);
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
var appLog = createLogger("app");
|
|
1024
|
+
var recallNodeLog = createLogger("recall-node");
|
|
1025
|
+
|
|
496
1026
|
// src/client/recall-node.ts
|
|
497
1027
|
function attachmentRefsFromBlocks(blocks) {
|
|
498
1028
|
const refs = [];
|
|
@@ -526,26 +1056,29 @@
|
|
|
526
1056
|
if (n < 1073741824) return (n / 1048576).toFixed(1) + "MB";
|
|
527
1057
|
return (n / 1073741824).toFixed(1) + "GB";
|
|
528
1058
|
}
|
|
529
|
-
function fileCardInfo(block) {
|
|
1059
|
+
function fileCardInfo(block, t = zhTranslate) {
|
|
530
1060
|
if (!block || block.type !== "file") return null;
|
|
531
1061
|
const ref = block.attachment;
|
|
532
|
-
const name = ref && typeof ref === "object" && typeof ref.name === "string" && ref.name !== "" ? ref.name : "
|
|
1062
|
+
const name = ref && typeof ref === "object" && typeof ref.name === "string" && ref.name !== "" ? ref.name : t("file.unnamed");
|
|
533
1063
|
return { name, ext: fileExtOf(name), size: fileCardSizeText(ref && typeof ref === "object" ? ref.bytes : void 0) };
|
|
534
1064
|
}
|
|
535
1065
|
var KIND_INFO = {
|
|
536
|
-
modified: {
|
|
537
|
-
restored: {
|
|
538
|
-
added: {
|
|
1066
|
+
modified: { labelKey: "kind.modified", cls: "modified" },
|
|
1067
|
+
restored: { labelKey: "kind.restored", cls: "restored" },
|
|
1068
|
+
added: { labelKey: "kind.added", cls: "added" }
|
|
539
1069
|
};
|
|
540
|
-
function
|
|
1070
|
+
function isChangeKind(kind) {
|
|
1071
|
+
return typeof kind === "string" && Object.prototype.hasOwnProperty.call(KIND_INFO, kind);
|
|
1072
|
+
}
|
|
1073
|
+
function summaryText(counts, t = zhTranslate) {
|
|
541
1074
|
const parts = [];
|
|
542
1075
|
for (const kind of Object.keys(KIND_INFO)) {
|
|
543
|
-
if (counts[kind] > 0) parts.push(KIND_INFO[kind].
|
|
1076
|
+
if (counts[kind] > 0) parts.push(t(KIND_INFO[kind].labelKey) + " " + counts[kind]);
|
|
544
1077
|
}
|
|
545
1078
|
return parts.join(" \xB7 ");
|
|
546
1079
|
}
|
|
547
1080
|
function buildRecallNode(React, util, ctx, sessionsSvc, workspacesSvc, uiWorkspaceSvc) {
|
|
548
|
-
const { api, ensureInit, showThrottledToast, writeClipboard, clockText: clockText2, pluginConfig, messageFor } = util;
|
|
1081
|
+
const { api, ensureInit, showThrottledToast, writeClipboard, clockText: clockText2, pluginConfig, messageFor, t } = util;
|
|
549
1082
|
function CopyIcon() {
|
|
550
1083
|
return React.createElement(
|
|
551
1084
|
"svg",
|
|
@@ -578,19 +1111,19 @@
|
|
|
578
1111
|
return React.createElement(
|
|
579
1112
|
"div",
|
|
580
1113
|
{ className: "dsh-recall-panel" },
|
|
581
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1114
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.loading"))
|
|
582
1115
|
);
|
|
583
1116
|
}
|
|
584
1117
|
if (recall.stage === "error") {
|
|
585
1118
|
return React.createElement(
|
|
586
1119
|
"div",
|
|
587
1120
|
{ className: "dsh-recall-panel" },
|
|
588
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1121
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.error.title")),
|
|
589
1122
|
React.createElement("div", { className: "dsh-recall-panel-note" }, recall.message || ""),
|
|
590
1123
|
React.createElement(
|
|
591
1124
|
"div",
|
|
592
1125
|
{ className: "dsh-recall-panel-actions" },
|
|
593
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, "
|
|
1126
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, t("common.close"))
|
|
594
1127
|
)
|
|
595
1128
|
);
|
|
596
1129
|
}
|
|
@@ -599,14 +1132,14 @@
|
|
|
599
1132
|
const total = typeof recall.total === "number" ? recall.total : changes.length;
|
|
600
1133
|
const counts = { modified: 0, restored: 0, added: 0 };
|
|
601
1134
|
for (const c of changes) {
|
|
602
|
-
if (c &&
|
|
1135
|
+
if (c && isChangeKind(c.kind)) counts[c.kind]++;
|
|
603
1136
|
}
|
|
604
1137
|
const rows = changes.map((c, i) => {
|
|
605
1138
|
const info = KIND_INFO[c.kind];
|
|
606
1139
|
return React.createElement(
|
|
607
1140
|
"div",
|
|
608
1141
|
{ className: "dsh-recall-file", key: i },
|
|
609
|
-
React.createElement("span", { className: "dsh-recall-badge dsh-recall-badge-" + (info ? info.cls : "") }, info ? info.
|
|
1142
|
+
React.createElement("span", { className: "dsh-recall-badge dsh-recall-badge-" + (info ? info.cls : "") }, info ? t(info.labelKey) : c.kind || ""),
|
|
610
1143
|
React.createElement("span", { className: "dsh-recall-rel" }, c.rel || "")
|
|
611
1144
|
);
|
|
612
1145
|
});
|
|
@@ -616,30 +1149,36 @@
|
|
|
616
1149
|
rows.push(React.createElement(
|
|
617
1150
|
"div",
|
|
618
1151
|
{ className: "dsh-recall-panel-note", key: "truncated" },
|
|
619
|
-
sessionOnly ? "
|
|
1152
|
+
t(sessionOnly ? "recall.truncated.chat" : "recall.truncated.files", { shown: changes.length, total })
|
|
620
1153
|
));
|
|
621
1154
|
}
|
|
1155
|
+
const summary = summaryText(counts, t);
|
|
1156
|
+
const rollbackLead = recall.time ? t("recall.confirm.rollbackAt", { time: clockText2(recall.time) }) : t("recall.confirm.rollbackNoTime");
|
|
1157
|
+
const filesLead = t("recall.confirm.files", {
|
|
1158
|
+
total,
|
|
1159
|
+
summary: summary ? t("common.paren", { text: summary }) : ""
|
|
1160
|
+
});
|
|
622
1161
|
return React.createElement(
|
|
623
1162
|
"div",
|
|
624
1163
|
{ className: "dsh-recall-panel" },
|
|
625
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1164
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.confirm.title")),
|
|
626
1165
|
sessionOnly ? (
|
|
627
1166
|
// session-only:零文件改动即无不可逆操作缺口——安全快照预告随之隐藏
|
|
628
1167
|
//(不打安全快照),主说明换成模式语义
|
|
629
1168
|
React.createElement(
|
|
630
1169
|
"div",
|
|
631
1170
|
{ className: "dsh-recall-panel-note" },
|
|
632
|
-
"
|
|
1171
|
+
t("recall.confirm.sessionOnly")
|
|
633
1172
|
)
|
|
634
1173
|
) : React.createElement(
|
|
635
1174
|
"div",
|
|
636
1175
|
{ className: "dsh-recall-panel-note" },
|
|
637
|
-
|
|
1176
|
+
rollbackLead + filesLead
|
|
638
1177
|
),
|
|
639
1178
|
sessionOnly ? null : React.createElement(
|
|
640
1179
|
"div",
|
|
641
1180
|
{ className: "dsh-recall-panel-note" },
|
|
642
|
-
canRevertChat ? "
|
|
1181
|
+
t(canRevertChat ? "recall.confirm.chatBoth" : "recall.confirm.chatFirst")
|
|
643
1182
|
),
|
|
644
1183
|
sessionOnly && changes.length > 0 ? (
|
|
645
1184
|
// 文件清单保留展示但降级为参考语义:清单照常算(Host preview 链路
|
|
@@ -647,7 +1186,7 @@
|
|
|
647
1186
|
React.createElement(
|
|
648
1187
|
"div",
|
|
649
1188
|
{ className: "dsh-recall-panel-note", key: "ref-note" },
|
|
650
|
-
"
|
|
1189
|
+
t("recall.confirm.refNote")
|
|
651
1190
|
)
|
|
652
1191
|
) : null,
|
|
653
1192
|
changes.length > 0 ? React.createElement("div", { className: "dsh-recall-list" }, ...rows) : null,
|
|
@@ -656,26 +1195,26 @@
|
|
|
656
1195
|
// 默认 both 与现状一致,面板每次打开都复位到默认起点
|
|
657
1196
|
React.createElement(
|
|
658
1197
|
"div",
|
|
659
|
-
{ className: "dsh-recall-scope", role: "radiogroup", "aria-label": "
|
|
1198
|
+
{ className: "dsh-recall-scope", role: "radiogroup", "aria-label": t("recall.scope.aria") },
|
|
660
1199
|
React.createElement(
|
|
661
1200
|
"label",
|
|
662
1201
|
{ className: "dsh-recall-scope-item" },
|
|
663
1202
|
React.createElement("input", { type: "radio", name: "dsh-recall-scope", checked: scope === "both", onChange: () => onScopeChange("both") }),
|
|
664
|
-
React.createElement("span", { className: "dsh-recall-scope-label" }, "
|
|
1203
|
+
React.createElement("span", { className: "dsh-recall-scope-label" }, t("recall.scope.both"))
|
|
665
1204
|
),
|
|
666
1205
|
React.createElement(
|
|
667
1206
|
"label",
|
|
668
1207
|
{ className: "dsh-recall-scope-item" },
|
|
669
1208
|
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" }, "
|
|
1209
|
+
React.createElement("span", { className: "dsh-recall-scope-label" }, t("recall.scope.sessionOnly"))
|
|
671
1210
|
)
|
|
672
1211
|
)
|
|
673
1212
|
) : null,
|
|
674
1213
|
React.createElement(
|
|
675
1214
|
"div",
|
|
676
1215
|
{ 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 ? "
|
|
1216
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, t("common.cancel")),
|
|
1217
|
+
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
1218
|
)
|
|
680
1219
|
);
|
|
681
1220
|
}
|
|
@@ -683,26 +1222,26 @@
|
|
|
683
1222
|
return React.createElement(
|
|
684
1223
|
"div",
|
|
685
1224
|
{ className: "dsh-recall-panel" },
|
|
686
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, scope === "session-only" ? "
|
|
1225
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t(scope === "session-only" ? "recall.executing.sessionOnly" : "recall.executing.both"))
|
|
687
1226
|
);
|
|
688
1227
|
}
|
|
689
1228
|
if (recall.stage === "done") {
|
|
690
1229
|
return React.createElement(
|
|
691
1230
|
"div",
|
|
692
1231
|
{ className: "dsh-recall-panel" },
|
|
693
|
-
React.createElement("div", { className: "dsh-recall-panel-title" }, "
|
|
1232
|
+
React.createElement("div", { className: "dsh-recall-panel-title" }, t("recall.done.title")),
|
|
694
1233
|
React.createElement(
|
|
695
1234
|
"div",
|
|
696
1235
|
{ className: "dsh-recall-panel-note" },
|
|
697
1236
|
scope === "session-only" ? (
|
|
698
1237
|
// session-only 文案矩阵:文件侧零改动是确定事实,失败时也只描述对话侧
|
|
699
|
-
recall.chatReverted ? "
|
|
700
|
-
) : recall.chatReverted ? "
|
|
1238
|
+
recall.chatReverted ? t("recall.done.sessionOnly.ok") : t("recall.done.sessionOnly.fail", { error: recall.chatError || t("common.unknownReason") })
|
|
1239
|
+
) : recall.chatReverted ? t("recall.done.both.ok") : t("recall.done.both.partial") + (recall.chatError ? t("recall.done.both.failChat", { error: recall.chatError }) : "")
|
|
701
1240
|
),
|
|
702
1241
|
React.createElement(
|
|
703
1242
|
"div",
|
|
704
1243
|
{ className: "dsh-recall-panel-actions" },
|
|
705
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, "
|
|
1244
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: closePanel }, t("common.close"))
|
|
706
1245
|
)
|
|
707
1246
|
);
|
|
708
1247
|
}
|
|
@@ -809,8 +1348,8 @@
|
|
|
809
1348
|
} catch (error) {
|
|
810
1349
|
}
|
|
811
1350
|
if (Date.now() >= deadline) {
|
|
812
|
-
|
|
813
|
-
showThrottledToast("
|
|
1351
|
+
recallNodeLog.warn("\u6B8B\u7559\u6392\u961F\u6D88\u606F\u672A\u80FD\u81EA\u52A8\u6E05\u7406\uFF08\u4F1A\u8BDD\u9762\u672A\u5C31\u7EEA\uFF09\uFF1A", itemIds);
|
|
1352
|
+
showThrottledToast(t("toast.staleQueue"));
|
|
814
1353
|
return;
|
|
815
1354
|
}
|
|
816
1355
|
setTimeout(attempt, 250);
|
|
@@ -849,18 +1388,21 @@
|
|
|
849
1388
|
if (!alive) return;
|
|
850
1389
|
if (res && res.has) {
|
|
851
1390
|
if (recent && Array.isArray(res.skipped) && res.skipped.length) {
|
|
852
|
-
const names = res.skipped.slice(0, 5).join("
|
|
853
|
-
showThrottledToast("
|
|
1391
|
+
const names = res.skipped.slice(0, 5).join(t("common.listSep")) + (res.skipped.length > 5 ? t("toast.skippedMore", { n: res.skipped.length }) : "");
|
|
1392
|
+
showThrottledToast(t("toast.skipped", { names }));
|
|
854
1393
|
}
|
|
855
1394
|
setHasSnapshot(true);
|
|
856
1395
|
return;
|
|
857
1396
|
}
|
|
858
1397
|
if (res && res.failed) {
|
|
859
|
-
if (recent) showThrottledToast("
|
|
1398
|
+
if (recent) showThrottledToast(t("toast.snapshotFailed", { error: String(res.error || t("common.unknownReason")).slice(0, 140) }));
|
|
860
1399
|
return;
|
|
861
1400
|
}
|
|
862
|
-
if (res && res.notice) {
|
|
863
|
-
if (recent)
|
|
1401
|
+
if (res && (res.artifactSeg || res.notice)) {
|
|
1402
|
+
if (recent) {
|
|
1403
|
+
const text2 = res.artifactSeg ? t("notice.buildRoot", { seg: res.artifactSeg }) : String(res.notice).slice(0, 140);
|
|
1404
|
+
showThrottledToast(text2);
|
|
1405
|
+
}
|
|
864
1406
|
return;
|
|
865
1407
|
}
|
|
866
1408
|
if (recent && attempts < MAX_ATTEMPTS) timer = setTimeout(schedule, RETRY_MS);
|
|
@@ -897,7 +1439,7 @@
|
|
|
897
1439
|
setRecall({ stage: "loading" });
|
|
898
1440
|
api("preview", { messageId, sessionId }).then((res) => {
|
|
899
1441
|
if (!res || !res.ok) {
|
|
900
|
-
setRecall({ stage: "error", message: messageFor(res, "
|
|
1442
|
+
setRecall({ stage: "error", message: messageFor(res, t("fallback.preview")) });
|
|
901
1443
|
return;
|
|
902
1444
|
}
|
|
903
1445
|
setRecall({
|
|
@@ -917,6 +1459,7 @@
|
|
|
917
1459
|
if (recall.stage !== "confirm") return;
|
|
918
1460
|
const changes = recall.changes || [];
|
|
919
1461
|
const previewCut = typeof recall.cutSeq === "number" ? recall.cutSeq : null;
|
|
1462
|
+
const notifyBase = { sessionId: String(sessionId || ""), messageId, scope };
|
|
920
1463
|
const previewTotal = typeof recall.total === "number" ? recall.total : changes.length;
|
|
921
1464
|
const attachmentFiles = pluginConfig.refillDraft && attachmentRefs.length > 0 && sessionId ? preloadAttachmentFiles(sessionId, attachmentRefs) : null;
|
|
922
1465
|
setRecall({ stage: "executing", changes });
|
|
@@ -926,7 +1469,7 @@
|
|
|
926
1469
|
setRecall({ stage: "loading" });
|
|
927
1470
|
api("preview", { messageId, sessionId }).then((res2) => {
|
|
928
1471
|
if (!res2 || !res2.ok) {
|
|
929
|
-
setRecall({ stage: "error", message: messageFor(res2, "
|
|
1472
|
+
setRecall({ stage: "error", message: messageFor(res2, t("fallback.preview")) });
|
|
930
1473
|
return;
|
|
931
1474
|
}
|
|
932
1475
|
setRecall({
|
|
@@ -943,12 +1486,20 @@
|
|
|
943
1486
|
});
|
|
944
1487
|
return;
|
|
945
1488
|
}
|
|
946
|
-
|
|
1489
|
+
api("notify", buildRecallNotify(notifyBase, {
|
|
1490
|
+
outcome: "execute-rejected",
|
|
1491
|
+
cutSeq: previewCut,
|
|
1492
|
+
code: res && typeof res.code === "string" ? res.code : void 0,
|
|
1493
|
+
error: messageFor(res, "")
|
|
1494
|
+
})).catch(() => {
|
|
1495
|
+
});
|
|
1496
|
+
setRecall({ stage: "error", message: messageFor(res, t("fallback.rollback")) });
|
|
947
1497
|
return;
|
|
948
1498
|
}
|
|
949
1499
|
const cutSeq = typeof res.cutSeq === "number" ? res.cutSeq : previewCut;
|
|
950
1500
|
let chatReverted = false;
|
|
951
1501
|
let chatError = "";
|
|
1502
|
+
let archiveRequested = false;
|
|
952
1503
|
let fillTarget = sessionId;
|
|
953
1504
|
if (cutSeq !== null && sessionsSvc && typeof sessionsSvc.fork === "function") {
|
|
954
1505
|
try {
|
|
@@ -962,25 +1513,43 @@
|
|
|
962
1513
|
api("lineage-record", { childId, parentId: sessionId }).catch(() => {
|
|
963
1514
|
});
|
|
964
1515
|
if (pluginConfig.archiveOriginal && workspacesSvc && typeof workspacesSvc.archiveSession === "function") {
|
|
1516
|
+
archiveRequested = true;
|
|
965
1517
|
workspacesSvc.archiveSession(sessionId, { stopActivity: true }).catch((error) => {
|
|
966
|
-
|
|
1518
|
+
recallNodeLog.warn("archive original session failed:", error);
|
|
967
1519
|
});
|
|
968
1520
|
}
|
|
969
1521
|
} else {
|
|
970
|
-
chatError = "
|
|
1522
|
+
chatError = t("recall.chat.noChild");
|
|
1523
|
+
api("notify", buildRecallNotify(notifyBase, { outcome: "fork-failed", cutSeq, error: chatError })).catch(() => {
|
|
1524
|
+
});
|
|
971
1525
|
}
|
|
972
1526
|
} catch (error) {
|
|
973
1527
|
chatError = String(error);
|
|
1528
|
+
api("notify", buildRecallNotify(notifyBase, { outcome: "fork-failed", cutSeq, error: chatError })).catch(() => {
|
|
1529
|
+
});
|
|
974
1530
|
}
|
|
975
1531
|
}
|
|
976
1532
|
if (pluginConfig.refillDraft) {
|
|
977
1533
|
fillDraft(fillTarget, text, attachmentFiles);
|
|
978
|
-
if (fileBlocks.length > 0) showThrottledToast("
|
|
1534
|
+
if (fileBlocks.length > 0) showThrottledToast(t("toast.fileAttachRefill"));
|
|
979
1535
|
}
|
|
980
1536
|
setHasSnapshot(false);
|
|
981
1537
|
setRecall({ stage: "done", count: typeof res.count === "number" ? res.count : changes.length, chatReverted, chatError });
|
|
1538
|
+
if (chatError === "") {
|
|
1539
|
+
api("notify", buildRecallNotify(notifyBase, {
|
|
1540
|
+
outcome: "complete",
|
|
1541
|
+
cutSeq,
|
|
1542
|
+
childSessionId: chatReverted ? String(fillTarget || "") || null : null,
|
|
1543
|
+
count: typeof res.count === "number" ? res.count : changes.length,
|
|
1544
|
+
chatReverted,
|
|
1545
|
+
archiveRequested
|
|
1546
|
+
})).catch(() => {
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
982
1549
|
}).catch((error) => {
|
|
983
1550
|
setRecall({ stage: "error", message: String(error) });
|
|
1551
|
+
api("notify", buildRecallNotify(notifyBase, { outcome: "execute-threw", cutSeq: previewCut, error: String(error) })).catch(() => {
|
|
1552
|
+
});
|
|
984
1553
|
});
|
|
985
1554
|
};
|
|
986
1555
|
const closePanel = () => {
|
|
@@ -997,7 +1566,7 @@
|
|
|
997
1566
|
));
|
|
998
1567
|
}
|
|
999
1568
|
for (let i = 0; i < fileBlocks.length; i++) {
|
|
1000
|
-
const info = fileCardInfo(fileBlocks[i]);
|
|
1569
|
+
const info = fileCardInfo(fileBlocks[i], t);
|
|
1001
1570
|
if (!info) continue;
|
|
1002
1571
|
bubbleChildren.push(React.createElement(
|
|
1003
1572
|
"div",
|
|
@@ -1021,8 +1590,8 @@
|
|
|
1021
1590
|
key: "copy",
|
|
1022
1591
|
type: "button",
|
|
1023
1592
|
className: "dsh-recall-action",
|
|
1024
|
-
"aria-label": copied ? "
|
|
1025
|
-
title: copied ? "
|
|
1593
|
+
"aria-label": copied ? t("action.copied") : t("action.copy"),
|
|
1594
|
+
title: copied ? t("action.copied") : t("action.copy"),
|
|
1026
1595
|
onClick: onCopy
|
|
1027
1596
|
}, copied ? React.createElement(CheckIcon, {}) : React.createElement(CopyIcon, {})));
|
|
1028
1597
|
if (hasSnapshot) {
|
|
@@ -1030,8 +1599,8 @@
|
|
|
1030
1599
|
key: "recall",
|
|
1031
1600
|
type: "button",
|
|
1032
1601
|
className: "dsh-recall-action",
|
|
1033
|
-
"aria-label": "
|
|
1034
|
-
title: "
|
|
1602
|
+
"aria-label": t("action.recall"),
|
|
1603
|
+
title: t("action.recall.title"),
|
|
1035
1604
|
onClick: openPreview
|
|
1036
1605
|
}, React.createElement(UndoIcon, {})));
|
|
1037
1606
|
}
|
|
@@ -1048,8 +1617,8 @@
|
|
|
1048
1617
|
|
|
1049
1618
|
// src/client/config-card.ts
|
|
1050
1619
|
function buildConfigForm(React, util, SectionToggle) {
|
|
1051
|
-
const { api, bytesToMb: bytesToMb2 } = util;
|
|
1052
|
-
function ConfigForm() {
|
|
1620
|
+
const { api, bytesToMb: bytesToMb2, t, setLocalePref } = util;
|
|
1621
|
+
function ConfigForm(props) {
|
|
1053
1622
|
const [baseline, setBaseline] = React.useState(null);
|
|
1054
1623
|
const [draft, setDraft] = React.useState(null);
|
|
1055
1624
|
const [envLocks, setEnvLocks] = React.useState({});
|
|
@@ -1071,15 +1640,18 @@
|
|
|
1071
1640
|
refillDraft: v.refillDraft !== false,
|
|
1072
1641
|
snapshotEnabled: v.snapshotEnabled !== false,
|
|
1073
1642
|
archiveOriginal: v.archiveOriginal !== false,
|
|
1074
|
-
retentionDays: String(v.retentionDays == null ? "" : v.retentionDays)
|
|
1643
|
+
retentionDays: String(v.retentionDays == null ? "" : v.retentionDays),
|
|
1644
|
+
locale: typeof v.locale === "string" && v.locale ? v.locale : "auto"
|
|
1075
1645
|
};
|
|
1646
|
+
setLocalePref(next.locale);
|
|
1647
|
+
if (props.onLocaleApplied) props.onLocaleApplied();
|
|
1076
1648
|
setDraft(next);
|
|
1077
1649
|
setBaseline(next);
|
|
1078
1650
|
setEnvLocks(res.envLocks || {});
|
|
1079
1651
|
setOverridden(res.overridden || {});
|
|
1080
1652
|
setWritable(res.writable !== false);
|
|
1081
1653
|
} else {
|
|
1082
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1654
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("config.msg.loadFailed"), error: true });
|
|
1083
1655
|
}
|
|
1084
1656
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1085
1657
|
}
|
|
@@ -1092,18 +1664,19 @@
|
|
|
1092
1664
|
function save() {
|
|
1093
1665
|
if (state.busy || !draft || !baseline) return;
|
|
1094
1666
|
const patch = {};
|
|
1095
|
-
for (const key of ["gcSnaps", "gcHours", "maxFileBytes", "maxSnapshotsPerWorkspace", "baseExcludes", "refillDraft", "snapshotEnabled", "archiveOriginal", "retentionDays"]) {
|
|
1096
|
-
|
|
1667
|
+
for (const key of ["gcSnaps", "gcHours", "maxFileBytes", "maxSnapshotsPerWorkspace", "baseExcludes", "refillDraft", "snapshotEnabled", "archiveOriginal", "retentionDays", "locale"]) {
|
|
1668
|
+
const value = draft[key];
|
|
1669
|
+
if (value !== void 0 && value !== baseline[key]) patch[key] = value;
|
|
1097
1670
|
}
|
|
1098
1671
|
if (!Object.keys(patch).length) {
|
|
1099
|
-
setState({ busy: false, message: "
|
|
1672
|
+
setState({ busy: false, message: t("config.msg.noChange"), error: false });
|
|
1100
1673
|
return;
|
|
1101
1674
|
}
|
|
1102
1675
|
const clean = {};
|
|
1103
1676
|
if (patch.gcSnaps !== void 0) {
|
|
1104
1677
|
const n = parseInt(String(patch.gcSnaps), 10);
|
|
1105
1678
|
if (!Number.isFinite(n) || n < 1) {
|
|
1106
|
-
setState({ busy: false, message: "
|
|
1679
|
+
setState({ busy: false, message: t("config.err.gcSnaps"), error: true });
|
|
1107
1680
|
return;
|
|
1108
1681
|
}
|
|
1109
1682
|
clean.gcSnaps = n;
|
|
@@ -1111,7 +1684,7 @@
|
|
|
1111
1684
|
if (patch.gcHours !== void 0) {
|
|
1112
1685
|
const n = parseInt(String(patch.gcHours), 10);
|
|
1113
1686
|
if (!Number.isFinite(n) || n < 1) {
|
|
1114
|
-
setState({ busy: false, message: "
|
|
1687
|
+
setState({ busy: false, message: t("config.err.gcHours"), error: true });
|
|
1115
1688
|
return;
|
|
1116
1689
|
}
|
|
1117
1690
|
clean.gcHours = n;
|
|
@@ -1119,7 +1692,7 @@
|
|
|
1119
1692
|
if (patch.maxFileBytes !== void 0) {
|
|
1120
1693
|
const mb = Number(patch.maxFileBytes);
|
|
1121
1694
|
if (!Number.isFinite(mb) || mb < 0.01) {
|
|
1122
|
-
setState({ busy: false, message: "
|
|
1695
|
+
setState({ busy: false, message: t("config.err.maxFileBytes"), error: true });
|
|
1123
1696
|
return;
|
|
1124
1697
|
}
|
|
1125
1698
|
clean.maxFileBytes = Math.round(mb * 1048576);
|
|
@@ -1127,7 +1700,7 @@
|
|
|
1127
1700
|
if (patch.maxSnapshotsPerWorkspace !== void 0) {
|
|
1128
1701
|
const n = parseInt(String(patch.maxSnapshotsPerWorkspace), 10);
|
|
1129
1702
|
if (!Number.isFinite(n) || n < 0) {
|
|
1130
|
-
setState({ busy: false, message: "
|
|
1703
|
+
setState({ busy: false, message: t("config.err.maxSnapshots"), error: true });
|
|
1131
1704
|
return;
|
|
1132
1705
|
}
|
|
1133
1706
|
clean.maxSnapshotsPerWorkspace = n;
|
|
@@ -1138,21 +1711,30 @@
|
|
|
1138
1711
|
if (patch.retentionDays !== void 0) {
|
|
1139
1712
|
const n = parseInt(String(patch.retentionDays), 10);
|
|
1140
1713
|
if (!Number.isFinite(n) || n < 0) {
|
|
1141
|
-
setState({ busy: false, message: "
|
|
1714
|
+
setState({ busy: false, message: t("config.err.retentionDays"), error: true });
|
|
1142
1715
|
return;
|
|
1143
1716
|
}
|
|
1144
1717
|
clean.retentionDays = n;
|
|
1145
1718
|
}
|
|
1719
|
+
if (patch.locale !== void 0) {
|
|
1720
|
+
const v = String(patch.locale);
|
|
1721
|
+
if (v !== "auto" && v !== "zh" && v !== "en") {
|
|
1722
|
+
setState({ busy: false, message: t("config.err.locale"), error: true });
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
clean.locale = v;
|
|
1726
|
+
}
|
|
1146
1727
|
if (patch.baseExcludes !== void 0) {
|
|
1147
1728
|
clean.baseExcludes = String(patch.baseExcludes).split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1148
1729
|
}
|
|
1149
|
-
setState({ busy: true, message: "
|
|
1730
|
+
setState({ busy: true, message: t("config.msg.saving"), error: false });
|
|
1150
1731
|
api("config-set", { patch: clean }).then((res) => {
|
|
1151
1732
|
if (res && res.ok) {
|
|
1152
|
-
|
|
1733
|
+
if (clean.locale !== void 0) setLocalePref(clean.locale);
|
|
1734
|
+
setState({ busy: false, message: t("config.msg.saved"), error: false });
|
|
1153
1735
|
load();
|
|
1154
1736
|
} else {
|
|
1155
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1737
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("config.msg.saveFailed"), error: true });
|
|
1156
1738
|
}
|
|
1157
1739
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1158
1740
|
}
|
|
@@ -1184,10 +1766,10 @@
|
|
|
1184
1766
|
step: opts && opts.step,
|
|
1185
1767
|
onChange: (e) => edit(key, e.target.value)
|
|
1186
1768
|
}),
|
|
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" }, "
|
|
1769
|
+
opts && opts.suffixKey ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t(opts.suffixKey)) : null,
|
|
1770
|
+
changed && !locked ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1771
|
+
overridden && overridden[key] !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null,
|
|
1772
|
+
locked ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-locked" }, t("config.tag.locked")) : null
|
|
1191
1773
|
),
|
|
1192
1774
|
React.createElement("div", { className: "dsh-recall-cfg-hint" }, hint)
|
|
1193
1775
|
);
|
|
@@ -1216,26 +1798,55 @@
|
|
|
1216
1798
|
disabled: !writable,
|
|
1217
1799
|
onClick: () => edit(key, !on)
|
|
1218
1800
|
}, 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" }, "
|
|
1801
|
+
changed ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1802
|
+
overridden && overridden[key] !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null
|
|
1221
1803
|
),
|
|
1222
1804
|
React.createElement("div", { className: "dsh-recall-cfg-hint" }, hint)
|
|
1223
1805
|
);
|
|
1224
1806
|
}
|
|
1807
|
+
function localeRow() {
|
|
1808
|
+
const changed = Boolean(draft && baseline && draft.locale !== baseline.locale);
|
|
1809
|
+
return React.createElement(
|
|
1810
|
+
"div",
|
|
1811
|
+
{ className: "dsh-recall-cfg-row", key: "locale" },
|
|
1812
|
+
React.createElement("span", { className: "dsh-recall-cfg-label", id: "dsh-recall-cfg-label-locale" }, t("config.locale.label")),
|
|
1813
|
+
React.createElement(
|
|
1814
|
+
"div",
|
|
1815
|
+
{ className: "dsh-recall-cfg-line" },
|
|
1816
|
+
React.createElement(
|
|
1817
|
+
"select",
|
|
1818
|
+
{
|
|
1819
|
+
id: "dsh-recall-cfg-locale",
|
|
1820
|
+
"aria-labelledby": "dsh-recall-cfg-label-locale",
|
|
1821
|
+
className: "dsh-recall-cfg-select",
|
|
1822
|
+
value: draft ? draft.locale : "auto",
|
|
1823
|
+
disabled: !writable,
|
|
1824
|
+
onChange: (e) => edit("locale", e.target.value)
|
|
1825
|
+
},
|
|
1826
|
+
React.createElement("option", { value: "auto" }, t("config.locale.auto")),
|
|
1827
|
+
React.createElement("option", { value: "zh" }, "\u4E2D\u6587"),
|
|
1828
|
+
React.createElement("option", { value: "en" }, "English")
|
|
1829
|
+
),
|
|
1830
|
+
changed ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1831
|
+
overridden && overridden.locale !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null
|
|
1832
|
+
),
|
|
1833
|
+
React.createElement("div", { className: "dsh-recall-cfg-hint" }, t("config.locale.hint"))
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1225
1836
|
function resetDefaults() {
|
|
1226
1837
|
if (state.busy || !writable) return;
|
|
1227
|
-
setState({ busy: true, message: "
|
|
1838
|
+
setState({ busy: true, message: t("config.msg.resetting"), error: false });
|
|
1228
1839
|
api("config-reset", {}).then((res) => {
|
|
1229
1840
|
if (res && res.ok) {
|
|
1230
1841
|
load();
|
|
1231
|
-
setState({ busy: false, message: "
|
|
1842
|
+
setState({ busy: false, message: t("config.msg.resetDone"), error: false });
|
|
1232
1843
|
} else {
|
|
1233
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1844
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("config.msg.resetFailed"), error: true });
|
|
1234
1845
|
}
|
|
1235
1846
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1236
1847
|
}
|
|
1237
1848
|
if (!draft || !baseline) {
|
|
1238
|
-
return React.createElement("div", { className: "dsh-recall-ex-note" }, state.message || "
|
|
1849
|
+
return React.createElement("div", { className: "dsh-recall-ex-note" }, state.message || t("config.msg.loading"));
|
|
1239
1850
|
}
|
|
1240
1851
|
return React.createElement(
|
|
1241
1852
|
"div",
|
|
@@ -1247,52 +1858,56 @@
|
|
|
1247
1858
|
React.createElement(
|
|
1248
1859
|
"div",
|
|
1249
1860
|
{ 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("
|
|
1861
|
+
// V5 表单分组:9 字段平铺 → 「快照行为 / 自动治理 / 界面」三组语义分组
|
|
1862
|
+
// 小标题,降低认知负担;「高级:基础排除表」沿用 SectionToggle 折叠,
|
|
1863
|
+
// 不重复加标题。
|
|
1864
|
+
React.createElement("div", { className: "dsh-recall-cfg-group" }, t("config.group.snapshot")),
|
|
1865
|
+
boolRow("snapshotEnabled", t("config.snapshotEnabled.label"), t("config.snapshotEnabled.hint")),
|
|
1866
|
+
boolRow("refillDraft", t("config.refillDraft.label"), t("config.refillDraft.hint")),
|
|
1867
|
+
boolRow("archiveOriginal", t("config.archiveOriginal.label"), t("config.archiveOriginal.hint")),
|
|
1868
|
+
React.createElement("div", { className: "dsh-recall-cfg-group" }, t("config.group.auto")),
|
|
1869
|
+
// 单位后缀统一挂输入框右侧(与状态标签同基线),不再只藏在说明文字里;
|
|
1870
|
+
// 传词表键而非字面量——单位词也是语言事实(条/小时 vs snapshots/hours)
|
|
1871
|
+
numRow("gcSnaps", t("config.gcSnaps.label"), t("config.gcSnaps.hint"), { suffixKey: "config.gcSnaps.suffix", min: 1, step: 1 }),
|
|
1872
|
+
numRow("gcHours", t("config.gcHours.label"), t("config.gcHours.hint"), { suffixKey: "config.gcHours.suffix", min: 1, step: 1 }),
|
|
1873
|
+
numRow("maxFileBytes", t("config.maxFileBytes.label"), t("config.maxFileBytes.hint"), { suffixKey: "config.maxFileBytes.suffix", min: 0.01, step: 0.5 }),
|
|
1874
|
+
numRow("maxSnapshotsPerWorkspace", t("config.maxSnapshotsPerWorkspace.label"), t("config.maxSnapshotsPerWorkspace.hint"), { suffixKey: "config.maxSnapshotsPerWorkspace.suffix", min: 0, step: 1 }),
|
|
1875
|
+
numRow("retentionDays", t("config.retentionDays.label"), t("config.retentionDays.hint"), { suffixKey: "config.retentionDays.suffix", min: 0, step: 1 }),
|
|
1876
|
+
React.createElement("div", { className: "dsh-recall-cfg-group" }, t("config.group.ui")),
|
|
1877
|
+
localeRow(),
|
|
1263
1878
|
// 操作区在「基础排除表」折叠头之前:按钮服务整个表单(含折叠区之外的字段),
|
|
1264
1879
|
// 排在折叠头之后会被误读为折叠区内容、折叠时像漏收起(用户实测反馈);
|
|
1265
1880
|
// 因此也不把按钮藏进折叠分支——否则折叠基础排除表后将无法保存。
|
|
1266
1881
|
React.createElement(
|
|
1267
1882
|
"div",
|
|
1268
1883
|
{ 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) }, "
|
|
1884
|
+
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,
|
|
1885
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy || !writable, onClick: () => setDraft(baseline ? Object.assign({}, baseline) : null) }, t("common.discard")),
|
|
1271
1886
|
React.createElement("button", {
|
|
1272
1887
|
type: "button",
|
|
1273
1888
|
className: "dsh-recall-btn",
|
|
1274
1889
|
disabled: state.busy || !writable,
|
|
1275
|
-
title: "
|
|
1890
|
+
title: t("config.reset.title"),
|
|
1276
1891
|
onClick: resetDefaults
|
|
1277
|
-
}, "
|
|
1892
|
+
}, t("config.reset")),
|
|
1278
1893
|
// 「保存」是表单唯一主动作,升主色实心按钮;放弃修改/恢复默认维持次级
|
|
1279
1894
|
// 灰底(同排按钮只有一个视觉焦点,配色令牌经官方主题产物核验,见 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" }, "
|
|
1895
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: state.busy || !writable, onClick: save }, t("common.save")),
|
|
1896
|
+
!writable ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.readonly")) : null
|
|
1282
1897
|
),
|
|
1283
1898
|
// divider:本条是折叠分区列表的首项,上方分界线把「字段表单」与「折叠
|
|
1284
1899
|
// 分区」分开(否则保存按钮行紧贴折叠头,读起来像同一组字段)
|
|
1285
|
-
React.createElement(SectionToggle, { title: "
|
|
1900
|
+
React.createElement(SectionToggle, { title: t("config.advanced.title"), open: showAdvanced, onToggle: () => setShowAdvanced((v) => !v), divider: true }),
|
|
1286
1901
|
showAdvanced ? React.createElement(
|
|
1287
1902
|
"div",
|
|
1288
1903
|
{ className: "dsh-recall-cfg-row", key: "baseExcludes" },
|
|
1289
1904
|
// V4:标签上提(与 numRow 同法);cfg-line 只剩 tags,textarea/hint 通栏
|
|
1290
|
-
React.createElement("span", { className: "dsh-recall-cfg-label", id: "dsh-recall-cfg-label-baseExcludes" }, "
|
|
1905
|
+
React.createElement("span", { className: "dsh-recall-cfg-label", id: "dsh-recall-cfg-label-baseExcludes" }, t("config.baseExcludes.label")),
|
|
1291
1906
|
React.createElement(
|
|
1292
1907
|
"div",
|
|
1293
1908
|
{ 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" }, "
|
|
1909
|
+
draft.baseExcludes !== baseline.baseExcludes ? React.createElement("span", { className: "dsh-recall-cfg-tag dsh-recall-cfg-tag-modified" }, t("config.tag.modified")) : null,
|
|
1910
|
+
overridden && overridden.baseExcludes !== void 0 ? React.createElement("span", { className: "dsh-recall-cfg-tag" }, t("config.tag.overridden")) : null
|
|
1296
1911
|
),
|
|
1297
1912
|
// textarea/hint 加 cfg-span 通栏:折叠区在共享 grid 内展开时,内容若只占
|
|
1298
1913
|
// 第二列,左侧标签列会成为竖直死区(实测 textarea 被挤窄);通栏后与
|
|
@@ -1307,7 +1922,7 @@
|
|
|
1307
1922
|
disabled: !writable,
|
|
1308
1923
|
onChange: (e) => edit("baseExcludes", e.target.value)
|
|
1309
1924
|
}),
|
|
1310
|
-
React.createElement("div", { className: "dsh-recall-cfg-hint dsh-recall-cfg-span" }, "
|
|
1925
|
+
React.createElement("div", { className: "dsh-recall-cfg-hint dsh-recall-cfg-span" }, t("config.baseExcludes.hint"))
|
|
1311
1926
|
) : null
|
|
1312
1927
|
)
|
|
1313
1928
|
);
|
|
@@ -1318,7 +1933,7 @@
|
|
|
1318
1933
|
// src/client/exclude-card.ts
|
|
1319
1934
|
var EXCLUDE_SUGGESTIONS = ["dist/", "build/", "out/", "coverage/", "*.log", ".env"];
|
|
1320
1935
|
function buildExcludeCards(React, util) {
|
|
1321
|
-
const { api } = util;
|
|
1936
|
+
const { api, t } = util;
|
|
1322
1937
|
function ExcludeCard(props) {
|
|
1323
1938
|
const file = props.file;
|
|
1324
1939
|
const [draft, setDraft] = React.useState(file.content || "");
|
|
@@ -1331,20 +1946,20 @@
|
|
|
1331
1946
|
setDraft((d) => (d && !d.endsWith("\n") ? d + "\n" : d) + pattern + "\n");
|
|
1332
1947
|
}
|
|
1333
1948
|
function addQuick() {
|
|
1334
|
-
const
|
|
1335
|
-
if (!
|
|
1336
|
-
appendPattern(
|
|
1949
|
+
const pattern = quick.trim();
|
|
1950
|
+
if (!pattern) return;
|
|
1951
|
+
appendPattern(pattern);
|
|
1337
1952
|
setQuick("");
|
|
1338
1953
|
}
|
|
1339
1954
|
function save() {
|
|
1340
1955
|
if (state.busy || !dirty) return;
|
|
1341
|
-
setState({ busy: true, message: "
|
|
1956
|
+
setState({ busy: true, message: t("exclude.msg.saving"), error: false });
|
|
1342
1957
|
api("exclude-set", { path: file.path, content: draft }).then((res) => {
|
|
1343
1958
|
if (res && res.ok) {
|
|
1344
1959
|
setBaseline(draft);
|
|
1345
|
-
setState({ busy: false, message: "
|
|
1960
|
+
setState({ busy: false, message: t("exclude.msg.saved"), error: false });
|
|
1346
1961
|
} else {
|
|
1347
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
1962
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("exclude.msg.saveFailed"), error: true });
|
|
1348
1963
|
}
|
|
1349
1964
|
}).catch((error) => {
|
|
1350
1965
|
setState({ busy: false, message: String(error), error: true });
|
|
@@ -1367,13 +1982,13 @@
|
|
|
1367
1982
|
React.createElement(
|
|
1368
1983
|
"div",
|
|
1369
1984
|
{ className: "dsh-recall-ex-note" },
|
|
1370
|
-
file.home ? "
|
|
1985
|
+
file.home ? t("exclude.note.home") : t("exclude.note.fallback")
|
|
1371
1986
|
),
|
|
1372
|
-
React.createElement("div", { className: "dsh-recall-ex-path" }, "
|
|
1373
|
-
React.createElement("div", { className: "dsh-recall-ex-note" }, "
|
|
1987
|
+
React.createElement("div", { className: "dsh-recall-ex-path" }, t("exclude.path", { path: file.path })),
|
|
1988
|
+
React.createElement("div", { className: "dsh-recall-ex-note" }, t("exclude.syntax")),
|
|
1374
1989
|
React.createElement("textarea", {
|
|
1375
1990
|
className: "dsh-recall-ex-area",
|
|
1376
|
-
"aria-label": "
|
|
1991
|
+
"aria-label": t("exclude.area.aria"),
|
|
1377
1992
|
value: draft,
|
|
1378
1993
|
spellCheck: false,
|
|
1379
1994
|
onChange: (e) => setDraft(e.target.value)
|
|
@@ -1384,8 +1999,8 @@
|
|
|
1384
1999
|
React.createElement("input", {
|
|
1385
2000
|
className: "dsh-recall-ex-input",
|
|
1386
2001
|
value: quick,
|
|
1387
|
-
placeholder: "
|
|
1388
|
-
"aria-label": "
|
|
2002
|
+
placeholder: t("exclude.quick.placeholder"),
|
|
2003
|
+
"aria-label": t("exclude.quick.aria"),
|
|
1389
2004
|
onChange: (e) => setQuick(e.target.value),
|
|
1390
2005
|
onKeyDown: (e) => {
|
|
1391
2006
|
if (e.key === "Enter") {
|
|
@@ -1394,23 +2009,23 @@
|
|
|
1394
2009
|
}
|
|
1395
2010
|
}
|
|
1396
2011
|
}),
|
|
1397
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: addQuick }, "
|
|
2012
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: addQuick }, t("exclude.add")),
|
|
1398
2013
|
...suggestions.map((s) => React.createElement("button", {
|
|
1399
2014
|
key: "chip-" + s,
|
|
1400
2015
|
type: "button",
|
|
1401
2016
|
className: "dsh-recall-ex-chip",
|
|
1402
|
-
title: "
|
|
2017
|
+
title: t("exclude.chip.title", { pattern: s }),
|
|
1403
2018
|
onClick: () => appendPattern(s)
|
|
1404
2019
|
}, s))
|
|
1405
2020
|
),
|
|
1406
2021
|
React.createElement(
|
|
1407
2022
|
"div",
|
|
1408
2023
|
{ 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 }, "
|
|
2024
|
+
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,
|
|
2025
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: !dirty || state.busy, onClick: discard }, t("common.discard")),
|
|
1411
2026
|
// 保存升主色实心(与配置表单同一约定:每卡唯一主动作,对齐官方插件卡
|
|
1412
2027
|
// footer 的 discard 幽灵 + save 实心组合)
|
|
1413
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: !dirty || state.busy, onClick: save }, "
|
|
2028
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn dsh-recall-btn-primary", disabled: !dirty || state.busy, onClick: save }, t("common.save"))
|
|
1414
2029
|
)
|
|
1415
2030
|
);
|
|
1416
2031
|
}
|
|
@@ -1425,10 +2040,10 @@
|
|
|
1425
2040
|
return;
|
|
1426
2041
|
}
|
|
1427
2042
|
if (res && res.unsupported) {
|
|
1428
|
-
setError("
|
|
2043
|
+
setError(t("exclude.unsupported"));
|
|
1429
2044
|
return;
|
|
1430
2045
|
}
|
|
1431
|
-
setError(res && (res.message || res.error) || "
|
|
2046
|
+
setError(res && (res.message || res.error) || t("exclude.loadFailed"));
|
|
1432
2047
|
}).catch((e) => setError(String(e)));
|
|
1433
2048
|
}
|
|
1434
2049
|
React.useEffect(() => {
|
|
@@ -1442,15 +2057,15 @@
|
|
|
1442
2057
|
React.createElement(
|
|
1443
2058
|
"div",
|
|
1444
2059
|
{ className: "dsh-recall-panel-actions" },
|
|
1445
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: load }, "
|
|
2060
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", onClick: load }, t("common.retry"))
|
|
1446
2061
|
)
|
|
1447
2062
|
);
|
|
1448
2063
|
}
|
|
1449
2064
|
if (files === null) {
|
|
1450
|
-
return React.createElement("div", { className: "dsh-recall-ex-note" }, "
|
|
2065
|
+
return React.createElement("div", { className: "dsh-recall-ex-note" }, t("exclude.loading"));
|
|
1451
2066
|
}
|
|
1452
2067
|
if (!files.length) {
|
|
1453
|
-
return React.createElement("div", { className: "dsh-recall-ex-note" }, "
|
|
2068
|
+
return React.createElement("div", { className: "dsh-recall-ex-note" }, t("exclude.empty"));
|
|
1454
2069
|
}
|
|
1455
2070
|
return React.createElement(
|
|
1456
2071
|
"div",
|
|
@@ -1502,7 +2117,7 @@
|
|
|
1502
2117
|
return result;
|
|
1503
2118
|
}
|
|
1504
2119
|
function buildSnapshotManager(React, util, sessionsSvc, workspacesSvc, uiWorkspaceSvc) {
|
|
1505
|
-
const { api, clockText: clockText2, sizeText: sizeText2, buildTree: buildTree2 } = util;
|
|
2120
|
+
const { api, clockText: clockText2, sizeText: sizeText2, buildTree: buildTree2, t } = util;
|
|
1506
2121
|
function DeleteButton(props) {
|
|
1507
2122
|
return React.createElement("button", {
|
|
1508
2123
|
type: "button",
|
|
@@ -1637,16 +2252,16 @@
|
|
|
1637
2252
|
api("status", { op: "clear" }).catch(() => {
|
|
1638
2253
|
});
|
|
1639
2254
|
}
|
|
1640
|
-
function run(op, extra,
|
|
2255
|
+
function run(op, extra, doneKey) {
|
|
1641
2256
|
if (state.busy) return;
|
|
1642
|
-
setState({ busy: true, message: "
|
|
2257
|
+
setState({ busy: true, message: t("manage.busy"), error: false });
|
|
1643
2258
|
api("manage", Object.assign({ op }, extra || {})).then((res) => {
|
|
1644
2259
|
if (res && res.ok) {
|
|
1645
2260
|
const deleted = res.deleted;
|
|
1646
|
-
setState({ busy: false, message: typeof deleted === "number" ? "
|
|
2261
|
+
setState({ busy: false, message: typeof deleted === "number" ? t("manage.deletedCount", { deleted }) : t(doneKey), error: false });
|
|
1647
2262
|
refresh();
|
|
1648
2263
|
} else {
|
|
1649
|
-
setState({ busy: false, message: res && (res.message || res.error) || "
|
|
2264
|
+
setState({ busy: false, message: res && (res.message || res.error) || t("common.opFailed"), error: true });
|
|
1650
2265
|
}
|
|
1651
2266
|
}).catch((e) => setState({ busy: false, message: String(e), error: true }));
|
|
1652
2267
|
}
|
|
@@ -1657,17 +2272,17 @@
|
|
|
1657
2272
|
"div",
|
|
1658
2273
|
{ className: "dsh-recall-tree-confirm" },
|
|
1659
2274
|
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 }, "
|
|
2275
|
+
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip dsh-recall-ex-chip-danger", onClick: props.onConfirm }, t("common.confirm")),
|
|
2276
|
+
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: props.onCancel }, t("common.cancel"))
|
|
1662
2277
|
);
|
|
1663
2278
|
}
|
|
1664
2279
|
function renderDeleteAllConfirm() {
|
|
1665
2280
|
if (!confirming || confirming.kind !== "all") return null;
|
|
1666
2281
|
return React.createElement(ConfirmRow, {
|
|
1667
|
-
text: "
|
|
2282
|
+
text: t("manage.confirm.all"),
|
|
1668
2283
|
onConfirm: () => {
|
|
1669
2284
|
setConfirming(null);
|
|
1670
|
-
run("deleteAll", {}, "
|
|
2285
|
+
run("deleteAll", {}, "manage.done.deleteAll");
|
|
1671
2286
|
},
|
|
1672
2287
|
onCancel: () => setConfirming(null)
|
|
1673
2288
|
});
|
|
@@ -1684,7 +2299,7 @@
|
|
|
1684
2299
|
const filteredItems = q ? (items || []).filter(
|
|
1685
2300
|
(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
2301
|
) : items;
|
|
1687
|
-
const tree = buildTree2(filteredItems);
|
|
2302
|
+
const tree = buildTree2(filteredItems, t);
|
|
1688
2303
|
const allSessionIds = Array.from(new Set((items || []).map((it) => it.sessionId).filter(Boolean)));
|
|
1689
2304
|
const versionMap = groupByLineage(allSessionIds, lineage);
|
|
1690
2305
|
let listById = null;
|
|
@@ -1703,17 +2318,17 @@
|
|
|
1703
2318
|
} catch (e) {
|
|
1704
2319
|
archivedIds = null;
|
|
1705
2320
|
}
|
|
1706
|
-
function confirmDelete(kind, key, extra,
|
|
1707
|
-
setConfirming({ kind, key, extra, text });
|
|
2321
|
+
function confirmDelete(kind, key, extra, textKey) {
|
|
2322
|
+
setConfirming({ kind, key, extra, text: textKey });
|
|
1708
2323
|
}
|
|
1709
|
-
function renderConfirm(kind, key, extra,
|
|
2324
|
+
function renderConfirm(kind, key, extra, textKey) {
|
|
1710
2325
|
if (!confirming || confirming.kind !== kind || confirming.key !== key) return null;
|
|
1711
2326
|
return React.createElement(ConfirmRow, {
|
|
1712
|
-
text,
|
|
2327
|
+
text: t(textKey),
|
|
1713
2328
|
onConfirm: () => {
|
|
1714
2329
|
const c = confirming;
|
|
1715
2330
|
setConfirming(null);
|
|
1716
|
-
run("delete", c.extra, "
|
|
2331
|
+
run("delete", c.extra, "manage.done.delete");
|
|
1717
2332
|
},
|
|
1718
2333
|
onCancel: () => setConfirming(null)
|
|
1719
2334
|
});
|
|
@@ -1737,18 +2352,18 @@
|
|
|
1737
2352
|
// 删除紧贴本条消息摘要(而非行尾):按行扫读时动作落在「看的那一行」,
|
|
1738
2353
|
// 不必横跳到右侧同列按钮再回读行号
|
|
1739
2354
|
React.createElement(DeleteButton, {
|
|
1740
|
-
title: "
|
|
1741
|
-
onClick: () => confirmDelete("snapshot", key, { messageId: it.id, root: it.root || null }, "
|
|
2355
|
+
title: t("tree.delete.snapshot.title"),
|
|
2356
|
+
onClick: () => confirmDelete("snapshot", key, { messageId: it.id, root: it.root || null }, "tree.delete.snapshot.confirm")
|
|
1742
2357
|
})
|
|
1743
2358
|
)
|
|
1744
2359
|
),
|
|
1745
|
-
renderConfirm("snapshot", key, { messageId: it.id, root: it.root || null }, "
|
|
2360
|
+
renderConfirm("snapshot", key, { messageId: it.id, root: it.root || null }, "tree.delete.snapshot.confirm")
|
|
1746
2361
|
);
|
|
1747
2362
|
}
|
|
1748
2363
|
function renderSession(s) {
|
|
1749
2364
|
const key = "session-" + (s.root || "") + "-" + s.sessionId;
|
|
1750
2365
|
const open = expanded.has(key);
|
|
1751
|
-
const label = s.title || (titlesPending && s.sessionId ? "\u2026" : "
|
|
2366
|
+
const label = s.title || (titlesPending && s.sessionId ? "\u2026" : t("tree.deletedSession"));
|
|
1752
2367
|
const version = s.sessionId ? versionMap.get(String(s.sessionId)) : null;
|
|
1753
2368
|
const switchable = Boolean(s.sessionId && listById && listById[s.sessionId] && !(archivedIds && archivedIds.has(String(s.sessionId))));
|
|
1754
2369
|
return React.createElement(
|
|
@@ -1766,7 +2381,7 @@
|
|
|
1766
2381
|
type: "button",
|
|
1767
2382
|
className: "dsh-recall-tree-toggle",
|
|
1768
2383
|
"aria-expanded": open,
|
|
1769
|
-
"aria-label": (open ? "
|
|
2384
|
+
"aria-label": t(open ? "tree.collapse" : "tree.expand", { label }),
|
|
1770
2385
|
onClick: (e) => {
|
|
1771
2386
|
e.stopPropagation();
|
|
1772
2387
|
toggle(key);
|
|
@@ -1776,11 +2391,11 @@
|
|
|
1776
2391
|
"span",
|
|
1777
2392
|
{ className: "dsh-recall-tree-label", title: s.sessionId || "" },
|
|
1778
2393
|
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
|
|
2394
|
+
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,
|
|
2395
|
+
React.createElement("span", { className: "dsh-recall-tree-meta" }, t("tree.snapCount", { n: s.items.length })),
|
|
1781
2396
|
s.sessionId ? React.createElement(DeleteButton, {
|
|
1782
|
-
title: "
|
|
1783
|
-
onClick: () => confirmDelete("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "
|
|
2397
|
+
title: t("tree.delete.session.title"),
|
|
2398
|
+
onClick: () => confirmDelete("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "tree.delete.session.confirm")
|
|
1784
2399
|
}) : null
|
|
1785
2400
|
),
|
|
1786
2401
|
// 「切换」留在行尾单占右侧:它是导航动作(离开当前视图),与行内
|
|
@@ -1788,7 +2403,7 @@
|
|
|
1788
2403
|
switchable ? React.createElement("button", {
|
|
1789
2404
|
type: "button",
|
|
1790
2405
|
className: "dsh-recall-ex-chip",
|
|
1791
|
-
title: "
|
|
2406
|
+
title: t("tree.switch.title"),
|
|
1792
2407
|
onClick: (ev) => {
|
|
1793
2408
|
ev.stopPropagation();
|
|
1794
2409
|
try {
|
|
@@ -1797,10 +2412,10 @@
|
|
|
1797
2412
|
} catch (e) {
|
|
1798
2413
|
}
|
|
1799
2414
|
}
|
|
1800
|
-
}, "
|
|
2415
|
+
}, t("tree.switch")) : null
|
|
1801
2416
|
),
|
|
1802
2417
|
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 }, "
|
|
2418
|
+
s.sessionId ? renderConfirm("session", key, { scope: "session", sessionId: s.sessionId, root: s.root || null }, "tree.delete.session.confirm") : null
|
|
1804
2419
|
);
|
|
1805
2420
|
}
|
|
1806
2421
|
function renderWorkspace(ws) {
|
|
@@ -1820,7 +2435,7 @@
|
|
|
1820
2435
|
type: "button",
|
|
1821
2436
|
className: "dsh-recall-tree-toggle",
|
|
1822
2437
|
"aria-expanded": open,
|
|
1823
|
-
"aria-label": (open ? "
|
|
2438
|
+
"aria-label": t(open ? "tree.collapse" : "tree.expand", { label: ws.name }),
|
|
1824
2439
|
onClick: (e) => {
|
|
1825
2440
|
e.stopPropagation();
|
|
1826
2441
|
toggle(key);
|
|
@@ -1830,20 +2445,26 @@
|
|
|
1830
2445
|
"span",
|
|
1831
2446
|
{ className: "dsh-recall-tree-label", title: ws.root || "" },
|
|
1832
2447
|
React.createElement("span", { className: "dsh-recall-tree-name" }, ws.name),
|
|
1833
|
-
React.createElement("span", { className: "dsh-recall-tree-meta" },
|
|
2448
|
+
React.createElement("span", { className: "dsh-recall-tree-meta" }, t("tree.wsCount", { n: sessionCount, m: snapCount })),
|
|
1834
2449
|
ws.root ? React.createElement(DeleteButton, {
|
|
1835
|
-
title: "
|
|
1836
|
-
onClick: () => confirmDelete("workspace", key, { scope: "workspace", root: ws.root }, "
|
|
2450
|
+
title: t("tree.delete.workspace.title"),
|
|
2451
|
+
onClick: () => confirmDelete("workspace", key, { scope: "workspace", root: ws.root }, "tree.delete.workspace.confirm")
|
|
1837
2452
|
}) : null
|
|
1838
2453
|
)
|
|
1839
2454
|
),
|
|
1840
2455
|
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 }, "
|
|
2456
|
+
ws.root ? renderConfirm("workspace", key, { scope: "workspace", root: ws.root }, "tree.delete.workspace.confirm") : null
|
|
1842
2457
|
);
|
|
1843
2458
|
}
|
|
1844
2459
|
const treeNodes = tree.map(renderWorkspace);
|
|
1845
2460
|
const loaded = items ? items.length : null;
|
|
1846
|
-
const countText = loaded === null ? "
|
|
2461
|
+
const countText = loaded === null ? t("manage.countLoading") : t("manage.countLoaded", { total }) + (limit < total ? t("manage.countShown", { shown: loaded }) : "");
|
|
2462
|
+
function errorLine(e, key) {
|
|
2463
|
+
const kindKey = e.kind ? "errorKind." + e.kind : "";
|
|
2464
|
+
const localized = kindKey !== "" && hasTranslation(kindKey);
|
|
2465
|
+
const base = localized ? t(kindKey) + (typeof e.count === "number" && e.count > 1 ? t("manage.errors.dup", { n: e.count }) : "") : String(e.hint || e.message || "");
|
|
2466
|
+
return React.createElement("div", { className: "dsh-recall-ex-note", key, title: e.message || "" }, clockText2(e.time) + " " + base);
|
|
2467
|
+
}
|
|
1847
2468
|
function loadMore() {
|
|
1848
2469
|
const next = Math.min(Math.max(total, limit), 2e3);
|
|
1849
2470
|
if (next <= limit) return;
|
|
@@ -1859,7 +2480,7 @@
|
|
|
1859
2480
|
React.createElement(
|
|
1860
2481
|
"div",
|
|
1861
2482
|
{ className: "dsh-recall-ex-note" },
|
|
1862
|
-
usage === null ? countText + "
|
|
2483
|
+
usage === null ? countText + t("manage.countEnd") : countText + t("manage.usageSuffix", { size: sizeText2(usage) })
|
|
1863
2484
|
),
|
|
1864
2485
|
// V6 健康行徽章化:git 状态用彩色 pill(成功/失败,官方状态行配对),
|
|
1865
2486
|
// 从普通 note 提升为卡片顶部横幅(渲染在搜索框与树之前);存储计数
|
|
@@ -1869,9 +2490,9 @@
|
|
|
1869
2490
|
{ className: "dsh-recall-ex-note", key: "health" },
|
|
1870
2491
|
React.createElement("span", {
|
|
1871
2492
|
className: "dsh-recall-health-pill " + (health.gitAvailable ? "dsh-recall-health-pill-ok" : "dsh-recall-health-pill-bad"),
|
|
1872
|
-
title: "
|
|
1873
|
-
}, health.gitAvailable ? "
|
|
1874
|
-
"
|
|
2493
|
+
title: t("manage.health.gitTitle")
|
|
2494
|
+
}, t(health.gitAvailable ? "manage.health.gitOk" : "manage.health.gitBad")),
|
|
2495
|
+
t("manage.health.stores", { n: health.homeStores }) + (health.fallbackStores ? t("manage.health.storesFallback", { n: health.fallbackStores }) : "")
|
|
1875
2496
|
) : null,
|
|
1876
2497
|
// 搜索行:图标绝对定位在框内左侧(pointer-events:none 不挡点击),输入框
|
|
1877
2498
|
// 靠 padding-left 让出图标位;高度由 CSS 的 .dsh-recall-search 覆写加高 20%
|
|
@@ -1898,8 +2519,8 @@
|
|
|
1898
2519
|
),
|
|
1899
2520
|
React.createElement("input", {
|
|
1900
2521
|
className: "dsh-recall-ex-input",
|
|
1901
|
-
placeholder: "
|
|
1902
|
-
"aria-label": "
|
|
2522
|
+
placeholder: t("manage.search.placeholder"),
|
|
2523
|
+
"aria-label": t("manage.search.aria"),
|
|
1903
2524
|
value: query,
|
|
1904
2525
|
spellCheck: false,
|
|
1905
2526
|
onChange: (e) => setQuery(e.target.value)
|
|
@@ -1913,41 +2534,41 @@
|
|
|
1913
2534
|
...[1, 2, 3, 4, 5].map((n) => React.createElement("div", { key: "sk-" + n, className: "dsh-recall-tree-skeleton-row" }))
|
|
1914
2535
|
) : null,
|
|
1915
2536
|
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" }, "
|
|
2537
|
+
items && items.length === 0 && !q ? React.createElement("div", { className: "dsh-recall-empty", key: "empty" }, t("manage.empty")) : null,
|
|
2538
|
+
q && filteredItems && filteredItems.length === 0 ? React.createElement("div", { className: "dsh-recall-empty", key: "no-match" }, t("manage.emptyFiltered")) : null,
|
|
1918
2539
|
renderDeleteAllConfirm(),
|
|
1919
2540
|
// V6:错误区从卡片最底上移到操作区上方(fail-loud 可见性);标题
|
|
1920
2541
|
// error 色带条数徽章,时间戳格式维持原样
|
|
1921
2542
|
errors && errors.length > 0 ? React.createElement(
|
|
1922
2543
|
"div",
|
|
1923
2544
|
{ key: "errors" },
|
|
1924
|
-
React.createElement("div", { className: "dsh-recall-errors-title" },
|
|
1925
|
-
(showAllErrors ? errors : errors.slice(0, 5)).map(
|
|
2545
|
+
React.createElement("div", { className: "dsh-recall-errors-title" }, t("manage.errors.title", { n: errors.length })),
|
|
2546
|
+
(showAllErrors ? errors : errors.slice(0, 5)).map(errorLine),
|
|
1926
2547
|
React.createElement(
|
|
1927
2548
|
"div",
|
|
1928
2549
|
{ 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 }, "
|
|
2550
|
+
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,
|
|
2551
|
+
React.createElement("button", { type: "button", className: "dsh-recall-ex-chip", onClick: clearErrors }, t("manage.errors.clear"))
|
|
1931
2552
|
)
|
|
1932
2553
|
) : null,
|
|
1933
2554
|
React.createElement(
|
|
1934
2555
|
"div",
|
|
1935
2556
|
{ 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 ? "
|
|
2557
|
+
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
2558
|
limit < total ? React.createElement("button", {
|
|
1938
2559
|
type: "button",
|
|
1939
2560
|
className: "dsh-recall-btn",
|
|
1940
2561
|
disabled: state.busy,
|
|
1941
2562
|
onClick: loadMore
|
|
1942
|
-
}, "
|
|
1943
|
-
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy, onClick: refresh }, "
|
|
2563
|
+
}, t("manage.loadMore")) : null,
|
|
2564
|
+
React.createElement("button", { type: "button", className: "dsh-recall-btn", disabled: state.busy, onClick: () => refresh() }, t("manage.refresh")),
|
|
1944
2565
|
React.createElement("button", {
|
|
1945
2566
|
type: "button",
|
|
1946
2567
|
className: "dsh-recall-btn",
|
|
1947
2568
|
disabled: state.busy,
|
|
1948
|
-
title: "
|
|
1949
|
-
onClick: () => run("gc", {}, "gc
|
|
1950
|
-
}, "
|
|
2569
|
+
title: t("manage.gc.title"),
|
|
2570
|
+
onClick: () => run("gc", {}, "manage.done.gc")
|
|
2571
|
+
}, t("manage.gc")),
|
|
1951
2572
|
// V5:全部删除固定为操作区最后一个按钮(排在立即 gc 之后)——即使
|
|
1952
2573
|
// 「加载更多」出现/消失也不漂移;danger 与普通按钮间在 panel-actions
|
|
1953
2574
|
// 统一 gap:8px 之上再加 btn-gap 的 8px 物理间隔,危险按钮与常规按钮
|
|
@@ -1956,9 +2577,9 @@
|
|
|
1956
2577
|
type: "button",
|
|
1957
2578
|
className: "dsh-recall-btn dsh-recall-btn-danger dsh-recall-btn-gap",
|
|
1958
2579
|
disabled: state.busy,
|
|
1959
|
-
title: "
|
|
2580
|
+
title: t("manage.deleteAll.title"),
|
|
1960
2581
|
onClick: () => setConfirming({ kind: "all" })
|
|
1961
|
-
}, "
|
|
2582
|
+
}, t("manage.deleteAll"))
|
|
1962
2583
|
)
|
|
1963
2584
|
);
|
|
1964
2585
|
}
|
|
@@ -2000,8 +2621,11 @@
|
|
|
2000
2621
|
const { ConfigForm } = buildConfigForm(React, util, SectionToggle);
|
|
2001
2622
|
const { ExcludeFilesSection } = buildExcludeCards(React, util);
|
|
2002
2623
|
const { ManageCard } = buildSnapshotManager(React, util, sessionsSvc, workspacesSvc, uiWorkspaceSvc);
|
|
2624
|
+
const { t } = util;
|
|
2003
2625
|
function RecallSettingsCard() {
|
|
2004
2626
|
const [sections, setSections] = React.useState({ exclude: false, manage: false });
|
|
2627
|
+
const [, setLocaleTick] = React.useState(0);
|
|
2628
|
+
const notifyLocale = React.useCallback(() => setLocaleTick((n) => n + 1), []);
|
|
2005
2629
|
function toggle(key) {
|
|
2006
2630
|
setSections((prev) => Object.assign({}, prev, { [key]: !prev[key] }));
|
|
2007
2631
|
}
|
|
@@ -2011,14 +2635,14 @@
|
|
|
2011
2635
|
React.createElement(
|
|
2012
2636
|
"div",
|
|
2013
2637
|
{ className: "dsh-recall-settings-body" },
|
|
2014
|
-
React.createElement(ConfigForm),
|
|
2015
|
-
React.createElement(SectionToggle, { title: "
|
|
2638
|
+
React.createElement(ConfigForm, { onLocaleApplied: notifyLocale }),
|
|
2639
|
+
React.createElement(SectionToggle, { title: t("section.exclude"), open: sections.exclude, onToggle: () => toggle("exclude") }),
|
|
2016
2640
|
// section-body 只做纵向间距与展开入场动画,不带描边/底色:外层卡片框
|
|
2017
2641
|
// 去掉后内容区再套一层框等于把「去包裹」加回来(用户实测反馈)。「高级:
|
|
2018
2642
|
// 基础排除表」不包——它在 cfg-grid 内本就走通栏,且 label 列对齐依赖
|
|
2019
2643
|
// grid item 身份,包容器会破坏跨行对齐。
|
|
2020
2644
|
sections.exclude ? React.createElement("div", { className: "dsh-recall-section-body" }, React.createElement(ExcludeFilesSection)) : null,
|
|
2021
|
-
React.createElement(SectionToggle, { title: "
|
|
2645
|
+
React.createElement(SectionToggle, { title: t("section.manage"), open: sections.manage, onToggle: () => toggle("manage") }),
|
|
2022
2646
|
sections.manage ? React.createElement("div", { className: "dsh-recall-section-body" }, React.createElement(ManageCard)) : null
|
|
2023
2647
|
)
|
|
2024
2648
|
);
|
|
@@ -2066,7 +2690,7 @@
|
|
|
2066
2690
|
);
|
|
2067
2691
|
});
|
|
2068
2692
|
} catch (error) {
|
|
2069
|
-
|
|
2693
|
+
appLog.error("slot register failed (" + slotKey + "):", error);
|
|
2070
2694
|
}
|
|
2071
2695
|
}
|
|
2072
2696
|
try {
|
|
@@ -2075,7 +2699,7 @@
|
|
|
2075
2699
|
RecallSettingsCard
|
|
2076
2700
|
));
|
|
2077
2701
|
} catch (error) {
|
|
2078
|
-
|
|
2702
|
+
appLog.error("settings card register failed:", error);
|
|
2079
2703
|
}
|
|
2080
2704
|
try {
|
|
2081
2705
|
slots.inject("plugins.bundle.config", () => slots.register(
|
|
@@ -2083,8 +2707,9 @@
|
|
|
2083
2707
|
RecallSettingsCard
|
|
2084
2708
|
));
|
|
2085
2709
|
} catch (error) {
|
|
2086
|
-
|
|
2710
|
+
appLog.error("plugin manager config register failed:", error);
|
|
2087
2711
|
}
|
|
2712
|
+
appLog.info("client applied");
|
|
2088
2713
|
};
|
|
2089
2714
|
}
|
|
2090
2715
|
|