10 · 主题编译流水线¶
主题切换不是“换一张壁纸”。一个主题源要生成终端、编辑器、Hyprland、Quickshell 等多个消费者的配置,再以原子方式成为活动版本。
输入、编译与输出¶
flowchart LR
Builtin[$OMARCHY_PATH/themes/name] --> Stage[next-theme]
User[~/.config/omarchy/themes/name] --> Stage
Stage --> Colors[colors.toml]
Colors --> Templates[default/themed/*.tpl]
UserTpl[~/.config/omarchy/themed] --> Templates
Templates --> Outputs[终端/编辑器/Hypr/Shell 配置]
Outputs --> Atomic[current/theme]
Atomic --> Runtime[Shell IPC + wallpaper + app hooks]
主题因此更接近一个小型编译器:colors.toml 是输入 token,template 是目标后端,current/theme 是活动产物。
omarchy-theme-set 的事务边界¶
切换过程有明确顺序:
- 获取 theme lock,防止两个切换并发;
- 创建干净的
next-themestaging; - 复制内置主题,再叠加用户主题;
- 若缺
colors.toml,尝试从 legacy Alacritty 配置迁出颜色; - 执行 themed templates;
- 保存旧 wallpaper 供过渡;
- 原子替换
current/theme; - 写
theme.name; - 更新 wallpaper,并经 IPC 推送 Shell 颜色/token;
- 释放锁;
- 并行执行应用 retint/restart;
- 触发
theme-set用户 hook。
stateDiagram-v2
[*] --> Locked
Locked --> Staged
Staged --> Compiled
Compiled --> Validated
Validated --> Swapped: atomic replace
Swapped --> Visible: IPC/wallpaper
Visible --> PostHooks
PostHooks --> [*]
Staged --> Failed
Compiled --> Failed
Failed --> [*]: current 保持旧版本
关键点是原子 swap 发生在完整编译后。读取者只会看到旧主题或新主题,不会看到“新终端颜色 + 旧 Shell token”的 staging 半成品。
模板系统¶
default/themed/*.tpl 把同一 palette 映射到不同配置格式。helper 支持:
- 十六进制与 RGB 形式;
- 颜色混合;
- gradient 生成;
- Shell surface 的 border 规范;
- 主题局部
shell.<section>.toml合并。
用户模板目录允许添加新消费者,而不用 fork 整个主题设置脚本。模板的输出路径属于契约,修改时要考虑应用是否热加载或需要 restart hook。
Shell 主题分两层¶
colors.toml 提供基础 palette;shell.toml 提供 surface role、间距、字号、圆角等语义 token。Shell 中最终表现为 Color、Style 与 Border singleton。
机器级 ~/.config/omarchy/shell.toml 覆盖活动主题并被实时监视。例如用户调大文字后切换主题,文字尺寸不应恢复默认。这里“主题选择”和“机器可访问性偏好”是两份正交状态。
远程主题是数据,不是代码¶
该快照最重要的安全变化之一,是对通过 git 安装的主题增加 staging 过滤:
- 拒绝所有
*.lua; - 拒绝终端程序配置等代码型文件;
- 拒绝
vscode.json等可携带命令的目标; - 遍历/复制时不跟随符号链接;
- URL 与主题名先经过格式约束;
- 只有经过 filter 的 staging 可以进入真正安装路径。
flowchart TD
Remote[远程 git 主题] --> Clone[临时 clone]
Clone --> Name[名称/URL 校验]
Name --> Walk[不跟随 symlink 遍历]
Walk --> Filter{允许的数据文件?}
Filter -->|否| Reject[拒绝安装]
Filter -->|是| Stage[受控 staging]
Stage --> Install[用户 themes 目录]
为什么特别拒绝 Lua?Hyprland 会 require 当前主题相关 Lua;若远程主题能写入那里,用户的一次“换配色”就变成自动执行代码。终端/编辑器配置也可能有命令执行能力,不能仅因扩展名不是 .sh 就当作纯数据。
用户自己创建或明确链接的本地工作主题拥有不同信任语义。系统允许高级用户写代码,但不让远程主题安装命令偷偷跨越授权边界。
并行 post action 的取舍¶
原子 swap 与 Shell/wallpaper 是用户可见主路径;不同应用的 retint/restart 在解锁后并行进行。这样慢应用不会长期占 theme lock,下一次操作也不必等待所有非核心消费者。
代价是短时间内个别应用可能还是旧颜色。设计选择“核心一致性优先、外围最终一致”,并用 hook 给用户扩展收尾动作。
headless 安装¶
安装阶段可能没有 Shell。主题设置仍应生成活动状态与 background link,只跳过需要图形 IPC 的步骤。这体现了一个通用原则:持久状态变更不应依赖当前 UI 恰好在运行。
失败与恢复¶
| 失败位置 | 结果 |
|---|---|
| source/theme 验证 | current 不变 |
| template 编译 | staging 丢弃,current 不变 |
| 原子 swap 前 | 用户继续使用旧主题 |
| Shell IPC | 文件状态已完成;运行时可 restart/reload 恢复 |
| 某应用 retint | 核心主题已生效;该外围应用可单独重试 |
| user hook | 不应回滚已可见主题 |
关键源码索引¶
bin/omarchy-theme-set路径与信任列表bin/omarchy-theme-set远程文件过滤bin/omarchy-theme-setstaging 与原子应用bin/omarchy-theme-setpost actionsbin/omarchy-theme-set-templates— token/template 编译default/themed/shell.toml.tpl— Shell 默认 token 模板docs/theming.md— 上游完整主题契约- 安全修复 commit — 阻止已安装主题执行代码
下一章看 Shell 如何把系统服务翻译成桌面体验:11 · 桌面服务适配层。