dsh-sessions-manager 3.4.2 → 3.5.1
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.en.md +6 -2
- package/README.md +6 -2
- package/lib/client.js +78 -21
- package/lib/client.js.map +2 -2
- package/lib/index.js +272 -85
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
- package/src/client/index.jsx +58 -14
- package/src/client/logic.js +8 -0
- package/src/compat/capabilities.js +34 -0
- package/src/compat/persistence.js +72 -0
- package/src/index.js +113 -60
- package/src/zstd-frame.js +68 -34
package/README.en.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
> DSH session manager: archive, move, restore, and inspect sessions from **Settings → 会话管理**; mark unread, move, and delete sessions directly from the **main sidebar**. Deleted sessions go to the recycle bin first and can be restored or permanently purged.
|
|
14
14
|
|
|
15
|
-
A persistent DSH plugin (host + browser halves)
|
|
15
|
+
A persistent DSH plugin (host + browser halves) covering both the **settings panel** and the **main sidebar**, so frequent session actions don't require opening Settings. It understands both legacy header lists/`readFrom` and the new snapshot/`SessionHandle` API, enabling actions according to the current Runtime's verified capabilities. Unverified purge or migration paths are disabled in both UI and Host to prevent false success.
|
|
16
16
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
@@ -43,7 +43,7 @@ A small dot is rendered to the left of each session row. Its color is driven by
|
|
|
43
43
|
| Color | State | Description |
|
|
44
44
|
|---|---|---|
|
|
45
45
|
| 🔵 Blue | Manually marked unread | Toggle via the ⋯ menu or by clicking the dot; auto-cleared when the session is opened |
|
|
46
|
-
| 🟡 Yellow | Working | The session is currently running (`running`) |
|
|
46
|
+
| 🟡 Yellow | Working | The session is currently running (`ongoing`, with legacy `running` compatibility) |
|
|
47
47
|
| 🟠 Amber | Awaiting feedback | The session has a follow-up question and is waiting for your input or confirmation (`warning`) |
|
|
48
48
|
| 🟢 Green | Completed but unread | The session is done (`done`) but you haven't reopened it yet; disappears after you view it |
|
|
49
49
|
| 🔴 Red | Error / needs attention | The session hit an error (`error`) |
|
|
@@ -149,6 +149,7 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
149
149
|
| POST | `/archived-sessions/storage` | Storage-usage aggregation (per-workspace ranking + largest sessions) `{ topN }` |
|
|
150
150
|
| POST | `/archived-sessions/auto-archive/settings` | Read or update the auto-archive policy `{ inactiveDays, skipStarred }`; reading lazily triggers the daily check |
|
|
151
151
|
| POST | `/archived-sessions/auto-archive/run` | Run a one-off auto-archive check immediately (ignores the daily throttle) |
|
|
152
|
+
| POST | `/archived-sessions/capabilities` | Report the persistence generation and per-action read/archive/trash/purge/cross-workspace-move capabilities |
|
|
152
153
|
|
|
153
154
|
> Deleting a session moves it to the recycle bin by default; only "permanently delete" inside the recycle bin physically removes the log. Permanently deleted sessions are hidden forever on the client side so they don't reappear in the sidebar or "ungrouped" due to DSH runtime caching.
|
|
154
155
|
|
|
@@ -157,6 +158,9 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
157
158
|
- **Supported platforms**: cross-platform (macOS / Windows / Linux) — wherever DSH runs the plugin runs; the host half is Node (about `^22.19` or `>=24`) and the client is React, with no OS-specific APIs.
|
|
158
159
|
- Works in both DSH Desktop and DSH web (same host + client halves).
|
|
159
160
|
- Peer dependencies are listed in `package.json`; `react` and `@deepseek-ai/*` are provided by the DSH runtime.
|
|
161
|
+
- `0.1.2-rc.1`: the existing read, archive, recycle-bin, permanent-purge, and cross-workspace move paths remain available.
|
|
162
|
+
- `0.1.3-alpha.1`: snapshot lists and read-only `SessionHandle` flows are supported. Permanent purge and cross-workspace move are disabled when no verified safe path is available; other management functions continue to work. The panel shows the effective capabilities.
|
|
163
|
+
- Unverified future runtimes expose only capabilities the plugin can safely identify; method presence alone is not presented as behavioral compatibility.
|
|
160
164
|
|
|
161
165
|
## License
|
|
162
166
|
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
> DSH 会话管理器:在**设置 → 会话管理**里统一归档、移动、恢复、查看详情;在**主页侧边栏**直接标记未读、移动、删除会话。删除先进入回收站,可恢复或彻底清理。
|
|
14
14
|
|
|
15
|
-
一个 DSH 持久化插件(host + browser
|
|
15
|
+
一个 DSH 持久化插件(host + browser 双半),同时覆盖「设置面板」与「主页侧边栏」两个入口,无需打开设置即可完成高频会话操作。插件同时识别旧式 header 列表/`readFrom` 与新版 snapshot/`SessionHandle`,并按当前 Runtime 的实际能力启用安全操作;尚未验证的删除或迁移路径会在界面与 Host 端同步禁用,避免假成功。
|
|
16
16
|
|
|
17
17
|
## 功能
|
|
18
18
|
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
| 颜色 | 状态 | 说明 |
|
|
44
44
|
|---|---|---|
|
|
45
45
|
| 🔵 蓝 | 手动标记未读 | 通过 ⋯ 菜单或点击圆点手动标记;进入会话后自动清除 |
|
|
46
|
-
| 🟡 黄 | 工作中 |
|
|
46
|
+
| 🟡 黄 | 工作中 | 会话正在运行(新版 `ongoing`,兼容旧版 `running`) |
|
|
47
47
|
| 🟠 琥珀 | 等待反馈 | 会话有追问,需要用户输入或确认(`warning`) |
|
|
48
48
|
| 🟢 绿 | 完成后未读 | 会话已完成(`done`)但你还没重新打开看过;看过一次后不再显示 |
|
|
49
49
|
| 🔴 红 | 出错 / 需关注 | 会话遇到错误(`error`) |
|
|
@@ -149,6 +149,7 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
149
149
|
| POST | `/archived-sessions/storage` | 存储占用聚合(按工作区排行 + 最大的会话)`{ topN }` |
|
|
150
150
|
| POST | `/archived-sessions/auto-archive/settings` | 读取或更新自动归档策略 `{ inactiveDays, skipStarred }`;读取时惰性触发每日检查 |
|
|
151
151
|
| POST | `/archived-sessions/auto-archive/run` | 立即执行一次自动归档检查(忽略每日节流) |
|
|
152
|
+
| POST | `/archived-sessions/capabilities` | 返回当前持久化代际及读取、归档、回收站、永久删除、跨工作区移动能力 |
|
|
152
153
|
|
|
153
154
|
> 删除会话默认进入回收站,只有回收站内的「彻底删除」才会物理移除日志。被彻底删除的会话由前端永久隐藏,避免 DSH 运行时缓存使其重新出现在侧栏或「未分组」中。
|
|
154
155
|
|
|
@@ -157,6 +158,9 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
157
158
|
- **适配系统**:跨平台(macOS / Windows / Linux)——只要 DSH 能在该系统运行即可;本插件 host 基于 Node(约 `^22.19` 或 `>=24`)、浏览器端为 React,不依赖特定操作系统 API。
|
|
158
159
|
- DSH Desktop / web 均可(同一套 host + client)。
|
|
159
160
|
- peerDependencies 见 `package.json`;`react`、`@deepseek-ai/*` 由 DSH 运行时提供。
|
|
161
|
+
- `0.1.2-rc.1`:现有读取、归档、回收站、永久删除和跨工作区移动能力保持可用。
|
|
162
|
+
- `0.1.3-alpha.1`:支持 snapshot 列表和 `SessionHandle` 只读流程;永久删除与跨工作区移动在缺少已验证安全路径时自动禁用,其余管理能力继续工作。能力以面板实际提示为准。
|
|
163
|
+
- 未经验证的未来 Runtime 默认只开放能够识别的安全能力;插件不会用方法存在与否冒充行为兼容。
|
|
160
164
|
|
|
161
165
|
## License
|
|
162
166
|
|
package/lib/client.js
CHANGED
|
@@ -40,6 +40,7 @@ var import_react = __toESM(require("react"), 1);
|
|
|
40
40
|
function dotStateFor({ manualUnread = false, dataState = "", isActive = false } = {}) {
|
|
41
41
|
if (manualUnread) return "manual";
|
|
42
42
|
switch (dataState) {
|
|
43
|
+
case "ongoing":
|
|
43
44
|
case "running":
|
|
44
45
|
return "running";
|
|
45
46
|
case "warning":
|
|
@@ -52,6 +53,10 @@ function dotStateFor({ manualUnread = false, dataState = "", isActive = false }
|
|
|
52
53
|
return null;
|
|
53
54
|
}
|
|
54
55
|
}
|
|
56
|
+
function authoritativeTitleForFirstPaint({ firstPaint = false, rendered = "", authoritative = "" } = {}) {
|
|
57
|
+
if (!firstPaint || !authoritative || rendered === authoritative) return null;
|
|
58
|
+
return authoritative;
|
|
59
|
+
}
|
|
55
60
|
function canDropOnWorkspace(item, target) {
|
|
56
61
|
if (!item || !target) return false;
|
|
57
62
|
if (item.workspacePath && target.path === item.workspacePath) return false;
|
|
@@ -278,6 +283,7 @@ function installSettingsNavIcons(ctx) {
|
|
|
278
283
|
function SessionPanel({ workspacesSvc }) {
|
|
279
284
|
const [sessions, setSessions] = (0, import_react.useState)(null);
|
|
280
285
|
const [workspaces, setWorkspaces] = (0, import_react.useState)([]);
|
|
286
|
+
const [capabilities, setCapabilities] = (0, import_react.useState)(null);
|
|
281
287
|
const initialPrefs = (0, import_react.useRef)(loadPanelPrefs()).current;
|
|
282
288
|
const [filter, setFilter] = (0, import_react.useState)(() => ["all", "active", "archived", "starred", "trash"].includes(initialPrefs.filter) ? initialPrefs.filter : "all");
|
|
283
289
|
const [query, setQuery] = (0, import_react.useState)("");
|
|
@@ -326,12 +332,14 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
326
332
|
setError(null);
|
|
327
333
|
Promise.all([
|
|
328
334
|
postJSON("/archived-sessions/sessions", {}),
|
|
329
|
-
postJSON("/archived-sessions/workspaces", {})
|
|
330
|
-
|
|
335
|
+
postJSON("/archived-sessions/workspaces", {}),
|
|
336
|
+
postJSON("/archived-sessions/capabilities", {})
|
|
337
|
+
]).then(([s, works, caps]) => {
|
|
331
338
|
const purged = dsmLoadPurged();
|
|
332
339
|
const visible = (s.items || []).filter((x) => !purged.has(String(x.sessionId)));
|
|
333
340
|
setSessions(visible);
|
|
334
341
|
setWorkspaces(works.items || []);
|
|
342
|
+
setCapabilities(caps || null);
|
|
335
343
|
setSelected({});
|
|
336
344
|
setDelTarget(null);
|
|
337
345
|
setConfirmBatch(false);
|
|
@@ -428,6 +436,9 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
428
436
|
const w = workspaces.find((x) => x.workspaceId === id);
|
|
429
437
|
return w ? w.path : "";
|
|
430
438
|
};
|
|
439
|
+
const actionCapability = (name) => capabilities && capabilities.actions && capabilities.actions[name] || { available: false, reason: "\u6B63\u5728\u68C0\u67E5\u5F53\u524D DSH \u7684\u517C\u5BB9\u80FD\u529B\u2026" };
|
|
440
|
+
const canPurge = actionCapability("purge");
|
|
441
|
+
const canMove = actionCapability("move");
|
|
431
442
|
const archivedList = sessions ? sessions.filter((x) => x.archived) : [];
|
|
432
443
|
const activeList = sessions ? sessions.filter((x) => !x.archived) : [];
|
|
433
444
|
const starredList = starredOf(sessions);
|
|
@@ -511,7 +522,9 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
511
522
|
postJSON("/archived-sessions/trash/verify", {}).then((r) => {
|
|
512
523
|
setTrashBusy(null);
|
|
513
524
|
setTrashCheck(r);
|
|
514
|
-
|
|
525
|
+
if (r.missing) showToast(`\u53D1\u73B0 ${r.missing} \u6761\u65E5\u5FD7\u7F3A\u5931`);
|
|
526
|
+
else if (r.unverified) showToast(`${r.unverified} \u6761\u65E5\u5FD7\u4F4D\u7F6E\u7531\u5F53\u524D Runtime \u7BA1\u7406\uFF0C\u65E0\u6CD5\u76F4\u63A5\u6838\u9A8C`);
|
|
527
|
+
else showToast("\u56DE\u6536\u7AD9\u6821\u9A8C\u901A\u8FC7");
|
|
515
528
|
}).catch((e) => {
|
|
516
529
|
setTrashBusy(null);
|
|
517
530
|
setError(String(e && e.message || e));
|
|
@@ -612,6 +625,10 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
612
625
|
});
|
|
613
626
|
};
|
|
614
627
|
const doMove = (it) => {
|
|
628
|
+
if (!canMove.available) {
|
|
629
|
+
setError(canMove.reason || "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8DE8\u5DE5\u4F5C\u533A\u79FB\u52A8");
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
615
632
|
const targetPath = moveMode === "new" ? newPath.trim() : wsPath(targetWs);
|
|
616
633
|
if (!targetPath) {
|
|
617
634
|
setError("\u8BF7\u9009\u62E9\u5DF2\u6709\u5DE5\u4F5C\u533A\u6216\u8F93\u5165\u65B0\u7684\u76EE\u6807\u76EE\u5F55\u8DEF\u5F84");
|
|
@@ -653,6 +670,10 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
653
670
|
});
|
|
654
671
|
};
|
|
655
672
|
const openMoveFor = (it) => {
|
|
673
|
+
if (!canMove.available) {
|
|
674
|
+
setError(canMove.reason || "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8DE8\u5DE5\u4F5C\u533A\u79FB\u52A8");
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
656
677
|
if (openMove === it.sessionId) {
|
|
657
678
|
setOpenMove(null);
|
|
658
679
|
return;
|
|
@@ -738,17 +759,22 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
738
759
|
children: "\u22EF"
|
|
739
760
|
}
|
|
740
761
|
),
|
|
741
|
-
openMenu === it.sessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "more-menu", role: "menu", children: items.map(([id, label]) =>
|
|
742
|
-
"
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
762
|
+
openMenu === it.sessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "more-menu", role: "menu", children: items.map(([id, label]) => {
|
|
763
|
+
const blocked = id === "move" && !canMove.available;
|
|
764
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
765
|
+
"button",
|
|
766
|
+
{
|
|
767
|
+
type: "button",
|
|
768
|
+
role: "menuitem",
|
|
769
|
+
className: "more-item" + (id === "delete" ? " more-item-danger" : ""),
|
|
770
|
+
disabled: blocked,
|
|
771
|
+
title: blocked ? canMove.reason : void 0,
|
|
772
|
+
onClick: () => runMenu(id, it),
|
|
773
|
+
children: label
|
|
774
|
+
},
|
|
775
|
+
id
|
|
776
|
+
);
|
|
777
|
+
}) })
|
|
752
778
|
] });
|
|
753
779
|
};
|
|
754
780
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv", role: "region", "aria-label": "\u4F1A\u8BDD\u7BA1\u7406", children: [
|
|
@@ -1191,22 +1217,25 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1191
1217
|
] }),
|
|
1192
1218
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
|
|
1193
1219
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u6C38\u4E45\u6E05\u7406" }),
|
|
1194
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !trash.length, onClick: () => setPurgeTarget("__all"), children: "\u6E05\u7A7A\u56DE\u6536\u7AD9" })
|
|
1220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !trash.length || !canPurge.available, title: !canPurge.available ? canPurge.reason : void 0, onClick: () => setPurgeTarget("__all"), children: "\u6E05\u7A7A\u56DE\u6536\u7AD9" })
|
|
1195
1221
|
] })
|
|
1196
1222
|
] }),
|
|
1223
|
+
!canPurge.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", role: "status", children: canPurge.reason }),
|
|
1197
1224
|
trashCheck && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: trashCheck.missing ? "archv-err" : "archv-empty", role: "status", children: [
|
|
1198
1225
|
"\u6821\u9A8C\u5B8C\u6210\uFF1A",
|
|
1199
1226
|
trashCheck.healthy,
|
|
1200
1227
|
" \u6761\u6B63\u5E38\uFF0C",
|
|
1201
1228
|
trashCheck.missing,
|
|
1202
|
-
" \u6761\u65E5\u5FD7\u7F3A\u5931\
|
|
1229
|
+
" \u6761\u65E5\u5FD7\u7F3A\u5931\uFF0C",
|
|
1230
|
+
trashCheck.unverified || 0,
|
|
1231
|
+
" \u6761\u65E0\u6CD5\u76F4\u63A5\u6838\u9A8C\u3002"
|
|
1203
1232
|
] }),
|
|
1204
1233
|
trash.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-trash-empty", children: "\u56DE\u6536\u7AD9\u4E3A\u7A7A\u3002\u5220\u9664\u7684\u4F1A\u8BDD\u4F1A\u5148\u8FDB\u5165\u8FD9\u91CC\uFF0C\u53EF\u6062\u590D\u6216\u5F7B\u5E95\u5220\u9664\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-trash-list", children: trash.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-trash-row", children: [
|
|
1205
1234
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-trash-name", title: t.sessionId, children: t.title || t.sessionId }),
|
|
1206
1235
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-trash-date", children: fmtDate(t.deletedAt) }),
|
|
1207
1236
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-trash-actions", children: [
|
|
1208
1237
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: trashBusy !== null, onClick: () => restoreTrash(t.sessionId), children: "\u6062\u590D" }),
|
|
1209
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null, onClick: () => setPurgeTarget(t.sessionId), children: "\u5F7B\u5E95\u5220\u9664" })
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !canPurge.available, title: !canPurge.available ? canPurge.reason : void 0, onClick: () => setPurgeTarget(t.sessionId), children: "\u5F7B\u5E95\u5220\u9664" })
|
|
1210
1239
|
] })
|
|
1211
1240
|
] }, t.sessionId)) })
|
|
1212
1241
|
] }),
|
|
@@ -1258,7 +1287,7 @@ var SIDEBAR_AUG_CSS = `
|
|
|
1258
1287
|
.dsm-sub-cur{font-size:11px;color:var(--dsw-alias-state-business-primary);flex:none;margin-left:8px}
|
|
1259
1288
|
.dsm-dot{position:absolute;left:6px;top:50%;transform:translateY(-50%);width:8px;height:8px;border-radius:50%;box-sizing:border-box;cursor:pointer;pointer-events:auto;z-index:1}
|
|
1260
1289
|
.dsm-dot-unread-manual{background:var(--dsw-alias-state-business-primary)}
|
|
1261
|
-
.dsm-dot-waiting{background:var(--dsw-alias-state-
|
|
1290
|
+
.dsm-dot-waiting{background:var(--dsw-alias-state-warn-primary,#F59E0B)}
|
|
1262
1291
|
.dsm-dot-unread{background:var(--dsw-alias-state-success-primary)}
|
|
1263
1292
|
.dsm-drag-source{opacity:.48}
|
|
1264
1293
|
.dsm-drop-target{position:relative;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent)!important;outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px;border-radius:8px}
|
|
@@ -1319,6 +1348,17 @@ var dsmTrashIds = null;
|
|
|
1319
1348
|
var dsmServerPurgedIds = /* @__PURE__ */ new Set();
|
|
1320
1349
|
var dsmAuthoritativeTitles = /* @__PURE__ */ new Map();
|
|
1321
1350
|
var dsmTrashTick = 0;
|
|
1351
|
+
var dsmCapabilities = null;
|
|
1352
|
+
async function dsmLoadCapabilities() {
|
|
1353
|
+
try {
|
|
1354
|
+
dsmCapabilities = await postJSON("/archived-sessions/capabilities", {});
|
|
1355
|
+
} catch (e) {
|
|
1356
|
+
}
|
|
1357
|
+
return dsmCapabilities;
|
|
1358
|
+
}
|
|
1359
|
+
function dsmActionCapability(name) {
|
|
1360
|
+
return dsmCapabilities && dsmCapabilities.actions && dsmCapabilities.actions[name];
|
|
1361
|
+
}
|
|
1322
1362
|
async function dsmLoadTrashIds() {
|
|
1323
1363
|
try {
|
|
1324
1364
|
const r = await postJSON("/archived-sessions/sidebar-state", {});
|
|
@@ -1549,6 +1589,11 @@ function installSidebarSessionMenuAug() {
|
|
|
1549
1589
|
const move = mk("\u79FB\u52A8\u4F1A\u8BDD", ICON_MOVE, false);
|
|
1550
1590
|
const del = mk("\u5220\u9664\u4F1A\u8BDD", ICON_DEL, true);
|
|
1551
1591
|
const mark = mk(dsmLoadManual().has(info.id) ? "\u6807\u8BB0\u5DF2\u8BFB" : "\u6807\u8BB0\u672A\u8BFB", ICON_UNREAD, false);
|
|
1592
|
+
const moveCapability = dsmActionCapability("move");
|
|
1593
|
+
if (!moveCapability || !moveCapability.available) {
|
|
1594
|
+
move.btn.disabled = true;
|
|
1595
|
+
move.btn.title = moveCapability && moveCapability.reason || "\u6B63\u5728\u68C0\u67E5\u5F53\u524D\u7248\u672C\u7684\u79FB\u52A8\u80FD\u529B";
|
|
1596
|
+
}
|
|
1552
1597
|
if (mark.btn.firstChild) mark.btn.firstChild.style.color = "var(--dsw-alias-state-business-primary)";
|
|
1553
1598
|
const chev = document.createElement("span");
|
|
1554
1599
|
chev.style.cssText = "margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:1";
|
|
@@ -1635,7 +1680,7 @@ function installSidebarStatusDots() {
|
|
|
1635
1680
|
// 蓝 手动标记未读
|
|
1636
1681
|
running: "#EAB308",
|
|
1637
1682
|
// 黄 工作中 (DSH running)
|
|
1638
|
-
feedback: "var(--dsw-alias-state-
|
|
1683
|
+
feedback: "var(--dsw-alias-state-warn-primary, #F59E0B)",
|
|
1639
1684
|
// 琥珀 需用户反馈 (DSH warning)
|
|
1640
1685
|
done: "var(--dsw-alias-state-success-primary)",
|
|
1641
1686
|
// 绿 完成后未读 (DSH done)
|
|
@@ -1643,6 +1688,7 @@ function installSidebarStatusDots() {
|
|
|
1643
1688
|
// 红 出错/需关注 (DSH error)
|
|
1644
1689
|
};
|
|
1645
1690
|
const manualUnread = dsmLoadManual();
|
|
1691
|
+
const titleInitializedRows = /* @__PURE__ */ new WeakSet();
|
|
1646
1692
|
const paint = () => {
|
|
1647
1693
|
const activeId = activeRowId();
|
|
1648
1694
|
if (activeId !== curActive && activeId && manualUnread.has(activeId)) {
|
|
@@ -1663,12 +1709,20 @@ function installSidebarStatusDots() {
|
|
|
1663
1709
|
}
|
|
1664
1710
|
if (row.style.display === "none") row.style.display = "";
|
|
1665
1711
|
const authoritativeTitle = dsmAuthoritativeTitles.get(id);
|
|
1666
|
-
if (
|
|
1712
|
+
if (!titleInitializedRows.has(row)) {
|
|
1667
1713
|
const node = rowNode(row) || {};
|
|
1668
1714
|
const expected = new Set([node.title, node.displayTitle, node.name].filter((value) => typeof value === "string"));
|
|
1669
1715
|
const spans = [...row.children].filter((el) => el.tagName === "SPAN" && !el.querySelector("[data-state]") && (el.textContent || "").trim());
|
|
1670
1716
|
const titleEl = spans.find((el) => expected.has((el.textContent || "").trim())) || spans[0];
|
|
1671
|
-
if (titleEl
|
|
1717
|
+
if (titleEl) {
|
|
1718
|
+
const correction = authoritativeTitleForFirstPaint({
|
|
1719
|
+
firstPaint: true,
|
|
1720
|
+
rendered: titleEl.textContent || "",
|
|
1721
|
+
authoritative: authoritativeTitle || ""
|
|
1722
|
+
});
|
|
1723
|
+
if (correction) titleEl.textContent = correction;
|
|
1724
|
+
titleInitializedRows.add(row);
|
|
1725
|
+
}
|
|
1672
1726
|
}
|
|
1673
1727
|
const sd = row.querySelector("[data-state]");
|
|
1674
1728
|
if (sd) sd.style.display = "none";
|
|
@@ -1785,6 +1839,8 @@ function installSidebarWorkspaceDrag() {
|
|
|
1785
1839
|
maybeRefresh();
|
|
1786
1840
|
}, true);
|
|
1787
1841
|
document.addEventListener("dragstart", (event) => {
|
|
1842
|
+
const moveCapability = dsmActionCapability("move");
|
|
1843
|
+
if (!moveCapability || !moveCapability.available) return;
|
|
1788
1844
|
const row = eventRow(event);
|
|
1789
1845
|
const item = row && sessionForRow(row);
|
|
1790
1846
|
if (!item || moving) return;
|
|
@@ -1864,6 +1920,7 @@ function installSidebarWorkspaceDrag() {
|
|
|
1864
1920
|
}
|
|
1865
1921
|
}
|
|
1866
1922
|
function apply(ctx) {
|
|
1923
|
+
dsmLoadCapabilities();
|
|
1867
1924
|
installSettingsNavIcons(ctx);
|
|
1868
1925
|
installSidebarSessionMenuAug();
|
|
1869
1926
|
installSidebarStatusDots();
|