Skip to content

Commit 1f86880

Browse files
LIlGGCopilot
andcommitted
feat: modernize Live2D frontend integration
- build the plugin with the Vite-based frontend package and Halo dev-mode support - add declarative custom tool actions and runtime config normalization - remove legacy bundled static assets in favor of the modern frontend runtime Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d81b4b9 commit 1f86880

57 files changed

Lines changed: 1543 additions & 2368 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
2. 通过 Halo-2.x [安装插件](https://docs.halo.run/user-guide/plugins#%E5%AE%89%E8%A3%85%E6%8F%92%E4%BB%B6) 功能安装本插件
2121
3. 打开网站,即可在左下角看到萌萌哒的看板娘哦~
2222

23+
## 开发说明
24+
25+
本仓库的现代化前端位于 `packages/live2d`,插件构建时会自动打包前端产物并同步到 Halo 插件静态资源目录,无需再手动复制 `dist` 文件。
26+
27+
本地联调时,可以在 Halo 后台开启 `插件设置 -> 高级设置 -> 前端调试模式`,并让其指向本地 Vite 服务地址(默认 `http://localhost:5173`)。这样 Halo 页面会直接加载开发中的前端模块,方便调试现代化后的 Live2d 运行时。
28+
2329
## 功能介绍
2430
- [x] 一只萌萌的看板娘,为网站增添一份活力
2531
- [x] 基于 `untitled-pixi-live2d-engine` 渲染,支持 Cubism 2 / 3 / 4 / 5 模型
@@ -107,6 +113,22 @@ live2d 渲染页面时将自动读取当前启用主题下的文件。
107113

108114
**需要特别注意的是,一旦用户指定了此 TIPS 文件,那么默认的 TIPS 文件将不再生效(除非当前文件加载失败,此时会回退使用默认的 TIPS 文件),因此建议自定义时将属性设置完整**
109115

116+
### 自定义工具
117+
如果内置工具不满足需求,可以通过 Halo 后台 `插件设置 -> 自定义工具` 声明额外工具按钮。
118+
119+
当前自定义工具使用声明式动作,不支持注入任意 JavaScript。首批支持的动作包括:
120+
121+
- 发送提示语
122+
- 显示、隐藏或切换看板娘
123+
- 切换聊天窗口
124+
- 切换模型或材质
125+
- 截图
126+
- 打开链接
127+
- 触发命名空间自定义事件
128+
- 加载指定模型
129+
130+
这种方式更适合在保证安全边界的前提下扩展 Live2d 能力。
131+
110132
## 鸣谢
111133
- 本插件代码借鉴了 [live2d-widget](https://github.com/stevenjoezhang/live2d-widget) 的理念及代码并完全重写 JS
112134
- 使用了 [hitokoto](https://hitokoto.cn/) 的一言接口

build.gradle

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
plugins {
2-
id "io.freefair.lombok" version "8.0.1"
3-
id "run.halo.plugin.devtools" version "0.5.0"
42
id 'java'
3+
id "io.freefair.lombok" version "9.2.0"
4+
id "run.halo.plugin.devtools" version "0.6.2"
55
}
66

77
group 'run.halo.live2d'
8-
sourceCompatibility = JavaVersion.VERSION_17
98

109
repositories {
1110
mavenCentral()
12-
maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' }
13-
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
14-
maven { url 'https://repo.spring.io/milestone' }
1511
}
1612

1713
configurations.runtimeClasspath {
1814
exclude group: 'com.fasterxml.jackson.core'
1915
}
2016

2117
dependencies {
22-
implementation platform('run.halo.tools.platform:plugin:2.11.0-SNAPSHOT')
18+
implementation platform('run.halo.tools.platform:plugin:2.24.0')
2319
implementation 'com.theokanning.openai-gpt3-java:api:0.17.0'
2420

2521
compileOnly 'run.halo.app:api'
@@ -32,20 +28,50 @@ test {
3228
useJUnitPlatform()
3329
}
3430

31+
java {
32+
toolchain {
33+
languageVersion = JavaLanguageVersion.of(21)
34+
}
35+
}
36+
3537
tasks.withType(JavaCompile).configureEach {
3638
options.encoding = "UTF-8"
39+
options.release = 21
40+
}
41+
42+
def live2dFrontendDir = file("packages/live2d")
43+
def generatedFrontendResourcesDir = layout.buildDirectory.dir("generated/resources/main/static/live2d")
44+
45+
tasks.register("buildFrontend", Exec) {
46+
workingDir = projectDir
47+
commandLine "pnpm", "--dir", live2dFrontendDir.absolutePath, "build"
48+
inputs.dir(fileTree(dir: live2dFrontendDir, excludes: ["dist/**", "node_modules/**"]))
49+
outputs.dir(file("${live2dFrontendDir}/dist"))
50+
}
51+
52+
tasks.register("syncFrontendAssets", Sync) {
53+
dependsOn tasks.named("buildFrontend")
54+
from("${live2dFrontendDir}/dist")
55+
into(generatedFrontendResourcesDir)
56+
}
57+
58+
tasks.named("processResources") {
59+
dependsOn tasks.named("syncFrontendAssets")
60+
from(generatedFrontendResourcesDir) {
61+
into "static/live2d"
62+
}
3763
}
3864

3965
halo {
40-
version = '2.11.2'
41-
port = 8092
66+
version = '2.24.2'
67+
port = 8090
4268
superAdminUsername = 'admin'
4369
superAdminPassword = 'admin'
44-
externalUrl = 'http://localhost:8092'
70+
externalUrl = 'http://localhost:8090'
4571
debug = true
4672
}
4773

4874
build {
4975
// build frontend before build
5076
tasks.getByName('compileJava')
51-
}
77+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-05-14
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
## Context
2+
3+
`packages/live2d` is now the maintained frontend runtime, but Halo pages are still initialized through `Live2dInitProcessor` and the legacy `static/js/live2d-autoload.min.js` bundle. The repository therefore carries two frontend integration models at once: the Lit/Pixi runtime that development is happening in, and the older static asset bootstrap that production still uses.
4+
5+
This change crosses backend bootstrapping, frontend packaging, runtime config handling, and local debugging workflow. It also has a migration concern: once Halo switches to the modern bundle path, the legacy CSS, bootstrap script, and supporting static libraries should be removable without regressing the widget's existing behavior.
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
- Make the Halo plugin load the modern frontend bundle as the only production widget bootstrap path.
11+
- Ensure plugin builds package the frontend output automatically so JAR artifacts ship the correct runtime entry and supporting chunks.
12+
- Replace the current broad injected config object with an explicit public config payload that only includes frontend-safe settings.
13+
- Let plugin users extend the toolbar through backend-configured custom tools that target a supported frontend action registry.
14+
- Preserve the current backend model API usage and existing user-visible widget flows while modernizing bootstrapping and packaging.
15+
- Provide a repeatable local development flow where Halo can target a Vite dev server without forking runtime logic.
16+
17+
**Non-Goals:**
18+
- Introduce new backend model metadata endpoints or change the existing model/texture API contracts.
19+
- Rework widget behavior that has already been covered by the modern parity changes unless required by the new bootstrap path.
20+
- Add runtime-configurable hot reload behavior for end users in production.
21+
- Preserve the old `live2d-autoload` source tree once the modern integration path is proven and active.
22+
23+
## Decisions
24+
25+
### Use a dedicated Halo bootstrap entry instead of inline `live2d.init(...)`
26+
The plugin should stop emitting a script tag for `live2d-autoload.min.js` plus inline initialization code. Instead, the backend should emit:
27+
28+
1. a frontend-safe JSON payload embedded in the page, and
29+
2. a module entry that reads that payload and starts the runtime.
30+
31+
This keeps execution logic in the frontend bundle, avoids string-building a JavaScript object in the backend, and gives development and production the same startup contract. The alternative was to keep the inline `live2d.init(path, config)` pattern and simply point it at the new bundle, but that would preserve the legacy bootstrap shape and continue coupling backend rendering to frontend API details.
32+
33+
### Publish a dedicated public runtime config object
34+
The backend should map plugin settings into an explicit public DTO instead of merging all settings groups and deleting a few unsafe fields afterward. The DTO should include only fields required by the runtime, such as model selection defaults, runtime toggles, tips sources, AI-chat timing or endpoint fields that are intentionally public, and declarative custom tool definitions.
35+
36+
This gives the frontend a stable contract and reduces the risk of accidentally exposing backend-only settings. The alternative was to continue with the current "merge everything, then trim" approach, but that makes future settings additions risky and hard to review.
37+
38+
### Replace executable custom tools with a declarative action registry
39+
The current frontend `customTools` support accepts executable callbacks or stringified code, which is useful for local experimentation but is not an acceptable backend configuration contract. Backend-configured custom tools should instead be modeled as declarative tool definitions that target a supported frontend action registry.
40+
41+
That registry should cover the core extension cases already visible in the runtime today:
42+
43+
- send a Live2D message
44+
- show, hide, or toggle the widget
45+
- toggle the AI chat window
46+
- switch model or switch texture
47+
- capture a screenshot
48+
- open a configured URL
49+
- emit a namespaced custom DOM event
50+
- optionally load a specific model/texture selection through an explicit safe action
51+
52+
This keeps the extension surface powerful while avoiding remote code execution through plugin settings. The alternative was to preserve the current `new Function(...)` path and merely pass its source from the backend, but that would turn plugin configuration into arbitrary browser-side code execution and make validation impossible.
53+
54+
### Package frontend assets through the Gradle resource pipeline
55+
Frontend build output should be generated from `packages/live2d` and synchronized into the plugin's packaged resources as part of the build lifecycle, preferably through a dedicated frontend build/sync task that `processResources` depends on. The plugin should package the emitted entry file and all hashed chunks under a namespaced static directory.
56+
57+
This keeps JAR packaging deterministic and avoids manual copy steps into source-controlled resource folders. The alternative was to copy dist output back into `src/main/resources/static`, but that would mix generated artifacts with maintained source assets and make cleanup harder.
58+
59+
### Support a dual-source bootstrap for production and local debugging
60+
The backend bootstrap should support two sources for the frontend entry:
61+
62+
- packaged plugin assets in production
63+
- a configured Vite dev-server entry in local development
64+
65+
Both sources should consume the same embedded public config payload so the runtime logic remains identical across environments. The alternative was to rely on the standalone demo page for local testing, but that does not validate the real Halo integration path or backend-provided config behavior.
66+
67+
### Remove legacy assets only after the modern bootstrap owns all entry points
68+
Legacy static JS/CSS/lib assets should be deleted once no backend processor or documented flow references them anymore. That removal is part of this change, but it should happen after the new bootstrap and packaging path are fully wired so the repository does not temporarily lose a working production entry.
69+
70+
The alternative was to delete the old files immediately and patch the rest afterward, but that would make the migration harder to verify and easier to break midway through implementation.
71+
72+
## Risks / Trade-offs
73+
74+
- **[Risk]** The modern frontend build may emit hashed chunk names that are awkward for the backend to reference directly. → **Mitigation:** define a stable bootstrap entry file and package the full dist directory beneath a dedicated static root.
75+
- **[Risk]** A new public config DTO could accidentally omit fields the frontend currently relies on. → **Mitigation:** derive the DTO from the modern runtime's normalized config usage and keep the spec focused on behavior the bootstrap must preserve.
76+
- **[Risk]** Custom-tool actions may be too limited and push users back toward script injection requests. → **Mitigation:** define a small but extensible action registry that covers current runtime capabilities and supports adding new safe actions over time.
77+
- **[Risk]** Development-mode Vite injection could drift from production behavior. → **Mitigation:** keep one config ingestion path and vary only the source of the frontend module.
78+
- **[Risk]** Removing legacy assets may break untracked references in docs or templates. → **Mitigation:** make legacy asset removal contingent on replacing all processor references and update documentation as part of the migration.
79+
80+
## Migration Plan
81+
82+
1. Introduce the public config payload and modern Halo bootstrap entry while keeping the existing runtime behavior intact.
83+
2. Wire the frontend package build into Gradle resource processing so packaged plugin assets contain the modern bundle.
84+
3. Replace executable custom-tool configuration with declarative action-backed tool definitions in the public config contract.
85+
4. Enable the local development bootstrap path against the same backend-provided config payload.
86+
5. Switch the backend processor to the new production entry path and remove remaining legacy static asset references.
87+
6. Delete the old `live2d-autoload`-based assets after the new integration path is the only reachable bootstrap.
88+
89+
Rollback is straightforward: revert the backend processor and packaged asset path to the legacy bootstrap until the modern entry is fixed.
90+
91+
## Open Questions
92+
93+
- None. The remaining work is implementation planning and wiring rather than product-scope uncertainty.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Why
2+
3+
The modern `packages/live2d` runtime now covers the widget behavior that previously lived in `src/main/resources/static/js/live2d-autoload.js`, but the Halo plugin still boots the legacy script and ships the old static asset set. That split keeps legacy code, blocks a clean frontend/backend integration path, and makes local debugging and release packaging harder than they need to be.
4+
5+
## What Changes
6+
7+
- Replace the backend's legacy `live2d-autoload.min.js` injection with a modern frontend bootstrap entry that starts the Lit-based runtime inside Halo pages.
8+
- Package the frontend build output as plugin static assets during the plugin build so production JARs ship the modern runtime and its chunks without manual copying.
9+
- Define a public backend-to-frontend configuration contract for the widget instead of exposing a broad raw settings object, while preserving the runtime fields the frontend still needs.
10+
- Add a declarative custom-tool extension model so plugin users can configure extra Live2D tools in Halo without injecting arbitrary frontend code.
11+
- Add a development integration path so Halo pages can load the modern frontend from a Vite dev server during local debugging without changing the production bootstrap flow.
12+
- Remove legacy frontend assets and bootstrap code that become unreachable after the modern integration is in place.
13+
14+
## Capabilities
15+
16+
### New Capabilities
17+
- `halo-plugin-frontend-integration`: The Halo plugin boots the modern frontend bundle, packages its build output into plugin assets, and supports both production and local development loading flows.
18+
- `live2d-public-runtime-config`: The backend publishes a dedicated public Live2D runtime config payload for frontend consumption instead of passing a broad settings object directly into inline initialization code.
19+
- `live2d-custom-tool-actions`: Plugin users can declare custom tools in backend settings that bind to a supported set of frontend-exposed Live2D actions without executing arbitrary scripts.
20+
21+
### Modified Capabilities
22+
- `multi-cubism-live2d-rendering`: The renderer integration currently assumes the legacy injected initialization contract remains sufficient; this change updates that requirement so the maintained renderer works with the new Halo bootstrap and public config contract.
23+
24+
## Impact
25+
26+
- Affected backend code: `Live2dInitProcessor`, settings/config mapping, plugin build wiring, and any development-mode plugin bootstrap support.
27+
- Affected frontend code: runtime bootstrap entry, config ingestion, custom tool action handling, asset path expectations, and local development workflow.
28+
- Affected packaged assets: legacy `static/js`, `static/css`, and `static/lib` resources tied to `live2d-autoload` are expected to be retired once the new bundle-based path is active.
29+
- Affected developer workflow: plugin builds and local debugging will depend on the frontend package build/dev flow being wired into the plugin lifecycle.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Halo pages SHALL bootstrap the modern Live2D frontend bundle
4+
The plugin SHALL initialize the widget on Halo pages through the maintained frontend bundle instead of the legacy `live2d-autoload` script.
5+
6+
#### Scenario: Production pages load the packaged frontend entry
7+
- **WHEN** a Halo page includes the Live2D plugin in a production build
8+
- **THEN** the backend MUST emit the modern frontend bootstrap entry from the plugin's packaged static assets
9+
- **AND** the page MUST no longer depend on `static/js/live2d-autoload.min.js` for widget startup
10+
11+
#### Scenario: Frontend bundle starts the existing widget runtime
12+
- **WHEN** the packaged frontend entry executes on a supported page
13+
- **THEN** it MUST initialize the existing modern Live2D runtime used by `packages/live2d`
14+
- **AND** the widget MUST continue rendering and responding through the maintained Lit/Pixi implementation
15+
16+
### Requirement: Plugin builds SHALL package the frontend dist output automatically
17+
The plugin build pipeline SHALL produce and package the frontend bundle and its emitted assets without requiring a manual copy step into source-managed resources.
18+
19+
#### Scenario: Plugin build includes runtime entry and dependent chunks
20+
- **WHEN** the plugin build runs for packaging
21+
- **THEN** the frontend package build output MUST be generated and synchronized into the plugin's packaged static assets
22+
- **AND** the packaged output MUST include the runtime entry file and any emitted chunks or assets required by that entry
23+
24+
#### Scenario: Packaged asset path remains stable for backend injection
25+
- **WHEN** the frontend build emits hashed chunk filenames
26+
- **THEN** the plugin packaging flow MUST still expose a stable entry path that the backend can inject into Halo pages
27+
- **AND** supporting chunks MUST remain resolvable beneath the same packaged static root
28+
29+
### Requirement: Local development SHALL support loading the modern frontend from a dev server
30+
The Halo integration SHALL provide a development path that loads the same runtime from a frontend dev server so developers can debug the widget in Halo pages without rebuilding the plugin for each change.
31+
32+
#### Scenario: Development bootstrap targets the dev server entry
33+
- **WHEN** Live2D frontend development mode is enabled for local debugging
34+
- **THEN** the backend MUST inject the configured frontend dev-server module entry instead of the packaged production asset entry
35+
- **AND** the loaded module MUST consume the same backend-provided runtime config contract as production
36+
37+
#### Scenario: Production bootstrap remains the default
38+
- **WHEN** development mode is not enabled
39+
- **THEN** the backend MUST load the packaged production frontend entry
40+
- **AND** the dev-server path MUST not be required for normal plugin operation

0 commit comments

Comments
 (0)