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

92 lines
10 KiB
Markdown

---
name: halo-plugin-dev
description: >
Use when creating or modifying a Halo CMS plugin: writing Java backend code,
configuring plugin.yaml, defining custom extensions (GVK), creating custom APIs
(CustomEndpoint, MVC controllers), building Vue-based UI with @halo-dev/ui-shared,
setting up Gradle builds with DevTools, defining RBAC role templates, declaring
pluginDependencies, exposing or consuming plugin extension points, publishing
shared events, registering custom FormKit inputs, generating API clients from
OpenAPI, registering theme-side Finder APIs, or handling plugin lifecycle
(start/stop/delete).
---
# Halo Plugin Development
Halo is built on **Spring Boot + Spring WebFlux + Vue 3**. A plugin consists of:
- **Backend (Java)**: runs inside Halo's JVM, uses Spring DI, reactive WebFlux, custom extensions (CRD-like), and custom APIs
- **Frontend (Vue/TypeScript)**: built into `main.js` + `style.css`, injected into Console and UC (User Center)
- **Manifest (`plugin.yaml`)**: plugin metadata, dependencies, settings, and config map names
> **Important**: Halo's plugin APIs, VO field names, extension annotations, and UI APIs evolve across versions. **Do not rely on training data for specific field names, method signatures, or type structures.** When writing code that accesses extension fields or calls shared beans, always fetch the relevant online doc from the References section below first.
## Quick Start
Create a new plugin project using the official scaffolding tool:
```bash
pnpm create halo-plugin
```
Follow the prompts (plugin name, domain, author, UI build tool: Rsbuild or Vite).
Then run with DevTools (requires Docker):
```bash
./gradlew haloServer
```
Visit `http://localhost:8090/console` — username/password defaults to `admin`/`admin`.
After code changes:
```bash
./gradlew reload
```
Or use `watch` for auto-reload:
```bash
./gradlew watch
```
## Development Workflow
1. **Scaffold**: `pnpm create halo-plugin`
2. **Backend**: write Java code under `src/main/java/`
3. **Frontend**: write Vue/TS code under `ui/src/` (or `console/src/`)
4. **Manifest**: configure `src/main/resources/plugin.yaml`
5. **Extensions**: declare YAML resources under `src/main/resources/extensions/`
6. **Run**: `./gradlew haloServer` (with Docker)
7. **Test**: visit Console at `http://localhost:8090/console`
8. **Build**: `./gradlew build` produces a JAR for distribution
## References Index
| File | Content | When to read |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| [references/api-changelog.md](references/api-changelog.md) | High-impact plugin API changes by Halo version, with docs routes | Before using version-sensitive APIs, upgrading Halo dependencies, or raising `spec.requires` |
| [references/plugin-structure.md](references/plugin-structure.md) | Directory structure, backend/frontend layout, build.gradle basics | Creating a new plugin from scratch or understanding the directory layout |
| [references/plugin-manifest.md](references/plugin-manifest.md) | plugin.yaml fields, version requirements, dependencies, settings/configMap | Writing or editing plugin.yaml |
| [references/plugin-interaction.md](references/plugin-interaction.md) | pluginDependencies, API modules, shared events, defining and consuming plugin extension points | Depending on another plugin, exposing an API module, sharing events, or making a plugin extensible |
| [references/devtools.md](references/devtools.md) | haloServer, reload, watch, generateApiClient, generateRoleTasks, debug config | Running `./gradlew haloServer`, hot reload, or debugging a plugin |
| [references/server-extension.md](references/server-extension.md) | Custom Extension (GVK), AbstractExtension, CRUD APIs, indexes, field/label selectors | Defining a custom data model, storage, or query indexes |
| [references/server-api.md](references/server-api.md) | CustomEndpoint, @Controller with @ApiVersion, query params, validation, OpenAPI docs | Writing a new backend API endpoint or controller |
| [references/server-lifecycle.md](references/server-lifecycle.md) | BasePlugin lifecycle (start/stop/delete), Scheme registration/cleanup | Handling plugin start, stop, delete, or scheme registration |
| [references/server-shared-beans.md](references/server-shared-beans.md) | ReactiveExtensionClient, SchemeManager, UserService, AttachmentService, ExtensionGetter, etc. | Injecting or calling Halo core services from plugin Java code |
| [references/server-security.md](references/server-security.md) | Role templates, RBAC rules, verbs, aggregation, UI permissions | Adding RBAC roles, API permissions, or UI permission checks |
| [references/ui-entry.md](references/ui-entry.md) | definePlugin, routes/ucRoutes, menu config, parentName, RouteMeta | Adding a new page to the Console or User Center |
| [references/ui-build.md](references/ui-build.md) | @halo-dev/ui-plugin-bundler-kit (Vite/Rsbuild), output dirs, migration | Configuring the frontend build (Vite/Rsbuild) or troubleshooting bundling |
| [references/ui-shared.md](references/ui-shared.md) | stores (currentUser, globalInfo), utils (date, permission, attachment, id), events | Formatting dates, checking permissions, handling attachments, generating IDs — do NOT install dayjs/date-fns or write your own date utils |
| [references/ui-extension-points.md](references/ui-extension-points.md) | ExtensionPoint keys, editor, attachment selector, dashboard widgets, list operations/fields | Extending existing Console UI (editor, lists, attachment picker, dashboard) |
| [references/ui-components.md](references/ui-components.md) | Base components (@halo-dev/components), business components, directives (v-permission, v-tooltip) | Looking for a UI component, directive, or modal API to use in Vue code |
| [references/ui-forms.md](references/ui-forms.md) | FormKit schema and component usage, custom inputs (select, attachment, array, etc.), validation | Building a form with FormKit schema or custom inputs |
| [references/ui-api-request.md](references/ui-api-request.md) | @halo-dev/api-client (coreApiClient, axiosInstance), generateApiClient Gradle task | Making HTTP requests from plugin UI to Halo APIs |
| [references/ui-tooling.md](references/ui-tooling.md) | unplugin-icons + Iconify, UnoCSS (Vite/Rsbuild config) | Adding icons or writing atomic CSS (UnoCSS) in plugin UI |
| [references/server-reconciler.md](references/server-reconciler.md) | Reconciler + ControllerBuilder, finalizers, retry scheduling, vs Watcher | Building a controller that watches and reconciles resource state |
| [references/server-search.md](references/server-search.md) | HaloDocument, HaloDocumentsProvider, SearchEngine, search events | Integrating with Halo search (indexing, searching, search events) |
| [references/theme-head-processor.md](references/theme-head-processor.md) | TemplateHeadProcessor for injecting scripts/styles/meta into theme head | Injecting scripts, styles, or meta tags into the theme `<head>` |
| [references/theme-content-handler.md](references/theme-content-handler.md) | ReactivePostContentHandler / ReactiveSinglePageContentHandler for modifying rendered HTML | Modifying post or page HTML after rendering |
| [references/theme-integration.md](references/theme-integration.md) | Finder API for themes, template variables, reverse proxy, static resources, CommentSubject | Adding theme-side template variables or Finder APIs |