Files
Serendipity edbf78f236 feat: 初始化 Halo 暗色模式插件
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试
- Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面
- 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等)
- 设计文档和调查文档
- Halo 插件/主题开发 Agent Skills

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 21:03:00 +08:00

3.4 KiB

Model Metadata (Annotations)

Themes can extend built-in Halo models with custom fields via AnnotationSetting resources (e.g. adding an icon to menu items, or a download URL to posts), then read those values in templates via the #annotations utility.

Defining a Metadata Form (AnnotationSetting)

Create a file (any name) in the theme root, e.g. annotation-setting.yaml, and declare an AnnotationSetting resource:

apiVersion: v1alpha1
kind: AnnotationSetting
metadata:
  name: theme-foo-menuitem-abc123 # recommended: add theme prefix + random suffix to avoid conflicts
spec:
  targetRef:
    group: ""
    kind: MenuItem
  formSchema:
    - $formkit: text
      name: icon
      label: Menu icon class
      value: ""

Multiple models can be declared in the same file separated by ---:

apiVersion: v1alpha1
kind: AnnotationSetting
metadata:
  name: theme-foo-post-abc123
spec:
  targetRef:
    group: content.halo.run
    kind: Post
  formSchema:
    - $formkit: text
      name: download_url
      label: Download URL
      value: ""

---
apiVersion: v1alpha1
kind: AnnotationSetting
metadata:
  name: theme-foo-menuitem-abc123
spec:
  targetRef:
    group: ""
    kind: MenuItem
  formSchema:
    - $formkit: text
      name: icon
      label: Icon
      value: ""

Supported Models

Model group kind
Post content.halo.run Post
Single page content.halo.run SinglePage
Post category content.halo.run Category
Post tag content.halo.run Tag
Menu item "" MenuItem
User "" User

Notes

  • All values in metadata.annotations are strings, so form values must also be strings.
  • Do not use components with non-string output such as number, group, or repeater.
  • For checkbox, explicitly set on-value / off-value to string values (e.g. "true" / "false").
  • Use a theme-name prefix plus a random suffix for metadata.name to avoid conflicts with other themes/plugins, e.g. theme-earth-post-wanfs5.

Reading Metadata in Templates

Halo provides a #annotations utility object in Thymeleaf with three methods:

#annotations.get(object, key) — Get a value

<div th:with="menu = ${menuFinder.getPrimary()}">
  <li th:each="item : ${menu.menuItems}">
    <i th:class="${#annotations.get(item, 'icon')}"></i>
    <a th:href="${item.status.href}" th:text="${item.status.displayName}"></a>
  </li>
</div>

#annotations.getOrDefault(object, key, defaultValue) — Get a value with fallback

<i th:class="${#annotations.getOrDefault(menuItem, 'icon', 'fa fa-link')}"></i>

#annotations.contains(object, key) — Check if a key exists

<i
  th:if="${#annotations.contains(menuItem, 'icon')}"
  th:class="${#annotations.get(menuItem, 'icon')}"
></i>

Online Docs

AnnotationSetting spec and supported models may change across Halo versions. Fetch the relevant doc if unsure about supported group/kind combinations or form schema constraints.