ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

如何给 DSH 写一个插件(并安装 / 卸载它)

如何给 DSH 写一个插件(并安装 / 卸载它)

如何给 DSH 写一个插件(并安装 / 卸载它)

本文基于真实环境@deepseek-ai/dsh@0.2.1-rc.5(Windows)的实操复盘。DSH 各版本插件 API 名字可能略有差异,本文以"已验证可加载"的写法为准——下文所有结论都来自实际安装、加载、卸载的验证,而非照抄社区教程。

0. 先说清 DSH 的插件模型

DSH 的口号是"万物皆是插件"。一个插件本质是一个包:通过package.json里的dsh清单声明自己,然后在它的"客户端半边"里往 DSH 的 UI 贡献样式、组件、页面、服务。

本文带你从零写一个把界面染成红色的插件my-red-theme,并走完安装、验证、卸载全流程。

1. 动手前先核对你的运行时(很多人栽在这)

网上教程常写"放进dsh-runtime/node_modules+ 改cordis.patch.yml",但你的真实环境是这样的:

  • 全局 CLI:npm i -g @deepseek-ai/dsh,本机版本0.2.1-rc.5。

  • 家目录(home):没设DSH_HOME时用默认~/.dsh(Windows 即C:\Users\<你>\.dsh)。

  • 插件按 profile 隔离:~/.dsh/profiles/<name>/各自有node_modules/和cordis.patch.yml。你实际在用的 profile 叫web,路径:

    C:\Users\<你>\.dsh\profiles\web\node_modules\ ← 插件都装这里 C:\Users\<你>\.dsh\profiles\web\cordis.patch.yml
  • 已装的dsh-better-sidebar就在这个web/node_modules里——它是你最好的"真实样本",比任何博客都准。

关键认知:没有"全局插件"这种事,插件是跟着 profile 走的。“把插件装进全局 dsh"的真实含义是"装到某个 profile 的node_modules并让 loader 挂载”。

2. 一个最小可加载插件的真实结构

照官方自带的cordis-plugin-development技能模板(里面有一个decoration装饰模板)来写,一个纯 UI 插件只需要 4 个文件:

my-red-theme/ ├── package.json # ESM + dsh 清单 ├── index.js # host 半边(空壳) ├── client.js # client 半边(真正干活的:注入样式) └── cordis.patch.yml # bundle patch:让 loader 挂载

package.json

{"name":"@local/my-red-theme","version":"1.0.0","private":true,"type":"module","exports":{".":"./index.js","./client":"./client.js"},"files":["index.js","client.js","cordis.patch.yml"],"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"platform":"web","immediately":true,"inject":[]}}}

注意三点(和社区版 Cordis 教程不同):

  • 用dsh字段声明插件,不是cordis: true。
  • 必须是ESM("type": "module")。用 CommonJS 会直接加载失败。
  • @local/是"本地开发插件"的约定作用域。

index.js(host 半边)

// 纯 UI 装饰不需要 host 逻辑exportfunctionapply(){}

client.js(client 半边——重点)

// 运行在浏览器里,document 可用window.__ModuleLoader__.load({id:'@local/my-red-theme',factory(require){constCSS_ID='my-red-theme-css';constRED_CSS=`:root, body, #app { --dsh-bg: #2b0000 !important; --dsh-fg: #ffd9d9 !important; --dsh-primary: #ff2f2f !important; background: var(--dsh-bg) !important; color: var(--dsh-fg) !important; } button, .btn { background: #ff2f2f !important; color: #fff !important; }`;functioninject(){if(typeofdocument==='undefined')return;if(document.getElementById(CSS_ID))return;// 幂等:热重载不重复插constel=document.createElement('style');el.id=CSS_ID;el.textContent=RED_CSS;document.head.appendChild(el);}inject();if(typeofdocument!=='undefined')document.addEventListener('DOMContentLoaded',inject);return{inject:[],apply(){}};},});

cordis.patch.yml

-insert:-id:my-red-themename:'@local/my-red-theme'# 必须与 package.json 的 name 完全一致

3. 最关键的坑:host 半边 ≠ client 半边

这是 90% 新手"写出来了但界面没反应"的原因:

  • host 半边(index.js)跑在Node里,document/window不存在。
  • client 半边(client.js,经window.__ModuleLoader__.load注册)才跑在浏览器里。

所以"让界面变红"这种 DOM 操作,只能写在 client 半边。很多教程让你在入口ctx.inject(['style'], ...)直接碰 DOM,在你的版本里会因为没有document而静默失败。

想加"侧边栏 Tab"也是同理:在client.js的apply(ctx)里用ctx.slots.register(...)(官方decoration模板就是这么注册 UI 的),并把inject改成['slots']。

4. 安装与卸载(命令清单)

dsh plugin子命令的本质,是把后续参数原样转发给该 profile 目录里的 pnpm。

安装(本地路径 / npm 包 / tgz 都支持)

dsh plugin--profile web add D:\path\to\my-red-theme
  • 它会把包装进~/.dsh/profiles/web/node_modules,并写入dsh.profile.bundles让 loader 挂载。
  • 装本地路径时,它创建的是link:软链(实测看到link:D:/.../my-red-theme),意味着你改工作区源码、重启 web 就能见效,不用重装。
  • 也可以是 npm 包名:dsh plugin --profile web add dsh-better-sidebar@0.28.2,或本地打的包add ./xxx.tgz。

卸载

dsh plugin--profile web remove @local/my-red-theme
  • 包名用package.json里的name(含@local/)。
  • 实测:执行后node_modules/@local/my-red-theme目录消失,profile 的package.json依赖与 bundle 列表均无残留。

验证是否真的加载了

  • 肉眼最直观:重启 web 界面,看变化(红主题装上去界面立刻全红)。
  • 看启动日志里有没有插件相关报错。
  • 浏览器开发者工具 → Elements,检查<head>里有没有你的<style id="my-red-theme-css">。

改完插件后必须重启 web 界面——client 半边的增删没有热更新,刷新页面才会重新加载。

5. 你可能没注意到的 7 件事

  1. 插件是 per-profile 的:没有"全局插件"。装到web只在web生效。
  2. host / client 半边之分:DOM 操作只能放 client 半边,否则document为undefined。
  3. cordis: true是社区版 Cordis 的写法,你的@deepseek-ai/dsh用的是package.json的dsh字段。照搬社区教程会装不上。
  4. 必须是 ESM。CommonJS 入口加载即失败。
  5. cordis.patch.yml的name必须和包名完全一致,否则 loader 按名字匹配不到,挂载不上。
  6. dsh plugin add走的是 pnpm,本地路径会建link:软链——这是最舒服的开发方式(改源码即生效,重启即可)。
  7. CSS 全用!important只是最快见效:要做"可切换主题",更优雅的做法是把样式包在[data-theme="red"]选择器里,再让插件动态给<html>加属性。DSH 自身的颜色 token(如--dsh-bg)最好去开发者工具里抄它的真实变量名再覆盖,比盲写!important更不容易破版。

6. 小结

写 DSH 插件 = 一个 ESM 包 +dsh清单 +client.js里用window.__ModuleLoader__.load注入逻辑;安装/卸载统一走dsh plugin --profile <name> add/remove。最大的两个坑是host/client 半边之分与ESM/清单格式——避开这两点,你就能在十分钟内让界面"全红",再往上逐步加服务、加 Tab。


附:本次实操时间线(复盘)

  1. 在d:/zhonglin/dsh/packages/my-red-theme按社区教程初版写了插件(@cordisjs/core+cordis: true+ CommonJS +ctx.inject)。
  2. 实地核对真实运行时:发现全局是@deepseek-ai/dsh@0.2.1-rc.5,家目录~/.dsh,插件在profiles/web/node_modules。
  3. 读官方cordis-plugin-development的decoration模板和已装的dsh-better-sidebar,确认真实格式:ESM +dsh清单 +window.__ModuleLoader__.load的 client 半边。
  4. 按真实格式重写插件(4 个文件),node --check语法校验通过。
  5. dsh plugin --profile web add <本地路径>安装 → 界面实测全红(验证加载成功)。
  6. dsh plugin --profile web remove @local/my-red-theme卸载 → 验证node_modules、依赖、bundle 列表均清理干净。
返回列表