diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 4b64692c..7cac0888 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -102,7 +102,8 @@ export class StrudelMirror { this.onDraw?.(haps, time, currentFrame, this.painters); }, drawTime); - // this approach might not work with multiple repls on screen.. + // this approach does not work with multiple repls on screen + // TODO: refactor onPaint usages + find fix, maybe remove painters here? Pattern.prototype.onPaint = function (onPaint) { self.painters.push(onPaint); return this; diff --git a/packages/core/pianoroll.mjs b/packages/core/pianoroll.mjs index 254dd94a..4a8fd8db 100644 --- a/packages/core/pianoroll.mjs +++ b/packages/core/pianoroll.mjs @@ -256,10 +256,13 @@ export function getDrawOptions(drawTime, options = {}) { return { fold: 1, ...options, cycles, playhead }; } +export const getPunchcardPainter = + (options = {}) => + (ctx, time, haps, drawTime, paintOptions = {}) => + pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) }); + Pattern.prototype.punchcard = function (options) { - return this.onPaint((ctx, time, haps, drawTime, paintOptions = {}) => - pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) }), - ); + return this.onPaint(getPunchcardPainter(options)); }; /** diff --git a/website/src/docs/MicroRepl.jsx b/website/src/docs/MicroRepl.jsx index cc401cd6..54b5e27e 100644 --- a/website/src/docs/MicroRepl.jsx +++ b/website/src/docs/MicroRepl.jsx @@ -1,6 +1,6 @@ -import { useState, useRef, useCallback, useEffect } from 'react'; +import { useState, useRef, useCallback, useMemo } from 'react'; import { Icon } from './Icon'; -import { getDrawContext, silence } from '@strudel.cycles/core'; +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'; @@ -9,8 +9,8 @@ import { useInView } from 'react-hook-inview'; const initialSettings = { keybindings: 'strudelTheme', - isLineNumbersDisplayed: false, - isActiveLineHighlighted: true, + isLineNumbersDisplayed: true, + isActiveLineHighlighted: false, isAutoCompletionEnabled: false, isPatternHighlightingEnabled: true, isFlashEnabled: true, @@ -21,10 +21,33 @@ const initialSettings = { fontSize: 18, }; -export function MicroRepl({ code, hideHeader = false, canvasHeight = 200, punchcard, punchcardLabels }) { - const init = useCallback(({ code }) => { - const drawContext = getDrawContext(); +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, @@ -34,12 +57,17 @@ export function MicroRepl({ code, hideHeader = false, canvasHeight = 200, punchc pattern: silence, settings: initialSettings, drawTime, - 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 }); - }); + 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) => { @@ -56,7 +84,7 @@ export function MicroRepl({ code, hideHeader = false, canvasHeight = 200, punchc threshold: 0.01, onEnter: () => { if (!editorRef.current) { - init({ code }); + init({ code, shouldDraw }); } }, }); @@ -65,12 +93,6 @@ export function MicroRepl({ code, hideHeader = false, canvasHeight = 200, punchc const editorRef = useRef(); const containerRef = useRef(); - const [canvasId] = useState(Date.now()); - const drawContext = useCallback( - punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, - [punchcard], - ); - return (