diff --git a/.agents/skills/rspress-best-practices/SKILL.md b/.agents/skills/rspress-best-practices/SKILL.md new file mode 100644 index 00000000..af0370b9 --- /dev/null +++ b/.agents/skills/rspress-best-practices/SKILL.md @@ -0,0 +1,82 @@ +--- +name: rspress-best-practices +description: Rspress best practices for config, CLI workflow, content organization, frontmatter, MDX, themes, i18n, search, static assets, deployment, and debugging. Use when writing, reviewing, or troubleshooting Rspress documentation sites. +--- + +# Rspress Best Practices + +Apply these rules when writing or reviewing Rspress (v2) sites. + +## Configuration + +- Use `rspress.config.ts` and `defineConfig` from `@rspress/core` +- Set `root` explicitly when docs are not under the default `docs/` directory +- Keep site-wide settings such as `title`, `description`, `icon`, `logo`, `base`, and `lang` in config instead of repeating them in page files +- Prefer first-class Rspress options before custom theme code or low-level bundler overrides +- Keep custom theme code in a top-level `theme/` directory and import original theme pieces from `@rspress/core/theme-original` + +## CLI + +- Use `rspress dev` for local development +- Use `rspress build` for production output +- Use `rspress preview` only for local preview of the built site +- Use `rspress eject` only when CSS variables, class overrides, or layout wrapping cannot solve the customization + +## Docs Structure And Navigation + +- Keep docs content under one clear docs root and group pages by topic or workflow, not by team ownership +- Use `_meta.json` or `_nav.json` to control sidebar and navigation labels/order instead of encoding order in filenames +- Put reusable MDX snippets or shared components in shared files instead of duplicating them across pages +- Keep landing pages concise and link to deeper task-oriented guides from them + +## Writing And Frontmatter + +- Add clear `title` and `description` frontmatter, and set `sidebar`, `outline`, `navbar`, or `footer` only when page defaults are not enough +- Use `pageType: home`, `doc`, `doc-wide`, `custom`, or `blank` intentionally based on layout needs +- Write task-first headings and short intros; avoid marketing-heavy copy in technical docs +- Prefer one topic per page and split overly long pages by workflow or feature area +- Keep code examples minimal, runnable, and version-accurate + +## MDX And Components + +- Use MDX for interactive docs and embedded components, but keep the main narrative understandable as plain markdown +- Prefer documented Rspress theme/runtime APIs over importing from internal source paths +- For app-wide UI or providers, use `globalUIComponents` or theme overrides instead of repeating imports in each page + +## Theme And Styling + +- Prefer CSS variables for brand colors, spacing, and surface styling +- Prefer BEM class overrides or `Layout` slots before ejecting built-in components +- In `theme/` files, keep `export * from '@rspress/core/theme-original'` unless intentionally replacing a named export +- Avoid full component ejection unless config, CSS, and wrapping cannot meet the requirement + +## I18n, Search, And AI + +- For multilingual sites, organize locale content under per-language directories and keep navigation mirrored where practical +- Keep descriptions and other frontmatter text in the same language as the page content +- Configure search intentionally: use local search for small or medium sites, and hosted search when scale or cross-version indexing requires it +- Enable `llms` or `ssgMd` only when the site benefits from machine-readable outputs, and keep descriptions accurate because those outputs surface page summaries + +## Assets And Public Files + +- Import source-managed images and components from docs/theme source when they belong to the content +- Use `public/` only for assets that must keep stable URL paths, such as favicons, social images, or download files +- Reference public assets by absolute site path and make sure they still work when `base` is set + +## Plugins And Integration + +- Prefer official Rspress plugins for search, preview, and API-doc scenarios before building custom solutions +- For component or library docs, use `@rspress/plugin-preview` and `@rspress/plugin-api-docgen` when interactive demos or API tables are needed +- Keep plugin usage explicit in config and remove unused plugins to reduce maintenance cost + +## Build, Deploy, And Debugging + +- Validate both `rspress dev` and `rspress build`; a page that works in dev can still fail during static generation +- Verify broken links, missing assets, and wrong `base` handling before deployment +- Keep generated output out of source control unless the hosting workflow explicitly requires committed artifacts +- When debugging content issues, inspect the resolved docs root, frontmatter, and theme overrides before assuming a bundler problem + +## Documentation + +- For the latest Rspress docs, read https://rspress.rs/llms.txt +- Use the config and API docs when checking exact option names or current behavior diff --git a/.agents/skills/rspress-custom-theme/SKILL.md b/.agents/skills/rspress-custom-theme/SKILL.md new file mode 100644 index 00000000..2ee763cf --- /dev/null +++ b/.agents/skills/rspress-custom-theme/SKILL.md @@ -0,0 +1,242 @@ +--- +name: rspress-custom-theme +description: Customize Rspress themes using CSS variables, Layout slots, component wrapping, or component ejection. Use when a user wants to change the look and feel of an Rspress site, override theme components, add custom navigation/sidebar/footer content, inject global providers, or modify the default Rspress theme in any way. Also use when a user mentions theme/index.tsx, Layout slots, BEM class overrides, or rspress eject. +--- + +# Rspress Custom Theme + +Guide for customizing Rspress (v2) themes. Rspress offers four levels of customization, from lightest to heaviest. Always prefer the lightest approach that meets the requirement — lighter approaches are more maintainable and survive Rspress upgrades. + +## Workflow + +1. **Understand the user's goal** — what do they want to change? (colors, layout, inject content, replace a component entirely?) +2. **Pick the right level** using the decision flow below +3. **Set up `theme/index.tsx`** if needed (Levels 1A, 3, 4 all need it) +4. **Implement** following the patterns in this skill and reference files +5. **Verify** the user's Rspress version is v2 (imports use `@rspress/core/*` not `rspress/*`) + +## Decision Flow + +| User wants to... | Level | Approach | +| ---------------------------------------------------------------- | ----- | --------------------------- | +| Change brand colors, fonts, spacing, shadows | 1 | CSS variables | +| Adjust a specific component's style (borders, padding, etc.) | 2 | BEM class overrides | +| Add content around existing components (banners, footers, logos) | 3 | Layout slots (wrap) | +| Override MDX rendering (custom `

`, ``, etc.) | 3 | `components` slot | +| Wrap the app in a provider (state, analytics, auth) | 4 | Eject `Root` | +| Replace built-in icons (logo, GitHub, search, etc.) | — | Icon re-export | +| Completely replace a built-in component | 4 | Eject that component | +| Add a global floating component (back-to-top, chat widget) | — | `globalUIComponents` config | +| Control page layout structure (hide sidebar, blank page) | — | Frontmatter `pageType` | + +--- + +## theme/index.tsx — The Entry Point + +Levels 1A, 3, and 4 all require a `theme/index.tsx` file in the project root (sibling to `docs/`). This is the single entry point for all theme customizations: + +```text +project/ +├── docs/ +├── theme/ +│ ├── index.tsx # Theme entry — re-exports + overrides +│ ├── index.css # CSS variable / BEM overrides (optional) +│ └── components/ # Ejected components (Level 4) +└── rspress.config.ts +``` + +Minimal setup: + +```tsx +// theme/index.tsx +import './index.css'; // optional +export * from '@rspress/core/theme-original'; +``` + +**Critical import rule**: Inside `theme/` files, always import from `@rspress/core/theme-original`. The path `@rspress/core/theme` resolves to your own `theme/index.tsx`, which causes circular imports. (In `docs/` MDX files, `@rspress/core/theme` is fine — it correctly points to your custom theme.) + +--- + +## Level 1: CSS Variables + +Override CSS custom properties for brand colors, backgrounds, text, code blocks, and more. + +**Option A** — `theme/index.css` (use when you also have component overrides in `theme/index.tsx`): + +```css +/* theme/index.css */ +:root { + --rp-c-brand: #7c3aed; + --rp-c-brand-light: #8b5cf6; + --rp-c-brand-dark: #6d28d9; +} +.dark { + --rp-c-brand: #a78bfa; +} +``` + +**Option B** — `globalStyles` (use when you only need CSS changes, no component overrides): + +```ts +// rspress.config.ts +export default defineConfig({ + globalStyles: path.join(__dirname, 'styles/custom.css'), +}); +``` + +> **Full variable list**: Read `references/css-variables.md` for all available CSS variables with light/dark defaults. + +--- + +## Level 2: BEM Class Overrides + +All built-in components follow BEM naming: `.rp-[component]__[element]--[modifier]`. + +Common targets: `.rp-nav`, `.rp-link`, `.rp-tabs`, `.rp-codeblock`, `.rp-codeblock__title`, `.rp-nav-menu__item--active`. + +Use these in your CSS file for targeted style changes when CSS variables aren't granular enough. + +--- + +## Level 3: Wrap (Layout Slots) + +Inject content at specific positions in the layout without replacing built-in components. Override `Layout` in `theme/index.tsx`: + +```tsx +// theme/index.tsx +import { Layout as OriginalLayout } from '@rspress/core/theme-original'; +export * from '@rspress/core/theme-original'; + +export function Layout() { + return ( + } bottom={} /> + ); +} +``` + +Use runtime hooks inside slot components — import from `@rspress/core/runtime`: `useDark()`, `useLang()`, `useVersion()`, `usePage()`, `useSite()`, `useFrontmatter()`, `useI18n()`. + +> **All slots & examples**: Read `references/layout-slots.md` for the complete slot list and usage patterns including i18n and MDX component overrides. + +--- + +## Level 4: Eject + +Copy a built-in component's source for full replacement. Only use when wrap/slots cannot achieve the customization. + +```bash +rspress eject # list available components +rspress eject DocFooter # eject to theme/components/DocFooter/ +``` + +Then re-export in `theme/index.tsx` (named export takes precedence over the wildcard): + +```tsx +export * from '@rspress/core/theme-original'; +export { DocFooter } from './components/DocFooter'; +``` + +> **Component list & patterns**: Read `references/eject-components.md` for available components, workflow, and common patterns. + +--- + +## Custom Icons + +Rspress has 27 built-in icons used across the UI. You can replace any of them by re-exporting your own icon component with the same name — no ejection needed. This uses the same `theme/index.tsx` mechanism: your named export takes precedence over the wildcard re-export. + +**Icon type**: Each icon is a React component or a URL string: + +```ts +import type { FC, SVGProps } from 'react'; +type Icon = FC> | string; +``` + +**Example 1** — Replace an icon with a custom SVG component: + +```tsx +// theme/index.tsx +export * from '@rspress/core/theme-original'; + +// Named export overrides the wildcard — replaces the GitHub icon site-wide +export const IconGithub = (props: React.SVGProps) => ( + + + +); +``` + +**Example 2** — Use an SVGR import: + +```tsx +// theme/index.tsx +export * from '@rspress/core/theme-original'; + +import CustomGithubIcon from './icons/github.svg?react'; +export const IconGithub = CustomGithubIcon; +``` + +**Using `SvgWrapper` in MDX or custom components**: + +```mdx +import { SvgWrapper, IconGithub } from '@rspress/core/theme'; + + +``` + +**Available icons**: `IconArrowDown`, `IconArrowRight`, `IconClose`, `IconCopy`, `IconDeprecated`, `IconDown`, `IconEdit`, `IconEmpty`, `IconExperimental`, `IconExternalLink`, `IconFile`, `IconGithub`, `IconGitlab`, `IconHeader`, `IconJump`, `IconLink`, `IconLoading`, `IconMenu`, `IconMoon`, `IconScrollToTop`, `IconSearch`, `IconSmallMenu`, `IconSuccess`, `IconSun`, `IconTitle`, `IconWrap`, `IconWrapped`. + +> **Source**: See the [icons source](https://github.com/web-infra-dev/rspress/blob/main/packages/core/src/theme/icons.ts) for default implementations. + +--- + +## Global UI Components + +For components that should render on every page without theme overrides: + +```ts +// rspress.config.ts +export default defineConfig({ + globalUIComponents: [ + path.join(__dirname, 'components', 'BackToTop.tsx'), + [ + path.join(__dirname, 'components', 'Analytics.tsx'), + { trackingId: '...' }, + ], + ], +}); +``` + +--- + +## Page Types + +Control layout per page via frontmatter `pageType`: + +| Value | Description | +| ---------- | ------------------------------------- | +| `home` | Home page with navbar | +| `doc` | Standard doc with sidebar and outline | +| `doc-wide` | Doc without sidebar/outline | +| `custom` | Custom content with navbar only | +| `blank` | Custom content without navbar | +| `404` | 404 error page | + +Fine-grained: set `navbar: false`, `sidebar: false`, `outline: false`, `footer: false` individually. + +--- + +## Common Pitfalls + +- **Circular import**: Using `@rspress/core/theme` instead of `@rspress/core/theme-original` in `theme/` files — causes infinite loop. +- **Eject over-use**: Ejecting when a Layout slot or CSS variable would suffice — creates upgrade burden. +- **Missing re-export**: Forgetting `export * from '@rspress/core/theme-original'` in `theme/index.tsx` — breaks all un-overridden components. +- **v1 imports**: Using `rspress/theme` or `@rspress/theme-default` — these are v1 paths. v2 uses `@rspress/core/theme-original`. + +## Reference + +- Custom theme guide: +- CSS variables: +- Layout component: +- Built-in icons: +- Built-in hooks: +- CLI commands (eject): diff --git a/.agents/skills/rspress-custom-theme/references/css-variables.md b/.agents/skills/rspress-custom-theme/references/css-variables.md new file mode 100644 index 00000000..a06573c0 --- /dev/null +++ b/.agents/skills/rspress-custom-theme/references/css-variables.md @@ -0,0 +1,177 @@ +# CSS Variables Reference + +Complete list of CSS variables exposed by Rspress for theme customization. + +- **Override location**: `theme/index.css` or `globalStyles` in `rspress.config.ts` +- **Dark mode selector**: `.dark { ... }` +- **Official docs**: + +--- + +## Brand Colors (shared) + +```css +:root { + --rp-c-brand: #0095ff; + --rp-c-brand-light: #33adff; + --rp-c-brand-lighter: #c6e0fd; + --rp-c-brand-dark: #0077ff; + --rp-c-brand-darker: #005fcc; + --rp-c-brand-tint: rgba(127, 163, 255, 0.16); +} +``` + +## Base Variables + +| Variable | Light | Dark | +| ---------------------- | --------------------- | ------------------------ | +| `--rp-c-bg` | `#ffffff` | `#121212` | +| `--rp-c-bg-soft` | `#f8f8f9` | `#292e37` | +| `--rp-c-bg-mute` | `#f1f1f1` | `#343a46` | +| `--rp-c-bg-alt` | `#fff` | `#000` | +| `--rp-c-divider` | `rgba(0, 0, 0, 0.25)` | `rgba(84, 84, 84, 0.65)` | +| `--rp-c-divider-light` | `rgba(0, 0, 0, 0.12)` | `rgba(84, 84, 84, 0.48)` | + +## Text Colors + +| Variable | Light | Dark | +| --------------- | ------------------------ | --------------------------- | +| `--rp-c-text-0` | `#000000` | `#ffffff` | +| `--rp-c-text-1` | `#242424` | `rgba(255, 255, 245, 0.93)` | +| `--rp-c-text-2` | `rgba(0, 0, 0, 0.7)` | `rgba(255, 255, 245, 0.65)` | +| `--rp-c-text-3` | `rgba(60, 60, 60, 0.33)` | `rgba(235, 235, 235, 0.38)` | +| `--rp-c-text-4` | `rgba(60, 60, 60, 0.18)` | `rgba(235, 235, 235, 0.18)` | +| `--rp-c-link` | `var(--rp-c-brand-dark)` | `var(--rp-c-brand-light)` | + +## Inline Code + +| Variable | Light | Dark | +| ------------------------- | --------------------------- | --------------------------- | +| `--rp-c-text-code` | `#476582` | `#c9def1` | +| `--rp-c-text-code-bg` | `rgba(153, 161, 179, 0.06)` | `rgba(255, 255, 255, 0.06)` | +| `--rp-c-text-code-border` | `rgba(0, 0, 0, 0.035)` | `rgba(255, 255, 255, 0.04)` | + +## Code Blocks + +| Variable | Light | Dark | +| ------------------------ | ------------------------------------- | ------------------------------------- | +| `--rp-code-font-size` | `0.875rem` | `0.875rem` | +| `--rp-code-title-bg` | `#f8f8f9` | `#191919` | +| `--rp-code-block-color` | `rgb(46, 52, 64)` | `rgb(229, 231, 235)` | +| `--rp-code-block-bg` | `var(--rp-c-bg)` | `var(--rp-c-bg)` | +| `--rp-code-block-border` | `1px solid var(--rp-c-divider-light)` | `1px solid var(--rp-c-divider-light)` | +| `--rp-code-block-shadow` | `none` | `none` | + +## Shiki Syntax Highlighting + +Rspress uses `.dark` on `html` as the public dark-mode toggle for general theme overrides. The Shiki token blocks below target `html:not(.rp-dark)` and `html.rp-dark`, which Rspress uses internally for syntax highlighting variables. + +### Light + +```css +html:not(.rp-dark) { + --shiki-foreground: inherit; + --shiki-background: transparent; + --shiki-token-constant: #1976d2; + --shiki-token-string: #31a94d; + --shiki-token-comment: rgb(182, 180, 180); + --shiki-token-keyword: #cf2727; + --shiki-token-parameter: #f59403; + --shiki-token-function: #7041c8; + --shiki-token-string-expression: #218438; + --shiki-token-punctuation: #242323; + --shiki-token-link: #22863a; + --shiki-token-deleted: #d32828; + --shiki-token-inserted: #22863a; +} +``` + +### Dark + +```css +html.rp-dark { + --shiki-foreground: inherit; + --shiki-background: transparent; + --shiki-token-constant: #6fb0fa; + --shiki-token-string: #f9a86e; + --shiki-token-comment: #6a727b; + --shiki-token-keyword: #f47481; + --shiki-token-parameter: #ff9800; + --shiki-token-function: #ae8eeb; + --shiki-token-string-expression: #4fb74d; + --shiki-token-punctuation: #bbbbbb; + --shiki-token-link: #f9a76d; + --shiki-token-deleted: #ee6d7a; + --shiki-token-inserted: #36c47f; +} +``` + +## Grays (shared) + +```css +:root { + --rp-c-gray: #8e8e8e; + --rp-c-gray-light-1: #aeaeae; + --rp-c-gray-light-2: #c7c7c7; + --rp-c-gray-light-3: #d1d1d1; + --rp-c-gray-light-4: #e5e5e5; + --rp-c-gray-light-5: #f2f2f2; +} +``` + +## Shadows (shared) + +```css +:root { + --rp-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.02), 0 1px 0 rgba(0, 0, 0, 0.06); + --rp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.06), 0 1px 4px rgba(0, 0, 0, 0.07); + --rp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08); + --rp-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12); + --rp-shadow-5: + 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16); +} +``` + +## Radius (shared) + +```css +:root { + --rp-radius: 1rem; + --rp-radius-small: 0.5rem; + --rp-radius-large: 1.5rem; +} +``` + +## Home Page + +Note: `...` in gradient values marks omitted gradient parameters, not literal CSS. See the official docs link above for complete values. + +| Variable | Light | Dark | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `--rp-home-hero-secondary-color` | `#a673ff` | `#a673ff` | +| `--rp-home-hero-title-color` | `transparent` | `transparent` | +| `--rp-home-hero-title-bg` | `linear-gradient(90deg, var(--rp-c-brand-dark) 0%, var(--rp-c-brand-dark) 30%, var(--rp-home-hero-secondary-color) 100%)` | (same) | +| `--rp-home-background-bg` | `radial-gradient(...), radial-gradient(...), radial-gradient(...), #fff` | `radial-gradient(...), radial-gradient(...), radial-gradient(...), #121212` | +| `--rp-home-feature-bg` | `linear-gradient(135deg, #fff, #f9f9f980)` | `linear-gradient(135deg, #ffffff00, #ffffff08)` | + +## Quick Start + +```css +/* Example brand color overrides for the custom theme scaffold. */ +/* For more CSS variables, see https://rspress.rs/ui/vars. */ +:root { + --rp-c-brand: #ff5e00; + --rp-c-brand-dark: #ff704d; + --rp-c-brand-darker: #ff704d; + --rp-c-brand-light: #ff7524; + --rp-c-brand-lighter: #ff7524; + --rp-c-brand-tint: rgba(255, 94, 0, 0.07); + + --rp-home-hero-secondary-color: #ff5e00; +} + +.dark { + --rp-c-brand: #ff8c4d; + --rp-home-hero-secondary-color: #ff8c4d; +} +``` diff --git a/.agents/skills/rspress-custom-theme/references/eject-components.md b/.agents/skills/rspress-custom-theme/references/eject-components.md new file mode 100644 index 00000000..235800fe --- /dev/null +++ b/.agents/skills/rspress-custom-theme/references/eject-components.md @@ -0,0 +1,154 @@ +# Eject Components Reference + +Eject copies a built-in component's source code into your project for full customization. This is the heaviest approach — ejected components do not receive automatic updates when Rspress upgrades. Prefer CSS variables, BEM overrides, or Layout slots whenever possible. + +Official reference: + +--- + +## Eject Command + +```bash +# List all available components +rspress eject + +# Eject a specific component +rspress eject +``` + +Ejected source is placed in `theme/components//`. + +## Available Components + +| Component | Description | Consider wrapping first? | +| ---------------- | ----------------------------------------- | ------------------------------------------------ | +| `Layout` | Main layout container with all slot props | Yes — use Layout slots instead | +| `Root` | Application root wrapper | Only eject for global providers | +| `Banner` | Notification banner at top of page | Check `top` slot first | +| `NavTitle` | Navigation logo and title | Check `navTitle` / `beforeNavTitle` slots | +| `HomeLayout` | Complete home page layout | Check home page slots first | +| `HomeHero` | Hero section on home page | Check `beforeHero` / `afterHero` slots | +| `HomeFeature` | Feature grid cards | Check `beforeFeatures` / `afterFeatures` slots | +| `HomeBackground` | Home page background effects | Try CSS variables first | +| `HomeFooter` | Home page footer | Check `bottom` slot first | +| `DocFooter` | Documentation page footer | Check `beforeDocFooter` / `afterDocFooter` slots | +| `EditLink` | "Edit this page" link | Configure via `themeConfig.editLink` | +| `LastUpdated` | Last updated timestamp | Usually config is enough | +| `PrevNextPage` | Previous/next page navigation | Check `beforeDocFooter` slot | +| `OverviewGroup` | Overview page group cards | — | +| `Tag` | Tag/label component | — | + +## Step-by-Step Eject Workflow + +1. **Eject the component:** + + ```bash + rspress eject DocFooter + ``` + +2. **Re-export in theme/index.tsx:** + + ```tsx + // theme/index.tsx + export * from '@rspress/core/theme-original'; + export { DocFooter } from './components/DocFooter'; + ``` + + The named export takes precedence over the wildcard re-export, so Rspress uses your custom version. + +3. **Modify the ejected source** in `theme/components/DocFooter/`. + +## Common Pattern: Root for Global Providers + +The most common eject use case is wrapping the entire app in a context provider (state management, analytics, auth, etc.): + +```tsx +// theme/components/Root/index.tsx +import type { RootProps } from '@rspress/core/theme'; + +export function Root({ children }: RootProps) { + return ( + + {children} + + ); +} +``` + +```tsx +// theme/index.tsx +export * from '@rspress/core/theme-original'; +export { Root } from './components/Root'; +``` + +## Common Pattern: Custom Home Page (HomeLayout) + +When the default home page structure (Hero + Features) doesn't meet the design requirements — for example, you need a completely different landing page with custom sections, animations, or a non-standard layout — write a custom `HomeLayout` component and re-export it directly: + +```tsx +// theme/components/HomeLayout/index.tsx +import { useSite, useLang } from '@rspress/core/runtime'; + +export function HomeLayout() { + const site = useSite(); + const lang = useLang(); + const { title, description } = site.siteData; + + return ( +
+
+

{title}

+

{description}

+ +
+ +
+ {/* Custom content: testimonials, stats, demos, etc. */} +
+
+ ); +} +``` + +```tsx +// theme/index.tsx +export * from '@rspress/core/theme-original'; +export { HomeLayout } from './components/HomeLayout'; +``` + +The named export overrides the built-in `HomeLayout` from the wildcard re-export — no need to eject first. + +If you only need to add content before/after the Hero or Features sections (without replacing the entire home page), prefer Layout slots (`beforeHero`, `afterHero`, `beforeFeatures`, `afterFeatures`) instead — see `references/layout-slots.md`. + +## Common Pattern: Custom Doc Footer + +```tsx +// theme/components/DocFooter/index.tsx +import { useFrontmatter } from '@rspress/core/runtime'; + +export function DocFooter() { + const frontmatter = useFrontmatter(); + return ( +
+ {frontmatter.author && Author: {frontmatter.author}} + Edit this page +
+ ); +} +``` + +## Important Notes + +- Always import from `@rspress/core/theme-original` in `theme/` files, never from `@rspress/core/theme` (the latter resolves to your own `theme/index.tsx`, causing circular imports). +- After ejecting, you own that component. Track Rspress changelogs for upstream changes you might want to incorporate manually. +- Run `rspress eject` (no args) to see the up-to-date list of available components — the list above may change between Rspress versions. diff --git a/.agents/skills/rspress-custom-theme/references/layout-slots.md b/.agents/skills/rspress-custom-theme/references/layout-slots.md new file mode 100644 index 00000000..1774218c --- /dev/null +++ b/.agents/skills/rspress-custom-theme/references/layout-slots.md @@ -0,0 +1,153 @@ +# Layout Slots Reference + +The `Layout` component accepts slot props (`React.ReactNode`) for injecting content at specific positions without replacing built-in components. This is the recommended way to extend Rspress before considering eject. + +Official reference: + +--- + +## All Available Slots + +### Navigation Bar + +| Slot | Position | +| ---------------- | ------------------------------------ | +| `beforeNav` | Before the entire navigation bar | +| `afterNav` | After the entire navigation bar | +| `beforeNavTitle` | Before the nav title/logo (top-left) | +| `navTitle` | Replaces the nav title content | +| `afterNavTitle` | After the nav title/logo | +| `beforeNavMenu` | Before the nav menu items | +| `afterNavMenu` | After the nav menu items | + +### Sidebar & Outline + +| Slot | Position | +| --------------- | ----------------------------------- | +| `beforeSidebar` | Above the left sidebar | +| `afterSidebar` | Below the left sidebar | +| `beforeOutline` | Above the right outline (TOC) panel | +| `afterOutline` | Below the right outline panel | + +### Home Page + +| Slot | Position | +| ---------------- | ------------------------ | +| `beforeHero` | Before the Hero section | +| `afterHero` | After the Hero section | +| `beforeFeatures` | Before the Features grid | +| `afterFeatures` | After the Features grid | + +### Doc Page + +| Slot | Position | +| ------------------ | ------------------------------------- | +| `beforeDoc` | At the very beginning of the doc page | +| `afterDoc` | At the very end of the doc page | +| `beforeDocContent` | Before the document content area | +| `afterDocContent` | After the document content area | +| `beforeDocFooter` | Before the doc footer (prev/next nav) | +| `afterDocFooter` | After the doc footer | + +### Global + +| Slot | Position | +| ------------ | ---------------------------------------------------------------------- | +| `top` | At the very top of the entire page | +| `bottom` | At the very bottom of the entire page | +| `components` | Custom MDX component overrides (`Record`) | + +--- + +## Usage Pattern + +All examples below follow the same structure in `theme/index.tsx`. The key parts: + +- Import `Layout` from `@rspress/core/theme-original` (not `@rspress/core/theme` — that causes circular imports) +- Re-export everything: `export * from '@rspress/core/theme-original'` +- Export your custom `Layout` that wraps the original with slot props + +### Basic — Single Slot + +```tsx +// theme/index.tsx +import { Layout as OriginalLayout } from '@rspress/core/theme-original'; +export * from '@rspress/core/theme-original'; + +export function Layout() { + return } />; +} +``` + +### Multiple Slots + +```tsx +// theme/index.tsx +import { Layout as OriginalLayout } from '@rspress/core/theme-original'; +export * from '@rspress/core/theme-original'; + +export function Layout() { + return ( + New version released!} + bottom={
© 2025 My Company
} + afterOutline={
Related resources
} + /> + ); +} +``` + +### With i18n Hooks + +```tsx +// theme/index.tsx +import { Layout as OriginalLayout } from '@rspress/core/theme-original'; +import { useLang } from '@rspress/core/runtime'; +export * from '@rspress/core/theme-original'; + +function LocalizedBanner() { + const lang = useLang(); + return
{lang === 'zh' ? '欢迎' : 'Welcome'}
; +} + +export function Layout() { + return } />; +} +``` + +### Override MDX Components + +The `components` slot accepts a `Record` to override how MDX elements render: + +```tsx +// theme/index.tsx +import { Layout as OriginalLayout } from '@rspress/core/theme-original'; +export * from '@rspress/core/theme-original'; + +function CustomH1({ children }: { children: React.ReactNode }) { + return ( +

{children}

+ ); +} + +export function Layout() { + return ; +} +``` + +--- + +## Available Hooks + +Use these hooks inside slot components. Import from `@rspress/core/runtime`. + +| Hook | Purpose | +| ------------------ | ----------------------------------- | +| `useDark()` | Returns whether dark mode is active | +| `useLang()` | Returns current language code | +| `useVersion()` | Returns current doc version | +| `usePage()` | Returns current page metadata | +| `usePages()` | Returns all pages metadata | +| `useSite()` | Returns site-level configuration | +| `useFrontmatter()` | Returns current page frontmatter | +| `useI18n()` | Returns i18n translation function | diff --git a/.agents/skills/rspress-description-generator/SKILL.md b/.agents/skills/rspress-description-generator/SKILL.md new file mode 100644 index 00000000..5c5f760c --- /dev/null +++ b/.agents/skills/rspress-description-generator/SKILL.md @@ -0,0 +1,118 @@ +--- +name: rspress-description-generator +description: Generate and maintain description frontmatter for Rspress documentation files (.md/.mdx). Use when a user wants to add SEO descriptions, improve search engine snippets, generate llms.txt metadata, prepare docs for AI summarization, or batch-update frontmatter across an Rspress doc site. Also use when adding new documentation pages to an Rspress project — every new doc file needs a description. +--- + +# Rspress Description Generator + +The `description` field in Rspress frontmatter generates `` tags, which are used for search engine snippets, social media previews, and AI-oriented formats like llms.txt. + +## Step 1 — Locate the docs root + +1. Find the Rspress config file. Search for `rspress.config.ts`, `.js`, `.mjs`, or `.cjs`. It may be at the project root or inside a subdirectory like `website/`. +2. Read the config and extract the `root` option. + - The value might be a plain string (`root: 'docs'`) or a JS expression (`root: path.join(__dirname, 'docs')`). In either case, determine the resolved directory path. + - If `root` is set, resolve it relative to the config file's directory. + - If `root` is not set, default to `docs` relative to the config file's directory. +3. Confirm the directory exists. If neither `docs` nor the configured root exists, check for `doc` as a fallback. + +## Step 2 — Detect i18n structure + +Rspress i18n projects place language subdirectories (e.g., `en/`, `zh/`) directly under the docs root: + +``` +docs/ +├── en/ +│ ├── guide/ +│ └── index.md +└── zh/ + ├── guide/ + └── index.md +``` + +Check if the docs root contains language subdirectories (two-letter codes like `en`, `zh`, `ja`, `ko`, etc.). If so, process each language directory separately — the description language should match the content language. + +If there are no language subdirectories, treat the entire docs root as a single-language site. + +## Step 3 — Scan and process files + +Glob for `**/*.md` and `**/*.mdx` under the docs root. Exclude: + +- `node_modules`, build output (`doc_build`, `.rspress`, `dist`) +- `_meta.json` / `_nav.json` (sidebar/nav config files, not doc pages) +- `**/shared/**` directories (reusable snippets included via `@import`, not standalone pages) + +For each file: + +1. **Read the file.** +2. **Check for existing `description` in frontmatter.** If it exists and is non-empty, skip. +3. **Check `pageType` in frontmatter.** For `home` pages, derive the description from the `hero.text` / `hero.tagline` fields or the features list, not from body content. +4. **Generate a description** following the writing guidelines below. +5. **Insert `description` into frontmatter:** + - If the file has frontmatter with a `title` field, insert `description` on the line after `title`. + - If the file has frontmatter without `title`, insert `description` as the first field. + - If the file has no frontmatter block, add one: + + ```yaml + --- + description: Your generated description here + --- + ``` + +### YAML formatting + +Most descriptions can be bare YAML strings: + +```yaml +description: Step-by-step guide to setting up your first Rspress site +``` + +If the description contains colons, quotes, or other special YAML characters, wrap in double quotes: + +```yaml +description: 'API reference for Rspress configuration: plugins, themes, and build options' +``` + +## Step 4 — Batch processing + +For sites with many files, use parallel agent calls to process independent files simultaneously. Group by directory (e.g., all files in `guide/`, then all in `api/`) to maintain focus and consistency within each section. + +After processing all files, do a quick scan to ensure no files were missed — re-glob and check for any remaining files without `description`. + +## Description Writing Guidelines + +The description serves three audiences: search engines (Google snippet), AI systems (llms.txt, summarization), and humans (scanning search results). A good description helps all three. + +### Rules + +- **Length**: 50–160 characters. Under 50 is too vague for search engines; over 160 gets truncated in snippets. +- **Language**: Match the document content. Chinese docs get Chinese descriptions, English docs get English descriptions. +- **Be direct**: State what the page covers. Avoid starting with "This document", "This page", "Learn about" — jump straight to the substance. +- **Be specific**: Mention concrete technologies, APIs, or concepts the page covers. "Configure Rspress plugins for search, analytics, and internationalization" beats "How to use plugins." +- **No markdown**: Plain text only, no formatting syntax. + +### Examples + +**Good:** + +| Content | Description | +| -------------------------- | ---------------------------------------------------------------------------- | +| Plugin development guide | Create custom Rspress plugins using the Node.js plugin API and runtime hooks | +| MDX component usage | Import and use React components in MDX documentation files | +| Rspress 快速开始 | 从安装到本地预览,搭建 Rspress 文档站点的完整流程 | +| 主题配置 | 自定义 Rspress 主题的导航栏、侧边栏、页脚和暗色模式 | +| Home page (pageType: home) | Rspress documentation framework — fast, MDX-powered static site generator | + +**Bad:** + +| Description | Why | +| ------------------------------------------------------- | ------------------------------------------------ | +| "About plugins" | Too vague — which plugins? what about them? | +| "This page explains how to configure the Rspress theme" | Wastes characters on "This page explains how to" | +| "Learn everything about Rspress!" | Marketing fluff, says nothing specific | + +## Documentation + +- Frontmatter fields: +- Basic config (`root` option): +- Full Rspress docs: diff --git a/.claude/skills/rspress-best-practices b/.claude/skills/rspress-best-practices new file mode 120000 index 00000000..0785fd57 --- /dev/null +++ b/.claude/skills/rspress-best-practices @@ -0,0 +1 @@ +../../.agents/skills/rspress-best-practices \ No newline at end of file diff --git a/.claude/skills/rspress-custom-theme b/.claude/skills/rspress-custom-theme new file mode 120000 index 00000000..5e838133 --- /dev/null +++ b/.claude/skills/rspress-custom-theme @@ -0,0 +1 @@ +../../.agents/skills/rspress-custom-theme \ No newline at end of file diff --git a/.claude/skills/rspress-description-generator b/.claude/skills/rspress-description-generator new file mode 120000 index 00000000..142a0156 --- /dev/null +++ b/.claude/skills/rspress-description-generator @@ -0,0 +1 @@ +../../.agents/skills/rspress-description-generator \ No newline at end of file diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 45b5839a..00000000 --- a/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*.md] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = false -insert_final_newline = true \ No newline at end of file diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index d7ba4b0a..c2775e57 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -20,15 +20,15 @@ jobs: - uses: pnpm/action-setup@v3 with: - version: 10 + version: 11 - uses: actions/setup-node@v4 with: - node-version: "20" + node-version: "24" cache: "pnpm" - name: Install dependencies run: pnpm install - name: Lint - run: pnpm lint \ No newline at end of file + run: pnpm check:ci \ No newline at end of file diff --git a/.gitignore b/.gitignore index f167dfd3..35f8d286 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,15 @@ -# Dependencies -/node_modules - -# Production -/build - -# Generated files -.docusaurus -.cache-loader - -# Misc +# Local .DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -# Editor - -.idea - -npm-debug.log* -yarn-debug.log* -yarn-error.log* +*.local +*.log* + +# Dist +node_modules +dist/ +doc_build/ +build/ + +# IDE +.vscode/* +!.vscode/extensions.json +.idea \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 2c3af062..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -pnpm lint diff --git a/.markdownlint.json b/.markdownlint.json deleted file mode 100644 index 9860909a..00000000 --- a/.markdownlint.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "MD033": false, - "MD024": false, - "MD029": false, - "MD041": false, - "MD010": { - "code_blocks": true, - "spaces_per_tab": 4 - }, - "MD046": false, - "line-length": false, - "fix": true -} \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index bf2e7648..00000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -shamefully-hoist=true diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index f75b997f..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["davidanson.vscode-markdownlint"] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index ad67b6c8..2a2d6066 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,13 @@ { - "[markdown]": { - "editor.defaultFormatter": "yzhang.markdown-all-in-one" - }, - "i18n-ally.localesPaths": [ - "i18n" - ] -} + "json.schemas": [ + { + "fileMatch": ["**/_meta.json"], + "url": "./node_modules/@rspress/core/meta-json-schema.json" + }, + { + "fileMatch": ["**/_nav.json"], + "url": "./node_modules/@rspress/core/nav-json-schema.json" + } + ], + "editor.defaultFormatter": "biomejs.biome" +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..2d1aab0a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,30 @@ +# Repository Guidelines + +## Project Structure & Module Organization + +This repository contains the Halo documentation site built with Rspress. Author documentation in `docs/`: user guides live under `docs/guide/`, developer material under `docs/developer-guide/`, and stable static assets under `docs/public/`. Use `_nav.json` and `_meta.json` files to control navigation labels and ordering. Site configuration belongs in `rspress.config.ts`; shared styling belongs in `styles/index.css`. The `build/` directory is generated output and must not be edited. + +## Build, Test, and Development Commands + +Use pnpm 11.24.0, as declared in `package.json`. + +- `pnpm install` installs dependencies. +- `pnpm dev` starts the local Rspress development server. +- `pnpm build` creates the production site in `build/` and checks static rendering and dead links. +- `pnpm preview` serves the latest production build locally. +- `pnpm check` runs Biome checks and applies safe fixes. +- `pnpm format` formats supported files with Biome. + +Do not hand-edit `pnpm-lock.yaml` or other generated artifacts. + +## Coding Style & Naming Conventions + +Biome is the source of truth for TypeScript, JavaScript, JSON, and CSS formatting. Use spaces for indentation and single quotes in JavaScript/TypeScript. Keep configuration in TypeScript and prefer existing Rspress options over custom components. Name documentation files in lowercase kebab-case, such as `migrate-from-1.x.md`. Use MDX only when a page needs components; otherwise prefer Markdown. Keep headings task-oriented and code samples minimal and runnable. + +## Testing Guidelines + +There is no dedicated automated test suite or coverage threshold. Treat `pnpm build` as the required validation for documentation changes. Before submitting, open affected pages with `pnpm dev` or `pnpm preview` and verify navigation, links, code blocks, and images. For visual changes, check both desktop and narrow viewport layouts. + +## Commit & Pull Request Guidelines + +The current history contains only generic `init` commits, so it does not establish a useful convention. Write short, imperative subjects that describe the change, for example `Document offline installation`. Keep each pull request focused on one topic, explain the user-facing impact, link the relevant issue when one exists, and include screenshots for layout or styling changes. Run `pnpm check` and `pnpm build` before requesting review; avoid force-pushing after review begins. diff --git a/OWNERS b/OWNERS deleted file mode 100644 index ef70b5ef..00000000 --- a/OWNERS +++ /dev/null @@ -1,10 +0,0 @@ -reviewers: -- ruibaby -- guqing -- JohnNiang -- wan92hen - -approvers: -- ruibaby -- guqing -- JohnNiang diff --git a/README.md b/README.md index ccbcf87c..96418faf 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # The open-source repo for [docs.halo.run](https://docs.halo.run) -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. +This website is built using [Rspress](https://rspress.rs/), a modern static website generator. ### Installation ``` -$ pnpm install +pnpm install ``` > If you don’t have pnpm installed, you can install it with the following command: @@ -17,7 +17,7 @@ npm install -g pnpm ### Local Development ``` -$ pnpm start +pnpm start ``` This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. @@ -25,7 +25,7 @@ This command starts a local development server and opens up a browser window. Mo ### Build ``` -$ pnpm build +pnpm build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..6799733e --- /dev/null +++ b/biome.json @@ -0,0 +1,37 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "formatter": { + "indentStyle": "space" + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "css": { + "parser": { + "cssModules": true + } + }, + "linter": { + "enabled": true, + "rules": { + "preset": "recommended", + "a11y": { + "noSvgWithoutTitle": "off" + } + } + } +} diff --git a/context7.json b/context7.json index fac45d77..0c9dd139 100644 --- a/context7.json +++ b/context7.json @@ -1,4 +1,4 @@ { "url": "https://context7.com/halo-dev/docs", "public_key": "pk_9qoEjMaOs3nLQ4cT00vyN" -} \ No newline at end of file +} diff --git a/docs/_nav.json b/docs/_nav.json new file mode 100644 index 00000000..95cbe1cd --- /dev/null +++ b/docs/_nav.json @@ -0,0 +1,39 @@ +[ + { + "text": "使用指南", + "link": "/guide/", + "activeMatch": "/guide/", + "position": "left" + }, + { + "text": "开发者指南", + "link": "/developer-guide/", + "activeMatch": "/developer-guide/", + "position": "left" + }, + { + "text": "版本", + "items": [ + { + "text": "历史版本", + "link": "https://github.com/halo-dev/docs/branches" + }, + { + "text": "已归档版本", + "link": "https://v2.archive-docs.halo.run/" + } + ] + }, + { + "text": "官网", + "link": "https://www.halo.run/" + }, + { + "text": "论坛", + "link": "https://bbs.halo.run" + }, + { + "text": "版本对比", + "link": "https://www.lxware.cn/halo?code=mcYhwMkn" + } +] diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index d0eb7313..00000000 --- a/docs/about.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: 关于文档 -description: 关于本文档站点的一些说明 ---- - -:::note -此文档使用 [Docusaurus](https://docusaurus.io/) 搭建,感谢 [Docusaurus](https://github.com/facebook/docusaurus) 社区所做的贡献。 -::: - -## 参与贡献 - -:::tip -如果你发现文档中有不正确或者需要添加的内容,非常欢迎参与到文档编辑当中。 -::: - -当前文档的仓库地址为 [halo-dev/docs](https://github.com/halo-dev/docs) ,所以你可以 fork 此仓库,修改之后提交 `Pull request` 等待我们合并即可。 diff --git a/docs/contribution/issue.md b/docs/contribution/issue.md deleted file mode 100644 index 26d52fb1..00000000 --- a/docs/contribution/issue.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: 问题反馈 -description: 问题反馈渠道及指南 ---- - -:::info -如果您在使用过程中遇到了什么问题,您可以通过下面的方式反馈,但请尽量按照要求提出反馈。 -::: - -## GitHub - -- [https://github.com/halo-dev/halo/issues](https://github.com/halo-dev/halo/issues) -- [https://github.com/orgs/lxware-dev/discussions](https://github.com/orgs/lxware-dev/discussions):Halo 付费版或者付费应用的问题可以在这里进行反馈。 - -如果你在使用过程中,遇到了一些 bug 或者需要添加某些新特性,请尽量在 GitHub 进行反馈,这非常有助于我们跟踪解决此问题,您也可以很方便的接收到处理状态。 - -建议步骤: - -1. 在 GitHub 搜索相关问题,看看是否有其他人已经提到了此问题。 -2. 如果当前还没有人遇到您类似的问题,可以创建新的 Issue 或者 Discussion。 -3. 选择正确的反馈类型。 -4. 请尽可能详细的按照模板填写内容。 -5. 提交反馈。 - -## Halo 官方社区 - -链接:[https://bbs.halo.run](https://bbs.halo.run) - -此平台主要目的用于与其他 Halo 用户进行交流。但如果您对 GitHub 不是很熟悉或者没有账号,您也可以在此平台进行反馈。 diff --git a/docs/contribution/pr.md b/docs/contribution/pr.md deleted file mode 100644 index 5f3b7e8a..00000000 --- a/docs/contribution/pr.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: 代码贡献 -description: 代码贡献指南 ---- - -欢迎关注并有想法参与 Halo 的开发,以下是关于如何参与到 Halo 项目的指南,仅供参考。 - -## 发现 Issue - -所有的代码尽可能都有依据(Issue),不是凭空产生。 - -### 寻找一个 Good First Issue - -> 这个步骤非常适合首次贡献者。 - -在 [halo-dev](https://github.com/halo-dev) 和 [halo-sigs](https://github.com/halo-sigs) 组织下,有非常多的仓库。每个仓库下都有可能包含一些“首次贡献者”友好的 Issue,主要是为了给贡献者提供一个友好的体验。该类 Issue -一般会用 `good-first-issue` 标签标记。标签 `good-first-issue` 表示该 Issue 不需要对 Halo 有深入的理解也能够参与。 - -请点击:[good-first-issue](https://github.com/issues?q=org%3Ahalo-dev+is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+no%3Aassignee+) -查看关于 Halo 的 Good First Issue。 - -### 认领 Issue - -若对任何一个 Issue 感兴趣,请尝试在 Issue 进行回复,讨论解决 Issue 的思路。确定后可直接通过 `/assign` 或者 `/assign @GitHub 用户名` 认领这个 -Issue。这样可避免两位贡献者在同一个问题上花时间。 - -## 代码贡献步骤 - -1. Fork 此仓库 - - 点击 Halo 仓库主页右上角的 `Fork` 按钮即可。 - -2. Clone 仓库到本地 - - ```bash - git clone https://github.com/{YOUR_USERNAME}/halo --recursive - # 或者 git clone git@github.com:{YOUR_USERNAME}/halo.git --recursive - ``` - -3. 添加主仓库 - - 添加主仓库方便未来同步主仓库最新的 commits 以及创建新的分支。 - - ```bash - git remote add upstream https://github.com/halo-dev/halo.git - # 或者 git remote add upstream git@github.com:halo-dev/halo.git - git fetch upstream main - ``` - -4. 创建新的开发分支 - - 我们需要从主仓库的主分支创建一个新的开发分支。 - - ```bash - git checkout upstream/main - git checkout -b {BRANCH_NAME} - ``` - -5. 提交代码 - - ```bash - git add . - git commit -s -m "Fix a bug king" - git push origin {BRANCH_NAME} - ``` - -6. 合并主分支 - - 在提交 Pull Request 之前,尽量保证当前分支和主分支的代码尽可能同步,这时需要我们手动操作。示例: - - ```bash - git fetch upstream/main - git merge upstream/main - git push origin {BRANCH_NAME} - ``` - -## Pull Request - -进入此阶段说明已经完成了代码的编写,测试和自测,并且准备好接受 Code Review。 - -### 创建 Pull Request - -回到自己的仓库页面,选择 `New pull request` 按钮,创建 `Pull request` 到原仓库的 `main` 分支。 -然后等待我们 Review 即可,如有 `Change Request`,再本地修改之后再次提交即可。 - -提交 Pull Request 的注意事项: - -- 提交 Pull Request 请充分自测。 -- 每个 Pull Request 尽量只解决一个 Issue,特殊情况除外。 -- 应尽可能多的添加单元测试,其他测试(集成测试和 E2E 测试)可看情况添加。 -- 不论需要解决的 Issue 发生在哪个版本,提交 Pull Request 的时候,请将主仓库的主分支设置为 `main`。例如:即使某个 Bug 于 Halo 2.0.x 被发现,但是提交 Pull Request 仍只针对 - `main` 分支,等待 Pull Request 合并之后,我们会通过 `/cherrypick release-2.0` 或者 `/cherry-pick release-2.1` 指令将此 Pull Request - 的修改应用到 `release-2.0` 和 `release-2.1` 分支上。 - -### 更新 commits - -Code Review 阶段可能需要 Pull Request 作者重新修改代码,请直接在当前分支 commit 并 push 即可,无需关闭并重新提交 Pull Request。示例: - -```bash -git add . -git commit -s -m "Refactor some code according code review" -git push origin bug/king -``` - -同时,若已经进入 Code Review 阶段,请不要强制推送 commits 到当前分支。否则 Reviewers 需要从头开始 Code Review。 - -### 开发规范 - -请参考 [https://docs.halo.run/developer-guide/core/code-style](https://docs.halo.run/developer-guide/core/code-style) -,请确保所有代码格式化之后再提交。 diff --git a/docs/developer-guide/_meta.json b/docs/developer-guide/_meta.json new file mode 100644 index 00000000..353dabe7 --- /dev/null +++ b/docs/developer-guide/_meta.json @@ -0,0 +1,39 @@ +[ + { + "type": "file", + "name": "index", + "label": "概览" + }, + { + "type": "dir", + "name": "core", + "label": "系统开发", + "collapsed": true + }, + { + "type": "dir", + "name": "plugin", + "label": "插件开发", + "collapsed": true + }, + { + "type": "dir", + "name": "theme", + "label": "主题开发", + "collapsed": true + }, + { + "type": "dir", + "name": "restful-api", + "label": "RESTful API", + "collapsed": true + }, + { + "type": "dir", + "name": "app-store", + "label": "应用市场", + "collapsed": true + }, + "form-schema", + "annotations-form" +] diff --git a/docs/developer-guide/annotations-form.md b/docs/developer-guide/annotations-form.md index 89899eaa..299e0aae 100644 --- a/docs/developer-guide/annotations-form.md +++ b/docs/developer-guide/annotations-form.md @@ -1,12 +1,13 @@ --- title: 元数据表单定义 +description: 介绍 Halo AnnotationSetting 资源与 FormKit Schema 的定义方式,为文章、页面、分类、标签、菜单项和用户配置可编辑的字符串元数据字段。 --- 在 Halo 2.0,所有的模型都包含了 `metadata.annotations` 字段,用于存储元数据信息。元数据信息可以用于存储一些自定义的信息,可以等同于扩展字段。此文档主要介绍如何在 Halo 中为具体的模型定义元数据编辑表单,至于如何在插件或者主题模板中使用,请看插件或者主题的文档。 定义元数据编辑表单同样使用 `FormKit Schema`,但和主题或插件的定义方式稍有不同,其中输入组件类型可参考 [表单定义](./form-schema.md)。 -:::info[提示] +:::info annotations 表单值必须为字符串 因为 `metadata.annotations` 是一个键值都为字符串类型的对象,所以表单项的值必须为字符串类型。这就意味着,FormKit 的 `number`、`group`、`repeater` 等类型的输入组件都不能使用。`checkbox` 类型的输入组件应通过 `on-value` 和 `off-value` 指定字符串值,以替代默认的布尔值。 ::: diff --git a/docs/developer-guide/app-store/_meta.json b/docs/developer-guide/app-store/_meta.json new file mode 100644 index 00000000..c76f5f2f --- /dev/null +++ b/docs/developer-guide/app-store/_meta.json @@ -0,0 +1 @@ +["publish-app", "app-review-guidelines"] diff --git a/docs/developer-guide/app-store/index.md b/docs/developer-guide/app-store/index.md new file mode 100644 index 00000000..8977b1d4 --- /dev/null +++ b/docs/developer-guide/app-store/index.md @@ -0,0 +1,5 @@ +--- +title: 应用市场 +description: 介绍 Halo 插件与主题在应用市场的发布流程、上架资料准备、首次审核规则、版本制品要求及发布后的应用维护方式。 +overview: true +--- diff --git a/docs/developer-guide/core/_meta.json b/docs/developer-guide/core/_meta.json new file mode 100644 index 00000000..165a8336 --- /dev/null +++ b/docs/developer-guide/core/_meta.json @@ -0,0 +1 @@ +["prepare", "run", "build", "framework"] diff --git a/docs/developer-guide/core/build.md b/docs/developer-guide/core/build.md index e98c0d9a..c49661bb 100644 --- a/docs/developer-guide/core/build.md +++ b/docs/developer-guide/core/build.md @@ -3,7 +3,7 @@ title: 构建 description: 构建为可执行 JAR 和 Docker 镜像的文档 --- -:::info +:::info 构建前准备 在此之前,我们推荐你先阅读[《准备工作》](./prepare),检查本地环境是否满足要求。 ::: @@ -61,7 +61,7 @@ cd path/to/halo 构建完成之后,在 Halo 项目下产生的 `application/build/libs/halo-${version}.jar` 即为构建完成的文件。 -最终部署文档可参考:[使用 JAR 文件部署](../../getting-started/install/jar-file.md)。 +最终部署文档可参考:[使用 JAR 文件部署](../../guide/install/jar-file.md)。 ## 构建 Docker 镜像 @@ -81,4 +81,4 @@ docker build -t halo-dev/halo:${tag_name} . docker images | grep halo ``` -最终部署文档可参考:[使用 Docker Compose 部署](../../getting-started/install/docker-compose.md)。 +最终部署文档可参考:[使用 Docker Compose 部署](../../guide/install/docker-compose.mdx)。 diff --git a/docs/developer-guide/core/code-style.md b/docs/developer-guide/core/code-style.md index c23fbc01..ef73e095 100644 --- a/docs/developer-guide/core/code-style.md +++ b/docs/developer-guide/core/code-style.md @@ -1,6 +1,6 @@ --- title: 代码风格 -description: 代码风格的相关配置说明 +description: 配置 Halo 项目的 Java 代码风格检查,涵盖安装 CheckStyle-IDEA 插件、导入 checkstyle.xml,并在 IntelliJ IDEA 中启用项目规则。 --- Halo 添加了 checkstyle 插件,来保证每位提交者代码的风格保持一致,减少无效代码的修改。本篇文章主要讲解如何在 IDEA 中添加 CheckStyle 插件,并引入项目所提供的 checkstyle.xml 配置。 diff --git a/docs/developer-guide/core/framework.md b/docs/developer-guide/core/framework.md index 8196c276..93a6506a 100644 --- a/docs/developer-guide/core/framework.md +++ b/docs/developer-guide/core/framework.md @@ -1,6 +1,6 @@ --- title: Halo 架构概览 -description: Halo 架构概览 +description: 概览 Halo 基于 Spring Boot、WebFlux、Reactor 和 R2DBC 的响应式架构,以及 Extension、Controller、资源生命周期、配置模型与 RBAC 等核心概念。 --- Halo 是一个基于 Spring Boot 的 Java Web 应用,Web 层不再使用 Servlet 技术,而是充分向异步和非阻塞的反应式编程靠拢,使用 Netty 作为 Web 服务器,使用 [Reactor](https://projectreactor.io/) 作为异步编程框架,使用 R2DBC 作为数据库访问框架,使用 WebFlux 作为 Web 层框架。 diff --git a/docs/developer-guide/core/index.md b/docs/developer-guide/core/index.md new file mode 100644 index 00000000..23a5a4ab --- /dev/null +++ b/docs/developer-guide/core/index.md @@ -0,0 +1,5 @@ +--- +title: 系统开发 +description: 涵盖 Halo 核心系统的开发环境准备、本地运行、可执行 JAR 与 Docker 镜像构建,以及基于 Spring Boot、WebFlux 和 Extension 的架构概念。 +overview: true +--- diff --git a/docs/developer-guide/core/prepare.md b/docs/developer-guide/core/prepare.md index 6e765f2c..e8a5f6e3 100644 --- a/docs/developer-guide/core/prepare.md +++ b/docs/developer-guide/core/prepare.md @@ -1,6 +1,6 @@ --- title: 准备工作 -description: 开发环境的准备工作 +description: 准备 Halo 核心开发环境,列出 OpenJDK、Node.js、pnpm、IntelliJ IDEA、Git 等工具要求,并说明工作目录及数据、主题、插件、附件和日志结构。 --- ## 环境要求 diff --git a/docs/developer-guide/core/run.md b/docs/developer-guide/core/run.md index 46fb143e..a04d08b8 100644 --- a/docs/developer-guide/core/run.md +++ b/docs/developer-guide/core/run.md @@ -1,9 +1,9 @@ --- title: 开发环境运行 -description: 开发环境运行的指南 +description: 在本地完整运行 Halo 开发环境,涵盖克隆仓库、安装并启动 Console 与 UC 前端、配置 IntelliJ IDEA Profile,以及使用 Gradle 启动后端。 --- -:::info +:::info 运行前准备 在此之前,我们推荐你先阅读[《准备工作》](./prepare),检查本地环境是否满足要求。 ::: @@ -15,7 +15,7 @@ description: 开发环境运行的指南 2. UI,包括 Console 控制台和 UC 个人中心(托管在 Halo 主项目) 3. 主题(Halo 主项目内已包含默认主题) -:::info[说明] +:::info UI 需要单独运行 从 Halo 2.11 开始,Halo 项目的 `ui` 目录同时包含了 Console(管理控制台)和 UC(个人中心),以下统称为 UI。 当前 Halo 主项目并不会将 UI 的构建资源托管到 Git 版本控制,所以在开发环境是需要同时运行 UI 项目的。当然,在我们的最终发布版本的时候会在 CI 中自动构建 UI 到 Halo 主项目。 @@ -56,7 +56,7 @@ VITE v8.0.0 ready in 805 ms ➜ Network: http://192.168.1.7:3000/ ``` -:::info[提示] +:::info 通过 Halo 代理访问 UI 请不要直接使用 UI 的运行端口 3000 访问,会因为跨域问题导致无法正常登录,建议按照后续的步骤以 dev 的配置文件运行 Halo,在 dev 的配置文件中,我们默认代理了 UI 页面的访问地址,所以后续访问 UI 页面使用 `http://localhost:8090/console` 和 `http://localhost:8090/uc` 访问即可,代理的相关配置: ```yaml diff --git a/docs/developer-guide/core/structure.md b/docs/developer-guide/core/structure.md index 66084b07..2a1e96e3 100644 --- a/docs/developer-guide/core/structure.md +++ b/docs/developer-guide/core/structure.md @@ -1,6 +1,6 @@ --- title: 系统结构 -description: Halo 项目的构成 +description: 介绍 Halo 服务、管理界面、评论插件和主题项目的系统构成,并说明 Spring Boot 配置目录的覆盖优先级与开发环境自定义配置方式。 --- [Halo](https://github.com/halo-dev/halo) 博客系统分为以下四个部分: @@ -31,6 +31,6 @@ description: Halo 项目的构成 在开发的时候,希望大家能够在 `~/halo-dev/application.yml` 中进行添加自定义配置。当然后面也会讲到如何用`运行参数` 和 `VM options` 进行控制配置,届时可根据具体情况进行选择。 -:::warning +:::warning 不要修改源码配置文件 开发的时候,我们不建议直接更改`项目源码`中的所包含的`配置文件`,包括 `application.yml`、`application-dev.yml`、`application-test.yml` 和 `application-user.yml`。 ::: diff --git a/docs/developer-guide/form-schema.md b/docs/developer-guide/form-schema.md index 7cd619e5..634fbbd9 100644 --- a/docs/developer-guide/form-schema.md +++ b/docs/developer-guide/form-schema.md @@ -1,5 +1,6 @@ --- title: 表单定义 +description: 介绍 Halo Setting 资源中的 FormKit Schema 表单规范,以及 select 等扩展输入组件的参数、静态与远程数据源和 YAML 配置示例。 --- 在 Halo 2.0,在 Console 端的所有表单我们都使用了 [FormKit](https://github.com/formkit/formkit) 的方案。FormKit 不仅支持使用 Vue 组件的形式来构建表单,同时支持使用 Schema 的形式来构建。因此,我们的 [Setting](https://github.com/halo-dev/halo/blob/87ccd61ae5cd35a38324c30502d4e9c0ced41c6a/src/main/java/run/halo/app/core/extension/Setting.java#L20) 资源中的表单定义,都是使用 FormKit Schema 来定义的,最常用的场景即主题和插件的设置表单定义。当然,如果要在 Halo 2.0 的插件中使用,也可以参考 FormKit 的文档使用 Vue 组件的形式使用,但不需要在插件中引入 FormKit。 @@ -11,7 +12,7 @@ FormKit 相关文档: - Form Schema: [https://formkit.com/essentials/schema](https://formkit.com/essentials/schema) - FormKit Inputs: [https://formkit.com/inputs](https://formkit.com/inputs) -:::tip +:::tip 组件支持范围 目前不支持 FormKit Pro 中的输入组件,但 Halo 额外提供了部分输入组件,将在下面文档列出。 ::: @@ -54,7 +55,7 @@ spec: value: "" ``` -:::tip +:::tip YAML 与 JSON 格式转换 需要注意的是,FormKit Schema 本身应该是 JSON 格式的,但目前我们定义一个表单所使用的是 YAML,可能在参考 FormKit 写法时需要手动转换一下。 ::: @@ -225,7 +226,7 @@ spec: fieldSelectorKey: metadata.name ``` -:::tip +:::tip 分页数据的默认选项 当远程数据具有分页时,可能会出现默认选项不在第一页的情况,此时 Select 组件将会发送另一个查询请求,以获取默认选项的数据。此接口会携带如下参数: ```ts @@ -241,7 +242,7 @@ fieldSelector: `${requestOption.fieldSelectorKey}=(value1,value2,value3)` 列表类型的输入组件,支持动态添加、删除数据项。 -:::tip +:::tip list 与 array 的区别 `list` 组件与 [array](#array) 组件功能类似,但它们的用途不同。`list` 组件适合展示基本类型的数据,而 `array` 组件更适合于展示复杂类型的数据。 ::: @@ -273,7 +274,7 @@ fieldSelector: `${requestOption.fieldSelectorKey}=(value1,value2,value3)` validation: required ``` -:::tip +:::tip list 子节点限制 `list` 组件有且只有一个子节点,并且必须为子节点传递 `index` 属性。若想提供多个字段组成对象,则建议改为使用 [array](#array) 组件。 ::: @@ -317,7 +318,7 @@ fieldSelector: `${requestOption.fieldSelectorKey}=(value1,value2,value3)` validation: required ``` -:::tip +:::tip verificationForm 不改变数据结构 尽管 `verificationForm` 本身是一个输入组件,但与其他输入组件不同的是,它仅仅用于包装待验证的数据,所以并不会破坏原始数据的格式。例如上述示例中的值在保存后为: ```json @@ -367,7 +368,7 @@ UI 效果: ### ~~repeater~~(已过时) -:::warning +:::warning 请使用 array 组件 `repeater` 组件已不再推荐使用,请使用 [array](#array) 组件代替。 ::: @@ -418,7 +419,7 @@ UI 效果: value: "" ``` -:::tip +:::tip 设置 repeater 默认值 使用 `repeater` 类型时,一定要设置默认值,如果不需要默认有任何元素,可以设置为 `[]`。 ::: @@ -443,7 +444,7 @@ UI 效果: #### 描述 -:::info +:::info Halo 2.22 的 attachment 类型变更 在 Halo 2.22 中,我们重构了原有的 attachment 表单类型,支持了预览和直接上传文件,并将旧版的表单类型更名为了 [attachmentInput](#attachmentinput)。 ::: @@ -531,7 +532,7 @@ UI 效果: value: [] ``` -:::info +:::info menuSelect 兼容 select 参数 menuSelect 基于 select,并兼容 select 的[参数](#select-params)。 ::: @@ -733,7 +734,7 @@ UI 效果: - `emptyText`: 当数组为空时显示的文本 - `itemLabels`: 列表元素上显示的内容,数据类型为 `{ type: "image" | "text" | "iconify"; label: string }[]` -:::tip +:::tip 建议设置 itemLabels 强烈建议为 `array` 设置 `itemLabels` 属性,以便于更直观的展示元素内容,设置的元素内容将按照设置顺序展示在列表元素上。 在 `itemLabels` 中定义 `label` 时,可以使用 `$value` 来指向当前项的值。 @@ -768,7 +769,7 @@ UI 效果: value: "" ``` -:::warning +:::warning itemLabels 的条件限制 由于目前无法通过单个 `itemLabels` 定义涵盖所有子项变动的情况,因此在 `itemLabels` 中使用 `$value` ,无法获取到具有 `if` 属性的组件值。 例如: @@ -877,7 +878,7 @@ UI 效果: 密钥输入组件,用于选择一个密钥资源。 -:::note +:::note 使用 Secret 存储敏感数据 在 Halo 中,我们提供了一种更加安全的数据存储模型,即 Secret,通常我们使用 Secret 来存储敏感数据,比如密码、token、密钥等。 主要注意的是,此表单类型通常与后端配合使用,需要在后端查询密钥资源。 diff --git a/docs/developer-guide/index.md b/docs/developer-guide/index.md new file mode 100644 index 00000000..b7dc0130 --- /dev/null +++ b/docs/developer-guide/index.md @@ -0,0 +1,5 @@ +--- +title: 开发者指南 +description: Halo 开发者指南入口,涵盖核心系统、插件与主题开发、RESTful API、应用市场发布,以及 FormKit 表单和模型元数据扩展。 +overview: true +--- diff --git a/docs/developer-guide/plugin/_meta.json b/docs/developer-guide/plugin/_meta.json new file mode 100644 index 00000000..8b79433c --- /dev/null +++ b/docs/developer-guide/plugin/_meta.json @@ -0,0 +1,43 @@ +[ + "introduction", + "prepare", + "ai", + "hello-world", + { + "type": "dir", + "name": "basics", + "label": "基础", + "collapsed": true + }, + { + "type": "dir", + "name": "api-reference", + "label": "API 参考", + "collapsed": true + }, + { + "type": "dir", + "name": "extension-points", + "label": "扩展点和定制化", + "collapsed": true + }, + { + "type": "dir", + "name": "interaction", + "label": "与其他插件交互", + "collapsed": true + }, + { + "type": "dir", + "name": "security", + "label": "安全和权限管理", + "collapsed": true + }, + { + "type": "dir", + "name": "examples", + "label": "案例和最佳实践", + "collapsed": true + }, + "api-changelog" +] diff --git a/docs/developer-guide/plugin/ai.md b/docs/developer-guide/plugin/ai.md new file mode 100644 index 00000000..9e23151c --- /dev/null +++ b/docs/developer-guide/plugin/ai.md @@ -0,0 +1,48 @@ +--- +title: AI 辅助 +description: 向 AI 提供 Halo 插件开发文档,或安装官方 Agent Skill,获取插件结构、后端、前端、权限、DevTools 与 OpenAPI 开发上下文 +--- + +为了帮助 AI 更全面地了解 Halo 插件的结构、开发流程与最佳实践,从而在插件开发和问题排查过程中提供更准确的帮助,可以向 AI 提供 Halo 开发文档,或安装面向插件开发的 Agent Skill。 + +## 提供文档上下文 + +如果 AI 工具支持读取网页,可以在提示词中提供以下地址: + +```text title='适合需要查阅多个文档时使用' +https://docs.halo.run/llms.txt +``` + +```text title='适合专注于插件开发时使用' +https://docs.halo.run/developer-guide/plugin/index.md +``` + +## Agent Skill + +Agent Skill 是可安装到 AI 开发工具中的领域知识包,能够让 AI 在特定场景下更准确地给出建议或执行操作。 + +[halo-dev/dev-skills](https://github.com/halo-dev/dev-skills) 仓库提供了 `halo-plugin-dev` Skill,包含以下内容: + +- 插件目录结构与 `plugin.yaml` 配置 +- Java 后端、扩展点与自定义 API 开发 +- RBAC 权限管理 +- Vue 3 前端与 Console、用户中心路由开发 +- DevTools 开发流程与 OpenAPI 客户端生成 + +### 安装 + +在 Cursor、Claude Code、Codex 等支持 Agent Skills 的 AI 开发工具中,可以通过 [Skills CLI](https://skills.sh/) 安装: + +```bash +# 全局安装,可在所有项目中使用 +npx skills add halo-dev/dev-skills@halo-plugin-dev -g + +# 或仅安装到当前项目 +npx skills add halo-dev/dev-skills@halo-plugin-dev +``` + +### 使用 + +安装完成后,通常在开发 Halo 插件时,Agent 会根据当前项目和任务自动识别并调用 `halo-plugin-dev` Skill,无需在提示词中显式指定。如果 Agent 未自动调用,可以在提示词中明确要求使用该 Skill。 + +AI 生成的代码仍需经过代码审查和功能验证后再用于生产环境。 diff --git a/docs/developer-guide/plugin/api-reference/_meta.json b/docs/developer-guide/plugin/api-reference/_meta.json new file mode 100644 index 00000000..ce18bcb4 --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/_meta.json @@ -0,0 +1,14 @@ +[ + { + "type": "dir", + "name": "server", + "label": "服务端", + "collapsed": true + }, + { + "type": "dir", + "name": "ui", + "label": "UI", + "collapsed": true + } +] diff --git a/docs/developer-guide/plugin/api-reference/index.md b/docs/developer-guide/plugin/api-reference/index.md new file mode 100644 index 00000000..4f8eb778 --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/index.md @@ -0,0 +1,5 @@ +--- +title: API 参考 +description: 汇总 Halo 插件服务端与管理端 UI API,涵盖自定义模型、控制器、配置读取、路由、请求工具及通用组件的使用方式 +overview: true +--- diff --git a/docs/developer-guide/plugin/api-reference/server/_meta.json b/docs/developer-guide/plugin/api-reference/server/_meta.json new file mode 100644 index 00000000..5e099afa --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/server/_meta.json @@ -0,0 +1,13 @@ +[ + "extension", + "reconciler", + "extension-client", + "setting-fetcher", + "reverseproxy", + "notification", + "finder-for-theme", + "template-for-theme", + "websocket", + "login-handler-enhancer", + "extension-getter" +] diff --git a/docs/developer-guide/plugin/api-reference/server/extension-client.md b/docs/developer-guide/plugin/api-reference/server/extension-client.md index bc50b131..a9f91c52 100644 --- a/docs/developer-guide/plugin/api-reference/server/extension-client.md +++ b/docs/developer-guide/plugin/api-reference/server/extension-client.md @@ -1,6 +1,6 @@ --- title: 与自定义模型交互 -description: 了解如何通过代码的方式操作数据 +description: 使用 ExtensionClient 和 ReactiveExtensionClient 增删改查自定义模型,并通过 ListOptions、Queries、Sort 和 PageRequest 构建查询、排序与分页条件 --- Halo 提供了两个类用于与自定义模型对象交互 `ExtensionClient` 和 `ReactiveExtensionClient`。 @@ -160,7 +160,7 @@ ListOptions.builder() 在 `FieldSelector` 中使用的所有字段都必须添加为索引,否则会抛出异常表示不支持该字段。关于如何使用索引请参考 [自定义模型使用索引](./extension.md#using-indexes)。 -:::note +:::note 使用 Queries 构建查询 从 2.22.0 开始,`QueryFactory` 已过时,请使用 `Queries` 创建查询条件。取反查询可以通过 `Queries.not(condition)` 或 `condition.not()` 构建。 diff --git a/docs/developer-guide/plugin/api-reference/server/extension-getter.md b/docs/developer-guide/plugin/api-reference/server/extension-getter.md index 80f0e348..d0fad1f7 100644 --- a/docs/developer-guide/plugin/api-reference/server/extension-getter.md +++ b/docs/developer-guide/plugin/api-reference/server/extension-getter.md @@ -1,6 +1,6 @@ --- title: 获取扩展 -description: 了解如何在插件中使用 `ExtensionGetter` 获取扩展 +description: 使用 ExtensionGetter 按扩展点类型获取单实例、多实例或全部扩展,并理解系统配置、Halo 默认实现与已启用插件实现之间的选择规则 --- `ExtensionGetter` 用于获取和管理 Halo 或其他插件提供的扩展。它提供了多种方法来根据扩展点获取扩展,确保插件能够灵活地集成和使用各种扩展功能。 @@ -47,7 +47,7 @@ public interface ExtensionGetter { 2. `getEnabledExtensions(Class extensionPoint)`: 根据传入的扩展点类获取所有已启用扩展。如果没有在扩展设置页面配置过则会返回所有可用的扩展。 3. `getExtensions(Class extensionPointClass)`: 获取所有与扩展点类相关的扩展,无论是否在扩展设置中启用它。 -:::tip +:::tip 根据扩展点类型选择方法 使用 `getEnabledExtension` 方法或者 `getEnabledExtensions` 方法取决于扩展点声明的 `type` 是 `SINGLETON` 还是 `MULTI_INSTANCE`。 通过使用 `ExtensionGetter`,开发者可以轻松地在插件中访问和管理各种扩展点,提升插件的功能和灵活性。 diff --git a/docs/developer-guide/plugin/api-reference/server/extension.md b/docs/developer-guide/plugin/api-reference/server/extension.md index 5e14d759..0d06883c 100644 --- a/docs/developer-guide/plugin/api-reference/server/extension.md +++ b/docs/developer-guide/plugin/api-reference/server/extension.md @@ -1,6 +1,6 @@ --- title: 自定义模型 -description: 了解什么是自定义模型及如何创建 +description: 定义并注册遵循 OpenAPI v3 的 Halo 自定义模型,配置字段校验与索引,并使用自动生成或自定义的 API 完成资源查询和业务扩展 --- ## 概述 @@ -206,7 +206,7 @@ public void start() { - keyType:索引值类型,必须实现 `Comparable`,例如 `String`、`Boolean`、`Integer`、`Long`、`Instant` 等。 - indexFunc:索引函数,用于获取索引值,接收当前自定义模型对象。单值索引返回一个 `keyType` 类型的值,可以返回 `null`;多值索引返回 `Set`。 -:::note +:::note 使用新版索引 API 从 2.22.0 开始,`IndexAttributeFactory.simpleAttribute()`、`IndexAttributeFactory.multiValueAttribute()` 和直接创建 `new IndexSpec()` 的写法已过时,请优先使用 `IndexSpecs.single()` 和 `IndexSpecs.multi()`。 @@ -223,7 +223,7 @@ Halo 默认会为每个自定义模型建立以下几个索引,因此不需要 创建了索引的字段可以在查询时使用 `fieldSelector` 参数来查询,参考 [自定义模型 API](#extension-apis)。 -:::tip +:::tip 为查询字段创建索引 - 索引是一种存储数据结构,可提供对数据集中字段的高效查找。 - 索引将自定义模型中的字段映射到数据库行,以便在查询特定字段时不需要完整的扫描。 diff --git a/docs/developer-guide/plugin/api-reference/server/index.md b/docs/developer-guide/plugin/api-reference/server/index.md new file mode 100644 index 00000000..3b7b57d0 --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/server/index.md @@ -0,0 +1,5 @@ +--- +title: 服务端 API 参考 +description: 查阅 Halo 插件服务端 API,掌握自定义模型与控制器、ExtensionClient、插件配置、通知、WebSocket 和主题数据扩展 +overview: true +--- diff --git a/docs/developer-guide/plugin/api-reference/server/login-handler-enhancer.md b/docs/developer-guide/plugin/api-reference/server/login-handler-enhancer.md index c06fe5e8..0c01b243 100644 --- a/docs/developer-guide/plugin/api-reference/server/login-handler-enhancer.md +++ b/docs/developer-guide/plugin/api-reference/server/login-handler-enhancer.md @@ -1,6 +1,6 @@ --- title: 登录增强 -description: 了解如何在登录时如何允许 Halo 做登录逻辑的增强切入。 +description: 在插件的认证流程中调用 LoginHandlerEnhancer,将登录成功与失败事件交给 Halo 统一处理记住我、设备管理和登录日志等安全逻辑 --- ## 背景 diff --git a/docs/developer-guide/plugin/api-reference/server/notification.md b/docs/developer-guide/plugin/api-reference/server/notification.md index a401de08..fe29829b 100644 --- a/docs/developer-guide/plugin/api-reference/server/notification.md +++ b/docs/developer-guide/plugin/api-reference/server/notification.md @@ -1,6 +1,6 @@ --- title: 发送和订阅通知 -description: 了解如何在插件中发送和订阅通知。 +description: 使用 ReasonType、Reason 和 NotificationTemplate 定义通知事件与模板,通过 NotificationReasonEmitter 触发事件并用 NotificationCenter 管理订阅 --- Halo 的通知功能提供了事件驱动的消息提醒机制,让用户能够及时获取系统内的关键事件。 diff --git a/docs/developer-guide/plugin/api-reference/server/reconciler.md b/docs/developer-guide/plugin/api-reference/server/reconciler.md index b708fdb9..5228c68c 100644 --- a/docs/developer-guide/plugin/api-reference/server/reconciler.md +++ b/docs/developer-guide/plugin/api-reference/server/reconciler.md @@ -1,6 +1,6 @@ --- title: 编写控制器 -description: 了解如何为自定义模型编写控制器 +description: 实现 Reconciler 和 ControllerBuilder,为自定义模型构建幂等调谐循环,配置事件匹配、启动同步、重试策略、状态回写与 Finalizers 清理逻辑 --- 控制器是 Halo 的关键组件,它们负责对每个自定义模型对象进行操作,协调所需状态和当前状态,参考: [控制器概述](../../../core/framework.md#controller)。 diff --git a/docs/developer-guide/plugin/api-reference/server/template-for-theme.md b/docs/developer-guide/plugin/api-reference/server/template-for-theme.md index d6a1af88..1ccafbbe 100644 --- a/docs/developer-guide/plugin/api-reference/server/template-for-theme.md +++ b/docs/developer-guide/plugin/api-reference/server/template-for-theme.md @@ -1,6 +1,6 @@ --- title: 在插件中提供主题模板 -description: 了解如何为主题扩充模板。 +description: 在 Halo 插件中提供主题可覆盖的 Thymeleaf 模板与路由,复用当前主题页面布局,并通过插件模板路径组织和引用公共模板片段 --- 当你在插件中创建了自己的自定义模型后,你可能需要在主题端提供一个模板来展示这些数据,这一般有两种方式: @@ -12,7 +12,7 @@ description: 了解如何为主题扩充模板。 首先,你需要在插件的 `resources` 目录下创建一个 `templates` 目录,然后在 `templates` 目录下提供你的模板,例如: -```text +```tree ├── templates │ ├── moment.html ``` diff --git a/docs/developer-guide/plugin/api-reference/ui/_meta.json b/docs/developer-guide/plugin/api-reference/ui/_meta.json new file mode 100644 index 00000000..c3bc4804 --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/ui/_meta.json @@ -0,0 +1,12 @@ +[ + "route", + "api-request", + "formkit", + "shared", + { + "type": "dir", + "name": "components", + "label": "组件", + "collapsed": true + } +] diff --git a/docs/developer-guide/plugin/api-reference/ui/api-request.md b/docs/developer-guide/plugin/api-reference/ui/api-request.md index 22ed281d..5c8384f4 100644 --- a/docs/developer-guide/plugin/api-reference/ui/api-request.md +++ b/docs/developer-guide/plugin/api-reference/ui/api-request.md @@ -67,6 +67,6 @@ axiosInstance.get("/apis/foo.halo.run/v1alpha1/bar").then(response => { 直接从 `axios` 导入的是共享的标准 Axios 模块,不包含 Halo 的认证配置。请勿修改它的全局 defaults 或 interceptors;需要独立配置时使用 `axios.create()`。`@halo-dev/api-client` 导出的 `axiosInstance` 是另一个带有 Halo 认证和统一错误处理的实例,也不应修改它的 defaults 或 interceptors。 -:::info[提醒] +:::info 同步提高 Halo 版本要求 如果插件中使用了 `@halo-dev/api-client@2.17.0` 和 `@halo-dev/ui-plugin-bundler-kit@2.17.0`,需要提升 `plugin.yaml` 中的 `spec.requires` 版本为 `>=2.17.0`。 ::: diff --git a/docs/developer-guide/plugin/api-reference/ui/components/_meta.json b/docs/developer-guide/plugin/api-reference/ui/components/_meta.json new file mode 100644 index 00000000..22c2258d --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/ui/components/_meta.json @@ -0,0 +1,14 @@ +[ + "uppy-upload", + "filter-dropdown", + "filter-clean-button", + "annotations-form", + "attachment-file-type-icon", + "attachment-selector-modal", + "has-permission", + "search-input", + "plugin-detail-modal", + "v-codemirror", + "v-tooltip", + "v-permission" +] diff --git a/docs/developer-guide/plugin/api-reference/ui/components/annotations-form.md b/docs/developer-guide/plugin/api-reference/ui/components/annotations-form.md index c2790da6..4b3f41e2 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/annotations-form.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/annotations-form.md @@ -1,6 +1,6 @@ --- title: AnnotationsForm -description: 元数据表单组件 +description: 使用 AnnotationsForm 根据自定义模型的 group 和 kind 自动渲染元数据表单,提交并校验自定义注解与规范表单数据后合并结果 --- 此组件用于提供统一的 [Annotations 表单](../../../../annotations-form.md),可以根据 `group` 和 `kind` 属性自动渲染对应的表单项。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/attachment-file-type-icon.md b/docs/developer-guide/plugin/api-reference/ui/components/attachment-file-type-icon.md index 608d99f4..07752ec9 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/attachment-file-type-icon.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/attachment-file-type-icon.md @@ -1,6 +1,6 @@ --- title: AttachmentFileTypeIcon -description: 附件文件类型图标组件 +description: 使用 AttachmentFileTypeIcon 根据附件文件名显示对应的类型图标,可通过 displayExt 控制扩展名展示并设置图标宽度和高度 --- 此组件用于根据文件名显示文件类型图标。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/attachment-selector-modal.md b/docs/developer-guide/plugin/api-reference/ui/components/attachment-selector-modal.md index 50f1ba9a..a6e18fbe 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/attachment-selector-modal.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/attachment-selector-modal.md @@ -1,11 +1,11 @@ --- title: AttachmentSelectorModal -description: 附件选择组件 +description: 在 Halo Console 中使用 AttachmentSelectorModal 打开附件选择器,限制可接受的文件类型与选择数量,并处理可见状态、关闭和选择事件 --- 此组件用于调出附件选择器,以供用户选择附件。 -:::info[注意] +:::info 仅支持 Console 此组件当前仅在 Console 中可用。 ::: diff --git a/docs/developer-guide/plugin/api-reference/ui/components/filter-clean-button.md b/docs/developer-guide/plugin/api-reference/ui/components/filter-clean-button.md index 590a4a42..6c711161 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/filter-clean-button.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/filter-clean-button.md @@ -1,6 +1,6 @@ --- title: FilterCleanButton -description: 过滤器清除按钮组件 +description: 使用 FilterCleanButton 为 Halo 插件的筛选界面提供统一的清除操作入口,并通过点击事件重置当前页面已经应用的过滤条件 --- ## 使用示例 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/filter-dropdown.md b/docs/developer-guide/plugin/api-reference/ui/components/filter-dropdown.md index b61e7be3..0a8d1e2b 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/filter-dropdown.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/filter-dropdown.md @@ -1,6 +1,6 @@ --- title: FilterDropdown -description: 过滤器下拉组件 +description: 使用 FilterDropdown 构建通用下拉筛选器,通过包含标签和值的选项列表渲染菜单,并使用 v-model 读取和更新当前筛选值 --- 此组件为通用的下拉筛选组件,可以接收一个对象数组作为选项,并使用 `v-model` 绑定选择的值。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/has-permission.md b/docs/developer-guide/plugin/api-reference/ui/components/has-permission.md index 99d4f02a..b5f5b538 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/has-permission.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/has-permission.md @@ -1,6 +1,6 @@ --- title: HasPermission -description: 权限判断组件 +description: 使用 HasPermission 组件声明界面元素所需的权限列表,仅向具备相应 UI 权限的当前用户渲染按钮、菜单或其他插槽内容 --- 此组件用于根据权限控制元素的显示与隐藏。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/index.md b/docs/developer-guide/plugin/api-reference/ui/components/index.md index 57741de2..fb453250 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/index.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/index.md @@ -1,6 +1,7 @@ --- title: 组件 -description: 在 Halo UI 中可使用的组件。 +description: 在 Halo 插件 UI 中安装并使用基础组件库,以及直接调用 Console 和 UC 全局注册的业务组件与指令来扩展管理界面功能 +overview: true --- 此文档将介绍所有在插件中可用的组件,以及它们的使用方法和区别。 @@ -34,9 +35,3 @@ import { VButton } from "@halo-dev/components"; 除了基础组件库,我们还为 Halo 的前端封装了一些业务组件和指令,这些组件已经在全局注册,你可以直接在插件中使用这些组件和指令。 以下是所有可用的业务组件和指令: - -```mdx-code-block -import DocCardList from '@theme/DocCardList'; - - -``` diff --git a/docs/developer-guide/plugin/api-reference/ui/components/plugin-detail-modal.md b/docs/developer-guide/plugin/api-reference/ui/components/plugin-detail-modal.md index afc78c23..fe870364 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/plugin-detail-modal.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/plugin-detail-modal.md @@ -1,6 +1,6 @@ --- title: PluginDetailModal -description: 插件详情弹窗组件 +description: 使用 PluginDetailModal 在当前操作流程中打开指定插件的详情与设置弹窗,通过 plugin.yaml 中的插件名称定位内容并处理关闭事件 --- 此组件可以在 UI 部分的任意组件中打开插件的详情和设置弹窗,可以用于实现在不打断正常操作流程的情况下让用户查看和修改插件的详细信息。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/search-input.md b/docs/developer-guide/plugin/api-reference/ui/components/search-input.md index 41dc624d..f3c6d9ea 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/search-input.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/search-input.md @@ -1,6 +1,6 @@ --- title: SearchInput -description: 搜索输入框组件 +description: 使用 SearchInput 为 Halo 插件页面提供关键词搜索输入框,通过 v-model 绑定查询文本,并在用户按下回车后再触发搜索以减少无效请求 --- 此组件适用于关键词搜索场景,输入数据的过程中不会触发搜索,只有在输入完成后,点击回车才会触发搜索。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/uppy-upload.md b/docs/developer-guide/plugin/api-reference/ui/components/uppy-upload.md index e68fb5de..e6f72614 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/uppy-upload.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/uppy-upload.md @@ -1,6 +1,6 @@ --- title: UppyUpload -description: 文件上传组件 +description: 使用 UppyUpload 向指定 API 端点上传文件,配置请求方法、元数据、文件限制和弹窗尺寸,并处理上传成功与错误事件 --- ## 使用方式 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/v-codemirror.md b/docs/developer-guide/plugin/api-reference/ui/components/v-codemirror.md index 00d2ab71..01705521 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/v-codemirror.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/v-codemirror.md @@ -1,6 +1,6 @@ --- title: VCodemirror -description: 代码编辑器组件 +description: 使用 VCodemirror 在 Halo 插件界面嵌入代码编辑器,通过 v-model 管理内容,并配置编辑语言、高度、扩展和变更事件 --- 此组件封装了 Codemirror 代码编辑器,适用于一些需要编辑代码的场景。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/v-permission.md b/docs/developer-guide/plugin/api-reference/ui/components/v-permission.md index 633ca4b0..6521e443 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/v-permission.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/v-permission.md @@ -1,6 +1,6 @@ --- title: v-permission -description: 权限指令 +description: 使用 v-permission 指令为单个界面元素声明所需权限列表,根据当前用户的 UI 权限自动控制按钮或其他操作元素的显示与隐藏 --- 与 [HasPermission](./has-permission.md) 组件相同,此指令也是用于根据权限控制元素的显示与隐藏。 diff --git a/docs/developer-guide/plugin/api-reference/ui/components/v-tooltip.md b/docs/developer-guide/plugin/api-reference/ui/components/v-tooltip.md index a5470b4d..b4ed6e64 100644 --- a/docs/developer-guide/plugin/api-reference/ui/components/v-tooltip.md +++ b/docs/developer-guide/plugin/api-reference/ui/components/v-tooltip.md @@ -1,6 +1,6 @@ --- title: v-tooltip -description: Tooltip 指令 +description: 使用 v-tooltip 指令为 Halo 插件界面中的任意元素添加简短提示文字,在用户悬停或聚焦图标、按钮等控件时说明其用途 --- 此指令用于在任何元素上添加一个提示框。 diff --git a/docs/developer-guide/plugin/api-reference/ui/index.md b/docs/developer-guide/plugin/api-reference/ui/index.md new file mode 100644 index 00000000..a4896bfc --- /dev/null +++ b/docs/developer-guide/plugin/api-reference/ui/index.md @@ -0,0 +1,5 @@ +--- +title: UI API 参考 +description: 查阅 Halo 插件管理端 UI API,掌握路由注册、API 请求、FormKit 扩展、共享工具库及常用控制台组件的用法 +overview: true +--- diff --git a/docs/developer-guide/plugin/api-reference/ui/route.md b/docs/developer-guide/plugin/api-reference/ui/route.md index ce75f607..33f3184f 100644 --- a/docs/developer-guide/plugin/api-reference/ui/route.md +++ b/docs/developer-guide/plugin/api-reference/ui/route.md @@ -94,7 +94,7 @@ export interface RouteRecordAppend { - `PostsRoot`(文章) - `NotificationsRoot`(消息) -:::info[提示] +:::info RouteRecordRaw 类型来源 `RouteRecordRaw` 来自 Vue Router,详见 [API 文档 | Vue Router](https://router.vuejs.org/zh/api/#Type-Aliases-RouteRecordRaw) ::: diff --git a/docs/developer-guide/plugin/appendices.md b/docs/developer-guide/plugin/appendices.md deleted file mode 100644 index 463f84a4..00000000 --- a/docs/developer-guide/plugin/appendices.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 附录 -description: 附录 ---- \ No newline at end of file diff --git a/docs/developer-guide/plugin/basics/_meta.json b/docs/developer-guide/plugin/basics/_meta.json new file mode 100644 index 00000000..281d9711 --- /dev/null +++ b/docs/developer-guide/plugin/basics/_meta.json @@ -0,0 +1,17 @@ +[ + "structure", + "manifest", + "devtools", + { + "type": "dir", + "name": "server", + "label": "服务端", + "collapsed": true + }, + { + "type": "dir", + "name": "ui", + "label": "UI", + "collapsed": true + } +] diff --git a/docs/developer-guide/plugin/basics/devtools.md b/docs/developer-guide/plugin/basics/devtools.md index 22386f56..f5798332 100644 --- a/docs/developer-guide/plugin/basics/devtools.md +++ b/docs/developer-guide/plugin/basics/devtools.md @@ -93,7 +93,7 @@ halo { - `debugPort`:调试模式下的调试端口号,默认是自动分配端口号,你可以修改此配置来固定调试端口号。 - `suspend`:是否在启动时挂起,如果开启则会在启动时挂起直到有调试器连接到 Halo 服务。 -:::warning +:::warning Halo 2.20 版本兼容性 由于 Halo 2.20.0 版本更改了初始化和登录流程,如果 `halo.version` 指定 `2.20.x` 版本需要将 `run.halo.plugin.devtools` 版本升级到 `0.2.0` 及以上。 ::: @@ -123,7 +123,7 @@ logging: run.halo.app: DEBUG ``` -更多配置项请参考 [Halo 配置列表](../../../getting-started/install/config.md#配置列表)。 +更多配置项请参考 [Halo 配置列表](../../../guide/install/config.md#配置列表)。 ### haloServer 任务 @@ -357,12 +357,12 @@ const { data } = await momentsConsoleApiClient.moment.listTags({ }); ``` -:::tip +:::tip API 客户端生成流程 它会先执行 `generateOpenApiDocs` 任务根据配置访问 `/v3/api-docs/extensionApis` 获取 OpenAPI 文档, 并将 OpenAPI 的 Schema 文件保存到 `openApi.outputDir` 目录下,然后再由 `generateApiClient` 任务根据 Schema 文件生成 API 客户端代码到 `openApi.generator.outputDir` 目录下。 ::: -:::warning +:::warning 避免误删生成目录 执行 `generateApiClient` 任务时会先删除 `openApi.generator.outputDir` 下的所有文件,因此建议将 API client 的输出目录设置为一个独立的目录,以避免误删其他文件。 执行 `generateApiClient` 前建议注释掉你所配置的 `build` 任务对应的 `dependsOn` 任务,以避免因依赖前端构建任务可能无法生成 API Client 的问题。 diff --git a/docs/developer-guide/plugin/basics/index.md b/docs/developer-guide/plugin/basics/index.md new file mode 100644 index 00000000..afd884d1 --- /dev/null +++ b/docs/developer-guide/plugin/basics/index.md @@ -0,0 +1,5 @@ +--- +title: 基础 +description: 掌握 Halo 插件项目结构、清单注册、开发工具,以及服务端生命周期、对象管理和管理端 UI 的入口与构建流程 +overview: true +--- diff --git a/docs/developer-guide/plugin/basics/manifest.md b/docs/developer-guide/plugin/basics/manifest.md index 2683243b..41945f9c 100644 --- a/docs/developer-guide/plugin/basics/manifest.md +++ b/docs/developer-guide/plugin/basics/manifest.md @@ -50,7 +50,7 @@ spec: | `spec.description` | 插件的简短描述,用于说明插件的用途。 | | `spec.license` | 插件的许可协议,包含协议名称和链接。参考:[Software License](https://en.wikipedia.org/wiki/Software_license)。 | -:::tip +:::tip settingName 需要对应资源 如果你在 plugin.yaml 中配置了 `settingName` 但确没有对应的 `Setting` 自定义模型资源文件,会导致插件无法启动,原因是 `Setting` 模型 `metadata.name` 为你配置的 `settingName` 的资源无法找到。 ::: @@ -100,7 +100,7 @@ halo: - C:\path\to\halo-plugin-hello-world ``` -:::tip +:::tip development 模式的插件加载方式 1. `development` 开发模式下,既可以运行 `fixed-plugin-path` 下的插件,也可以运行通过 `Console` 管理端安装的 JAR 格式的插件。 2. 如果使用 [DevTools 运行方式](../hello-world.md#run-with-devtools) 来开发插件,则不需要配置 `runtime-mode` 和 `fixed-plugin-path`。 diff --git a/docs/developer-guide/plugin/basics/server/_meta.json b/docs/developer-guide/plugin/basics/server/_meta.json new file mode 100644 index 00000000..327b21f4 --- /dev/null +++ b/docs/developer-guide/plugin/basics/server/_meta.json @@ -0,0 +1 @@ +["lifecycle", "object-management"] diff --git a/docs/developer-guide/plugin/basics/server/index.md b/docs/developer-guide/plugin/basics/server/index.md new file mode 100644 index 00000000..806895e8 --- /dev/null +++ b/docs/developer-guide/plugin/basics/server/index.md @@ -0,0 +1,5 @@ +--- +title: 服务端基础 +description: 理解 Halo 插件服务端的生命周期与对象管理方式,正确注册、获取和销毁 Spring Bean 等插件运行期对象 +overview: true +--- diff --git a/docs/developer-guide/plugin/basics/server/lifecycle.md b/docs/developer-guide/plugin/basics/server/lifecycle.md index 7aea5326..f0c24c2a 100644 --- a/docs/developer-guide/plugin/basics/server/lifecycle.md +++ b/docs/developer-guide/plugin/basics/server/lifecycle.md @@ -1,6 +1,6 @@ --- title: 生命周期 -description: 了解插件从启动到卸载的过程 +description: 理解 Halo 插件安装后的启动、停止和删除生命周期,在 BasePlugin 对应方法中初始化资源、清理自定义模型并处理卸载任务 --- 根据[插件项目文件结构](../../basics/structure.md)所展示的 `StarterPlugin.java` 中,具有如下方法: @@ -27,7 +27,7 @@ public void delete() { 1. 继承 `run.halo.app.plugin.BasePlugin` 类后,你可以重写这些方法来干预插件的生命周期,例如在插件启动时初始化一些资源,在插件停止时清理掉这些资源。 2. 一个插件项目只允许有一个类继承 `BasePlugin` 类且标记为 Bean,此时这个类将被作为插件的后端入口,如果有多个类继承了 `BasePlugin` 会导致插件无法启动或生命周期方法无法被调用。 -:::tip +:::tip 将 BasePlugin 注册为 Bean 如果一个类继承了 `BasePlugin` 类但没有标记为 Bean,那么它将不会被 Halo 识别到,其中的生命周期方法也不会被调用。 ::: diff --git a/docs/developer-guide/plugin/basics/server/object-management.md b/docs/developer-guide/plugin/basics/server/object-management.md index 8f4a7ea2..230535ff 100644 --- a/docs/developer-guide/plugin/basics/server/object-management.md +++ b/docs/developer-guide/plugin/basics/server/object-management.md @@ -1,6 +1,6 @@ --- title: 插件中的对象管理 -description: 了解如何在创建中创建对象和管理对象依赖 +description: 使用 Spring Bean 与依赖注入管理 Halo 插件对象,并调用自定义模型、用户、通知、内容、认证、限流和系统信息等共享服务 --- 在插件中你可以使用 [Spring Framework](https://spring.io/projects/spring-framework/) 提供的常用 Bean 注解来标注一个类,然后就能使用依赖注入功能注入其他类的对象。这省去了使用工厂创建类和维护的过程,你可以像开发一个常规的 Spring 项目一样来开发插件,目前支持以下 Spring Framework 的特性: diff --git a/docs/developer-guide/plugin/basics/structure.md b/docs/developer-guide/plugin/basics/structure.md index 3195528f..37b16c90 100644 --- a/docs/developer-guide/plugin/basics/structure.md +++ b/docs/developer-guide/plugin/basics/structure.md @@ -1,11 +1,11 @@ --- title: 插件项目结构 -description: 了解插件项目的文件结构 +description: 认识 Halo 插件项目的 Java 后端、Vue 前端、plugin.yaml 描述文件与 Gradle 构建结构,明确源码、静态资源和 UI 构建产物的存放位置 --- 当你创建一个新的插件项目时,典型的目录结构如下所示: -```text +```tree ├── ui │ ├── src │ │ ├── assets @@ -50,7 +50,7 @@ description: 了解插件项目的文件结构 - `plugin.yaml`:这是插件的描述文件,位于 `src/main/resources` 目录下。该文件是必须的,包含插件的基本信息,如插件名称、版本、作者、描述以及依赖等内容。 - `resources/ui`:插件 JAR 中的推荐 UI 资源目录。Gradle 会将 `ui/build/dist` 的完整构建产物复制到 `build/resources/main/ui` 后打包,其中可能包含 `ui-plugin.json`、入口、样式、异步分块和其他静态资源。如果插件不包含 UI 部分,此目录可以忽略。 -:::warning[注意] +:::warning 优先使用 resources/ui 从 2.11 开始,Halo 支持了 UC 个人中心,且个人中心和 Console 的插件机制共享,因此推荐使用 `resources/ui`。Halo 2.x 仍兼容旧项目使用的 `resources/console`,并优先读取 `ui`。 ::: diff --git a/docs/developer-guide/plugin/basics/ui/_meta.json b/docs/developer-guide/plugin/basics/ui/_meta.json new file mode 100644 index 00000000..3afd53f4 --- /dev/null +++ b/docs/developer-guide/plugin/basics/ui/_meta.json @@ -0,0 +1 @@ +["intro", "entry", "build"] diff --git a/docs/developer-guide/plugin/basics/ui/build.md b/docs/developer-guide/plugin/basics/ui/build.md index 2e10700c..8783a5de 100644 --- a/docs/developer-guide/plugin/basics/ui/build.md +++ b/docs/developer-guide/plugin/basics/ui/build.md @@ -1,6 +1,6 @@ --- title: 构建 -description: UI 部分的构建说明 +description: 使用 ui-plugin-bundler-kit 的 Vite 或 Rsbuild 预配置构建 Halo 插件 UI,选择 ESM 或 IIFE 输出并正确打包共享依赖、清单和异步资源 --- 在 [halo-dev/create-halo-plugin](https://github.com/halo-dev/create-halo-plugin) 工具中,我们已经配置好了 UI 的构建工具和流程,此文档主要说明一些构建细节以及其他可能的构建选项。 @@ -269,7 +269,7 @@ export default viteConfig({ 成功的 ESM 构建会额外生成保留文件 `ui-plugin.json`,并可能包含异步 JavaScript、CSS 和其他静态资源。以下目录仅作示意,实际入口和启动样式路径以 `ui-plugin.json` 为准: -```text +```tree build/dist/ ├── ui-plugin.json ├── main..js # 默认 ESM 入口 diff --git a/docs/developer-guide/plugin/basics/ui/entry.md b/docs/developer-guide/plugin/basics/ui/entry.md index 936bfd9f..b1a2e2ed 100644 --- a/docs/developer-guide/plugin/basics/ui/entry.md +++ b/docs/developer-guide/plugin/basics/ui/entry.md @@ -1,6 +1,6 @@ --- title: 入口文件 -description: UI 扩展部分的入口文件 +description: 使用 definePlugin 编写 Halo 插件唯一的 UI 源码入口,通过 PluginModule 注册 FormKit 输入、全局组件、Console 与 UC 路由和扩展点 --- 入口文件用于定义 Halo 核心需要加载的 `PluginModule`,每个插件有且只有一个源码入口。使用 `@halo-dev/ui-plugin-bundler-kit` 构建时,IIFE 和 ESM 都会生成一个主入口;ESM 还可以包含异步 JavaScript、CSS 和其他静态资源分块。构建和打包方式请参考 [构建](./build.md)。 diff --git a/docs/developer-guide/plugin/basics/ui/index.md b/docs/developer-guide/plugin/basics/ui/index.md new file mode 100644 index 00000000..cd3f6004 --- /dev/null +++ b/docs/developer-guide/plugin/basics/ui/index.md @@ -0,0 +1,5 @@ +--- +title: UI 基础 +description: 搭建 Halo 插件管理端 UI,了解前端项目结构、入口文件注册机制、依赖配置及生产构建和资源打包流程 +overview: true +--- diff --git a/docs/developer-guide/plugin/basics/ui/intro.md b/docs/developer-guide/plugin/basics/ui/intro.md index d72b6a25..2a569a4b 100644 --- a/docs/developer-guide/plugin/basics/ui/intro.md +++ b/docs/developer-guide/plugin/basics/ui/intro.md @@ -1,6 +1,6 @@ --- title: 介绍 -description: 介绍插件 UI 部分的基础知识。 +description: 认识 Halo 插件 UI 的用途与开发基础,使用 Vue 3、TypeScript、Node.js 和 pnpm 为 Console 控制台及 UC 个人中心添加页面和功能扩展 --- Halo 插件体系的 UI 部分可以让开发者在 Console 控制台和 UC 个人中心添加新的页面或者扩展已有的功能。 diff --git a/docs/developer-guide/plugin/examples/index.md b/docs/developer-guide/plugin/examples/index.md new file mode 100644 index 00000000..12b07f7d --- /dev/null +++ b/docs/developer-guide/plugin/examples/index.md @@ -0,0 +1,5 @@ +--- +title: 案例和最佳实践 +description: 通过 Todo List 完整案例串联 Halo 插件的自定义模型、控制器、服务端逻辑与管理端 UI 开发,并总结可复用的最佳实践 +overview: true +--- diff --git a/docs/developer-guide/plugin/extension-points/_meta.json b/docs/developer-guide/plugin/extension-points/_meta.json new file mode 100644 index 00000000..ce18bcb4 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/_meta.json @@ -0,0 +1,14 @@ +[ + { + "type": "dir", + "name": "server", + "label": "服务端", + "collapsed": true + }, + { + "type": "dir", + "name": "ui", + "label": "UI", + "collapsed": true + } +] diff --git a/docs/developer-guide/plugin/extension-points/index.md b/docs/developer-guide/plugin/extension-points/index.md new file mode 100644 index 00000000..64302c65 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/index.md @@ -0,0 +1,5 @@ +--- +title: 扩展点和定制化 +description: 查阅 Halo 插件服务端与管理端 UI 扩展点,定制认证、存储、搜索、通知、主题处理及控制台页面和操作入口 +overview: true +--- diff --git a/docs/developer-guide/plugin/extension-points/server/_meta.json b/docs/developer-guide/plugin/extension-points/server/_meta.json new file mode 100644 index 00000000..705a68a6 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/server/_meta.json @@ -0,0 +1,18 @@ +[ + "additional-webfilter", + "authentication-webfilter", + "attachment", + "comment-subject", + "comment-widget", + "element-tag-post-processor", + "excerpt-generator", + "halo-documents-provider", + "notifier", + "search-engine", + "template-head-processor", + "template-footer-processor", + "post-content", + "singlepage-content", + "user-creating-handler", + "username-password-authentication-manager" +] diff --git a/docs/developer-guide/plugin/extension-points/server/halo-documents-provider.md b/docs/developer-guide/plugin/extension-points/server/halo-documents-provider.md index c6eb4e67..50f5ec3c 100644 --- a/docs/developer-guide/plugin/extension-points/server/halo-documents-provider.md +++ b/docs/developer-guide/plugin/extension-points/server/halo-documents-provider.md @@ -1,6 +1,6 @@ --- title: 搜索文档提供者 -description: 为搜索引擎提供可索引文档数据的扩展点。 +description: 实现 HaloDocumentsProvider 扩展点,为 Halo 搜索引擎返回文章、页面等可索引文档及类型标识,并注册对应的多实例扩展定义 --- 搜索文档提供者扩展点用于为 Halo 搜索引擎提供可索引的文档数据,例如:文章、页面等。当重建搜索索引时,Halo 会收集所有启用的文档提供者,获取它们提供的文档数据并写入搜索引擎。 diff --git a/docs/developer-guide/plugin/extension-points/server/index.md b/docs/developer-guide/plugin/extension-points/server/index.md index 45cb79ec..78f2d5d8 100644 --- a/docs/developer-guide/plugin/extension-points/server/index.md +++ b/docs/developer-guide/plugin/extension-points/server/index.md @@ -1,6 +1,7 @@ --- -title: 扩展点 -description: Halo 服务端为插件提供的扩展点接口 +title: 服务端扩展点 +description: 理解 Halo 服务端扩展点与扩展的关系,通过实现接口、注册 Spring Bean 并声明 ExtensionDefinition 资源接入可组合的后端能力 +overview: true --- 术语: @@ -48,7 +49,7 @@ spec: description: "Support sending notifications to users via email" ``` -:::tip +:::tip 声明扩展点资源 单实例或多实例的扩展点需要声明对应的 `ExtensionPointDefinition` 自定义模型对象被称之为扩展点定义,用于描述该扩展点的信息,例如:扩展点的名称、描述、扩展点的类型等。 单实例或多实例扩展点的实现也必须声明一个对应的 `ExtensionDefinition` 自定义模型对象被称之为扩展定义,用于描述该扩展的信息,例如:扩展的名称、描述、对应扩展点的对象名称等。 @@ -57,9 +58,3 @@ spec: 关于如何在插件中声明自定义模型对象请参考:[自定义模型](../../api-reference/server/extension.md#declare-extension-object) 以下是目前已支持的扩展点列表: - -```mdx-code-block -import DocCardList from '@theme/DocCardList'; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/server/username-password-authentication-manager.md b/docs/developer-guide/plugin/extension-points/server/username-password-authentication-manager.md index 94087043..790a7387 100644 --- a/docs/developer-guide/plugin/extension-points/server/username-password-authentication-manager.md +++ b/docs/developer-guide/plugin/extension-points/server/username-password-authentication-manager.md @@ -1,6 +1,6 @@ --- title: 用户名密码认证管理器 -description: 提供扩展用户名密码的身份验证的方法 +description: 实现 UsernamePasswordAuthenticationManager 单实例扩展点,用 LDAP 等第三方身份验证服务替换 Halo 默认的用户名密码认证逻辑 --- 用户名密码认证管理器扩展点用于替换 Halo 默认的用户名密码认证管理器实现,例如:使用第三方的身份验证服务,一个例子是 LDAP。 diff --git a/docs/developer-guide/plugin/extension-points/ui/_meta.json b/docs/developer-guide/plugin/extension-points/ui/_meta.json new file mode 100644 index 00000000..d58cf590 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/_meta.json @@ -0,0 +1,24 @@ +[ + "attachment-selector-create", + "editor-create", + "plugin-self-tabs-create", + "default-editor-extension-create", + "comment-subject-ref-create", + "backup-tabs-create", + "plugin-installation-tabs-create", + "theme-list-tabs-create", + "post-list-item-operation-create", + "single-page-list-item-operation-create", + "comment-list-item-operation-create", + "reply-list-item-operation-create", + "plugin-list-item-operation-create", + "backup-list-item-operation-create", + "attachment-list-item-operation-create", + "theme-list-item-operation-create", + "plugin-list-item-field-create", + "post-list-item-field-create", + "single-page-list-item-field-create", + "user-detail-tabs-create", + "uc-user-profile-tabs-create", + "dashboard-widgets" +] diff --git a/docs/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.md deleted file mode 100644 index 441f69d6..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: 附件数据列表操作菜单 -description: 扩展附件数据列表操作菜单 - attachment:list-item:operation:create ---- - -此扩展点用于扩展附件数据列表的操作菜单项。 - -![附件数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "attachment:list-item:operation:create": ( - attachment: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: Attachment) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 示例 - -此示例将实现一个下载附件到本地的操作菜单项。 - -```ts -import { definePlugin, type OperationItem } from "@halo-dev/ui-shared"; -import { Toast, VDropdownItem } from "@halo-dev/components"; -import { markRaw, type Ref } from "vue"; -import type { Attachment } from "@halo-dev/api-client"; - -export default definePlugin({ - extensionPoints: { - "attachment:list-item:operation:create": ( - attachment: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: Attachment) => { - if (!item?.status?.permalink) { - Toast.error("该附件没有下载地址"); - return; - } - - const a = document.createElement("a"); - a.href = item.status.permalink; - a.download = item?.spec.displayName || item.metadata.name; - a.click(); - }, - label: "下载", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); - -``` - -## 实现案例 - -- [https://github.com/halo-dev/plugin-s3](https://github.com/halo-dev/plugin-s3) - -## 类型定义 - -### Attachment - -```mdx-code-block -import Attachment from "./interface/Attachment.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.mdx new file mode 100644 index 00000000..63eedf12 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.mdx @@ -0,0 +1,94 @@ +--- +title: 附件数据列表操作菜单 +description: 扩展附件数据列表操作菜单 - attachment:list-item:operation:create +--- + +此扩展点用于扩展附件数据列表的操作菜单项。 + +![附件数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/attachment-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "attachment:list-item:operation:create": ( + attachment: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: Attachment) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 示例 + +此示例将实现一个下载附件到本地的操作菜单项。 + +```ts +import { definePlugin, type OperationItem } from "@halo-dev/ui-shared"; +import { Toast, VDropdownItem } from "@halo-dev/components"; +import { markRaw, type Ref } from "vue"; +import type { Attachment } from "@halo-dev/api-client"; + +export default definePlugin({ + extensionPoints: { + "attachment:list-item:operation:create": ( + attachment: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: Attachment) => { + if (!item?.status?.permalink) { + Toast.error("该附件没有下载地址"); + return; + } + + const a = document.createElement("a"); + a.href = item.status.permalink; + a.download = item?.spec.displayName || item.metadata.name; + a.click(); + }, + label: "下载", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); + +``` + +## 实现案例 + +- [https://github.com/halo-dev/plugin-s3](https://github.com/halo-dev/plugin-s3) + +## 类型定义 + +### Attachment + +import Attachment from "./interface/Attachment.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.md deleted file mode 100644 index c545d7d4..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: 备份数据列表操作菜单 -description: 扩展备份数据列表操作菜单 - backup:list-item:operation:create ---- - -此扩展点用于扩展备份数据列表的操作菜单项。 - -![备份数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "backup:list-item:operation:create": ( - backup: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: Backup) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.mdx new file mode 100644 index 00000000..66d7dc24 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.mdx @@ -0,0 +1,39 @@ +--- +title: 备份数据列表操作菜单 +description: 扩展备份数据列表操作菜单 - backup:list-item:operation:create +--- + +此扩展点用于扩展备份数据列表的操作菜单项。 + +![备份数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/backup-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "backup:list-item:operation:create": ( + backup: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: Backup) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.md deleted file mode 100644 index edcd1b01..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: 评论数据列表操作菜单 -description: 扩展评论数据列表操作菜单 - comment:list-item:operation:create ---- - -此扩展点用于扩展评论数据列表的操作菜单项。 - -![评论数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "comment:list-item:operation:create": ( - comment: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: ListedComment) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 示例 - -此示例将实现一个操作菜单项。 - -```ts -import type { ListedComment } from "@halo-dev/api-client"; -import { VDropdownItem } from "@halo-dev/components"; -import { definePlugin } from "@halo-dev/ui-shared"; -import { markRaw } from "vue"; - -export default definePlugin({ - extensionPoints: { - "comment:list-item:operation:create": () => { - return [ - { - priority: 21, - component: markRaw(VDropdownItem), - label: "测试评论菜单", - visible: true, - permissions: [], - action: async (comment: ListedComment) => { - console.log(comment) - }, - }, - ]; - }, - }, -}); -``` - -## 类型定义 - -### ListedComment - -```mdx-code-block -import ListedComment from "./interface/ListedComment.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.mdx new file mode 100644 index 00000000..7fec7f4b --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.mdx @@ -0,0 +1,77 @@ +--- +title: 评论数据列表操作菜单 +description: 扩展评论数据列表操作菜单 - comment:list-item:operation:create +--- + +此扩展点用于扩展评论数据列表的操作菜单项。 + +![评论数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/comment-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "comment:list-item:operation:create": ( + comment: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: ListedComment) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 示例 + +此示例将实现一个操作菜单项。 + +```ts +import type { ListedComment } from "@halo-dev/api-client"; +import { VDropdownItem } from "@halo-dev/components"; +import { definePlugin } from "@halo-dev/ui-shared"; +import { markRaw } from "vue"; + +export default definePlugin({ + extensionPoints: { + "comment:list-item:operation:create": () => { + return [ + { + priority: 21, + component: markRaw(VDropdownItem), + label: "测试评论菜单", + visible: true, + permissions: [], + action: async (comment: ListedComment) => { + console.log(comment) + }, + }, + ]; + }, + }, +}); +``` + +## 类型定义 + +### ListedComment + +import ListedComment from "./interface/ListedComment.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/comment-subject-ref-create.md b/docs/developer-guide/plugin/extension-points/ui/comment-subject-ref-create.md index 6c9e51c4..a8f70099 100644 --- a/docs/developer-guide/plugin/extension-points/ui/comment-subject-ref-create.md +++ b/docs/developer-guide/plugin/extension-points/ui/comment-subject-ref-create.md @@ -5,7 +5,7 @@ description: 扩展评论来源显示 - comment:subject-ref:create Console 的评论管理列表的评论来源默认仅支持显示来自文章和页面的评论,如果其他插件中的业务模块也使用了评论,那么就可以通过该拓展点来扩展评论来源的显示。 -:::info[提示] +:::info 需要后端扩展点配合 此扩展点需要后端配合使用,请参考 [评论主体展示](../server/comment-subject.md)。 ::: diff --git a/docs/developer-guide/plugin/extension-points/ui/default-editor-extension-create.md b/docs/developer-guide/plugin/extension-points/ui/default-editor-extension-create.md index 7b3935f8..ae13f046 100644 --- a/docs/developer-guide/plugin/extension-points/ui/default-editor-extension-create.md +++ b/docs/developer-guide/plugin/extension-points/ui/default-editor-extension-create.md @@ -17,7 +17,7 @@ export default definePlugin({ }); ``` -:::info[提示] +:::info 扩展类型与 Tiptap 一致 AnyExtension 类型来自 [Tiptap](https://github.com/ueberdosis/tiptap),这意味着 Halo 默认编辑器的扩展点返回类型与 Tiptap 的扩展完全一致,Tiptap 的扩展文档可参考:[https://tiptap.dev/docs/editor/api/extensions](https://tiptap.dev/docs/editor/api/extensions)。此外,Halo 也为默认编辑器的扩展提供了一些独有的参数,用于实现工具栏、指令等扩展。 ::: diff --git a/docs/developer-guide/plugin/extension-points/ui/index.md b/docs/developer-guide/plugin/extension-points/ui/index.md deleted file mode 100644 index 8d6b594c..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: 扩展点 -description: Halo UI 为插件提供的扩展点接口 ---- - -UI 扩展点是用于扩展 Console 和 UC 的界面的接口,通过实现扩展点接口,插件可以在 Console 和 UC 中扩展功能。 - -以下是目前已支持的扩展点列表: - -```mdx-code-block -import DocCardList from '@theme/DocCardList'; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/index.mdx b/docs/developer-guide/plugin/extension-points/ui/index.mdx new file mode 100644 index 00000000..e501fc20 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/index.mdx @@ -0,0 +1,9 @@ +--- +title: UI 扩展点 +description: 使用 Halo UI 扩展点在 Console 控制台和 UC 个人中心接入插件功能,定位可用的界面扩展接口并选择对应实现方式 +overview: true +--- + +UI 扩展点是用于扩展 Console 和 UC 的界面的接口,通过实现扩展点接口,插件可以在 Console 和 UC 中扩展功能。 + +以下是目前已支持的扩展点列表: diff --git a/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.md b/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.md deleted file mode 100644 index 508d08c7..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: 插件数据列表显示字段 -description: 扩展插件数据列表显示字段 - plugin:list-item:field:create ---- - -此扩展点用于扩展插件数据列表的显示字段。 - -![插件数据列表显示字段](/img/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "plugin:list-item:field:create": (plugin: Ref): EntityFieldItem[] | Promise => { - return [ - { - priority: 0, - position: "start", - component: markRaw(FooComponent), - props: {}, - permissions: [], - hidden: false, - } - ]; - }, - }, -}); -``` - -```ts title="EntityFieldItem" -export interface EntityFieldItem { - priority: number; - position: "start" | "end"; - component: Raw; - props?: Record\; - permissions?: string[]; - hidden?: boolean; -} -``` - -## 示例 - -此示例将添加一个显示插件 requires(版本要求)的字段。 - -```ts -import { definePlugin } from "@halo-dev/ui-shared"; -import { markRaw, type Ref } from "vue"; -import type { Plugin } from "@halo-dev/api-client"; -import { VEntityField } from "@halo-dev/components"; - -export default definePlugin({ - extensionPoints: { - "plugin:list-item:field:create": (plugin: Ref) => { - return [ - { - priority: 0, - position: "end", - component: markRaw(VEntityField), - props: { - description: plugin.value.spec.requires, - }, - permissions: [], - hidden: false, - }, - ]; - }, - }, -}); -``` - -## 实现案例 - -- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) - -## 类型定义 - -### Plugin - -```mdx-code-block -import Plugin from "./interface/Plugin.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.mdx b/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.mdx new file mode 100644 index 00000000..17845bbb --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.mdx @@ -0,0 +1,82 @@ +--- +title: 插件数据列表显示字段 +description: 扩展插件数据列表显示字段 - plugin:list-item:field:create +--- + +此扩展点用于扩展插件数据列表的显示字段。 + +![插件数据列表显示字段](/img/developer-guide/plugin/extension-points/ui/plugin-list-item-field-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "plugin:list-item:field:create": (plugin: Ref): EntityFieldItem[] | Promise => { + return [ + { + priority: 0, + position: "start", + component: markRaw(FooComponent), + props: {}, + permissions: [], + hidden: false, + } + ]; + }, + }, +}); +``` + +```ts title="EntityFieldItem" +export interface EntityFieldItem { + priority: number; + position: "start" | "end"; + component: Raw; + props?: Record\; + permissions?: string[]; + hidden?: boolean; +} +``` + +## 示例 + +此示例将添加一个显示插件 requires(版本要求)的字段。 + +```ts +import { definePlugin } from "@halo-dev/ui-shared"; +import { markRaw, type Ref } from "vue"; +import type { Plugin } from "@halo-dev/api-client"; +import { VEntityField } from "@halo-dev/components"; + +export default definePlugin({ + extensionPoints: { + "plugin:list-item:field:create": (plugin: Ref) => { + return [ + { + priority: 0, + position: "end", + component: markRaw(VEntityField), + props: { + description: plugin.value.spec.requires, + }, + permissions: [], + hidden: false, + }, + ]; + }, + }, +}); +``` + +## 实现案例 + +- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) + +## 类型定义 + +### Plugin + +import Plugin from "./interface/Plugin.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.md deleted file mode 100644 index 1c7e31de..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: 插件数据列表操作菜单 -description: 扩展插件数据列表操作菜单 - plugin:list-item:operation:create ---- - -此扩展点用于扩展插件数据列表的操作菜单项。 - -![插件数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "plugin:list-item:operation:create": ( - plugin: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: Plugin) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 实现案例 - -- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) - -## 类型定义 - -### Plugin - -```mdx-code-block -import Plugin from "./interface/Plugin.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.mdx new file mode 100644 index 00000000..508181da --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.mdx @@ -0,0 +1,51 @@ +--- +title: 插件数据列表操作菜单 +description: 扩展插件数据列表操作菜单 - plugin:list-item:operation:create +--- + +此扩展点用于扩展插件数据列表的操作菜单项。 + +![插件数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/plugin-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "plugin:list-item:operation:create": ( + plugin: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: Plugin) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 实现案例 + +- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) + +## 类型定义 + +### Plugin + +import Plugin from "./interface/Plugin.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.md b/docs/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.md deleted file mode 100644 index 88b5d54f..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: 插件详情选项卡 -description: 扩展当前插件的详情选项卡 - plugin:self:tabs:create ---- - -此扩展点用于在 Console 的插件详情页面中添加自定义选项卡,可以用于自定义插件的配置页面。 - -![插件详情选项卡](/img/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "plugin:self:tabs:create": (): PluginTab[] | Promise => { - return [ - { - id: "foo", - label: "foo", - component: markRaw(FooComponent), - permissions: [], - }, - ]; - }, - }, -}); -``` - -```ts title="PluginTab" -export interface PluginTab { - id: string; // 选项卡 ID,不能与设置表单的 group 重复 - label: string; // 选项卡标题 - component: Raw; // 选项卡面板组件 - permissions?: string[]; // 选项卡权限 -} -``` - -其中,`component` 组件可以注入(inject)以下属性: - -- `plugin`:当前插件对象,类型为 Ref\<[Plugin](#plugin)\>。 - -## 示例 - -此示例实现了一个自定义选项卡,用于获取插件的数据并显示名称。 - -```ts -import { definePlugin, PluginTab } from "@halo-dev/ui-shared"; -import MyComponent from "./views/my-component.vue"; -import { markRaw } from "vue"; -export default definePlugin({ - components: {}, - routes: [], - extensionPoints: { - "plugin:self:tabs:create": () : PluginTab[] => { - return [ - { - id: "my-tab-panel", - label: "My Tab Panel", - component: markRaw(MyComponent), - permissions: [] - }, - ]; - }, - }, -}); -``` - -```vue title="./views/my-component.vue" - - - -``` - -## 实现案例 - -- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) - -## 类型定义 - -### Plugin - -```mdx-code-block -import Plugin from "./interface/Plugin.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.mdx b/docs/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.mdx new file mode 100644 index 00000000..7a4a2177 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.mdx @@ -0,0 +1,88 @@ +--- +title: 插件详情选项卡 +description: 扩展当前插件的详情选项卡 - plugin:self:tabs:create +--- + +此扩展点用于在 Console 的插件详情页面中添加自定义选项卡,可以用于自定义插件的配置页面。 + +![插件详情选项卡](/img/developer-guide/plugin/extension-points/ui/plugin-self-tabs-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "plugin:self:tabs:create": (): PluginTab[] | Promise => { + return [ + { + id: "foo", + label: "foo", + component: markRaw(FooComponent), + permissions: [], + }, + ]; + }, + }, +}); +``` + +```ts title="PluginTab" +export interface PluginTab { + id: string; // 选项卡 ID,不能与设置表单的 group 重复 + label: string; // 选项卡标题 + component: Raw; // 选项卡面板组件 + permissions?: string[]; // 选项卡权限 +} +``` + +其中,`component` 组件可以注入(inject)以下属性: + +- `plugin`:当前插件对象,类型为 Ref\<[Plugin](#plugin)\>。 + +## 示例 + +此示例实现了一个自定义选项卡,用于获取插件的数据并显示名称。 + +```ts +import { definePlugin, PluginTab } from "@halo-dev/ui-shared"; +import MyComponent from "./views/my-component.vue"; +import { markRaw } from "vue"; +export default definePlugin({ + components: {}, + routes: [], + extensionPoints: { + "plugin:self:tabs:create": () : PluginTab[] => { + return [ + { + id: "my-tab-panel", + label: "My Tab Panel", + component: markRaw(MyComponent), + permissions: [] + }, + ]; + }, + }, +}); +``` + +```vue title="./views/my-component.vue" + + + +``` + +## 实现案例 + +- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) + +## 类型定义 + +### Plugin + +import Plugin from "./interface/Plugin.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/post-list-item-field-create.md b/docs/developer-guide/plugin/extension-points/ui/post-list-item-field-create.md deleted file mode 100644 index 277a13af..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/post-list-item-field-create.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: 文章数据列表显示字段 -description: 扩展文章数据列表显示字段 - post:list-item:field:create ---- - -此扩展点用于扩展文章数据列表的显示字段。 - -![文章数据列表显示字段](/img/developer-guide/plugin/extension-points/ui/post-list-item-field-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "post:list-item:field:create": (post: Ref): EntityFieldItem[] | Promise => { - return [ - { - priority: 0, - position: "start", - component: markRaw(FooComponent), - props: {}, - permissions: [], - hidden: false, - } - ]; - }, - }, -}); -``` - -```ts title="EntityFieldItem" -export interface EntityFieldItem { - priority: number; - position: "start" | "end"; - component: Raw; - props?: Record\; - permissions?: string[]; - hidden?: boolean; -} -``` - -## 示例 - -此示例将添加一个显示文章 slug(别名)的字段。 - -```ts -import { definePlugin } from "@halo-dev/ui-shared"; -import { markRaw, type Ref } from "vue"; -import type { ListedPost } from "@halo-dev/api-client"; -import { VEntityField } from "@halo-dev/components"; - -export default definePlugin({ - extensionPoints: { - "post:list-item:field:create": (post: Ref) => { - return [ - { - priority: 0, - position: "end", - component: markRaw(VEntityField), - props: { - description: post.value.post.spec.slug, - }, - permissions: [], - hidden: false, - }, - ]; - }, - }, -}); -``` - -## 类型定义 - -### ListedPost - -```mdx-code-block -import ListedPost from "./interface/ListedPost.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/post-list-item-field-create.mdx b/docs/developer-guide/plugin/extension-points/ui/post-list-item-field-create.mdx new file mode 100644 index 00000000..d324c603 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/post-list-item-field-create.mdx @@ -0,0 +1,78 @@ +--- +title: 文章数据列表显示字段 +description: 扩展文章数据列表显示字段 - post:list-item:field:create +--- + +此扩展点用于扩展文章数据列表的显示字段。 + +![文章数据列表显示字段](/img/developer-guide/plugin/extension-points/ui/post-list-item-field-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "post:list-item:field:create": (post: Ref): EntityFieldItem[] | Promise => { + return [ + { + priority: 0, + position: "start", + component: markRaw(FooComponent), + props: {}, + permissions: [], + hidden: false, + } + ]; + }, + }, +}); +``` + +```ts title="EntityFieldItem" +export interface EntityFieldItem { + priority: number; + position: "start" | "end"; + component: Raw; + props?: Record\; + permissions?: string[]; + hidden?: boolean; +} +``` + +## 示例 + +此示例将添加一个显示文章 slug(别名)的字段。 + +```ts +import { definePlugin } from "@halo-dev/ui-shared"; +import { markRaw, type Ref } from "vue"; +import type { ListedPost } from "@halo-dev/api-client"; +import { VEntityField } from "@halo-dev/components"; + +export default definePlugin({ + extensionPoints: { + "post:list-item:field:create": (post: Ref) => { + return [ + { + priority: 0, + position: "end", + component: markRaw(VEntityField), + props: { + description: post.value.post.spec.slug, + }, + permissions: [], + hidden: false, + }, + ]; + }, + }, +}); +``` + +## 类型定义 + +### ListedPost + +import ListedPost from "./interface/ListedPost.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.md deleted file mode 100644 index d686e185..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: 文章数据列表操作菜单 -description: 扩展文章数据列表操作菜单 - post:list-item:operation:create ---- - -此扩展点用于扩展文章数据列表的操作菜单项。 - -![文章数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "post:list-item:operation:create": ( - post: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: ListedPost) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 示例 - -此示例将实现一个操作菜单项,点击后会将文章内容作为文件下载到本地。 - -```ts -import type { ListedPost } from "@halo-dev/api-client"; -import { VDropdownItem } from "@halo-dev/components"; -import { definePlugin } from "@halo-dev/ui-shared"; -import axios from "axios"; -import { markRaw } from "vue"; - -export default definePlugin({ - extensionPoints: { - "post:list-item:operation:create": () => { - return [ - { - priority: 21, - component: markRaw(VDropdownItem), - label: "下载到本地", - visible: true, - permissions: [], - action: async (post: ListedPost) => { - const { data } = await axios.get( - `/apis/api.console.halo.run/v1alpha1/posts/${post.post.metadata.name}/head-content` - ); - const blob = new Blob([data.raw], { - type: "text/plain;charset=utf-8", - }); - const url = window.URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `${post.post.spec.title}.${data.rawType}`; - link.click(); - }, - }, - ]; - }, - }, -}); -``` - -## 类型定义 - -### ListedPost - -```mdx-code-block -import ListedPost from "./interface/ListedPost.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.mdx new file mode 100644 index 00000000..c0055000 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.mdx @@ -0,0 +1,88 @@ +--- +title: 文章数据列表操作菜单 +description: 扩展文章数据列表操作菜单 - post:list-item:operation:create +--- + +此扩展点用于扩展文章数据列表的操作菜单项。 + +![文章数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/post-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "post:list-item:operation:create": ( + post: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: ListedPost) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 示例 + +此示例将实现一个操作菜单项,点击后会将文章内容作为文件下载到本地。 + +```ts +import type { ListedPost } from "@halo-dev/api-client"; +import { VDropdownItem } from "@halo-dev/components"; +import { definePlugin } from "@halo-dev/ui-shared"; +import axios from "axios"; +import { markRaw } from "vue"; + +export default definePlugin({ + extensionPoints: { + "post:list-item:operation:create": () => { + return [ + { + priority: 21, + component: markRaw(VDropdownItem), + label: "下载到本地", + visible: true, + permissions: [], + action: async (post: ListedPost) => { + const { data } = await axios.get( + `/apis/api.console.halo.run/v1alpha1/posts/${post.post.metadata.name}/head-content` + ); + const blob = new Blob([data.raw], { + type: "text/plain;charset=utf-8", + }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${post.post.spec.title}.${data.rawType}`; + link.click(); + }, + }, + ]; + }, + }, +}); +``` + +## 类型定义 + +### ListedPost + +import ListedPost from "./interface/ListedPost.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.md deleted file mode 100644 index ccbe7bf7..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: 回复数据列表操作菜单 -description: 扩展回复数据列表操作菜单 - reply:list-item:operation:create ---- - -此扩展点用于扩展回复数据列表的操作菜单项。 - -![回复数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "reply:list-item:operation:create": ( - reply: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: ListedReply) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 示例 - -此示例将实现一个操作菜单项。 - -```ts -import type { ListedReply } from "@halo-dev/api-client"; -import { VDropdownItem } from "@halo-dev/components"; -import { definePlugin } from "@halo-dev/ui-shared"; -import { markRaw } from "vue"; - -export default definePlugin({ - extensionPoints: { - "reply:list-item:operation:create": () => { - return [ - { - priority: 21, - component: markRaw(VDropdownItem), - label: "测试回复菜单", - visible: true, - permissions: [], - action: async (reply: ListedReply) => { - console.log(reply) - }, - }, - ]; - }, - }, -}); -``` - -## 类型定义 - -### ListedReply - -```mdx-code-block -import ListedReply from "./interface/ListedReply.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.mdx new file mode 100644 index 00000000..d17752f4 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.mdx @@ -0,0 +1,77 @@ +--- +title: 回复数据列表操作菜单 +description: 扩展回复数据列表操作菜单 - reply:list-item:operation:create +--- + +此扩展点用于扩展回复数据列表的操作菜单项。 + +![回复数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/reply-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "reply:list-item:operation:create": ( + reply: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: ListedReply) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 示例 + +此示例将实现一个操作菜单项。 + +```ts +import type { ListedReply } from "@halo-dev/api-client"; +import { VDropdownItem } from "@halo-dev/components"; +import { definePlugin } from "@halo-dev/ui-shared"; +import { markRaw } from "vue"; + +export default definePlugin({ + extensionPoints: { + "reply:list-item:operation:create": () => { + return [ + { + priority: 21, + component: markRaw(VDropdownItem), + label: "测试回复菜单", + visible: true, + permissions: [], + action: async (reply: ListedReply) => { + console.log(reply) + }, + }, + ]; + }, + }, +}); +``` + +## 类型定义 + +### ListedReply + +import ListedReply from "./interface/ListedReply.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.md b/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.md deleted file mode 100644 index 23c90c97..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: 页面数据列表显示字段 -description: 扩展页面数据列表显示字段 - single-page:list-item:field:create ---- - -此扩展点用于扩展页面数据列表的显示字段。 - -![页面数据列表显示字段](/img/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "single-page:list-item:field:create": (singlePage: Ref): EntityFieldItem[] | Promise => { - return [ - { - priority: 0, - position: "start", - component: markRaw(FooComponent), - props: {}, - permissions: [], - hidden: false, - } - ]; - }, - }, -}); -``` - -```ts title="EntityFieldItem" -export interface EntityFieldItem { - priority: number; - position: "start" | "end"; - component: Raw; - props?: Record\; - permissions?: string[]; - hidden?: boolean; -} -``` - -## 示例 - -此示例将添加一个显示页面 slug(别名)的字段。 - -```ts -import { definePlugin } from "@halo-dev/ui-shared"; -import { markRaw, type Ref } from "vue"; -import type { ListedSinglePage } from "@halo-dev/api-client"; -import { VEntityField } from "@halo-dev/components"; - -export default definePlugin({ - extensionPoints: { - "single-page:list-item:field:create": (singlePage: Ref) => { - return [ - { - priority: 0, - position: "end", - component: markRaw(VEntityField), - props: { - description: singlePage.value.page.spec.slug, - }, - permissions: [], - hidden: false, - }, - ]; - }, - }, -}); -``` - -## 类型定义 - -### ListedSinglePage - -```mdx-code-block -import ListedSinglePage from "./interface/ListedSinglePage.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.mdx b/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.mdx new file mode 100644 index 00000000..1058ea55 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.mdx @@ -0,0 +1,78 @@ +--- +title: 页面数据列表显示字段 +description: 扩展页面数据列表显示字段 - single-page:list-item:field:create +--- + +此扩展点用于扩展页面数据列表的显示字段。 + +![页面数据列表显示字段](/img/developer-guide/plugin/extension-points/ui/single-page-list-item-field-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "single-page:list-item:field:create": (singlePage: Ref): EntityFieldItem[] | Promise => { + return [ + { + priority: 0, + position: "start", + component: markRaw(FooComponent), + props: {}, + permissions: [], + hidden: false, + } + ]; + }, + }, +}); +``` + +```ts title="EntityFieldItem" +export interface EntityFieldItem { + priority: number; + position: "start" | "end"; + component: Raw; + props?: Record\; + permissions?: string[]; + hidden?: boolean; +} +``` + +## 示例 + +此示例将添加一个显示页面 slug(别名)的字段。 + +```ts +import { definePlugin } from "@halo-dev/ui-shared"; +import { markRaw, type Ref } from "vue"; +import type { ListedSinglePage } from "@halo-dev/api-client"; +import { VEntityField } from "@halo-dev/components"; + +export default definePlugin({ + extensionPoints: { + "single-page:list-item:field:create": (singlePage: Ref) => { + return [ + { + priority: 0, + position: "end", + component: markRaw(VEntityField), + props: { + description: singlePage.value.page.spec.slug, + }, + permissions: [], + hidden: false, + }, + ]; + }, + }, +}); +``` + +## 类型定义 + +### ListedSinglePage + +import ListedSinglePage from "./interface/ListedSinglePage.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.md deleted file mode 100644 index 890901fe..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: 页面数据列表操作菜单 -description: 扩展页面数据列表操作菜单 - single-page:list-item:operation:create ---- - -此扩展点用于扩展页面数据列表的操作菜单项。 - -![页面数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "single-page:list-item:operation:create": ( - singlePage: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: ListedSinglePage) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 示例 - -此示例将实现一个操作菜单项,点击后会将页面内容作为文件下载到本地。 - -```ts -import type { ListedSinglePage } from "@halo-dev/api-client"; -import { VDropdownItem } from "@halo-dev/components"; -import { definePlugin } from "@halo-dev/ui-shared"; -import axios from "axios"; -import { markRaw } from "vue"; - -export default definePlugin({ - extensionPoints: { - "single-page:list-item:operation:create": () => { - return [ - { - priority: 21, - component: markRaw(VDropdownItem), - label: "下载到本地", - visible: true, - permissions: [], - action: async (singlePage: ListedSinglePage) => { - const { data } = await axios.get( - `/apis/api.console.halo.run/v1alpha1/single-pages/${singlePage.page.metadata.name}/head-content` - ); - const blob = new Blob([data.raw], { - type: "text/plain;charset=utf-8", - }); - const url = window.URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `${singlePage.page.spec.title}.${data.rawType}`; - link.click(); - }, - }, - ]; - }, - }, -}); -``` - -## 类型定义 - -### ListedSinglePage - -```mdx-code-block -import ListedSinglePage from "./interface/ListedSinglePage.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.mdx new file mode 100644 index 00000000..4c1c3256 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.mdx @@ -0,0 +1,88 @@ +--- +title: 页面数据列表操作菜单 +description: 扩展页面数据列表操作菜单 - single-page:list-item:operation:create +--- + +此扩展点用于扩展页面数据列表的操作菜单项。 + +![页面数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/single-page-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "single-page:list-item:operation:create": ( + singlePage: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: ListedSinglePage) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 示例 + +此示例将实现一个操作菜单项,点击后会将页面内容作为文件下载到本地。 + +```ts +import type { ListedSinglePage } from "@halo-dev/api-client"; +import { VDropdownItem } from "@halo-dev/components"; +import { definePlugin } from "@halo-dev/ui-shared"; +import axios from "axios"; +import { markRaw } from "vue"; + +export default definePlugin({ + extensionPoints: { + "single-page:list-item:operation:create": () => { + return [ + { + priority: 21, + component: markRaw(VDropdownItem), + label: "下载到本地", + visible: true, + permissions: [], + action: async (singlePage: ListedSinglePage) => { + const { data } = await axios.get( + `/apis/api.console.halo.run/v1alpha1/single-pages/${singlePage.page.metadata.name}/head-content` + ); + const blob = new Blob([data.raw], { + type: "text/plain;charset=utf-8", + }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${singlePage.page.spec.title}.${data.rawType}`; + link.click(); + }, + }, + ]; + }, + }, +}); +``` + +## 类型定义 + +### ListedSinglePage + +import ListedSinglePage from "./interface/ListedSinglePage.md"; + + diff --git a/docs/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.md b/docs/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.md deleted file mode 100644 index 8d4f1696..00000000 --- a/docs/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: 主题数据列表操作菜单 -description: 扩展主题数据列表操作菜单 - theme:list-item:operation:create ---- - -此扩展点用于扩展主题数据列表的操作菜单项。 - -![主题数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.png) - -## 定义方式 - -```ts -export default definePlugin({ - extensionPoints: { - "theme:list-item:operation:create": ( - theme: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VDropdownItem), - props: {}, - action: (item?: Theme) => { - // do something - }, - label: "foo", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -```mdx-code-block -import OperationItem from "./interface/OperationItem.md"; - - -``` - -## 示例 - -此示例将实现一个跳转到前台预览主题的操作菜单项。 - -```ts -import { definePlugin, type OperationItem } from "@halo-dev/ui-shared"; -import { VButton } from "@halo-dev/components"; -import { markRaw, type Ref } from "vue"; -import type { Theme } from "@halo-dev/api-client"; - -export default definePlugin({ - extensionPoints: { - "theme:list-item:operation:create": ( - theme: Ref - ): OperationItem[] | Promise[]> => { - return [ - { - priority: 10, - component: markRaw(VButton), - props: { - size: "sm", - }, - action: (item?: Theme) => { - window.open(`/?preview-theme=${item?.metadata.name}`); - }, - label: "前台预览", - hidden: false, - permissions: [], - children: [], - }, - ]; - }, - }, -}); -``` - -## 实现案例 - -- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) - -## 类型定义 - -### Theme - -```mdx-code-block -import Theme from "./interface/Theme.md"; - - -``` diff --git a/docs/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.mdx b/docs/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.mdx new file mode 100644 index 00000000..0a86de83 --- /dev/null +++ b/docs/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.mdx @@ -0,0 +1,87 @@ +--- +title: 主题数据列表操作菜单 +description: 扩展主题数据列表操作菜单 - theme:list-item:operation:create +--- + +此扩展点用于扩展主题数据列表的操作菜单项。 + +![主题数据列表操作菜单](/img/developer-guide/plugin/extension-points/ui/theme-list-item-operation-create.png) + +## 定义方式 + +```ts +export default definePlugin({ + extensionPoints: { + "theme:list-item:operation:create": ( + theme: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VDropdownItem), + props: {}, + action: (item?: Theme) => { + // do something + }, + label: "foo", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +import OperationItem from "./interface/OperationItem.md"; + + + +## 示例 + +此示例将实现一个跳转到前台预览主题的操作菜单项。 + +```ts +import { definePlugin, type OperationItem } from "@halo-dev/ui-shared"; +import { VButton } from "@halo-dev/components"; +import { markRaw, type Ref } from "vue"; +import type { Theme } from "@halo-dev/api-client"; + +export default definePlugin({ + extensionPoints: { + "theme:list-item:operation:create": ( + theme: Ref + ): OperationItem[] | Promise[]> => { + return [ + { + priority: 10, + component: markRaw(VButton), + props: { + size: "sm", + }, + action: (item?: Theme) => { + window.open(`/?preview-theme=${item?.metadata.name}`); + }, + label: "前台预览", + hidden: false, + permissions: [], + children: [], + }, + ]; + }, + }, +}); +``` + +## 实现案例 + +- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store) + +## 类型定义 + +### Theme + +import Theme from "./interface/Theme.md"; + + diff --git a/docs/developer-guide/plugin/index.md b/docs/developer-guide/plugin/index.md new file mode 100644 index 00000000..e08ab273 --- /dev/null +++ b/docs/developer-guide/plugin/index.md @@ -0,0 +1,5 @@ +--- +title: 插件开发 +description: 系统了解 Halo 插件开发流程,从环境准备、项目结构和基础 API 到扩展点、插件交互、安全权限及完整案例实践 +overview: true +--- diff --git a/docs/developer-guide/plugin/interaction/_meta.json b/docs/developer-guide/plugin/interaction/_meta.json new file mode 100644 index 00000000..c493e2cb --- /dev/null +++ b/docs/developer-guide/plugin/interaction/_meta.json @@ -0,0 +1 @@ +["dependency", "shared-events", "making-plugin-extensible"] diff --git a/docs/developer-guide/plugin/interaction/dependency.md b/docs/developer-guide/plugin/interaction/dependency.md index b16b9e6d..23427e85 100644 --- a/docs/developer-guide/plugin/interaction/dependency.md +++ b/docs/developer-guide/plugin/interaction/dependency.md @@ -94,7 +94,7 @@ Halo 插件系统支持在 `plugin.yaml` 文件中通过 `pluginDependencies` 在 Gradle 项目中,推荐使用标准的项目结构,以便于插件代码的管理和依赖的声明。以下是一个典型的 Halo 插件项目结构示例: -```plaintext +```tree my-halo-plugin/ ├── build.gradle # 项目的构建配置 ├── settings.gradle # Gradle 设置文件 @@ -117,7 +117,7 @@ my-halo-plugin/ 以下是一个优化后的项目结构示例,包含 `api` 和 `plugin` 模块: -```plaintext +```tree my-halo-plugin/ ├── build.gradle # 根项目构建配置文件 ├── settings.gradle # 设置文件,声明子模块 diff --git a/docs/developer-guide/plugin/interaction/index.md b/docs/developer-guide/plugin/interaction/index.md new file mode 100644 index 00000000..433bac64 --- /dev/null +++ b/docs/developer-guide/plugin/interaction/index.md @@ -0,0 +1,5 @@ +--- +title: 与其他插件交互 +description: 实现 Halo 插件之间的协作,配置插件依赖、通过事件总线共享数据,并设计可供其他插件接入的稳定扩展点与接口 +overview: true +--- diff --git a/docs/developer-guide/plugin/interaction/shared-events.md b/docs/developer-guide/plugin/interaction/shared-events.md index e72662a8..f7a7e7ab 100644 --- a/docs/developer-guide/plugin/interaction/shared-events.md +++ b/docs/developer-guide/plugin/interaction/shared-events.md @@ -130,7 +130,7 @@ public class CustomEventPublisher { 3. 配置插件 B 的 `plugin.yaml` 中的 `pluginDependencies` 依赖插件 A,参考 [插件依赖声明](dependency.md#依赖声明方式) -:::info +:::info 将依赖插件 API 声明为 compileOnly 关于为什么必须将插件 A 的 `plugin-a-api` 声明为 `compileOnly`? 插件类加载的顺序是: diff --git a/docs/developer-guide/plugin/introduction.md b/docs/developer-guide/plugin/introduction.md index 1bcaee82..fc43a1c4 100644 --- a/docs/developer-guide/plugin/introduction.md +++ b/docs/developer-guide/plugin/introduction.md @@ -1,6 +1,6 @@ --- title: 介绍 -description: Halo 插件机制的简介 +description: 认识 Halo 的可插拔架构、按需安装与卸载机制以及插件开发接口,了解插件如何以低耦合方式扩展系统功能并保持模块可维护性 --- Halo 采用可插拔架构,功能模块之间耦合度低、灵活性提高,支持用户按需安装、卸载插件,操作便捷。同时提供插件开发接口以确保较高扩展性和可维护性,这个系列的文档将帮助你了解如何开发 Halo 插件。 diff --git a/docs/developer-guide/plugin/prepare.md b/docs/developer-guide/plugin/prepare.md index de624924..67ee9ad7 100644 --- a/docs/developer-guide/plugin/prepare.md +++ b/docs/developer-guide/plugin/prepare.md @@ -1,13 +1,13 @@ --- title: 准备工作 -description: 插件开发的准备工作 +description: 准备 Halo 插件开发所需的 Java、Spring Boot、Vue、TypeScript、Node.js、包管理器与 Git 环境 --- 在 Halo 中,插件是使用 Java 和 JavaScript / TypeScript 编写的,UI 使用 [Vuejs](https://vuejs.org) 编写。 在创建你的第一个插件之前,请确保你具备以下条件: -- 你能通过 [Docker 运行 Halo](../../getting-started/install/docker) 或在[开发环境运行 Halo](../core/run.md)。 +- 你能通过 [Docker 运行 Halo](../../guide/install/docker.mdx) 或在[开发环境运行 Halo](../core/run.md)。 - 你熟悉 Java Web 开发并掌握 [Spring Boot](https://spring.io/projects/spring-boot/) 框架。 - 你需要在计算机上安装最新的 LTS 版本的 Node.js,如果你还没有 Node.js 安装,你可以在这里下载 [Node.js 18 LTS](https://nodejs.org/)。 - 你熟悉 Vue 和 TypeScript。 @@ -15,15 +15,3 @@ description: 插件开发的准备工作 - Git 是一个版本控制系统,用于跟踪代码的更改,您需要 Git 来下载示例插件并发布插件。 同时需要先阅读 [Halo 架构概览](../core/framework.md) 以了解 Halo 的核心概念和技术栈。 - -## AI 辅助开发 - -Halo 官方为插件开发者提供了 Agent Skills,支持在 Cursor、Claude Code、Codex 等 AI 开发工具中使用,以获得 Halo 插件开发的深度上下文和辅助能力。 - -- [halo-dev/dev-skills](https://github.com/halo-dev/dev-skills) - 包含 `halo-plugin-dev` Skill,涵盖插件目录结构、Java 后端开发、Vue 3 前端开发、RBAC 权限管理、DevTools 工作流、OpenAPI 客户端生成等内容。 - -安装方式: - -```bash -npx skills add halo-dev/dev-skills@halo-plugin-dev -g -``` diff --git a/docs/developer-guide/plugin/security/index.md b/docs/developer-guide/plugin/security/index.md new file mode 100644 index 00000000..8ca5c9a7 --- /dev/null +++ b/docs/developer-guide/plugin/security/index.md @@ -0,0 +1,5 @@ +--- +title: 安全和权限管理 +description: 为 Halo 插件配置安全与权限管理,使用 RBAC、角色模板和 UI 权限控制保护服务端 API 与管理端功能入口 +overview: true +--- diff --git a/docs/developer-guide/plugin/security/role-template.md b/docs/developer-guide/plugin/security/role-template.md index 60ee69d6..3de75e42 100644 --- a/docs/developer-guide/plugin/security/role-template.md +++ b/docs/developer-guide/plugin/security/role-template.md @@ -46,7 +46,7 @@ rules: - 以 `/api` 开头,且以 `/api//[//]` 规则组成 APIs,最少路径层级为 3 即 `/api//`,最多路径层级为 5 即包含 `` 和 ``,例如 `/api/v1/posts`。 - 以 `/apis///[//]` 规则组成的 APIs,最少路径层级为 4 即 `/apis///`,最多路径层级为 6 即包含 `` 和 ``,例如 `/apis/my-plugin.halo.run/v1alpha1/persons`。 -:::info[注意] +:::info API 路径限制 `[]`包裹的部分表示可选,`/api` 前缀被 Halo 保留,不允许插件定义以 `/api` 开头的资源型 APIs,所以插件的资源型 APIs 都是以 `/apis` 开头的。 ::: diff --git a/docs/developer-guide/plugin/security/ui-permission.md b/docs/developer-guide/plugin/security/ui-permission.md index 8bb6faa8..335d0da0 100644 --- a/docs/developer-guide/plugin/security/ui-permission.md +++ b/docs/developer-guide/plugin/security/ui-permission.md @@ -1,6 +1,6 @@ --- title: UI 权限控制 -description: 了解如何控制用户界面的操作权限。 +description: 通过角色模板的 ui-permissions 注解声明 Halo 插件前端权限,并在路由、菜单和组件中使用 permissions 或 HasPermission 控制界面可见操作 --- UI(用户界面)权限控制是指在应用程序中,通过用户角色或身份的不同,控制用户界面上可见和可操作的元素。 这种权限控制的目的是根据用户的权限等级和角色,动态调整他们在应用中的操作权限,从而确保系统的安全性和功能的正确使用。 diff --git a/docs/developer-guide/restful-api/_meta.json b/docs/developer-guide/restful-api/_meta.json new file mode 100644 index 00000000..8b0c4b38 --- /dev/null +++ b/docs/developer-guide/restful-api/_meta.json @@ -0,0 +1 @@ +["introduction", "api-client"] diff --git a/docs/developer-guide/restful-api/api-client.md b/docs/developer-guide/restful-api/api-client.md index 1e1a236f..d40780fb 100644 --- a/docs/developer-guide/restful-api/api-client.md +++ b/docs/developer-guide/restful-api/api-client.md @@ -13,7 +13,7 @@ description: 介绍使用 API Client 请求库发起 API 请求的方式 pnpm install @halo-dev/api-client axios ``` -:::info[提示] +:::info 推荐使用 TypeScript 推荐在项目中引入 TypeScript,可以获得更好的类型提示。 ::: @@ -107,6 +107,6 @@ coreApiClient.content.post.listPost().then(response => { }) ``` -:::info[提示] +:::info 查看认证方式 认证方式的说明请参考:[认证方式](./introduction.md#认证方式) ::: diff --git a/docs/developer-guide/restful-api/index.md b/docs/developer-guide/restful-api/index.md new file mode 100644 index 00000000..535beafd --- /dev/null +++ b/docs/developer-guide/restful-api/index.md @@ -0,0 +1,5 @@ +--- +title: RESTful API +description: 介绍 Halo RESTful API 的认证与调用方式,以及使用 @halo-dev/api-client 访问 Core、Console、UC 和 Public API 的方法。 +overview: true +--- diff --git a/docs/developer-guide/restful-api/introduction.md b/docs/developer-guide/restful-api/introduction.md index 90da9a77..de86b5e8 100644 --- a/docs/developer-guide/restful-api/introduction.md +++ b/docs/developer-guide/restful-api/introduction.md @@ -27,7 +27,7 @@ Halo 提供了 RESTful 风格的 API,Halo 的前端(主要为 Console 和 UC 个人令牌是一种用于访问 Halo API 的安全凭证,你可以使用个人令牌代替您的 Halo 账户密码进行身份验证。 -在个人中心的**个人令牌**页面中,可以根据当前用户已有的权限创建个人令牌,创建方式可参考:[个人中心 / 个人令牌](../../user-guide/user-center.md#个人令牌) +在个人中心的**个人令牌**页面中,可以根据当前用户已有的权限创建个人令牌,创建方式可参考:[个人中心 / 个人令牌](../../guide/use/user-center.md#个人令牌) 创建成功后,将会得到一个 `pat_` 开头的字符串,接下来在所需请求的请求头中添加 `Authorization` 字段,值为 `Bearer ` 即可。 @@ -59,10 +59,10 @@ axios.get('https://demo.halo.run/apis/content.halo.run/v1alpha1/posts', { ### Basic Auth -:::warning +:::warning Basic Auth 默认关闭 Basic Auth 认证方式已经在 Halo 2.20 默认关闭,需要手动添加 `halo.security.basic-auth.disabled=false` 启动参数来开启。 -配置详情可见:[配置列表](../../getting-started/install/config.md#halo-独有配置) +配置详情可见:[配置列表](../../guide/install/config.md#halo-独有配置) ::: Basic Auth 是一种通过用户名和密码进行身份验证的方式,你可以使用 Halo 账户的用户名和密码进行身份验证。 diff --git a/docs/developer-guide/theme/_meta.json b/docs/developer-guide/theme/_meta.json new file mode 100644 index 00000000..02e1b7ea --- /dev/null +++ b/docs/developer-guide/theme/_meta.json @@ -0,0 +1,28 @@ +[ + "prepare", + "ai", + "config", + "structure", + "ui-plugin", + "page-layout", + "static-resources", + "settings", + "annotations", + { + "type": "dir", + "name": "template-variables", + "label": "模板编写", + "collapsed": true + }, + { + "type": "dir", + "name": "finder-apis", + "label": "Finder API", + "collapsed": true + }, + "image-optimization", + "global-variables", + "template-tag", + "code-snippets", + "api-changelog" +] diff --git a/docs/developer-guide/theme/ai.md b/docs/developer-guide/theme/ai.md new file mode 100644 index 00000000..bb6f71a6 --- /dev/null +++ b/docs/developer-guide/theme/ai.md @@ -0,0 +1,49 @@ +--- +title: AI 辅助 +description: 向 AI 提供 Halo 主题开发文档,或安装官方 Agent Skill,获取主题结构、Thymeleaf、Finder API、静态资源与设置表单开发上下文 +--- + +为了帮助 AI 更全面地了解 Halo 主题的结构、开发流程与最佳实践,从而在主题开发和问题排查过程中提供更准确的帮助,可以向 AI 提供 Halo 开发文档,或安装面向主题开发的 Agent Skill。 + +## 提供文档上下文 + +如果 AI 工具支持读取网页,可以在提示词中提供以下地址: + +```text title='适合需要查阅多个文档时使用' +https://docs.halo.run/llms.txt +``` + +```text title='适合专注于主题开发时使用' +https://docs.halo.run/developer-guide/theme/index.md +``` + +## Agent Skill + +Agent Skill 是可安装到 AI 开发工具中的领域知识包,能够让 AI 在特定场景下更准确地给出建议或执行操作。 + +[halo-dev/dev-skills](https://github.com/halo-dev/dev-skills) 仓库提供了 `halo-theme-dev` Skill,包含以下内容: + +- 主题目录结构与 `theme.yaml`、`settings.yaml` 配置 +- Thymeleaf 页面模板、布局片段与模板路由 +- 模板变量与 Finder API +- 静态资源管理与 Vite 集成 +- 主题设置表单与模型元数据 +- 最小主题和 Vite 主题初始模板 + +### 安装 + +在 Cursor、Claude Code、Codex 等支持 Agent Skills 的 AI 开发工具中,可以通过 [Skills CLI](https://skills.sh/) 安装: + +```bash +# 全局安装,可在所有项目中使用 +npx skills add halo-dev/dev-skills@halo-theme-dev -g + +# 或仅安装到当前项目 +npx skills add halo-dev/dev-skills@halo-theme-dev +``` + +### 使用 + +安装完成后,通常在开发 Halo 主题时,Agent 会根据当前项目和任务自动识别并调用 `halo-theme-dev` Skill,无需在提示词中显式指定。如果 Agent 未自动调用,可以在提示词中明确要求使用该 Skill。 + +AI 生成的代码仍需经过代码审查和功能验证后再用于生产环境。 diff --git a/docs/developer-guide/theme/annotations.md b/docs/developer-guide/theme/annotations.md index 53a7cebf..aeb12427 100644 --- a/docs/developer-guide/theme/annotations.md +++ b/docs/developer-guide/theme/annotations.md @@ -1,5 +1,6 @@ --- title: 模型元数据 +description: 介绍 Halo 主题模板的 annotations 表达式对象,演示获取模型元数据、设置默认值及判断 annotations 字段是否存在。 --- 在 [元数据表单定义](../annotations-form.md) 我们介绍了如何为模型添加元数据表单,此文档将介绍如何在主题模板中使用元数据。 diff --git a/docs/developer-guide/theme/config.md b/docs/developer-guide/theme/config.md index db14bcda..5cbbe012 100644 --- a/docs/developer-guide/theme/config.md +++ b/docs/developer-guide/theme/config.md @@ -1,6 +1,6 @@ --- title: 配置文件 -description: 关于主题配置文件的文档。 +description: 配置 Halo 主题的 theme.yaml,说明主题标识、作者、版本、兼容要求、设置项、自定义模板与许可证等字段,并介绍重载和旧版迁移方法 --- 目前 Halo 2.0 的主题必须在根目录包含 `theme.yaml`,用于配置主题的基本信息,如主题名称、版本、作者等。 @@ -90,6 +90,6 @@ npx @halo-dev/convert-theme-config-to-next theme 执行完成之后即可看到主题目录下生成了 `theme.2.0.yaml` 文件,重命名为 `theme.yaml` 即可。 -:::tip +:::tip 修改转换后的资源名称 转换完成之后需要修改 `metadata.name`、`spec.settingName` 和 `spec.configMapName`。 ::: diff --git a/docs/developer-guide/theme/finder-apis.md b/docs/developer-guide/theme/finder-apis.md deleted file mode 100644 index fc4522f3..00000000 --- a/docs/developer-guide/theme/finder-apis.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Finder API -description: 本文档介绍 Finder API 的使用方法。 ---- - -import DocCardList from '@theme/DocCardList'; - -目前在主题模板中获取数据可以使用对应路由提供的 [模板变量](./template-variables),但为了满足在任意位置获取数据的需求,我们提供了 Finder API。 - - diff --git a/docs/developer-guide/theme/finder-apis/_meta.json b/docs/developer-guide/theme/finder-apis/_meta.json new file mode 100644 index 00000000..6f44f5be --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/_meta.json @@ -0,0 +1,12 @@ +[ + "category", + "tag", + "post", + "single-page", + "comment", + "contributor", + "menu", + "site-stats", + "theme", + "plugin" +] diff --git a/docs/developer-guide/theme/finder-apis/category.md b/docs/developer-guide/theme/finder-apis/category.md deleted file mode 100644 index 4ecef87a..00000000 --- a/docs/developer-guide/theme/finder-apis/category.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: 文章分类 -description: 文章分类 - CategoryFinder ---- - -import CategoryVo from "../vo/_CategoryVo.md" -import CategoryTreeVo from "../vo/_CategoryTreeVo.md" - -## getByName(name) - -```js -categoryFinder.getByName(name) -``` - -:::info[提示] -通常建议配合 [主题设置](../settings.md) 和 [分类选择器](../../form-schema.md#categoryselect) 使用,让用户自行选择所需的分类。 -::: - -### 描述 - -根据 `metadata.name` 获取文章分类。 - -### 参数 - -1. `name:string` - 分类的唯一标识 `metadata.name`。 - -### 返回值 - -[#CategoryVo](#categoryvo) - -### 示例 - -```html -
- -
-``` - -## getByNames(names) - -```js -categoryFinder.getByNames(names) -``` - -:::info[提示] -通常建议配合 [主题设置](../settings.md) 和 [分类选择器](../../form-schema.md#categoryselect) 使用,让用户自行选择所需的分类。 -::: - -### 描述 - -根据一组 `metadata.name` 获取文章分类。 - -### 参数 - -1. `names:List` - 分类的唯一标识 `metadata.name` 的集合。 - -### 返回值 - -List\<[#CategoryVo](#categoryvo)\> - -### 示例 - -```html -
- -
-``` - -## list(page,size) - -```js -categoryFinder.list(page,size) -``` - -### 描述 - -根据分页参数获取分类列表。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 - -### 返回值 - -[#ListResult\](#listresultcategoryvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## listAll() - -```js -categoryFinder.listAll() -``` - -### 描述 - -获取所有文章分类。 - -### 参数 - -无 - -### 返回值 - -List\<[#CategoryVo](#categoryvo)\> - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## listAsTree() - -```js -categoryFinder.listAsTree() -``` - -### 描述 - -获取所有文章分类的多层级结构。 - -### 参数 - -无 - -### 返回值 - -List\<[#CategoryTreeVo](#categorytreevo)\> - -### 示例 - -```html -
-
    -
  • -
-
-``` - -```html title="/templates/category-tree.html" - -``` - -## getBreadcrumbs(name) - -```js -categoryFinder.getBreadcrumbs('category-foo') -``` - -### 描述 - -获取分类树结构的路径节点,可以通过此方法来构建面包屑导航。 - -### 参数 - -- `name:string` - 分类的唯一标识 `metadata.name`,必填。 - -### 返回值 - -List\<[#CategoryVo](#categoryvo)\> - -### 示例 - -```html -
- - - / - -
-``` - -## 类型定义 - -### CategoryVo - - - -### ListResult\ - -```json title="ListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#CategoryVo>", // 分类列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0 // 总页数 -} -``` - -- [#CategoryVo](#categoryvo) - -### CategoryTreeVo - - - -- [#CategoryTreeVo](#categorytreevo) diff --git a/docs/developer-guide/theme/finder-apis/category.mdx b/docs/developer-guide/theme/finder-apis/category.mdx new file mode 100644 index 00000000..a3002b2a --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/category.mdx @@ -0,0 +1,224 @@ +--- +title: 文章分类 +description: 文章分类 - CategoryFinder +--- + +import CategoryVo from "../vo/_CategoryVo.md" +import CategoryTreeVo from "../vo/_CategoryTreeVo.md" + +## getByName(name) + +```js +categoryFinder.getByName(name) +``` + +:::info 配合分类选择器使用 +通常建议配合 [主题设置](../settings.md) 和 [分类选择器](../../form-schema.md#categoryselect) 使用,让用户自行选择所需的分类。 +::: + +### 描述 + +根据 `metadata.name` 获取文章分类。 + +### 参数 + +1. `name:string` - 分类的唯一标识 `metadata.name`。 + +### 返回值 + +[#CategoryVo](#categoryvo) + +### 示例 + +```html +
+ +
+``` + +## getByNames(names) + +```js +categoryFinder.getByNames(names) +``` + +:::info 配合分类选择器使用 +通常建议配合 [主题设置](../settings.md) 和 [分类选择器](../../form-schema.md#categoryselect) 使用,让用户自行选择所需的分类。 +::: + +### 描述 + +根据一组 `metadata.name` 获取文章分类。 + +### 参数 + +1. `names:List` - 分类的唯一标识 `metadata.name` 的集合。 + +### 返回值 + +List\<[#CategoryVo](#categoryvo)\> + +### 示例 + +```html +
+ +
+``` + +## list(page,size) + +```js +categoryFinder.list(page,size) +``` + +### 描述 + +根据分页参数获取分类列表。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 + +### 返回值 + +[#ListResult\](#listresultcategoryvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## listAll() + +```js +categoryFinder.listAll() +``` + +### 描述 + +获取所有文章分类。 + +### 参数 + +无 + +### 返回值 + +List\<[#CategoryVo](#categoryvo)\> + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## listAsTree() + +```js +categoryFinder.listAsTree() +``` + +### 描述 + +获取所有文章分类的多层级结构。 + +### 参数 + +无 + +### 返回值 + +List\<[#CategoryTreeVo](#categorytreevo)\> + +### 示例 + +```html +
+
    +
  • +
+
+``` + +```html title="/templates/category-tree.html" + +``` + +## getBreadcrumbs(name) + +```js +categoryFinder.getBreadcrumbs('category-foo') +``` + +### 描述 + +获取分类树结构的路径节点,可以通过此方法来构建面包屑导航。 + +### 参数 + +- `name:string` - 分类的唯一标识 `metadata.name`,必填。 + +### 返回值 + +List\<[#CategoryVo](#categoryvo)\> + +### 示例 + +```html +
+ + + / + +
+``` + +## 类型定义 + +### CategoryVo + + + +### ListResult\ + +```json title="ListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#CategoryVo>", // 分类列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +- [#CategoryVo](#categoryvo) + +### CategoryTreeVo + + + +- [#CategoryTreeVo](#categorytreevo) diff --git a/docs/developer-guide/theme/finder-apis/comment.md b/docs/developer-guide/theme/finder-apis/comment.md deleted file mode 100644 index 9e42f8bd..00000000 --- a/docs/developer-guide/theme/finder-apis/comment.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: 评论 -description: 评论 - CommentFinder ---- - -import CommentVo from "../vo/_CommentVo.md" -import ReplyVo from "../vo/_ReplyVo.md" - -## getByName(name) - -```js -commentFinder.getByName(name) -``` - -### 描述 - -根据 `metadata.name` 获取评论。 - -### 参数 - -1. `name:string` - 评论的唯一标识 `metadata.name`。 - -### 返回值 - -[#CommentVo](#commentvo) - -### 示例 - -```html -
- -
-
-``` - -## list(ref,page,size) - -```js -commentFinder.list(ref,page,size) -``` - -### 描述 - -根据评论的 `metadata.name` 和分页参数获取回复列表。 - -### 参数 - -1. `ref:#Ref` - 评论的唯一标识 `metadata.name`。 -2. `page:int` - 分页页码,从 1 开始 -3. `size:int` - 分页条数 - -- [#Ref](#ref) - -### 返回值 - -[#ListResult\](#listresultcommentvo) - -### 示例 - -```html -
    -
  • - -
    -
  • -
-``` - -## listReply(commentName,page,size) - -```js -commentFinder.listReply(commentName,page,size) -``` - -### 描述 - -根据评论的 `metadata.name` 和分页参数获取回复列表。 - -### 参数 - -1. `commentName:string` - 评论的唯一标识 `metadata.name`。 -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 - -### 返回值 - -[#ListResult\](#listresultreplyvo) - -### 示例 - -```html -
    -
  • - -
    -
  • -
-``` - -## 类型定义 - -### CommentVo - - - -### ListResult\ - -```json title="ListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#CommentVo>", // 评论列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0 // 总页数 -} -``` - -- [#CommentVo](#commentvo) - -### ReplyVo - - - -### ListResult\ - -```json title="ListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#ReplyVo>", // 回复列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0 // 总页数 -} -``` - -- [#ReplyVo](#replyvo) - -### Ref - -```json title="Ref" -{ - "group": "string", - "kind": "string", - "version": "string", - "name": "string" -} -``` diff --git a/docs/developer-guide/theme/finder-apis/comment.mdx b/docs/developer-guide/theme/finder-apis/comment.mdx new file mode 100644 index 00000000..2c1019a8 --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/comment.mdx @@ -0,0 +1,155 @@ +--- +title: 评论 +description: 使用 CommentFinder 按名称查询评论、按内容引用分页获取评论列表,并查询指定评论的回复,包含 CommentVo、ReplyVo 和 Ref 类型定义 +--- + +import CommentVo from "../vo/_CommentVo.md" +import ReplyVo from "../vo/_ReplyVo.md" + +## getByName(name) + +```js +commentFinder.getByName(name) +``` + +### 描述 + +根据 `metadata.name` 获取评论。 + +### 参数 + +1. `name:string` - 评论的唯一标识 `metadata.name`。 + +### 返回值 + +[#CommentVo](#commentvo) + +### 示例 + +```html +
+ +
+
+``` + +## list(ref,page,size) + +```js +commentFinder.list(ref,page,size) +``` + +### 描述 + +根据评论的 `metadata.name` 和分页参数获取回复列表。 + +### 参数 + +1. `ref:#Ref` - 评论的唯一标识 `metadata.name`。 +2. `page:int` - 分页页码,从 1 开始 +3. `size:int` - 分页条数 + +- [#Ref](#ref) + +### 返回值 + +[#ListResult\](#listresultcommentvo) + +### 示例 + +```html +
    +
  • + +
    +
  • +
+``` + +## listReply(commentName,page,size) + +```js +commentFinder.listReply(commentName,page,size) +``` + +### 描述 + +根据评论的 `metadata.name` 和分页参数获取回复列表。 + +### 参数 + +1. `commentName:string` - 评论的唯一标识 `metadata.name`。 +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 + +### 返回值 + +[#ListResult\](#listresultreplyvo) + +### 示例 + +```html +
    +
  • + +
    +
  • +
+``` + +## 类型定义 + +### CommentVo + + + +### ListResult\ + +```json title="ListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#CommentVo>", // 评论列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +- [#CommentVo](#commentvo) + +### ReplyVo + + + +### ListResult\ + +```json title="ListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#ReplyVo>", // 回复列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +- [#ReplyVo](#replyvo) + +### Ref + +```json title="Ref" +{ + "group": "string", + "kind": "string", + "version": "string", + "name": "string" +} +``` diff --git a/docs/developer-guide/theme/finder-apis/contributor.md b/docs/developer-guide/theme/finder-apis/contributor.mdx similarity index 100% rename from docs/developer-guide/theme/finder-apis/contributor.md rename to docs/developer-guide/theme/finder-apis/contributor.mdx diff --git a/docs/developer-guide/theme/finder-apis/index.md b/docs/developer-guide/theme/finder-apis/index.md new file mode 100644 index 00000000..24d272b5 --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/index.md @@ -0,0 +1,7 @@ +--- +title: Finder API +description: 本文档介绍 Finder API 的使用方法。 +overview: true +--- + +目前在主题模板中获取数据可以使用对应路由提供的 [模板变量](../template-variables/index.md),但为了满足在任意位置获取数据的需求,我们提供了 Finder API。 diff --git a/docs/developer-guide/theme/finder-apis/menu.md b/docs/developer-guide/theme/finder-apis/menu.md deleted file mode 100644 index f1ed29bb..00000000 --- a/docs/developer-guide/theme/finder-apis/menu.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: 导航菜单 -description: 导航菜单 - MenuFinder ---- - -import MenuItemVo from "../vo/_MenuItemVo.md" -import MenuVo from "../vo/_MenuVo.md" - -## getByName(name) - -```js -menuFinder.getByName(name) -``` - -### 描述 - -根据 `metadata.name` 获取菜单。 - -### 参数 - -1. `name:string` - 菜单的唯一标识 `metadata.name`。 - -### 返回值 - -[#MenuVo](#menuvo) - -### 示例 - -```html -
- -
-``` - -## getPrimary() - -```js -menuFinder.getPrimary() -``` - -### 描述 - -获取主菜单。 - -### 参数 - -无 - -### 返回值 - -[#MenuVo](#menuvo) - -### 示例 - -```html -
- -
-``` - -## 类型定义 - -### MenuVo - - - -### MenuItemVo - - diff --git a/docs/developer-guide/theme/finder-apis/menu.mdx b/docs/developer-guide/theme/finder-apis/menu.mdx new file mode 100644 index 00000000..b0ecd7ca --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/menu.mdx @@ -0,0 +1,87 @@ +--- +title: 导航菜单 +description: 使用 MenuFinder 按唯一标识查询导航菜单或获取站点主菜单,并通过 Thymeleaf 遍历 MenuVo 与 MenuItemVo 渲染菜单链接 +--- + +import MenuItemVo from "../vo/_MenuItemVo.md" +import MenuVo from "../vo/_MenuVo.md" + +## getByName(name) + +```js +menuFinder.getByName(name) +``` + +### 描述 + +根据 `metadata.name` 获取菜单。 + +### 参数 + +1. `name:string` - 菜单的唯一标识 `metadata.name`。 + +### 返回值 + +[#MenuVo](#menuvo) + +### 示例 + +```html +
+ +
+``` + +## getPrimary() + +```js +menuFinder.getPrimary() +``` + +### 描述 + +获取主菜单。 + +### 参数 + +无 + +### 返回值 + +[#MenuVo](#menuvo) + +### 示例 + +```html +
+ +
+``` + +## 类型定义 + +### MenuVo + + + +### MenuItemVo + + diff --git a/docs/developer-guide/theme/finder-apis/plugin.md b/docs/developer-guide/theme/finder-apis/plugin.md index 8585e584..7aa33128 100644 --- a/docs/developer-guide/theme/finder-apis/plugin.md +++ b/docs/developer-guide/theme/finder-apis/plugin.md @@ -1,6 +1,6 @@ --- title: 插件 -description: 插件 - PluginFinder +description: 使用 PluginFinder 检查指定 Halo 插件是否已安装并启用,或进一步按 Semantic Version 版本范围判断插件是否可用 --- ## available(pluginName) diff --git a/docs/developer-guide/theme/finder-apis/post.md b/docs/developer-guide/theme/finder-apis/post.md deleted file mode 100644 index 2d63b606..00000000 --- a/docs/developer-guide/theme/finder-apis/post.md +++ /dev/null @@ -1,600 +0,0 @@ ---- -title: 文章 -description: 文章 - PostFinder ---- - -import CategoryVo from "../vo/_CategoryVo.md"; -import TagVo from "../vo/_TagVo.md"; -import PostVo from "../vo/_PostVo.md"; -import ContentVo from "../vo/_ContentVo.md" -import ContributorVo from "../vo/_ContributorVo.md" -import ListedPostVo from "../vo/_ListedPostVo.md" - -## getByName(postName) - -```js -postFinder.getByName(postName); -``` - -### 描述 - -根据 `metadata.name` 获取文章。 - -### 参数 - -1. `postName:string` - 文章的唯一标识 `metadata.name`。 - -### 返回值 - -[#PostVo](#postvo) - -### 示例 - -```html -
- -
-``` - -## content(postName) - -```js -postFinder.content(postName); -``` - -### 描述 - -根据文章的 `metadata.name` 单独获取文章内容。 - -### 参数 - -1. `postName:string` - 文章的唯一标识 `metadata.name`。 - -### 返回值 - -[#ContentVo](#contentvo) - -### 示例 - -```html -
-
-
-``` - -## cursor(postName) - -```js -postFinder.cursor(postName); -``` - -### 描述 - -根据文章的 `metadata.name` 获取相邻的文章(上一篇 / 下一篇)。 - -:::info[提示] -上一篇文章是指发布时间较当前文章更早的文章,下一篇文章是指发布时间较当前文章更新的文章。 -::: - -### 参数 - -1. `postName:string` - 文章的唯一标识 `metadata.name`。 - -### 返回值 - -[#NavigationPostVo](#navigationpostvo) - -### 示例 - -```html title="/templates/post.html" - -``` - -## cursorByCategory(postName) - -```js -postFinder.cursorByCategory(postName); -``` - -### 描述 - -根据文章的 `metadata.name` 获取同一主分类下相邻的文章(上一篇 / 下一篇)。 - -主分类为文章 `spec.categories` 中的第一个分类。此方法只匹配同一分类下的文章,不会包含子分类中的文章。如果当前文章没有分类、未发布或者不存在,将返回空的导航结果。 - -:::info[提示] -上一篇文章是指发布时间较当前文章更早的文章,下一篇文章是指发布时间较当前文章更新的文章。 -::: - -对应的 Public API 为: - -```bash -GET /apis/api.content.halo.run/v1alpha1/posts/{name}/navigation?scope=category -``` - -### 参数 - -1. `postName:string` - 文章的唯一标识 `metadata.name`。 - -### 返回值 - -[#NavigationPostVo](#navigationpostvo) - -### 示例 - -```html title="/templates/post.html" - -``` - -## listAll() - -```js -postFinder.listAll(); -``` - -### 描述 - -获取所有文章。 - -### 参数 - -无 - -### 返回值 - -List\<[#ListedPostVo](#listedpostvo)\> - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## random(maxSize) - -```js -postFinder.random(maxSize); -``` - -### 描述 - -随机获取文章列表。 - -### 参数 - -1. `maxSize:int` - 获取文章的最大数量,取值范围为 1 到 100。 - -### 返回值 - -List\<[#ListedPostVo](#listedpostvo)\> - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## `list({...})` - -```js -postFinder.list({ - page: 1, - size: 10, - tagName: 'fake-tag', - categoryName: 'fake-category', - ownerName: 'fake-owner', - pinned: true, - sort: {'spec.publishTime,desc', 'metadata.creationTimestamp,asc'} -}); -``` - -### 描述 - -统一参数的文章列表查询方法,支持分页、标签、分类、创建者、置顶状态、排序等参数,且均为可选参数。 - -可以使用此方法来代替 `list(page, size)`、`listByCategory(page, size, categoryName)`、`listByTag(page, size, tag)`、`listByOwner(page, size, owner)` 方法。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 -3. `tagName:string` - 标签唯一标识 `metadata.name` -4. `categoryName:string` - 分类唯一标识 `metadata.name` -5. `ownerName:string` - 创建者用户名 `name` -6. `pinned:boolean` - 置顶状态,`true` 仅返回置顶文章,`false` 仅返回非置顶文章;不传时不按置顶状态筛选 -7. `sort:string[]` - 排序字段,格式为 `字段名,排序方式`,排序方式可选值为 `asc` 或 `desc`,如 `spec.publishTime,desc`,传递时需要使用 `{}` 形式并用逗号分隔表示数组。 - -### 返回值 - -[#ListResult\](#listresultlistedpostvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## list(page,size) - -```js -postFinder.list(page, size); -``` - -### 描述 - -根据分页参数获取文章列表。 - -**已过时**: 请使用 `list({...})` 方法代替。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 - -### 返回值 - -[#ListResult\](#listresultlistedpostvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## listByCategory(page,size,categoryName) - -```js -postFinder.listByCategory(page, size, categoryName); -``` - -### 描述 - -根据分类标识和分页参数获取文章列表。 - -**已过时**: 请使用 `list({...})` 方法代替。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 -3. `categoryName:string` - 文章分类唯一标识 `metadata.name` - -### 返回值 - -[#ListResult\](#listresultlistedpostvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## listByTag(page,size,tag) - -```js -postFinder.listByTag(page, size, tag); -``` - -### 描述 - -根据标签标识和分页参数获取文章列表。 - -**已过时**: 请使用 `list({...})` 方法代替。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 -3. `tag:string` - 文章分类唯一标识 `metadata.name` - -### 返回值 - -[#ListResult\](#listresultlistedpostvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## listByOwner(page,size,owner) - -```js -postFinder.listByOwner(page, size, owner); -``` - -### 描述 - -根据创建者用户名和分页参数获取文章列表。 - -**已过时**: 请使用 `list({...})` 方法代替。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 -3. `owner:string` - 创建者用户名 `name` - -### 返回值 - -[#ListResult\](#listresultlistedpostvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## archives(page,size) - -```js -postFinder.archives(page, size); -``` - -### 描述 - -根据分页参数获取文章归档列表。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 - -### 返回值 - -[#ListResult\](#listresultpostarchivevo) - -### 示例 - -```html - - -

-
    - -
  • - - -
  • -
    -
-
-
-``` - -## archives(page,size,year) - -```js -postFinder.archives(page, size, year); -``` - -### 描述 - -根据年份和分页参数获取文章归档列表。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 -3. `year:string` - 年份 - -### 返回值 - -[#ListResult\](#listresultpostarchivevo) - -### 示例 - -```html - - -

-
    - -
  • - - -
  • -
    -
-
-
-``` - -## archives(page,size,year,month) - -```js -postFinder.archives(page, size, year, month); -``` - -### 描述 - -根据年月和分页参数获取文章归档列表。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 -3. `year:string` - 年份 -4. `month:string` - 月份 - -### 返回值 - -[#ListResult\](#listresultpostarchivevo) - -### 示例 - -```html - - -

-
    - -
  • - - -
  • -
    -
-
-
-``` - -## 类型定义 - -### CategoryVo - - - -### TagVo - - - -### ContributorVo - - - -### PostVo - - - -- [#CategoryVo](#categoryvo) -- [#TagVo](#tagvo) -- [#ContributorVo](#contributorvo) -- [#ContentVo](#contentvo) - -### ContentVo - - - -### NavigationPostVo - -```json title="NavigationPostVo" -{ - "previous": "#ListedPostVo", // 上一篇文章,发布时间较当前文章更早的文章 - "next": "#ListedPostVo" // 下一篇文章,发布时间较当前文章更新的文章 -} -``` - -- [#PostVo](#postvo) - -### ListedPostVo - - - -- [#CategoryVo](#categoryvo) -- [#TagVo](#tagvo) -- [#ContributorVo](#contributorvo) - -### ListResult\ - -```json title="ListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#ListedPostVo>", // 文章列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0 // 总页数 -} -``` - -- [#ListedPostVo](#listedpostvo) - -### PostArchiveVo - -```json title="PostArchiveVo" -{ - "year": "string", - "months": [ - { - "month": "string", - "posts": "#ListedPostVo" - } - ] -} -``` - -- [#ListedPostVo](#listedpostvo) - -### ListResult\ - -```json title="ListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#PostArchiveVo>", // 文章归档数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0 // 总页数 -} -``` - -- [#PostArchiveVo](#postarchivevo) diff --git a/docs/developer-guide/theme/finder-apis/post.mdx b/docs/developer-guide/theme/finder-apis/post.mdx new file mode 100644 index 00000000..56378370 --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/post.mdx @@ -0,0 +1,600 @@ +--- +title: 文章 +description: 使用 PostFinder 查询文章、正文、上下篇导航、随机文章和分页列表,按分类、标签、作者与置顶状态筛选,并获取按年月组织的文章归档 +--- + +import CategoryVo from "../vo/_CategoryVo.md"; +import TagVo from "../vo/_TagVo.md"; +import PostVo from "../vo/_PostVo.md"; +import ContentVo from "../vo/_ContentVo.md" +import ContributorVo from "../vo/_ContributorVo.md" +import ListedPostVo from "../vo/_ListedPostVo.md" + +## getByName(postName) + +```js +postFinder.getByName(postName); +``` + +### 描述 + +根据 `metadata.name` 获取文章。 + +### 参数 + +1. `postName:string` - 文章的唯一标识 `metadata.name`。 + +### 返回值 + +[#PostVo](#postvo) + +### 示例 + +```html +
+ +
+``` + +## content(postName) + +```js +postFinder.content(postName); +``` + +### 描述 + +根据文章的 `metadata.name` 单独获取文章内容。 + +### 参数 + +1. `postName:string` - 文章的唯一标识 `metadata.name`。 + +### 返回值 + +[#ContentVo](#contentvo) + +### 示例 + +```html +
+
+
+``` + +## cursor(postName) + +```js +postFinder.cursor(postName); +``` + +### 描述 + +根据文章的 `metadata.name` 获取相邻的文章(上一篇 / 下一篇)。 + +:::info 上一篇与下一篇的定义 +上一篇文章是指发布时间较当前文章更早的文章,下一篇文章是指发布时间较当前文章更新的文章。 +::: + +### 参数 + +1. `postName:string` - 文章的唯一标识 `metadata.name`。 + +### 返回值 + +[#NavigationPostVo](#navigationpostvo) + +### 示例 + +```html title="/templates/post.html" + +``` + +## cursorByCategory(postName) + +```js +postFinder.cursorByCategory(postName); +``` + +### 描述 + +根据文章的 `metadata.name` 获取同一主分类下相邻的文章(上一篇 / 下一篇)。 + +主分类为文章 `spec.categories` 中的第一个分类。此方法只匹配同一分类下的文章,不会包含子分类中的文章。如果当前文章没有分类、未发布或者不存在,将返回空的导航结果。 + +:::info 上一篇与下一篇的定义 +上一篇文章是指发布时间较当前文章更早的文章,下一篇文章是指发布时间较当前文章更新的文章。 +::: + +对应的 Public API 为: + +```bash +GET /apis/api.content.halo.run/v1alpha1/posts/{name}/navigation?scope=category +``` + +### 参数 + +1. `postName:string` - 文章的唯一标识 `metadata.name`。 + +### 返回值 + +[#NavigationPostVo](#navigationpostvo) + +### 示例 + +```html title="/templates/post.html" + +``` + +## listAll() + +```js +postFinder.listAll(); +``` + +### 描述 + +获取所有文章。 + +### 参数 + +无 + +### 返回值 + +List\<[#ListedPostVo](#listedpostvo)\> + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## random(maxSize) + +```js +postFinder.random(maxSize); +``` + +### 描述 + +随机获取文章列表。 + +### 参数 + +1. `maxSize:int` - 获取文章的最大数量,取值范围为 1 到 100。 + +### 返回值 + +List\<[#ListedPostVo](#listedpostvo)\> + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## `list({...})` + +```js +postFinder.list({ + page: 1, + size: 10, + tagName: 'fake-tag', + categoryName: 'fake-category', + ownerName: 'fake-owner', + pinned: true, + sort: {'spec.publishTime,desc', 'metadata.creationTimestamp,asc'} +}); +``` + +### 描述 + +统一参数的文章列表查询方法,支持分页、标签、分类、创建者、置顶状态、排序等参数,且均为可选参数。 + +可以使用此方法来代替 `list(page, size)`、`listByCategory(page, size, categoryName)`、`listByTag(page, size, tag)`、`listByOwner(page, size, owner)` 方法。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 +3. `tagName:string` - 标签唯一标识 `metadata.name` +4. `categoryName:string` - 分类唯一标识 `metadata.name` +5. `ownerName:string` - 创建者用户名 `name` +6. `pinned:boolean` - 置顶状态,`true` 仅返回置顶文章,`false` 仅返回非置顶文章;不传时不按置顶状态筛选 +7. `sort:string[]` - 排序字段,格式为 `字段名,排序方式`,排序方式可选值为 `asc` 或 `desc`,如 `spec.publishTime,desc`,传递时需要使用 `{}` 形式并用逗号分隔表示数组。 + +### 返回值 + +[#ListResult\](#listresultlistedpostvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## list(page,size) + +```js +postFinder.list(page, size); +``` + +### 描述 + +根据分页参数获取文章列表。 + +**已过时**: 请使用 `list({...})` 方法代替。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 + +### 返回值 + +[#ListResult\](#listresultlistedpostvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## listByCategory(page,size,categoryName) + +```js +postFinder.listByCategory(page, size, categoryName); +``` + +### 描述 + +根据分类标识和分页参数获取文章列表。 + +**已过时**: 请使用 `list({...})` 方法代替。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 +3. `categoryName:string` - 文章分类唯一标识 `metadata.name` + +### 返回值 + +[#ListResult\](#listresultlistedpostvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## listByTag(page,size,tag) + +```js +postFinder.listByTag(page, size, tag); +``` + +### 描述 + +根据标签标识和分页参数获取文章列表。 + +**已过时**: 请使用 `list({...})` 方法代替。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 +3. `tag:string` - 文章分类唯一标识 `metadata.name` + +### 返回值 + +[#ListResult\](#listresultlistedpostvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## listByOwner(page,size,owner) + +```js +postFinder.listByOwner(page, size, owner); +``` + +### 描述 + +根据创建者用户名和分页参数获取文章列表。 + +**已过时**: 请使用 `list({...})` 方法代替。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 +3. `owner:string` - 创建者用户名 `name` + +### 返回值 + +[#ListResult\](#listresultlistedpostvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## archives(page,size) + +```js +postFinder.archives(page, size); +``` + +### 描述 + +根据分页参数获取文章归档列表。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 + +### 返回值 + +[#ListResult\](#listresultpostarchivevo) + +### 示例 + +```html + + +

+
    + +
  • + + +
  • +
    +
+
+
+``` + +## archives(page,size,year) + +```js +postFinder.archives(page, size, year); +``` + +### 描述 + +根据年份和分页参数获取文章归档列表。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 +3. `year:string` - 年份 + +### 返回值 + +[#ListResult\](#listresultpostarchivevo) + +### 示例 + +```html + + +

+
    + +
  • + + +
  • +
    +
+
+
+``` + +## archives(page,size,year,month) + +```js +postFinder.archives(page, size, year, month); +``` + +### 描述 + +根据年月和分页参数获取文章归档列表。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 +3. `year:string` - 年份 +4. `month:string` - 月份 + +### 返回值 + +[#ListResult\](#listresultpostarchivevo) + +### 示例 + +```html + + +

+
    + +
  • + + +
  • +
    +
+
+
+``` + +## 类型定义 + +### CategoryVo + + + +### TagVo + + + +### ContributorVo + + + +### PostVo + + + +- [#CategoryVo](#categoryvo) +- [#TagVo](#tagvo) +- [#ContributorVo](#contributorvo) +- [#ContentVo](#contentvo) + +### ContentVo + + + +### NavigationPostVo + +```json title="NavigationPostVo" +{ + "previous": "#ListedPostVo", // 上一篇文章,发布时间较当前文章更早的文章 + "next": "#ListedPostVo" // 下一篇文章,发布时间较当前文章更新的文章 +} +``` + +- [#PostVo](#postvo) + +### ListedPostVo + + + +- [#CategoryVo](#categoryvo) +- [#TagVo](#tagvo) +- [#ContributorVo](#contributorvo) + +### ListResult\ + +```json title="ListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#ListedPostVo>", // 文章列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +- [#ListedPostVo](#listedpostvo) + +### PostArchiveVo + +```json title="PostArchiveVo" +{ + "year": "string", + "months": [ + { + "month": "string", + "posts": "#ListedPostVo" + } + ] +} +``` + +- [#ListedPostVo](#listedpostvo) + +### ListResult\ + +```json title="ListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#PostArchiveVo>", // 文章归档数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +- [#PostArchiveVo](#postarchivevo) diff --git a/docs/developer-guide/theme/finder-apis/single-page.md b/docs/developer-guide/theme/finder-apis/single-page.mdx similarity index 100% rename from docs/developer-guide/theme/finder-apis/single-page.md rename to docs/developer-guide/theme/finder-apis/single-page.mdx diff --git a/docs/developer-guide/theme/finder-apis/tag.md b/docs/developer-guide/theme/finder-apis/tag.md deleted file mode 100644 index 0e8a4c5d..00000000 --- a/docs/developer-guide/theme/finder-apis/tag.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: 文章标签 -description: 文章标签 - TagFinder ---- - -import TagVo from "../vo/_TagVo.md" - -## getByName(name) - -```js -tagFinder.getByName(name) -``` - -:::info[提示] -通常建议配合 [主题设置](../settings.md) 和 [标签选择器](../../form-schema.md#tagselect) 使用,让用户自行选择所需的标签。 -::: - -### 描述 - -根据 `metadata.name` 获取标签。 - -### 参数 - -1. `name:string` - 标签的唯一标识 `metadata.name`。 - -### 返回值 - -[#TagVo](#tagvo) - -### 示例 - -```html -
- -
-``` - -## getByNames(names) - -```js -tagFinder.getByNames(names) -``` - -:::info[提示] -通常建议配合 [主题设置](../settings.md) 和 [标签选择器](../../form-schema.md#tagselect) 使用,让用户自行选择所需的标签。 -::: - -### 描述 - -根据一组 `metadata.name` 获取标签。 - -### 参数 - -1. `names:List` - 标签的唯一标识 `metadata.name` 的集合。 - -### 返回值 - -List\<[#TagVo](#tagvo)\> - -### 示例 - -```html -
- -
-``` - -## list(page,size) - -```js -tagFinder.list(page,size) -``` - -### 描述 - -根据分页参数获取标签列表。 - -### 参数 - -1. `page:int` - 分页页码,从 1 开始 -2. `size:int` - 分页条数 - -### 返回值 - -[#ListResult\](#listresulttagvo) - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## listAll() - -```js -tagFinder.listAll() -``` - -### 描述 - -获取所有文章标签。 - -### 参数 - -无 - -### 返回值 - -List\<[#TagVo](#tagvo)\> - -### 示例 - -```html -
    -
  • - -
  • -
-``` - -## 类型定义 - -### TagVo - - - -### ListResult\ - -```json title="ListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#TagVo>", // 标签列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0 // 总页数 -} -``` - -- [#TagVo](#tagvo) diff --git a/docs/developer-guide/theme/finder-apis/tag.mdx b/docs/developer-guide/theme/finder-apis/tag.mdx new file mode 100644 index 00000000..456b7b70 --- /dev/null +++ b/docs/developer-guide/theme/finder-apis/tag.mdx @@ -0,0 +1,147 @@ +--- +title: 文章标签 +description: 使用 TagFinder 按唯一标识批量查询文章标签、分页获取标签列表或读取全部标签,并在 Thymeleaf 模板中渲染标签链接 +--- + +import TagVo from "../vo/_TagVo.md" + +## getByName(name) + +```js +tagFinder.getByName(name) +``` + +:::info 配合标签选择器使用 +通常建议配合 [主题设置](../settings.md) 和 [标签选择器](../../form-schema.md#tagselect) 使用,让用户自行选择所需的标签。 +::: + +### 描述 + +根据 `metadata.name` 获取标签。 + +### 参数 + +1. `name:string` - 标签的唯一标识 `metadata.name`。 + +### 返回值 + +[#TagVo](#tagvo) + +### 示例 + +```html +
+ +
+``` + +## getByNames(names) + +```js +tagFinder.getByNames(names) +``` + +:::info 配合标签选择器使用 +通常建议配合 [主题设置](../settings.md) 和 [标签选择器](../../form-schema.md#tagselect) 使用,让用户自行选择所需的标签。 +::: + +### 描述 + +根据一组 `metadata.name` 获取标签。 + +### 参数 + +1. `names:List` - 标签的唯一标识 `metadata.name` 的集合。 + +### 返回值 + +List\<[#TagVo](#tagvo)\> + +### 示例 + +```html +
+ +
+``` + +## list(page,size) + +```js +tagFinder.list(page,size) +``` + +### 描述 + +根据分页参数获取标签列表。 + +### 参数 + +1. `page:int` - 分页页码,从 1 开始 +2. `size:int` - 分页条数 + +### 返回值 + +[#ListResult\](#listresulttagvo) + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## listAll() + +```js +tagFinder.listAll() +``` + +### 描述 + +获取所有文章标签。 + +### 参数 + +无 + +### 返回值 + +List\<[#TagVo](#tagvo)\> + +### 示例 + +```html +
    +
  • + +
  • +
+``` + +## 类型定义 + +### TagVo + + + +### ListResult\ + +```json title="ListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#TagVo>", // 标签列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +- [#TagVo](#tagvo) diff --git a/docs/developer-guide/theme/finder-apis/theme.md b/docs/developer-guide/theme/finder-apis/theme.md index 172ba3f8..8d8c3aa3 100644 --- a/docs/developer-guide/theme/finder-apis/theme.md +++ b/docs/developer-guide/theme/finder-apis/theme.md @@ -1,6 +1,6 @@ --- title: 主题 -description: 主题 - ThemeFinder +description: 使用 ThemeFinder 获取当前启用的 Halo 主题或按唯一标识查询主题,并读取主题版本、作者、设置、自定义模板等 ThemeVo 信息 --- ## activation() diff --git a/docs/developer-guide/theme/global-variables.md b/docs/developer-guide/theme/global-variables.md deleted file mode 100644 index 3ed501a6..00000000 --- a/docs/developer-guide/theme/global-variables.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: 全局变量 -description: 本文档介绍 Halo 为模板引擎提供的专有全局变量。 ---- - -import SiteSettingVo from "./vo/_SiteSettingVo.md" -import ThemeVo from "./vo/_ThemeVo.md" - -Halo 目前为模板引擎在全局提供了一些变量,本文档将列出已提供的变量以及介绍这些变量的使用方法。 - -## site - -### 描述 - -提供了部分可公开的系统相关的设置项,其中所有参数均来自于 Console 的系统设置。 - -### 类型 - - - -### 示例 - -显示站点标题: - -```html -

-``` - -显示站点 Logo: - -```html -Logo -``` - -显示当前 Halo 版本: - -```html - -``` - -## #halo.matchVersion(constraint) - -### 描述 - -用于判断当前运行的 Halo 版本是否满足指定的语义化版本范围,适合在主题模板中为依赖新版 Halo 能力的片段添加兼容判断。 - -版本范围格式遵循 [Semantic Range Expressions](https://github.com/zafarkhaja/jsemver#range-expressions),例如 `>=2.25.0`、`>2.0.0 & <3.0.0` 等。 - -:::tip -开发版本 `0.0.0` 会始终返回 `true`,以便在本地开发环境中调试主题模板。 -::: - -### 示例 - -仅在 Halo 版本满足要求时渲染模板片段: - -```html -
- -
-``` - -判断一个版本范围: - -```html -
- -
-``` - -## theme - -### 描述 - -关于当前激活主题的信息。 - -### 类型 - - - -### 示例 - -显示主题名称: - -```html -

-``` - -在静态资源加入版本号参数,以防止升级之后的缓存问题: - -```html - - -``` diff --git a/docs/developer-guide/theme/global-variables.mdx b/docs/developer-guide/theme/global-variables.mdx new file mode 100644 index 00000000..59ae1f86 --- /dev/null +++ b/docs/developer-guide/theme/global-variables.mdx @@ -0,0 +1,94 @@ +--- +title: 全局变量 +description: 本文档介绍 Halo 为模板引擎提供的专有全局变量。 +--- + +import SiteSettingVo from "./vo/_SiteSettingVo.md" +import ThemeVo from "./vo/_ThemeVo.md" + +Halo 目前为模板引擎在全局提供了一些变量,本文档将列出已提供的变量以及介绍这些变量的使用方法。 + +## site + +### 描述 + +提供了部分可公开的系统相关的设置项,其中所有参数均来自于 Console 的系统设置。 + +### 类型 + + + +### 示例 + +显示站点标题: + +```html +

+``` + +显示站点 Logo: + +```html +Logo +``` + +显示当前 Halo 版本: + +```html + +``` + +## #halo.matchVersion(constraint) + +### 描述 + +用于判断当前运行的 Halo 版本是否满足指定的语义化版本范围,适合在主题模板中为依赖新版 Halo 能力的片段添加兼容判断。 + +版本范围格式遵循 [Semantic Range Expressions](https://github.com/zafarkhaja/jsemver#range-expressions),例如 `>=2.25.0`、`>2.0.0 & <3.0.0` 等。 + +:::tip 开发版本行为 +开发版本 `0.0.0` 会始终返回 `true`,以便在本地开发环境中调试主题模板。 +::: + +### 示例 + +仅在 Halo 版本满足要求时渲染模板片段: + +```html +
+ +
+``` + +判断一个版本范围: + +```html +
+ +
+``` + +## theme + +### 描述 + +关于当前激活主题的信息。 + +### 类型 + + + +### 示例 + +显示主题名称: + +```html +

+``` + +在静态资源加入版本号参数,以防止升级之后的缓存问题: + +```html + + +``` diff --git a/docs/developer-guide/theme/image-optimization.md b/docs/developer-guide/theme/image-optimization.md index 83eac94e..c4227753 100644 --- a/docs/developer-guide/theme/image-optimization.md +++ b/docs/developer-guide/theme/image-optimization.md @@ -13,13 +13,13 @@ description: 本文档介绍如何使用 Halo 的缩略图特性来优化图片 - 提升加载性能:通过为图片提供多个尺寸的缩略图,浏览器可以选择最适合当前视窗的图片进行加载,从而减少不必要的带宽使用,提升页面加载速度,改善用户体验。 - 兼容性好:响应式图片是基于 HTML 标准的实现,不需要额外的 JavaScript 或 CSS,因此兼容性非常好。 -:::info +:::info 了解响应式图片 建议阅读 [响应式图片](https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images) 文档,以了解更多关于响应式图片的知识,以及如何在不同场景下使用。 ::: ## Finder API -:::info +:::info Halo 2.22 自动添加响应式属性 从 Halo 2.22 开始,Halo 会自动为页面上的所有图片添加响应式图片的相关属性,无需主题主动适配,如果你的主题需要手动为部分图片自定义属性,可以使用下方的 `Finder API`。 ::: diff --git a/docs/developer-guide/theme/index.md b/docs/developer-guide/theme/index.md new file mode 100644 index 00000000..cdb2bbdb --- /dev/null +++ b/docs/developer-guide/theme/index.md @@ -0,0 +1,5 @@ +--- +title: 主题开发 +description: 涵盖 Halo 主题开发的环境准备、配置与目录结构、页面布局、静态资源、设置表单、模板变量、Finder API、图片优化和 UI 扩展。 +overview: true +--- diff --git a/docs/developer-guide/theme/prepare.md b/docs/developer-guide/theme/prepare.md index 02a1d7d8..a6ba5988 100644 --- a/docs/developer-guide/theme/prepare.md +++ b/docs/developer-guide/theme/prepare.md @@ -1,15 +1,15 @@ --- title: 准备工作 -description: 主题开发所需的准备工作和基本的项目搭建 +description: 搭建 Halo 主题本地开发环境,从模板创建包含 theme.yaml 和 Thymeleaf 页面模板的项目,安装并启用主题以预览页面效果 --- 此文档将讲解 Halo 2.0 主题开发的基本流程,从创建主题项目到最终预览主题效果。 ## 搭建开发环境 -Halo 在本地开发环境的运行可参考[开发环境运行](../core/run.md),或者使用 [Docker](../../getting-started/install/docker.md) 运行。 +Halo 在本地开发环境的运行可参考[开发环境运行](../core/run.md),或者使用 [Docker](../../guide/install/docker.mdx) 运行。 -:::tip +:::tip 启用主题实时更新 为了保证在开发时,主题代码可以实时生效,需要注意以下事项: - 使用 Halo 源码运行时,需要在配置文件中包含如下配置: @@ -53,11 +53,11 @@ spec: url: "https://github.com/halo-sigs/theme-foo/blob/main/LICENSE" ``` -:::info[提示] +:::info 查看主题配置文档 主题的配置文件详细文档请参考 [配置文件](./config.md)。 ::: -:::info[提示] +:::info 查看主题目录结构 主题项目的目录结构请参考 [主题目录结构](./structure.md)。 ::: @@ -69,24 +69,12 @@ spec: - [halo-sigs/theme-vite-starter](https://github.com/halo-dev/theme-vite-starter) - 与 Vite 集成的主题模板,由 Vite 负责资源构建。 - [halo-sigs/theme-astro-starter](https://github.com/halo-sigs/theme-astro-starter) - 以 Astro 作为预渲染框架的主题模板。 -:::info[提示] +:::info 从模板仓库创建主题 以上 GitHub 都被设置为了模板仓库(Template repository),点击仓库主页的 `Use this template` 按钮即可通过此模板创建一个新的仓库。 创建新的主题仓库并克隆到本地开发环境之后,需要确保主题文件夹名称和 `theme.yaml` 中的 `metadata.name` 字段一致,否则可能导致部分资源无法正常加载。 ::: -## AI 辅助开发 - -Halo 官方为主题开发者提供了 Agent Skills,支持在 Cursor、Claude Code、Codex 等 AI 开发工具中使用,以获得 Halo 主题开发的深度上下文和辅助能力。 - -- [halo-dev/dev-skills](https://github.com/halo-dev/dev-skills) - 包含 `halo-theme-dev` Skill,涵盖主题目录结构、Thymeleaf 模板、Finder API、静态资源管理、主题设置表单等内容,并提供了最小主题和 Vite 主题的初始模板。 - -安装方式: - -```bash -npx skills add halo-dev/dev-skills@halo-theme-dev -g -``` - ## 创建第一个页面模板 Halo 使用 [Thymeleaf](https://www.thymeleaf.org/) 作为后端模板引擎,后缀为 `.html`,与单纯编写 HTML 一致。在 Halo 的主题中,主题的模板文件存放于 `templates` 目录下,例如 `~/halo2-dev/themes/theme-foo/templates`。为了此文档方便演示,我们先在 `templates` 创建一个首页的模板文件 `index.html`: diff --git a/docs/developer-guide/theme/settings.md b/docs/developer-guide/theme/settings.md index 3e2639de..5ec5b34d 100644 --- a/docs/developer-guide/theme/settings.md +++ b/docs/developer-guide/theme/settings.md @@ -1,11 +1,11 @@ --- title: 设置选项 -description: 介绍主题如何定义以及使用设置选项。 +description: 通过 settings.yaml 和 FormKit 表单定义 Halo 主题设置项,关联 Setting 与 ConfigMap,并在 Thymeleaf 模板中读取配置和重载更新 --- 此文档将讲解如何在主题中定义和使用设置项,如 [表单定义](../form-schema) 中所说,目前 Halo 的 Console 端的所有表单都使用了 [FormKit](https://github.com/formkit/formkit) 的方案。 -:::tip +:::tip 先了解 FormKit 表单定义 有关 FormKit 定义表单的更多信息,请参考 [表单定义](../form-schema),此文档仅针对主题中的设置项进行讲解。 ::: @@ -39,7 +39,7 @@ spec: url: "https://github.com/halo-sigs/theme-foo/blob/main/LICENSE" ``` -:::tip +:::tip 保持配置名称一致 `settingName` 和 `configMapName` 必须同时配置,且可以自定义名称,但是 `settingName` 必须和 Setting 的 `metadata.name` 一致。 ::: @@ -82,7 +82,7 @@ spec: value: "double" ``` -:::tip +:::tip 保持 Setting 名称一致 Setting 资源的 `metadata.name` 必须和 `theme.yaml` 中的 `spec.settingName` 一致。 ::: @@ -134,6 +134,6 @@ npx @halo-dev/convert-theme-config-to-next settings 执行完成之后即可看到主题目录下生成了 `settings.2.0.yaml` 文件,重命名为 `settings.yaml` 即可。 -:::tip +:::tip 修改转换后的资源名称 转换完成之后需要修改 `metadata.name` 字段。 ::: diff --git a/docs/developer-guide/theme/static-resources.md b/docs/developer-guide/theme/static-resources.md index 7bd22609..40ad07d4 100644 --- a/docs/developer-guide/theme/static-resources.md +++ b/docs/developer-guide/theme/static-resources.md @@ -1,6 +1,6 @@ --- title: 静态资源 -description: 本文档介绍主题的静态资源的引用方法。 +description: 在 Halo 主题中通过 Thymeleaf 资源链接和 theme.assets() API 引用 templates/assets 下的样式、脚本与图片 --- 通过 [目录结构](./structure.md) 的讲解我们可以知道,目前主题的静态资源统一托管在 `/templates/assets/` 目录下,下面讲解一下如何在模板中使用,大致会分为两种引入方式。 @@ -24,7 +24,7 @@ description: 本文档介绍主题的静态资源的引用方法。 以上方式仅支持在 HTML 标签中使用,且必须使用 `@{}` 包裹才能渲染为正确的路径。如果需要在非 HTML 标签中得到正确的路径,我们提供了 `#theme.assets()` API。 -:::info[注意] +:::info 资源地址无需包含 /assets/ 需要注意的是,调用 `#theme.assets()` 的时候,资源地址不需要添加 `/assets/`。 ::: @@ -50,6 +50,6 @@ function loadScript(url) { ``` -:::info[提示] +:::info 在 JavaScript 中使用 Thymeleaf 关于在 JavaScript 中使用 Thymeleaf 语法可以参考 Thymeleaf 官方文档:[JavaScript inlining](https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#javascript-inlining) ::: diff --git a/docs/developer-guide/theme/structure.md b/docs/developer-guide/theme/structure.md index 1b2a7a6a..321312d0 100644 --- a/docs/developer-guide/theme/structure.md +++ b/docs/developer-guide/theme/structure.md @@ -1,11 +1,11 @@ --- title: 目录结构 -description: 主题的目录结构介绍 +description: 了解 Halo 主题项目的标准目录结构,以及模板、静态资源、主题配置、设置表单、预览图和 Console、用户中心 UI 扩展各自的存放位置 --- Halo 2.0 的主题基本目录结构如下: -```bash title="~/halo2-dev/themes/my-theme" +```tree title="~/halo2-dev/themes/my-theme" my-theme ├── templates/ │ ├── assets/ diff --git a/docs/developer-guide/theme/template-route-mapping.md b/docs/developer-guide/theme/template-route-mapping.md index f72852ff..b9cb11fe 100644 --- a/docs/developer-guide/theme/template-route-mapping.md +++ b/docs/developer-guide/theme/template-route-mapping.md @@ -79,7 +79,7 @@ customTemplates: 最终使用者即可在文章设置、独立页面设置、分类设置中选择自定义模板。 -:::info[提示] +:::info 自定义模板要求 1. 自定义模板与默认模板的功能相同,区别仅在于可以让使用者选择不同于默认模板风格的模板。 2. 自定义模板的文件名需要以 `.html` 结尾,且需要在 `/templates/` 目录下创建。 diff --git a/docs/developer-guide/theme/template-tag.md b/docs/developer-guide/theme/template-tag.md index 0394a7fe..84613065 100644 --- a/docs/developer-guide/theme/template-tag.md +++ b/docs/developer-guide/theme/template-tag.md @@ -51,6 +51,6 @@ Halo 为满足部分代码注入和模板扩展点的需求,提供了一些专 ``` -:::info[注意] +:::info 建议实现完整标签 为了保证 Halo 的功能完整性,建议主题开发者尽可能在主题中实现此标签。 ::: diff --git a/docs/developer-guide/theme/template-variables.md b/docs/developer-guide/theme/template-variables.md deleted file mode 100644 index 48dd15c6..00000000 --- a/docs/developer-guide/theme/template-variables.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: 模板编写 ---- - -此章节我们将详细介绍如何在主题中编写页面的模板,以下是 Halo 核心中支持的所有模板: - -```mdx-code-block -import DocCardList from '@theme/DocCardList'; - - -``` diff --git a/docs/developer-guide/theme/template-variables/_meta.json b/docs/developer-guide/theme/template-variables/_meta.json new file mode 100644 index 00000000..a8c2bca3 --- /dev/null +++ b/docs/developer-guide/theme/template-variables/_meta.json @@ -0,0 +1,13 @@ +[ + "index_", + "post", + "page", + "archives", + "tags", + "tag", + "categories", + "category", + "author", + "auth", + "error" +] diff --git a/docs/developer-guide/theme/template-variables/archives.md b/docs/developer-guide/theme/template-variables/archives.mdx similarity index 100% rename from docs/developer-guide/theme/template-variables/archives.md rename to docs/developer-guide/theme/template-variables/archives.mdx diff --git a/docs/developer-guide/theme/template-variables/auth.md b/docs/developer-guide/theme/template-variables/auth.md index ad5c3680..127eae5d 100644 --- a/docs/developer-guide/theme/template-variables/auth.md +++ b/docs/developer-guide/theme/template-variables/auth.md @@ -1,6 +1,6 @@ --- title: 认证页面 -description: 自定义登录、注册等页面 +description: 通过覆盖 Halo 内置 Thymeleaf 模板,自定义登录、注册、退出、密码重置与两步验证页面,并复用认证流程的布局、脚本和基础资源 --- Halo 2.20 重构了登录、注册等页面,现在支持通过主题自定义认证相关的页面。 @@ -13,7 +13,7 @@ Halo 2.20 重构了登录、注册等页面,现在支持通过主题自定义 要实现自定义登录、注册等模板,只需要在主题的 `templates` 目录中新建与 Halo 源码中 `application/src/main/resources/templates` 同名的模板文件即可,下面是 Halo 源码中的目录结构: -```bash +```tree ├── challenges │   └── two-factor │   ├── totp.html 两步验证页面 diff --git a/docs/developer-guide/theme/template-variables/author.md b/docs/developer-guide/theme/template-variables/author.mdx similarity index 100% rename from docs/developer-guide/theme/template-variables/author.md rename to docs/developer-guide/theme/template-variables/author.mdx diff --git a/docs/developer-guide/theme/template-variables/categories.md b/docs/developer-guide/theme/template-variables/categories.mdx similarity index 100% rename from docs/developer-guide/theme/template-variables/categories.md rename to docs/developer-guide/theme/template-variables/categories.mdx diff --git a/docs/developer-guide/theme/template-variables/category.md b/docs/developer-guide/theme/template-variables/category.md deleted file mode 100644 index 2a775540..00000000 --- a/docs/developer-guide/theme/template-variables/category.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: 分类归档 -description: category.html - /categories/:slug ---- - -import CategoryVo from "../vo/_CategoryVo.md" -import TagVo from "../vo/_TagVo.md" -import ContributorVo from "../vo/_ContributorVo.md"; -import ListedPostVo from "../vo/_ListedPostVo.md" - -用于根据分类列出所有文章的页面。 - -## 路由信息 - -- 模板路径:`/templates/category.html` -- 访问路径:`/categories/:slug` - -### 自定义模板 - -除了上面提到的 `category.html`,主题作者还可以添加多种形式的额外渲染模板,提供给用户选择,可以通过这个功能实现将网站上的文章内容进行领域划分,比如网站上同时存在新闻、文档、博客等分区,那么就可以利用这个功能提供多个模板,同时 Halo 还支持为分类设置文章渲染模板,详情可见[新建文章分类](../../../user-guide/posts.md#新建文章分类)。 - -定义方式为: - -```yaml title="theme.yaml" -customTemplates: - category: - - name: {name} - description: {description} - screenshot: {screenshot} - file: {file}.html -``` - -- `name`:模板名称 -- `description`:模板描述 -- `screenshot`:模板预览图 -- `file`:模板文件名,需要在 `/templates/` 目录下创建 - -示例: - -```yaml title="theme.yaml" -customTemplates: - category: - - name: 新闻 - description: 用于展示新闻分类下的文章 - screenshot: - file: category_news.html - - name: 博客 - description: 用于展示博客分类下的文章 - screenshot: - file: category_blog.html -``` - -:::info -需要注意,修改 theme.yaml 需要[重载主题配置](../../../user-guide/themes.md#重载主题配置)。 -::: - -## 变量 - -### category - -#### 变量类型 - -[#CategoryVo](#categoryvo) - -### posts - -#### 变量类型 - -[#UrlContextListResult\](#urlcontextlistresultlistedpostvo) - -#### 示例 - -```html title="/templates/category.html" -
-

-
    -
  • - -
  • -
- -
-``` - -### _templateId - -#### 变量值 - -`category` - -## 类型定义 - -### CategoryVo - - - -### TagVo - - - -### ContributorVo - - - -### ListedPostVo - - - -- [#CategoryVo](#categoryvo) -- [#TagVo](#tagvo) -- [#ContributorVo](#contributorvo) - -### UrlContextListResult\ - -```json title="UrlContextListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#ListedPostVo>", // 文章列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0, // 总页数 - "nextUrl": "string", // 下一页链接 - "prevUrl": "string" // 上一页链接 -} -``` - -- [#ListedPostVo](#listedpostvo) diff --git a/docs/developer-guide/theme/template-variables/category.mdx b/docs/developer-guide/theme/template-variables/category.mdx new file mode 100644 index 00000000..2e6aaeec --- /dev/null +++ b/docs/developer-guide/theme/template-variables/category.mdx @@ -0,0 +1,146 @@ +--- +title: 分类归档 +description: category.html - /categories/:slug +--- + +import CategoryVo from "../vo/_CategoryVo.md" +import TagVo from "../vo/_TagVo.md" +import ContributorVo from "../vo/_ContributorVo.md"; +import ListedPostVo from "../vo/_ListedPostVo.md" + +用于根据分类列出所有文章的页面。 + +## 路由信息 + +- 模板路径:`/templates/category.html` +- 访问路径:`/categories/:slug` + +### 自定义模板 + +除了上面提到的 `category.html`,主题作者还可以添加多种形式的额外渲染模板,提供给用户选择,可以通过这个功能实现将网站上的文章内容进行领域划分,比如网站上同时存在新闻、文档、博客等分区,那么就可以利用这个功能提供多个模板,同时 Halo 还支持为分类设置文章渲染模板,详情可见[新建文章分类](../../../guide/use/posts.md#新建文章分类)。 + +定义方式为: + +```yaml title="theme.yaml" +customTemplates: + category: + - name: {name} + description: {description} + screenshot: {screenshot} + file: {file}.html +``` + +- `name`:模板名称 +- `description`:模板描述 +- `screenshot`:模板预览图 +- `file`:模板文件名,需要在 `/templates/` 目录下创建 + +示例: + +```yaml title="theme.yaml" +customTemplates: + category: + - name: 新闻 + description: 用于展示新闻分类下的文章 + screenshot: + file: category_news.html + - name: 博客 + description: 用于展示博客分类下的文章 + screenshot: + file: category_blog.html +``` + +:::info 修改 theme.yaml 后重载配置 +需要注意,修改 theme.yaml 需要[重载主题配置](../../../guide/use/themes.md#重载主题配置)。 +::: + +## 变量 + +### category + +#### 变量类型 + +[#CategoryVo](#categoryvo) + +### posts + +#### 变量类型 + +[#UrlContextListResult\](#urlcontextlistresultlistedpostvo) + +#### 示例 + +```html title="/templates/category.html" +
+

+
    +
  • + +
  • +
+ +
+``` + +### _templateId + +#### 变量值 + +`category` + +## 类型定义 + +### CategoryVo + + + +### TagVo + + + +### ContributorVo + + + +### ListedPostVo + + + +- [#CategoryVo](#categoryvo) +- [#TagVo](#tagvo) +- [#ContributorVo](#contributorvo) + +### UrlContextListResult\ + +```json title="UrlContextListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#ListedPostVo>", // 文章列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0, // 总页数 + "nextUrl": "string", // 下一页链接 + "prevUrl": "string" // 上一页链接 +} +``` + +- [#ListedPostVo](#listedpostvo) diff --git a/docs/developer-guide/theme/template-variables/error.md b/docs/developer-guide/theme/template-variables/error.md index d5211ece..45576585 100644 --- a/docs/developer-guide/theme/template-variables/error.md +++ b/docs/developer-guide/theme/template-variables/error.md @@ -1,6 +1,6 @@ --- title: 错误页面 -description: 关于错误页面的模板变量 +description: 配置 Halo 主题的 404、4xx、500、5xx 与默认错误模板,了解状态码对应的模板匹配顺序及 error 变量字段和渲染方式 --- ## 路由信息 @@ -8,7 +8,7 @@ description: 关于错误页面的模板变量 - 模板路径:`/templates/error/{404,4xx,500,5xx,error}.html` - 访问路径:无固定访问路径,由异常决定 -:::info[提示] +:::info 错误模板匹配顺序 错误页面的可使用模板由状态码决定,例如 404 状态码对应的模板为 `/templates/error/404.html` 或者 `/templates/error/4xx.html`。也可以使用 `/templates/error/error.html` 作为默认模板。 识别顺序如下: diff --git a/docs/developer-guide/theme/template-variables/index.md b/docs/developer-guide/theme/template-variables/index.md new file mode 100644 index 00000000..4f1bdecf --- /dev/null +++ b/docs/developer-guide/theme/template-variables/index.md @@ -0,0 +1,7 @@ +--- +title: 模板编写 +description: 介绍 Halo 主题中首页、文章、单页面、归档、分类、标签、作者、认证和错误页面的模板路径、访问路由、可用变量及渲染示例。 +overview: true +--- + +此章节我们将详细介绍如何在主题中编写页面的模板,以下是 Halo 核心中支持的所有模板: diff --git a/docs/developer-guide/theme/template-variables/index_.md b/docs/developer-guide/theme/template-variables/index_.md deleted file mode 100644 index 0cbe8141..00000000 --- a/docs/developer-guide/theme/template-variables/index_.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: 首页 -description: index.html - / ---- - -import CategoryVo from "../vo/_CategoryVo.md" -import TagVo from "../vo/_TagVo.md" -import ContributorVo from "../vo/_ContributorVo.md"; -import ListedPostVo from "../vo/_ListedPostVo.md" - -网站的首页模板,在这个模板中默认设置了最新文章列表的变量,也可以通过调用 [Finder API](../finder-apis.md) 和 [主题设置](../settings.md) 来展示其他数据。 - -## 路由信息 - -- 模板路径:`/templates/index.html` -- 访问路径:`/` - -## 变量 - -### posts - -#### 变量类型 - -[#UrlContextListResult\](#urlcontextlistresultlistedpostvo) - -#### 示例 - -```html title="/templates/index.html" -
-
    -
  • - -
  • -
- -
-``` - -### _templateId - -#### 变量值 - -`index` - -## 类型定义 - -### CategoryVo - - - -### TagVo - - - -### ContributorVo - - - -### ListedPostVo - - - -- [#CategoryVo](#categoryvo) -- [#TagVo](#tagvo) -- [#ContributorVo](#contributorvo) - -### UrlContextListResult\ - -```json title="UrlContextListResult" -{ - "page": 0, // 当前页码 - "size": 0, // 每页条数 - "total": 0, // 总条数 - "items": "List<#ListedPostVo>", // 文章列表数据 - "first": true, // 是否为第一页 - "last": true, // 是否为最后一页 - "hasNext": true, // 是否有下一页 - "hasPrevious": true, // 是否有上一页 - "totalPages": 0, // 总页数 - "nextUrl": "string", // 下一页链接 - "prevUrl": "string" // 上一页链接 -} -``` - -- [#ListedPostVo](#listedpostvo) diff --git a/docs/developer-guide/theme/template-variables/index_.mdx b/docs/developer-guide/theme/template-variables/index_.mdx new file mode 100644 index 00000000..2ee97d9a --- /dev/null +++ b/docs/developer-guide/theme/template-variables/index_.mdx @@ -0,0 +1,100 @@ +--- +title: 首页 +description: 编写 Halo 主题的 index.html 首页模板,使用 posts 分页变量渲染最新文章列表、上下页链接,并了解首页路由与模板标识 +--- + +import CategoryVo from "../vo/_CategoryVo.md" +import TagVo from "../vo/_TagVo.md" +import ContributorVo from "../vo/_ContributorVo.md"; +import ListedPostVo from "../vo/_ListedPostVo.md" + +网站的首页模板,在这个模板中默认设置了最新文章列表的变量,也可以通过调用 [Finder API](../finder-apis.md) 和 [主题设置](../settings.md) 来展示其他数据。 + +## 路由信息 + +- 模板路径:`/templates/index.html` +- 访问路径:`/` + +## 变量 + +### posts + +#### 变量类型 + +[#UrlContextListResult\](#urlcontextlistresultlistedpostvo) + +#### 示例 + +```html title="/templates/index.html" +
+
    +
  • + +
  • +
+ +
+``` + +### _templateId + +#### 变量值 + +`index` + +## 类型定义 + +### CategoryVo + + + +### TagVo + + + +### ContributorVo + + + +### ListedPostVo + + + +- [#CategoryVo](#categoryvo) +- [#TagVo](#tagvo) +- [#ContributorVo](#contributorvo) + +### UrlContextListResult\ + +```json title="UrlContextListResult" +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#ListedPostVo>", // 文章列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0, // 总页数 + "nextUrl": "string", // 下一页链接 + "prevUrl": "string" // 上一页链接 +} +``` + +- [#ListedPostVo](#listedpostvo) diff --git a/docs/developer-guide/theme/template-variables/page.md b/docs/developer-guide/theme/template-variables/page.md deleted file mode 100644 index 56cac963..00000000 --- a/docs/developer-guide/theme/template-variables/page.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: 单页面 -description: page.html - /:slug ---- - -import SinglePageVo from "../vo/_SinglePageVo.md" -import ContributorVo from "../vo/_ContributorVo.md" -import ContentVo from "../vo/_ContentVo.md" - -页面与文章类似,同样包含页面标题和富文本形式的页面内容。与文章不同的是页面无法设置所属分类和标签信息,一般用于站点中单一展示功能的页面,例如常见的站点关于页面、联系我们页面等。 - -## 路由信息 - -- 模板路径:`/templates/page.html` -- 访问路径:`/:slug` - -### 自定义模板 - -除了上面提到的 `page.html`,主题作者还可以添加多种形式的额外渲染模板,提供给用户选择,此举可以丰富网站的使用类型。 - -定义方式为: - -```yaml title="theme.yaml" -customTemplates: - page: - - name: {name} - description: {description} - screenshot: {screenshot} - file: {file}.html -``` - -- `name`:模板名称 -- `description`:模板描述 -- `screenshot`:模板预览图 -- `file`:模板文件名,需要在 `/templates/` 目录下创建 - -示例: - -```yaml title="theme.yaml" -customTemplates: - page: - - name: 关于公司 - description: 用于展示公司的一些信息 - screenshot: - file: page_about.html -``` - -:::info -需要注意,修改 theme.yaml 需要[重载主题配置](../../../user-guide/themes.md#重载主题配置)。 -::: - -## 变量 - -### singlePage - -#### 变量类型 - -[#SinglePageVo](#singlepagevo) - -#### 示例 - -```html title="/templates/page.html" -
-

-
-
-``` - -### _templateId - -#### 变量值 - -`page` - -## 类型定义 - -### SinglePageVo - - - -- [#ContentVo](#contentvo) -- [#ContributorVo](#contributorvo) - -### ContentVo - - - -### ContributorVo - - diff --git a/docs/developer-guide/theme/template-variables/page.mdx b/docs/developer-guide/theme/template-variables/page.mdx new file mode 100644 index 00000000..7402b73d --- /dev/null +++ b/docs/developer-guide/theme/template-variables/page.mdx @@ -0,0 +1,90 @@ +--- +title: 单页面 +description: 编写 Halo 主题的 page.html 单页面模板,使用 singlePage 变量渲染标题和正文,并通过 theme.yaml 定义可供用户选择的自定义页面模板 +--- + +import SinglePageVo from "../vo/_SinglePageVo.md" +import ContributorVo from "../vo/_ContributorVo.md" +import ContentVo from "../vo/_ContentVo.md" + +页面与文章类似,同样包含页面标题和富文本形式的页面内容。与文章不同的是页面无法设置所属分类和标签信息,一般用于站点中单一展示功能的页面,例如常见的站点关于页面、联系我们页面等。 + +## 路由信息 + +- 模板路径:`/templates/page.html` +- 访问路径:`/:slug` + +### 自定义模板 + +除了上面提到的 `page.html`,主题作者还可以添加多种形式的额外渲染模板,提供给用户选择,此举可以丰富网站的使用类型。 + +定义方式为: + +```yaml title="theme.yaml" +customTemplates: + page: + - name: {name} + description: {description} + screenshot: {screenshot} + file: {file}.html +``` + +- `name`:模板名称 +- `description`:模板描述 +- `screenshot`:模板预览图 +- `file`:模板文件名,需要在 `/templates/` 目录下创建 + +示例: + +```yaml title="theme.yaml" +customTemplates: + page: + - name: 关于公司 + description: 用于展示公司的一些信息 + screenshot: + file: page_about.html +``` + +:::info 修改 theme.yaml 后重载配置 +需要注意,修改 theme.yaml 需要[重载主题配置](../../../guide/use/themes.md#重载主题配置)。 +::: + +## 变量 + +### singlePage + +#### 变量类型 + +[#SinglePageVo](#singlepagevo) + +#### 示例 + +```html title="/templates/page.html" +
+

+
+
+``` + +### _templateId + +#### 变量值 + +`page` + +## 类型定义 + +### SinglePageVo + + + +- [#ContentVo](#contentvo) +- [#ContributorVo](#contributorvo) + +### ContentVo + + + +### ContributorVo + + diff --git a/docs/developer-guide/theme/template-variables/post.md b/docs/developer-guide/theme/template-variables/post.md deleted file mode 100644 index 0be11f4c..00000000 --- a/docs/developer-guide/theme/template-variables/post.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: 文章 -description: post.html - /archives/:slug ---- - -import CategoryVo from "../vo/_CategoryVo.md" -import TagVo from "../vo/_TagVo.md" -import ContentVo from "../vo/_ContentVo.md" -import ContributorVo from "../vo/_ContributorVo.md" -import PostVo from "../vo/_PostVo.md" - -文章详情页面的模板。 - -## 路由信息 - -- 模板路径:`/templates/post.html` -- 访问路径:默认为 `/archives/:slug`,用户可手动更改为其他路由形式,可参考:[主题路由设置](../../../user-guide/settings.md#主题路由设置) - -### 自定义模板 - -除了上面提到的 `post.html`,主题作者还可以添加多种形式的额外渲染模板,提供给用户选择,此举可以丰富网站的使用类型,用户设置方式可参考 [文章设置](../../../user-guide/posts.md#文章设置)。 - -定义方式为: - -```yaml title="theme.yaml" -customTemplates: - post: - - name: {name} - description: {description} - screenshot: {screenshot} - file: {file}.html -``` - -- `name`:模板名称 -- `description`:模板描述 -- `screenshot`:模板预览图 -- `file`:模板文件名,需要在 `/templates/` 目录下创建 - -示例: - -```yaml title="theme.yaml" -customTemplates: - post: - - name: 文档 - description: 文档类型的文章 - screenshot: - file: post_documentation.html -``` - -:::info -需要注意,修改 theme.yaml 需要[重载主题配置](../../../user-guide/themes.md#重载主题配置)。 -::: - -## 变量 - -### post - -#### 变量类型 - -[#PostVo](#postvo) - -#### 示例 - -```html title="/templates/post.html" -
-

-
-
-``` - -### _templateId - -#### 变量值 - -`post` - -## 类型定义 - -### CategoryVo - - - -### TagVo - - - -### ContributorVo - - - -### ContentVo - - - -### PostVo - - diff --git a/docs/developer-guide/theme/template-variables/post.mdx b/docs/developer-guide/theme/template-variables/post.mdx new file mode 100644 index 00000000..cf293487 --- /dev/null +++ b/docs/developer-guide/theme/template-variables/post.mdx @@ -0,0 +1,97 @@ +--- +title: 文章 +description: post.html - /archives/:slug +--- + +import CategoryVo from "../vo/_CategoryVo.md" +import TagVo from "../vo/_TagVo.md" +import ContentVo from "../vo/_ContentVo.md" +import ContributorVo from "../vo/_ContributorVo.md" +import PostVo from "../vo/_PostVo.md" + +文章详情页面的模板。 + +## 路由信息 + +- 模板路径:`/templates/post.html` +- 访问路径:默认为 `/archives/:slug`,用户可手动更改为其他路由形式,可参考:[主题路由设置](../../../guide/use/settings.md#主题路由设置) + +### 自定义模板 + +除了上面提到的 `post.html`,主题作者还可以添加多种形式的额外渲染模板,提供给用户选择,此举可以丰富网站的使用类型,用户设置方式可参考 [文章设置](../../../guide/use/posts.md#文章设置)。 + +定义方式为: + +```yaml title="theme.yaml" +customTemplates: + post: + - name: {name} + description: {description} + screenshot: {screenshot} + file: {file}.html +``` + +- `name`:模板名称 +- `description`:模板描述 +- `screenshot`:模板预览图 +- `file`:模板文件名,需要在 `/templates/` 目录下创建 + +示例: + +```yaml title="theme.yaml" +customTemplates: + post: + - name: 文档 + description: 文档类型的文章 + screenshot: + file: post_documentation.html +``` + +:::info 修改 theme.yaml 后重载配置 +需要注意,修改 theme.yaml 需要[重载主题配置](../../../guide/use/themes.md#重载主题配置)。 +::: + +## 变量 + +### post + +#### 变量类型 + +[#PostVo](#postvo) + +#### 示例 + +```html title="/templates/post.html" +
+

+
+
+``` + +### _templateId + +#### 变量值 + +`post` + +## 类型定义 + +### CategoryVo + + + +### TagVo + + + +### ContributorVo + + + +### ContentVo + + + +### PostVo + + diff --git a/docs/developer-guide/theme/template-variables/tag.md b/docs/developer-guide/theme/template-variables/tag.mdx similarity index 100% rename from docs/developer-guide/theme/template-variables/tag.md rename to docs/developer-guide/theme/template-variables/tag.mdx diff --git a/docs/developer-guide/theme/template-variables/tags.md b/docs/developer-guide/theme/template-variables/tags.md deleted file mode 100644 index dad701f6..00000000 --- a/docs/developer-guide/theme/template-variables/tags.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: 文章标签集合 -description: tags.html - /tags ---- - -import TagVo from '../vo/_TagVo.md' - -用于列出所有文章标签的页面,可以用于实现标签墙等功能。 - -## 路由信息 - -- 模板路径:`/templates/tags.html` -- 访问路径:`/tags` - -## 变量 - -### tags - -#### 变量类型 - -List\<[#TagVo](#tagvo)\> - -#### 示例 - -```html title="/templates/tags.html" -
    -
  • -
-``` - -### _templateId - -#### 变量值 - -`tags` - -## 类型定义 - -### TagVo - - diff --git a/docs/developer-guide/theme/template-variables/tags.mdx b/docs/developer-guide/theme/template-variables/tags.mdx new file mode 100644 index 00000000..26e29214 --- /dev/null +++ b/docs/developer-guide/theme/template-variables/tags.mdx @@ -0,0 +1,41 @@ +--- +title: 文章标签集合 +description: 编写 Halo 主题的 tags.html 标签集合模板,使用 tags 变量读取全部文章标签并渲染标签墙,同时了解页面路由和模板标识 +--- + +import TagVo from '../vo/_TagVo.md' + +用于列出所有文章标签的页面,可以用于实现标签墙等功能。 + +## 路由信息 + +- 模板路径:`/templates/tags.html` +- 访问路径:`/tags` + +## 变量 + +### tags + +#### 变量类型 + +List\<[#TagVo](#tagvo)\> + +#### 示例 + +```html title="/templates/tags.html" +
    +
  • +
+``` + +### _templateId + +#### 变量值 + +`tags` + +## 类型定义 + +### TagVo + + diff --git a/docs/developer-guide/theme/ui-plugin.md b/docs/developer-guide/theme/ui-plugin.md index d03657fb..0299aa85 100644 --- a/docs/developer-guide/theme/ui-plugin.md +++ b/docs/developer-guide/theme/ui-plugin.md @@ -11,7 +11,7 @@ description: 通过主题扩展 Console 和 UC 界面 将 UI 项目放在主题根目录的 `ui-plugin` 目录中: -```text +```tree theme-root/ ├── templates/ ├── theme.yaml diff --git a/docs/getting-started/first-post.md b/docs/getting-started/first-post.md deleted file mode 100644 index 3c370d91..00000000 --- a/docs/getting-started/first-post.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: 第一篇文章 -description: Halo 安装后写第一篇文章:进入控制台、编辑发布、分类标签与前台访问。 ---- - - -