5.3 KiB
5.3 KiB
Product Client (toju-app)
Owns the user-facing Angular 21 desktop chat experience: rendering and orchestrating chat, voice, screen-share, plugin UI, theming, and identity flows on top of the Electron window.api bridge and the server WebSocket. Houses every bounded context the end user interacts with, organized DDD-style under src/app/domains/.
Format reference:
- Vocabulary — bold term, one-sentence definition, aliases to avoid.
- Relationships — bullets with bold terms and cardinality.
- Boundaries / IO — what this subdomain exposes and consumes.
- Invariants — rules that always hold.
- Flagged ambiguities — terms in dispute with proposed resolutions.
See
agents-docs/AGENTS_CONTEXT.mdfor the contract. Update in the same turn a trigger fires (seeagents-docs/AGENT_WORKFLOW.md§ CONTEXT.md upkeep).
Vocabulary
| Term | Definition | Aliases to avoid |
|---|---|---|
| Domain | A bounded context under src/app/domains/<name>/ that owns its own models, services, NgRx slice, and components — e.g. chat, voice-session, plugins. |
"module", "feature" (Angular reserves these for different things) |
| Shared kernel | Cross-domain contracts in src/app/shared-kernel/ — wire-format models, P2P transfer utilities, plugin contracts, signaling contracts — imported by multiple domains. |
"common", "core" |
| Infrastructure | Technical-runtime concerns shared by domains: persistence/ (client-side store wiring), realtime/ (WebSocket adapter), and mobile/ (Capacitor/native facades). Not a domain. |
"shared", "lib" |
| Mobile facade | An injectable service under infrastructure/mobile/ that wraps Capacitor or web adapters so domains never import @capacitor/* directly. |
"Capacitor service", "native plugin" |
| Capacitor SQLite backend | Native persistence path selected by DatabaseService when PlatformService.isCapacitor is true; uses @capacitor-community/sqlite instead of IndexedDB. |
"mobile database", "Capacitor DB" |
| Rules file | A pure-function module suffixed *.rules.ts that encodes domain logic without Angular or NgRx dependencies — easy to unit-test. |
"helpers", "utils" |
| Custom emoji | User-created image emoji assets stored locally, synced peer-to-peer, and referenced from messages/reactions by stable :emoji[id](name) tokens. |
"sticker", "emote" |
| App locale | The active UI language for the product client, resolved by resolveAppLocale() in core/i18n/; only en is shipped today. |
"language", "i18n locale" |
| Translation catalog | JSON string tables under public/i18n/catalog/*.json, merged to public/i18n/en.json via npm run i18n:sync, loaded at startup by AppI18nService. |
"locale file", "messages file" |
| Client instance | Stable per-install UUID (metoyou.clientInstanceId) sent on WebSocket identify and voice-state payloads so the signaling server can route multi-device sessions. |
"device id", "session id" |
| Voice owner connection | The single client instance whose clientInstanceId matches the user's active voiceState.clientInstanceId and therefore owns mic/WebRTC for that identity. |
"active voice client" |
Relationships
- A Domain owns zero or more Components, Services, NgRx slices, and Rules files.
- A Domain may consume Shared kernel contracts but must never import from another Domain directly — cross-domain coupling goes through the shared kernel or NgRx events.
- The Realtime infrastructure adapts server WebSocket envelopes (defined in
src/app/shared-kernel/signaling-contracts.tsand mirrored inserver/src/websocket/types.ts) into NgRx actions consumed by domains. - The Plugins domain consumes plugin manifests loaded by Electron's
plugin-library.tsand exposes a sandboxed runtime that other domains may hook into. - The Custom emoji domain owns custom emoji assets and usage ranking; the Chat domain consumes it for reactions and composer insertion.
Boundaries / IO
- Exposes: the Angular SPA bundle served at
:4200in dev (ng serve) or mounted by Electron in production; NgRx store events that other domains in this subdomain consume; UI surface to the end user. - Consumes:
window.api.*IPC surface exposed by Electron preload (defined inelectron/preload.tsandelectron/api/).- WebSocket envelopes from
server/src/websocket/(typed byshared-kernel/signaling-contracts.ts). - REST endpoints from the server's
server/src/routes/(server directory, invites, join requests, link metadata, klipy). - Plugin manifests resolved by Electron's plugin library.
Invariants
- A Domain never imports from another Domain directly — only through the Shared kernel or NgRx actions.
- Rules files stay framework-free (no Angular, no NgRx) so they can be Vitest-tested as plain functions.
- Wire-format types (anything that crosses the WebSocket or IPC boundary) live in Shared kernel, never inside a single domain.
Flagged ambiguities
- None recorded yet — add entries when a domain term resists clean definition.
Agent-procedural rules (TDD, lint, etc.) live in /AGENTS.md. Per-domain implementation detail lives in src/app/domains/<name>/README.md. This file is the bounded-context domain artefact for the product client as a whole.