deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
English · MIT
一个 DeepSeek Harness(DSH)Web 客户端插件: 给日渐繁多的插件按钮一个归宿。 屏幕最右侧一条细长把手,鼠标一碰滑出抽屉—— 里面按分组、来源、搜索、最近使用排好你装过的每一个插件按钮,最常用的那几个还能一键贴回原位。
装上就生效,不需要改动任何一个插件。 它不修改第三方插件的代码,不要求插件作者适配,也不动你的 profile 配置。
同一个输入栏,收纳前 / 收纳后 —— 插件照常在跑,只是入口不再挤在工具行:

它长在应用最右侧。抽屉里按「你添加的」和「DSH 自带」两块分开,每条带来源标签与收纳状态,可搜索、可分组、可拖拽排序:
收起态就是右缘这样一条细长把手(鼠标碰一下、或点一下就能拉开):
设置 → 插件收纳篮 里还有一张更宽松的管理页,和抽屉共享同一份状态:
DSH 的插件都往同一排位置塞按钮:输入框工具行、会话标题栏、左侧栏底部。 装到第五个插件时,那排按钮已经开始互相挤,图标又都长得差不多,只能靠 hover 提示认人。
插件收纳篮把这些按钮收拢起来:插件照常在跑,浮窗照常能开,只是入口不再散落一地。
aria-label,图标直接复用按钮里的原 SVG,开箱就是中文名和原图标插件=你自己装的 / 系统=DSH 自带的 / 未识别),抽屉里也用「你添加的插件」与「DSH 自带(默认不碰)」两块分开,顶部还能按来源筛选设置 → 插件收纳篮 是一张宽松的管理页(按分组列出全部条目、每条直接选分组、系统项折叠在下面、导出/导入配置)。它和抽屉共用同一个状态,设置里改完抽屉立刻同步registrant,一键复制# 从 GitHub 安装(推荐)
dsh plugin --profile web add github:wqy-cell/dsh-plugin-basket
# 本地开发安装
dsh plugin --profile web add link:<本目录绝对路径>
装完重启 Web 载体(dsh web)并刷新页面即可。
本仓库是唯一源码,DSH 实际从 profile 的两处副本加载,所以改完要同步:
pwsh -File sync-plugin.ps1 # 同步到 ~/.dsh/profiles/web/{plugins,node_modules}
pwsh -File sync-plugin.ps1 -Check # 只比对不写入
| 动作 | 怎么做 |
|---|---|
| 打开抽屉 | 点输入栏最左的 🧺,或点屏幕最右的细长把手(9 × 420 的极窄竖条,中间一个向左的折线;悬停 0.2 秒也会自动滑出) |
| 打开某个插件 | 点抽屉里的图标 —— 等同于点它原来的按钮,插件的浮窗照常弹出 |
| 收纳 / 贴回 | 鼠标移到条目上,点右侧的 📥 图标;底部「全部收纳 / 全部贴回」是全局开关 |
| 标为常用 | 条目右侧的 📌,常用项会置顶到「常用」分区 |
| 分类 | ① 底部「智能分组」按名字关键词一键归类;② 条目右侧的 📁 菜单里选「移动到分组」;③ 直接把条目拖到分组标题上(拖到「未分组」即移出) |
| 只看某一类 | 顶部三档筛选:全部 / 你添加的 / 系统自带(带实时计数);选「系统自带」会自动展开那块,不用点两下 |
| 管理分组 | 新建空分组用「你添加的插件」标题右侧的「+ 分组」;在分组标题上悬停会露出 ▴ ▾ ✎ ✕(上移 / 下移 / 改名 / 删除,删除后条目回到未分组) |
| 排序 | 把条目拖到另一个条目的上半 / 下半即插到它前面 / 后面;不想拖就用 📁 菜单里的「移到最前 / 移到最后」。没手动排过的分组保持原始顺序,不会被擅自重排 |
| 搜索 | 抽屉顶部搜索框,匹配名字 / entry id / 来源 |
| 调宽 / 收起 | 拖抽屉左缘调宽;光标移开抽屉就自动收起(0.24 秒余量,擦边不会误收),点击抽屉以外的任何地方也会收起;也可以点标题栏的 › 或按 Esc |
| 看系统按钮 | 抽屉里「系统自带的按钮」分区点开(用来源筛选选「系统自带」会自动展开) |
| 出问题时 | 抽屉底部「诊断」→「复制诊断信息」,把每个席位的锚点/注册项/DOM/映射与每个插件的 registrant 一起贴出来 |
| 备份 / 搬家 | 底部「配置」先给一行摘要(几条设置 / 几个分组 / 宽度),需要时才点「导出 JSON」展开文本框「复制这段」;到新环境点「导入…」粘贴后「应用」。导入是整体替换、不是合并;同一处还有「恢复默认」(保留你调过的宽度) |
| 批量管理 | 设置 → 插件收纳篮:一张宽松的表格视图(每条一行,直接下拉选分组、标常用、收纳/贴回),系统项折叠在下方 |
首次打开会看到「待整理 N 个插件按钮」。选全部收纳=全部收进抽屉,输入栏立刻清爽; 选逐个挑=什么都不动,你自己一个个决定。
DSH 的界面由插槽(slot)组成,每个席位下挂着一串注册项。本插件用四层机制接管这些按钮:
| 层 | 做什么 | 靠什么 |
|---|---|---|
| ① 发现 | 读出每个席位当前的注册项 | ctx.slots.entriesOfSlot(slot)(官方公开的检查面) |
| ② 映射 | 把注册项对应到真实 DOM 节点 | [data-slot="…"] 锚点的第 i 个子节点 ↔ 排序后第 i 个注册项 |
| ③ 隐藏 | 让原按钮从原位消失,但仍然活着 | 给节点打 data-pb-hidden 属性 + 一条 display:none !important 样式 |
| ④ 镜像 | 抽屉里画自己的条目,点击时转发 | node.querySelector("button").click() |
为什么隐藏用 DOM 标记,而不是官方支持的「同 id + 更低优先级 shadow 注册」?
因为 shadow 会让原按钮根本不渲染。那样一来既读不到它的 aria-label 与 SVG 图标,
也无法用"点原按钮"来打开插件的浮窗——原按钮必须一直活着,这也是本插件能零改造的前提。
不看 registrant。 DSH 源码注释里明确说它是自报的、不能用来做归属判定:
entry.registrantisoptions.registrant ?? fiber.name… so a package could name itself something else — attributing by it would let a package impersonate another
而且实测在真实环境里它是 空的(第一版做完后看到"全部未识别"就是这么来的)。
改用「席位 → 官方 entry id 名册」:从已安装 DSH 版本的插槽目录里提取每个被监视席位上官方按钮的 id 清单,名单里没有的就不是这一版自带的东西。其中 conversation.input.left / conversation.input.right 的官方名册是空的 —— 这是最强的一条信息:那两个席位上出现的每一个注册项都必然是你装的插件。
判定顺序:
registrant 前缀(有值就用,比名册更直接;实测常常为空)TerminalRecovery 这类拿不到字面 id 的官方占用者)任何一条判错都能一键改回:条目菜单里有「标记为我添加的插件 / 标记为 DSH 自带 / 恢复自动判定」。诊断里也会写明每一条的判定依据。
输入栏左右两席、输入框下方、会话标题栏左右两席、左侧栏底部。
conversation.session.header.corner 永不接管:那是单席位,被系统右侧栏的展开按钮占着,收了会直接干掉系统入口。
抽屉用 z-index: 400。实测这套界面的层级分布是:
| 层 | 实测值 |
|---|---|
| 框架在流 UI | ≤ 101(layout 20 / 系统右侧栏 60 / 会话区 100) |
| 本抽屉 | 400 |
| 弹窗层 | 1000 – 1100 |
| 插件自己的浮窗 | 9997 – 10001 |
所以抽屉盖得住系统右侧栏与所有在流 UI,又被设置弹窗和插件浮窗正确压住 —— 从抽屉里点开插件后,插件面板是压在抽屉上面的。这条已固化为回归测试(mock-boot.cjs 场景 8),避免以后被误改。
本插件建立在「插槽锚点的 DOM 顺序与注册项顺序一致」这一约定上。它做了防御,但你应该知道边界:
position: fixed 在带 transform 的祖先里会被捕获成相对定位,这是一种控制台不报错的静默失败。插件会量自己的右缘是否贴住视口右边,不对就在抽屉顶部给出告警并写进诊断。0.1.6-alpha.1。升级 DSH 之后若官方在监视席位里新增了按钮,它可能被误判成"你添加的"——在条目菜单里「标记为 DSH 自带」一次即可(手工标记优先于一切自动判定,且记在配置里)。conversation.input.left 由会话输入栏声明,Hero 新会话页上不存在,此时抽屉会显示空态。node test/mock-boot.cjs # 行为测试(模拟 DSH 客户端环境 + 假 DOM)
node test/check-install.cjs # 装机自检(重启前跑,确认装对了)
mock-boot.cjs 覆盖:四层机制全链路、系统项判定、映射错位降级、单按钮崩溃隔离、
观测器过滤、通用角标探测、抽屉定位自检、slots 服务不可用降级、NEVER 席位保护、
UI SSR 渲染、配置持久化与边界夹紧。
check-install.cjs 只验证装机事实(不碰运行中的服务器):源包完整性、两处装载目录
与源是否逐字节一致、副本身份、bundle 注册 id 是否等于包名(dsh-client-modules
的硬闸门,不等就整包静默不激活)、profile 的依赖与挂载声明、以及备份是否还在。
MIT
dsh-plugin-basket — a collapsible drawer on the far right of the DSH Web client that gathers every third-party plugin button scattered across the composer row, the session header, and the sidebar footer. Categorize, search, pin, and pop the ones you use most back into place.
Same composer row, before / after — the plugins keep running, their entries just stop crowding the tool row:

Zero plugin modifications required. It discovers slot registrations through the public
ctx.slots.entriesOfSlot() inspection surface, maps them onto the [data-slot] anchor's
children, hides the originals with a data-pb-hidden attribute, and mirrors them as its own
tiles — clicking a tile forwards a real click to the original button, so each plugin's own
panel opens exactly as before.
Built-in DSH buttons are never touched (with a manual override), the single-seat
conversation.session.header.corner is never adopted, and every failure path degrades to a
read-only listing instead of a blank screen.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。