v1.1.1: second integration-feedback release

Readout legibility (the #1 ask):
- bold own-cell decimal point + colon in seven/fourteen-seg (not hairlines)
- weight prop thickens strokes; gap controls inter-cell kerning

Display colors:
- SegmentDisplay defaults to theme.ledGreen, AlphaDisplay to theme.ledAmber
- valueColor fn + value-space zones on SegmentDisplay (self-coloring readouts)
- charset: colon in both, $ and % in 14-seg; disabled + data-disabled

Knob captions: sublabel (second line) + labelSize (independent of dial size).

New components:
- ToggleSwitch (rocker/slider boolean, role=switch)
- Gauge (read-only radial arc meter, round sibling of Meter)
- TransportButton (play/stop/record/pause/panic, record blinks)
- LampRow (console-print lamp bank), LabeledField, Rack (panel chrome)

Responsiveness: fill on Meter + Fader (stretch to container).
Niceties: PushButton leadingIcon; theme ledGreen/ledAmber/panel tokens.

54 -> 63 exports. 38 unit tests. Browser-verified.
This commit is contained in:
Dreamodus 2026-07-13 19:20:23 -07:00
parent d8b7b55505
commit 5b97091902
32 changed files with 1535 additions and 133 deletions

View file

@ -66,34 +66,68 @@ export interface KnobLabelProps {
fontFamily?: string;
/** Vertical offset from center; defaults to just below the knob body. */
dy?: number;
/**
* Secondary caption line rendered under the main label a unit or live
* descriptor (`SEED` over `world #7`). Not uppercased or letter-spaced.
*/
sublabel?: React.ReactNode;
/** Sub-caption font size. Default: 0.82× the label size. */
subFontSize?: number;
/** Sub-caption color. Default: the label color at reduced opacity. */
subColor?: string;
textProps?: React.SVGProps<SVGTextElement>;
}
/** Small caption, e.g. the parameter name. */
/** Small caption, e.g. the parameter name — with an optional second line. */
export const KnobLabel: React.FC<KnobLabelProps> = ({
children,
fontSize,
color = 'currentColor',
fontFamily,
dy,
sublabel,
subFontSize,
subColor,
textProps,
}) => {
const { size, center } = useKnobContext();
const fs = fontSize ?? size * 0.12;
const y = center + (dy ?? size * 0.38);
const subFs = subFontSize ?? fs * 0.82;
return (
<text
data-part="label"
x={center}
y={center + (dy ?? size * 0.38)}
textAnchor="middle"
dominantBaseline="central"
fontSize={fontSize ?? size * 0.12}
fill={color}
fontFamily={fontFamily}
letterSpacing="0.08em"
style={{ pointerEvents: 'none', textTransform: 'uppercase' }}
{...textProps}
>
{children}
</text>
<>
<text
data-part="label"
x={center}
y={y}
textAnchor="middle"
dominantBaseline="central"
fontSize={fs}
fill={color}
fontFamily={fontFamily}
letterSpacing="0.08em"
style={{ pointerEvents: 'none', textTransform: 'uppercase' }}
{...textProps}
>
{children}
</text>
{sublabel != null && sublabel !== '' && (
<text
data-part="sublabel"
x={center}
y={y + fs * 0.72 + subFs * 0.72}
textAnchor="middle"
dominantBaseline="central"
fontSize={subFs}
fill={subColor ?? color}
fontFamily={fontFamily}
opacity={subColor ? 1 : 0.7}
style={{ pointerEvents: 'none' }}
{...textProps}
>
{sublabel}
</text>
)}
</>
);
};