返回目录
其他 待识别

dsh-plugin-basket

wqy-cell/dsh-plugin-basket

插件收纳篮:DSH 最右侧的可收起抽屉,零改造收纳各席位上的第三方插件按钮(分组 / 来源 / 搜索 / 置顶 / 排序) · A DeepSeek Harness plugin: a collapsible drawer that gathers third-party plugin buttons.

Stars
0
Forks
0
Issues
0
更新
5 天前

PROJECT TOPICS

项目标签

PROJECT README

README

dsh-plugin-basket · 插件收纳篮

English · MIT

一个 DeepSeek Harness(DSH)Web 客户端插件: 给日渐繁多的插件按钮一个归宿。 屏幕最右侧一条细长把手,鼠标一碰滑出抽屉—— 里面按分组、来源、搜索、最近使用排好你装过的每一个插件按钮,最常用的那几个还能一键贴回原位。

装上就生效,不需要改动任何一个插件。 它不修改第三方插件的代码,不要求插件作者适配,也不动你的 profile 配置。

📸 效果

同一个输入栏,收纳前 / 收纳后 —— 插件照常在跑,只是入口不再挤在工具行:

收纳前后对比

它长在应用最右侧。抽屉里按「你添加的」和「DSH 自带」两块分开,每条带来源标签与收纳状态,可搜索、可分组、可拖拽排序:

插件收纳篮抽屉

收起态就是右缘这样一条细长把手(鼠标碰一下、或点一下就能拉开):

收起态的把手

设置 → 插件收纳篮 里还有一张更宽松的管理页,和抽屉共享同一份状态:

设置里的管理页

❓ 它解决什么问题

DSH 的插件都往同一排位置塞按钮:输入框工具行、会话标题栏、左侧栏底部。 装到第五个插件时,那排按钮已经开始互相挤,图标又都长得差不多,只能靠 hover 提示认人。

插件收纳篮把这些按钮收拢起来:插件照常在跑,浮窗照常能开,只是入口不再散落一地。

✨ 核心特性

  • 零改造接管:自动发现各席位上的第三方插件按钮,一个插件都不用改
  • 右侧细长把手:收起时只占屏幕最右 9px(一根 9 × 420 的极窄竖条,中间一个向左的折线);悬停或点击滑出,移开自动收起
  • 自动识别:名字直接读按钮的 aria-label,图标直接复用按钮里的原 SVG,开箱就是中文名和原图标
  • 状态会跟着走:原按钮上的角标数字与运行圆点会同步到抽屉条目上(按类名语义识别,不写死任何插件的类名)
  • 收纳 / 贴回:一键把按钮从原位收进抽屉,或贴回它原本的席位(标题栏的回标题栏,输入栏的回输入栏)
  • 分类 · 搜索 · 最近使用 · 常用:抽屉内分区展示,支持按名字 / id / 来源搜索,标星常用
  • 绝不动系统按钮:自动区分「第三方插件按钮」与「DSH 自带按钮」,自带的一律不碰(也可右键强制收纳)
  • 来源一眼可辨:每条都带来源标签(插件=你自己装的 / 系统=DSH 自带的 / 未识别),抽屉里也用「你添加的插件」与「DSH 自带(默认不碰)」两块分开,顶部还能按来源筛选
  • 设置页也有入口设置 → 插件收纳篮 是一张宽松的管理页(按分组列出全部条目、每条直接选分组、系统项折叠在下面、导出/导入配置)。它和抽屉共用同一个状态,设置里改完抽屉立刻同步
  • 分类可自动也可手动:「智能分组」按名字关键词一键归类(不写死任何插件包名,认不出就留在未分组);也能手动新建空分组、重命名、排序、删组,或把条目直接拖到分组标题上
  • 宽度可拖:抽屉左缘拖拽调宽(260–560px),配置落 localStorage,刷新保留
  • 首启引导:第一次打开给「全部收纳 / 逐个挑」,不会静默把你的按钮变没;未整理时输入栏的 🧺 上会有一个小圆点,不拉开抽屉也知道有东西可整理
  • 悬停即来、离开即走:把手悬停 0.2 秒或点一下就滑出;光标移开抽屉、或点击抽屉以外任何地方都会自动收起(收起带与展开对称的滑出动画,不是一下消失)。只有在宽度拖拽中、或在搜索框里打字时才不收
  • 自带诊断:抽屉底部「诊断」会列出每个席位的「锚点在不在 / 注册项几个 / DOM 子节点几个 / 映射成不成立」,以及每个条目的真实 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 图标, 也无法用"点原按钮"来打开插件的浮窗——原按钮必须一直活着,这也是本插件能零改造的前提。

怎么判断"这是 DSH 自带的,还是你装的"

不看 registrant DSH 源码注释里明确说它是自报的、不能用来做归属判定:

entry.registrant is options.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 的官方名册是的 —— 这是最强的一条信息:那两个席位上出现的每一个注册项都必然是你装的插件。

判定顺序:

  1. 手工标记(条目菜单里标的,优先于一切自动判定)
  2. registrant 前缀(有值就用,比名册更直接;实测常常为空)
  3. 该席位的官方 entry id 名册(主要依据)
  4. 官方组件名(补住 TerminalRecovery 这类拿不到字面 id 的官方占用者)
  5. 都认不出 → 「未识别」,保守不参与自动收纳,等你手工标一次

任何一条判错都能一键改回:条目菜单里有「标记为我添加的插件 / 标记为 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 顺序与注册项顺序一致」这一约定上。它做了防御,但你应该知道边界:

  • 任何一层失败都不白屏:锚点找不到、DOM 与注册项数量不一致时,该席位自动降级为「只读」(只列出条目,不隐藏、不代理),并在抽屉顶部显示告警。
  • 单个插件按钮崩了不会连累别人:若某个插件按钮在渲染时抛错,框架会在原位放一个错误占位。本插件保留该位置并把这一条标成「渲染失败」(不可点、不可收纳),同一排的其它按钮照常收纳与点击,不会因为一个坏按钮让整排失效。
  • 不打扰流式输出:DOM 监听只认「被监视席位内部的变动」与「席位挂载/卸载」两类信号,聊天区正文的持续改写会被忽略——模型输出时不会反复重扫。
  • 抽屉位置会自检position: fixed 在带 transform 的祖先里会被捕获成相对定位,这是一种控制台不报错的静默失败。插件会量自己的右缘是否贴住视口右边,不对就在抽屉顶部给出告警并写进诊断。
  • 官方名册跟着 DSH 版本走:名册取自 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


English

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:

before / after

the drawer

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,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。