Back to home@zhm20001

dsh-plugin-palette-board

本项目为 DeepSeek Harness web 控制台带来一块 2D 调色盘应用板。唤出悬浮面板,即时搜索、分类过滤、全键盘导航,把散落在侧栏与浏览器里的插件页面收进一张可自定义的卡片网格。

Stars
0
Language
TypeScript
Created
Aug 31, 2026
Updated
Aug 31, 2026
GitHub repo

Introduction

dsh-plugin-palette-board

Raycast / Launchpad 风格的 2D 应用调色盘:⌘K 一键唤出的全键盘悬浮应用网格,DeepSeek Harness (dsh) web 控制台插件。

dsh Version Node Version Zero Dependency License

简体中文 | English


📖 简介

dsh-plugin-palette-board 为 DSH (DeepSeek Harness) web 控制台带来一块 2D 调色盘应用板⌘K / Ctrl+K / Alt+Space 唤出玻璃拟态悬浮面板,即时搜索、分类过滤、全键盘导航,把散落在侧栏与浏览器里的插件页面收进一张可自定义的卡片网格。

卡片目录固化在本地 JSON 文件(~/.dsh/storages/palette-board.json),不播种、不预知任何插件——用面板上的「注册」手写卡片、点「扫描」一键收编侧栏入口,或让第三方插件经 ctx.paletteHub 服务入驻。调色盘服务面向所有 client 插件开放,软依赖接入、一行注册。


📸 界面预览

插件经宿主 Slot 系统在侧栏底部注册入口(lib/client.js 为轻量零依赖浏览器 bundle),面板本体作为全屏覆盖层直接挂载 document.body

主面板(fresh 纸感主题)

Palette Board (fresh theme)

即时搜索 + 动态分类过滤 + 卡片网格:悬停「⋯」溢出菜单置顶 / 调序 / 编辑 / 删除,头部按钮循环三档尺寸、一键切换双主题,状态条实时显示目录同步健康度。

扫描收编 —— 把侧栏入口变成卡片

Scan modal

扫描按钮读取侧栏 footer 动作 slot 的注册表,列出尚未收编的入口,一键转为点击即触发的自定义卡片。

注册自定义插件 —— 零代码收编任意页面

Register modal

运行时注册 / 编辑弹窗:填一个「页面链接」即可把任何插件的 web 页面收进调色盘,或手写 footerTrigger 卡片转触发侧栏按钮。

暗色玻璃主题(glass)

Palette Board (glass theme)

fresh(清新纸感,默认)与 glass(暗色玻璃拟态)双主题,头部按钮切换、localStorage 记忆,CSS 变量驱动零重渲染成本。

挂载点一览

挂载点位置展示内容
sidebar.footer.action侧边栏底部操作区调色盘常驻唤出按钮(rail 收起态为圆形图标)
document.body portal全屏覆盖层调色盘主面板(搜索 / 分类 / 网格 / 注册 / 扫描)
右下角浮动球常驻 meta 入口可拖拽换位、位置记忆、面板打开时自动隐藏
host 静态路由 /palette-board/guide新标签页内置接入指南(双语)

✨ 核心特性

  • ⌨️ 全键盘交互⌘K / Ctrl+K / Alt+Space 唤出,Esc 关闭, 进入网格,方向键 / Home / End 在卡片间移动(roving tabindex),回车执行;IME 组合期间不抢键。
  • 🗂️ 目录固化本地 JSON~/.dsh/storages/palette-board.json 双段结构(entries 用户手写卡片 + layout 插件卡片布局),编辑 / 删除 / 新增 / 置顶 / 调序实时回写,刷新重启原样恢复,可直接手工编辑或备份。
  • 🛡️ 故障告警三分列:PUT 回写失败、启动读取失败(文件不动,绝不覆盖没读到过的文件)、超上限拒收(200 卡 / 256KB,宿主存储层裁定并映射 HTTP 413)——状态条分列提示,删减卡片后自动恢复。
  • 🔌 paletteHub 服务化插拔:client 半以 cordis 服务发布 ctx.paletteHub,其他插件软依赖接入、一行注册卡片;palette-board 停用时各方安然无恙。
  • 🔍 扫描收编:读取 sidebar.footer.action slot 注册表,一键收编现存侧栏入口为自定义卡片;口径与 footerTrigger 触发区同源。
  • 🧹 筛模式:漏斗开关隐藏失效卡(触发按钮不在侧栏、链接 404/410),只影响显示不动目录。
  • 📏 尺寸三档:紧凑 / 标准 / 宽大循环切换,面板与网格随档伸缩,localStorage 记忆。
  • 🌗 双主题fresh 纸感 / glass 玻璃一键切换;界面 zh / en 双语跟随 dsh 语言偏好。
  • 模态可访问性role="dialog" + aria-modal + Tab 焦点陷阱 + Esc 关闭 + 焦点归还。
  • 克隆即用lib/ 构建产物随仓库提交、运行时零依赖——克隆后无需 install / build 即可挂载。

🚀 快速上手

1. 环境准备

  • Node.js >= 20
  • dsh web >= 0.1.2-alpha.1(新版宿主,见「架构备注 · 版本基线」)

2. 克隆并挂载

# 1. 克隆。lib/ 构建产物随仓库提交、运行时零依赖——克隆即可挂载,
#    普通使用者不需要 install / build;仅当你改动 src/ 时才需要:
git clone https://github.com/zhm20001/dsh-plugin-palette-board.git
cd dsh-plugin-palette-board

# 2. 挂载进 dsh web profile:profile package.json 的 dependencies 加
#    "dsh-plugin-palette-board": "link:/path/to/dsh-plugin-palette-board"
#    并把 "dsh-plugin-palette-board" 加入 dsh.profile.bundles 数组,然后:
cd ~/.dsh/profiles/web && pnpm install

# 3. 重启 dsh web(host 半变更需要重启;client 半改动热加载,硬刷新浏览器即可)

bundle 通道会自动合并本包 cordis.patch.yml 的 insert 行,无需改 profile 自己的 patch 文件。手动通道(把 insert 行写进 profile 的 cordis.patch.yml)也可以,但两种通道不要同时用

3. 首次使用

浏览器里 ⌘K / Ctrl+K / Alt+Space 唤出调色盘(侧栏设置按钮上方的调色盘入口、右下角浮动球同样可以)。目录一开始是空的——用面板上的「注册」手写卡片、点「扫描」收编侧栏入口,或让第三方插件经 ctx.paletteHub 入驻(见下文「第三方插件接入」)。


⚙️ 目录文件与存储

类别存储位置说明
卡片目录(唯一真源)~/.dsh/storages/palette-board.json{ entries, layout } 双段:entries 为用户手写卡片,layout 为插件卡片布局记录
主题 / 尺寸 / 浮动球位置浏览器 localStorage纯 UI 偏好,不入目录文件

💡 目录可直接手工编辑或备份;损坏时 host 先备份 .bak 再重置为空目录,v1.6–v1.7 纯数组旧格式自动迁移。写入仅经回环 same-origin 的 entries API,带 Host 围栏。


🔌 第三方插件接入(paletteHub,软依赖)

调色盘的全部注册面都在浏览器侧务必用软依赖——把 'paletteHub' 写进模块级 inject 是硬契约,palette-board 停用时你的插件会永远 pending 并让整个 web GUI 启动失败:

// my-plugin/src/client/index.tsx
import type {} from 'dsh-plugin-palette-board/client'  // 纯类型导入:触发 ctx.paletteHub 类型合并
import type { Context } from '@deepseek-ai/cordis'

export function apply(ctx: Context): void {
  // 软依赖:palette-board 未启用时本插件照常激活,回调零开销跳过
  ctx.inject(['paletteHub'], (sctx) => {
    const unregister = sctx.paletteHub.register({
      id: 'my-plugin:telemetry',
      title: 'Container Live Telemetry',
      description: 'Real-time Docker & GPU kernel metrics inspector.',
      category: 'Developer Tools',
      color: 'emerald',
      badge: 'v1.4',
      actionLabel: 'Inspect',
      onClick: () => { /* 打开你的 UI / 调用你的服务 */ },
    })
    sctx.effect(() => unregister, 'my-plugin: palette entry')  // 停用 / HMR 时自动注销
  })
}

完整示例见 examples/demo-plugin.tsx要点

  • ctx.inject(['paletteHub'], …) 的子作用域在服务出现时执行回调、palette-board 停用时整体销毁——两种状态都安全,不要改成模块级 inject = ['paletteHub']
  • register 的 disposer 务必包在 sctx.effect 里,否则 fiber 卸载后注册残留,重激活会重复。
  • import type {} 编译期擦除:不产生运行时依赖,不触碰 client bundle 纯度门(跨插件协作只走 cordis 服务,禁止值导入)。
  • icon 收任意 ComponentType<{ className?: string }>(24x24 线性风格最佳);不传则用自带 Sparkles。
  • 零改动通道:不想写代码?Register 弹窗填「页面链接」即可收编任何插件的 web 页面;或点扫描按钮一键收编侧栏入口。
  • 插件卡片本体是会话级的:刷新后由插件重新激活时再次注册(插件还在就自动回来);你的置顶 / 调序以布局记录落盘固化,插件回来原地生效。想要永久入口,用 Register 弹窗、扫描收编或手工编辑目录文件。

⌨️ 快捷键

按键行为
⌘K / Ctrl+K唤出 / 收起调色盘
Alt+Space唤出 / 收起调色盘
Esc关闭(IME 组合期间与可编辑元素内不抢;弹窗内由弹窗自监听关闭)
(搜索框)进入卡片网格
/ Home / End网格内移动焦点(首行再 回搜索框)
Enter执行焦点卡片

🛠️ 本地开发

pnpm install     # 仅开发需要(运行时零依赖,克隆即用)
pnpm build       # tsc 声明 → lib/types,tsdown → lib/index.js + lib/client.js
pnpm typecheck   # tsc --noEmit
pnpm test        # node:test 单测(宿主存储层 / 扫描纯函数,无外部依赖)
pnpm watch       # 监听构建(配合 dsh 宿主 client HMR)

⚠️ 改动 src/ 后记得 pnpm build 并把 lib/ 产物一并提交——仓库以 lib/ 产物直接分发。

dsh-plugin-palette-board/
├── package.json               # dsh.client 声明 + exports["./client"] + dsh.bundle.patch
├── cordis.patch.yml           # bundle patch:insert 挂载行(id: palette-board)
├── src/
│   ├── index.ts               # host 半:cordis 插件 + guide / entries 路由
│   ├── entries-store.ts       # 卡片目录 JSON 文件存储(校验/迁移/原子写/备份)
│   ├── guide.ts               # 接入指南静态页(双语,host 半服务)
│   ├── types.ts               # PaletteItem / PaletteHubService 共享类型面
│   └── client/                # client 半:面板 / 注册 / 扫描 / 主题 / i18n / paletteHub 服务
│       ├── index.tsx          # 入口:provide 服务 + locale 词典 + 快捷键 + portal + 侧栏入口
│       ├── PaletteBoard.tsx   # 主面板(搜索/分类/网格/键盘导航/筛模式/状态条)
│       └── …                  # 完整清单见 CONTEXT.md 领域术语表
├── test/                      # node:test 单测
└── lib/                       # 构建产物(随仓库提交)

🧩 架构备注

  • 双半结构:遵循真实 dsh web 插件规范——Node 侧 cordis 插件(inject = ['webServer', 'webRuntime'])+ 浏览器侧 __ModuleLoader__ client bundle,经 dsh.client 声明 + exports["./client"] + dsh.bundle.patch 挂载。
  • 产物契约lib/index.js(host 半,Node ESM);lib/client.js(client 半,浏览器 CJS 闭包工厂),require 只允许平台模块表词(本插件只用到 react / react/jsx-runtime / react-dom/client),其余全部内联。
  • paletteHub 只在 client 半:host 半承担插件身份、/palette-board/guide 静态路由与 entries API。host 半不要 default 导出(loader 的 unwrapExports 会丢掉模块级 name / inject 声明)。
  • store 纪律createPaletteHubStore() 每激活创建一实例(禁止模块级单例);快照不可变、引用稳定,配 useSyncExternalStore 消费。
  • portal 挂载:调色盘是全屏覆盖层,不进 shell slot 布局——直接向 document.body 挂 root(data-dsh-palette-board 锚点),附 body 重组守卫与根级错误边界。
  • 版本基线:按 dsh 0.1.2-alpha.1 的模块表与扫描约定实现,仅适配新版本 dsh,不做老版本兼容——扫描收编依赖宿主 slots 服务的公开枚举面,老版本宿主上守卫会让扫描结果为空(其余功能不受影响);如遇扫描始终为空,请先把 dsh 更新到当前版本。

📄 License

本项目基于 MIT License 开源。host / client 双半源码、构建产物(lib/)、接入指南页与全部文档同许可。