# Voice Session Domain Tracks voice session metadata across client-side navigation and manages the voice workspace UI state (expanded, minimized, hidden). This domain does not touch WebRTC directly; actual connections live in `voice-connection` and `infrastructure/realtime`. ## Module map ``` voice-session/ ├── application/ │ ├── voice-session.facade.ts Tracks active voice session, drives floating controls │ └── voice-workspace.service.ts Workspace mode (hidden/expanded/minimized), focused stream, mini-window position │ ├── domain/ │ ├── voice-session.logic.ts isViewingVoiceSessionServer, buildVoiceSessionRoom │ └── voice-session.models.ts VoiceSessionInfo interface │ ├── infrastructure/ │ └── voice-settings.storage.ts Persists audio device IDs, volumes, bitrate, latency, noise reduction to localStorage │ ├── feature/ │ ├── voice-controls/ Full voice control panel (mic, deafen, devices, screen share, settings) │ └── floating-voice-controls/ Minimal overlay when user navigates away from the voice server │ └── index.ts Barrel exports ``` ## How the pieces connect The facade manages session bookkeeping. The workspace service owns view state. Settings storage provides persistence for user preferences. Neither service opens any WebRTC connections. ```mermaid graph TD VSF[VoiceSessionFacade] VWS[VoiceWorkspaceService] VSS[voiceSettingsStorage] Logic[voice-session.logic] VC[VoiceControlsComponent] FC[FloatingVoiceControlsComponent] Store[NgRx Store] VC --> VSF VC --> VWS VC --> VSS FC --> VSF FC --> VWS VSF --> Logic VSF --> Store VWS --> VSF click VSF "application/voice-session.facade.ts" "Tracks active voice session" _blank click VWS "application/voice-workspace.service.ts" "Workspace mode and focused stream" _blank click VSS "infrastructure/voice-settings.storage.ts" "localStorage persistence for audio settings" _blank click Logic "domain/voice-session.logic.ts" "Pure helper functions" _blank click VC "feature/voice-controls/" "Full voice control panel" _blank click FC "feature/floating-voice-controls/" "Minimal floating overlay" _blank ``` ## Session lifecycle ```mermaid stateDiagram-v2 [*] --> NoSession NoSession --> Active: startSession(info) Active --> Active: checkCurrentRoute(serverId) Active --> NoSession: endSession() state Active { [*] --> ViewingServer ViewingServer --> AwayFromServer: navigated to different server AwayFromServer --> ViewingServer: navigated back / navigateToVoiceServer() } ``` When a voice session is active and the user navigates away from the voice-connected server, `showFloatingControls` becomes `true` and the floating overlay appears. Clicking the overlay dispatches `RoomsActions.viewServer` to navigate back. ## Workspace modes `VoiceWorkspaceService` controls the voice workspace panel state. The workspace is only visible when the user is viewing the voice-connected server. ```mermaid stateDiagram-v2 [*] --> Hidden Hidden --> Expanded: open() Expanded --> Minimized: minimize() Expanded --> Hidden: close() / showChat() Minimized --> Expanded: restore() Minimized --> Hidden: close() Expanded --> Hidden: voice session ends Minimized --> Hidden: voice session ends ``` The minimized mode renders a draggable mini-window. Its position is tracked in `miniWindowPosition` and clamped to viewport bounds on resize. `focusedStreamId` controls which screen-share stream gets the widescreen treatment in expanded mode. ## Voice settings Settings are stored in localStorage under a single JSON key. All values are validated and clamped on load to defend against corrupt storage. | Setting | Default | Range | |---|---|---| | inputDevice | `""` | device ID string | | outputDevice | `""` | device ID string | | inputVolume | 100 | 0 -- 100 | | outputVolume | 100 | 0 -- 100 | | audioBitrate | 96 kbps | 32 -- 256 | | latencyProfile | `"balanced"` | low / balanced / high | | noiseReduction | `true` | boolean | | screenShareQuality | `"balanced"` | low / balanced / high | | askScreenShareQuality | `true` | boolean | | includeSystemAudio | `false` | boolean | `loadVoiceSettingsFromStorage()` and `saveVoiceSettingsToStorage(patch)` are the only entry points. The save function merges the patch with the current stored value so callers only need to pass changed fields.