Vetta
主题开发

06 / 主题开发

主题模块能力参考

理解 appearance、component、region、page、runtime、host hook 和主题存储的职责边界。

ThemeModule 是主题的公开组合合同。主题只声明自己真正提供的能力;未提供的区域继续使用默认 UI。

import type { ThemeModule } from "@vetta/theme-sdk";

const theme: ThemeModule = {
  meta: {
    id: "my-theme",
    name: "My Theme",
    sdkVersion: "0.1.0",
    version: "0.1.0",
  },
  appearance: {},
  components: {},
  regions: {},
  pages: [],
  runtime: [],
};

export default theme;

能力优先级

同一区域的生效顺序是:

Region overrideComponent overrideAppearance configDefault UI

只需要改背景、边框和颜色时停在 appearance;只替换一个按钮或列表项时使用 component;只有需要重新组合整个区域时才使用 region。

appearance

ThemeAppearance 提供:

  • colorScheme:主题激活时偏好的 lightdark 模式。
  • colors.common:明暗模式共享的 token 覆盖。
  • colors.light / colors.dark:模式专属颜色覆盖。
  • surfaces:在宿主登记的表面槽位上增加背景图、四角图、九宫格或水平切片装饰。

appearance 不改变组件行为。表面装饰不应拦截指针事件,也不应参与内容布局。

components

Component override 替换一个已登记的局部组件。实现必须与宿主 props 合同兼容:

  • 透传 onClickdisabledtitlearia-*data-*
  • 作为按钮、trigger 或焦点目标时转发 ref
  • 不依赖 Desktop 内部 atom、router、IPC 或私有 hook。
  • 不吞掉默认 action,也不假设父组件未承诺的 DOM 结构。

主题可以从 @vetta/theme-sdk/app-shellsidebar 等公开子路径读取 model hook,再把 model 传给官方 props-driven view。

regions

Region override 接管一个完整区域,可以重排默认组件、插入主题 UI 并复用 @vetta/theme-ui。它仍不拥有业务数据加载和跨领域流程。

Region props 提供稳定 model、actions 和 classNames。不要在主题中重新实现项目查询、会话切换、权限确认或 IPC。

pages

主题可以声明自己的页面:

{
  id: "sanctum",
  title: { "zh-CN": "洞府", "en-US": "Sanctum" },
  layout: "main",
  component: SanctumPage,
  nav: { order: 20 }
}
layout覆盖范围
content保留应用壳与标准内容约束
main接管主内容区,保留全局壳
app使用最宽的主题页面范围

宿主通过固定 /theme/$themeId/$pageId 路由承载页面。页面 ID 在主题内稳定,标题必须提供可本地化记录。

runtime

runtime 是主题激活期间常驻的无 UI React 组件,适合把宿主公开数据同步到主题自有状态。它通常返回 null

不要用 runtime 绕过主题边界执行通用业务逻辑、访问私有 store 或持有无法清理的全局副作用。所有 effect 必须在卸载时释放。

主题自有存储

useThemeStorage() 提供按当前 themeId 隔离的 JSON 可序列化键值存储:

import { useThemeStorage } from "@vetta/theme-sdk/storage";

const storage = useThemeStorage();
const value = storage.get("progress");
storage.set("progress", { score: 42 });
  • 只支持 null、布尔、数字、字符串、数组和普通对象。
  • 写入先更新内存缓存,再由宿主异步持久化。
  • 主题不能传入任意 themeId,隔离由宿主保证。
  • clear() 只清理当前主题的数据。

数据默认落在 ~/.vetta/desktop-app/themes/<themeId>/data.json。当前主题卸载流程不会自动清理该目录。

包边界

应放内容
@vetta/theme-sdk主题协议、registry、host facade、model hook 和 props 类型
@vetta/theme-ui不绑定 Desktop 私有状态的表面、装饰和布局 primitive
具体主题包主题组件、图片、样式、页面和 runtime
desktop真实业务状态、IPC、router、host adapter 和默认 connected container

主题不得导入 @shared/*@domains/*desktop/src/**。需要新的稳定能力时,先把窄合同加入 Theme SDK,而不是深度导入宿主实现。

验收清单

  1. 未启用主题时默认 UI 行为不变。
  2. 明暗模式、窗口尺寸和页面切换没有内容遮挡。
  3. component override 保留键盘、焦点和 aria 行为。
  4. region 不复制宿主数据获取或权限逻辑。
  5. runtime 和订阅在停用主题时完整清理。
  6. 用户可见文案走 i18n。
  7. 通过仓库 verify:ui:* 流程验证真实桌面宿主。

本页内容