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.2 KiB

Plugin Interaction

Use this reference when a plugin depends on another plugin, exposes Java types for other plugins, shares events, or defines/consumes extension points.

Official docs:

Dependencies

Declare runtime plugin dependencies in plugin.yaml under spec.pluginDependencies.

spec:
  pluginDependencies:
    required-plugin: ">=1.0.0 & <2.0.0"
    optional-plugin?: "1.*"
  • Dependency keys are plugin metadata.name values.
  • Optional dependencies use a ? suffix and require Halo 2.20.11+.
  • Prefer explicit versions or ranges. Avoid broad * constraints for production plugins.

API Modules

When other plugins need to compile against your public Java types, put those types in a separate API module and publish it. Keep implementation code in the plugin module.

Consumer plugins should normally depend on the provider API module with compileOnly, not package the provider classes into their plugin jar.

dependencies {
    compileOnly "run.halo.example:plugin-a-api:1.0.0"
}

Shared Events

Use Spring events for plugin-to-plugin notifications. Mark custom event classes with @SharedEvent when dependent plugins should be able to listen to them.

import org.springframework.context.ApplicationEvent;
import run.halo.app.plugin.SharedEvent;

@SharedEvent
public class CustomSharedEvent extends ApplicationEvent {
    public CustomSharedEvent(Object source) {
        super(source);
    }
}

Listen with @EventListener or ApplicationListener. Built-in shared events include post publish/update/delete/visibility changes, user login/logout, and third-party login disconnection events. Check the official docs for the current event class names before importing.

Extension Points

To make a plugin extensible:

  1. Define an interface that extends org.pf4j.ExtensionPoint.
  2. Declare an ExtensionPointDefinition resource under src/main/resources/extensions/.
  3. Publish the interface in an API module so extension plugins can compile against it.
  4. Resolve enabled implementations with ExtensionGetter.
apiVersion: plugin.halo.run/v1alpha1
kind: ExtensionPointDefinition
metadata:
  name: my-plugin-reactive-notifier
spec:
  className: run.halo.example.ReactiveNotifier
  displayName: "Reactive Notifier"
  description: "Extends notification delivery"
  type: MULTI_INSTANCE

Use a plugin-prefixed metadata.name to avoid collisions. Use SINGLE_INSTANCE only when exactly one enabled implementation makes sense; otherwise use MULTI_INSTANCE.

extensionGetter.getEnabledExtensions(ReactiveNotifier.class)
    .flatMap(notifier -> notifier.notify(context))
    .then();