Full Changelog: 1.4.0...v1.4.1
feat: 新增可配置的AI自动生成句子定时任务
-
新增aiCron配置项支持自定义Cron表达式
-
将固定凌晨2点的AI生成任务改为动态可配置
-
监听配置变更自动重新加载定时任务
-
提供常用预设时间选项方便快速选择
Generated from v1.4.1
Full Changelog: 1.4.0...v1.4.1
新增aiCron配置项支持自定义Cron表达式
将固定凌晨2点的AI生成任务改为动态可配置
监听配置变更自动重新加载定时任务
提供常用预设时间选项方便快速选择
Generated from v1.4.1
2.1.0。fa-solid fa-location-arrow。本版本带来两项核心改进——Steam 数据缓存定时预热架构与游戏元数据补全,并对公开 REST API 的响应结构做了规范化调整。
自本版本起,公开 REST API(/profile、/games、/recent、/badges)的响应字段统一从 Steam 原始的 snake_case 改为 camelCase,并裁剪了内部字段、补充了格式化字段(如 headerImageUrl、delisted、playtimeFormatted 等)。已通过 REST API 调用这些接口的主题或自定义前端需要同步更新 ,Finder API 不受影响。
完整字段定义请参考 README - REST API 与 使用指南 - API 接口,或 Halo 后台的 OpenAPI(Swagger)文档。
IStoreBrowseService/GetItems 为库外游戏或 Steam API 暂未返回名称的游戏补全本地化名称、真实封面与商店链接(新发布游戏的封面带内容哈希,已无法靠 AppID 拼接)appdetails 接口 success:false 的处理:先用 GetItems 的 visible=false 二次确认是否真的不可见,避免接口限流时把正常游戏误判为下架headerImageUrl 行为调整:无真实封面时改为返回 null(旧版返回 appId 拼接路径,对新游戏本就是 404)。appdetails 的 success 字段语义、修正 header_image 示例地址,完善 GetItems 行为说明AstraHub 星图库采用 REST API + WebSocket 的方式对外提供数据。
主题页面建议先请求状态接口,用于判断 AstraHub 插件是否已启用、星图库数据是否可用。
const statusRes = await fetch('/apis/anonymous.astrahub.halo.run/v1alpha1/star-gallery/-/status', {
method: 'GET',
credentials: 'same-origin',
headers: {
Accept: 'application/json',
},
});
if (!statusRes.ok) {
// 插件未安装、未启用,或接口不可用
}
const status = await statusRes.json();
if (!status.available) {
// 插件已启用,但星图库数据尚不可用
}
返回示例:
{
"available": true,
"generatedAt": "2026-06-28T00:00:00Z",
"relationCount": 64
}
状态可用后,请求完整快照接口。
const snapshotRes = await fetch('/apis/anonymous.astrahub.halo.run/v1alpha1/star-gallery/-/snapshot', {
method: 'GET',
credentials: 'same-origin',
headers: {
Accept: 'application/json',
},
});
const snapshot = await snapshotRes.json();
快照主要字段:
snapshot.profile // 本站 / 本星系信息snapshot.sectors // 星系联盟卡片snapshot.posts // 星际资讯每个星系卡片会提供 planetsUrl,主题可以直接请求该地址获取星系成员。
const planetsRes = await fetch(`${sector.planetsUrl}?page=1&pageSize=24`, {
method: 'GET',
credentials: 'same-origin',
headers: {
Accept: 'application/json',
},
});
const planets = await planetsRes.json();
返回示例:
{
"items": [],
"page": 1,
"pageSize": 24,
"total": 98,
"hasMore": true
}
主题可选接入 WebSocket。当 AstraHub 同步到新数据时,前台可以自动刷新星图库。
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(
`${protocol}//${location.host}/apis/anonymous.astrahub.halo.run/v1alpha1/astrahub/ws/star-gallery`
);
ws.addEventListener('message', (event) => {
const snapshot = JSON.parse(event.data);
// 使用新的 snapshot 重新渲染页面
});
posts 中推荐按以下字段处理:
post.url // 文章类资讯的原文地址post.siteUrl // 星球 / 站点地址post.type // 资讯类型推荐逻辑:
const isArticle = ['rss', 'article', 'post'].includes(
String(post.type || '').toLowerCase()
);
if (isArticle && post.url) {
// 显示:打开原文
// 跳转:post.url
}
if (!isArticle && post.siteUrl) {
// 显示:定位星球
// 跳转:post.siteUrl
}
如果状态接口请求失败,主题可提示:
需要安装或启动 AstraHub 星链插件
请在 Halo 后台启用 AstraHub 星链插件,并同步星图库数据。
QQ 交流群:1040502725
主题设置新增【菜单栏】设置选项
顶部导航栏支持新增自定义按钮
预加载页面载入页逻辑优化
修复了部分已知 bug
对博客内容页面进行了适配。
修复了一些bug
feat(theme): 添加产品管理和项目集功能支持
Full Changelog: 1.0.0-beta5...1.0.0-beta6
Generated from 1.0.0-beta6
refactor(portfolio): 改造默认页面路由及项目内容渲染逻辑
feat(menu): 为菜单项添加 Iconify 图标支持功能
Full Changelog: 1.0.0-beta4...1.0.0-beta5
Generated from 1.0.0-beta5
新增开发者调试日志
修复了一些bug
新增首页信息流按发布时间混入文章卡片
增加了全站 pjax 无刷新加载
删除无用调试事件输出
为多个 Halo 站点提供“身份中心 + 接入站”的单点登录能力。一个站点作为身份中心统一承接登录与授权,其它 Halo 站点作为接入站跳转到身份中心完成认证,并在回调后建立本站登录态、绑定本地用户、同步用户资料和映射本地角色。
| 地址 | 账号/密码 |
|---|---|
| 演示站 | demo/demo123. |
halo-plugin-sso 适合一组 Halo 站点共用同一套账号体系的场景,例如主站、文档站、社区站、资源站分别部署,但希望用户只使用一个中心账号登录。
插件采用 Authorization Code + PKCE 的登录闭环:
当前能力包括:
displayName、description、logo、website 会从身份中心站点基础信息同步。>= 2.25.0https://auth.example.comhttps://blog.example.com生产环境建议使用 HTTPS。开发调试时可在插件设置中临时开启“开发环境允许 HTTP localhost”,但别把这个当生产方案,真上生产这么干就有点硬莽了。
完整接入至少需要两个 Halo 站点:
下面用:
https://auth.example.comhttps://blog.example.com在身份中心站点和每个接入站点都安装并启用本插件。
构建插件:
./gradlew build
构建完成后,插件 JAR 位于:
build/libs/
将 JAR 上传到 Halo 后台插件管理并启用。
进入身份中心站点后台:
控制台 -> 插件 -> 统一身份认证 -> 设置
在“基础设置”中配置:
| 配置项 | 建议值 | 说明 |
|---|---|---|
| 运行模式 | 身份中心模式 | 当前站点负责统一登录和授权 |
| 中心标准角色 | subscriber、author、editor 等 |
只有选中的中心角色会下发给接入站 |
| 要求邮箱已验证 | 开启 | 未验证邮箱的中心用户不允许跨站登录 |
然后进入插件控制台页面:
控制台 -> 系统工具 -> 统一身份认证
在“接入站”区域创建一个接入站:
| 字段 | 示例 | 说明 |
|---|---|---|
| 接入站名称 | 我的博客 | 控制台展示用 |
| 站点地址 | https://blog.example.com |
接入站外部访问地址 |
| Redirect URI | https://blog.example.com/apis/public.sso.muyin.site/v1alpha1/client/callback |
接入站回调地址 |
| 启用状态 | 开启 | 关闭后该接入站不能继续登录 |
创建成功后会得到:
Client IDClient SecretClient Secret 只展示一次,复制保存好。这个东西丢了就重新生成或重建接入站,别指望页面再掏出来给你看。
进入接入站后台:
控制台 -> 插件 -> 统一身份认证 -> 设置
在“基础设置”中配置:
| 配置项 | 示例 | 说明 |
|---|---|---|
| 运行模式 | 接入站模式 | 当前站点负责跳转登录和建立本地登录态 |
| 身份中心地址 | https://auth.example.com |
身份中心站点外部地址 |
| Client ID | 从身份中心复制 | 对应身份中心创建的接入站 |
| Client Secret | 从身份中心复制 | 服务端换取 Token 使用 |
| 默认本地角色 | guest 或其它低权限角色 |
未命中角色映射时使用 |
| 登录时同步用户资料 | 按需开启 | 同步邮箱、用户名、展示名称和头像 |
| 自动跳转 SSO 登录 | 按需开启 | 开启后访问 /login 自动跳转身份中心 |
接入站保存配置后,插件会定期从身份中心同步认证提供者信息。同步来源是身份中心 Halo 系统基础设置:
| AuthProvider 字段 | 身份中心来源 |
|---|---|
displayName |
站点标题 |
description |
站点副标题 |
logo |
站点 Logo,未配置时使用 favicon,再未配置时使用插件默认图标 |
website |
站点外部访问地址 |
如果身份中心临时不可用,接入站会保留上一次同步成功的本地认证提供者信息,不会把登录入口改成空壳。
进入接入站插件控制台:
控制台 -> 系统工具 -> 统一身份认证 -> 角色映射
创建映射关系:
| 中心角色 | 本地角色 | 说明 |
|---|---|---|
subscriber |
guest |
普通用户 |
author |
contributor |
投稿或作者 |
editor |
editor |
编辑 |
规则说明:
适合希望接入站完全交给身份中心登录的场景。
/login。如果需要临时访问接入站本地登录页,可以访问:
/login?sso_local=1
适合接入站需要同时保留本地登录和 SSO 登录的场景。
muyin-sso。/login。手动入口由插件声明的 AuthProvider 提供:
metadata.name: muyin-sso
authenticationUrl: /apis/public.sso.muyin.site/v1alpha1/client/login
接入站登录入口支持 return_url 参数:
/apis/public.sso.muyin.site/v1alpha1/client/login?return_url=/archives/demo
登录完成后会回到指定地址。插件会校验返回地址,只允许本站相对路径,避免被拿去做开放重定向。这个地方不校验就等于给钓鱼链接递刀,不能省。
插件会记录接入站登录过程中的关键结果:
在插件控制台的“审计日志”中可以:
success / failure插件设置中可以开启“自动清理过期审计日志”。默认关闭,开启后后台任务会定期按保留天数清理过期日志。
接入站和身份中心之间主要使用以下接口。
身份中心 OAuth API:
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/apis/public.sso.muyin.site/v1alpha1/oauth/authorize |
OAuth 授权端点 |
POST |
/apis/public.sso.muyin.site/v1alpha1/oauth/token |
使用授权码换取 Token |
GET |
/apis/public.sso.muyin.site/v1alpha1/oauth/userinfo |
获取中心用户信息和中心角色 |
GET |
/apis/public.sso.muyin.site/v1alpha1/oauth/notice |
授权拒绝提示页 |
公共辅助 API:
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/apis/public.sso.muyin.site/v1alpha1/metadata |
获取身份中心认证提供者元信息 |
GET |
/apis/public.sso.muyin.site/v1alpha1/roles/list |
获取身份中心可下发角色列表 |
GET |
/apis/public.sso.muyin.site/v1alpha1/client/login |
接入站发起 SSO 登录 |
GET |
/apis/public.sso.muyin.site/v1alpha1/client/callback |
接入站处理 SSO 回调 |
Console API 主要由插件后台页面调用,覆盖接入站管理、角色映射、用户绑定、审计日志和设置读取。开发调试时可查看:
api-docs/openapi/v3_0/ssoApis.json
优先检查身份中心中登记的 Redirect URI 是否和接入站实际地址完全一致。协议、域名、端口、路径都要一致。
标准回调路径为:
https://接入站域名/apis/public.sso.muyin.site/v1alpha1/client/callback
/login 没有跳转身份中心检查接入站插件设置:
如果你关闭了自动跳转,需要在 Halo 认证提供者中启用 muyin-sso,然后在默认登录页手动选择“统一身份认证”。
检查 Halo 认证提供者配置中是否启用了 muyin-sso。插件声明 AuthProvider 不等于 Halo 一定展示它,展示与否由 Halo 的认证提供者启用状态控制。
接入站会从身份中心 /metadata 同步认证提供者信息。请检查:
https 地址。检查三处:
这是当前设计,不是 bug。插件不做统一退出登录。身份中心退出只影响身份中心本地会话,不会强制接入站本地会话失效。
本地开发依赖:
启动 Halo 开发服务:
./gradlew haloServer
前端开发:
cd ui
pnpm install
pnpm dev
运行测试:
./gradlew test
构建插件:
./gradlew build
生成的插件 JAR 在:
build/libs/
GPL-3.0 © Lywq
为 Halo 提供现代化评论体验,支持富文本评论、图片上传、验证码、表情回应、举报、黑灰名单、精选/置顶、评论徽章与 AI 助手。
测试版本,不建议用于生产环境,请多多反馈。
Generated from v2.3.20
fix: 修复文章无标题标签或纯图片时内容溢出问题
h1/h2/h3 标题标签时(如纯图片文章、纯文字无标题文章),导致的内容区域布局溢出问题。本次更新(v5.1.1)修复了友链自助提交功能中插件全局函数 ID 不匹配及 linkPageUrl 为空校验失败的问题,优化了体验
同时全面重写了自定义页面
适配了更多插件
更新功能,对部分插件进行适配,修复已知bug
功能逐步完善中
二次元少女风格的 Halo 2.x 博客主题。本次为问题修复版本。
下载下方的 moesora.zip,进入 Halo 后台 主题 → 主题管理:首次使用选「安装主题」上传;已安装的通过「升级」上传该压缩包即可。
Halo 2.14.0 及以上版本。
完整说明见仓库 README。
FALSE_POSITIVE 状态,“仅通过"的记录显示为"误报通过”,不显示"通过/拒绝"按钮processComment() 始终调用 preFilterService.check(),用户已确认为误报的评论会被再次拦截。新增 processFalsePositive() 方法跳过前置过滤和去重检查hasExistingReply() 找到已有的 FILTERED→PENDING 记录导致 AI 回复无法生成。processFalsePositive() 复用已有记录,不经过去重检查processComment() 同步等待 AI 生成完成,HTTP 请求长时间不返回。改为 .subscribe() 异步执行,API 立即返回status=PASS, published=false, reply="",导致显示"通过/拒绝"按钮且内容为空。改为 status=FALSE_POSITIVEextractChoice 无匹配时返回原始文本 — AI 返回非预期文本时被误判为违规类别。改为返回空字符串触发安全拦截approveOriginalComment 缺少乐观锁重试 — 并发更新 Comment/Reply 时可能静默失败。添加 Retry.backoff(3, 100ms) 重试checkBlockedCommenters 重复代码 — FilterService 新增 isCommenterBlocked(commentName) 公共方法,AiReplyOrchestrator 改为调用它batchLoading 状态,操作期间禁用按钮Generated from v2.3.19
更新较多,建议全新安装此主题!
v1.0.9: 功能增强与大规模优化
详情请参阅更新日志(在 GitHub 上查看)。
Please refer to Changelog (view on GitHub) for details.
howiehz-higan-zh-hans.zip为简体中文默认安装包,仅包含.br。
howiehz-higan-zh-hans-tiny.zip为简体中文轻量安装包,移除默认字体资源,默认 logo 资源将替换为 1x1 占位图,不包含预压缩文件,且不注入 Mermaid / instant.page 运行时。
howiehz-higan-zh-hans-full-precompressed.zip为简体中文完整预压缩包,包含.gz/.br/.zst。
howiehz-higan-en.zipis the English default package with.bronly.
howiehz-higan-en-tiny.zipis the English tiny package that removes the default font assets, replaces the default logo asset with a 1x1 placeholder image, omits precompressed assets, and excludes Mermaid / instant.page runtime injection.
howiehz-higan-en-full-precompressed.zipis the English full precompressed package with.gz/.br/.zst.
Generated from v2.3.18
二次元少女风格的 Halo 2.x 博客主题,首个公开发布版本 (。・ω・。)ノ♡
主色为樱花粉 #f9a8d4,可在后台自由更换。
下载下方的 moesora.zip,进入 Halo 后台 主题 → 主题管理 → 安装主题,上传该压缩包并启用即可。
更新时下载新版 moesora.zip,通过 主题 → 升级 上传安装包。
Halo 2.14.0 及以上版本。
v1.0.8: 接入新 logo,优化了一些SEO问题
classifyWithChoice 和 classifyWithChat 均使用了 GenerateTextRequest.Builder.system() 方法,而该方法在当前 AI Foundation 版本中不被支持或导致运行时错误,导致所有评论均被拦截并显示"AI分类服务不可用,安全拦截"。现改为将 system prompt 合并到 user prompt 中,与可用的 chat() 方法保持一致的调用方式classifyWithChoice NPE — .map() 返回 null 时触发 Reactor 内部 NullPointerException,改为 .flatMap() + Mono.empty() 正确触发 fallbackAiFoundationDelegate、AiFoundationClient、CommentPreFilterService 中增加关键诊断日志(分类开始、fallback 触发、分类结果、异常详情),便于排查分类链路问题OutputSpec.choice 结构化输出不被模型支持时,自动退回到普通 chat 调用并从响应文本中提取分类值(classifyWithChat fallback)fix(seo): 修复开启 OG/JSON-LD 时文章页 500 错误 (v1.0.6)
此版本修改了区域(Region)的默认值,建议在更新插件之后验证存储策略是否正常,如果有异常建议调整 Region 字段值。
Full Changelog: v1.15.0...v1.16.0
Generated from v1.16.0
release: v1.0.5
功能增强:
Bug 修复:
无障碍:
i18n:
Generated from v2.3.17
approved 置为 false,进入待审核队列,前端不再展示该评论AiFoundationDelegate 类,AiFoundationClient 不再直接引用 AI Foundation 类,修复未安装 AI Foundation 时插件无法启动的问题(NoClassDefFoundError)preFilterEnabled 从 false 迁移为 true(新默认值)BeanDefinitionStoreException: Failed to parse AiFoundationClient,将 AI Foundation API 引用隔离到委托类preFilterEnabled 默认值从 false 改为 true,新安装和升级用户均默认启用penalize() 遗漏 approved=null — Halo 评论创建时 approved 可能为 null,原代码仅处理 approved=true 的情况保险箱插件为 Halo 站点提供基于规则和密码的资源访问保护。命中规则后,访客会先进入解锁页,密码验证通过后插件写入带有效期的 HttpOnly Cookie,并自动跳回原资源。
*、**、? 通配符。plugin:safebox:rule:view、plugin:safebox:rule:manage 控制;公开解锁接口自动授权匿名访问。| 类型 | 值 | 适用场景 | 说明 |
|---|---|---|---|
| 附件 | ATTACHMENT |
私密图片、PDF、压缩包等上传文件 | 只保护 /upload 下的资源。 |
| 指定路由 | ROUTE |
会员页、下载页、活动页等前台路径 | 保护命中的前台路由,不影响 Halo 管理后台和插件接口。 |
| 整站私密 | SITE |
临时闭站、内部站点、密码访问站点 | 默认按 /** 匹配,可通过排除路径放行部分页面。 |
插件内置排除以下路径,避免把后台、接口、登录页和静态资源锁死:
/safebox/**
/apis/**
/console/**
/uc/**
/login
/logout
/oauth2/**
/actuator/**
/plugins/**
/themes/**
/assets/**
| 字段 | 说明 |
|---|---|
| 规则名称 | 控制台展示名称,也是解锁页默认标题来源。 |
| 规则说明 | 解锁页说明文案;为空时使用默认提示。 |
| 保护对象类型 | ATTACHMENT、ROUTE、SITE 三选一。 |
| 保护路径 | 一行一个路径模式,也支持用逗号分隔。建议填写以 / 开头的 pathname,不要填写完整 URL。 |
| 排除路径 | 命中这些路径时直接放行,常用于整站私密下放开公开页面。 |
| 访问密码 | 创建时必填;更新时留空表示保留原密码。密码以加盐 SHA-256 哈希保存。 |
| 优先级 | 数字越大越先匹配。相同优先级下,路径越具体越先匹配。 |
| 解锁有效期 | 单位秒,最小 60 秒,默认 3600 秒。 |
| 启用状态 | 停用后该规则不参与命中。 |
插件会从启用规则中选择最终命中的一条规则:
/upload 下的资源。/docs 会命中 /docs/a.pdf。priority 倒序排列。priority 相同时,路径匹配越具体越优先。访问行为:
GET、HEAD 请求命中规则且未解锁时,会以 303 See Other 跳转到 /safebox/unlock?path=...。GET、HEAD 请求命中规则且未解锁时,会返回 401 Unauthorized。ttlSeconds。常用示例:
/upload/private/report.pdf # 保护单个附件
/upload/private/** # 保护整个附件目录
/vip # 保护 /vip 及其子路径
/** # 整站私密
/public/** # 可作为整站私密的排除路径
/safebox/unlock,验证成功后自动回到原资源。如果只想保护某个附件,也可以在 Halo 附件列表中打开该附件的操作菜单,选择「新增保险箱规则」。附件没有固定访问地址时不能创建规则,因为插件需要稳定的访问路径才能进行匹配。
主题不需要主动判断资源是否受保护,访问拦截由插件的 WebFilter 统一处理。主题侧主要做一件事:按自己的设计覆盖解锁页模板。
受保护资源会统一跳转到:
/safebox/unlock?path=/upload/example.png
主题可以在当前主题的模板目录中提供 safebox-unlock.html 覆盖插件内置解锁页;未提供时使用插件内置模板。
templates/
safebox-unlock.html
解锁表单必须使用 POST 提交到 unlockAction,并保留 path、password 两个字段:
<!doctype html>
<html lang="zh-CN" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title th:text="${displayName} + ' - 访问验证'">访问验证</title>
</head>
<body>
<main>
<h1 th:text="${displayName}">受保护资源</h1>
<p th:text="${description}">该资源已开启访问保护,请输入密码后继续访问。</p>
<p th:if="${error != null and error != ''}" th:text="${error}">密码不正确。</p>
<form method="post" th:action="${unlockAction}">
<input type="hidden" name="path" th:value="${path}">
<label for="password">访问密码</label>
<input id="password" name="password" type="password" autocomplete="current-password" required>
<button type="submit">解锁资源</button>
</form>
</main>
</body>
</html>可用模板变量:
| 变量 | 说明 |
|---|---|
displayName |
匹配规则名称;为空时为 受保护资源。 |
description |
匹配规则说明;为空时为默认提示。 |
path |
待解锁资源路径,验证通过后会跳回该路径。 |
error |
错误文案;密码错误时为 密码不正确。。 |
unlockAction |
解锁表单提交地址,当前为 /apis/public.safebox.muyin.site/v1alpha1/unlock。 |
unlockPagePath |
固定解锁页路径,当前为 /safebox/unlock。 |
rule |
匹配到的 SafeBoxRule 对象,可读取 rule.spec.displayName、rule.spec.description 等字段。 |
注意事项:
path 和 password 少一个都无法解锁。>= 2.20.14# 启动 Halo 开发服务器
./gradlew haloServer
# 修改后热重载插件
./gradlew reload
# 开发前端资源
cd ui
pnpm install
pnpm dev./gradlew build构建完成后,可以在 build/libs 目录找到插件 JAR 文件。
./gradlew test
cd ui
pnpm test:unit
pnpm type-checkGPL-3.0 © Lywq
Generated from 1.0.0
为 Halo 主题页面注入一套可配置的自定义右键菜单。插件通过 Halo 的 TemplateHeadProcessor 自动注入运行时资源,不需要改主题模板。
pjax:complete、pjax:end、turbo:load、swup:contentReplaced 后重新初始化。| 项目 | 要求 |
|---|---|
| Halo | >= 2.25.0 |
| Java | 21 |
| Node.js | 推荐 24,CI 使用 24 |
| pnpm | 推荐 10,项目锁定 pnpm@10.12.4 |
可选依赖:
| 插件 | 用途 |
|---|---|
PluginMember >= 1.1.4-alpha6 |
会员收藏、会员签到 |
LinksSubmit >= 2.3.5 |
申请友链 |
这些依赖是可选的。没装也能使用基础右键功能,只是对应菜单项会提示依赖不可用。
本地构建安装:
./gradlew build
构建产物位于 build/libs,上传生成的 JAR 即可。
| 配置项 | 说明 | 默认值 |
|---|---|---|
| 启用全能右键 | 总开关,关闭后不注入右键菜单 | 开启 |
| 启用路径 | 留空表示全站主题页面启用 | [] |
| 排除路径 | 命中后不启用,优先级高于启用路径 | /console/**、/uc/**、/apis/** |
| PJAX 兼容模式 | 前台路由切换后重新读取配置并初始化 | 开启 |
| 编辑区域保留系统右键 | 输入框、文本域、可编辑区域不拦截右键 | 开启 |
支持以下写法:
| 写法 | 示例 | 含义 |
|---|---|---|
| 精确路径 | /about |
仅匹配 /about |
| 递归匹配 | /posts/** |
匹配 /posts 以及 /posts/hello、/posts/a/b |
| 单层匹配 | /photos/* |
匹配 /photos/a,不匹配 /photos/a/b |
| 路径变量 | /archives/{slug} |
匹配 /archives/hello |
排除规则永远优先。比如启用路径配置 /posts/**,排除路径配置 /posts/private/**,那么 /posts/private/secret 不会启用菜单。
| 字段 | 说明 |
|---|---|
enabled |
是否启用当前菜单项 |
id |
菜单项标识,留空会自动生成 |
label |
菜单显示名称 |
action |
点击后执行的动作 |
context |
显示场景:common、text、link、image |
icon |
图标,推荐使用 Iconify 名称,例如 ri:link |
url |
自定义链接动作使用 |
target |
打开方式:_self 或 _blank |
order |
排序值,越小越靠前 |
图标支持 Iconify 名称、http(s) 图片地址、站内绝对路径、data:image/* 和内联 SVG。默认菜单使用的部分 Remix Icon 会内置为 SVG,其他 Iconify 图标会从 https://api.iconify.design 加载。
| 动作 | 显示场景建议 | 行为 |
|---|---|---|
copyPageLink |
common |
复制当前页面链接 |
copyPageTitleLink |
common |
复制当前页面标题和链接 |
copySelection |
text |
复制当前选中的文本 |
copyImageUrl |
image |
复制当前图片地址 |
openImage |
image |
打开当前图片 |
copyLinkUrl |
link |
复制当前链接地址 |
openLink |
link |
打开当前链接 |
scrollTop |
common |
平滑返回页面顶部 |
reload |
common |
刷新当前页面 |
home |
common |
跳转到 / |
search |
common 或 text |
有选中文本时跳转到 /search?keyword=...,否则跳转到 /search |
customLink |
common |
打开自定义链接 |
memberFavorite |
common |
调用会员插件收藏能力 |
memberSignIn |
common |
调用会员插件签到能力 |
openLinksSubmit |
common |
打开友链自助提交弹窗 |
customLink 的 url 支持占位符:
{url} 当前页面链接,已 URL 编码
{title} 当前页面标题,已 URL 编码
{selection} 当前选中文本,已 URL 编码
示例:
https://example.com/share?title={title}&url={url}
memberFavorite 会优先使用当前 Halo 文章上下文作为收藏对象。插件会从主题模板变量 post 中读取:
metadata.namespec.titlestatus.permalinkspec.cover如果当前页面没有 post 变量,运行时会尝试读取页面中的 data-member-favorite-subject 作为备用文章标识。
memberSignIn 会等待会员插件前台 API 就绪,再执行签到。用户未登录时会跳转到登录页。
openLinksSubmit 会尝试调用 window.LinksSubmit.openLinksSubmitOverlay(),也兼容 window.openLinksSubmitOverlay()。如果主题页面没有注入友链弹窗,会提示“友链申请弹窗未注入”。
插件会向主题页面 <head> 注入:
/plugins/right-menu/assets/static/right-menu/right-menu.css#right-menu-config/plugins/right-menu/assets/static/right-menu/right-menu.js默认不会在 /console/**、/uc/**、/apis/** 生效。右键菜单挂载在 document.body 下,层级较高,通常不需要主题额外适配。
如果菜单没有显示,优先检查:
right-menu-config 和 right-menu-runtime。/plugins/right-menu/assets/static/right-menu/right-menu.js。启动 Halo 开发环境:
./gradlew haloServer
前端依赖安装与监听构建:
cd ui
pnpm install
pnpm dev
项目结构:
| 路径 | 说明 |
|---|---|
src/main/resources/plugin.yaml |
插件元信息、Halo 版本要求、可选依赖 |
src/main/resources/extensions/settings.yaml |
插件设置表单 |
src/main/resources/extensions/reverseProxy.yaml |
静态资源反向代理 |
src/main/java/site/muyin/rightmenu/processor/RightMenuHeadProcessor.java |
主题 <head> 注入入口 |
src/main/java/site/muyin/rightmenu/setting/RightMenuSetting.java |
设置模型与默认菜单 |
src/main/java/site/muyin/rightmenu/support/RightMenuPathMatcher.java |
路径匹配规则 |
src/main/java/site/muyin/rightmenu/support/RightMenuPostContextExtractor.java |
文章上下文提取 |
src/main/java/site/muyin/rightmenu/template/RightMenuHtmlTpl.java |
注入 HTML 片段生成 |
src/main/resources/static/right-menu/right-menu.js |
前台运行时 |
src/main/resources/static/right-menu/right-menu.css |
前台样式 |
ui/src/__tests__/right-menu-runtime.test.ts |
前台运行时单元测试 |
常用命令:
# 后端单元测试
./gradlew test
# 后端 + 前端校验
./gradlew check
# 完整构建插件 JAR
./gradlew build
# 前端运行时测试
cd ui
pnpm test:unit
./gradlew build 会先执行 ui 模块的 assemble,再把前端产物复制到插件资源目录。
gradle.properties 中的 version。./gradlew check 和 ./gradlew build。这是默认行为。插件默认排除了 /console/** 和 /uc/**,避免影响 Halo 后台和用户中心。
菜单项按 context 判断是否显示。image 只在图片上右键出现,link 只在链接上右键出现,text 需要页面存在选中文本。
插件优先使用 navigator.clipboard.writeText,失败后会回退到 document.execCommand('copy')。如果浏览器权限、非安全上下文或主题脚本拦截复制行为,仍可能失败。
默认内置图标不依赖网络。非内置 Iconify 图标会请求 https://api.iconify.design,如果站点网络策略拦截该域名,建议改用站内图片路径或内联 SVG。
当前页面需要是 Halo 文章页,并且主题上下文里存在 post 变量;或者页面中提供 data-member-favorite-subject 作为备用文章标识。
GPL-3.0 © Lywq
Full Changelog: v1.0.0-beta.1...v1.0.0-beta.2
Generated from v1.0.0-beta.2
Generated from v2.3.16
Generated from v2.3.15
fix: 分页加 touch-manipulation,修复移动端需两次点击的问题
Full Changelog: 1.0.0-beta3...1.0.0-beta4
Generated from 1.0.0-beta4
fix: 分页按钮改为文字+图标,活跃页用主题色
分页改为数字页码+省略号,移除 select 下拉
保险箱插件为 Halo 站点提供基于规则和密码的资源访问保护。命中规则后,访客会先进入解锁页,密码验证通过后插件写入带有效期的 HttpOnly Cookie,并自动跳回原资源。
*、**、? 通配符。plugin:safebox:rule:view、plugin:safebox:rule:manage 控制;公开解锁接口自动授权匿名访问。| 类型 | 值 | 适用场景 | 说明 |
|---|---|---|---|
| 附件 | ATTACHMENT |
私密图片、PDF、压缩包等上传文件 | 只保护 /upload 下的资源。 |
| 指定路由 | ROUTE |
会员页、下载页、活动页等前台路径 | 保护命中的前台路由,不影响 Halo 管理后台和插件接口。 |
| 整站私密 | SITE |
临时闭站、内部站点、密码访问站点 | 默认按 /** 匹配,可通过排除路径放行部分页面。 |
插件内置排除以下路径,避免把后台、接口、登录页和静态资源锁死:
/safebox/**
/apis/**
/console/**
/uc/**
/login
/logout
/oauth2/**
/actuator/**
/plugins/**
/themes/**
/assets/**
| 字段 | 说明 |
|---|---|
| 规则名称 | 控制台展示名称,也是解锁页默认标题来源。 |
| 规则说明 | 解锁页说明文案;为空时使用默认提示。 |
| 保护对象类型 | ATTACHMENT、ROUTE、SITE 三选一。 |
| 保护路径 | 一行一个路径模式,也支持用逗号分隔。建议填写以 / 开头的 pathname,不要填写完整 URL。 |
| 排除路径 | 命中这些路径时直接放行,常用于整站私密下放开公开页面。 |
| 访问密码 | 创建时必填;更新时留空表示保留原密码。密码以加盐 SHA-256 哈希保存。 |
| 优先级 | 数字越大越先匹配。相同优先级下,路径越具体越先匹配。 |
| 解锁有效期 | 单位秒,最小 60 秒,默认 3600 秒。 |
| 启用状态 | 停用后该规则不参与命中。 |
插件会从启用规则中选择最终命中的一条规则:
/upload 下的资源。/docs 会命中 /docs/a.pdf。priority 倒序排列。priority 相同时,路径匹配越具体越优先。访问行为:
GET、HEAD 请求命中规则且未解锁时,会以 303 See Other 跳转到 /safebox/unlock?path=...。GET、HEAD 请求命中规则且未解锁时,会返回 401 Unauthorized。ttlSeconds。常用示例:
/upload/private/report.pdf # 保护单个附件
/upload/private/** # 保护整个附件目录
/vip # 保护 /vip 及其子路径
/** # 整站私密
/public/** # 可作为整站私密的排除路径
/safebox/unlock,验证成功后自动回到原资源。如果只想保护某个附件,也可以在 Halo 附件列表中打开该附件的操作菜单,选择「新增保险箱规则」。附件没有固定访问地址时不能创建规则,因为插件需要稳定的访问路径才能进行匹配。
主题不需要主动判断资源是否受保护,访问拦截由插件的 WebFilter 统一处理。主题侧主要做一件事:按自己的设计覆盖解锁页模板。
受保护资源会统一跳转到:
/safebox/unlock?path=/upload/example.png
主题可以在当前主题的模板目录中提供 safebox-unlock.html 覆盖插件内置解锁页;未提供时使用插件内置模板。
templates/
safebox-unlock.html
解锁表单必须使用 POST 提交到 unlockAction,并保留 path、password 两个字段:
<!doctype html>
<html lang="zh-CN" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title th:text="${displayName} + ' - 访问验证'">访问验证</title>
</head>
<body>
<main>
<h1 th:text="${displayName}">受保护资源</h1>
<p th:text="${description}">该资源已开启访问保护,请输入密码后继续访问。</p>
<p th:if="${error != null and error != ''}" th:text="${error}">密码不正确。</p>
<form method="post" th:action="${unlockAction}">
<input type="hidden" name="path" th:value="${path}">
<label for="password">访问密码</label>
<input id="password" name="password" type="password" autocomplete="current-password" required>
<button type="submit">解锁资源</button>
</form>
</main>
</body>
</html>
可用模板变量:
| 变量 | 说明 |
|---|---|
displayName |
匹配规则名称;为空时为 受保护资源。 |
description |
匹配规则说明;为空时为默认提示。 |
path |
待解锁资源路径,验证通过后会跳回该路径。 |
error |
错误文案;密码错误时为 密码不正确。。 |
unlockAction |
解锁表单提交地址,当前为 /apis/public.safebox.muyin.site/v1alpha1/unlock。 |
unlockPagePath |
固定解锁页路径,当前为 /safebox/unlock。 |
rule |
匹配到的 SafeBoxRule 对象,可读取 rule.spec.displayName、rule.spec.description 等字段。 |
注意事项:
path 和 password 少一个都无法解锁。>= 2.20.14# 启动 Halo 开发服务器
./gradlew haloServer
# 修改后热重载插件
./gradlew reload
# 开发前端资源
cd ui
pnpm install
pnpm dev
./gradlew build
构建完成后,可以在 build/libs 目录找到插件 JAR 文件。
./gradlew test
cd ui
pnpm test:unit
pnpm type-check
GPL-3.0 © Lywq
页脚内容改为可排序小部件
修复新建文章时报错的问题
Earthquake 首个正式版本。
布局与交互
响应式导航栏 md 断点,平板正确折叠为汉堡菜单
View Transitions 圆形扩散切换深色模式(可在主题设置开启)
文章列表交错淡入,默认单条布局
首页文章封面图可点击跳转
评论区
去掉白色卡片框,注入 --halo-cw-* 变量跟随主题与深色模式
样式系统
OKLCH 单变量配色,–hue 驱动全套语义 token,30+ 预设
原生 CSS(移除 SCSS/sass-embedded)
prefers-reduced-motion 无障碍守卫
本次更新将版本号推进至 v1.0.4。核心重点在于彻底解决多用户混杂对话时“完整对话”弹窗上下文不清晰的问题,并对插件的后台管理 UI 进行了全面重写与现代化升级。同时,修正了之前版本中遗失的部分配置功能,并深度优化了移动端适配体验。
max-width: calc(100vw - 32px)),防止溢出屏幕。初版
theme-color-init.js,减少首屏阻塞请求。body visibility:hidden 式的首屏隐藏方案。DOMContentLoaded 后更早结束,不再强依赖 window.load。qweather-icons.csslenis.cssaos.cssswiper-bundle.min.csswelcome.cssdefer:
APlayer.min.jsMeting2.min.jscursor-init.jsfontawesome.min.css 中的 font-display 从 block 调整为 swap。qweather-icons.css 增加 font-display: swap。equipments.html 的静态中文文案,修复乱码问题。详情请参阅更新日志(在 GitHub 上查看)。
Please refer to Changelog (view on GitHub) for details.
howiehz-higan-zh-hans.zip为简体中文默认安装包,仅包含.br。
howiehz-higan-zh-hans-tiny.zip为简体中文轻量安装包,移除默认字体资源,默认 logo 资源将替换为 1x1 占位图,不包含预压缩文件,且不注入 Mermaid / instant.page 运行时。
howiehz-higan-zh-hans-full-precompressed.zip为简体中文完整预压缩包,包含.gz/.br/.zst。
howiehz-higan-en.zipis the English default package with.bronly.
howiehz-higan-en-tiny.zipis the English tiny package that removes the default font assets, replaces the default logo asset with a 1x1 placeholder image, omits precompressed assets, and excludes Mermaid / instant.page runtime injection.
howiehz-higan-en-full-precompressed.zipis the English full precompressed package with.gz/.br/.zst.
Quiet 首个正式版本,提供极简内容型博客主题的基础能力,包括浅色 / 暗色模式、首页最近更新、文章列表大小调节、文章目录、阅读进度、侧边栏模块、代码块复制、分类 / 标签 / 归档 / 作者页适配和响应式布局。
首次发布 InkNest 主题。
提供首页、文章、页面、分类、标签、归档、作者、友情链接和 404 页面模板。
支持搜索插件入口、评论组件、友情链接插件、图片灯箱、代码复制、阅读进度和 SEO 元信息。
linksTitle 变量时可能产生异常的问题Full Changelog: v2.2.0...v2.2.1
Generated from v2.2.1
Queries.equal() 服务端过滤spec.sentiment、spec.published、spec.postKind 索引.jar 文件 → 插件 → 安装 → 本地上传Quiet 首个正式版本,提供极简内容型博客主题的基础能力,包括浅色 / 暗色模式、首页最近更新、文章列表大小调节、文章目录、阅读进度、侧边栏模块、代码块复制、分类 / 标签 / 归档 / 作者页适配和响应式布局。
Full Changelog: v1.1.3...v1.2.0
Generated from v1.2.0
修复加密草稿同步问题并支持自定义锁定说明,优化ui。
站内页面切换时仅替换主体内容,保留全局持久层,解决切换页面时音乐播放组件中断、播放器重新初始化等问题,提升浏览体验。
后台主题设置中可自由开启或关闭 PJAX 功能,适配不同站点需求。关闭后恢复传统整页跳转。
PJAX 切换时会先加载新页面所需 CSS,再替换页面内容,避免出现短暂无样式页面、图标放大、布局闪烁等问题。
开启 PJAX 后自动使用页面切换遮罩,减少切页过程中的视觉突兀感。
Font Awesome、AOS、Swiper 等关键样式改为稳定加载,降低冷加载和 PJAX 切页时的样式闪烁概率。
增加页面进入与离开钩子,支持页面脚本在 PJAX 切换后重新初始化,并在离开页面时清理定时器、滚动监听、弹窗状态等资源。
适配 Lenis 平滑滚动、AOS 动画、Swiper 轮播等组件在 PJAX 场景下的重新初始化与状态刷新。
ExtensionGetter.getEnabledExtension(AiModelService.class) 获取 AI 服务,替换原先的跨 ClassLoader 反射调用方式(Issue #1 )plugin.yaml 中声明可选插件依赖 ai-foundation?: "*",建立正确的插件依赖关系,插件在未安装 AI Foundation 时仍可正常加载store.halo.run/recommended-apps 注解,安装本插件后可在应用市场推荐安装 AI Foundation 插件OutputSpec.choice 结构化输出,替换原先的字符串匹配解析,分类更可靠GenerateTextRequest 并设置 maxRetries=2,由 SDK 自动重试瞬时错误{{conversation}}→{{conversation_history}})、GitHub 链接(nxxy335→sunny-335)、Cravatar 链接(cravatar.cn→cn.cravatar.com)等错误,补充缺失的配置项文档generateAndPublish 中 Mono<Void> 的 empty 信号触发 switchIfEmpty 导致 publishReply 被调用两次,第二次覆盖 published=falseDisposableBean 正确释放资源:deep(.btn-content) 设置 inline-flex 布局,图标和文字并排显示px-3 改为 pl-3 pr-8 为下拉箭头预留空间详情请参阅更新日志(在 GitHub 上查看)。
Please refer to Changelog (view on GitHub) for details.
howiehz-higan-zh-hans.zip为简体中文默认安装包,仅包含.br。
howiehz-higan-zh-hans-tiny.zip为简体中文轻量安装包,移除默认字体资源,默认 logo 资源将替换为 1x1 占位图,不包含预压缩文件,且不注入 Mermaid / instant.page 运行时。
howiehz-higan-zh-hans-full-precompressed.zip为简体中文完整预压缩包,包含.gz/.br/.zst。
howiehz-higan-en.zipis the English default package with.bronly.
howiehz-higan-en-tiny.zipis the English tiny package that removes the default font assets, replaces the default logo asset with a 1x1 placeholder image, omits precompressed assets, and excludes Mermaid / instant.page runtime injection.
howiehz-higan-en-full-precompressed.zipis the English full precompressed package with.gz/.br/.zst.
What’s Changed
feat: 支持自定义目录(TOC)的显示层级和折叠层级 (#207) by @AirboZH in #208
Full Changelog: v1.4.9…v1.5.0
Tip
当前版本不再强制依赖于 AI Foundation 插件,但使用 AI 对话时仍旧需要,更新后仍旧需要重载主题设置。
Full Changelog: v2.0.1...v2.1.0
Generated from v2.1.0
splitHtmlLines() 方法,行号显示时完整保留 hljs 语法高亮结构,修复行号导致高亮失效的问题observeCodeBlocks() 和 observeImages() 调用,移除重复的模块 init() 调用,提升性能pendingInit 未使用变量@supports 特性查询,并为不支持 :has() 的浏览器提供 CSS fallbacktouchstart 绑定,避免点击双触发;底部折叠按钮严格依据状态显隐applyThemeColors 改为数据驱动(BASE_COLORS),processCode 拆分为 ensureWrapper/addHeader 子方法observeCodeBlocks 仅在启用高亮或代码折叠时观察代码块,避免仅启用长图折叠时误装饰代码console.log 调试日志,保留 console.warn 错误日志code-enhance.css(不存在 .min.css 版本)enableCodeHighlight 重命名为 enableHighlight,语义更简洁injectResources、buildProperties、appendHljsResources、appendConfig、appendBaseResources 等方法,优化模板资源注入逻辑hasAnyFeatureEnabled() 方法,简化全功能禁用时的跳过判断hasContentVariable() 方法,简化内容模板判断逻辑static/images/dan.svgCodeEnhanceHeadProcessor 类注释文档onErrorResume 改为记录完整异常堆栈feat(core): 优化会员插件的积分和储值账户功能
Halo CMS 插件 —— 统一管理和展示 GitHub、Gitee、产品、插件、工具等项目作品。
/projects 列表页和 /projects/{slug} 详情页,支持主题覆盖projectFinder,主题模板可直接调用,灵活控制渲染.jar 文件git clone https://github.com/liuyiwuqing/plugin-portfolio.git
cd plugin-portfolio
./gradlew build
构建产物位于 build/libs/plugin-portfolio-*.jar,在 Halo 控制台上传安装。
插件启用后,在 控制台 → 插件 → 项目集 → 设置 中可配置:
| 配置项 | 说明 | 默认值 |
|---|---|---|
| 启用默认展示页 | 开启后提供 /projects 和 /projects/{slug} 路由 |
开启 |
| 默认每页数量 | 列表页每页显示项目数 | 12 |
| 默认 SEO 标题 | 页面 <title> 和 OG 标题 |
项目作品集 |
| 默认 SEO 描述 | 页面 <meta description> |
集中展示开源项目、产品、插件、工具和其他开发作品。 |
| 平台选项 | 项目来源平台下拉选项 | GitHub / Gitee / 独立站点 / 私有项目 / 其他 |
| 类型选项 | 项目类型下拉选项 | 开源项目 / 产品 / 插件 / 网站 / 工具 / 类库 / 其他 |
平台和类型选项支持自由增删改,value 建议使用小写英文、数字和下划线。
插件注册 projectFinder,在 Thymeleaf 模板中直接注入使用。Finder 只返回已发布项目。
| 方法 | 返回值 | 说明 |
|---|---|---|
list(page, size) |
ListResult<Project> |
分页获取项目 |
listBy(keyword, platform, type, tag, page, size) |
ListResult<Project> |
按关键词、平台、类型、标签组合筛选 |
listByPlatform(platform, page, size) |
ListResult<Project> |
按平台筛选 |
listByType(type, page, size) |
ListResult<Project> |
按类型筛选 |
listByTechStack(techStack, page, size) |
ListResult<Project> |
按技术栈筛选 |
listByTag(tag, page, size) |
ListResult<Project> |
按展示标签筛选 |
featured(page, size) |
ListResult<Project> |
获取推荐项目 |
recent(size) |
List<Project> |
获取最近项目(不分页) |
count() |
long |
公开项目总数 |
options() |
ProjectOptions |
获取平台和类型选项列表 |
setting() |
PortfolioSetting |
获取插件通用设置 |
platformLabels() |
Map<String, String> |
平台 value → label 映射表 |
typeLabels() |
Map<String, String> |
类型 value → label 映射表 |
renderContent(project) |
String |
将项目 Markdown 内容渲染为安全 HTML |
listAll() |
Flux<Project> |
获取全部公开项目流 |
getBySlug(slug) |
Mono<Project> |
按 slug 获取单个项目 |
展示推荐项目:
<th:block th:with="projects=${projectFinder.featured(1, 6)},
platformLabels=${projectFinder.platformLabels()},
typeLabels=${projectFinder.typeLabels()}">
<div class="project-grid">
<article th:each="project : ${projects.items}" class="project-card">
<a th:href="@{/projects/{slug}(slug=${project.slug})}">
<img th:if="${project.cover}" th:src="${project.cover}" th:alt="${project.title}" />
<h3 th:text="${project.title}">项目标题</h3>
<p th:text="${project.summary}">项目简介</p>
<span th:text="${platformLabels[project.platform] ?: project.platform}">平台</span>
<span th:text="${typeLabels[project.type] ?: project.type}">类型</span>
</a>
</article>
</div>
</th:block>
渲染项目详情内容:
<div th:utext="${projectFinder.renderContent(project)}"></div>
按技术栈筛选:
<th:block th:with="projects=${projectFinder.listByTechStack('Vue', 1, 10)}">
<div th:each="project : ${projects.items}">
<a th:href="@{/projects/{slug}(slug=${project.slug})}" th:text="${project.title}">标题</a>
</div>
</th:block>
插件内置两个 Thymeleaf 模板:
| 路由 | 模板 | 说明 |
|---|---|---|
/projects |
projects.html |
项目列表页,响应式网格布局,支持分页 |
/projects/{slug} |
project-detail.html |
项目详情页,展示封面、元数据和 Markdown 内容 |
主题可通过提供同名模板覆盖默认页面。关闭设置中的「启用默认展示页」后,插件不再注册路由,完全由主题自行渲染。
在 Halo 富文本编辑器中,可通过工具栏或斜杠命令 / 插入「项目卡片」节点。选择一个已发布项目后,文章中会嵌入一个可交互的卡片组件。
卡片在前端渲染为 <portfolio-project-card data-slug="xxx"> 标签,发布时由插件自动替换为带样式的 HTML 卡片,包含封面、标题、摘要和元数据标签。
每个项目(Project)包含以下字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title |
String | ✅ | 项目标题(最长 120 字符) |
slug |
String | ✅ | URL 友好标识(最长 80 字符) |
summary |
String | - | 项目摘要(最长 500 字符) |
content |
String | - | 项目详情,支持 Markdown |
cover |
String | - | 封面图 URL |
platform |
String | - | 来源平台(如 github) |
type |
String | - | 项目类型(如 open_source) |
techStacks |
List<String> | - | 技术栈标签 |
tags |
List<String> | - | 展示标签 |
repoUrl |
String | - | 仓库地址 |
demoUrl |
String | - | 演示地址 |
docsUrl |
String | - | 文档地址 |
priority |
Integer | - | 排序值,越大越靠前 |
featured |
Boolean | - | 是否推荐 |
status |
Enum | - | DRAFT / PUBLISHED / ARCHIVED |
createTime |
String | - | 创建时间 |
updateTime |
String | - | 更新时间 |
另有 sourceProvider、repoOwner、repoName 字段为后续仓库同步功能预留。
# 终端 1:启动 Halo 开发服务器(含后端热重载)
./gradlew haloServer
# 终端 2:启动前端开发服务器(含 HMR)
cd ui
pnpm install
pnpm dev
Halo 开发服务器默认运行在 http://localhost:8090,前端开发服务器提供模块热替换。
src/main/java/site/muyin/portfolio/
├── PortfolioPlugin.java # 插件生命周期,注册 GVK 和索引
├── scheme/Project.java # 核心数据模型
├── enums/ # 枚举(项目状态)
├── model/ # 数据传输对象
├── query/ # 查询参数构建
├── setting/ # 插件设置 POJO
├── service/ # 业务逻辑接口
│ └── impl/ # 业务逻辑实现
├── endpoint/ # REST API(Console + Public)
│ └── routes/ # 路由处理器
├── finders/ # Finder API 接口
│ └── impl/ # Finder API 实现
├── content/ # 内容渲染(Markdown + 项目卡片)
└── router/ # 默认页面路由
ui/src/
├── index.ts # 插件入口,注册路由和编辑器扩展
├── views/ProjectManagerView.vue # 项目管理页面
├── api/portfolio.ts # API 客户端和类型定义
├── editor/ # TipTap 编辑器扩展
└── assets/ # 静态资源
# 完整构建(后端 + 前端)
./gradlew build
# 仅构建前端
cd ui && pnpm build
# 运行后端测试
./gradlew test
# 运行前端检查
cd ui && pnpm check
构建产物:build/libs/plugin-portfolio-*.jar
项目使用 GitHub Actions 自动化:
ci.yaml)— 推送或 PR 到 main 分支时触发,执行构建和测试cd.yaml)— 发布 GitHub Release 时触发,构建并产出插件 jar| 层级 | 技术 |
|---|---|
| 后端语言 | Java 21 |
| 后端框架 | Spring WebFlux(响应式) |
| 插件平台 | Halo Plugin SDK |
| 构建工具 | Gradle 9.4 |
| 前端框架 | Vue 3(Composition API) |
| 前端语言 | TypeScript |
| 前端构建 | Rsbuild(基于 Rspack) |
| 包管理 | pnpm |
| Markdown | CommonMark |
| 测试 | JUnit 5 / Vitest |
Tip
此版本为重大更新,升级后请务必在插件设置中重载插件配置!!并且当前插件将会依赖于 AI Foundation 插件,请务必下载安装此插件。
Warning
不过由于架构的更新,由此带来的问题是,前端包的体积大了近十倍,尽管我已经尽力做了优化,但当前前端包的体积仍旧达到了 500kb,这可能会产生一些网络访问的问题。
当前版本开始,从底层架构开始完全重构此次插件,得益于架构的更新,现在 Live2d 将支持如下功能:
并且从当前版本开始,插件的 AI 底层将基于 Halo Ai Foundation 插件。因此也将整体 AI 功能进行了重构,现在 Live2d 的 AI 功能将支持更多丰富的功能,例如:
pixi.js 重构底层代码。--- 光速发布的 2.0.1 版本 ---
Generated from v2.0.1
详情请参阅更新日志(在 GitHub 上查看)。
Please refer to Changelog (view on GitHub) for details.
howiehz-higan-zh-hans.zip为简体中文默认安装包,仅包含.br。
howiehz-higan-zh-hans-tiny.zip为简体中文轻量安装包,移除默认字体资源,默认 logo 资源将替换为 1x1 占位图,不包含预压缩文件,且不注入 Mermaid / instant.page 运行时。
howiehz-higan-zh-hans-full-precompressed.zip为简体中文完整预压缩包,包含.gz/.br/.zst。
howiehz-higan-en.zipis the English default package with.bronly.
howiehz-higan-en-tiny.zipis the English tiny package that removes the default font assets, replaces the default logo asset with a 1x1 placeholder image, omits precompressed assets, and excludes Mermaid / instant.page runtime injection.
howiehz-higan-en-full-precompressed.zipis the English full precompressed package with.gz/.br/.zst.
feat(image): 添加共享图片列表显示模式支持网格和瀑布流布局
Generated from v1.3.3
基于 AI 的 Halo 博客评论自动回复插件,支持 AI 虚拟角色回复、自审核、自动发布和对话式连续回复。
AiModelService,无需 pluginDependencies,彻底解决跨 ClassLoader 类加载问题PluginManager 无法通过 Spring 依赖注入获取)DefaultSpringPlugin 包级私有类反射访问权限问题gradlew 缺少执行权限)AiFoundationConfiguration 配置类这是 Beta 版本,可能存在未发现的问题,建议在生产环境使用前充分测试。