dsh-sessions-manager 3.7.2 → 3.7.4
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 +16 -7
- package/README.md +16 -7
- package/lib/client.js +98 -5
- package/lib/client.js.map +2 -2
- package/lib/index.js +83 -7
- package/lib/index.js.map +3 -3
- package/package.json +9 -9
- package/src/active-session.js +87 -0
- package/src/auto-archive.js +9 -2
- package/src/client/index.jsx +135 -11
- package/src/index.js +42 -4
package/README.en.md
CHANGED
|
@@ -20,7 +20,7 @@ A persistent DSH plugin (host + browser halves) covering both the **settings pan
|
|
|
20
20
|
|
|
21
21
|
- **Unified panel**: three always-visible top-level views — **All / Active / Archived** — while the low-frequency ones (**Starred / Empty / Recycle bin**) are tucked behind an arrow button at the end of the filter row and slide out horizontally on click (they stay expanded automatically while a low-frequency view is selected). Search across title, session ID, and workspace, plus workspace filters and creation-time/title sorting. The result line is a single sentence such as "Showing 5 sessions, 4 more subagents folded under their parents". A maintenance row below the filters holds the **Storage usage**, **Manage tags** and **Auto-archive** tools, which expand on demand instead of taking up a view.
|
|
22
22
|
- **Starred sessions**: an always-visible star button on each session row toggles starring optimistically with rollback on failure; the Starred view is orthogonal to DSH's active/archived states and they stack. The star index is plugin-owned (schema v3) and never touches DSH logs; stars are cleaned up automatically when a session is permanently purged.
|
|
23
|
-
- **Tags & saved filters**: label sessions with custom tags (up to 200 global, 10 per session; rename, merge, delete — **deleting a tag never touches the sessions**), filter the list by tag, and save the current "view + workspace + sort + tag" combination as a preset (up to 20) for one-click reuse. Tags and presets live in plugin-owned indexes (same discipline as the star index) inside this machine's DSH data directory — they do not follow your account
|
|
23
|
+
- **Tags & saved filters**: label sessions with custom tags (up to 200 global, 10 per session; rename, merge, delete — **deleting a tag never touches the sessions**), filter the list by tag, and save the current "view + workspace + sort + tag" combination as a preset (up to 20) for one-click reuse. Tags and presets live in plugin-owned indexes (same discipline as the star index) inside this machine's DSH data directory — they do not follow your account.
|
|
24
24
|
- **Cold-title synchronization**: the sidebar is corrected from the latest log-backed `session/title`, so renamed cold sessions no longer need to be opened before showing their current name. On cold starts of large stores a title that is not ready yet simply renders blank while it fills in the background — and once it arrives, **rows already on screen and the “⑂ branch” chip's tooltip update automatically**, no page rebuild needed.
|
|
25
25
|
- **Sidebar workspace drag-and-drop**: drag a session onto a workspace heading to move it, with a highlighted drop target, same-workspace protection, failure feedback, and the More → Move session menu retained for keyboard access.
|
|
26
26
|
- **Archive / Restore**: archive hides a session from the sidebar; restore unarchives it and puts it back into its original workspace group.
|
|
@@ -83,13 +83,15 @@ So held-open sessions get **deferred (queued) moves**:
|
|
|
83
83
|
- **Results are no longer silent**: a background completion or a final give-up (5 consecutive non-lock failures) becomes a persisted notice, surfaced as a toast the next time a DSH page is open (give-ups include the reason). Notices stay on the server until acknowledged (up to 20, at most 7 days) — closing the page before the toast never loses one.
|
|
84
84
|
- **To actually move an active session**: request the move (it queues), then **restart DSH and don't open that session first** — it completes a few seconds after startup. Pending entries survive restarts.
|
|
85
85
|
|
|
86
|
-
### Session
|
|
86
|
+
### Session formats v3 / v4 (DSH 0.1.5+ / 0.1.7+)
|
|
87
87
|
|
|
88
|
-
DSH 0.1.5 upgrades the session log format to **v3**; the runtime performs the migration itself while reading:
|
|
88
|
+
DSH 0.1.5 upgrades the session log format to **v3**, and 0.1.7 upgrades it again to **v4**; the runtime performs the migration itself while reading:
|
|
89
89
|
|
|
90
|
-
- Migration is **automatic** and **keeps the original files** — so one session directory legitimately holds
|
|
91
|
-
- Migration is **one-way, with no downgrade read**: after
|
|
92
|
-
- Only **supported** old logs are migrated; unknown events
|
|
90
|
+
- Migration is **automatic** and **keeps the original files** — so one session directory legitimately holds multiple generations side by side (`session.v2.jsonl.zstd` next to higher generations). This plugin always reads the **highest generation**, matching the runtime.
|
|
91
|
+
- Migration is **one-way, with no downgrade read**: after opening sessions on a newer runtime, rolling the runtime back makes those sessions unreadable. Back up `~/.dsh/sessions` before a rollback.
|
|
92
|
+
- Only **supported** old logs are migrated; unknown events or corrupted data are **refused without any repair**. Such sessions surface as `SessionFormatUnsupportedMigrationError` / `SessionFormatError` on read/move — the plugin cannot repair them and reports the error verbatim.
|
|
93
|
+
- **Since 0.1.7 newly created sessions strictly require a v4 header**: from v3.7.2 this plugin re-samples the session header against the current backend's format during cross-workspace moves — sessions created in older formats (v3 and earlier) can be moved again, and the relocated copy lands directly on the current format (verified end-to-end against a real v3 log). Earlier plugin versions fail to move such sessions on 0.1.7.
|
|
94
|
+
- **0.2.0 is still v4**: the current format version is **probed dynamically** from the backend's format catalog package (never hardcoded), so 0.1.7 and 0.2.0 share one code path.
|
|
93
95
|
|
|
94
96
|
## Screenshots
|
|
95
97
|
|
|
@@ -209,7 +211,14 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
209
211
|
- Peer dependencies are listed in `package.json`; `react` and `@deepseek-ai/*` are provided by the DSH runtime.
|
|
210
212
|
- `0.1.2-rc.1`: the existing read, archive, recycle-bin, permanent-purge, and cross-workspace move paths remain available.
|
|
211
213
|
- `0.1.3-alpha.1`: snapshot lists and chunked `SessionHandle` read flows are supported. Permanent purge and cross-workspace move are available through guarded path derivation + write-ownership probes (see the behavior notes below), and disable themselves whenever a safe path cannot be verified; other management capabilities are unaffected. The panel shows the effective capabilities.
|
|
212
|
-
- `0.1.5-rc.1` / `0.1.5-rc.2` (
|
|
214
|
+
- `0.1.5-rc.1` / `0.1.5-rc.2` (both verified): the session log format is upgraded to **v3** and this plugin is adapted — it reads the **highest generation** (matching the runtime), tolerates v0/v2/v3 generations side by side, follows the new `SessionHandle.read()` shape (`{ eventState, events }`), moves sessions as **whole-directory moves** (reclaiming superseded-generation copies automatically), and queues moves for sessions DSH holds open (see "Moving an active session" above).
|
|
215
|
+
- `0.1.7-rc.2` (verified): the session format is upgraded again to **v4** and the format catalog moves to its own package; this plugin is adapted — reads go through the official automatic migration, and cross-workspace moves re-sample the header against the backend's current format (sessions created on older formats can be moved again, and the relocated copy lands on the current format).
|
|
216
|
+
- `0.2.0-rc.1` (verified):
|
|
217
|
+
- Still format **v4** (the version is probed from the backend's format catalog, never hardcoded).
|
|
218
|
+
- `ctx.sessions` is now a session **data-stream** service with no `open`: opening a session goes through the official `uiWorkspace.openSession` — the settings panel's "open session" action and subagent navigation are both adapted (issue #12).
|
|
219
|
+
- Install gate: 0.2.0 validates the `@deepseek-ai/dsh*` ranges in a plugin's `peerDependencies`; this plugin widened them to `>=0.1.0-rc.6`, which passes on both `0.1.7-rc.2` and `0.2.0-rc.1` (issue #13).
|
|
220
|
+
- `ctx.storageDomain` is the **domain data form** in 0.2.0: writes are schema-validated and durable before they resolve. This plugin only writes fields that exist in the official domain schema, verified end-to-end against a **real domain + real WorkspaceRegistry** (archive / restore / list / storage stats / cross-workspace move) with a read-back from the storage medium to prove the archive state really persisted.
|
|
221
|
+
- Active-session detection: since 0.1.7 the host exposes no "current session" accessor (all three historical probes come back empty), so the **client reports the open session id every 15 seconds** and the host trusts it only within a 45-second freshness window. It backs two guards: refusing to move the session you are looking at (409 / queued) and skipping it in auto-archive. A stopped heartbeat (panel closed, tab hidden, process gone) means "cannot prove it is active" — the move is allowed rather than locking the session forever.
|
|
213
222
|
- Unverified future runtimes expose only capabilities the plugin can safely identify; method presence alone is not presented as behavioral compatibility.
|
|
214
223
|
|
|
215
224
|
### Behavior differences and degradations on the SessionHandle era (`0.1.3+`)
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
- **统一面板**:顶部提供「全部 / 活动 / 已归档」三个常驻视图,低频的「已收藏 / 空白 / 回收站」收进行尾的箭头按钮,点按横向滑出展开(低频视图处于选中态时自动保持展开);支持按标题、会话 ID、工作区搜索,按工作区筛选,并按创建时间或标题排序。结果行合并为一句说明,如「显示 5 个会话,另有 4 个子代理折叠在父会话下」。筛选栏下方的维护栏收纳「存储占用」「标签管理」「自动归档」等按需展开的面板级工具,不占用视图位置。
|
|
22
22
|
- **收藏(星标)**:会话行左侧常驻星标按钮,单击即收藏 / 取消(乐观更新、失败回滚);「已收藏」视图与 DSH 的活动 / 归档状态正交、可叠加;收藏索引为插件自有 schema v3,不触碰 DSH 日志,会话被彻底删除时自动清理。
|
|
23
|
-
- **标签与保存筛选**:给会话贴自定义标签(全局 ≤200、单会话 ≤10;支持重命名、合并、删除——**删除标签只删标签,绝不触碰会话**),并按标签筛选列表;「视图 + 工作区 + 排序 + 标签」的组合可保存为筛选预设(≤20 条)一键应用。标签与预设存在插件自有索引里(同星标索引纪律),跟随这台机器的 DSH
|
|
23
|
+
- **标签与保存筛选**:给会话贴自定义标签(全局 ≤200、单会话 ≤10;支持重命名、合并、删除——**删除标签只删标签,绝不触碰会话**),并按标签筛选列表;「视图 + 工作区 + 排序 + 标签」的组合可保存为筛选预设(≤20 条)一键应用。标签与预设存在插件自有索引里(同星标索引纪律),跟随这台机器的 DSH 数据目录、不跟账号走。
|
|
24
24
|
- **冷态标题同步**:侧栏使用日志中最新的 `session/title` 修正冷启动缓存,改名后的会话无需先打开即可显示新名称;冷启动大库里标题先留空、后台分批补齐,补齐后**已经在屏幕上的侧栏行和「⑂ 分支」标记的悬停说明会跟着自动更新**,不必等页面重建。
|
|
25
25
|
- **侧栏跨工作区拖拽**:直接把会话拖到目标工作区标题即可切换工作区;目标高亮、同工作区拦截、失败反馈,并保留“更多 → 移动会话”作为键盘操作入口。
|
|
26
26
|
- **归档 / 恢复**:归档把会话从侧栏隐藏;恢复取消归档并放回原工作区分组。
|
|
@@ -83,13 +83,15 @@ DSH(0.1.5 起)给每个会话日志加了**单写者锁**:会话一旦被
|
|
|
83
83
|
- **结果不再静默**:后台完成或最终放弃(连续 5 次非占用失败)会落成通知,下次打开 DSH 页面时以提示弹出(放弃类附原因)。通知在服务端保留到确认展示(最多 20 条、最长 7 天),没弹就不会丢。
|
|
84
84
|
- **要真正搬走一个活跃会话**:先发起移动(进队列),再**重启 DSH 并先别打开那个会话**——启动后几秒内会自动完成。未完成的项跨重启保留,不会丢。
|
|
85
85
|
|
|
86
|
-
### 关于会话格式 v3(DSH 0.1.5+)
|
|
86
|
+
### 关于会话格式 v3 / v4(DSH 0.1.5+ / 0.1.7+)
|
|
87
87
|
|
|
88
|
-
DSH 0.1.5 把会话日志格式升级到 **v3
|
|
88
|
+
DSH 0.1.5 把会话日志格式升级到 **v3**,0.1.7 再升到 **v4**,均由 runtime 自己在读取时完成迁移:
|
|
89
89
|
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
90
|
+
- 迁移是**自动**的,且**保留原文件**——所以同一个会话目录里可能同时出现 `session.v2.jsonl.zstd` 与更高代的 `session.vN.jsonl.zstd`,属官方预期行为;本插件始终按**最高代**读取,与官方一致。
|
|
91
|
+
- 迁移**单向、不支持降级读取**:升级并打开过会话之后,把 runtime 回滚到旧版本会导致那些会话无法读取。回滚前请先备份 `~/.dsh/sessions`。
|
|
92
|
+
- 官方只迁移**受支持的旧日志**;遇到未知事件或损坏数据会**拒绝迁移且不做任何修复**。这类会话在插件里会表现为读取/移动报 `SessionFormatUnsupportedMigrationError` / `SessionFormatError`——插件无法代修,只能如实报错(可先确认该会话是否确实异常,再决定保留或删除)。
|
|
93
|
+
- **0.1.7 起新建会话严格要求 v4 头部**:本插件自 v3.7.2 起在跨工作区移动时自动按当前后端的格式重新采样会话头——旧格式(v3 及更早)会话可以正常移动,且移动后的副本直接落在当前格式(已用真实 v3 旧日志端到端实测)。更早的插件版本在 0.1.7 上移动旧格式会话会失败。
|
|
94
|
+
- **0.2.0 仍是 v4**:当前格式版本由插件从后端的格式目录包**动态探测**(不写死版本号),所以 0.1.7 / 0.2.0 共用同一套处理。
|
|
93
95
|
|
|
94
96
|
## 截图
|
|
95
97
|
|
|
@@ -209,8 +211,15 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
209
211
|
- peerDependencies 见 `package.json`;`react`、`@deepseek-ai/*` 由 DSH 运行时提供。
|
|
210
212
|
- `0.1.2-rc.1`:现有读取、归档、回收站、永久删除和跨工作区移动能力保持可用。
|
|
211
213
|
- `0.1.3-alpha.1`:支持 snapshot 列表和 `SessionHandle` 只读流程;永久删除与跨工作区移动经由守卫式路径推导 + 写所有权探测开放(见下文),无法安全验证时自动禁用,其余管理能力不受影响。能力以面板实际提示为准。
|
|
212
|
-
- `0.1.5-rc.1` / `0.1.5-rc.2
|
|
214
|
+
- `0.1.5-rc.1` / `0.1.5-rc.2`(已实测):会话日志格式升级到 **v3**,本插件已适配——按**最高代**读取(与官方一致)、v0/v2/v3 多代共存、跟随 `SessionHandle.read()` 的新返回结构
|
|
213
215
|
(`{ eventState, events }`)、跨工作区移动按**整目录搬运**并自动回收被取代的旧代副本;被 DSH 打开着的会话改为**排队延迟移动**(见上文「移动活跃会话」)。
|
|
216
|
+
- `0.1.7-rc.2`(已实测):会话格式再升 **v4**,格式版本目录拆分到独立包;本插件已适配——读取走官方自动迁移,跨工作区移动自动按后端当前格式重采样会话头(旧格式会话可正常移动,落盘即当前格式)。
|
|
217
|
+
- `0.2.0-rc.1`(已实测):
|
|
218
|
+
- 会话格式仍为 **v4**(版本由后端格式目录包动态探测,不写死)。
|
|
219
|
+
- `ctx.sessions` 已退化为会话**数据流**服务(不再提供 `open`):打开会话改走官方 `uiWorkspace.openSession`——设置面板与侧栏的「打开会话」、子代理导航均已适配(issue #12)。
|
|
220
|
+
- 安装门禁:0.2.0 会校验插件 `peerDependencies` 中的 `@deepseek-ai/dsh*` 版本范围,本插件已放宽为 `>=0.1.0-rc.6`,`0.1.7-rc.2` 与 `0.2.0-rc.1` 双双通过(issue #13)。
|
|
221
|
+
- 存储域(`ctx.storageDomain`)在 0.2.0 是**领域数据形式**:写入经 schema 校验并在返回前落盘。本插件只写官方域 schema 内的字段,已用**真实域 + 真实 WorkspaceRegistry** 端到端实测(归档 / 恢复 / 列表 / 存储统计 / 跨工作区移动),并回到磁盘介质核验归档状态真的持久化。
|
|
222
|
+
- 活跃会话判定:0.1.7 起 host 侧不再有「当前会话」访问器(三个历史探测全部落空),改由**客户端每 15 秒上报**当前会话 id、host 只在 45 秒新鲜期内采信;用于「移动正在看的会话」(409 / 排队)与「自动归档跳过当前会话」两条保护。心跳停止(面板关闭、标签页隐藏、进程退出)即视为无法证明活跃,放行移动而不是把会话永久锁死。
|
|
214
223
|
- 未经验证的未来 Runtime 默认只开放能够识别的安全能力;插件不会用方法存在与否冒充行为兼容。
|
|
215
224
|
|
|
216
225
|
### 新版 Runtime(SessionHandle 世代,`0.1.3+`)的行为差异与降级说明
|
package/lib/client.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
apply: () => apply,
|
|
34
|
+
dsmActiveSessionId: () => dsmActiveSessionId,
|
|
34
35
|
inject: () => inject
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -543,7 +544,39 @@ async function postJSON(path, body) {
|
|
|
543
544
|
var dsmClientCtx = null;
|
|
544
545
|
function dsmSessionsService() {
|
|
545
546
|
try {
|
|
546
|
-
|
|
547
|
+
if (!dsmClientCtx || typeof dsmClientCtx.get !== "function") return null;
|
|
548
|
+
const native = dsmClientCtx.get("sessions");
|
|
549
|
+
if (native && (typeof native.open === "function" || typeof native.openSubagent === "function")) return native;
|
|
550
|
+
const ui = dsmClientCtx.get("uiWorkspace");
|
|
551
|
+
if (!ui || typeof ui.openSession !== "function") return native;
|
|
552
|
+
let lastOpened = null;
|
|
553
|
+
const bridge = {
|
|
554
|
+
open(id) {
|
|
555
|
+
if (id === null || id === void 0) return;
|
|
556
|
+
const target = String(id);
|
|
557
|
+
ui.openSession(target);
|
|
558
|
+
lastOpened = target;
|
|
559
|
+
},
|
|
560
|
+
openSubagent(address) {
|
|
561
|
+
if (address === null || address === void 0) return;
|
|
562
|
+
if (typeof address === "object") {
|
|
563
|
+
ui.openSession(address);
|
|
564
|
+
lastOpened = String(address.childSessionId);
|
|
565
|
+
} else {
|
|
566
|
+
bridge.open(address);
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
// 新核心的子代理目录由投影自动维护,无需手动刷新。
|
|
570
|
+
refreshSubagents() {
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
Object.defineProperty(bridge, "list", {
|
|
574
|
+
configurable: true,
|
|
575
|
+
get() {
|
|
576
|
+
return { getSnapshot: () => ({ current: lastOpened }) };
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
return bridge;
|
|
547
580
|
} catch (e) {
|
|
548
581
|
return null;
|
|
549
582
|
}
|
|
@@ -582,6 +615,49 @@ async function dsmOpenSessionById(childId, directParentId) {
|
|
|
582
615
|
}
|
|
583
616
|
return "failed";
|
|
584
617
|
}
|
|
618
|
+
var DSM_CLIENT_ID = (() => {
|
|
619
|
+
try {
|
|
620
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
621
|
+
} catch (e) {
|
|
622
|
+
}
|
|
623
|
+
return `c${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
624
|
+
})();
|
|
625
|
+
function dsmActiveSessionId() {
|
|
626
|
+
try {
|
|
627
|
+
if (!dsmClientCtx || typeof dsmClientCtx.get !== "function") return null;
|
|
628
|
+
const ui = dsmClientCtx.get("uiWorkspace");
|
|
629
|
+
if (ui) {
|
|
630
|
+
const snap = ui.selection && typeof ui.selection.getSnapshot === "function" ? ui.selection.getSnapshot() : null;
|
|
631
|
+
if (snap && snap.sessionId != null) return String(snap.sessionId);
|
|
632
|
+
const ref = ui.mainReference;
|
|
633
|
+
if (ref && ref.sessionId != null) return String(ref.sessionId);
|
|
634
|
+
}
|
|
635
|
+
const native = dsmClientCtx.get("sessions");
|
|
636
|
+
if (native && native.list && typeof native.list.getSnapshot === "function") {
|
|
637
|
+
const snap = native.list.getSnapshot();
|
|
638
|
+
if (snap && snap.current != null) return String(snap.current);
|
|
639
|
+
}
|
|
640
|
+
} catch (e) {
|
|
641
|
+
}
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
var DSM_ACTIVE_HEARTBEAT_MS = 15e3;
|
|
645
|
+
var DSM_ACTIVE_MIN_REPORT_MS = 3e4;
|
|
646
|
+
var dsmLastActiveReport = { id: null, at: 0 };
|
|
647
|
+
async function reportActiveSession(force) {
|
|
648
|
+
const id = dsmActiveSessionId();
|
|
649
|
+
const now = Date.now();
|
|
650
|
+
if (!force && id === dsmLastActiveReport.id && now - dsmLastActiveReport.at < DSM_ACTIVE_MIN_REPORT_MS) return;
|
|
651
|
+
dsmLastActiveReport = { id, at: now };
|
|
652
|
+
try {
|
|
653
|
+
await postJSON("/archived-sessions/active-session", { clientId: DSM_CLIENT_ID, sessionId: id });
|
|
654
|
+
} catch (e) {
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
async function postActiveAware(path, body) {
|
|
658
|
+
await reportActiveSession(true).catch(() => null);
|
|
659
|
+
return postJSON(path, body);
|
|
660
|
+
}
|
|
585
661
|
var NAV_CSS = `
|
|
586
662
|
[data-dsh-nav-sessions] > svg:first-child { display: none; }
|
|
587
663
|
[data-dsh-nav-sessions]::before {
|
|
@@ -1460,7 +1536,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1460
1536
|
}
|
|
1461
1537
|
setBusy(it.sessionId);
|
|
1462
1538
|
setError(null);
|
|
1463
|
-
|
|
1539
|
+
postActiveAware("/archived-sessions/move", { sessionId: it.sessionId, targetPath }).then((r) => {
|
|
1464
1540
|
setBusy(null);
|
|
1465
1541
|
setOpenMove(null);
|
|
1466
1542
|
setMoveMode("existing");
|
|
@@ -1517,7 +1593,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1517
1593
|
setBusy("__batch__");
|
|
1518
1594
|
setError(null);
|
|
1519
1595
|
setRetry(null);
|
|
1520
|
-
|
|
1596
|
+
postActiveAware("/archived-sessions/move-many", { sessionIds: ids, targetPath }).then((r) => {
|
|
1521
1597
|
setBusy(null);
|
|
1522
1598
|
setBatchMoveOpen(false);
|
|
1523
1599
|
setMoveMode("existing");
|
|
@@ -3037,7 +3113,7 @@ function installSidebarSessionMenuAug() {
|
|
|
3037
3113
|
e.preventDefault();
|
|
3038
3114
|
closeSub();
|
|
3039
3115
|
closeMenu();
|
|
3040
|
-
|
|
3116
|
+
postActiveAware("/archived-sessions/move", { sessionId: info.id, targetPath: w.path }).then((r2) => {
|
|
3041
3117
|
const label = w.title || pathName(w.path) || "\u76EE\u6807\u5DE5\u4F5C\u533A";
|
|
3042
3118
|
if (r2 && r2.queued) {
|
|
3043
3119
|
toast(r2.notes && r2.notes.length ? r2.notes.join(" ") : "\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\uFF0C\u5DF2\u6392\u961F\u5F85\u79FB\u52A8\uFF1B\u91CD\u542F DSH \u540E\u4F1A\u81EA\u52A8\u5B8C\u6210\uFF08\u8BF7\u5148\u522B\u6253\u5F00\u5B83\uFF09\u3002");
|
|
@@ -3736,7 +3812,7 @@ function installSidebarWorkspaceDrag() {
|
|
|
3736
3812
|
clearVisuals();
|
|
3737
3813
|
row.classList.add("dsm-drag-busy");
|
|
3738
3814
|
try {
|
|
3739
|
-
const result = await
|
|
3815
|
+
const result = await postActiveAware("/archived-sessions/move", { sessionId: item.sessionId, targetPath: target.path });
|
|
3740
3816
|
if (result && result.queued) {
|
|
3741
3817
|
await dsmLoadTrashIds();
|
|
3742
3818
|
toast(result.notes && result.notes.length ? result.notes.join(" ") : "\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\uFF0C\u5DF2\u6392\u961F\u5F85\u79FB\u52A8\uFF1B\u91CD\u542F DSH \u540E\u4F1A\u81EA\u52A8\u5B8C\u6210\uFF08\u8BF7\u5148\u522B\u6253\u5F00\u5B83\uFF09\u3002");
|
|
@@ -3792,6 +3868,23 @@ function installSidebarWorkspaceDrag() {
|
|
|
3792
3868
|
function apply(ctx) {
|
|
3793
3869
|
dsmClientCtx = ctx;
|
|
3794
3870
|
dsmLoadCapabilities();
|
|
3871
|
+
const beat = () => {
|
|
3872
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
3873
|
+
reportActiveSession(false).catch(() => {
|
|
3874
|
+
});
|
|
3875
|
+
};
|
|
3876
|
+
beat();
|
|
3877
|
+
const beatTimer = setInterval(beat, DSM_ACTIVE_HEARTBEAT_MS);
|
|
3878
|
+
const beatAc = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
3879
|
+
if (beatAc && typeof document !== "undefined" && document.addEventListener) {
|
|
3880
|
+
document.addEventListener("visibilitychange", () => {
|
|
3881
|
+
if (!document.hidden) beat();
|
|
3882
|
+
}, { signal: beatAc.signal });
|
|
3883
|
+
}
|
|
3884
|
+
ctx.effect(() => () => {
|
|
3885
|
+
clearInterval(beatTimer);
|
|
3886
|
+
if (beatAc) beatAc.abort();
|
|
3887
|
+
});
|
|
3795
3888
|
installSettingsNavIcons(ctx);
|
|
3796
3889
|
installSidebarSessionMenuAug();
|
|
3797
3890
|
const disposeSidebarDots = installSidebarStatusDots();
|