Files
Toju/toju-app/CONTEXT.md
Myx 80d7728e66
All checks were successful
Queue Release Build / prepare (push) Successful in 19s
Deploy Web Apps / deploy (push) Successful in 8m19s
Queue Release Build / build-windows (push) Successful in 27m48s
Queue Release Build / build-linux (push) Successful in 47m35s
Queue Release Build / build-android (push) Successful in 21m15s
Queue Release Build / finalize (push) Successful in 2m30s
fix: No longer displays edited on all messages and fix Disconnected from signaling server on multiple clients
2026-06-07 15:05:12 +02:00

5.4 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.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"
Client instance Stable per-tab UUID (metoyou.clientInstanceId in sessionStorage) sent on WebSocket identify and voice-state payloads so the signaling server can route multi-device sessions without evicting other tabs or synced profiles. "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.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.