|
| 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. |
0 commit comments