From d183d7d16b637d0c60c93e0dc546289937ed2795 Mon Sep 17 00:00:00 2001 From: Dreamodus Date: Tue, 14 Jul 2026 11:16:44 -0700 Subject: [PATCH] v1.1.3: Radix-composable buttons, meter ballistics/dBFS/loudness, PanKnob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PushButton/Button forward native button props (...rest) and compose their own event handlers with consumer/Radix-injected ones, so a Button works as a Radix `asChild` trigger (dropdowns/tooltips/popovers). Prop types now extend React.ButtonHTMLAttributes. - Meter gains opt-in metering: `ballistics` (PPM attack/decay), `peakDecay` (peak-hold fall tail), `scale="db"` (feed amplitude → dBFS with reference lines; min/max default -60/0), and `integrated` (windowed LUFS-ish loudness readout + marker). New rAF engine in meterEngine.ts with ampToDb/dbToAmp helpers; default behavior unchanged. - New `PanKnob` — bipolar stereo pan: fills from center, center detent, L/C/R readout, -50..50 default. - Docs: ApiDocs rows + recipes (dBFS meter, pan strip, Button-as-trigger), gallery cards (dBFS meter, Pan) + a Regenerate Button in the panel. CHANGELOG 1.1.3; version 1.1.3; install URLs bumped. Library + docs typecheck and build; 46 tests pass. - Meter dBFS readout: muted-grey unit labels (LUFS matched to dB), value-first layout in an aligned 2-column grid so peak/loudness decimals line up, and ghost pad digits removed — the loudness row no longer looks like a stray tag. - Action buttons (Button / PushButton mode="action") no longer show an LED by default — an action has no state to indicate. led defaults to 'strip' for toggle/momentary and false for action; led="dot"/"strip" opts back in. - Meter dBFS readout: units (dB / LUFS) sit centered directly under each value; grid alignment tidied and ghost pad digits removed. Docs: dBFS-meter card moved next to the Meters card (same component) + a horizontal example. --- README.md | 11 +- apps/docs/src/sections/ApiDocs.tsx | 24 +- apps/docs/src/sections/Buttons.tsx | 9 +- apps/docs/src/sections/Gallery.tsx | 70 +++++- apps/docs/src/sections/GettingStarted.tsx | 4 +- apps/docs/src/sections/Hero.tsx | 4 +- packages/dreamknob/CHANGELOG.md | 40 ++++ packages/dreamknob/README.md | 13 +- packages/dreamknob/package.json | 2 +- packages/dreamknob/src/index.ts | 2 + packages/dreamknob/src/skins/Meter.tsx | 206 +++++++++++++++--- packages/dreamknob/src/skins/PanKnob.tsx | 60 +++++ packages/dreamknob/src/skins/PushButton.tsx | 67 ++++-- .../dreamknob/src/skins/meterEngine.test.ts | 45 ++++ packages/dreamknob/src/skins/meterEngine.ts | 167 ++++++++++++++ 15 files changed, 644 insertions(+), 80 deletions(-) create mode 100644 packages/dreamknob/src/skins/PanKnob.tsx create mode 100644 packages/dreamknob/src/skins/meterEngine.test.ts create mode 100644 packages/dreamknob/src/skins/meterEngine.ts diff --git a/README.md b/README.md index 04cf677..efeb404 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,11 @@ import { MetalKnob, LEDKnob, Fader, logTaper } from 'dreamknob' rounding, discrete `values` lists, evenly spaced detents (`steps`), linear / log / power / custom tapers. - **Styles**: `FlatKnob`, `MetalKnob`, `RubberKnob`, `VintageKnob`, `LEDKnob`, `NeonKnob`, - `SteppedKnob`, `ImageKnob` (film-strips), `Fader`, `LEDFader`, `Meter`, `MeterBridge`, - `Gauge`, `XYPad`, `PushButton`, `IndicatorLamp`, `LampRow`, `ToggleSwitch`, - `SegmentSwitch`, `TransportButton`, `ScrubField`, `SegmentDisplay`, `AlphaDisplay`, - plus `LabeledField`/`Rack` layout — every color themeable per instance. + `SteppedKnob`, `PanKnob`, `ImageKnob` (film-strips), `Fader`, `LEDFader`, `Meter` + (PPM ballistics · dBFS · loudness), `MeterBridge`, `Gauge`, `XYPad`, `PushButton`, + `Button`, `IndicatorLamp`, `LampRow`, `ToggleSwitch`, `SegmentSwitch`, + `TransportButton`, `ScrubField`, `SegmentDisplay`, `AlphaDisplay`, plus + `LabeledField`/`Rack` layout — every color themeable per instance. - **Composable**: `` + primitives (`Arc`, `Pointer`, `Ticks`, `TickLabels`, `Face`, `KnobValue`, `KnobLabel`, `GlowFilter`) for custom designs, or go fully headless with `useKnob`. @@ -50,7 +51,7 @@ See `packages/dreamknob/README.md` and the docs app for the full API. The easiest way is the published release on our Forgejo: ```bash -pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.2/dreamknob-1.1.2.tgz +pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz ``` Or build locally — the package builds to `packages/dreamknob/dist` (ESM + CJS + types): diff --git a/apps/docs/src/sections/ApiDocs.tsx b/apps/docs/src/sections/ApiDocs.tsx index ac44fa2..5df8acb 100644 --- a/apps/docs/src/sections/ApiDocs.tsx +++ b/apps/docs/src/sections/ApiDocs.tsx @@ -128,14 +128,15 @@ export const ApiDocs: React.FC = () => ( + - + - - + + @@ -311,12 +312,25 @@ function MyKnob() { -// A declarative channel strip +// A declarative channel strip — now with a pan knob + -`} + + +// A pro dBFS meter — feed amplitude, get ballistics + peak-decay + loudness + + +// A Button as a Radix dropdown trigger (it forwards + composes handlers) + + + + + {/* … */} +`} />

Precision & number handling

diff --git a/apps/docs/src/sections/Buttons.tsx b/apps/docs/src/sections/Buttons.tsx index 02676f4..1cf6753 100644 --- a/apps/docs/src/sections/Buttons.tsx +++ b/apps/docs/src/sections/Buttons.tsx @@ -82,7 +82,7 @@ export const Buttons: React.FC = () => { + { ); }; +const DbMeterDemo: React.FC = () => { + // Drive with linear amplitude; the meter converts to dBFS internally. + const [amp, setAmp] = React.useState<[number, number]>([0.3, 0.28]); + React.useEffect(() => { + let t = 0; + const id = setInterval(() => { + t += 0.11; + const spike = Math.random() < 0.05 ? 0.4 : 0; + setAmp([ + Math.max(0.001, Math.min(1.2, 0.34 + Math.sin(t) * 0.22 + Math.random() * 0.16 + spike)), + Math.max(0.001, Math.min(1.2, 0.32 + Math.sin(t * 1.25 + 1) * 0.22 + Math.random() * 0.16 + spike)), + ]); + }, 90); + return () => clearInterval(id); + }, []); + return ( +
+ + +
+ ); +}; + +const PanDemo: React.FC = () => ( +
+ + +
+); + const ImageKnobDemo: React.FC = () => { const src = useGeneratedStrip(); if (!src) return null; @@ -509,6 +569,10 @@ export const Gallery: React.FC = () => ( + + + + @@ -536,6 +600,10 @@ export const Gallery: React.FC = () => ( + + + + ( ) · peer deps: react >= 18, react-dom >= 18`} + code={`pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz\n# (or npm install ) · peer deps: react >= 18, react-dom >= 18`} />

Installs straight from the{' '} - v1.1.2 release + v1.1.3 release {' '} on our Forgejo. Zero runtime dependencies, ESM + CJS, full TypeScript types. No CSS file to import — everything is SVG and inline styles. diff --git a/apps/docs/src/sections/Hero.tsx b/apps/docs/src/sections/Hero.tsx index f66c4b4..2808d9c 100644 --- a/apps/docs/src/sections/Hero.tsx +++ b/apps/docs/src/sections/Hero.tsx @@ -25,10 +25,10 @@ export const Hero: React.FC = () => { range, any decimal precision, and colors you fully control.

- pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.2/dreamknob-1.1.2.tgz + pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz