dsh-prime-memory 0.20.0-beta.25 → 0.20.0-beta.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/client.js +236 -138
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -17,6 +17,17 @@
17
17
  - **建议状态(`longtask-hint-get`)以纯端点提供**:判定 = 上下文占用达 `contextThresholdPct` 或 todo 漂移达 `driftThreshold`(部署配置可调);配合 `longtask-compress-tail` 作手动压缩入口。
18
18
  - **撤下浮动球 UI(`client/src/longtask-fab.ts`;beta.1 起曾随包发布)**:悬浮建议面板的归属本在 context-compression 侧(「建议启动压缩」是其领域),pm 属误载——2026-10-02 用户裁定撤下。撤下不违座位契约(settings.section 恰一个 + 输入栏 pill,body 单例不占座位),但全应用级 overlay 会与其他插件的悬浮 UI 抢占屏幕角落;长任务开关与手动压缩入口待长任务定版时落记忆设置分节。
19
19
  - **`session-mode-set` 的 `mode` 转为可选**:与 `longTask` 二选一必传——纯切长任务开关不再被迫发送档位(旧客户端恒传 mode,行为不变)。响应 `mode` 改回当前生效值。
20
+ ## [0.20.0-beta.26] — 2026-10-03
21
+
22
+ ### 新增(Hall/Room 增长后的可用性)
23
+
24
+ - **Hall/Room 名称搜索过滤**:Hall 页新增搜索框(slug、显示名、别名,大小写不敏感)
25
+ —— hall 自身命中显示全部子 room,否则只留命中的子 room,整组无命中即隐藏;
26
+ 搜索时匹配组自动展开(折叠态不再藏住结果);自生长未注册区同滤。
27
+ Room 页同款搜索框,与退场筛选 chips 叠加生效。
28
+ - **Hall 分组折叠,默认全折叠**:hall 头只显示「合计 N 条 · M 个 room」,点头部或
29
+ ▸/▾ 钮展开/收起子 room;「展开全部/收起全部」批量钮;展开状态 localStorage 记忆
30
+ (`dsh.memory.halls.open`)。归类点选(点 hall 头完成归类)优先级不变。
20
31
  ## [0.20.0-beta.25] — 2026-10-02
21
32
 
22
33
  > 自生长 room 退场后状态不可见修复 + Hall 子 room 显示规则(用户裁定)。
package/dist/client.js CHANGED
@@ -3897,6 +3897,15 @@ var __defProp = Object.defineProperty;
3897
3897
  const [editing, setEditing] = (0, import_react16.useState)(null);
3898
3898
  const [expandedRooms, setExpandedRooms] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
3899
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)("");
3900
3909
  const dragRef = (0, import_react16.useRef)(null);
3901
3910
  const [ghost, setGhost] = (0, import_react16.useState)(null);
3902
3911
  const onDragLabelDown = (ev, slug) => {
@@ -4107,6 +4116,39 @@ var __defProp = Object.defineProperty;
4107
4116
  })).sort((a, b) => a.hall.localeCompare(b.hall));
4108
4117
  }, [registry, rooms]);
4109
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
+ );
4110
4152
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
4111
4153
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8 }, children: [
4112
4154
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "Hall = Room 大类 · 大类上限 200,小类「大类/小类」细分不占额度 · 与 Wing(域)筛选无关" }),
@@ -4165,6 +4207,22 @@ var __defProp = Object.defineProperty;
4165
4207
  ),
4166
4208
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: btn, onClick: loadAll, title: "重拉注册表与计数", children: "刷新" })
4167
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
+ ] }),
4168
4226
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4169
4227
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4170
4228
  "input",
@@ -4268,163 +4326,187 @@ var __defProp = Object.defineProperty;
4268
4326
  msg ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.hint, children: msg }) : null,
4269
4327
  error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.error, children: error }) : null,
4270
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,
4271
- groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { style: S.intro, children: "注册表为空。在上方注册 Hall/Room(如 dsh-plugin),或把自生长 slug「收编」进词表 —— 之后候选标注器会优先把相关记忆挂到这些 slug 上。" }) : groups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginBottom: 10, border: "1px solid var(--dsh-mem-border)", borderRadius: 8, overflow: "hidden" }, children: [
4272
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4273
- "div",
4274
- {
4275
- "data-hall": g.hall,
4276
- onDragOver: (ev) => {
4277
- if (!hiPriv || busy) return;
4278
- ev.preventDefault();
4279
- ev.dataTransfer.dropEffect = "move";
4280
- setDragOverHall(g.hall);
4281
- },
4282
- onDragLeave: () => setDragOverHall((cur) => cur === g.hall ? null : cur),
4283
- onDrop: (ev) => {
4284
- ev.preventDefault();
4285
- setDragOverHall(null);
4286
- if (!hiPriv || busy) return;
4287
- const from = ev.dataTransfer.getData("text/dsh-room");
4288
- if (from) beginReclass(from, g.hall);
4289
- },
4290
- onClick: () => {
4291
- if (!picking || busy) return;
4292
- const from = picking;
4293
- setPicking(null);
4294
- beginReclass(from, g.hall);
4295
- },
4296
- style: {
4297
- display: "flex",
4298
- alignItems: "center",
4299
- gap: 8,
4300
- padding: "5px 10px",
4301
- background: "var(--dsh-mem-bg-inset)",
4302
- outline: dragOverHall === g.hall || picking !== null ? "2px dashed var(--dsh-mem-accent)" : void 0,
4303
- outlineOffset: dragOverHall === g.hall || picking !== null ? "-2px" : void 0,
4304
- cursor: picking ? "pointer" : void 0
4305
- },
4306
- title: picking ? "点击此 hall:把「" + picking + "」归入 " + g.hall + " 下(major/minor,预览后确认)" : hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
4307
- children: [
4308
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { fontSize: 12, fontWeight: 600 }, children: [
4309
- g.hall,
4310
- g.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: " (" + g.label + ")" }) : null
4311
- ] }),
4312
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `合计 ${g.total} 条 · ${g.entries.length} 个 room` }),
4313
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4314
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4315
- "button",
4316
- {
4317
- type: "button",
4318
- disabled: !hiPriv || busy,
4319
- title: `预填注册表单为 ${g.hall}/…(注册该 hall 下的小类)`,
4320
- onClick: () => setRegSlug(g.hall + "/"),
4321
- style: btn,
4322
- children: "+小类"
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;
4323
4358
  }
4324
- )
4325
- ]
4326
- }
4327
- ),
4328
- g.entries.map((e) => {
4329
- const standalone = e.slug === g.hall;
4330
- const count = rooms.find((r) => r.room === e.slug)?.count ?? 0;
4331
- const retired = e.status === "retired";
4332
- 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: [
4333
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4334
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
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: [
4335
4373
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4336
4374
  "button",
4337
4375
  {
4338
4376
  type: "button",
4339
- title: expandedRooms.has(e.slug) ? "收起内容" : "展开查看该 Room 的记录内容(默认收起)",
4340
- onClick: () => void toggleRoomExpand(e.slug),
4377
+ title: hallOpen ? "收起子 room" : "展开子 room(默认折叠)",
4378
+ onClick: (ev) => {
4379
+ ev.stopPropagation();
4380
+ toggleHall(g.hall);
4381
+ },
4341
4382
  style: { ...btn, borderRadius: 999, padding: "1px 7px", flexShrink: 0 },
4342
- children: expandedRooms.has(e.slug) ? "▾" : "▸"
4383
+ children: hallOpen ? "▾" : "▸"
4343
4384
  }
4344
4385
  ),
4345
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4346
- "span",
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",
4347
4394
  {
4348
- style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default", touchAction: "none" },
4349
- onPointerDown: (ev) => onDragLabelDown(ev, e.slug),
4350
- onPointerMove: onDragLabelMove,
4351
- onPointerUp: onDragLabelUp,
4352
- title: hiPriv && !busy ? "按住拖到某个 hall 头上归类(major/minor;记录随迁,旧名进别名)" : e.description ? `${e.slug} — ${e.description}` : e.slug,
4353
- children: [
4354
- standalone ? e.slug : "· " + e.slug.slice(g.hall.length + 1),
4355
- e.source === "pre-registered" ? " ⭐" : ""
4356
- ]
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: "+小类"
4357
4404
  }
4358
- ),
4359
- e.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: e.label }) : null,
4360
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: count + " 条" }),
4361
- retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4362
- (e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e.aliases ?? []).join(", ") }) : null
4363
- ] }),
4364
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4365
- !hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4366
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "编辑显示名/归类说明", onClick: () => {
4367
- setEditing({ slug: e.slug, label: e.label ?? "", description: e.description ?? "" });
4368
- setForm(null);
4369
- setPreview(null);
4370
- setPicking(null);
4371
- }, style: btn, children: "编辑" }),
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: [
4372
4416
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4373
4417
  "button",
4374
4418
  {
4375
4419
  type: "button",
4376
- disabled: busy,
4377
- title: picking === e.slug ? "再点一次取消挑选;然后点击目标 hall 头完成归类" : "归类到已有 hall 下:点击后选目标 hall(记录随迁,旧名进别名)",
4378
- onClick: () => {
4379
- if (picking === e.slug) {
4380
- setPicking(null);
4381
- return;
4382
- }
4383
- setForm({ action: "rename", from: e.slug, to: "" });
4384
- setPreview(null);
4385
- setPicking(e.slug);
4386
- setMsg(null);
4387
- setError(null);
4388
- },
4389
- style: { ...btn, ...picking === e.slug ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : {} },
4390
- children: picking === e.slug ? "选 hall…" : "归类"
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
+ ]
4391
4438
  }
4392
4439
  ),
4393
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: `改名(= merge 1:1 + 注册表改名)`, onClick: () => {
4394
- setForm({ action: "rename", from: e.slug, to: "" });
4395
- setPreview(null);
4396
- }, style: btn, children: "改名" }),
4397
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "合并到另一个 Room(dryRun 预览→确认实跑)", onClick: () => {
4398
- setForm({ action: "merge", from: e.slug, to: "" });
4399
- setPreview(null);
4400
- }, style: btn, children: "合并" }),
4401
- /* @__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 ? "恢复" : "退役" }),
4402
- !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
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
4403
4444
  ] }),
4404
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(e.slug), style: btn, children: "导出" })
4405
- ] })
4406
- ] }),
4407
- 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: [
4408
- (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: [
4409
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
4410
- " ",
4411
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4412
- /* @__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 })
4413
- ] }, it.id)),
4414
- (roomRecords[e.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.muted, children: "该 Room 暂无记录。" }) : null,
4415
- (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
4416
- ] }) : null
4417
- ] }, e.slug);
4418
- })
4419
- ] }, g.hall)),
4420
- grownOnly.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginTop: 4 }, children: [
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: [
4421
4503
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { type: "button", onClick: () => setGrownOpen(!grownOpen), style: btn, title: grownOpen ? "收起未注册自生长 Room" : "展开未注册自生长 Room", children: [
4422
4504
  grownOpen ? "▾" : "▸",
4423
4505
  " 自生长未注册 · 共 ",
4424
- grownOnly.length
4506
+ visibleGrown.length
4425
4507
  ] }),
4426
4508
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "(只在 tags 里涌现,不在词表 —— 收编后标注器才会优先挂靠)" }),
4427
- grownOpen ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginTop: 6 }, children: grownOnly.map((r) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
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: [
4428
4510
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4429
4511
  "span",
4430
4512
  {
@@ -4499,6 +4581,7 @@ var __defProp = Object.defineProperty;
4499
4581
  const [renaming, setRenaming] = (0, import_react17.useState)(null);
4500
4582
  const [expanded, setExpanded] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
4501
4583
  const [retiredFilter, setRetiredFilter] = (0, import_react17.useState)("");
4584
+ const [filter, setFilter] = (0, import_react17.useState)("");
4502
4585
  const [records, setRecords] = (0, import_react17.useState)({});
4503
4586
  const loadAll = (0, import_react17.useCallback)(() => {
4504
4587
  rpc("dsh-memory/rooms-get", {}).then((r) => {
@@ -4649,6 +4732,10 @@ var __defProp = Object.defineProperty;
4649
4732
  if (retiredFilter === "retired") return row2.entry?.status === "retired";
4650
4733
  return true;
4651
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;
4652
4739
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
4653
4740
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { ...S.flexRow, marginBottom: 8, flexWrap: "wrap" }, children: [
4654
4741
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "Room 级管理:存在(注册/收编/退役)· 名称(显示名/说明/改名)· 条目(展开浏览) · 归类到 hall 请到「Hall」页" }),
@@ -4699,7 +4786,7 @@ var __defProp = Object.defineProperty;
4699
4786
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, onClick: () => setRenaming(null), style: btn2, children: "取消" }),
4700
4787
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "记录随迁;与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4701
4788
  ] }) : null,
4702
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }, children: [
4789
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }, children: [
4703
4790
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: "退场筛选" }),
4704
4791
  [["", "全部"], ["active", "活跃"], ["retired", "已退役"]].map(([val, label]) => {
4705
4792
  const on = retiredFilter === val;
@@ -4722,7 +4809,18 @@ var __defProp = Object.defineProperty;
4722
4809
  },
4723
4810
  val
4724
4811
  );
4725
- })
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
4726
4824
  ] }),
4727
4825
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginBottom: 10 }, children: [
4728
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 } }),
@@ -4733,7 +4831,7 @@ var __defProp = Object.defineProperty;
4733
4831
  ] }),
4734
4832
  msg ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.hint, children: msg }) : null,
4735
4833
  error ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.error, children: error }) : null,
4736
- rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { style: S.intro, children: "暂无 Room:对话/反刍会从标签涌现,或在上方注册。" }) : rows.map((row2) => {
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) => {
4737
4835
  const e = row2.entry;
4738
4836
  const retired = e?.status === "retired";
4739
4837
  const grownOnly = !e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prime-memory",
3
- "version": "0.20.0-beta.25",
3
+ "version": "0.20.0-beta.26",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",