Skip to content
dsh.fish
Bundle

dsh-veneer

Veneer — a lightweight visual editor to reskin the DeepSeek Harness UI: edit background surfaces (color/image/opacity) with theme persistence.

Source
swordordead
stars
2 stars
License
MIT
Updated
Updated 4 days ago

Readme

# ✨ Veneer:给 DeepSeek Harness 换一套你喜欢的界面

Veneer 是 DeepSeek Harness(DSH)的**可视化换肤插件**。无需修改 CSS 或配置文件:在侧栏打开「编辑」,就能调整**背景、图片、字体、颜色和细节样式**;保存后,下次打开仍会保留。

![Veneer 换肤后的 DSH 界面](docs/images/veneer-overview.png)

## 🎨 用法一眼看懂

1. 在 DSH 左下角点击 **「编辑」**。
2. 选择**背景、字体、AI 主题或样式**,边改边预览。
3. 点击 **「保存主题」**,设置会自动保留。

![Veneer 可视化编辑面板](docs/images/veneer-editor.png)

## 🚀 安装

```bash
dsh plugin --profile web add github:swordordead/dsh-Veneer
```

安装完成后**重启一次 `dsh web`**,刷新网页,即可在侧栏底部找到 **「编辑」**。

也可以使用 npm 包安装(发布后):

```bash
dsh plugin --profile web add dsh-veneer
```

> 💡 **这是一个 DSH 社区插件。** 欢迎在 [GitHub Issues](https://github.com/swordordead/dsh-Veneer/issues) 提交问题与建议。

---
## 🪄 能做什么

| 能力 | 说明 |
|------|------|
| 🎨 背景换肤 | 12 个背景面:侧边栏背景 / 主背景(消息气泡、输入框、各浮层) / Veneer 面板自身 |
| 🖼 背景图 | 每个背景可上传或拖放图片(自动识别真实格式)、设「变淡」透明度;可在预览中拖拽裁切焦点、用横纵滑块精调,或一键居中;轻点预览可收缩/展开 |
| 💾 保存/复制/重置 | 持久化到 DSH Host Settings(浏览器 `localStorage` 兼容快照兜底,Harness 更新不丢);可复制 CSS;每张字体卡片可单独「重置」,底部「重置全部设置」恢复背景、字体和文字颜色 |
| 🔤 字体与文字颜色 | 分别调整全局、侧边栏、主内容、输入区和 Veneer 面板的字体与文字颜色;每个位置都支持「✎ 自定义字体名」直接输入本机字体 |
| 📦 导出/导入主题 | 一键导出全部设置(含背景图 URL)为 JSON,可复制或下载;导入可完整恢复(同机),跨机器时图片引用可能失效 |
| 🤖 AI 生成主题 | 「AI 主题」标签页选模型(与对话共用 Harness 模型目录与凭据,零配置)+ 一句话描述(或选风格),复用对话同一模型运行时生成整套背景+字体主题,预览确认后再应用/放弃 |
| ✨ 样式精调 | 「样式」标签页微调圆角 / 边框 / 阴影 / 强调色(真实存在的 CSS 变量才开放),改完自动保存,支持单组恢复默认 |
| 🗂 模板库 | 「样式」标签页顶部内置 8 套主题模板(莫兰迪/赛博/暖棕/极简/深空/森林/海洋/复古纸),点选即预览,应用一键切换整套主题 |

## 🧩 架构

- **host 半区**(`lib/index.js`):图片上传 API + 图片静态服务(嗅探真实图片格式,不信任扩展名) + AI 生成代理(`POST /api/dsh-veneer/ai/generate`,优先走 Harness 的 `ctx.llm` 统一运行时复用对话模型目录与凭据;仅当请求方显式携带 `apiKey` 时才直连 DeepSeek,且只允许 `api.deepseek.com` 上游) + 模型目录接口(`GET /api/dsh-veneer/ai/models`)
- **client 半区**(`lib/client.js`):侧栏按钮 + 浮动面板 + 背景/字体/样式编辑逻辑
- **主题存储**(`lib/theme-storage.js`):Host Settings 命名空间 `dsh-veneer`(唯一字段 `theme`)+ `localStorage` 兼容快照的双写协调、读取优先级与迁移决策(Host 合法 → Host 优先并刷新快照;Host 空 → boot 时迁移旧本地主题一次;Host 不可用/非法 → 本地兜底且不覆盖 Host)
- **设置契约**:host 端用 `@deepseek-ai/dsh-settings` 的 `settingsNamespace()` 注册 Schema;settings 服务未挂载时 client 退回 `localStorage`,但 Harness 仍须提供静态模块 `@deepseek-ai/dsh-settings` / `@deepseek-ai/schemastery`(zip 安装器会预检);client 端经动态可选 `settingsScope` 读写订阅,重置使用 `unset` 清空字段而非写 `null`
- 入口:`package.json` 的 `dsh.client` 声明 + `ctx.slots.inject("sidebar.footer.action", …)` 把按钮挂进侧栏底栏

## 📁 项目结构

```
dsh-veneer/
├── lib/
│   ├── index.js      # host 半区:图片上传/静态服务 + AI 生成代理
│   ├── client.js     # client 半区:侧栏按钮 + 换肤面板 + 默认皮肤预设
│   ├── theme-storage.js # 主题解析/规范化/读写协调/Host 迁移决策(纯逻辑,可单测)
│   ├── llm-errors.js # AI 错误码 → 友好中文文案映射
│   └── assets/       # 内置默认皮肤底图(随 npm 包分发,host 启动时复制到用户存储)
├── cordis.patch.yml  # bundle 注册层(随包发布,`dsh plugin add` 自动应用)
├── package.json      # 插件声明(dsh.client / dsh.bundle)+ npm 发布清单
├── install.bat       # zip 离线安装入口(双击运行)
├── install.ps1       # zip 离线安装脚本(幂等 + SHA256 校验 + 与 dsh plugin 互斥)
├── sync.ps1          # 开发期同步到本地 DSH profile
├── tools/
│   ├── pack-release.ps1        # 打 zip 离线发布包
│   └── validate_templates.cjs  # 模板 / AI 契约校验
├── test/             # Playwright(Python)回归脚本
└── docs/             # 架构 / 变更记录 / 计划
```

## 🌟 默认皮肤预设(装上即用)

首次安装打开 DSH 即为作者常用皮肤:白底 + 三张底图(侧栏 / 主背景 / 输入框)+ 深蓝文字,
无需任何配置。实现方式:

- 底图随 npm 包分发(`lib/assets/`),host 启动时把缺失的图复制到用户 DSH 存储目录
  (`~/.dsh/storages/dsh-veneer/images/`),幂等且**绝不覆盖**用户自己上传的同名图片;
- client 端在浏览器 `localStorage` 无已保存主题时自动套用预设;用户一旦保存自己的主题,
  即以用户数据为准,预设不再干扰;「重置全部设置」后回到预设。
- 底图来源为作者自用皮肤图片,随包分发仅供个人换肤使用(MIT)。

## 🤖 AI 生成主题(模型与凭据说明)

- 在面板「AI 主题」标签页选择模型(提供方 + 模型两级下拉),与对话共用 Harness 模型目录与凭据,**零 API Key 配置**,也不会把 Key 存到 `localStorage`。
- 生成请求走 Harness 的 `ctx.llm` 统一运行时(不带 `sessionId` 的一次性 completion),不进对话历史、不阻塞主对话。
- 模型服务不可用、凭据缺失/无效、限流(429)、无额度(402)或网络失败时,面板内显示友好中文错误,不影响已保存的主题。
- 生成结果先进入「预览」态,确认「应用」才会持久化;「放弃」恢复生成前状态。

## 🚀 安装部署

### 方式一:官方插件命令(推荐)

DSH 的插件机制支持「bundle」:只要包的 `package.json` 声明了
`"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`,`dsh plugin add`
就会自动装包并注册,接收者**零手动配置**:

```bash
# 从 npm 安装
dsh plugin --profile web add dsh-veneer

# 或直接从 GitHub 仓库安装(会自动跑构建脚本,首次可能提示允许 build)
dsh plugin --profile web add github:swordordead/dsh-Veneer

# 或从本地目录/zip 解压目录安装
dsh plugin --profile web add /path/to/dsh-veneer
```

首次安装或含 Host 端改动的升级完成后重启一次 `dsh web`,再刷新浏览器;无需手动
改配置文件。仅前端 client 更新时强制刷新即可。卸载同样一条命令:
`dsh plugin --profile web remove dsh-veneer`。

> 前置条件:`dsh` CLI 与 `pnpm` 都在 PATH 中(`dsh plugin` 底层转发给 pnpm)。

### 方式二:zip + 双击安装(离线 / 无 node / pnpm 环境)

针对无法使用命令行或没有 Node 环境的接收者,提供了 zip 包 + 一键安装脚本:

1. 打包(发布前先按版本号策略递增 `package.json` 的 `version`):

   ```powershell
   powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\pack-release.ps1
   ```

   生成 `release\dsh-veneer-vX.Y.Z.zip`(只含运行期必需文件:`package.json`、
   `cordis.patch.yml`、`lib/`、`install.bat`、`install.ps1`、`使用说明.txt`),
   可上传 GitHub Releases 或直接发文件。

2. 接收者:解压 → 双击 `install.bat`。脚本自动完成复制 + 注册,幂等(重复执行不会
   重复注册)、带 SHA256 校验,无需 node/pnpm。首次安装或版本包含 Host 端改动时,
   需重启一次 `dsh web`;仅 `lib/client.js` 变化时强制刷新浏览器即可。
   **与方式一互斥**:若接收者已用 `dsh plugin --profile web add dsh-veneer` 安装过,
   `install.ps1` 会在复制前检测到并安全退出,不会覆盖 pnpm 管理的目录;此时应使用
   `dsh plugin --profile web update dsh-veneer` 更新。

3. 接收者升级:重新下载新包再次双击并按上条规则重启/刷新即可。主题存于 Harness
   Settings(`settings.yaml`),不随插件目录覆盖,已有设置会保留。

> 自定义 DSH 数据目录(设置了 `DSH_HOME`)的接收者:先 `set DSH_HOME=...` 再运行,或
> `powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -DshHome "目录"`。

### 手动方式(了解原理)

```powershell
$dst = "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-veneer"
New-Item -ItemType Directory -Path "$dst" -Force | Out-Null
Copy-Item -Recurse -Force .\* "$dst"
```

再在 `profiles/web/cordis.patch.yml` 注册 `dsh-veneer`,重启 `dsh web`。
(zip 里的 `install.bat` 就是把这两步自动化的脚本。)

### 日常同步(改完代码后)

```powershell
powershell -ExecutionPolicy Bypass -File .\sync.ps1 -AllowManagedOverwrite
```

`sync.ps1` 默认拒绝覆盖由 `dsh plugin` 管理的目录;上述开关只用于维护者明确进行本地开发部署。
脚本会先对 `lib/client.js`、`lib/index.js`、`lib/theme-storage.js`、`lib/llm-errors.js`
做 `node --check`,仅复制运行期白名单(`package.json`、`cordis.patch.yml`、`lib/`)到
`$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-veneer`,最后比对 SHA256。
仅前端 client 改动时刷新浏览器即可;`lib/index.js`、依赖或插件注册变化后需重启 `dsh web`。

### 发布到 npm(维护者)

```bash
npm login      # 首次需登录 npm 账号
npm publish    # 发布 dsh-veneer,接收者即可 `dsh plugin --profile web add dsh-veneer`
```

`package.json` 的 `files` 白名单只发布 `lib/` + `cordis.patch.yml`(npm 会自动附带
package.json / README / LICENSE),docs、test、install 脚本不会进 npm 包。

## ✅ 测试

Node 单元测试(无需 DSH / 浏览器,随时可跑):

```powershell
npm test
# 等价于: node --experimental-test-module-mocks --test
#(Node 24 起 module mocking 需要该 flag;host-settings.test.mjs 依赖 mock.module)

# - test/ai-colors.test.mjs       AI 颜色容错单测(22 用例,mock canvas)
# - test/ai-colors.browser.test.mjs 真实浏览器引擎验证 CSS Color 4(oklch/hwb/lab/color()),
#                                    依赖 playwright(devDependency,缺失时自动跳过)
# - test/llm-errors.test.mjs      LLM 错误码映射单测
# - test/theme-storage.test.mjs   主题解析/规范化/Host 迁移决策单测
# - test/client-storage-integration.test.mjs  client 工厂有/无 settingsScope 双写集成单测
# - test/host-settings.test.mjs    Host 设置命名空间 + Schema 注册(可选依赖)单测
# - test/install-script-check.ps1  zip 安装器 BOM/依赖/官方管理互斥/离线复制校验
```

Playwright 回归脚本(需 DSH 跑在 `127.0.0.1:3080`):

```powershell
python test\veneer-skin-check.py                 # 面板结构 / 导出导入 / 字体选择器 / 无 pageerror
python test\system-fonts-check.py             # 系统字体扫描 + 字体/文字颜色精调 + 重置语义
python test\background-image-position-check.py # 背景图位置滑块 / 预览拖拽裁切 / 变淡蒙层 / 移除图
python test\import-security-check.py           # 导入安全:CSS 注入 / 非法 token / 恶意 URL / 合法 rgb 色
python test\ai-theme-check.py                 # AI 生成(mock)/ 预览应用放弃 / 样式维度 / 模板库 / 无 pageerror
```

模板与 AI 契约校验工具(开发期使用):

```powershell
node tools\validate_templates.cjs
```

## 📄 License

MIT

Install

dsh plugin --profile web add github:swordordead/dsh-Veneer

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source