@whybebabo/dsh-better-workspace
在原生工作区列表之上叠加主题分组:emoji 分组文件夹、筛选与搜索、批量归档,以及带安全边界的永久删除。
安装
dsh plugin --profile web add @whybebabo/dsh-better-workspace
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-better-workspace
[English](./README.en.md) | 简体中文
dsh-better-workspace 是 DeepSeek Harness 的侧边栏主题分组插件。它叠加在原生"工作区"区域之上,不替换官方列表:会话仍由宿主管理,插件只在这块区域上重新组织显示,并补上宿主没有的批量操作与安全删除。
有了它,散落在各个工作区里的会话可以按自己的习惯归类:
- 主题分组:每个工作区内部是一棵树,用 emoji 标记的"分组"文件夹收纳会话,另有系统默认分组与「已归档」桶;
- 筛选与搜索:工作区下拉筛选、面板内本地搜索、「只看此分组」;
- 批量操作:归档 / 取消归档 / 移动到分组 / 永久删除,在选择模式下由标题栏一行原生图标按钮承载;
- 安全删除:DSH 本身没有删除会话的命令,插件自行实现,规则是"宁可拒删"(见[永久删除的安全边界](#永久删除的安全边界));
- 与原生一致:状态点、置顶上浮、行内悬浮卡、行尾操作条、定时任务时钟、图标与配色,都对齐宿主自己的实现。
插件不会接管 DSH 的其余部分:侧栏折叠、窄视口或路由不可达时,面板会完全隐藏并交还原生 UI(见[失效保护](#覆盖策略与失效保护))。
界面预览
工作方式
- 叠加而非替换:面板渲染在
shell.overlay,用结构选择器定位原生工作区区域,覆盖矩形与该区域自身盒子重合。被覆盖区域加上inert+aria-hidden,官方弹窗出现时自动释放。 - 主题数据单独存放:分组与归属关系写在
<DSH_HOME>/dsh-better-workspace/topics.v1.json这个单文件 sidecar 里,可直接备份、可与宿主升级解耦;卸载插件后数据仍在。 - 顺序分两种:分组顺序写入 sidecar(跨窗口共享);工作区与会话的拖拽顺序存在本地偏好
localStorage。 - 全部走官方命令:打开会话、归档、置顶、分叉、建工作区都调用宿主
uiWorkspace/workspaces服务,重命名会话走宿主自己的sessions.using(...)(0.2.1 的UiWorkspace没有这个方法,官方改名对话框也是这么做的);插件不声明、不借用其它包的插槽。
安装
环境要求
- DeepSeek Harness Desktop 或 Web
0.2.1-rc.x,@deepseek-ai/cordis4.0.5; - Node.js ≥ 22.19;
- 本机可用 Git(以
github:方式安装时)。
从 npm 安装(推荐)
dsh plugin --profile web add @whybebabo/dsh-better-workspace
DSH 会把插件装进 Web profile,并把 @whybebabo/dsh-better-workspace 注册到 dsh.profile.bundles,不需要克隆仓库、手动安装依赖或构建客户端。
没有全局 dsh 命令时,用 npx 执行:
npx @deepseek-ai/dsh plugin --profile web add @whybebabo/dsh-better-workspace
安装命令需要本机可以使用 Git 与 pnpm;使用 npx 时还需要 Node.js。
Desktop 会修改 desktop profile,装之前建议先备份 cordis.patch.yml / package.json / pnpm-lock.yaml:
dsh plugin --profile desktop add @whybebabo/dsh-better-workspace
从 GitHub 安装
不经过 npm,直接从仓库安装某个发布版本:
dsh plugin --profile web add github:whybebabo/dsh-better-workspace#v0.1.10
把 #v0.1.10 换成你要的 tag。这条路径同样需要 Git 与 pnpm,也不需要克隆仓库。
从源码安装
需要改代码或跟 main 分支时:
git clone https://github.com/whybebabo/dsh-better-workspace.git cd dsh-better-workspace npm install && npm run build
先在不影响现有 profile 的隔离环境里验证一遍:
dsh --profile topic-e2e --from-default-profile web --dump-config dsh plugin --profile topic-e2e add "link:$(pwd)" dsh --profile topic-e2e --no-open --port 8791 # 打印带 token 的 URL
确认无误后装到日常使用的 profile:
dsh plugin --profile web add "link:$(pwd)"
以 link: 方式安装时,宿主读取的始终是当前目录的产物:改完代码 npm run build 并重启即可,不必重新 add。
启动或重启 DSH
安装后关闭旧的 DSH Web 进程并重新启动:
dsh web
使用 npx 的启动方式:
npx @deepseek-ai/dsh web
打开侧栏,点「工作区」标题旁出现的 ▾,或把鼠标移到「工作区」上——看到工作区下拉与分组树,说明插件已加载。
升级
发布新版本后重新执行安装命令并替换版本:
dsh plugin --profile web add @whybebabo/dsh-better-workspace
随后重启 DSH。以源码方式安装的则用:
git pull && npm install && npm run build
卸载
dsh plugin --profile web remove @whybebabo/dsh-better-workspace
主题数据保留在 <DSH_HOME>/dsh-better-workspace/,需要清除请手动删除该目录。
验证安装
插件在宿主上注册了只读的 GET /state。浏览器访问(端口换成你实际使用的):
Invoke-WebRequest 'http://127.0.0.1:3080/dsh-better-workspace/api/state' | Select-Object StatusCode
预期状态码 200,返回体里 ok: true、result.workspaces 是你的分组数据。
该路由只接受本机回环 + 同源请求,用别的机器访问会拿到 403,这不是故障。
用法
分组
- 新建分组:工作区
⋯菜单,或标题栏 + 菜单里的「新建主题并放入…」;也可以把会话拖到目标分组来实现归类。新分组默认排在该工作区分组列表的最上面,创建后不用再滚到底部去找。从工作区⋯菜单创建时,所属工作区会自动展开,新分组滚进视野并短暂高亮——即使之前折叠了这个工作区,也不会出现「建完却找不到」。新建会话界面的分组选择栏(输入框上方的分组 chip)里新建分组,则会把当前会话直接归入新分组,chip 立即显示它。 - 新建会话:分组行尾的 + 直接在该分组下新建会话(工作区行尾的 + 则在该工作区新建)。
- 改名:双击分组名直接内联编辑(重名与非法字符会即时提示并禁用保存)。
- 悬停分组名:显示完整的分组名称,名字被省略号截断时也能一眼看全。
- 排序:拖动分组行,拖到目标行的上/下半区决定插入位置。拖动开始时,同一级的所有行——包括被拖动的那一行——会从指针所在位置向外依次收起(300 ms 缓动,逐行 20 ms 延迟,最远 4 档),被拖分组庞大的会话列表不会再挡住目标表头;松手后恢复原来的展开状态,并把各行以 200 ms 缓动滑到新位置,而不是瞬间换序。拖动会话时同理:本工作区的其它分组会收起,只保留被拖会话所在分组的行(那是组内排序的区域),把会话拖到较远的分组只需对准它的表头。
- 只看此分组:分组
⋯菜单里的筛选会同时锁定它所属的工作区——同名分组不会跨工作区串味。 - 删除分组:只清除归属,组内会话回到系统默认分组,不删会话。
每个工作区都有系统默认分组,不能改名或删除,但可以像普通分组一样拖动排序。
会话
- 双击重命名(与桌面端一致);
- 悬停出现原版同款悬浮卡:完整标题、相对时间、空闲/进行中/待回答状态,已归档则显示归档标记;
- 悬停被截断的标题:与原生一致,标题以约 30 px/s 匀速滚到末尾后停在末尾(不回弹、不来回摆);移开鼠标立刻回到开头。溢出不足 8px 时不滚(否则看起来像抖动),开启「减少动态效果」时直接跳到末尾;
- 行尾 hover 出现
⋯ / 归档 / 置顶,悬停时行内时间让位; - 拖拽:拖到别的工作区会拒绝(护栏),拖到别的分组则直接归入;拖动期间本工作区的其它分组会自动收起(被拖会话所在分组保持展开,组内排序照常),因此把会话拖到远处的分组只需对准它的表头;
- 置顶会真正上浮到所属分组顶部(按宿主置顶顺序,最近置顶在前)。
新建会话落在哪里
- 工作区行的 +、rail 上的「新建会话」:会话不被写入任何分组归属,因此留在系统默认分组里,并按原生顺序排在该桶最前面——与官方列表一致;
- 标题栏 + →「新会话」:先选工作区与分组(默认分组也在列表里),再建会话并按所选归组;
- 分组行的 +、+→「新会话」里显式选中的分组:先建会话,再把它归入该分组;
- 正在「只看此分组」时新建:归入当前筛选的那个分组(此时它就是你正在看的地方);只有新空白确实属于该工作区时才会写入,另一个窗口在别的工作区新建的空白不会被误归;
- 分叉会话:子会话跟随源会话所在的分组(行菜单、官方分叉快捷键、聊天视图里的分叉都适用);树投影直接按源分组渲染,不会先在默认分组闪一下再跳过去,写入归属只是持久化;源会话本身就在默认分组时不写归属,子会话自然留在默认分组;
- 早先版本会把隐式新建的会话塞进「最近创建的分组」;分组当时按创建时间升序渲染,那个分组通常排在最后,于是新会话直接掉到了工作区最底部。该行为已移除;现在新建分组本身默认置顶,更不会把新会话带到列表末尾。
默认分组本身也能被拖到别的位置(与原生一致:它只是「不能改名、不能删除」,排序不受限)。把它拖到下面时,不归组的会话会跟着它排在那个位置——这是你自己排的序,不是插件又把新会话送到底部;拖回顶部即可恢复。
手动排序与新建会话
拖动会话产生的顺序只对它点名过的 id 生效:这些 id 在各自原有的槽位上互相置换,未出现在手动顺序里的会话(例如拖拽之后才新建的那个)保持宿主给的位置,不会被降级到末尾。这样「先手动排一次,再新建会话」不会把新会话挤到列表底部。
在此之上还有一条与原生对齐的活动提升:某个会话的 updatedAt 变新(发出第一条消息、被回复、另一个窗口写入、本来就还没落账的新会话首次出现),它会被提到所属工作区顺序的最前面,并写回本地顺序。宿主不会为「新会话」重新挂载一个已经存在的空白,而是直接复用那个空白;它此前只是靠 blank 被顶到前面,第一条消息清掉这个标记后就会掉回旧槽位——活动提升就是补上这一步。刷新页面不做整表重排:首次快照只记录时间戳,不会改动你已经排好的顺序。
批量操作
点标题栏的「选择」进入选择模式,选择模式接管标题栏:退出、已选数量、归档/取消归档/移动/永久删除排成一行原生图标按钮,窄侧栏不折行。
- 选择混入已归档条目时,归档与移动置灰;混入未归档条目时取消归档置灰;混入未分组会话时移动置灰;
- 工具栏下方直接说明置灰原因,悬停也有提示;
- 暂存中的「新会话」占位行不参与批量选择;
- 「全选」只包含当前显示出来的行:分组头复选框、分组 ⋯ 菜单、工作区 ⋯ 菜单走同一个派生(
selectableIds),超过「显示全部 60 条」的分组会提示「已选中 N 条;另有 M 条未显示」,展开后再选即可——不会悄悄把没渲染过的会话带进批量删除; - 另一窗口删掉的会话会从批量
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:10,最后更新 2026-11-03