Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
# @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.

View File

@@ -0,0 +1,50 @@
# Tasks — enterprise-react-audio-stream-hooks-components (chunk C)
## 0. Pre-flight (non-negotiable)
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
- [ ] 0.2 Account for every `.claude/worktrees/*` entry; prune stale ones via `bun scripts/worktree-prune.ts --remove`.
- [ ] 0.3 Confirm `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json | wc -l`.
## 1. Provider
- [ ] 1.1 `src/provider/stream-provider.tsx` — StrictMode-safe singleton, injectable `sessionFactory`
- [ ] 1.2 `src/provider/stream-context.ts`, `stream-provider.test.tsx`, `.md`, `index.ts`
## 2. Hooks (6 dirs)
- [ ] 2.1 `src/hooks/use-stream-session/`
- [ ] 2.2 `src/hooks/use-stream-room/`
- [ ] 2.3 `src/hooks/use-stream-peers/`
- [ ] 2.4 `src/hooks/use-stream-mixer/` — DAW solo semantics
- [ ] 2.5 `src/hooks/use-stream-levels/`
- [ ] 2.6 `src/hooks/use-stream-recording/`
- [ ] 2.7 `src/hooks/use-stream-permissions/`
## 3. Components (4 dirs)
- [ ] 3.1 `src/components/stream-room/` + `.stories.tsx`
- [ ] 3.2 `src/components/stream-mixer/` + `.stories.tsx`
- [ ] 3.3 `src/components/stream-recorder/` + `.stories.tsx`
- [ ] 3.4 `src/components/stream-permissions-banner/` + `.stories.tsx`
## 4. Subpath exports
- [ ] 4.1 `package.json` `exports`: `./provider`, `./hooks`, `./hooks/<name>` × 6, `./components`, `./components/<name>` × 4
## 5. Tests + docs
- [ ] 5.1 Per-hook `.test.tsx` against a fake `StreamSession` injected via provider
- [ ] 5.2 Per-component `.test.tsx` covering aria + pointer + keyboard
- [ ] 5.3 Every `it(...)` carries `{ timeout: 5000 }` (30000 async)
- [ ] 5.4 Sibling `.md` per implementation (≥200 words)
- [ ] 5.5 README: `## Hooks` + `## Components` sections enumerating every export
## 6. Pre-archive verification
- [ ] 6.1 `bunx turbo lint typecheck test --filter=@suaveplan/react-audio-stream` — green on chunk-C files
- [ ] 6.2 `bunx turbo test:coverage --filter=@suaveplan/react-audio-stream` — 100/100/100/100 on touched modules
## 7. Hand-off (no archive yet)
- [ ] 7.1 Commit on `feat/react-audio-stream-hooks-components`. No spec fold / no archive.