dsh-prime-memory 0.21.0 → 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 (63) hide show
  1. package/dist/client.js +1581 -1150
  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 +21 -2
  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/projection/slots.js +9 -2
  34. package/dist/prompts/graph-projection.js +3 -0
  35. package/dist/prompts/l1-extraction.d.ts +4 -1
  36. package/dist/prompts/l1-extraction.js +27 -4
  37. package/dist/settings.js +106 -11
  38. package/dist/skills/crystallize.d.ts +41 -0
  39. package/dist/skills/crystallize.js +75 -0
  40. package/dist/stats.d.ts +18 -1
  41. package/dist/stats.js +184 -5
  42. package/dist/store/file-versions.d.ts +2 -0
  43. package/dist/store/file-versions.js +2 -0
  44. package/dist/store/l1.d.ts +2 -0
  45. package/dist/store/l1.js +4 -0
  46. package/dist/store/memory-sources.d.ts +59 -0
  47. package/dist/store/memory-sources.js +214 -0
  48. package/dist/store/session-recalled.d.ts +25 -0
  49. package/dist/store/session-recalled.js +163 -0
  50. package/dist/store/skill-store.d.ts +99 -0
  51. package/dist/store/skill-store.js +330 -0
  52. package/dist/store/sqlite.d.ts +5 -0
  53. package/dist/store/sqlite.js +30 -4
  54. package/dist/tools/index.d.ts +0 -13
  55. package/dist/tools/index.js +58 -7
  56. package/dist/tools/skills.d.ts +13 -0
  57. package/dist/tools/skills.js +130 -0
  58. package/dist/types.d.ts +6 -0
  59. package/dist/util/maturity.d.ts +27 -0
  60. package/dist/util/maturity.js +26 -0
  61. package/dist/wing-backfill.js +7 -4
  62. package/dsh.plugin.json +2 -2
  63. package/package.json +14 -14
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,272 +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-specific-menu, var(--dsw-alias-bg-layer-3, #ffffff));",
233
- " --dsh-mem-border: var(--dsw-alias-border-l2, rgba(0,0,0,0.10));",
234
- " --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(0,0,0,0.12));",
235
- " --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(0,0,0,0));",
236
- " --dsh-mem-text-1: var(--dsw-alias-label-primary, #0f1115);",
237
- " --dsh-mem-text-2: var(--dsw-alias-label-secondary, #61666b);",
238
- " --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #6e7781);",
239
- " --dsh-mem-danger: var(--dsw-alias-state-error-primary, #d0403f);",
240
- // 成本折线图系列色:8 档固定色;PALETTE 只引用 var(),1 档锚品牌蓝、8 档中性"其他"
241
- " --dsh-mem-chart-1: #4d6bfe;",
242
- " --dsh-mem-chart-2: #0e9c8f;",
243
- " --dsh-mem-chart-3: #1f9d55;",
244
- " --dsh-mem-chart-4: #a8821c;",
245
- " --dsh-mem-chart-5: #d97a0d;",
246
- " --dsh-mem-chart-6: #d64570;",
247
- " --dsh-mem-chart-7: #7c5cff;",
248
- " --dsh-mem-chart-8: #61666b;",
249
- // 档位色 = 灰 → 品牌蓝的渐变阶(chat/work 过渡蓝、auto 品牌蓝);
250
- // 文字对比度按 pill 实际底色(流光内底 = bg-card 97% + 档位色 3%)复算 AA 达标
251
- " --dsh-mem-mode-chat: #5a69b0;",
252
- " --dsh-mem-mode-work: #5263ca;",
253
- " --dsh-mem-mode-auto: #3d5be0;",
254
- // 开关旋钮 / 进度条底(滑轨已随 ModeSlider 删除,填充渐变与停点令牌一并移除)
255
- " --dsh-mem-thumb: #ffffff;",
256
- " --dsh-mem-track: rgba(128,140,150,0.32);",
257
- // wing 八边形令牌(WingWheel):连线 / 角默认 / 角选中 / 空角与未打标态;
258
- // 全部引用既有中性色与品牌蓝体系,双主题各自声明,无裸色溢出
259
- " --dsh-mem-wing-line: rgba(128,140,150,0.35);",
260
- " --dsh-mem-wing-corner: var(--dsh-mem-text-2);",
261
- " --dsh-mem-wing-corner-on: var(--dsh-mem-accent);",
262
- " --dsh-mem-wing-empty: var(--dsh-mem-text-3);",
263
- " --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.05));",
264
- " --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,
265
534
  "}",
266
- // ── 令牌(暗色:body[data-ds-dark-theme] 是 dsh 前端的暗色开关) ──
267
535
  "body[data-ds-dark-theme] {",
268
- " --dsh-mem-accent: #6e85ff;",
269
- " --dsh-mem-accent-text: #7b90ff;",
270
- " --dsh-mem-accent-fill: #465ce8;",
271
- " --dsh-mem-accent-weak: rgba(110,133,255,0.14);",
272
- " --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #2c2c2e);",
273
- " --dsh-mem-bg-inset: var(--dsw-alias-bg-layer-1, #232324);",
274
- " --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));",
275
- " --dsh-mem-bg-pop: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3, #353638));",
276
- " --dsh-mem-border: var(--dsw-alias-border-l2, rgba(255,255,255,0.12));",
277
- " --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(255,255,255,0.16));",
278
- " --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(255,255,255,0.06));",
279
- " --dsh-mem-text-1: var(--dsw-alias-label-primary, #f9fafb);",
280
- " --dsh-mem-text-2: var(--dsw-alias-label-secondary, #cfd3d6);",
281
- " --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #8892a6);",
282
- " --dsh-mem-danger: var(--dsw-alias-state-error-primary, #f4707b);",
283
- " --dsh-mem-chart-1: #6e85ff;",
284
- " --dsh-mem-chart-2: #35c4b5;",
285
- " --dsh-mem-chart-3: #52c98d;",
286
- " --dsh-mem-chart-4: #d9b23e;",
287
- " --dsh-mem-chart-5: #f59e5b;",
288
- " --dsh-mem-chart-6: #f47ba2;",
289
- " --dsh-mem-chart-7: #a78bfa;",
290
- " --dsh-mem-chart-8: #8892a6;",
291
- " --dsh-mem-mode-chat: #97a4ff;",
292
- " --dsh-mem-mode-work: #8295ff;",
293
- " --dsh-mem-mode-auto: #7b90ff;",
294
- " --dsh-mem-thumb: #e8ebf5;",
295
- " --dsh-mem-track: rgba(148,160,180,0.30);",
296
- " --dsh-mem-wing-line: rgba(148,160,180,0.32);",
297
- " --dsh-mem-wing-corner: var(--dsh-mem-text-2);",
298
- " --dsh-mem-wing-corner-on: var(--dsh-mem-accent);",
299
- " --dsh-mem-wing-empty: var(--dsh-mem-text-3);",
300
- " --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.3));",
301
- " --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,
302
537
  "}",
303
- // ── 主题切换过渡:只过渡颜色/阴影(不碰 transform),明暗翻转不生硬 ──
304
- ".dsh-mem-root, .dsh-mem-root * { transition: background-color .18s ease, border-color .18s ease, color .18s ease, box-shadow .18s ease; }",
305
- // ── 控件:按钮(次级)── 圆角体系:控件 8 / 卡片 10 / 浮层 12 / 胶囊 999
306
- ".dsh-mem-btn {",
307
- " padding: 5px 14px; font-size: 13px; line-height: 20px; border-radius: 8px; cursor: pointer;",
308
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
309
- " 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,
310
542
  "}",
311
- ".dsh-mem-btn:hover:not(:disabled) { border-color: var(--dsh-mem-border-strong); background: var(--dsh-mem-bg-hover); }",
312
- ".dsh-mem-btn:active:not(:disabled) { transform: scale(0.98); }",
313
- ".dsh-mem-btn:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
314
- ".dsh-mem-btn:disabled { opacity: 0.45; cursor: not-allowed; }",
315
- // ── 控件:输入框 / 下拉 ──
316
- ".dsh-mem-input {",
317
- " padding: 5px 10px; font-size: 13px; border-radius: 8px; color: var(--dsh-mem-text-1);",
318
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
319
- " transition: border-color .15s ease, box-shadow .15s ease;",
543
+ 'html[data-dsh-mem-skin="dark"] {',
544
+ ...DARK_TOK,
320
545
  "}",
321
- ".dsh-mem-input:focus {",
322
- " outline: none; border-color: var(--dsh-mem-accent);",
323
- " box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
324
- "}",
325
- // ── 下拉触发钮(NSel):观感与输入框一致(8px 圆角/同令牌边框底色),
326
- // 文字 ellipsis + CSS 描边 chevron(展开旋转 180°);弹出面板见 .dsh-mem-pop ──
327
- ".dsh-mem-select {",
328
- " display: inline-flex; align-items: center; justify-content: space-between; gap: 8px;",
329
- " width: 100%; min-width: 0; padding: 5px 10px; font: inherit; font-size: 13px; line-height: 20px;",
330
- " text-align: left; border-radius: 8px; cursor: pointer; color: var(--dsh-mem-text-1);",
331
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
332
- " transition: border-color .15s ease, box-shadow .15s ease;",
333
- "}",
334
- ".dsh-mem-select:focus-visible {",
335
- " outline: none; border-color: var(--dsh-mem-accent);",
336
- " box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
337
- "}",
338
- ".dsh-mem-select:disabled { opacity: 0.45; cursor: not-allowed; }",
339
- ".dsh-mem-select-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
340
- // 下拉弹出面板:dsh MenuDropdown 同款——浮层 12 圆角 + menu 底 + lv3 投影;
341
- // 选项行 10 圆角 + hover 底色 + 选中打勾(active 由 data-active 标记)
342
- ".dsh-mem-sel { position: relative; display: inline-flex; min-width: 0; vertical-align: top; }",
343
- ".dsh-mem-sel-chev {",
344
- " width: 8px; height: 8px; flex: none; margin-right: 2px;",
345
- " border-right: 1.5px solid var(--dsh-mem-text-3); border-bottom: 1.5px solid var(--dsh-mem-text-3);",
346
- " transform: rotate(45deg); transition: transform .12s ease;",
347
- "}",
348
- ".dsh-mem-sel-chev-open { transform: rotate(225deg); }",
349
- ".dsh-mem-pop {",
350
- " position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;",
351
- " min-width: 100%; width: max-content; max-width: 340px; max-height: 264px;",
352
- " overflow-y: auto; overscroll-behavior: contain; padding: 4px;",
353
- " background: var(--dsh-mem-bg-pop); border: 1px solid var(--dsh-mem-border-pop);",
354
- " border-radius: 12px; box-shadow: var(--dsh-mem-shadow-pop);",
355
- "}",
356
- ".dsh-mem-pop-opt {",
357
- " display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box;",
358
- " min-height: 32px; padding: 5px 10px; font: inherit; font-size: 13px; line-height: 20px;",
359
- " text-align: left; color: var(--dsh-mem-text-1); cursor: pointer;",
360
- " background: none; border: none; outline: none; border-radius: 10px;",
361
- "}",
362
- '.dsh-mem-pop-opt:hover, .dsh-mem-pop-opt[data-active="1"] { background: var(--dsh-mem-bg-hover); }',
363
- ".dsh-mem-pop-opt-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
364
- ".dsh-mem-pop-check { flex: none; font-size: 12px; color: var(--dsh-mem-text-1); }",
365
- ".dsh-mem-pop-empty { padding: 10px; font-size: 13px; color: var(--dsh-mem-text-3); }",
366
- // ── Tab(下划线式):active 品牌蓝下划线 + 主文字色 ──
367
- ".dsh-mem-tab {",
368
- " padding: 6px 12px; font-size: 13px; cursor: pointer; background: none; border: none;",
369
- " color: var(--dsh-mem-text-2); border-bottom: 2px solid transparent; margin-bottom: -1px;",
370
- "}",
371
- ".dsh-mem-tab:hover { color: var(--dsh-mem-text-1); }",
372
- ".dsh-mem-tab-on { font-weight: 600; color: var(--dsh-mem-text-1); border-bottom-color: var(--dsh-mem-accent); }",
373
- ".dsh-mem-tab:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: -2px; }",
374
- // ── 卡片:hover 微抬(边框加深),只给可交互卡片用 ──
375
- ".dsh-mem-card {",
376
- " border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
377
- " box-shadow: var(--dsh-mem-shadow-card);",
378
- "}",
379
- ".dsh-mem-card-hover:hover { border-color: var(--dsh-mem-border-strong); }",
380
- // ── 场景卡折叠箭头:展开态旋转 90°(进 reduced-motion 压制名单) ──
381
- ".dsh-mem-scene-chev { display: inline-block; transition: transform .15s ease; color: var(--dsh-mem-text-3); }",
382
- // ── 记忆类型标签:tint 风格(彩底淡色 + 彩字),--dsh-mem-tag-c 由类型类给定 ──
383
- ".dsh-mem-tag {",
384
- " display: inline-block; padding: 1px 8px; border-radius: 999px;",
385
- " font-size: 11px; font-weight: 600; line-height: 18px; white-space: nowrap;",
386
- " color: var(--dsh-mem-tag-c, var(--dsh-mem-text-2));",
387
- " background: color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 12%, transparent);",
388
- " border: 1px solid color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 28%, transparent);",
389
- "}",
390
- ".dsh-mem-tag-persona { --dsh-mem-tag-c: #6f42c1; }",
391
- ".dsh-mem-tag-episodic { --dsh-mem-tag-c: #0757b4; }",
392
- ".dsh-mem-tag-instruction, .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #8a5a00; }",
393
- ".dsh-mem-tag-work-fact { --dsh-mem-tag-c: #0757b4; }",
394
- ".dsh-mem-tag-work-task { --dsh-mem-tag-c: #116629; }",
395
- ".dsh-mem-tag-work-method { --dsh-mem-tag-c: #6f42c1; }",
396
- "body[data-ds-dark-theme] .dsh-mem-tag-persona { --dsh-mem-tag-c: #c297ff; }",
397
- "body[data-ds-dark-theme] .dsh-mem-tag-episodic { --dsh-mem-tag-c: #6cb2ff; }",
398
- "body[data-ds-dark-theme] .dsh-mem-tag-instruction, body[data-ds-dark-theme] .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #e3b341; }",
399
- "body[data-ds-dark-theme] .dsh-mem-tag-work-fact { --dsh-mem-tag-c: #6cb2ff; }",
400
- "body[data-ds-dark-theme] .dsh-mem-tag-work-task { --dsh-mem-tag-c: #6fca74; }",
401
- "body[data-ds-dark-theme] .dsh-mem-tag-work-method { --dsh-mem-tag-c: #c297ff; }",
402
- // ── pill 边缘流光(品牌蓝族,chat/work/auto 三档共用;off 无):border 区画旋转
403
- // conic 光带;内部必须用【不透明】底色盖住光带——半透明内层会让 conic 透进按钮
404
- // 内部干扰文字(实测事故)。不透明底 = 主题底混 3% 档位色(--dsh-mem-pill-tint
405
- // 由 pill inline 给定;3% 保证档位色文字 AA,暗色智能档余量 4.63:1)
406
- ".dsh-mem-flow {",
407
- " border: 1px solid transparent;",
408
- " background:",
409
- " linear-gradient(",
410
- " color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe)),",
411
- " color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe))",
412
- " ) padding-box,",
413
- " conic-gradient(from var(--dsh-mem-angle),",
414
- " rgba(61,91,224,0.9), rgba(77,107,254,0.95), rgba(147,168,255,1),",
415
- " rgba(110,133,255,0.9), rgba(61,91,224,0.9)) border-box;",
416
- " animation: dshMemFlow 3s linear infinite;",
417
- "}",
418
- // ── off 档 pill:dsh 透明按钮——无底无边框只留文字,hover 才出 interactive 淡底
419
- //(裸 button 会露 UA 默认灰底+描边,必须显式压掉) ──
420
- ".dsh-mem-pill-off { border: none; background: transparent; }",
421
- ".dsh-mem-pill-off:hover { background: var(--dsh-mem-bg-hover); }",
422
- ".dsh-mem-pill-off:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
423
- // 流光态焦点环(同一物理按钮的两态焦点反馈对称,配方同 .dsh-mem-btn)
424
- ".dsh-mem-flow:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
425
- // ── 触屏隐形热区(手机端适配):触点目标补足 44px 标准(iOS HIG)。
426
- // 伪元素不参与布局(浮层/输入栏几何零变化),指针事件落在宿主元素上。
427
- // 全端统一不做 pointer:coarse 分端(桌面点中目标变大是纯收益)。
428
- // pill:视觉高 24px,::after 上下各外扩 10px;只上下不左右——左右是宿主输入栏
429
- // 邻位控件(模式选择器),外扩会制造误触重叠带 ──
430
- ".dsh-mem-pill-hit::after {",
431
- " content: ''; position: absolute; left: 0; right: 0; top: -10px; bottom: -10px;",
432
- "}",
433
- // ── 浮层(dsh 原生菜单同配方:不透明实底 + inverted 描边(浅色不可见)+ lv3 阴影) ──
434
- ".dsh-mem-popover {",
435
- " border-radius: 12px;",
436
- " border: 1px solid var(--dsh-mem-border-pop);",
437
- " background: var(--dsh-mem-bg-pop);",
438
- " box-shadow: var(--dsh-mem-shadow-pop);",
439
- " color: var(--dsh-mem-text-1);",
440
- "}",
441
- // ── 重建面板 ──(模态本体走 NModal:原生 Modal 优先,回退 rb-overlay/rb-modal)
442
- ".dsh-mem-rb-card {",
443
- " border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
444
- " box-shadow: var(--dsh-mem-shadow-card); padding: 12px 14px; margin-bottom: 14px; font-size: 13px;",
445
- // 剪掉行内溢出的固定宽控件(如维度输入框),防其顶破卡片圆角边界
446
- " overflow: hidden;",
447
- "}",
448
- ".dsh-mem-rb-bar { height: 8px; border-radius: 4px; overflow: hidden; flex: 1; background: var(--dsh-mem-track); }",
449
- ".dsh-mem-rb-fill { height: 100%; border-radius: 4px; background: var(--dsh-mem-accent-fill); transition: width .4s ease; }",
450
- // 无位移阶段(单次 LLM 调用可达分钟级)的呼吸进度条:宽度满格,透明度呼吸表达"在跑"
451
- "@keyframes dshMemRbBreath { 0%,100% { opacity: 1; } 50% { opacity: .35; } }",
452
- ".dsh-mem-rb-fill-indet { animation: dshMemRbBreath 1.6s ease-in-out infinite; transition: none; }",
453
- ".dsh-mem-rb-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35);",
454
- " display: flex; align-items: center; justify-content: center; z-index: 2000; }",
455
- ".dsh-mem-rb-modal { width: 440px; max-width: calc(100vw - 48px); border-radius: 12px;",
456
- " padding: 18px 20px; font-size: 13px; line-height: 1.6;",
457
- " border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
458
- " box-shadow: 0 16px 48px rgba(0,0,0,0.24); }",
459
- "body[data-ds-dark-theme] .dsh-mem-rb-modal { box-shadow: 0 16px 48px rgba(0,0,0,0.6); }",
460
- ".dsh-mem-rb-muted { font-size: 12px; color: var(--dsh-mem-text-3); }",
461
- // ── 会话信息区(悬浮卡下半部):分隔线 + 2×2 指标 + 状态行;纯静态 DOM,
462
- // 不挂 rAF 循环,轮询数据到达才触发本组件小树 re-render ──
463
- ".dsh-mem-sinfo { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--dsh-mem-border); }",
464
- ".dsh-mem-sinfo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; }",
465
- ".dsh-mem-sinfo-val { font-size: 13px; font-weight: 600; color: var(--dsh-mem-text-1); line-height: 18px; font-variant-numeric: tabular-nums; }",
466
- ".dsh-mem-sinfo-label { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
467
- ".dsh-mem-sinfo-warn { font-size: 11px; color: var(--dsh-mem-danger); line-height: 16px; margin-bottom: 6px; }",
468
- ".dsh-mem-sinfo-note { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 16px; margin-top: 8px; }",
469
- ".dsh-mem-sinfo-sum { font-size: 11px; color: var(--dsh-mem-text-3); line-height: 16px; margin-top: 8px; }",
470
- // reduced-motion 兜底放样式表末尾:同特异性下后置声明才能压过上面的组件类
471
- "@media (prefers-reduced-motion: reduce) {",
472
- " .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; }",
473
- " .dsh-mem-flow { animation: none; }",
474
- "}"
546
+ ...COMPONENT_CSS
475
547
  ].join("\n");
476
- 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);
477
558
  }
478
559
 
479
560
  // client/src/tabs/ConflictsTab.tsx
@@ -805,46 +886,243 @@ var __defProp = Object.defineProperty;
805
886
  }
806
887
  function Side(props) {
807
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;
808
890
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 6 }, children: [
809
891
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, fontWeight: props.accent ? 600 : void 0 }, children: props.label }),
810
892
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: S.content, children: props.content || GONE }),
811
893
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...S.muted, fontFamily: "ui-monospace, Consolas, monospace" }, children: props.id }),
812
- 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
813
896
  ] });
814
897
  }
815
898
 
816
- // client/src/tabs/CostTab.tsx
899
+ // client/src/tabs/ConnectTab.tsx
817
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");
818
1096
 
819
1097
  // client/src/ui/controls.tsx
820
- var import_jsx_runtime2 = require("react/jsx-runtime");
1098
+ var import_jsx_runtime4 = require("react/jsx-runtime");
821
1099
  function Switch(props) {
822
1100
  const on = !!props.checked;
823
1101
  const disabled = !!props.disabled;
824
1102
  const base = { ...S.switch, ...on ? S.switchOn : S.switchOff, ...disabled ? S.switchDisabled : null };
825
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
826
1104
  "div",
827
1105
  {
828
1106
  style: base,
829
1107
  onClick: () => {
830
1108
  if (!disabled && props.onChange) props.onChange(!on);
831
1109
  },
832
- 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 } })
833
1111
  }
834
1112
  );
835
1113
  }
836
1114
  function SwitchRow(props) {
837
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: S.switchRow, children: [
838
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Switch, { checked: props.checked, disabled: props.disabled, onChange: props.onChange }),
839
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
840
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: S.switchLabel, children: props.label }),
841
- /* @__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 || "" })
842
1120
  ] })
843
1121
  ] });
844
1122
  }
845
1123
  function ActionButton(props) {
846
1124
  const disabled = !!props.disabled;
847
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1125
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
848
1126
  "button",
849
1127
  {
850
1128
  type: "button",
@@ -868,10 +1146,10 @@ var __defProp = Object.defineProperty;
868
1146
  function Segmented(props) {
869
1147
  const value = props.value;
870
1148
  const disabled = !!props.disabled;
871
- 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) => {
872
1150
  const on = opt.key === value;
873
1151
  const optDisabled = disabled || !!opt.disabled;
874
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1152
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
875
1153
  "span",
876
1154
  {
877
1155
  title: opt.disabledTitle || opt.title || "",
@@ -892,7 +1170,7 @@ var __defProp = Object.defineProperty;
892
1170
  }
893
1171
 
894
1172
  // client/src/tabs/CostTab.tsx
895
- var import_jsx_runtime3 = require("react/jsx-runtime");
1173
+ var import_jsx_runtime5 = require("react/jsx-runtime");
896
1174
  function renderCostChart(buckets, models, maxY, fmtDate, fmtInt, palette) {
897
1175
  const W = 600;
898
1176
  const H = 200;
@@ -907,17 +1185,17 @@ var __defProp = Object.defineProperty;
907
1185
  const y = (v) => T + ih - v / maxY * ih;
908
1186
  const yTicks = [0, maxY / 2, maxY];
909
1187
  const xIdx = n > 2 ? [0, Math.floor((n - 1) / 2), n - 1] : n === 2 ? [0, 1] : [0];
910
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { viewBox: "0 0 " + W + " " + H, style: { width: "100%", height: "auto", display: "block" }, children: [
911
- /* @__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 }),
912
1190
  yTicks.map((v) => {
913
- 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);
914
1192
  }),
915
1193
  xIdx.map((i) => {
916
- 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);
917
1195
  }),
918
1196
  models.map((m, mi) => {
919
1197
  const pts = buckets.map((b, i) => x(i) + "," + y(b.byModel[m] || 0)).join(" ");
920
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1198
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
921
1199
  "polyline",
922
1200
  {
923
1201
  points: pts,
@@ -961,13 +1239,13 @@ var __defProp = Object.defineProperty;
961
1239
  var tdStyle = { fontSize: 12.5, color: "var(--dsh-mem-text-1)", textAlign: "right", padding: "4px 10px", fontFamily: "ui-monospace, Consolas, monospace" };
962
1240
  function CostTab(props) {
963
1241
  const rpc = props.rpc;
964
- const [data, setData] = (0, import_react3.useState)(null);
965
- const [error, setError] = (0, import_react3.useState)(null);
966
- const [granularity, setGranularity] = (0, import_react3.useState)("day");
967
- const [layer, setLayer] = (0, import_react3.useState)("");
968
- const [rangeDays, setRangeDays] = (0, import_react3.useState)(0);
969
- const [rangeOpen, setRangeOpen] = (0, import_react3.useState)(false);
970
- 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)(() => {
971
1249
  setError(null);
972
1250
  rpc("dsh-memory/token-cost", {
973
1251
  granularity,
@@ -979,7 +1257,7 @@ var __defProp = Object.defineProperty;
979
1257
  setError(String(e && e.message || e));
980
1258
  });
981
1259
  }, [rpc, granularity, rangeDays]);
982
- (0, import_react3.useEffect)(() => {
1260
+ (0, import_react5.useEffect)(() => {
983
1261
  load();
984
1262
  const timer = setInterval(load, 5e3);
985
1263
  return () => {
@@ -1052,13 +1330,13 @@ var __defProp = Object.defineProperty;
1052
1330
  });
1053
1331
  const cell = (lc, r, pick) => {
1054
1332
  const w = lc.win[r];
1055
- 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);
1056
1334
  };
1057
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1059
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Segmented, { value: layer, options: LAYER_OPTS, onChange: setLayer }),
1060
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Segmented, { value: granularity, options: GRAN_OPTS, onChange: setGranularity }),
1061
- /* @__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)(
1062
1340
  NButton,
1063
1341
  {
1064
1342
  onClick: () => {
@@ -1067,12 +1345,12 @@ var __defProp = Object.defineProperty;
1067
1345
  children: rangeDays > 0 ? "近 " + rangeDays + " 天" : "近N天"
1068
1346
  }
1069
1347
  ),
1070
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.grow }),
1071
- /* @__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: "刷新" })
1072
1350
  ] }),
1073
- rangeOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
1074
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.muted, children: "展示近 N 天(正整数,清空=默认窗口;超出保留期后端自动回退)" }),
1075
- /* @__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)(
1076
1354
  NInput,
1077
1355
  {
1078
1356
  value: rangeDays === 0 ? "" : String(rangeDays),
@@ -1090,94 +1368,94 @@ var __defProp = Object.defineProperty;
1090
1368
  }
1091
1369
  )
1092
1370
  ] }) : null,
1093
- error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.error, children: "成本读取失败:" + error }) : null,
1094
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "成本趋势(按模型)" }),
1095
- 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: [
1096
1374
  renderCostChart(buckets, models, maxY, fmtDate, fmtInt, PALETTE),
1097
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px 12px", margin: "6px 0 14px" }, children: models.map((m, mi) => {
1098
- 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)(
1099
1377
  "span",
1100
1378
  {
1101
1379
  style: { display: "inline-flex", alignItems: "center", gap: 4, fontSize: 12, color: "var(--dsh-mem-text-2)" },
1102
1380
  children: [
1103
- /* @__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" } }),
1104
1382
  m
1105
1383
  ]
1106
1384
  },
1107
1385
  "lg" + m
1108
1386
  );
1109
1387
  }) })
1110
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: S.muted, children: data ? "暂无成本数据(触发一次蒸馏后这里会出现趋势)。" : "加载中…" }),
1111
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "层级成本(输出 token)" }),
1112
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1113
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1114
- /* @__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: "层级" }),
1115
1393
  RANGES.map((r) => {
1116
- 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);
1117
1395
  })
1118
1396
  ] }) }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: layerTable.map((lc) => {
1120
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1121
- /* @__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() }),
1122
1400
  RANGES.map((r) => cell(lc, r, (w) => w.outputTokens))
1123
1401
  ] }, lc.layer);
1124
1402
  }) })
1125
1403
  ] }),
1126
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "层级成本(单次 avg)" }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1128
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1129
- /* @__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: "层级" }),
1130
1408
  RANGES.map((r) => {
1131
- 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);
1132
1410
  })
1133
1411
  ] }) }),
1134
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: layerTable.map((lc) => {
1135
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1136
- /* @__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() }),
1137
1415
  RANGES.map((r) => cell(lc, r, (w) => w.avgOutputTokens))
1138
1416
  ] }, lc.layer);
1139
1417
  }) })
1140
1418
  ] }),
1141
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "层级成本(单次 median)" }),
1142
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse", marginBottom: 14 }, children: [
1143
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1144
- /* @__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: "层级" }),
1145
1423
  RANGES.map((r) => {
1146
- 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);
1147
1425
  })
1148
1426
  ] }) }),
1149
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: layerTable.map((lc) => {
1150
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
1151
- /* @__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() }),
1152
1430
  RANGES.map((r) => cell(lc, r, (w) => w.medianOutputTokens))
1153
1431
  ] }, lc.layer);
1154
1432
  }) })
1155
1433
  ] }),
1156
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.panelLabel, children: "时间窗口总览" }),
1157
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.statGrid, children: windows.map((w) => {
1158
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
1159
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.statNum, children: fmtInt(w.outputTokens + w.reasoningTokens) }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: S.statLabel, children: RANGE_LABELS[w.range] + " · 总输出 token" }),
1161
- /* @__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 + " 次调用" })
1162
1440
  ] }, w.range);
1163
1441
  }) }),
1164
- byModel.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1165
- /* @__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: "按模型(累计)" }),
1166
1444
  byModel.map((m) => {
1167
1445
  const label = fmtModel(m.provider, m.model);
1168
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: S.infoRow, children: [
1169
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: S.infoKey, children: label }),
1170
- /* @__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) })
1171
1449
  ] }, "m-" + label);
1172
1450
  })
1173
1451
  ] }) : null,
1174
- 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
1175
1453
  ] });
1176
1454
  }
1177
1455
 
1178
1456
  // client/src/tabs/LogTab.tsx
1179
- var import_react4 = require("react");
1180
- var import_jsx_runtime4 = require("react/jsx-runtime");
1457
+ var import_react6 = require("react");
1458
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1181
1459
  var SOURCES = [
1182
1460
  { id: "", label: "系统日志" },
1183
1461
  { id: "recall_turn", label: "召回追踪" },
@@ -1200,11 +1478,11 @@ var __defProp = Object.defineProperty;
1200
1478
  }
1201
1479
  function LogTab(props) {
1202
1480
  const rpc = props.rpc;
1203
- const [source, setSource] = (0, import_react4.useState)("");
1204
- const [lines, setLines] = (0, import_react4.useState)(null);
1205
- const [error, setError] = (0, import_react4.useState)(null);
1206
- const preRef = (0, import_react4.useRef)(null);
1207
- 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)(() => {
1208
1486
  setError(null);
1209
1487
  if (source === "") {
1210
1488
  rpc("dsh-memory/log-tail", { lines: 200 }).then((r) => {
@@ -1218,25 +1496,25 @@ var __defProp = Object.defineProperty;
1218
1496
  else setError(r && r.error ? r.error.message : "RPC error");
1219
1497
  }).catch((e) => setError(String(e && e.message || e)));
1220
1498
  }, [rpc, source]);
1221
- (0, import_react4.useEffect)(() => {
1499
+ (0, import_react6.useEffect)(() => {
1222
1500
  load();
1223
1501
  }, [load]);
1224
- (0, import_react4.useEffect)(() => {
1502
+ (0, import_react6.useEffect)(() => {
1225
1503
  if (lines && preRef.current) preRef.current.scrollTop = preRef.current.scrollHeight;
1226
1504
  }, [lines]);
1227
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1228
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10, gap: 8 }, children: [
1229
- SOURCES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NButton, { onClick: () => setSource(s.id), children: source === s.id ? "● " + s.label : s.label }, s.id)),
1230
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: S.grow }),
1231
- /* @__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: "刷新" })
1232
1510
  ] }),
1233
- /* @__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})` }),
1234
- 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") || "(暂无记录)" })
1235
1513
  ] });
1236
1514
  }
1237
1515
 
1238
1516
  // client/src/tabs/OverviewTab.tsx
1239
- var import_react13 = require("react");
1517
+ var import_react15 = require("react");
1240
1518
 
1241
1519
  // client/src/pill/modes.ts
1242
1520
  var MODES = [
@@ -1256,11 +1534,11 @@ var __defProp = Object.defineProperty;
1256
1534
  }
1257
1535
 
1258
1536
  // client/src/tabs/DistillSettings.tsx
1259
- var import_react9 = require("react");
1537
+ var import_react11 = require("react");
1260
1538
 
1261
1539
  // client/src/tabs/BudgetInputs.tsx
1262
- var import_react5 = require("react");
1263
- var import_jsx_runtime5 = require("react/jsx-runtime");
1540
+ var import_react7 = require("react");
1541
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1264
1542
  var LAYERS = [
1265
1543
  ["extract", "抽取"],
1266
1544
  ["dedup", "去重"],
@@ -1281,7 +1559,7 @@ var __defProp = Object.defineProperty;
1281
1559
  const onError = props.onError;
1282
1560
  const scope = props.scope ?? "all";
1283
1561
  const layers = scope === "all" || scope === "input" ? LAYERS : LAYERS.filter((l) => SCOPE_KEYS[scope].includes(l[0]));
1284
- const [draft, setDraft] = (0, import_react5.useState)(null);
1562
+ const [draft, setDraft] = (0, import_react7.useState)(null);
1285
1563
  if (!data || !data.budgets) return null;
1286
1564
  const cur = data.budgets.current || {};
1287
1565
  const def = data.budgets.defaults || {};
@@ -1377,7 +1655,7 @@ var __defProp = Object.defineProperty;
1377
1655
  return false;
1378
1656
  };
1379
1657
  const inputBox = (key, _label, title, width, placeholder, onCommit) => {
1380
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1658
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1381
1659
  NInput,
1382
1660
  {
1383
1661
  type: "number",
@@ -1408,10 +1686,10 @@ var __defProp = Object.defineProperty;
1408
1686
  const showInput = scope === "all" || scope === "input";
1409
1687
  const effNote = layers.map((l) => eff[l[0]] || "?").join(" / ");
1410
1688
  const rowLabel = (key) => key === "extract" ? "抽取输出" : key === "dedup" ? "去重输出" : key === "l2" ? "L2 输出" : "L3 输出";
1411
- const rowStyle = { display: "flex", alignItems: "center", gap: 8 };
1412
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1413
- showOutputs ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginTop: 12 }, children: [
1414
- /* @__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)(
1415
1693
  "div",
1416
1694
  {
1417
1695
  style: S.switchLabel,
@@ -1419,8 +1697,8 @@ var __defProp = Object.defineProperty;
1419
1697
  children: "输出预算"
1420
1698
  }
1421
1699
  ),
1422
- /* @__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: [
1423
- /* @__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]) }),
1424
1702
  inputBox(
1425
1703
  l[0],
1426
1704
  l[1],
@@ -1429,11 +1707,11 @@ var __defProp = Object.defineProperty;
1429
1707
  def[l[0]],
1430
1708
  commitOutputs
1431
1709
  ),
1432
- /* @__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" })
1433
1711
  ] }, l[0])) })
1434
1712
  ] }) : null,
1435
- showInput ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginTop: 12 }, children: [
1436
- /* @__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)(
1437
1715
  "div",
1438
1716
  {
1439
1717
  style: S.switchLabel,
@@ -1441,8 +1719,8 @@ var __defProp = Object.defineProperty;
1441
1719
  children: "输入预算"
1442
1720
  }
1443
1721
  ),
1444
- /* @__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: [
1445
- /* @__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: "单次输入" }),
1446
1724
  inputBox(
1447
1725
  "input",
1448
1726
  "输入",
@@ -1451,15 +1729,15 @@ var __defProp = Object.defineProperty;
1451
1729
  ib.fallback,
1452
1730
  commitInput
1453
1731
  ),
1454
- /* @__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: "字符" })
1455
1733
  ] }) })
1456
1734
  ] }) : null
1457
1735
  ] });
1458
1736
  }
1459
1737
 
1460
1738
  // client/src/tabs/ChannelEditor.tsx
1461
- var import_react6 = require("react");
1462
- var import_jsx_runtime6 = require("react/jsx-runtime");
1739
+ var import_react8 = require("react");
1740
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1463
1741
  var STY = {
1464
1742
  block: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" },
1465
1743
  head: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 6 },
@@ -1490,15 +1768,15 @@ var __defProp = Object.defineProperty;
1490
1768
  ];
1491
1769
  function ChannelEditor(props) {
1492
1770
  const { channel, rpc, disabled } = props;
1493
- const [baseText, setBaseText] = (0, import_react6.useState)(channel.runtimeBaseURL);
1494
- const [keyText, setKeyText] = (0, import_react6.useState)("");
1495
- const baseTimer = (0, import_react6.useRef)(void 0);
1496
- const dirty = (0, import_react6.useRef)(false);
1497
- (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)(() => {
1498
1776
  if (!dirty.current) setBaseText(channel.runtimeBaseURL);
1499
1777
  }, [channel.runtimeBaseURL]);
1500
- (0, import_react6.useEffect)(() => () => clearTimeout(baseTimer.current), []);
1501
- const commit = (patch) => {
1778
+ (0, import_react8.useEffect)(() => () => clearTimeout(baseTimer.current), []);
1779
+ const commit2 = (patch) => {
1502
1780
  rpc("dsh-memory/settings-set", patch).catch(() => {
1503
1781
  });
1504
1782
  };
@@ -1508,14 +1786,14 @@ var __defProp = Object.defineProperty;
1508
1786
  clearTimeout(baseTimer.current);
1509
1787
  baseTimer.current = setTimeout(() => {
1510
1788
  dirty.current = false;
1511
- commit({ directBaseURL: v.trim() });
1789
+ commit2({ directBaseURL: v.trim() });
1512
1790
  }, 600);
1513
1791
  };
1514
1792
  const commitKey = () => {
1515
1793
  const v = keyText.trim();
1516
1794
  if (v) {
1517
1795
  dirty.current = true;
1518
- commit({ directApiKey: v });
1796
+ commit2({ directApiKey: v });
1519
1797
  dirty.current = false;
1520
1798
  }
1521
1799
  setKeyText("");
@@ -1526,29 +1804,29 @@ var __defProp = Object.defineProperty;
1526
1804
  const deployedDirect = channel.deployed === "direct";
1527
1805
  const apiKeySet = channel.runtimeApiKeySet || channel.deployedApiKeySet;
1528
1806
  const showWarn = isDirect && !channel.directReady;
1529
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.block, children: [
1530
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.head, children: [
1531
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.title, children: "蒸馏通道" }),
1532
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: isDirect ? STY.chipAccent : STY.chipMuted, children: isDirect ? "直连端点" : "复用宿主" }),
1533
- /* @__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: [
1534
1812
  "生效:",
1535
1813
  runtime ? runtime === "direct" ? "运行时直连" : "运行时宿主" : "跟随部署配置"
1536
1814
  ] })
1537
1815
  ] }),
1538
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.desc, children: "direct 通道走插件原生 HTTP,不依赖宿主 provider 注册表;direct 失败自动回退宿主路由链作兜底安全网。" }),
1539
- /* @__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)(
1540
1818
  Segmented,
1541
1819
  {
1542
1820
  value: runtime,
1543
1821
  disabled,
1544
1822
  options: MODE_OPTIONS.map((m) => ({ key: m.key, label: m.label, title: m.title })),
1545
- onChange: (k) => commit({ distillMode: k })
1823
+ onChange: (k) => commit2({ distillMode: k })
1546
1824
  }
1547
1825
  ),
1548
- editing ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1549
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.fieldRow, children: [
1550
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.fieldLabel, children: "端点 URL" }),
1551
- /* @__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)(
1552
1830
  NInput,
1553
1831
  {
1554
1832
  style: STY.input,
@@ -1560,19 +1838,19 @@ var __defProp = Object.defineProperty;
1560
1838
  if (e.key === "Enter") {
1561
1839
  clearTimeout(baseTimer.current);
1562
1840
  dirty.current = false;
1563
- commit({ directBaseURL: baseText.trim() });
1841
+ commit2({ directBaseURL: baseText.trim() });
1564
1842
  }
1565
1843
  }
1566
1844
  }
1567
1845
  ),
1568
- 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: [
1569
1847
  "部署基线:",
1570
1848
  channel.deployedBaseURL || "(空)"
1571
1849
  ] }) : null
1572
1850
  ] }),
1573
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: STY.fieldRow, children: [
1574
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.fieldLabel, children: "API Key" }),
1575
- /* @__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)(
1576
1854
  NInput,
1577
1855
  {
1578
1856
  style: STY.input,
@@ -1588,41 +1866,41 @@ var __defProp = Object.defineProperty;
1588
1866
  onBlur: commitKey
1589
1867
  }
1590
1868
  ),
1591
- apiKeySet ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.chipAccent, children: "已配置" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: STY.chipMuted, children: "未配置" }),
1592
- /* @__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)(
1593
1871
  NButton,
1594
1872
  {
1595
1873
  disabled,
1596
1874
  title: "写入空串清除运行时密钥(部署 .yml 的密钥不受影响)",
1597
- onClick: () => commit({ directApiKey: "" }),
1875
+ onClick: () => commit2({ directApiKey: "" }),
1598
1876
  children: "清除"
1599
1877
  }
1600
1878
  )
1601
1879
  ] }),
1602
- showWarn ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: STY.warn, children: "⚠ direct 未配置完整:需同时有端点 URL 与模型(llm.model / 全局链主路由),否则会回退宿主路由。" }) : null
1603
- ] }) : /* @__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" ? "运行时锁定" : "跟随部署配置") + ")" })
1604
1882
  ] });
1605
1883
  }
1606
1884
 
1607
1885
  // client/src/tabs/RouteChainEditor.tsx
1608
- var import_react8 = require("react");
1886
+ var import_react10 = require("react");
1609
1887
 
1610
1888
  // client/src/ui/NSel.tsx
1611
- var import_react7 = require("react");
1612
- var import_jsx_runtime7 = require("react/jsx-runtime");
1889
+ var import_react9 = require("react");
1890
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1613
1891
  function NSel(props) {
1614
1892
  const options = props.options || [];
1615
1893
  const value = props.value || "";
1616
1894
  const disabled = !!props.disabled;
1617
- const [open, setOpen] = (0, import_react7.useState)(false);
1618
- const [idx, setIdx] = (0, import_react7.useState)(-1);
1619
- const wrapRef = (0, import_react7.useRef)(null);
1620
- 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);
1621
1899
  let selectedLabel = "";
1622
1900
  for (let si = 0; si < options.length; si++) {
1623
1901
  if (options[si].id === value) selectedLabel = options[si].label;
1624
1902
  }
1625
- (0, import_react7.useEffect)(() => {
1903
+ (0, import_react9.useEffect)(() => {
1626
1904
  if (!open) return void 0;
1627
1905
  const onDown = (e) => {
1628
1906
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -1632,7 +1910,7 @@ var __defProp = Object.defineProperty;
1632
1910
  document.removeEventListener("mousedown", onDown);
1633
1911
  };
1634
1912
  }, [open]);
1635
- (0, import_react7.useEffect)(() => {
1913
+ (0, import_react9.useEffect)(() => {
1636
1914
  if (!open || !listRef.current) return;
1637
1915
  const el = listRef.current.querySelector('[data-active="1"]');
1638
1916
  if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
@@ -1695,8 +1973,8 @@ var __defProp = Object.defineProperty;
1695
1973
  const to = e.relatedTarget;
1696
1974
  if (!to || wrapRef.current && !wrapRef.current.contains(to)) setOpen(false);
1697
1975
  };
1698
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-mem-sel", style: props.style, ref: wrapRef, onKeyDown: onKey, onBlur, children: [
1699
- /* @__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)(
1700
1978
  "button",
1701
1979
  {
1702
1980
  type: "button",
@@ -1712,13 +1990,13 @@ var __defProp = Object.defineProperty;
1712
1990
  }
1713
1991
  },
1714
1992
  children: [
1715
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-select-label", children: selectedLabel || props.placeholder || "(请选择)" }),
1716
- /* @__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 })
1717
1995
  ]
1718
1996
  }
1719
1997
  ),
1720
- 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) => {
1721
- 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)(
1722
2000
  "button",
1723
2001
  {
1724
2002
  type: "button",
@@ -1736,8 +2014,8 @@ var __defProp = Object.defineProperty;
1736
2014
  if (idx !== i) setIdx(i);
1737
2015
  },
1738
2016
  children: [
1739
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-mem-pop-opt-label", children: o.label }),
1740
- 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
1741
2019
  ]
1742
2020
  },
1743
2021
  o.id
@@ -1747,7 +2025,7 @@ var __defProp = Object.defineProperty;
1747
2025
  }
1748
2026
 
1749
2027
  // client/src/tabs/RouteChainEditor.tsx
1750
- var import_jsx_runtime8 = require("react/jsx-runtime");
2028
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1751
2029
  var modelsCache = {};
1752
2030
  var EFFORT_VOCAB = ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"];
1753
2031
  var STY2 = {
@@ -1776,26 +2054,26 @@ var __defProp = Object.defineProperty;
1776
2054
  const disabled = !!props.disabled;
1777
2055
  const scope = props.scope ?? "global";
1778
2056
  const isLayer = scope !== "global";
1779
- const [info, setInfo] = (0, import_react8.useState)(null);
1780
- const [rows, setRows] = (0, import_react8.useState)(null);
1781
- const [rowErrs, setRowErrs] = (0, import_react8.useState)({});
1782
- const [err, setErr] = (0, import_react8.useState)(null);
1783
- const [manual, setManual] = (0, import_react8.useState)({ idx: -1, text: "" });
1784
- 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);
1785
2063
  function refreshInfo() {
1786
2064
  rpc("dsh-memory/llm-providers", {}).then((r) => {
1787
2065
  if (r && r.ok && pendingWrites.current === 0) setInfo(r.value);
1788
2066
  }).catch(() => {
1789
2067
  });
1790
2068
  }
1791
- (0, import_react8.useEffect)(() => {
2069
+ (0, import_react10.useEffect)(() => {
1792
2070
  refreshInfo();
1793
2071
  const timer = setInterval(refreshInfo, 5e3);
1794
2072
  return () => {
1795
2073
  clearInterval(timer);
1796
2074
  };
1797
2075
  }, [rpc]);
1798
- (0, import_react8.useEffect)(() => {
2076
+ (0, import_react10.useEffect)(() => {
1799
2077
  if (!info || !info.providers) return;
1800
2078
  info.providers.forEach((p) => {
1801
2079
  if (!p.id || modelsCache[p.id]) return;
@@ -1807,7 +2085,7 @@ var __defProp = Object.defineProperty;
1807
2085
  }, [rpc, info]);
1808
2086
  const layerView = isLayer && info && info.layerChains ? info.layerChains[scope] : null;
1809
2087
  const savedRows = isLayer ? layerView?.runtime ?? [] : info && info.chain ? info.chain.current : [];
1810
- (0, import_react8.useEffect)(() => {
2088
+ (0, import_react10.useEffect)(() => {
1811
2089
  if (rows === null && info && savedRows.length) {
1812
2090
  setRows(savedRows.map(copyRow));
1813
2091
  }
@@ -1955,17 +2233,17 @@ var __defProp = Object.defineProperty;
1955
2233
  providersById[p.id] = p;
1956
2234
  });
1957
2235
  function roRow(e, i) {
1958
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.roRow, children: [
1959
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY2.badge, children: i === 0 ? "主" : String(i + 1) }),
1960
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...STY2.mono, color: "var(--dsh-mem-text-2)" }, children: e.provider + " / " + e.model }),
1961
- /* @__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 : "跟随部署配置" })
1962
2240
  ] }, "ro" + i);
1963
2241
  }
1964
2242
  if (info.pinned) {
1965
2243
  const effPin = isLayer ? layerView?.effectiveChain ?? [] : info.chain && info.chain.effectiveChain || [];
1966
- 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: [
1967
2245
  effPin.map(roRow),
1968
- /* @__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 的配置。" })
1969
2247
  ] });
1970
2248
  }
1971
2249
  if (rows === null) {
@@ -1973,21 +2251,21 @@ var __defProp = Object.defineProperty;
1973
2251
  const lv = layerView;
1974
2252
  const src = lv?.source ?? "global";
1975
2253
  if (src === "static" && lv) {
1976
- 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: [
1977
2255
  lv.static.map((e, i) => roRow({ provider: e.provider, model: e.model, effort: e.reasoningEffort || "" }, i)),
1978
- /* @__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: "自定义本层链" }) })
1979
2257
  ] });
1980
2258
  }
1981
2259
  const previewRows = lv?.effectiveChain ?? [];
1982
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
1983
- previewRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.switchDesc, children: "本层跟随全局链,暂无可用路由。" }) : previewRows.map(roRow),
1984
- /* @__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: "自定义本层链" }) })
1985
2263
  ] });
1986
2264
  }
1987
2265
  const effFollow = info.chain && info.chain.effectiveChain || [];
1988
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
1989
- effFollow.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.switchDesc, children: "蒸馏跟随默认模型,未配置回退链。" }) : effFollow.map(roRow),
1990
- /* @__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: "编辑为运行时链" }) })
1991
2269
  ] });
1992
2270
  }
1993
2271
  const capped = rows.length >= 8;
@@ -2028,10 +2306,10 @@ var __defProp = Object.defineProperty;
2028
2306
  const effortOptions = [{ id: "", label: "跟随部署配置" }].concat(
2029
2307
  curEfforts.filter((k) => EFFORT_VOCAB.indexOf(k) >= 0).map((k) => ({ id: k, label: k }))
2030
2308
  );
2031
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...STY2.row, ...rowErrs[i] ? STY2.rowErr : null }, children: [
2032
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.line, children: [
2033
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: STY2.badge, children: isPrimary ? "主" : String(i + 1) }),
2034
- /* @__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)(
2035
2313
  NSel,
2036
2314
  {
2037
2315
  style: { flex: 1, minWidth: 150 },
@@ -2044,7 +2322,7 @@ var __defProp = Object.defineProperty;
2044
2322
  }
2045
2323
  }
2046
2324
  ),
2047
- !row2.provider ? null : manualInput ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2325
+ !row2.provider ? null : manualInput ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2048
2326
  NInput,
2049
2327
  {
2050
2328
  style: { flex: 1, minWidth: 150 },
@@ -2063,7 +2341,7 @@ var __defProp = Object.defineProperty;
2063
2341
  }
2064
2342
  }
2065
2343
  }
2066
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2344
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2067
2345
  NSel,
2068
2346
  {
2069
2347
  style: { flex: 1, minWidth: 150 },
@@ -2076,7 +2354,7 @@ var __defProp = Object.defineProperty;
2076
2354
  }
2077
2355
  }
2078
2356
  ),
2079
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2357
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2080
2358
  NSel,
2081
2359
  {
2082
2360
  style: { flexShrink: 0, width: 118 },
@@ -2090,8 +2368,8 @@ var __defProp = Object.defineProperty;
2090
2368
  }
2091
2369
  )
2092
2370
  ] }),
2093
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.actions, children: [
2094
- /* @__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)(
2095
2373
  NButton,
2096
2374
  {
2097
2375
  style: STY2.ico,
@@ -2103,7 +2381,7 @@ var __defProp = Object.defineProperty;
2103
2381
  children: "↑"
2104
2382
  }
2105
2383
  ),
2106
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2384
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2107
2385
  NButton,
2108
2386
  {
2109
2387
  style: STY2.ico,
@@ -2115,7 +2393,7 @@ var __defProp = Object.defineProperty;
2115
2393
  children: "↓"
2116
2394
  }
2117
2395
  ),
2118
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2396
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2119
2397
  NButton,
2120
2398
  {
2121
2399
  style: STY2.ico,
@@ -2128,14 +2406,14 @@ var __defProp = Object.defineProperty;
2128
2406
  }
2129
2407
  )
2130
2408
  ] }),
2131
- isPrimary && !row2.provider ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY2.note, children: "跟随默认模型" + (info.default ? ":" + info.default.provider + " / " + info.default.model : "") + "(档位跟随部署配置,选定模型后可单独设置)" }) : null,
2132
- !known ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: STY2.warn, children: "⚠ 供应商 " + row2.provider + " 已不在已注册路由中:该路由调用会失败并被链跳过(不阻止保存)。" }) : null,
2133
- 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
2134
2412
  ] }, "row" + i);
2135
2413
  });
2136
2414
  const effChain = isLayer ? layerView?.effectiveChain ?? [] : info.chain && info.chain.effectiveChain || [];
2137
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: STY2.wrap, children: [
2138
- /* @__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)(
2139
2417
  "div",
2140
2418
  {
2141
2419
  style: { ...S.switchDesc, marginBottom: 8 },
@@ -2144,9 +2422,9 @@ var __defProp = Object.defineProperty;
2144
2422
  }
2145
2423
  ),
2146
2424
  rowEls,
2147
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(NButton, { style: STY2.add, disabled: disabled || capped, onClick: addRow, children: capped ? "已达上限(8 条)" : "+ 添加回退路由" }),
2148
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 10 }, children: [
2149
- /* @__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)(
2150
2428
  NButton,
2151
2429
  {
2152
2430
  style: isLayer ? { ...STY2.ghost, color: "var(--dsh-mem-danger)", border: "1px solid var(--dsh-mem-danger)" } : STY2.ghost,
@@ -2155,13 +2433,13 @@ var __defProp = Object.defineProperty;
2155
2433
  children: isLayer ? "清除自定义 · 跟随全局" : "清空并跟随部署配置"
2156
2434
  }
2157
2435
  ),
2158
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: S.grow }),
2159
- /* @__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: "保存" })
2160
2438
  ] }),
2161
- err ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { ...STY2.warn, marginTop: 8 }, children: "✕ " + err }) : null,
2162
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" }, children: [
2163
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 4 }, children: "实际链" + (dirty ? "(保存后更新;当前显示已保存值)" : "") }),
2164
- /* @__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)(
2165
2443
  "div",
2166
2444
  {
2167
2445
  style: { fontSize: 12, color: "var(--dsh-mem-text-2)", wordBreak: "break-all", fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace" },
@@ -2173,7 +2451,7 @@ var __defProp = Object.defineProperty;
2173
2451
  }
2174
2452
 
2175
2453
  // client/src/tabs/DistillSettings.tsx
2176
- var import_jsx_runtime9 = require("react/jsx-runtime");
2454
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2177
2455
  var STY3 = {
2178
2456
  hint: { fontSize: 11, color: "var(--dsh-mem-text-3)", margin: "0 0 8px" },
2179
2457
  panelHead: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 8 },
@@ -2202,9 +2480,9 @@ var __defProp = Object.defineProperty;
2202
2480
  verticalAlign: "middle",
2203
2481
  flexShrink: 0
2204
2482
  };
2205
- if (props.kind === "runtime") return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...base, background: "var(--dsh-mem-accent)" } });
2206
- 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)" } });
2207
- 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)" } });
2208
2486
  }
2209
2487
  var LAYER_META = {
2210
2488
  l1: { seg: "L1", title: "L1 · 抽取 / 去重" },
@@ -2214,9 +2492,9 @@ var __defProp = Object.defineProperty;
2214
2492
  function DistillSettings(props) {
2215
2493
  const rpc = props.rpc;
2216
2494
  const disabled = !!props.disabled;
2217
- const [info, setInfo] = (0, import_react9.useState)(null);
2218
- const [tab, setTab] = (0, import_react9.useState)("g");
2219
- (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)(() => {
2220
2498
  let alive = true;
2221
2499
  const refresh = () => {
2222
2500
  rpc("dsh-memory/llm-providers", {}).then((r) => {
@@ -2239,46 +2517,46 @@ var __defProp = Object.defineProperty;
2239
2517
  ...["l1", "l2", "l3"].map((k) => ({
2240
2518
  key: k,
2241
2519
  title: LAYER_META[k].title + " · " + (dotOf(k) === "runtime" ? "运行时自定义" : dotOf(k) === "static" ? "部署 YAML 层链(只读)" : "跟随全局"),
2242
- label: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2243
- /* @__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) }),
2244
2522
  LAYER_META[k].seg
2245
2523
  ] })
2246
2524
  }))
2247
2525
  ];
2248
2526
  const chipTitle = (k) => dotOf(k) === "runtime" ? "本层走设置页自定义链" : dotOf(k) === "static" ? "本层走部署 YAML 层链(UI 只读,自定义可覆盖)" : "本层未单独配置,走全局默认链";
2249
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2250
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Segmented, { value: tab, options: segOptions, onChange: (k) => setTab(k) }),
2251
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: STY3.hint, children: [
2252
- /* @__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" }),
2253
2531
  " 自定义 · ",
2254
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dot, { kind: "static" }),
2532
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Dot, { kind: "static" }),
2255
2533
  " 部署 YAML · ",
2256
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Dot, { kind: "global" }),
2534
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Dot, { kind: "global" }),
2257
2535
  " 跟随全局",
2258
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { title: "每层实际链:运行时自定义 → 部署 YAML 层链 → 全局默认链,逐级兜底;部署 pin 时运行时编辑只读", children: "(层链优先于全局)" })
2536
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { title: "每层实际链:运行时自定义 → 部署 YAML 层链 → 全局默认链,逐级兜底;部署 pin 时运行时编辑只读", children: "(层链优先于全局)" })
2259
2537
  ] }),
2260
- tab === "g" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2261
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: STY3.panelHead, children: [
2262
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: STY3.panelTitle, children: "全局默认链" }),
2263
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { ...STY3.chip, ...STY3.chipAccent }, children: "运行时 · 可编辑" }),
2264
- /* @__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("、") : "当前无层使用" })
2265
2543
  ] }),
2266
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RouteChainEditor, { rpc, disabled }, "g"),
2267
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BudgetInputs, { rpc, disabled, data: props.data, setData: props.setData, onError: props.onError, scope: "input" }, "g-budget"),
2268
- info?.channel ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChannelEditor, { channel: info.channel, rpc, disabled }) : null
2269
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2270
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: STY3.panelHead, children: [
2271
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: STY3.panelTitle, children: LAYER_META[tab].title }),
2272
- /* @__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" : "跟随全局" })
2273
2551
  ] }),
2274
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RouteChainEditor, { rpc, disabled, scope: tab }, tab),
2275
- /* @__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")
2276
2554
  ] })
2277
2555
  ] });
2278
2556
  }
2279
2557
 
2280
2558
  // client/src/tabs/EmbeddingSection.tsx
2281
- var import_react10 = require("react");
2559
+ var import_react12 = require("react");
2282
2560
 
2283
2561
  // client/src/dimensions.ts
2284
2562
  var DIMS_MIN = 1;
@@ -2326,7 +2604,7 @@ var __defProp = Object.defineProperty;
2326
2604
  }
2327
2605
 
2328
2606
  // client/src/tabs/EmbeddingSection.tsx
2329
- var import_jsx_runtime10 = require("react/jsx-runtime");
2607
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2330
2608
  var RSTY = {
2331
2609
  block: { marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--dsh-mem-border)" },
2332
2610
  title: { fontSize: 12, fontWeight: 600, color: "var(--dsh-mem-text-3)", margin: "0 0 2px" },
@@ -2350,17 +2628,17 @@ var __defProp = Object.defineProperty;
2350
2628
  function EmbeddingSection(props) {
2351
2629
  const rpc = props.rpc;
2352
2630
  const loose = asLoose(rpc);
2353
- const [st, setSt] = (0, import_react10.useState)(null);
2354
- const [err, setErr] = (0, import_react10.useState)(null);
2355
- const busyPollRef = (0, import_react10.useRef)(null);
2356
- const [rBase, setRBase] = (0, import_react10.useState)("");
2357
- const [rModel, setRModel] = (0, import_react10.useState)("");
2358
- const [rDims, setRDims] = (0, import_react10.useState)("");
2359
- const [rKey, setRKey] = (0, import_react10.useState)("");
2360
- const remoteDirty = (0, import_react10.useRef)({ base: false, model: false, dims: false });
2361
- const baseTimer = (0, import_react10.useRef)(void 0);
2362
- const modelTimer = (0, import_react10.useRef)(void 0);
2363
- 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)(() => {
2364
2642
  rpc("dsh-memory/embedding-state-get", {}).then((r) => {
2365
2643
  if (r && r.ok && r.value && r.value.supported !== false) {
2366
2644
  const v = r.value;
@@ -2379,10 +2657,10 @@ var __defProp = Object.defineProperty;
2379
2657
  setErr(String(e && e.message || e));
2380
2658
  });
2381
2659
  }, [rpc]);
2382
- (0, import_react10.useEffect)(() => {
2660
+ (0, import_react12.useEffect)(() => {
2383
2661
  load();
2384
2662
  }, [load]);
2385
- (0, import_react10.useEffect)(() => {
2663
+ (0, import_react12.useEffect)(() => {
2386
2664
  let stopped = false;
2387
2665
  const busyFlag = { v: false };
2388
2666
  busyPollRef.current = busyFlag;
@@ -2398,11 +2676,11 @@ var __defProp = Object.defineProperty;
2398
2676
  busyPollRef.current = null;
2399
2677
  };
2400
2678
  }, [load]);
2401
- (0, import_react10.useEffect)(() => () => {
2679
+ (0, import_react12.useEffect)(() => () => {
2402
2680
  clearTimeout(baseTimer.current);
2403
2681
  clearTimeout(modelTimer.current);
2404
2682
  }, []);
2405
- (0, import_react10.useEffect)(() => {
2683
+ (0, import_react12.useEffect)(() => {
2406
2684
  if (!st || !st.remote) return;
2407
2685
  if (!remoteDirty.current.base) setRBase(st.remote.baseURL || "");
2408
2686
  if (!remoteDirty.current.model) setRModel(st.remote.model || "");
@@ -2463,15 +2741,15 @@ var __defProp = Object.defineProperty;
2463
2741
  });
2464
2742
  };
2465
2743
  if (err === "__unsupported__") {
2466
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2467
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "语义检索(嵌入)" }),
2468
- /* @__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: "存储处于降级状态,嵌入管理不可用。" })
2469
2747
  ] });
2470
2748
  }
2471
2749
  if (!st) {
2472
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2473
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "dsh-mem-rb-muted", children: err ? "嵌入状态读取失败:" + err : "嵌入状态读取中…" }),
2474
- 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
2475
2753
  ] });
2476
2754
  }
2477
2755
  const switchConfirm = "切换嵌入源后将按新模型重建向量索引(期间语义检索暂退化为关键词匹配,不影响对话)。确定切换?";
@@ -2497,40 +2775,40 @@ var __defProp = Object.defineProperty;
2497
2775
  const remote = st.remote;
2498
2776
  let runtimeRow = null;
2499
2777
  if (rt.phase === "installing") {
2500
- runtimeRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { marginTop: 8, fontSize: 12 }, children: [
2501
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2502
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "安装推理运行时中… 已耗时 " + Math.round(rt.elapsedMs / 1e3) + "s(约 100~200MB,视网络)" }),
2503
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2504
- /* @__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: "取消" })
2505
2783
  ] }),
2506
- /* @__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 输出…" })
2507
2785
  ] });
2508
2786
  } else if (rt.phase === "error") {
2509
- 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 || "未知") + "(重新切换嵌入源可重试)" });
2510
2788
  } else if (rt.phase === "ready") {
2511
- 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 + ")" });
2512
2790
  } else if (st.source === "local") {
2513
- 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)。" });
2514
2792
  }
2515
2793
  let applyRow = null;
2516
2794
  if (ap.phase === "warming") {
2517
- 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: "加载嵌入模型中…(首次需数秒)" });
2518
2796
  } else if (ap.phase === "switching") {
2519
- 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: "切换嵌入源中…" });
2520
2798
  } else if (ap.phase === "error") {
2521
- 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 + "(已保存的嵌入源不变,重启后仍按原源运行)" });
2522
2800
  } else if (st.reindex && st.reindex.running) {
2523
2801
  const rj = st.reindex;
2524
2802
  const rDone = rj.l1Done + rj.l0Done;
2525
2803
  const rTotal = rj.l1Total + rj.l0Total;
2526
2804
  const rPct = rTotal > 0 ? Math.round(rDone / rTotal * 100) : 0;
2527
- applyRow = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { marginTop: 8 }, children: [
2528
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2529
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-rb-muted", children: "重嵌入中 L1 " + rj.l1Done + "/" + rj.l1Total + " · L0 " + rj.l0Done + "/" + rj.l0Total + "(" + rPct + "%)" }),
2530
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2531
- /* @__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: "取消" })
2532
2810
  ] }),
2533
- /* @__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 + "%" } }) }) })
2534
2812
  ] });
2535
2813
  }
2536
2814
  const modelCards = st.models.map((m) => {
@@ -2539,24 +2817,24 @@ var __defProp = Object.defineProperty;
2539
2817
  const pct = mDl && dl.overallTotal > 0 ? Math.round(dl.overallReceived / dl.overallTotal * 100) : 0;
2540
2818
  let action;
2541
2819
  if (mDl) {
2542
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, minWidth: 200 }, children: [
2543
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2544
- /* @__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" : "") + ")" }),
2545
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2546
- /* @__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: "取消" })
2547
2825
  ] }),
2548
- /* @__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 + "%" } }) }) })
2549
2827
  ] });
2550
2828
  } else if (isActive) {
2551
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2552
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-work-task", children: "使用中" }),
2553
- localInfo && localInfo.state === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "dsh-mem-rb-muted", children: "模型加载中…" }) : null,
2554
- localInfo && localInfo.state === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" }, children: "加载失败:" + (localInfo.error || "") }) : null,
2555
- 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
2556
2834
  ] });
2557
2835
  } else if (m.state === "downloaded") {
2558
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2559
- /* @__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)(
2560
2838
  NButton,
2561
2839
  {
2562
2840
  disabled: ap.busy,
@@ -2564,7 +2842,7 @@ var __defProp = Object.defineProperty;
2564
2842
  children: "启用"
2565
2843
  }
2566
2844
  ),
2567
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2845
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2568
2846
  NButton,
2569
2847
  {
2570
2848
  disabled: dlActive,
@@ -2574,7 +2852,7 @@ var __defProp = Object.defineProperty;
2574
2852
  )
2575
2853
  ] });
2576
2854
  } else {
2577
- action = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2855
+ action = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2578
2856
  NButton,
2579
2857
  {
2580
2858
  disabled: dlActive || !st.ceilings.local,
@@ -2584,16 +2862,16 @@ var __defProp = Object.defineProperty;
2584
2862
  }
2585
2863
  );
2586
2864
  }
2587
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2865
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2588
2866
  "div",
2589
2867
  {
2590
2868
  style: { ...S.flexRow, padding: "8px 0", borderBottom: "1px solid var(--dsh-mem-border)", flexWrap: "wrap" },
2591
2869
  children: [
2592
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { minWidth: 150 }, children: [
2593
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600 }, children: m.name }),
2594
- /* @__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 })
2595
2873
  ] }),
2596
- /* @__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 }),
2597
2875
  action
2598
2876
  ]
2599
2877
  },
@@ -2606,11 +2884,11 @@ var __defProp = Object.defineProperty;
2606
2884
  const reindexRunning = !!(st.reindex && st.reindex.running);
2607
2885
  const canRebuild = vecOk && st.source !== "off" && !reindexRunning && !st.apply.busy;
2608
2886
  const rebuildHint = !vecOk ? "向量能力不可用" : st.source === "off" ? "嵌入源已关闭,请先启用" : reindexRunning ? "重建已在进行中" : st.apply.busy ? "嵌入源切换进行中" : "只补缺失向量;已有向量不动。零向量内容会被跳过(重试无意义)";
2609
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2610
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: S.flexRow, children: [
2611
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "语义检索(嵌入源)" }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.grow }),
2613
- /* @__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)(
2614
2892
  Segmented,
2615
2893
  {
2616
2894
  value: st.source,
@@ -2623,18 +2901,18 @@ var __defProp = Object.defineProperty;
2623
2901
  }
2624
2902
  )
2625
2903
  ] }),
2626
- /* @__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 + ")" : "") }),
2627
- st.activeNote ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 4, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: st.activeNote }) : null,
2628
- err && err !== "__unsupported__" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: err }) : null,
2629
- 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,
2630
2908
  runtimeRow,
2631
2909
  applyRow,
2632
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.block, children: [
2633
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.title, children: "远程连接(生效值;运行时覆盖优先于部署 YAML)" }),
2634
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.note, children: "生效端点 " + (remote.baseURL || "(未配置)") + " · 模型 " + (remote.model || "(未配置)") + " · " + (remote.dimensions > 0 ? remote.dimensions + " 维" : "维度跟随部署") + (remote.apiKeySet ? " · 密钥已设置" : " · 密钥未设置") }),
2635
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.row, children: [
2636
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "端点 URL" }),
2637
- /* @__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)(
2638
2916
  NInput,
2639
2917
  {
2640
2918
  style: RSTY.input,
@@ -2651,9 +2929,9 @@ var __defProp = Object.defineProperty;
2651
2929
  }
2652
2930
  )
2653
2931
  ] }),
2654
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.row, children: [
2655
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "模型名" }),
2656
- /* @__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)(
2657
2935
  NInput,
2658
2936
  {
2659
2937
  style: RSTY.input,
@@ -2669,8 +2947,8 @@ var __defProp = Object.defineProperty;
2669
2947
  }
2670
2948
  }
2671
2949
  ),
2672
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "维度" }),
2673
- /* @__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)(
2674
2952
  NInput,
2675
2953
  {
2676
2954
  style: { width: 90, minWidth: 0, flex: "0 1 90px" },
@@ -2690,9 +2968,9 @@ var __defProp = Object.defineProperty;
2690
2968
  }
2691
2969
  )
2692
2970
  ] }),
2693
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.row, children: [
2694
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.label, children: "API Key" }),
2695
- /* @__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)(
2696
2974
  NInput,
2697
2975
  {
2698
2976
  style: RSTY.input,
@@ -2707,8 +2985,8 @@ var __defProp = Object.defineProperty;
2707
2985
  onBlur: commitRemoteKey
2708
2986
  }
2709
2987
  ),
2710
- remote.apiKeySet ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.chipAccent, children: "已设置" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: RSTY.chipMuted, children: "未设置" }),
2711
- /* @__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)(
2712
2990
  NButton,
2713
2991
  {
2714
2992
  title: "写入空串清除运行时密钥(部署 YAML 的密钥不受影响)",
@@ -2718,12 +2996,12 @@ var __defProp = Object.defineProperty;
2718
2996
  )
2719
2997
  ] })
2720
2998
  ] }),
2721
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: S.panelLabel, children: "本地模型目录(下载后离线可用,不随插件分发)" }),
2999
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: S.panelLabel, children: "本地模型目录(下载后离线可用,不随插件分发)" }),
2722
3000
  modelCards,
2723
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RSTY.block, children: [
2724
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.title, children: "向量索引" }),
2725
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RSTY.note, children: vecNote }),
2726
- /* @__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)(
2727
3005
  NButton,
2728
3006
  {
2729
3007
  disabled: !canRebuild,
@@ -2737,8 +3015,8 @@ var __defProp = Object.defineProperty;
2737
3015
  }
2738
3016
 
2739
3017
  // client/src/tabs/RebuildPanel.tsx
2740
- var import_react11 = require("react");
2741
- var import_jsx_runtime11 = require("react/jsx-runtime");
3018
+ var import_react13 = require("react");
3019
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2742
3020
  var RB_PHASE_LABEL = {
2743
3021
  preparing: "准备中(归档旧数据 · 清空检索库)",
2744
3022
  distilling: "分块蒸馏中",
@@ -2746,21 +3024,21 @@ var __defProp = Object.defineProperty;
2746
3024
  };
2747
3025
  function RebuildPanel(props) {
2748
3026
  const rpc = props.rpc;
2749
- const [rbRaw, setRb] = (0, import_react11.useState)(null);
2750
- const [confirmOpen, setConfirmOpen] = (0, import_react11.useState)(false);
2751
- const [busy, setBusy] = (0, import_react11.useState)(false);
2752
- const [rbError, setRbError] = (0, import_react11.useState)(null);
2753
- 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)(() => {
2754
3032
  rpc("dsh-memory/rebuild-status", {}).then((r) => {
2755
3033
  if (r && r.ok) setRb(r.value);
2756
3034
  }).catch(() => {
2757
3035
  });
2758
3036
  }, [rpc]);
2759
- (0, import_react11.useEffect)(() => {
3037
+ (0, import_react13.useEffect)(() => {
2760
3038
  refresh();
2761
3039
  }, [refresh]);
2762
3040
  const running = !!(rbRaw && rbRaw.running);
2763
- (0, import_react11.useEffect)(() => {
3041
+ (0, import_react13.useEffect)(() => {
2764
3042
  if (!running) return;
2765
3043
  const timer = setInterval(refresh, 1500);
2766
3044
  return () => {
@@ -2805,11 +3083,11 @@ var __defProp = Object.defineProperty;
2805
3083
  } else if (!running && rb.phase === "failed") {
2806
3084
  lastNote = "上次重建:失败:" + (rb.error || "未知错误");
2807
3085
  }
2808
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2809
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
2810
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "重建记忆" }),
2811
- /* @__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;旧数据先归档(不删除)" }),
2812
- 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)(
2813
3091
  NButton,
2814
3092
  {
2815
3093
  disabled: busy || empty,
@@ -2822,13 +3100,13 @@ var __defProp = Object.defineProperty;
2822
3100
  }
2823
3101
  )
2824
3102
  ] }),
2825
- running ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
2826
- /* @__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 + "%" } }) }),
2827
- /* @__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 + " 条" })
2828
3106
  ] }) : null,
2829
- lastNote ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
2830
- rbError ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rbError }) : null,
2831
- 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)(
2832
3110
  NModal,
2833
3111
  {
2834
3112
  open: true,
@@ -2837,7 +3115,7 @@ var __defProp = Object.defineProperty;
2837
3115
  },
2838
3116
  title: "确认重建全部记忆?",
2839
3117
  footer: [
2840
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3118
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2841
3119
  NButton,
2842
3120
  {
2843
3121
  onClick: () => {
@@ -2847,15 +3125,15 @@ var __defProp = Object.defineProperty;
2847
3125
  },
2848
3126
  "cancel"
2849
3127
  ),
2850
- /* @__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")
2851
3129
  ],
2852
3130
  children: [
2853
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
3131
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
2854
3132
  "将以 L0 原始对话为事实源重新蒸馏:",
2855
- /* @__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 + " 条消息" }),
2856
3134
  ",预计 ≥" + rb.estCalls + " 次蒸馏调用。"
2857
3135
  ] }),
2858
- /* @__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.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。" })
2859
3137
  ]
2860
3138
  }
2861
3139
  ) : null
@@ -2863,8 +3141,8 @@ var __defProp = Object.defineProperty;
2863
3141
  }
2864
3142
 
2865
3143
  // client/src/tabs/RuminatePanel.tsx
2866
- var import_react12 = require("react");
2867
- var import_jsx_runtime12 = require("react/jsx-runtime");
3144
+ var import_react14 = require("react");
3145
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2868
3146
  var RM_PHASE_LABEL = {
2869
3147
  refreshing: "轻量刷新中",
2870
3148
  distilling: "L1 蒸馏中",
@@ -2885,11 +3163,11 @@ var __defProp = Object.defineProperty;
2885
3163
  var RM_IDLE_INTERVAL = 3e4;
2886
3164
  function RuminatePanel(props) {
2887
3165
  const rpc = props.rpc;
2888
- const [rmRaw, setRm] = (0, import_react12.useState)(null);
2889
- const [confirmOpen, setConfirmOpen] = (0, import_react12.useState)(false);
2890
- const [busy, setBusy] = (0, import_react12.useState)(false);
2891
- const [rmError, setRmError] = (0, import_react12.useState)(null);
2892
- 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)(() => {
2893
3171
  return rpc("dsh-memory/ruminate-status", {}).then((r) => {
2894
3172
  if (r && r.ok) {
2895
3173
  const st = r.value;
@@ -2904,14 +3182,14 @@ var __defProp = Object.defineProperty;
2904
3182
  }).catch(() => {
2905
3183
  });
2906
3184
  }, [rpc]);
2907
- const pollRef = (0, import_react12.useRef)(1500);
2908
- const backoffRef = (0, import_react12.useRef)(1);
2909
- const lastDetailRef = (0, import_react12.useRef)(null);
2910
- (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)(() => {
2911
3189
  refresh();
2912
3190
  }, [refresh]);
2913
3191
  const running = !!(rmRaw && (rmRaw.running || RM_RUNNING_PHASES.indexOf(rmRaw.phase) >= 0));
2914
- (0, import_react12.useEffect)(() => {
3192
+ (0, import_react14.useEffect)(() => {
2915
3193
  if (!running) return;
2916
3194
  let alive = true;
2917
3195
  let timer;
@@ -2930,13 +3208,13 @@ var __defProp = Object.defineProperty;
2930
3208
  lastDetailRef.current = null;
2931
3209
  };
2932
3210
  }, [running, refresh]);
2933
- (0, import_react12.useEffect)(() => {
3211
+ (0, import_react14.useEffect)(() => {
2934
3212
  if (running) return;
2935
3213
  const timer = setInterval(refresh, RM_IDLE_INTERVAL);
2936
3214
  return () => clearInterval(timer);
2937
3215
  }, [running, refresh]);
2938
- const [, setTick] = (0, import_react12.useState)(0);
2939
- (0, import_react12.useEffect)(() => {
3216
+ const [, setTick] = (0, import_react14.useState)(0);
3217
+ (0, import_react14.useEffect)(() => {
2940
3218
  if (!running) return;
2941
3219
  const timer = setInterval(() => setTick((n) => n + 1), 1e3);
2942
3220
  return () => clearInterval(timer);
@@ -2987,11 +3265,11 @@ var __defProp = Object.defineProperty;
2987
3265
  }
2988
3266
  const unit = rm.phase === "refreshing" || rm.phase === "relabeling" ? "步" : "会话";
2989
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 + "%)" : "进行中…";
2990
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dsh-mem-rb-card", children: [
2991
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
2992
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { fontWeight: 600, whiteSpace: "nowrap" }, children: "反刍整理" }),
2993
- /* @__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" }),
2994
- 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)(
2995
3273
  NButton,
2996
3274
  {
2997
3275
  disabled: busy,
@@ -3004,27 +3282,27 @@ var __defProp = Object.defineProperty;
3004
3282
  }
3005
3283
  )
3006
3284
  ] }),
3007
- running ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
3008
- /* @__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)(
3009
3287
  "div",
3010
3288
  {
3011
3289
  className: "dsh-mem-rb-fill" + (indet ? " dsh-mem-rb-fill-indet" : ""),
3012
3290
  style: { width: indet ? "100%" : barPct + "%" }
3013
3291
  }
3014
3292
  ) }),
3015
- /* @__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 + " 条" })
3016
3294
  ] }) : null,
3017
- running && rm.detail ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 6, whiteSpace: "normal" }, children: "当前:" + rm.detail }) : null,
3018
- lastNote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 }, children: lastNote }) : null,
3019
- rmError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" }, children: rmError }) : null,
3020
- 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)(
3021
3299
  NModal,
3022
3300
  {
3023
3301
  open: true,
3024
3302
  onClose: () => setConfirmOpen(false),
3025
3303
  title: "确认反刍整理?",
3026
3304
  footer: [
3027
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3305
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3028
3306
  NButton,
3029
3307
  {
3030
3308
  onClick: () => setConfirmOpen(false),
@@ -3032,11 +3310,11 @@ var __defProp = Object.defineProperty;
3032
3310
  },
3033
3311
  "cancel"
3034
3312
  ),
3035
- /* @__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")
3036
3314
  ],
3037
3315
  children: [
3038
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: "将扫描未蒸馏缓冲,按会话逐个跑蒸馏管线,完成后强制 L2 场景整合与 L3 画像更新。" }),
3039
- /* @__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 刷新。" })
3040
3318
  ]
3041
3319
  }
3042
3320
  ) : null
@@ -3044,13 +3322,13 @@ var __defProp = Object.defineProperty;
3044
3322
  }
3045
3323
 
3046
3324
  // client/src/tabs/OverviewTab.tsx
3047
- var import_jsx_runtime13 = require("react/jsx-runtime");
3325
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3048
3326
  function OverviewTab(props) {
3049
3327
  const rpc = props.rpc;
3050
- const [stats, setStats] = (0, import_react13.useState)(null);
3051
- const [settingsData, setSettingsData] = (0, import_react13.useState)(null);
3052
- const [error, setError] = (0, import_react13.useState)(null);
3053
- 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)(() => {
3054
3332
  rpc("dsh-memory/stats", {}).then((r) => {
3055
3333
  if (r && r.ok) setStats(r.value);
3056
3334
  else setError(r && r.error ? r.error.message : "RPC error");
@@ -3062,7 +3340,7 @@ var __defProp = Object.defineProperty;
3062
3340
  }).catch(() => {
3063
3341
  });
3064
3342
  }, [rpc]);
3065
- (0, import_react13.useEffect)(() => {
3343
+ (0, import_react15.useEffect)(() => {
3066
3344
  load();
3067
3345
  const timer = setInterval(load, 5e3);
3068
3346
  return () => {
@@ -3123,10 +3401,11 @@ var __defProp = Object.defineProperty;
3123
3401
  const toolRuminate = settingsData && settingsData.settings ? settingsData.settings.toolRuminate !== false : true;
3124
3402
  const toolConflict = settingsData && settingsData.settings ? settingsData.settings.toolConflict !== false : true;
3125
3403
  const toolMutate = settingsData && settingsData.settings ? settingsData.settings.toolMutate !== false : true;
3126
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
3127
- 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: [
3128
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "记忆模式" }),
3129
- /* @__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)(
3130
3409
  SwitchRow,
3131
3410
  {
3132
3411
  label: "记忆模式",
@@ -3137,7 +3416,7 @@ var __defProp = Object.defineProperty;
3137
3416
  }
3138
3417
  }
3139
3418
  ),
3140
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3419
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3141
3420
  SwitchRow,
3142
3421
  {
3143
3422
  label: "捕获",
@@ -3149,7 +3428,7 @@ var __defProp = Object.defineProperty;
3149
3428
  }
3150
3429
  }
3151
3430
  ),
3152
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3431
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3153
3432
  SwitchRow,
3154
3433
  {
3155
3434
  label: "蒸馏",
@@ -3161,7 +3440,7 @@ var __defProp = Object.defineProperty;
3161
3440
  }
3162
3441
  }
3163
3442
  ),
3164
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3443
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3165
3444
  SwitchRow,
3166
3445
  {
3167
3446
  label: "召回",
@@ -3173,8 +3452,8 @@ var __defProp = Object.defineProperty;
3173
3452
  }
3174
3453
  }
3175
3454
  ),
3176
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "高权限模式" }),
3177
- /* @__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)(
3178
3457
  SwitchRow,
3179
3458
  {
3180
3459
  label: "高权限模式",
@@ -3185,8 +3464,20 @@ var __defProp = Object.defineProperty;
3185
3464
  }
3186
3465
  }
3187
3466
  ),
3188
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "工具封印" }),
3189
- /* @__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)(
3190
3481
  SwitchRow,
3191
3482
  {
3192
3483
  label: "Room 管理工具",
@@ -3197,7 +3488,7 @@ var __defProp = Object.defineProperty;
3197
3488
  }
3198
3489
  }
3199
3490
  ),
3200
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3491
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3201
3492
  SwitchRow,
3202
3493
  {
3203
3494
  label: "图谱工具",
@@ -3208,7 +3499,7 @@ var __defProp = Object.defineProperty;
3208
3499
  }
3209
3500
  }
3210
3501
  ),
3211
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3502
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3212
3503
  SwitchRow,
3213
3504
  {
3214
3505
  label: "反刍工具",
@@ -3219,7 +3510,7 @@ var __defProp = Object.defineProperty;
3219
3510
  }
3220
3511
  }
3221
3512
  ),
3222
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3513
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3223
3514
  SwitchRow,
3224
3515
  {
3225
3516
  label: "冲突工具",
@@ -3230,7 +3521,7 @@ var __defProp = Object.defineProperty;
3230
3521
  }
3231
3522
  }
3232
3523
  ),
3233
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3524
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3234
3525
  SwitchRow,
3235
3526
  {
3236
3527
  label: "写删工具",
@@ -3241,8 +3532,8 @@ var __defProp = Object.defineProperty;
3241
3532
  }
3242
3533
  }
3243
3534
  ),
3244
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "人工冲突裁决" }),
3245
- /* @__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)(
3246
3537
  SwitchRow,
3247
3538
  {
3248
3539
  label: "人工冲突裁决",
@@ -3253,42 +3544,42 @@ var __defProp = Object.defineProperty;
3253
3544
  }
3254
3545
  }
3255
3546
  ),
3256
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "蒸馏参数" }),
3257
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DistillSettings, { rpc, disabled: !master, data: settingsData, setData: setSettingsData, onError: setError }),
3258
- 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
3259
3550
  ] }) : null,
3260
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(EmbeddingSection, { rpc }),
3261
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RebuildPanel, { rpc }),
3262
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RuminatePanel, { rpc }),
3263
- degraded ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "⚠ " + stats.message + "。上方数据为最后一次成功读取的值,记忆功能当前未工作。" }) : null,
3264
- 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: [
3265
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "记忆概况" }),
3266
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.statGrid, children: tiles.map((t) => {
3267
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "dsh-mem-card", style: S.statTile, children: [
3268
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.statNum, children: t.num }),
3269
- /* @__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 })
3270
3561
  ] }, t.label);
3271
3562
  }) }),
3272
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: S.panelLabel, children: "运行状态" }),
3563
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.panelLabel, children: "运行状态" }),
3273
3564
  infos.map((row2) => {
3274
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: S.infoRow, children: [
3275
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: S.infoKey, children: row2[0] }),
3276
- /* @__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] })
3277
3568
  ] }, row2[0]);
3278
3569
  })
3279
3570
  ] }),
3280
- /* @__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 工具查询。" })
3281
3572
  ] });
3282
3573
  }
3283
3574
 
3284
3575
  // client/src/tabs/PersonaTab.tsx
3285
- var import_react14 = require("react");
3286
- var import_jsx_runtime14 = require("react/jsx-runtime");
3576
+ var import_react16 = require("react");
3577
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3287
3578
  function PersonaTab(props) {
3288
3579
  const rpc = props.rpc;
3289
- const [content, setContent] = (0, import_react14.useState)(null);
3290
- const [error, setError] = (0, import_react14.useState)(null);
3291
- 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)(() => {
3292
3583
  setError(null);
3293
3584
  rpc("dsh-memory/persona", {}).then((r) => {
3294
3585
  if (r && r.ok) setContent(r.value.content);
@@ -3297,23 +3588,23 @@ var __defProp = Object.defineProperty;
3297
3588
  setError(String(e && e.message || e));
3298
3589
  });
3299
3590
  }, [rpc]);
3300
- (0, import_react14.useEffect)(() => {
3591
+ (0, import_react16.useEffect)(() => {
3301
3592
  load();
3302
3593
  }, [load]);
3303
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3304
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3305
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: S.muted, children: error ? "加载失败" : content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像" }),
3306
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: S.grow }),
3307
- /* @__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: "刷新" })
3308
3599
  ] }),
3309
- error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { ...S.error, marginBottom: 10 }, children: "画像读取失败:" + error + "(点右上“刷新”重试)" }) : null,
3310
- 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 会自动产出。" })
3311
3602
  ] });
3312
3603
  }
3313
3604
 
3314
3605
  // client/src/tabs/RecordsTab.tsx
3315
- var import_react15 = require("react");
3316
- var import_jsx_runtime15 = require("react/jsx-runtime");
3606
+ var import_react17 = require("react");
3607
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3317
3608
  function retiredSelOf(r) {
3318
3609
  if (r === "active") return false;
3319
3610
  if (r === "retired") return true;
@@ -3332,29 +3623,29 @@ var __defProp = Object.defineProperty;
3332
3623
  function RecordsTab(props) {
3333
3624
  const rpc = props.rpc;
3334
3625
  const limit = 50;
3335
- const [items, setItems] = (0, import_react15.useState)([]);
3336
- const [hasMore, setHasMore] = (0, import_react15.useState)(false);
3337
- const [total, setTotal] = (0, import_react15.useState)(null);
3338
- const [sceneOptions, setSceneOptions] = (0, import_react15.useState)([]);
3339
- const [loading, setLoading] = (0, import_react15.useState)(false);
3340
- const [truncated, setTruncated] = (0, import_react15.useState)(false);
3341
- const [error, setError] = (0, import_react15.useState)(null);
3342
- const [expandedId, setExpandedId] = (0, import_react15.useState)(null);
3343
- const [sel, setSel] = (0, import_react15.useState)(/* @__PURE__ */ new Set());
3344
- const [hiPriv, setHiPriv] = (0, import_react15.useState)(false);
3345
- const [hiPrivBusy, setHiPrivBusy] = (0, import_react15.useState)(false);
3346
- const [query, setQuery] = (0, import_react15.useState)("");
3347
- const [typeFilter, setTypeFilter] = (0, import_react15.useState)("");
3348
- const [sceneFilter, setSceneFilter] = (0, import_react15.useState)("");
3349
- const [hallFilter, setHallFilter] = (0, import_react15.useState)([]);
3350
- const [tagFilter, setTagFilter] = (0, import_react15.useState)("");
3351
- const [untaggedOnly, setUntaggedOnly] = (0, import_react15.useState)(false);
3352
- const [orphanCount, setOrphanCount] = (0, import_react15.useState)(0);
3353
- const [retiredFilter, setRetiredFilter] = (0, import_react15.useState)("");
3354
- const [wingCatalog, setHallCatalog] = (0, import_react15.useState)(null);
3355
- const [last, setLast] = (0, import_react15.useState)({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" });
3356
- const seqRef = (0, import_react15.useRef)(0);
3357
- 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)(
3358
3649
  (conds, offset, append) => {
3359
3650
  setLoading(true);
3360
3651
  setError(null);
@@ -3403,23 +3694,23 @@ var __defProp = Object.defineProperty;
3403
3694
  setLast(conds);
3404
3695
  fetchPage(conds, 0, false);
3405
3696
  };
3406
- const loadRooms = (0, import_react15.useCallback)(() => {
3697
+ const loadRooms = (0, import_react17.useCallback)(() => {
3407
3698
  rpc("dsh-memory/rooms-get", {}).then((r) => {
3408
3699
  if (r && r.ok) setOrphanCount(r.value.orphanCount ?? 0);
3409
3700
  }).catch(() => {
3410
3701
  });
3411
3702
  }, [rpc]);
3412
- (0, import_react15.useEffect)(() => {
3703
+ (0, import_react17.useEffect)(() => {
3413
3704
  fetchPage({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" }, 0, false);
3414
3705
  loadRooms();
3415
3706
  }, [fetchPage, loadRooms]);
3416
- const loadHiPriv = (0, import_react15.useCallback)(() => {
3707
+ const loadHiPriv = (0, import_react17.useCallback)(() => {
3417
3708
  rpc("dsh-memory/settings-get", {}).then((r) => {
3418
3709
  if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
3419
3710
  }).catch(() => {
3420
3711
  });
3421
3712
  }, [rpc]);
3422
- (0, import_react15.useEffect)(() => {
3713
+ (0, import_react17.useEffect)(() => {
3423
3714
  loadHiPriv();
3424
3715
  }, [loadHiPriv]);
3425
3716
  const toggleHiPriv = () => {
@@ -3480,11 +3771,11 @@ var __defProp = Object.defineProperty;
3480
3771
  } else if (r) setError(r.error ? r.error.message : "退场失败");
3481
3772
  }).catch((e) => setError(String(e && e.message || e)));
3482
3773
  };
3483
- const [retired, setRetired] = (0, import_react15.useState)([]);
3484
- const [retiredTotal, setRetiredTotal] = (0, import_react15.useState)(0);
3485
- const [showRetired, setShowRetired] = (0, import_react15.useState)(false);
3486
- const [retiredBusy, setRetiredBusy] = (0, import_react15.useState)(false);
3487
- 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)(() => {
3488
3779
  setRetiredBusy(true);
3489
3780
  rpc("dsh-memory/records-retired", { limit: 100, offset: 0 }).then((r) => {
3490
3781
  if (r && r.ok) {
@@ -3493,7 +3784,7 @@ var __defProp = Object.defineProperty;
3493
3784
  } else if (r) setError(r.error ? r.error.message : "已退场列表加载失败");
3494
3785
  }).catch((e) => setError(String(e && e.message || e))).finally(() => setRetiredBusy(false));
3495
3786
  }, [rpc]);
3496
- (0, import_react15.useEffect)(() => {
3787
+ (0, import_react17.useEffect)(() => {
3497
3788
  if (showRetired) loadRetired();
3498
3789
  }, [showRetired, loadRetired]);
3499
3790
  const restoreRecords = (ids) => {
@@ -3513,11 +3804,11 @@ var __defProp = Object.defineProperty;
3513
3804
  unknown: "已退场"
3514
3805
  };
3515
3806
  const countText = total !== null ? "共 " + total + " 条" : items.length + " 条" + (hasMore ? "+" : "");
3516
- const [usageSummary, setUsageSummary] = (0, import_react15.useState)(null);
3807
+ const [usageSummary, setUsageSummary] = (0, import_react17.useState)(null);
3517
3808
  const selCount = sel.size;
3518
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
3519
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.toolbar, children: [
3520
- /* @__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)(
3521
3812
  NInput,
3522
3813
  {
3523
3814
  style: { flex: 1, minWidth: 160 },
@@ -3531,7 +3822,7 @@ var __defProp = Object.defineProperty;
3531
3822
  }
3532
3823
  }
3533
3824
  ),
3534
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3825
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3535
3826
  NSel,
3536
3827
  {
3537
3828
  style: { maxWidth: 200 },
@@ -3544,7 +3835,7 @@ var __defProp = Object.defineProperty;
3544
3835
  onChange: setTypeFilter
3545
3836
  }
3546
3837
  ),
3547
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3838
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3548
3839
  NSel,
3549
3840
  {
3550
3841
  style: { maxWidth: 220 },
@@ -3557,7 +3848,7 @@ var __defProp = Object.defineProperty;
3557
3848
  onChange: setSceneFilter
3558
3849
  }
3559
3850
  ),
3560
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3851
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3561
3852
  WingMultiSelect,
3562
3853
  {
3563
3854
  options: wingCatalog ?? Array.from(new Set(items.map((m) => m.hall).filter((h) => !!h))).map((id) => ({ id, label: id })),
@@ -3565,8 +3856,8 @@ var __defProp = Object.defineProperty;
3565
3856
  onChange: setHallFilter
3566
3857
  }
3567
3858
  ),
3568
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(NButton, { onClick: search, children: "搜索" }),
3569
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3859
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(NButton, { onClick: search, children: "搜索" }),
3860
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3570
3861
  NButton,
3571
3862
  {
3572
3863
  style: {
@@ -3580,8 +3871,8 @@ var __defProp = Object.defineProperty;
3580
3871
  }
3581
3872
  )
3582
3873
  ] }),
3583
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { marginBottom: 10, display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
3584
- /* @__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)(
3585
3876
  "button",
3586
3877
  {
3587
3878
  type: "button",
@@ -3606,10 +3897,10 @@ var __defProp = Object.defineProperty;
3606
3897
  children: "无绑定 · " + orphanCount
3607
3898
  }
3608
3899
  ),
3609
- (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: [
3610
3901
  "筛选中:",
3611
3902
  untaggedOnly ? "无绑定" : tagFilter,
3612
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3903
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3613
3904
  "button",
3614
3905
  {
3615
3906
  type: "button",
@@ -3626,7 +3917,7 @@ var __defProp = Object.defineProperty;
3626
3917
  }
3627
3918
  )
3628
3919
  ] }),
3629
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3920
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3630
3921
  "button",
3631
3922
  {
3632
3923
  type: "button",
@@ -3655,15 +3946,15 @@ var __defProp = Object.defineProperty;
3655
3946
  children: "导出孤儿"
3656
3947
  }
3657
3948
  ),
3658
- /* @__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」标签页。" })
3659
3950
  ] }),
3660
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3661
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
3662
- usageSummary ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, title: "老化权重效果口径:参与召回尝试的记忆中,进 topN(被注入上下文)的比例", children: "召回使用 " + usageSummary.used + "/" + usageSummary.attempted + "(" + usageSummary.rate + "%)" }) : null,
3663
- /* @__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: "退场筛查" }),
3664
3955
  [["", "全部"], ["active", "仅活跃"], ["retired", "仅退场"]].map(([val, label]) => {
3665
3956
  const on = retiredFilter === val;
3666
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3957
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3667
3958
  "button",
3668
3959
  {
3669
3960
  type: "button",
@@ -3683,7 +3974,7 @@ var __defProp = Object.defineProperty;
3683
3974
  val
3684
3975
  );
3685
3976
  }),
3686
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3977
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3687
3978
  NButton,
3688
3979
  {
3689
3980
  style: selCount > 0 ? { color: "var(--dsh-mem-danger)" } : void 0,
@@ -3693,7 +3984,7 @@ var __defProp = Object.defineProperty;
3693
3984
  children: "删除选中" + (selCount > 0 ? "(" + selCount + ")" : "")
3694
3985
  }
3695
3986
  ),
3696
- selCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3987
+ selCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3697
3988
  NButton,
3698
3989
  {
3699
3990
  onClick: () => {
@@ -3702,8 +3993,8 @@ var __defProp = Object.defineProperty;
3702
3993
  children: "清空选择"
3703
3994
  }
3704
3995
  ) : null,
3705
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3706
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3996
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
3997
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3707
3998
  NButton,
3708
3999
  {
3709
4000
  onClick: () => {
@@ -3714,13 +4005,13 @@ var __defProp = Object.defineProperty;
3714
4005
  }
3715
4006
  )
3716
4007
  ] }),
3717
- !hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.hint, children: "提示:删除记忆需先开启高权限模式(右上开关或概览页「高权限模式」),关闭时删除按钮不可用。" }) : null,
3718
- error ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.error, children: error }) : null,
3719
- truncated ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.hint, children: "搜索分页已达检索上限(200 条),更早的结果未显示。请用更精确的关键词或类型/情境过滤。" }) : null,
3720
- 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) => {
3721
4012
  const open = expandedId === m.id;
3722
4013
  const checked = sel.has(m.id);
3723
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4014
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3724
4015
  "div",
3725
4016
  {
3726
4017
  className: "dsh-mem-card dsh-mem-card-hover",
@@ -3729,8 +4020,8 @@ var __defProp = Object.defineProperty;
3729
4020
  setExpandedId(open ? null : m.id);
3730
4021
  },
3731
4022
  children: [
3732
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.cardHead, children: [
3733
- /* @__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)(
3734
4025
  "input",
3735
4026
  {
3736
4027
  type: "checkbox",
@@ -3745,9 +4036,9 @@ var __defProp = Object.defineProperty;
3745
4036
  }
3746
4037
  }
3747
4038
  ),
3748
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + m.type, children: TYPE_LABELS[m.type] || m.type }),
3749
- 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,
3750
- 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)(
3751
4042
  "span",
3752
4043
  {
3753
4044
  title: "该记忆已退场(软删):移出检索面但仍可恢复,并未真正删除",
@@ -3762,8 +4053,23 @@ var __defProp = Object.defineProperty;
3762
4053
  children: "已退场" + (m.retiredReason ? " · " + (RETIRE_REASON_LABEL[m.retiredReason] || m.retiredReason) : "")
3763
4054
  }
3764
4055
  ) : null,
3765
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "优先级 " + m.priority }),
3766
- 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)(
3767
4073
  "span",
3768
4074
  {
3769
4075
  style: {
@@ -3778,7 +4084,7 @@ var __defProp = Object.defineProperty;
3778
4084
  children: "老化 " + Math.round(m.agingWeight * 100) + "%"
3779
4085
  }
3780
4086
  ) : null,
3781
- m.usage ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4087
+ m.usage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3782
4088
  "span",
3783
4089
  {
3784
4090
  style: S.muted,
@@ -3786,10 +4092,10 @@ var __defProp = Object.defineProperty;
3786
4092
  children: "用 " + m.usage.used + "/" + m.usage.attempts
3787
4093
  }
3788
4094
  ) : null,
3789
- m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "相关度 " + Number(m.score).toFixed(2) }) : null,
3790
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3791
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: fmtTime(m.updatedAt) }),
3792
- 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)(
3793
4099
  NButton,
3794
4100
  {
3795
4101
  style: { padding: "0 7px", minWidth: 26, height: 26, fontSize: 12, color: "var(--dsh-mem-accent)" },
@@ -3800,7 +4106,7 @@ var __defProp = Object.defineProperty;
3800
4106
  },
3801
4107
  children: "恢复"
3802
4108
  }
3803
- ) : hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4109
+ ) : hiPriv ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3804
4110
  NButton,
3805
4111
  {
3806
4112
  style: { padding: "0 7px", minWidth: 26, height: 26, fontSize: 12, color: "var(--dsh-mem-danger)" },
@@ -3813,34 +4119,34 @@ var __defProp = Object.defineProperty;
3813
4119
  }
3814
4120
  ) : null
3815
4121
  ] }),
3816
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.content, children: m.content }),
3817
- 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
3818
4124
  ]
3819
4125
  },
3820
4126
  m.id
3821
4127
  );
3822
4128
  }),
3823
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginTop: 12 }, children: [
3824
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(NButton, { onClick: () => setShowRetired((v) => !v), children: (showRetired ? "收起" : "展开") + "「已退场」(可恢复)" }),
3825
- showRetired && retiredBusy ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "加载中…" }) : null,
3826
- 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
3827
4133
  ] }),
3828
- 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: [
3829
- retired.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-card", style: S.card, children: [
3830
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.cardHead, children: [
3831
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: RETIRE_REASON_LABEL[m.retiredReason] || m.retiredReason }),
3832
- m.verdict ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "结论 " + m.verdict }) : null,
3833
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3834
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: fmtTime(m.retiredAt) }),
3835
- /* @__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: "恢复" })
3836
4142
  ] }),
3837
- /* @__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 })
3838
4144
  ] }, m.id)),
3839
- 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
3840
4146
  ] }) }) : null,
3841
- hasMore ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.flexRow, children: [
3842
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
3843
- /* @__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)(
3844
4150
  NButton,
3845
4151
  {
3846
4152
  disabled: loading,
@@ -3854,9 +4160,9 @@ var __defProp = Object.defineProperty;
3854
4160
  ] });
3855
4161
  }
3856
4162
  function WingMultiSelect(props) {
3857
- const [open, setOpen] = (0, import_react15.useState)(false);
3858
- const wrapRef = (0, import_react15.useRef)(null);
3859
- (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)(() => {
3860
4166
  if (!open) return;
3861
4167
  const onDown = (e) => {
3862
4168
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -3876,8 +4182,8 @@ var __defProp = Object.defineProperty;
3876
4182
  const toggle = (id) => {
3877
4183
  props.onChange(props.selected.includes(id) ? props.selected.filter((x) => x !== id) : [...props.selected, id]);
3878
4184
  };
3879
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { ref: wrapRef, style: { position: "relative", maxWidth: 150 }, children: [
3880
- /* @__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)(
3881
4187
  "button",
3882
4188
  {
3883
4189
  type: "button",
@@ -3886,31 +4192,31 @@ var __defProp = Object.defineProperty;
3886
4192
  "aria-expanded": open,
3887
4193
  onClick: () => setOpen(!open),
3888
4194
  children: [
3889
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-select-label", title: summary, children: summary }),
3890
- /* @__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" : "") })
3891
4197
  ]
3892
4198
  }
3893
4199
  ),
3894
- open ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "dsh-mem-pop", role: "listbox", style: { left: 0, right: "auto", minWidth: 150 }, children: [
3895
- 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,
3896
4202
  props.options.map((o) => {
3897
4203
  const active = props.selected.includes(o.id);
3898
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: "dsh-mem-pop-opt", onClick: () => toggle(o.id), "aria-selected": active, children: [
3899
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-pop-check", children: active ? "✓" : "" }),
3900
- /* @__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 })
3901
4207
  ] }, o.id);
3902
4208
  }),
3903
- props.selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: "dsh-mem-pop-opt", onClick: () => props.onChange([]), children: [
3904
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "dsh-mem-pop-check" }),
3905
- /* @__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: "清除筛选" })
3906
4212
  ] }) : null
3907
4213
  ] }) : null
3908
4214
  ] });
3909
4215
  }
3910
4216
 
3911
4217
  // client/src/tabs/RoomsTab.tsx
3912
- var import_react16 = require("react");
3913
- var import_jsx_runtime16 = require("react/jsx-runtime");
4218
+ var import_react18 = require("react");
4219
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3914
4220
  function majorOf(slug) {
3915
4221
  const i = slug.indexOf("/");
3916
4222
  return i === -1 ? slug : slug.slice(0, i);
@@ -3939,26 +4245,26 @@ var __defProp = Object.defineProperty;
3939
4245
  };
3940
4246
  function RoomsTab(props) {
3941
4247
  const rpc = props.rpc;
3942
- const [registry, setRegistry] = (0, import_react16.useState)([]);
3943
- const [rooms, setRooms] = (0, import_react16.useState)([]);
3944
- const [orphanCount, setOrphanCount] = (0, import_react16.useState)(0);
3945
- const [busy, setBusy] = (0, import_react16.useState)(false);
3946
- const [msg, setMsg] = (0, import_react16.useState)(null);
3947
- const [error, setError] = (0, import_react16.useState)(null);
3948
- const [hiPriv, setHiPriv] = (0, import_react16.useState)(false);
3949
- const [hiPrivBusy, setHiPrivBusy] = (0, import_react16.useState)(false);
3950
- const [regSlug, setRegSlug] = (0, import_react16.useState)("");
3951
- const [regLabel, setRegLabel] = (0, import_react16.useState)("");
3952
- const [regDesc, setRegDesc] = (0, import_react16.useState)("");
3953
- const [form, setForm] = (0, import_react16.useState)(null);
3954
- const [preview, setPreview] = (0, import_react16.useState)(null);
3955
- const [grownOpen, setGrownOpen] = (0, import_react16.useState)(false);
3956
- const [dragOverHall, setDragOverHall] = (0, import_react16.useState)(null);
3957
- const [picking, setPicking] = (0, import_react16.useState)(null);
3958
- const [editing, setEditing] = (0, import_react16.useState)(null);
3959
- const [expandedRooms, setExpandedRooms] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
3960
- const [roomRecords, setRoomRecords] = (0, import_react16.useState)({});
3961
- 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)(() => {
3962
4268
  try {
3963
4269
  const v = window.localStorage.getItem("dsh.memory.halls.open");
3964
4270
  return v ? new Set(JSON.parse(v)) : /* @__PURE__ */ new Set();
@@ -3966,9 +4272,9 @@ var __defProp = Object.defineProperty;
3966
4272
  return /* @__PURE__ */ new Set();
3967
4273
  }
3968
4274
  });
3969
- const [filter, setFilter] = (0, import_react16.useState)("");
3970
- const dragRef = (0, import_react16.useRef)(null);
3971
- 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);
3972
4278
  const onDragLabelDown = (ev, slug) => {
3973
4279
  if (!hiPriv || busy || ev.button !== 0) return;
3974
4280
  ev.currentTarget.setPointerCapture(ev.pointerId);
@@ -3993,7 +4299,7 @@ var __defProp = Object.defineProperty;
3993
4299
  setDragOverHall(null);
3994
4300
  if (hall) beginReclass(d.from, hall);
3995
4301
  };
3996
- const loadAll = (0, import_react16.useCallback)(() => {
4302
+ const loadAll = (0, import_react18.useCallback)(() => {
3997
4303
  rpc("dsh-memory/rooms-get", {}).then((r) => {
3998
4304
  if (r && r.ok) {
3999
4305
  setRooms(r.value.rooms ?? []);
@@ -4002,13 +4308,13 @@ var __defProp = Object.defineProperty;
4002
4308
  }
4003
4309
  }).catch(() => void 0);
4004
4310
  }, [rpc]);
4005
- const loadHiPriv = (0, import_react16.useCallback)(() => {
4311
+ const loadHiPriv = (0, import_react18.useCallback)(() => {
4006
4312
  rpc("dsh-memory/settings-get", {}).then((r) => {
4007
4313
  if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
4008
4314
  }).catch(() => {
4009
4315
  });
4010
4316
  }, [rpc]);
4011
- (0, import_react16.useEffect)(() => {
4317
+ (0, import_react18.useEffect)(() => {
4012
4318
  loadAll();
4013
4319
  loadHiPriv();
4014
4320
  }, [loadAll, loadHiPriv]);
@@ -4159,7 +4465,7 @@ var __defProp = Object.defineProperty;
4159
4465
  if (r.value.csv) downloadCsv(r.value.csv, `dsh-memory-room-${slug.replace(/\//g, "-")}-records.csv`);
4160
4466
  else setError("导出为空");
4161
4467
  });
4162
- const groups = (0, import_react16.useMemo)(() => {
4468
+ const groups = (0, import_react18.useMemo)(() => {
4163
4469
  const countOf = new Map(rooms.map((r) => [r.room, r.count]));
4164
4470
  const byHall = /* @__PURE__ */ new Map();
4165
4471
  for (const e of registry) {
@@ -4176,7 +4482,7 @@ var __defProp = Object.defineProperty;
4176
4482
  total: entries.reduce((s2, e) => s2 + (countOf.get(e.slug) ?? 0), 0)
4177
4483
  })).sort((a, b) => a.hall.localeCompare(b.hall));
4178
4484
  }, [registry, rooms]);
4179
- 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]);
4180
4486
  const persistHalls = (next) => {
4181
4487
  try {
4182
4488
  window.localStorage.setItem("dsh.memory.halls.open", JSON.stringify([...next]));
@@ -4196,7 +4502,7 @@ var __defProp = Object.defineProperty;
4196
4502
  persistHalls(next);
4197
4503
  };
4198
4504
  const normFilter = filter.trim().toLowerCase();
4199
- const visibleGroups = (0, import_react16.useMemo)(() => {
4505
+ const visibleGroups = (0, import_react18.useMemo)(() => {
4200
4506
  if (!normFilter) return groups;
4201
4507
  return groups.map((g) => {
4202
4508
  const hallHit = g.hall.toLowerCase().includes(normFilter) || (g.label ?? "").toLowerCase().includes(normFilter);
@@ -4206,15 +4512,15 @@ var __defProp = Object.defineProperty;
4206
4512
  return { ...g, entries };
4207
4513
  }).filter((g) => g.entries.length > 0);
4208
4514
  }, [groups, normFilter]);
4209
- const visibleGrown = (0, import_react16.useMemo)(
4515
+ const visibleGrown = (0, import_react18.useMemo)(
4210
4516
  () => normFilter ? grownOnly.filter((r) => r.room.toLowerCase().includes(normFilter)) : grownOnly,
4211
4517
  [grownOnly, normFilter]
4212
4518
  );
4213
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
4214
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8 }, children: [
4215
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "Hall = Room 大类 · 大类上限 200,小类「大类/小类」细分不占额度 · 与 Wing(域)筛选无关" }),
4216
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4217
- /* @__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)(
4218
4524
  "button",
4219
4525
  {
4220
4526
  type: "button",
@@ -4231,7 +4537,7 @@ var __defProp = Object.defineProperty;
4231
4537
  children: "导出词表"
4232
4538
  }
4233
4539
  ),
4234
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4540
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4235
4541
  "button",
4236
4542
  {
4237
4543
  type: "button",
@@ -4251,7 +4557,7 @@ var __defProp = Object.defineProperty;
4251
4557
  ]
4252
4558
  }
4253
4559
  ),
4254
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4560
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4255
4561
  "button",
4256
4562
  {
4257
4563
  type: "button",
@@ -4266,10 +4572,10 @@ var __defProp = Object.defineProperty;
4266
4572
  children: hiPriv ? "高权限:开" : "高权限:关"
4267
4573
  }
4268
4574
  ),
4269
- /* @__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: "刷新" })
4270
4576
  ] }),
4271
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center", marginBottom: 8, flexWrap: "wrap" }, children: [
4272
- /* @__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)(
4273
4579
  "input",
4274
4580
  {
4275
4581
  value: filter,
@@ -4278,14 +4584,14 @@ var __defProp = Object.defineProperty;
4278
4584
  style: { ...S.input, width: 250, fontSize: 12 }
4279
4585
  }
4280
4586
  ),
4281
- filter ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, onClick: () => setFilter(""), children: "清除" }) : null,
4282
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `匹配 ${visibleGroups.reduce((s2, g) => s2 + g.entries.length, 0)} room / ${visibleGroups.length} hall` }),
4283
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4284
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, title: "展开全部 hall 的子 room", onClick: () => setAllHalls(true), children: "展开全部" }),
4285
- /* @__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: "收起全部" })
4286
4592
  ] }),
4287
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4288
- /* @__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)(
4289
4595
  "input",
4290
4596
  {
4291
4597
  value: regSlug,
@@ -4295,7 +4601,7 @@ var __defProp = Object.defineProperty;
4295
4601
  style: { ...S.input, width: 210, fontSize: 12 }
4296
4602
  }
4297
4603
  ),
4298
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4604
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4299
4605
  "input",
4300
4606
  {
4301
4607
  value: regLabel,
@@ -4305,7 +4611,7 @@ var __defProp = Object.defineProperty;
4305
4611
  style: { ...S.input, width: 110, fontSize: 12 }
4306
4612
  }
4307
4613
  ),
4308
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4614
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4309
4615
  "input",
4310
4616
  {
4311
4617
  value: regDesc,
@@ -4315,7 +4621,7 @@ var __defProp = Object.defineProperty;
4315
4621
  style: { ...S.input, width: 170, fontSize: 12 }
4316
4622
  }
4317
4623
  ),
4318
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4624
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4319
4625
  "button",
4320
4626
  {
4321
4627
  type: "button",
@@ -4325,26 +4631,26 @@ var __defProp = Object.defineProperty;
4325
4631
  children: "注册"
4326
4632
  }
4327
4633
  ),
4328
- !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: "管理操作需先开启高权限模式" })
4329
4635
  ] }),
4330
- 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: [
4331
- /* @__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: [
4332
4638
  "编辑 ",
4333
4639
  editing.slug
4334
4640
  ] }),
4335
- /* @__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 } }),
4336
- /* @__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 } }),
4337
- /* @__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: "保存" }),
4338
- /* @__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: "取消" })
4339
4645
  ] }) : null,
4340
- 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: [
4341
- /* @__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: [
4342
4648
  form.action === "merge" ? "合并" : "归类/改名",
4343
4649
  " ",
4344
4650
  form.from,
4345
4651
  " →"
4346
4652
  ] }),
4347
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4653
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4348
4654
  "input",
4349
4655
  {
4350
4656
  value: form.to,
@@ -4354,7 +4660,7 @@ var __defProp = Object.defineProperty;
4354
4660
  style: { ...S.input, width: 190, fontSize: 12 }
4355
4661
  }
4356
4662
  ),
4357
- 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)(
4358
4664
  "button",
4359
4665
  {
4360
4666
  type: "button",
@@ -4365,32 +4671,32 @@ var __defProp = Object.defineProperty;
4365
4671
  },
4366
4672
  gg.hall
4367
4673
  )) }) : null,
4368
- /* @__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: "预览" }),
4369
- /* @__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: () => {
4370
4676
  setForm(null);
4371
4677
  setPreview(null);
4372
4678
  setPicking(null);
4373
4679
  }, style: btn, children: "取消" }),
4374
- picking ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "挑选中:点击目标 hall 头完成归类" }) : null,
4375
- /* @__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: "与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4376
4682
  ] }) : null,
4377
- 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: [
4378
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: preview.notice }),
4379
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, marginTop: 4 }, children: [
4380
- /* @__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: [
4381
4687
  "确认执行",
4382
4688
  preview.action === "merge" ? "合并" : "归类/改名"
4383
4689
  ] }),
4384
- /* @__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: "再改改" })
4385
4691
  ] })
4386
4692
  ] }) : null,
4387
- msg ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.hint, children: msg }) : null,
4388
- error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.error, children: error }) : null,
4389
- 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,
4390
- 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) => {
4391
4697
  const hallOpen = !!normFilter || expandedHalls.has(g.hall);
4392
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginBottom: 10, border: "1px solid var(--dsh-mem-border)", borderRadius: 8, overflow: "hidden" }, children: [
4393
- /* @__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)(
4394
4700
  "div",
4395
4701
  {
4396
4702
  "data-hall": g.hall,
@@ -4431,7 +4737,7 @@ var __defProp = Object.defineProperty;
4431
4737
  },
4432
4738
  title: picking ? "点击此 hall:把「" + picking + "」归入 " + g.hall + " 下(major/minor,预览后确认)" : hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
4433
4739
  children: [
4434
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4740
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4435
4741
  "button",
4436
4742
  {
4437
4743
  type: "button",
@@ -4444,13 +4750,13 @@ var __defProp = Object.defineProperty;
4444
4750
  children: hallOpen ? "▾" : "▸"
4445
4751
  }
4446
4752
  ),
4447
- /* @__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: [
4448
4754
  g.hall,
4449
- 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
4450
4756
  ] }),
4451
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `合计 ${g.total} 条 · ${g.entries.length} 个 room` }),
4452
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4453
- /* @__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)(
4454
4760
  "button",
4455
4761
  {
4456
4762
  type: "button",
@@ -4471,10 +4777,10 @@ var __defProp = Object.defineProperty;
4471
4777
  const standalone = e.slug === g.hall;
4472
4778
  const count = rooms.find((r) => r.room === e.slug)?.count ?? 0;
4473
4779
  const retired = e.status === "retired";
4474
- 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: [
4475
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4476
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
4477
- /* @__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)(
4478
4784
  "button",
4479
4785
  {
4480
4786
  type: "button",
@@ -4484,7 +4790,7 @@ var __defProp = Object.defineProperty;
4484
4790
  children: expandedRooms.has(e.slug) ? "▾" : "▸"
4485
4791
  }
4486
4792
  ),
4487
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4793
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4488
4794
  "span",
4489
4795
  {
4490
4796
  style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default", touchAction: "none" },
@@ -4498,20 +4804,20 @@ var __defProp = Object.defineProperty;
4498
4804
  ]
4499
4805
  }
4500
4806
  ),
4501
- e.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: e.label }) : null,
4502
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: count + " 条" }),
4503
- retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4504
- (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
4505
4811
  ] }),
4506
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4507
- !hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4508
- /* @__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: () => {
4509
4815
  setEditing({ slug: e.slug, label: e.label ?? "", description: e.description ?? "" });
4510
4816
  setForm(null);
4511
4817
  setPreview(null);
4512
4818
  setPicking(null);
4513
4819
  }, style: btn, children: "编辑" }),
4514
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4820
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4515
4821
  "button",
4516
4822
  {
4517
4823
  type: "button",
@@ -4532,43 +4838,43 @@ var __defProp = Object.defineProperty;
4532
4838
  children: picking === e.slug ? "选 hall…" : "归类"
4533
4839
  }
4534
4840
  ),
4535
- /* @__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: () => {
4536
4842
  setForm({ action: "rename", from: e.slug, to: "" });
4537
4843
  setPreview(null);
4538
4844
  }, style: btn, children: "改名" }),
4539
- /* @__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: () => {
4540
4846
  setForm({ action: "merge", from: e.slug, to: "" });
4541
4847
  setPreview(null);
4542
4848
  }, style: btn, children: "合并" }),
4543
- /* @__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 ? "恢复" : "退役" }),
4544
- !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
4545
4851
  ] }),
4546
- /* @__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: "导出" })
4547
4853
  ] })
4548
4854
  ] }),
4549
- 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: [
4550
- (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: [
4551
- /* @__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 }),
4552
4858
  " ",
4553
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4554
- /* @__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 })
4555
4861
  ] }, it.id)),
4556
- (roomRecords[e.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.muted, children: "该 Room 暂无记录。" }) : null,
4557
- (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
4558
4864
  ] }) : null
4559
4865
  ] }, e.slug);
4560
4866
  }) : null
4561
4867
  ] }, g.hall);
4562
4868
  }),
4563
- visibleGrown.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginTop: 4 }, children: [
4564
- /* @__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: [
4565
4871
  grownOpen ? "▾" : "▸",
4566
4872
  " 自生长未注册 · 共 ",
4567
4873
  visibleGrown.length
4568
4874
  ] }),
4569
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "(只在 tags 里涌现,不在词表 —— 收编后标注器才会优先挂靠)" }),
4570
- 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: [
4571
- /* @__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)(
4572
4878
  "span",
4573
4879
  {
4574
4880
  style: { ...btn, cursor: hiPriv && !busy ? "grab" : "default", borderRadius: 999, padding: "2px 8px", touchAction: "none" },
@@ -4579,8 +4885,8 @@ var __defProp = Object.defineProperty;
4579
4885
  children: r.room + " · " + r.count
4580
4886
  }
4581
4887
  ),
4582
- /* @__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: "收编" }),
4583
- /* @__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)(
4584
4890
  "button",
4585
4891
  {
4586
4892
  type: "button",
@@ -4607,8 +4913,8 @@ var __defProp = Object.defineProperty;
4607
4913
  }
4608
4914
 
4609
4915
  // client/src/tabs/RoomTab.tsx
4610
- var import_react17 = require("react");
4611
- var import_jsx_runtime17 = require("react/jsx-runtime");
4916
+ var import_react19 = require("react");
4917
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4612
4918
  var btn2 = {
4613
4919
  cursor: "pointer",
4614
4920
  fontSize: 12,
@@ -4629,22 +4935,22 @@ var __defProp = Object.defineProperty;
4629
4935
  }
4630
4936
  function RoomTab(props) {
4631
4937
  const rpc = props.rpc;
4632
- const [registry, setRegistry] = (0, import_react17.useState)([]);
4633
- const [counts, setCounts] = (0, import_react17.useState)([]);
4634
- const [orphanCount, setOrphanCount] = (0, import_react17.useState)(0);
4635
- const [busy, setBusy] = (0, import_react17.useState)(false);
4636
- const [msg, setMsg] = (0, import_react17.useState)(null);
4637
- const [error, setError] = (0, import_react17.useState)(null);
4638
- const [hiPriv, setHiPriv] = (0, import_react17.useState)(false);
4639
- const [hiPrivBusy, setHiPrivBusy] = (0, import_react17.useState)(false);
4640
- const [reg, setReg] = (0, import_react17.useState)({ slug: "", label: "", description: "" });
4641
- const [editing, setEditing] = (0, import_react17.useState)(null);
4642
- const [renaming, setRenaming] = (0, import_react17.useState)(null);
4643
- const [expanded, setExpanded] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
4644
- const [retiredFilter, setRetiredFilter] = (0, import_react17.useState)("");
4645
- const [filter, setFilter] = (0, import_react17.useState)("");
4646
- const [records, setRecords] = (0, import_react17.useState)({});
4647
- 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)(() => {
4648
4954
  rpc("dsh-memory/rooms-get", {}).then((r) => {
4649
4955
  if (r && r.ok) {
4650
4956
  setCounts(r.value.rooms ?? []);
@@ -4653,13 +4959,13 @@ var __defProp = Object.defineProperty;
4653
4959
  }
4654
4960
  }).catch(() => void 0);
4655
4961
  }, [rpc]);
4656
- const loadHiPriv = (0, import_react17.useCallback)(() => {
4962
+ const loadHiPriv = (0, import_react19.useCallback)(() => {
4657
4963
  rpc("dsh-memory/settings-get", {}).then((r) => {
4658
4964
  if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
4659
4965
  }).catch(() => {
4660
4966
  });
4661
4967
  }, [rpc]);
4662
- (0, import_react17.useEffect)(() => {
4968
+ (0, import_react19.useEffect)(() => {
4663
4969
  loadAll();
4664
4970
  loadHiPriv();
4665
4971
  }, [loadAll, loadHiPriv]);
@@ -4797,11 +5103,11 @@ var __defProp = Object.defineProperty;
4797
5103
  const visibleRows = normFilter ? rows.filter(
4798
5104
  (row2) => row2.slug.toLowerCase().includes(normFilter) || (row2.entry?.label ?? "").toLowerCase().includes(normFilter) || (row2.entry?.aliases ?? []).some((a) => a.toLowerCase().includes(normFilter))
4799
5105
  ) : rows;
4800
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
4801
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8, flexWrap: "wrap" }, children: [
4802
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "Room 级管理:存在(注册/收编/退役)· 名称(显示名/说明/改名)· 条目(展开浏览) · 归类到 hall 请到「Hall」页" }),
4803
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4804
- /* @__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 () => {
4805
5111
  const r = await rpc("dsh-memory/rooms-export", { kind: "orphans" });
4806
5112
  if (!r.ok) {
4807
5113
  setError(r.error.message);
@@ -4812,7 +5118,7 @@ var __defProp = Object.defineProperty;
4812
5118
  "导出孤儿 · ",
4813
5119
  orphanCount
4814
5120
  ] }),
4815
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5121
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4816
5122
  "button",
4817
5123
  {
4818
5124
  type: "button",
@@ -4823,35 +5129,35 @@ var __defProp = Object.defineProperty;
4823
5129
  children: hiPriv ? "高权限:开" : "高权限:关"
4824
5130
  }
4825
5131
  ),
4826
- /* @__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: "刷新" })
4827
5133
  ] }),
4828
- 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: [
4829
- /* @__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: [
4830
5136
  "编辑 ",
4831
5137
  editing.slug
4832
5138
  ] }),
4833
- /* @__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 } }),
4834
- /* @__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 } }),
4835
- /* @__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: "保存" }),
4836
- /* @__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: "取消" })
4837
5143
  ] }) : null,
4838
- 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: [
4839
- /* @__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: [
4840
5146
  "改名 ",
4841
5147
  renaming.slug,
4842
5148
  " →"
4843
5149
  ] }),
4844
- /* @__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 } }),
4845
- /* @__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: "预览" }),
4846
- /* @__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: "确认执行" }),
4847
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: () => setRenaming(null), style: btn2, children: "取消" }),
4848
- /* @__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: "记录随迁;与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4849
5155
  ] }) : null,
4850
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }, children: [
4851
- /* @__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: "退场筛选" }),
4852
5158
  [["", "全部"], ["active", "活跃"], ["retired", "已退役"]].map(([val, label]) => {
4853
5159
  const on = retiredFilter === val;
4854
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5160
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4855
5161
  "button",
4856
5162
  {
4857
5163
  type: "button",
@@ -4871,8 +5177,8 @@ var __defProp = Object.defineProperty;
4871
5177
  val
4872
5178
  );
4873
5179
  }),
4874
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
4875
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5180
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.grow }),
5181
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4876
5182
  "input",
4877
5183
  {
4878
5184
  value: filter,
@@ -4881,24 +5187,24 @@ var __defProp = Object.defineProperty;
4881
5187
  style: { ...S.input, width: 230, fontSize: 12 }
4882
5188
  }
4883
5189
  ),
4884
- 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
4885
5191
  ] }),
4886
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4887
- /* @__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 } }),
4888
- /* @__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 } }),
4889
- /* @__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 } }),
4890
- /* @__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: "注册" }),
4891
- !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: "管理操作需先开启高权限模式" })
4892
5198
  ] }),
4893
- msg ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.hint, children: msg }) : null,
4894
- error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.error, children: error }) : null,
4895
- 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) => {
4896
5202
  const e = row2.entry;
4897
5203
  const retired = e?.status === "retired";
4898
5204
  const grownOnly = !e;
4899
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { marginBottom: 6, border: "1px solid var(--dsh-mem-border)", borderRadius: 8 }, children: [
4900
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "4px 10px" }, children: [
4901
- /* @__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)(
4902
5208
  "button",
4903
5209
  {
4904
5210
  type: "button",
@@ -4908,28 +5214,28 @@ var __defProp = Object.defineProperty;
4908
5214
  children: expanded.has(row2.slug) ? "▾" : "▸"
4909
5215
  }
4910
5216
  ),
4911
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
4912
- /* @__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: [
4913
5219
  row2.slug,
4914
5220
  e?.source === "pre-registered" ? " ⭐" : ""
4915
5221
  ] }),
4916
- e?.label ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: e.label }) : null,
4917
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: row2.count + " 条" }),
4918
- grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "(自生长未注册)" }) : null,
4919
- retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4920
- (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
4921
5227
  ] }),
4922
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4923
- !hiPriv || grownOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4924
- /* @__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: () => {
4925
5231
  setEditing({ slug: row2.slug, label: e?.label ?? "", description: e?.description ?? "" });
4926
5232
  setRenaming(null);
4927
5233
  }, style: btn2, children: "编辑" }),
4928
- /* @__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: () => {
4929
5235
  setRenaming({ slug: row2.slug, to: "" });
4930
5236
  setEditing(null);
4931
5237
  }, style: btn2, children: "改名" }),
4932
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5238
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4933
5239
  "button",
4934
5240
  {
4935
5241
  type: "button",
@@ -4940,29 +5246,29 @@ var __defProp = Object.defineProperty;
4940
5246
  children: retired ? "恢复" : "退役"
4941
5247
  }
4942
5248
  ),
4943
- !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
4944
5250
  ] }),
4945
- grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4946
- /* @__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: "收编" }),
4947
- /* @__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: () => {
4948
5254
  setRenaming(null);
4949
5255
  retire(row2.slug, false, true);
4950
5256
  }, style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "退场" })
4951
5257
  ] }) : null,
4952
- /* @__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: "导出" })
4953
5259
  ] })
4954
5260
  ] }),
4955
- 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: [
4956
- (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: [
4957
- /* @__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 }),
4958
5264
  " ",
4959
- 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,
4960
5266
  " ",
4961
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4962
- /* @__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 })
4963
5269
  ] }, it.id)),
4964
- (records[row2.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.muted, children: "该 Room 暂无记忆条目。" }) : null,
4965
- (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
4966
5272
  ] }) : null
4967
5273
  ] }, row2.slug);
4968
5274
  })
@@ -4970,13 +5276,13 @@ var __defProp = Object.defineProperty;
4970
5276
  }
4971
5277
 
4972
5278
  // client/src/tabs/ScenesTab.tsx
4973
- var import_react18 = require("react");
4974
- var import_jsx_runtime18 = require("react/jsx-runtime");
5279
+ var import_react20 = require("react");
5280
+ var import_jsx_runtime20 = require("react/jsx-runtime");
4975
5281
  function SceneCard(props) {
4976
5282
  const s = props.s;
4977
- const [open, setOpen] = (0, import_react18.useState)(false);
4978
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "dsh-mem-card dsh-mem-card-hover", style: S.card, children: [
4979
- /* @__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)(
4980
5286
  "div",
4981
5287
  {
4982
5288
  style: { ...S.sceneHead, cursor: "pointer", userSelect: "none" },
@@ -4984,23 +5290,23 @@ var __defProp = Object.defineProperty;
4984
5290
  setOpen(!open);
4985
5291
  },
4986
5292
  children: [
4987
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "dsh-mem-scene-chev", style: { transform: open ? "rotate(90deg)" : "none" }, children: "▸" }),
4988
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.sceneTitle, children: s.path }),
4989
- s.heat ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "热度 " + s.heat }) : null,
4990
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
4991
- /* @__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) })
4992
5298
  ]
4993
5299
  }
4994
5300
  ),
4995
- s.summary ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { ...S.muted, marginBottom: 6 }, children: s.summary }) : null,
4996
- 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
4997
5303
  ] });
4998
5304
  }
4999
5305
  function ScenesTab(props) {
5000
5306
  const rpc = props.rpc;
5001
- const [items, setItems] = (0, import_react18.useState)(null);
5002
- const [error, setError] = (0, import_react18.useState)(null);
5003
- 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)(() => {
5004
5310
  rpc("dsh-memory/scenes", {}).then((r) => {
5005
5311
  if (r && r.ok) {
5006
5312
  setItems(r.value.items);
@@ -5010,25 +5316,25 @@ var __defProp = Object.defineProperty;
5010
5316
  setError(String(e && e.message || e));
5011
5317
  });
5012
5318
  }, [rpc]);
5013
- (0, import_react18.useEffect)(() => {
5319
+ (0, import_react20.useEffect)(() => {
5014
5320
  load();
5015
5321
  }, [load]);
5016
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
5017
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
5018
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: items ? items.length + " 个场景块" : "加载中…" }),
5019
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
5020
- /* @__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: "刷新" })
5021
5327
  ] }),
5022
- error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.error, children: error }) : null,
5023
- 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,
5024
5330
  (items || []).map((s) => {
5025
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SceneCard, { s }, s.path);
5331
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SceneCard, { s }, s.path);
5026
5332
  })
5027
5333
  ] });
5028
5334
  }
5029
5335
 
5030
5336
  // client/src/panel.tsx
5031
- var import_jsx_runtime19 = require("react/jsx-runtime");
5337
+ var import_jsx_runtime21 = require("react/jsx-runtime");
5032
5338
  var TABS = [
5033
5339
  ["overview", "概览"],
5034
5340
  ["records", "记忆"],
@@ -5038,30 +5344,71 @@ var __defProp = Object.defineProperty;
5038
5344
  ["room", "Room"],
5039
5345
  ["persona", "画像"],
5040
5346
  ["cost", "成本"],
5041
- ["log", "日志"]
5347
+ ["log", "日志"],
5348
+ ["connect", "来源"],
5349
+ ["skills", "技能"]
5350
+ ];
5351
+ var SKIN_OPTS = [
5352
+ ["light", "浅色"],
5353
+ ["dark", "深色"],
5354
+ ["auto", "跟随"]
5042
5355
  ];
5043
5356
  function MemoryPanel(props) {
5044
5357
  const rpc = props.rpc;
5045
- const [tab, setTab] = (0, import_react19.useState)("overview");
5046
- ensureThemeStyle();
5047
- (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);
5048
5363
  watchSidebarIcon();
5049
- }, []);
5364
+ }, [skin]);
5365
+ const switchSkin = (next) => {
5366
+ setSkin(next);
5367
+ setSkinState(next);
5368
+ };
5050
5369
  let body;
5051
- if (tab === "overview") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(OverviewTab, { rpc });
5052
- else if (tab === "records") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RecordsTab, { rpc });
5053
- else if (tab === "conflicts") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConflictsTab, { rpc });
5054
- else if (tab === "scenes") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ScenesTab, { rpc });
5055
- else if (tab === "rooms") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RoomsTab, { rpc });
5056
- else if (tab === "room") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RoomTab, { rpc });
5057
- else if (tab === "persona") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PersonaTab, { rpc });
5058
- else if (tab === "cost") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CostTab, { rpc });
5059
- else body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LogTab, { rpc });
5060
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "dsh-mem-root", style: S.section, children: [
5061
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { style: S.heading, children: "记忆 (Memory)" }),
5062
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { style: S.intro, children: "L0~L3 分层蒸馏记忆:浏览被记住的内容,控制记忆模式开关。" }),
5063
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.tabbar, children: TABS.map((t) => {
5064
- 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)(
5065
5412
  "button",
5066
5413
  {
5067
5414
  className: tab === t[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
@@ -5078,7 +5425,7 @@ var __defProp = Object.defineProperty;
5078
5425
  }
5079
5426
 
5080
5427
  // client/src/pill/MemoryModePill.tsx
5081
- var import_react22 = require("react");
5428
+ var import_react25 = require("react");
5082
5429
 
5083
5430
  // src/util/context-occupancy.ts
5084
5431
  var CONTEXT_METER_CIRCUMFERENCE = 34.55751918948772;
@@ -5394,23 +5741,23 @@ var __defProp = Object.defineProperty;
5394
5741
  }
5395
5742
 
5396
5743
  // client/src/pill/WingWheel.tsx
5397
- var import_react21 = require("react");
5744
+ var import_react24 = require("react");
5398
5745
 
5399
5746
  // client/src/pill/SessionInfoArea.tsx
5400
- var import_react20 = require("react");
5401
- var import_jsx_runtime20 = require("react/jsx-runtime");
5747
+ var import_react22 = require("react");
5748
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5402
5749
  function sinfoCell(val, label, title) {
5403
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { title: title || void 0, children: [
5404
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-val", children: val }),
5405
- /* @__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 })
5406
5753
  ] });
5407
5754
  }
5408
5755
  function SessionInfoArea(props) {
5409
5756
  const rpc = props.rpc;
5410
5757
  const sessionId = props.sessionId;
5411
- const [stats, setStats] = (0, import_react20.useState)(void 0);
5412
- const busyRef = (0, import_react20.useRef)(false);
5413
- (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)(() => {
5414
5761
  if (!rpc || !sessionId) return void 0;
5415
5762
  let alive = true;
5416
5763
  let timer = null;
@@ -5443,7 +5790,7 @@ var __defProp = Object.defineProperty;
5443
5790
  }, [rpc, sessionId]);
5444
5791
  if (stats === null) return null;
5445
5792
  if (stats === void 0) {
5446
- 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: [
5447
5794
  sinfoCell("…", "召回命中"),
5448
5795
  sinfoCell("…", "攒批进度"),
5449
5796
  sinfoCell("…", "本会话记忆"),
@@ -5492,21 +5839,104 @@ var __defProp = Object.defineProperty;
5492
5839
  else if (stats.retrieval === "none") note = "检索不可用(FTS 与向量均失效)";
5493
5840
  }
5494
5841
  const ago = fmtAgo(gl.lastExtractAt);
5495
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-sinfo", children: [
5496
- warn ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-warn", children: warn }) : null,
5497
- /* @__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: [
5498
5845
  sinfoCell(rcVal, rcLabel, rcTitle),
5499
5846
  sinfoCell(dVal, dLabel, dTitle),
5500
5847
  sinfoCell(String(di.producedRecords || 0), "本会话记忆", pTitle),
5501
5848
  sinfoCell(stats.l0Count != null ? String(stats.l0Count) : "…", "会话消息")
5502
5849
  ] }),
5503
- note ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-note", children: note }) : null,
5504
- /* @__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
+ ] })
5505
5914
  ] });
5506
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
+ }
5507
5937
 
5508
5938
  // client/src/pill/WingWheel.tsx
5509
- var import_jsx_runtime21 = require("react/jsx-runtime");
5939
+ var import_jsx_runtime24 = require("react/jsx-runtime");
5510
5940
  var SIZE = 196;
5511
5941
  var CENTER = SIZE / 2;
5512
5942
  var R = 64;
@@ -5549,17 +5979,17 @@ var __defProp = Object.defineProperty;
5549
5979
  function WingWheel(props) {
5550
5980
  ensureThemeStyle();
5551
5981
  const isOff = props.mode === "off";
5552
- const [overview, setOverview] = (0, import_react21.useState)(null);
5553
- const [backfillBusy, setBackfillBusy] = (0, import_react21.useState)(false);
5554
- const [localError, setLocalError] = (0, import_react21.useState)(null);
5555
- const timersRef = (0, import_react21.useRef)([]);
5556
- (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)(
5557
5987
  () => () => {
5558
5988
  for (const t of timersRef.current) window.clearTimeout(t);
5559
5989
  },
5560
5990
  []
5561
5991
  );
5562
- (0, import_react21.useEffect)(() => {
5992
+ (0, import_react24.useEffect)(() => {
5563
5993
  let alive = true;
5564
5994
  props.rpc("dsh-memory/wing-overview", {}).then((r) => {
5565
5995
  if (!alive) return;
@@ -5605,28 +6035,28 @@ var __defProp = Object.defineProperty;
5605
6035
  finish();
5606
6036
  });
5607
6037
  };
5608
- const boxRef = (0, import_react21.useRef)(null);
5609
- const startRef = (0, import_react21.useRef)(null);
5610
- const movedRef = (0, import_react21.useRef)(false);
5611
- const downIndexRef = (0, import_react21.useRef)(null);
5612
- const dragTargetRef = (0, import_react21.useRef)(null);
5613
- 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);
5614
6044
  const lockedIndex = (() => {
5615
6045
  if (props.halls.length !== 1 || !overview) return null;
5616
6046
  const i = overview.corners.findIndex((c) => c.id === props.halls[0]);
5617
6047
  return i >= 0 ? i : null;
5618
6048
  })();
5619
6049
  const activeCorner = drag ? drag.index : lockedIndex;
5620
- const blockPosRef = (0, import_react21.useRef)(
6050
+ const blockPosRef = (0, import_react24.useRef)(
5621
6051
  activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER }
5622
6052
  );
5623
- const blockTargetRef = (0, import_react21.useRef)(blockPosRef.current);
5624
- const [blockPos, setBlockPos] = (0, import_react21.useState)(blockPosRef.current);
5625
- (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)(() => {
5626
6056
  blockTargetRef.current = activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER };
5627
6057
  }, [activeCorner]);
5628
- const reducedMotionRef = (0, import_react21.useRef)(false);
5629
- (0, import_react21.useEffect)(() => {
6058
+ const reducedMotionRef = (0, import_react24.useRef)(false);
6059
+ (0, import_react24.useEffect)(() => {
5630
6060
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
5631
6061
  const apply2 = () => {
5632
6062
  reducedMotionRef.current = mq.matches;
@@ -5635,7 +6065,7 @@ var __defProp = Object.defineProperty;
5635
6065
  mq.addEventListener("change", apply2);
5636
6066
  return () => mq.removeEventListener("change", apply2);
5637
6067
  }, []);
5638
- (0, import_react21.useEffect)(() => {
6068
+ (0, import_react24.useEffect)(() => {
5639
6069
  let raf = 0;
5640
6070
  const loop = () => {
5641
6071
  const cur = blockPosRef.current;
@@ -5724,8 +6154,8 @@ var __defProp = Object.defineProperty;
5724
6154
  }).join(" ");
5725
6155
  const grayStyle = isOff ? { opacity: 0.45, pointerEvents: "none" } : {};
5726
6156
  const boundariesDisabled = props.halls.length === 0 || isOff;
5727
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { width: SIZE }, children: [
5728
- /* @__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)(
5729
6159
  "div",
5730
6160
  {
5731
6161
  ref: boxRef,
@@ -5735,7 +6165,7 @@ var __defProp = Object.defineProperty;
5735
6165
  onPointerUp: onBoxUp,
5736
6166
  onPointerCancel: onBoxUp,
5737
6167
  children: [
5738
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6168
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5739
6169
  "svg",
5740
6170
  {
5741
6171
  width: SIZE,
@@ -5743,7 +6173,7 @@ var __defProp = Object.defineProperty;
5743
6173
  style: { position: "absolute", inset: 0, pointerEvents: "none" },
5744
6174
  "aria-hidden": "true",
5745
6175
  children: [
5746
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6176
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5747
6177
  "polygon",
5748
6178
  {
5749
6179
  points: polyPoints,
@@ -5754,7 +6184,7 @@ var __defProp = Object.defineProperty;
5754
6184
  opacity: 0.45
5755
6185
  }
5756
6186
  ),
5757
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6187
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5758
6188
  "polygon",
5759
6189
  {
5760
6190
  points: polyPoints,
@@ -5765,7 +6195,7 @@ var __defProp = Object.defineProperty;
5765
6195
  ),
5766
6196
  [0, 1, 2, 3, 4, 5, 6, 7].map((i) => {
5767
6197
  const p = cornerPos(i);
5768
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6198
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5769
6199
  "line",
5770
6200
  {
5771
6201
  x1: CENTER,
@@ -5781,7 +6211,7 @@ var __defProp = Object.defineProperty;
5781
6211
  ]
5782
6212
  }
5783
6213
  ),
5784
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6214
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5785
6215
  "div",
5786
6216
  {
5787
6217
  "aria-hidden": "true",
@@ -5800,7 +6230,7 @@ var __defProp = Object.defineProperty;
5800
6230
  }
5801
6231
  }
5802
6232
  ),
5803
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6233
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5804
6234
  "button",
5805
6235
  {
5806
6236
  type: "button",
@@ -5834,7 +6264,7 @@ var __defProp = Object.defineProperty;
5834
6264
  const dim = activeCorner != null && !active;
5835
6265
  const empty = count === 0;
5836
6266
  const p = cornerPos(i);
5837
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6267
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5838
6268
  "div",
5839
6269
  {
5840
6270
  title: id ? `「${label}」${count ?? 0} 条
@@ -5863,8 +6293,8 @@ var __defProp = Object.defineProperty;
5863
6293
  zIndex: 2
5864
6294
  },
5865
6295
  children: [
5866
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: label }),
5867
- /* @__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} 条` })
5868
6298
  ]
5869
6299
  },
5870
6300
  id ?? i
@@ -5873,7 +6303,7 @@ var __defProp = Object.defineProperty;
5873
6303
  ]
5874
6304
  }
5875
6305
  ),
5876
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6306
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5877
6307
  "div",
5878
6308
  {
5879
6309
  title: props.halls.length === 0 ? "选定某个角(单 Wing)后,这两项才生效" : "锁定 Wing 时两类无角记忆是否参与召回",
@@ -5887,9 +6317,9 @@ var __defProp = Object.defineProperty;
5887
6317
  pointerEvents: boundariesDisabled ? "none" : void 0
5888
6318
  },
5889
6319
  children: [
5890
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `另有 ${overview.unlabeled} 条未打标` : "未打标" }),
5891
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
5892
- /* @__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)(
5893
6323
  Segmented,
5894
6324
  {
5895
6325
  value: props.hallIncludeUnlabeled ? "in" : "ex",
@@ -5900,7 +6330,7 @@ var __defProp = Object.defineProperty;
5900
6330
  onChange: (key) => props.onCommitWingBoundaries({ includeUnlabeled: key === "in" })
5901
6331
  }
5902
6332
  ),
5903
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6333
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5904
6334
  Segmented,
5905
6335
  {
5906
6336
  value: props.hallIncludeGeneral ? "in" : "ex",
@@ -5915,7 +6345,7 @@ var __defProp = Object.defineProperty;
5915
6345
  ]
5916
6346
  }
5917
6347
  ),
5918
- /* @__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)(
5919
6349
  Segmented,
5920
6350
  {
5921
6351
  value: isOff ? "off" : "on",
@@ -5930,7 +6360,7 @@ var __defProp = Object.defineProperty;
5930
6360
  onChange: (key) => props.onCommit(key === "off" ? "off" : "auto")
5931
6361
  }
5932
6362
  ) }),
5933
- 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)(
5934
6364
  Segmented,
5935
6365
  {
5936
6366
  value: props.recall === null ? "follow" : props.recall ? "on" : "off",
@@ -5943,7 +6373,7 @@ var __defProp = Object.defineProperty;
5943
6373
  onChange: (key) => props.onCommitRecall(key === "on" ? true : key === "off" ? false : null)
5944
6374
  }
5945
6375
  ) }) : null,
5946
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6376
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5947
6377
  "div",
5948
6378
  {
5949
6379
  style: {
@@ -5954,8 +6384,8 @@ var __defProp = Object.defineProperty;
5954
6384
  marginTop: 10
5955
6385
  },
5956
6386
  children: [
5957
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `未打标 ${overview.unlabeled} 条` : "未打标计数加载中" }),
5958
- /* @__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)(
5959
6389
  ActionButton,
5960
6390
  {
5961
6391
  label: backfillBusy ? "回填中…" : "回填",
@@ -5967,12 +6397,13 @@ var __defProp = Object.defineProperty;
5967
6397
  ]
5968
6398
  }
5969
6399
  ),
5970
- 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,
5971
- /* @__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 })
5972
6403
  ] });
5973
6404
  }
5974
6405
  function Row(props) {
5975
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6406
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5976
6407
  "div",
5977
6408
  {
5978
6409
  style: {
@@ -5982,7 +6413,7 @@ var __defProp = Object.defineProperty;
5982
6413
  marginTop: 10
5983
6414
  },
5984
6415
  children: [
5985
- /* @__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 }),
5986
6417
  props.children
5987
6418
  ]
5988
6419
  }
@@ -5990,9 +6421,9 @@ var __defProp = Object.defineProperty;
5990
6421
  }
5991
6422
  var LABEL_FALLBACK = ["工作", "人际", "学习", "创作娱乐", "居家", "健康", "财务", "出行"];
5992
6423
  function useViewportClamp(popRef) {
5993
- const shiftRef = (0, import_react21.useRef)(0);
5994
- const [shiftX, setShiftX] = (0, import_react21.useState)(0);
5995
- (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)(() => {
5996
6427
  const clamp = () => {
5997
6428
  const el = popRef.current;
5998
6429
  if (!el) return;
@@ -6022,24 +6453,24 @@ var __defProp = Object.defineProperty;
6022
6453
  }
6023
6454
 
6024
6455
  // client/src/pill/MemoryModePill.tsx
6025
- var import_jsx_runtime22 = require("react/jsx-runtime");
6456
+ var import_jsx_runtime25 = require("react/jsx-runtime");
6026
6457
  function MemoryModePill(props) {
6027
6458
  const rpc = props.rpc;
6028
6459
  const sessionId = props.sessionId || props.session && props.session.sessionId;
6029
- const [mode, setMode] = (0, import_react22.useState)(null);
6030
- const [recall, setRecall] = (0, import_react22.useState)(null);
6031
- const [recallResolved, setRecallResolved] = (0, import_react22.useState)(true);
6032
- const [halls, setWings] = (0, import_react22.useState)([]);
6033
- const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0, import_react22.useState)(true);
6034
- const [hallIncludeGeneral, setWingIncludeGeneral] = (0, import_react22.useState)(false);
6035
- const [wingLabels, setHallLabels] = (0, import_react22.useState)({});
6036
- const [error, setError] = (0, import_react22.useState)(null);
6037
- const [open, setOpen] = (0, import_react22.useState)(false);
6038
- const wrapRef = (0, import_react22.useRef)(null);
6039
- 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);
6040
6471
  const shiftX = useViewportClamp(popRef);
6041
- const seqRef = (0, import_react22.useRef)(0);
6042
- const load = (0, import_react22.useCallback)(() => {
6472
+ const seqRef = (0, import_react25.useRef)(0);
6473
+ const load = (0, import_react25.useCallback)(() => {
6043
6474
  if (!sessionId || !rpc) return;
6044
6475
  const token = ++seqRef.current;
6045
6476
  setError(null);
@@ -6064,13 +6495,13 @@ var __defProp = Object.defineProperty;
6064
6495
  }).catch(() => {
6065
6496
  });
6066
6497
  }, [sessionId, rpc]);
6067
- (0, import_react22.useEffect)(() => {
6498
+ (0, import_react25.useEffect)(() => {
6068
6499
  load();
6069
6500
  }, [load]);
6070
- (0, import_react22.useEffect)(() => {
6501
+ (0, import_react25.useEffect)(() => {
6071
6502
  watchSidebarIcon();
6072
6503
  }, []);
6073
- (0, import_react22.useEffect)(() => {
6504
+ (0, import_react25.useEffect)(() => {
6074
6505
  initOccupancyIndicator(
6075
6506
  (endpoint, payload) => rpc(endpoint, payload)
6076
6507
  );
@@ -6078,7 +6509,7 @@ var __defProp = Object.defineProperty;
6078
6509
  watchContextMeter();
6079
6510
  noteOccupancySession(sessionId ?? null);
6080
6511
  }, [sessionId, rpc]);
6081
- (0, import_react22.useEffect)(() => {
6512
+ (0, import_react25.useEffect)(() => {
6082
6513
  if (!open) return;
6083
6514
  const onDown = (e) => {
6084
6515
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -6093,7 +6524,7 @@ var __defProp = Object.defineProperty;
6093
6524
  document.removeEventListener("keydown", onKey);
6094
6525
  };
6095
6526
  }, [open]);
6096
- const commit = (next) => {
6527
+ const commit2 = (next) => {
6097
6528
  if (!rpc || !sessionId || mode === null || next === mode) return;
6098
6529
  const prev = mode;
6099
6530
  const token = seqRef.current;
@@ -6218,8 +6649,8 @@ var __defProp = Object.defineProperty;
6218
6649
  pillStyle.boxShadow = "0 0 12px color-mix(in srgb, " + info.color + " 30%, transparent)";
6219
6650
  pillStyle["--dsh-mem-pill-tint"] = info.color;
6220
6651
  }
6221
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
6222
- /* @__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)(
6223
6654
  "button",
6224
6655
  {
6225
6656
  type: "button",
@@ -6232,11 +6663,11 @@ var __defProp = Object.defineProperty;
6232
6663
  style: pillStyle,
6233
6664
  children: [
6234
6665
  "记忆 · ",
6235
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: faceLabel })
6666
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: faceLabel })
6236
6667
  ]
6237
6668
  }
6238
6669
  ),
6239
- open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6670
+ open ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6240
6671
  "div",
6241
6672
  {
6242
6673
  ref: popRef,
@@ -6247,19 +6678,19 @@ var __defProp = Object.defineProperty;
6247
6678
  transform: "translateX(calc(-50% + " + shiftX + "px))",
6248
6679
  zIndex: 1e3
6249
6680
  },
6250
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6681
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6251
6682
  "div",
6252
6683
  {
6253
6684
  className: "dsh-mem-popover",
6254
6685
  style: { position: "relative", padding: "10px 12px" },
6255
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6686
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6256
6687
  WingWheel,
6257
6688
  {
6258
6689
  mode: mode || "auto",
6259
6690
  halls,
6260
6691
  hallIncludeUnlabeled,
6261
6692
  hallIncludeGeneral,
6262
- onCommit: commit,
6693
+ onCommit: commit2,
6263
6694
  onCommitWing: commitWing,
6264
6695
  onCommitWingBoundaries: commitWingBoundaries,
6265
6696
  recall: loaded ? recall : void 0,