banana770/dsh-model-select-collapse

dsh-model-select-collapse

DSH (DeepSeek Harness) web UI plugin: collapsible per-provider groups in the model selector. English + Chinese docs.

待分类collapsibledeepseek-harnessdsh

dsh-model-select-collapse

给 DSH(DeepSeek Harness)网页版的模型选择器加上「按供应商分组折叠」能力,让长长的模型列表不再是一拉到底的一大串。

English · 简体中文


这个插件是做什么的

装好之后,DSH 输入框里的模型选择菜单会多出可点击的分组标题:

装之前装之后
所有供应商的模型全部平铺,一长串滚动每家供应商是一个分组,标题一行,可以收起来
没办法藏起暂时不用的供应商点一下标题就能收起 / 展开这家供应商
每次关掉菜单,状态就没了折叠状态按供应商分别记住

具体来说:

  • 每个供应商分组都有一行标题,显示供应商名称和该组里的模型数量。
  • 点标题即可收起或展开这个分组,旁边的箭头图标会跟着转动,指示当前状态。
  • 折叠状态按供应商 id 存在浏览器的 localStorage 里,键名是 dsh.modelSelect.collapsed,所以关掉菜单再打开、甚至刷新页面,状态都还在。
  • 没有记录的供应商(比如你后来才导入的)默认是展开的,不需要任何配置。

除此之外,模型选择器的一切都保持原样:两级菜单(模型列表 / 推理强度)、键盘操作、加载与报错状态、目录加载失败时的「重试」,以及选择失败时的提示条。

它解决什么问题

DSH 会把所有供应商的模型平铺在一个菜单里。五六个供应商时就已经要滚很久,十几个的时候基本就是一面「模型墙」,你真正常用的那两三个反而被埋在中间。这个插件不改变原来的布局和排序,只是让你能把暂时不用的供应商收起来。

行为细节

行为说明
怎么折叠点击供应商分组标题
默认状态展开;只有你主动收起的那些分组会被记录为已收起
存储位置localStorage 里的 dsh.modelSelect.collapsed(一个 { 供应商id: true } 映射)
作用范围仅当前浏览器,本地状态,不会同步到任何地方
新导入的供应商默认展开(没有历史记录)
键盘操作标题是真正的 button,可以用 Tab 聚焦、用回车 / 空格触发
无障碍每个分组是带标题的 role="group" 区块,标题上带有 aria-expanded 状态

对 DSH 0.1.7-alpha.2 的兼容说明

DSH 0.1.7-alpha.2 把 @deepseek-ai/dsh-client-ui-primitives 里的图标导出统一改了名:

0.1.7-alpha.2 之前0.1.7-alpha.2 及以后
IconChevronDownOutline14IconChevronDownOutlineRegular
IconChevronRightOutline14IconChevronRightOutlineRegular
IconCheckOutline16IconCheckOutlineRegular
IconWarningOutline16IconWarningOutlineRegular

旧名字在新版里变成了 undefined,而把 undefined 当成 React 元素类型用会抛 Element type is invalid。DSH 的插槽机制随后会把崩掉的这一项判定为「退役」,官方模型选择器顺势顶上——表现出来就是折叠按钮凭空消失,而不是看起来像插件坏了。

所以 lib/client.js 在加载时会同时探测新旧两种命名,取存在的那个;万一两个都没有,就退化成空组件,宁可少一个图标也不让整块区域崩掉。因此同一个版本在旧版和新版 DSH 上都能跑。Toast 在新旧版本里都有且签名兼容,同样按「可选」处理。

安装

从 GitHub 安装(推荐)

dsh plugin --profile web add github:banana770/dsh-model-select-collapse

从本地目录安装

dsh plugin --profile web add /path/to/dsh-model-select-collapse

装完后重载 DSH 网页界面(重启 DSH,或者强制刷新页面),然后从输入框那里打开模型选择器即可。

手动安装

真正起作用的只有四个文件:

package.json       清单:声明客户端入口(dsh.client)和 bundle 补丁
cordis.patch.yml   把插件挂进 profile 的 bundle 清单(insert: model-select-collapse)
lib/index.js       宿主侧半边:空的空实现,只为让加载器里保留这一条记录
lib/client.js      真正的逻辑:替代官方模型选择器并加上分组折叠

把整个目录放到 profile 的安装位置,并确认 cordis.patch.yml 里那条 insert(id: model-select-collapse,name: @dsh-external/dsh-model-select-collapse)是生效的。没有构建步骤。

卸载

在侧边栏 插件 页面的「已安装」列表里移除 dsh-model-select-collapse,然后重载界面。如果是手动安装的,把 cordis.patch.yml 里对应那条 insert 删掉并重启 DSH。

实现方式

  • 抢占插槽(seat shadowing):DSH 输入框上的模型选择器是 conversation.input.model 这个插槽。官方插件用默认优先级 0 注册,本插件用 -1 注册;单插槽的规则是「优先级最低者渲染」,于是本插件的视图替换掉官方视图。
  • 不另起数据层:所有供应商和模型数据都来自 DSH 自己的 modelDirectories 服务(directoryFor(sessionId) 返回一个 SnapshotStore,这里通过 useSyncExternalStore 订阅)——和官方视图同源,本插件只负责重新渲染。
  • 样式隔离:整张样式表只注入一次到 document.head,带 data-plugin-css 标记;所有类名都加 mse_ 前缀,不会和官方类名撞车。
  • 不注册字典:官方的 model-selection 插件已经注册了 model 这个命名空间的 zh / en 字典,本插件注册插槽时声明同一命名空间以复用它们——因为重复注册同一命名空间会抛错。

想改点东西

你想改什么改哪里
没有记录的供应商默认收还是展lib/client.js 里的 loadCollapsed()
存储键名lib/client.js 里的 STORAGE_KEY
收起时箭头的方向css 块里的 .mse_groupChevronCollapsed
宽度、间距、配色lib/client.js 里的 css 块(基于 --dsw-* 主题变量)

隐私

  • 数据不出本机:插件自己不发任何网络请求,也不连任何第三方服务;它读写的只是官方模型选择器本来就在用的那个 DSH 模型目录服务。
  • 没有埋点、没有统计、没有账号。
  • 不含也不需要任何 API 密钥;供应商的凭证仍然留在 DSH 自己保管它们的地方。
  • 只写入一项本地状态:localStorage 里的 dsh.modelSelect.collapsed,记录你把哪些供应商分组收了起来。里面只有供应商 id 和布尔值,别的什么都没有;删掉它就等于把所有分组恢复成展开。
  • 不读取会话内容,不访问任何文件。

兼容性

  • 面向 DSH 的 web profile(platform: "web")。
  • 已在 DSH 0.1.7-alpha.2 上验证通过。由于图标查找同时探测新旧两种命名,在仍使用旧命名的版本上也能运行。
  • 使用 DSH 自带的 react(peerDependencies: >=18 <19),不会重复安装第二份。
  • 没有构建步骤:lib/client.js 就是最终可直接挂载的产物。

许可

BSD-3-Clause —— 可自由使用、修改和再分发。