Files
skills/docs/ui/react/react-audio-stream/README.md
2026-05-29 08:20:56 +01:00

133 lines
14 KiB
Markdown

# @suaveplan/react-audio-stream - Stream-tier React surface for the Suaveplan audio family — session-core foundation (chunk A). Provides StreamSession (FSM lifecycle), InterruptionClassifier, StreamSourcePlugin, error codes, and OTel instruments. Transports, hooks, components, and transport-share land in chunks B/C/D.
React 19 surface for live audio streaming sessions: a Provider, six hooks, and four
accessible components. Chunk C of the four-chunk `enterprise-react-audio-stream` split.
> Chunks A (session-core) and B (transports) deliver the headless engine. Chunk D
> delivers `useTransportShare` and its banner. This chunk delivers the React surface
> consumers actually touch.
<!-- begin:auto-docs -->
## Documentation tree
- [shared-transport-indicator](./src/components/shared-transport-indicator/shared-transport-indicator.md) — `SharedTransportIndicator` renders the spec-mandated "Shared transport active" badge whenever the local peer is in `'re…
- [shared-transport-indicator](./src/components/shared-transport-indicator/shared-transport-indicator.md) — `SharedTransportIndicator` renders the spec-mandated "Shared transport active" badge whenever the local peer is in `'re…
- [stream-mixer](./src/components/stream-mixer/stream-mixer.md) — `<StreamMixer>` renders one strip per remote peer, each with a volume range slider, a mute toggle, a solo toggle, and a…
- [stream-mixer](./src/components/stream-mixer/stream-mixer.md) — `<StreamMixer>` renders one strip per remote peer, each with a volume range slider, a mute toggle, a solo toggle, and a…
- [stream-permissions-banner](./src/components/stream-permissions-banner/stream-permissions-banner.md) — `<StreamPermissionsBanner>` is the visible counterpart to `useStreamPermissions()`. It renders a `role="status"` ARIA-l…
- [stream-permissions-banner](./src/components/stream-permissions-banner/stream-permissions-banner.md) — `<StreamPermissionsBanner>` is the visible counterpart to `useStreamPermissions()`. It renders a `role="status"` ARIA-l…
- [stream-recorder](./src/components/stream-recorder/stream-recorder.md) — `<StreamRecorder>` is a record/stop control with a live duration display. It delegates recording lifecycle to `useStrea…
- [stream-recorder](./src/components/stream-recorder/stream-recorder.md) — `<StreamRecorder>` is a record/stop control with a live duration display. It delegates recording lifecycle to `useStrea…
- [stream-room](./src/components/stream-room/stream-room.md) — `<StreamRoom>` is the headless layout primitive for the room scope. It renders no markup of its own — instead it expose…
- [stream-room](./src/components/stream-room/stream-room.md) — `<StreamRoom>` is the headless layout primitive for the room scope. It renders no markup of its own — instead it expose…
- [error-codes](./src/error-codes/error-codes.md) — The error-codes module is the canonical source of truth for every failure mode published by `@suaveplan/react-audio-str…
- [error-codes](./src/error-codes/error-codes.md) — The error-codes module is the canonical source of truth for every failure mode published by `@suaveplan/react-audio-str…
- [internal](./src/hooks/internal/use-stream-context.md) — Internal helper consumed by every public chunk-C hook. It reads the `StreamReactContext`, returns the non-null `{ sessi…
- [use-stream-context](./src/hooks/internal/use-stream-context.md) — Internal helper consumed by every public chunk-C hook. It reads the `StreamReactContext`, returns the non-null `{ sessi…
- [use-stream-levels](./src/hooks/use-stream-levels/use-stream-levels.md) — `useStreamLevels` projects realtime peak/RMS samples out of the session. Called without an argument it returns the loca…
- [use-stream-levels](./src/hooks/use-stream-levels/use-stream-levels.md) — `useStreamLevels` projects realtime peak/RMS samples out of the session. Called without an argument it returns the loca…
- [use-stream-mixer](./src/hooks/use-stream-mixer/use-stream-mixer.md) — `useStreamMixer` exposes per-peer mixer controls — level, mute, solo, and the per-peer FX slot count — and implements D…
- [use-stream-mixer](./src/hooks/use-stream-mixer/use-stream-mixer.md) — `useStreamMixer` exposes per-peer mixer controls — level, mute, solo, and the per-peer FX slot count — and implements D…
- [use-stream-peers](./src/hooks/use-stream-peers/use-stream-peers.md) — `useStreamPeers` returns the live remote-peer roster as a stable `readonly Peer[]`. Each peer entry carries the partici…
- [use-stream-peers](./src/hooks/use-stream-peers/use-stream-peers.md) — `useStreamPeers` returns the live remote-peer roster as a stable `readonly Peer[]`. Each peer entry carries the partici…
- [use-stream-permissions](./src/hooks/use-stream-permissions/use-stream-permissions.md) — `useStreamPermissions` exposes the microphone permission state with the typed `MediaPermissionState` enum (`"granted" |…
- [use-stream-permissions](./src/hooks/use-stream-permissions/use-stream-permissions.md) — `useStreamPermissions` exposes the microphone permission state with the typed `MediaPermissionState` enum (`"granted" |…
- [use-stream-recording](./src/hooks/use-stream-recording/use-stream-recording.md) — `useStreamRecording` exposes the start/stop controls for capturing a session as a single audio file. The hook delegates…
- [use-stream-recording](./src/hooks/use-stream-recording/use-stream-recording.md) — `useStreamRecording` exposes the start/stop controls for capturing a session as a single audio file. The hook delegates…
- [use-stream-room](./src/hooks/use-stream-room/use-stream-room.md) — `useStreamRoom` projects the current room scope out of the session: the room id (or `null` when the session is not conn…
- [use-stream-room](./src/hooks/use-stream-room/use-stream-room.md) — `useStreamRoom` projects the current room scope out of the session: the room id (or `null` when the session is not conn…
- [use-stream-session](./src/hooks/use-stream-session/use-stream-session.md) — `useStreamSession` is the entry-point hook for chunk-C consumers. It returns the live `StreamSession` instance, its cur…
- [use-stream-session](./src/hooks/use-stream-session/use-stream-session.md) — `useStreamSession` is the entry-point hook for chunk-C consumers. It returns the live `StreamSession` instance, its cur…
- [use-transport-share](./src/hooks/use-transport-share/use-transport-share.md) — `useTransportShare` is the React entry point to the transport-share subsystem. It composes a `TransportShareCoordinator…
- [use-transport-share](./src/hooks/use-transport-share/use-transport-share.md) — `useTransportShare` is the React entry point to the transport-share subsystem. It composes a `TransportShareCoordinator…
- [stream-context](./src/provider/stream-context.md) — `StreamReactContext` is the single React context that publishes the live `StreamSession` to every chunk-C hook and comp…
- [stream-provider](./src/provider/stream-provider.md) — `<StreamProvider>` is the singleton React surface that owns the live `StreamSession` for a tree. It calls the caller-su…
- [session](./src/session/reconnect.md) — The reconnect module owns the auto-reconnect lifecycle that `StreamSession` runs after a disconnect interruption. It wr…
- [interruption-classifier](./src/session/interruption-classifier/interruption-classifier.md) — `InterruptionClassifier` maps a structural disconnect cause to exactly one of `network | media-permission | signalling…
- [interruption-classifier](./src/session/interruption-classifier/interruption-classifier.md) — `InterruptionClassifier` maps a structural disconnect cause to exactly one of `network | media-permission | signalling…
- [session-fsm](./src/session/session-fsm/session-fsm.md) — The `session-fsm` module declares the canonical seven-state finite state machine that drives every `StreamSession`. Bui…
- [session-fsm](./src/session/session-fsm/session-fsm.md) — The `session-fsm` module declares the canonical seven-state finite state machine that drives every `StreamSession`. Bui…
- [stream-session](./src/session/stream-session/stream-session.md) — `StreamSession` is the public face of the stream-tier session core. It wraps the FSM, the transport adapter, the interr…
- [stream-session](./src/session/stream-session/stream-session.md) — `StreamSession` is the public face of the stream-tier session core. It wraps the FSM, the transport adapter, the interr…
- [stream-source-plugin](./src/session/stream-source-plugin/stream-source-plugin.md) — `StreamSourcePlugin` is the bridge that lets a remote peer's `MediaStream` participate in the audio engine's plugin-cha…
- [stream-source-plugin](./src/session/stream-source-plugin/stream-source-plugin.md) — `StreamSourcePlugin` is the bridge that lets a remote peer's `MediaStream` participate in the audio engine's plugin-cha…
- [reconnect](./src/session/reconnect.md) — The reconnect module owns the auto-reconnect lifecycle that `StreamSession` runs after a disconnect interruption. It wr…
- [telemetry](./src/telemetry/telemetry.md) — The telemetry module owns every OpenTelemetry instrument published by `@suaveplan/react-audio-stream`. Concentrating th…
- [telemetry](./src/telemetry/telemetry.md) — The telemetry module owns every OpenTelemetry instrument published by `@suaveplan/react-audio-stream`. Concentrating th…
- [control-channel](./src/transport-share/control-channel.md) — The control-channel module is the wire-protocol heart of transport sharing. It defines the envelope shapes that flow be…
- [share-state](./src/transport-share/share-state.md) — The share-state module owns the role-machine that decides whether a peer is in `'local'`, `'driving'`, or `'receiving'`…
- [transports](./src/transports/transport.md) — The `transport.types.ts` module declares the structural contract that `StreamSession` (authored in chunk A of the `@sua…
- [trystero](./src/transports/trystero/trystero-transport.md) — `TrysteroTransport` is the serverless `StreamTransportAdapter` powered by `@suaveplan/network-discovery-trystero`. It p…
- [trystero-transport](./src/transports/trystero/trystero-transport.md) — `TrysteroTransport` is the serverless `StreamTransportAdapter` powered by `@suaveplan/network-discovery-trystero`. It p…
- [webrtc](./src/transports/webrtc/webrtc-transport.md) — `WebRtcTransport` is the low-latency default `StreamTransportAdapter` for `@suaveplan/react-audio-stream`. It wires a c…
- [webrtc-transport](./src/transports/webrtc/webrtc-transport.md) — `WebRtcTransport` is the low-latency default `StreamTransportAdapter` for `@suaveplan/react-audio-stream`. It wires a c…
- [websocket](./src/transports/websocket/websocket-transport.md) — `WebSocketTransport` is the fallback `StreamTransportAdapter` consumers choose when a corporate or carrier NAT blocks W…
- [websocket-transport](./src/transports/websocket/websocket-transport.md) — `WebSocketTransport` is the fallback `StreamTransportAdapter` consumers choose when a corporate or carrier NAT blocks W…
- [transport](./src/transports/transport.md) — The `transport.types.ts` module declares the structural contract that `StreamSession` (authored in chunk A of the `@sua…
- [test-setup](./src/test-setup.md) — `test-setup.ts` is the per-worker bootstrap file referenced by `vitest.config.ts`'s `setupFiles`. It registers `@testin…
- [test-utils](./src/test-utils.md) — `test-utils.tsx` provides the in-memory `FakeSession` and the `withProvider` wrapper used by every chunk-C hook + compo…
<!-- end:auto-docs -->
## Provider
- `<StreamProvider sessionFactory={…}>` — StrictMode-safe singleton; injects a
`StreamSession` lazily; fans seven session event channels into a single render-bump
counter; disconnects on unmount.
- `StreamReactContext` — exposed for advanced consumers that need raw context access.
- Type-only mirrors (`SessionState`, `Peer`, `PeerMixerState`, `MediaPermissionState`,
`LevelSample`, `RecordingResult`, `StreamSessionFactory`, `StreamSessionLike`,
`SessionEventMap`, `ReactAudioStreamErrorLike`) for downstream typing.
## Hooks
- `useStreamSession()` — session, FSM state, and `connect` / `join` / `leave` /
`disconnect`.
- `useStreamRoom()` — `roomId`, `isJoined`, `joinedAt`, `participants`.
- `useStreamPeers()` — live `readonly Peer[]`.
- `useStreamMixer()` — per-peer `setVolume` / `setMute` / `setSolo` with DAW
solo semantics (when any peer is soloed, every non-soloed peer's track-volume → 0).
Every mutation routes through the session's `setTrackVolume` / `setTrackMute` /
`setTrackSolo`.
- `useStreamLevels(peerId?)` — `{ peak, rms }` for the local mic or named peer.
- `useStreamRecording()` — `start(peerIds?)` and `stop()` returning a
`RecordingResult` blob.
- `useStreamPermissions()` — typed `MediaPermissionState` with `request()` callback.
## Components
- `<StreamRoom>` — headless render-prop wrapper exposing the room snapshot plus join /
leave callbacks.
- `<StreamMixer>` — per-peer mixer strip with range slider, `aria-pressed`
mute/solo toggles, FX-slot read-out; supports `renderStrip` render-prop.
- `<StreamRecorder>` — record/stop toggle with `mm:ss` duration display and ARIA-live
read-out.
- `<StreamPermissionsBanner>` — `role="status"` `aria-live="polite"` banner; renders
nothing when permission is granted.
## Quick start
```tsx
import { StreamProvider, useStreamSession } from "@suaveplan/react-audio-stream";
function Room() {
const { state, connect, join } = useStreamSession();
if (state === "idle") return <button onClick={() => connect("lobby").then(join)}>Join</button>;
return <p>Joined!</p>;
}
export function App() {
return (
<StreamProvider sessionFactory={() => createMyStreamSession()}>
<Room />
</StreamProvider>
);
}
```
## Constraints (from the canonical spec)
- Zero browser audio APIs: no `AudioContext` / `AudioNode` / `AudioWorklet` imports.
- DAW solo semantics live in the mixer hook, not in each component.
- Every hook throws `REACT_AUDIO_STREAM.NO_PROVIDER` outside a `<StreamProvider>`.