dsh-obsidian-notes 0.5.1 → 0.5.2

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
@@ -64,7 +64,7 @@ not enough.
64
64
  2. Search for **`dsh-obsidian-notes`** and install it.
65
65
  3. Restart DeepSeek Harness.
66
66
 
67
- > This path requires the package to be published on npm (target release 0.5.0).
67
+ > This path requires the package to be published on npm (current release **0.5.2**).
68
68
  > If search shows nothing, use one of the two paths below.
69
69
 
70
70
  ### 2. CLI (after the npm release)
package/README.zh.md CHANGED
@@ -48,7 +48,7 @@ DSH 生态里已经有一个 Obsidian 方向的插件
48
48
  2. 搜索 **`dsh-obsidian-notes`** 并安装;
49
49
  3. 重启 DeepSeek Harness。
50
50
 
51
- > 这条路径要求 npm 上已有该包(目标版本 0.5.0)。搜不到时用下面两种方式。
51
+ > 这条路径要求 npm 上已有该包(当前版本 **0.5.2**)。搜不到时用下面两种方式。
52
52
 
53
53
  ### 2. CLI(npm 发布后)
54
54
 
package/lib/client.js CHANGED
@@ -2043,6 +2043,38 @@ window.__ModuleLoader__.load({
2043
2043
  return `${d.getFullYear()}/${String(d.getMonth() + 1).padStart(2, "0")}/${String(d.getDate()).padStart(2, "0")}`;
2044
2044
  }
2045
2045
 
2046
+ /**
2047
+ * 会话行次序:与 DSH 内置侧边栏的**默认**排序(`orderBy: 'updated'`,
2048
+ * 「最近更新」)一致。
2049
+ *
2050
+ * 不能直接用 Workspace 的 `sessionIds` 次序:那是**挂载次序**(host 侧
2051
+ * `attachSession` 把新会话 prepend 到数组头),一个很久以前创建、现在仍在
2052
+ * 用的会话会停在旧位置(表现为「当前会话排在倒数第二」)。DSH 默认视图
2053
+ * 的做法是:按会话的 `updatedAt` 降序;时间相同时按 id 升序 —— 官方
2054
+ * `compareSessionRecency` 就是这两个比较,id 作稳定 tie-break 是为了同一份
2055
+ * 数据每次渲染次序稳定,不随 Map 迭代顺序抖动。
2056
+ * @param a - 会话 id。
2057
+ * @param b - 会话 id。
2058
+ * @param byId - 会话 id → 会话摘要(`updatedAt` 为 epoch ms)。
2059
+ * @returns 排序比较值(负 = a 更靠前)。
2060
+ */
2061
+ function compareSessionRecency(a, b, byId) {
2062
+ const ta = byId[a]?.updatedAt ?? Number.NEGATIVE_INFINITY;
2063
+ const tb = byId[b]?.updatedAt ?? Number.NEGATIVE_INFINITY;
2064
+ if (ta !== tb) return tb - ta;
2065
+ return a < b ? -1 : 1;
2066
+ }
2067
+
2068
+ /**
2069
+ * 按「最近更新」排会话 id 列表(不修改入参)。
2070
+ * @param sessionIds - 待排的会话 id。
2071
+ * @param byId - 会话 id → 会话摘要。
2072
+ * @returns 新的已排序数组。
2073
+ */
2074
+ function sortedSessionIds(sessionIds, byId) {
2075
+ return [...sessionIds].sort((a, b) => compareSessionRecency(a, b, byId));
2076
+ }
2077
+
2046
2078
  /**
2047
2079
  * 一个会话及其全部子代理后代(闭包,含自身)。
2048
2080
  * 删父会话必须连子代理日志一起删:父级日志消失后,子会话会变成无法归属的
@@ -2718,7 +2750,9 @@ window.__ModuleLoader__.load({
2718
2750
  return true;
2719
2751
  };
2720
2752
 
2721
- // 分组:工作区 sessionIds 账户为主,cwd === path 兜底;其余入「未分组」
2753
+ // 分组:工作区 sessionIds 账户为主,cwd === path 兜底;其余入「未分组」。
2754
+ // 组内次序**不**沿用 Workspace 的 sessionIds:那是挂载次序(新会话 prepend),
2755
+ // 会让人气正旺的旧会话沉底。统一按「最近更新」排,与 DSH 内置侧边栏默认一致。
2722
2756
  const groups = useMemo(() => {
2723
2757
  const accounted = new Set();
2724
2758
  const list = [];
@@ -2737,11 +2771,12 @@ window.__ModuleLoader__.load({
2737
2771
  accounted.add(sid);
2738
2772
  }
2739
2773
  }
2740
- list.push({ key: w.workspaceId, workspace: w, sessionIds: wsIds });
2774
+ list.push({ key: w.workspaceId, workspace: w, sessionIds: sortedSessionIds(wsIds, byId) });
2741
2775
  }
2742
- const ungrouped = ids
2743
- .filter((sid) => !accounted.has(sid) && visibleSession(sid))
2744
- .sort((a, b) => (byId[b].updatedAt ?? 0) - (byId[a].updatedAt ?? 0));
2776
+ const ungrouped = sortedSessionIds(
2777
+ ids.filter((sid) => !accounted.has(sid) && visibleSession(sid)),
2778
+ byId,
2779
+ );
2745
2780
  return { list, ungrouped };
2746
2781
  }, [items, ids, byId, archived, current, purged]); // eslint-disable-line react-hooks/exhaustive-deps
2747
2782
 
@@ -2796,15 +2831,13 @@ window.__ModuleLoader__.load({
2796
2831
  const q = query.trim().toLowerCase();
2797
2832
  const matched = useMemo(() => {
2798
2833
  if (q === "") return [];
2799
- return ids
2800
- .filter((sid) => {
2801
- if (!visibleSession(sid)) return false;
2802
- const s = byId[sid];
2803
- const title = (s.title ?? "").toLowerCase();
2804
- const display = (s.displayTitle ?? "").toLowerCase();
2805
- return title.includes(q) || display.includes(q);
2806
- })
2807
- .sort((a, b) => (byId[b].updatedAt ?? 0) - (byId[a].updatedAt ?? 0));
2834
+ return sortedSessionIds(ids.filter((sid) => {
2835
+ if (!visibleSession(sid)) return false;
2836
+ const s = byId[sid];
2837
+ const title = (s.title ?? "").toLowerCase();
2838
+ const display = (s.displayTitle ?? "").toLowerCase();
2839
+ return title.includes(q) || display.includes(q);
2840
+ }), byId);
2808
2841
  }, [ids, byId, archived, q, current, purged]);
2809
2842
 
2810
2843
  // ── 批量操作 ──────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-obsidian-notes",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Obsidian-style notes panel for DeepSeek Harness Web: vault directory tree, Markdown preview/edit/save, cite-to-chat, batch archive/delete of sessions and workspaces. DSH 内嵌 Obsidian 风格笔记面板:目录树 + Markdown 预览/编辑/保存 + 引用到对话 + 会话/工作区批量操作。",
5
5
  "engines": {
6
6
  "node": "^22.19 || >=24"
package/src/client.js CHANGED
@@ -946,6 +946,38 @@
946
946
  return `${d.getFullYear()}/${String(d.getMonth() + 1).padStart(2, "0")}/${String(d.getDate()).padStart(2, "0")}`;
947
947
  }
948
948
 
949
+ /**
950
+ * 会话行次序:与 DSH 内置侧边栏的**默认**排序(`orderBy: 'updated'`,
951
+ * 「最近更新」)一致。
952
+ *
953
+ * 不能直接用 Workspace 的 `sessionIds` 次序:那是**挂载次序**(host 侧
954
+ * `attachSession` 把新会话 prepend 到数组头),一个很久以前创建、现在仍在
955
+ * 用的会话会停在旧位置(表现为「当前会话排在倒数第二」)。DSH 默认视图
956
+ * 的做法是:按会话的 `updatedAt` 降序;时间相同时按 id 升序 —— 官方
957
+ * `compareSessionRecency` 就是这两个比较,id 作稳定 tie-break 是为了同一份
958
+ * 数据每次渲染次序稳定,不随 Map 迭代顺序抖动。
959
+ * @param a - 会话 id。
960
+ * @param b - 会话 id。
961
+ * @param byId - 会话 id → 会话摘要(`updatedAt` 为 epoch ms)。
962
+ * @returns 排序比较值(负 = a 更靠前)。
963
+ */
964
+ function compareSessionRecency(a, b, byId) {
965
+ const ta = byId[a]?.updatedAt ?? Number.NEGATIVE_INFINITY;
966
+ const tb = byId[b]?.updatedAt ?? Number.NEGATIVE_INFINITY;
967
+ if (ta !== tb) return tb - ta;
968
+ return a < b ? -1 : 1;
969
+ }
970
+
971
+ /**
972
+ * 按「最近更新」排会话 id 列表(不修改入参)。
973
+ * @param sessionIds - 待排的会话 id。
974
+ * @param byId - 会话 id → 会话摘要。
975
+ * @returns 新的已排序数组。
976
+ */
977
+ function sortedSessionIds(sessionIds, byId) {
978
+ return [...sessionIds].sort((a, b) => compareSessionRecency(a, b, byId));
979
+ }
980
+
949
981
  /**
950
982
  * 一个会话及其全部子代理后代(闭包,含自身)。
951
983
  * 删父会话必须连子代理日志一起删:父级日志消失后,子会话会变成无法归属的
@@ -1621,7 +1653,9 @@
1621
1653
  return true;
1622
1654
  };
1623
1655
 
1624
- // 分组:工作区 sessionIds 账户为主,cwd === path 兜底;其余入「未分组」
1656
+ // 分组:工作区 sessionIds 账户为主,cwd === path 兜底;其余入「未分组」。
1657
+ // 组内次序**不**沿用 Workspace 的 sessionIds:那是挂载次序(新会话 prepend),
1658
+ // 会让人气正旺的旧会话沉底。统一按「最近更新」排,与 DSH 内置侧边栏默认一致。
1625
1659
  const groups = useMemo(() => {
1626
1660
  const accounted = new Set();
1627
1661
  const list = [];
@@ -1640,11 +1674,12 @@
1640
1674
  accounted.add(sid);
1641
1675
  }
1642
1676
  }
1643
- list.push({ key: w.workspaceId, workspace: w, sessionIds: wsIds });
1677
+ list.push({ key: w.workspaceId, workspace: w, sessionIds: sortedSessionIds(wsIds, byId) });
1644
1678
  }
1645
- const ungrouped = ids
1646
- .filter((sid) => !accounted.has(sid) && visibleSession(sid))
1647
- .sort((a, b) => (byId[b].updatedAt ?? 0) - (byId[a].updatedAt ?? 0));
1679
+ const ungrouped = sortedSessionIds(
1680
+ ids.filter((sid) => !accounted.has(sid) && visibleSession(sid)),
1681
+ byId,
1682
+ );
1648
1683
  return { list, ungrouped };
1649
1684
  }, [items, ids, byId, archived, current, purged]); // eslint-disable-line react-hooks/exhaustive-deps
1650
1685
 
@@ -1699,15 +1734,13 @@
1699
1734
  const q = query.trim().toLowerCase();
1700
1735
  const matched = useMemo(() => {
1701
1736
  if (q === "") return [];
1702
- return ids
1703
- .filter((sid) => {
1704
- if (!visibleSession(sid)) return false;
1705
- const s = byId[sid];
1706
- const title = (s.title ?? "").toLowerCase();
1707
- const display = (s.displayTitle ?? "").toLowerCase();
1708
- return title.includes(q) || display.includes(q);
1709
- })
1710
- .sort((a, b) => (byId[b].updatedAt ?? 0) - (byId[a].updatedAt ?? 0));
1737
+ return sortedSessionIds(ids.filter((sid) => {
1738
+ if (!visibleSession(sid)) return false;
1739
+ const s = byId[sid];
1740
+ const title = (s.title ?? "").toLowerCase();
1741
+ const display = (s.displayTitle ?? "").toLowerCase();
1742
+ return title.includes(q) || display.includes(q);
1743
+ }), byId);
1711
1744
  }, [ids, byId, archived, q, current, purged]);
1712
1745
 
1713
1746
  // ── 批量操作 ──────────────────────────────────────────────────────────────