Files
2026-05-27 23:29:53 +01:00

42 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @suaveplan/react-audio-stream — hooks + components (chunk C)
- **Target path**: `packages/ui/react/react-audio-stream/`
- **Tier**: browser/react
- **Date**: 2026-05-23
- **Status**: PROPOSED — chunk C of the 4-chunk `enterprise-react-audio-stream` split.
## Why
Chunks A (session-core) and B (transports) deliver the headless engine — the FSM, transports, source-plugin bridge, error codes, telemetry. This chunk delivers the React surface that consumers actually touch: a Provider, 6 hooks, 4 accessible components. Transport-share concerns (the 7th hook and its component indicator) live in chunk D so this chunk stays disjoint.
## What changes
1. **`<StreamProvider>`** — `src/provider/stream-provider.tsx` wires a `StreamSession` instance into React context. `sessionFactory` injectable (consumer creates the session with their chosen transport adapter); StrictMode-safe singleton; surfaces `'state-changed'`/`'interruption'` events into hook subscribers via a typed event bus.
2. **Hooks** (each in its own feature dir with `.ts` + `.types.ts` + `.test.tsx` + `.md` + `index.ts`):
- `useStreamSession()` — returns the session + current FSM state + lifecycle methods (`connect`, `join`, `leave`, `disconnect`)
- `useStreamRoom()` — returns `{ roomId, isJoined, joinedAt, participants }` derived from session state
- `useStreamPeers()` — returns live `Peer[]` (id + display name + connection state + RTT)
- `useStreamMixer()` — exposes per-peer `setVolume`, `setMute`, `setSolo` (DAW solo semantics — when any peer is soloed, every non-soloed peer's track-volume → 0); reads from each peer chain's `setTrackVolume`/`setTrackMute`/`setTrackSolo`
- `useStreamLevels(peerId?)` — returns realtime peak/RMS for the local mic or the named peer; piggybacks on `@suaveplan/audio-analysis-spectrum`'s level computation via the peer chain's analyzer slot
- `useStreamRecording()` — `start()`, `stop()` returning a `Blob`; mixes the active session output (or a subset of peers) into a single audio file via `audio-engine-browser`'s offline render
- `useStreamPermissions()` — wraps `navigator.mediaDevices.getUserMedia` permission state with the typed `MediaPermissionState` enum
3. **Components** (each with `.tsx` + `.types.ts` + `.test.tsx` + `.stories.tsx` + `.md` + `index.ts`):
- `<StreamRoom>` — controlled wrapper exposing room state + join/leave controls; render-prop pattern for layout flexibility
- `<StreamMixer>` — per-peer mixer strip with level meter + mute/solo buttons + volume slider; delegates to `useStreamMixer()`
- `<StreamRecorder>` — record/stop button with duration display; delegates to `useStreamRecording()`
- `<StreamPermissionsBanner>` — permission state banner with explicit request button; ARIA-live for state changes
4. **Subpath exports** — `package.json` adds `./provider`, `./hooks`, `./hooks/use-stream-session` (and 5 more), `./components`, `./components/stream-room` (and 3 more).
## Requirements landed by this chunk
From the original spec:
- Per-peer stream mixer (full — `<StreamMixer>` + `useStreamMixer`)
- Subpath exports for `./provider`, `./hooks/*`, `./components/*`
## Impact
- ~35 files (6 hooks × 5 + 4 components × 6 + provider + barrel touches).
- Imports `StreamSession`, `StreamSourcePlugin`, `ReactAudioStreamError` from chunk A — local-mirror types in tests if A hasn't merged.
- Imports nothing from chunk B (consumers wire transports themselves via the session factory).
- No spec fold / no archive — chunk D performs both.