主题系统
Qomicex 主题系统采用 primitive → semantic → component 三级语义 token 结构,以.qtheme 主题包承载,实现启动器与插件 UI 全量即时换肤,无需重建 plugin-ui dist。
三级语义模型
- v1 落点:
.qtheme直接表达 semantic/component 层(即--*平铺变量),primitive 派生与theme.mjs计算层留待 v2 - plugin-ui 组件只消费
var(--*),零内联色值 → 换主题 = 全组件即时换肤,消除「改组件须重建 dist」的陈旧问题
语义 token 命名
点分命名,. 归一化为 -(background.emphasis → --background-emphasis)。
色板 token(标准组件级集合 = plugin-ui 消费全集)
扩展语义(v1 可选,emit 为
--foreground-accent 等,供前瞻):foreground.accent、foreground.muted、foreground.destructive、background.elevated、background.emphasis、background.sunken、border.strong、border.accent、accent.hover、accent.active、status.success、status.warning、status.error。
非色 token
space.*、font.family/size/weight、motion.*、shadow.* 归入 v2 非色扩展。
.qtheme 包格式
theme.json schema v1
非法 theme.json 会被
src/theme/schema.ts 拒绝并给出友好错误。
主题管理器
主题管理器(src/theme/manager.ts)提供:
applyTheme(json):校验 + 注入 CSS 变量到:root→ 全组件即时换肤;持久化主题 id 到localStorageclearTheme():移除自定义主题,回到设置层 light/dark/预设restoreSavedTheme():启动时恢复已注册主题useTheme():React Hook,订阅变更事件驱动重渲染registerTheme():注册打包内置.qtheme(含 Catppuccin 四预设)
主题贡献(contributes)
插件与主题包可通过 manifests 声明三类贡献,v1 仅颜色主题:图标主题(icon-theme.json)
图标主题是.qtheme 包中与 theme.json 同级的新文件,为插件侧边栏、菜单项等图标提供覆盖映射。
格式
字段说明
图标定义对象
渲染优先级
PluginIcon 组件渲染顺序:
- 当前主题的
icon-theme.json中查找src键 → 匹配则渲染 SVG/char/URL - 未命中,
src为 URL →<img> - 未命中,
src为 FontAwesome 类名 →<FontAwesomeIcon> - 均未命中 →
null
注册方式
图标主题通过主题管理器注册,与颜色主题绑定:registerTheme(colorJson, iconJson?)— 同时注册颜色 + 图标主题registerIconTheme(themeId, iconJson)— 单独为已注册的颜色主题绑定图标
getActiveIconTheme() 或 useIconTheme()(React Hook)获取。主题切换时映射自动更新。
字体/连字贡献
manifest 声明
插件 manifests 通过contributes 字段声明字体资源:
fontLinks(string[]):字体 CSS/CDN URL 列表。插件激活时,启动器自动注入<link rel="stylesheet">到<head>;插件停用时自动移除。- 可用
font-feature-settings作用域约定,在特定区域启用连字(见下方)。
连字作用域约定
启动器预定义.font-ligatures 类(插件内等价类 .p-font-ligatures),应用在需要启用字体连字的区域(如控制台日志、版本号 diff 等):
.p-font-ligatures 获得同等效果。
与 plugin-ui 的关系
硬性约定:plugin-ui 组件只允许var(--*) 消费 token,禁止内联色值。审计确认 packages/plugin-ui/src/ 全量零内联色值 → 换主题 = 即时换肤,无需重建 dist。
预设主题
启动器内置 Catppuccin 四预设(latte / frappe / macchiato / mocha),已迁移到新 token 命名。切换预设或自定义.qtheme 时,所有消费 var(--*) 的 UI 组件即时响应。
插件驱动主题覆盖
插件可通过__PLUGIN_API__ 动态覆盖启动器 UI 的 CSS 变量,无需分发 .qtheme 包即可实现运行时换肤。
优先级层级
applyThemeOverride注入的<style>特异性高于.qtheme和@layer base- 若用户设置了主题色(
themeColor.ts的 inline style),--primary/--ring/--primary-foreground三者由 inline 接管,override 中这三者不生效
API
使用示例
安全限制
- token 白名单:仅允许 30 个预定义语义 token(
background、foreground、primary、accent、border、status-*等) - HSL 格式校验:值必须严格匹配
H S% L%(如142 71% 48%),拒绝任意 CSS 注入 - 不在白名单的 token 和非法格式会被静默忽略
生命周期
applyThemeOverride可重复调用,后者覆盖前者- 插件停用时会自动清除 override:停用流程会无条件调用
clearThemeOverride,恢复为.qtheme或内置 light/dark 主题。因此无需在停用钩子里手动清理;若希望在插件仍启用时撤销覆盖,再显式调用clearThemeOverride clearThemeOverride仅移除 override 样式,不影响.qtheme或内置 light/dark 主题