## Summary - Send a serializable Live2D expression snapshot from the stage renderer to the separate Settings window. - Route expression changes back to the renderer that owns the loaded model, with owner ID checks for stale-window isolation. - Add browser regressions for the component boundary and the complete BroadcastChannel round trip. Fixes #2450 ## Verification - `pnpm typecheck` - `pnpm lint` - `../../node_modules/.bin/vitest run --project browser src/components/scenarios/settings/model-settings/live2d.browser.test.ts` - `node_modules/.bin/vitest run --config apps/stage-tamagotchi/vitest.config.ts --project browser apps/stage-tamagotchi/src/renderer/composables/model-settings-runtime.browser.test.ts` - `./node_modules/.bin/electron-vite build` - Vishot Electron capture with the issue Live2D fixture on the merge base and this branch - `sem diff upstream/main...HEAD --no-cosmetics -v --file-exts .ts .tsx` ## Visual changes | Before | After | |---|---| |  |  | | Settings / Models / Live2D expressions: empty list | Settings / Models / Live2D expressions: model entries are available |
Stage UI
Shared core for stage
Button analytics
Register the shared plugin once in each Vue application:
import { trackButtonPlugin } from '@proj-airi/stage-ui/directives/track-button'
createApp(App)
.use(trackButtonPlugin)
.mount('#app')
Buttons that represent a product-analysis click intent can then declare a typed event without wrapping their business handler:
<Button
v-track-button="{ name: 'update_check_clicked', channel: selectedChannel }"
@click="checkForUpdates()"
/>
Keep async outcomes, confirmed state changes, impressions, and lifecycle events in their owning business flows instead of attaching them to the initial click.
Histoire (UI storyboard)
pnpm -F @proj-airi/stage-ui run story:dev
Project structure
- If a story is bound to a specific component, it can be placed beside the component in the
srcfolder. e.g.,MyComponent.story.vue - If a story is not bound to a specific component, then it should be placed in the
storiesfolder. e.g.,MyStory.story.vue