Files
suaveplan/openspec/archive/2026-05-23-enterprise-react-control-surface/proposal.md
2026-05-27 23:29:53 +01:00

58 lines
4.6 KiB
Markdown

# @suaveplan/react-control-surface — 2D atomic controls + runtime
- **Target path**: `packages/ui/react/react-control-surface/`
- **Tier**: browser/react
- **Date**: 2026-05-23
- **Status**: PROPOSED
## Why
`@suaveplan/control-surface-schema` defines the JSON; `@suaveplan/audio-fx-plugin-base` defines the parameter machinery; `@suaveplan/react-audio` provides `useParameter` for chain-driven automation. What's missing is the actual atomic React components — `<Knob>`, `<Slider>`, `<Button>`, `<Meter>`, etc. — plus a runtime that walks a `ControlSurface` document and renders the live DOM tree, wiring every control's `onChange` to its bound consumer (plugin parameter, MIDI CC, transport state, DMX channel).
This is the 2D renderer. The 3D R3F sibling (`@suaveplan/r3f-control-surface`) reads the same schema; both stay in lock-step via the shared `BoundControlProps` contract.
## What changes
1. **NEW package** `@suaveplan/react-control-surface` at `packages/ui/react/react-control-surface/`.
2. **`BoundControlProps`** — the shape every control component implements: `{ value: number; onChange: (v: number) => void; min: number; max: number; step?: number; default?: number; disabled?: boolean; label?: string; ariaLabel?: string; renderHint?: "auto" | "2d" }`. Pure value-in / value-out — no audio knowledge.
3. **Atomic controls** (each is a feature directory with `.tsx` + `.types.ts` + `.test.tsx` + `.stories.tsx` + `.md`):
- `<Knob>` — rotary, drag-vertical-to-change, double-click-resets, keyboard-arrow-step, fine-mode (Shift), aria-slider, range mapping via `curve` (linear/log/exp/custom)
- `<SliderV>`, `<SliderH>` — vertical/horizontal sliders with detents
- `<ButtonMomentary>`, `<ButtonLatching>` — press-and-hold / toggle behaviours
- `<Switch2>`, `<Switch3>` — two- and three-position toggles
- `<LED>` — output indicator (boolean or 0..1 brightness)
- `<DigitalDisplay>` — 7-segment or LCD-style readout for numeric / text / scrolling
- `<MeterVU>`, `<MeterPeak>` — single-channel level meters with optional peak-hold + ballistic curves
- `<MeterSpectral>` — multi-band spectrum visualisation (consumes `@suaveplan/audio-analysis-spectrum` data)
- `<XYPad>` — 2-axis touch surface
- `<JogWheel>` — endless rotary with velocity
- `<PianoKey>` — single piano key with state (idle/pressed/highlighted)
- `<DrumPad>` — velocity-sensitive pad with optional aftertouch via pointer pressure
- `<TextLabel>`, `<Image>` — static decorative elements
4. **Grouping primitives** — `<Group>`, `<Rack>`, `<Panel>`. Provide layout (grid/absolute/stack) and aria-labelling.
5. **Runtime** — `<Surface schema={ControlSurface} bindings={BindingRegistry} />`. Walks the schema, renders each node via the atomic component for its `kind`, and wires each control's `onChange` to its binding consumer via the registry.
6. **`BindingRegistry`** — discriminated dispatcher consumers register handlers with:
- `pluginParam` → calls `useParameter` from `@suaveplan/react-audio`
- `transport` → calls transport hooks
- `midiOut` → calls `useMidiOutput` from `@suaveplan/react-midi`
- `midiIn` → subscribes via `useMidiInput`, drives the control's `value` back
- `dmx` → calls into a `@suaveplan/react-dmx` hook (deferred — gated by feature flag)
- `compound` → fans out to multiple handlers
7. **Skin contract** — `<SurfaceSkinProvider id="native-instruments">` injects CSS variables consumed by every atomic control. Skin packages (`control-surface-skin-*`) supply the variables.
8. **Accessibility** — every control is keyboard-operable, ARIA-labelled, supports `prefers-reduced-motion`, exposes `role="slider"` / `role="switch"` / etc. with proper `aria-valuenow` / `aria-valuetext`.
9. **Error codes** — `REACT_CONTROL_SURFACE.{UNKNOWN_KIND, BINDING_HANDLER_MISSING, INVALID_SCHEMA}` via `defineCodes`.
10. **OTel** — counter `control_surface_interactions_total{kind}`, histogram `control_surface_render_duration_ms`.
## Impact
- New browser-tier React package; depends on `@suaveplan/control-surface-schema@workspace:^`, `@suaveplan/react-audio@workspace:^`, `@suaveplan/react-midi@workspace:^`, React 19 peer.
- Unblocks: `react-control-surface-editor`, `r3f-control-surface` (mirrors this surface 1:1 in R3F), every downstream DAW/instrument UI.
- The 3D sibling reuses `BoundControlProps`, `BindingRegistry`, and the schema — only the rendering layer differs.
## Out of scope
- 3D atomic controls — `r3f-control-surface` (Wave 2b).
- Drag-and-drop layout editor — `react-control-surface-editor` (Wave 4).
- Skin packages — separate per-device families.
- Network replication — `network-control-surface` (Wave 3).