dsh-pinv1.0.3
polunzh/dsh-pin
dsh plugin --profile web add github:polunzh/dsh-pin#4baf332414c7960c49488c9dfe2a8253a850eeaa
介绍
给 DeepSeek Harness 侧边栏补上真正的置顶:Workspaces 置顶 + 插件面板置顶,取消时精确还原 | Pin DSH workspaces and sidebar plugin panels to the top
标签
概述
dsh-pin
给 DeepSeek Harness 的侧边栏补上真正的「置顶」:Workspaces 置顶 + 插件面板置顶。
npm 包名:@polunzh/dsh-pin · GitHub 仓库:polunzh/dsh-pin
悬停任意工作区行 → 点 📌 钉到 Workspaces 最上面;悬停任意插件面板行 → 点 📌 钉到面板列表顶部。 再点一次取消置顶,会按记录的前后锚点精确还原到原位置;置顶状态持久化,重启/刷新后保持。
| 工作区行悬停:📌 置顶 / 取消置顶 | 插件面板行悬停:📌 置顶 / 取消置顶 |
|---|---|
先说结论:DSH 到底支持什么?
写这个插件前,我把运行中的 DSH Desktop 0.2.2-rc.1(/Applications/DeepSeek Harness.app/Contents/Resources/app.asar/dsh/)解包核对了源码:
| 能力 | 官方支持情况 | 证据 |
|---|---|---|
| Workspaces | ✅ 完整支持 | dsh-workspace、dsh-api-workspace-controller、dsh-client-ui-workspace 都挂在 Web 组合里(@deepseek-ai/dsh-web-app/cordis.patch.yml 的 workspace / workspace-controller / ui-workspace 行);侧边栏有 Workspaces 分组、增删改名、拖拽排序、搜索 |
| Session | ✅ 完整支持 | dsh-session、dsh-session-persistence-jsonl、dsh-api-session-controller、dsh-client-ui-session;侧边栏按工作区分组列出会话,可新建/恢复/归档/搜索 |
| Session 置顶 | ✅ 支持(工作区内) | pinnedSessionIds + pinSession/unpinSession RPC;dsh-client-ui-workspace 的行菜单里有「置顶/取消置顶」,并渲染「已置顶」分组(locale 里 row.pinned = 已置顶) |
| Workspace 置顶 | ❌ 不支持 | 工作区顺序只能靠拖拽(workspace.insertBefore),没有「钉到最上面」的入口或状态 |
| 插件面板置顶 | ❌ 不支持 | sidebar.panellist 的显示顺序由注册时的 order 固定;sidebar 的 pin 回调只用于「隐藏进 More」,默认 UI 没有任何 pin 控件,且 pin 选择只存在内存、插件重载即丢(@deepseek-ai/dsh-client-ui-sidebar/README.md) |
所以:Workspaces 和 Session 本身是官方功能,不需要插件补;真正缺的是 Workspaces 的置顶和插件面板的置顶 —— 这正是 dsh-pin 补的两件事。
功能
- 📌 Workspaces 置顶:把工作区钉到 Workspaces 列表最上面,多个置顶按「最近置顶优先」排列。
- 📌 插件面板置顶:把侧边栏插件面板(第三方插件的入口行、内置的「Plugins」「Automation tasks」都行)钉到面板列表顶部,其余面板保持注册顺序。
- 精确还原(多置顶安全):取消置顶时按置顶前的相邻锚点插回原位;锚点消失才回退到列表末尾。取消一个不会打乱仍在置顶的其他项。
- 持久化:工作区顺序写宿主的 Workspaces 注册表(全局生效,所有浏览器/客户端一致);置顶记录与面板顺序存当前浏览器 localStorage。
- 跟随主题:按钮和置顶标记只用
--dsw-*design token,深浅色/第三方主题都跟随;窄屏(折叠 rail)自动隐藏面板按钮;触屏(无 hover)常显。 - 中英文:走客户端
locale服务注册dsh-pin字典。 - 安全:不新增任何后端路由、不碰会话内容与凭据,只调用宿主已有的
workspaces.insertBeforeRPC 与浏览器存储。
安装
从 npm 安装(推荐):
dsh plugin --profile desktop add @polunzh/dsh-pin
或从 GitHub 源码安装 / 本地开发:
dsh plugin --profile desktop add github:polunzh/dsh-pin
# 本地开发
dsh plugin --profile desktop add link:/path/to/dsh-pin
也可以在 DSH 的插件市场 / 设置里搜索 dsh-pin 安装。安装后重启 DSH(或让插件管理器重载 profile)即可在侧边栏看到 📌 按钮。
纯客户端插件:宿主半是 no-op,所有逻辑在 lib/client.js。
使用
- 置顶工作区:鼠标悬停 Workspaces 里的任意工作区行,行右侧出现 📌 → 点击。该工作区移动到 Workspaces 最上面。
- 置顶插件面板:悬停左侧「Plugins / Automation tasks / 第三方面板」的任意一行,行右侧出现 📌 → 点击。该面板移动到面板列表顶部。
- 取消置顶:再点一次 📌。已置顶的项会常驻一个小图钉(工作区在行尾、面板在行右侧),直接点它也能取消。
- 刷新页面 / 重启应用,置顶状态保持。
交互细节(1.0.2 修正):图钉与相邻的原生行内按钮同尺寸(24×24 热区),点击不会连带展开/折叠所在的工作区文件夹; 已置顶项的常驻图钉与 hover 时浮现的按钮在同一位置,所以鼠标移到图钉上不会“图标跑掉、点不到”; 已置顶面板的图钉在鼠标移开后保持可见,一眼能看出哪些面板被置顶。
工作原理
侧边栏(React)
├─ 工作区行 [data-row-key="workspace:<uuid>"]
│ └─ 注入 📌(追加进行内的 rowActions,悬停可见)
│ └─ 点击 → ctx.workspaces.insertBefore(id, 当前第一个工作区 id)
│ └─ 宿主工作区注册表持久化顺序(全局)
└─ 插件面板行 button[class*="panelRow"]
└─ 注入 📌(面板列表内的绝对定位按钮,悬停显示)
└─ 点击 → DOM 重排 + localStorage 记录
- 工作区顺序走 DSH 官方
workspaces.insertBefore(workspaceId, beforeWorkspaceId?):与用户拖拽排序同一条通道,宿主持久化、跨客户端生效。 - 取消置顶的锚点是「置顶前紧跟在它后面的工作区 id」;
insertBefore省略锚点即追加到末尾,作为锚点失效时的兜底。 - 插件面板顺序按
ctx.slots.entriesOfSlot("sidebar.panellist")的注册order还原,所以取消置顶后其他面板顺序不会乱。 - React 会重渲染侧边栏:插件用
MutationObserver兜底重新注入按钮、重放面板顺序,并把置顶状态同步到行属性上。 - 记录键:
dsh-pin.workspaces.v1(工作区置顶记录)、dsh-pin.panels.v1(面板置顶顺序)。
边界与已知限制
- 仅 Web GUI / Desktop 的 Web 渲染层 可用;TUI / headless 没有侧边栏,插件不生效。
- 「未分组(Ungrouped)」不是真实工作区,不支持工作区置顶(也没有可持久化的顺序)。
- 工作区顺序是宿主全局的;面板顺序和置顶记录是每浏览器本地的(DSH 没有「面板置顶」的宿主概念)。
- 平铺/树形等视图切换不影响工作区顺序,但工作区的组内显示顺序仍由 DSH 自身的排序模式决定。
- 依赖应用内部 DOM 结构(
data-row-key、panelRow、React fiber 取面板 id)。DSH 大版本升级后如果结构变化,按钮可能暂时失效 —— 升级/适配后即可恢复,不会破坏应用本身。 - 调试:客户端暴露只读诊断钩子
window.__dshPin(workspaceOrder()、workspaceRecords、panelPins、lastError)。
开发
src/client-src.js 客户端源码(含构建占位符)
lib/pin-core.mjs 纯逻辑核心(置顶计划 / 记录解析 / 面板排序,Node 可测)
lib/client.js 客户端 bundle(由 build 生成,勿手改)
lib/index.js 宿主半(no-op)
scripts/build-client.mjs 构建(内联 pin-core + 校验语法)
test/pin-core.test.mjs 纯逻辑单测(node:test,零依赖)
test/e2e/pin-interaction.mjs 置顶按钮交互回归测试(可点击性/不消失/不误触)
docs/API-NOTES.md DSH 客户端插件 API 契约笔记
docs/DOM-CONTRACTS.md 侧边栏 DOM / React 契约与 E2E 实验记录
test/e2e/ CDP 端到端脚本(需要跑着的 DSH 实例)
npm test # 纯逻辑单测(13 项)
npm run build # 重新生成 lib/client.js
npm run check # 校验 lib/client.js 与源码同步
E2E(需要一台跑着的 DSH web 实例 + headless Chrome CDP;会真实点击 📌 并断言顺序/持久化/还原):
# 侧栏 DOM / 注入契约 / 重排稳定性(37 项)
DSH_PIN_LAB_URL="http://127.0.0.1:3091/?token=…" npm run test:e2e
# 置顶按钮可点击性 / 不消失 / 不误触(15 项,1.0.2 修复的回归测试)
DSH_PIN_LAB_URL="http://127.0.0.1:3091/?token=…" npm run test:pin
# 可覆盖:DSH_PIN_CDP=http://127.0.0.1:9222
更多实现细节见:
- docs/API-NOTES.md —— DSH 0.2.2 客户端插件契约(加载格式 / inject / effect / workspaces RPC / 面板 slot / locale / patch / design token)
- docs/DOM-CONTRACTS.md —— 侧边栏 DOM 与 React fiber 契约、重排稳定性实验、注入约定
发布与 CI
- CI(
.github/workflows/ci.yml):npm test+npm run check(校验提交的 client bundle 与源码同步)。 - 发布(
.github/workflows/publish.yml):push 到main时,若package.json的版本还没发布过,就用 npm trusted publishing(OIDC) 自动发布;仓库里不存放长期 token。本包已配置好 trusted publisher(repopolunzh/dsh-pin+ workflowpublish.yml),bump 版本后 push 即可自动发版。 - 如需首次接管一个新包名:先用带 scope 写权限的 token 手工
npm publish --access public一次,再运行npm trust github <pkg> --file publish.yml --repo polunzh/dsh-pin --allow-publish登记 trusted publisher。
许可
MIT,见 LICENSE。
星级评价
基本信息
插件信息来自公开 GitHub 仓库与 SkillHub,版权归原作者所有;安装前请自行审查仓库代码。