## Summary Address maintainer feedback from #1622 ([@nekomeowww's comment](https://github.com/moeru-ai/airi/pull/1622#discussion_r2342632282)). The project already uses `async-mutex` in other packages (`stage-tamagotchi`, `electron-screen-capture`), and it's in the workspace catalog. This replaces the custom `AsyncMutex` implementation with the shared dependency. - Replace `AsyncMutex.run()` → `Mutex.runExclusive()` - Replace `AsyncMutex.reset()` → `Mutex.cancel()` - Remove `async-mutex.ts` and its unit tests - Remove `AsyncMutex` from inference barrel exports - Add `async-mutex` as direct dependency of `@proj-airi/stage-ui` **Regarding `@moeru/eventa` suggestion** ([comment](https://github.com/moeru-ai/airi/pull/1622#discussion_r2342631692)): The `waitForMessage` pattern is a thin request-response abstraction over `postMessage`, where the worker is driven by `@huggingface/transformers` internally. Eventa's transport-agnostic RPC is better suited for bidirectional channels (Electron IPC, WebSocket) than for this one-way "post and wait" pattern. No change for now. ## Test plan - [x] `pnpm exec vitest run packages/stage-ui/src/libs/inference/` — 11 tests pass (4 removed with custom impl) - [x] `pnpm -F @proj-airi/stage-ui typecheck` — no errors - [x] `pnpm lint:fix` — no new errors in changed files --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Stage UI
Shared core for stage
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