dsh-session-menu
DSH bundle: session context-menu extras - export log, reveal workspace folder, mark unread, permanent delete.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 21, 2026
- Updated
- Oct 21, 2026
Introduction
会话菜单增强(Session menu extras)
English
Session menu extras — Adds export log, reveal workspace folder, mark-as-unread and permanent delete to a DSH session's context menu, plus an unread dot on the row.
A DSH bundle (DeepSeek Harness plugin).
Install from the DSH plugin market
-
Open Plugins in the DSH Web sidebar, select More next to Experimental plugins, and switch on Custom plugin markets.
-
Open the Discover tab and add this index source:
https://silentforfar.github.io/dsh-market-index/index.json -
Find Session menu extras (
@silentforfar/dsh-session-menu) in the list and press install.
Install straight from this repository
Pass this spec to plugin_manager's install_bundle (or the equivalent bundle install in your profile):
git+https://github.com/Silentforfar/dsh-session-menu.git#<commit-sha>
Use a full 40-character commit SHA from this repository's history, so the install is pinned and reproducible.
Requirements. Desktop (Windows/macOS/Linux) build of DSH 0.2.1-rc.2 or newer. The permanent-delete row moves the session log into <DSH_HOME>/trash/sessions/ instead of unlinking it.
License. MIT. The rest of this README documents the plugin in Chinese.
给侧栏会话行的「…」菜单补充几项动作。
| 菜单项 | 行为 |
|---|---|
| 导出记录 | 下载 dsh-session-<id>.zip(会话 + 子会话 + 附件),走 Host 的 /api/session.export |
| 打开工作目录 | 在资源管理器里定位这个会话的 cwd(仅桌面端、且会话有目录时出现) |
| 标记为未读 / 标记为已读 | 本机未读标记,未读会话标题前显示小圆点;切到该会话后自动清除 |
| 永久删除 | 二次确认后把会话日志目录移进 DSH 回收站,列表里当场消失 |
右键打开菜单
在侧栏的会话行上点右键,会打开这一行自己的「…」菜单(和点右侧省略号完全一样的气泡)。
实现方式:DSH 没有给行提供右键菜单的扩展点,所以插件在 document 的捕获阶段监听 contextmenu,
命中 [data-row-key^="session:"] 的行时阻止默认菜单,并点一下该行自己的菜单按钮——
复用宿主菜单的全部内容(含本插件新增的行),菜单出现在省略号的位置,而不是鼠标处。
空白的新建会话行、工作区行和其它区域不拦截,保持浏览器/宿主默认行为。
会话列表永远展开
工作区里的会话默认只显示 5 个,多出来的折叠成「展开其余 N 个会话 / 收起」。 本插件让它始终全部展开、并且不显示「收起」:
- 侧栏把每组的可见条数存在组件内部的 watermark 里(没有对外接口),
所以插件用
MutationObserver盯住button[data-row-key^="overflow:"][aria-expanded="false"], 一出现就点它,直到该组全部展开(观察回调在绘制前运行,正常看不到闪烁); - 展开后宿主会把同一个按钮变成「收起」,插件用一条样式把它隐藏:
button[data-row-key^="overflow:"][aria-expanded="true"]{display:none!important}。
注意:watermark 是组件状态,切换面板/折叠侧栏导致会话树重挂载时会回到默认值, 观察器会立刻再展开一次(可能看到一次 300ms 的展开动画)。
分叉来源与同名副本标记
DSH 的「在新对话中分支 / 分叉会话」会把父会话到那一刻为止的整段事件历史复制进新会话
(buildForkSeed),新会话在列表里却和普通会话长得一模一样。本插件在会话标题后面补两个小胶囊:
| 胶囊 | 出现条件 | 含义 |
|---|---|---|
⤷ 分叉自 <父标题>(窄栏只显示 ⤷) | 该会话的 parentId 存在(Host 的 parentSessionId)且不是子代理 | 这个会话是从哪个会话分叉出来的;悬停提示说明分叉复制历史 |
同名 ×N(窄栏只显示 ×N) | 有 N(>1)个非空会话共用同一个标题 | 常见于分叉副本;悬停提示说明它们各写各的日志 |
实现要点:
- 行组件只在标题前提供了一个 16px 的 leading 位(被状态点/未读点占用),
没有标题旁的扩展位,所以标记是插进行 DOM 的:找到
[data-row-key^="session:"], 在第二个子节点(标题span)后面插入.dsm-badges; - 标题优先:整组最多占行宽的 42%;行宽小于 360px 时只显示
⤷/×N的紧凑形态, 完整文案放在title悬停提示里;只有把侧栏拉宽(行宽 ≥ 360px)才展开⤷ 分叉自 …全文; - React 重渲染会把行结构刷新,插件用
MutationObserver(120ms 节流)对账: 标记文本没变就不动 DOM,变了才重写,行消失或不再满足条件就删掉; - 数据来自客户端会话列表
ctx.sessions.list.getSnapshot().byId[id].parentId(Hostapi-session.list的parentSessionId),不额外发请求; - 子代理会话(
origin === 'subagent')不显示分叉胶囊,避免与宿主自己的层级展示重复。
永久删除怎么实现的
DSH 的会话持久化接口故意没有删除 API(见 dsh-session-persistence 的 “No deletion or retention API”),
所以这一项是带外维护:
- 客户端菜单行
/purge-session <session-id>(会话 ID 由命令参数给出,不是当前会话); - 如果该会话还有活着的 Agent,直接拒绝(文件被占用时一样会失败并提示);
- 在持久化根目录里按会话 ID 的编码找到
<root>/<project>/<encoded-id>/,移动到%USERPROFILE%\.dsh\trash\sessions/<时间戳>-<id>/; - 顺带把投影缓存
%USERPROFILE%\.dsh\storages\session_projcache\sessions/<id>.json也移进回收站; - 向客户端发一条
api-session/removed(Host 在会话销毁时用的同一条事件), 侧栏当场移除这一行,不需要刷新页面。
要点:
- 是移动不是删除,事后可以从回收站目录手工恢复;
- 删除完成后侧栏立刻移除该行(Host 发出
api-session/removed),不用刷新页面; - 子会话不会级联删除,会变成孤儿;父会话不会被改写;
- 如果改过持久化根目录,需要在插件配置里加
sessionRoot,否则会报“没有找到日志目录”; - 附件对象是内容寻址、可能被多个会话共享,不会被回收。
命令本身也可以直接在输入框敲:
/purge-session session-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
为什么没有「在新窗口中打开 / 移动到分组 / 开始侧边任务」
- 在新窗口中打开:DSH 的 Web 端没有会话深链,新窗口只会打开空白首页。
- 移动到分组:分组就是工作区,按 cwd 归属,没有把单个会话挪到别的工作区的接口。
- 开始侧边任务:右侧栏是浏览器/终端/文档面板,没有「侧边会话」这种对象。
已有的菜单项(不重复实现)
置顶(100)、重命名(200)、分叉会话(300)、归档(400)、复制 Session ID(450); 本插件按 460 / 470 / 500 / 600 插在它们后面。
结构
index.js Host 端:/purge-session 命令
purge.js 会话目录定位与移入回收站(可单测)
client.js 四个菜单行 + 未读圆点
locale/zh.json Plugins 页展示的标题与描述
locale/en.json
icon.svg
cordis.patch.yml 插入插件行