# 主题系统规范 > 主题系统 = 插件能控制的所有 CSS 的收敛。**每套主题 = 一个独立 CSS 文件**:内置主题在 `plugin/assets/themes/*.css`(随插件打包携带),用户主题在 `/web-themes-xyy/*.css`(Linux/macOS 默认 `~/.dsh/web-themes-xyy/`,Windows 默认 `%USERPROFILE%\.dsh\web-themes-xyy\` 即 `C:\Users\<用户名>\.dsh\web-themes-xyy\`;放文件即新主题,无需打包/升级插件)。 > 主题文件里用到的全部变量(14 个 `--dsw-alias-*` 平台 token、`--mdvr-*` 身份色、`--hl-*` 语法高亮、L2/L3 排版常量)的名单与默认值见 `docs/variables.md`。 ## 一、插件能控制什么 / 到什么程度 | 层 | 内容 | 控制范围 | 程度 | | --- | --- | --- | --- | | ① 全局 token | `--dsw-alias-*` 14 个(bg 三层/overlay/边框×2/品牌/文字×2/语义×4/侧栏) | 整个应用表面、文字、边框、语义色 | 浅/深双值,全量覆盖 | | ② 强调变量 | `--mdvr-*`(accent 系 5 + link 系 2 + highlight 系 2 + quote 系 2 + code/table 系 3 + 字体 2 + 旋钮 1,共 17 个核心;另有标题/文本/列表/派生扩展) | 排版层的全部色彩细节 | 浅/深双值 | | ③ 元素排版 | 对话 Markdown 排版由产品 `._markdown_*` 规则兜底,主题通过自身 ③ 段(`:where()` 零优先级 + 按需 `!important`)增量覆盖 | 所有 Markdown 元素(标题/段落/列表/代码/表格/引用/分割线/链接/高亮/选区等) | 主题 ③ 段(规则级) | | ④ 面板 UI | `plugin/assets/panel.css` + 自绘组件(版本卡片 / 主题设置页 / 主题编辑器) | 插件自有 UI | 完全控制 | ### 明确做不到(平台限制) 1. **不改产品 DOM**:不能改类名、结构、属性;不能操作 `document.body` / `window`。 2. **零优先级**:`:where()` 意味着产品显式样式永远优先,主题只兜底「裸语义元素」;要覆盖产品显式样式需按需 `!important`。 3. **token 名单固定**:14 个 `--dsw-alias-*` 由平台 `Theme.listTokens` 决定,不可新增(新颜色只能走 `--mdvr-*` 自定义变量)。 4. **选择持久化**(v1.16.0):选中的主题(系统自带 / 内置 / 用户)写入浏览器 `localStorage`(键 `mdvr:theme:selection`),刷新 / 新开页面 / 重启后自动恢复上次选择;多标签页经 `storage` 事件实时同步。启动恢复带校验:用户主题先拉目录再应用,读取失败保留存储下次重试、主题被删则清存储回默认;`applySelection(..., persist=false)` 只用于启动恢复路径,避免回退值污染已保存选择。存储不可用(隐私模式 / 沙箱)时静默回退会话级内存态。外观模式不重复持久化(产品 `theme.setTheme` 自带)。 5. **无 JS 进对话流**:只能注册平台预留的座位(run 卡片、设置页、侧栏动作等)。 ## 二、主题文件格式(三段式) 每个主题一个 CSS 文件,三段式结构(全部带注释,参考 `plugin/assets/themes/strawberry-mocha.css`): ```css /* 文件头注释:主题 id / 名称 / 风格说明 / 色值来源 */ /* ① 浅色档:14 个全局 token + --mdvr-* 强调变量 */ body { ... } /* ② 深色档:同 ① 的深色取值 */ body[data-ds-dark-theme] { ... } /* ③ 元素级定制(可选):追加在 ① ② 之后,:where() 增量覆盖产品兜底 */ :where(...) { ... } ``` 要点: - **挂载机制与产品一致**:浅色写 `body`、深色写 `body[data-ds-dark-theme]`——产品用**属性选择器**标记深色,不是 `@media (prefers-color-scheme)`;选择器拼错则深色档完全不生效且不回退。插件样式注入晚于产品样式表,同选择器后者胜出。 - **变量齐全性**:`--mdvr-sans` / `--mdvr-mono` / `--mdvr-mm` 是排版常量;`--mdvr-link*` / `--mdvr-highlight*` 是公共约定(链接蓝 + 琥珀高亮);`--mdvr-accent*` / `--mdvr-quote*` / `--mdvr-code-*` / `--mdvr-table-*` 是主题身份色。变量分层(L0 → L1 → L2 → L3)与全部默认值见 `docs/variables.md`。 - **元素作用域**:主题 ③ 段全部规则以 `[class*="_markdown_"]` 限定(产品对话 Markdown 容器的 CSS Modules 稳定子串),只影响对话内容,设置面板等 UI 不被波及;`:where()` 包裹选择器,零优先级兜底。选区 / 滚动条 / 焦点环(主题签名层)刻意保持全站生效。 - **`!important` 的使用**:产品对对话 Markdown 有显式样式(`._markdown_*` 类规则,特异性 ≥ (0,2,x)),`:where()` 归零后必然输给产品;主题身份属性需要 `!important` 取胜(内置主题 ③ 段即如此,见注释说明)。 ## 三、选择模型 设置页「主题设置」分两组互斥单选: 1. **「系统自带」(默认,`DEFAULT_SELECTION = 'system-native'`)**:插件零干预——不注入 token/排版/变量,界面保持 DSH 出厂观感,深浅跟随系统/外观选择。 2. **内置 / 用户主题(互斥单选)**:选中即注入「主题 CSS + panel.css」,整体替换观感,取消选中(回到系统自带)即还原。 **外观 ☀️ 浅色 / 🌙 深色 / 🖥️ 跟随系统**:对任意选中主题可用(仅 `themeService` 缺失时按钮禁用),走产品 `theme.setTheme` 官方接口,实时生效且偏好持久化。主题 CSS 自带 `body`(浅)与 `body[data-ds-dark-theme]`(深)两档,切换外观档即跟随生效;只写单档的主题在另一档保持原样。 **选择持久化(v1.16.0)**:选中的主题写入浏览器 `localStorage`,新开页面 / 刷新 / 重启后自动恢复上次选择(打开新标签页也一样),多标签页同时开着时修改一处、其余实时跟随(`storage` 事件)。用户选择会覆盖「刷新恢复默认」的旧行为;存储不可用时(隐私模式 / 受限沙箱)自动回退为会话级内存态。 ## 四、用户主题(动态添加、可编辑) - **目录**:`/web-themes-xyy/`,默认 Linux/macOS `~/.dsh/web-themes-xyy/`、Windows `%USERPROFILE%\.dsh\web-themes-xyy\`(即 `C:\Users\<用户名>\.dsh\web-themes-xyy\`)。解析优先级(v2.1.3 起):包装器注入 `__MDVR_DSH_HOME__`(自定义 `DSH_HOME` 一并生效)→ shell 探测 `$HOME` / `%USERPROFILE%` → `workspaceRoot` 推导 → `null` 降级,不硬编码机器路径(host.js `resolveUserThemesDir`);设置页标题显示 Host 返回的真实绝对路径。放任意 `*.css` 即新主题,**无需打包/升级插件**。 - **id 约束**:文件名仅允许字母/数字/下划线/连字符,且不以点或连字符开头(`^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`)。 - **添加方式三选一**: 1. 手动放入 `*.css`(三段式格式,参考内置主题,推荐 `strawberry-mocha` 为标准参考实现)→ 设置页「🔄 刷新用户主题」即生效; 2. 设置页「🆕 新建用户主题」(v1.17.0):**可选底子内置主题**(4 选 1,默认草莓猛男粉)——起步内容 = 所选内置主题 CSS 整体复制(浅/深/元素定制全注释),选好文件名保存即可;编辑器内也可随时切换底子(会替换起步内容); 3. 已有主题卡片右上「✏️ 编辑」:改 CSS 后「💾 保存」写回原文件。 - **编辑器能力**:实时语法高亮(透明 textarea 叠彩色 pre:注释/字符串/选择器/变量/at 规则/颜色/数值/属性名分色,跟随主题变量配色);「🧹 格式化」一键排版(补分号、花括号换行、2 空格缩进、注释保留,结果通过语法校验才覆盖原文);「Tab」插入缩进;保存走 Host `themes.user.save`(事务化分块上传 + 双端 CSS 校验 + 100KB 上限),沙箱显式放开;保存成功后若该主题正被使用则自动重新应用(修改即时生效)。 - **内置主题只读**:`plugin/assets/themes/*.css` 的卡片无「✏️ 编辑」按钮;但内置主题与共享资产由 Host 从文件读取,改文件后刷新/重启插件即生效,无需重新打包。 ## 五、内置主题 | 文件 | 名称 | 风格 | | --- | --- | --- | | `plugin/assets/themes/strawberry-mocha.css` | 草莓猛男粉(标准参考实现) | 丝绒草莓甜点 × Catppuccin Mocha 暗夜:原生列表符号 + 全量 `--mdvr-*` 身份色 + `--hl-*` 语法高亮 + L2/L3 排版常量(全变量化参考实现,带完整 ③ 元素段,变量分层见 `docs/variables.md`) | | `plugin/assets/themes/Cyber-Titanium-native.css` | Cyber Titanium · 钛影 | 2026 Mac 极客:Space Black 空间黑 × 阳极钛紫 × 电光青 × 冷银 | | `plugin/assets/themes/High-Vis-Clarity-native.css` | High-Vis Clarity · 高清晰 | 旧显示器/低色域友好:高反差冷白 × 强辨识纯天蓝 × 黄金重点 | | `plugin/assets/themes/Pine-Smoke-Ink-native.css` | 松烟墨黛 | 2026 中文文人:徽墨沉香 × 矿物朱砂 × 远山黛蓝 × 宣纸冷白 | 四个内置主题按**同一变量契约 100% 对齐**(变量名单/三层作用域逐项一致,仅配色不同),`strawberry-mocha` 为参考实现;`THEME_META`(`plugin/src/client.core.js`)注册各主题的显示名/描述/双档色板预览。 ## 六、构建与切换机制 - **构建**:`node scripts/build-client.js` = 拷贝 `plugin/src/client.core.js` → `plugin/client.js` + 资产完整性检查。CSS 资产不内联,Host 运行时从文件读取。 - **define 传输**:`node scripts/minify.js` 生成精简双半(仅删注释/折叠空白,token 流等价断言),一次传 host+client 双半;define 后 `cordis_inspect_self` 核对双半完整再 run。 - **发布门禁**:`node scripts/check-release.js` 自动核对 MANIFEST 一致性、构建产物、资产完整性、CSS 结构闭合、内置主题浅/深双档挂载(`body` + `body[data-ds-dark-theme]`)、panel.css 的 `var(--mdvr-*)` 全部带回退。 - **注入与原子切换**:`applySelection(id)` 先构建目标 CSS(主题文件缺失/unavailable 时返回失败不动现状),成功插入新样式后再卸载旧样式;「系统自带」只注入 panel.css。 - **卸载**:`ctx.effect` 持有样式表 disposer,stop/update/undefine 自动还原注入的样式。 ## 七、如何新增一个主题 1. 在 `plugin/assets/themes/`(内置)或 `/web-themes-xyy/`(用户;Windows `%USERPROFILE%\.dsh\web-themes-xyy\`)新建 `.css`(复制任意主题为模板,改头注释:主题 id / 名称 / 风格说明)。 2. 定 14 个 `--dsw-alias-*` token 的浅/深值。浅档语义色按 WCAG AA 实测值:error `#c74330` / success `#287b38` / warn `#985d00`(白底与页面底均达标)。 > ⚠️ **浅色分界色注意**:浅色档边框/分层底色 vs 背景的对比是灰阶差显示器的重灾区——WCAG 1.4.11 非文本要求 ≥3:1,粉调审美受约束时至少 ≥2:1 肉眼可辨。调整优先级:先改主题专属 `--mdvr-code-border` 等,再谨慎动 L0 `--dsw-alias-border-*`(会波及整个产品 UI)。 3. 定 accent 系变量(tint 配方:`result = round(α·A + (1−α)·S)`,浅档 α=12%、深档 α=14%)。 4. 需要差异化元素时写 ③ 段扩展规则(`:where()` + markdown 限定;必要时 `!important`)。 5. 内置主题在 `plugin/src/client.core.js` 的 `THEME_META` 加一行(显示名/描述/双档色板预览),然后 `node scripts/build-client.js`;用户主题自动出现在列表,无需注册。 6. `node scripts/check-release.js` 过门禁 → 定义新 Package → update → 设置页验证(浅/深两档 + 面板)。 ## 八、参考实现(第三方用户手册) > v1.17.0 起插件资产 `template.css`(青瓷最小参考版)已删除,不再有独立模板资产;「完整参考」职责由**内置主题 `strawberry-mocha`(标准参考实现)**承担。 - **参考实现 = `plugin/assets/themes/strawberry-mocha.css`**:全变量化 + 完整注释,演示「一个主题能改的全部内容」——① 浅色档 / 深色档:14 个全局 token(官方语义注释)+ `--mdvr-*` 变量(每个标注影响哪些元素);② 元素级定制:全部可控元素规则(标题/段落/列表/任务框/行内代码/代码块/引用/表格/分割线/链接/粗体/高亮/删除线/上下标/kbd/图片);③ 扩展示例:选区 / 滚动条 / 斑马纹 / 焦点可见性;④ 尾部配色速查:tint 配方、浅档语义色 AA 实测值、链接色建议、边条优先原则、深档亮度控制。(`docs/variables.md` 维护完整变量契约名单。) - **「🆕 新建用户主题」起点(v1.17.0)**:默认取内置 `strawberry-mocha` 内容本身,可在编辑器内切换底子为任意内置主题(最大 4 选 1:猛男粉 / 钛影 / 高清晰 / 松烟墨黛);切换 = 整体替换起步内容。底子主题未加载(极少见)时回退内联最小起步。 - 编写主题时的硬性检查:浅色 `body` / 深色 `body[data-ds-dark-theme]`;元素定制一律 `:where()`;`--dsw-alias-*` 固定 14 个不可新增;正文对比度 ≥4.5:1。