import { useState, useRef, useCallback, useMemo } from 'react'; import { Icon } from './Icon'; import { silence, getPunchcardPainter } from '@strudel.cycles/core'; import { transpiler } from '@strudel.cycles/transpiler'; import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; import { StrudelMirror } from '@strudel/codemirror'; import { prebake } from '@strudel/repl'; import { useInView } from 'react-hook-inview'; const initialSettings = { keybindings: 'strudelTheme', isLineNumbersDisplayed: true, isActiveLineHighlighted: false, isAutoCompletionEnabled: false, isPatternHighlightingEnabled: true, isFlashEnabled: true, isTooltipEnabled: false, isLineWrappingEnabled: false, theme: 'strudelTheme', fontFamily: 'monospace', fontSize: 18, }; export function MicroRepl({ code, hideHeader = false, canvasHeight = 200, onTrigger, onPaint, punchcard, punchcardLabels = true, }) { const id = useMemo(() => s4(), []); const canvasId = useMemo(() => `canvas-${id}`, [id]); const shouldDraw = !!punchcard; const init = useCallback(({ code, shouldDraw }) => { const drawTime = [-2, 2]; const drawContext = shouldDraw ? document.querySelector('#' + canvasId)?.getContext('2d') : null; let onDraw; if (shouldDraw) { onDraw = (haps, time, frame, painters) => { painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); painters?.forEach((painter) => { // ctx time haps drawTime paintOptions painter(drawContext, time, haps, drawTime, { clear: false }); }); }; } const editor = new StrudelMirror({ defaultOutput: webaudioOutput, getTime: () => getAudioContext().currentTime, transpiler, autodraw: !!shouldDraw, root: containerRef.current, initialCode: '// LOADING', pattern: silence, settings: initialSettings, drawTime, onDraw, editPattern: (pat, id) => { if (onTrigger) { pat = pat.onTrigger(onTrigger, false); } if (onPaint) { editor.painters.push(onPaint); } else if (punchcard) { editor.painters.push(getPunchcardPainter({ labels: !!punchcardLabels })); } return pat; }, prebake, onUpdateState: (state) => { setReplState({ ...state }); }, }); // init settings editor.updateSettings(initialSettings); editor.setCode(code); editorRef.current = editor; }, []); const [ref, isVisible] = useInView({ threshold: 0.01, onEnter: () => { if (!editorRef.current) { init({ code, shouldDraw }); } }, }); const [replState, setReplState] = useState({}); const { started, isDirty, error } = replState; const editorRef = useRef(); const containerRef = useRef(); return (
{!hideHeader && (
)}
{error &&
{error.message}
}
{shouldDraw && ( { if (el && el.width !== el.clientWidth) { el.width = el.clientWidth; } //const ratio = el.clientWidth / canvasHeight; //const targetWidth = Math.round(el.width * ratio); //if (el.width !== targetWidth) { // el.width = targetWidth; //} }} > )} {/* !!log.length && (
{log.map(({ message }, i) => (
{message}
))}
) */}
); } function cx(...classes) { // : Array return classes.filter(Boolean).join(' '); } function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); }