DeepSeek Harness 插件

dsh-settings-ui

DSH web plugin: unified settings-page UI kit. Provides the ctx.settingsUi client service — themed primitives + a settings store + a declarative form — so plugins register a settings section without(英文原文)

跳到安装方式

来源信息

GitHub 仓库
KaramachiA217/dsh-settings-ui
最近更新
2026年8月19日
分类
插件市场与管理
GitHub stars
1
载体类型
plugin
目录证据
上游声明已找到 dsh.bundle
证据路径
package.json#dsh.bundle
核对版本
0.1.0-rc.8
上游核对日期
2026-08-20

该证据由上游目录提供。本站没有安装、运行或安全审核这个插件。

安装

默认先复制一段 Prompt,让 Agent 读 GitHub 仓库和源码;需要自己装时再切到命令。

复制这段 Prompt,发给 DSH、Codex 或其他 Agent,让它先读 GitHub 仓库和源码。

请先不要安装或执行任何命令。阅读这个插件的 GitHub 仓库、README 和关键源码,然后用清楚、直接的方式回答以下问题,帮助我判断它是否适合我的需求:

1. 这个插件是什么,解决什么问题;
2. 适合哪些用户和典型使用场景;
3. 安装后如何使用,并给出一个最小使用示例;
4. 有哪些已知限制,以及隐私、安全、兼容性或维护风险;
5. 给出“推荐 / 有条件推荐 / 不推荐”的明确建议和理由。

请区分仓库明确说明、根据源码推断和未知信息。证据不足时请明确说明,不要猜测或照抄 README。

GitHub:https://github.com/KaramachiA217/dsh-settings-ui
插件名:dsh-settings-ui
作者:KaramachiA217

检查来源文件

安装前先看这个插件目录里的 README 和其他文件。

文件资源管理器4 个文件
README.zh.md来源说明 · 只读预览
README 语言

English | 中文

dsh-settings-ui

> 完整使用与开发手册见 [GUIDE.zh.md](./GUIDE.zh.md)

DSH Web 插件:统一设置页 UI kit + 浮层面板 kit。对外暴露 ctx.settingsUi 客户端服务,让其它插件用一套统一样式(对齐 dsh-better-sidebar 风格、--dsw-* 语义 token)接入设置页与 shell.overlay 浮层,不用再各自手写 UI 组件、CSS 和「加载/保存/busy/错误/已保存/revision 冲突」状态逻辑

> 样式 token:所有 var(--dsw-alias-*, fallback) 的备用色均按官方暗色主题实值对齐(v0.2.6);在官方壳内这些 fallback 恒被真实 token 覆盖,仅在脱离官方壳时兜底。共享样式类名 sui- 前缀,与官方 --dsw-* / better-sidebar 的 --dsh-sidebar-width 命名空间互不冲突。

定位与向后兼容

  • 本 kit 只新增一个服务settingsUi),不接管、不过滤 settings.section / shell.overlay 槽。
  • 原有直连方式完全保留:插件继续用 ctx.slots.inject('settings.section', ...) 也照常工作(无需迁移)。
  • 想用新方式的插件,inject 里加 settingsUi,改用 ctx.settingsUi.section(...) / ctx.settingsUi.overlay(...) 即可。

兼容性声明

  • 已验证:dsh 0.1.0-rc.5(官方壳全套,desktop / desktop-dev profile 实测,含 framework-only bundles 挂载)。
  • rc.6(2026-08-17 已验证):kit 0.2.18 tarball 在 rc.6 双链路实测通过——桌面端(rc6-min profile,13648)+ web 端(rc6-web profile,3090)bundles 直挂:源码同 commit 铁证(上游 master = rc6 npm 构建提交 = rc5 checkout HEAD,47f9438)+ 契约面核对一致(React 18.3.1 / --dsw-alias-* / slots ledger / installLocale / locale.register)+ 冷启动零错误 + bundle 实核 + UI 手测通过。rc.6 下 link: 开发挂载 ESM 解析失败,必须 tarball。
  • rc.7(0.3.0,2026-08-20)pluginCard() 面向 rc7 的 keyed settings.plugin.item 槽 + 官方 ctx.settingsScope(保存即生效、revision 栅栏由官方保证)。经典面(settings.section / settings.general.item / shell.overlay)在 rc7 未变,section()/overlay() 照常工作。pluginCard() 是 rc7 时代的路径,新品配置卡建议走它。
  • 未列出的版本组合未验证,不声明兼容。

三级 API 速选(便捷 vs 自由)

kit 是「通用 UI 注册入口」:**同一套原子组件 + sui-* 样式 + 状态**,按诉求分三级用——

诉求用哪个一句话
官方「插件配置」Tab放一张配置卡(rc7 范式,经 settingsScope 保存)ui.pluginCard(config)rc7 便捷声明 { key, header?, fields?, content?, showIn?, chrome? },keyed settings.plugin.item + 官方 scope 后端,kit 渲染卡壳
设置页加一张配置卡片(传统/rc6 回退)ui.section(config)便捷声明 { id, order, label, render, inject? },自动包 .sui-root 根 + 共享样式 + 统计卡计数
窗口最前层级开自己的浮窗(可拖拽 / 最小化 / 置顶 / 位置持久化)ui.overlay(config) + ui.Panel + ui.createPanelStore自由注册 shell.overlay 浮层;Panel 给 chrome;createPanelStore({ persist }) 管开合/位置/置顶
  • rc7 便捷 = 官方「插件配置」Tab 卡,保存由官方 settingsScope 承担(save-as-you-go、revision 栅栏)。
  • 便捷 = 设置页卡片,适合「配置类」插件(proxy / mcp / search / skill 的设置卡)。
  • 自由 = 任意浮窗,适合「伴随式」插件(桌面助手 / 会话伴侣 / 搜索面板),位置与最小化可 persist 到 localStorage。
  • 三级共享原子组件(SectionHeader / Field / Card / Button / Switch / Rows …)与 sui-* 样式;h = React.createElement
  • 需要「加载/保存/busy/error/revision」就用 ui.createSettingsStore + ui.useSettings(见 §3)。

rc7 便捷最小骨架(官方插件配置 Tab 卡):

const card = ui.pluginCard({
  key: 'my-plugin',                 // 必填:settings 命名空间 = Tab 派发键
  header: { title: '我的插件', desc: '一句话说明' },
  fields: [
    { key: 'enabled', type: 'switch', label: '启用' },
    { key: 'endpoint', type: 'text', label: '服务地址' },
  ],
})
// card.store(settingsScope 后端):每个字段编辑即经 scope.set/unset 持久化

设置页便捷最小骨架section())见下方「快速示例(完整插件)」。

自由最小骨架(浮窗):

function MyWindow(props) {
  const { ui, panel } = props
  return ui.h(ui.Panel, { title: '我的窗口', panel },
    ui.h(ui.Card, {}, '内容……'),
  )
}
const plugin = {
  inject: ['slots', 'settingsUi'],
  apply(ctx) {
    const ui = ctx.settingsUi
    const panel = ui.createPanelStore({ persist: 'my.window.v1' })  // apply 里建一次
    ui.overlay({ id: 'my-window', order: 100, inject: () => ({ ui, panel }), render: MyWindow })
  },
}

安装(挂进 profile)

与普通客户端插件一致:单包、cordis.patch.yml 单行装配、dsh.client 声明、提交 lib/。本包声明了 dsh.bundle.patch,是标准 bundle。官方 npm 安装(首选)——一步完成「加依赖 + reconcile 追加进 dsh.profile.bundles」:

dsh plugin --profile <profile-name> add dsh-settings-ui

> npm latest 目前 0.2.22;0.4.0 发布后同命令升级dsh plugin --profile <profile-name> add dsh-settings-ui@latest)。⚠️ 发布后 24h 内安装会撞 pnpm v11 minimumReleaseAge 供应链冷却期、静默回退旧版——在 profile 的 pnpm-workspace.yamlminimumReleaseAge: 0,或等满 24h 再装。

本地开发(可选,二选一):npm pack 出 tgz 后 dsh plugin --profile <profile-name> add ./dsh-settings-ui-<ver>.tgz,或手写 profile:

1. 在 profile 的 package.json 加依赖(file: 挂载 tgz;⚠️ rc.6 起 link: 会因 ESM 解析失败,一律用 tarball)。 2. 在 dsh.profile.bundles 里加一行 "dsh-settings-ui"。 3. pnpm install,然后硬刷新页面(client 改动无需重启)。

示例(desktop profile):

{
  "dependencies": { "dsh-settings-ui": "file:./dsh-settings-ui-<ver>.tgz" },
  "dsh": { "profile": { "bundles": ["dsh-base", "dsh-web-app", "dsh-settings-ui", "..."] } }
}

API 参考

所有能力都挂在 ctx.settingsUi 上。消费插件只需 inject: ['settingsUi'],然后在 apply(ctx) 里取 const ui = ctx.settingsUi

1. 原子组件(统一主题,共享样式只注入一次)

| 组件 | 用途 | 主要 props | |---|---|---| | ui.SectionHeader | 标题 + 一句话描述 | { title, desc } | | ui.Field | 标签 + 控件 + 提示竖排 | { label, hint, children } | | ui.TextInput | 单行输入 | { value, onChange, placeholder, type, disabled?, autoFocus?, onKeyDown?, min?, max? } | | ui.TextArea | 多行输入(等宽字体) | { value, onChange, placeholder, rows, onKeyDown?, disabled? } | | ui.Select | 下拉 | { value, onChange, disabled?, children } | | ui.Button | 按钮 | { kind: 'primary'\|'secondary'\|'danger', disabled, onClick, children } | | ui.Switch | 开关 | { checked, onChange, disabled, label, title? } | | ui.Checkbox | 复选框 | { checked, onChange, disabled?, label? }(原生 input + 内联文案) | | ui.Radio | 单选框 | { checked, onChange, disabled?, label?, name?, value? }(onChange 收 value) | | ui.Card | 卡片容器 | { row?, children }row = 横向行卡) | | ui.StatusDot | 状态点 + 文案 | { color, text, extra } | | ui.Badge | 圆角徽标 | { children, tone?, outline? }(tone: info/success/warn/error/neutral) | | ui.Spinner | 加载旋转圈 | { size?, style? } | | ui.List / ui.ListItem | 结构化行列表 | List { children }ListItem { children, onClick?, title? } | | ui.Dialog | 模态对话框 | { open, title, onClose?, footer?, children, width? }(ESC 关闭 / Tab 焦点陷阱 / aria-modal / 关闭后焦点归还) | | ui.ErrorBoundary | 错误边界 | { title?, fallback?, onError?, children }——子组件崩溃渲染错误横幅,不白屏;把手/入口放边界外 | | ui.Tabs | 下划线标签页 | { items: [{id,label,badge?}], active, onChange }role=tablist + 方向键导航) | | ui.Banner | 横幅 | { kind: 'error'\|'saved'\|'warn', children } | | ui.EmptyState | 空态占位 | { text?, children } | | ui.toast / ui.ToastHost / ui.useToast | 一次性通知 | ui.toast(text, { kind?, ttlMs? }) 广播到已挂载的 ToastHost(每插件根挂一个) | | ui.h | React.createElement 别名 | (type, props, ...children) |

2. 声明式行渲染 ui.Rows

ui.Rows({
  fields: [
    { key: 'enabled', type: 'switch', label: '启用' },
    { key: 'apiKey', type: 'text', label: 'API Key', placeholder: 'sk-...', hint: '已设置则留空保持不变' },
    { key: 'timeoutMs', type: 'number', label: '超时(ms)' },
    { key: 'transport', type: 'select', label: '传输方式', options: [{ value: 'stdio', label: 'stdio' }, { value: 'http', label: 'http' }] },
    { key: 'args', type: 'textarea', label: '参数', rows: 4 },
  ],
  values: doc,                      // 当前表单对象
  onChange: (key, value) => patchDoc({ [key]: value }),
})

type 缺省为 textswitchSwitchtextareaTextAreaselectSelect,其余走 TextInput

3. 设置状态 ui.createSettingsStore + ui.useSettings

统一「加载/保存/busy/error/saved/revision 冲突」:

const store = ui.createSettingsStore({ get: () => call('get'), update: (p) => call('update', p) }, { savedTtlMs: 3000 })
// store.refresh() / store.commit(payload) / store.run(asyncFn) / store.get()/subscribe()

function MySection(props) {
  const s = ui.useSettings(store)   // { doc, revision, busy, error, saved, loaded, dirty }
  // s.doc 表单、s.error 错误、s.busy 禁用按钮、s.dirty 未保存更改、store.commit(...) 保存
}
  • refresh():调 get() 载入 doc(若返回对象含 revision,自动提取);成功清 dirty
  • commit(payload)busy 期间调 update(payload),成功后 refresh() 并闪现 savedsavedTtlMs 后自清,默认 3000ms);返回 update() 结果(undefined → true,兼容旧布尔用法);settings-conflict 会自动重载后报错。
  • run(fn):对「增/删/启停」这类动作做同样包裹(busy + 错误 + 成功后刷新);返回 fn() 结果(undefined → true)。
  • dirty:任何 set({ doc }) 表单编辑置 truerefresh()/成功写入后归 false——离开确认/未保存提示直接读 s.dirty

4. 注册 ui.section(config)

ui.section({
  id: 'my-plugin',                 // settings.section 的 id(导航键)
  order: 200,                      // 导航位置
  label: () => '我的设置',          // 导航文案(字符串或函数)
  inject: () => ({ api: { get: () => call('get'), update: (p) => call('update', p) } }),
  render: MySection,               // React 组件,接收 compose 后的 props(含 inject 返回的 face)
})

render组件(内部可调 ui.useSettings 等 hook),section() 会包上统一的 .sui-root 根并注入共享样式。

4.5 注册官方「插件配置」Tab 卡 ui.pluginCard(config)(rc7,v0.3.0)

rc7 官方教程 adding-a-settings-card 的新范式 = 插件配置 Tab 卡(keyed settings.plugin.item 按 settings 命名空间派发),持久化走官方 ctx.settingsScope(保存即生效、revision 栅栏、key 的 presence 标记覆盖)。pluginCard() 把「scope 绑定 + 状态机 + 卡壳」一次性封装好:

const card = ui.pluginCard({
  key: 'my-plugin',                 // 必填:settings 命名空间 = Tab 派发键(白名单 ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$)
  order: 10,                        // 可选:聚合层排序(keyed 槽自身无 order)
  locale: 'settings.myPlugin',      // 可选:透传 slot 字典 ns
  header: { title: '我的插件', desc: '一句话说明', meta: 'v1.0' },  // kit 渲染的卡头
  fields: [                         // 推荐:kit Rows 声明式表单
    { key: 'enabled', type: 'switch', label: '启用' },
  ],
  // 或 content: (ctx) => <element>  自由内容出口(ctx = { ui, store, scope, key })
  showIn: 'official-tab',           // 'official-tab' | 'settings-page' | 'both'
  chrome: 'full',                   // 'full'(kit 卡壳)| 'minimal'(只内容)
  api: { get, update },             // 可选:settings-page 无 scope 时的 fenced 回退
})
// card = { key, store, scope, showIn };card.store 为 settingsScope 后端
//   store.setField('enabled', false)  直接保存(save-as-you-go)
//   store.unsetField('enabled')       清回 composition 层
  • 传输 = ctx.settingsScope.bind({ namespace: key }):保存即生效、revision 栅栏与冲突恢复由官方 scope 保证,kit 不重造(校准注:勿把 host 侧 settings.watch/applies:'restart' 范式套到 pluginCard/settingsScope)。
  • settingsScope 是可选服务(kit 用 ctx.get('settingsScope') 探测,不进 inject 硬依赖):headless/无官方设置面缺席时,official-tab 路径给出清晰诊断(说明缺了什么、后果是什么)并拒绝注册;settings-page 路径可回退 fenced api
  • self-protectionkey 白名单校验 + 重复 key 先自查告警拒绝(官方 keyed 覆盖语义的自我保护)。
  • 卡壳由 kit 渲染(卡头/内容区/状态条),内容走 fields Rows 或 content 自由出口——对未来「统一管理 + 用户自定义壳」的能力位,靠服务/子槽注入,不 import 官方组件源码(对齐三原则)。

5. 通用设置统计卡(自动)

> ⚠ 侧栏入口局限:官方侧栏只有 sidebar.footer.action 一个可叠加槽(sidebar.workspaces / sidebar.settings 都是单例槽),所以插件想在侧栏加「独立图标席位 / 平行工作位 / 额外设置入口」当前做不到,只能往底部动作区追加(task-board 入口即走此槽)。设置页内容(settings.section)与浮层(shell.overlay)则完全可自由叠加。详见 [GUIDE.zh.md §4b](./GUIDE.zh.md)「官方槽位边界与已知局限」。

kit 会在「设置 → 通用设置」自动注册一张卡片:显示当前通过 ui.section()ui.overlay()ui.pluginCard()(v0.3.0 起并入)接入的配置界面总数,点击卡片展开可查看具体插件名(导航文案 + id,浮层带「浮层」前缀,pluginCard 显示其 key)与 kit 版本号。计数实时跟随注册/卸载(section()/overlay()/pluginCard() 注册时以 registrant: 'dsh-settings-ui' 标记 ledger 条目,统计卡按该标记过滤——ledger 只保留白名单字段,registrant 是其中之一)。卡片文案跟随官方 locale 服务(kit 注册 dsh-settings-ui 字典命名空间,zh/en;locale 缺席时退回内置中文),消费方无需做任何事。

6. 浮层面板(v0.2):ui.overlay + ui.Panel + ui.createPanelStore

插件在窗口最前层级(官方 shell.overlay 槽,frame 级、click-through 层)创建自己的悬浮页面:

function AssistantPanel(props) {
  const { ui, panel } = props
  return ui.h(ui.Panel, { title: '桌面助手', panel, onClose: () => {} },
    // body 内容(复用全部原子组件与 sui-* 类)
    ui.h(ui.Card, {}, '……'),
  )
}

const plugin = {
  inject: ['slots', 'settingsUi'],
  apply(ctx) {
    const ui = ctx.settingsUi
    // store 在 apply 里创建一次(与 settings store 同铁律)
    const panel = ui.createPanelStore({ persist: 'my-assistant.panel.v1', initiallyOpen: false })
    ui.overlay({
      id: 'my-assistant-overlay', order: 100,
      inject: () => ({ ui, panel }),
      render: AssistantPanel,
    })
  },
}
  • ui.overlay(config):注册 shell.overlay 浮层;config: { id, order?, label?, render, inject?, locale? },带 registrant 标记。
  • ui.Panel:浮层 chrome(.sui-overlay-panel)——标题栏拖拽移动右下角拖拽 resize、最小化(/)、关闭(×)、点击置顶(kit 全局 z 计数器,多浮层点谁谁在上)、窗口缩放自动 re-clamp。props:{ title, panel, onClose?, style?, children }
  • ui.createPanelStore({ persist?, initiallyOpen? }):状态 { open, minimized, pos, anchor, size, z }open/close/toggle/toggleMinimized/move/setAnchor/resize/setZpersist 传 localStorage key 时位置/锚点/最小化/尺寸跨刷新保留
  • ui.usePanel(store):React 快照 hook。
  • 浮层样式类(.sui-overlay-panel/-head/-body/-title.sui-pill-tabs.sui-overlay-item/-list.sui-mark 等)随共享样式自动注入;面板默认靠右上、右侧停靠自动补偿 --dsh-sidebar-width(该变量由 dsh-better-sidebar 发布为右侧面板宽度,拖动时逐帧更新、收起时被移除;变量缺失回退 0px,与官方 --dsw-* token 无关)。

快速示例(完整插件)

window.__ModuleLoader__.load({
  id: 'my-settings-plugin',
  factory: (require) => {
    const React = require('react')
    async function call(method, payload = {}) { /* 同源 fetch,返回 json.value */ }

    function MySection(props) {
      const { ui, store } = props   // store 来自 inject(在 apply 里创建一次,不能在渲染里建)
      const s = ui.useSettings(store)
      React.useEffect(() => { void store.refresh() }, [store])
      return ui.h(React.Fragment, null,
        ui.SectionHeader({ title: '我的设置', desc: '一句话说明' }),
        ui.Card({},
          ui.Rows({
            fields: [{ key: 'enabled', type: 'switch', label: '启用' }],
            values: s.doc ?? {},
            onChange: (k, v) => store.set({ doc: { ...(s.doc ?? {}), [k]: v } }),
          }),
        ),
        s.error ? ui.Banner({ kind: 'error' }, s.error) : null,
        s.saved ? ui.Banner({ kind: 'saved' }, '已保存并生效') : null,
        ui.h('div', { className: 'sui-actions' },
          ui.Button({ kind: 'primary', disabled: s.busy, onClick: () => store.commit(s.doc) }, '保存'),
        ),
      )
    }

    const plugin = {
      name: 'my-settings-plugin',
      inject: ['slots', 'settingsUi'],
      apply(ctx) {
        // 关键:store 在 apply 里创建一次,通过 inject 传进组件;
        // 不要在组件渲染函数里 createSettingsStore(每次渲染新建 store → useSettings 依赖变化 → 死循环卡死页面)。
        const store = ctx.settingsUi.createSettingsStore({ get: () => call('get'), update: (p) => call('update', p) })
        ctx.settingsUi.section({
          id: 'my-settings-plugin', order: 300, label: () => '我的设置',
          inject: () => ({ ui: ctx.settingsUi, store }),
          render: MySection,
        })
      },
    }
    return plugin
  },
})

---

Roadmap(未来规划)

  • 1.0.0 功能更新ui.describeForm(消费官方 settings.describe 的 schemastery schema → 自动渲染表单 + 用户覆盖标注 + redactSecrets 只写框 + revision 冲突处理),构建在已落地的 settingsScope 后端之上。
  • 工程补强.d.ts 与实现自动校验(公开发布后类型漂移风险升值)。
  • 能力边界(官方契约限制,kit 不做):侧栏平行席位(sidebar.workspaces / sidebar.settings 为单例槽);浅色主题。
  • 已知限制(文档化):ToastHost 多实例同显(每插件只挂一个 host 即规避)。
  • 维护承诺:上游 rc 漂移时按既定方法论重跑契约核对;反馈请走 GitHub 讨论区评论。

---

家族单轨说明(rc.7 对齐)

rc7 官方把「配置卡上车」让给其插件配置 Tab(keyed settings.plugin.item,官方教程 adding-a-settings-card 已不再提 settings.section)。kit 的定位随之升级为「官方范式之上的加速层」:

  • 新品配置界面一律走 ui.pluginCard()(官方 Tab)——统一 keyed 卡 + settingsScope 轨道,避免「设置页一半、插件 Tab 一半」的两轨碎片化。
  • 存量 section() 仍完全支持,无需迁移(rc6 环境/传统设置页/headless 回退照常可用)。
  • 对齐三原则:①契约层只用官方槽/服务(settings.plugin.item / settings.section / shell.overlay / settingsScope / locale),绝不平行另造 settings 表面或 allowlist;②视觉层 .sui-* 全部解析自官方 token(--dsw-alias-* / --ds-font-family-code / --dsh-sidebar-width),硬编码仅作 token 缺失兜底;③代码层不 import 官方卡 chrome(client bundle 纯度门禁值导入),对齐靠契约+token+观感,卡壳/表单/状态机/a11y kit 自建但 token 对齐。