/** * Console — the FIVE real dock drawers (operator dock restructure). Each renderer * takes (ctx, depth); what shows is gated by depth (condensed | expanded): * * learn — Learning : feedback-mode selector, solo/arm chooser, live params * inputs — Inputs : input source * route — Outputs : per-output control matrix for the ACTIVE mode/backend, * a Mode-specific config section, and (Editor mode) the * MEMLNaut serial panel. The old separate "Synth" and * "Particle/Visual" drawers are REMOVED — their config now * lives here under the active Mode (TOP dock selector). * settings — Settings : icon style + input-map shape (settings-store) * help — Help : keymap + the loop explanation * * The TOP dock selector ("Mode") chooses the active OUTPUT backend/target; this * drawer renders whatever that backend needs. * * Engine wiring: the feedback-mode pill → controller.setMode; the arm flags → * engine.feedback.setFocus; per-output rows write the shared MFParam store. * Where engine support does not yet exist the UI + state are wired and a TODO * references the relevant spec — no faked engine behaviour. */ import type { ReactNode } from 'react'; import { Badge, Button, PillToggle, Slider, Switch } from '../primitives'; import type { ConsoleCtx, DrawerDepth, DrawerKey, FeedbackModeUI, SoloMode } from './types'; import type { InputMode } from '../inputs'; import { OutputControlRow } from '../dock/OutputControlRow'; import { OutputsBackendConfig, BackendStatusChip } from '../dock/OutputsBackendConfig'; import { shapeValues } from './model'; import { outputModeDescriptor } from './output-mode'; import { useSettings, unfocusedIconCss } from '../settings/settings-store'; import type { UnfocusedIconColour, InputMapMode } from '../settings/settings-store'; import { EditorPanel } from '../serial/EditorPanel'; import { TrainingHealth } from './TrainingHealth'; import { LearningIcon, InputsIcon, OutputsIcon, SettingsIcon, HelpIcon, } from './icons'; function Chip({ children, tone }: { children: ReactNode; tone?: string }) { return ( {children} ); } function SectionLabel({ children }: { children: ReactNode }) { return (
{FEEDBACK_DESC[ctx.feedbackMode]}
)}Explore adds a slow random walk (Ornstein-Uhlenbeck) on the outputs so the sound roams; likes and dislikes registered mid-wander steer the net toward what you want. 0 = off. Jolt (firmware TogB1) and Explore (RVX1) drive the same core gestures as the MEMLNaut hardware.
)}{SOLO_DESC[ctx.soloMode]} Solo only freezes the rest as far as a shared network allows. The other two behaviours need real core support (`train_masked`) and aren't selectable yet.
State machine: idle → exploring → commit / cancel. While Explore & place is exploring, training is paused and the joystick auditions a random scratchpad net; + commits a placed anchor and restores the real net.
Drag the on-screen manifold / XY pad. Two axes feed the net directly — this is the default.
)} {/* ---- Game Controller ---- */} {inp.inputMode === 'gamepad' && depth === 'expanded' && ( <>Connect a controller and press any button to wake it. Sticks drive the input map; buttons fire the verdicts above.
> )} {/* ---- MIDI ---- */} {inp.inputMode === 'midi' && depth === 'expanded' && ( <>No MIDI inputs detected. Connect a device — it appears here automatically.
) : (The net has {inp.engineInputSize} dedicated inputs — each active axis drives its own dimension 1:1 (no blending). {inputMismatch ? ` Its ${inp.engineInputSize} slots don't match the ${inp.axisCount} active axes; ` + `changing the layout offers a reshape to ${inp.axisCount} inputs (warm-started).` : ''}
)} > ); } // =========================================================================== // 3. OUTPUTS / ROUTING (dock-spec §3, §4) // =========================================================================== const VISUAL_NAMES = [ 'Flow', 'Scale', 'Speed', 'Hue', 'Spread', 'Size', 'Trail', 'Turb', 'Attract', 'Radius', 'DispRate', 'DispAmt', 'Lifetime', 'Respawn', 'Advection', 'Inertia', 'Drag', 'Repulse', 'RepCnt', 'RepRate', ]; /** * Per-Mode config section shown ABOVE the per-output rows. The synth Mode shows * transport + tempo; the particle Mode names the outputs; MIDI/OSC/Editor show * their own affordances. Replaces the removed Synth + Visual drawers. */ function ModeConfig(ctx: ConsoleCtx, depth: DrawerDepth) { switch (ctx.outputMode) { case 'synth': return ( <>The active engine follows the selected mode ({ctx.mode.label}). {/* TODO(dock-spec §5): arpeggiator + tiered synth presets + the 18-section group-override matrix are workstream E. Master volume + bpm sliders were deleted (simplification audit S16) — they drove no engine parameter. */}
)} > ); case 'editor': return ( <>Flow-field visualiser driven by the first {Math.min(20, ctx.params.length)} outputs (no audio). {/* TODO(backends-spec §4): port FlowFieldVisualizer + visual preset chips. */}
) : null; case 'midi': // The full MIDI config (port picker, CC count, per-output CC/channel/name) // + preset bar render via OutputsBackendConfig in RoutingDrawer below. return depth === 'expanded' ? (Each output sends a real Web MIDI CC. Pick a port and set CC# / channel per output.
) : null; case 'osc': return depth === 'expanded' ? (Each output sends to an OSC path with a physical range, over the WebSocket bridge.
) : null; default: return null; } } function RoutingDrawer(ctx: ConsoleCtx, depth: DrawerDepth) { const values = shapeValues(ctx.params, null); // bar uses shaped held/live value snapshot const counts = ctx.params.reduceFocused / active icons are always accent orange. This sets the resting colour of unfocused icons (preview below).
The 2D input surface: a rectangular XY map or a circular joystick-style disc. "Follow mode" uses the active mode's declared input (joystick → circular, else rectangular).
)}Roundness of buttons, control rows, dock icons and panels. Pills and the circular verdict buttons are intentionally exempt. Default 2px.
)} > ); } // =========================================================================== // 5. HELP // =========================================================================== const KEYS: [string, string][] = [ ['1', 'down − / explore'], ['2', 'commit +'], ['space / ↑', 'commit +'], ['↓', 'down − / explore'], ['3–5', 'open drawers'], ['\\', 'expand drawer'], ['z', 'undo'], ['[ ] =', 'split (composite)'], ['dbl-click mark', 'follow mouse (Esc exits)'], ]; function HelpDrawer() { return ( <>Drag the manifold to explore. Hear something good → + to keep it. Wrong → − to push away or re-roll (set the behaviour in the Learning drawer). Went too far → undo. The dock reveals exactly as much machinery as you reach for.