Skip to content

Commit d81b4b9

Browse files
LIlGGCopilot
andcommitted
feat: improve live2d widget parity
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 16a6433 commit d81b4b9

32 files changed

Lines changed: 1093 additions & 530 deletions

File tree

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-13
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Context
2+
3+
The Lit-based frontend has already replaced the legacy autoload script for rendering, tips dispatch, and toolbar actions. After narrowing the parity review, the confirmed missing feature is specific: when a configured full TIPS source fails to load, the modern runtime does not fall back to the bundled `live2d-tips.json` file the way the legacy script did.
4+
5+
Other previously discussed items are intentionally excluded from this change. In particular, `hitokoto` handling is currently treated as standardized behavior in the new runtime rather than a confirmed feature regression, and broader lifecycle or config-compatibility items are treated as follow-up optimizations unless they later prove to be user-facing breakages.
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
- Restore the legacy tips fallback contract in a typed, explicit way.
11+
- Keep the modern component architecture, UnoCSS styling, and event-driven runtime unchanged outside the fallback fix.
12+
- Limit the work to the one confirmed user-facing parity gap.
13+
14+
**Non-Goals:**
15+
- Recreate the legacy autoload DOM structure or CSS implementation.
16+
- Change backend API contracts or toolbar behavior.
17+
- Reclassify standardization or optimization items as feature regressions without new evidence.
18+
19+
## Decisions
20+
21+
### Make tips loading return an explicit success-or-fallback signal
22+
The legacy script treated a missing or invalid configured tips file as a trigger to fall back to the bundled default tips. The modern loader currently resolves an empty object on failure, which prevents the fallback branch from being reached. The replacement should make the fallback decision explicit by distinguishing “loaded tip config” from “failed/empty result,” then merging plugin/theme/default tips only after the full source is chosen.
23+
24+
**Alternative considered:** keep the current helper shape and infer failure from empty objects later. This keeps call sites unchanged, but it is brittle because an empty object is also a valid JavaScript value and silently hides fallback decisions.
25+
26+
## Risks / Trade-offs
27+
28+
- **Tips fallback changes may alter current edge-case behavior for malformed custom files** → Keep the merge order unchanged and only change the source-selection decision when the configured full tips file is unusable.
29+
- **A narrow fix can leave adjacent cleanup for later** → Explicitly document that the current scope is intentionally limited to the confirmed TIPS fallback regression.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Why
2+
3+
The modern Live2D runtime already covers the rendering, tool, and most tip flows. After narrowing the comparison with `src/main/resources/static/js/live2d-autoload.js`, the only clear missing user-facing feature is that a broken or unavailable custom full TIPS source no longer falls back to the bundled default TIPS file.
4+
5+
## What Changes
6+
7+
- Restore the legacy full-TIPS fallback contract so the bundled default `live2d-tips.json` is used when a configured custom `tipsPath` resource is missing or invalid.
8+
- Keep the modern Lit and UnoCSS architecture while correcting the source-selection logic for full TIPS loading.
9+
- Add regression coverage for the fallback chain so future refactors do not silently drop default TIPS behavior again.
10+
- Record that other previously discussed items, such as `hitokoto` response handling, are currently treated as standardization or optimization rather than confirmed missing features.
11+
12+
## Capabilities
13+
14+
### New Capabilities
15+
- `live2d-widget-behavior-parity`: Preserve the remaining confirmed user-facing parity gap by restoring bundled default TIPS fallback when custom full TIPS loading fails.
16+
17+
### Modified Capabilities
18+
19+
## Impact
20+
21+
- Affected code: `packages/live2d/src/events/tip-events.ts`, `packages/live2d/src/helpers/loadTipsResource.ts`
22+
- Affected behavior: fallback from configured `tipsPath` to bundled default full TIPS
23+
- No backend API or UI surface expansion is required; changes stay within the existing frontend tip-loading flow
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Full tips sources SHALL fall back to the bundled defaults when unavailable
4+
The runtime SHALL continue using plugin tips and theme tips, but it MUST fall back to the bundled full tips file when a configured full tips source cannot be loaded or parsed.
5+
6+
#### Scenario: Missing custom tips file falls back to bundled defaults
7+
- **WHEN** `tipsPath` is configured and the referenced resource cannot be fetched successfully
8+
- **THEN** the runtime MUST load the bundled `live2d-tips.json` file as the full tips source
9+
- **AND** plugin-level and theme-level tips MUST still be merged with that fallback source using the existing priority order
10+
11+
#### Scenario: Invalid custom tips file falls back to bundled defaults
12+
- **WHEN** `tipsPath` points to a resource whose response cannot be parsed into the expected tips structure
13+
- **THEN** the runtime MUST ignore that invalid full tips source
14+
- **AND** it MUST continue initialization with the bundled default full tips file
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## 1. TIPS fallback parity
2+
3+
- [x] 1.1 Refactor full tips loading so missing or invalid configured `tipsPath` resources fall back to the bundled `live2d-tips.json` file before merge.
4+
- [x] 1.2 Add regression coverage for the fallback chain so default full TIPS remain available when custom full TIPS loading fails.
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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
## Context
2+
3+
`packages/live2d` has replaced most of `live2d-autoload.js` with Lit components, typed config normalization, and modular tools. The remaining gaps are not renderer-level gaps anymore; they are compatibility gaps in widget lifecycle and console status behavior.
4+
5+
Today the modern widget unmounts the canvas subtree when it is hidden, which forces model recreation and re-runs initialization flows on reopen. The modern runtime also normalizes `consoleShowStatu`, but it only logs model-load completion instead of preserving the legacy console status output.
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
- Preserve the modern Lit/UnoCSS architecture while restoring the remaining user-visible legacy behaviors that still matter.
11+
- Keep widget hide/show behavior stateful within the same page session so reopen does not recreate the runtime unnecessarily.
12+
- Reintroduce the `consoleShowStatu` compatibility behavior in a maintainable way.
13+
14+
**Non-Goals:**
15+
- Reintroduce legacy DOM structure, legacy CSS files, or script-loader based initialization.
16+
- Change backend APIs or introduce new server-side endpoints.
17+
- Change the current hitokoto implementation beyond leaving it in its present modernized state.
18+
- Achieve byte-for-byte parity with the old script where the modern implementation is intentionally better.
19+
20+
## Decisions
21+
22+
### Keep the widget subtree mounted and separate visibility from initialization
23+
The modern runtime should stop using conditional rendering to remove the widget subtree on hide. Instead, the widget should mount once, keep the canvas/tools/tips subtree alive, and switch between visible and hidden presentation states.
24+
25+
This preserves the current model instance, avoids repeat model fetches on reopen, and matches the legacy expectation that dismissing the widget is a temporary hide instead of a teardown. The alternative was to keep the current remount behavior and patch over the regressions with cache/localStorage state, but that still recreates listeners and model state and would not match the legacy behavior as closely.
26+
27+
### Extract console compatibility output into a dedicated helper
28+
The legacy `consoleShowStatu` behavior combined a compatibility banner with status logging. The modern runtime should implement that as an explicit helper with stable metadata constants instead of reproducing the obfuscated legacy snippet.
29+
30+
This keeps the behavior readable and testable while preserving the observable contract exposed by the legacy config flag. The alternative was to leave the partial implementation in `model.ts`, but that would continue to advertise compatibility without actually providing the legacy output.
31+
32+
## Risks / Trade-offs
33+
34+
- **[Risk]** Keeping the widget mounted means the renderer stays alive while hidden. → **Mitigation:** only preserve state within the current page session and continue using the existing explicit quit/toggle controls instead of background polling or reloading.
35+
- **[Risk]** Console compatibility output may diverge from the old banner formatting. → **Mitigation:** preserve the same information contract (`version`, `updateTime`, and status intent) without copying the obfuscated implementation.
36+
37+
## Migration Plan
38+
39+
1. Add spec coverage for widget lifecycle parity and console status compatibility.
40+
2. Refactor the widget visibility flow so quit/toggle hide the mounted runtime instead of tearing it down.
41+
3. Add a console compatibility helper and wire it to normalized config flags.
42+
4. Verify the existing modern runtime still preserves the already-migrated renderer and tips behaviors.
43+
44+
## Open Questions
45+
46+
- None. The remaining gaps are well-defined by the legacy script and current implementation.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Why
2+
3+
`packages/live2d` has already modernized most of the legacy `live2d-autoload.js` runtime, but a few legacy behaviors still have no equivalent or have regressed during the migration. Capturing those gaps now gives the project a concrete parity target before the old script stops being the functional reference.
4+
5+
## What Changes
6+
7+
- Audit the remaining behavior gaps between `packages/live2d` and `src/main/resources/static/js/live2d-autoload.js`, then codify the ones that should still be preserved in the modern runtime.
8+
- Restore the legacy dismiss/reopen behavior so hiding the widget does not unnecessarily destroy and recreate the mounted runtime state during the same page session.
9+
- Restore the `consoleShowStatu` compatibility contract so the modern runtime still exposes the legacy console status output beyond the current model-load log line.
10+
11+
## Capabilities
12+
13+
### New Capabilities
14+
- None.
15+
16+
### Modified Capabilities
17+
- `live2d-widget-behavior-parity`: expand parity requirements to cover the remaining legacy widget lifecycle and console status behaviors that are still missing from the modernized frontend.
18+
19+
## Impact
20+
21+
- Affected code: `packages/live2d/src/components/*`, `packages/live2d/src/live2d/model.ts`, and related config/runtime helpers where compatibility behavior is defined.
22+
- Affected behavior: widget hide/show lifecycle, console compatibility output, and parity expectations tracked in OpenSpec.
23+
- No backend API changes are expected; the work stays within the existing frontend runtime and current server contracts.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Widget dismissal SHALL preserve mounted runtime state during the current page session
4+
The modern widget SHALL treat quit and toggle dismissal as a visibility change instead of tearing down the mounted runtime subtree, so the current model and tool state remain available when the widget is reopened on the same page.
5+
6+
#### Scenario: Quit hides the mounted widget without resetting the current model
7+
- **WHEN** a user dismisses the widget through the `quit` tool after switching to another model or texture
8+
- **THEN** the widget MUST transition to a hidden state without recreating the Live2D runtime immediately
9+
- **AND** reopening the widget during the same page session MUST continue from the current model state instead of reloading the default model
10+
11+
#### Scenario: Reopening after dismissal does not duplicate initialization side effects
12+
- **WHEN** the widget is hidden and then shown again on the same page
13+
- **THEN** the runtime MUST not register duplicate tip listeners or re-run first-open initialization solely because of that visibility change
14+
15+
### Requirement: Console status compatibility SHALL remain available through the legacy config flag
16+
When `consoleShowStatus` or its legacy alias `consoleShowStatu` is enabled, the modern runtime SHALL preserve the observable console compatibility output expected from the legacy widget runtime.
17+
18+
#### Scenario: Console compatibility output includes widget metadata and load status
19+
- **WHEN** the widget initializes with console status output enabled
20+
- **THEN** the runtime MUST emit readable console output that includes the plugin version/update metadata represented by the legacy runtime
21+
- **AND** it MUST continue emitting model load completion status for the requested model selection
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## 1. Widget lifecycle parity
2+
3+
- [x] 1.1 Refactor `Live2dWidget` and related components so hide/show toggles visibility without unmounting the mounted runtime subtree during the same page session.
4+
- [x] 1.2 Keep quit/toggle dismissal behavior compatible with the legacy 24-hour suppression flow while preventing duplicate initialization side effects on reopen.
5+
6+
## 2. Compatibility behavior restoration
7+
8+
- [x] 2.1 Add a readable console compatibility helper that restores the legacy `consoleShowStatu` metadata/status output through the normalized config flag.
9+
10+
## 3. Validation
11+
12+
- [x] 3.1 Add or update coverage for the restored parity behaviors at the spec-relevant frontend layer.
13+
- [x] 3.2 Run the existing package build and relevant checks to confirm the modern runtime still works after the parity fixes.

0 commit comments

Comments
 (0)