dsh-prime-memory 0.22.1 → 0.22.2

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.
Files changed (62) hide show
  1. package/dist/client.js +1581 -1153
  2. package/dist/config.d.ts +149 -0
  3. package/dist/config.js +35 -0
  4. package/dist/conflict-service.d.ts +8 -0
  5. package/dist/conflict-service.js +19 -0
  6. package/dist/contract.d.ts +131 -0
  7. package/dist/domain-gate.d.ts +8 -0
  8. package/dist/domain-gate.js +30 -0
  9. package/dist/graph/constraints.d.ts +4 -2
  10. package/dist/graph/constraints.js +4 -2
  11. package/dist/hooks/recall.d.ts +2 -0
  12. package/dist/hooks/recall.js +20 -1
  13. package/dist/hooks/skill-recall.d.ts +25 -0
  14. package/dist/hooks/skill-recall.js +72 -0
  15. package/dist/index.d.ts +98 -0
  16. package/dist/index.js +26 -1
  17. package/dist/memport/format.d.ts +70 -0
  18. package/dist/memport/format.js +305 -0
  19. package/dist/memport/prefilter.d.ts +62 -0
  20. package/dist/memport/prefilter.js +132 -0
  21. package/dist/memport/scan.d.ts +13 -0
  22. package/dist/memport/scan.js +100 -0
  23. package/dist/metadata-validators.d.ts +22 -0
  24. package/dist/metadata-validators.js +45 -0
  25. package/dist/pipeline/l1.js +16 -2
  26. package/dist/pipeline/ruminate.d.ts +3 -0
  27. package/dist/pipeline/ruminate.js +50 -1
  28. package/dist/pipeline/slot-migrate.d.ts +45 -0
  29. package/dist/pipeline/slot-migrate.js +45 -0
  30. package/dist/projection/recall.d.ts +74 -0
  31. package/dist/projection/recall.js +148 -0
  32. package/dist/projection/slots.d.ts +5 -0
  33. package/dist/prompts/graph-projection.js +3 -0
  34. package/dist/prompts/l1-extraction.d.ts +4 -1
  35. package/dist/prompts/l1-extraction.js +27 -4
  36. package/dist/settings.js +3 -0
  37. package/dist/skills/crystallize.d.ts +41 -0
  38. package/dist/skills/crystallize.js +75 -0
  39. package/dist/stats.d.ts +18 -1
  40. package/dist/stats.js +184 -5
  41. package/dist/store/file-versions.d.ts +2 -0
  42. package/dist/store/file-versions.js +2 -0
  43. package/dist/store/l1.d.ts +2 -0
  44. package/dist/store/l1.js +4 -0
  45. package/dist/store/memory-sources.d.ts +59 -0
  46. package/dist/store/memory-sources.js +214 -0
  47. package/dist/store/session-recalled.d.ts +25 -0
  48. package/dist/store/session-recalled.js +163 -0
  49. package/dist/store/skill-store.d.ts +99 -0
  50. package/dist/store/skill-store.js +330 -0
  51. package/dist/store/sqlite.d.ts +5 -0
  52. package/dist/store/sqlite.js +30 -4
  53. package/dist/tools/index.d.ts +0 -13
  54. package/dist/tools/index.js +58 -7
  55. package/dist/tools/skills.d.ts +13 -0
  56. package/dist/tools/skills.js +130 -0
  57. package/dist/types.d.ts +6 -0
  58. package/dist/util/maturity.d.ts +27 -0
  59. package/dist/util/maturity.js +26 -0
  60. package/dist/wing-backfill.js +7 -4
  61. package/dsh.plugin.json +1 -1
  62. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -32,7 +32,7 @@ var __defProp = Object.defineProperty;
32
32
  module.exports = __toCommonJS(entry_exports);
33
33
 
34
34
  // client/src/panel.tsx
35
- var import_react19 = require("react");
35
+ var import_react21 = require("react");
36
36
 
37
37
  // client/src/sidebar-icon.ts
38
38
  var BOOK_ICON_SVG = '<svg data-mem-icon="1" viewBox="0 0 16 16" width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" style="flex-shrink:0"><path d="M8 3.4C6.6 2.5 4.6 2.4 2.9 3.1v9.3c1.7-.7 3.7-.6 5.1.3 1.4-.9 3.4-1 5.1-.3V3.1C11.4 2.4 9.4 2.5 8 3.4Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/><path d="M8 3.4v9.3" stroke="currentColor" stroke-width="1.2"/></svg>';
@@ -68,12 +68,70 @@ var __defProp = Object.defineProperty;
68
68
  }
69
69
  }
70
70
 
71
+ // client/src/skin.ts
72
+ var SKIN_KEY = "dsh-mem-skin";
73
+ function rootEl() {
74
+ return document.documentElement;
75
+ }
76
+ function lsGet() {
77
+ try {
78
+ return localStorage.getItem(SKIN_KEY);
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+ function lsSet(v) {
84
+ try {
85
+ localStorage.setItem(SKIN_KEY, v);
86
+ } catch {
87
+ }
88
+ }
89
+ function hostDark() {
90
+ return typeof document !== "undefined" && document.body.hasAttribute("data-ds-dark-theme");
91
+ }
92
+ function sysDark() {
93
+ try {
94
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+ function effectiveSkin(skin) {
100
+ if (skin === "light") return "light";
101
+ if (skin === "dark") return "dark";
102
+ return hostDark() || sysDark() ? "dark" : "light";
103
+ }
104
+ function getSkin() {
105
+ const v = lsGet();
106
+ return v === "light" || v === "dark" ? v : "auto";
107
+ }
108
+ function setSkin(skin) {
109
+ lsSet(skin);
110
+ }
111
+ function commit(skin) {
112
+ rootEl().setAttribute("data-dsh-mem-skin", effectiveSkin(skin));
113
+ }
114
+ var hostWatched = false;
115
+ function ensureHostWatch() {
116
+ if (hostWatched) return;
117
+ hostWatched = true;
118
+ const obs = new MutationObserver(() => {
119
+ if (getSkin() === "auto") commit("auto");
120
+ });
121
+ obs.observe(document.body, { attributes: true, attributeFilter: ["data-ds-dark-theme"] });
122
+ }
123
+ function applySkin(skin) {
124
+ commit(skin);
125
+ ensureHostWatch();
126
+ return effectiveSkin(skin);
127
+ }
128
+
71
129
  // client/src/styles.ts
72
130
  var S = {
73
131
  section: { padding: "0 4px" },
74
132
  heading: { fontSize: 16, fontWeight: 600, margin: "0 0 4px", color: "var(--dsh-mem-text-1)" },
75
133
  intro: { fontSize: 13, color: "var(--dsh-mem-text-3)", margin: "0 0 12px" },
76
- tabbar: { display: "flex", gap: 2, borderBottom: "1px solid var(--dsh-mem-border)", marginBottom: 14 },
134
+ tabbar: { display: "flex", gap: 1, flexWrap: "nowrap", overflowX: "auto", borderBottom: "1px solid var(--dsh-mem-border)", marginBottom: 14 },
77
135
  error: {
78
136
  marginTop: 10,
79
137
  fontSize: 13,
@@ -208,275 +266,295 @@ var __defProp = Object.defineProperty;
208
266
 
209
267
  // client/src/theme.ts
210
268
  var THEME_STYLE_ID = "dsh-mem-theme-style";
211
- function ensureThemeStyle() {
212
- if (document.getElementById(THEME_STYLE_ID)) return;
213
- const el = document.createElement("style");
214
- el.id = THEME_STYLE_ID;
215
- el.textContent = [
216
- // conic 角度要 @property 注册才能插值动画;不支持 @property 的浏览器里
217
- // var(--dsh-mem-angle) 无定义 → conic 层失效 → background 整条退化为无背景
218
- //(光带边框与内底一起消失,只剩文字色)。2026 常青浏览器均已支持,仅作记录。
219
- "@property --dsh-mem-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; }",
220
- "@keyframes dshMemFlow { to { --dsh-mem-angle: 360deg; } }",
221
- // ── 令牌(浅色)。中性色链选用【真实存在】的 dsw 令牌(design-platform.css 校对):
222
- // bg-layer-2/3、bg-overlay、border-l1/l2/l3、border-inverted、label-*、
223
- // interactive-bg-hover、state-error-primary、tooltip-bg、dsw-shadow-lv1/lv3 ──
269
+ var LIGHT_TOK = [
270
+ " --dsh-mem-accent: #4d6bfe;",
271
+ " --dsh-mem-accent-text: #3d5be0;",
272
+ " --dsh-mem-accent-fill: #3d5be0;",
273
+ " --dsh-mem-accent-weak: rgba(77,107,254,0.10);",
274
+ " --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #ffffff);",
275
+ " --dsh-mem-bg-inset: var(--dsw-alias-bg-overlay, #e9ecf2);",
276
+ " --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(38,49,72,0.06));",
277
+ " --dsh-mem-bg-pop: var(--dsw-alias-bg-layer-3, #ffffff);",
278
+ " --dsh-mem-bg-pop-base: var(--dsw-alias-bg-primary, #ffffff);",
279
+ " --dsh-mem-border: var(--dsw-alias-border-l2, rgba(0,0,0,0.10));",
280
+ " --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(0,0,0,0.12));",
281
+ " --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(0,0,0,0));",
282
+ " --dsh-mem-text-1: var(--dsw-alias-label-primary, #0f1115);",
283
+ " --dsh-mem-text-2: var(--dsw-alias-label-secondary, #61666b);",
284
+ " --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #6e7781);",
285
+ " --dsh-mem-danger: var(--dsw-alias-state-error-primary, #d0403f);",
286
+ // 成本折线图系列色:8 档固定色;PALETTE 只引用 var(),1 档锚品牌蓝、8 档中性"其他"
287
+ " --dsh-mem-chart-1: #4d6bfe;",
288
+ " --dsh-mem-chart-2: #0e9c8f;",
289
+ " --dsh-mem-chart-3: #1f9d55;",
290
+ " --dsh-mem-chart-4: #a8821c;",
291
+ " --dsh-mem-chart-5: #d97a0d;",
292
+ " --dsh-mem-chart-6: #d64570;",
293
+ " --dsh-mem-chart-7: #7c5cff;",
294
+ " --dsh-mem-chart-8: #61666b;",
295
+ // 档位色 = 灰 → 品牌蓝的渐变阶(chat/work 过渡蓝、auto 品牌蓝);
296
+ // 文字对比度按 pill 实际底色(流光内底 = bg-card 97% + 档位色 3%)复算 AA 达标
297
+ " --dsh-mem-mode-chat: #5a69b0;",
298
+ " --dsh-mem-mode-work: #5263ca;",
299
+ " --dsh-mem-mode-auto: #3d5be0;",
300
+ // 开关旋钮 / 进度条底(滑轨已随 ModeSlider 删除,填充渐变与停点令牌一并移除)
301
+ " --dsh-mem-thumb: #ffffff;",
302
+ " --dsh-mem-track: rgba(128,140,150,0.32);",
303
+ // wing 八边形令牌(WingWheel):连线 / 角默认 / 角选中 / 空角与未打标态;
304
+ // 全部引用既有中性色与品牌蓝体系,双主题各自声明,无裸色溢出
305
+ " --dsh-mem-wing-line: rgba(128,140,150,0.35);",
306
+ " --dsh-mem-wing-corner: var(--dsh-mem-text-2);",
307
+ " --dsh-mem-wing-corner-on: var(--dsh-mem-accent);",
308
+ " --dsh-mem-wing-empty: var(--dsh-mem-text-3);",
309
+ " --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.05));",
310
+ " --dsh-mem-shadow-pop: var(--dsw-shadow-lv3, 0 0 1px 0 rgba(0,0,0,.2), 0 0 4px 0 rgba(0,0,0,.02), 0 12px 32px 0 rgba(0,0,0,0.08));"
311
+ ];
312
+ var DARK_TOK = [
313
+ " --dsh-mem-accent: #6e85ff;",
314
+ " --dsh-mem-accent-text: #7b90ff;",
315
+ " --dsh-mem-accent-fill: #465ce8;",
316
+ " --dsh-mem-accent-weak: rgba(110,133,255,0.14);",
317
+ " --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #2c2c2e);",
318
+ " --dsh-mem-bg-inset: var(--dsw-alias-bg-layer-1, #232324);",
319
+ " --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));",
320
+ " --dsh-mem-bg-pop: var(--dsw-alias-bg-layer-3, #353638);",
321
+ " --dsh-mem-bg-pop-base: var(--dsw-alias-bg-primary, #1b1c1e);",
322
+ " --dsh-mem-border: var(--dsw-alias-border-l2, rgba(255,255,255,0.12));",
323
+ " --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(255,255,255,0.16));",
324
+ " --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(255,255,255,0.06));",
325
+ " --dsh-mem-text-1: var(--dsw-alias-label-primary, #f9fafb);",
326
+ " --dsh-mem-text-2: var(--dsw-alias-label-secondary, #cfd3d6);",
327
+ " --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #8892a6);",
328
+ " --dsh-mem-danger: var(--dsw-alias-state-error-primary, #f4707b);",
329
+ " --dsh-mem-chart-1: #6e85ff;",
330
+ " --dsh-mem-chart-2: #35c4b5;",
331
+ " --dsh-mem-chart-3: #52c98d;",
332
+ " --dsh-mem-chart-4: #d9b23e;",
333
+ " --dsh-mem-chart-5: #f59e5b;",
334
+ " --dsh-mem-chart-6: #f47ba2;",
335
+ " --dsh-mem-chart-7: #a78bfa;",
336
+ " --dsh-mem-chart-8: #8892a6;",
337
+ " --dsh-mem-mode-chat: #97a4ff;",
338
+ " --dsh-mem-mode-work: #8295ff;",
339
+ " --dsh-mem-mode-auto: #7b90ff;",
340
+ " --dsh-mem-thumb: #e8ebf5;",
341
+ " --dsh-mem-track: rgba(148,160,180,0.30);",
342
+ " --dsh-mem-wing-line: rgba(148,160,180,0.32);",
343
+ " --dsh-mem-wing-corner: var(--dsh-mem-text-2);",
344
+ " --dsh-mem-wing-corner-on: var(--dsh-mem-accent);",
345
+ " --dsh-mem-wing-empty: var(--dsh-mem-text-3);",
346
+ " --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.3));",
347
+ " --dsh-mem-shadow-pop: var(--dsw-shadow-lv3, 0 0 1px 0 rgba(0,0,0,.2), 0 0 4px 0 rgba(0,0,0,.02), 0 12px 32px 0 rgba(0,0,0,0.08));"
348
+ ];
349
+ var COMPONENT_CSS = [
350
+ // conic 角度要 @property 注册才能插值动画;不支持 @property 的浏览器里
351
+ // var(--dsh-mem-angle) 无定义 → conic 层失效 → background 整条退化为无背景
352
+ //(光带边框与内底一起消失,只剩文字色)。2026 常青浏览器均已支持,仅作记录。
353
+ "@property --dsh-mem-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; }",
354
+ "@keyframes dshMemFlow { to { --dsh-mem-angle: 360deg; } }",
355
+ // ── 主题切换过渡:只过渡颜色/阴影(不碰 transform),明暗翻转不生硬 ──
356
+ ".dsh-mem-root, .dsh-mem-root * { transition: background-color .18s ease, border-color .18s ease, color .18s ease, box-shadow .18s ease; }",
357
+ // ── 控件:按钮(次级)── 圆角体系:控件 8 / 卡片 10 / 浮层 12 / 胶囊 999
358
+ ".dsh-mem-btn {",
359
+ " padding: 5px 14px; font-size: 13px; line-height: 20px; border-radius: 8px; cursor: pointer;",
360
+ " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
361
+ " transition: background-color .15s ease, border-color .15s ease, transform .08s ease;",
362
+ "}",
363
+ ".dsh-mem-btn:hover:not(:disabled) { border-color: var(--dsh-mem-border-strong); background: var(--dsh-mem-bg-hover); }",
364
+ ".dsh-mem-btn:active:not(:disabled) { transform: scale(0.98); }",
365
+ ".dsh-mem-btn:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
366
+ ".dsh-mem-btn:disabled { opacity: 0.45; cursor: not-allowed; }",
367
+ // ── 控件:输入框 / 下拉 ──
368
+ ".dsh-mem-input {",
369
+ " padding: 5px 10px; font-size: 13px; border-radius: 8px; color: var(--dsh-mem-text-1);",
370
+ " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
371
+ " transition: border-color .15s ease, box-shadow .15s ease;",
372
+ "}",
373
+ ".dsh-mem-input:focus {",
374
+ " outline: none; border-color: var(--dsh-mem-accent);",
375
+ " box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
376
+ "}",
377
+ // ── 下拉触发钮(NSel):观感与输入框一致(8px 圆角/同令牌边框底色),
378
+ // 文字 ellipsis + CSS 描边 chevron(展开旋转 180°);弹出面板见 .dsh-mem-pop ──
379
+ ".dsh-mem-select {",
380
+ " display: inline-flex; align-items: center; justify-content: space-between; gap: 8px;",
381
+ " width: 100%; min-width: 0; padding: 5px 10px; font: inherit; font-size: 13px; line-height: 20px;",
382
+ " text-align: left; border-radius: 8px; cursor: pointer; color: var(--dsh-mem-text-1);",
383
+ " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
384
+ " transition: border-color .15s ease, box-shadow .15s ease;",
385
+ "}",
386
+ ".dsh-mem-select:focus-visible {",
387
+ " outline: none; border-color: var(--dsh-mem-accent);",
388
+ " box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
389
+ "}",
390
+ ".dsh-mem-select:disabled { opacity: 0.45; cursor: not-allowed; }",
391
+ ".dsh-mem-select-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
392
+ // 下拉弹出面板:dsh MenuDropdown 同款——浮层 12 圆角 + menu 底 + lv3 投影;
393
+ // 选项行 10 圆角 + hover 底色 + 选中打勾(active 由 data-active 标记)
394
+ ".dsh-mem-sel { position: relative; display: inline-flex; min-width: 0; vertical-align: top; }",
395
+ ".dsh-mem-sel-chev {",
396
+ " width: 8px; height: 8px; flex: none; margin-right: 2px;",
397
+ " border-right: 1.5px solid var(--dsh-mem-text-3); border-bottom: 1.5px solid var(--dsh-mem-text-3);",
398
+ " transform: rotate(45deg); transition: transform .12s ease;",
399
+ "}",
400
+ ".dsh-mem-sel-chev-open { transform: rotate(225deg); }",
401
+ ".dsh-mem-pop {",
402
+ " position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;",
403
+ " min-width: 100%; width: max-content; max-width: 340px; max-height: 264px;",
404
+ " overflow-y: auto; overscroll-behavior: contain; padding: 4px;",
405
+ // 不透明实底:基色垫底+别名色整层叠印(别名即使带 alpha 也被垫死),2026-10-09 用户修复
406
+ " background: linear-gradient(var(--dsh-mem-bg-pop), var(--dsh-mem-bg-pop)) , var(--dsh-mem-bg-pop-base); border: 1px solid var(--dsh-mem-border-pop);",
407
+ " border-radius: 12px; box-shadow: var(--dsh-mem-shadow-pop);",
408
+ "}",
409
+ ".dsh-mem-pop-opt {",
410
+ " display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box;",
411
+ " min-height: 32px; padding: 5px 10px; font: inherit; font-size: 13px; line-height: 20px;",
412
+ " text-align: left; color: var(--dsh-mem-text-1); cursor: pointer;",
413
+ " background: none; border: none; outline: none; border-radius: 10px;",
414
+ "}",
415
+ '.dsh-mem-pop-opt:hover, .dsh-mem-pop-opt[data-active="1"] { background: var(--dsh-mem-bg-hover); }',
416
+ ".dsh-mem-pop-opt-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
417
+ ".dsh-mem-pop-check { flex: none; font-size: 12px; color: var(--dsh-mem-text-1); }",
418
+ ".dsh-mem-pop-empty { padding: 10px; font-size: 13px; color: var(--dsh-mem-text-3); }",
419
+ // ── Tab(下划线式):active 品牌蓝下划线 + 主文字色 ──
420
+ ".dsh-mem-tab {",
421
+ " padding: 6px 6px; font-size: 13px; cursor: pointer; background: none; border: none; white-space: nowrap;",
422
+ " color: var(--dsh-mem-text-2); border-bottom: 2px solid transparent; margin-bottom: -1px;",
423
+ "}",
424
+ ".dsh-mem-tab:hover { color: var(--dsh-mem-text-1); }",
425
+ ".dsh-mem-tab-on { font-weight: 600; color: var(--dsh-mem-text-1); border-bottom-color: var(--dsh-mem-accent); }",
426
+ ".dsh-mem-tab:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: -2px; }",
427
+ // ── 卡片:hover 微抬(边框加深),只给可交互卡片用 ──
428
+ ".dsh-mem-card {",
429
+ " border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
430
+ " box-shadow: var(--dsh-mem-shadow-card);",
431
+ "}",
432
+ ".dsh-mem-card-hover:hover { border-color: var(--dsh-mem-border-strong); }",
433
+ // ── 场景卡折叠箭头:展开态旋转 90°(进 reduced-motion 压制名单) ──
434
+ ".dsh-mem-scene-chev { display: inline-block; transition: transform .15s ease; color: var(--dsh-mem-text-3); }",
435
+ // ── 记忆类型标签:tint 风格(彩底淡色 + 彩字),--dsh-mem-tag-c 由类型类给定 ──
436
+ ".dsh-mem-tag {",
437
+ " display: inline-block; padding: 1px 8px; border-radius: 999px;",
438
+ " font-size: 11px; font-weight: 600; line-height: 18px; white-space: nowrap;",
439
+ " color: var(--dsh-mem-tag-c, var(--dsh-mem-text-2));",
440
+ " background: color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 12%, transparent);",
441
+ " border: 1px solid color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 28%, transparent);",
442
+ "}",
443
+ ".dsh-mem-tag-persona { --dsh-mem-tag-c: #6f42c1; }",
444
+ ".dsh-mem-tag-episodic { --dsh-mem-tag-c: #0757b4; }",
445
+ ".dsh-mem-tag-instruction, .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #8a5a00; }",
446
+ ".dsh-mem-tag-work-fact { --dsh-mem-tag-c: #0757b4; }",
447
+ ".dsh-mem-tag-work-task { --dsh-mem-tag-c: #116629; }",
448
+ ".dsh-mem-tag-work-method { --dsh-mem-tag-c: #6f42c1; }",
449
+ "body[data-ds-dark-theme] .dsh-mem-tag-persona { --dsh-mem-tag-c: #c297ff; }",
450
+ "body[data-ds-dark-theme] .dsh-mem-tag-episodic { --dsh-mem-tag-c: #6cb2ff; }",
451
+ "body[data-ds-dark-theme] .dsh-mem-tag-instruction, body[data-ds-dark-theme] .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #e3b341; }",
452
+ "body[data-ds-dark-theme] .dsh-mem-tag-work-fact { --dsh-mem-tag-c: #6cb2ff; }",
453
+ "body[data-ds-dark-theme] .dsh-mem-tag-work-task { --dsh-mem-tag-c: #6fca74; }",
454
+ "body[data-ds-dark-theme] .dsh-mem-tag-work-method { --dsh-mem-tag-c: #c297ff; }",
455
+ // ── pill 边缘流光(品牌蓝族,chat/work/auto 三档共用;off 无):border 区画旋转
456
+ // conic 光带;内部必须用【不透明】底色盖住光带——半透明内层会让 conic 透进按钮
457
+ // 内部干扰文字(实测事故)。不透明底 = 主题底混 3% 档位色(--dsh-mem-pill-tint
458
+ // 由 pill inline 给定;3% 保证档位色文字 AA,暗色智能档余量 4.63:1)
459
+ ".dsh-mem-flow {",
460
+ " border: 1px solid transparent;",
461
+ " background:",
462
+ " linear-gradient(",
463
+ " color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe)),",
464
+ " color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe))",
465
+ " ) padding-box,",
466
+ " conic-gradient(from var(--dsh-mem-angle),",
467
+ " rgba(61,91,224,0.9), rgba(77,107,254,0.95), rgba(147,168,255,1),",
468
+ " rgba(110,133,255,0.9), rgba(61,91,224,0.9)) border-box;",
469
+ " animation: dshMemFlow 3s linear infinite;",
470
+ "}",
471
+ // ── off 档 pill:dsh 透明按钮——无底无边框只留文字,hover 才出 interactive 淡底
472
+ //(裸 button 会露 UA 默认灰底+描边,必须显式压掉) ──
473
+ ".dsh-mem-pill-off { border: none; background: transparent; }",
474
+ ".dsh-mem-pill-off:hover { background: var(--dsh-mem-bg-hover); }",
475
+ ".dsh-mem-pill-off:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
476
+ // 流光态焦点环(同一物理按钮的两态焦点反馈对称,配方同 .dsh-mem-btn)
477
+ ".dsh-mem-flow:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
478
+ // ── 触屏隐形热区(手机端适配):触点目标补足 44px 标准(iOS HIG)。
479
+ // 伪元素不参与布局(浮层/输入栏几何零变化),指针事件落在宿主元素上。
480
+ // 全端统一不做 pointer:coarse 分端(桌面点中目标变大是纯收益)。
481
+ // pill:视觉高 24px,::after 上下各外扩 10px;只上下不左右——左右是宿主输入栏
482
+ // 邻位控件(模式选择器),外扩会制造误触重叠带 ──
483
+ ".dsh-mem-pill-hit::after {",
484
+ " content: ''; position: absolute; left: 0; right: 0; top: -10px; bottom: -10px;",
485
+ "}",
486
+ // ── 浮层(dsh 原生菜单同配方:不透明实底 + inverted 描边(浅色不可见)+ lv3 阴影) ──
487
+ ".dsh-mem-popover {",
488
+ " border-radius: 12px;",
489
+ " border: 1px solid var(--dsh-mem-border-pop);",
490
+ " background: linear-gradient(var(--dsh-mem-bg-pop), var(--dsh-mem-bg-pop)) , var(--dsh-mem-bg-pop-base);",
491
+ " box-shadow: var(--dsh-mem-shadow-pop);",
492
+ " color: var(--dsh-mem-text-1);",
493
+ "}",
494
+ // ── 重建面板 ──(模态本体走 NModal:原生 Modal 优先,回退 rb-overlay/rb-modal)
495
+ ".dsh-mem-rb-card {",
496
+ " border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
497
+ " box-shadow: var(--dsh-mem-shadow-card); padding: 12px 14px; margin-bottom: 14px; font-size: 13px;",
498
+ // 剪掉行内溢出的固定宽控件(如维度输入框),防其顶破卡片圆角边界
499
+ " overflow: hidden;",
500
+ "}",
501
+ ".dsh-mem-rb-bar { height: 8px; border-radius: 4px; overflow: hidden; flex: 1; background: var(--dsh-mem-track); }",
502
+ ".dsh-mem-rb-fill { height: 100%; border-radius: 4px; background: var(--dsh-mem-accent-fill); transition: width .4s ease; }",
503
+ // 无位移阶段(单次 LLM 调用可达分钟级)的呼吸进度条:宽度满格,透明度呼吸表达"在跑"
504
+ "@keyframes dshMemRbBreath { 0%,100% { opacity: 1; } 50% { opacity: .35; } }",
505
+ ".dsh-mem-rb-fill-indet { animation: dshMemRbBreath 1.6s ease-in-out infinite; transition: none; }",
506
+ ".dsh-mem-rb-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35);",
507
+ " display: flex; align-items: center; justify-content: center; z-index: 2000; }",
508
+ ".dsh-mem-rb-modal { width: 440px; max-width: calc(100vw - 48px); border-radius: 12px;",
509
+ " padding: 18px 20px; font-size: 13px; line-height: 1.6;",
510
+ " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
511
+ " box-shadow: 0 16px 48px rgba(0,0,0,0.24); }",
512
+ "body[data-ds-dark-theme] .dsh-mem-rb-modal { box-shadow: 0 16px 48px rgba(0,0,0,0.6); }",
513
+ ".dsh-mem-rb-muted { font-size: 12px; color: var(--dsh-mem-text-3); }",
514
+ // ── 会话信息区(悬浮卡下半部):分隔线 + 2×2 指标 + 状态行;纯静态 DOM,
515
+ // 不挂 rAF 循环,轮询数据到达才触发本组件小树 re-render ──
516
+ ".dsh-mem-sinfo { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--dsh-mem-border); }",
517
+ ".dsh-mem-sinfo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; }",
518
+ ".dsh-mem-sinfo-val { font-size: 13px; font-weight: 600; color: var(--dsh-mem-text-1); line-height: 18px; font-variant-numeric: tabular-nums; }",
519
+ ".dsh-mem-sinfo-label { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
520
+ ".dsh-mem-sinfo-warn { font-size: 11px; color: var(--dsh-mem-danger); line-height: 16px; margin-bottom: 6px; }",
521
+ ".dsh-mem-sinfo-note { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 16px; margin-top: 8px; }",
522
+ ".dsh-mem-sinfo-sum { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 16px; margin-top: 8px; }",
523
+ // reduced-motion 兜底放样式表末尾:同特异性下后置声明才能压过上面的组件类
524
+ "@media (prefers-reduced-motion: reduce) {",
525
+ " .dsh-mem-root, .dsh-mem-root *, .dsh-mem-btn, .dsh-mem-input, .dsh-mem-select, .dsh-mem-rb-fill, .dsh-mem-scene-chev, .dsh-mem-sel-chev { transition: none; }",
526
+ " .dsh-mem-flow { animation: none; }",
527
+ "}"
528
+ ];
529
+ function buildStyleSheet() {
530
+ return [
531
+ // auto/跟随宿主的兜底:浅色在 :root,暗色在 body[data-ds-dark-theme]
224
532
  ":root {",
225
- " --dsh-mem-accent: #4d6bfe;",
226
- " --dsh-mem-accent-text: #3d5be0;",
227
- " --dsh-mem-accent-fill: #3d5be0;",
228
- " --dsh-mem-accent-weak: rgba(77,107,254,0.10);",
229
- " --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #ffffff);",
230
- " --dsh-mem-bg-inset: var(--dsw-alias-bg-overlay, #e9ecf2);",
231
- " --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(38,49,72,0.06));",
232
- " --dsh-mem-bg-pop: var(--dsw-alias-bg-layer-3, #ffffff);",
233
- " --dsh-mem-bg-pop-base: var(--dsw-alias-bg-primary, #ffffff);",
234
- " --dsh-mem-border: var(--dsw-alias-border-l2, rgba(0,0,0,0.10));",
235
- " --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(0,0,0,0.12));",
236
- " --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(0,0,0,0));",
237
- " --dsh-mem-text-1: var(--dsw-alias-label-primary, #0f1115);",
238
- " --dsh-mem-text-2: var(--dsw-alias-label-secondary, #61666b);",
239
- " --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #6e7781);",
240
- " --dsh-mem-danger: var(--dsw-alias-state-error-primary, #d0403f);",
241
- // 成本折线图系列色:8 档固定色;PALETTE 只引用 var(),1 档锚品牌蓝、8 档中性"其他"
242
- " --dsh-mem-chart-1: #4d6bfe;",
243
- " --dsh-mem-chart-2: #0e9c8f;",
244
- " --dsh-mem-chart-3: #1f9d55;",
245
- " --dsh-mem-chart-4: #a8821c;",
246
- " --dsh-mem-chart-5: #d97a0d;",
247
- " --dsh-mem-chart-6: #d64570;",
248
- " --dsh-mem-chart-7: #7c5cff;",
249
- " --dsh-mem-chart-8: #61666b;",
250
- // 档位色 = 灰 → 品牌蓝的渐变阶(chat/work 过渡蓝、auto 品牌蓝);
251
- // 文字对比度按 pill 实际底色(流光内底 = bg-card 97% + 档位色 3%)复算 AA 达标
252
- " --dsh-mem-mode-chat: #5a69b0;",
253
- " --dsh-mem-mode-work: #5263ca;",
254
- " --dsh-mem-mode-auto: #3d5be0;",
255
- // 开关旋钮 / 进度条底(滑轨已随 ModeSlider 删除,填充渐变与停点令牌一并移除)
256
- " --dsh-mem-thumb: #ffffff;",
257
- " --dsh-mem-track: rgba(128,140,150,0.32);",
258
- // wing 八边形令牌(WingWheel):连线 / 角默认 / 角选中 / 空角与未打标态;
259
- // 全部引用既有中性色与品牌蓝体系,双主题各自声明,无裸色溢出
260
- " --dsh-mem-wing-line: rgba(128,140,150,0.35);",
261
- " --dsh-mem-wing-corner: var(--dsh-mem-text-2);",
262
- " --dsh-mem-wing-corner-on: var(--dsh-mem-accent);",
263
- " --dsh-mem-wing-empty: var(--dsh-mem-text-3);",
264
- " --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.05));",
265
- " --dsh-mem-shadow-pop: var(--dsw-shadow-lv3, 0 0 1px 0 rgba(0,0,0,.2), 0 0 4px 0 rgba(0,0,0,.02), 0 12px 32px 0 rgba(0,0,0,0.08));",
533
+ ...LIGHT_TOK,
266
534
  "}",
267
- // ── 令牌(暗色:body[data-ds-dark-theme] 是 dsh 前端的暗色开关) ──
268
535
  "body[data-ds-dark-theme] {",
269
- " --dsh-mem-accent: #6e85ff;",
270
- " --dsh-mem-accent-text: #7b90ff;",
271
- " --dsh-mem-accent-fill: #465ce8;",
272
- " --dsh-mem-accent-weak: rgba(110,133,255,0.14);",
273
- " --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #2c2c2e);",
274
- " --dsh-mem-bg-inset: var(--dsw-alias-bg-layer-1, #232324);",
275
- " --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));",
276
- " --dsh-mem-bg-pop: var(--dsw-alias-bg-layer-3, #353638);",
277
- " --dsh-mem-bg-pop-base: var(--dsw-alias-bg-primary, #1b1c1e);",
278
- " --dsh-mem-border: var(--dsw-alias-border-l2, rgba(255,255,255,0.12));",
279
- " --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(255,255,255,0.16));",
280
- " --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(255,255,255,0.06));",
281
- " --dsh-mem-text-1: var(--dsw-alias-label-primary, #f9fafb);",
282
- " --dsh-mem-text-2: var(--dsw-alias-label-secondary, #cfd3d6);",
283
- " --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #8892a6);",
284
- " --dsh-mem-danger: var(--dsw-alias-state-error-primary, #f4707b);",
285
- " --dsh-mem-chart-1: #6e85ff;",
286
- " --dsh-mem-chart-2: #35c4b5;",
287
- " --dsh-mem-chart-3: #52c98d;",
288
- " --dsh-mem-chart-4: #d9b23e;",
289
- " --dsh-mem-chart-5: #f59e5b;",
290
- " --dsh-mem-chart-6: #f47ba2;",
291
- " --dsh-mem-chart-7: #a78bfa;",
292
- " --dsh-mem-chart-8: #8892a6;",
293
- " --dsh-mem-mode-chat: #97a4ff;",
294
- " --dsh-mem-mode-work: #8295ff;",
295
- " --dsh-mem-mode-auto: #7b90ff;",
296
- " --dsh-mem-thumb: #e8ebf5;",
297
- " --dsh-mem-track: rgba(148,160,180,0.30);",
298
- " --dsh-mem-wing-line: rgba(148,160,180,0.32);",
299
- " --dsh-mem-wing-corner: var(--dsh-mem-text-2);",
300
- " --dsh-mem-wing-corner-on: var(--dsh-mem-accent);",
301
- " --dsh-mem-wing-empty: var(--dsh-mem-text-3);",
302
- " --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.3));",
303
- " --dsh-mem-shadow-pop: var(--dsw-shadow-lv3, 0 0 1px 0 rgba(0,0,0,.2), 0 0 4px 0 rgba(0,0,0,.02), 0 12px 32px 0 rgba(0,0,0,0.08));",
536
+ ...DARK_TOK,
304
537
  "}",
305
- // ── 主题切换过渡:只过渡颜色/阴影(不碰 transform),明暗翻转不生硬 ──
306
- ".dsh-mem-root, .dsh-mem-root * { transition: background-color .18s ease, border-color .18s ease, color .18s ease, box-shadow .18s ease; }",
307
- // ── 控件:按钮(次级)── 圆角体系:控件 8 / 卡片 10 / 浮层 12 / 胶囊 999
308
- ".dsh-mem-btn {",
309
- " padding: 5px 14px; font-size: 13px; line-height: 20px; border-radius: 8px; cursor: pointer;",
310
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
311
- " transition: background-color .15s ease, border-color .15s ease, transform .08s ease;",
538
+ // 皮肤显式覆写:html[data-dsh-mem-skin] 由 skin.ts 始终写成具体 light|dark。
539
+ // 与宿主暗色块同特异度(元素+属性),但后置声明胜出,从而能压过宿主强切明暗。
540
+ 'html[data-dsh-mem-skin="light"] {',
541
+ ...LIGHT_TOK,
312
542
  "}",
313
- ".dsh-mem-btn:hover:not(:disabled) { border-color: var(--dsh-mem-border-strong); background: var(--dsh-mem-bg-hover); }",
314
- ".dsh-mem-btn:active:not(:disabled) { transform: scale(0.98); }",
315
- ".dsh-mem-btn:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
316
- ".dsh-mem-btn:disabled { opacity: 0.45; cursor: not-allowed; }",
317
- // ── 控件:输入框 / 下拉 ──
318
- ".dsh-mem-input {",
319
- " padding: 5px 10px; font-size: 13px; border-radius: 8px; color: var(--dsh-mem-text-1);",
320
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
321
- " transition: border-color .15s ease, box-shadow .15s ease;",
543
+ 'html[data-dsh-mem-skin="dark"] {',
544
+ ...DARK_TOK,
322
545
  "}",
323
- ".dsh-mem-input:focus {",
324
- " outline: none; border-color: var(--dsh-mem-accent);",
325
- " box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
326
- "}",
327
- // ── 下拉触发钮(NSel):观感与输入框一致(8px 圆角/同令牌边框底色),
328
- // 文字 ellipsis + CSS 描边 chevron(展开旋转 180°);弹出面板见 .dsh-mem-pop ──
329
- ".dsh-mem-select {",
330
- " display: inline-flex; align-items: center; justify-content: space-between; gap: 8px;",
331
- " width: 100%; min-width: 0; padding: 5px 10px; font: inherit; font-size: 13px; line-height: 20px;",
332
- " text-align: left; border-radius: 8px; cursor: pointer; color: var(--dsh-mem-text-1);",
333
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
334
- " transition: border-color .15s ease, box-shadow .15s ease;",
335
- "}",
336
- ".dsh-mem-select:focus-visible {",
337
- " outline: none; border-color: var(--dsh-mem-accent);",
338
- " box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
339
- "}",
340
- ".dsh-mem-select:disabled { opacity: 0.45; cursor: not-allowed; }",
341
- ".dsh-mem-select-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
342
- // 下拉弹出面板:dsh MenuDropdown 同款——浮层 12 圆角 + menu 底 + lv3 投影;
343
- // 选项行 10 圆角 + hover 底色 + 选中打勾(active 由 data-active 标记)
344
- ".dsh-mem-sel { position: relative; display: inline-flex; min-width: 0; vertical-align: top; }",
345
- ".dsh-mem-sel-chev {",
346
- " width: 8px; height: 8px; flex: none; margin-right: 2px;",
347
- " border-right: 1.5px solid var(--dsh-mem-text-3); border-bottom: 1.5px solid var(--dsh-mem-text-3);",
348
- " transform: rotate(45deg); transition: transform .12s ease;",
349
- "}",
350
- ".dsh-mem-sel-chev-open { transform: rotate(225deg); }",
351
- ".dsh-mem-pop {",
352
- " position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;",
353
- " min-width: 100%; width: max-content; max-width: 340px; max-height: 264px;",
354
- " overflow-y: auto; overscroll-behavior: contain; padding: 4px;",
355
- " // 不透明实底:基色垫底+别名色整层叠印(别名即使带 alpha 也被垫死),2026-10-09 用户修复",
356
- " background: linear-gradient(var(--dsh-mem-bg-pop), var(--dsh-mem-bg-pop)) , var(--dsh-mem-bg-pop-base); border: 1px solid var(--dsh-mem-border-pop);",
357
- " border-radius: 12px; box-shadow: var(--dsh-mem-shadow-pop);",
358
- "}",
359
- ".dsh-mem-pop-opt {",
360
- " display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box;",
361
- " min-height: 32px; padding: 5px 10px; font: inherit; font-size: 13px; line-height: 20px;",
362
- " text-align: left; color: var(--dsh-mem-text-1); cursor: pointer;",
363
- " background: none; border: none; outline: none; border-radius: 10px;",
364
- "}",
365
- '.dsh-mem-pop-opt:hover, .dsh-mem-pop-opt[data-active="1"] { background: var(--dsh-mem-bg-hover); }',
366
- ".dsh-mem-pop-opt-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
367
- ".dsh-mem-pop-check { flex: none; font-size: 12px; color: var(--dsh-mem-text-1); }",
368
- ".dsh-mem-pop-empty { padding: 10px; font-size: 13px; color: var(--dsh-mem-text-3); }",
369
- // ── Tab(下划线式):active 品牌蓝下划线 + 主文字色 ──
370
- ".dsh-mem-tab {",
371
- " padding: 6px 12px; font-size: 13px; cursor: pointer; background: none; border: none;",
372
- " color: var(--dsh-mem-text-2); border-bottom: 2px solid transparent; margin-bottom: -1px;",
373
- "}",
374
- ".dsh-mem-tab:hover { color: var(--dsh-mem-text-1); }",
375
- ".dsh-mem-tab-on { font-weight: 600; color: var(--dsh-mem-text-1); border-bottom-color: var(--dsh-mem-accent); }",
376
- ".dsh-mem-tab:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: -2px; }",
377
- // ── 卡片:hover 微抬(边框加深),只给可交互卡片用 ──
378
- ".dsh-mem-card {",
379
- " border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
380
- " box-shadow: var(--dsh-mem-shadow-card);",
381
- "}",
382
- ".dsh-mem-card-hover:hover { border-color: var(--dsh-mem-border-strong); }",
383
- // ── 场景卡折叠箭头:展开态旋转 90°(进 reduced-motion 压制名单) ──
384
- ".dsh-mem-scene-chev { display: inline-block; transition: transform .15s ease; color: var(--dsh-mem-text-3); }",
385
- // ── 记忆类型标签:tint 风格(彩底淡色 + 彩字),--dsh-mem-tag-c 由类型类给定 ──
386
- ".dsh-mem-tag {",
387
- " display: inline-block; padding: 1px 8px; border-radius: 999px;",
388
- " font-size: 11px; font-weight: 600; line-height: 18px; white-space: nowrap;",
389
- " color: var(--dsh-mem-tag-c, var(--dsh-mem-text-2));",
390
- " background: color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 12%, transparent);",
391
- " border: 1px solid color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 28%, transparent);",
392
- "}",
393
- ".dsh-mem-tag-persona { --dsh-mem-tag-c: #6f42c1; }",
394
- ".dsh-mem-tag-episodic { --dsh-mem-tag-c: #0757b4; }",
395
- ".dsh-mem-tag-instruction, .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #8a5a00; }",
396
- ".dsh-mem-tag-work-fact { --dsh-mem-tag-c: #0757b4; }",
397
- ".dsh-mem-tag-work-task { --dsh-mem-tag-c: #116629; }",
398
- ".dsh-mem-tag-work-method { --dsh-mem-tag-c: #6f42c1; }",
399
- "body[data-ds-dark-theme] .dsh-mem-tag-persona { --dsh-mem-tag-c: #c297ff; }",
400
- "body[data-ds-dark-theme] .dsh-mem-tag-episodic { --dsh-mem-tag-c: #6cb2ff; }",
401
- "body[data-ds-dark-theme] .dsh-mem-tag-instruction, body[data-ds-dark-theme] .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #e3b341; }",
402
- "body[data-ds-dark-theme] .dsh-mem-tag-work-fact { --dsh-mem-tag-c: #6cb2ff; }",
403
- "body[data-ds-dark-theme] .dsh-mem-tag-work-task { --dsh-mem-tag-c: #6fca74; }",
404
- "body[data-ds-dark-theme] .dsh-mem-tag-work-method { --dsh-mem-tag-c: #c297ff; }",
405
- // ── pill 边缘流光(品牌蓝族,chat/work/auto 三档共用;off 无):border 区画旋转
406
- // conic 光带;内部必须用【不透明】底色盖住光带——半透明内层会让 conic 透进按钮
407
- // 内部干扰文字(实测事故)。不透明底 = 主题底混 3% 档位色(--dsh-mem-pill-tint
408
- // 由 pill inline 给定;3% 保证档位色文字 AA,暗色智能档余量 4.63:1)
409
- ".dsh-mem-flow {",
410
- " border: 1px solid transparent;",
411
- " background:",
412
- " linear-gradient(",
413
- " color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe)),",
414
- " color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe))",
415
- " ) padding-box,",
416
- " conic-gradient(from var(--dsh-mem-angle),",
417
- " rgba(61,91,224,0.9), rgba(77,107,254,0.95), rgba(147,168,255,1),",
418
- " rgba(110,133,255,0.9), rgba(61,91,224,0.9)) border-box;",
419
- " animation: dshMemFlow 3s linear infinite;",
420
- "}",
421
- // ── off 档 pill:dsh 透明按钮——无底无边框只留文字,hover 才出 interactive 淡底
422
- //(裸 button 会露 UA 默认灰底+描边,必须显式压掉) ──
423
- ".dsh-mem-pill-off { border: none; background: transparent; }",
424
- ".dsh-mem-pill-off:hover { background: var(--dsh-mem-bg-hover); }",
425
- ".dsh-mem-pill-off:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
426
- // 流光态焦点环(同一物理按钮的两态焦点反馈对称,配方同 .dsh-mem-btn)
427
- ".dsh-mem-flow:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
428
- // ── 触屏隐形热区(手机端适配):触点目标补足 44px 标准(iOS HIG)。
429
- // 伪元素不参与布局(浮层/输入栏几何零变化),指针事件落在宿主元素上。
430
- // 全端统一不做 pointer:coarse 分端(桌面点中目标变大是纯收益)。
431
- // pill:视觉高 24px,::after 上下各外扩 10px;只上下不左右——左右是宿主输入栏
432
- // 邻位控件(模式选择器),外扩会制造误触重叠带 ──
433
- ".dsh-mem-pill-hit::after {",
434
- " content: ''; position: absolute; left: 0; right: 0; top: -10px; bottom: -10px;",
435
- "}",
436
- // ── 浮层(dsh 原生菜单同配方:不透明实底 + inverted 描边(浅色不可见)+ lv3 阴影) ──
437
- ".dsh-mem-popover {",
438
- " border-radius: 12px;",
439
- " border: 1px solid var(--dsh-mem-border-pop);",
440
- " background: linear-gradient(var(--dsh-mem-bg-pop), var(--dsh-mem-bg-pop)) , var(--dsh-mem-bg-pop-base);",
441
- " box-shadow: var(--dsh-mem-shadow-pop);",
442
- " color: var(--dsh-mem-text-1);",
443
- "}",
444
- // ── 重建面板 ──(模态本体走 NModal:原生 Modal 优先,回退 rb-overlay/rb-modal)
445
- ".dsh-mem-rb-card {",
446
- " border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
447
- " box-shadow: var(--dsh-mem-shadow-card); padding: 12px 14px; margin-bottom: 14px; font-size: 13px;",
448
- // 剪掉行内溢出的固定宽控件(如维度输入框),防其顶破卡片圆角边界
449
- " overflow: hidden;",
450
- "}",
451
- ".dsh-mem-rb-bar { height: 8px; border-radius: 4px; overflow: hidden; flex: 1; background: var(--dsh-mem-track); }",
452
- ".dsh-mem-rb-fill { height: 100%; border-radius: 4px; background: var(--dsh-mem-accent-fill); transition: width .4s ease; }",
453
- // 无位移阶段(单次 LLM 调用可达分钟级)的呼吸进度条:宽度满格,透明度呼吸表达"在跑"
454
- "@keyframes dshMemRbBreath { 0%,100% { opacity: 1; } 50% { opacity: .35; } }",
455
- ".dsh-mem-rb-fill-indet { animation: dshMemRbBreath 1.6s ease-in-out infinite; transition: none; }",
456
- ".dsh-mem-rb-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35);",
457
- " display: flex; align-items: center; justify-content: center; z-index: 2000; }",
458
- ".dsh-mem-rb-modal { width: 440px; max-width: calc(100vw - 48px); border-radius: 12px;",
459
- " padding: 18px 20px; font-size: 13px; line-height: 1.6;",
460
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
461
- " box-shadow: 0 16px 48px rgba(0,0,0,0.24); }",
462
- "body[data-ds-dark-theme] .dsh-mem-rb-modal { box-shadow: 0 16px 48px rgba(0,0,0,0.6); }",
463
- ".dsh-mem-rb-muted { font-size: 12px; color: var(--dsh-mem-text-3); }",
464
- // ── 会话信息区(悬浮卡下半部):分隔线 + 2×2 指标 + 状态行;纯静态 DOM,
465
- // 不挂 rAF 循环,轮询数据到达才触发本组件小树 re-render ──
466
- ".dsh-mem-sinfo { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--dsh-mem-border); }",
467
- ".dsh-mem-sinfo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; }",
468
- ".dsh-mem-sinfo-val { font-size: 13px; font-weight: 600; color: var(--dsh-mem-text-1); line-height: 18px; font-variant-numeric: tabular-nums; }",
469
- ".dsh-mem-sinfo-label { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
470
- ".dsh-mem-sinfo-warn { font-size: 11px; color: var(--dsh-mem-danger); line-height: 16px; margin-bottom: 6px; }",
471
- ".dsh-mem-sinfo-note { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 16px; margin-top: 8px; }",
472
- ".dsh-mem-sinfo-sum { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 16px; margin-top: 8px; }",
473
- // reduced-motion 兜底放样式表末尾:同特异性下后置声明才能压过上面的组件类
474
- "@media (prefers-reduced-motion: reduce) {",
475
- " .dsh-mem-root, .dsh-mem-root *, .dsh-mem-btn, .dsh-mem-input, .dsh-mem-select, .dsh-mem-rb-fill, .dsh-mem-scene-chev, .dsh-mem-sel-chev { transition: none; }",
476
- " .dsh-mem-flow { animation: none; }",
477
- "}"
546
+ ...COMPONENT_CSS
478
547
  ].join("\n");
479
- document.head.appendChild(el);
548
+ }
549
+ function ensureThemeStyle(skin) {
550
+ const eff = effectiveSkin(skin ?? getSkin());
551
+ if (!document.getElementById(THEME_STYLE_ID)) {
552
+ const el = document.createElement("style");
553
+ el.id = THEME_STYLE_ID;
554
+ el.textContent = buildStyleSheet();
555
+ document.head.appendChild(el);
556
+ }
557
+ document.documentElement.setAttribute("data-dsh-mem-skin", eff);
480
558
  }
481
559
 
482
560
  // client/src/tabs/ConflictsTab.tsx
@@ -808,46 +886,243 @@ var __defProp = Object.defineProperty;
808
886
  }
809
887
  function Side(props) {
810
888
  const axis = props.pair && props.side ? axisText(props.pair, props.side) : "";
889
+ const mt = props.pair && props.side ? props.side === "winner" ? props.pair.winner_maturity : props.pair.loser_maturity : void 0;
811
890
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 6 }, children: [
812
891
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, fontWeight: props.accent ? 600 : void 0 }, children: props.label }),
813
892
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: S.content, children: props.content || GONE }),
814
893
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, fontFamily: "ui-monospace, Consolas, monospace" }, children: props.id }),
815
- axis ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, marginTop: 2, fontSize: 11 }, children: axis }) : null
894
+ axis ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, marginTop: 2, fontSize: 11 }, children: axis }) : null,
895
+ mt ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, marginTop: 2, fontSize: 11 }, children: "成熟度 · " + (mt === "high" ? "高" : mt === "mid" ? "中" : "低") }) : null
816
896
  ] });
817
897
  }
818
898
 
819
- // client/src/tabs/CostTab.tsx
899
+ // client/src/tabs/ConnectTab.tsx
820
900
  var import_react3 = require("react");
901
+ var import_jsx_runtime2 = require("react/jsx-runtime");
902
+ function errMsg(r) {
903
+ return r && r.error && r.error.message ? r.error.message : "RPC 错误";
904
+ }
905
+ function SourceCard(props) {
906
+ const { src, rpc, onChanged, onErr } = props;
907
+ const retire = (active) => {
908
+ rpc("dsh-memory/memory-sources-retire", { id: src.id, active }).then((r) => {
909
+ if (r && r.ok) onChanged();
910
+ else onErr(errMsg(r));
911
+ }).catch((e) => onErr(String(e?.message || e)));
912
+ };
913
+ const retired = src.status === "retired";
914
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-mem-card", style: S.card, children: [
915
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: S.cardHead, children: [
916
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: src.label || src.id }),
917
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: S.muted, children: "· " + src.kind + (retired ? " · 已退役" : src.disabled ? " · 已停用" : "") }),
918
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.grow }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(NButton, { onClick: () => retire(!retired), children: retired ? "恢复" : "退役" })
920
+ ] }),
921
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...S.detail }, children: src.path }),
922
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...S.muted, marginTop: 4 }, children: "上次扫描 " + (src.lastScanAt || "—") + " · 上次导入 " + (src.lastImportAt || "—") })
923
+ ] });
924
+ }
925
+ function ConnectTab(props) {
926
+ const rpc = props.rpc;
927
+ const [data, setData] = (0, import_react3.useState)(null);
928
+ const [scan, setScan] = (0, import_react3.useState)(null);
929
+ const [error, setError] = (0, import_react3.useState)(null);
930
+ const [busy, setBusy] = (0, import_react3.useState)(false);
931
+ const [form, setForm] = (0, import_react3.useState)({ id: "", kind: "file", path: "", label: "" });
932
+ const load = (0, import_react3.useCallback)(() => {
933
+ rpc("dsh-memory/memory-sources-list", {}).then((r) => {
934
+ if (r && r.ok) {
935
+ setData(r.value);
936
+ setError(null);
937
+ } else setError(errMsg(r));
938
+ }).catch((e) => setError(String(e?.message || e)));
939
+ }, [rpc]);
940
+ (0, import_react3.useEffect)(() => {
941
+ load();
942
+ }, [load]);
943
+ const add = () => {
944
+ if (!form.id.trim() || !form.path.trim()) {
945
+ setError("登记来源需要 id 与绝对路径 path");
946
+ return;
947
+ }
948
+ rpc("dsh-memory/memory-sources-add", { id: form.id.trim(), kind: form.kind, path: form.path.trim(), label: form.label.trim() || void 0 }).then((r) => {
949
+ if (r && r.ok) {
950
+ setError(null);
951
+ setForm({ ...form, id: "", path: "", label: "" });
952
+ load();
953
+ } else setError(errMsg(r));
954
+ }).catch((e) => setError(String(e?.message || e)));
955
+ };
956
+ const runScan = () => {
957
+ setBusy(true);
958
+ rpc("dsh-memory/memport-scan", {}).then((r) => {
959
+ if (r && r.ok) {
960
+ setScan(r.value);
961
+ setError(null);
962
+ load();
963
+ } else setError(errMsg(r));
964
+ }).catch((e) => setError(String(e?.message || e))).finally(() => setBusy(false));
965
+ };
966
+ const input = (key, placeholder) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
967
+ "input",
968
+ {
969
+ className: "dsh-mem-input",
970
+ style: { flex: key === "path" ? 2 : 1, minWidth: 120 },
971
+ value: form[key],
972
+ placeholder,
973
+ onChange: (e) => setForm({ ...form, [key]: e.target.value })
974
+ }
975
+ );
976
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: S.intro, children: [
978
+ "登记外部 AI 工具的记忆来源(仅存路径指针,正文不复制入库)。扫描为只读预览;实际导入由「memport 导入」经 memory_import 完成。整面需在设置开启",
979
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: " memport.enabled" }),
980
+ "。"
981
+ ] }),
982
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: S.toolbar, children: [
983
+ input("id", "来源 id(小写-连字符)"),
984
+ input("kind", "kind: claude/codex/workbuddy/file…"),
985
+ input("path", "绝对路径(目录或 .mem)"),
986
+ input("label", "显示名(可选)"),
987
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(NButton, { onClick: add, children: "登记来源" })
988
+ ] }),
989
+ !data?.memoryMutate ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.hint, children: "提示:登记/退役需先在面板开启「高权限模式」。" }) : null,
990
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { ...S.flexRow, margin: "8px 0 12px" }, children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: S.muted, children: data ? data.sources.length ? data.sources.length + " 个来源" : "暂无来源" : "加载中…" }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.grow }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(NButton, { onClick: runScan, disabled: busy, children: busy ? "扫描中…" : "扫描全部来源" }),
994
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(NButton, { onClick: load, children: "刷新" })
995
+ ] }),
996
+ error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.error, children: error }) : null,
997
+ (data?.sources || []).map((src) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SourceCard, { rpc, src, onChanged: load, onErr: setError }, src.id)),
998
+ scan ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: 12 }, children: [
999
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: S.heading, children: [
1000
+ "扫描预览",
1001
+ scan.notice ? `(${scan.notice})` : ""
1002
+ ] }),
1003
+ scan.sources.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-mem-card", style: S.card, children: [
1004
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: S.cardHead, children: [
1005
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: s.label || s.sourceId }),
1006
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: S.muted, children: ":新 " + s.counts.new + " · 近重复 " + s.counts["near-dup"] + " · 疑似冲突 " + s.counts["conflict-candidate"] + " · 重复 " + s.counts.dup }),
1007
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.grow }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: S.muted, children: s.files + " 包 · " + s.total + " 条" })
1009
+ ] }),
1010
+ s.errors.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...S.error, whiteSpace: "pre-wrap" }, children: s.errors.join("\n") }) : null,
1011
+ s.sampleNew.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...S.detail }, children: s.sampleNew.map((n) => `[${n.type}] ${n.content}`).join("\n") }) : s.counts.new > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.muted, children: "有新记录(样本省略)。" }) : null
1012
+ ] }, s.sourceId)),
1013
+ scan.enabled && scan.sources.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.muted, children: "无启用来源,或来源下未发现 .mem。" }) : null
1014
+ ] }) : null
1015
+ ] });
1016
+ }
1017
+
1018
+ // client/src/tabs/SkillsTab.tsx
1019
+ var import_react4 = require("react");
1020
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1021
+ function errMsg2(r) {
1022
+ return r && r.error && r.error.message ? r.error.message : "RPC 错误";
1023
+ }
1024
+ var TITLES = { candidate: "候选", active: "活跃", archived: "已归档" };
1025
+ function SkillsTab(props) {
1026
+ const rpc = props.rpc;
1027
+ const [data, setData] = (0, import_react4.useState)(null);
1028
+ const [error, setError] = (0, import_react4.useState)(null);
1029
+ const [busy, setBusy] = (0, import_react4.useState)(false);
1030
+ const load = (0, import_react4.useCallback)(() => {
1031
+ rpc("dsh-memory/skills-list", {}).then((r) => {
1032
+ if (r && r.ok) {
1033
+ setData(r.value);
1034
+ setError(null);
1035
+ } else setError(errMsg2(r));
1036
+ }).catch((e) => setError(String(e?.message || e)));
1037
+ }, [rpc]);
1038
+ (0, import_react4.useEffect)(() => {
1039
+ load();
1040
+ }, [load]);
1041
+ const act = (id, action, tier) => {
1042
+ if (busy) return;
1043
+ setBusy(true);
1044
+ rpc("dsh-memory/skills-action", { id, action, tier }).then((r) => {
1045
+ if (r && r.ok) {
1046
+ setError(r.value.changed ? null : r.value.notice || null);
1047
+ load();
1048
+ } else setError(errMsg2(r));
1049
+ }).catch((e) => setError(String(e?.message || e))).finally(() => setBusy(false));
1050
+ };
1051
+ if (data && !data.enabled) {
1052
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: S.intro, children: "技能结晶(程序性记忆)未启用。到「概览」页开启「技能」开关(或部署配置 skills.enabled),重复工作流会固化为技能候选,在此审阅晋升。" });
1053
+ }
1054
+ const mutateHint = data && !data.memoryMutate ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.hint, children: "提示:晋升/归档/固定需在面板开启「高权限模式」。" }) : null;
1055
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1057
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.muted, children: data ? `${data.skills.length} 个技能 · 自动晋升${data.allowPromote ? "开" : "关(人工审阅)"} · 需证据会话达阈` : "加载中…" }),
1058
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.grow }),
1059
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NButton, { onClick: load, children: "刷新" })
1060
+ ] }),
1061
+ mutateHint,
1062
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.error, children: error }) : null,
1063
+ data && data.skills.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: S.intro, children: '暂无技能。技能从重复出现的"工作方法"记忆结晶而来。' }) : null,
1064
+ (data?.skills || []).map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-mem-card", style: S.card, children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: S.cardHead, children: [
1066
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: s.title }),
1067
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-mem-tag", style: { marginLeft: 6 }, children: TITLES[s.status] }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.muted, children: ` · 档:${s.tier}${s.pinned ? " · 已固定" : ""} · 覆盖 ${s.sessionCount} 会话` }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.grow })
1070
+ ] }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: 6, flexWrap: "wrap", marginTop: 6 }, children: [
1072
+ s.status === "candidate" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NButton, { onClick: () => act(s.id, "promote"), disabled: busy, children: "晋升" }) : null,
1073
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1074
+ "select",
1075
+ {
1076
+ className: "dsh-mem-select",
1077
+ style: { width: "auto", padding: "5px 10px" },
1078
+ value: s.tier,
1079
+ onChange: (e) => act(s.id, "set-tier", e.target.value),
1080
+ children: [
1081
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "full", children: "全文档" }),
1082
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "excerpt", children: "摘录档" }),
1083
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "hint", children: "提示档" })
1084
+ ]
1085
+ }
1086
+ ),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NButton, { onClick: () => act(s.id, s.pinned ? "unpin" : "pin"), disabled: busy, children: s.pinned ? "取消固定" : "固定" }),
1088
+ s.status !== "archived" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NButton, { onClick: () => act(s.id, "archive"), disabled: busy, children: "归档" }) : null
1089
+ ] })
1090
+ ] }, s.id))
1091
+ ] });
1092
+ }
1093
+
1094
+ // client/src/tabs/CostTab.tsx
1095
+ var import_react5 = require("react");
821
1096
 
822
1097
  // client/src/ui/controls.tsx
823
- var import_jsx_runtime2 = require("react/jsx-runtime");
1098
+ var import_jsx_runtime4 = require("react/jsx-runtime");
824
1099
  function Switch(props) {
825
1100
  const on = !!props.checked;
826
1101
  const disabled = !!props.disabled;
827
1102
  const base = { ...S.switch, ...on ? S.switchOn : S.switchOff, ...disabled ? S.switchDisabled : null };
828
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
829
1104
  "div",
830
1105
  {
831
1106
  style: base,
832
1107
  onClick: () => {
833
1108
  if (!disabled && props.onChange) props.onChange(!on);
834
1109
  },
835
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { ...S.knob, left: on ? 18 : 2 } })
1110
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { ...S.knob, left: on ? 18 : 2 } })
836
1111
  }
837
1112
  );
838
1113
  }
839
1114
  function SwitchRow(props) {
840
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: S.switchRow, children: [
841
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Switch, { checked: props.checked, disabled: props.disabled, onChange: props.onChange }),
842
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.switchLabel, children: props.label }),
844
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.switchDesc, children: props.desc || "" })
1115
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: S.switchRow, children: [
1116
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Switch, { checked: props.checked, disabled: props.disabled, onChange: props.onChange }),
1117
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1118
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: S.switchLabel, children: props.label }),
1119
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: S.switchDesc, children: props.desc || "" })
845
1120
  ] })
846
1121
  ] });
847
1122
  }
848
1123
  function ActionButton(props) {
849
1124
  const disabled = !!props.disabled;
850
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1125
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
851
1126
  "button",
852
1127
  {
853
1128
  type: "button",
@@ -871,10 +1146,10 @@ var __defProp = Object.defineProperty;
871
1146
  function Segmented(props) {
872
1147
  const value = props.value;
873
1148
  const disabled = !!props.disabled;
874
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...S.seg, ...disabled ? S.switchDisabled : null }, children: props.options.map((opt, i) => {
1149
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...S.seg, ...disabled ? S.switchDisabled : null }, children: props.options.map((opt, i) => {
875
1150
  const on = opt.key === value;
876
1151
  const optDisabled = disabled || !!opt.disabled;
877
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1152
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
878
1153
  "span",
879
1154
  {
880
1155
  title: opt.disabledTitle || opt.title || "",
@@ -895,7 +1170,7 @@ var __defProp = Object.defineProperty;
895
1170
  }
896
1171
 
897
1172
  // client/src/tabs/CostTab.tsx
898
- var import_jsx_runtime3 = require("react/jsx-runtime");
1173
+ var import_jsx_runtime5 = require("react/jsx-runtime");
899
1174
  function renderCostChart(buckets, models, maxY, fmtDate, fmtInt, palette) {
900
1175
  const W = 600;
901
1176
  const H = 200;
@@ -910,17 +1185,17 @@ var __defProp = Object.defineProperty;
910
1185
  const y = (v) => T + ih - v / maxY * ih;
911
1186
  const yTicks = [0, maxY / 2, maxY];
912
1187
  const xIdx = n > 2 ? [0, Math.floor((n - 1) / 2), n - 1] : n === 2 ? [0, 1] : [0];
913
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { viewBox: "0 0 " + W + " " + H, style: { width: "100%", height: "auto", display: "block" }, children: [
914
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: L, y1: y(0), x2: W - R2, y2: y(0), stroke: "var(--dsh-mem-border)", strokeWidth: 1 }),
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { viewBox: "0 0 " + W + " " + H, style: { width: "100%", height: "auto", display: "block" }, children: [
1189
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: L, y1: y(0), x2: W - R2, y2: y(0), stroke: "var(--dsh-mem-border)", strokeWidth: 1 }),
915
1190
  yTicks.map((v) => {
916
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: L - 6, y: y(v) + 4, textAnchor: "end", fontSize: 10, fill: "var(--dsh-mem-text-3)", children: fmtInt(v) }, "yt" + v);
1191
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: L - 6, y: y(v) + 4, textAnchor: "end", fontSize: 10, fill: "var(--dsh-mem-text-3)", children: fmtInt(v) }, "yt" + v);
917
1192
  }),
918
1193
  xIdx.map((i) => {
919
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: x(i), y: H - 8, textAnchor: "middle", fontSize: 10, fill: "var(--dsh-mem-text-3)", children: fmtDate(buckets[i] ? buckets[i].ts : 0) }, "xt" + i);
1194
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: x(i), y: H - 8, textAnchor: "middle", fontSize: 10, fill: "var(--dsh-mem-text-3)", children: fmtDate(buckets[i] ? buckets[i].ts : 0) }, "xt" + i);
920
1195
  }),
921
1196
  models.map((m, mi) => {
922
1197
  const pts = buckets.map((b, i) => x(i) + "," + y(b.byModel[m] || 0)).join(" ");
923
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1198
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
924
1199
  "polyline",
925
1200
  {
926
1201
  points: pts,
@@ -964,13 +1239,13 @@ var __defProp = Object.defineProperty;
964
1239
  var tdStyle = { fontSize: 12.5, color: "var(--dsh-mem-text-1)", textAlign: "right", padding: "4px 10px", fontFamily: "ui-monospace, Consolas, monospace" };
965
1240
  function CostTab(props) {
966
1241
  const rpc = props.rpc;
967
- const [data, setData] = (0, import_react3.useState)(null);
968
- const [error, setError] = (0, import_react3.useState)(null);
969
- const [granularity, setGranularity] = (0, import_react3.useState)("day");
970
- const [layer, setLayer] = (0, import_react3.useState)("");
971
- const [rangeDays, setRangeDays] = (0, import_react3.useState)(0);
972
- const [rangeOpen, setRangeOpen] = (0, import_react3.useState)(false);
973
- const load = (0, import_react3.useCallback)(() => {
1242
+ const [data, setData] = (0, import_react5.useState)(null);
1243
+ const [error, setError] = (0, import_react5.useState)(null);
1244
+ const [granularity, setGranularity] = (0, import_react5.useState)("day");
1245
+ const [layer, setLayer] = (0, import_react5.useState)("");
1246
+ const [rangeDays, setRangeDays] = (0, import_react5.useState)(0);
1247
+ const [rangeOpen, setRangeOpen] = (0, import_react5.useState)(false);
1248
+ const load = (0, import_react5.useCallback)(() => {
974
1249
  setError(null);
975
1250
  rpc("dsh-memory/token-cost", {
976
1251
  granularity,
@@ -982,7 +1257,7 @@ var __defProp = Object.defineProperty;
982
1257
  setError(String(e && e.message || e));
983
1258
  });
984
1259
  }, [rpc, granularity, rangeDays]);
985
- (0, import_react3.useEffect)(() => {
1260
+ (0, import_react5.useEffect)(() => {
986
1261
  load();
987
1262
  const timer = setInterval(load, 5e3);
988
1263
  return () => {
@@ -1055,13 +1330,13 @@ var __defProp = Object.defineProperty;
1055
1330
  });
1056
1331
  const cell = (lc, r, pick) => {
1057
1332
  const w = lc.win[r];
1058
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: tdStyle, children: w ? fmtInt(pick(w)) : "0" }, r);
1333
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { style: tdStyle, children: w ? fmtInt(pick(w)) : "0" }, r);
1059
1334
  };
1060
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1061
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1062
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Segmented, { value: layer, options: LAYER_OPTS, onChange: setLayer }),
1063
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Segmented, { value: granularity, options: GRAN_OPTS, onChange: setGranularity }),
1064
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1335
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1336
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1337
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Segmented, { value: layer, options: LAYER_OPTS, onChange: setLayer }),
1338
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Segmented, { value: granularity, options: GRAN_OPTS, onChange: setGranularity }),
1339
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1065
1340
  NButton,
1066
1341
  {
1067
1342
  onClick: () => {
@@ -1070,12 +1345,12 @@ var __defProp = Object.defineProperty;
1070
1345
  children: rangeDays > 0 ? "近 " + rangeDays + " 天" : "近N天"
1071
1346
  }
1072
1347
  ),
1073
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.grow }),
1074
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NButton, { onClick: load, children: "刷新" })
1348
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.grow }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NButton, { onClick: load, children: "刷新" })
1075
1350
  ] }),
1076
- rangeOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1077
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.muted, children: "展示近 N 天(正整数,清空=默认窗口;超出保留期后端自动回退)" }),
1078
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1351
+ rangeOpen ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1352
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: S.muted, children: "展示近 N 天(正整数,清空=默认窗口;超出保留期后端自动回退)" }),
1353
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1079
1354
  NInput,
1080
1355
  {
1081
1356
  value: rangeDays === 0 ? "" : String(rangeDays),
@@ -1093,94 +1368,94 @@ var __defProp = Object.defineProperty;
1093
1368
  }
1094
1369
  )
1095
1370
  ] }) : null,
1096
- error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.error, children: "成本读取失败:" + error }) : null,
1097
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "成本趋势(按模型)" }),
1098
- buckets.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1371
+ error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.error, children: "成本读取失败:" + error }) : null,
1372
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "成本趋势(按模型)" }),
1373
+ buckets.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1099
1374
  renderCostChart(buckets, models, maxY, fmtDate, fmtInt, PALETTE),
1100
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px 12px", margin: "6px 0 14px" }, children: models.map((m, mi) => {
1101
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1375
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px 12px", margin: "6px 0 14px" }, children: models.map((m, mi) => {
1376
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1102
1377
  "span",
1103
1378
  {
1104
1379
  style: { display: "inline-flex", alignItems: "center", gap: 4, fontSize: 12, color: "var(--dsh-mem-text-2)" },
1105
1380
  children: [
1106
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { width: 10, height: 10, borderRadius: 4, background: PALETTE[mi % PALETTE.length], display: "inline-block" } }),
1381
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { width: 10, height: 10, borderRadius: 4, background: PALETTE[mi % PALETTE.length], display: "inline-block" } }),
1107
1382
  m
1108
1383
  ]
1109
1384
  },
1110
1385
  "lg" + m
1111
1386
  );
1112
1387
  }) })
1113
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: S.muted, children: data ? "暂无成本数据(触发一次蒸馏后这里会出现趋势)。" : "加载中…" }),
1114
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "层级成本(输出 token)" }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1116
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1117
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { style: thFirst, children: "层级" }),
1388
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: S.muted, children: data ? "暂无成本数据(触发一次蒸馏后这里会出现趋势)。" : "加载中…" }),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "层级成本(输出 token)" }),
1390
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1391
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { children: [
1392
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { style: thFirst, children: "层级" }),
1118
1393
  RANGES.map((r) => {
1119
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { style: thStyle, children: RANGE_LABELS[r] }, r);
1394
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { style: thStyle, children: RANGE_LABELS[r] }, r);
1120
1395
  })
1121
1396
  ] }) }),
1122
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: layerTable.map((lc) => {
1123
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1124
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: tdFirst, children: lc.layer.toUpperCase() }),
1397
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { children: layerTable.map((lc) => {
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { children: [
1399
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { style: tdFirst, children: lc.layer.toUpperCase() }),
1125
1400
  RANGES.map((r) => cell(lc, r, (w) => w.outputTokens))
1126
1401
  ] }, lc.layer);
1127
1402
  }) })
1128
1403
  ] }),
1129
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "层级成本(单次 avg)" }),
1130
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1131
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1132
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { style: thFirst, children: "层级" }),
1404
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "层级成本(单次 avg)" }),
1405
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1406
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { children: [
1407
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { style: thFirst, children: "层级" }),
1133
1408
  RANGES.map((r) => {
1134
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { style: thStyle, children: RANGE_LABELS[r] + "-avg" }, r);
1409
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { style: thStyle, children: RANGE_LABELS[r] + "-avg" }, r);
1135
1410
  })
1136
1411
  ] }) }),
1137
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: layerTable.map((lc) => {
1138
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1139
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: tdFirst, children: lc.layer.toUpperCase() }),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { children: layerTable.map((lc) => {
1413
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { children: [
1414
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { style: tdFirst, children: lc.layer.toUpperCase() }),
1140
1415
  RANGES.map((r) => cell(lc, r, (w) => w.avgOutputTokens))
1141
1416
  ] }, lc.layer);
1142
1417
  }) })
1143
1418
  ] }),
1144
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "层级成本(单次 median)" }),
1145
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1146
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1147
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { style: thFirst, children: "层级" }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "层级成本(单次 median)" }),
1420
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { children: [
1422
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { style: thFirst, children: "层级" }),
1148
1423
  RANGES.map((r) => {
1149
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { style: thStyle, children: RANGE_LABELS[r] + "-median" }, r);
1424
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { style: thStyle, children: RANGE_LABELS[r] + "-median" }, r);
1150
1425
  })
1151
1426
  ] }) }),
1152
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: layerTable.map((lc) => {
1153
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1154
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: tdFirst, children: lc.layer.toUpperCase() }),
1427
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { children: layerTable.map((lc) => {
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { style: tdFirst, children: lc.layer.toUpperCase() }),
1155
1430
  RANGES.map((r) => cell(lc, r, (w) => w.medianOutputTokens))
1156
1431
  ] }, lc.layer);
1157
1432
  }) })
1158
1433
  ] }),
1159
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "时间窗口总览" }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.statGrid, children: windows.map((w) => {
1161
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
1162
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.statNum, children: fmtInt(w.outputTokens + w.reasoningTokens) }),
1163
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.statLabel, children: RANGE_LABELS[w.range] + " · 总输出 token" }),
1164
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.muted, children: "文字 " + fmtInt(w.outputTokens) + " · 思考 " + fmtInt(w.reasoningTokens) + " · " + w.calls + " 次调用" })
1434
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "时间窗口总览" }),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.statGrid, children: windows.map((w) => {
1436
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
1437
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.statNum, children: fmtInt(w.outputTokens + w.reasoningTokens) }),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.statLabel, children: RANGE_LABELS[w.range] + " · 总输出 token" }),
1439
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.muted, children: "文字 " + fmtInt(w.outputTokens) + " · 思考 " + fmtInt(w.reasoningTokens) + " · " + w.calls + " 次调用" })
1165
1440
  ] }, w.range);
1166
1441
  }) }),
1167
- byModel.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1168
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "按模型(累计)" }),
1442
+ byModel.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: S.panelLabel, children: "按模型(累计)" }),
1169
1444
  byModel.map((m) => {
1170
1445
  const label = fmtModel(m.provider, m.model);
1171
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: S.infoRow, children: [
1172
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.infoKey, children: label }),
1173
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.infoVal, children: m.calls + " 次 · 输出 " + fmtInt(m.outputTokens) + " · 思考 " + fmtInt(m.reasoningTokens) })
1446
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: S.infoRow, children: [
1447
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: S.infoKey, children: label }),
1448
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: S.infoVal, children: m.calls + " 次 · 输出 " + fmtInt(m.outputTokens) + " · 思考 " + fmtInt(m.reasoningTokens) })
1174
1449
  ] }, "m-" + label);
1175
1450
  })
1176
1451
  ] }) : null,
1177
- data ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: S.hint, children: "输入按字符、输出/思考按 token 计;趋势图 Y 轴为输出 token,上方可切换层级与颗粒度。" }) : null
1452
+ data ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: S.hint, children: "输入按字符、输出/思考按 token 计;趋势图 Y 轴为输出 token,上方可切换层级与颗粒度。" }) : null
1178
1453
  ] });
1179
1454
  }
1180
1455
 
1181
1456
  // client/src/tabs/LogTab.tsx
1182
- var import_react4 = require("react");
1183
- var import_jsx_runtime4 = require("react/jsx-runtime");
1457
+ var import_react6 = require("react");
1458
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1184
1459
  var SOURCES = [
1185
1460
  { id: "", label: "系统日志" },
1186
1461
  { id: "recall_turn", label: "召回追踪" },
@@ -1203,11 +1478,11 @@ var __defProp = Object.defineProperty;
1203
1478
  }
1204
1479
  function LogTab(props) {
1205
1480
  const rpc = props.rpc;
1206
- const [source, setSource] = (0, import_react4.useState)("");
1207
- const [lines, setLines] = (0, import_react4.useState)(null);
1208
- const [error, setError] = (0, import_react4.useState)(null);
1209
- const preRef = (0, import_react4.useRef)(null);
1210
- const load = (0, import_react4.useCallback)(() => {
1481
+ const [source, setSource] = (0, import_react6.useState)("");
1482
+ const [lines, setLines] = (0, import_react6.useState)(null);
1483
+ const [error, setError] = (0, import_react6.useState)(null);
1484
+ const preRef = (0, import_react6.useRef)(null);
1485
+ const load = (0, import_react6.useCallback)(() => {
1211
1486
  setError(null);
1212
1487
  if (source === "") {
1213
1488
  rpc("dsh-memory/log-tail", { lines: 200 }).then((r) => {
@@ -1221,25 +1496,25 @@ var __defProp = Object.defineProperty;
1221
1496
  else setError(r && r.error ? r.error.message : "RPC error");
1222
1497
  }).catch((e) => setError(String(e && e.message || e)));
1223
1498
  }, [rpc, source]);
1224
- (0, import_react4.useEffect)(() => {
1499
+ (0, import_react6.useEffect)(() => {
1225
1500
  load();
1226
1501
  }, [load]);
1227
- (0, import_react4.useEffect)(() => {
1502
+ (0, import_react6.useEffect)(() => {
1228
1503
  if (lines && preRef.current) preRef.current.scrollTop = preRef.current.scrollHeight;
1229
1504
  }, [lines]);
1230
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1231
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10, gap: 8 }, children: [
1232
- SOURCES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NButton, { onClick: () => setSource(s.id), children: source === s.id ? "● " + s.label : s.label }, s.id)),
1233
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: S.grow }),
1234
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NButton, { onClick: load, children: "刷新" })
1505
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1506
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10, gap: 8 }, children: [
1507
+ SOURCES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { onClick: () => setSource(s.id), children: source === s.id ? "● " + s.label : s.label }, s.id)),
1508
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: S.grow }),
1509
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NButton, { onClick: load, children: "刷新" })
1235
1510
  ] }),
1236
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...S.muted, marginBottom: 10 }, children: error ? "加载失败" : lines === null ? "加载中…" : `最近 ${lines.length} 条(${SOURCES.find((s) => s.id === source)?.label})` }),
1237
- error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "读取失败:" + error + "(点右上“刷新”重试)" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: S.pre, ref: preRef, children: (lines || []).join("\n") || "(暂无记录)" })
1511
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...S.muted, marginBottom: 10 }, children: error ? "加载失败" : lines === null ? "加载中…" : `最近 ${lines.length} 条(${SOURCES.find((s) => s.id === source)?.label})` }),
1512
+ error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "读取失败:" + error + "(点右上“刷新”重试)" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { style: S.pre, ref: preRef, children: (lines || []).join("\n") || "(暂无记录)" })
1238
1513
  ] });
1239
1514
  }
1240
1515
 
1241
1516
  // client/src/tabs/OverviewTab.tsx
1242
- var import_react13 = require("react");
1517
+ var import_react15 = require("react");
1243
1518
 
1244
1519
  // client/src/pill/modes.ts
1245
1520
  var MODES = [
@@ -1259,11 +1534,11 @@ var __defProp = Object.defineProperty;
1259
1534
  }
1260
1535
 
1261
1536
  // client/src/tabs/DistillSettings.tsx
1262
- var import_react9 = require("react");
1537
+ var import_react11 = require("react");
1263
1538
 
1264
1539
  // client/src/tabs/BudgetInputs.tsx
1265
- var import_react5 = require("react");
1266
- var import_jsx_runtime5 = require("react/jsx-runtime");
1540
+ var import_react7 = require("react");
1541
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1267
1542
  var LAYERS = [
1268
1543
  ["extract", "抽取"],
1269
1544
  ["dedup", "去重"],
@@ -1284,7 +1559,7 @@ var __defProp = Object.defineProperty;
1284
1559
  const onError = props.onError;
1285
1560
  const scope = props.scope ?? "all";
1286
1561
  const layers = scope === "all" || scope === "input" ? LAYERS : LAYERS.filter((l) => SCOPE_KEYS[scope].includes(l[0]));
1287
- const [draft, setDraft] = (0, import_react5.useState)(null);
1562
+ const [draft, setDraft] = (0, import_react7.useState)(null);
1288
1563
  if (!data || !data.budgets) return null;
1289
1564
  const cur = data.budgets.current || {};
1290
1565
  const def = data.budgets.defaults || {};
@@ -1380,7 +1655,7 @@ var __defProp = Object.defineProperty;
1380
1655
  return false;
1381
1656
  };
1382
1657
  const inputBox = (key, _label, title, width, placeholder, onCommit) => {
1383
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1658
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1384
1659
  NInput,
1385
1660
  {
1386
1661
  type: "number",
@@ -1411,10 +1686,10 @@ var __defProp = Object.defineProperty;
1411
1686
  const showInput = scope === "all" || scope === "input";
1412
1687
  const effNote = layers.map((l) => eff[l[0]] || "?").join(" / ");
1413
1688
  const rowLabel = (key) => key === "extract" ? "抽取输出" : key === "dedup" ? "去重输出" : key === "l2" ? "L2 输出" : "L3 输出";
1414
- const rowStyle = { display: "flex", alignItems: "center", gap: 8 };
1415
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1416
- showOutputs ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginTop: 12 }, children: [
1417
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1689
+ const rowStyle2 = { display: "flex", alignItems: "center", gap: 8 };
1690
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1691
+ showOutputs ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 12 }, children: [
1692
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1418
1693
  "div",
1419
1694
  {
1420
1695
  style: S.switchLabel,
@@ -1422,8 +1697,8 @@ var __defProp = Object.defineProperty;
1422
1697
  children: "输出预算"
1423
1698
  }
1424
1699
  ),
1425
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: layers.map((l) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: rowStyle, children: [
1426
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: rowLabel(l[0]) }),
1700
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: layers.map((l) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: rowStyle2, children: [
1701
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: rowLabel(l[0]) }),
1427
1702
  inputBox(
1428
1703
  l[0],
1429
1704
  l[1],
@@ -1432,11 +1707,11 @@ var __defProp = Object.defineProperty;
1432
1707
  def[l[0]],
1433
1708
  commitOutputs
1434
1709
  ),
1435
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "token" })
1710
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "token" })
1436
1711
  ] }, l[0])) })
1437
1712
  ] }) : null,
1438
- showInput ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginTop: 12 }, children: [
1439
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1713
+ showInput ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 12 }, children: [
1714
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1440
1715
  "div",
1441
1716
  {
1442
1717
  style: S.switchLabel,
@@ -1444,8 +1719,8 @@ var __defProp = Object.defineProperty;
1444
1719
  children: "输入预算"
1445
1720
  }
1446
1721
  ),
1447
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: rowStyle, children: [
1448
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: "单次输入" }),
1722
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: rowStyle2, children: [
1723
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: "单次输入" }),
1449
1724
  inputBox(
1450
1725
  "input",
1451
1726
  "输入",
@@ -1454,15 +1729,15 @@ var __defProp = Object.defineProperty;
1454
1729
  ib.fallback,
1455
1730
  commitInput
1456
1731
  ),
1457
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "字符" })
1732
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "字符" })
1458
1733
  ] }) })
1459
1734
  ] }) : null
1460
1735
  ] });
1461
1736
  }
1462
1737
 
1463
1738
  // client/src/tabs/ChannelEditor.tsx
1464
- var import_react6 = require("react");
1465
- var import_jsx_runtime6 = require("react/jsx-runtime");
1739
+ var import_react8 = require("react");
1740
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1466
1741
  var STY = {
1467
1742
  block: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" },
1468
1743
  head: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 6 },
@@ -1493,15 +1768,15 @@ var __defProp = Object.defineProperty;
1493
1768
  ];
1494
1769
  function ChannelEditor(props) {
1495
1770
  const { channel, rpc, disabled } = props;
1496
- const [baseText, setBaseText] = (0, import_react6.useState)(channel.runtimeBaseURL);
1497
- const [keyText, setKeyText] = (0, import_react6.useState)("");
1498
- const baseTimer = (0, import_react6.useRef)(void 0);
1499
- const dirty = (0, import_react6.useRef)(false);
1500
- (0, import_react6.useEffect)(() => {
1771
+ const [baseText, setBaseText] = (0, import_react8.useState)(channel.runtimeBaseURL);
1772
+ const [keyText, setKeyText] = (0, import_react8.useState)("");
1773
+ const baseTimer = (0, import_react8.useRef)(void 0);
1774
+ const dirty = (0, import_react8.useRef)(false);
1775
+ (0, import_react8.useEffect)(() => {
1501
1776
  if (!dirty.current) setBaseText(channel.runtimeBaseURL);
1502
1777
  }, [channel.runtimeBaseURL]);
1503
- (0, import_react6.useEffect)(() => () => clearTimeout(baseTimer.current), []);
1504
- const commit = (patch) => {
1778
+ (0, import_react8.useEffect)(() => () => clearTimeout(baseTimer.current), []);
1779
+ const commit2 = (patch) => {
1505
1780
  rpc("dsh-memory/settings-set", patch).catch(() => {
1506
1781
  });
1507
1782
  };
@@ -1511,14 +1786,14 @@ var __defProp = Object.defineProperty;
1511
1786
  clearTimeout(baseTimer.current);
1512
1787
  baseTimer.current = setTimeout(() => {
1513
1788
  dirty.current = false;
1514
- commit({ directBaseURL: v.trim() });
1789
+ commit2({ directBaseURL: v.trim() });
1515
1790
  }, 600);
1516
1791
  };
1517
1792
  const commitKey = () => {
1518
1793
  const v = keyText.trim();
1519
1794
  if (v) {
1520
1795
  dirty.current = true;
1521
- commit({ directApiKey: v });
1796
+ commit2({ directApiKey: v });
1522
1797
  dirty.current = false;
1523
1798
  }
1524
1799
  setKeyText("");
@@ -1529,29 +1804,29 @@ var __defProp = Object.defineProperty;
1529
1804
  const deployedDirect = channel.deployed === "direct";
1530
1805
  const apiKeySet = channel.runtimeApiKeySet || channel.deployedApiKeySet;
1531
1806
  const showWarn = isDirect && !channel.directReady;
1532
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.block, children: [
1533
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.head, children: [
1534
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.title, children: "蒸馏通道" }),
1535
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: isDirect ? STY.chipAccent : STY.chipMuted, children: isDirect ? "直连端点" : "复用宿主" }),
1536
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { title: "direct = 插件原生直连 OpenAI 兼容端点,与付费 API 解耦;失败自动回退宿主路由链。", style: { marginLeft: "auto", fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: [
1807
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY.block, children: [
1808
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY.head, children: [
1809
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY.title, children: "蒸馏通道" }),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: isDirect ? STY.chipAccent : STY.chipMuted, children: isDirect ? "直连端点" : "复用宿主" }),
1811
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { title: "direct = 插件原生直连 OpenAI 兼容端点,与付费 API 解耦;失败自动回退宿主路由链。", style: { marginLeft: "auto", fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: [
1537
1812
  "生效:",
1538
1813
  runtime ? runtime === "direct" ? "运行时直连" : "运行时宿主" : "跟随部署配置"
1539
1814
  ] })
1540
1815
  ] }),
1541
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.desc, children: "direct 通道走插件原生 HTTP,不依赖宿主 provider 注册表;direct 失败自动回退宿主路由链作兜底安全网。" }),
1542
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1816
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY.desc, children: "direct 通道走插件原生 HTTP,不依赖宿主 provider 注册表;direct 失败自动回退宿主路由链作兜底安全网。" }),
1817
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1543
1818
  Segmented,
1544
1819
  {
1545
1820
  value: runtime,
1546
1821
  disabled,
1547
1822
  options: MODE_OPTIONS.map((m) => ({ key: m.key, label: m.label, title: m.title })),
1548
- onChange: (k) => commit({ distillMode: k })
1823
+ onChange: (k) => commit2({ distillMode: k })
1549
1824
  }
1550
1825
  ),
1551
- editing ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1552
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.fieldRow, children: [
1553
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.fieldLabel, children: "端点 URL" }),
1554
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1826
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY.fieldRow, children: [
1828
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY.fieldLabel, children: "端点 URL" }),
1829
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1555
1830
  NInput,
1556
1831
  {
1557
1832
  style: STY.input,
@@ -1563,19 +1838,19 @@ var __defProp = Object.defineProperty;
1563
1838
  if (e.key === "Enter") {
1564
1839
  clearTimeout(baseTimer.current);
1565
1840
  dirty.current = false;
1566
- commit({ directBaseURL: baseText.trim() });
1841
+ commit2({ directBaseURL: baseText.trim() });
1567
1842
  }
1568
1843
  }
1569
1844
  }
1570
1845
  ),
1571
- deployedDirect && !channel.runtimeBaseURL ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: [
1846
+ deployedDirect && !channel.runtimeBaseURL ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: [
1572
1847
  "部署基线:",
1573
1848
  channel.deployedBaseURL || "(空)"
1574
1849
  ] }) : null
1575
1850
  ] }),
1576
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.fieldRow, children: [
1577
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.fieldLabel, children: "API Key" }),
1578
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1851
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY.fieldRow, children: [
1852
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY.fieldLabel, children: "API Key" }),
1853
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1579
1854
  NInput,
1580
1855
  {
1581
1856
  style: STY.input,
@@ -1591,41 +1866,41 @@ var __defProp = Object.defineProperty;
1591
1866
  onBlur: commitKey
1592
1867
  }
1593
1868
  ),
1594
- apiKeySet ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.chipAccent, children: "已配置" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.chipMuted, children: "未配置" }),
1595
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1869
+ apiKeySet ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY.chipAccent, children: "已配置" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY.chipMuted, children: "未配置" }),
1870
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1596
1871
  NButton,
1597
1872
  {
1598
1873
  disabled,
1599
1874
  title: "写入空串清除运行时密钥(部署 .yml 的密钥不受影响)",
1600
- onClick: () => commit({ directApiKey: "" }),
1875
+ onClick: () => commit2({ directApiKey: "" }),
1601
1876
  children: "清除"
1602
1877
  }
1603
1878
  )
1604
1879
  ] }),
1605
- showWarn ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.warn, children: "⚠ direct 未配置完整:需同时有端点 URL 与模型(llm.model / 全局链主路由),否则会回退宿主路由。" }) : null
1606
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.preview, children: isDirect ? "当前将直连端点:" + (channel.runtimeBaseURL || channel.deployedBaseURL || "(未配置端点)") + (apiKeySet ? " · 密钥已配置" : " · 未配置密钥") : "当前复用宿主 ctx.llm(" + (runtime === "host" ? "运行时锁定" : "跟随部署配置") + ")" })
1880
+ showWarn ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY.warn, children: "⚠ direct 未配置完整:需同时有端点 URL 与模型(llm.model / 全局链主路由),否则会回退宿主路由。" }) : null
1881
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY.preview, children: isDirect ? "当前将直连端点:" + (channel.runtimeBaseURL || channel.deployedBaseURL || "(未配置端点)") + (apiKeySet ? " · 密钥已配置" : " · 未配置密钥") : "当前复用宿主 ctx.llm(" + (runtime === "host" ? "运行时锁定" : "跟随部署配置") + ")" })
1607
1882
  ] });
1608
1883
  }
1609
1884
 
1610
1885
  // client/src/tabs/RouteChainEditor.tsx
1611
- var import_react8 = require("react");
1886
+ var import_react10 = require("react");
1612
1887
 
1613
1888
  // client/src/ui/NSel.tsx
1614
- var import_react7 = require("react");
1615
- var import_jsx_runtime7 = require("react/jsx-runtime");
1889
+ var import_react9 = require("react");
1890
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1616
1891
  function NSel(props) {
1617
1892
  const options = props.options || [];
1618
1893
  const value = props.value || "";
1619
1894
  const disabled = !!props.disabled;
1620
- const [open, setOpen] = (0, import_react7.useState)(false);
1621
- const [idx, setIdx] = (0, import_react7.useState)(-1);
1622
- const wrapRef = (0, import_react7.useRef)(null);
1623
- const listRef = (0, import_react7.useRef)(null);
1895
+ const [open, setOpen] = (0, import_react9.useState)(false);
1896
+ const [idx, setIdx] = (0, import_react9.useState)(-1);
1897
+ const wrapRef = (0, import_react9.useRef)(null);
1898
+ const listRef = (0, import_react9.useRef)(null);
1624
1899
  let selectedLabel = "";
1625
1900
  for (let si = 0; si < options.length; si++) {
1626
1901
  if (options[si].id === value) selectedLabel = options[si].label;
1627
1902
  }
1628
- (0, import_react7.useEffect)(() => {
1903
+ (0, import_react9.useEffect)(() => {
1629
1904
  if (!open) return void 0;
1630
1905
  const onDown = (e) => {
1631
1906
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -1635,7 +1910,7 @@ var __defProp = Object.defineProperty;
1635
1910
  document.removeEventListener("mousedown", onDown);
1636
1911
  };
1637
1912
  }, [open]);
1638
- (0, import_react7.useEffect)(() => {
1913
+ (0, import_react9.useEffect)(() => {
1639
1914
  if (!open || !listRef.current) return;
1640
1915
  const el = listRef.current.querySelector('[data-active="1"]');
1641
1916
  if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
@@ -1698,8 +1973,8 @@ var __defProp = Object.defineProperty;
1698
1973
  const to = e.relatedTarget;
1699
1974
  if (!to || wrapRef.current && !wrapRef.current.contains(to)) setOpen(false);
1700
1975
  };
1701
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-mem-sel", style: props.style, ref: wrapRef, onKeyDown: onKey, onBlur, children: [
1702
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dsh-mem-sel", style: props.style, ref: wrapRef, onKeyDown: onKey, onBlur, children: [
1977
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1703
1978
  "button",
1704
1979
  {
1705
1980
  type: "button",
@@ -1715,13 +1990,13 @@ var __defProp = Object.defineProperty;
1715
1990
  }
1716
1991
  },
1717
1992
  children: [
1718
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-select-label", children: selectedLabel || props.placeholder || "(请选择)" }),
1719
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-sel-chev" + (open ? " dsh-mem-sel-chev-open" : ""), "aria-hidden": true })
1993
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-mem-select-label", children: selectedLabel || props.placeholder || "(请选择)" }),
1994
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-mem-sel-chev" + (open ? " dsh-mem-sel-chev-open" : ""), "aria-hidden": true })
1720
1995
  ]
1721
1996
  }
1722
1997
  ),
1723
- open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-mem-pop", ref: listRef, role: "listbox", children: options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-mem-pop-empty", children: "无选项" }) : options.map((o, i) => {
1724
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1998
+ open && !disabled ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dsh-mem-pop", ref: listRef, role: "listbox", children: options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "dsh-mem-pop-empty", children: "无选项" }) : options.map((o, i) => {
1999
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1725
2000
  "button",
1726
2001
  {
1727
2002
  type: "button",
@@ -1739,8 +2014,8 @@ var __defProp = Object.defineProperty;
1739
2014
  if (idx !== i) setIdx(i);
1740
2015
  },
1741
2016
  children: [
1742
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-pop-opt-label", children: o.label }),
1743
- o.id === value ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-pop-check", children: "✓" }) : null
2017
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-mem-pop-opt-label", children: o.label }),
2018
+ o.id === value ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dsh-mem-pop-check", children: "✓" }) : null
1744
2019
  ]
1745
2020
  },
1746
2021
  o.id
@@ -1750,7 +2025,7 @@ var __defProp = Object.defineProperty;
1750
2025
  }
1751
2026
 
1752
2027
  // client/src/tabs/RouteChainEditor.tsx
1753
- var import_jsx_runtime8 = require("react/jsx-runtime");
2028
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1754
2029
  var modelsCache = {};
1755
2030
  var EFFORT_VOCAB = ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"];
1756
2031
  var STY2 = {
@@ -1779,26 +2054,26 @@ var __defProp = Object.defineProperty;
1779
2054
  const disabled = !!props.disabled;
1780
2055
  const scope = props.scope ?? "global";
1781
2056
  const isLayer = scope !== "global";
1782
- const [info, setInfo] = (0, import_react8.useState)(null);
1783
- const [rows, setRows] = (0, import_react8.useState)(null);
1784
- const [rowErrs, setRowErrs] = (0, import_react8.useState)({});
1785
- const [err, setErr] = (0, import_react8.useState)(null);
1786
- const [manual, setManual] = (0, import_react8.useState)({ idx: -1, text: "" });
1787
- const pendingWrites = (0, import_react8.useRef)(0);
2057
+ const [info, setInfo] = (0, import_react10.useState)(null);
2058
+ const [rows, setRows] = (0, import_react10.useState)(null);
2059
+ const [rowErrs, setRowErrs] = (0, import_react10.useState)({});
2060
+ const [err, setErr] = (0, import_react10.useState)(null);
2061
+ const [manual, setManual] = (0, import_react10.useState)({ idx: -1, text: "" });
2062
+ const pendingWrites = (0, import_react10.useRef)(0);
1788
2063
  function refreshInfo() {
1789
2064
  rpc("dsh-memory/llm-providers", {}).then((r) => {
1790
2065
  if (r && r.ok && pendingWrites.current === 0) setInfo(r.value);
1791
2066
  }).catch(() => {
1792
2067
  });
1793
2068
  }
1794
- (0, import_react8.useEffect)(() => {
2069
+ (0, import_react10.useEffect)(() => {
1795
2070
  refreshInfo();
1796
2071
  const timer = setInterval(refreshInfo, 5e3);
1797
2072
  return () => {
1798
2073
  clearInterval(timer);
1799
2074
  };
1800
2075
  }, [rpc]);
1801
- (0, import_react8.useEffect)(() => {
2076
+ (0, import_react10.useEffect)(() => {
1802
2077
  if (!info || !info.providers) return;
1803
2078
  info.providers.forEach((p) => {
1804
2079
  if (!p.id || modelsCache[p.id]) return;
@@ -1810,7 +2085,7 @@ var __defProp = Object.defineProperty;
1810
2085
  }, [rpc, info]);
1811
2086
  const layerView = isLayer && info && info.layerChains ? info.layerChains[scope] : null;
1812
2087
  const savedRows = isLayer ? layerView?.runtime ?? [] : info && info.chain ? info.chain.current : [];
1813
- (0, import_react8.useEffect)(() => {
2088
+ (0, import_react10.useEffect)(() => {
1814
2089
  if (rows === null && info && savedRows.length) {
1815
2090
  setRows(savedRows.map(copyRow));
1816
2091
  }
@@ -1958,17 +2233,17 @@ var __defProp = Object.defineProperty;
1958
2233
  providersById[p.id] = p;
1959
2234
  });
1960
2235
  function roRow(e, i) {
1961
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.roRow, children: [
1962
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY2.badge, children: i === 0 ? "主" : String(i + 1) }),
1963
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...STY2.mono, color: "var(--dsh-mem-text-2)" }, children: e.provider + " / " + e.model }),
1964
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { marginLeft: "auto", flexShrink: 0, fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: e.effort ? "档位 " + e.effort : "跟随部署配置" })
2236
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.roRow, children: [
2237
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: STY2.badge, children: i === 0 ? "主" : String(i + 1) }),
2238
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { ...STY2.mono, color: "var(--dsh-mem-text-2)" }, children: e.provider + " / " + e.model }),
2239
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { marginLeft: "auto", flexShrink: 0, fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: e.effort ? "档位 " + e.effort : "跟随部署配置" })
1965
2240
  ] }, "ro" + i);
1966
2241
  }
1967
2242
  if (info.pinned) {
1968
2243
  const effPin = isLayer ? layerView?.effectiveChain ?? [] : info.chain && info.chain.effectiveChain || [];
1969
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
2244
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.wrap, children: [
1970
2245
  effPin.map(roRow),
1971
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY2.note, children: "部署已锁定路由(pin),调整请修改 cordis.patch.yml 中 llm 的配置。" })
2246
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: STY2.note, children: "部署已锁定路由(pin),调整请修改 cordis.patch.yml 中 llm 的配置。" })
1972
2247
  ] });
1973
2248
  }
1974
2249
  if (rows === null) {
@@ -1976,21 +2251,21 @@ var __defProp = Object.defineProperty;
1976
2251
  const lv = layerView;
1977
2252
  const src = lv?.source ?? "global";
1978
2253
  if (src === "static" && lv) {
1979
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
2254
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.wrap, children: [
1980
2255
  lv.static.map((e, i) => roRow({ provider: e.provider, model: e.model, effort: e.reasoningEffort || "" }, i)),
1981
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { onClick: forkStatic, disabled, children: "自定义本层链" }) })
2256
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { onClick: forkStatic, disabled, children: "自定义本层链" }) })
1982
2257
  ] });
1983
2258
  }
1984
2259
  const previewRows = lv?.effectiveChain ?? [];
1985
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
1986
- previewRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.switchDesc, children: "本层跟随全局链,暂无可用路由。" }) : previewRows.map(roRow),
1987
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { onClick: forkStatic, disabled, children: "自定义本层链" }) })
2260
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.wrap, children: [
2261
+ previewRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.switchDesc, children: "本层跟随全局链,暂无可用路由。" }) : previewRows.map(roRow),
2262
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { onClick: forkStatic, disabled, children: "自定义本层链" }) })
1988
2263
  ] });
1989
2264
  }
1990
2265
  const effFollow = info.chain && info.chain.effectiveChain || [];
1991
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
1992
- effFollow.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.switchDesc, children: "蒸馏跟随默认模型,未配置回退链。" }) : effFollow.map(roRow),
1993
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { onClick: forkStatic, disabled, children: "编辑为运行时链" }) })
2266
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.wrap, children: [
2267
+ effFollow.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.switchDesc, children: "蒸馏跟随默认模型,未配置回退链。" }) : effFollow.map(roRow),
2268
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { onClick: forkStatic, disabled, children: "编辑为运行时链" }) })
1994
2269
  ] });
1995
2270
  }
1996
2271
  const capped = rows.length >= 8;
@@ -2031,10 +2306,10 @@ var __defProp = Object.defineProperty;
2031
2306
  const effortOptions = [{ id: "", label: "跟随部署配置" }].concat(
2032
2307
  curEfforts.filter((k) => EFFORT_VOCAB.indexOf(k) >= 0).map((k) => ({ id: k, label: k }))
2033
2308
  );
2034
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...STY2.row, ...rowErrs[i] ? STY2.rowErr : null }, children: [
2035
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.line, children: [
2036
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY2.badge, children: isPrimary ? "主" : String(i + 1) }),
2037
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2309
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { ...STY2.row, ...rowErrs[i] ? STY2.rowErr : null }, children: [
2310
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.line, children: [
2311
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: STY2.badge, children: isPrimary ? "主" : String(i + 1) }),
2312
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2038
2313
  NSel,
2039
2314
  {
2040
2315
  style: { flex: 1, minWidth: 150 },
@@ -2047,7 +2322,7 @@ var __defProp = Object.defineProperty;
2047
2322
  }
2048
2323
  }
2049
2324
  ),
2050
- !row2.provider ? null : manualInput ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2325
+ !row2.provider ? null : manualInput ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2051
2326
  NInput,
2052
2327
  {
2053
2328
  style: { flex: 1, minWidth: 150 },
@@ -2066,7 +2341,7 @@ var __defProp = Object.defineProperty;
2066
2341
  }
2067
2342
  }
2068
2343
  }
2069
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2344
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2070
2345
  NSel,
2071
2346
  {
2072
2347
  style: { flex: 1, minWidth: 150 },
@@ -2079,7 +2354,7 @@ var __defProp = Object.defineProperty;
2079
2354
  }
2080
2355
  }
2081
2356
  ),
2082
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2357
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2083
2358
  NSel,
2084
2359
  {
2085
2360
  style: { flexShrink: 0, width: 118 },
@@ -2093,8 +2368,8 @@ var __defProp = Object.defineProperty;
2093
2368
  }
2094
2369
  )
2095
2370
  ] }),
2096
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.actions, children: [
2097
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2371
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.actions, children: [
2372
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2098
2373
  NButton,
2099
2374
  {
2100
2375
  style: STY2.ico,
@@ -2106,7 +2381,7 @@ var __defProp = Object.defineProperty;
2106
2381
  children: "↑"
2107
2382
  }
2108
2383
  ),
2109
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2384
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2110
2385
  NButton,
2111
2386
  {
2112
2387
  style: STY2.ico,
@@ -2118,7 +2393,7 @@ var __defProp = Object.defineProperty;
2118
2393
  children: "↓"
2119
2394
  }
2120
2395
  ),
2121
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2396
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2122
2397
  NButton,
2123
2398
  {
2124
2399
  style: STY2.ico,
@@ -2131,14 +2406,14 @@ var __defProp = Object.defineProperty;
2131
2406
  }
2132
2407
  )
2133
2408
  ] }),
2134
- isPrimary && !row2.provider ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY2.note, children: "跟随默认模型" + (info.default ? ":" + info.default.provider + " / " + info.default.model : "") + "(档位跟随部署配置,选定模型后可单独设置)" }) : null,
2135
- !known ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY2.warn, children: "⚠ 供应商 " + row2.provider + " 已不在已注册路由中:该路由调用会失败并被链跳过(不阻止保存)。" }) : null,
2136
- rowErrs[i] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY2.warn, children: "✕ " + rowErrs[i] }) : null
2409
+ isPrimary && !row2.provider ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: STY2.note, children: "跟随默认模型" + (info.default ? ":" + info.default.provider + " / " + info.default.model : "") + "(档位跟随部署配置,选定模型后可单独设置)" }) : null,
2410
+ !known ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: STY2.warn, children: "⚠ 供应商 " + row2.provider + " 已不在已注册路由中:该路由调用会失败并被链跳过(不阻止保存)。" }) : null,
2411
+ rowErrs[i] ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: STY2.warn, children: "✕ " + rowErrs[i] }) : null
2137
2412
  ] }, "row" + i);
2138
2413
  });
2139
2414
  const effChain = isLayer ? layerView?.effectiveChain ?? [] : info.chain && info.chain.effectiveChain || [];
2140
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
2141
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2415
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: STY2.wrap, children: [
2416
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2142
2417
  "div",
2143
2418
  {
2144
2419
  style: { ...S.switchDesc, marginBottom: 8 },
@@ -2147,9 +2422,9 @@ var __defProp = Object.defineProperty;
2147
2422
  }
2148
2423
  ),
2149
2424
  rowEls,
2150
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { style: STY2.add, disabled: disabled || capped, onClick: addRow, children: capped ? "已达上限(8 条)" : "+ 添加回退路由" }),
2151
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 10 }, children: [
2152
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2425
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { style: STY2.add, disabled: disabled || capped, onClick: addRow, children: capped ? "已达上限(8 条)" : "+ 添加回退路由" }),
2426
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 10 }, children: [
2427
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2153
2428
  NButton,
2154
2429
  {
2155
2430
  style: isLayer ? { ...STY2.ghost, color: "var(--dsh-mem-danger)", border: "1px solid var(--dsh-mem-danger)" } : STY2.ghost,
@@ -2158,13 +2433,13 @@ var __defProp = Object.defineProperty;
2158
2433
  children: isLayer ? "清除自定义 · 跟随全局" : "清空并跟随部署配置"
2159
2434
  }
2160
2435
  ),
2161
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.grow }),
2162
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { variant: "primary", disabled, onClick: save, children: "保存" })
2436
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2437
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { variant: "primary", disabled, onClick: save, children: "保存" })
2163
2438
  ] }),
2164
- err ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { ...STY2.warn, marginTop: 8 }, children: "✕ " + err }) : null,
2165
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" }, children: [
2166
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 4 }, children: "实际链" + (dirty ? "(保存后更新;当前显示已保存值)" : "") }),
2167
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2439
+ err ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { ...STY2.warn, marginTop: 8 }, children: "✕ " + err }) : null,
2440
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" }, children: [
2441
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 4 }, children: "实际链" + (dirty ? "(保存后更新;当前显示已保存值)" : "") }),
2442
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2168
2443
  "div",
2169
2444
  {
2170
2445
  style: { fontSize: 12, color: "var(--dsh-mem-text-2)", wordBreak: "break-all", fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace" },
@@ -2176,7 +2451,7 @@ var __defProp = Object.defineProperty;
2176
2451
  }
2177
2452
 
2178
2453
  // client/src/tabs/DistillSettings.tsx
2179
- var import_jsx_runtime9 = require("react/jsx-runtime");
2454
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2180
2455
  var STY3 = {
2181
2456
  hint: { fontSize: 11, color: "var(--dsh-mem-text-3)", margin: "0 0 8px" },
2182
2457
  panelHead: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 8 },
@@ -2205,9 +2480,9 @@ var __defProp = Object.defineProperty;
2205
2480
  verticalAlign: "middle",
2206
2481
  flexShrink: 0
2207
2482
  };
2208
- if (props.kind === "runtime") return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...base, background: "var(--dsh-mem-accent)" } });
2209
- if (props.kind === "static") return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...base, background: "transparent", border: "1.5px solid var(--dsh-mem-text-3)" } });
2210
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...base, background: "var(--dsh-mem-track)" } });
2483
+ if (props.kind === "runtime") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { ...base, background: "var(--dsh-mem-accent)" } });
2484
+ if (props.kind === "static") return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { ...base, background: "transparent", border: "1.5px solid var(--dsh-mem-text-3)" } });
2485
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { ...base, background: "var(--dsh-mem-track)" } });
2211
2486
  }
2212
2487
  var LAYER_META = {
2213
2488
  l1: { seg: "L1", title: "L1 · 抽取 / 去重" },
@@ -2217,9 +2492,9 @@ var __defProp = Object.defineProperty;
2217
2492
  function DistillSettings(props) {
2218
2493
  const rpc = props.rpc;
2219
2494
  const disabled = !!props.disabled;
2220
- const [info, setInfo] = (0, import_react9.useState)(null);
2221
- const [tab, setTab] = (0, import_react9.useState)("g");
2222
- (0, import_react9.useEffect)(() => {
2495
+ const [info, setInfo] = (0, import_react11.useState)(null);
2496
+ const [tab, setTab] = (0, import_react11.useState)("g");
2497
+ (0, import_react11.useEffect)(() => {
2223
2498
  let alive = true;
2224
2499
  const refresh = () => {
2225
2500
  rpc("dsh-memory/llm-providers", {}).then((r) => {
@@ -2242,46 +2517,46 @@ var __defProp = Object.defineProperty;
2242
2517
  ...["l1", "l2", "l3"].map((k) => ({
2243
2518
  key: k,
2244
2519
  title: LAYER_META[k].title + " · " + (dotOf(k) === "runtime" ? "运行时自定义" : dotOf(k) === "static" ? "部署 YAML 层链(只读)" : "跟随全局"),
2245
- label: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2246
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dot, { kind: dotOf(k) }),
2520
+ label: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
2521
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Dot, { kind: dotOf(k) }),
2247
2522
  LAYER_META[k].seg
2248
2523
  ] })
2249
2524
  }))
2250
2525
  ];
2251
2526
  const chipTitle = (k) => dotOf(k) === "runtime" ? "本层走设置页自定义链" : dotOf(k) === "static" ? "本层走部署 YAML 层链(UI 只读,自定义可覆盖)" : "本层未单独配置,走全局默认链";
2252
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2253
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Segmented, { value: tab, options: segOptions, onChange: (k) => setTab(k) }),
2254
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: STY3.hint, children: [
2255
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dot, { kind: "runtime" }),
2527
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
2528
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { value: tab, options: segOptions, onChange: (k) => setTab(k) }),
2529
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: STY3.hint, children: [
2530
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Dot, { kind: "runtime" }),
2256
2531
  " 自定义 · ",
2257
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dot, { kind: "static" }),
2532
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Dot, { kind: "static" }),
2258
2533
  " 部署 YAML · ",
2259
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dot, { kind: "global" }),
2534
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Dot, { kind: "global" }),
2260
2535
  " 跟随全局",
2261
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { title: "每层实际链:运行时自定义 → 部署 YAML 层链 → 全局默认链,逐级兜底;部署 pin 时运行时编辑只读", children: "(层链优先于全局)" })
2536
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { title: "每层实际链:运行时自定义 → 部署 YAML 层链 → 全局默认链,逐级兜底;部署 pin 时运行时编辑只读", children: "(层链优先于全局)" })
2262
2537
  ] }),
2263
- tab === "g" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2264
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: STY3.panelHead, children: [
2265
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: STY3.panelTitle, children: "全局默认链" }),
2266
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...STY3.chip, ...STY3.chipAccent }, children: "运行时 · 可编辑" }),
2267
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...STY3.inUse, marginLeft: "auto" }, children: users.length ? "在用:" + users.map((k) => LAYER_META[k].seg).join("、") : "当前无层使用" })
2538
+ tab === "g" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
2539
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: STY3.panelHead, children: [
2540
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: STY3.panelTitle, children: "全局默认链" }),
2541
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { ...STY3.chip, ...STY3.chipAccent }, children: "运行时 · 可编辑" }),
2542
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { ...STY3.inUse, marginLeft: "auto" }, children: users.length ? "在用:" + users.map((k) => LAYER_META[k].seg).join("、") : "当前无层使用" })
2268
2543
  ] }),
2269
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RouteChainEditor, { rpc, disabled }, "g"),
2270
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: "input" }, "g-budget"),
2271
- info?.channel ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChannelEditor, { channel: info.channel, rpc, disabled }) : null
2272
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2273
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: STY3.panelHead, children: [
2274
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: STY3.panelTitle, children: LAYER_META[tab].title }),
2275
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...STY3.chip, ...dotOf(tab) === "runtime" ? STY3.chipAccent : STY3.chipMuted }, title: chipTitle(tab), children: dotOf(tab) === "runtime" ? "运行时自定义" : dotOf(tab) === "static" ? "静态 · YAML" : "跟随全局" })
2544
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RouteChainEditor, { rpc, disabled }, "g"),
2545
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: "input" }, "g-budget"),
2546
+ info?.channel ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChannelEditor, { channel: info.channel, rpc, disabled }) : null
2547
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
2548
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: STY3.panelHead, children: [
2549
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: STY3.panelTitle, children: LAYER_META[tab].title }),
2550
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { ...STY3.chip, ...dotOf(tab) === "runtime" ? STY3.chipAccent : STY3.chipMuted }, title: chipTitle(tab), children: dotOf(tab) === "runtime" ? "运行时自定义" : dotOf(tab) === "static" ? "静态 · YAML" : "跟随全局" })
2276
2551
  ] }),
2277
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RouteChainEditor, { rpc, disabled, scope: tab }, tab),
2278
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: tab }, tab + "-budget")
2552
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RouteChainEditor, { rpc, disabled, scope: tab }, tab),
2553
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: tab }, tab + "-budget")
2279
2554
  ] })
2280
2555
  ] });
2281
2556
  }
2282
2557
 
2283
2558
  // client/src/tabs/EmbeddingSection.tsx
2284
- var import_react10 = require("react");
2559
+ var import_react12 = require("react");
2285
2560
 
2286
2561
  // client/src/dimensions.ts
2287
2562
  var DIMS_MIN = 1;
@@ -2329,7 +2604,7 @@ var __defProp = Object.defineProperty;
2329
2604
  }
2330
2605
 
2331
2606
  // client/src/tabs/EmbeddingSection.tsx
2332
- var import_jsx_runtime10 = require("react/jsx-runtime");
2607
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2333
2608
  var RSTY = {
2334
2609
  block: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" },
2335
2610
  title: { fontSize: 12, fontWeight: 600, color: "var(--dsh-mem-text-3)", margin: "0 0 2px" },
@@ -2353,17 +2628,17 @@ var __defProp = Object.defineProperty;
2353
2628
  function EmbeddingSection(props) {
2354
2629
  const rpc = props.rpc;
2355
2630
  const loose = asLoose(rpc);
2356
- const [st, setSt] = (0, import_react10.useState)(null);
2357
- const [err, setErr] = (0, import_react10.useState)(null);
2358
- const busyPollRef = (0, import_react10.useRef)(null);
2359
- const [rBase, setRBase] = (0, import_react10.useState)("");
2360
- const [rModel, setRModel] = (0, import_react10.useState)("");
2361
- const [rDims, setRDims] = (0, import_react10.useState)("");
2362
- const [rKey, setRKey] = (0, import_react10.useState)("");
2363
- const remoteDirty = (0, import_react10.useRef)({ base: false, model: false, dims: false });
2364
- const baseTimer = (0, import_react10.useRef)(void 0);
2365
- const modelTimer = (0, import_react10.useRef)(void 0);
2366
- const load = (0, import_react10.useCallback)(() => {
2631
+ const [st, setSt] = (0, import_react12.useState)(null);
2632
+ const [err, setErr] = (0, import_react12.useState)(null);
2633
+ const busyPollRef = (0, import_react12.useRef)(null);
2634
+ const [rBase, setRBase] = (0, import_react12.useState)("");
2635
+ const [rModel, setRModel] = (0, import_react12.useState)("");
2636
+ const [rDims, setRDims] = (0, import_react12.useState)("");
2637
+ const [rKey, setRKey] = (0, import_react12.useState)("");
2638
+ const remoteDirty = (0, import_react12.useRef)({ base: false, model: false, dims: false });
2639
+ const baseTimer = (0, import_react12.useRef)(void 0);
2640
+ const modelTimer = (0, import_react12.useRef)(void 0);
2641
+ const load = (0, import_react12.useCallback)(() => {
2367
2642
  rpc("dsh-memory/embedding-state-get", {}).then((r) => {
2368
2643
  if (r && r.ok && r.value && r.value.supported !== false) {
2369
2644
  const v = r.value;
@@ -2382,10 +2657,10 @@ var __defProp = Object.defineProperty;
2382
2657
  setErr(String(e && e.message || e));
2383
2658
  });
2384
2659
  }, [rpc]);
2385
- (0, import_react10.useEffect)(() => {
2660
+ (0, import_react12.useEffect)(() => {
2386
2661
  load();
2387
2662
  }, [load]);
2388
- (0, import_react10.useEffect)(() => {
2663
+ (0, import_react12.useEffect)(() => {
2389
2664
  let stopped = false;
2390
2665
  const busyFlag = { v: false };
2391
2666
  busyPollRef.current = busyFlag;
@@ -2401,11 +2676,11 @@ var __defProp = Object.defineProperty;
2401
2676
  busyPollRef.current = null;
2402
2677
  };
2403
2678
  }, [load]);
2404
- (0, import_react10.useEffect)(() => () => {
2679
+ (0, import_react12.useEffect)(() => () => {
2405
2680
  clearTimeout(baseTimer.current);
2406
2681
  clearTimeout(modelTimer.current);
2407
2682
  }, []);
2408
- (0, import_react10.useEffect)(() => {
2683
+ (0, import_react12.useEffect)(() => {
2409
2684
  if (!st || !st.remote) return;
2410
2685
  if (!remoteDirty.current.base) setRBase(st.remote.baseURL || "");
2411
2686
  if (!remoteDirty.current.model) setRModel(st.remote.model || "");
@@ -2466,15 +2741,15 @@ var __defProp = Object.defineProperty;
2466
2741
  });
2467
2742
  };
2468
2743
  if (err === "__unsupported__") {
2469
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2470
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "语义检索(嵌入)" }),
2471
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", children: "存储处于降级状态,嵌入管理不可用。" })
2744
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2745
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "语义检索(嵌入)" }),
2746
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", children: "存储处于降级状态,嵌入管理不可用。" })
2472
2747
  ] });
2473
2748
  }
2474
2749
  if (!st) {
2475
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2476
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", children: err ? "嵌入状态读取失败:" + err : "嵌入状态读取中…" }),
2477
- err ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { style: { marginTop: 8 }, onClick: load, children: "重试" }) : null
2750
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2751
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", children: err ? "嵌入状态读取失败:" + err : "嵌入状态读取中…" }),
2752
+ err ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { style: { marginTop: 8 }, onClick: load, children: "重试" }) : null
2478
2753
  ] });
2479
2754
  }
2480
2755
  const switchConfirm = "切换嵌入源后将按新模型重建向量索引(期间语义检索暂退化为关键词匹配,不影响对话)。确定切换?";
@@ -2500,40 +2775,40 @@ var __defProp = Object.defineProperty;
2500
2775
  const remote = st.remote;
2501
2776
  let runtimeRow = null;
2502
2777
  if (rt.phase === "installing") {
2503
- runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { marginTop: 8, fontSize: 12 }, children: [
2504
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2505
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "安装推理运行时中… 已耗时 " + Math.round(rt.elapsedMs / 1e3) + "s(约 100~200MB,视网络)" }),
2506
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2507
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-runtime-cancel", {}), children: "取消" })
2778
+ runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { marginTop: 8, fontSize: 12 }, children: [
2779
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
2780
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "安装推理运行时中… 已耗时 " + Math.round(rt.elapsedMs / 1e3) + "s(约 100~200MB,视网络)" }),
2781
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
2782
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-runtime-cancel", {}), children: "取消" })
2508
2783
  ] }),
2509
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("pre", { style: { ...S.pre, maxHeight: 68, marginTop: 6, fontSize: 11, opacity: 0.85 }, children: (rt.lastLines || []).join("\n") || "等待 npm 输出…" })
2784
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { style: { ...S.pre, maxHeight: 68, marginTop: 6, fontSize: 11, opacity: 0.85 }, children: (rt.lastLines || []).join("\n") || "等待 npm 输出…" })
2510
2785
  ] });
2511
2786
  } else if (rt.phase === "error") {
2512
- runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "运行时安装失败:" + (rt.error || "未知") + "(重新切换嵌入源可重试)" });
2787
+ runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "运行时安装失败:" + (rt.error || "未知") + "(重新切换嵌入源可重试)" });
2513
2788
  } else if (rt.phase === "ready") {
2514
- runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "推理运行时就绪(transformers.js v" + rt.installedVersion + ")" });
2789
+ runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "推理运行时就绪(transformers.js v" + rt.installedVersion + ")" });
2515
2790
  } else if (st.source === "local") {
2516
- runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "首次启用本地嵌入时会自动安装推理运行时(约 100~200MB)。" });
2791
+ runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "首次启用本地嵌入时会自动安装推理运行时(约 100~200MB)。" });
2517
2792
  }
2518
2793
  let applyRow = null;
2519
2794
  if (ap.phase === "warming") {
2520
- applyRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "加载嵌入模型中…(首次需数秒)" });
2795
+ applyRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "加载嵌入模型中…(首次需数秒)" });
2521
2796
  } else if (ap.phase === "switching") {
2522
- applyRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "切换嵌入源中…" });
2797
+ applyRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: "切换嵌入源中…" });
2523
2798
  } else if (ap.phase === "error") {
2524
- applyRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "切换失败:" + ap.message + "(已保存的嵌入源不变,重启后仍按原源运行)" });
2799
+ applyRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "切换失败:" + ap.message + "(已保存的嵌入源不变,重启后仍按原源运行)" });
2525
2800
  } else if (st.reindex && st.reindex.running) {
2526
2801
  const rj = st.reindex;
2527
2802
  const rDone = rj.l1Done + rj.l0Done;
2528
2803
  const rTotal = rj.l1Total + rj.l0Total;
2529
2804
  const rPct = rTotal > 0 ? Math.round(rDone / rTotal * 100) : 0;
2530
- applyRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { marginTop: 8 }, children: [
2531
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2532
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-rb-muted", children: "重嵌入中 L1 " + rj.l1Done + "/" + rj.l1Total + " · L0 " + rj.l0Done + "/" + rj.l0Total + "(" + rPct + "%)" }),
2533
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2534
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-reindex-cancel", {}), children: "取消" })
2805
+ applyRow = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { marginTop: 8 }, children: [
2806
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
2807
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-rb-muted", children: "重嵌入中 L1 " + rj.l1Done + "/" + rj.l1Total + " · L0 " + rj.l0Done + "/" + rj.l0Total + "(" + rPct + "%)" }),
2808
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
2809
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-reindex-cancel", {}), children: "取消" })
2535
2810
  ] }),
2536
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: rPct + "%" } }) }) })
2811
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: rPct + "%" } }) }) })
2537
2812
  ] });
2538
2813
  }
2539
2814
  const modelCards = st.models.map((m) => {
@@ -2542,24 +2817,24 @@ var __defProp = Object.defineProperty;
2542
2817
  const pct = mDl && dl.overallTotal > 0 ? Math.round(dl.overallReceived / dl.overallTotal * 100) : 0;
2543
2818
  let action;
2544
2819
  if (mDl) {
2545
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, minWidth: 200 }, children: [
2546
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2547
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: (dl.phase === "verifying" ? "校验中 " : "") + fmtMB(dl.overallReceived) + " / " + fmtMB(dl.overallTotal) + "(文件 " + dl.fileIndex + "/" + dl.fileCount + "," + pct + "%" + (dl.speedBps > 0 && dl.phase === "downloading" ? "," + fmtMB(dl.speedBps) + "/s" : "") + ")" }),
2548
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2549
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-download-cancel", {}), children: "取消" })
2820
+ action = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { flex: 1, minWidth: 200 }, children: [
2821
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
2822
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: (dl.phase === "verifying" ? "校验中 " : "") + fmtMB(dl.overallReceived) + " / " + fmtMB(dl.overallTotal) + "(文件 " + dl.fileIndex + "/" + dl.fileCount + "," + pct + "%" + (dl.speedBps > 0 && dl.phase === "downloading" ? "," + fmtMB(dl.speedBps) + "/s" : "") + ")" }),
2823
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
2824
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { onClick: () => call("dsh-memory/embedding-download-cancel", {}), children: "取消" })
2550
2825
  ] }),
2551
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }) })
2826
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { ...S.flexRow, marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }) })
2552
2827
  ] });
2553
2828
  } else if (isActive) {
2554
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2555
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-task", children: "使用中" }),
2556
- localInfo && localInfo.state === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-rb-muted", children: "模型加载中…" }) : null,
2557
- localInfo && localInfo.state === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "加载失败:" + (localInfo.error || "") }) : null,
2558
- localInfo && localInfo.state === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-rb-muted", children: "已就绪" }) : null
2829
+ action = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
2830
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-task", children: "使用中" }),
2831
+ localInfo && localInfo.state === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-rb-muted", children: "模型加载中…" }) : null,
2832
+ localInfo && localInfo.state === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "加载失败:" + (localInfo.error || "") }) : null,
2833
+ localInfo && localInfo.state === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-rb-muted", children: "已就绪" }) : null
2559
2834
  ] });
2560
2835
  } else if (m.state === "downloaded") {
2561
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2562
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2836
+ action = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
2837
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2563
2838
  NButton,
2564
2839
  {
2565
2840
  disabled: ap.busy,
@@ -2567,7 +2842,7 @@ var __defProp = Object.defineProperty;
2567
2842
  children: "启用"
2568
2843
  }
2569
2844
  ),
2570
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2845
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2571
2846
  NButton,
2572
2847
  {
2573
2848
  disabled: dlActive,
@@ -2577,7 +2852,7 @@ var __defProp = Object.defineProperty;
2577
2852
  )
2578
2853
  ] });
2579
2854
  } else {
2580
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2855
+ action = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2581
2856
  NButton,
2582
2857
  {
2583
2858
  disabled: dlActive || !st.ceilings.local,
@@ -2587,16 +2862,16 @@ var __defProp = Object.defineProperty;
2587
2862
  }
2588
2863
  );
2589
2864
  }
2590
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2865
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2591
2866
  "div",
2592
2867
  {
2593
2868
  style: { ...S.flexRow, padding: "8px 0", borderBottom: "1px solid var(--dsh-mem-border)", flexWrap: "wrap" },
2594
2869
  children: [
2595
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { minWidth: 150 }, children: [
2596
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600 }, children: m.name }),
2597
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", children: m.tags.join(" · ") + " · " + m.dims + " 维 · 上下文 " + m.contextTokens })
2870
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { minWidth: 150 }, children: [
2871
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { fontWeight: 600 }, children: m.name }),
2872
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", children: m.tags.join(" · ") + " · " + m.dims + " 维 · 上下文 " + m.contextTokens })
2598
2873
  ] }),
2599
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { flex: 1, minWidth: 180, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: m.description }),
2874
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { flex: 1, minWidth: 180, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: m.description }),
2600
2875
  action
2601
2876
  ]
2602
2877
  },
@@ -2609,11 +2884,11 @@ var __defProp = Object.defineProperty;
2609
2884
  const reindexRunning = !!(st.reindex && st.reindex.running);
2610
2885
  const canRebuild = vecOk && st.source !== "off" && !reindexRunning && !st.apply.busy;
2611
2886
  const rebuildHint = !vecOk ? "向量能力不可用" : st.source === "off" ? "嵌入源已关闭,请先启用" : reindexRunning ? "重建已在进行中" : st.apply.busy ? "嵌入源切换进行中" : "只补缺失向量;已有向量不动。零向量内容会被跳过(重试无意义)";
2612
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2613
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2614
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "语义检索(嵌入源)" }),
2615
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2616
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2887
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2888
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: S.flexRow, children: [
2889
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "语义检索(嵌入源)" }),
2890
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.grow }),
2891
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2617
2892
  Segmented,
2618
2893
  {
2619
2894
  value: st.source,
@@ -2626,18 +2901,18 @@ var __defProp = Object.defineProperty;
2626
2901
  }
2627
2902
  )
2628
2903
  ] }),
2629
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 4 }, children: st.source === "off" ? "当前:关键词(BM25)检索,不做向量嵌入" : st.source === "remote" ? "当前:远程嵌入(" + (remote.model || "未配置模型") + (remote.baseURL ? " · " + remote.baseURL : "") + ")" : "当前:本地嵌入" + (st.activeModel ? "(" + st.activeModel + ")" : "") }),
2630
- st.activeNote ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 4, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: st.activeNote }) : null,
2631
- err && err !== "__unsupported__" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: err }) : null,
2632
- dl && dl.phase === "error" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "下载失败:" + (dl.error || "") }) : null,
2904
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 4 }, children: st.source === "off" ? "当前:关键词(BM25)检索,不做向量嵌入" : st.source === "remote" ? "当前:远程嵌入(" + (remote.model || "未配置模型") + (remote.baseURL ? " · " + remote.baseURL : "") + ")" : "当前:本地嵌入" + (st.activeModel ? "(" + st.activeModel + ")" : "") }),
2905
+ st.activeNote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 4, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: st.activeNote }) : null,
2906
+ err && err !== "__unsupported__" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: err }) : null,
2907
+ dl && dl.phase === "error" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "下载失败:" + (dl.error || "") }) : null,
2633
2908
  runtimeRow,
2634
2909
  applyRow,
2635
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.block, children: [
2636
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.title, children: "远程连接(生效值;运行时覆盖优先于部署 YAML)" }),
2637
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.note, children: "生效端点 " + (remote.baseURL || "(未配置)") + " · 模型 " + (remote.model || "(未配置)") + " · " + (remote.dimensions > 0 ? remote.dimensions + " 维" : "维度跟随部署") + (remote.apiKeySet ? " · 密钥已设置" : " · 密钥未设置") }),
2638
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.row, children: [
2639
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "端点 URL" }),
2640
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2910
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: RSTY.block, children: [
2911
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: RSTY.title, children: "远程连接(生效值;运行时覆盖优先于部署 YAML)" }),
2912
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: RSTY.note, children: "生效端点 " + (remote.baseURL || "(未配置)") + " · 模型 " + (remote.model || "(未配置)") + " · " + (remote.dimensions > 0 ? remote.dimensions + " 维" : "维度跟随部署") + (remote.apiKeySet ? " · 密钥已设置" : " · 密钥未设置") }),
2913
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: RSTY.row, children: [
2914
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: RSTY.label, children: "端点 URL" }),
2915
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2641
2916
  NInput,
2642
2917
  {
2643
2918
  style: RSTY.input,
@@ -2654,9 +2929,9 @@ var __defProp = Object.defineProperty;
2654
2929
  }
2655
2930
  )
2656
2931
  ] }),
2657
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.row, children: [
2658
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "模型名" }),
2659
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2932
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: RSTY.row, children: [
2933
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: RSTY.label, children: "模型名" }),
2934
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2660
2935
  NInput,
2661
2936
  {
2662
2937
  style: RSTY.input,
@@ -2672,8 +2947,8 @@ var __defProp = Object.defineProperty;
2672
2947
  }
2673
2948
  }
2674
2949
  ),
2675
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "维度" }),
2676
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2950
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: RSTY.label, children: "维度" }),
2951
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2677
2952
  NInput,
2678
2953
  {
2679
2954
  style: { width: 90, minWidth: 0, flex: "0 1 90px" },
@@ -2693,9 +2968,9 @@ var __defProp = Object.defineProperty;
2693
2968
  }
2694
2969
  )
2695
2970
  ] }),
2696
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.row, children: [
2697
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "API Key" }),
2698
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2971
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: RSTY.row, children: [
2972
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: RSTY.label, children: "API Key" }),
2973
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2699
2974
  NInput,
2700
2975
  {
2701
2976
  style: RSTY.input,
@@ -2710,8 +2985,8 @@ var __defProp = Object.defineProperty;
2710
2985
  onBlur: commitRemoteKey
2711
2986
  }
2712
2987
  ),
2713
- remote.apiKeySet ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.chipAccent, children: "已设置" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.chipMuted, children: "未设置" }),
2714
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2988
+ remote.apiKeySet ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: RSTY.chipAccent, children: "已设置" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: RSTY.chipMuted, children: "未设置" }),
2989
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2715
2990
  NButton,
2716
2991
  {
2717
2992
  title: "写入空串清除运行时密钥(部署 YAML 的密钥不受影响)",
@@ -2721,12 +2996,12 @@ var __defProp = Object.defineProperty;
2721
2996
  )
2722
2997
  ] })
2723
2998
  ] }),
2724
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.panelLabel, children: "本地模型目录(下载后离线可用,不随插件分发)" }),
2999
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.panelLabel, children: "本地模型目录(下载后离线可用,不随插件分发)" }),
2725
3000
  modelCards,
2726
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.block, children: [
2727
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.title, children: "向量索引" }),
2728
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.note, children: vecNote }),
2729
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.row, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3001
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: RSTY.block, children: [
3002
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: RSTY.title, children: "向量索引" }),
3003
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: RSTY.note, children: vecNote }),
3004
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: RSTY.row, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2730
3005
  NButton,
2731
3006
  {
2732
3007
  disabled: !canRebuild,
@@ -2740,8 +3015,8 @@ var __defProp = Object.defineProperty;
2740
3015
  }
2741
3016
 
2742
3017
  // client/src/tabs/RebuildPanel.tsx
2743
- var import_react11 = require("react");
2744
- var import_jsx_runtime11 = require("react/jsx-runtime");
3018
+ var import_react13 = require("react");
3019
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2745
3020
  var RB_PHASE_LABEL = {
2746
3021
  preparing: "准备中(归档旧数据 · 清空检索库)",
2747
3022
  distilling: "分块蒸馏中",
@@ -2749,21 +3024,21 @@ var __defProp = Object.defineProperty;
2749
3024
  };
2750
3025
  function RebuildPanel(props) {
2751
3026
  const rpc = props.rpc;
2752
- const [rbRaw, setRb] = (0, import_react11.useState)(null);
2753
- const [confirmOpen, setConfirmOpen] = (0, import_react11.useState)(false);
2754
- const [busy, setBusy] = (0, import_react11.useState)(false);
2755
- const [rbError, setRbError] = (0, import_react11.useState)(null);
2756
- const refresh = (0, import_react11.useCallback)(() => {
3027
+ const [rbRaw, setRb] = (0, import_react13.useState)(null);
3028
+ const [confirmOpen, setConfirmOpen] = (0, import_react13.useState)(false);
3029
+ const [busy, setBusy] = (0, import_react13.useState)(false);
3030
+ const [rbError, setRbError] = (0, import_react13.useState)(null);
3031
+ const refresh = (0, import_react13.useCallback)(() => {
2757
3032
  rpc("dsh-memory/rebuild-status", {}).then((r) => {
2758
3033
  if (r && r.ok) setRb(r.value);
2759
3034
  }).catch(() => {
2760
3035
  });
2761
3036
  }, [rpc]);
2762
- (0, import_react11.useEffect)(() => {
3037
+ (0, import_react13.useEffect)(() => {
2763
3038
  refresh();
2764
3039
  }, [refresh]);
2765
3040
  const running = !!(rbRaw && rbRaw.running);
2766
- (0, import_react11.useEffect)(() => {
3041
+ (0, import_react13.useEffect)(() => {
2767
3042
  if (!running) return;
2768
3043
  const timer = setInterval(refresh, 1500);
2769
3044
  return () => {
@@ -2808,11 +3083,11 @@ var __defProp = Object.defineProperty;
2808
3083
  } else if (!running && rb.phase === "failed") {
2809
3084
  lastNote = "上次重建:失败:" + (rb.error || "未知错误");
2810
3085
  }
2811
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2812
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
2813
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "重建记忆" }),
2814
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 }, children: running ? (RB_PHASE_LABEL[rb.phase] || rb.phase) + " · " + rb.done + "/" + rb.total + " 会话(" + pct + "%)" : "从 L0 原始对话重新蒸馏 L1/L2/L3;旧数据先归档(不删除)" }),
2815
- running ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(NButton, { disabled: busy || rb.cancelRequested, onClick: cancel, children: rb.cancelRequested ? "取消中…" : "取消重建" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3086
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "dsh-mem-rb-card", children: [
3087
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
3088
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "重建记忆" }),
3089
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 }, children: running ? (RB_PHASE_LABEL[rb.phase] || rb.phase) + " · " + rb.done + "/" + rb.total + " 会话(" + pct + "%)" : "从 L0 原始对话重新蒸馏 L1/L2/L3;旧数据先归档(不删除)" }),
3090
+ running ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NButton, { disabled: busy || rb.cancelRequested, onClick: cancel, children: rb.cancelRequested ? "取消中…" : "取消重建" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2816
3091
  NButton,
2817
3092
  {
2818
3093
  disabled: busy || empty,
@@ -2825,13 +3100,13 @@ var __defProp = Object.defineProperty;
2825
3100
  }
2826
3101
  )
2827
3102
  ] }),
2828
- running ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
2829
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }),
2830
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: "产出 " + rb.recordsBuilt + " 条" })
3103
+ running ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
3104
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }) }),
3105
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: "产出 " + rb.recordsBuilt + " 条" })
2831
3106
  ] }) : null,
2832
- lastNote ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
2833
- rbError ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rbError }) : null,
2834
- confirmOpen ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3107
+ lastNote ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
3108
+ rbError ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rbError }) : null,
3109
+ confirmOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2835
3110
  NModal,
2836
3111
  {
2837
3112
  open: true,
@@ -2840,7 +3115,7 @@ var __defProp = Object.defineProperty;
2840
3115
  },
2841
3116
  title: "确认重建全部记忆?",
2842
3117
  footer: [
2843
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3118
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2844
3119
  NButton,
2845
3120
  {
2846
3121
  onClick: () => {
@@ -2850,15 +3125,15 @@ var __defProp = Object.defineProperty;
2850
3125
  },
2851
3126
  "cancel"
2852
3127
  ),
2853
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(NButton, { variant: "primary", disabled: busy, onClick: start, children: busy ? "启动中…" : "开始重建" }, "confirm")
3128
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NButton, { variant: "primary", disabled: busy, onClick: start, children: busy ? "启动中…" : "开始重建" }, "confirm")
2854
3129
  ],
2855
3130
  children: [
2856
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
3131
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
2857
3132
  "将以 L0 原始对话为事实源重新蒸馏:",
2858
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("b", { children: rb.sessionCount + " 个会话 · " + rb.messageCount + " 条消息" }),
3133
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("b", { children: rb.sessionCount + " 个会话 · " + rb.messageCount + " 条消息" }),
2859
3134
  ",预计 ≥" + rb.estCalls + " 次蒸馏调用。"
2860
3135
  ] }),
2861
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { marginTop: 8 }, children: "现有 L1 记忆 / L2 场景 / L3 画像会整体归档(*.bak.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。" })
3136
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { marginTop: 8 }, children: "现有 L1 记忆 / L2 场景 / L3 画像会整体归档(*.bak.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。" })
2862
3137
  ]
2863
3138
  }
2864
3139
  ) : null
@@ -2866,8 +3141,8 @@ var __defProp = Object.defineProperty;
2866
3141
  }
2867
3142
 
2868
3143
  // client/src/tabs/RuminatePanel.tsx
2869
- var import_react12 = require("react");
2870
- var import_jsx_runtime12 = require("react/jsx-runtime");
3144
+ var import_react14 = require("react");
3145
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2871
3146
  var RM_PHASE_LABEL = {
2872
3147
  refreshing: "轻量刷新中",
2873
3148
  distilling: "L1 蒸馏中",
@@ -2888,11 +3163,11 @@ var __defProp = Object.defineProperty;
2888
3163
  var RM_IDLE_INTERVAL = 3e4;
2889
3164
  function RuminatePanel(props) {
2890
3165
  const rpc = props.rpc;
2891
- const [rmRaw, setRm] = (0, import_react12.useState)(null);
2892
- const [confirmOpen, setConfirmOpen] = (0, import_react12.useState)(false);
2893
- const [busy, setBusy] = (0, import_react12.useState)(false);
2894
- const [rmError, setRmError] = (0, import_react12.useState)(null);
2895
- const refresh = (0, import_react12.useCallback)(() => {
3166
+ const [rmRaw, setRm] = (0, import_react14.useState)(null);
3167
+ const [confirmOpen, setConfirmOpen] = (0, import_react14.useState)(false);
3168
+ const [busy, setBusy] = (0, import_react14.useState)(false);
3169
+ const [rmError, setRmError] = (0, import_react14.useState)(null);
3170
+ const refresh = (0, import_react14.useCallback)(() => {
2896
3171
  return rpc("dsh-memory/ruminate-status", {}).then((r) => {
2897
3172
  if (r && r.ok) {
2898
3173
  const st = r.value;
@@ -2907,14 +3182,14 @@ var __defProp = Object.defineProperty;
2907
3182
  }).catch(() => {
2908
3183
  });
2909
3184
  }, [rpc]);
2910
- const pollRef = (0, import_react12.useRef)(1500);
2911
- const backoffRef = (0, import_react12.useRef)(1);
2912
- const lastDetailRef = (0, import_react12.useRef)(null);
2913
- (0, import_react12.useEffect)(() => {
3185
+ const pollRef = (0, import_react14.useRef)(1500);
3186
+ const backoffRef = (0, import_react14.useRef)(1);
3187
+ const lastDetailRef = (0, import_react14.useRef)(null);
3188
+ (0, import_react14.useEffect)(() => {
2914
3189
  refresh();
2915
3190
  }, [refresh]);
2916
3191
  const running = !!(rmRaw && (rmRaw.running || RM_RUNNING_PHASES.indexOf(rmRaw.phase) >= 0));
2917
- (0, import_react12.useEffect)(() => {
3192
+ (0, import_react14.useEffect)(() => {
2918
3193
  if (!running) return;
2919
3194
  let alive = true;
2920
3195
  let timer;
@@ -2933,13 +3208,13 @@ var __defProp = Object.defineProperty;
2933
3208
  lastDetailRef.current = null;
2934
3209
  };
2935
3210
  }, [running, refresh]);
2936
- (0, import_react12.useEffect)(() => {
3211
+ (0, import_react14.useEffect)(() => {
2937
3212
  if (running) return;
2938
3213
  const timer = setInterval(refresh, RM_IDLE_INTERVAL);
2939
3214
  return () => clearInterval(timer);
2940
3215
  }, [running, refresh]);
2941
- const [, setTick] = (0, import_react12.useState)(0);
2942
- (0, import_react12.useEffect)(() => {
3216
+ const [, setTick] = (0, import_react14.useState)(0);
3217
+ (0, import_react14.useEffect)(() => {
2943
3218
  if (!running) return;
2944
3219
  const timer = setInterval(() => setTick((n) => n + 1), 1e3);
2945
3220
  return () => clearInterval(timer);
@@ -2990,11 +3265,11 @@ var __defProp = Object.defineProperty;
2990
3265
  }
2991
3266
  const unit = rm.phase === "refreshing" || rm.phase === "relabeling" ? "步" : "会话";
2992
3267
  const progressText = sub ? sub.label + " " + sub.done + "/" + sub.total + "(" + Math.round(sub.done / sub.total * 100) + "%)" : rm.total > 0 ? rm.done + "/" + rm.total + " " + unit + "(" + pct + "%)" : "进行中…";
2993
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2994
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
2995
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "反刍整理" }),
2996
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 }, children: running ? (RM_PHASE_LABEL[rm.phase] || rm.phase) + " · " + progressText + (elapsedText ? " · 已用 " + elapsedText : "") : "冲刷未蒸馏缓冲,跑一轮 L1→L2→L3 消化;不清库不改 L0" }),
2997
- running ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { disabled: busy || rm.cancelRequested, onClick: cancel, children: rm.cancelRequested ? "取消中…" : "取消整理" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3268
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "dsh-mem-rb-card", children: [
3269
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
3270
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "反刍整理" }),
3271
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 }, children: running ? (RM_PHASE_LABEL[rm.phase] || rm.phase) + " · " + progressText + (elapsedText ? " · 已用 " + elapsedText : "") : "冲刷未蒸馏缓冲,跑一轮 L1→L2→L3 消化;不清库不改 L0" }),
3272
+ running ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(NButton, { disabled: busy || rm.cancelRequested, onClick: cancel, children: rm.cancelRequested ? "取消中…" : "取消整理" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2998
3273
  NButton,
2999
3274
  {
3000
3275
  disabled: busy,
@@ -3007,27 +3282,27 @@ var __defProp = Object.defineProperty;
3007
3282
  }
3008
3283
  )
3009
3284
  ] }),
3010
- running ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
3011
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3285
+ running ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
3286
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dsh-mem-rb-bar", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3012
3287
  "div",
3013
3288
  {
3014
3289
  className: "dsh-mem-rb-fill" + (indet ? " dsh-mem-rb-fill-indet" : ""),
3015
3290
  style: { width: indet ? "100%" : barPct + "%" }
3016
3291
  }
3017
3292
  ) }),
3018
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: sub ? sub.label + " " + sub.done + "/" + sub.total : "产出 " + rm.recordsBuilt + " 条" })
3293
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" }, children: sub ? sub.label + " " + sub.done + "/" + sub.total : "产出 " + rm.recordsBuilt + " 条" })
3019
3294
  ] }) : null,
3020
- running && rm.detail ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 6, whiteSpace: "normal" }, children: "当前:" + rm.detail }) : null,
3021
- lastNote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
3022
- rmError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rmError }) : null,
3023
- confirmOpen ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3295
+ running && rm.detail ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 6, whiteSpace: "normal" }, children: "当前:" + rm.detail }) : null,
3296
+ lastNote ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
3297
+ rmError ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rmError }) : null,
3298
+ confirmOpen ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3024
3299
  NModal,
3025
3300
  {
3026
3301
  open: true,
3027
3302
  onClose: () => setConfirmOpen(false),
3028
3303
  title: "确认反刍整理?",
3029
3304
  footer: [
3030
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3305
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3031
3306
  NButton,
3032
3307
  {
3033
3308
  onClick: () => setConfirmOpen(false),
@@ -3035,11 +3310,11 @@ var __defProp = Object.defineProperty;
3035
3310
  },
3036
3311
  "cancel"
3037
3312
  ),
3038
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NButton, { variant: "primary", disabled: busy, onClick: start, children: busy ? "启动中…" : "反刍整理" }, "confirm")
3313
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(NButton, { variant: "primary", disabled: busy, onClick: start, children: busy ? "启动中…" : "反刍整理" }, "confirm")
3039
3314
  ],
3040
3315
  children: [
3041
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: "将扫描未蒸馏缓冲,按会话逐个跑蒸馏管线,完成后强制 L2 场景整合与 L3 画像更新。" }),
3042
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 8 }, children: "与重建不同:不清空 L1 检索库、不归档旧产物、不改 L0;仅消化攒而未蒸馏的切片,无缓冲时做轻量 L2/L3 刷新。" })
3316
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { children: "将扫描未蒸馏缓冲,按会话逐个跑蒸馏管线,完成后强制 L2 场景整合与 L3 画像更新。" }),
3317
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { marginTop: 8 }, children: "与重建不同:不清空 L1 检索库、不归档旧产物、不改 L0;仅消化攒而未蒸馏的切片,无缓冲时做轻量 L2/L3 刷新。" })
3043
3318
  ]
3044
3319
  }
3045
3320
  ) : null
@@ -3047,13 +3322,13 @@ var __defProp = Object.defineProperty;
3047
3322
  }
3048
3323
 
3049
3324
  // client/src/tabs/OverviewTab.tsx
3050
- var import_jsx_runtime13 = require("react/jsx-runtime");
3325
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3051
3326
  function OverviewTab(props) {
3052
3327
  const rpc = props.rpc;
3053
- const [stats, setStats] = (0, import_react13.useState)(null);
3054
- const [settingsData, setSettingsData] = (0, import_react13.useState)(null);
3055
- const [error, setError] = (0, import_react13.useState)(null);
3056
- const load = (0, import_react13.useCallback)(() => {
3328
+ const [stats, setStats] = (0, import_react15.useState)(null);
3329
+ const [settingsData, setSettingsData] = (0, import_react15.useState)(null);
3330
+ const [error, setError] = (0, import_react15.useState)(null);
3331
+ const load = (0, import_react15.useCallback)(() => {
3057
3332
  rpc("dsh-memory/stats", {}).then((r) => {
3058
3333
  if (r && r.ok) setStats(r.value);
3059
3334
  else setError(r && r.error ? r.error.message : "RPC error");
@@ -3065,7 +3340,7 @@ var __defProp = Object.defineProperty;
3065
3340
  }).catch(() => {
3066
3341
  });
3067
3342
  }, [rpc]);
3068
- (0, import_react13.useEffect)(() => {
3343
+ (0, import_react15.useEffect)(() => {
3069
3344
  load();
3070
3345
  const timer = setInterval(load, 5e3);
3071
3346
  return () => {
@@ -3126,10 +3401,11 @@ var __defProp = Object.defineProperty;
3126
3401
  const toolRuminate = settingsData && settingsData.settings ? settingsData.settings.toolRuminate !== false : true;
3127
3402
  const toolConflict = settingsData && settingsData.settings ? settingsData.settings.toolConflict !== false : true;
3128
3403
  const toolMutate = settingsData && settingsData.settings ? settingsData.settings.toolMutate !== false : true;
3129
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
3130
- settingsData && settingsData.supported === false ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: S.hint, children: "settings 服务不可用,记忆模式开关未启用(记忆保持全开)。" }) : settingsData ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: S.switchPanel, children: [
3131
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "记忆模式" }),
3132
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3404
+ const skillsEnabled = settingsData && settingsData.settings ? settingsData.settings.skillsEnabled === true : false;
3405
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
3406
+ settingsData && settingsData.supported === false ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.hint, children: "settings 服务不可用,记忆模式开关未启用(记忆保持全开)。" }) : settingsData ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.switchPanel, children: [
3407
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "记忆模式" }),
3408
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3133
3409
  SwitchRow,
3134
3410
  {
3135
3411
  label: "记忆模式",
@@ -3140,7 +3416,7 @@ var __defProp = Object.defineProperty;
3140
3416
  }
3141
3417
  }
3142
3418
  ),
3143
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3419
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3144
3420
  SwitchRow,
3145
3421
  {
3146
3422
  label: "捕获",
@@ -3152,7 +3428,7 @@ var __defProp = Object.defineProperty;
3152
3428
  }
3153
3429
  }
3154
3430
  ),
3155
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3431
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3156
3432
  SwitchRow,
3157
3433
  {
3158
3434
  label: "蒸馏",
@@ -3164,7 +3440,7 @@ var __defProp = Object.defineProperty;
3164
3440
  }
3165
3441
  }
3166
3442
  ),
3167
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3443
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3168
3444
  SwitchRow,
3169
3445
  {
3170
3446
  label: "召回",
@@ -3176,8 +3452,8 @@ var __defProp = Object.defineProperty;
3176
3452
  }
3177
3453
  }
3178
3454
  ),
3179
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "高权限模式" }),
3180
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3455
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "高权限模式" }),
3456
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3181
3457
  SwitchRow,
3182
3458
  {
3183
3459
  label: "高权限模式",
@@ -3188,8 +3464,20 @@ var __defProp = Object.defineProperty;
3188
3464
  }
3189
3465
  }
3190
3466
  ),
3191
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "工具封印" }),
3192
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3467
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "技能结晶" }),
3468
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3469
+ SwitchRow,
3470
+ {
3471
+ label: "技能(程序性记忆)",
3472
+ desc: skillsEnabled ? "已开启:重复工作方法会结晶为技能候选,注入活跃技能,可在「技能」页审阅晋升" : "默认关闭;开启后注册 memory_skill_* 工具与注入(新装工具需重启后完全生效)",
3473
+ checked: skillsEnabled,
3474
+ onChange: (v) => {
3475
+ toggle("skillsEnabled", v);
3476
+ }
3477
+ }
3478
+ ),
3479
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "工具封印" }),
3480
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3193
3481
  SwitchRow,
3194
3482
  {
3195
3483
  label: "Room 管理工具",
@@ -3200,7 +3488,7 @@ var __defProp = Object.defineProperty;
3200
3488
  }
3201
3489
  }
3202
3490
  ),
3203
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3491
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3204
3492
  SwitchRow,
3205
3493
  {
3206
3494
  label: "图谱工具",
@@ -3211,7 +3499,7 @@ var __defProp = Object.defineProperty;
3211
3499
  }
3212
3500
  }
3213
3501
  ),
3214
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3502
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3215
3503
  SwitchRow,
3216
3504
  {
3217
3505
  label: "反刍工具",
@@ -3222,7 +3510,7 @@ var __defProp = Object.defineProperty;
3222
3510
  }
3223
3511
  }
3224
3512
  ),
3225
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3513
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3226
3514
  SwitchRow,
3227
3515
  {
3228
3516
  label: "冲突工具",
@@ -3233,7 +3521,7 @@ var __defProp = Object.defineProperty;
3233
3521
  }
3234
3522
  }
3235
3523
  ),
3236
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3524
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3237
3525
  SwitchRow,
3238
3526
  {
3239
3527
  label: "写删工具",
@@ -3244,8 +3532,8 @@ var __defProp = Object.defineProperty;
3244
3532
  }
3245
3533
  }
3246
3534
  ),
3247
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "人工冲突裁决" }),
3248
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3535
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "人工冲突裁决" }),
3536
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3249
3537
  SwitchRow,
3250
3538
  {
3251
3539
  label: "人工冲突裁决",
@@ -3256,42 +3544,42 @@ var __defProp = Object.defineProperty;
3256
3544
  }
3257
3545
  }
3258
3546
  ),
3259
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "蒸馏参数" }),
3260
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DistillSettings, { rpc, disabled: !master, data: settingsData, setData: setSettingsData, onError: setError }),
3261
- ceilingNote ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: S.hint, children: ceilingNote }) : null
3547
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "蒸馏参数" }),
3548
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DistillSettings, { rpc, disabled: !master, data: settingsData, setData: setSettingsData, onError: setError }),
3549
+ ceilingNote ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.hint, children: ceilingNote }) : null
3262
3550
  ] }) : null,
3263
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(EmbeddingSection, { rpc }),
3264
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RebuildPanel, { rpc }),
3265
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RuminatePanel, { rpc }),
3266
- degraded ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "⚠ " + stats.message + "。上方数据为最后一次成功读取的值,记忆功能当前未工作。" }) : null,
3267
- error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.error, children: "获取状态失败:" + error }) : !stats ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: S.intro, children: "正在读取记忆状态…" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
3268
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "记忆概况" }),
3269
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.statGrid, children: tiles.map((t) => {
3270
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
3271
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.statNum, children: t.num }),
3272
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.statLabel, children: t.label })
3551
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(EmbeddingSection, { rpc }),
3552
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RebuildPanel, { rpc }),
3553
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RuminatePanel, { rpc }),
3554
+ degraded ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "⚠ " + stats.message + "。上方数据为最后一次成功读取的值,记忆功能当前未工作。" }) : null,
3555
+ error ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.error, children: "获取状态失败:" + error }) : !stats ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.intro, children: "正在读取记忆状态…" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
3556
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "记忆概况" }),
3557
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.statGrid, children: tiles.map((t) => {
3558
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
3559
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.statNum, children: t.num }),
3560
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.statLabel, children: t.label })
3273
3561
  ] }, t.label);
3274
3562
  }) }),
3275
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "运行状态" }),
3563
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "运行状态" }),
3276
3564
  infos.map((row2) => {
3277
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: S.infoRow, children: [
3278
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.infoKey, children: row2[0] }),
3279
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.infoVal, children: row2[1] })
3565
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.infoRow, children: [
3566
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.infoKey, children: row2[0] }),
3567
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.infoVal, children: row2[1] })
3280
3568
  ] }, row2[0]);
3281
3569
  })
3282
3570
  ] }),
3283
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: S.hint, children: "浏览各层记忆内容请切换上方 Tab;原始对话(L0)不入浏览器,可由模型侧 conversation_search 工具查询。" })
3571
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.hint, children: "浏览各层记忆内容请切换上方 Tab;原始对话(L0)不入浏览器,可由模型侧 conversation_search 工具查询。" })
3284
3572
  ] });
3285
3573
  }
3286
3574
 
3287
3575
  // client/src/tabs/PersonaTab.tsx
3288
- var import_react14 = require("react");
3289
- var import_jsx_runtime14 = require("react/jsx-runtime");
3576
+ var import_react16 = require("react");
3577
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3290
3578
  function PersonaTab(props) {
3291
3579
  const rpc = props.rpc;
3292
- const [content, setContent] = (0, import_react14.useState)(null);
3293
- const [error, setError] = (0, import_react14.useState)(null);
3294
- const load = (0, import_react14.useCallback)(() => {
3580
+ const [content, setContent] = (0, import_react16.useState)(null);
3581
+ const [error, setError] = (0, import_react16.useState)(null);
3582
+ const load = (0, import_react16.useCallback)(() => {
3295
3583
  setError(null);
3296
3584
  rpc("dsh-memory/persona", {}).then((r) => {
3297
3585
  if (r && r.ok) setContent(r.value.content);
@@ -3300,23 +3588,23 @@ var __defProp = Object.defineProperty;
3300
3588
  setError(String(e && e.message || e));
3301
3589
  });
3302
3590
  }, [rpc]);
3303
- (0, import_react14.useEffect)(() => {
3591
+ (0, import_react16.useEffect)(() => {
3304
3592
  load();
3305
3593
  }, [load]);
3306
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3307
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3308
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: S.muted, children: error ? "加载失败" : content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像" }),
3309
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: S.grow }),
3310
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(NButton, { onClick: load, children: "刷新" })
3594
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
3595
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3596
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: error ? "加载失败" : content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像" }),
3597
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
3598
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(NButton, { onClick: load, children: "刷新" })
3311
3599
  ] }),
3312
- error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "画像读取失败:" + error + "(点右上“刷新”重试)" }) : null,
3313
- content ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { style: S.pre, children: content }) : content === null ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { style: S.intro, children: "画像尚未生成;蒸馏若干记忆后 L3 会自动产出。" })
3600
+ error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "画像读取失败:" + error + "(点右上“刷新”重试)" }) : null,
3601
+ content ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("pre", { style: S.pre, children: content }) : content === null ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { style: S.intro, children: "画像尚未生成;蒸馏若干记忆后 L3 会自动产出。" })
3314
3602
  ] });
3315
3603
  }
3316
3604
 
3317
3605
  // client/src/tabs/RecordsTab.tsx
3318
- var import_react15 = require("react");
3319
- var import_jsx_runtime15 = require("react/jsx-runtime");
3606
+ var import_react17 = require("react");
3607
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3320
3608
  function retiredSelOf(r) {
3321
3609
  if (r === "active") return false;
3322
3610
  if (r === "retired") return true;
@@ -3335,29 +3623,29 @@ var __defProp = Object.defineProperty;
3335
3623
  function RecordsTab(props) {
3336
3624
  const rpc = props.rpc;
3337
3625
  const limit = 50;
3338
- const [items, setItems] = (0, import_react15.useState)([]);
3339
- const [hasMore, setHasMore] = (0, import_react15.useState)(false);
3340
- const [total, setTotal] = (0, import_react15.useState)(null);
3341
- const [sceneOptions, setSceneOptions] = (0, import_react15.useState)([]);
3342
- const [loading, setLoading] = (0, import_react15.useState)(false);
3343
- const [truncated, setTruncated] = (0, import_react15.useState)(false);
3344
- const [error, setError] = (0, import_react15.useState)(null);
3345
- const [expandedId, setExpandedId] = (0, import_react15.useState)(null);
3346
- const [sel, setSel] = (0, import_react15.useState)(/* @__PURE__ */ new Set());
3347
- const [hiPriv, setHiPriv] = (0, import_react15.useState)(false);
3348
- const [hiPrivBusy, setHiPrivBusy] = (0, import_react15.useState)(false);
3349
- const [query, setQuery] = (0, import_react15.useState)("");
3350
- const [typeFilter, setTypeFilter] = (0, import_react15.useState)("");
3351
- const [sceneFilter, setSceneFilter] = (0, import_react15.useState)("");
3352
- const [hallFilter, setHallFilter] = (0, import_react15.useState)([]);
3353
- const [tagFilter, setTagFilter] = (0, import_react15.useState)("");
3354
- const [untaggedOnly, setUntaggedOnly] = (0, import_react15.useState)(false);
3355
- const [orphanCount, setOrphanCount] = (0, import_react15.useState)(0);
3356
- const [retiredFilter, setRetiredFilter] = (0, import_react15.useState)("");
3357
- const [wingCatalog, setHallCatalog] = (0, import_react15.useState)(null);
3358
- const [last, setLast] = (0, import_react15.useState)({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" });
3359
- const seqRef = (0, import_react15.useRef)(0);
3360
- const fetchPage = (0, import_react15.useCallback)(
3626
+ const [items, setItems] = (0, import_react17.useState)([]);
3627
+ const [hasMore, setHasMore] = (0, import_react17.useState)(false);
3628
+ const [total, setTotal] = (0, import_react17.useState)(null);
3629
+ const [sceneOptions, setSceneOptions] = (0, import_react17.useState)([]);
3630
+ const [loading, setLoading] = (0, import_react17.useState)(false);
3631
+ const [truncated, setTruncated] = (0, import_react17.useState)(false);
3632
+ const [error, setError] = (0, import_react17.useState)(null);
3633
+ const [expandedId, setExpandedId] = (0, import_react17.useState)(null);
3634
+ const [sel, setSel] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
3635
+ const [hiPriv, setHiPriv] = (0, import_react17.useState)(false);
3636
+ const [hiPrivBusy, setHiPrivBusy] = (0, import_react17.useState)(false);
3637
+ const [query, setQuery] = (0, import_react17.useState)("");
3638
+ const [typeFilter, setTypeFilter] = (0, import_react17.useState)("");
3639
+ const [sceneFilter, setSceneFilter] = (0, import_react17.useState)("");
3640
+ const [hallFilter, setHallFilter] = (0, import_react17.useState)([]);
3641
+ const [tagFilter, setTagFilter] = (0, import_react17.useState)("");
3642
+ const [untaggedOnly, setUntaggedOnly] = (0, import_react17.useState)(false);
3643
+ const [orphanCount, setOrphanCount] = (0, import_react17.useState)(0);
3644
+ const [retiredFilter, setRetiredFilter] = (0, import_react17.useState)("");
3645
+ const [wingCatalog, setHallCatalog] = (0, import_react17.useState)(null);
3646
+ const [last, setLast] = (0, import_react17.useState)({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" });
3647
+ const seqRef = (0, import_react17.useRef)(0);
3648
+ const fetchPage = (0, import_react17.useCallback)(
3361
3649
  (conds, offset, append) => {
3362
3650
  setLoading(true);
3363
3651
  setError(null);
@@ -3406,23 +3694,23 @@ var __defProp = Object.defineProperty;
3406
3694
  setLast(conds);
3407
3695
  fetchPage(conds, 0, false);
3408
3696
  };
3409
- const loadRooms = (0, import_react15.useCallback)(() => {
3697
+ const loadRooms = (0, import_react17.useCallback)(() => {
3410
3698
  rpc("dsh-memory/rooms-get", {}).then((r) => {
3411
3699
  if (r && r.ok) setOrphanCount(r.value.orphanCount ?? 0);
3412
3700
  }).catch(() => {
3413
3701
  });
3414
3702
  }, [rpc]);
3415
- (0, import_react15.useEffect)(() => {
3703
+ (0, import_react17.useEffect)(() => {
3416
3704
  fetchPage({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" }, 0, false);
3417
3705
  loadRooms();
3418
3706
  }, [fetchPage, loadRooms]);
3419
- const loadHiPriv = (0, import_react15.useCallback)(() => {
3707
+ const loadHiPriv = (0, import_react17.useCallback)(() => {
3420
3708
  rpc("dsh-memory/settings-get", {}).then((r) => {
3421
3709
  if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
3422
3710
  }).catch(() => {
3423
3711
  });
3424
3712
  }, [rpc]);
3425
- (0, import_react15.useEffect)(() => {
3713
+ (0, import_react17.useEffect)(() => {
3426
3714
  loadHiPriv();
3427
3715
  }, [loadHiPriv]);
3428
3716
  const toggleHiPriv = () => {
@@ -3483,11 +3771,11 @@ var __defProp = Object.defineProperty;
3483
3771
  } else if (r) setError(r.error ? r.error.message : "退场失败");
3484
3772
  }).catch((e) => setError(String(e && e.message || e)));
3485
3773
  };
3486
- const [retired, setRetired] = (0, import_react15.useState)([]);
3487
- const [retiredTotal, setRetiredTotal] = (0, import_react15.useState)(0);
3488
- const [showRetired, setShowRetired] = (0, import_react15.useState)(false);
3489
- const [retiredBusy, setRetiredBusy] = (0, import_react15.useState)(false);
3490
- const loadRetired = (0, import_react15.useCallback)(() => {
3774
+ const [retired, setRetired] = (0, import_react17.useState)([]);
3775
+ const [retiredTotal, setRetiredTotal] = (0, import_react17.useState)(0);
3776
+ const [showRetired, setShowRetired] = (0, import_react17.useState)(false);
3777
+ const [retiredBusy, setRetiredBusy] = (0, import_react17.useState)(false);
3778
+ const loadRetired = (0, import_react17.useCallback)(() => {
3491
3779
  setRetiredBusy(true);
3492
3780
  rpc("dsh-memory/records-retired", { limit: 100, offset: 0 }).then((r) => {
3493
3781
  if (r && r.ok) {
@@ -3496,7 +3784,7 @@ var __defProp = Object.defineProperty;
3496
3784
  } else if (r) setError(r.error ? r.error.message : "已退场列表加载失败");
3497
3785
  }).catch((e) => setError(String(e && e.message || e))).finally(() => setRetiredBusy(false));
3498
3786
  }, [rpc]);
3499
- (0, import_react15.useEffect)(() => {
3787
+ (0, import_react17.useEffect)(() => {
3500
3788
  if (showRetired) loadRetired();
3501
3789
  }, [showRetired, loadRetired]);
3502
3790
  const restoreRecords = (ids) => {
@@ -3516,11 +3804,11 @@ var __defProp = Object.defineProperty;
3516
3804
  unknown: "已退场"
3517
3805
  };
3518
3806
  const countText = total !== null ? "共 " + total + " 条" : items.length + " 条" + (hasMore ? "+" : "");
3519
- const [usageSummary, setUsageSummary] = (0, import_react15.useState)(null);
3807
+ const [usageSummary, setUsageSummary] = (0, import_react17.useState)(null);
3520
3808
  const selCount = sel.size;
3521
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
3522
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.toolbar, children: [
3523
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3809
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
3810
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: S.toolbar, children: [
3811
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3524
3812
  NInput,
3525
3813
  {
3526
3814
  style: { flex: 1, minWidth: 160 },
@@ -3534,7 +3822,7 @@ var __defProp = Object.defineProperty;
3534
3822
  }
3535
3823
  }
3536
3824
  ),
3537
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3825
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3538
3826
  NSel,
3539
3827
  {
3540
3828
  style: { maxWidth: 200 },
@@ -3547,7 +3835,7 @@ var __defProp = Object.defineProperty;
3547
3835
  onChange: setTypeFilter
3548
3836
  }
3549
3837
  ),
3550
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3838
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3551
3839
  NSel,
3552
3840
  {
3553
3841
  style: { maxWidth: 220 },
@@ -3560,7 +3848,7 @@ var __defProp = Object.defineProperty;
3560
3848
  onChange: setSceneFilter
3561
3849
  }
3562
3850
  ),
3563
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3851
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3564
3852
  WingMultiSelect,
3565
3853
  {
3566
3854
  options: wingCatalog ?? Array.from(new Set(items.map((m) => m.hall).filter((h) => !!h))).map((id) => ({ id, label: id })),
@@ -3568,8 +3856,8 @@ var __defProp = Object.defineProperty;
3568
3856
  onChange: setHallFilter
3569
3857
  }
3570
3858
  ),
3571
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(NButton, { onClick: search, children: "搜索" }),
3572
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3859
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(NButton, { onClick: search, children: "搜索" }),
3860
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3573
3861
  NButton,
3574
3862
  {
3575
3863
  style: {
@@ -3583,8 +3871,8 @@ var __defProp = Object.defineProperty;
3583
3871
  }
3584
3872
  )
3585
3873
  ] }),
3586
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { marginBottom: 10, display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
3587
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3874
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { marginBottom: 10, display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
3875
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3588
3876
  "button",
3589
3877
  {
3590
3878
  type: "button",
@@ -3609,10 +3897,10 @@ var __defProp = Object.defineProperty;
3609
3897
  children: "无绑定 · " + orphanCount
3610
3898
  }
3611
3899
  ),
3612
- (tagFilter || untaggedOnly) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { style: S.muted, children: [
3900
+ (tagFilter || untaggedOnly) && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: S.muted, children: [
3613
3901
  "筛选中:",
3614
3902
  untaggedOnly ? "无绑定" : tagFilter,
3615
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3903
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3616
3904
  "button",
3617
3905
  {
3618
3906
  type: "button",
@@ -3629,7 +3917,7 @@ var __defProp = Object.defineProperty;
3629
3917
  }
3630
3918
  )
3631
3919
  ] }),
3632
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3920
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3633
3921
  "button",
3634
3922
  {
3635
3923
  type: "button",
@@ -3658,15 +3946,15 @@ var __defProp = Object.defineProperty;
3658
3946
  children: "导出孤儿"
3659
3947
  }
3660
3948
  ),
3661
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "Room 标签/分类管理:见「Hall」「Room」标签页。" })
3949
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "Room 标签/分类管理:见「Hall」「Room」标签页。" })
3662
3950
  ] }),
3663
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3664
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
3665
- usageSummary ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, title: "老化权重效果口径:参与召回尝试的记忆中,进 topN(被注入上下文)的比例", children: "召回使用 " + usageSummary.used + "/" + usageSummary.attempted + "(" + usageSummary.rate + "%)" }) : null,
3666
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "退场筛查" }),
3951
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3952
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
3953
+ usageSummary ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, title: "老化权重效果口径:参与召回尝试的记忆中,进 topN(被注入上下文)的比例", children: "召回使用 " + usageSummary.used + "/" + usageSummary.attempted + "(" + usageSummary.rate + "%)" }) : null,
3954
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "退场筛查" }),
3667
3955
  [["", "全部"], ["active", "仅活跃"], ["retired", "仅退场"]].map(([val, label]) => {
3668
3956
  const on = retiredFilter === val;
3669
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3957
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3670
3958
  "button",
3671
3959
  {
3672
3960
  type: "button",
@@ -3686,7 +3974,7 @@ var __defProp = Object.defineProperty;
3686
3974
  val
3687
3975
  );
3688
3976
  }),
3689
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3977
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3690
3978
  NButton,
3691
3979
  {
3692
3980
  style: selCount > 0 ? { color: "var(--dsh-mem-danger)" } : void 0,
@@ -3696,7 +3984,7 @@ var __defProp = Object.defineProperty;
3696
3984
  children: "删除选中" + (selCount > 0 ? "(" + selCount + ")" : "")
3697
3985
  }
3698
3986
  ),
3699
- selCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3987
+ selCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3700
3988
  NButton,
3701
3989
  {
3702
3990
  onClick: () => {
@@ -3705,8 +3993,8 @@ var __defProp = Object.defineProperty;
3705
3993
  children: "清空选择"
3706
3994
  }
3707
3995
  ) : null,
3708
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3709
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3996
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
3997
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3710
3998
  NButton,
3711
3999
  {
3712
4000
  onClick: () => {
@@ -3717,13 +4005,13 @@ var __defProp = Object.defineProperty;
3717
4005
  }
3718
4006
  )
3719
4007
  ] }),
3720
- !hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.hint, children: "提示:删除记忆需先开启高权限模式(右上开关或概览页「高权限模式」),关闭时删除按钮不可用。" }) : null,
3721
- error ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.error, children: error }) : null,
3722
- truncated ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.hint, children: "搜索分页已达检索上限(200 条),更早的结果未显示。请用更精确的关键词或类型/情境过滤。" }) : null,
3723
- items.length === 0 && !loading && !error ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.intro, children: "暂无记忆。对话几轮后,蒸馏管线会自动抽取记忆。" }) : items.map((m) => {
4008
+ !hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.hint, children: "提示:删除记忆需先开启高权限模式(右上开关或概览页「高权限模式」),关闭时删除按钮不可用。" }) : null,
4009
+ error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.error, children: error }) : null,
4010
+ truncated ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.hint, children: "搜索分页已达检索上限(200 条),更早的结果未显示。请用更精确的关键词或类型/情境过滤。" }) : null,
4011
+ items.length === 0 && !loading && !error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { style: S.intro, children: "暂无记忆。对话几轮后,蒸馏管线会自动抽取记忆。" }) : items.map((m) => {
3724
4012
  const open = expandedId === m.id;
3725
4013
  const checked = sel.has(m.id);
3726
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4014
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3727
4015
  "div",
3728
4016
  {
3729
4017
  className: "dsh-mem-card dsh-mem-card-hover",
@@ -3732,8 +4020,8 @@ var __defProp = Object.defineProperty;
3732
4020
  setExpandedId(open ? null : m.id);
3733
4021
  },
3734
4022
  children: [
3735
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.cardHead, children: [
3736
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4023
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: S.cardHead, children: [
4024
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3737
4025
  "input",
3738
4026
  {
3739
4027
  type: "checkbox",
@@ -3748,9 +4036,9 @@ var __defProp = Object.defineProperty;
3748
4036
  }
3749
4037
  }
3750
4038
  ),
3751
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + m.type, children: TYPE_LABELS[m.type] || m.type }),
3752
- m.hall ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-fact", children: "Wing · " + (wingCatalog?.find((h) => h.id === m.hall)?.label || m.hall) }) : null,
3753
- m.retired ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4039
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + m.type, children: TYPE_LABELS[m.type] || m.type }),
4040
+ m.hall ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-fact", children: "Wing · " + (wingCatalog?.find((h) => h.id === m.hall)?.label || m.hall) }) : null,
4041
+ m.retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3754
4042
  "span",
3755
4043
  {
3756
4044
  title: "该记忆已退场(软删):移出检索面但仍可恢复,并未真正删除",
@@ -3765,8 +4053,23 @@ var __defProp = Object.defineProperty;
3765
4053
  children: "已退场" + (m.retiredReason ? " · " + (RETIRE_REASON_LABEL[m.retiredReason] || m.retiredReason) : "")
3766
4054
  }
3767
4055
  ) : null,
3768
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "优先级 " + m.priority }),
3769
- m.agingWeight != null ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4056
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "优先级 " + m.priority }),
4057
+ m.maturity ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
4058
+ "span",
4059
+ {
4060
+ title: "成熟度(优先级 + 被用次数 + 新鲜度合成):" + m.maturity,
4061
+ style: {
4062
+ fontSize: 11,
4063
+ padding: "1px 6px",
4064
+ borderRadius: 999,
4065
+ whiteSpace: "nowrap",
4066
+ border: "1px solid var(--dsh-mem-border)",
4067
+ color: "var(--dsh-mem-text-2)"
4068
+ },
4069
+ children: m.maturity === "high" ? "成熟·高" : m.maturity === "mid" ? "成熟·中" : "成熟·低"
4070
+ }
4071
+ ) : null,
4072
+ m.agingWeight != null ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3770
4073
  "span",
3771
4074
  {
3772
4075
  style: {
@@ -3781,7 +4084,7 @@ var __defProp = Object.defineProperty;
3781
4084
  children: "老化 " + Math.round(m.agingWeight * 100) + "%"
3782
4085
  }
3783
4086
  ) : null,
3784
- m.usage ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4087
+ m.usage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3785
4088
  "span",
3786
4089
  {
3787
4090
  style: S.muted,
@@ -3789,10 +4092,10 @@ var __defProp = Object.defineProperty;
3789
4092
  children: "用 " + m.usage.used + "/" + m.usage.attempts
3790
4093
  }
3791
4094
  ) : null,
3792
- m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "相关度 " + Number(m.score).toFixed(2) }) : null,
3793
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3794
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: fmtTime(m.updatedAt) }),
3795
- m.retired ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4095
+ m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "相关度 " + Number(m.score).toFixed(2) }) : null,
4096
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4097
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: fmtTime(m.updatedAt) }),
4098
+ m.retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3796
4099
  NButton,
3797
4100
  {
3798
4101
  style: { padding: "0 7px", minWidth: 26, height: 26, fontSize: 12, color: "var(--dsh-mem-accent)" },
@@ -3803,7 +4106,7 @@ var __defProp = Object.defineProperty;
3803
4106
  },
3804
4107
  children: "恢复"
3805
4108
  }
3806
- ) : hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4109
+ ) : hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3807
4110
  NButton,
3808
4111
  {
3809
4112
  style: { padding: "0 7px", minWidth: 26, height: 26, fontSize: 12, color: "var(--dsh-mem-danger)" },
@@ -3816,34 +4119,34 @@ var __defProp = Object.defineProperty;
3816
4119
  }
3817
4120
  ) : null
3818
4121
  ] }),
3819
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.content, children: m.content }),
3820
- open ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.detail, children: "id: " + m.id + "\n情境: " + (m.scene || "-") + "\n版本: v" + m.version + "(去重合并次数 " + m.version + ")\n创建: " + fmtTime(m.createdAt) + "\n活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" + (m.sourceAnchors && m.sourceAnchors.length > 0 ? "来源锚点: " + m.sourceAnchors.join(", ") : "来源锚点: -") }) : null
4122
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.content, children: m.content }),
4123
+ open ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.detail, children: "id: " + m.id + "\n情境: " + (m.scene || "-") + "\n版本: v" + m.version + "(去重合并次数 " + m.version + ")\n创建: " + fmtTime(m.createdAt) + "\n活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" + (m.sourceAnchors && m.sourceAnchors.length > 0 ? "来源锚点: " + m.sourceAnchors.join(", ") : "来源锚点: -") }) : null
3821
4124
  ]
3822
4125
  },
3823
4126
  m.id
3824
4127
  );
3825
4128
  }),
3826
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginTop: 12 }, children: [
3827
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(NButton, { onClick: () => setShowRetired((v) => !v), children: (showRetired ? "收起" : "展开") + "「已退场」(可恢复)" }),
3828
- showRetired && retiredBusy ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "加载中…" }) : null,
3829
- showRetired && !retiredBusy && retiredTotal > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "共 " + retiredTotal + " 条可恢复" }) : null
4129
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { ...S.flexRow, marginTop: 12 }, children: [
4130
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(NButton, { onClick: () => setShowRetired((v) => !v), children: (showRetired ? "收起" : "展开") + "「已退场」(可恢复)" }),
4131
+ showRetired && retiredBusy ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "加载中…" }) : null,
4132
+ showRetired && !retiredBusy && retiredTotal > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "共 " + retiredTotal + " 条可恢复" }) : null
3830
4133
  ] }),
3831
- showRetired ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { marginTop: 8 }, children: retired.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.hint, children: retiredBusy ? " " : "没有已退场的记忆。" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
3832
- retired.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-card", style: S.card, children: [
3833
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.cardHead, children: [
3834
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: RETIRE_REASON_LABEL[m.retiredReason] || m.retiredReason }),
3835
- m.verdict ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "结论 " + m.verdict }) : null,
3836
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3837
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: fmtTime(m.retiredAt) }),
3838
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(NButton, { disabled: retiredBusy, title: "恢复到检索面", onClick: () => restoreRecords([m.id]), children: "恢复" })
4134
+ showRetired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginTop: 8 }, children: retired.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { style: S.hint, children: retiredBusy ? " " : "没有已退场的记忆。" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
4135
+ retired.map((m) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "dsh-mem-card", style: S.card, children: [
4136
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: S.cardHead, children: [
4137
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: RETIRE_REASON_LABEL[m.retiredReason] || m.retiredReason }),
4138
+ m.verdict ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "结论 " + m.verdict }) : null,
4139
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4140
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: fmtTime(m.retiredAt) }),
4141
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(NButton, { disabled: retiredBusy, title: "恢复到检索面", onClick: () => restoreRecords([m.id]), children: "恢复" })
3839
4142
  ] }),
3840
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.content, children: m.content })
4143
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.content, children: m.content })
3841
4144
  ] }, m.id)),
3842
- retiredTotal > retired.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: S.hint, children: "共 " + retiredTotal + " 条,此处显示 " + retired.length + " 条" }) : null
4145
+ retiredTotal > retired.length ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { style: S.hint, children: "共 " + retiredTotal + " 条,此处显示 " + retired.length + " 条" }) : null
3843
4146
  ] }) }) : null,
3844
- hasMore ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.flexRow, children: [
3845
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3846
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4147
+ hasMore ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: S.flexRow, children: [
4148
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4149
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3847
4150
  NButton,
3848
4151
  {
3849
4152
  disabled: loading,
@@ -3857,9 +4160,9 @@ var __defProp = Object.defineProperty;
3857
4160
  ] });
3858
4161
  }
3859
4162
  function WingMultiSelect(props) {
3860
- const [open, setOpen] = (0, import_react15.useState)(false);
3861
- const wrapRef = (0, import_react15.useRef)(null);
3862
- (0, import_react15.useEffect)(() => {
4163
+ const [open, setOpen] = (0, import_react17.useState)(false);
4164
+ const wrapRef = (0, import_react17.useRef)(null);
4165
+ (0, import_react17.useEffect)(() => {
3863
4166
  if (!open) return;
3864
4167
  const onDown = (e) => {
3865
4168
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -3879,8 +4182,8 @@ var __defProp = Object.defineProperty;
3879
4182
  const toggle = (id) => {
3880
4183
  props.onChange(props.selected.includes(id) ? props.selected.filter((x) => x !== id) : [...props.selected, id]);
3881
4184
  };
3882
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { ref: wrapRef, style: { position: "relative", maxWidth: 150 }, children: [
3883
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4185
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: wrapRef, style: { position: "relative", maxWidth: 150 }, children: [
4186
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3884
4187
  "button",
3885
4188
  {
3886
4189
  type: "button",
@@ -3889,31 +4192,31 @@ var __defProp = Object.defineProperty;
3889
4192
  "aria-expanded": open,
3890
4193
  onClick: () => setOpen(!open),
3891
4194
  children: [
3892
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-select-label", title: summary, children: summary }),
3893
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-sel-chev" + (open ? " dsh-mem-sel-chev-open" : "") })
4195
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-select-label", title: summary, children: summary }),
4196
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-sel-chev" + (open ? " dsh-mem-sel-chev-open" : "") })
3894
4197
  ]
3895
4198
  }
3896
4199
  ),
3897
- open ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-pop", role: "listbox", style: { left: 0, right: "auto", minWidth: 150 }, children: [
3898
- props.options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dsh-mem-pop-empty", children: "暂无可筛 Hall" }) : null,
4200
+ open ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "dsh-mem-pop", role: "listbox", style: { left: 0, right: "auto", minWidth: 150 }, children: [
4201
+ props.options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "dsh-mem-pop-empty", children: "暂无可筛 Hall" }) : null,
3899
4202
  props.options.map((o) => {
3900
4203
  const active = props.selected.includes(o.id);
3901
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: "dsh-mem-pop-opt", onClick: () => toggle(o.id), "aria-selected": active, children: [
3902
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-pop-check", children: active ? "✓" : "" }),
3903
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-pop-label", children: o.label })
4204
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", className: "dsh-mem-pop-opt", onClick: () => toggle(o.id), "aria-selected": active, children: [
4205
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-pop-check", children: active ? "✓" : "" }),
4206
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-pop-label", children: o.label })
3904
4207
  ] }, o.id);
3905
4208
  }),
3906
- props.selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: "dsh-mem-pop-opt", onClick: () => props.onChange([]), children: [
3907
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-pop-check" }),
3908
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-pop-label", children: "清除筛选" })
4209
+ props.selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", className: "dsh-mem-pop-opt", onClick: () => props.onChange([]), children: [
4210
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-pop-check" }),
4211
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-pop-label", children: "清除筛选" })
3909
4212
  ] }) : null
3910
4213
  ] }) : null
3911
4214
  ] });
3912
4215
  }
3913
4216
 
3914
4217
  // client/src/tabs/RoomsTab.tsx
3915
- var import_react16 = require("react");
3916
- var import_jsx_runtime16 = require("react/jsx-runtime");
4218
+ var import_react18 = require("react");
4219
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3917
4220
  function majorOf(slug) {
3918
4221
  const i = slug.indexOf("/");
3919
4222
  return i === -1 ? slug : slug.slice(0, i);
@@ -3942,26 +4245,26 @@ var __defProp = Object.defineProperty;
3942
4245
  };
3943
4246
  function RoomsTab(props) {
3944
4247
  const rpc = props.rpc;
3945
- const [registry, setRegistry] = (0, import_react16.useState)([]);
3946
- const [rooms, setRooms] = (0, import_react16.useState)([]);
3947
- const [orphanCount, setOrphanCount] = (0, import_react16.useState)(0);
3948
- const [busy, setBusy] = (0, import_react16.useState)(false);
3949
- const [msg, setMsg] = (0, import_react16.useState)(null);
3950
- const [error, setError] = (0, import_react16.useState)(null);
3951
- const [hiPriv, setHiPriv] = (0, import_react16.useState)(false);
3952
- const [hiPrivBusy, setHiPrivBusy] = (0, import_react16.useState)(false);
3953
- const [regSlug, setRegSlug] = (0, import_react16.useState)("");
3954
- const [regLabel, setRegLabel] = (0, import_react16.useState)("");
3955
- const [regDesc, setRegDesc] = (0, import_react16.useState)("");
3956
- const [form, setForm] = (0, import_react16.useState)(null);
3957
- const [preview, setPreview] = (0, import_react16.useState)(null);
3958
- const [grownOpen, setGrownOpen] = (0, import_react16.useState)(false);
3959
- const [dragOverHall, setDragOverHall] = (0, import_react16.useState)(null);
3960
- const [picking, setPicking] = (0, import_react16.useState)(null);
3961
- const [editing, setEditing] = (0, import_react16.useState)(null);
3962
- const [expandedRooms, setExpandedRooms] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
3963
- const [roomRecords, setRoomRecords] = (0, import_react16.useState)({});
3964
- const [expandedHalls, setExpandedHalls] = (0, import_react16.useState)(() => {
4248
+ const [registry, setRegistry] = (0, import_react18.useState)([]);
4249
+ const [rooms, setRooms] = (0, import_react18.useState)([]);
4250
+ const [orphanCount, setOrphanCount] = (0, import_react18.useState)(0);
4251
+ const [busy, setBusy] = (0, import_react18.useState)(false);
4252
+ const [msg, setMsg] = (0, import_react18.useState)(null);
4253
+ const [error, setError] = (0, import_react18.useState)(null);
4254
+ const [hiPriv, setHiPriv] = (0, import_react18.useState)(false);
4255
+ const [hiPrivBusy, setHiPrivBusy] = (0, import_react18.useState)(false);
4256
+ const [regSlug, setRegSlug] = (0, import_react18.useState)("");
4257
+ const [regLabel, setRegLabel] = (0, import_react18.useState)("");
4258
+ const [regDesc, setRegDesc] = (0, import_react18.useState)("");
4259
+ const [form, setForm] = (0, import_react18.useState)(null);
4260
+ const [preview, setPreview] = (0, import_react18.useState)(null);
4261
+ const [grownOpen, setGrownOpen] = (0, import_react18.useState)(false);
4262
+ const [dragOverHall, setDragOverHall] = (0, import_react18.useState)(null);
4263
+ const [picking, setPicking] = (0, import_react18.useState)(null);
4264
+ const [editing, setEditing] = (0, import_react18.useState)(null);
4265
+ const [expandedRooms, setExpandedRooms] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
4266
+ const [roomRecords, setRoomRecords] = (0, import_react18.useState)({});
4267
+ const [expandedHalls, setExpandedHalls] = (0, import_react18.useState)(() => {
3965
4268
  try {
3966
4269
  const v = window.localStorage.getItem("dsh.memory.halls.open");
3967
4270
  return v ? new Set(JSON.parse(v)) : /* @__PURE__ */ new Set();
@@ -3969,9 +4272,9 @@ var __defProp = Object.defineProperty;
3969
4272
  return /* @__PURE__ */ new Set();
3970
4273
  }
3971
4274
  });
3972
- const [filter, setFilter] = (0, import_react16.useState)("");
3973
- const dragRef = (0, import_react16.useRef)(null);
3974
- const [ghost, setGhost] = (0, import_react16.useState)(null);
4275
+ const [filter, setFilter] = (0, import_react18.useState)("");
4276
+ const dragRef = (0, import_react18.useRef)(null);
4277
+ const [ghost, setGhost] = (0, import_react18.useState)(null);
3975
4278
  const onDragLabelDown = (ev, slug) => {
3976
4279
  if (!hiPriv || busy || ev.button !== 0) return;
3977
4280
  ev.currentTarget.setPointerCapture(ev.pointerId);
@@ -3996,7 +4299,7 @@ var __defProp = Object.defineProperty;
3996
4299
  setDragOverHall(null);
3997
4300
  if (hall) beginReclass(d.from, hall);
3998
4301
  };
3999
- const loadAll = (0, import_react16.useCallback)(() => {
4302
+ const loadAll = (0, import_react18.useCallback)(() => {
4000
4303
  rpc("dsh-memory/rooms-get", {}).then((r) => {
4001
4304
  if (r && r.ok) {
4002
4305
  setRooms(r.value.rooms ?? []);
@@ -4005,13 +4308,13 @@ var __defProp = Object.defineProperty;
4005
4308
  }
4006
4309
  }).catch(() => void 0);
4007
4310
  }, [rpc]);
4008
- const loadHiPriv = (0, import_react16.useCallback)(() => {
4311
+ const loadHiPriv = (0, import_react18.useCallback)(() => {
4009
4312
  rpc("dsh-memory/settings-get", {}).then((r) => {
4010
4313
  if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
4011
4314
  }).catch(() => {
4012
4315
  });
4013
4316
  }, [rpc]);
4014
- (0, import_react16.useEffect)(() => {
4317
+ (0, import_react18.useEffect)(() => {
4015
4318
  loadAll();
4016
4319
  loadHiPriv();
4017
4320
  }, [loadAll, loadHiPriv]);
@@ -4162,7 +4465,7 @@ var __defProp = Object.defineProperty;
4162
4465
  if (r.value.csv) downloadCsv(r.value.csv, `dsh-memory-room-${slug.replace(/\//g, "-")}-records.csv`);
4163
4466
  else setError("导出为空");
4164
4467
  });
4165
- const groups = (0, import_react16.useMemo)(() => {
4468
+ const groups = (0, import_react18.useMemo)(() => {
4166
4469
  const countOf = new Map(rooms.map((r) => [r.room, r.count]));
4167
4470
  const byHall = /* @__PURE__ */ new Map();
4168
4471
  for (const e of registry) {
@@ -4179,7 +4482,7 @@ var __defProp = Object.defineProperty;
4179
4482
  total: entries.reduce((s2, e) => s2 + (countOf.get(e.slug) ?? 0), 0)
4180
4483
  })).sort((a, b) => a.hall.localeCompare(b.hall));
4181
4484
  }, [registry, rooms]);
4182
- const grownOnly = (0, import_react16.useMemo)(() => rooms.filter((r) => !registry.some((e) => e.slug === r.room)), [rooms, registry]);
4485
+ const grownOnly = (0, import_react18.useMemo)(() => rooms.filter((r) => !registry.some((e) => e.slug === r.room)), [rooms, registry]);
4183
4486
  const persistHalls = (next) => {
4184
4487
  try {
4185
4488
  window.localStorage.setItem("dsh.memory.halls.open", JSON.stringify([...next]));
@@ -4199,7 +4502,7 @@ var __defProp = Object.defineProperty;
4199
4502
  persistHalls(next);
4200
4503
  };
4201
4504
  const normFilter = filter.trim().toLowerCase();
4202
- const visibleGroups = (0, import_react16.useMemo)(() => {
4505
+ const visibleGroups = (0, import_react18.useMemo)(() => {
4203
4506
  if (!normFilter) return groups;
4204
4507
  return groups.map((g) => {
4205
4508
  const hallHit = g.hall.toLowerCase().includes(normFilter) || (g.label ?? "").toLowerCase().includes(normFilter);
@@ -4209,15 +4512,15 @@ var __defProp = Object.defineProperty;
4209
4512
  return { ...g, entries };
4210
4513
  }).filter((g) => g.entries.length > 0);
4211
4514
  }, [groups, normFilter]);
4212
- const visibleGrown = (0, import_react16.useMemo)(
4515
+ const visibleGrown = (0, import_react18.useMemo)(
4213
4516
  () => normFilter ? grownOnly.filter((r) => r.room.toLowerCase().includes(normFilter)) : grownOnly,
4214
4517
  [grownOnly, normFilter]
4215
4518
  );
4216
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
4217
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8 }, children: [
4218
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "Hall = Room 大类 · 大类上限 200,小类「大类/小类」细分不占额度 · 与 Wing(域)筛选无关" }),
4219
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4220
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4519
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
4520
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8 }, children: [
4521
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "Hall = Room 大类 · 大类上限 200,小类「大类/小类」细分不占额度 · 与 Wing(域)筛选无关" }),
4522
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
4523
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4221
4524
  "button",
4222
4525
  {
4223
4526
  type: "button",
@@ -4234,7 +4537,7 @@ var __defProp = Object.defineProperty;
4234
4537
  children: "导出词表"
4235
4538
  }
4236
4539
  ),
4237
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4540
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4238
4541
  "button",
4239
4542
  {
4240
4543
  type: "button",
@@ -4254,7 +4557,7 @@ var __defProp = Object.defineProperty;
4254
4557
  ]
4255
4558
  }
4256
4559
  ),
4257
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4560
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4258
4561
  "button",
4259
4562
  {
4260
4563
  type: "button",
@@ -4269,10 +4572,10 @@ var __defProp = Object.defineProperty;
4269
4572
  children: hiPriv ? "高权限:开" : "高权限:关"
4270
4573
  }
4271
4574
  ),
4272
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, onClick: loadAll, title: "重拉注册表与计数", children: "刷新" })
4575
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", style: btn, onClick: loadAll, title: "重拉注册表与计数", children: "刷新" })
4273
4576
  ] }),
4274
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center", marginBottom: 8, flexWrap: "wrap" }, children: [
4275
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4577
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center", marginBottom: 8, flexWrap: "wrap" }, children: [
4578
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4276
4579
  "input",
4277
4580
  {
4278
4581
  value: filter,
@@ -4281,14 +4584,14 @@ var __defProp = Object.defineProperty;
4281
4584
  style: { ...S.input, width: 250, fontSize: 12 }
4282
4585
  }
4283
4586
  ),
4284
- filter ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, onClick: () => setFilter(""), children: "清除" }) : null,
4285
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `匹配 ${visibleGroups.reduce((s2, g) => s2 + g.entries.length, 0)} room / ${visibleGroups.length} hall` }),
4286
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4287
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, title: "展开全部 hall 的子 room", onClick: () => setAllHalls(true), children: "展开全部" }),
4288
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, title: "收起全部 hall(只留 hall 头)", onClick: () => setAllHalls(false), children: "收起全部" })
4587
+ filter ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", style: btn, onClick: () => setFilter(""), children: "清除" }) : null,
4588
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: `匹配 ${visibleGroups.reduce((s2, g) => s2 + g.entries.length, 0)} room / ${visibleGroups.length} hall` }),
4589
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
4590
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", style: btn, title: "展开全部 hall 的子 room", onClick: () => setAllHalls(true), children: "展开全部" }),
4591
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", style: btn, title: "收起全部 hall(只留 hall 头)", onClick: () => setAllHalls(false), children: "收起全部" })
4289
4592
  ] }),
4290
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4291
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4593
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4594
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4292
4595
  "input",
4293
4596
  {
4294
4597
  value: regSlug,
@@ -4298,7 +4601,7 @@ var __defProp = Object.defineProperty;
4298
4601
  style: { ...S.input, width: 210, fontSize: 12 }
4299
4602
  }
4300
4603
  ),
4301
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4604
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4302
4605
  "input",
4303
4606
  {
4304
4607
  value: regLabel,
@@ -4308,7 +4611,7 @@ var __defProp = Object.defineProperty;
4308
4611
  style: { ...S.input, width: 110, fontSize: 12 }
4309
4612
  }
4310
4613
  ),
4311
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4614
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4312
4615
  "input",
4313
4616
  {
4314
4617
  value: regDesc,
@@ -4318,7 +4621,7 @@ var __defProp = Object.defineProperty;
4318
4621
  style: { ...S.input, width: 170, fontSize: 12 }
4319
4622
  }
4320
4623
  ),
4321
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4624
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4322
4625
  "button",
4323
4626
  {
4324
4627
  type: "button",
@@ -4328,26 +4631,26 @@ var __defProp = Object.defineProperty;
4328
4631
  children: "注册"
4329
4632
  }
4330
4633
  ),
4331
- !hiPriv && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
4634
+ !hiPriv && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
4332
4635
  ] }),
4333
- editing ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4334
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: S.muted, children: [
4636
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4637
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { style: S.muted, children: [
4335
4638
  "编辑 ",
4336
4639
  editing.slug
4337
4640
  ] }),
4338
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { value: editing.label, onChange: (ev) => setEditing({ ...editing, label: ev.target.value }), placeholder: "显示名(可中文)", disabled: busy, style: { ...S.input, width: 140, fontSize: 12 } }),
4339
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { value: editing.description, onChange: (ev) => setEditing({ ...editing, description: ev.target.value }), placeholder: "归类说明(喂标注器)", disabled: busy, style: { ...S.input, width: 200, fontSize: 12 } }),
4340
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: saveEditing, style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "保存" }),
4341
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), style: btn, children: "取消" })
4641
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("input", { value: editing.label, onChange: (ev) => setEditing({ ...editing, label: ev.target.value }), placeholder: "显示名(可中文)", disabled: busy, style: { ...S.input, width: 140, fontSize: 12 } }),
4642
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("input", { value: editing.description, onChange: (ev) => setEditing({ ...editing, description: ev.target.value }), placeholder: "归类说明(喂标注器)", disabled: busy, style: { ...S.input, width: 200, fontSize: 12 } }),
4643
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, onClick: saveEditing, style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "保存" }),
4644
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), style: btn, children: "取消" })
4342
4645
  ] }) : null,
4343
- form ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4344
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: S.muted, children: [
4646
+ form ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4647
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { style: S.muted, children: [
4345
4648
  form.action === "merge" ? "合并" : "归类/改名",
4346
4649
  " ",
4347
4650
  form.from,
4348
4651
  " →"
4349
4652
  ] }),
4350
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4653
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4351
4654
  "input",
4352
4655
  {
4353
4656
  value: form.to,
@@ -4357,7 +4660,7 @@ var __defProp = Object.defineProperty;
4357
4660
  style: { ...S.input, width: 190, fontSize: 12 }
4358
4661
  }
4359
4662
  ),
4360
- form.action === "rename" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "inline-flex", gap: 4, flexWrap: "wrap" }, children: groups.filter((gg) => gg.hall !== majorOf(form.from)).map((gg) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4663
+ form.action === "rename" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { display: "inline-flex", gap: 4, flexWrap: "wrap" }, children: groups.filter((gg) => gg.hall !== majorOf(form.from)).map((gg) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4361
4664
  "button",
4362
4665
  {
4363
4666
  type: "button",
@@ -4368,32 +4671,32 @@ var __defProp = Object.defineProperty;
4368
4671
  },
4369
4672
  gg.hall
4370
4673
  )) }) : null,
4371
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => previewMerge(), style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "预览" }),
4372
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => {
4674
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, onClick: () => previewMerge(), style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "预览" }),
4675
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, onClick: () => {
4373
4676
  setForm(null);
4374
4677
  setPreview(null);
4375
4678
  setPicking(null);
4376
4679
  }, style: btn, children: "取消" }),
4377
- picking ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "挑选中:点击目标 hall 头完成归类" }) : null,
4378
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4680
+ picking ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "挑选中:点击目标 hall 头完成归类" }) : null,
4681
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4379
4682
  ] }) : null,
4380
- preview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", marginBottom: 8, padding: "6px 8px", border: "1px solid var(--dsh-mem-danger)", borderRadius: 8, fontSize: 12 }, children: [
4381
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: preview.notice }),
4382
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, marginTop: 4 }, children: [
4383
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", disabled: busy, onClick: execMerge, style: { ...btn, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: [
4683
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", marginBottom: 8, padding: "6px 8px", border: "1px solid var(--dsh-mem-danger)", borderRadius: 8, fontSize: 12 }, children: [
4684
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { children: preview.notice }),
4685
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", gap: 6, marginTop: 4 }, children: [
4686
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("button", { type: "button", disabled: busy, onClick: execMerge, style: { ...btn, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: [
4384
4687
  "确认执行",
4385
4688
  preview.action === "merge" ? "合并" : "归类/改名"
4386
4689
  ] }),
4387
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => setPreview(null), style: btn, children: "再改改" })
4690
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, onClick: () => setPreview(null), style: btn, children: "再改改" })
4388
4691
  ] })
4389
4692
  ] }) : null,
4390
- msg ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.hint, children: msg }) : null,
4391
- error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.error, children: error }) : null,
4392
- ghost ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { position: "fixed", left: ghost.x + 12, top: ghost.y + 12, zIndex: 9999, pointerEvents: "none", fontSize: 12, padding: "2px 10px", borderRadius: 999, border: "1px solid var(--dsh-mem-accent)", background: "var(--dsh-mem-bg-inset)", color: "var(--dsh-mem-accent)" }, children: ghost.text }) : null,
4393
- visibleGroups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { style: S.intro, children: normFilter ? "无匹配的 hall/room。" : "注册表为空。在上方注册 Hall/Room(如 dsh-plugin),或把自生长 slug「收编」进词表 —— 之后候选标注器会优先把相关记忆挂到这些 slug 上。" }) : visibleGroups.map((g) => {
4693
+ msg ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.hint, children: msg }) : null,
4694
+ error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.error, children: error }) : null,
4695
+ ghost ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { position: "fixed", left: ghost.x + 12, top: ghost.y + 12, zIndex: 9999, pointerEvents: "none", fontSize: 12, padding: "2px 10px", borderRadius: 999, border: "1px solid var(--dsh-mem-accent)", background: "var(--dsh-mem-bg-inset)", color: "var(--dsh-mem-accent)" }, children: ghost.text }) : null,
4696
+ visibleGroups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { style: S.intro, children: normFilter ? "无匹配的 hall/room。" : "注册表为空。在上方注册 Hall/Room(如 dsh-plugin),或把自生长 slug「收编」进词表 —— 之后候选标注器会优先把相关记忆挂到这些 slug 上。" }) : visibleGroups.map((g) => {
4394
4697
  const hallOpen = !!normFilter || expandedHalls.has(g.hall);
4395
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginBottom: 10, border: "1px solid var(--dsh-mem-border)", borderRadius: 8, overflow: "hidden" }, children: [
4396
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4698
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { marginBottom: 10, border: "1px solid var(--dsh-mem-border)", borderRadius: 8, overflow: "hidden" }, children: [
4699
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4397
4700
  "div",
4398
4701
  {
4399
4702
  "data-hall": g.hall,
@@ -4434,7 +4737,7 @@ var __defProp = Object.defineProperty;
4434
4737
  },
4435
4738
  title: picking ? "点击此 hall:把「" + picking + "」归入 " + g.hall + " 下(major/minor,预览后确认)" : hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
4436
4739
  children: [
4437
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4740
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4438
4741
  "button",
4439
4742
  {
4440
4743
  type: "button",
@@ -4447,13 +4750,13 @@ var __defProp = Object.defineProperty;
4447
4750
  children: hallOpen ? "▾" : "▸"
4448
4751
  }
4449
4752
  ),
4450
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { fontSize: 12, fontWeight: 600 }, children: [
4753
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { style: { fontSize: 12, fontWeight: 600 }, children: [
4451
4754
  g.hall,
4452
- g.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: " (" + g.label + ")" }) : null
4755
+ g.label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: " (" + g.label + ")" }) : null
4453
4756
  ] }),
4454
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `合计 ${g.total} 条 · ${g.entries.length} 个 room` }),
4455
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4456
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4757
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: `合计 ${g.total} 条 · ${g.entries.length} 个 room` }),
4758
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
4759
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4457
4760
  "button",
4458
4761
  {
4459
4762
  type: "button",
@@ -4474,10 +4777,10 @@ var __defProp = Object.defineProperty;
4474
4777
  const standalone = e.slug === g.hall;
4475
4778
  const count = rooms.find((r) => r.room === e.slug)?.count ?? 0;
4476
4779
  const retired = e.status === "retired";
4477
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { padding: "4px 10px", borderTop: "1px solid var(--dsh-mem-border)", opacity: retired ? 0.55 : 1 }, children: [
4478
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4479
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
4480
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4780
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "4px 10px", borderTop: "1px solid var(--dsh-mem-border)", opacity: retired ? 0.55 : 1 }, children: [
4781
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4782
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
4783
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4481
4784
  "button",
4482
4785
  {
4483
4786
  type: "button",
@@ -4487,7 +4790,7 @@ var __defProp = Object.defineProperty;
4487
4790
  children: expandedRooms.has(e.slug) ? "▾" : "▸"
4488
4791
  }
4489
4792
  ),
4490
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4793
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4491
4794
  "span",
4492
4795
  {
4493
4796
  style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default", touchAction: "none" },
@@ -4501,20 +4804,20 @@ var __defProp = Object.defineProperty;
4501
4804
  ]
4502
4805
  }
4503
4806
  ),
4504
- e.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: e.label }) : null,
4505
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: count + " 条" }),
4506
- retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4507
- (e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e.aliases ?? []).join(", ") }) : null
4807
+ e.label ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: e.label }) : null,
4808
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: count + " 条" }),
4809
+ retired ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4810
+ (e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e.aliases ?? []).join(", ") }) : null
4508
4811
  ] }),
4509
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4510
- !hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4511
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
4812
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4813
+ !hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4814
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
4512
4815
  setEditing({ slug: e.slug, label: e.label ?? "", description: e.description ?? "" });
4513
4816
  setForm(null);
4514
4817
  setPreview(null);
4515
4818
  setPicking(null);
4516
4819
  }, style: btn, children: "编辑" }),
4517
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4820
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4518
4821
  "button",
4519
4822
  {
4520
4823
  type: "button",
@@ -4535,43 +4838,43 @@ var __defProp = Object.defineProperty;
4535
4838
  children: picking === e.slug ? "选 hall…" : "归类"
4536
4839
  }
4537
4840
  ),
4538
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: `改名(= merge 1:1 + 注册表改名)`, onClick: () => {
4841
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, title: `改名(= merge 1:1 + 注册表改名)`, onClick: () => {
4539
4842
  setForm({ action: "rename", from: e.slug, to: "" });
4540
4843
  setPreview(null);
4541
4844
  }, style: btn, children: "改名" }),
4542
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "合并到另一个 Room(dryRun 预览→确认实跑)", onClick: () => {
4845
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, title: "合并到另一个 Room(dryRun 预览→确认实跑)", onClick: () => {
4543
4846
  setForm({ action: "merge", from: e.slug, to: "" });
4544
4847
  setPreview(null);
4545
4848
  }, style: btn, children: "合并" }),
4546
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: retired ? "恢复该 Room 到词表" : "退役该 Room(存量 tags 不动)", onClick: () => retire(e.slug, retired), style: { ...btn, ...retired ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : { color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" } }, children: retired ? "恢复" : "退役" }),
4547
- !retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "退役并退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => retire(e.slug, false, true), style: { ...btn, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "退场" }) : null
4849
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, title: retired ? "恢复该 Room 到词表" : "退役该 Room(存量 tags 不动)", onClick: () => retire(e.slug, retired), style: { ...btn, ...retired ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : { color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" } }, children: retired ? "恢复" : "退役" }),
4850
+ !retired ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, title: "退役并退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => retire(e.slug, false, true), style: { ...btn, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "退场" }) : null
4548
4851
  ] }),
4549
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(e.slug), style: btn, children: "导出" })
4852
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(e.slug), style: btn, children: "导出" })
4550
4853
  ] })
4551
4854
  ] }),
4552
- expandedRooms.has(e.slug) ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { margin: "4px 0 2px", padding: "4px 8px", border: "1px dashed var(--dsh-mem-border)", borderRadius: 6 }, children: [
4553
- (roomRecords[e.slug]?.items ?? []).map((it) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { fontSize: 11, padding: "3px 0", borderBottom: "1px solid var(--dsh-mem-border)" }, children: [
4554
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
4855
+ expandedRooms.has(e.slug) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { margin: "4px 0 2px", padding: "4px 8px", border: "1px dashed var(--dsh-mem-border)", borderRadius: 6 }, children: [
4856
+ (roomRecords[e.slug]?.items ?? []).map((it) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { fontSize: 11, padding: "3px 0", borderBottom: "1px solid var(--dsh-mem-border)" }, children: [
4857
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
4555
4858
  " ",
4556
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4557
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { marginTop: 2, wordBreak: "break-all", color: "var(--dsh-mem-text-2)" }, children: it.content.length > 120 ? it.content.slice(0, 120) + "…" : it.content })
4859
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4860
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { marginTop: 2, wordBreak: "break-all", color: "var(--dsh-mem-text-2)" }, children: it.content.length > 120 ? it.content.slice(0, 120) + "…" : it.content })
4558
4861
  ] }, it.id)),
4559
- (roomRecords[e.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.muted, children: "该 Room 暂无记录。" }) : null,
4560
- (roomRecords[e.slug]?.total ?? 0) > (roomRecords[e.slug]?.items.length ?? 0) ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { ...S.muted, fontSize: 11 }, children: "共 " + roomRecords[e.slug].total + " 条,仅显示前 " + roomRecords[e.slug].items.length + " 条" }) : null
4862
+ (roomRecords[e.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.muted, children: "该 Room 暂无记录。" }) : null,
4863
+ (roomRecords[e.slug]?.total ?? 0) > (roomRecords[e.slug]?.items.length ?? 0) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { ...S.muted, fontSize: 11 }, children: "共 " + roomRecords[e.slug].total + " 条,仅显示前 " + roomRecords[e.slug].items.length + " 条" }) : null
4561
4864
  ] }) : null
4562
4865
  ] }, e.slug);
4563
4866
  }) : null
4564
4867
  ] }, g.hall);
4565
4868
  }),
4566
- visibleGrown.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginTop: 4 }, children: [
4567
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", onClick: () => setGrownOpen(!grownOpen), style: btn, title: grownOpen ? "收起未注册自生长 Room" : "展开未注册自生长 Room", children: [
4869
+ visibleGrown.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { marginTop: 4 }, children: [
4870
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("button", { type: "button", onClick: () => setGrownOpen(!grownOpen), style: btn, title: grownOpen ? "收起未注册自生长 Room" : "展开未注册自生长 Room", children: [
4568
4871
  grownOpen ? "▾" : "▸",
4569
4872
  " 自生长未注册 · 共 ",
4570
4873
  visibleGrown.length
4571
4874
  ] }),
4572
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "(只在 tags 里涌现,不在词表 —— 收编后标注器才会优先挂靠)" }),
4573
- grownOpen ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginTop: 6 }, children: visibleGrown.map((r) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
4574
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4875
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "(只在 tags 里涌现,不在词表 —— 收编后标注器才会优先挂靠)" }),
4876
+ grownOpen ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginTop: 6 }, children: visibleGrown.map((r) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
4877
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4575
4878
  "span",
4576
4879
  {
4577
4880
  style: { ...btn, cursor: hiPriv && !busy ? "grab" : "default", borderRadius: 999, padding: "2px 8px", touchAction: "none" },
@@ -4582,8 +4885,8 @@ var __defProp = Object.defineProperty;
4582
4885
  children: r.room + " · " + r.count
4583
4886
  }
4584
4887
  ),
4585
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown,原地)", onClick: () => adopt(r.room), style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }),
4586
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4888
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown,原地)", onClick: () => adopt(r.room), style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }),
4889
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4587
4890
  "button",
4588
4891
  {
4589
4892
  type: "button",
@@ -4610,8 +4913,8 @@ var __defProp = Object.defineProperty;
4610
4913
  }
4611
4914
 
4612
4915
  // client/src/tabs/RoomTab.tsx
4613
- var import_react17 = require("react");
4614
- var import_jsx_runtime17 = require("react/jsx-runtime");
4916
+ var import_react19 = require("react");
4917
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4615
4918
  var btn2 = {
4616
4919
  cursor: "pointer",
4617
4920
  fontSize: 12,
@@ -4632,22 +4935,22 @@ var __defProp = Object.defineProperty;
4632
4935
  }
4633
4936
  function RoomTab(props) {
4634
4937
  const rpc = props.rpc;
4635
- const [registry, setRegistry] = (0, import_react17.useState)([]);
4636
- const [counts, setCounts] = (0, import_react17.useState)([]);
4637
- const [orphanCount, setOrphanCount] = (0, import_react17.useState)(0);
4638
- const [busy, setBusy] = (0, import_react17.useState)(false);
4639
- const [msg, setMsg] = (0, import_react17.useState)(null);
4640
- const [error, setError] = (0, import_react17.useState)(null);
4641
- const [hiPriv, setHiPriv] = (0, import_react17.useState)(false);
4642
- const [hiPrivBusy, setHiPrivBusy] = (0, import_react17.useState)(false);
4643
- const [reg, setReg] = (0, import_react17.useState)({ slug: "", label: "", description: "" });
4644
- const [editing, setEditing] = (0, import_react17.useState)(null);
4645
- const [renaming, setRenaming] = (0, import_react17.useState)(null);
4646
- const [expanded, setExpanded] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
4647
- const [retiredFilter, setRetiredFilter] = (0, import_react17.useState)("");
4648
- const [filter, setFilter] = (0, import_react17.useState)("");
4649
- const [records, setRecords] = (0, import_react17.useState)({});
4650
- const loadAll = (0, import_react17.useCallback)(() => {
4938
+ const [registry, setRegistry] = (0, import_react19.useState)([]);
4939
+ const [counts, setCounts] = (0, import_react19.useState)([]);
4940
+ const [orphanCount, setOrphanCount] = (0, import_react19.useState)(0);
4941
+ const [busy, setBusy] = (0, import_react19.useState)(false);
4942
+ const [msg, setMsg] = (0, import_react19.useState)(null);
4943
+ const [error, setError] = (0, import_react19.useState)(null);
4944
+ const [hiPriv, setHiPriv] = (0, import_react19.useState)(false);
4945
+ const [hiPrivBusy, setHiPrivBusy] = (0, import_react19.useState)(false);
4946
+ const [reg, setReg] = (0, import_react19.useState)({ slug: "", label: "", description: "" });
4947
+ const [editing, setEditing] = (0, import_react19.useState)(null);
4948
+ const [renaming, setRenaming] = (0, import_react19.useState)(null);
4949
+ const [expanded, setExpanded] = (0, import_react19.useState)(/* @__PURE__ */ new Set());
4950
+ const [retiredFilter, setRetiredFilter] = (0, import_react19.useState)("");
4951
+ const [filter, setFilter] = (0, import_react19.useState)("");
4952
+ const [records, setRecords] = (0, import_react19.useState)({});
4953
+ const loadAll = (0, import_react19.useCallback)(() => {
4651
4954
  rpc("dsh-memory/rooms-get", {}).then((r) => {
4652
4955
  if (r && r.ok) {
4653
4956
  setCounts(r.value.rooms ?? []);
@@ -4656,13 +4959,13 @@ var __defProp = Object.defineProperty;
4656
4959
  }
4657
4960
  }).catch(() => void 0);
4658
4961
  }, [rpc]);
4659
- const loadHiPriv = (0, import_react17.useCallback)(() => {
4962
+ const loadHiPriv = (0, import_react19.useCallback)(() => {
4660
4963
  rpc("dsh-memory/settings-get", {}).then((r) => {
4661
4964
  if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
4662
4965
  }).catch(() => {
4663
4966
  });
4664
4967
  }, [rpc]);
4665
- (0, import_react17.useEffect)(() => {
4968
+ (0, import_react19.useEffect)(() => {
4666
4969
  loadAll();
4667
4970
  loadHiPriv();
4668
4971
  }, [loadAll, loadHiPriv]);
@@ -4800,11 +5103,11 @@ var __defProp = Object.defineProperty;
4800
5103
  const visibleRows = normFilter ? rows.filter(
4801
5104
  (row2) => row2.slug.toLowerCase().includes(normFilter) || (row2.entry?.label ?? "").toLowerCase().includes(normFilter) || (row2.entry?.aliases ?? []).some((a) => a.toLowerCase().includes(normFilter))
4802
5105
  ) : rows;
4803
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
4804
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8, flexWrap: "wrap" }, children: [
4805
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "Room 级管理:存在(注册/收编/退役)· 名称(显示名/说明/改名)· 条目(展开浏览) · 归类到 hall 请到「Hall」页" }),
4806
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4807
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", style: btn2, title: "导出无 Room 绑定的孤儿清单(含候选)", onClick: () => run(async () => {
5106
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
5107
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8, flexWrap: "wrap" }, children: [
5108
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: "Room 级管理:存在(注册/收编/退役)· 名称(显示名/说明/改名)· 条目(展开浏览) · 归类到 hall 请到「Hall」页" }),
5109
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.grow }),
5110
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { type: "button", style: btn2, title: "导出无 Room 绑定的孤儿清单(含候选)", onClick: () => run(async () => {
4808
5111
  const r = await rpc("dsh-memory/rooms-export", { kind: "orphans" });
4809
5112
  if (!r.ok) {
4810
5113
  setError(r.error.message);
@@ -4815,7 +5118,7 @@ var __defProp = Object.defineProperty;
4815
5118
  "导出孤儿 · ",
4816
5119
  orphanCount
4817
5120
  ] }),
4818
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5121
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4819
5122
  "button",
4820
5123
  {
4821
5124
  type: "button",
@@ -4826,35 +5129,35 @@ var __defProp = Object.defineProperty;
4826
5129
  children: hiPriv ? "高权限:开" : "高权限:关"
4827
5130
  }
4828
5131
  ),
4829
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", style: btn2, onClick: loadAll, children: "刷新" })
5132
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", style: btn2, onClick: loadAll, children: "刷新" })
4830
5133
  ] }),
4831
- editing ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4832
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: S.muted, children: [
5134
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
5135
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { style: S.muted, children: [
4833
5136
  "编辑 ",
4834
5137
  editing.slug
4835
5138
  ] }),
4836
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: editing.label, onChange: (e) => setEditing({ ...editing, label: e.target.value }), placeholder: "显示名(可中文)", disabled: busy, style: { ...S.input, width: 140, fontSize: 12 } }),
4837
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: editing.description, onChange: (e) => setEditing({ ...editing, description: e.target.value }), placeholder: "归类说明(喂标注器)", disabled: busy, style: { ...S.input, width: 200, fontSize: 12 } }),
4838
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: saveEditing, style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "保存" }),
4839
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), style: btn2, children: "取消" })
5139
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { value: editing.label, onChange: (e) => setEditing({ ...editing, label: e.target.value }), placeholder: "显示名(可中文)", disabled: busy, style: { ...S.input, width: 140, fontSize: 12 } }),
5140
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { value: editing.description, onChange: (e) => setEditing({ ...editing, description: e.target.value }), placeholder: "归类说明(喂标注器)", disabled: busy, style: { ...S.input, width: 200, fontSize: 12 } }),
5141
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, onClick: saveEditing, style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "保存" }),
5142
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), style: btn2, children: "取消" })
4840
5143
  ] }) : null,
4841
- renaming ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4842
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: S.muted, children: [
5144
+ renaming ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { position: "sticky", top: 0, zIndex: 5, background: "var(--dsh-mem-bg-inset)", display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
5145
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { style: S.muted, children: [
4843
5146
  "改名 ",
4844
5147
  renaming.slug,
4845
5148
  " →"
4846
5149
  ] }),
4847
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: renaming.to, onChange: (e) => setRenaming({ ...renaming, to: e.target.value }), placeholder: "新 slug(可两级:hall/子类)", disabled: busy, style: { ...S.input, width: 200, fontSize: 12 } }),
4848
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: previewRename, style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "预览" }),
4849
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: execRename, style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "确认执行" }),
4850
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: () => setRenaming(null), style: btn2, children: "取消" }),
4851
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "记录随迁;与反刍并发会互相覆盖,请在反刍空闲时执行。" })
5150
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { value: renaming.to, onChange: (e) => setRenaming({ ...renaming, to: e.target.value }), placeholder: "新 slug(可两级:hall/子类)", disabled: busy, style: { ...S.input, width: 200, fontSize: 12 } }),
5151
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, onClick: previewRename, style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" }, children: "预览" }),
5152
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, onClick: execRename, style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "确认执行" }),
5153
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, onClick: () => setRenaming(null), style: btn2, children: "取消" }),
5154
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: "记录随迁;与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4852
5155
  ] }) : null,
4853
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }, children: [
4854
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "退场筛选" }),
5156
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }, children: [
5157
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: "退场筛选" }),
4855
5158
  [["", "全部"], ["active", "活跃"], ["retired", "已退役"]].map(([val, label]) => {
4856
5159
  const on = retiredFilter === val;
4857
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5160
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4858
5161
  "button",
4859
5162
  {
4860
5163
  type: "button",
@@ -4874,8 +5177,8 @@ var __defProp = Object.defineProperty;
4874
5177
  val
4875
5178
  );
4876
5179
  }),
4877
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4878
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5180
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.grow }),
5181
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4879
5182
  "input",
4880
5183
  {
4881
5184
  value: filter,
@@ -4884,24 +5187,24 @@ var __defProp = Object.defineProperty;
4884
5187
  style: { ...S.input, width: 230, fontSize: 12 }
4885
5188
  }
4886
5189
  ),
4887
- filter ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", style: btn2, onClick: () => setFilter(""), children: "清除" }) : null
5190
+ filter ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", style: btn2, onClick: () => setFilter(""), children: "清除" }) : null
4888
5191
  ] }),
4889
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4890
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: reg.slug, onChange: (e) => setReg({ ...reg, slug: e.target.value }), placeholder: "slug(如 dsh-plugin 或 dsh-plugin/merge)", disabled: !hiPriv || busy, style: { ...S.input, width: 210, fontSize: 12 } }),
4891
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: reg.label, onChange: (e) => setReg({ ...reg, label: e.target.value }), placeholder: "名称(可中文)", disabled: !hiPriv || busy, style: { ...S.input, width: 110, fontSize: 12 } }),
4892
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { value: reg.description, onChange: (e) => setReg({ ...reg, description: e.target.value }), placeholder: "归类说明(喂标注器)", disabled: !hiPriv || busy, style: { ...S.input, width: 170, fontSize: 12 } }),
4893
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy || !reg.slug.trim(), onClick: register, style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy || !reg.slug.trim() ? 0.5 : 1 }, children: "注册" }),
4894
- !hiPriv && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
5192
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
5193
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { value: reg.slug, onChange: (e) => setReg({ ...reg, slug: e.target.value }), placeholder: "slug(如 dsh-plugin 或 dsh-plugin/merge)", disabled: !hiPriv || busy, style: { ...S.input, width: 210, fontSize: 12 } }),
5194
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { value: reg.label, onChange: (e) => setReg({ ...reg, label: e.target.value }), placeholder: "名称(可中文)", disabled: !hiPriv || busy, style: { ...S.input, width: 110, fontSize: 12 } }),
5195
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { value: reg.description, onChange: (e) => setReg({ ...reg, description: e.target.value }), placeholder: "归类说明(喂标注器)", disabled: !hiPriv || busy, style: { ...S.input, width: 170, fontSize: 12 } }),
5196
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: !hiPriv || busy || !reg.slug.trim(), onClick: register, style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy || !reg.slug.trim() ? 0.5 : 1 }, children: "注册" }),
5197
+ !hiPriv && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
4895
5198
  ] }),
4896
- msg ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.hint, children: msg }) : null,
4897
- error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.error, children: error }) : null,
4898
- visibleRows.length === 0 ? normFilter ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { style: S.intro, children: "无匹配的 Room。" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { style: S.intro, children: "暂无 Room:对话/反刍会从标签涌现,或在上方注册。" }) : visibleRows.map((row2) => {
5199
+ msg ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.hint, children: msg }) : null,
5200
+ error ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.error, children: error }) : null,
5201
+ visibleRows.length === 0 ? normFilter ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { style: S.intro, children: "无匹配的 Room。" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { style: S.intro, children: "暂无 Room:对话/反刍会从标签涌现,或在上方注册。" }) : visibleRows.map((row2) => {
4899
5202
  const e = row2.entry;
4900
5203
  const retired = e?.status === "retired";
4901
5204
  const grownOnly = !e;
4902
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { marginBottom: 6, border: "1px solid var(--dsh-mem-border)", borderRadius: 8 }, children: [
4903
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "4px 10px" }, children: [
4904
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5205
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { marginBottom: 6, border: "1px solid var(--dsh-mem-border)", borderRadius: 8 }, children: [
5206
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "4px 10px" }, children: [
5207
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4905
5208
  "button",
4906
5209
  {
4907
5210
  type: "button",
@@ -4911,28 +5214,28 @@ var __defProp = Object.defineProperty;
4911
5214
  children: expanded.has(row2.slug) ? "▾" : "▸"
4912
5215
  }
4913
5216
  ),
4914
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
4915
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: { fontSize: 12, opacity: retired ? 0.6 : 1 }, children: [
5217
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
5218
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { style: { fontSize: 12, opacity: retired ? 0.6 : 1 }, children: [
4916
5219
  row2.slug,
4917
5220
  e?.source === "pre-registered" ? " ⭐" : ""
4918
5221
  ] }),
4919
- e?.label ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: e.label }) : null,
4920
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: row2.count + " 条" }),
4921
- grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "(自生长未注册)" }) : null,
4922
- retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4923
- (e?.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e?.aliases ?? []).join(", ") }) : null
5222
+ e?.label ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: e.label }) : null,
5223
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: row2.count + " 条" }),
5224
+ grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: "(自生长未注册)" }) : null,
5225
+ retired ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
5226
+ (e?.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e?.aliases ?? []).join(", ") }) : null
4924
5227
  ] }),
4925
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4926
- !hiPriv || grownOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4927
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
5228
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
5229
+ !hiPriv || grownOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5230
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
4928
5231
  setEditing({ slug: row2.slug, label: e?.label ?? "", description: e?.description ?? "" });
4929
5232
  setRenaming(null);
4930
5233
  }, style: btn2, children: "编辑" }),
4931
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "改名(可两级 slug;记录随迁,旧名进别名)", onClick: () => {
5234
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, title: "改名(可两级 slug;记录随迁,旧名进别名)", onClick: () => {
4932
5235
  setRenaming({ slug: row2.slug, to: "" });
4933
5236
  setEditing(null);
4934
5237
  }, style: btn2, children: "改名" }),
4935
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5238
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4936
5239
  "button",
4937
5240
  {
4938
5241
  type: "button",
@@ -4943,29 +5246,29 @@ var __defProp = Object.defineProperty;
4943
5246
  children: retired ? "恢复" : "退役"
4944
5247
  }
4945
5248
  ),
4946
- !retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "退役并退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => retire(row2.slug, false, true), style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "退场" }) : null
5249
+ !retired ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, title: "退役并退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => retire(row2.slug, false, true), style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "退场" }) : null
4947
5250
  ] }),
4948
- grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4949
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown)", onClick: () => adopt(row2.slug), style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }),
4950
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => {
5251
+ grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5252
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown)", onClick: () => adopt(row2.slug), style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }),
5253
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => {
4951
5254
  setRenaming(null);
4952
5255
  retire(row2.slug, false, true);
4953
5256
  }, style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "退场" })
4954
5257
  ] }) : null,
4955
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(row2.slug), style: btn2, children: "导出" })
5258
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(row2.slug), style: btn2, children: "导出" })
4956
5259
  ] })
4957
5260
  ] }),
4958
- expanded.has(row2.slug) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { margin: "0 10px 6px", padding: "4px 8px", border: "1px dashed var(--dsh-mem-border)", borderRadius: 6 }, children: [
4959
- (records[row2.slug]?.items ?? []).map((it) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { fontSize: 11, padding: "3px 0", borderBottom: "1px solid var(--dsh-mem-border)" }, children: [
4960
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
5261
+ expanded.has(row2.slug) ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { margin: "0 10px 6px", padding: "4px 8px", border: "1px dashed var(--dsh-mem-border)", borderRadius: 6 }, children: [
5262
+ (records[row2.slug]?.items ?? []).map((it) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { fontSize: 11, padding: "3px 0", borderBottom: "1px solid var(--dsh-mem-border)" }, children: [
5263
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
4961
5264
  " ",
4962
- it.retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "[已退场]" }) : null,
5265
+ it.retired ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "[已退场]" }) : null,
4963
5266
  " ",
4964
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4965
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginTop: 2, wordBreak: "break-all", color: "var(--dsh-mem-text-2)", opacity: it.retired ? 0.55 : 1 }, children: it.content.length > 140 ? it.content.slice(0, 140) + "…" : it.content })
5267
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
5268
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { marginTop: 2, wordBreak: "break-all", color: "var(--dsh-mem-text-2)", opacity: it.retired ? 0.55 : 1 }, children: it.content.length > 140 ? it.content.slice(0, 140) + "…" : it.content })
4966
5269
  ] }, it.id)),
4967
- (records[row2.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.muted, children: "该 Room 暂无记忆条目。" }) : null,
4968
- (records[row2.slug]?.total ?? 0) > (records[row2.slug]?.items.length ?? 0) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { ...S.muted, fontSize: 11 }, children: "共 " + records[row2.slug].total + " 条,仅显示前 " + records[row2.slug].items.length + " 条" }) : null
5270
+ (records[row2.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.muted, children: "该 Room 暂无记忆条目。" }) : null,
5271
+ (records[row2.slug]?.total ?? 0) > (records[row2.slug]?.items.length ?? 0) ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { ...S.muted, fontSize: 11 }, children: "共 " + records[row2.slug].total + " 条,仅显示前 " + records[row2.slug].items.length + " 条" }) : null
4969
5272
  ] }) : null
4970
5273
  ] }, row2.slug);
4971
5274
  })
@@ -4973,13 +5276,13 @@ var __defProp = Object.defineProperty;
4973
5276
  }
4974
5277
 
4975
5278
  // client/src/tabs/ScenesTab.tsx
4976
- var import_react18 = require("react");
4977
- var import_jsx_runtime18 = require("react/jsx-runtime");
5279
+ var import_react20 = require("react");
5280
+ var import_jsx_runtime20 = require("react/jsx-runtime");
4978
5281
  function SceneCard(props) {
4979
5282
  const s = props.s;
4980
- const [open, setOpen] = (0, import_react18.useState)(false);
4981
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "dsh-mem-card dsh-mem-card-hover", style: S.card, children: [
4982
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5283
+ const [open, setOpen] = (0, import_react20.useState)(false);
5284
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-card dsh-mem-card-hover", style: S.card, children: [
5285
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4983
5286
  "div",
4984
5287
  {
4985
5288
  style: { ...S.sceneHead, cursor: "pointer", userSelect: "none" },
@@ -4987,23 +5290,23 @@ var __defProp = Object.defineProperty;
4987
5290
  setOpen(!open);
4988
5291
  },
4989
5292
  children: [
4990
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "dsh-mem-scene-chev", style: { transform: open ? "rotate(90deg)" : "none" }, children: "▸" }),
4991
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.sceneTitle, children: s.path }),
4992
- s.heat ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "热度 " + s.heat }) : null,
4993
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
4994
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "更新 " + fmtTime(s.updated) })
5293
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "dsh-mem-scene-chev", style: { transform: open ? "rotate(90deg)" : "none" }, children: "▸" }),
5294
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: S.sceneTitle, children: s.path }),
5295
+ s.heat ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: S.muted, children: "热度 " + s.heat }) : null,
5296
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: S.grow }),
5297
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: S.muted, children: "更新 " + fmtTime(s.updated) })
4995
5298
  ]
4996
5299
  }
4997
5300
  ),
4998
- s.summary ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { ...S.muted, marginBottom: 6 }, children: s.summary }) : null,
4999
- open ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("pre", { style: S.pre, children: s.content || "(空)" }) : null
5301
+ s.summary ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { ...S.muted, marginBottom: 6 }, children: s.summary }) : null,
5302
+ open ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("pre", { style: S.pre, children: s.content || "(空)" }) : null
5000
5303
  ] });
5001
5304
  }
5002
5305
  function ScenesTab(props) {
5003
5306
  const rpc = props.rpc;
5004
- const [items, setItems] = (0, import_react18.useState)(null);
5005
- const [error, setError] = (0, import_react18.useState)(null);
5006
- const load = (0, import_react18.useCallback)(() => {
5307
+ const [items, setItems] = (0, import_react20.useState)(null);
5308
+ const [error, setError] = (0, import_react20.useState)(null);
5309
+ const load = (0, import_react20.useCallback)(() => {
5007
5310
  rpc("dsh-memory/scenes", {}).then((r) => {
5008
5311
  if (r && r.ok) {
5009
5312
  setItems(r.value.items);
@@ -5013,25 +5316,25 @@ var __defProp = Object.defineProperty;
5013
5316
  setError(String(e && e.message || e));
5014
5317
  });
5015
5318
  }, [rpc]);
5016
- (0, import_react18.useEffect)(() => {
5319
+ (0, import_react20.useEffect)(() => {
5017
5320
  load();
5018
5321
  }, [load]);
5019
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
5020
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
5021
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: items ? items.length + " 个场景块" : "加载中…" }),
5022
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
5023
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NButton, { onClick: load, children: "刷新" })
5322
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
5323
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
5324
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: S.muted, children: items ? items.length + " 个场景块" : "加载中…" }),
5325
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: S.grow }),
5326
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(NButton, { onClick: load, children: "刷新" })
5024
5327
  ] }),
5025
- error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.error, children: error }) : null,
5026
- items && items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { style: S.intro, children: "暂无场景块。累计 5 条新记忆后 L2 会自动整合出第一个场景。" }) : null,
5328
+ error ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: S.error, children: error }) : null,
5329
+ items && items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { style: S.intro, children: "暂无场景块。累计 5 条新记忆后 L2 会自动整合出第一个场景。" }) : null,
5027
5330
  (items || []).map((s) => {
5028
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SceneCard, { s }, s.path);
5331
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SceneCard, { s }, s.path);
5029
5332
  })
5030
5333
  ] });
5031
5334
  }
5032
5335
 
5033
5336
  // client/src/panel.tsx
5034
- var import_jsx_runtime19 = require("react/jsx-runtime");
5337
+ var import_jsx_runtime21 = require("react/jsx-runtime");
5035
5338
  var TABS = [
5036
5339
  ["overview", "概览"],
5037
5340
  ["records", "记忆"],
@@ -5041,30 +5344,71 @@ var __defProp = Object.defineProperty;
5041
5344
  ["room", "Room"],
5042
5345
  ["persona", "画像"],
5043
5346
  ["cost", "成本"],
5044
- ["log", "日志"]
5347
+ ["log", "日志"],
5348
+ ["connect", "来源"],
5349
+ ["skills", "技能"]
5350
+ ];
5351
+ var SKIN_OPTS = [
5352
+ ["light", "浅色"],
5353
+ ["dark", "深色"],
5354
+ ["auto", "跟随"]
5045
5355
  ];
5046
5356
  function MemoryPanel(props) {
5047
5357
  const rpc = props.rpc;
5048
- const [tab, setTab] = (0, import_react19.useState)("overview");
5049
- ensureThemeStyle();
5050
- (0, import_react19.useEffect)(() => {
5358
+ const [tab, setTab] = (0, import_react21.useState)("overview");
5359
+ const [skin, setSkinState] = (0, import_react21.useState)(() => getSkin());
5360
+ ensureThemeStyle(skin);
5361
+ (0, import_react21.useEffect)(() => {
5362
+ applySkin(skin);
5051
5363
  watchSidebarIcon();
5052
- }, []);
5364
+ }, [skin]);
5365
+ const switchSkin = (next) => {
5366
+ setSkin(next);
5367
+ setSkinState(next);
5368
+ };
5053
5369
  let body;
5054
- if (tab === "overview") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(OverviewTab, { rpc });
5055
- else if (tab === "records") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RecordsTab, { rpc });
5056
- else if (tab === "conflicts") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConflictsTab, { rpc });
5057
- else if (tab === "scenes") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ScenesTab, { rpc });
5058
- else if (tab === "rooms") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RoomsTab, { rpc });
5059
- else if (tab === "room") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RoomTab, { rpc });
5060
- else if (tab === "persona") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PersonaTab, { rpc });
5061
- else if (tab === "cost") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CostTab, { rpc });
5062
- else body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LogTab, { rpc });
5063
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "dsh-mem-root", style: S.section, children: [
5064
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { style: S.heading, children: "记忆 (Memory)" }),
5065
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { style: S.intro, children: "L0~L3 分层蒸馏记忆:浏览被记住的内容,控制记忆模式开关。" }),
5066
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.tabbar, children: TABS.map((t) => {
5067
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5370
+ if (tab === "overview") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OverviewTab, { rpc });
5371
+ else if (tab === "records") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RecordsTab, { rpc });
5372
+ else if (tab === "conflicts") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ConflictsTab, { rpc });
5373
+ else if (tab === "scenes") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ScenesTab, { rpc });
5374
+ else if (tab === "rooms") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RoomsTab, { rpc });
5375
+ else if (tab === "room") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(RoomTab, { rpc });
5376
+ else if (tab === "persona") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PersonaTab, { rpc });
5377
+ else if (tab === "cost") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CostTab, { rpc });
5378
+ else if (tab === "connect") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ConnectTab, { rpc });
5379
+ else if (tab === "skills") body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SkillsTab, { rpc });
5380
+ else body = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LogTab, { rpc });
5381
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "dsh-mem-root", style: S.section, children: [
5382
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { style: S.heading, children: "记忆 (Memory)" }),
5383
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { style: S.intro, children: "L0~L3 分层蒸馏记忆:浏览被记住的内容,控制记忆模式开关。" }),
5384
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
5385
+ "div",
5386
+ {
5387
+ style: {
5388
+ display: "flex",
5389
+ justifyContent: "flex-end",
5390
+ alignItems: "center",
5391
+ gap: 2,
5392
+ marginTop: -6,
5393
+ marginBottom: 10
5394
+ },
5395
+ children: [
5396
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)", marginRight: 4 }, children: "皮肤" }),
5397
+ SKIN_OPTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5398
+ "button",
5399
+ {
5400
+ className: skin === s[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
5401
+ onClick: () => switchSkin(s[0]),
5402
+ title: `皮肤:${s[1]}`,
5403
+ children: s[1]
5404
+ },
5405
+ s[0]
5406
+ ))
5407
+ ]
5408
+ }
5409
+ ),
5410
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: S.tabbar, children: TABS.map((t) => {
5411
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5068
5412
  "button",
5069
5413
  {
5070
5414
  className: tab === t[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
@@ -5081,7 +5425,7 @@ var __defProp = Object.defineProperty;
5081
5425
  }
5082
5426
 
5083
5427
  // client/src/pill/MemoryModePill.tsx
5084
- var import_react22 = require("react");
5428
+ var import_react25 = require("react");
5085
5429
 
5086
5430
  // src/util/context-occupancy.ts
5087
5431
  var CONTEXT_METER_CIRCUMFERENCE = 34.55751918948772;
@@ -5397,23 +5741,23 @@ var __defProp = Object.defineProperty;
5397
5741
  }
5398
5742
 
5399
5743
  // client/src/pill/WingWheel.tsx
5400
- var import_react21 = require("react");
5744
+ var import_react24 = require("react");
5401
5745
 
5402
5746
  // client/src/pill/SessionInfoArea.tsx
5403
- var import_react20 = require("react");
5404
- var import_jsx_runtime20 = require("react/jsx-runtime");
5747
+ var import_react22 = require("react");
5748
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5405
5749
  function sinfoCell(val, label, title) {
5406
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { title: title || void 0, children: [
5407
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-val", children: val }),
5408
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-label", children: label })
5750
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { title: title || void 0, children: [
5751
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "dsh-mem-sinfo-val", children: val }),
5752
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "dsh-mem-sinfo-label", children: label })
5409
5753
  ] });
5410
5754
  }
5411
5755
  function SessionInfoArea(props) {
5412
5756
  const rpc = props.rpc;
5413
5757
  const sessionId = props.sessionId;
5414
- const [stats, setStats] = (0, import_react20.useState)(void 0);
5415
- const busyRef = (0, import_react20.useRef)(false);
5416
- (0, import_react20.useEffect)(() => {
5758
+ const [stats, setStats] = (0, import_react22.useState)(void 0);
5759
+ const busyRef = (0, import_react22.useRef)(false);
5760
+ (0, import_react22.useEffect)(() => {
5417
5761
  if (!rpc || !sessionId) return void 0;
5418
5762
  let alive = true;
5419
5763
  let timer = null;
@@ -5446,7 +5790,7 @@ var __defProp = Object.defineProperty;
5446
5790
  }, [rpc, sessionId]);
5447
5791
  if (stats === null) return null;
5448
5792
  if (stats === void 0) {
5449
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
5793
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "dsh-mem-sinfo", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
5450
5794
  sinfoCell("…", "召回命中"),
5451
5795
  sinfoCell("…", "攒批进度"),
5452
5796
  sinfoCell("…", "本会话记忆"),
@@ -5495,21 +5839,104 @@ var __defProp = Object.defineProperty;
5495
5839
  else if (stats.retrieval === "none") note = "检索不可用(FTS 与向量均失效)";
5496
5840
  }
5497
5841
  const ago = fmtAgo(gl.lastExtractAt);
5498
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-sinfo", children: [
5499
- warn ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-warn", children: warn }) : null,
5500
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
5842
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "dsh-mem-sinfo", children: [
5843
+ warn ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "dsh-mem-sinfo-warn", children: warn }) : null,
5844
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
5501
5845
  sinfoCell(rcVal, rcLabel, rcTitle),
5502
5846
  sinfoCell(dVal, dLabel, dTitle),
5503
5847
  sinfoCell(String(di.producedRecords || 0), "本会话记忆", pTitle),
5504
5848
  sinfoCell(stats.l0Count != null ? String(stats.l0Count) : "…", "会话消息")
5505
5849
  ] }),
5506
- note ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-note", children: note }) : null,
5507
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-sum", children: "待蒸馏 " + (gl.pendingTotal || 0) + " · 上次蒸馏 " + (ago || "尚未蒸馏") })
5850
+ note ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "dsh-mem-sinfo-note", children: note }) : null,
5851
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "dsh-mem-sinfo-sum", children: "待蒸馏 " + (gl.pendingTotal || 0) + " · 上次蒸馏 " + (ago || "尚未蒸馏") })
5852
+ ] });
5853
+ }
5854
+
5855
+ // client/src/pill/SessionRecalledArea.tsx
5856
+ var import_react23 = require("react");
5857
+ var import_jsx_runtime23 = require("react/jsx-runtime");
5858
+ var SHOW_CAP = 6;
5859
+ var POLL_MS2 = 4e3;
5860
+ var MATURITY_LABEL = { high: "成熟·高", mid: "成熟·中", low: "成熟·低" };
5861
+ function SessionRecalledArea(props) {
5862
+ const rpc = props.rpc;
5863
+ const sessionId = props.sessionId;
5864
+ const [records, setRecords] = (0, import_react23.useState)(void 0);
5865
+ (0, import_react23.useEffect)(() => {
5866
+ if (!rpc || !sessionId) return void 0;
5867
+ let alive = true;
5868
+ let timer = null;
5869
+ let seq = 0;
5870
+ const tick = () => {
5871
+ const token = ++seq;
5872
+ rpc("dsh-memory/session-recalled", { sessionId }).then((r) => {
5873
+ if (!alive || token !== seq) return;
5874
+ if (r && r.ok && r.value) setRecords(r.value.records);
5875
+ }).catch(() => {
5876
+ }).then(() => {
5877
+ if (alive) timer = setTimeout(tick, POLL_MS2);
5878
+ });
5879
+ };
5880
+ tick();
5881
+ return () => {
5882
+ alive = false;
5883
+ if (timer) clearTimeout(timer);
5884
+ };
5885
+ }, [rpc, sessionId]);
5886
+ if (records === void 0) return null;
5887
+ const recentFirst = [...records].reverse();
5888
+ const shown = recentFirst.slice(0, SHOW_CAP);
5889
+ const hidden = recentFirst.length - shown.length;
5890
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "dsh-mem-sinfo", children: [
5891
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5892
+ "div",
5893
+ {
5894
+ style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 6 },
5895
+ title: "本会话召回注入、且目前仍在上下文里的记忆(压缩/清空后自动消失,不是常驻)",
5896
+ children: "本会话召回 " + records.length + " 条"
5897
+ }
5898
+ ),
5899
+ records.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", lineHeight: "16px" }, children: "还没召回到记忆(聊几轮后按相关性自动浮现)" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5900
+ shown.map((r) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: rowStyle, title: rowTitle(r), children: [
5901
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5902
+ "span",
5903
+ {
5904
+ className: "dsh-mem-tag dsh-mem-tag-" + r.type,
5905
+ style: { flexShrink: 0, whiteSpace: "nowrap" },
5906
+ children: TYPE_LABELS[r.type] || r.type
5907
+ }
5908
+ ),
5909
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "dsh-mem-sinfo-val", style: textStyle, children: r.content }),
5910
+ r.maturity ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { fontSize: 10, color: "var(--dsh-mem-text-3)", whiteSpace: "nowrap", flexShrink: 0 }, children: MATURITY_LABEL[r.maturity] }) : null
5911
+ ] }, r.id)),
5912
+ hidden > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "+ " + hidden + " 条更早召回(详见面板「记忆」页)" }) : null
5913
+ ] })
5508
5914
  ] });
5509
5915
  }
5916
+ var rowStyle = {
5917
+ display: "flex",
5918
+ alignItems: "flex-start",
5919
+ gap: 6,
5920
+ lineHeight: "16px"
5921
+ };
5922
+ var textStyle = {
5923
+ fontSize: 12,
5924
+ lineHeight: "16px",
5925
+ fontWeight: 400,
5926
+ minWidth: 0,
5927
+ display: "-webkit-box",
5928
+ WebkitLineClamp: 2,
5929
+ WebkitBoxOrient: "vertical",
5930
+ overflow: "hidden"
5931
+ };
5932
+ function rowTitle(r) {
5933
+ const head = "[" + (TYPE_LABELS[r.type] || r.type) + "] " + r.content;
5934
+ const sc = r.usageScenario && r.usageScenario.length > 0 ? "\n使用场景:" + r.usageScenario.join(";") : "";
5935
+ return head + sc;
5936
+ }
5510
5937
 
5511
5938
  // client/src/pill/WingWheel.tsx
5512
- var import_jsx_runtime21 = require("react/jsx-runtime");
5939
+ var import_jsx_runtime24 = require("react/jsx-runtime");
5513
5940
  var SIZE = 196;
5514
5941
  var CENTER = SIZE / 2;
5515
5942
  var R = 64;
@@ -5552,17 +5979,17 @@ var __defProp = Object.defineProperty;
5552
5979
  function WingWheel(props) {
5553
5980
  ensureThemeStyle();
5554
5981
  const isOff = props.mode === "off";
5555
- const [overview, setOverview] = (0, import_react21.useState)(null);
5556
- const [backfillBusy, setBackfillBusy] = (0, import_react21.useState)(false);
5557
- const [localError, setLocalError] = (0, import_react21.useState)(null);
5558
- const timersRef = (0, import_react21.useRef)([]);
5559
- (0, import_react21.useEffect)(
5982
+ const [overview, setOverview] = (0, import_react24.useState)(null);
5983
+ const [backfillBusy, setBackfillBusy] = (0, import_react24.useState)(false);
5984
+ const [localError, setLocalError] = (0, import_react24.useState)(null);
5985
+ const timersRef = (0, import_react24.useRef)([]);
5986
+ (0, import_react24.useEffect)(
5560
5987
  () => () => {
5561
5988
  for (const t of timersRef.current) window.clearTimeout(t);
5562
5989
  },
5563
5990
  []
5564
5991
  );
5565
- (0, import_react21.useEffect)(() => {
5992
+ (0, import_react24.useEffect)(() => {
5566
5993
  let alive = true;
5567
5994
  props.rpc("dsh-memory/wing-overview", {}).then((r) => {
5568
5995
  if (!alive) return;
@@ -5608,28 +6035,28 @@ var __defProp = Object.defineProperty;
5608
6035
  finish();
5609
6036
  });
5610
6037
  };
5611
- const boxRef = (0, import_react21.useRef)(null);
5612
- const startRef = (0, import_react21.useRef)(null);
5613
- const movedRef = (0, import_react21.useRef)(false);
5614
- const downIndexRef = (0, import_react21.useRef)(null);
5615
- const dragTargetRef = (0, import_react21.useRef)(null);
5616
- const [drag, setDrag] = (0, import_react21.useState)(null);
6038
+ const boxRef = (0, import_react24.useRef)(null);
6039
+ const startRef = (0, import_react24.useRef)(null);
6040
+ const movedRef = (0, import_react24.useRef)(false);
6041
+ const downIndexRef = (0, import_react24.useRef)(null);
6042
+ const dragTargetRef = (0, import_react24.useRef)(null);
6043
+ const [drag, setDrag] = (0, import_react24.useState)(null);
5617
6044
  const lockedIndex = (() => {
5618
6045
  if (props.halls.length !== 1 || !overview) return null;
5619
6046
  const i = overview.corners.findIndex((c) => c.id === props.halls[0]);
5620
6047
  return i >= 0 ? i : null;
5621
6048
  })();
5622
6049
  const activeCorner = drag ? drag.index : lockedIndex;
5623
- const blockPosRef = (0, import_react21.useRef)(
6050
+ const blockPosRef = (0, import_react24.useRef)(
5624
6051
  activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER }
5625
6052
  );
5626
- const blockTargetRef = (0, import_react21.useRef)(blockPosRef.current);
5627
- const [blockPos, setBlockPos] = (0, import_react21.useState)(blockPosRef.current);
5628
- (0, import_react21.useEffect)(() => {
6053
+ const blockTargetRef = (0, import_react24.useRef)(blockPosRef.current);
6054
+ const [blockPos, setBlockPos] = (0, import_react24.useState)(blockPosRef.current);
6055
+ (0, import_react24.useEffect)(() => {
5629
6056
  blockTargetRef.current = activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER };
5630
6057
  }, [activeCorner]);
5631
- const reducedMotionRef = (0, import_react21.useRef)(false);
5632
- (0, import_react21.useEffect)(() => {
6058
+ const reducedMotionRef = (0, import_react24.useRef)(false);
6059
+ (0, import_react24.useEffect)(() => {
5633
6060
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
5634
6061
  const apply2 = () => {
5635
6062
  reducedMotionRef.current = mq.matches;
@@ -5638,7 +6065,7 @@ var __defProp = Object.defineProperty;
5638
6065
  mq.addEventListener("change", apply2);
5639
6066
  return () => mq.removeEventListener("change", apply2);
5640
6067
  }, []);
5641
- (0, import_react21.useEffect)(() => {
6068
+ (0, import_react24.useEffect)(() => {
5642
6069
  let raf = 0;
5643
6070
  const loop = () => {
5644
6071
  const cur = blockPosRef.current;
@@ -5727,8 +6154,8 @@ var __defProp = Object.defineProperty;
5727
6154
  }).join(" ");
5728
6155
  const grayStyle = isOff ? { opacity: 0.45, pointerEvents: "none" } : {};
5729
6156
  const boundariesDisabled = props.halls.length === 0 || isOff;
5730
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { width: SIZE }, children: [
5731
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6157
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { width: SIZE }, children: [
6158
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5732
6159
  "div",
5733
6160
  {
5734
6161
  ref: boxRef,
@@ -5738,7 +6165,7 @@ var __defProp = Object.defineProperty;
5738
6165
  onPointerUp: onBoxUp,
5739
6166
  onPointerCancel: onBoxUp,
5740
6167
  children: [
5741
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6168
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5742
6169
  "svg",
5743
6170
  {
5744
6171
  width: SIZE,
@@ -5746,7 +6173,7 @@ var __defProp = Object.defineProperty;
5746
6173
  style: { position: "absolute", inset: 0, pointerEvents: "none" },
5747
6174
  "aria-hidden": "true",
5748
6175
  children: [
5749
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6176
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5750
6177
  "polygon",
5751
6178
  {
5752
6179
  points: polyPoints,
@@ -5757,7 +6184,7 @@ var __defProp = Object.defineProperty;
5757
6184
  opacity: 0.45
5758
6185
  }
5759
6186
  ),
5760
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6187
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5761
6188
  "polygon",
5762
6189
  {
5763
6190
  points: polyPoints,
@@ -5768,7 +6195,7 @@ var __defProp = Object.defineProperty;
5768
6195
  ),
5769
6196
  [0, 1, 2, 3, 4, 5, 6, 7].map((i) => {
5770
6197
  const p = cornerPos(i);
5771
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6198
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5772
6199
  "line",
5773
6200
  {
5774
6201
  x1: CENTER,
@@ -5784,7 +6211,7 @@ var __defProp = Object.defineProperty;
5784
6211
  ]
5785
6212
  }
5786
6213
  ),
5787
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6214
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5788
6215
  "div",
5789
6216
  {
5790
6217
  "aria-hidden": "true",
@@ -5803,7 +6230,7 @@ var __defProp = Object.defineProperty;
5803
6230
  }
5804
6231
  }
5805
6232
  ),
5806
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6233
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5807
6234
  "button",
5808
6235
  {
5809
6236
  type: "button",
@@ -5837,7 +6264,7 @@ var __defProp = Object.defineProperty;
5837
6264
  const dim = activeCorner != null && !active;
5838
6265
  const empty = count === 0;
5839
6266
  const p = cornerPos(i);
5840
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6267
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5841
6268
  "div",
5842
6269
  {
5843
6270
  title: id ? `「${label}」${count ?? 0} 条
@@ -5866,8 +6293,8 @@ var __defProp = Object.defineProperty;
5866
6293
  zIndex: 2
5867
6294
  },
5868
6295
  children: [
5869
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: label }),
5870
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 8, opacity: 0.75, fontVariantNumeric: "tabular-nums" }, children: count === null ? " " : empty ? "空角" : `${count} 条` })
6296
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: label }),
6297
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 8, opacity: 0.75, fontVariantNumeric: "tabular-nums" }, children: count === null ? " " : empty ? "空角" : `${count} 条` })
5871
6298
  ]
5872
6299
  },
5873
6300
  id ?? i
@@ -5876,7 +6303,7 @@ var __defProp = Object.defineProperty;
5876
6303
  ]
5877
6304
  }
5878
6305
  ),
5879
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6306
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5880
6307
  "div",
5881
6308
  {
5882
6309
  title: props.halls.length === 0 ? "选定某个角(单 Wing)后,这两项才生效" : "锁定 Wing 时两类无角记忆是否参与召回",
@@ -5890,9 +6317,9 @@ var __defProp = Object.defineProperty;
5890
6317
  pointerEvents: boundariesDisabled ? "none" : void 0
5891
6318
  },
5892
6319
  children: [
5893
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `另有 ${overview.unlabeled} 条未打标` : "未打标" }),
5894
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5895
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6320
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `另有 ${overview.unlabeled} 条未打标` : "未打标" }),
6321
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
6322
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5896
6323
  Segmented,
5897
6324
  {
5898
6325
  value: props.hallIncludeUnlabeled ? "in" : "ex",
@@ -5903,7 +6330,7 @@ var __defProp = Object.defineProperty;
5903
6330
  onChange: (key) => props.onCommitWingBoundaries({ includeUnlabeled: key === "in" })
5904
6331
  }
5905
6332
  ),
5906
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6333
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5907
6334
  Segmented,
5908
6335
  {
5909
6336
  value: props.hallIncludeGeneral ? "in" : "ex",
@@ -5918,7 +6345,7 @@ var __defProp = Object.defineProperty;
5918
6345
  ]
5919
6346
  }
5920
6347
  ),
5921
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Row, { label: "会话", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6348
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Row, { label: "会话", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5922
6349
  Segmented,
5923
6350
  {
5924
6351
  value: isOff ? "off" : "on",
@@ -5933,7 +6360,7 @@ var __defProp = Object.defineProperty;
5933
6360
  onChange: (key) => props.onCommit(key === "off" ? "off" : "auto")
5934
6361
  }
5935
6362
  ) }),
5936
- props.recall !== void 0 && props.onCommitRecall ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Row, { label: "注入", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6363
+ props.recall !== void 0 && props.onCommitRecall ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Row, { label: "注入", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5937
6364
  Segmented,
5938
6365
  {
5939
6366
  value: props.recall === null ? "follow" : props.recall ? "on" : "off",
@@ -5946,7 +6373,7 @@ var __defProp = Object.defineProperty;
5946
6373
  onChange: (key) => props.onCommitRecall(key === "on" ? true : key === "off" ? false : null)
5947
6374
  }
5948
6375
  ) }) : null,
5949
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6376
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5950
6377
  "div",
5951
6378
  {
5952
6379
  style: {
@@ -5957,8 +6384,8 @@ var __defProp = Object.defineProperty;
5957
6384
  marginTop: 10
5958
6385
  },
5959
6386
  children: [
5960
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `未打标 ${overview.unlabeled} 条` : "未打标计数加载中" }),
5961
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6387
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `未打标 ${overview.unlabeled} 条` : "未打标计数加载中" }),
6388
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5962
6389
  ActionButton,
5963
6390
  {
5964
6391
  label: backfillBusy ? "回填中…" : "回填",
@@ -5970,12 +6397,13 @@ var __defProp = Object.defineProperty;
5970
6397
  ]
5971
6398
  }
5972
6399
  ),
5973
- props.error || localError ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-danger)", marginTop: 8 }, children: props.error || localError }) : null,
5974
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SessionInfoArea, { rpc: props.rpc, sessionId: props.sessionId })
6400
+ props.error || localError ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-danger)", marginTop: 8 }, children: props.error || localError }) : null,
6401
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SessionRecalledArea, { rpc: props.rpc, sessionId: props.sessionId }),
6402
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SessionInfoArea, { rpc: props.rpc, sessionId: props.sessionId })
5975
6403
  ] });
5976
6404
  }
5977
6405
  function Row(props) {
5978
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6406
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5979
6407
  "div",
5980
6408
  {
5981
6409
  style: {
@@ -5985,7 +6413,7 @@ var __defProp = Object.defineProperty;
5985
6413
  marginTop: 10
5986
6414
  },
5987
6415
  children: [
5988
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: props.label }),
6416
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: props.label }),
5989
6417
  props.children
5990
6418
  ]
5991
6419
  }
@@ -5993,9 +6421,9 @@ var __defProp = Object.defineProperty;
5993
6421
  }
5994
6422
  var LABEL_FALLBACK = ["工作", "人际", "学习", "创作娱乐", "居家", "健康", "财务", "出行"];
5995
6423
  function useViewportClamp(popRef) {
5996
- const shiftRef = (0, import_react21.useRef)(0);
5997
- const [shiftX, setShiftX] = (0, import_react21.useState)(0);
5998
- (0, import_react21.useEffect)(() => {
6424
+ const shiftRef = (0, import_react24.useRef)(0);
6425
+ const [shiftX, setShiftX] = (0, import_react24.useState)(0);
6426
+ (0, import_react24.useEffect)(() => {
5999
6427
  const clamp = () => {
6000
6428
  const el = popRef.current;
6001
6429
  if (!el) return;
@@ -6025,24 +6453,24 @@ var __defProp = Object.defineProperty;
6025
6453
  }
6026
6454
 
6027
6455
  // client/src/pill/MemoryModePill.tsx
6028
- var import_jsx_runtime22 = require("react/jsx-runtime");
6456
+ var import_jsx_runtime25 = require("react/jsx-runtime");
6029
6457
  function MemoryModePill(props) {
6030
6458
  const rpc = props.rpc;
6031
6459
  const sessionId = props.sessionId || props.session && props.session.sessionId;
6032
- const [mode, setMode] = (0, import_react22.useState)(null);
6033
- const [recall, setRecall] = (0, import_react22.useState)(null);
6034
- const [recallResolved, setRecallResolved] = (0, import_react22.useState)(true);
6035
- const [halls, setWings] = (0, import_react22.useState)([]);
6036
- const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0, import_react22.useState)(true);
6037
- const [hallIncludeGeneral, setWingIncludeGeneral] = (0, import_react22.useState)(false);
6038
- const [wingLabels, setHallLabels] = (0, import_react22.useState)({});
6039
- const [error, setError] = (0, import_react22.useState)(null);
6040
- const [open, setOpen] = (0, import_react22.useState)(false);
6041
- const wrapRef = (0, import_react22.useRef)(null);
6042
- const popRef = (0, import_react22.useRef)(null);
6460
+ const [mode, setMode] = (0, import_react25.useState)(null);
6461
+ const [recall, setRecall] = (0, import_react25.useState)(null);
6462
+ const [recallResolved, setRecallResolved] = (0, import_react25.useState)(true);
6463
+ const [halls, setWings] = (0, import_react25.useState)([]);
6464
+ const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0, import_react25.useState)(true);
6465
+ const [hallIncludeGeneral, setWingIncludeGeneral] = (0, import_react25.useState)(false);
6466
+ const [wingLabels, setHallLabels] = (0, import_react25.useState)({});
6467
+ const [error, setError] = (0, import_react25.useState)(null);
6468
+ const [open, setOpen] = (0, import_react25.useState)(false);
6469
+ const wrapRef = (0, import_react25.useRef)(null);
6470
+ const popRef = (0, import_react25.useRef)(null);
6043
6471
  const shiftX = useViewportClamp(popRef);
6044
- const seqRef = (0, import_react22.useRef)(0);
6045
- const load = (0, import_react22.useCallback)(() => {
6472
+ const seqRef = (0, import_react25.useRef)(0);
6473
+ const load = (0, import_react25.useCallback)(() => {
6046
6474
  if (!sessionId || !rpc) return;
6047
6475
  const token = ++seqRef.current;
6048
6476
  setError(null);
@@ -6067,13 +6495,13 @@ var __defProp = Object.defineProperty;
6067
6495
  }).catch(() => {
6068
6496
  });
6069
6497
  }, [sessionId, rpc]);
6070
- (0, import_react22.useEffect)(() => {
6498
+ (0, import_react25.useEffect)(() => {
6071
6499
  load();
6072
6500
  }, [load]);
6073
- (0, import_react22.useEffect)(() => {
6501
+ (0, import_react25.useEffect)(() => {
6074
6502
  watchSidebarIcon();
6075
6503
  }, []);
6076
- (0, import_react22.useEffect)(() => {
6504
+ (0, import_react25.useEffect)(() => {
6077
6505
  initOccupancyIndicator(
6078
6506
  (endpoint, payload) => rpc(endpoint, payload)
6079
6507
  );
@@ -6081,7 +6509,7 @@ var __defProp = Object.defineProperty;
6081
6509
  watchContextMeter();
6082
6510
  noteOccupancySession(sessionId ?? null);
6083
6511
  }, [sessionId, rpc]);
6084
- (0, import_react22.useEffect)(() => {
6512
+ (0, import_react25.useEffect)(() => {
6085
6513
  if (!open) return;
6086
6514
  const onDown = (e) => {
6087
6515
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -6096,7 +6524,7 @@ var __defProp = Object.defineProperty;
6096
6524
  document.removeEventListener("keydown", onKey);
6097
6525
  };
6098
6526
  }, [open]);
6099
- const commit = (next) => {
6527
+ const commit2 = (next) => {
6100
6528
  if (!rpc || !sessionId || mode === null || next === mode) return;
6101
6529
  const prev = mode;
6102
6530
  const token = seqRef.current;
@@ -6221,8 +6649,8 @@ var __defProp = Object.defineProperty;
6221
6649
  pillStyle.boxShadow = "0 0 12px color-mix(in srgb, " + info.color + " 30%, transparent)";
6222
6650
  pillStyle["--dsh-mem-pill-tint"] = info.color;
6223
6651
  }
6224
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
6225
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
6652
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
6653
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6226
6654
  "button",
6227
6655
  {
6228
6656
  type: "button",
@@ -6235,11 +6663,11 @@ var __defProp = Object.defineProperty;
6235
6663
  style: pillStyle,
6236
6664
  children: [
6237
6665
  "记忆 · ",
6238
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: faceLabel })
6666
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: faceLabel })
6239
6667
  ]
6240
6668
  }
6241
6669
  ),
6242
- open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6670
+ open ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6243
6671
  "div",
6244
6672
  {
6245
6673
  ref: popRef,
@@ -6250,19 +6678,19 @@ var __defProp = Object.defineProperty;
6250
6678
  transform: "translateX(calc(-50% + " + shiftX + "px))",
6251
6679
  zIndex: 1e3
6252
6680
  },
6253
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6681
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6254
6682
  "div",
6255
6683
  {
6256
6684
  className: "dsh-mem-popover",
6257
6685
  style: { position: "relative", padding: "10px 12px" },
6258
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6686
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6259
6687
  WingWheel,
6260
6688
  {
6261
6689
  mode: mode || "auto",
6262
6690
  halls,
6263
6691
  hallIncludeUnlabeled,
6264
6692
  hallIncludeGeneral,
6265
- onCommit: commit,
6693
+ onCommit: commit2,
6266
6694
  onCommitWing: commitWing,
6267
6695
  onCommitWingBoundaries: commitWingBoundaries,
6268
6696
  recall: loaded ? recall : void 0,