dsh-prime-memory 0.20.0-beta.12 → 0.20.0-beta.13
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 +12 -0
- package/dist/client.js +107 -30
- package/dist/store/rooms-registry.d.ts +3 -1
- package/dist/store/rooms-registry.js +5 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -17,6 +17,18 @@
|
|
|
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.13] — 2026-10-02
|
|
21
|
+
|
|
22
|
+
> 补全归类逻辑:把 room 归到已有 hall 下(此前只能新增 hall,或合并进别的 room 抹掉身份)。面板支持**拖拽归类**——拖 room 放到 hall 头上即可。
|
|
23
|
+
|
|
24
|
+
### 新增
|
|
25
|
+
|
|
26
|
+
- **Hall 页拖拽归类**:room 行可拖(hiPriv 开启时抓手光标),hall 头为放置目标(虚线高亮);松手 = 预填改名(major/minor)并直接出 dryRun 预览,确认后实跑——记录随迁、旧名进别名、分组视图即挂到该 hall 下。触屏/不支持拖放的环境用 room 行「归类」按钮或改名表单里的 hall 快选 chips 兜底。
|
|
27
|
+
- 后端修复:`renameSlug` 目标校验 `isTag` → `isRoomSlug`——此前两级目标(major/minor)被误拒,「归类到 hall 下」这条路实际不通(且 rename 编排会先重写记录再在注册表改名时抛错,留下半程状态)。按 C3 卸除/变更纪律:红测试先行(unit + HTTP 面集成各一),验红后修复回绿。
|
|
28
|
+
|
|
29
|
+
### 修复
|
|
30
|
+
|
|
31
|
+
- 同上:`renameSlug` 两级目标校验(beta.5 引入的缺口)。
|
|
20
32
|
## [0.20.0-beta.12] — 2026-10-02
|
|
21
33
|
|
|
22
34
|
> lint 清账 + 卸除纪律成文(ENGINEERING-NOTES 新增 C3)。**无功能变更**。
|
package/dist/client.js
CHANGED
|
@@ -3978,6 +3978,10 @@ var __defProp = Object.defineProperty;
|
|
|
3978
3978
|
const i = slug.indexOf("/");
|
|
3979
3979
|
return i === -1 ? slug : slug.slice(0, i);
|
|
3980
3980
|
}
|
|
3981
|
+
function suffixOf(slug) {
|
|
3982
|
+
const i = slug.indexOf("/");
|
|
3983
|
+
return i === -1 ? slug : slug.slice(i + 1);
|
|
3984
|
+
}
|
|
3981
3985
|
function downloadCsv(csv, name) {
|
|
3982
3986
|
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
|
3983
3987
|
const url = URL.createObjectURL(blob);
|
|
@@ -4012,6 +4016,7 @@ var __defProp = Object.defineProperty;
|
|
|
4012
4016
|
const [form, setForm] = (0, import_react16.useState)(null);
|
|
4013
4017
|
const [preview, setPreview] = (0, import_react16.useState)(null);
|
|
4014
4018
|
const [grownOpen, setGrownOpen] = (0, import_react16.useState)(false);
|
|
4019
|
+
const [dragOverHall, setDragOverHall] = (0, import_react16.useState)(null);
|
|
4015
4020
|
const loadAll = (0, import_react16.useCallback)(() => {
|
|
4016
4021
|
rpc("dsh-memory/rooms-get", {}).then((r) => {
|
|
4017
4022
|
if (r && r.ok) {
|
|
@@ -4073,22 +4078,41 @@ var __defProp = Object.defineProperty;
|
|
|
4073
4078
|
loadAll();
|
|
4074
4079
|
} else setError(r && r.error ? r.error.message : "收编失败");
|
|
4075
4080
|
});
|
|
4076
|
-
const previewMerge = () => run(async () => {
|
|
4077
|
-
|
|
4078
|
-
|
|
4081
|
+
const previewMerge = (override) => run(async () => {
|
|
4082
|
+
const fm = override ?? form;
|
|
4083
|
+
if (!fm) return;
|
|
4084
|
+
const to = fm.to.trim();
|
|
4079
4085
|
if (!to) {
|
|
4080
4086
|
setError("目标 slug 不能为空");
|
|
4081
4087
|
return;
|
|
4082
4088
|
}
|
|
4083
|
-
if (to ===
|
|
4089
|
+
if (to === fm.from) {
|
|
4084
4090
|
setError("目标与源相同");
|
|
4085
4091
|
return;
|
|
4086
4092
|
}
|
|
4087
|
-
const r = await rpc("dsh-memory/room-admin", { action:
|
|
4093
|
+
const r = await rpc("dsh-memory/room-admin", { action: fm.action, from: fm.from, to });
|
|
4088
4094
|
if (r && r.ok) {
|
|
4089
|
-
setPreview({ action:
|
|
4095
|
+
setPreview({ action: fm.action, from: fm.from, to, notice: String(r.value?.notice ?? "") });
|
|
4090
4096
|
} else setError(r && r.error ? r.error.message : "预览失败");
|
|
4091
4097
|
});
|
|
4098
|
+
const beginReclass = (from, hall) => {
|
|
4099
|
+
if (!busy) {
|
|
4100
|
+
setMsg(null);
|
|
4101
|
+
setError(null);
|
|
4102
|
+
}
|
|
4103
|
+
if (!hall) {
|
|
4104
|
+
setError("尚无 hall:先注册一个(如 dsh-plugin),再归类。");
|
|
4105
|
+
return;
|
|
4106
|
+
}
|
|
4107
|
+
if (majorOf(from) === hall) {
|
|
4108
|
+
setMsg(`${from} 已在 ${hall} 下。`);
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
4111
|
+
const f = { action: "rename", from, to: `${hall}/${suffixOf(from)}` };
|
|
4112
|
+
setForm(f);
|
|
4113
|
+
setPreview(null);
|
|
4114
|
+
void previewMerge(f);
|
|
4115
|
+
};
|
|
4092
4116
|
const execMerge = () => run(async () => {
|
|
4093
4117
|
if (!preview) return;
|
|
4094
4118
|
const r = await rpc("dsh-memory/room-admin", { action: preview.action, from: preview.from, to: preview.to, dryRun: false });
|
|
@@ -4239,41 +4263,83 @@ var __defProp = Object.defineProperty;
|
|
|
4239
4263
|
msg ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.hint, children: msg }) : null,
|
|
4240
4264
|
error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.error, children: error }) : null,
|
|
4241
4265
|
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: [
|
|
4242
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4266
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4267
|
+
"div",
|
|
4268
|
+
{
|
|
4269
|
+
onDragOver: (ev) => {
|
|
4270
|
+
if (!hiPriv || busy) return;
|
|
4271
|
+
ev.preventDefault();
|
|
4272
|
+
ev.dataTransfer.dropEffect = "move";
|
|
4273
|
+
setDragOverHall(g.hall);
|
|
4274
|
+
},
|
|
4275
|
+
onDragLeave: () => setDragOverHall((cur) => cur === g.hall ? null : cur),
|
|
4276
|
+
onDrop: (ev) => {
|
|
4277
|
+
ev.preventDefault();
|
|
4278
|
+
setDragOverHall(null);
|
|
4279
|
+
if (!hiPriv || busy) return;
|
|
4280
|
+
const from = ev.dataTransfer.getData("text/dsh-room");
|
|
4281
|
+
if (from) beginReclass(from, g.hall);
|
|
4282
|
+
},
|
|
4283
|
+
style: {
|
|
4284
|
+
display: "flex",
|
|
4285
|
+
alignItems: "center",
|
|
4286
|
+
gap: 8,
|
|
4287
|
+
padding: "5px 10px",
|
|
4288
|
+
background: "var(--dsh-mem-bg-inset)",
|
|
4289
|
+
outline: dragOverHall === g.hall ? "2px dashed var(--dsh-mem-accent)" : void 0,
|
|
4290
|
+
outlineOffset: dragOverHall === g.hall ? "-2px" : void 0
|
|
4291
|
+
},
|
|
4292
|
+
title: hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
|
|
4293
|
+
children: [
|
|
4294
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { fontSize: 12, fontWeight: 600 }, children: [
|
|
4295
|
+
g.hall,
|
|
4296
|
+
g.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: " (" + g.label + ")" }) : null
|
|
4297
|
+
] }),
|
|
4298
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: `合计 ${g.total} 条 · ${g.entries.length} 个 room` }),
|
|
4299
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
|
|
4300
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
4301
|
+
"button",
|
|
4302
|
+
{
|
|
4303
|
+
type: "button",
|
|
4304
|
+
disabled: !hiPriv || busy,
|
|
4305
|
+
title: `预填注册表单为 ${g.hall}/…(注册该 hall 下的小类)`,
|
|
4306
|
+
onClick: () => setRegSlug(g.hall + "/"),
|
|
4307
|
+
style: btn,
|
|
4308
|
+
children: "+小类"
|
|
4309
|
+
}
|
|
4310
|
+
)
|
|
4311
|
+
]
|
|
4312
|
+
}
|
|
4313
|
+
),
|
|
4261
4314
|
g.entries.map((e) => {
|
|
4262
4315
|
const standalone = e.slug === g.hall;
|
|
4263
4316
|
const count = rooms.find((r) => r.room === e.slug)?.count ?? 0;
|
|
4264
4317
|
const retired = e.status === "retired";
|
|
4265
4318
|
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: [
|
|
4266
4319
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
|
|
4267
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4320
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
4321
|
+
"span",
|
|
4322
|
+
{
|
|
4323
|
+
style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default" },
|
|
4324
|
+
draggable: hiPriv && !busy,
|
|
4325
|
+
onDragStart: (ev) => {
|
|
4326
|
+
ev.dataTransfer.setData("text/dsh-room", e.slug);
|
|
4327
|
+
ev.dataTransfer.effectAllowed = "move";
|
|
4328
|
+
},
|
|
4329
|
+
title: hiPriv && !busy ? "拖到某个 hall 头上归类(major/minor;记录随迁,旧名进别名)" : e.description ? `${e.slug} — ${e.description}` : e.slug,
|
|
4330
|
+
children: [
|
|
4331
|
+
standalone ? e.slug : "· " + e.slug.slice(g.hall.length + 1),
|
|
4332
|
+
e.source === "pre-registered" ? " ⭐" : ""
|
|
4333
|
+
]
|
|
4334
|
+
}
|
|
4335
|
+
),
|
|
4271
4336
|
e.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: e.label }) : null,
|
|
4272
4337
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: count + " 条" }),
|
|
4273
4338
|
retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
|
|
4274
4339
|
(e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "别名: " + (e.aliases ?? []).join(", ") }) : null,
|
|
4275
4340
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
|
|
4276
4341
|
!hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
4342
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "归类到已有 hall 下(major/minor;记录随迁)", onClick: () => beginReclass(e.slug, groups.find((gg) => gg.hall !== majorOf(e.slug))?.hall ?? ""), style: btn, children: "归类" }),
|
|
4277
4343
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: `改名(= merge 1:1 + 注册表改名)`, onClick: () => {
|
|
4278
4344
|
setForm({ action: "rename", from: e.slug, to: "" });
|
|
4279
4345
|
setPreview(null);
|
|
@@ -4303,7 +4369,18 @@ var __defProp = Object.defineProperty;
|
|
|
4303
4369
|
style: { ...S.input, width: 180, fontSize: 12 }
|
|
4304
4370
|
}
|
|
4305
4371
|
),
|
|
4306
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("
|
|
4372
|
+
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)(
|
|
4373
|
+
"button",
|
|
4374
|
+
{
|
|
4375
|
+
type: "button",
|
|
4376
|
+
title: `归入 ${gg.hall} 下(${gg.hall}/${suffixOf(form.from)})`,
|
|
4377
|
+
onClick: () => setForm({ ...form, to: `${gg.hall}/${suffixOf(form.from)}` }),
|
|
4378
|
+
style: { ...btn, borderRadius: 999, padding: "1px 8px" },
|
|
4379
|
+
children: gg.hall
|
|
4380
|
+
},
|
|
4381
|
+
gg.hall
|
|
4382
|
+
)) }) : null,
|
|
4383
|
+
/* @__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: "预览" }),
|
|
4307
4384
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => {
|
|
4308
4385
|
setForm(null);
|
|
4309
4386
|
setPreview(null);
|
|
@@ -50,7 +50,9 @@ export declare class RoomRegistryStore {
|
|
|
50
50
|
}>;
|
|
51
51
|
/** 改状态(active↔retired);无变化返回 false。 */
|
|
52
52
|
setStatus(slug: string, status: 'active' | 'retired'): Promise<boolean>;
|
|
53
|
-
/** 改名(merge 1:1 收尾):slug 换名 + 旧 slug 进 aliases。新名与既有条目冲突时走 merge。
|
|
53
|
+
/** 改名(merge 1:1 收尾):slug 换名 + 旧 slug 进 aliases。新名与既有条目冲突时走 merge。
|
|
54
|
+
* 目标用两级制词表(isRoomSlug)——把 standalone room 归类到 hall 下(改名 major/minor)
|
|
55
|
+
* 走的就是这条路;isTag 会误拒带 slash 的目标。 */
|
|
54
56
|
renameSlug(oldSlug: string, newSlug: string): Promise<boolean>;
|
|
55
57
|
/** 合并收尾:fromSlug 条目转 retired + toSlug 进 aliases(记录重写由调用方游标执行)。 */
|
|
56
58
|
markMerged(fromSlug: string, toSlug: string): Promise<boolean>;
|
|
@@ -170,11 +170,13 @@ export class RoomRegistryStore {
|
|
|
170
170
|
await this.persist();
|
|
171
171
|
return true;
|
|
172
172
|
}
|
|
173
|
-
/** 改名(merge 1:1 收尾):slug 换名 + 旧 slug 进 aliases。新名与既有条目冲突时走 merge。
|
|
173
|
+
/** 改名(merge 1:1 收尾):slug 换名 + 旧 slug 进 aliases。新名与既有条目冲突时走 merge。
|
|
174
|
+
* 目标用两级制词表(isRoomSlug)——把 standalone room 归类到 hall 下(改名 major/minor)
|
|
175
|
+
* 走的就是这条路;isTag 会误拒带 slash 的目标。 */
|
|
174
176
|
async renameSlug(oldSlug, newSlug) {
|
|
175
177
|
this.ensureLoaded();
|
|
176
|
-
if (!
|
|
177
|
-
throw new Error(`非法 Room slug: ${newSlug}`);
|
|
178
|
+
if (!isRoomSlug(newSlug))
|
|
179
|
+
throw new Error(`非法 Room slug: ${newSlug}(两级制:major 或 major/minor)`);
|
|
178
180
|
const found = this.entries.find((e) => e.slug === oldSlug);
|
|
179
181
|
if (!found)
|
|
180
182
|
return false;
|