--- description: "DeepSeek Harness Web GUI 主题工作台:八套预设风格、强调色与面板透明度微调,以及宿主端壁纸库。" --- # dsh-style-hub [English](README.md) | 中文 ## 概述 `dsh-style-hub` 是一个组合插件(宿主半 + 浏览器半),在**一张设置卡片**里提供三件事: - **预设风格** —— 八套内置配色,以一等公民身份注册成主题,交给设计系统的 token 目录去生效,而不是另写一份与之对抗的样式表。 - **微调** —— 品牌强调色,以及「面板透明度」滑杆:把抬升表面变半透,让底下的图片透出来。 - **你自己的壁纸库** —— 上传的图片存放在宿主的数据目录里,由一条路由提供,作为图层绘制在**应用框架之下**。 入口在 **设置 → 插件 → 插件配置 → Style Hub**。主开关关闭后,外观会完全恢复原样:本插件从不替你的部署决定「正常」长什么样。 ## 目录 - [安装](#安装) - [使用](#使用) - [设置项参考](#设置项参考) - [壁纸库](#壁纸库) - [设计取舍](#设计取舍) - [已知限制](#已知限制) - [开发](#开发) ----- ## 安装 本包自己接线:`dsh.bundle.patch` 指向随包的 `cordis.patch.yml`,它在组合既有的各层之上插入**一行**宿主条目(`dsh-style-hub`);浏览器半则由 `package.json` 里的 `dsh.client` 清单另行发现。两半都不需要手写条目。 **在 DSH Desktop 的 profile 里**(最常见的试用方式): 1. 解包进 profile 的 `node_modules`,目录名与包名一致: ```sh cd "$DSH_HOME/profiles/web/node_modules" # 例如 %APPDATA%/dsh-desktop/harness/profiles/web/node_modules tar -xzf dsh-style-hub-0.1.0.tgz && mv package dsh-style-hub ``` 2. 在该 profile 的 `package.json` 里,把 `"dsh-style-hub"` 加进 `dependencies` 与 `dsh.profile.bundles`。 3. 重新安装该 profile(pnpm,hoisted)并重启应用。 **在你自己拥有的组合里**:把 `dsh-style-hub` 加进与 `@deepseek-ai/dsh-base`、`@deepseek-ai/dsh-web-app` 同一张 bundle 列表,安装后重启。 **要求**:一个已经包含 `dsh-client-ui-settings-plugins`(卡片槽位)、`dsh-client-ui-theme`(主题注册表)与 `dsh-client-locale` 的 Web 组合。运行时 peer 范围是 `>=0.1.5-rc.2 <0.2.0`;`npm run build` 以 `0.1.5-rc.3` 做类型检查——那是该系列首个附带类型声明的版本。 宿主会注册 `style-hub` 设置命名空间、提供 `/api/style-hub/wallpapers` 路由,并在每次页面渲染时注入开机壁纸规则。`webServer` 是**可选** peer:没有 HTTP 层面的 profile 里该 bundle 照样加载,只是路由与开机规则暂时退场。 ## 使用 打开 **设置 → 插件 → 插件配置 → Style Hub**。 - 点选风格卡片;选 **Stock** 则完全不打扰内置的「外观」偏好。 - 拖动 **强调色** 与 **面板透明度** 微调当前风格;透明度为 `1` 时保留主题自身的填充。 - 选择或上传壁纸,再设置 **填充方式**、**不透明度**、**模糊** 与 **压暗**。 卡片是**即时写入**的,没有保存按钮——因为每个字段都是通过客户端设置作用域的一次带修订号围栏的单字段写入。 ### 关闭时的归还契约 `setTheme` 只持久化 `light` / `dark` / `system`,所以本插件注册的风格无法独自跨越刷新存活;真正重放它的是 `style-hub` 这一节。也正因如此,用户**在本插件介入之前**持有的偏好必须被别处记住:浏览器半在启动时捕获它,并持续观察之后的每次 `theme/change`,因此关掉主开关时是「把偏好还回去」,而不是钉死某个默认值。**Stock** 则完全不写「外观」——选它就是「让用户自己决定」。 ## 设置项参考 这一节是扁平的(一个字段一次写入),每个值都被校验两遍:宿主 schema 一遍,`validateSettings` 一遍(区间约束放在后者,这样已存文档可以在 schema 不升级的前提下重新判定)。 | 字段 | 类型 | 取值 / 区间 | 默认值 | | --- | --- | --- | --- | | `enabled` | boolean | 主开关 | `false` | | `themeId` | string | `stock` 或内置预设 id | `stock` | | `accent` | string | `#rrggbb`,空字符串表示跟随当前风格 | `""` | | `panelOpacity` | number | `0.8` … `1` | `1` | | `wallpaperId` | string | 十六进制存储 id,或空 | `""` | | `wallpaperFit` | string | `cover` \| `contain` | `cover` | | `wallpaperOpacity` | number | `0` … `1` | `1` | | `wallpaperBlur` | number | `0` … `50` px | `0` | | `wallpaperDim` | number | `0` … `1` | `0` | 内置预设 id:`nord`、`dracula`、`mocha`、`tokyo-night`、`gruvbox-dark`、`solarized-light`、`github-light`、`latte`。注册时加 `sh:` 前缀,因此不可能与其它插件的主题撞 id。 ## 壁纸库 - **存储** —— 宿主数据目录下的一个目录(`$DSH_HOME/dsh-style-hub`,可用 `DSH_STYLE_HUB_DIR` 覆盖):`images/` 存字节,`catalogue.json` 存索引。 - **路由** —— 只有一条前缀路由 `/api/style-hub/wallpapers`,按自身子路径分发:`GET` 目录、`POST` 上传、`GET`/`DELETE /:id`。 - **可接受的上传** —— 按魔数识别的 PNG、JPEG、WebP(声明的 Content-Type 永不被信任),上限 15 MB。 - **安全** —— 写操作要求同源 `Origin`/`Referer`;id 是 32 位十六进制,也是路由唯一会读取的路径段;展示名会去掉任何可能被响应头或标记上下文误读的字符。 宿主把同一条壁纸规则渲染进 `index.html` 的注入行,因此第一帧就已经是你的图片;之后该图层由浏览器半接管整个会话。 ## 设计取舍 **不做菜单模糊。** 给菜单加 `backdrop-filter` 需要一个设计系统并未提供的稳定钩子——`--dsw-mask-blur` 虽有声明却无人使用,菜单卡片也没有本插件可以依赖的类名。等价效果改用本插件自己拥有的手段实现:面板半透明(图片之上的表面变半透)与壁纸模糊(图片本身被柔化)。若日后设计系统公布菜单钩子,届时再加模糊选项也不会破坏现有实现。 **有壁纸时画布封顶。** `ui-layout` 用不透明的 `--dsw-alias-bg-base` 刷满画框,所以在启用壁纸时把该色的 alpha 钉在 `0.85`——足够让图片读得出来,又不至于让画框不再是画框。 **token 是推导出来的,不是抄来的。** 每套预设只有十二个色块;`tokensFor` 由它们推导出整份 `--dsw-alias-*` 目录,且任何预设都不产出 `--dsw-static-*`(那是共享常量,覆盖它会把一种风格泄漏给所有其它风格)。测试套件会从已安装的设计系统样式表里读出必需的 token 名单,因此校验对着的是系统本身,而不是一份会过期的副本。 **只有一层微调。** 强调色与透明度作为**同一层**覆盖叠放并带指纹,因此一次「什么都没变」的协调不会产生任何写入;空层会被拆除,而不是以「已应用但为空」的姿态留在那里。 ## 已知限制 - **偏好是重放的,不是持久化的。** 风格选择存放在 `style-hub` 一节里;内置「外观」偏好始终是 `light`/`dark`/`system`。刷新后由该节重放。 - **首帧可能短暂显示原生配色。** 壁纸会注入 `index.html`,配色不会:选定风格落地前可能有一瞬闪烁。开机配色注入是已知的后续改进。 - **风格生效期间手动改「外观」会被拉回。** 这是刻意的——功能开着,就该由风格说了算;而那次新选择会被记住,作为日后归还的偏好。想自己掌控「外观」,关掉主开关即可。 - **不存在 `menuBlur`。** 见上文[设计取舍](#设计取舍)。 ## 开发 ```sh npm install --legacy-peer-deps npm run check # 类型检查、构建三种产物、语法检查、测试 ``` `npm run build` 产出: - `lib/index.js` —— 宿主半,ESM,裸模块标识符保持外部。 - `lib/client.js` —— 浏览器半,包在组合模块加载器所期望的 `window.__ModuleLoader__.load({ id, factory })` 外壳里。 - `lib/types/**` —— 声明树(单独产出,因为源码使用 `.ts` 扩展名导入)。 - `test/build/*.test.js` —— 测试产物(测试直接导入 TypeScript 源码,必须先打包)。 测试用的都是 `node:test`: ```sh npm test ``` 两半如何协作见 [docs/architecture.md](docs/architecture.md)。 ## 许可 MIT,见 [LICENSE](LICENSE)。