import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { Icon } from './Icon'; import { silence, noteToMidi, _mod } from '@strudel/core'; import { getDrawContext, getPunchcardPainter } from '@strudel/draw'; import { transpiler } from '@strudel/transpiler'; import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio'; import { StrudelMirror } from '@strudel/codemirror'; import { prebake } from '../repl/prebake.mjs'; import { loadModules, setVersionDefaultsFrom } from '../repl/util.mjs'; import Claviature from '@components/Claviature'; import useClient from '@src/useClient.mjs'; let prebaked, modulesLoading, audioReady; if (typeof window !== 'undefined') { prebaked = prebake(); modulesLoading = loadModules(); audioReady = initAudioOnFirstClick(); } export function MiniRepl({ tune, tunes, hideHeader = false, canvasHeight = 100, onTrigger, punchcard, punchcardLabels = true, claviature, claviatureLabels, maxHeight, autodraw, drawTime, }) { const code = tunes ? tunes[0] : tune; const id = useMemo(() => s4(), []); const shouldShowCanvas = !!punchcard; const canvasId = shouldShowCanvas ? useMemo(() => `canvas-${id}`, [id]) : null; autodraw = !!punchcard || !!claviature || !!autodraw; drawTime = (drawTime ?? punchcard) ? [0, 4] : [-2, 2]; if (claviature) { drawTime = [0, 0]; } const [activeNotes, setActiveNotes] = useState([]); const init = useCallback(({ code, autodraw }) => { const drawContext = canvasId ? document.querySelector('#' + canvasId)?.getContext('2d') : getDrawContext(); const editor = new StrudelMirror({ id, defaultOutput: webaudioOutput, getTime: () => getAudioContext().currentTime, transpiler, autodraw, root: containerRef.current, initialCode: '// LOADING', pattern: silence, drawTime, drawContext, editPattern: (pat, id) => { if (onTrigger) { pat = pat.onTrigger(onTrigger, false); } if (claviature) { pat = pat.onPaint((ctx, time, haps, drawTime) => { const active = haps .map((hap) => hap.value.note) .filter(Boolean) .map((n) => (typeof n === 'string' ? noteToMidi(n) : n)); setActiveNotes(active); }); } if (punchcard) { pat = pat.punchcard({ labels: !!punchcardLabels }); } return pat; }, prebake: async () => Promise.all([modulesLoading, prebaked]), onUpdateState: (state) => { setReplState({ ...state }); }, onToggle: (playing) => { if (!playing) { // clearHydra(); // TBD: doesn't work with multiple MiniRepl's on a page } }, beforeStart: () => audioReady, afterEval: ({ code }) => setVersionDefaultsFrom(code), }); // init settings editor.setCode(code); editorRef.current = editor; }, []); const [replState, setReplState] = useState({}); const { started, isDirty, error } = replState; const editorRef = useRef(); const containerRef = useRef(); const client = useClient(); const [tuneIndex, setTuneIndex] = useState(0); const changeTune = (index) => { index = _mod(index, tunes.length); setTuneIndex(index); editorRef.current?.setCode(tunes[index]); editorRef.current?.evaluate(); }; if (!client) { return
{code};
}
return (