sage-mem 2.1.20 → 2.1.21

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/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  **Cross-session memory for DeepSeek Harness — plain markdown files, no database, no worker.**
14
14
 
15
- **Current release: `2.1.20`** — the npm badge above is the dynamic source of truth. **Contract baseline: `1.0.0`** — the first stable release, whose public commitments, the capability boundaries and the list of things it explicitly cannot do are frozen in the
15
+ **Current release: `2.1.21`** — the npm badge above is the dynamic source of truth. **Contract baseline: `1.0.0`** — the first stable release, whose public commitments, the capability boundaries and the list of things it explicitly cannot do are frozen in the
16
16
  [frozen contract](https://github.com/gezi-wen/sage-mem/blob/main/docs/1.0.0-contract.md).
17
17
 
18
18
  If it is useful to you, a Star is the most direct feedback.
package/README.zh.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  > File-based cross-session memory for DeepSeek Harness — every memory is a plain markdown file you can open, edit and delete. No database, no worker, no port.
16
16
 
17
- **当前发布版本:`2.1.20`** —— 顶部 npm 徽章是动态权威。**契约基线:`1.0.0`** —— 第一个稳定版,公开承诺、能力边界与「明确做不到」的清单冻结在
17
+ **当前发布版本:`2.1.21`** —— 顶部 npm 徽章是动态权威。**契约基线:`1.0.0`** —— 第一个稳定版,公开承诺、能力边界与「明确做不到」的清单冻结在
18
18
  [契约冻结](https://github.com/gezi-wen/sage-mem/blob/main/docs/1.0.0-contract.md)。
19
19
 
20
20
  如果它对你有用,给个 Star 是最直接的反馈。
package/lib/client.js CHANGED
@@ -2808,6 +2808,20 @@ const StarmapRuntime = (function (require, _react) {
2808
2808
  }, props.label);
2809
2809
  }
2810
2810
 
2811
+ /**
2812
+ * 活动卡片「展开态」的合并(Q16,2.1.21)。
2813
+ *
2814
+ * 为什么是个纯函数:连续点两张卡(或同一帧里点两次)时,事件回调手里那个
2815
+ * `expanded` 可能还是上一帧的快照 —— 拿快照拼新对象会把另一笔改动覆盖掉。
2816
+ * 所以 Section 走**函数式 setState**,把「怎么合并」收敛到这里:
2817
+ * 只翻转目标文件那一条,其余原样保留。测试也直接喂 prev 来验这条语义。
2818
+ */
2819
+ function mergeExpanded(prev, file) {
2820
+ const next = Object.assign({}, prev);
2821
+ next[file] = !(prev && prev[file]);
2822
+ return next;
2823
+ }
2824
+
2811
2825
  function Card(props) {
2812
2826
  const item = props.item;
2813
2827
  const meta = TYPE_META[item.type] || TYPE_META.reference;
@@ -5640,7 +5654,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
5640
5654
  // 只负责「按状态/筛选把卡片摆出来 + 四种空态」;状态、筛选语义、动作回调全部由 Section 传。
5641
5655
  // 抽出的判据:它是 Section 里最大的一块纯渲染,而卡片组件(Card / ArchivedCard)本来就在 50 共享层。
5642
5656
  function FilesView(props) {
5643
- const { loading, totalCount, openAdd, createIndexSkeleton, statusFilter, setStatusFilter, items, archiveItems, filtered, setTypeFilter, setTagFilter, setQuery, busy, restoring, archDelete, archEdit, archLoad, archSaving, archText, setArchText, openArchEdit, saveArchEdit, cancelArchEdit, restoreFile, setArchDelete, setArchEdit, deleteArchivedFile, expanded, deleting, archiving, archiveReason, setArchiveReason, setArchiving, setDeleting, confirmArchive, toggleLock, openEdit, confirmDelete } = props;
5657
+ const { loading, totalCount, openAdd, createIndexSkeleton, statusFilter, setStatusFilter, items, archiveItems, filtered, setTypeFilter, setTagFilter, setQuery, busy, restoring, archDelete, archEdit, archLoad, archSaving, archText, setArchText, openArchEdit, saveArchEdit, cancelArchEdit, restoreFile, setArchDelete, setArchEdit, deleteArchivedFile, expanded, onToggleExpanded, deleting, archiving, archiveReason, setArchiveReason, setArchiving, setDeleting, confirmArchive, toggleLock, openEdit, confirmDelete } = props;
5644
5658
  let filesView;
5645
5659
  if (loading && totalCount === 0) {
5646
5660
  filesView = h("div", { className: "smem-skeleton", key: "loading", "data-loading": "1" }, [0, 1, 2].map(function (i) {
@@ -5704,11 +5718,10 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
5704
5718
  onCancelArchive: function () { setArchiving(null); setArchiveReason(""); },
5705
5719
  onConfirmArchive: confirmArchive,
5706
5720
  onToggleLock: toggleLock,
5707
- onToggle: function (file) {
5708
- const nx = {};
5709
- nx[file] = !expanded[file];
5710
- setExpanded(Object.assign({}, expanded, nx));
5711
- },
5721
+ // 展开 / 收起是**业务回调**(Q16):卡片只上报「点了哪张」,
5722
+ // 合并逻辑归 Section。旧版这里引用旧 Section 闭包里的 `setExpanded` ——
5723
+ // 拆件后它不在本函数作用域,点击即 ReferenceError: setExpanded is not defined。
5724
+ onToggle: onToggleExpanded,
5712
5725
  onEdit: openEdit,
5713
5726
  onAskDelete: function (file) { setDeleting(file); setArchiving(null); },
5714
5727
  onCancelDelete: function () { setDeleting(null); },
@@ -5828,6 +5841,12 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
5828
5841
  const query = sQuery[0], setQuery = sQuery[1];
5829
5842
  const sExpanded = react.useState({});
5830
5843
  const expanded = sExpanded[0], setExpanded = sExpanded[1];
5844
+ // Q16:卡片「展开 / 收起」是业务回调(FilesView 只上报点了哪张卡)。
5845
+ // 必须走**函数式** setState:事件里捕获的 expanded 可能是上一帧快照,
5846
+ // 直接 Object.assign({}, expanded, …) 会把连续点击的上一笔改动覆盖掉。
5847
+ function onToggleExpanded(file) {
5848
+ setExpanded(function (prev) { return mergeExpanded(prev, file); });
5849
+ }
5831
5850
  const sDeleting = react.useState(null);
5832
5851
  const deleting = sDeleting[0], setDeleting = sDeleting[1];
5833
5852
  const sFormOpen = react.useState(false);
@@ -6840,7 +6859,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
6840
6859
  archText: archText, setArchText: setArchText,
6841
6860
  openArchEdit: openArchEdit, saveArchEdit: saveArchEdit, cancelArchEdit: cancelArchEdit,
6842
6861
  restoreFile: restoreFile, setArchDelete: setArchDelete, setArchEdit: setArchEdit, deleteArchivedFile: deleteArchivedFile,
6843
- expanded: expanded, deleting: deleting, archiving: archiving,
6862
+ expanded: expanded, onToggleExpanded: onToggleExpanded, deleting: deleting, archiving: archiving,
6844
6863
  archiveReason: archiveReason, setArchiveReason: setArchiveReason,
6845
6864
  setArchiving: setArchiving, setDeleting: setDeleting,
6846
6865
  confirmArchive: confirmArchive, toggleLock: toggleLock,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sage-mem",
3
3
  "description": "Cross-session memory for DeepSeek Harness — plain markdown files, no database, no worker.",
4
- "version": "2.1.20",
4
+ "version": "2.1.21",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "icon": "./icon.png",