/** * OutputEditor — the per-output menu (state · min · max · static value · curve). * Opens on hover over an output column/cell. Ported from `OutputEditor.jsx`. */ import type { CSSProperties, ReactNode } from 'react'; import { CurvePad } from './CurvePad'; import type { MFParam, ParamStatus } from './model'; const OE_STATUS: { v: ParamStatus; label: string; color: string }[] = [ { v: 'off', label: 'Off', color: 'var(--fg-dim)' }, { v: 'fixed', label: 'Fixed', color: 'var(--accent-2)' }, { v: 'live', label: 'Live', color: 'var(--accent)' }, ]; function MiniSlider({ label, value, onChange, disabled, }: { label: ReactNode; value: number; onChange: (v: number) => void; disabled?: boolean; }) { return ( {label} {value.toFixed(2)} onChange(parseFloat(e.target.value))} className="mf-slider-input" style={{ width: '100%' }} /> ); } export interface OutputEditorProps { param: MFParam; onChange: (patch: Partial) => void; onHold: () => void; onLeave: () => void; place: CSSProperties; } export function OutputEditor({ param, onChange, onHold, onLeave, place }: OutputEditorProps) { const isLive = param.status === 'live'; return ( {param.name} {param.group} {OE_STATUS.map((s) => { const on = param.status === s.v; return ( onChange({ status: s.v })} style={{ flex: 1, border: 0, borderRadius: 'var(--r-pill)', padding: '4px 0', cursor: 'pointer', fontFamily: 'var(--font-mono)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.06em', background: on ? s.color : 'transparent', color: on ? 'var(--bg)' : 'var(--fg-mute)', }} > {s.label} ); })} onChange({ min: v })} /> onChange({ max: v })} /> onChange({ val: v })} disabled={isLive} /> onChange({ curve: c })} size={170} /> drag a bar to set value · ⌥/alt-click cycles state ); }