Files
nanakura dc26878386
CI / Lint (push) Canceled after 0s
CI / Build Test (stage-web) (push) Canceled after 0s
CI / Build Test (ui-loading-screens) (push) Canceled after 0s
CI / Build Test (ui-transitions) (push) Canceled after 0s
CI / Unit Test (push) Canceled after 0s
CI / Type Check (push) Canceled after 0s
CI / Check Provenance (push) Canceled after 0s
Sync Labels / sync-labels (push) Successful in 1m43s
chore: prune unused apps and services for the moeka fork
- Remove stage-tamagotchi, stage-pocket, server, and engine workspaces with their workflows, addons, and docs
- Switch the toolchain from pnpm to bun, replace lockfiles with bun.lock
- Rewrite remaining product references to Moeka
2026-09-14 16:40:49 +07:00

2.2 KiB

Stage UI

Shared core for stage

Character-card module settings

The card store owns three distinct states:

  • moduleDefaults stores global provider, model, voice, and display selections.
  • Each card stores explicit overrides. An empty string means inherit.
  • Module stores expose the resolved runtime selections used by the application.

Use card commands for activation and explicit edits. Settings pages must not save cards from watchers: remote snapshots also trigger them. The synchronization leader owns these commands; followers receive snapshots.

Models inherit only within the same provider. Voices also require the same model. A different provider without a model stays unconfigured rather than receiving an unrelated model id. The editor requires a model for an explicit chat or vision provider unless that model can be inherited safely.

Defaults are seeded once from the current runtime on upgrade. This cannot recover historical global values that an older card already overwrote. Existing speech-noop selections are preserved because they may represent intentional silence. Users can explicitly choose Inherit global settings in the editor; importing or saving an unrelated card field does not change it.

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)

https://histoire.dev/

bun run --filter @proj-airi/stage-ui story:dev

Project structure

  1. If a story is bound to a specific component, it can be placed beside the component in the src folder. e.g., MyComponent.story.vue
  2. If a story is not bound to a specific component, then it should be placed in the stories folder. e.g., MyStory.story.vue