dsh-prime-memory 0.16.1 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/cordis.patch.yml +6 -0
- package/dist/client.js +1333 -168
- package/dist/config.d.ts +37 -0
- package/dist/config.js +7 -0
- package/dist/contract.d.ts +155 -1
- package/dist/hooks/capture-recovery.d.ts +64 -0
- package/dist/hooks/capture-recovery.js +105 -0
- package/dist/hooks/capture.js +123 -1
- package/dist/hooks/recall-ack.d.ts +48 -0
- package/dist/hooks/recall-ack.js +79 -0
- package/dist/hooks/recall.d.ts +23 -1
- package/dist/hooks/recall.js +121 -6
- package/dist/index.d.ts +22 -0
- package/dist/index.js +18 -4
- package/dist/metadata-validators.d.ts +5 -0
- package/dist/metadata-validators.js +11 -1
- package/dist/pipeline/l1.d.ts +4 -0
- package/dist/pipeline/l1.js +9 -2
- package/dist/pipeline/reconcile.js +4 -0
- package/dist/pipeline/ruminate.d.ts +11 -1
- package/dist/pipeline/ruminate.js +18 -1
- package/dist/pipeline/runner.js +62 -0
- package/dist/prompts/boundary.d.ts +17 -0
- package/dist/prompts/boundary.js +20 -0
- package/dist/prompts/graph-projection.js +4 -1
- package/dist/prompts/l1-dedup.d.ts +3 -3
- package/dist/prompts/l1-dedup.js +9 -1
- package/dist/prompts/l1-extraction.d.ts +3 -3
- package/dist/prompts/l1-extraction.js +12 -0
- package/dist/prompts/persona.js +6 -0
- package/dist/prompts/scene.js +7 -1
- package/dist/room-review.d.ts +196 -0
- package/dist/room-review.js +281 -0
- package/dist/stats.d.ts +26 -2
- package/dist/stats.js +270 -13
- package/dist/store/l0.d.ts +4 -0
- package/dist/store/l0.js +8 -0
- package/dist/store/l1.d.ts +34 -0
- package/dist/store/l1.js +68 -1
- package/dist/store/recall-usage.d.ts +43 -0
- package/dist/store/recall-usage.js +149 -0
- package/dist/store/rooms-registry.d.ts +66 -0
- package/dist/store/rooms-registry.js +250 -0
- package/dist/store/sqlite.d.ts +16 -0
- package/dist/store/sqlite.js +57 -0
- package/dist/store/trace.d.ts +39 -0
- package/dist/store/trace.js +149 -0
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.js +249 -2
- package/dist/types.d.ts +4 -0
- package/dist/util/redact.d.ts +27 -0
- package/dist/util/redact.js +228 -0
- package/dsh.plugin.json +1 -1
- package/package.json +10 -10
- package/dist/hall-backfill.d.ts +0 -33
- package/dist/hall-backfill.js +0 -95
- package/dist/hall-migrate.d.ts +0 -32
- package/dist/hall-migrate.js +0 -88
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
|
|
35
|
+
var import_react19 = 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>';
|
|
@@ -1178,20 +1178,46 @@ var __defProp = Object.defineProperty;
|
|
|
1178
1178
|
// client/src/tabs/LogTab.tsx
|
|
1179
1179
|
var import_react4 = require("react");
|
|
1180
1180
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1181
|
+
var SOURCES = [
|
|
1182
|
+
{ id: "", label: "系统日志" },
|
|
1183
|
+
{ id: "recall_turn", label: "召回追踪" },
|
|
1184
|
+
{ id: "distill_run", label: "蒸馏追踪" }
|
|
1185
|
+
];
|
|
1186
|
+
function fmtEvent(ev) {
|
|
1187
|
+
if (ev.kind === "recall_turn") {
|
|
1188
|
+
const outcome = String(ev.outcome ?? "?");
|
|
1189
|
+
const hits = ev.hitIds?.length ?? 0;
|
|
1190
|
+
const injected = ev.injectedIds?.length ?? 0;
|
|
1191
|
+
const q = ev.queryText ? ` query="${String(ev.queryText).slice(0, 40)}"` : ` sha=${String(ev.querySha ?? "")}`;
|
|
1192
|
+
return `[${new Date(Number(ev.ts)).toLocaleTimeString()}] recall ${outcome}${q} hits=${hits} injected=${injected} suppressed=${String(ev.suppressedCount ?? 0)} ${String(ev.durationMs ?? 0)}ms`;
|
|
1193
|
+
}
|
|
1194
|
+
if (ev.kind === "distill_run") {
|
|
1195
|
+
const by = ev.byKind ? " byKind=" + JSON.stringify(ev.byKind) : "";
|
|
1196
|
+
const err = ev.errorKind ? " error=" + String(ev.errorKind) : "";
|
|
1197
|
+
return `[${new Date(Number(ev.ts)).toLocaleTimeString()}] distill ${String(ev.layer)}${ev.runId ? " run=" + String(ev.runId) : ""} ok=${String(ev.ok)} in=${String(ev.inputChars ?? 0)}ch new=${String(ev.newRecords ?? 0)} ${String(ev.durationMs ?? 0)}ms${by}${err}`;
|
|
1198
|
+
}
|
|
1199
|
+
return JSON.stringify(ev);
|
|
1200
|
+
}
|
|
1181
1201
|
function LogTab(props) {
|
|
1182
1202
|
const rpc = props.rpc;
|
|
1203
|
+
const [source, setSource] = (0, import_react4.useState)("");
|
|
1183
1204
|
const [lines, setLines] = (0, import_react4.useState)(null);
|
|
1184
1205
|
const [error, setError] = (0, import_react4.useState)(null);
|
|
1185
1206
|
const preRef = (0, import_react4.useRef)(null);
|
|
1186
1207
|
const load = (0, import_react4.useCallback)(() => {
|
|
1187
1208
|
setError(null);
|
|
1188
|
-
|
|
1189
|
-
|
|
1209
|
+
if (source === "") {
|
|
1210
|
+
rpc("dsh-memory/log-tail", { lines: 200 }).then((r) => {
|
|
1211
|
+
if (r && r.ok) setLines(r.value.lines);
|
|
1212
|
+
else setError(r && r.error ? r.error.message : "RPC error");
|
|
1213
|
+
}).catch((e) => setError(String(e && e.message || e)));
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
rpc("dsh-memory/trace-tail", { lines: 200, kind: source }).then((r) => {
|
|
1217
|
+
if (r && r.ok) setLines(r.value.events.map(fmtEvent));
|
|
1190
1218
|
else setError(r && r.error ? r.error.message : "RPC error");
|
|
1191
|
-
}).catch((e) =>
|
|
1192
|
-
|
|
1193
|
-
});
|
|
1194
|
-
}, [rpc]);
|
|
1219
|
+
}).catch((e) => setError(String(e && e.message || e)));
|
|
1220
|
+
}, [rpc, source]);
|
|
1195
1221
|
(0, import_react4.useEffect)(() => {
|
|
1196
1222
|
load();
|
|
1197
1223
|
}, [load]);
|
|
@@ -1199,12 +1225,13 @@ var __defProp = Object.defineProperty;
|
|
|
1199
1225
|
if (lines && preRef.current) preRef.current.scrollTop = preRef.current.scrollHeight;
|
|
1200
1226
|
}, [lines]);
|
|
1201
1227
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
1202
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
1203
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
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)),
|
|
1204
1230
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: S.grow }),
|
|
1205
1231
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NButton, { onClick: load, children: "刷新" })
|
|
1206
1232
|
] }),
|
|
1207
|
-
|
|
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") || "(暂无记录)" })
|
|
1208
1235
|
] });
|
|
1209
1236
|
}
|
|
1210
1237
|
|
|
@@ -1618,8 +1645,8 @@ var __defProp = Object.defineProperty;
|
|
|
1618
1645
|
setOpen(false);
|
|
1619
1646
|
setIdx(-1);
|
|
1620
1647
|
if (refocus && wrapRef.current) {
|
|
1621
|
-
const
|
|
1622
|
-
if (
|
|
1648
|
+
const btn3 = wrapRef.current.querySelector("button");
|
|
1649
|
+
if (btn3 && btn3.focus) btn3.focus();
|
|
1623
1650
|
}
|
|
1624
1651
|
};
|
|
1625
1652
|
const pick = (id) => {
|
|
@@ -3260,10 +3287,11 @@ var __defProp = Object.defineProperty;
|
|
|
3260
3287
|
const [sceneFilter, setSceneFilter] = (0, import_react15.useState)("");
|
|
3261
3288
|
const [hallFilter, setHallFilter] = (0, import_react15.useState)([]);
|
|
3262
3289
|
const [tagFilter, setTagFilter] = (0, import_react15.useState)("");
|
|
3290
|
+
const [untaggedOnly, setUntaggedOnly] = (0, import_react15.useState)(false);
|
|
3291
|
+
const [orphanCount, setOrphanCount] = (0, import_react15.useState)(0);
|
|
3263
3292
|
const [retiredFilter, setRetiredFilter] = (0, import_react15.useState)("");
|
|
3264
|
-
const [rooms, setRooms] = (0, import_react15.useState)([]);
|
|
3265
3293
|
const [wingCatalog, setHallCatalog] = (0, import_react15.useState)(null);
|
|
3266
|
-
const [last, setLast] = (0, import_react15.useState)({ query: "", type: "", scene: "", halls: [], tag: "", retired: "" });
|
|
3294
|
+
const [last, setLast] = (0, import_react15.useState)({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" });
|
|
3267
3295
|
const seqRef = (0, import_react15.useRef)(0);
|
|
3268
3296
|
const fetchPage = (0, import_react15.useCallback)(
|
|
3269
3297
|
(conds, offset, append) => {
|
|
@@ -3276,6 +3304,7 @@ var __defProp = Object.defineProperty;
|
|
|
3276
3304
|
if (conds.scene) payload.scene = conds.scene;
|
|
3277
3305
|
if (conds.halls.length > 0) payload.halls = conds.halls;
|
|
3278
3306
|
if (conds.tag) payload.tag = conds.tag;
|
|
3307
|
+
if (conds.untagged) payload.untagged = true;
|
|
3279
3308
|
const retiredSel = retiredSelOf(conds.retired);
|
|
3280
3309
|
if (retiredSel !== void 0) payload.retired = retiredSel;
|
|
3281
3310
|
rpc("dsh-memory/list-records", payload).then((r) => {
|
|
@@ -3287,6 +3316,7 @@ var __defProp = Object.defineProperty;
|
|
|
3287
3316
|
}
|
|
3288
3317
|
const v = r.value;
|
|
3289
3318
|
setItems((prev) => append ? prev.concat(v.items) : v.items);
|
|
3319
|
+
if (v.usageSummary) setUsageSummary(v.usageSummary);
|
|
3290
3320
|
if (!append) setSel(/* @__PURE__ */ new Set());
|
|
3291
3321
|
setHasMore(!!v.hasMore);
|
|
3292
3322
|
setTotal(v.total === void 0 || v.total === null ? null : v.total);
|
|
@@ -3302,24 +3332,24 @@ var __defProp = Object.defineProperty;
|
|
|
3302
3332
|
[rpc]
|
|
3303
3333
|
);
|
|
3304
3334
|
const search = () => {
|
|
3305
|
-
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter, retired: retiredFilter };
|
|
3335
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter, untagged: untaggedOnly, retired: retiredFilter };
|
|
3306
3336
|
setLast(conds);
|
|
3307
3337
|
fetchPage(conds, 0, false);
|
|
3308
3338
|
};
|
|
3309
3339
|
const applyRetiredFilter = (next) => {
|
|
3310
3340
|
setRetiredFilter(next);
|
|
3311
|
-
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter, retired: next };
|
|
3341
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter, untagged: untaggedOnly, retired: next };
|
|
3312
3342
|
setLast(conds);
|
|
3313
3343
|
fetchPage(conds, 0, false);
|
|
3314
3344
|
};
|
|
3315
3345
|
const loadRooms = (0, import_react15.useCallback)(() => {
|
|
3316
3346
|
rpc("dsh-memory/rooms-get", {}).then((r) => {
|
|
3317
|
-
if (r && r.ok)
|
|
3347
|
+
if (r && r.ok) setOrphanCount(r.value.orphanCount ?? 0);
|
|
3318
3348
|
}).catch(() => {
|
|
3319
3349
|
});
|
|
3320
3350
|
}, [rpc]);
|
|
3321
3351
|
(0, import_react15.useEffect)(() => {
|
|
3322
|
-
fetchPage({ query: "", type: "", scene: "", halls: [], tag: "", retired: "" }, 0, false);
|
|
3352
|
+
fetchPage({ query: "", type: "", scene: "", halls: [], tag: "", untagged: false, retired: "" }, 0, false);
|
|
3323
3353
|
loadRooms();
|
|
3324
3354
|
}, [fetchPage, loadRooms]);
|
|
3325
3355
|
const loadHiPriv = (0, import_react15.useCallback)(() => {
|
|
@@ -3422,6 +3452,7 @@ var __defProp = Object.defineProperty;
|
|
|
3422
3452
|
unknown: "已退场"
|
|
3423
3453
|
};
|
|
3424
3454
|
const countText = total !== null ? "共 " + total + " 条" : items.length + " 条" + (hasMore ? "+" : "");
|
|
3455
|
+
const [usageSummary, setUsageSummary] = (0, import_react15.useState)(null);
|
|
3425
3456
|
const selCount = sel.size;
|
|
3426
3457
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
3427
3458
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: S.toolbar, children: [
|
|
@@ -3488,40 +3519,86 @@ var __defProp = Object.defineProperty;
|
|
|
3488
3519
|
}
|
|
3489
3520
|
)
|
|
3490
3521
|
] }),
|
|
3491
|
-
|
|
3492
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3522
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { marginBottom: 10, display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
|
|
3523
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3524
|
+
"button",
|
|
3525
|
+
{
|
|
3526
|
+
type: "button",
|
|
3527
|
+
title: untaggedOnly ? "取消孤儿筛选" : "只看没有任何 Room 绑定的记忆",
|
|
3528
|
+
onClick: () => {
|
|
3529
|
+
const next = !untaggedOnly;
|
|
3530
|
+
setUntaggedOnly(next);
|
|
3531
|
+
if (next) setTagFilter("");
|
|
3532
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: next ? "" : tagFilter, untagged: next, retired: retiredFilter };
|
|
3533
|
+
setLast(conds);
|
|
3534
|
+
fetchPage(conds, 0, false);
|
|
3535
|
+
},
|
|
3536
|
+
style: {
|
|
3537
|
+
cursor: "pointer",
|
|
3538
|
+
fontSize: 12,
|
|
3539
|
+
padding: "2px 8px",
|
|
3540
|
+
borderRadius: 999,
|
|
3541
|
+
border: untaggedOnly ? "1px solid var(--dsh-mem-accent)" : "1px solid var(--dsh-mem-border)",
|
|
3542
|
+
background: untaggedOnly ? "var(--dsh-mem-bg-inset)" : "transparent",
|
|
3543
|
+
color: untaggedOnly ? "var(--dsh-mem-accent)" : "var(--dsh-mem-text-2)"
|
|
3544
|
+
},
|
|
3545
|
+
children: "无绑定 · " + orphanCount
|
|
3546
|
+
}
|
|
3547
|
+
),
|
|
3548
|
+
(tagFilter || untaggedOnly) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { style: S.muted, children: [
|
|
3549
|
+
"筛选中:",
|
|
3550
|
+
untaggedOnly ? "无绑定" : tagFilter,
|
|
3551
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3496
3552
|
"button",
|
|
3497
3553
|
{
|
|
3498
3554
|
type: "button",
|
|
3499
|
-
title:
|
|
3555
|
+
title: "清除 Room/孤儿筛选",
|
|
3500
3556
|
onClick: () => {
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag:
|
|
3557
|
+
setTagFilter("");
|
|
3558
|
+
setUntaggedOnly(false);
|
|
3559
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: "", untagged: false, retired: retiredFilter };
|
|
3504
3560
|
setLast(conds);
|
|
3505
3561
|
fetchPage(conds, 0, false);
|
|
3506
3562
|
},
|
|
3507
|
-
style: {
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3563
|
+
style: { cursor: "pointer", marginLeft: 6, background: "transparent", border: "none", color: "var(--dsh-mem-accent)", fontSize: 12 },
|
|
3564
|
+
children: "×清除"
|
|
3565
|
+
}
|
|
3566
|
+
)
|
|
3567
|
+
] }),
|
|
3568
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3569
|
+
"button",
|
|
3570
|
+
{
|
|
3571
|
+
type: "button",
|
|
3572
|
+
title: "导出无 Room 绑定的孤儿清单 CSV(含候选)",
|
|
3573
|
+
onClick: () => {
|
|
3574
|
+
rpc("dsh-memory/rooms-export", { kind: "orphans" }).then((r) => {
|
|
3575
|
+
if (!r || !r.ok || !r.value?.csv) return;
|
|
3576
|
+
const blob = new Blob([r.value.csv], { type: "text/csv;charset=utf-8" });
|
|
3577
|
+
const url = URL.createObjectURL(blob);
|
|
3578
|
+
const a = document.createElement("a");
|
|
3579
|
+
a.href = url;
|
|
3580
|
+
a.download = "dsh-memory-room-orphans.csv";
|
|
3581
|
+
a.click();
|
|
3582
|
+
URL.revokeObjectURL(url);
|
|
3583
|
+
}).catch(() => void 0);
|
|
3517
3584
|
},
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3585
|
+
style: {
|
|
3586
|
+
cursor: "pointer",
|
|
3587
|
+
fontSize: 12,
|
|
3588
|
+
padding: "2px 8px",
|
|
3589
|
+
borderRadius: 6,
|
|
3590
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
3591
|
+
background: "transparent",
|
|
3592
|
+
color: "var(--dsh-mem-text-2)"
|
|
3593
|
+
},
|
|
3594
|
+
children: "导出孤儿"
|
|
3595
|
+
}
|
|
3596
|
+
),
|
|
3597
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "Room 标签/分类管理:见「Hall」「Room」标签页。" })
|
|
3598
|
+
] }),
|
|
3523
3599
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
3524
3600
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
|
|
3601
|
+
usageSummary ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, title: "老化权重效果口径:参与召回尝试的记忆中,进 topN(被注入上下文)的比例", children: "召回使用 " + usageSummary.used + "/" + usageSummary.attempted + "(" + usageSummary.rate + "%)" }) : null,
|
|
3525
3602
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "退场筛查" }),
|
|
3526
3603
|
[["", "全部"], ["active", "仅活跃"], ["retired", "仅退场"]].map(([val, label]) => {
|
|
3527
3604
|
const on = retiredFilter === val;
|
|
@@ -3625,6 +3702,29 @@ var __defProp = Object.defineProperty;
|
|
|
3625
3702
|
}
|
|
3626
3703
|
) : null,
|
|
3627
3704
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "优先级 " + m.priority }),
|
|
3705
|
+
m.agingWeight != null ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3706
|
+
"span",
|
|
3707
|
+
{
|
|
3708
|
+
style: {
|
|
3709
|
+
fontSize: 11,
|
|
3710
|
+
padding: "1px 6px",
|
|
3711
|
+
borderRadius: 999,
|
|
3712
|
+
whiteSpace: "nowrap",
|
|
3713
|
+
border: "1px solid " + (m.agingWeight <= 0.6 ? "var(--dsh-mem-danger)" : "var(--dsh-mem-border)"),
|
|
3714
|
+
color: m.agingWeight <= 0.6 ? "var(--dsh-mem-danger)" : "var(--dsh-mem-text-2)"
|
|
3715
|
+
},
|
|
3716
|
+
title: "老化权重 " + Math.round(m.agingWeight * 100) + "%(读路径衰减系数 max(0.5, 0.5^(Δ天/半衰期)));越低越老",
|
|
3717
|
+
children: "老化 " + Math.round(m.agingWeight * 100) + "%"
|
|
3718
|
+
}
|
|
3719
|
+
) : null,
|
|
3720
|
+
m.usage ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3721
|
+
"span",
|
|
3722
|
+
{
|
|
3723
|
+
style: S.muted,
|
|
3724
|
+
title: "召回使用 " + m.usage.used + "/" + m.usage.attempts + "(进 topN 才算 used)" + (m.usage.lastUsedAt ? ";最近使用 " + fmtTime(new Date(m.usage.lastUsedAt).toISOString()) : "") + (m.usage.lastAttemptAt ? ";最近尝试 " + fmtTime(new Date(m.usage.lastAttemptAt).toISOString()) : ""),
|
|
3725
|
+
children: "用 " + m.usage.used + "/" + m.usage.attempts
|
|
3726
|
+
}
|
|
3727
|
+
) : null,
|
|
3628
3728
|
m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "相关度 " + Number(m.score).toFixed(2) }) : null,
|
|
3629
3729
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: S.grow }),
|
|
3630
3730
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: fmtTime(m.updatedAt) }),
|
|
@@ -3747,14 +3847,1075 @@ var __defProp = Object.defineProperty;
|
|
|
3747
3847
|
] });
|
|
3748
3848
|
}
|
|
3749
3849
|
|
|
3750
|
-
// client/src/tabs/
|
|
3850
|
+
// client/src/tabs/RoomsTab.tsx
|
|
3751
3851
|
var import_react16 = require("react");
|
|
3752
3852
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3853
|
+
function majorOf(slug) {
|
|
3854
|
+
const i = slug.indexOf("/");
|
|
3855
|
+
return i === -1 ? slug : slug.slice(0, i);
|
|
3856
|
+
}
|
|
3857
|
+
function suffixOf(slug) {
|
|
3858
|
+
const i = slug.indexOf("/");
|
|
3859
|
+
return i === -1 ? slug : slug.slice(i + 1);
|
|
3860
|
+
}
|
|
3861
|
+
function downloadCsv(csv, name) {
|
|
3862
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
|
3863
|
+
const url = URL.createObjectURL(blob);
|
|
3864
|
+
const a = document.createElement("a");
|
|
3865
|
+
a.href = url;
|
|
3866
|
+
a.download = name;
|
|
3867
|
+
a.click();
|
|
3868
|
+
URL.revokeObjectURL(url);
|
|
3869
|
+
}
|
|
3870
|
+
var btn = {
|
|
3871
|
+
cursor: "pointer",
|
|
3872
|
+
fontSize: 12,
|
|
3873
|
+
padding: "2px 8px",
|
|
3874
|
+
borderRadius: 6,
|
|
3875
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
3876
|
+
background: "transparent",
|
|
3877
|
+
color: "var(--dsh-mem-text-2)"
|
|
3878
|
+
};
|
|
3879
|
+
function RoomsTab(props) {
|
|
3880
|
+
const rpc = props.rpc;
|
|
3881
|
+
const [registry, setRegistry] = (0, import_react16.useState)([]);
|
|
3882
|
+
const [rooms, setRooms] = (0, import_react16.useState)([]);
|
|
3883
|
+
const [orphanCount, setOrphanCount] = (0, import_react16.useState)(0);
|
|
3884
|
+
const [busy, setBusy] = (0, import_react16.useState)(false);
|
|
3885
|
+
const [msg, setMsg] = (0, import_react16.useState)(null);
|
|
3886
|
+
const [error, setError] = (0, import_react16.useState)(null);
|
|
3887
|
+
const [hiPriv, setHiPriv] = (0, import_react16.useState)(false);
|
|
3888
|
+
const [hiPrivBusy, setHiPrivBusy] = (0, import_react16.useState)(false);
|
|
3889
|
+
const [regSlug, setRegSlug] = (0, import_react16.useState)("");
|
|
3890
|
+
const [regLabel, setRegLabel] = (0, import_react16.useState)("");
|
|
3891
|
+
const [regDesc, setRegDesc] = (0, import_react16.useState)("");
|
|
3892
|
+
const [form, setForm] = (0, import_react16.useState)(null);
|
|
3893
|
+
const [preview, setPreview] = (0, import_react16.useState)(null);
|
|
3894
|
+
const [grownOpen, setGrownOpen] = (0, import_react16.useState)(false);
|
|
3895
|
+
const [dragOverHall, setDragOverHall] = (0, import_react16.useState)(null);
|
|
3896
|
+
const [picking, setPicking] = (0, import_react16.useState)(null);
|
|
3897
|
+
const [editing, setEditing] = (0, import_react16.useState)(null);
|
|
3898
|
+
const [expandedRooms, setExpandedRooms] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
|
|
3899
|
+
const [roomRecords, setRoomRecords] = (0, import_react16.useState)({});
|
|
3900
|
+
const [expandedHalls, setExpandedHalls] = (0, import_react16.useState)(() => {
|
|
3901
|
+
try {
|
|
3902
|
+
const v = window.localStorage.getItem("dsh.memory.halls.open");
|
|
3903
|
+
return v ? new Set(JSON.parse(v)) : /* @__PURE__ */ new Set();
|
|
3904
|
+
} catch {
|
|
3905
|
+
return /* @__PURE__ */ new Set();
|
|
3906
|
+
}
|
|
3907
|
+
});
|
|
3908
|
+
const [filter, setFilter] = (0, import_react16.useState)("");
|
|
3909
|
+
const dragRef = (0, import_react16.useRef)(null);
|
|
3910
|
+
const [ghost, setGhost] = (0, import_react16.useState)(null);
|
|
3911
|
+
const onDragLabelDown = (ev, slug) => {
|
|
3912
|
+
if (!hiPriv || busy || ev.button !== 0) return;
|
|
3913
|
+
ev.currentTarget.setPointerCapture(ev.pointerId);
|
|
3914
|
+
dragRef.current = { from: slug, x: ev.clientX, y: ev.clientY, active: false };
|
|
3915
|
+
};
|
|
3916
|
+
const onDragLabelMove = (ev) => {
|
|
3917
|
+
const d = dragRef.current;
|
|
3918
|
+
if (!d) return;
|
|
3919
|
+
if (!d.active && Math.hypot(ev.clientX - d.x, ev.clientY - d.y) > 6) d.active = true;
|
|
3920
|
+
if (!d.active) return;
|
|
3921
|
+
setGhost({ x: ev.clientX, y: ev.clientY, text: d.from });
|
|
3922
|
+
const hall = document.elementFromPoint(ev.clientX, ev.clientY)?.closest?.("[data-hall]")?.getAttribute("data-hall") ?? null;
|
|
3923
|
+
setDragOverHall(hall);
|
|
3924
|
+
};
|
|
3925
|
+
const onDragLabelUp = (ev) => {
|
|
3926
|
+
const d = dragRef.current;
|
|
3927
|
+
dragRef.current = null;
|
|
3928
|
+
setGhost(null);
|
|
3929
|
+
if (!d) return;
|
|
3930
|
+
if (!d.active) return;
|
|
3931
|
+
const hall = document.elementFromPoint(ev.clientX, ev.clientY)?.closest?.("[data-hall]")?.getAttribute("data-hall");
|
|
3932
|
+
setDragOverHall(null);
|
|
3933
|
+
if (hall) beginReclass(d.from, hall);
|
|
3934
|
+
};
|
|
3935
|
+
const loadAll = (0, import_react16.useCallback)(() => {
|
|
3936
|
+
rpc("dsh-memory/rooms-get", {}).then((r) => {
|
|
3937
|
+
if (r && r.ok) {
|
|
3938
|
+
setRooms(r.value.rooms ?? []);
|
|
3939
|
+
setRegistry(r.value.registry ?? []);
|
|
3940
|
+
setOrphanCount(r.value.orphanCount ?? 0);
|
|
3941
|
+
}
|
|
3942
|
+
}).catch(() => void 0);
|
|
3943
|
+
}, [rpc]);
|
|
3944
|
+
const loadHiPriv = (0, import_react16.useCallback)(() => {
|
|
3945
|
+
rpc("dsh-memory/settings-get", {}).then((r) => {
|
|
3946
|
+
if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
|
|
3947
|
+
}).catch(() => {
|
|
3948
|
+
});
|
|
3949
|
+
}, [rpc]);
|
|
3950
|
+
(0, import_react16.useEffect)(() => {
|
|
3951
|
+
loadAll();
|
|
3952
|
+
loadHiPriv();
|
|
3953
|
+
}, [loadAll, loadHiPriv]);
|
|
3954
|
+
const toggleHiPriv = () => {
|
|
3955
|
+
const next = !hiPriv;
|
|
3956
|
+
if (next && !window.confirm("开启高权限模式:模型获得写入/删除记忆工具,并解锁 Hall 页的分类管理操作。确定开启?")) return;
|
|
3957
|
+
setHiPrivBusy(true);
|
|
3958
|
+
rpc("dsh-memory/settings-set", { memoryMutate: next }).then((r) => {
|
|
3959
|
+
if (r && r.ok) setHiPriv(next);
|
|
3960
|
+
else if (r) setError(r.error ? r.error.message : "切换高权限模式失败");
|
|
3961
|
+
setHiPrivBusy(false);
|
|
3962
|
+
loadHiPriv();
|
|
3963
|
+
}).catch((e) => {
|
|
3964
|
+
setHiPrivBusy(false);
|
|
3965
|
+
setError(String(e && e.message || e));
|
|
3966
|
+
});
|
|
3967
|
+
};
|
|
3968
|
+
const run = (fn) => {
|
|
3969
|
+
if (busy) return;
|
|
3970
|
+
setBusy(true);
|
|
3971
|
+
setError(null);
|
|
3972
|
+
setMsg(null);
|
|
3973
|
+
fn().finally(() => setBusy(false));
|
|
3974
|
+
};
|
|
3975
|
+
const register = () => run(async () => {
|
|
3976
|
+
const r = await rpc("dsh-memory/room-register", {
|
|
3977
|
+
slug: regSlug.trim(),
|
|
3978
|
+
label: regLabel.trim() || void 0,
|
|
3979
|
+
description: regDesc.trim() || void 0
|
|
3980
|
+
});
|
|
3981
|
+
if (r && r.ok) {
|
|
3982
|
+
setMsg(r.value?.notice ?? `已注册:${regSlug.trim()}`);
|
|
3983
|
+
setRegSlug("");
|
|
3984
|
+
setRegLabel("");
|
|
3985
|
+
setRegDesc("");
|
|
3986
|
+
loadAll();
|
|
3987
|
+
} else setError(r && r.error ? r.error.message : "注册失败");
|
|
3988
|
+
});
|
|
3989
|
+
const adopt = (slug) => run(async () => {
|
|
3990
|
+
const r = await rpc("dsh-memory/room-register", { slug, source: "grown" });
|
|
3991
|
+
if (r && r.ok) {
|
|
3992
|
+
setMsg(`已收编:${slug}(source=grown)`);
|
|
3993
|
+
loadAll();
|
|
3994
|
+
} else setError(r && r.error ? r.error.message : "收编失败");
|
|
3995
|
+
});
|
|
3996
|
+
const previewMerge = (override) => run(async () => {
|
|
3997
|
+
const fm = override ?? form;
|
|
3998
|
+
if (!fm) return;
|
|
3999
|
+
const to = fm.to.trim();
|
|
4000
|
+
if (!to) {
|
|
4001
|
+
setError("目标 slug 不能为空");
|
|
4002
|
+
return;
|
|
4003
|
+
}
|
|
4004
|
+
if (to === fm.from) {
|
|
4005
|
+
setError("目标与源相同");
|
|
4006
|
+
return;
|
|
4007
|
+
}
|
|
4008
|
+
const r = await rpc("dsh-memory/room-admin", { action: fm.action, from: fm.from, to });
|
|
4009
|
+
if (r && r.ok) {
|
|
4010
|
+
setPreview({ action: fm.action, from: fm.from, to, notice: String(r.value?.notice ?? "") });
|
|
4011
|
+
} else setError(r && r.error ? r.error.message : "预览失败");
|
|
4012
|
+
});
|
|
4013
|
+
const beginReclass = (from, hall) => {
|
|
4014
|
+
if (!busy) {
|
|
4015
|
+
setMsg(null);
|
|
4016
|
+
setError(null);
|
|
4017
|
+
}
|
|
4018
|
+
if (!hall) {
|
|
4019
|
+
setError("尚无 hall:先注册一个(如 dsh-plugin),再归类。");
|
|
4020
|
+
return;
|
|
4021
|
+
}
|
|
4022
|
+
if (majorOf(from) === hall) {
|
|
4023
|
+
if (registry.some((e) => e.slug === from)) {
|
|
4024
|
+
setMsg(`${from} 已在 ${hall} 下。`);
|
|
4025
|
+
return;
|
|
4026
|
+
}
|
|
4027
|
+
setPicking(null);
|
|
4028
|
+
void adopt(from);
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
4031
|
+
const f = { action: "rename", from, to: `${hall}/${suffixOf(from)}` };
|
|
4032
|
+
setForm(f);
|
|
4033
|
+
setPreview(null);
|
|
4034
|
+
void previewMerge(f);
|
|
4035
|
+
};
|
|
4036
|
+
const execMerge = () => run(async () => {
|
|
4037
|
+
if (!preview) return;
|
|
4038
|
+
if (preview.action === "rename" && !registry.some((e) => e.slug === preview.from)) {
|
|
4039
|
+
const rr = await rpc("dsh-memory/room-register", { slug: preview.from, source: "grown" });
|
|
4040
|
+
if (!rr || !rr.ok) {
|
|
4041
|
+
setError(rr && rr.error ? rr.error.message : "收编源失败");
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
const r = await rpc("dsh-memory/room-admin", { action: preview.action, from: preview.from, to: preview.to, dryRun: false });
|
|
4046
|
+
if (r && r.ok) {
|
|
4047
|
+
setMsg(String(r.value?.notice ?? "完成"));
|
|
4048
|
+
setPreview(null);
|
|
4049
|
+
setForm(null);
|
|
4050
|
+
loadAll();
|
|
4051
|
+
} else setError(r && r.error ? r.error.message : "执行失败");
|
|
4052
|
+
});
|
|
4053
|
+
const retire = (slug, active, withRecords = false) => run(async () => {
|
|
4054
|
+
if (!active) {
|
|
4055
|
+
const tip = withRecords ? `退役 Room:${slug} 并退场其下全部记忆?(记录为软删,可在记忆页「已退场」区逐条恢复)` : `退役 Room:${slug}?(存量记录的 tags 不动,仅词表不再推荐;可随时恢复)`;
|
|
4056
|
+
if (!window.confirm(tip)) return;
|
|
4057
|
+
}
|
|
4058
|
+
const r = await rpc("dsh-memory/room-admin", { action: "retire", slug, active, withRecords: withRecords || void 0 });
|
|
4059
|
+
if (r && r.ok) {
|
|
4060
|
+
setMsg(String(r.value?.notice ?? "完成"));
|
|
4061
|
+
loadAll();
|
|
4062
|
+
if (expandedRooms.has(slug)) {
|
|
4063
|
+
const rr = await rpc("dsh-memory/list-records", { tag: slug, limit: 10, retired: false });
|
|
4064
|
+
if (rr && rr.ok) setRoomRecords((m) => ({ ...m, [slug]: { items: rr.value.items, total: rr.value.total ?? null } }));
|
|
4065
|
+
}
|
|
4066
|
+
} else setError(r && r.error ? r.error.message : "操作失败");
|
|
4067
|
+
});
|
|
4068
|
+
const saveEditing = () => run(async () => {
|
|
4069
|
+
if (!editing) return;
|
|
4070
|
+
const r = await rpc("dsh-memory/room-admin", { action: "update", slug: editing.slug, label: editing.label, description: editing.description });
|
|
4071
|
+
if (r && r.ok) {
|
|
4072
|
+
setMsg(String(r.value?.notice ?? "已更新"));
|
|
4073
|
+
setEditing(null);
|
|
4074
|
+
loadAll();
|
|
4075
|
+
} else setError(r && r.error ? r.error.message : "更新失败");
|
|
4076
|
+
});
|
|
4077
|
+
const toggleRoomExpand = (slug) => run(async () => {
|
|
4078
|
+
if (expandedRooms.has(slug)) {
|
|
4079
|
+
const next = new Set(expandedRooms);
|
|
4080
|
+
next.delete(slug);
|
|
4081
|
+
setExpandedRooms(next);
|
|
4082
|
+
return;
|
|
4083
|
+
}
|
|
4084
|
+
const r = await rpc("dsh-memory/list-records", { tag: slug, limit: 10, retired: false });
|
|
4085
|
+
if (r && r.ok) {
|
|
4086
|
+
const next = new Set(expandedRooms);
|
|
4087
|
+
next.add(slug);
|
|
4088
|
+
setExpandedRooms(next);
|
|
4089
|
+
setRoomRecords((m) => ({ ...m, [slug]: { items: r.value.items, total: r.value.total ?? null } }));
|
|
4090
|
+
} else setError(r && r.error ? r.error.message : "加载失败");
|
|
4091
|
+
});
|
|
4092
|
+
const exportRecords = (slug) => run(async () => {
|
|
4093
|
+
const r = await rpc("dsh-memory/rooms-export", { kind: "records", tag: slug });
|
|
4094
|
+
if (!r.ok) {
|
|
4095
|
+
setError(r.error.message);
|
|
4096
|
+
return;
|
|
4097
|
+
}
|
|
4098
|
+
if (r.value.csv) downloadCsv(r.value.csv, `dsh-memory-room-${slug.replace(/\//g, "-")}-records.csv`);
|
|
4099
|
+
else setError("导出为空");
|
|
4100
|
+
});
|
|
4101
|
+
const groups = (0, import_react16.useMemo)(() => {
|
|
4102
|
+
const countOf = new Map(rooms.map((r) => [r.room, r.count]));
|
|
4103
|
+
const byHall = /* @__PURE__ */ new Map();
|
|
4104
|
+
for (const e of registry) {
|
|
4105
|
+
if (e.status !== "active") continue;
|
|
4106
|
+
const hall = majorOf(e.slug);
|
|
4107
|
+
const list = byHall.get(hall);
|
|
4108
|
+
if (list) list.push(e);
|
|
4109
|
+
else byHall.set(hall, [e]);
|
|
4110
|
+
}
|
|
4111
|
+
return [...byHall.entries()].map(([hall, entries]) => ({
|
|
4112
|
+
hall,
|
|
4113
|
+
label: registry.find((e) => e.slug === hall)?.label,
|
|
4114
|
+
entries: [...entries].sort((a, b) => a.slug.localeCompare(b.slug)),
|
|
4115
|
+
total: entries.reduce((s2, e) => s2 + (countOf.get(e.slug) ?? 0), 0)
|
|
4116
|
+
})).sort((a, b) => a.hall.localeCompare(b.hall));
|
|
4117
|
+
}, [registry, rooms]);
|
|
4118
|
+
const grownOnly = (0, import_react16.useMemo)(() => rooms.filter((r) => !registry.some((e) => e.slug === r.room)), [rooms, registry]);
|
|
4119
|
+
const persistHalls = (next) => {
|
|
4120
|
+
try {
|
|
4121
|
+
window.localStorage.setItem("dsh.memory.halls.open", JSON.stringify([...next]));
|
|
4122
|
+
} catch {
|
|
4123
|
+
}
|
|
4124
|
+
};
|
|
4125
|
+
const toggleHall = (hall) => setExpandedHalls((cur) => {
|
|
4126
|
+
const next = new Set(cur);
|
|
4127
|
+
if (next.has(hall)) next.delete(hall);
|
|
4128
|
+
else next.add(hall);
|
|
4129
|
+
persistHalls(next);
|
|
4130
|
+
return next;
|
|
4131
|
+
});
|
|
4132
|
+
const setAllHalls = (open) => {
|
|
4133
|
+
const next = open ? new Set(groups.map((g) => g.hall)) : /* @__PURE__ */ new Set();
|
|
4134
|
+
setExpandedHalls(next);
|
|
4135
|
+
persistHalls(next);
|
|
4136
|
+
};
|
|
4137
|
+
const normFilter = filter.trim().toLowerCase();
|
|
4138
|
+
const visibleGroups = (0, import_react16.useMemo)(() => {
|
|
4139
|
+
if (!normFilter) return groups;
|
|
4140
|
+
return groups.map((g) => {
|
|
4141
|
+
const hallHit = g.hall.toLowerCase().includes(normFilter) || (g.label ?? "").toLowerCase().includes(normFilter);
|
|
4142
|
+
const entries = hallHit ? g.entries : g.entries.filter(
|
|
4143
|
+
(e) => e.slug.toLowerCase().includes(normFilter) || (e.label ?? "").toLowerCase().includes(normFilter) || (e.aliases ?? []).some((a) => a.toLowerCase().includes(normFilter))
|
|
4144
|
+
);
|
|
4145
|
+
return { ...g, entries };
|
|
4146
|
+
}).filter((g) => g.entries.length > 0);
|
|
4147
|
+
}, [groups, normFilter]);
|
|
4148
|
+
const visibleGrown = (0, import_react16.useMemo)(
|
|
4149
|
+
() => normFilter ? grownOnly.filter((r) => r.room.toLowerCase().includes(normFilter)) : grownOnly,
|
|
4150
|
+
[grownOnly, normFilter]
|
|
4151
|
+
);
|
|
4152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
|
|
4153
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8 }, children: [
|
|
4154
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "Hall = Room 大类 · 大类上限 200,小类「大类/小类」细分不占额度 · 与 Wing(域)筛选无关" }),
|
|
4155
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
|
|
4156
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4157
|
+
"button",
|
|
4158
|
+
{
|
|
4159
|
+
type: "button",
|
|
4160
|
+
title: "导出分类数据 CSV(Room 词表+计数)",
|
|
4161
|
+
onClick: () => run(async () => {
|
|
4162
|
+
const r = await rpc("dsh-memory/rooms-export", { kind: "rooms" });
|
|
4163
|
+
if (!r.ok) {
|
|
4164
|
+
setError(r.error.message);
|
|
4165
|
+
return;
|
|
4166
|
+
}
|
|
4167
|
+
if (r.value.csv) downloadCsv(r.value.csv, "dsh-memory-rooms.csv");
|
|
4168
|
+
}),
|
|
4169
|
+
style: btn,
|
|
4170
|
+
children: "导出词表"
|
|
4171
|
+
}
|
|
4172
|
+
),
|
|
4173
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4174
|
+
"button",
|
|
4175
|
+
{
|
|
4176
|
+
type: "button",
|
|
4177
|
+
title: "导出无 Room 绑定的孤儿清单(含候选)",
|
|
4178
|
+
onClick: () => run(async () => {
|
|
4179
|
+
const r = await rpc("dsh-memory/rooms-export", { kind: "orphans" });
|
|
4180
|
+
if (!r.ok) {
|
|
4181
|
+
setError(r.error.message);
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
4184
|
+
if (r.value.csv) downloadCsv(r.value.csv, "dsh-memory-room-orphans.csv");
|
|
4185
|
+
}),
|
|
4186
|
+
style: btn,
|
|
4187
|
+
children: [
|
|
4188
|
+
"导出孤儿 · ",
|
|
4189
|
+
orphanCount
|
|
4190
|
+
]
|
|
4191
|
+
}
|
|
4192
|
+
),
|
|
4193
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4194
|
+
"button",
|
|
4195
|
+
{
|
|
4196
|
+
type: "button",
|
|
4197
|
+
style: {
|
|
4198
|
+
...btn,
|
|
4199
|
+
color: hiPriv ? "var(--dsh-mem-danger)" : void 0,
|
|
4200
|
+
border: hiPriv ? "1px solid var(--dsh-mem-danger)" : btn.border
|
|
4201
|
+
},
|
|
4202
|
+
disabled: hiPrivBusy,
|
|
4203
|
+
title: hiPriv ? "关闭高权限模式(收回模型写删工具与管理操作)" : "开启高权限模式以解锁分类管理操作",
|
|
4204
|
+
onClick: toggleHiPriv,
|
|
4205
|
+
children: hiPriv ? "高权限:开" : "高权限:关"
|
|
4206
|
+
}
|
|
4207
|
+
),
|
|
4208
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, onClick: loadAll, title: "重拉注册表与计数", children: "刷新" })
|
|
4209
|
+
] }),
|
|
4210
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center", marginBottom: 8, flexWrap: "wrap" }, children: [
|
|
4211
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4212
|
+
"input",
|
|
4213
|
+
{
|
|
4214
|
+
value: filter,
|
|
4215
|
+
onChange: (e) => setFilter(e.target.value),
|
|
4216
|
+
placeholder: "搜索 hall/room(slug、显示名、别名)",
|
|
4217
|
+
style: { ...S.input, width: 250, fontSize: 12 }
|
|
4218
|
+
}
|
|
4219
|
+
),
|
|
4220
|
+
filter ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, onClick: () => setFilter(""), children: "清除" }) : null,
|
|
4221
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `匹配 ${visibleGroups.reduce((s2, g) => s2 + g.entries.length, 0)} room / ${visibleGroups.length} hall` }),
|
|
4222
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
|
|
4223
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, title: "展开全部 hall 的子 room", onClick: () => setAllHalls(true), children: "展开全部" }),
|
|
4224
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, title: "收起全部 hall(只留 hall 头)", onClick: () => setAllHalls(false), children: "收起全部" })
|
|
4225
|
+
] }),
|
|
4226
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
|
|
4227
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4228
|
+
"input",
|
|
4229
|
+
{
|
|
4230
|
+
value: regSlug,
|
|
4231
|
+
onChange: (e) => setRegSlug(e.target.value),
|
|
4232
|
+
placeholder: "slug(如 dsh-plugin 或 dsh-plugin/merge)",
|
|
4233
|
+
disabled: !hiPriv || busy,
|
|
4234
|
+
style: { ...S.input, width: 210, fontSize: 12 }
|
|
4235
|
+
}
|
|
4236
|
+
),
|
|
4237
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4238
|
+
"input",
|
|
4239
|
+
{
|
|
4240
|
+
value: regLabel,
|
|
4241
|
+
onChange: (e) => setRegLabel(e.target.value),
|
|
4242
|
+
placeholder: "名称(可中文)",
|
|
4243
|
+
disabled: !hiPriv || busy,
|
|
4244
|
+
style: { ...S.input, width: 110, fontSize: 12 }
|
|
4245
|
+
}
|
|
4246
|
+
),
|
|
4247
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4248
|
+
"input",
|
|
4249
|
+
{
|
|
4250
|
+
value: regDesc,
|
|
4251
|
+
onChange: (e) => setRegDesc(e.target.value),
|
|
4252
|
+
placeholder: "归类说明(喂标注器)",
|
|
4253
|
+
disabled: !hiPriv || busy,
|
|
4254
|
+
style: { ...S.input, width: 170, fontSize: 12 }
|
|
4255
|
+
}
|
|
4256
|
+
),
|
|
4257
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4258
|
+
"button",
|
|
4259
|
+
{
|
|
4260
|
+
type: "button",
|
|
4261
|
+
disabled: !hiPriv || busy || !regSlug.trim(),
|
|
4262
|
+
onClick: register,
|
|
4263
|
+
style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy || !regSlug.trim() ? 0.5 : 1 },
|
|
4264
|
+
children: "注册"
|
|
4265
|
+
}
|
|
4266
|
+
),
|
|
4267
|
+
!hiPriv && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
|
|
4268
|
+
] }),
|
|
4269
|
+
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: [
|
|
4270
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: S.muted, children: [
|
|
4271
|
+
"编辑 ",
|
|
4272
|
+
editing.slug
|
|
4273
|
+
] }),
|
|
4274
|
+
/* @__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 } }),
|
|
4275
|
+
/* @__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 } }),
|
|
4276
|
+
/* @__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: "保存" }),
|
|
4277
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), style: btn, children: "取消" })
|
|
4278
|
+
] }) : null,
|
|
4279
|
+
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: [
|
|
4280
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: S.muted, children: [
|
|
4281
|
+
form.action === "merge" ? "合并" : "归类/改名",
|
|
4282
|
+
" ",
|
|
4283
|
+
form.from,
|
|
4284
|
+
" →"
|
|
4285
|
+
] }),
|
|
4286
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4287
|
+
"input",
|
|
4288
|
+
{
|
|
4289
|
+
value: form.to,
|
|
4290
|
+
onChange: (ev) => setForm({ ...form, to: ev.target.value }),
|
|
4291
|
+
placeholder: "目标 slug",
|
|
4292
|
+
disabled: busy,
|
|
4293
|
+
style: { ...S.input, width: 190, fontSize: 12 }
|
|
4294
|
+
}
|
|
4295
|
+
),
|
|
4296
|
+
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)(
|
|
4297
|
+
"button",
|
|
4298
|
+
{
|
|
4299
|
+
type: "button",
|
|
4300
|
+
title: `归入 ${gg.hall} 下(${gg.hall}/${suffixOf(form.from)})`,
|
|
4301
|
+
onClick: () => setForm({ ...form, to: `${gg.hall}/${suffixOf(form.from)}` }),
|
|
4302
|
+
style: { ...btn, borderRadius: 999, padding: "1px 8px" },
|
|
4303
|
+
children: gg.hall
|
|
4304
|
+
},
|
|
4305
|
+
gg.hall
|
|
4306
|
+
)) }) : null,
|
|
4307
|
+
/* @__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: "预览" }),
|
|
4308
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => {
|
|
4309
|
+
setForm(null);
|
|
4310
|
+
setPreview(null);
|
|
4311
|
+
setPicking(null);
|
|
4312
|
+
}, style: btn, children: "取消" }),
|
|
4313
|
+
picking ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "挑选中:点击目标 hall 头完成归类" }) : null,
|
|
4314
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "与反刍并发会互相覆盖,请在反刍空闲时执行。" })
|
|
4315
|
+
] }) : null,
|
|
4316
|
+
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: [
|
|
4317
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: preview.notice }),
|
|
4318
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, marginTop: 4 }, children: [
|
|
4319
|
+
/* @__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: [
|
|
4320
|
+
"确认执行",
|
|
4321
|
+
preview.action === "merge" ? "合并" : "归类/改名"
|
|
4322
|
+
] }),
|
|
4323
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => setPreview(null), style: btn, children: "再改改" })
|
|
4324
|
+
] })
|
|
4325
|
+
] }) : null,
|
|
4326
|
+
msg ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.hint, children: msg }) : null,
|
|
4327
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.error, children: error }) : null,
|
|
4328
|
+
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,
|
|
4329
|
+
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) => {
|
|
4330
|
+
const hallOpen = !!normFilter || expandedHalls.has(g.hall);
|
|
4331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginBottom: 10, border: "1px solid var(--dsh-mem-border)", borderRadius: 8, overflow: "hidden" }, children: [
|
|
4332
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4333
|
+
"div",
|
|
4334
|
+
{
|
|
4335
|
+
"data-hall": g.hall,
|
|
4336
|
+
onDragOver: (ev) => {
|
|
4337
|
+
if (!hiPriv || busy) return;
|
|
4338
|
+
ev.preventDefault();
|
|
4339
|
+
ev.dataTransfer.dropEffect = "move";
|
|
4340
|
+
setDragOverHall(g.hall);
|
|
4341
|
+
},
|
|
4342
|
+
onDragLeave: () => setDragOverHall((cur) => cur === g.hall ? null : cur),
|
|
4343
|
+
onDrop: (ev) => {
|
|
4344
|
+
ev.preventDefault();
|
|
4345
|
+
setDragOverHall(null);
|
|
4346
|
+
if (!hiPriv || busy) return;
|
|
4347
|
+
const from = ev.dataTransfer.getData("text/dsh-room");
|
|
4348
|
+
if (from) beginReclass(from, g.hall);
|
|
4349
|
+
},
|
|
4350
|
+
onClick: () => {
|
|
4351
|
+
if (picking) {
|
|
4352
|
+
if (!busy) {
|
|
4353
|
+
const from = picking;
|
|
4354
|
+
setPicking(null);
|
|
4355
|
+
beginReclass(from, g.hall);
|
|
4356
|
+
}
|
|
4357
|
+
return;
|
|
4358
|
+
}
|
|
4359
|
+
toggleHall(g.hall);
|
|
4360
|
+
},
|
|
4361
|
+
style: {
|
|
4362
|
+
display: "flex",
|
|
4363
|
+
alignItems: "center",
|
|
4364
|
+
gap: 8,
|
|
4365
|
+
padding: "5px 10px",
|
|
4366
|
+
background: "var(--dsh-mem-bg-inset)",
|
|
4367
|
+
outline: dragOverHall === g.hall || picking !== null ? "2px dashed var(--dsh-mem-accent)" : void 0,
|
|
4368
|
+
outlineOffset: dragOverHall === g.hall || picking !== null ? "-2px" : void 0,
|
|
4369
|
+
cursor: "pointer"
|
|
4370
|
+
},
|
|
4371
|
+
title: picking ? "点击此 hall:把「" + picking + "」归入 " + g.hall + " 下(major/minor,预览后确认)" : hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
|
|
4372
|
+
children: [
|
|
4373
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4374
|
+
"button",
|
|
4375
|
+
{
|
|
4376
|
+
type: "button",
|
|
4377
|
+
title: hallOpen ? "收起子 room" : "展开子 room(默认折叠)",
|
|
4378
|
+
onClick: (ev) => {
|
|
4379
|
+
ev.stopPropagation();
|
|
4380
|
+
toggleHall(g.hall);
|
|
4381
|
+
},
|
|
4382
|
+
style: { ...btn, borderRadius: 999, padding: "1px 7px", flexShrink: 0 },
|
|
4383
|
+
children: hallOpen ? "▾" : "▸"
|
|
4384
|
+
}
|
|
4385
|
+
),
|
|
4386
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { fontSize: 12, fontWeight: 600 }, children: [
|
|
4387
|
+
g.hall,
|
|
4388
|
+
g.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: " (" + g.label + ")" }) : null
|
|
4389
|
+
] }),
|
|
4390
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `合计 ${g.total} 条 · ${g.entries.length} 个 room` }),
|
|
4391
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
|
|
4392
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4393
|
+
"button",
|
|
4394
|
+
{
|
|
4395
|
+
type: "button",
|
|
4396
|
+
disabled: !hiPriv || busy,
|
|
4397
|
+
title: `预填注册表单为 ${g.hall}/…(注册该 hall 下的小类)`,
|
|
4398
|
+
onClick: (ev) => {
|
|
4399
|
+
ev.stopPropagation();
|
|
4400
|
+
setRegSlug(g.hall + "/");
|
|
4401
|
+
},
|
|
4402
|
+
style: btn,
|
|
4403
|
+
children: "+小类"
|
|
4404
|
+
}
|
|
4405
|
+
)
|
|
4406
|
+
]
|
|
4407
|
+
}
|
|
4408
|
+
),
|
|
4409
|
+
hallOpen ? g.entries.map((e) => {
|
|
4410
|
+
const standalone = e.slug === g.hall;
|
|
4411
|
+
const count = rooms.find((r) => r.room === e.slug)?.count ?? 0;
|
|
4412
|
+
const retired = e.status === "retired";
|
|
4413
|
+
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: [
|
|
4414
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
4415
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
|
|
4416
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4417
|
+
"button",
|
|
4418
|
+
{
|
|
4419
|
+
type: "button",
|
|
4420
|
+
title: expandedRooms.has(e.slug) ? "收起内容" : "展开查看该 Room 的记录内容(默认收起)",
|
|
4421
|
+
onClick: () => void toggleRoomExpand(e.slug),
|
|
4422
|
+
style: { ...btn, borderRadius: 999, padding: "1px 7px", flexShrink: 0 },
|
|
4423
|
+
children: expandedRooms.has(e.slug) ? "▾" : "▸"
|
|
4424
|
+
}
|
|
4425
|
+
),
|
|
4426
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4427
|
+
"span",
|
|
4428
|
+
{
|
|
4429
|
+
style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default", touchAction: "none" },
|
|
4430
|
+
onPointerDown: (ev) => onDragLabelDown(ev, e.slug),
|
|
4431
|
+
onPointerMove: onDragLabelMove,
|
|
4432
|
+
onPointerUp: onDragLabelUp,
|
|
4433
|
+
title: hiPriv && !busy ? "按住拖到某个 hall 头上归类(major/minor;记录随迁,旧名进别名)" : e.description ? `${e.slug} — ${e.description}` : e.slug,
|
|
4434
|
+
children: [
|
|
4435
|
+
standalone ? e.slug : "· " + e.slug.slice(g.hall.length + 1),
|
|
4436
|
+
e.source === "pre-registered" ? " ⭐" : ""
|
|
4437
|
+
]
|
|
4438
|
+
}
|
|
4439
|
+
),
|
|
4440
|
+
e.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: e.label }) : null,
|
|
4441
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: count + " 条" }),
|
|
4442
|
+
retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
|
|
4443
|
+
(e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e.aliases ?? []).join(", ") }) : null
|
|
4444
|
+
] }),
|
|
4445
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
|
|
4446
|
+
!hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
4447
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
|
|
4448
|
+
setEditing({ slug: e.slug, label: e.label ?? "", description: e.description ?? "" });
|
|
4449
|
+
setForm(null);
|
|
4450
|
+
setPreview(null);
|
|
4451
|
+
setPicking(null);
|
|
4452
|
+
}, style: btn, children: "编辑" }),
|
|
4453
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4454
|
+
"button",
|
|
4455
|
+
{
|
|
4456
|
+
type: "button",
|
|
4457
|
+
disabled: busy,
|
|
4458
|
+
title: picking === e.slug ? "再点一次取消挑选;然后点击目标 hall 头完成归类" : "归类到已有 hall 下:点击后选目标 hall(记录随迁,旧名进别名)",
|
|
4459
|
+
onClick: () => {
|
|
4460
|
+
if (picking === e.slug) {
|
|
4461
|
+
setPicking(null);
|
|
4462
|
+
return;
|
|
4463
|
+
}
|
|
4464
|
+
setForm({ action: "rename", from: e.slug, to: "" });
|
|
4465
|
+
setPreview(null);
|
|
4466
|
+
setPicking(e.slug);
|
|
4467
|
+
setMsg(null);
|
|
4468
|
+
setError(null);
|
|
4469
|
+
},
|
|
4470
|
+
style: { ...btn, ...picking === e.slug ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : {} },
|
|
4471
|
+
children: picking === e.slug ? "选 hall…" : "归类"
|
|
4472
|
+
}
|
|
4473
|
+
),
|
|
4474
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: `改名(= merge 1:1 + 注册表改名)`, onClick: () => {
|
|
4475
|
+
setForm({ action: "rename", from: e.slug, to: "" });
|
|
4476
|
+
setPreview(null);
|
|
4477
|
+
}, style: btn, children: "改名" }),
|
|
4478
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "合并到另一个 Room(dryRun 预览→确认实跑)", onClick: () => {
|
|
4479
|
+
setForm({ action: "merge", from: e.slug, to: "" });
|
|
4480
|
+
setPreview(null);
|
|
4481
|
+
}, style: btn, children: "合并" }),
|
|
4482
|
+
/* @__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 ? "恢复" : "退役" }),
|
|
4483
|
+
!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
|
|
4484
|
+
] }),
|
|
4485
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(e.slug), style: btn, children: "导出" })
|
|
4486
|
+
] })
|
|
4487
|
+
] }),
|
|
4488
|
+
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: [
|
|
4489
|
+
(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: [
|
|
4490
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
|
|
4491
|
+
" ",
|
|
4492
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
|
|
4493
|
+
/* @__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 })
|
|
4494
|
+
] }, it.id)),
|
|
4495
|
+
(roomRecords[e.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.muted, children: "该 Room 暂无记录。" }) : null,
|
|
4496
|
+
(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
|
|
4497
|
+
] }) : null
|
|
4498
|
+
] }, e.slug);
|
|
4499
|
+
}) : null
|
|
4500
|
+
] }, g.hall);
|
|
4501
|
+
}),
|
|
4502
|
+
visibleGrown.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginTop: 4 }, children: [
|
|
4503
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", onClick: () => setGrownOpen(!grownOpen), style: btn, title: grownOpen ? "收起未注册自生长 Room" : "展开未注册自生长 Room", children: [
|
|
4504
|
+
grownOpen ? "▾" : "▸",
|
|
4505
|
+
" 自生长未注册 · 共 ",
|
|
4506
|
+
visibleGrown.length
|
|
4507
|
+
] }),
|
|
4508
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "(只在 tags 里涌现,不在词表 —— 收编后标注器才会优先挂靠)" }),
|
|
4509
|
+
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: [
|
|
4510
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4511
|
+
"span",
|
|
4512
|
+
{
|
|
4513
|
+
style: { ...btn, cursor: hiPriv && !busy ? "grab" : "default", borderRadius: 999, padding: "2px 8px", touchAction: "none" },
|
|
4514
|
+
onPointerDown: (ev) => onDragLabelDown(ev, r.room),
|
|
4515
|
+
onPointerMove: onDragLabelMove,
|
|
4516
|
+
onPointerUp: onDragLabelUp,
|
|
4517
|
+
title: hiPriv && !busy ? "自生长 slug(未注册)——按住拖到某个 hall 头上 = 收编并归为其子类" : "自生长 slug(未注册)",
|
|
4518
|
+
children: r.room + " · " + r.count
|
|
4519
|
+
}
|
|
4520
|
+
),
|
|
4521
|
+
/* @__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: "收编" }),
|
|
4522
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4523
|
+
"button",
|
|
4524
|
+
{
|
|
4525
|
+
type: "button",
|
|
4526
|
+
disabled: !hiPriv || busy,
|
|
4527
|
+
title: "归类到已有 hall 下:点击后选目标 hall(收编+子类一步,记录随迁)",
|
|
4528
|
+
onClick: () => {
|
|
4529
|
+
if (picking === r.room) {
|
|
4530
|
+
setPicking(null);
|
|
4531
|
+
return;
|
|
4532
|
+
}
|
|
4533
|
+
setForm({ action: "rename", from: r.room, to: "" });
|
|
4534
|
+
setPreview(null);
|
|
4535
|
+
setPicking(r.room);
|
|
4536
|
+
setMsg(null);
|
|
4537
|
+
setError(null);
|
|
4538
|
+
},
|
|
4539
|
+
style: { ...btn, opacity: !hiPriv || busy ? 0.5 : 1, ...picking === r.room ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : {} },
|
|
4540
|
+
children: picking === r.room ? "选 hall…" : "归"
|
|
4541
|
+
}
|
|
4542
|
+
)
|
|
4543
|
+
] }, r.room)) }) : null
|
|
4544
|
+
] }) : null
|
|
4545
|
+
] });
|
|
4546
|
+
}
|
|
4547
|
+
|
|
4548
|
+
// client/src/tabs/RoomTab.tsx
|
|
4549
|
+
var import_react17 = require("react");
|
|
4550
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
4551
|
+
var btn2 = {
|
|
4552
|
+
cursor: "pointer",
|
|
4553
|
+
fontSize: 12,
|
|
4554
|
+
padding: "2px 8px",
|
|
4555
|
+
borderRadius: 6,
|
|
4556
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
4557
|
+
background: "transparent",
|
|
4558
|
+
color: "var(--dsh-mem-text-2)"
|
|
4559
|
+
};
|
|
4560
|
+
function downloadCsv2(csv, name) {
|
|
4561
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
|
4562
|
+
const url = URL.createObjectURL(blob);
|
|
4563
|
+
const a = document.createElement("a");
|
|
4564
|
+
a.href = url;
|
|
4565
|
+
a.download = name;
|
|
4566
|
+
a.click();
|
|
4567
|
+
URL.revokeObjectURL(url);
|
|
4568
|
+
}
|
|
4569
|
+
function RoomTab(props) {
|
|
4570
|
+
const rpc = props.rpc;
|
|
4571
|
+
const [registry, setRegistry] = (0, import_react17.useState)([]);
|
|
4572
|
+
const [counts, setCounts] = (0, import_react17.useState)([]);
|
|
4573
|
+
const [orphanCount, setOrphanCount] = (0, import_react17.useState)(0);
|
|
4574
|
+
const [busy, setBusy] = (0, import_react17.useState)(false);
|
|
4575
|
+
const [msg, setMsg] = (0, import_react17.useState)(null);
|
|
4576
|
+
const [error, setError] = (0, import_react17.useState)(null);
|
|
4577
|
+
const [hiPriv, setHiPriv] = (0, import_react17.useState)(false);
|
|
4578
|
+
const [hiPrivBusy, setHiPrivBusy] = (0, import_react17.useState)(false);
|
|
4579
|
+
const [reg, setReg] = (0, import_react17.useState)({ slug: "", label: "", description: "" });
|
|
4580
|
+
const [editing, setEditing] = (0, import_react17.useState)(null);
|
|
4581
|
+
const [renaming, setRenaming] = (0, import_react17.useState)(null);
|
|
4582
|
+
const [expanded, setExpanded] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
|
|
4583
|
+
const [retiredFilter, setRetiredFilter] = (0, import_react17.useState)("");
|
|
4584
|
+
const [filter, setFilter] = (0, import_react17.useState)("");
|
|
4585
|
+
const [records, setRecords] = (0, import_react17.useState)({});
|
|
4586
|
+
const loadAll = (0, import_react17.useCallback)(() => {
|
|
4587
|
+
rpc("dsh-memory/rooms-get", {}).then((r) => {
|
|
4588
|
+
if (r && r.ok) {
|
|
4589
|
+
setCounts(r.value.rooms ?? []);
|
|
4590
|
+
setRegistry(r.value.registry ?? []);
|
|
4591
|
+
setOrphanCount(r.value.orphanCount ?? 0);
|
|
4592
|
+
}
|
|
4593
|
+
}).catch(() => void 0);
|
|
4594
|
+
}, [rpc]);
|
|
4595
|
+
const loadHiPriv = (0, import_react17.useCallback)(() => {
|
|
4596
|
+
rpc("dsh-memory/settings-get", {}).then((r) => {
|
|
4597
|
+
if (r && r.ok && r.value) setHiPriv(!!r.value.settings.memoryMutate);
|
|
4598
|
+
}).catch(() => {
|
|
4599
|
+
});
|
|
4600
|
+
}, [rpc]);
|
|
4601
|
+
(0, import_react17.useEffect)(() => {
|
|
4602
|
+
loadAll();
|
|
4603
|
+
loadHiPriv();
|
|
4604
|
+
}, [loadAll, loadHiPriv]);
|
|
4605
|
+
const toggleHiPriv = () => {
|
|
4606
|
+
const next = !hiPriv;
|
|
4607
|
+
if (next && !window.confirm("开启高权限模式:模型获得写入/删除记忆工具,并解锁 Room 管理操作。确定开启?")) return;
|
|
4608
|
+
rpc("dsh-memory/settings-set", { memoryMutate: next }).then((r) => {
|
|
4609
|
+
if (r && r.ok) setHiPriv(next);
|
|
4610
|
+
setHiPrivBusy(false);
|
|
4611
|
+
loadHiPriv();
|
|
4612
|
+
}).catch(() => setHiPrivBusy(false));
|
|
4613
|
+
};
|
|
4614
|
+
const run = (fn) => {
|
|
4615
|
+
if (busy) return;
|
|
4616
|
+
setBusy(true);
|
|
4617
|
+
setError(null);
|
|
4618
|
+
setMsg(null);
|
|
4619
|
+
fn().finally(() => setBusy(false));
|
|
4620
|
+
};
|
|
4621
|
+
const register = () => run(async () => {
|
|
4622
|
+
const r = await rpc("dsh-memory/room-register", { slug: reg.slug.trim(), label: reg.label.trim() || void 0, description: reg.description.trim() || void 0 });
|
|
4623
|
+
if (r && r.ok) {
|
|
4624
|
+
setMsg(r.value?.notice ?? `已注册:${reg.slug.trim()}`);
|
|
4625
|
+
setReg({ slug: "", label: "", description: "" });
|
|
4626
|
+
loadAll();
|
|
4627
|
+
} else setError(r && r.error ? r.error.message : "注册失败");
|
|
4628
|
+
});
|
|
4629
|
+
const adopt = (slug) => run(async () => {
|
|
4630
|
+
const r = await rpc("dsh-memory/room-register", { slug, source: "grown" });
|
|
4631
|
+
if (r && r.ok) {
|
|
4632
|
+
setMsg(`已收编:${slug}(source=grown)`);
|
|
4633
|
+
loadAll();
|
|
4634
|
+
} else setError(r && r.error ? r.error.message : "收编失败");
|
|
4635
|
+
});
|
|
4636
|
+
const previewRename = () => run(async () => {
|
|
4637
|
+
if (!renaming) return;
|
|
4638
|
+
const to = renaming.to.trim();
|
|
4639
|
+
if (!to) {
|
|
4640
|
+
setError("目标 slug 不能为空");
|
|
4641
|
+
return;
|
|
4642
|
+
}
|
|
4643
|
+
if (to === renaming.slug) {
|
|
4644
|
+
setError("目标与源相同");
|
|
4645
|
+
return;
|
|
4646
|
+
}
|
|
4647
|
+
if (!registry.some((e) => e.slug === renaming.slug)) {
|
|
4648
|
+
const rr = await rpc("dsh-memory/room-register", { slug: renaming.slug, source: "grown" });
|
|
4649
|
+
if (!rr || !rr.ok) {
|
|
4650
|
+
setError(rr && rr.error ? rr.error.message : "收编源失败");
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
4653
|
+
loadAll();
|
|
4654
|
+
}
|
|
4655
|
+
const r = await rpc("dsh-memory/room-admin", { action: "rename", from: renaming.slug, to });
|
|
4656
|
+
if (r && r.ok) setMsg(String(r.value?.notice ?? ""));
|
|
4657
|
+
else setError(r && r.error ? r.error.message : "预览失败");
|
|
4658
|
+
});
|
|
4659
|
+
const execRename = () => run(async () => {
|
|
4660
|
+
if (!renaming) return;
|
|
4661
|
+
if (!registry.some((e) => e.slug === renaming.slug)) {
|
|
4662
|
+
await rpc("dsh-memory/room-register", { slug: renaming.slug, source: "grown" });
|
|
4663
|
+
}
|
|
4664
|
+
const r = await rpc("dsh-memory/room-admin", { action: "rename", from: renaming.slug, to: renaming.to.trim(), dryRun: false });
|
|
4665
|
+
if (r && r.ok) {
|
|
4666
|
+
setMsg(String(r.value?.notice ?? "完成"));
|
|
4667
|
+
setRenaming(null);
|
|
4668
|
+
loadAll();
|
|
4669
|
+
} else setError(r && r.error ? r.error.message : "执行失败");
|
|
4670
|
+
});
|
|
4671
|
+
const saveEditing = () => run(async () => {
|
|
4672
|
+
if (!editing) return;
|
|
4673
|
+
const r = await rpc("dsh-memory/room-admin", { action: "update", slug: editing.slug, label: editing.label, description: editing.description });
|
|
4674
|
+
if (r && r.ok) {
|
|
4675
|
+
setMsg(String(r.value?.notice ?? "已更新"));
|
|
4676
|
+
setEditing(null);
|
|
4677
|
+
loadAll();
|
|
4678
|
+
} else setError(r && r.error ? r.error.message : "更新失败");
|
|
4679
|
+
});
|
|
4680
|
+
const retire = (slug, active, withRecords = false) => run(async () => {
|
|
4681
|
+
if (!active) {
|
|
4682
|
+
const tip = withRecords ? `退役 Room:${slug} 并退场其下全部记忆?(记录为软删,可在记忆页「已退场」区逐条恢复)` : `退役 Room:${slug}?(存量记录的 tags 不动,仅词表不再推荐;可随时恢复)`;
|
|
4683
|
+
if (!window.confirm(tip)) return;
|
|
4684
|
+
}
|
|
4685
|
+
const r = await rpc("dsh-memory/room-admin", { action: "retire", slug, active, withRecords: withRecords || void 0 });
|
|
4686
|
+
if (r && r.ok) {
|
|
4687
|
+
setMsg(String(r.value?.notice ?? "完成"));
|
|
4688
|
+
loadAll();
|
|
4689
|
+
if (expanded.has(slug)) {
|
|
4690
|
+
const rr = await rpc("dsh-memory/list-records", { tag: slug, limit: 10, retired: false });
|
|
4691
|
+
if (rr && rr.ok) setRecords((m) => ({ ...m, [slug]: { items: rr.value.items, total: rr.value.total ?? null } }));
|
|
4692
|
+
}
|
|
4693
|
+
} else setError(r && r.error ? r.error.message : "操作失败");
|
|
4694
|
+
});
|
|
4695
|
+
const exportRecords = (slug) => run(async () => {
|
|
4696
|
+
const r = await rpc("dsh-memory/rooms-export", { kind: "records", tag: slug });
|
|
4697
|
+
if (!r.ok) {
|
|
4698
|
+
setError(r.error.message);
|
|
4699
|
+
return;
|
|
4700
|
+
}
|
|
4701
|
+
if (r.value.csv) {
|
|
4702
|
+
const blob = new Blob([r.value.csv], { type: "text/csv;charset=utf-8" });
|
|
4703
|
+
const url = URL.createObjectURL(blob);
|
|
4704
|
+
const a = document.createElement("a");
|
|
4705
|
+
a.href = url;
|
|
4706
|
+
a.download = `dsh-memory-room-${slug.replace(/\//g, "-")}-records.csv`;
|
|
4707
|
+
a.click();
|
|
4708
|
+
URL.revokeObjectURL(url);
|
|
4709
|
+
}
|
|
4710
|
+
});
|
|
4711
|
+
const toggleExpand = (slug) => run(async () => {
|
|
4712
|
+
if (expanded.has(slug)) {
|
|
4713
|
+
const next = new Set(expanded);
|
|
4714
|
+
next.delete(slug);
|
|
4715
|
+
setExpanded(next);
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
const r = await rpc("dsh-memory/list-records", { tag: slug, limit: 10, retired: false });
|
|
4719
|
+
if (r && r.ok) {
|
|
4720
|
+
const next = new Set(expanded);
|
|
4721
|
+
next.add(slug);
|
|
4722
|
+
setExpanded(next);
|
|
4723
|
+
setRecords((m) => ({ ...m, [slug]: { items: r.value.items, total: r.value.total ?? null } }));
|
|
4724
|
+
} else setError(r && r.error ? r.error.message : "加载失败");
|
|
4725
|
+
});
|
|
4726
|
+
const countOf = (slug) => counts.find((c) => c.room === slug)?.count ?? 0;
|
|
4727
|
+
const rows = [
|
|
4728
|
+
...registry.map((e) => ({ slug: e.slug, count: countOf(e.slug), entry: e })),
|
|
4729
|
+
...counts.filter((c) => !registry.some((e) => e.slug === c.room)).map((c) => ({ slug: c.room, count: c.count }))
|
|
4730
|
+
].filter((row2) => {
|
|
4731
|
+
if (retiredFilter === "active") return row2.entry?.status !== "retired";
|
|
4732
|
+
if (retiredFilter === "retired") return row2.entry?.status === "retired";
|
|
4733
|
+
return true;
|
|
4734
|
+
});
|
|
4735
|
+
const normFilter = filter.trim().toLowerCase();
|
|
4736
|
+
const visibleRows = normFilter ? rows.filter(
|
|
4737
|
+
(row2) => row2.slug.toLowerCase().includes(normFilter) || (row2.entry?.label ?? "").toLowerCase().includes(normFilter) || (row2.entry?.aliases ?? []).some((a) => a.toLowerCase().includes(normFilter))
|
|
4738
|
+
) : rows;
|
|
4739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
|
|
4740
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8, flexWrap: "wrap" }, children: [
|
|
4741
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "Room 级管理:存在(注册/收编/退役)· 名称(显示名/说明/改名)· 条目(展开浏览) · 归类到 hall 请到「Hall」页" }),
|
|
4742
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
|
|
4743
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { type: "button", style: btn2, title: "导出无 Room 绑定的孤儿清单(含候选)", onClick: () => run(async () => {
|
|
4744
|
+
const r = await rpc("dsh-memory/rooms-export", { kind: "orphans" });
|
|
4745
|
+
if (!r.ok) {
|
|
4746
|
+
setError(r.error.message);
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
if (r.value.csv) downloadCsv2(r.value.csv, "dsh-memory-room-orphans.csv");
|
|
4750
|
+
}), children: [
|
|
4751
|
+
"导出孤儿 · ",
|
|
4752
|
+
orphanCount
|
|
4753
|
+
] }),
|
|
4754
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4755
|
+
"button",
|
|
4756
|
+
{
|
|
4757
|
+
type: "button",
|
|
4758
|
+
style: { ...btn2, color: hiPriv ? "var(--dsh-mem-danger)" : void 0, border: hiPriv ? "1px solid var(--dsh-mem-danger)" : btn2.border },
|
|
4759
|
+
disabled: hiPrivBusy,
|
|
4760
|
+
title: hiPriv ? "关闭高权限模式" : "开启高权限模式以解锁 Room 管理操作",
|
|
4761
|
+
onClick: toggleHiPriv,
|
|
4762
|
+
children: hiPriv ? "高权限:开" : "高权限:关"
|
|
4763
|
+
}
|
|
4764
|
+
),
|
|
4765
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", style: btn2, onClick: loadAll, children: "刷新" })
|
|
4766
|
+
] }),
|
|
4767
|
+
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: [
|
|
4768
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: S.muted, children: [
|
|
4769
|
+
"编辑 ",
|
|
4770
|
+
editing.slug
|
|
4771
|
+
] }),
|
|
4772
|
+
/* @__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 } }),
|
|
4773
|
+
/* @__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 } }),
|
|
4774
|
+
/* @__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: "保存" }),
|
|
4775
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: () => setEditing(null), style: btn2, children: "取消" })
|
|
4776
|
+
] }) : null,
|
|
4777
|
+
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: [
|
|
4778
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: S.muted, children: [
|
|
4779
|
+
"改名 ",
|
|
4780
|
+
renaming.slug,
|
|
4781
|
+
" →"
|
|
4782
|
+
] }),
|
|
4783
|
+
/* @__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 } }),
|
|
4784
|
+
/* @__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: "预览" }),
|
|
4785
|
+
/* @__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: "确认执行" }),
|
|
4786
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: () => setRenaming(null), style: btn2, children: "取消" }),
|
|
4787
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "记录随迁;与反刍并发会互相覆盖,请在反刍空闲时执行。" })
|
|
4788
|
+
] }) : null,
|
|
4789
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }, children: [
|
|
4790
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "退场筛选" }),
|
|
4791
|
+
[["", "全部"], ["active", "活跃"], ["retired", "已退役"]].map(([val, label]) => {
|
|
4792
|
+
const on = retiredFilter === val;
|
|
4793
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4794
|
+
"button",
|
|
4795
|
+
{
|
|
4796
|
+
type: "button",
|
|
4797
|
+
title: val === "" ? "全部 room" : val === "active" ? "只看词表未退役的 room" : "只看已退役的 room",
|
|
4798
|
+
onClick: () => setRetiredFilter(val),
|
|
4799
|
+
style: {
|
|
4800
|
+
cursor: "pointer",
|
|
4801
|
+
fontSize: 12,
|
|
4802
|
+
padding: "2px 10px",
|
|
4803
|
+
borderRadius: 999,
|
|
4804
|
+
border: on ? "1px solid var(--dsh-mem-accent)" : "1px solid var(--dsh-mem-border)",
|
|
4805
|
+
background: on ? "var(--dsh-mem-bg-inset)" : "transparent",
|
|
4806
|
+
color: on ? "var(--dsh-mem-accent)" : "var(--dsh-mem-text-2)"
|
|
4807
|
+
},
|
|
4808
|
+
children: label
|
|
4809
|
+
},
|
|
4810
|
+
val
|
|
4811
|
+
);
|
|
4812
|
+
}),
|
|
4813
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.grow }),
|
|
4814
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4815
|
+
"input",
|
|
4816
|
+
{
|
|
4817
|
+
value: filter,
|
|
4818
|
+
onChange: (e) => setFilter(e.target.value),
|
|
4819
|
+
placeholder: "搜索 room(slug、显示名、别名)",
|
|
4820
|
+
style: { ...S.input, width: 230, fontSize: 12 }
|
|
4821
|
+
}
|
|
4822
|
+
),
|
|
4823
|
+
filter ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", style: btn2, onClick: () => setFilter(""), children: "清除" }) : null
|
|
4824
|
+
] }),
|
|
4825
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
|
|
4826
|
+
/* @__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 } }),
|
|
4827
|
+
/* @__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 } }),
|
|
4828
|
+
/* @__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 } }),
|
|
4829
|
+
/* @__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: "注册" }),
|
|
4830
|
+
!hiPriv && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
|
|
4831
|
+
] }),
|
|
4832
|
+
msg ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.hint, children: msg }) : null,
|
|
4833
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.error, children: error }) : null,
|
|
4834
|
+
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) => {
|
|
4835
|
+
const e = row2.entry;
|
|
4836
|
+
const retired = e?.status === "retired";
|
|
4837
|
+
const grownOnly = !e;
|
|
4838
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { marginBottom: 6, border: "1px solid var(--dsh-mem-border)", borderRadius: 8 }, children: [
|
|
4839
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "4px 10px" }, children: [
|
|
4840
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4841
|
+
"button",
|
|
4842
|
+
{
|
|
4843
|
+
type: "button",
|
|
4844
|
+
title: expanded.has(row2.slug) ? "收起记忆条目" : "展开查看该 Room 的记忆条目(默认收起)",
|
|
4845
|
+
onClick: () => void toggleExpand(row2.slug),
|
|
4846
|
+
style: { ...btn2, borderRadius: 999, padding: "1px 7px", flexShrink: 0 },
|
|
4847
|
+
children: expanded.has(row2.slug) ? "▾" : "▸"
|
|
4848
|
+
}
|
|
4849
|
+
),
|
|
4850
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
|
|
4851
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { style: { fontSize: 12, opacity: retired ? 0.6 : 1 }, children: [
|
|
4852
|
+
row2.slug,
|
|
4853
|
+
e?.source === "pre-registered" ? " ⭐" : ""
|
|
4854
|
+
] }),
|
|
4855
|
+
e?.label ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: e.label }) : null,
|
|
4856
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: row2.count + " 条" }),
|
|
4857
|
+
grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "(自生长未注册)" }) : null,
|
|
4858
|
+
retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
|
|
4859
|
+
(e?.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e?.aliases ?? []).join(", ") }) : null
|
|
4860
|
+
] }),
|
|
4861
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
|
|
4862
|
+
!hiPriv || grownOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
4863
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
|
|
4864
|
+
setEditing({ slug: row2.slug, label: e?.label ?? "", description: e?.description ?? "" });
|
|
4865
|
+
setRenaming(null);
|
|
4866
|
+
}, style: btn2, children: "编辑" }),
|
|
4867
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "改名(可两级 slug;记录随迁,旧名进别名)", onClick: () => {
|
|
4868
|
+
setRenaming({ slug: row2.slug, to: "" });
|
|
4869
|
+
setEditing(null);
|
|
4870
|
+
}, style: btn2, children: "改名" }),
|
|
4871
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
4872
|
+
"button",
|
|
4873
|
+
{
|
|
4874
|
+
type: "button",
|
|
4875
|
+
disabled: busy,
|
|
4876
|
+
title: retired ? "恢复该 Room 到词表" : "退役该 Room(存量 tags 不动)",
|
|
4877
|
+
onClick: () => retire(row2.slug, retired),
|
|
4878
|
+
style: { ...btn2, ...retired ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : { color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" } },
|
|
4879
|
+
children: retired ? "恢复" : "退役"
|
|
4880
|
+
}
|
|
4881
|
+
),
|
|
4882
|
+
!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
|
|
4883
|
+
] }),
|
|
4884
|
+
grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
4885
|
+
/* @__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: "收编" }),
|
|
4886
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => {
|
|
4887
|
+
setRenaming(null);
|
|
4888
|
+
retire(row2.slug, false, true);
|
|
4889
|
+
}, style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "退场" })
|
|
4890
|
+
] }) : null,
|
|
4891
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(row2.slug), style: btn2, children: "导出" })
|
|
4892
|
+
] })
|
|
4893
|
+
] }),
|
|
4894
|
+
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: [
|
|
4895
|
+
(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: [
|
|
4896
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
|
|
4897
|
+
" ",
|
|
4898
|
+
it.retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "[已退场]" }) : null,
|
|
4899
|
+
" ",
|
|
4900
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
|
|
4901
|
+
/* @__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 })
|
|
4902
|
+
] }, it.id)),
|
|
4903
|
+
(records[row2.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.muted, children: "该 Room 暂无记忆条目。" }) : null,
|
|
4904
|
+
(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
|
|
4905
|
+
] }) : null
|
|
4906
|
+
] }, row2.slug);
|
|
4907
|
+
})
|
|
4908
|
+
] });
|
|
4909
|
+
}
|
|
4910
|
+
|
|
4911
|
+
// client/src/tabs/ScenesTab.tsx
|
|
4912
|
+
var import_react18 = require("react");
|
|
4913
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
3753
4914
|
function SceneCard(props) {
|
|
3754
4915
|
const s = props.s;
|
|
3755
|
-
const [open, setOpen] = (0,
|
|
3756
|
-
return /* @__PURE__ */ (0,
|
|
3757
|
-
/* @__PURE__ */ (0,
|
|
4916
|
+
const [open, setOpen] = (0, import_react18.useState)(false);
|
|
4917
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "dsh-mem-card dsh-mem-card-hover", style: S.card, children: [
|
|
4918
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3758
4919
|
"div",
|
|
3759
4920
|
{
|
|
3760
4921
|
style: { ...S.sceneHead, cursor: "pointer", userSelect: "none" },
|
|
@@ -3762,23 +4923,23 @@ var __defProp = Object.defineProperty;
|
|
|
3762
4923
|
setOpen(!open);
|
|
3763
4924
|
},
|
|
3764
4925
|
children: [
|
|
3765
|
-
/* @__PURE__ */ (0,
|
|
3766
|
-
/* @__PURE__ */ (0,
|
|
3767
|
-
s.heat ? /* @__PURE__ */ (0,
|
|
3768
|
-
/* @__PURE__ */ (0,
|
|
3769
|
-
/* @__PURE__ */ (0,
|
|
4926
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "dsh-mem-scene-chev", style: { transform: open ? "rotate(90deg)" : "none" }, children: "▸" }),
|
|
4927
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.sceneTitle, children: s.path }),
|
|
4928
|
+
s.heat ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "热度 " + s.heat }) : null,
|
|
4929
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
|
|
4930
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "更新 " + fmtTime(s.updated) })
|
|
3770
4931
|
]
|
|
3771
4932
|
}
|
|
3772
4933
|
),
|
|
3773
|
-
s.summary ? /* @__PURE__ */ (0,
|
|
3774
|
-
open ? /* @__PURE__ */ (0,
|
|
4934
|
+
s.summary ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { ...S.muted, marginBottom: 6 }, children: s.summary }) : null,
|
|
4935
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("pre", { style: S.pre, children: s.content || "(空)" }) : null
|
|
3775
4936
|
] });
|
|
3776
4937
|
}
|
|
3777
4938
|
function ScenesTab(props) {
|
|
3778
4939
|
const rpc = props.rpc;
|
|
3779
|
-
const [items, setItems] = (0,
|
|
3780
|
-
const [error, setError] = (0,
|
|
3781
|
-
const load = (0,
|
|
4940
|
+
const [items, setItems] = (0, import_react18.useState)(null);
|
|
4941
|
+
const [error, setError] = (0, import_react18.useState)(null);
|
|
4942
|
+
const load = (0, import_react18.useCallback)(() => {
|
|
3782
4943
|
rpc("dsh-memory/scenes", {}).then((r) => {
|
|
3783
4944
|
if (r && r.ok) {
|
|
3784
4945
|
setItems(r.value.items);
|
|
@@ -3788,54 +4949,58 @@ var __defProp = Object.defineProperty;
|
|
|
3788
4949
|
setError(String(e && e.message || e));
|
|
3789
4950
|
});
|
|
3790
4951
|
}, [rpc]);
|
|
3791
|
-
(0,
|
|
4952
|
+
(0, import_react18.useEffect)(() => {
|
|
3792
4953
|
load();
|
|
3793
4954
|
}, [load]);
|
|
3794
|
-
return /* @__PURE__ */ (0,
|
|
3795
|
-
/* @__PURE__ */ (0,
|
|
3796
|
-
/* @__PURE__ */ (0,
|
|
3797
|
-
/* @__PURE__ */ (0,
|
|
3798
|
-
/* @__PURE__ */ (0,
|
|
4955
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
4956
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
4957
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: items ? items.length + " 个场景块" : "加载中…" }),
|
|
4958
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
|
|
4959
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NButton, { onClick: load, children: "刷新" })
|
|
3799
4960
|
] }),
|
|
3800
|
-
error ? /* @__PURE__ */ (0,
|
|
3801
|
-
items && items.length === 0 ? /* @__PURE__ */ (0,
|
|
4961
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.error, children: error }) : null,
|
|
4962
|
+
items && items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { style: S.intro, children: "暂无场景块。累计 5 条新记忆后 L2 会自动整合出第一个场景。" }) : null,
|
|
3802
4963
|
(items || []).map((s) => {
|
|
3803
|
-
return /* @__PURE__ */ (0,
|
|
4964
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SceneCard, { s }, s.path);
|
|
3804
4965
|
})
|
|
3805
4966
|
] });
|
|
3806
4967
|
}
|
|
3807
4968
|
|
|
3808
4969
|
// client/src/panel.tsx
|
|
3809
|
-
var
|
|
4970
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
3810
4971
|
var TABS = [
|
|
3811
4972
|
["overview", "概览"],
|
|
3812
4973
|
["records", "记忆"],
|
|
3813
4974
|
["conflicts", "冲突"],
|
|
3814
4975
|
["scenes", "场景"],
|
|
4976
|
+
["rooms", "Hall"],
|
|
4977
|
+
["room", "Room"],
|
|
3815
4978
|
["persona", "画像"],
|
|
3816
4979
|
["cost", "成本"],
|
|
3817
4980
|
["log", "日志"]
|
|
3818
4981
|
];
|
|
3819
4982
|
function MemoryPanel(props) {
|
|
3820
4983
|
const rpc = props.rpc;
|
|
3821
|
-
const [tab, setTab] = (0,
|
|
4984
|
+
const [tab, setTab] = (0, import_react19.useState)("overview");
|
|
3822
4985
|
ensureThemeStyle();
|
|
3823
|
-
(0,
|
|
4986
|
+
(0, import_react19.useEffect)(() => {
|
|
3824
4987
|
watchSidebarIcon();
|
|
3825
4988
|
}, []);
|
|
3826
4989
|
let body;
|
|
3827
|
-
if (tab === "overview") body = /* @__PURE__ */ (0,
|
|
3828
|
-
else if (tab === "records") body = /* @__PURE__ */ (0,
|
|
3829
|
-
else if (tab === "conflicts") body = /* @__PURE__ */ (0,
|
|
3830
|
-
else if (tab === "scenes") body = /* @__PURE__ */ (0,
|
|
3831
|
-
else if (tab === "
|
|
3832
|
-
else if (tab === "
|
|
3833
|
-
else body = /* @__PURE__ */ (0,
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
/* @__PURE__ */ (0,
|
|
3838
|
-
|
|
4990
|
+
if (tab === "overview") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(OverviewTab, { rpc });
|
|
4991
|
+
else if (tab === "records") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RecordsTab, { rpc });
|
|
4992
|
+
else if (tab === "conflicts") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConflictsTab, { rpc });
|
|
4993
|
+
else if (tab === "scenes") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ScenesTab, { rpc });
|
|
4994
|
+
else if (tab === "rooms") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RoomsTab, { rpc });
|
|
4995
|
+
else if (tab === "room") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RoomTab, { rpc });
|
|
4996
|
+
else if (tab === "persona") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(PersonaTab, { rpc });
|
|
4997
|
+
else if (tab === "cost") body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CostTab, { rpc });
|
|
4998
|
+
else body = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LogTab, { rpc });
|
|
4999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "dsh-mem-root", style: S.section, children: [
|
|
5000
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { style: S.heading, children: "记忆 (Memory)" }),
|
|
5001
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { style: S.intro, children: "L0~L3 分层蒸馏记忆:浏览被记住的内容,控制记忆模式开关。" }),
|
|
5002
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: S.tabbar, children: TABS.map((t) => {
|
|
5003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
3839
5004
|
"button",
|
|
3840
5005
|
{
|
|
3841
5006
|
className: tab === t[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
|
|
@@ -3852,7 +5017,7 @@ var __defProp = Object.defineProperty;
|
|
|
3852
5017
|
}
|
|
3853
5018
|
|
|
3854
5019
|
// client/src/pill/MemoryModePill.tsx
|
|
3855
|
-
var
|
|
5020
|
+
var import_react22 = require("react");
|
|
3856
5021
|
|
|
3857
5022
|
// src/util/context-occupancy.ts
|
|
3858
5023
|
var CONTEXT_METER_CIRCUMFERENCE = 34.55751918948772;
|
|
@@ -4168,23 +5333,23 @@ var __defProp = Object.defineProperty;
|
|
|
4168
5333
|
}
|
|
4169
5334
|
|
|
4170
5335
|
// client/src/pill/WingWheel.tsx
|
|
4171
|
-
var
|
|
5336
|
+
var import_react21 = require("react");
|
|
4172
5337
|
|
|
4173
5338
|
// client/src/pill/SessionInfoArea.tsx
|
|
4174
|
-
var
|
|
4175
|
-
var
|
|
5339
|
+
var import_react20 = require("react");
|
|
5340
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
4176
5341
|
function sinfoCell(val, label, title) {
|
|
4177
|
-
return /* @__PURE__ */ (0,
|
|
4178
|
-
/* @__PURE__ */ (0,
|
|
4179
|
-
/* @__PURE__ */ (0,
|
|
5342
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { title: title || void 0, children: [
|
|
5343
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-val", children: val }),
|
|
5344
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-label", children: label })
|
|
4180
5345
|
] });
|
|
4181
5346
|
}
|
|
4182
5347
|
function SessionInfoArea(props) {
|
|
4183
5348
|
const rpc = props.rpc;
|
|
4184
5349
|
const sessionId = props.sessionId;
|
|
4185
|
-
const [stats, setStats] = (0,
|
|
4186
|
-
const busyRef = (0,
|
|
4187
|
-
(0,
|
|
5350
|
+
const [stats, setStats] = (0, import_react20.useState)(void 0);
|
|
5351
|
+
const busyRef = (0, import_react20.useRef)(false);
|
|
5352
|
+
(0, import_react20.useEffect)(() => {
|
|
4188
5353
|
if (!rpc || !sessionId) return void 0;
|
|
4189
5354
|
let alive = true;
|
|
4190
5355
|
let timer = null;
|
|
@@ -4217,7 +5382,7 @@ var __defProp = Object.defineProperty;
|
|
|
4217
5382
|
}, [rpc, sessionId]);
|
|
4218
5383
|
if (stats === null) return null;
|
|
4219
5384
|
if (stats === void 0) {
|
|
4220
|
-
return /* @__PURE__ */ (0,
|
|
5385
|
+
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: [
|
|
4221
5386
|
sinfoCell("…", "召回命中"),
|
|
4222
5387
|
sinfoCell("…", "攒批进度"),
|
|
4223
5388
|
sinfoCell("…", "本会话记忆"),
|
|
@@ -4266,21 +5431,21 @@ var __defProp = Object.defineProperty;
|
|
|
4266
5431
|
else if (stats.retrieval === "none") note = "检索不可用(FTS 与向量均失效)";
|
|
4267
5432
|
}
|
|
4268
5433
|
const ago = fmtAgo(gl.lastExtractAt);
|
|
4269
|
-
return /* @__PURE__ */ (0,
|
|
4270
|
-
warn ? /* @__PURE__ */ (0,
|
|
4271
|
-
/* @__PURE__ */ (0,
|
|
5434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-sinfo", children: [
|
|
5435
|
+
warn ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-warn", children: warn }) : null,
|
|
5436
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dsh-mem-sinfo-grid", children: [
|
|
4272
5437
|
sinfoCell(rcVal, rcLabel, rcTitle),
|
|
4273
5438
|
sinfoCell(dVal, dLabel, dTitle),
|
|
4274
5439
|
sinfoCell(String(di.producedRecords || 0), "本会话记忆", pTitle),
|
|
4275
5440
|
sinfoCell(stats.l0Count != null ? String(stats.l0Count) : "…", "会话消息")
|
|
4276
5441
|
] }),
|
|
4277
|
-
note ? /* @__PURE__ */ (0,
|
|
4278
|
-
/* @__PURE__ */ (0,
|
|
5442
|
+
note ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-note", children: note }) : null,
|
|
5443
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "dsh-mem-sinfo-sum", children: "待蒸馏 " + (gl.pendingTotal || 0) + " · 上次蒸馏 " + (ago || "尚未蒸馏") })
|
|
4279
5444
|
] });
|
|
4280
5445
|
}
|
|
4281
5446
|
|
|
4282
5447
|
// client/src/pill/WingWheel.tsx
|
|
4283
|
-
var
|
|
5448
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
4284
5449
|
var SIZE = 196;
|
|
4285
5450
|
var CENTER = SIZE / 2;
|
|
4286
5451
|
var R = 64;
|
|
@@ -4323,17 +5488,17 @@ var __defProp = Object.defineProperty;
|
|
|
4323
5488
|
function WingWheel(props) {
|
|
4324
5489
|
ensureThemeStyle();
|
|
4325
5490
|
const isOff = props.mode === "off";
|
|
4326
|
-
const [overview, setOverview] = (0,
|
|
4327
|
-
const [backfillBusy, setBackfillBusy] = (0,
|
|
4328
|
-
const [localError, setLocalError] = (0,
|
|
4329
|
-
const timersRef = (0,
|
|
4330
|
-
(0,
|
|
5491
|
+
const [overview, setOverview] = (0, import_react21.useState)(null);
|
|
5492
|
+
const [backfillBusy, setBackfillBusy] = (0, import_react21.useState)(false);
|
|
5493
|
+
const [localError, setLocalError] = (0, import_react21.useState)(null);
|
|
5494
|
+
const timersRef = (0, import_react21.useRef)([]);
|
|
5495
|
+
(0, import_react21.useEffect)(
|
|
4331
5496
|
() => () => {
|
|
4332
5497
|
for (const t of timersRef.current) window.clearTimeout(t);
|
|
4333
5498
|
},
|
|
4334
5499
|
[]
|
|
4335
5500
|
);
|
|
4336
|
-
(0,
|
|
5501
|
+
(0, import_react21.useEffect)(() => {
|
|
4337
5502
|
let alive = true;
|
|
4338
5503
|
props.rpc("dsh-memory/wing-overview", {}).then((r) => {
|
|
4339
5504
|
if (!alive) return;
|
|
@@ -4379,28 +5544,28 @@ var __defProp = Object.defineProperty;
|
|
|
4379
5544
|
finish();
|
|
4380
5545
|
});
|
|
4381
5546
|
};
|
|
4382
|
-
const boxRef = (0,
|
|
4383
|
-
const startRef = (0,
|
|
4384
|
-
const movedRef = (0,
|
|
4385
|
-
const downIndexRef = (0,
|
|
4386
|
-
const dragTargetRef = (0,
|
|
4387
|
-
const [drag, setDrag] = (0,
|
|
5547
|
+
const boxRef = (0, import_react21.useRef)(null);
|
|
5548
|
+
const startRef = (0, import_react21.useRef)(null);
|
|
5549
|
+
const movedRef = (0, import_react21.useRef)(false);
|
|
5550
|
+
const downIndexRef = (0, import_react21.useRef)(null);
|
|
5551
|
+
const dragTargetRef = (0, import_react21.useRef)(null);
|
|
5552
|
+
const [drag, setDrag] = (0, import_react21.useState)(null);
|
|
4388
5553
|
const lockedIndex = (() => {
|
|
4389
5554
|
if (props.halls.length !== 1 || !overview) return null;
|
|
4390
5555
|
const i = overview.corners.findIndex((c) => c.id === props.halls[0]);
|
|
4391
5556
|
return i >= 0 ? i : null;
|
|
4392
5557
|
})();
|
|
4393
5558
|
const activeCorner = drag ? drag.index : lockedIndex;
|
|
4394
|
-
const blockPosRef = (0,
|
|
5559
|
+
const blockPosRef = (0, import_react21.useRef)(
|
|
4395
5560
|
activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER }
|
|
4396
5561
|
);
|
|
4397
|
-
const blockTargetRef = (0,
|
|
4398
|
-
const [blockPos, setBlockPos] = (0,
|
|
4399
|
-
(0,
|
|
5562
|
+
const blockTargetRef = (0, import_react21.useRef)(blockPosRef.current);
|
|
5563
|
+
const [blockPos, setBlockPos] = (0, import_react21.useState)(blockPosRef.current);
|
|
5564
|
+
(0, import_react21.useEffect)(() => {
|
|
4400
5565
|
blockTargetRef.current = activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER };
|
|
4401
5566
|
}, [activeCorner]);
|
|
4402
|
-
const reducedMotionRef = (0,
|
|
4403
|
-
(0,
|
|
5567
|
+
const reducedMotionRef = (0, import_react21.useRef)(false);
|
|
5568
|
+
(0, import_react21.useEffect)(() => {
|
|
4404
5569
|
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
4405
5570
|
const apply2 = () => {
|
|
4406
5571
|
reducedMotionRef.current = mq.matches;
|
|
@@ -4409,7 +5574,7 @@ var __defProp = Object.defineProperty;
|
|
|
4409
5574
|
mq.addEventListener("change", apply2);
|
|
4410
5575
|
return () => mq.removeEventListener("change", apply2);
|
|
4411
5576
|
}, []);
|
|
4412
|
-
(0,
|
|
5577
|
+
(0, import_react21.useEffect)(() => {
|
|
4413
5578
|
let raf = 0;
|
|
4414
5579
|
const loop = () => {
|
|
4415
5580
|
const cur = blockPosRef.current;
|
|
@@ -4498,8 +5663,8 @@ var __defProp = Object.defineProperty;
|
|
|
4498
5663
|
}).join(" ");
|
|
4499
5664
|
const grayStyle = isOff ? { opacity: 0.45, pointerEvents: "none" } : {};
|
|
4500
5665
|
const boundariesDisabled = props.halls.length === 0 || isOff;
|
|
4501
|
-
return /* @__PURE__ */ (0,
|
|
4502
|
-
/* @__PURE__ */ (0,
|
|
5666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { width: SIZE }, children: [
|
|
5667
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4503
5668
|
"div",
|
|
4504
5669
|
{
|
|
4505
5670
|
ref: boxRef,
|
|
@@ -4509,7 +5674,7 @@ var __defProp = Object.defineProperty;
|
|
|
4509
5674
|
onPointerUp: onBoxUp,
|
|
4510
5675
|
onPointerCancel: onBoxUp,
|
|
4511
5676
|
children: [
|
|
4512
|
-
/* @__PURE__ */ (0,
|
|
5677
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4513
5678
|
"svg",
|
|
4514
5679
|
{
|
|
4515
5680
|
width: SIZE,
|
|
@@ -4517,7 +5682,7 @@ var __defProp = Object.defineProperty;
|
|
|
4517
5682
|
style: { position: "absolute", inset: 0, pointerEvents: "none" },
|
|
4518
5683
|
"aria-hidden": "true",
|
|
4519
5684
|
children: [
|
|
4520
|
-
/* @__PURE__ */ (0,
|
|
5685
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4521
5686
|
"polygon",
|
|
4522
5687
|
{
|
|
4523
5688
|
points: polyPoints,
|
|
@@ -4528,7 +5693,7 @@ var __defProp = Object.defineProperty;
|
|
|
4528
5693
|
opacity: 0.45
|
|
4529
5694
|
}
|
|
4530
5695
|
),
|
|
4531
|
-
/* @__PURE__ */ (0,
|
|
5696
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4532
5697
|
"polygon",
|
|
4533
5698
|
{
|
|
4534
5699
|
points: polyPoints,
|
|
@@ -4539,7 +5704,7 @@ var __defProp = Object.defineProperty;
|
|
|
4539
5704
|
),
|
|
4540
5705
|
[0, 1, 2, 3, 4, 5, 6, 7].map((i) => {
|
|
4541
5706
|
const p = cornerPos(i);
|
|
4542
|
-
return /* @__PURE__ */ (0,
|
|
5707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4543
5708
|
"line",
|
|
4544
5709
|
{
|
|
4545
5710
|
x1: CENTER,
|
|
@@ -4555,7 +5720,7 @@ var __defProp = Object.defineProperty;
|
|
|
4555
5720
|
]
|
|
4556
5721
|
}
|
|
4557
5722
|
),
|
|
4558
|
-
/* @__PURE__ */ (0,
|
|
5723
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4559
5724
|
"div",
|
|
4560
5725
|
{
|
|
4561
5726
|
"aria-hidden": "true",
|
|
@@ -4574,7 +5739,7 @@ var __defProp = Object.defineProperty;
|
|
|
4574
5739
|
}
|
|
4575
5740
|
}
|
|
4576
5741
|
),
|
|
4577
|
-
/* @__PURE__ */ (0,
|
|
5742
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4578
5743
|
"button",
|
|
4579
5744
|
{
|
|
4580
5745
|
type: "button",
|
|
@@ -4608,7 +5773,7 @@ var __defProp = Object.defineProperty;
|
|
|
4608
5773
|
const dim = activeCorner != null && !active;
|
|
4609
5774
|
const empty = count === 0;
|
|
4610
5775
|
const p = cornerPos(i);
|
|
4611
|
-
return /* @__PURE__ */ (0,
|
|
5776
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4612
5777
|
"div",
|
|
4613
5778
|
{
|
|
4614
5779
|
title: id ? `「${label}」${count ?? 0} 条
|
|
@@ -4637,8 +5802,8 @@ var __defProp = Object.defineProperty;
|
|
|
4637
5802
|
zIndex: 2
|
|
4638
5803
|
},
|
|
4639
5804
|
children: [
|
|
4640
|
-
/* @__PURE__ */ (0,
|
|
4641
|
-
/* @__PURE__ */ (0,
|
|
5805
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: label }),
|
|
5806
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 8, opacity: 0.75, fontVariantNumeric: "tabular-nums" }, children: count === null ? " " : empty ? "空角" : `${count} 条` })
|
|
4642
5807
|
]
|
|
4643
5808
|
},
|
|
4644
5809
|
id ?? i
|
|
@@ -4647,7 +5812,7 @@ var __defProp = Object.defineProperty;
|
|
|
4647
5812
|
]
|
|
4648
5813
|
}
|
|
4649
5814
|
),
|
|
4650
|
-
/* @__PURE__ */ (0,
|
|
5815
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4651
5816
|
"div",
|
|
4652
5817
|
{
|
|
4653
5818
|
title: props.halls.length === 0 ? "选定某个角(单 Wing)后,这两项才生效" : "锁定 Wing 时两类无角记忆是否参与召回",
|
|
@@ -4661,9 +5826,9 @@ var __defProp = Object.defineProperty;
|
|
|
4661
5826
|
pointerEvents: boundariesDisabled ? "none" : void 0
|
|
4662
5827
|
},
|
|
4663
5828
|
children: [
|
|
4664
|
-
/* @__PURE__ */ (0,
|
|
4665
|
-
/* @__PURE__ */ (0,
|
|
4666
|
-
/* @__PURE__ */ (0,
|
|
5829
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `另有 ${overview.unlabeled} 条未打标` : "未打标" }),
|
|
5830
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
5831
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4667
5832
|
Segmented,
|
|
4668
5833
|
{
|
|
4669
5834
|
value: props.hallIncludeUnlabeled ? "in" : "ex",
|
|
@@ -4674,7 +5839,7 @@ var __defProp = Object.defineProperty;
|
|
|
4674
5839
|
onChange: (key) => props.onCommitWingBoundaries({ includeUnlabeled: key === "in" })
|
|
4675
5840
|
}
|
|
4676
5841
|
),
|
|
4677
|
-
/* @__PURE__ */ (0,
|
|
5842
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4678
5843
|
Segmented,
|
|
4679
5844
|
{
|
|
4680
5845
|
value: props.hallIncludeGeneral ? "in" : "ex",
|
|
@@ -4689,7 +5854,7 @@ var __defProp = Object.defineProperty;
|
|
|
4689
5854
|
]
|
|
4690
5855
|
}
|
|
4691
5856
|
),
|
|
4692
|
-
/* @__PURE__ */ (0,
|
|
5857
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Row, { label: "会话", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4693
5858
|
Segmented,
|
|
4694
5859
|
{
|
|
4695
5860
|
value: isOff ? "off" : "on",
|
|
@@ -4704,7 +5869,7 @@ var __defProp = Object.defineProperty;
|
|
|
4704
5869
|
onChange: (key) => props.onCommit(key === "off" ? "off" : "auto")
|
|
4705
5870
|
}
|
|
4706
5871
|
) }),
|
|
4707
|
-
props.recall !== void 0 && props.onCommitRecall ? /* @__PURE__ */ (0,
|
|
5872
|
+
props.recall !== void 0 && props.onCommitRecall ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Row, { label: "注入", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4708
5873
|
Segmented,
|
|
4709
5874
|
{
|
|
4710
5875
|
value: props.recall === null ? "follow" : props.recall ? "on" : "off",
|
|
@@ -4717,7 +5882,7 @@ var __defProp = Object.defineProperty;
|
|
|
4717
5882
|
onChange: (key) => props.onCommitRecall(key === "on" ? true : key === "off" ? false : null)
|
|
4718
5883
|
}
|
|
4719
5884
|
) }) : null,
|
|
4720
|
-
/* @__PURE__ */ (0,
|
|
5885
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4721
5886
|
"div",
|
|
4722
5887
|
{
|
|
4723
5888
|
style: {
|
|
@@ -4728,8 +5893,8 @@ var __defProp = Object.defineProperty;
|
|
|
4728
5893
|
marginTop: 10
|
|
4729
5894
|
},
|
|
4730
5895
|
children: [
|
|
4731
|
-
/* @__PURE__ */ (0,
|
|
4732
|
-
/* @__PURE__ */ (0,
|
|
5896
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `未打标 ${overview.unlabeled} 条` : "未打标计数加载中" }),
|
|
5897
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4733
5898
|
ActionButton,
|
|
4734
5899
|
{
|
|
4735
5900
|
label: backfillBusy ? "回填中…" : "回填",
|
|
@@ -4741,12 +5906,12 @@ var __defProp = Object.defineProperty;
|
|
|
4741
5906
|
]
|
|
4742
5907
|
}
|
|
4743
5908
|
),
|
|
4744
|
-
props.error || localError ? /* @__PURE__ */ (0,
|
|
4745
|
-
/* @__PURE__ */ (0,
|
|
5909
|
+
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,
|
|
5910
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SessionInfoArea, { rpc: props.rpc, sessionId: props.sessionId })
|
|
4746
5911
|
] });
|
|
4747
5912
|
}
|
|
4748
5913
|
function Row(props) {
|
|
4749
|
-
return /* @__PURE__ */ (0,
|
|
5914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4750
5915
|
"div",
|
|
4751
5916
|
{
|
|
4752
5917
|
style: {
|
|
@@ -4756,7 +5921,7 @@ var __defProp = Object.defineProperty;
|
|
|
4756
5921
|
marginTop: 10
|
|
4757
5922
|
},
|
|
4758
5923
|
children: [
|
|
4759
|
-
/* @__PURE__ */ (0,
|
|
5924
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: props.label }),
|
|
4760
5925
|
props.children
|
|
4761
5926
|
]
|
|
4762
5927
|
}
|
|
@@ -4764,9 +5929,9 @@ var __defProp = Object.defineProperty;
|
|
|
4764
5929
|
}
|
|
4765
5930
|
var LABEL_FALLBACK = ["工作", "人际", "学习", "创作娱乐", "居家", "健康", "财务", "出行"];
|
|
4766
5931
|
function useViewportClamp(popRef) {
|
|
4767
|
-
const shiftRef = (0,
|
|
4768
|
-
const [shiftX, setShiftX] = (0,
|
|
4769
|
-
(0,
|
|
5932
|
+
const shiftRef = (0, import_react21.useRef)(0);
|
|
5933
|
+
const [shiftX, setShiftX] = (0, import_react21.useState)(0);
|
|
5934
|
+
(0, import_react21.useEffect)(() => {
|
|
4770
5935
|
const clamp = () => {
|
|
4771
5936
|
const el = popRef.current;
|
|
4772
5937
|
if (!el) return;
|
|
@@ -4796,24 +5961,24 @@ var __defProp = Object.defineProperty;
|
|
|
4796
5961
|
}
|
|
4797
5962
|
|
|
4798
5963
|
// client/src/pill/MemoryModePill.tsx
|
|
4799
|
-
var
|
|
5964
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
4800
5965
|
function MemoryModePill(props) {
|
|
4801
5966
|
const rpc = props.rpc;
|
|
4802
5967
|
const sessionId = props.sessionId || props.session && props.session.sessionId;
|
|
4803
|
-
const [mode, setMode] = (0,
|
|
4804
|
-
const [recall, setRecall] = (0,
|
|
4805
|
-
const [recallResolved, setRecallResolved] = (0,
|
|
4806
|
-
const [halls, setWings] = (0,
|
|
4807
|
-
const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0,
|
|
4808
|
-
const [hallIncludeGeneral, setWingIncludeGeneral] = (0,
|
|
4809
|
-
const [wingLabels, setHallLabels] = (0,
|
|
4810
|
-
const [error, setError] = (0,
|
|
4811
|
-
const [open, setOpen] = (0,
|
|
4812
|
-
const wrapRef = (0,
|
|
4813
|
-
const popRef = (0,
|
|
5968
|
+
const [mode, setMode] = (0, import_react22.useState)(null);
|
|
5969
|
+
const [recall, setRecall] = (0, import_react22.useState)(null);
|
|
5970
|
+
const [recallResolved, setRecallResolved] = (0, import_react22.useState)(true);
|
|
5971
|
+
const [halls, setWings] = (0, import_react22.useState)([]);
|
|
5972
|
+
const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0, import_react22.useState)(true);
|
|
5973
|
+
const [hallIncludeGeneral, setWingIncludeGeneral] = (0, import_react22.useState)(false);
|
|
5974
|
+
const [wingLabels, setHallLabels] = (0, import_react22.useState)({});
|
|
5975
|
+
const [error, setError] = (0, import_react22.useState)(null);
|
|
5976
|
+
const [open, setOpen] = (0, import_react22.useState)(false);
|
|
5977
|
+
const wrapRef = (0, import_react22.useRef)(null);
|
|
5978
|
+
const popRef = (0, import_react22.useRef)(null);
|
|
4814
5979
|
const shiftX = useViewportClamp(popRef);
|
|
4815
|
-
const seqRef = (0,
|
|
4816
|
-
const load = (0,
|
|
5980
|
+
const seqRef = (0, import_react22.useRef)(0);
|
|
5981
|
+
const load = (0, import_react22.useCallback)(() => {
|
|
4817
5982
|
if (!sessionId || !rpc) return;
|
|
4818
5983
|
const token = ++seqRef.current;
|
|
4819
5984
|
setError(null);
|
|
@@ -4838,13 +6003,13 @@ var __defProp = Object.defineProperty;
|
|
|
4838
6003
|
}).catch(() => {
|
|
4839
6004
|
});
|
|
4840
6005
|
}, [sessionId, rpc]);
|
|
4841
|
-
(0,
|
|
6006
|
+
(0, import_react22.useEffect)(() => {
|
|
4842
6007
|
load();
|
|
4843
6008
|
}, [load]);
|
|
4844
|
-
(0,
|
|
6009
|
+
(0, import_react22.useEffect)(() => {
|
|
4845
6010
|
watchSidebarIcon();
|
|
4846
6011
|
}, []);
|
|
4847
|
-
(0,
|
|
6012
|
+
(0, import_react22.useEffect)(() => {
|
|
4848
6013
|
initOccupancyIndicator(
|
|
4849
6014
|
(endpoint, payload) => rpc(endpoint, payload)
|
|
4850
6015
|
);
|
|
@@ -4852,7 +6017,7 @@ var __defProp = Object.defineProperty;
|
|
|
4852
6017
|
watchContextMeter();
|
|
4853
6018
|
noteOccupancySession(sessionId ?? null);
|
|
4854
6019
|
}, [sessionId, rpc]);
|
|
4855
|
-
(0,
|
|
6020
|
+
(0, import_react22.useEffect)(() => {
|
|
4856
6021
|
if (!open) return;
|
|
4857
6022
|
const onDown = (e) => {
|
|
4858
6023
|
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
@@ -4992,8 +6157,8 @@ var __defProp = Object.defineProperty;
|
|
|
4992
6157
|
pillStyle.boxShadow = "0 0 12px color-mix(in srgb, " + info.color + " 30%, transparent)";
|
|
4993
6158
|
pillStyle["--dsh-mem-pill-tint"] = info.color;
|
|
4994
6159
|
}
|
|
4995
|
-
return /* @__PURE__ */ (0,
|
|
4996
|
-
/* @__PURE__ */ (0,
|
|
6160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
|
|
6161
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
4997
6162
|
"button",
|
|
4998
6163
|
{
|
|
4999
6164
|
type: "button",
|
|
@@ -5006,11 +6171,11 @@ var __defProp = Object.defineProperty;
|
|
|
5006
6171
|
style: pillStyle,
|
|
5007
6172
|
children: [
|
|
5008
6173
|
"记忆 · ",
|
|
5009
|
-
/* @__PURE__ */ (0,
|
|
6174
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: faceLabel })
|
|
5010
6175
|
]
|
|
5011
6176
|
}
|
|
5012
6177
|
),
|
|
5013
|
-
open ? /* @__PURE__ */ (0,
|
|
6178
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5014
6179
|
"div",
|
|
5015
6180
|
{
|
|
5016
6181
|
ref: popRef,
|
|
@@ -5021,12 +6186,12 @@ var __defProp = Object.defineProperty;
|
|
|
5021
6186
|
transform: "translateX(calc(-50% + " + shiftX + "px))",
|
|
5022
6187
|
zIndex: 1e3
|
|
5023
6188
|
},
|
|
5024
|
-
children: /* @__PURE__ */ (0,
|
|
6189
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5025
6190
|
"div",
|
|
5026
6191
|
{
|
|
5027
6192
|
className: "dsh-mem-popover",
|
|
5028
6193
|
style: { position: "relative", padding: "10px 12px" },
|
|
5029
|
-
children: /* @__PURE__ */ (0,
|
|
6194
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
5030
6195
|
WingWheel,
|
|
5031
6196
|
{
|
|
5032
6197
|
mode: mode || "auto",
|