MoYu-CN/dsh-theme-pack ↗★ 0

dsh-theme-pack

提供四款一键切换的界面主题皮肤,基于官方主题机制构建。 适合想要美化界面且不希望破坏布局或修改DOM的用户。

套件
dsh-theme-pack
相容性
待驗證
版本
0.1.0
授權
MIT
最近更新
2026年10月12日

同名套件的其他儲存庫

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:MoYu-CN/dsh-theme-pack

dsh-theme-pack —— 主题皮肤包

English summary — A theme skin pack for DSH (DeepSeek Harness): Celadon and Mist (light), Ocean and Violet (dark), plus a Default card that goes back to the stock look. Install: reference this folder from your DSH profile ("dsh-theme-pack": "link:/dsh-theme-pack"), then restart DSH for the host half and reload the page for the client half — the picker shows up in Settings → General. Highlights: registers official ctx.theme alias tokens only — no layout changes, no DOM surgery, no prompt injection. Language: the settings UI follows the host language setting — Chinese and English dictionaries are both built in. Model-facing prompts stay in Chinese by design. License: MIT.

给 DSH 界面换一套配色:青瓷 / 晨雾(浅色)、深海 / 紫夜(深色),外加一项「默认」切回官方外观。

它做的事很小、很干净:只用官方 ctx.theme.register() 注册 alias token 的颜色值,不改布局、不动 DOM 结构、不注入任何提示词。


1. 它是什么

一个 DSH(DeepSeek Harness)插件,分两半:

半文件干什么
Hostlib/index.js把客户端 bundle 经 webServer 路由发给浏览器;皮肤选择读写 /skin.json,经 GET/POST /__themepack/skin 暴露
Clientlib/client.js注册 4 套主题(ctx.theme.register),并在「设置 → 通用」里加一行皮肤选择器
  • 客户端路由:/__themepack/client.js
  • 皮肤路由:/__themepack/skin(GET 读 / POST 写,只接受同源请求)
  • 数据文件:/skin.json,内容形如 {"skin":"celadon"}
  • 数据目录解析顺序:DSH_THEME_PACK_DATA → 由 process.argv[1] / process.execPath 反推安装根 → DSH_INSTALL_DIR → 兜底插件源码目录往上两级

2. 怎么装

插件的安装由 DSH 的 bundle / 插件管理器负责,本仓库只提供可被安装的插件目录。通用做法(在 DSH 运行的那个 profile 里):

  1. 把本目录放到一个稳定位置(例:/dsh-theme-pack)。
  2. 在 profile 的配置里以本地路径引用它,例如 "dsh-theme-pack": "link:/dsh-theme-pack"。
    • 用 link: 而不是 file::不拷贝、直接软链,改代码立刻生效。
  3. Host 半需要重启 DSH 才换模块代际;客户端半需要刷新页面。
  4. 装好后进「设置 → 通用」,应能看到「Theme skin / 主题皮肤」那一行,点卡片即可切换。

三个安装期的坑(都实测踩过)

  • 别声明 peerDependencies。 桌面端启动时会把 link: 插件搬进 profiles/.generations/live/,迁移中做 peer 校验;宿主的 @deepseek-ai/cordis 在 staging closure 里解析不到 → 整批迁移失败、profile maintenance 被冻结。cordis 是宿主提供的运行时,插件不该声明它。
  • 别 import @deepseek-ai/schemastery。 link: 形态下插件从源码目录解析依赖,那里没有 node_modules → ERR_MODULE_NOT_FOUND 会拖垮整批启动。所以本插件的持久化走自建文件 + webServer 路由,不挂 cordis 设置系统。
  • 别硬编码安装路径。 换台机器、换个安装位置就会写到不存在的盘上。数据目录一律从运行环境反推(见上)。

3. 有哪些主题

id名字配色
official默认 / Default切回官方外观(theme.setTheme("system"))
celadon青瓷 / Celadon浅色,主色 #0f766e,底色 #f6f8f7
mist晨雾 / Mist浅色,主色 #b45309,底色 #f8f5f0
ocean深海 / Ocean深色,主色 #6ea8fe,底色 #0d1522
violet紫夜 / Violet深色,主色 #b79cff,底色 #16121e

每套主题覆盖 72 个 token:--dsw-alias-*(背景 / 文字 / 按钮 / 交互态 / Markdown)、--dsw-specific-*(侧栏 / 气泡 / 输入框)、--dsw-linear-gradient-* 与 --shiki-token-*(代码高亮)。

中英文案随界面语言切换(settings.themePack 命名空间,zh / en 内联在 lib/client.js)。

顶部「默认」卡片与官方 AppearanceRow 联动:在官方那一行切走时,这里的选中态同步跳回「默认」;但持久化的皮肤不丢,重启仍会恢复。

4. 已知边界

  • 只覆盖颜色 token,不改布局。 主题里没有任何尺寸 / 结构类改动,所以「布局长得不对」不是本插件的问题。
  • 依赖宿主的 token 名。 主题注册的是 alias token;宿主换版本如果改了 token 名,未被覆盖的那些会退回官方值(不会报错,只是看着不一致)。
  • 风格是「注册一套主题」,不是「覆盖官方那套」。 所以官方外观那一行切 light/dark 与本插件互不干扰,两边各记各的。
  • 皮肤是每台机器 / 每份数据目录一份(skin.json),不跟随 profile 同步。
  • 路由无内置鉴权,靠同源校验(Sec-Fetch-Site / Origin / Referer 与 Host 对齐);因此 DSH Web 必须绑回环地址。
  • 本仓库自带测试,见 §5「怎么跑测试」。 覆盖:4 套主题都能注册、注册的 token 只有颜色类且名字都在宿主注册表内、中英字典键集合一致、启用/禁用时路由与 tapIndex 的注册与注销、skin.json 读写的降级路径,外加一组「把已知有病的坏数据喂给同一批校验函数」的对照。

5. 怎么跑测试

测试在 test/ 下,从干净检出就能跑:插件位置由 import.meta.url 反推(test/_harness.mjs),没有写死的安装路径;数据目录只走 DSH_THEME_PACK_DATA —— 没设就自建临时目录并在进程退出时删掉。

node test/theme-pack.test.mjs             # 主验收(下面说明为什么通常要配 DSH_APP_DIR)
node test/theme-skin-concurrent.test.mjs  # 并发写皮肤:临时名不撞车、`.tmp` 不残留(只要 node,不用 DSH_APP_DIR)
node test/parse-check.mjs                 # 真解析校验(含 test/_broken-control.js 这个必须 FAIL 的对照组)

为什么这一份常常需要 Electron-as-node

theme-pack.test.mjs 有几节刻意用部署里真的东西,而不是自己抄一份替身:

节借的是什么没配 DSH_APP_DIR 时会怎样
②元素树 / ⑤偏好层安装目录里的真 react 与 react/jsx-runtime整份脚本 SKIP 并退出 0
④ token 审计部署自带 @deepseek-ai/dsh-client-* 的 lib/client.js(抽 --* 声明)同上

所以要么给它安装目录:

$env:DSH_APP_DIR = "\resources\app"     # 例:\resources\app
node test/theme-pack.test.mjs

要么直接用 Electron-as-node 跑(这时 process.execPath 就是 DSH Desktop.exe,脚本自己能反推出安装根):

$env:ELECTRON_RUN_AS_NODE = "1"
& "\DSH Desktop.exe" test/theme-pack.test.mjs

⚠ 用 & 直接调 .exe 时 PowerShell 不会等它,退出码是空的 —— 那种「没输出」不是证据。要拿退出码请用 Start-Process -Wait -PassThru。

拿不到安装根时脚本会打印 SKIP … 说明并退出 0 —— 是「跳过」,不是「通过」。 别把退出码 0 当成验收通过。

环境变量

变量要不要设说明
DSH_APP_DIR见上DSH 安装目录里的 resources/app。不设时依次试 DSH_INSTALL_DIR → 由 process.execPath 反推 → 常见默认安装位置。
DSH_THEME_PACK_DATA可选插件的 skin.json 所在目录。不设就自建临时目录并自动清理。
DSH_TEST_OUT_DIR可选机器可读产物 theme-findings.json 写到哪。不设就写系统临时目录 —— 测试绝不往仓库里写文件。
DSH_THEME_PACK_SRC可选把「被测插件」整个换成另一份副本。这是对照组开关:指向一份故意改坏的副本时,这份验收必须变红。

对照组:证明判据会失败

主脚本第 ⑥ 节会把已知有病的坏数据喂给第 ②③④ 节用的同一批校验函数,必须全部报病(实测 8 条对照全中):

喂进去的坏数据期望报出的病
--dsh-sidebar-inline-padding: 12px(官方的布局类 token)不是颜色类 token
--dsw-alias-bad name!: #fff变量名不符合 CSS 自定义属性语法
--dsw-alias-bg-base:(空白值)值为空
--dsw-alias-bg-base-secondary(部署里不存在的名字)部署里没有这个名字(会静默失效)
缺一条 en 的字典中英键集合不一致
重名注册 / 未注册 id 的 setTheme假 ctx 会像宿主一样拒绝

另外 test/_broken-control.js 是语法上就写坏的对照文件,parse-check.mjs 对它必须报 RESULT … :: FAIL;若它也 OK,说明那份解析检查是空转的假证据。

还有一条端到端的对照:拿一份改了 className 的插件副本跑同一份验收,必须变红。做法(副本只能建在插件目录之外):

# 1) 在插件目录之外复制一份完整插件(lib/client.js 与 lib/index.js 都要,index.js 会被 import)
# 2) 在副本里把 client.js 的 `className: "tp-group",` 改成 `className: "tp-group-BROKEN",`
$env:DSH_THEME_PACK_SRC = ""
node test/theme-pack.test.mjs

实测:主组 EXITCODE=0 / PASS=88 / FAIL=0;对照组 EXITCODE=1 / PASS=87 / FAIL=1, 红的那条正是 FAIL 第一层是一组 .tp-group :: tp-group-BROKEN。

并发写皮肤那一份(theme-skin-concurrent.test.mjs)自带对照组:test/_before-concurrent-tmp/ 是共写同一个 ${file}.tmp 的改动前快照。指向它时那份测试必须变红:

$env:DSH_THEME_PACK_SRC = "$PWD/test/_before-concurrent-tmp"
node test/theme-skin-concurrent.test.mjs     # 必须 FAIL

实测:主组(真源码)EXITCODE=0 / PASS=4 / FAIL=0;对照组 EXITCODE=1 / PASS=2 / FAIL=2 (红的两条是「每次 POST 都回 200」与「落盘是某一次的完整内容」—— 共写 tmp 会把文件写成半截)。

⚠ 只把临时名改成唯一名不够:Windows 上两个 rename 同时覆盖同一个 skin.json 会有一个 EPERM 失败、失败那次的 .tmp 还留在数据目录里(实测 30 轮 × 4 并发里 37 次抛错)。 所以 writeSkin 同时做了两件事:唯一临时名 + 串行队列。

6. 兼容性

  • DSH Desktop 2.0.17(官方包 @deepseek-ai/* 0.2.0-rc.2)实测可用。
  • 需要宿主提供 theme / slots / locale 三个服务,以及官方 @deepseek-ai/dsh-client-locale、@deepseek-ai/dsh-client-ui-slots、@deepseek-ai/dsh-client-ui-theme(见 package.json 的 dsh.client.inject)。
  • 运行时为 Electron 自带的 Node(实测 v24.18.1);engines.node 标为 ^22.19.0 || >=24.0.0。

7. 目录结构

dsh-theme-pack/
├── lib/
│   ├── index.js        # host 半:路由 + skin.json 读写
│   └── client.js       # 客户端 bundle:4 套主题 + 设置行
├── test/               # 见 §5「怎么跑测试」
│   ├── _harness.mjs            # 插件路径 / 数据目录 / 官方包定位(各测试共用)
│   ├── theme-pack.test.mjs     # 主验收(①②③④⑤⑥ 六节)
│   ├── theme-skin-concurrent.test.mjs  # 并发写皮肤(`.tmp` 不撞车 / 不残留)
│   ├── _before-concurrent-tmp/ # 上面那份的改动前快照(对照组:必须变红)
│   ├── parse-check.mjs         # 真解析校验
│   └── _broken-control.js      # 故意写坏的对照组(必须 FAIL)
├── icon.svg
├── cordis.patch.yml    # bundle 注入声明
├── package.json
├── LICENSE             # MIT
└── README.md

8. 贡献

  • 提 PR 前请说明「改了什么、为什么这么改、怎么验证的」;只改颜色值的 PR 最好附上前后对比截图。
  • 改主题 token 时注意 lib/client.js 里 THEMES 与 lib/index.js 里 SKIN_IDS 是两处对齐的清单,新增皮肤必须同时改。
  • 不要引入 node_modules 依赖,不要声明 peerDependencies(理由见上)。

9. License

MIT —— Copyright (c) 2026 MoYu-CN