Files
moeka-project/packages/stage-ui
Garfield Lee e447b15b0d refactor(provider-inference): move provider tests closer to implementations (#2529)
# Refactor: Move Provider Tests to provider-inference

## Summary

Move azure-openai and openrouter-ai provider tests from `stage-ui` to
`provider-inference` package, placing them closer to their
implementations.

## Changes

- **Moved test files**:
-
`packages/stage-ui/src/libs/providers/providers/azure-openai/index.test.ts`
→
`packages/provider-inference/src/providers/cloud/azure-openai/index.test.ts`
-
`packages/stage-ui/src/libs/providers/providers/openrouter-ai/index.test.ts`
→
`packages/provider-inference/src/providers/cloud/openrouter-ai/index.test.ts`

- **Simplified test imports**: Updated tests to use direct provider
definitions instead of `getDefinedProvider()`, removing unnecessary
lookup logic

- **Added dependency**: `@proj-airi/core-agent` as devDependency for
test utilities

## Benefits

1. Tests are co-located with their implementations
2. Direct imports are simpler and more maintainable
3. Better package organization following module boundaries

## Verification

```bash
# Run tests for provider-inference
pnpm -F @proj-airi/provider-inference exec vitest run

# Typecheck
pnpm -F @proj-airi/provider-inference typecheck
```

## Test Results

All tests pass after the refactoring.
2026-09-12 10:25:16 +08:00
..
2026-03-12 05:09:32 +08:00
2026-08-26 20:13:10 +08:00
2026-08-26 20:13:10 +08:00
2026-08-26 20:13:10 +08:00

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 configureForAuthentication for login and logout. It updates global defaults, then reapplies the active card without saving defaults into that card. Use card commands for activation and explicit edits. Settings pages must not save cards from watchers: authentication and 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/

pnpm -F @proj-airi/stage-ui run 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