Outgoing voice was gated on the observer's roster copy of the remote user's voice state, which is signaling gossip. The signal server broadcasts `user_left` for any socket it declares dead, so a suspended laptop or a flaky hop wiped that copy and the observer detached its microphone from a peer that never left the channel - a silent member with no way back through the UI. `decideVoicePathRouting` now closes a path only on positive evidence: we left voice, the peer itself reported another channel or none, or the connection is gone. Missing gossip holds an established path instead. Opening still needs confirmation, so a guess never starts sending; the same rule gates playback, camera video, and the microphone a new connection puts in its first offer. Peers are also asked for their voice state when a connection or data channel comes up, so a rebuilt path re-confirms itself. Alongside it, the microphone can be switched mid-call: capture moves to a device service and rules, the live track is swapped with `replaceTrack` so the session is never renegotiated, and the speaking indicator follows the new stream.
Product Client
Angular 21 renderer for MetoYou / Toju. This package is managed from the repository root, so the main build, test, lint, and Electron integration commands are run there rather than from a local package.json.
Commands
npm run startstarts the Angular dev server.npm run buildbuilds the client todist/client.npm run watchruns the Angular build in watch mode.npm run testruns the product-client Vitest suite.npm run lintruns ESLint across the repo.npm run formatformats Angular HTML templates.npm run sort:propssorts Angular template properties.npm run electron:devornpm run devruns the client with Electron.
Structure
| Path | Description |
|---|---|
src/app/domains/ |
Bounded contexts and public domain entry points |
src/app/infrastructure/ |
Shared technical runtime such as persistence and realtime |
src/app/shared-kernel/ |
Cross-domain contracts and shared models |
src/app/features/ |
App-level composition and transitional feature shells |
src/app/core/ |
Platform adapters, compatibility entry points, and cross-domain technical helpers |
src/app/shared/ |
Shared UI primitives and utilities |
src/app/store/ |
NgRx reducers, effects, selectors, and actions |
public/ |
Static assets copied into the Angular build |
Key Docs
- src/app/domains/README.md
- src/app/shared-kernel/README.md
- src/app/infrastructure/persistence/README.md
- src/app/infrastructure/realtime/README.md
- ../docs/architecture.md
- AGENTS.md
Notes
angular.jsondefines build, serve, and lint targets for the product client.- Product-client tests currently run through the root Vitest setup instead of an Angular
testarchitect target. - If the renderer-to-desktop contract changes, update the Angular bridge, Electron preload API, and IPC handlers together.