Some checks failed
Deploy Web Apps / deploy (push) Successful in 5m52s
Build Android APK / build-android-apk (push) Failing after 23m15s
Queue Release Build / prepare (push) Successful in 1m42s
Queue Release Build / build-linux (push) Failing after 9m33s
Queue Release Build / build-windows (push) Successful in 26m5s
Queue Release Build / finalize (push) Has been skipped
58 lines
4.9 KiB
Markdown
58 lines
4.9 KiB
Markdown
# 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.md` for the contract. Update in the same turn a trigger fires (see `agents-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" |
|
|
|
|
## 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.ts` and mirrored in `server/src/websocket/types.ts`) into NgRx actions consumed by domains.
|
|
- The **Plugins** domain consumes plugin manifests loaded by Electron's `plugin-library.ts` and 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 `:4200` in 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 in `electron/preload.ts` and `electron/api/`).
|
|
- WebSocket envelopes from `server/src/websocket/` (typed by `shared-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.*
|