feat: 初始化 Halo 暗色模式插件

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-06 21:03:00 +08:00
commit edbf78f236
115 changed files with 14745 additions and 0 deletions
@@ -0,0 +1,176 @@
# UI Entry & Routes
The frontend entry file exports a plugin definition using `definePlugin` from `@halo-dev/ui-shared`.
## Entry File (`ui/src/index.ts`)
```ts
import { definePlugin } from "@halo-dev/ui-shared";
import HomeView from "./views/HomeView.vue";
import { IconComputer } from "@halo-dev/components";
import { markRaw } from "vue";
export default definePlugin({
components: {
// Global component registration
// "MyComponent": MyComponent
},
routes: [
// Console routes
{
parentName: "Root",
route: {
path: "/my-plugin",
name: "MyPluginHome",
component: HomeView,
meta: {
title: "My Plugin",
permissions: [],
menu: {
name: "My Plugin",
group: "tool", // dashboard | content | interface | system | tool
icon: markRaw(IconComputer),
priority: 40,
},
},
},
},
],
ucRoutes: [
// UC (User Center) routes
{
parentName: "Root",
route: {
path: "/uc-my-plugin",
name: "MyPluginUCHome",
component: HomeView,
meta: {
permissions: [],
menu: {
name: "My Plugin",
priority: 40,
},
},
},
},
],
extensionPoints: {
// UI extension point implementations
},
formkit: {
inputs: {
// Custom FormKit inputs (Halo 2.25+)
},
},
});
```
## Route Definition
### With parentName (RouteRecordAppend)
```ts
{
parentName: "Root",
route: { /* RouteRecordRaw */ }
}
```
### Without parentName (RouteRecordRaw)
```ts
{
path: "/standalone",
name: "StandalonePage",
component: MyView
}
```
## Console Parent Routes
| parentName | Section |
| ----------------- | --------------------- |
| `Root` | Top level |
| `AttachmentsRoot` | Attachment management |
| `CommentsRoot` | Comments |
| `SinglePagesRoot` | Single pages |
| `PostsRoot` | Posts |
| `MenusRoot` | Menus |
| `ThemeRoot` | Themes |
| `OverviewRoot` | Overview |
| `BackupRoot` | Backups |
| `PluginsRoot` | Plugins |
| `SettingsRoot` | Settings |
| `UsersRoot` | Users |
| `ToolsRoot` | Tools |
## UC Parent Routes
| parentName | Section |
| ------------------- | ------------- |
| `PostsRoot` | Posts |
| `NotificationsRoot` | Notifications |
## RouteMeta
```ts
interface RouteMeta {
title?: string; // Browser tab title
searchable?: boolean; // Include in Console global search
permissions?: string[]; // Required UI permissions
menu?: {
name: string; // Menu display name
group?: CoreMenuGroupId; // Built-in group or custom group name
icon?: Component; // Vue icon component (use markRaw)
priority: number; // Sort order (lower = higher)
mobile?: boolean; // Show on mobile
};
}
```
## Using markRaw for Icons
Always wrap icon components with `markRaw`:
```ts
import { IconComputer } from "@halo-dev/components";
import { markRaw } from "vue";
icon: markRaw(IconComputer);
```
## Permissions in Routes
```ts
meta: {
permissions: ["plugin:my-plugin:person:manage"];
}
```
The route is hidden if the user lacks any of the listed permissions.
## Custom FormKit Inputs
Halo 2.25+ lets plugin UI entries register custom FormKit inputs for use in
plugin-provided FormKit Schema.
```ts
import { createInput } from "@formkit/vue";
import { definePlugin } from "@halo-dev/ui-shared";
import { defineAsyncComponent } from "vue";
export default definePlugin({
formkit: {
inputs: {
myPluginInput: createInput(
defineAsyncComponent(() => import("./components/MyPluginInput.vue")),
),
},
},
});
```
Use a plugin-prefixed input name to avoid collisions with Halo built-ins or
earlier-loaded plugins. If the input definition uses `@formkit/vue`, require
`@halo-dev/ui-plugin-bundler-kit@2.25.0+` and set `plugin.yaml` `spec.requires`
to `>=2.25.0`.