diff --git a/MAP.md b/MAP.md index 143a4df..c58cad0 100644 --- a/MAP.md +++ b/MAP.md @@ -59,7 +59,7 @@ anchor + locked decisions) and the `docs/specs/*-spec.md` set. consts (mode_id, engine_id, ml dims, params, voice_spaces, ui) — the SOURCE OF TRUTH for `MF_MODES`. Switching mode reshapes the WASM net to the mode's `ml` dims (ConsoleApp P5.3; boot mode paf_synth → 4→[10,10,14]→33). -- `manifold/src/dock/` — `OutputControlRow` (add/delete card identity + off/fixed/live + mute + solo/arm + min/max/curve, plus MIDI card fields), `output-state.ts`, +- `manifold/src/dock/` — `OutputControlRow` (editable names, corner delete, cycling off/fixed/live status, mute + solo/arm + min/max/curve, plus MIDI card fields), `output-state.ts`, `OutputsBackendConfig.tsx` (per-backend specialised Outputs panel — the sole per-backend editor; the centered add-card control lives below the rows). - `manifold/src/backends/` — `OutputBackend` adapter + `BackendManager` (spine consumer); `midi-backend.ts` (WebMIDI), `osc-backend.ts`+`osc-client.ts` (OSC-over-WS), `vcv-backend.ts` (VCV-over-WS), `cv-backend.ts` diff --git a/manifold/ONBOARDING.md b/manifold/ONBOARDING.md index 9ac50ee..c6c03ef 100644 --- a/manifold/ONBOARDING.md +++ b/manifold/ONBOARDING.md @@ -165,9 +165,10 @@ for the narrow pane. - `output-state.ts` — the per-output control model: `OutputControl` (state/muted/armed/min/max/ curve/fixedValue + backend specs `MidiCcSpec`/`OscSpec`/`VcvSpec`), `toOutputControl()`, `buildArmMask()` (solo focus). - - `OutputControlRow.tsx` — one output row: name · M(mute) · S(solo/arm) · off|fixed|live · dual-range - · curve pad · live value; MIDI mode adds the card's name, CC#, and channel fields. **Writes eagerly - to the shared `MFParam` store via `onChange`.** The centered `+` control follows the last visible card. + - `OutputControlRow.tsx` — one output card: editable name · M(mute) · S(solo/arm) · cycling + off/fixed/live status · dual-range · live value; the curve pad is a right-hand column and MIDI mode + adds CC#/channel fields. The delete `×` sits on the upper-right corner. **Writes eagerly to the shared + `MFParam` store via `onChange`.** The centered `+` control follows the last visible card. - `OutputsBackendConfig.tsx` — preset bar (save/restore/rename/delete) + transport/device config (MIDI port/templates, OSC path/range, VCV polarity); MIDI per-output fields live on `OutputControlRow`. diff --git a/manifold/src/console/Drawers.tsx b/manifold/src/console/Drawers.tsx index 6129482..1c9c497 100644 --- a/manifold/src/console/Drawers.tsx +++ b/manifold/src/console/Drawers.tsx @@ -770,6 +770,7 @@ function RoutingDrawer(ctx: ConsoleCtx, depth: DrawerDepth) { flex: expanded ? 1 : undefined, minHeight: expanded ? 0 : undefined, maxHeight: expanded ? undefined : 220, + padding: expanded ? '8px 10px' : '6px 10px', overflow: 'auto', }} > diff --git a/manifold/src/dock/OutputControlRow.tsx b/manifold/src/dock/OutputControlRow.tsx index 3246816..2ed97ff 100644 --- a/manifold/src/dock/OutputControlRow.tsx +++ b/manifold/src/dock/OutputControlRow.tsx @@ -2,7 +2,7 @@ * OutputControlRow — the shared per-output baseline control row (dock-spec §3.2). * Reused across the Routing, Synth and Visual drawers. Renders the FULL baseline: * - * name · M (mute) · S (solo/arm) · [off|fixed|live] · dual-range · curve · value + * editable name · M (mute) · S (solo/arm) · cycling status · dual-range · curve · value * * Writes eagerly through `onChange` into the single shared MFParam store * (ConsoleApp owns it) — never a second data path (dock-spec §3.2, §8). @@ -65,7 +65,7 @@ export interface OutputControlRowProps { onDelete?: () => void; /** Show the curve pad inline (expand depth); hidden in compact rows. */ showCurve?: boolean; - /** Show the per-card MIDI name, CC, and channel controls. */ + /** Show the per-card MIDI CC and channel controls. */ showMidi?: boolean; } @@ -81,28 +81,37 @@ export function OutputControlRow({ const muted = param.muted ?? false; const armed = param.armed ?? false; const off = param.status === 'off'; - const barVal = param.status === 'fixed' ? param.val : value; + const barVal = param.status === 'fixed' ? param.min + param.val * (param.max - param.min) : value; const midi = param.midi ?? defaultMidiSpec(param.engineIndex ?? 0); + const stateIndex = Math.max(0, STATE_META.findIndex((s) => s.v === param.status)); + const state = STATE_META[stateIndex]; + const nextState = STATE_META[(stateIndex + 1) % STATE_META.length]; return (
-
- {showMidi ? ( +
+
onChange({ midi: { ...midi, name: e.target.value } })} + aria-label={`Output name for ${param.name}`} + value={param.name} + onChange={(e) => + onChange({ + name: e.target.value, + ...(showMidi ? { midi: { ...midi, name: e.target.value } } : {}), + }) + } style={{ flex: 1, minWidth: 0, @@ -116,195 +125,158 @@ export function OutputControlRow({ padding: '1px 0', }} /> - ) : ( - - {param.name} - + {param.group} + onChange({ muted: !muted })} + /> + onChange({ armed: !armed })} + /> +
+ + {showMidi && ( +
+ + +
)} - {param.group} - {onDelete && ( + +
- )} - onChange({ muted: !muted })} - /> - onChange({ armed: !armed })} - /> -
- - {showMidi && ( -
- - + onChange({ min: v })} + onMax={(v) => onChange({ max: v })} + />
- )} -
- {/* tri-state segmented */} -
- {STATE_META.map((s) => { - const on = param.status === s.v; - return ( - - ); - })} -
- onChange({ min: v })} - onMax={(v) => onChange({ max: v })} - /> -
- - {/* value bar (live model value, or held fixed value) */} -
+ title={`value ${barVal.toFixed(3)}${muted ? ' (muted)' : ''}`} + > +
+
+ + {param.status === 'fixed' && ( + + )}
- {param.status === 'fixed' && ( - - )} - {showCurve && ( -
+
onChange({ curve: c })} size={88} />
)} + + {onDelete && ( + + )}
); }