DreamKnob/packages/dreamknob/README.md
Dreamodus 8b462dfb83 v1.1.4: light-theme support (LED wells, panel buttons, readouts)
The controls whose chrome was hardcoded dark now adapt to a light base —
previously they rendered as dark chips/holes on light themes. (Flat knobs and
faders already adapted: their face is the `face` token.)

- `ledWell` theme token — the recessed screen behind LED segments (LEDKnob,
  LEDFader, Meter) and the default well of SegmentDisplay / AlphaDisplay now
  comes from the theme. Dark base keeps #0b0d0e; light base is `transparent`,
  so LED controls and readouts render as clean flat rings / bars / digits on
  the light panel. faceColor / background props still override.
- `scheme` theme token ('dark' | 'light', set by base) + a shared
  panelButtonChrome() helper drive the raised gradient/border/shadow of
  PushButton (and Button), TransportButton and SegmentSwitch — light raised
  buttons on light themes instead of dark chips; unlit LED pips flip to a
  faint dark so they still read.
- Docs: a "Light theme" gallery card showing the panel family under
  base="light"; ledWell/scheme documented; install URLs bumped to v1.1.4.

Library + docs typecheck and build; 46 tests pass.
2026-07-14 14:21:57 -07:00

184 lines
8.2 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.

# dreamknob
Studio-grade React knobs & faders. Rotary, linear, 2D, 3D, digital and analog — with a
headless core, any number range, any decimal precision, and colors you fully control.
Zero runtime dependencies.
```
pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.4/dreamknob-1.1.4.tgz
```
(Latest release tarballs: https://git.dreamodus.software/Dreamodus/DreamKnob/releases)
## Quick start
```tsx
import { FlatKnob } from 'dreamknob'
function Volume() {
const [value, setValue] = useState(64)
return (
<FlatKnob
size={90}
value={value}
onChange={setValue}
min={0}
max={100}
step={0.5}
label="Volume"
unit="%"
aria-label="Volume"
/>
)
}
```
Uncontrolled works too: pass `defaultValue` instead of `value`/`onChange`
(it also becomes the double-click reset target).
## Interaction model
Every component shares one core:
| Gesture | Behaviour |
| --- | --- |
| Drag | `rotary` (grab & turn, follows the pointer angle), `vertical`, `horizontal`, `both`, or absolute `track-*` (faders) |
| Scroll wheel | one `step` per notch (else 1% of range); `Shift` = fine on continuous values — snapped controls always move whole steps |
| `↑ → / ↓ ←` | ± one step (`Shift` = 10×) |
| `PageUp/PageDown` | ± 10 % of range |
| `Home/End` | min / max |
| Double-click | reset to `defaultValue` — or open the type-in editor with `editable` |
| `Escape` | cancel an in-flight drag, restoring the start value |
| Touch / pen | pointer capture, `touch-action: none` |
All knobs render `role="slider"` with `aria-valuemin/max/now` (and `aria-valuetext`
when you provide `getAriaValueText`).
## Value handling
- `min` / `max` — any numbers (negative, fractional, huge).
- `step` — decimal snap increments (`0.5`, `0.001`, `1e-7`…) with float-drift-safe
rounding; display precision inferred automatically, overridable via `decimals`.
- `values={[0.25, 0.5, 1, 2, 4]}` — discrete allowed values.
- `steps={5}` — evenly spaced detents (selector knobs).
- `taper``linearTaper` (default), `logTaper` (frequencies), `powTaper(n)` (gain),
or your own `{ toNormalized, fromNormalized }`.
- `origin={0}` — bipolar anchor: arcs/fader fills draw from this value (pan, gain trim).
- `detents={[0]}` + `detentSize` — magnetic snap points while dragging.
- `editable` — double-click (knobs) or click the readout (faders) to type an exact
value; `parseValue` customizes parsing (`"1.2k"` → 1200 out of the box).
- `rotaryMode``'absolute'` (rc-knob feel), `'relative'` (no grab jump), or
`'pickup'` (MIDI-style: engages when the pointer sweeps past the value).
- `dragAcceleration` — velocity gain for relative drags; `hideCursorOnDrag` hides
the cursor mid-gesture.
- `wheelStep` / `wheelRequiresFocus` — tune wheel notches, or keep hover-scroll free.
- `onChange` / `onChangeStart` / `onChangeEnd` — gesture-aware callbacks with
`meta.source` (`'drag' | 'wheel' | 'keyboard' | 'reset' | 'edit' | 'api'`).
- `wrap` — endless encoder mode: values roll around min↔max (pair with
`angleOffset={0} angleRange={360}`).
- `animateChanges` — tween the pointer on programmatic changes (preset loads);
gestures never animate and `prefers-reduced-motion` is honored.
- `valueBubble` — floating readout above the control while dragging.
- `name` — hidden form input for plain `<form>` posts on every knob, fader and
`PushButton` (`XYPad` uses `nameX`/`nameY`). Interactive components forward a ref
to their root element, and primitive-drawn SVG parts carry `data-part` attributes
for CSS styling.
## Prebuilt skins
```tsx
import {
FlatKnob, // clean 2D arc knob (arcFrom="center" for pan knobs)
MetalKnob, // 3D brushed aluminum, knurled rim (tone="silver" | "dark")
RubberKnob, // 3D soft-touch synth knob with glow halo
VintageKnob, // chicken-head bakelite over a printed scale
LEDKnob, // segmented LED ring + true seven-segment readout
NeonKnob, // glowing arc for dark UIs
SteppedKnob, // detented selector (positions={['LP','BP','HP']})
PanKnob, // bipolar stereo pan — center detent, L<n>/C/R<n> readout
Fader, // linear channel fader, vertical or horizontal
LEDFader, // segmented LED meter-fader with color zones
ImageKnob, // film-strip sprite knob (KnobMan-style PNG strips)
Meter, // LED level meter — multi-channel, PPM ballistics, dBFS scale, loudness
MeterBridge, // labeled multi-strip meter block with per-channel peak text
Gauge, // read-only radial arc meter (round sibling of Meter) for CPU/RAM/load
XYPad, // two-parameter pad (cutoff/resonance, vector mixing)
PushButton, // latch/hold panel button — LED strip or corner dot (toggle/momentary)
Button, // click-to-fire command button (no LED by default; led="dot" opt-in)
IndicatorLamp, // pressable LED lamp — the hardware checkbox (blink, momentary)
LampRow, // row of indicator lamps with console-print captions
ToggleSwitch, // rocker/slider boolean — the knob slides OFF↔ON
SegmentSwitch, // segmented option selector (radiogroup, LED tick)
TransportButton,// play/stop/record/pause/panic family (record blinks)
ScrubField, // AE-style numeric field: drag the label to scrub, click to type
SegmentDisplay, // seven-segment numeric display (weight, gap, value-color zones)
AlphaDisplay, // fourteen-segment alphanumeric display (now incl. : $ %)
LabeledField, // label · control · unit in one aligned inspector row
Rack, // channel-strip / rack container (panel chrome + spacing)
} from 'dreamknob'
```
Knob skins also take `sublabel` (a second caption line) and `labelSize` (caption
size independent of the dial); `Meter` and `Fader` take `fill` to stretch to their
container. `Meter` adds `ballistics` (PPM attack/decay), `peakDecay`, `scale="db"`
(feed amplitude, show dBFS) and `integrated` (windowed loudness) — all opt-in.
`PushButton`/`Button` forward native button props and compose handlers, so a
`Button` works as a Radix `asChild` trigger.
## Theming
```tsx
import { DreamknobProvider } from 'dreamknob'
<DreamknobProvider base="dark" theme={{ accent: '#ff4d6b' }}>
{/* every control below inherits the tokens; instance props still win */}
</DreamknobProvider>
```
`base="light"` swaps in light-background defaults. Tokens: `accent`, `track`, `face`,
`text`, `label`, `ticks`, `focusRing`, `zoneGood`/`zoneWarn`/`zoneHot` (meter zones),
`ledGreen`/`ledAmber` (display defaults), `panel` (rack chrome), `ledWell` (LED display
well — transparent on the light base), `fontMono`, `fontUI`. `scheme` (`'dark'|'light'`)
is set by `base` and drives the panel-button/switch chrome; you rarely set it directly.
Every knob skin and fader takes the core props plus `size`, `label`, `showValue`,
`unit`, `format`, and per-part color props (`color`, `trackColor`, `faceColor`,
`bodyColor`, …). `VintageKnob` has no readout, so it omits `showValue`/`unit`/`format`;
the display components (`Meter`, `SegmentDisplay`, `AlphaDisplay`) have their own
smaller prop sets.
Also exported for custom builds: primitives (`Arc`, `Pointer`, `Ticks`, `TickLabels`,
`Face`, `KnobValue`, `KnobLabel`, `GlowFilter`), theming (`useKnobTheme`, `darkTheme`,
`lightTheme`), editing (`ValueInput`, `defaultParseValue`), colors (`mixColors`,
`sampleGradient`), and the math kit (`clamp`, `roundTo`, `snapToStep`,
`decimalsFromStep`, `findClosest`, `applyDetents`, tapers, angle/arc helpers).
## Compose your own
```tsx
import { Knob, Arc, Pointer, Ticks, KnobValue } from 'dreamknob'
<Knob size={96} min={-24} max={24} step={0.5} defaultValue={0} aria-label="Trim">
<Ticks count={25} activeColor="#ffd23e" />
<Arc radius={36} thickness={3} color="#ffd23e" from="center" />
<Pointer type="triangle" radius={33} length={9} width={8} color="#ffd23e" />
<KnobValue unit=" dB" />
</Knob>
```
`<Knob>` also accepts a render function: `{(ctx) => <YourSVG angle={ctx.angle} />}`.
## Fully headless
```tsx
import { useKnob } from 'dreamknob'
const { value, normalized, angle, isDragging, bind, setValue } = useKnob({
min: 0, max: 11, step: 0.1, defaultValue: 11, 'aria-label': 'Volume',
})
return <div {...bind}>your own rendering</div>
```
## License
MIT © Dreamodus Software Inc.