Theme Skins for DeepSeek Harness
一个 DeepSeek Harness 桌面版插件,把 Codex-Skins 那套"壁纸 + 半透明面板"的主题系统搬到 Harness 上,并提供三件事:
- 主题管理界面:设置 →「主题皮肤」里的主题库、当前主题、壁纸可见度、重命名、删除、导出。
- 导入 Codex 主题:扫描本机
CodexDreamSkin\themes,或导入任意主题文件夹 / 分发 ZIP。 - AI 主题设计:用一句话(可配一张图、一段 MP4)生成或改造一套主题,直接入库。
同时把能力暴露成 skin_theme 工具,智能体在对话里也能列出、导入、应用、生成皮肤。
效果
| 蜡笔小新-春游(自带 · 动态) | DeepSeek-鲸鱼娘(自带 · 静态) |
|---|---|
![]() |
![]() |

上面两张是插件自带的默认主题(首次启动自动入库,删掉后不会再自动回来), 第三张是设置 →「主题皮肤」的界面:主题库、配色摘要、当前使用中的标记都在这里。
自带主题
安装后首次启动会把两个自带主题复制进皮肤库(<DSH_HOME>/skin/themes/):
| 主题 | 类型 | 说明 |
|---|---|---|
| 蜡笔小新-春游 | 动态 | 樱花树下野餐的浅色主题,带 background.mp4 循环播放 |
| DeepSeek-鲸鱼娘 | 静态 | 浅色插画壁纸,蓝色系半透明面板 |
它们只是复制一份进你的皮肤库:改色、重命名、删除都只影响你自己的副本,
删掉之后不会再被自动补回来(state.json 里的 seeded 标记只允许这样做一次)。
安装
包名(也是加载器解析的模块名)是 @xxue-z/dsh-skin:scope 属于作者本人,避免和别的插件重名。
插件用宿主自带的 plugin_manager 安装,装的是目录;Harness 按 package.json 的 name
去 profile 的 node_modules 里解析它,所以「装哪个名字」和「代码里叫什么」必须一致。
推荐:从 Release 附件安装
# 1. 从 Releases 下载 deepseek-harness-skin-vX.Y.Z.tar.gz 并解压到一个固定目录
# 例如 C:\ProgramCustom\Tool\dsh-skin-1.0.7
# 解压后该目录里应当直接有 package.json / index.js / lib/,没有多余的一层
# 2. 在 Harness 会话里安装
plugin_manager install_bundle C:\ProgramCustom\Tool\dsh-skin-1.0.7
附件的结构和内容由 CI 按 package.json 的 files 生成,只有运行时文件
(入口、lib/、locale/、patch、图标)和 README/CHANGELOG,并附带 .sha256 可校验。
也可以:用仓库地址安装
插件管理器的输入框接受 https://github.com/xxue-z/deepseek-harness-skin 这类仓库地址,
它会转换成 github:xxue-z/deepseek-harness-skin,由 pnpm 拉取某个提交的快照。这条路能用,
但要知道它的含义:
- 拉的是提交快照,不是发布版本;版本号取自仓库里的
package.json,未必对应某个标签; - pnpm 会做内容哈希,但没有我们发布的
.sha256和 Release 说明; - 装完后
node_modules/@xxue-z/dsh-skin是一份真实拷贝,与 Release 方式互不相干。
两种方式选一个即可,不要对同一个包名同时保留两份来源,否则加载器和 pnpm 会指向不同目录。
安装之后
需要重启一次 DeepSeek Harness:Host 半走 Loader 加载,Web 半随模块图下发;重启后刷新页面。 设置 →「主题皮肤」页头显示版本号,点开可检查更新。
升级
| 当前安装方式 | 做法 |
|---|---|
| Release 目录 | 解压新版本到另一个目录 → 卸载旧条目 → install_bundle 新目录 → 重启 |
| 仓库地址 | 在插件管理器里卸载后重新安装,或指定新版本,然后重启 |
| 设置页「下载并更新」 | 直接点:由 Host 取包并解包、替换插件目录,然后重启一次应用(版本号在启动时读取) |
「下载并更新」只对插件自己目录的安装有效(profile 里是真实拷贝)。如果 profile 里那条是指向
安装目录的符号链接 / junction(install_bundle 就是把目录链接进来),点它会提示「当前插件目录是
符号链接,请到那个目录执行 git pull 或重新 install_bundle」——不会替换任何文件。仓库安装的更新走:
cd <仓库目录>
git pull # 或 git checkout v1.0.13
然后重启应用。下载由 Host 完成(浏览器直连 GitHub 会被 CORS 挡住),所以按钮能否用取决于 Host 进程 能否访问 GitHub;Host 也连不上时会给出具体原因,可改用「打开发布页」手动下载附件,再按「Release 目录」 那一行安装。
升级到改了包名的版本(例如从 @local/dsh-skin 到 @xxue-z/dsh-skin)时,必须先卸载旧名字那条:
loader 里残留的旧条目会继续去找一个已不存在的目录,表现为
failed to import: ENOENT ... index.js。
卸载
在插件管理器里点「卸载」,或在 Harness 会话里:
plugin_manager remove_bundle @xxue-z/dsh-skin
卸载后再删掉你解压出来的安装目录,并重启一次应用让 loader 释放那条配置。
卸载不会删除皮肤数据(<DSH_HOME>/skin/),重装后主题会直接回来。
插件显示「包元信息错误 / ENOENT」
这是 loader 里那条配置指向的目录已经不存在或为空(常见于手动删掉安装目录、或换过安装路径)。
处理:先把这条卸载掉(界面里的「卸载」或 plugin_manager remove_bundle),再按上面的步骤重新安装,
最后重启。只重装不卸载会留下两条指向不同目录的配置,报错依旧。
使用
设置 →「主题皮肤」:
- 扫描本机主题:读取
%LOCALAPPDATA%\CodexDreamSkin\themes\*(macOS 为~/Library/Application Support/CodexDreamSkin/themes),把每个主题转换入库。 - 导入 ZIP / 从文件夹导入:Codex 主题包(
theme.json+theme.css+ 壁纸)直接拖进来。 - AI 设计:写一句描述,可再附一张图片和一段 MP4(各一份),生成草稿并入库:
- 只给图片 → 静态主题,图片就是壁纸;
- 只给视频 → 动态主题,浏览器抽视频第一帧当壁纸与静态回退,视频循环播放;
- 两个都给 → 图片当壁纸、视频当动态背景。
「设计模型」是下拉框,读的是宿主实时的模型目录(与聊天框同一个注册表,按供应商分组);
遇到不能看图的模型会自动只发文字并提示。高级里只有一项「主题名」(决定皮肤的
name); 氛围和额外要求直接写进描述——它们是同一句话,拆成三个输入框只会让模型分不清主次。 生成失败会自动回退到离线配色,界面会明确标注。
- 卡片详情里可以调 壁纸可见度(0–100%,等价于 Codex 的 transparency),
可以「按壁纸重新取色」(给没有
colors的 preset 补配色)、重命名、导出 JSON、删除。 重命名只改显示名称:id、目录、壁纸/视频文件都不动,所以正在使用的主题也能改; 从 Codex 主题包导入的皮肤,下次「扫描本机主题」会用包里的名字覆盖回去。 - 动态主题设置:页头按钮,弹窗里三行——自动播放、静音、播放/暂停快捷键。 快捷键走宿主自带的快捷键系统,应用不在前台也能用,并且会出现在 「设置 → 快捷键」里,可以自己改键位(弹窗里的键帽会自动跟着变)。
- 高级设置 → 视频列表:动态主题设置弹窗底部,默认折叠。打开后可以向当前主题
(不论是不是动态主题)注入一个视频标签,按某个文件夹的顺序逐集播放:
- 启动注入视频列表:开关本身不落盘,每次启动客户端都从「关」开始, 用快捷键或在设置里再打开即可;关掉即移除标签,动态主题回到自带 MP4,静态主题一张不留。 任何时刻都只有一个背景视频元素——视频列表顶替主题自带的那个,不会叠两层。
- 视频列表文件夹:按资源管理器的自然顺序播(
第2集在第10集前),只认一层目录里的mp4/m4v/webm/ogv/mov;文件夹增删文件会在下次读取时生效。 - 播放倍速 0.5×–2×、小窗口播放(画中画)(浏览器不支持时开关禁用)。
- 历史播放文件夹:最近 20 个,双击即可再次使用。
- 播放进度:按「文件名 + 大小 + 修改时间」记住播到第几个、第几秒;重开开关或重启后从那里续播, 文件被删则回到原序号位置。总开关仍然每次启动关闭。
- 播放/暂停沿用
Ctrl+Alt+P与静音开关,对视频列表同样生效;列表开关是独立的skin.togglePlaylist,默认Ctrl+Alt+L。
- 补全动态背景:动态壁纸上线之前导入的皮肤只留了
codex.video元数据, 详情里会提示「原主题带 MP4」,按钮只重读background.mp4挂上去, 不动配色和透明度(重新扫描也行,但那会把整份文档按主题包重写)。
对话里(skin_theme 工具):
skin_theme {"action":"list"}
skin_theme {"action":"import","path":"C:\\Users\\you\\AppData\\Local\\CodexDreamSkin\\themes\\studio.crimson-mecha-nova"}
skin_theme {"action":"design","description":"赛博霓虹,暗紫底色"}
skin_theme {"action":"design","description":"夏日午后,清爽一点,文字对比度要高","videoPath":"D:\\clips\\summer.mp4","name":"夏日"}
skin_theme {"action":"apply","id":"skin.crimson-mecha-nova"}
skin_theme {"action":"transparency","id":"skin.crimson-mecha-nova","transparency":45}
它是怎么生效的
- 配色:
ctx.theme.register({id, colorScheme, tokens})把主题注册进 Harness 主题注册表,setTheme(id)切换;透明度变化用ctx.theme.overrideTokens()叠层,不重注册(避免闪烁)。 token 映射见 docs/superpowers/specs/2026-10-28-dsh-skin-plugin-design.md。 - 底色方案:浅色/深色不是按主题里写的
appearance选的,而是按面板/画布亮度算出来的 (插件只换 alias token,其余表面沿用宿主内置调色板,选错就整片变黑)。 - 壁纸:插件自有
<style data-plugin="deepseek-harness-skin">注入body::before固定图层。 - 动态壁纸:Codex 主题里的
background.mp4会被一起导入(≤16 MB、H.264/avc1、 单视频轨),由/skin/assets/<id>/video以支持Range的方式输出,客户端在#root之下挂一个固定<video>层。海报始终是底:视频loadeddata之后才盖上, 解码失败、自动播放被拒、系统「减少动态效果」、窗口隐藏都会退回海报或暂停, 不会黑屏。透明度的白色暗化层由海报和视频共用同一个数值,切换时不跳亮度。 - 播放快捷键:注册为宿主的
skin.toggleVideo命令,默认Ctrl+Alt+P(macOS⌘⌥P);没有快捷键服务时退回到窗口内监听同一个组合键。 - 视频列表:播放源由
playbackFor(skin)统一决定——列表开着且当前项存在就用列表, 否则用主题自带 MP4,两者永远只有一个<video>,所以播放/暂停、静音、倍速都只作用在一个元素上。 列表项由/skin/assets/playlist/video?folder=…&name=…输出,Host 先校验name无分隔符、realpath后仍在该文件夹内才给字节;列表本身来自/skin/api/videos,只读目录、不递归。 进度由客户端回报给/skin/api/playlist-track,Host 写进state.json的playlist.position, 所以「继续上次那个」在重启后依然成立;而开关只在 Host 内存里,进程一换就是关。 静态主题上列表层用z-index:1盖在插件的壁纸层之上、UI 之下,关掉时整层连类一起移除。 - 分层不透明度:四类表面各有下限——画布/侧栏跟随"壁纸可见度"(默认 100%,保留 0.08/0.10 的 一层底色压字),卡片 0.86,弹窗与浮动面板 0.95,输入框 0.90。同一层透明度会让设置面板变成 "看穿对话"的玻璃窗,分层之后壁纸在画布和侧栏上照样透,叠在上面的面板则始终可读。
- 默认可见度 100%:只有
theme.css自己写了 alpha(例如「星潮」的侧栏 0.7、「上杉绘梨衣」的 0.32)才沿用作者值;没写的主题一律按当前默认(100%)渲染,所以改默认值会作用到所有这类主题, 而不是把旧默认值固化在每个theme.json里。详情面板的「恢复默认」可以清掉单主题的覆写。 - 可读性蒙版:壁纸和视频上那层雾用的是主题自己的
colors.background,只调透明度, 与 Codex 的rgb(var(--ds-bg-rgb) / alpha)一致。浅色主题因此是白雾、深色主题是深灰雾, 而不是固定白色——固定白会把深色壁纸洗成灰,固定黑会把亮色壁纸压闷。 - 持久化:Harness 的主题偏好 schema 不接受第三方主题 id,所以插件自己存
<DSH_HOME>/skin/state.json,启动时重新注册并恢复。
数据位置
<DSH_HOME>/skin/
state.json # 当前皮肤、每个皮肤的透明度覆写、动态背景与视频列表设置
themes/<id>/theme.json # 皮肤定义(配色、壁纸焦点、视频元数据、来源元数据)
themes/<id>/wallpaper.<ext> # 壁纸原图(必需,也是视频的海报帧)
themes/<id>/video.mp4 # 动态壁纸(可选)
state.json 里的 playlist 存文件夹、倍速、画中画、最近 20 条历史和 position(第几个、第几秒);
没有「开关」字段——开关是 Host 进程内的状态,每次启动都是关。
视频文件本身仍在你自己选的文件夹里,插件从不会复制或修改它们。
删除只会动这个目录;不会碰你的 Codex 主题原文件。
目录结构
| 路径 | 作用 |
|---|---|
index.js |
Host 半:建库、注册 /skin 路由、注册 skin_theme 工具 |
client.js |
Web 半:主题应用(token + 壁纸)+ 设置页 UI |
lib/convert.js |
Codex theme.json/theme.css → 皮肤,含 Safe CSS 的可用信息提取 |
lib/service.js |
所有操作(列表/导入/应用/透明度/删除/设计/视频列表),HTTP 与工具共用 |
lib/playlist.js |
视频列表:列目录(自然排序)、进度签名、路径逃逸校验 |
lib/palette.js |
颜色解析、对比度修正、从文字/图片调色板推导整套配色 |
lib/designer.js |
提示词、模型调用、JSON 解析、离线回退 |
lib/store.js |
原子写入的皮肤库 |
lib/mp4.js |
有界 MP4 结构解析(品牌、轨道、编码、尺寸、时长),不解码 |
lib/zip.js |
最小 ZIP 读取器(stored + deflate,无第三方依赖) |
lib/api.js |
/skin/api/* 与 /skin/assets/* |
lib/tool.js |
skin_theme 工具定义 |
tools/asar.mjs |
维护者工具:只读查看安装包 app.asar 里的文件(排查宿主 API 用) |
自测
node tools/selftest.mjs # Host:配色/转换/ZIP/存储/HTTP
node tools/client-selftest.mjs # Web:主题注册、壁纸样式、透明度、渲染
两个脚本都不需要网络、不需要安装依赖。
已知边界
- 视频壁纸的编码:只接受 H.264(
avc1)、8-bit、单视频轨、≤16 MB,分辨率上限 2560×1600,时长 0.2 秒–5 分钟。HEVC / AV1 / 分片 MP4 会在导入时被跳过(海报照常导入, 界面给出一条警告),而不是等到播放时才失败。 - Codex 的
theme.css只提取与 DOM 无关的部分(圆角、表面透明度、模糊、字体);[data-ds-part]选择器不会逐条翻译到 Harness DOM——那等于猜选择器,宁可少做。 - 画布保底:本应用把对话文字直接画在画布上(Codex 每条消息有独立卡片), 所以即使可见度拉满,画布仍保留 8% 底色(侧栏 10%)+ 壁纸暗化,让文字有个底。
- 浮层不跟手:设置面板的底色来自
--dsw-alias-bg-layer-2(dockkit 浮动面板), 它和画布是不同 token;"壁纸可见度"滑到很高时,画布/侧栏会更透,而浮动面板保持 0.95。 若某天宿主把设置面板改成画布 token,这条分层就失效——届时需要按新 DOM 再调一次。 - 自动关闭:在设置 →「外观」里重新选择 浅色/深色/跟随系统,会停用当前皮肤(同 Codex 的 "Follow theme"),壁纸层同时移除。


No comments yet. Be the first to write one.