dsh-recent-tasks
DSH 侧边栏里常驻末位、可折叠的「最近任务」工作区分组:侧边栏入口与 Mod+Alt+N / Mod+Shift+N 快捷键在专用目录里开新对话,对话因此归入这个分组,而不是「未分组」。该分组是真正的工作区记录,而非插件画出来的仿制品,折叠、改名、搜索与拖拽排序都正常可用;目录、分组名、常驻末尾与自动重建都是实时设置。
Install
dsh plugin --profile web add dsh-recent-tasks
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-recent-tasks
[English](README.md) | 中文




DSH 侧边栏里与「工作区」同级、可折叠的「最近任务」区块:没有工作区的对话归到这里,而不是和归档目录、已删除项目一起堆在未分组。这个区块不是插件画出来的仿制品,而是一条真正的工作区记录——所以从它开出来的对话,真的属于它。
DSH 按会话 cwd 归属的工作区给对话分组。不带工作区开出来的对话压根没有归属,于是落进「未分组」这个兜底桶。本插件为一个专用目录注册一条真正的工作区记录:于是「没有工作区」变成一个有正常行为的普通分组——改名、拖拽排序、置顶、归档、搜索全都可用——并且这个分组在侧边栏里被提升成一个独立可折叠的区块,与「工作区」同层、两者互不影响地展开收缩。
部分 · 说明
分组 · 通过 ctx.workspaceRegistry 为 <recentTasksDir> 注册的真 workspace 记录,创建时排在最后;此后插件不再改写持久顺序,你拖到哪里它就待在哪里
入口 · 侧边栏底部、设置旁的「+ 最近任务对话」,以及 Mod+Alt+N / Mod+Shift+N 快捷键,两者都会在该工作区里开会话
设置 · 设置 → 内置插件 里的「最近任务」页签——目录、分组名、自动重建——由本插件的官方实时配置驱动
Host API · POST /dsh-recent-tasks/api/state 与 /ensure,只读且幂等;唯一的写入路径是官方设置表单
效果图
<p align="center">
<img src="assets/screenshot-1-recent-tasks-group.webp" width="618" height="582" alt="效果图 — DSH 侧边栏里的「最近任务」区块,列出所有没有工作区的对话">
</p>
区块的行为与其他分组完全一致:GPT-6 特点介绍 是正在运行的那条对话,其余按使用时间排列,侧边栏底部的「+ 最近任务对话」在同一个目录里开新对话。(此图摄于提升为区块之前;提升后的读法见下面「侧边栏层级」一节。)
为什么要注册真工作区
另一条路——自己画一个分组——走不通:侧边栏的分组就是 workspace 存储域,没有任何插槽能让第三方插件插进一个平行分组。注册记录取代影子侧边栏的好处:
- 那里开的会话就是普通会话,
cwd是最近任务目录,分组的sessionIds走官方路径自己长出来(Workspace.sessionIds按realpath(cwd) === path过滤,也因此不可能把某条对话拖进别的分组); - 文件访问边界不变:会话的沙箱根目录就是这个目录,需要越界时在会话内提权,与工作区对话完全一致(设计 §8.7);
- 不打补丁改官方代码,不动
workspace命名空间的任何文案,「未分组」照旧;插件自己的分组名靠改写它自己的那条记录跟随界面语言,绝不碰官方命名空间,也绝不碰别的分组。
侧边栏层级:最近任务是一个独立区块
「最近任务」在侧边栏读作与「工作区」同层的区块,两块各自折叠:点「工作区」前面的箭头会收起其它工作区分组(最近任务不受影响),点「最近任务」前的箭头收起它自己那组对话;它下面的对话行相对标题缩进一级。
- 这是表现层适配,不是新功能:仍然是一条真工作区记录,插件不写任何工作区数据——既不重排列表,也不碰自己以外的任何分组。
- 它只认官方稳定属性(
data-row-key、role、内联的--dsh-workspace-indent),不认官方 class——那些是构建哈希,版本一更新就变。DSH 升级若把抓手挪走,这个提升会自动停用:侧边栏回到官方原样,不留残留、不报错,只在控制台留一行说明。 - 自动停用的场景:切成「单列表」、搜索中、把侧边栏收成 56px 细栏,以及树模式下「最近任务」被嵌套在别的目录之下(那时它不是同层)。这些状态下再切回来会立刻恢复。
- 已知取舍:这个区块随工作区列表一起滚动;「工作区」折叠期间,位于被隐藏分组里的当前会话高亮看不到,展开即恢复;官方重渲染的同一帧里(React 换掉容器、我们的重放还没跑到)被折叠的分组可能闪现一帧。
- 折叠状态只存在你自己的浏览器里(
dsh.recent-tasks.workspaces-collapsed.v1),不上报、不同步。
环境要求
- DSH 的
web或desktopprofile,0.2.0-rc.1及以上(开发与验证基于0.2.0-rc.2,cordis~4.0.4) - Node.js
>= 22.19.0——只在从源码构建时需要 - 装进 profile 时推荐用 pnpm
- 无网络访问、无原生模块、无安装脚本、无运行时依赖
安装
bundle 条目(id: recent-tasks)由本包自带的cordis.patch.yml自行声明,不需要手写补丁文件。
从 npm 安装(推荐)
dsh plugin --profile desktop add dsh-recent-tasks
已发布到 npm:dsh-recent-tasks。包里声明了 repository,插件市场因此能把它对应回本仓库并展示版本与下载量;安装走预构建包,完全跳过构建授权那一步。这是插件市场优先采用的安装方式。
从 GitHub 安装
按仓库身份安装,拿到 main 的最新提交——想试未发布改动时有用;在没有走 npm 的情况下,插件市场也是靠它把这个包对应到 winditer/dsh-recent-tasks:
dsh plugin --profile desktop add github:winditer/dsh-recent-tasks
Web profile 用 --profile web。重启 DSH(完全退出再打开),侧边栏底部就会出现「最近任务」分组。
构建产物 dist/client.js 是特意提交进仓库的:github: 安装不会执行本包的构建(pnpm 默认阻止生命周期脚本),提交进去的 bundle 才能让这条路开箱可用,使用端不需要编译任何东西。
从应用内安装
设置 → 插件 → 安装 bundle,指向本目录(或一个 file: 规格)。scripts/install-into-profile.sh 是等价的手工路径:pnpm add file:<repo> 外加一条 dsh.profile.bundles。
dependencies 那条不能省——只写进 dsh.profile.bundles 的名字会被桌面端崩溃恢复流程抹掉(它按 dependencies 重新推导 bundle 列表)。
从预构建 tarball 安装
每个 Release 都会附带打包好的安装包,既不用下载整个仓库,使用端也不需要编译:
dsh plugin --profile desktop add https://github.com/winditer/dsh-recent-tasks/releases/download/v0.4.1/dsh-recent-tasks-0.4.1.tgz
这个 URL 特意固定到 tag,而不是写成 releases/latest/download/…:latest 每次请求现算,但文件名是字面量——带版本号的文件名会在下一个 Release 改名的那一刻让那条 URL 失效。
从源码安装(开发)
git clone https://github.com/winditer/dsh-recent-tasks.git && cd dsh-recent-tasks npm install npm run build # 重新生成 dist/client.js dsh plugin --profile desktop add .
dist/client.js 每次启动前由 node scripts/build.mjs 重新生成;万一缺失,Host 半边照常加载,应用日志里会出现 client bundle not found; run \pnpm run build\ before launch。
设置
三项实时配置就是普通的官方设置,只出现在一处:设置 → 内置插件 顶部的「最近任务」页签(与「插件列表」「插件市场」并列)。它们就是本插件自己的实时 Config,页签里通过 configForms 写同一份配置文档。
从 0.3.x 升级:这三项原本在 设置 → 通用设置 里。那一行已删除——配置键、默认值和落盘位置都没变,只是入口换了地方。
键 · 默认 · 含义
recentTasksDir · ~/Documents/DSH 最近任务 · 分组占用的目录。首次使用时创建;必须是绝对路径。改它会立刻为新目录注册/复用记录。
title · 最近任务 · 创建记录时使用的分组名。当记录里的标题仍是本插件自己的默认名(最近任务 / Recent tasks)时,它会跟随界面语言;其他任何标题都算你手动定的,永不被改写。
autoRepair · true · 分组被删除后自动重建。关掉它,删除就变成可见的错误,而不是静默重建。
enabled · true · 总开关(仅 patch,无表单)。
apiPrefix · /dsh-recent-tasks/api · Host API 路径(仅 patch)。
改动由 dsh-config-editor 落盘到当前 profile 的 cordis.patch.yml(原子写、校验、失败回滚),并对运行中的 Host 实时生效:loader 把新值提交进插件的 Volatile 引用并发出 loader/volatile-update,Host 半边据此立刻重新 ensure。本插件自己从不写任何配置文件。
优先级:profile patch 层优先于本包内置默认值,所以用户改动能跨升级保留。
行为细节
- 绝不静默失败。 每次拒绝都是一个错误码(
bad-config、dir-not-writable、dir-not-directory、missing-record、disabled、unavailable…),由客户端从本插件自己的 locale 命名空间渲染;Host 从不下发展示文案。 - 接口很小、只在本地。 两个幂等 POST(
state、ensure),且必须带content-type: application/json;这条限制同时也是 CSRF 防线——跨站「简单请求」设不了这个类型,而本路由不响应预检。接口不暴露任何机密,也从不接受浏览器传来的路径或命令。 state绝不写盘。 不 mkdir、不创建、不排序;记录被删就以workspaceId: null+missing-record如实上报,只有ensure会修复。- 能复用就不新建。 配置目录已经是工作区时复用那条记录,且不动它的标题(DSH 的
create按realpath幂等,复用时会忽略标题)。 - 只在新建时排到末尾。 DSH 的
create会把新记录前插到持久顺序头部,所以刚装好的插件本来会把区块落在你已有工作区的上面。插件因此只把自己这次新建的记录追加到末尾(insertBefore(id)不带锚点):已存在的记录保持原位,包括你拖到的地方。它从不改动别的工作区。 - 分组名跟随界面语言。 侧边栏那一行和右上角工作区切换菜单都是原样渲染记录里持久化的
title——DSH 只对自己那个default-workspace哨兵值做本地化,插件也无法往内置workspace命名空间里加词条。所以只要记录里的标题还是本插件自己的默认名(最近任务/Recent tasks),客户端半边就会用官方的workspaces.rename(侧边栏自己「重命名」用的同一个调用)把它改成当前语言:两处立
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::7,最后更新 2026-11-04