first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
# `<PluginParameterKnob>`
Rotary-knob primitive for a single `AudioFxPlugin` parameter. The knob
implements the full keyboard contract mandated by the
`<PluginSlot>` accessibility requirement, exposes `role="slider"` with
ARIA range metadata derived from the parameter's `min`/`max`/`unit`
declaration, and supports an `Enter`-to-edit type-in affordance.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `<PluginParameterKnob>` is the rotary half of the generic
parameter-control surface. Specialised plugin UIs (an EQ panel, a
compressor panel) often wrap the knob with their own visual chrome
while reusing the keyboard, ARIA, and value-management logic. The
knob is therefore deliberately presentational: the entire keyboard
keymap, value clamping, and typed-edit flow live here, while the
caller supplies the plugin and parameter declaration.
The knob subscribes to the parameter via the Phase-1
`useParameter` hook, so every change is reflected at the provider's
declared `sampleRateHz` — by default 60 Hz, matching one tick per
animation frame.
## Features
- `role="slider"`, `aria-valuemin`, `aria-valuemax`, `aria-valuenow`,
`aria-valuetext` (unit-aware via `formatAriaValueText`).
- `←`/`→` fine step (1 % of range), `↑`/`↓` coarse step (10 %),
`PageUp`/`PageDown` ×100 page step, `Home`/`End` jump to range
endpoints.
- `Shift` ×10, `Alt` ÷10 modifier multipliers.
- `Enter` opens a numeric type-edit input; `Enter` commits, `Escape`
reverts, blur commits.
- `data-parameter-id` attribute on the wrapper so plugin authors can
target specific knobs from a stylesheet.
- Pure CSS-class API (`rax-knob`, `rax-knob__dial`, `rax-knob__readout`,
`rax-knob__input`) — no styling assumptions baked in.
## Usage: Basic
```tsx
import { PluginParameterKnob } from "@suaveplan/react-audio/components/plugin-parameter-knob";
function GainKnob({ plugin }) {
const param = plugin.parameterIndex.get("gain")!;
return <PluginParameterKnob plugin={plugin} parameter={param} />;
}
```
## Usage: Advanced
```tsx
// Iterate every parameter on a plugin, rendering each with a custom label
// override and theming via the className API.
function PluginPanel({ plugin }) {
return (
<div className="grid grid-cols-4 gap-4">
{plugin.parameters.map((p) => (
<PluginParameterKnob
key={p.id}
plugin={plugin}
parameter={p}
label={p.name.toUpperCase()}
className="ring-1 ring-zinc-700 rounded-md p-2"
/>
))}
</div>
);
}
```
## API Reference
### Props
- `plugin: AudioFxPlugin` — the plugin instance whose parameter is bound.
- `parameter: PluginParameter` — the declaration to render. The parameter
MUST be present in `plugin.parameterIndex`; otherwise the underlying
`useParameter` hook throws `REACT_AUDIO.UNKNOWN_PARAMETER`.
- `label?: string` — optional visible label override; defaults to
`parameter.name`.
- `className?: string` — optional CSS class appended to the wrapper.
### Behaviour
- Each parameter update flows through `param.set(...)`, which delegates
to `plugin.setParameter`. Value subscriptions go through the
Phase-1 `useParameter` hook.
- The typed-edit input is rendered only when the user presses `Enter`
on the knob; it auto-focuses, supports `Enter`/`Escape`/blur, and
clamps the parsed value to the declared range.
## Implementation Notes
- The keyboard handler delegates to the pure `resolveParameterKeyboardChange`
helper, which is shared with `<PluginParameterSlider>` to guarantee
identical step semantics between the two primitives.
- `formatAriaValueText` is called with `signedDb: true` so dB readouts
carry the `+` sign — matching the convention used by every other
audio tool that ships unit-aware screen-reader text.
- No animation libraries are imported; visual rotation should be
composed by the consumer via CSS. The component respects
`prefers-reduced-motion` by virtue of being CSS-only.