# Voice & video calls — handoff

## Summary

Paarrot supports Matrix RTC voice/video calls via MSC3401 (`org.matrix.msc3401.call.member`) and LiveKit. Calls can be docked in the sidebar, support screen share, remote cursors, mute/deafen, and are wired to the local HTTP API for external control.

## User-facing behavior

- Join/start calls from room UI (when homeserver exposes RTC/LiveKit via `.well-known`)
- Docked call panel in sidebar; full overlay mode
- Mute, deafen, camera, screen share controls
- Call participants shown in room nav (`CallParticipantsIndicator`)

## Architecture

```
useCallService (CallProvider)
  → CallService (LiveKit room, Matrix call member state)
    → useRtcConfig: .well-known m.rtc + LiveKit focus
  → UI: CallOverlay, DockedCallPanel, SidebarDockedCallPanel
  → getCallService() global ref for Electron API (paarrot-api.ts)
```

## Key files

| Path | Role |
|------|------|
| `cinny/src/app/features/call/useCall.ts` | React context, global CallService ref |
| `cinny/src/app/features/call/CallService.ts` | Core call logic |
| `cinny/src/app/features/call/types.ts` | CallState, ActiveCall, events |
| `cinny/src/app/features/call/useRtcConfig.ts` | Homeserver RTC / LiveKit discovery |
| `cinny/src/app/features/call/CallOverlay.tsx` | Full-screen call UI |
| `cinny/src/app/features/call/DockedCallPanel.tsx` | Docked UI |
| `cinny/src/app/features/call/SidebarDockedCallPanel.tsx` | Sidebar variant |
| `cinny/src/app/features/call/useRemoteCursor.ts` | Screen-share cursor sync |
| `cinny/src/app/features/call/CallSounds.ts` | Join/leave sounds |
| `cinny/src/app/features/room-nav/CallParticipantsIndicator.tsx` | Nav badge |
| `cinny/src/types/matrix/room.ts` | `StateEvent.CallMember` |

## Data model

| Key | Scope | Notes |
|-----|-------|-------|
| `org.matrix.msc3401.call.member` | Room state | Call membership |
| LiveKit JWT / room | Ephemeral | From homeserver RTC focus |

Call mute/deafen/video state lives in `CallService` memory, not Matrix account data.

## Dependencies

- `livekit-client` (verify in package.json)
- Homeserver `.well-known` RTC configuration
- Browser/Electron media permissions (mic, camera, screen capture)
- Electron `desktopCapturer` for screen share (main process)

## Integration points

- **local-api**: mute/deafen/status endpoints
- **Stream Deck**: uses call mute/deafen via API
- **Room nav**: participant indicators
- **Electron main**: screen capture IPC if applicable

## Testing

### Manual

1. Use a homeserver with Matrix RTC + LiveKit configured.
2. Start voice call in a room; verify audio both ways.
3. Toggle mute/deafen; verify API `/status` reflects state.
4. Screen share; verify remote cursor if enabled.
5. Dock/undock call panel.

### Automated

- None identified

## Known issues & gotchas

- `callSupported` false until `.well-known` RTC fetch completes — UI should hide call buttons when unsupported
- `getCallService()` must be set before API mute actions work
- Platform differences: screen capture APIs differ Electron vs browser vs Android

## Future work

- Call recording, noise suppression settings
- Better reconnection / call transfer
- MSC3401 spec tracking as it stabilizes

## Related docs

- [local-api HANDOFF](../local-api/HANDOFF.md)
- Matrix MSC3401 (upstream spec)

## Add your extra things here

- `fetchWellKnownWithRTC`, `getLiveKitFocus`, `getLiveKitHomeserverPriority` in `useRtcConfig.ts`
- Global singleton: `globalCallServiceInstance` in `useCall.ts` for non-React consumers
