DSH plugin 界面开发:设置卡片怎么注册、Host 半与浏览器半怎么配合、client 打包怎么写

简介: DeepSeek Harness(DSH)插件界面开发:一个包里写 Host 半(用 settings.installSection 注册命名空间)与浏览器半(向 settings.plugin.item 槽注册卡片),用 ctx.settingsScope 读写,并按 dsh.client 打包 ./client。

DeepSeek Harness(DSH)插件界面开发:一个包里写 Host 半(用 settings.installSection 注册命名空间)与浏览器半(向 settings.plugin.item 槽注册卡片),用 ctx.settingsScope 读写,并按 dsh.client 打包 ./client。

本文转自 DSH Plugin Hub 插件市场


DSH plugin 的界面开发不是「写个前端页面」,而是在同一个包里写两半代码:Host 半用 ctx.settings.installSection() 注册一个设置命名空间,浏览器半向 settings.plugin.item 槽注册一张卡片;两半靠同一个命名空间自动配对,无需改动宿主仓库。 无论叫 DSH插件 还是 DeepSeek插件,界面开发的这套两半结构完全一致。

DSH plugin 界面开发的两半结构:一个包,两个入口

设置页由 Host 半与浏览器半组成,缺一半卡片不会出现。 官方 Cookbook 的原话是:Host 服务每个已注册的设置命名空间,「Plugins」区按卡片编辑的命名空间给它们配对,两半都在一个包里——Host 半在 src/,浏览器半在 src/client/,通过 exports['./client'] 导出并用 dsh.client 声明(来源)。

my-plugin/
├── src/index.ts          # Host 半:注册命名空间
├── src/client/index.tsx  # 浏览器半:注册卡片
└── package.json          # exports['./client'] + dsh.client

配对键只有一个:命名空间。 建议把它写成常量(如 MY_PLUGIN_NS = 'my-plugin'),两半引用同一个常量,避免拼写漂移导致「卡片没出现」。

DSH plugin 的 Host 半:注册设置命名空间

已有 cordis.yml 条目的插件走 ctx.settings.installSection(),它会把条目分层放在用户文档之下,并且在没有设置提供方挂载时依然工作。 官方给出的宿主半写法如下:

import type {
    Context } from '@deepseek-ai/cordis'
import type {
   } from '@deepseek-ai/dsh-settings'
import z from '@deepseek-ai/schemastery'

export const MY_PLUGIN_NS = 'my-plugin'

export interface Config {
   
  endpoint?: string
  retries?: number
}

export const Config: z<Config> = z.object({
   
  endpoint: z.string(),
  retries: z.number().step(1).min(0).default(3),
})

export function apply(ctx: Context, config: Config) {
   
  let source = () => config
  ctx.inject(['settings'], (settingsCtx) => {
   
    settingsCtx.settings.installSection(ctx, MY_PLUGIN_NS, Config, config, {
   
      // schema 表达不了的约束:拒绝这次写入,而不是留到下次使用才报错。
      validate: value => void assertReachable(value.endpoint),
      setSource: (current) => {
    source = current },
      onChange: () => {
    rebuildFromSettings(source()) },
    })
  })
}

两个容易忽略的声明:给字段加 role('secret') 会让它的值不出现在任何响应里(卡片要么把它写进 update/mutate 载荷,要么通过 credentials 域引用凭据);applies: 'restart' 告诉配置界面「拥有者要到下次启动才真正生效这次变更」。

DSH plugin 的浏览器半:把卡片挂进槽位

卡片注册进 settings.plugin.item 槽,key 必须与 Host 半的命名空间一致。 官方示例:

import type {
    Context as ClientContext } from '@deepseek-ai/cordis'
// 仅类型导入:该槽位的声明。跨插件协作一律走 cordis 服务,
// 值导入会被 client 的 bundle-purity 关卡拦下。
import type {
   } from '@deepseek-ai/dsh-client-ui-settings-plugins/client'

export const inject = ['slots', 'locale', 'connection', 'remote', 'settingsScope']

export function apply(ctx: ClientContext): void {
   
  const card = new MyPluginCardController(ctx.settingsScope.bind({
    namespace: MY_PLUGIN_NS }))
  ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
   
    name: 'settings.plugin.item',
    key: MY_PLUGIN_NS,
    locale: 'settings.myPlugin',
    inject: () => card.inject(),
  }, MyPluginCard))
}

卡片拥有自己内部的一切:外观、控件、文案。注意上面那行 import type {}——跨插件只能用类型导入,值导入会被 bundle-purity 关卡拒绝。

DSH plugin 配置读写语义:value / base / user 三层

作用域快照携带表单需要的三层信息,判断「是否被覆盖」看的是键的存在性而不是值。(来源)

层 含义 用途
value 解析后的生效值 表单回显
base 组合层 展示「默认来自哪里」
user 原始用户层 键存在 = 该字段被用户覆盖

写操作用 scope.set(field, value) 存单个字段,scope.unset(field) 把它清回组合层。每次写入都用读到的 revision 做栅栏——这能避免两个设置面板同时修改时互相覆盖。

DSH plugin 界面显示规则与打包要求

显示规则很直接:Host 服务该键且卡片注册了该键才渲染;Host 没服务就整张卡片不出现;Host 服务了但没有卡片认领,则什么都不渲染——这正是 ui-theme、permission、llm-* 这些命名空间不出现在该标签页的原因。卡片顺序等于注册顺序,带 key 的条目不能自己声明 order。

打包三条硬要求(来源):

  1. package.json 用 exports['./client'] 暴露浏览器半,并声明 dsh.client(含 platform 与它依赖的 client 包);
  2. bundle 必须是加载器期望的 lazy-CJS 工厂产物——仓库外没有公开 preset,需要自行复现同样的输出格式;
  3. 不能跨插件值导入:卡片要自带外观与暂存模型,自己拥有暂存与 revision 栅栏。

只要 cordis.yml 挂上这个插件,它就出现在页面上,无需重建 Web 应用——因为客户端模块系统是扫描已启用条目、按 dsh.client 服务构建产物的。

DSH plugin 界面开发自检与下一步

发布前过一遍:

  1. 命名空间常量是否两半共用;
  2. Host 半是否处理了 validate(schema 表达不了的约束)、setSource、onChange;
  3. 敏感字段是否加了 role('secret'),需要在下次启动才生效的是否标了 applies: 'restart';
  4. 卡片是否只用类型导入跨插件;
  5. exports['./client'] 与 dsh.client 是否齐备,bundle 是否为 lazy-CJS 工厂产物。

工具调用的卡片呈现是另一条线(presentCall / presentResult,且必须是纯函数),见 怎么写工具插件。界面之外的配置声明见 插件配置怎么用;打包与发布见 打包成 bundle 与 发布到插件中心。


常见问题

DSH plugin 界面开发为什么一定要写两半代码?

DSH plugin 的设置页由 Host 半与浏览器半共同构成:Host 半注册命名空间并决定服务哪些配置键,浏览器半注册卡片并决定怎么渲染。两半靠命名空间自动配对——「Plugins」区按卡片编辑的命名空间给它们配对,所以你只要在一个包的 src/ 与 src/client/ 里各写一半即可,无需改动宿主仓库(来源:官方 Cookbook「添加设置卡片」)。

DSH plugin 的 Host 半怎么注册设置命名空间?

DSH plugin 的 Host 半用 ctx.settings.installSection() 注册设置命名空间:传入插件上下文、命名空间常量、Config schema、当前配置与 { validate, setSource, onChange }。判断标准是「这个插件是否已有 cordis.yml 条目」——已有条目的消费方走 installSection 更稳,它会把条目分层放在用户文档之下,并且在没有设置提供方挂载时依然工作(来源:官方 Cookbook「添加设置卡片」)。

DSH plugin 的浏览器半怎么把卡片挂到设置页?

DSH plugin 的浏览器半把卡片注册进 settings.plugin.item 槽:用 ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({ name, key, locale, inject }, Card)),其中 key 必须是 Host 半注册的同一个命名空间。卡片内部通过 ctx.settingsScope.bind({ namespace }) 拿到作用域来读写配置(来源:官方 Cookbook「添加设置卡片」)。

ctx.settingsScope 的快照里 value、base、user 分别是什么?

DSH plugin 设置卡片的作用域快照携带表单需要的三层信息:value 是解析后的生效值、base 是组合层、user 是原始用户层。判断某个字段是否被用户覆盖,看的是 user 里那个键的存在性而不是它的值;scope.set(field, value) 写入单个字段,scope.unset(field) 把它清回组合层(来源:官方 Cookbook「添加设置卡片」)。

DSH plugin 发布浏览器半时对打包有什么硬要求?

DSH plugin 发布浏览器半时对打包有三条硬要求:① package.json 用 exports['./client'] 暴露浏览器半并声明 dsh.client;② bundle 必须是加载器期望的 lazy-CJS 工厂产物;③ 不能跨插件做值导入——client 的 bundle-purity 关卡会拒绝跨插件的 value import,所以卡片要自带自己的外观与暂存模型。仓库内用共享 preset 的 clientBundle(),仓库外需自行复现同样的输出格式(来源:官方 Cookbook「添加设置卡片」)。


本文转自 DSH Plugin Hub 插件市场,版权归属 DSH Plugin Hub 插件市场。

分类:插件开发

原文地址:https://dsh-plugin.org/zh/tutorials/develop-plugin-ui

首页:DSH Plugin Hub

相关文章
|
2月前
|
存储 开发工具 git
DeepSeek Harness 更新会丢配置吗?dsh 升级后会话、插件保留说明与更新前备份
更新 dsh 只换程序本体,不动数据:密钥在 $DSH_HOME/.credentials.yaml、profile 在 $DSH_HOME/profiles、会话数据都独立保留。本文说明更新到底动什么、备份哪些目录,以及升级后怎么验证插件没坏。
383 3
DeepSeek Harness 更新会丢配置吗?dsh 升级后会话、插件保留说明与更新前备份
|
监控 架构师 Java
JVM 11 调优指南:如何进行JVM调优,JVM调优参数
JVM 11的优化指南:如何进行JVM调优,以及JVM调优参数有哪些”这篇文章将包含JVM 11调优的核心概念、重要性、调优参数,并提供12个实用的代码示例,每个示例都会结合JVM调优参数和Java代码
844 2
|
22天前
|
JavaScript API 开发工具
DeepSeek Harness 源码怎么构建?DSH plugin 本地开发调试与源码版 npx 差异指南
DeepSeek Harness 从源码构建分五步:装 Node.js 与 pnpm → git clone → pnpm install → pnpm run build → pnpm dsh 启动。源码版最适合本地开发 DSH plugin——dsh plugin 会把参数原样转发给 pnpm,支持本地路径调试;开发完可提交到 DSH Plugin Hub 收录。
210 3
DeepSeek Harness 源码怎么构建?DSH plugin 本地开发调试与源码版 npx 差异指南
|
22天前
|
缓存 Linux iOS开发
DeepSeek Harness 自动更新怎么开?dsh 本体自动更新与插件升级提醒的做法
DeepSeek Harness 本体没有自动更新开关:最省事是用 npx 启动(每次按最新解析),全局安装则用系统计划任务定时跑 npm install -g @latest;插件侧另有启动时检查更新与可更新徽标。
271 1
DeepSeek Harness 自动更新怎么开?dsh 本体自动更新与插件升级提醒的做法
|
8天前
DSH plugin 开发示例:最小插件、工具插件、事件插件、带配置插件与服务插件的完整代码示例
DeepSeek Harness(DSH)插件开发示例合集:最小插件、模型工具、事件监听、带 Config 配置与服务提供者五个可直接抄的示例,每个含目录结构、完整代码与运行方式。
105 1
|
22天前
|
缓存 Shell Linux
dsh 更新了版本没变怎么办?DeepSeek Harness 多份安装、PATH 与缓存排查
dsh 更新了版本没变,先分清三层:磁盘上的包、PATH 命中的命令、正在跑的进程。最常见是服务没重启与多份安装;本文给 which -a、npm root -g、hash -r 与缓存、镜像滞后的逐项排查。
206 3
|
22天前
|
JavaScript 开发者
DSH plugin 从零怎么写?最小插件目录、本地构建与装进 profile 调试的完整起步流程
写第一个 DSH plugin 只需要一个导出 apply 函数的模块:先用 patch 覆盖层把本地文件插进 Web 界面验证,再做成包用 dsh plugin --profile add 装进 profile,最后用 --dump-config 与日志排错。
177 2
|
22天前
|
缓存 运维 安全
dsh 更新命令速查表:升级 DeepSeek Harness 本体与插件的命令、对应场景与执行后怎么验证
dsh 更新命令一张表看完:npx 重跑即最新、npm 全局用 npm update -g、源码用 git pull 加重建;插件更新走 dsh plugin --profile update 或在市场点更新。每条命令都标明适用场景与执行后的验证方式。
238 0
|
8天前
|
JavaScript 开发工具 git
DSH plugin 开发环境搭建:Node 与 pnpm 准备、两种获取 Harness 的方式、工程依赖与脚手架
DeepSeek Harness(DSH)插件开发环境搭建:装 Node 与 pnpm,用 npx @deepseek-ai/dsh 或源码检出获得 Harness,再用 create-dsh-plugin 生成工程,装好 cordis、dsh-tools、schemastery 三类依赖。
141 0
|
1月前
|
开发工具 git C++
dsh 更新插件用什么命令?DeepSeek Harness 单个插件更新、更新失败兜底与回滚指南
dsh 更新插件用 dsh plugin --profile <name> update <pkg>:该子命令把参数原样转发给 pnpm;本文区分本体更新与插件更新命令,给出单插件更新步骤、失败兜底与版本回滚方法。
368 0
dsh 更新插件用什么命令?DeepSeek Harness 单个插件更新、更新失败兜底与回滚指南

热门文章

最新文章