跳至正文

主题协议 ​

主题以浏览器 ESM 模块替换首页或登录页,通过宿主注入的 ThemeContext 读取状态、执行上传和登录。首次开发先完成主题快速开始。

主题包 ​

ZIP 根目录必须直接包含 theme.json,不要多包一层目录:

json
{
  "id": "example.my-theme",
  "defaultLocale": "en-US",
  "nameKey": "package.name",
  "version": "1.0.0",
  "apiVersion": 1,
  "supportedLocales": ["zh-CN", "en-US"],
  "messages": {
    "zh-CN": { "package.name": "我的主题", "accent.label": "主色" },
    "en-US": { "package.name": "My theme", "accent.label": "Accent color" }
  },
  "pages": {
    "home": { "entry": "entry.js" },
    "login": { "entry": "entry.js" }
  },
  "styles": ["style.css"],
  "configSchema": {
    "type": "object",
    "properties": {
      "accent": {
        "type": "string",
        "default": "#286456",
        "x-ui": { "labelKey": "accent.label" }
      }
    }
  }
}

至少提供一个页面。页面也可声明自己的 styles;preview 可指向包内 PNG、JPEG 或 WebP 预览图。JavaScript 必须是浏览器 ESM,依赖随包构建,不执行服务器安装脚本。上限为 ZIP 20 MiB、解压后 50 MiB、512 项;入口、样式和预览图必须存在,文件路径不能越界。

同 ID、同版本不能覆盖安装。分发内容变化时增加 version。框架和依赖随主题打包,不引用宿主组件或共享 Vue 实例。包内资源可公开访问,不包含凭据。

生命周期与能力 ​

ts
import type { ThemeContext } from "@picloom/theme-sdk";

export function mount(container: HTMLElement, ctx: ThemeContext) {
  const unsubscribe = ctx.subscribe((state) => {
    container.textContent = state.site.name;
  });
  return {
    dispose() {
      unsubscribe();
      container.replaceChildren();
    },
  };
}

完整类型以 SDK 为准。subscribe 立即提供一次状态,随后响应变化;返回快照不用于修改核心状态。主题通过操作函数提交行为:

  • selectProfile、uploads.add/addUrls/cancel/resume/dismiss:上传方案和队列。
  • organization.list(kind, query)、create(kind, name)、select({ tagIds, collectionIds }):查询、创建当前登录用户的标签或集合,选择新上传的初始归类。kind 为 tags 或 collections;列表返回 { items, total },接受 q、limit(默认 50、范围 1–100)、offset(默认 0)。快照的 organization 提供当前选择,每次入队时固定;游客隐藏此入口。
  • links:链接格式及复制偏好。
  • login:认证、会话刷新与安全返回,主题展示 snapshot.login。
  • navigate:首页、登录页和后台跳转。
  • setLocale、setMode:共享语言及显示偏好。
  • asset:解析包内资源;config:配置;t:主题字典;localized:解析多语言配置。

文件选择、拖拽、粘贴由主题绑定;不能无差别拦截文本框粘贴。上传进度到达 100% 后仍可能处于服务端处理状态,成功以 status: success 为准。核心负责分片、并发、CSRF、上传密钥及权限。

离开页面必须清理主题自行创建的定时器、DOM 监听器和框架实例。首页与登录页共享宿主上传队列;进入后台会加载新文档,有未结束任务时提示离页。关闭、刷新或跨文档导航不保证上传恢复。

在 Vue 中,将 subscribe() 提供的快照保存到本地 ref,再由它驱动渲染。ctx.t()、ctx.localized() 和 ctx.links.format() 不会为主题自己的 Vue 实例建立响应式依赖;使用 computed 时,显式依赖快照中的 locale 或 copy.format。配置在一次挂载期间固定,草稿变化后重新加载预览。

配置与多语言文本 ​

configSchema 生成后台配置表单,主题通过 ctx.config 读取保存值。省略 Schema 时配置为 {}。类型、组合约束和默认值规则见 Schema 参考,字段标签及控件见包文案与表单提示。主题不支持密钥和不可变字段,所有配置都可能公开给访客。

多语言内容字段使用 x-localized:

json
{
  "type": "string",
  "x-localized": true,
  "minLength": 1,
  "default": { "en-US": "Welcome", "zh-CN": "欢迎" }
}

default 和保存值使用语言字典;字符串约束、enum 和 const 分别校验每种语言的文本。字典键只能是核心支持的语言标识。通过 ctx.localized(ctx.config.heading) 读取当前语言;缺失或为空时返回空字符串,不跨语言回退,也不应自行补回管理员已清空的内容。

草稿与完整校验 ​

默认值仅初始化安装草稿和显式新增数组项;保存、读取、预览和应用不补值。主题字符串未声明 maxLength 时,上限为 10000 个 Unicode 字符。

操作校验内容
保存草稿类型、未知字段、enum/const、数值范围、字符串长度、模式与格式
预览或应用上述规则,以及 required、组合和条件约束、数组长度与唯一性

预览和应用按全部 supportedLocales 校验,包括站点尚未启用的语言。每种语言生成自己的配置视图执行约束;条件不能跨语言比较,也不写回视图。必需多语言字段须覆盖全部支持语言。缺失翻译可先保存草稿,违反 minLength: 1 的空串仍不能保存。

visibleWhen 读取当前编辑语言的配置视图;隐藏不删除值,也不绕过校验。password 控件只是遮罩,不能保护主题配置中的凭据。URL 和 CSS 配置值由主题按用途处理。

多语言 ​

清单必须声明非空的 supportedLocales,只能选择核心支持的语言;defaultLocale 必须属于该列表。主题名称和作者文案使用 messages,通过 ctx.t(key, params) 读取,支持 {name} 参数替换。字典完整性和回退规则见包文案与表单提示。

访客可用语言为站点启用语言与主题支持语言的交集。快照中的 locales 是语言标识列表,languages 提供名称与方向,defaultLocale 是站点默认语言。主题自行提供语言切换器;setLocale() 拒绝范围外语言,预览中切换不会持久化访客偏好。

上传、登录错误由核心翻译,语言切换不重启通知关闭计时器。新增核心语言时先注册语言并补充核心字典,再由主题增加支持声明和文案。

相关说明 ​

安装、草稿、应用及语言设置见使用主题。

独立项目见主题快速开始,官方主题构建见工具维护。

恢复入口见恢复默认页面,备份见主题数据。

管理接口见 HTTP API。

文档随 main 分支维护;部署与升级请核对产物版本及兼容说明。