dsh-prime-memory 0.20.0-beta.17 → 0.20.0-beta.19

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 CHANGED
@@ -17,6 +17,20 @@
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.19] — 2026-10-02
21
+
22
+ > 归类交互重做(实测反馈:HTML5 拖拽在部分 webview 不触发;点 hall 后预览被埋在源行里像"没反应")。
23
+
24
+ ### 变更
25
+
26
+ - **指针自制拖拽**(ego-browser 同款纯 pointer 方案):按住 room/自生长 slug → 浮动幽灵 chip 跟手 → 悬停 hall 头虚线高亮 → 松手归类。不依赖 HTML5 DnD,任何 webview 都生效。点选归类(「归类」/「归」→ 挑选态 → 点 hall 头)保留。
27
+ - **全局操作栏**:合并/归类/改名的表单与 dryRun 预览**常驻页首**(红色确认框不随行滚动、不再被埋在源 room 行里)——点 hall 后一定看得见。
28
+ - **room 行布局**:按钮固定一行不再折行(导出不再孤行);名称/别名在左侧信息区自然换行(别名可断词)。
29
+ ## [0.20.0-beta.18] — 2026-10-02
30
+
31
+ ### 维护
32
+
33
+ - lint 清账:beta.17 的 isTag→isRoomSlug 替换遗留 stats.ts / rooms-registry.ts 两个死 import(发布门漏检——管道后 `$?` 取的是 tail 退出码,D2 坑复犯)。无功能变更。
20
34
  ## [0.20.0-beta.17] — 2026-10-02
21
35
 
22
36
  > **重启后 hall/room 关系丢失修复**:`rooms-registry.json` 的加载器仍在用 `isTag` 过滤 slug——重启读盘时所有 `major/minor` 两级条目被当非法**静默丢弃**(单段 hall 能通过所以留存)。这是 isTag/isRoomSlug 混用的第三处;同批扫掉了 `normTags` 的同款雷。
package/dist/client.js CHANGED
@@ -4044,6 +4044,32 @@ var __defProp = Object.defineProperty;
4044
4044
  const [grownOpen, setGrownOpen] = (0, import_react16.useState)(false);
4045
4045
  const [dragOverHall, setDragOverHall] = (0, import_react16.useState)(null);
4046
4046
  const [picking, setPicking] = (0, import_react16.useState)(null);
4047
+ const dragRef = (0, import_react16.useRef)(null);
4048
+ const [ghost, setGhost] = (0, import_react16.useState)(null);
4049
+ const onDragLabelDown = (ev, slug) => {
4050
+ if (!hiPriv || busy || ev.button !== 0) return;
4051
+ ev.currentTarget.setPointerCapture(ev.pointerId);
4052
+ dragRef.current = { from: slug, x: ev.clientX, y: ev.clientY, active: false };
4053
+ };
4054
+ const onDragLabelMove = (ev) => {
4055
+ const d = dragRef.current;
4056
+ if (!d) return;
4057
+ if (!d.active && Math.hypot(ev.clientX - d.x, ev.clientY - d.y) > 6) d.active = true;
4058
+ if (!d.active) return;
4059
+ setGhost({ x: ev.clientX, y: ev.clientY, text: d.from });
4060
+ const hall = document.elementFromPoint(ev.clientX, ev.clientY)?.closest?.("[data-hall]")?.getAttribute("data-hall") ?? null;
4061
+ setDragOverHall(hall);
4062
+ };
4063
+ const onDragLabelUp = (ev) => {
4064
+ const d = dragRef.current;
4065
+ dragRef.current = null;
4066
+ setGhost(null);
4067
+ if (!d) return;
4068
+ if (!d.active) return;
4069
+ const hall = document.elementFromPoint(ev.clientX, ev.clientY)?.closest?.("[data-hall]")?.getAttribute("data-hall");
4070
+ setDragOverHall(null);
4071
+ if (hall) beginReclass(d.from, hall);
4072
+ };
4047
4073
  const loadAll = (0, import_react16.useCallback)(() => {
4048
4074
  rpc("dsh-memory/rooms-get", {}).then((r) => {
4049
4075
  if (r && r.ok) {
@@ -4299,12 +4325,61 @@ var __defProp = Object.defineProperty;
4299
4325
  ),
4300
4326
  !hiPriv && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "管理操作需先开启高权限模式" })
4301
4327
  ] }),
4328
+ form ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap", padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 8 }, children: [
4329
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: S.muted, children: [
4330
+ form.action === "merge" ? "合并" : "归类/改名",
4331
+ " ",
4332
+ form.from,
4333
+ " →"
4334
+ ] }),
4335
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4336
+ "input",
4337
+ {
4338
+ value: form.to,
4339
+ onChange: (ev) => setForm({ ...form, to: ev.target.value }),
4340
+ placeholder: "目标 slug",
4341
+ disabled: busy,
4342
+ style: { ...S.input, width: 190, fontSize: 12 }
4343
+ }
4344
+ ),
4345
+ 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)(
4346
+ "button",
4347
+ {
4348
+ type: "button",
4349
+ title: `归入 ${gg.hall} 下(${gg.hall}/${suffixOf(form.from)})`,
4350
+ onClick: () => setForm({ ...form, to: `${gg.hall}/${suffixOf(form.from)}` }),
4351
+ style: { ...btn, borderRadius: 999, padding: "1px 8px" },
4352
+ children: gg.hall
4353
+ },
4354
+ gg.hall
4355
+ )) }) : null,
4356
+ /* @__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: "预览" }),
4357
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => {
4358
+ setForm(null);
4359
+ setPreview(null);
4360
+ setPicking(null);
4361
+ }, style: btn, children: "取消" }),
4362
+ picking ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "挑选中:点击目标 hall 头完成归类" }) : null,
4363
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4364
+ ] }) : null,
4365
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginBottom: 8, padding: "6px 8px", border: "1px solid var(--dsh-mem-danger)", borderRadius: 8, fontSize: 12 }, children: [
4366
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: preview.notice }),
4367
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, marginTop: 4 }, children: [
4368
+ /* @__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: [
4369
+ "确认执行",
4370
+ preview.action === "merge" ? "合并" : "归类/改名"
4371
+ ] }),
4372
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => setPreview(null), style: btn, children: "再改改" })
4373
+ ] })
4374
+ ] }) : null,
4302
4375
  msg ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.hint, children: msg }) : null,
4303
4376
  error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.error, children: error }) : null,
4377
+ 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,
4304
4378
  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: [
4305
4379
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4306
4380
  "div",
4307
4381
  {
4382
+ "data-hall": g.hall,
4308
4383
  onDragOver: (ev) => {
4309
4384
  if (!hiPriv || busy) return;
4310
4385
  ev.preventDefault();
@@ -4361,18 +4436,16 @@ var __defProp = Object.defineProperty;
4361
4436
  const standalone = e.slug === g.hall;
4362
4437
  const count = rooms.find((r) => r.room === e.slug)?.count ?? 0;
4363
4438
  const retired = e.status === "retired";
4364
- 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: [
4365
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
4439
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { padding: "4px 10px", borderTop: "1px solid var(--dsh-mem-border)", opacity: retired ? 0.55 : 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4440
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexWrap: "wrap", gap: "2px 8px", alignItems: "baseline" }, children: [
4366
4441
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4367
4442
  "span",
4368
4443
  {
4369
- style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default" },
4370
- draggable: hiPriv && !busy,
4371
- onDragStart: (ev) => {
4372
- ev.dataTransfer.setData("text/dsh-room", e.slug);
4373
- ev.dataTransfer.effectAllowed = "move";
4374
- },
4375
- title: hiPriv && !busy ? "拖到某个 hall 头上归类(major/minor;记录随迁,旧名进别名)" : e.description ? `${e.slug} — ${e.description}` : e.slug,
4444
+ style: { fontSize: 12, cursor: hiPriv && !busy ? "grab" : "default", touchAction: "none" },
4445
+ onPointerDown: (ev) => onDragLabelDown(ev, e.slug),
4446
+ onPointerMove: onDragLabelMove,
4447
+ onPointerUp: onDragLabelUp,
4448
+ title: hiPriv && !busy ? "按住拖到某个 hall 头上归类(major/minor;记录随迁,旧名进别名)" : e.description ? `${e.slug} — ${e.description}` : e.slug,
4376
4449
  children: [
4377
4450
  standalone ? e.slug : "· " + e.slug.slice(g.hall.length + 1),
4378
4451
  e.source === "pre-registered" ? " ⭐" : ""
@@ -4382,8 +4455,9 @@ var __defProp = Object.defineProperty;
4382
4455
  e.label ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: e.label }) : null,
4383
4456
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: count + " 条" }),
4384
4457
  retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "已退役" }) : null,
4385
- (e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "别名: " + (e.aliases ?? []).join(", ") }) : null,
4386
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4458
+ (e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...S.muted, wordBreak: "break-all" }, children: "别名: " + (e.aliases ?? []).join(", ") }) : null
4459
+ ] }),
4460
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0, alignItems: "center" }, children: [
4387
4461
  !hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4388
4462
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4389
4463
  "button",
@@ -4417,53 +4491,8 @@ var __defProp = Object.defineProperty;
4417
4491
  /* @__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 ? "恢复" : "退役" })
4418
4492
  ] }),
4419
4493
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(e.slug), style: btn, children: "导出" })
4420
- ] }),
4421
- form && form.from === e.slug ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginTop: 4, flexWrap: "wrap" }, children: [
4422
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: S.muted, children: [
4423
- form.action === "merge" ? "合并" : "改名",
4424
- " ",
4425
- form.from,
4426
- " →"
4427
- ] }),
4428
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4429
- "input",
4430
- {
4431
- value: form.to,
4432
- onChange: (ev) => setForm({ ...form, to: ev.target.value }),
4433
- placeholder: "目标 slug",
4434
- disabled: busy,
4435
- style: { ...S.input, width: 180, fontSize: 12 }
4436
- }
4437
- ),
4438
- 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)(
4439
- "button",
4440
- {
4441
- type: "button",
4442
- title: `归入 ${gg.hall} 下(${gg.hall}/${suffixOf(form.from)})`,
4443
- onClick: () => setForm({ ...form, to: `${gg.hall}/${suffixOf(form.from)}` }),
4444
- style: { ...btn, borderRadius: 999, padding: "1px 8px" },
4445
- children: gg.hall
4446
- },
4447
- gg.hall
4448
- )) }) : null,
4449
- /* @__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: "预览" }),
4450
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => {
4451
- setForm(null);
4452
- setPreview(null);
4453
- }, style: btn, children: "取消" }),
4454
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "合并/改名与反刍并发会互相覆盖,请在反刍空闲时执行。" })
4455
- ] }) : null,
4456
- preview && (form ? form.from === e.slug : false) ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginTop: 4, padding: "6px 8px", border: "1px solid var(--dsh-mem-accent)", borderRadius: 6, fontSize: 12 }, children: [
4457
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: preview.notice }),
4458
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { display: "flex", gap: 6, marginTop: 4 }, children: [
4459
- /* @__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: [
4460
- "确认执行",
4461
- preview.action === "merge" ? "合并" : "改名"
4462
- ] }),
4463
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, onClick: () => setPreview(null), style: btn, children: "再改改" })
4464
- ] })
4465
- ] }) : null
4466
- ] }, e.slug);
4494
+ ] })
4495
+ ] }) }, e.slug);
4467
4496
  })
4468
4497
  ] }, g.hall)),
4469
4498
  grownOnly.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { marginTop: 4 }, children: [
@@ -4477,13 +4506,11 @@ var __defProp = Object.defineProperty;
4477
4506
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4478
4507
  "span",
4479
4508
  {
4480
- style: { ...btn, cursor: hiPriv && !busy ? "grab" : "default", borderRadius: 999, padding: "2px 8px" },
4481
- draggable: hiPriv && !busy,
4482
- onDragStart: (ev) => {
4483
- ev.dataTransfer.setData("text/dsh-room", r.room);
4484
- ev.dataTransfer.effectAllowed = "move";
4485
- },
4486
- title: hiPriv && !busy ? "自生长 slug(未注册)——拖到某个 hall 头上 = 收编并归为其子类" : "自生长 slug(未注册)",
4509
+ style: { ...btn, cursor: hiPriv && !busy ? "grab" : "default", borderRadius: 999, padding: "2px 8px", touchAction: "none" },
4510
+ onPointerDown: (ev) => onDragLabelDown(ev, r.room),
4511
+ onPointerMove: onDragLabelMove,
4512
+ onPointerUp: onDragLabelUp,
4513
+ title: hiPriv && !busy ? "自生长 slug(未注册)——按住拖到某个 hall 头上 = 收编并归为其子类" : "自生长 slug(未注册)",
4487
4514
  children: r.room + " · " + r.count
4488
4515
  }
4489
4516
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prime-memory",
3
- "version": "0.20.0-beta.17",
3
+ "version": "0.20.0-beta.19",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",