--- name: msi-icons description: dsh-msi-icons 模型选择器美化插件的维护与扩展指南:架构与 HTTP 接口、厂商图标配置(内置 6 家 + 预留库 16 家)、平铺四分类契约、置顶主力超时路由、依赖降级行为、排障手册。修改/扩展/排障本插件前必读。 --- # dsh-msi-icons 插件维护指南 模型选择器美化 + 置顶主力超时路由。三大能力: 1. **厂商官方图标**:内置智谱 Z / 千问 #6950EF / DeepSeek 鲸鱼 / 超算红算 / MiMo 艺术字 / 免费绿徽章;预留库 16 家(OpenAI/Claude/Gemini/Meta/Mistral/Perplexity/Ollama/HF/NVIDIA/OpenRouter/Groq/硅基流动/MiniMax/文心/Kimi/Anthropic),可配置激活。 2. **平铺四分类**(用户定稿 v2.1,不渲染任何组标题):📌置顶 → 原生区(免费垫底)→ 插件区 `-mv`(紫) → 视觉区 `-视觉`(黄);同名去重。 3. **置顶主力超时路由**:置顶模型首 token 45s 超时或未出内容即报错 → 自动切换下一个置顶模型重试;全部不可用 → 抛错停止。 ## 架构 ``` lib/client.js 浏览器半:MutationObserver 监听弹层 → 行装饰(图标/后缀/爱心) → 四分类容器搬家(源 section 隐藏,行移入 msi-cat 容器) → 置顶 localStorage(msi.pins.v2) + POST /msi-icons/pins 同步 Host lib/index.js Host 半:HTTP 路由 ×4 + llm/stream waterfall 超时路由器 assets/icons/ 预留官方图标库(16 家,开箱即用) ``` - 装饰目标类名:`._7KE1Ra_menu / ._7KE1Ra_groups / section._7KE1Ra_group / ._7KE1Ra_groupTitle / ._7KE1Ra_option / ._7KE1Ra_optionCopy / ._7KE1Ra_modelName / ._7KE1Ra_trigger` - ⚠️ 这些是 DSH 构建产物 CSS-module 哈希,**随 DSH 大版本可能变化**。变化时插件静默失效(护栏保证不报错不干扰),需更新类名映射。 - 行装饰一次性:`dataset.msiDone="1"`;触发按钮文本异步变化会重评估(`msiTrigText` 比对)。 ## HTTP 接口(接口层面文档) | 路由 | 方法 | 请求 | 响应 | |---|---|---|---| | `/msi-icons/pins` | POST | `{"pins":[{"title":"组名","model":"模型id"}]}` | `{"ok":true,"resolved":n}`(title=provider displayName,Host 经 llm.listProviders 解析成降级链并落盘) | | `/msi-icons/state` | GET | - | `{"ok":true,"pinned":[...],"firstTokenTimeoutMs":45000}` | | `/msi-icons/vendors` | GET | - | `{"ok":true,"vendors":[{match,icon,rank}]}`(live 读 vendors.json,改文件即生效) | | `/msi-icons/icon?file=x.svg` | GET | - | SVG 文件;两级查找:`~/.dsh/msi-icons/icons/` → 插件 `assets/icons/`;文件名白名单 `[A-Za-z0-9._-]{1,64}.svg` 防穿越 | ## 厂商图标 **内置表**(client.js `detectVendor` 关键词 → 图标):免费→绿徽章;千问|qwen→官方符号;超算|kimi→红算;mimo|小米→艺术字;deepseek→鲸鱼;智谱|glm→Z。未命中 → `matchCustomVendor` 查配置表 → 兜底「··」。 **接入新厂商三步**(无需改码/重启): 1. 官方 SVG 放 `~/.dsh/msi-icons/icons/`(或依赖内置预留库则跳过) 2. `~/.dsh/msi-icons/vendors.json` 加 `{"match":"关键词正则","rank":6,"icon":"文件.svg"}` 3. 刷新页面。`vendors.example.json` 是带说明的模板。 **加新模型三场景**:老厂商新模型=零成本(加进 provider 模型表即可,图标/分类/路由全自动);换老厂商图标=改内置表;全新厂商=上面三步。 ## 分类逻辑与契约(v2.1 定稿,勿回退) - 四容器 `ensureCat(box, kind)`:native(-900) → plugin(-800) → vision(-700),pinBox(-1000) 置顶。 - 源 section 处理后 `display:none` + `dataset.msiSrc="1"`;行 `dataset.msiKey = 原始组名::原始模型名`(装饰前文本,改名不断链)。 - **契约红线**: - CSS 绝不能加 `._7KE1Ra_group{display:flex !important}`——!important 压过内联 display:none,隐藏空壳组会整组露出(v2 翻过车)。 - `slots.register` 的 **name 必须是父 slot 名**(如 "shell.overlay"),写成插件自定义 id 会引导链崩溃、前端全体回退(balance-dashboard 翻过车)。 - 置顶 key 必须用装饰前原始文本计算。 - 回退:git 历史取回旧版;v1 厂商内三级分类版备份在作者本地 `_archive/`(未随仓库发布)。 ## 超时路由(llm/stream waterfall) - 触发条件:首内容 chunk 45s 未到(`Promise.race` + `it.return()` 关闭旧流)或未产出内容就收到 `finish{reason.kind==='error'}`。 - 不路由:用户主动中止(aborted 直接透传);视觉链 provider(名含 vision/modlens);无置顶;`inFallback` WeakSet 标记的二次派发(防递归)。 - 派发方式:`runtime.stream(Object.assign({}, options, {provider, model}))`(浅拷贝防 frozen options),瀑布重入由 WeakSet 放行。 - 日志:`[msi-timeout-router] provider/model 不可用,切换下一个置顶模型`;全灭抛 `msi-timeout-router: 置顶主力模型全部不可用`。 ## 依赖与降级(无硬依赖,全部优雅降级) - **不依赖 modlens / vision-router / 任何其他插件**。只挂 `llm/stream` 和选择器 DOM,按运行时实际存在的 provider/行装饰。 - 卸载 modlens:无 `(modlens vision)` 行 → 插件区空=不可见,其余一切照常。 - 卸载 vision-router:无 自动识图/Vision Chain 行 → 视觉区空=不可见;原生多模态模型图片直出不受影响;超时路由照常。 - 别人的电脑:按实际模型关键词给图标,认识的有官方图标,不认识的兜底「··」或用 vendors.json 配置。 - 唯一环境契约:DSH 版本(类名哈希)+ 页面引导链健康(任一插件引导崩溃 → 前端回退态,所有客户端插件失效——这是 DSH 机制;排障先查 `document.body` 是否有 "Failed to load plugins")。 ## 排障手册 1. **图标全消失/前端错乱** → 先看页面有无 "Failed to load plugins"(引导链崩,全体回退);再看 console 报错定位是哪个插件。 2. **图标部分消失** → DSH 升级换了类名哈希;F12 确认新类名,更新 client.js 的类名映射。 3. **置顶丢失** → localStorage 按 origin 存储;Host 侧 `~/.dsh/msi-icons/pins.json` 是路由用的降级链(刷新页面后客户端会自动同步)。 4. **路由不生效** → `GET /msi-icons/state` 看 pinned 是否为空;为空=浏览器没同步(刷新页面)或 displayName 对不上。 5. **验收命令**:`GET /msi-icons/state`、`GET /msi-icons/vendors`、页面 console 无引导错误、选择器四区+爱心可见。 ## 安装 / 重启工作流 ```powershell # 用户安装(一条命令,bundle patch 自动挂进 profile 层栈) dsh plugin --profile web add github:hun1315/dsh-msi-icons # 本地开发安装(指向插件源码目录,link 模式 client.js 改动实时生效) dsh plugin --profile web add <插件源码目录> # 重启使 Host 半生效(必须走优雅重启,绝不 shell 里杀宿主) dsh plugin list --profile web # 确认依赖已登记 # <你的优雅重启脚本/方式> ``` 注意:直接往 `profiles/web/node_modules/` 手放目录**不会**被装载——必须 `dsh plugin add` 注册依赖。Host 半改动需重启;client.js 是 link 实时生效(刷新页面即可)。