Download rack/src/studio_rack.jsx from PYTHAI/mindX: direct link, hf CLI and curl.
- Browser
- Download file 5.7 kB
-
https://huggingface.co/spaces/PYTHAI/mindX/resolve/main/rack/src/studio_rack.jsx
- Command line
-
hf download hf://spaces/PYTHAI/mindX/rack/src/studio_rack.jsx
-
curl -L -o studio_rack.jsx https://huggingface.co/spaces/PYTHAI/mindX/resolve/main/rack/src/studio_rack.jsx
5.7 kB
| // mindX studio — the settings rack as dreamknob hardware. | |
| // | |
| // IT OWNS NO STATE. Every value comes from window.mindxStudio.knobs() and every turn goes back | |
| // through window.mindxStudio.setKnob(); the rack repaints on the window 'mindx:studio' event, which | |
| // the engine fires (once per frame at most) whenever a knob moves — by hand here, by a fallback | |
| // slider, or by a live mapping from .pixel .gesture .mic .cam. So a knob that is following your | |
| // microphone turns on its own, and the readout under it is what the audio graph was handed. | |
| // Delete this script and studio.js shows plain sliders that drive the same engine. | |
| // | |
| // dreamknob 1.1.4 — MIT, (c) Dreamodus Software Inc. Bundled with React 18 by rack/build.mjs. | |
| import { useEffect, useRef, useState } from 'react' | |
| import { createRoot } from 'react-dom/client' | |
| import { DreamknobProvider, Rack, MetalKnob, PanKnob, Fader, IndicatorLamp, linearTaper, logTaper, powTaper } from 'dreamknob' | |
| const MONO = "'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace" | |
| const TAPERS = { lin: linearTaper, log: logTaper, pow2: powTaper(2) } | |
| const SECTIONS = ['OUTPUT', 'EQ', 'FX', 'SCOPE'] | |
| const api = () => window.mindxStudio | |
| const colors = () => (api() && api().colors ? api().colors() : {}) | |
| function themeFrom(c, light) { | |
| return { | |
| accent: c.gold || '#d6b25e', | |
| track: c.line2 || 'rgba(232,230,223,.08)', | |
| face: c.canvas || '#0a0d0c', | |
| text: c.text || '#e8e6df', | |
| label: c.text3 || '#6d7771', | |
| ticks: c.text3 || '#6d7771', | |
| focusRing: c.gold || '#d6b25e', | |
| panel: light ? 'rgba(255,255,255,.85)' : 'rgba(14,18,17,.85)', | |
| ledGreen: c.green || '#52d98a', | |
| ledAmber: c.gold || '#d6b25e', | |
| fontMono: MONO, | |
| } | |
| } | |
| function useWidth(ref) { | |
| const [w, setW] = useState(800) | |
| useEffect(() => { | |
| const el = ref.current | |
| if (!el) return | |
| const measure = () => setW(el.clientWidth || 800) | |
| measure() | |
| if (typeof ResizeObserver !== 'undefined') { const ro = new ResizeObserver(measure); ro.observe(el); return () => ro.disconnect() } | |
| window.addEventListener('resize', measure) | |
| return () => window.removeEventListener('resize', measure) | |
| }, [ref]) | |
| return w | |
| } | |
| function Cell({ k, size }) { | |
| const set = (v) => api().setKnob(k.id, v) | |
| const common = { | |
| value: k.value, min: k.min, max: k.max, step: k.step || undefined, taper: TAPERS[k.taper] || linearTaper, | |
| defaultValue: k.def, onChange: set, label: k.label, 'aria-label': k.label, | |
| detents: k.detents, origin: k.origin, wheelRequiresFocus: true, | |
| } | |
| const c = colors() | |
| let control | |
| if (k.kind === 'fader') { | |
| control = <Fader {...common} orientation="horizontal" fill breadth={40} showValue valuePosition="end" | |
| format={() => k.text} color={c.gold} tickCount={9} editable /> | |
| } else if (k.kind === 'pan') { | |
| control = <PanKnob {...common} size={size} showValue format={() => k.text} color={c.gold} /> | |
| } else { | |
| control = <MetalKnob {...common} size={size} tone="dark" color={k.live ? c.green : c.gold} showValue format={() => k.text} | |
| sublabel={k.sub || undefined} valueBubble bubbleFormat={() => k.text} editable /> | |
| } | |
| return ( | |
| <div className={'sr-cell' + (k.kind === 'fader' ? ' sr-wide' : '')}> | |
| {control} | |
| <button type="button" className={'sr-map' + (k.mapped ? ' on' : '')} onClick={() => api().focusMapping(k.id)} | |
| title={k.mapped ? 'mapped to ' + k.mapped + (k.live ? ' (live)' : ' (source idle: turns by hand)') : 'map this knob to a sense'}> | |
| {k.mapped ? (k.live ? '● ' : '○ ') + k.mapped : 'map…'} | |
| </button> | |
| </div> | |
| ) | |
| } | |
| function StudioRack() { | |
| const [, tick] = useState(0) | |
| const [light, setLight] = useState(() => document.documentElement.style.colorScheme === 'light' || getComputedStyle(document.documentElement).colorScheme === 'light') | |
| const ref = useRef(document.getElementById('rack')) | |
| const width = useWidth(ref) | |
| useEffect(() => { | |
| const on = () => tick((n) => n + 1) | |
| const th = () => { setLight(getComputedStyle(document.documentElement).colorScheme === 'light'); tick((n) => n + 1) } | |
| window.addEventListener('mindx:studio', on) | |
| window.addEventListener('mindx:studio-theme', th) | |
| return () => { window.removeEventListener('mindx:studio', on); window.removeEventListener('mindx:studio-theme', th) } | |
| }, []) | |
| const knobs = api().knobs() | |
| const size = width < 420 ? 58 : width < 760 ? 64 : 72 | |
| const anyLive = knobs.some((k) => k.live) | |
| return ( | |
| <DreamknobProvider base={light ? 'light' : 'dark'} theme={themeFrom(colors(), light)}> | |
| <div className="sr-wrap"> | |
| {SECTIONS.map((sec) => ( | |
| <div className="sr-sec" key={sec}> | |
| <Rack orientation="column" title={sec} gap={8} padding={10} accentColor={colors().gold} style={{ width: '100%', boxSizing: 'border-box', minWidth: 0 }}> | |
| <div className="sr-knobs"> | |
| {knobs.filter((k) => k.sec === sec).map((k) => <Cell key={k.id} k={k} size={size} />)} | |
| </div> | |
| </Rack> | |
| </div> | |
| ))} | |
| </div> | |
| <div style={{ marginTop: 8 }}> | |
| <IndicatorLamp readOnly on={anyLive} color={colors().green} label={anyLive ? 'a sense is turning a knob' : 'every knob by hand'} labelSize={11} /> | |
| </div> | |
| </DreamknobProvider> | |
| ) | |
| } | |
| function mount() { | |
| const host = document.getElementById('rack') | |
| if (!host || !window.mindxStudio || host.__mounted) return !!(host && host.__mounted) | |
| host.__mounted = true | |
| window.__mindxStudioRack = true | |
| createRoot(host).render(<StudioRack />) | |
| return true | |
| } | |
| if (!mount()) { | |
| const t0 = Date.now() | |
| const iv = setInterval(() => { if (mount() || Date.now() - t0 > 10000) clearInterval(iv) }, 120) | |
| } | |