diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 92dd6214..d7d67607 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -146,8 +146,7 @@ export class StrudelMirror { onChange: (v) => { if (v.docChanged) { this.code = v.state.doc.toString(); - // TODO: repl is still untouched to make sure the old Repl.jsx stays untouched.. - // this.repl.setCode(this.code); + this.repl.setCode?.(this.code); } }, onEvaluate: () => this.evaluate(), diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 0bd5229d..b3c7a543 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -16,13 +16,36 @@ export function repl({ transpiler, onToggle, editPattern, + onUpdateState, }) { + const state = { + schedulerError: undefined, + evalError: undefined, + code: '// LOADING', + activeCode: '// LOADING', + pattern: undefined, + miniLocations: [], + widgets: [], + pending: true, + started: false, + }; + + const updateState = (update) => { + Object.assign(state, update); + state.isDirty = state.code !== state.activeCode; + state.error = state.evalError || state.schedulerError; + onUpdateState?.(state); + }; + const scheduler = new Cyclist({ interval, onTrigger: getTrigger({ defaultOutput, getTime }), onError: onSchedulerError, getTime, - onToggle, + onToggle: (started) => { + updateState({ started }); + onToggle?.(started); + }, }); let pPatterns = {}; let allTransform; @@ -43,6 +66,7 @@ export function repl({ throw new Error('no code to evaluate'); } try { + updateState({ code, pending: true }); await beforeEval?.({ code }); shouldHush && hush(); let { pattern, meta } = await _evaluate(code, transpiler); @@ -58,17 +82,28 @@ export function repl({ } logger(`[eval] code updated`); setPattern(pattern, autostart); + updateState({ + miniLocations: meta?.miniLocations || [], + widgets: meta?.widgets || [], + activeCode: code, + pattern, + evalError: undefined, + schedulerError: undefined, + pending: false, + }); afterEval?.({ code, pattern, meta }); return pattern; } catch (err) { // console.warn(`[repl] eval error: ${err.message}`); logger(`[eval] error: ${err.message}`, 'error'); + updateState({ evalError: err, pending: false }); onEvalError?.(err); } }; const stop = () => scheduler.stop(); const start = () => scheduler.start(); const pause = () => scheduler.pause(); + const toggle = () => scheduler.toggle(); const setCps = (cps) => scheduler.setCps(cps); const setCpm = (cpm) => scheduler.setCps(cpm / 60); @@ -127,8 +162,8 @@ export function repl({ setCpm, setcpm: setCpm, }); - - return { scheduler, evaluate, start, stop, pause, setCps, setPattern }; + const setCode = (code) => updateState({ code }); + return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state }; } export const getTrigger = diff --git a/packages/repl/repl-component.mjs b/packages/repl/repl-component.mjs index ebcca532..554be3bc 100644 --- a/packages/repl/repl-component.mjs +++ b/packages/repl/repl-component.mjs @@ -92,6 +92,12 @@ class StrudelRepl extends HTMLElement { afterEval: ({ code }) => { // window.location.hash = '#' + code2hash(code); }, + onUpdateState: (state) => { + const event = new CustomEvent('update', { + detail: state, + }); + this.dispatchEvent(event); + }, }); // init settings this.editor.updateSettings(this.settings); diff --git a/website/src/docs/Icon.jsx b/website/src/docs/Icon.jsx new file mode 100644 index 00000000..64d5f88a --- /dev/null +++ b/website/src/docs/Icon.jsx @@ -0,0 +1,38 @@ +export function Icon({ type }) { + return ( + + ); +} diff --git a/website/src/docs/MicroRepl.jsx b/website/src/docs/MicroRepl.jsx new file mode 100644 index 00000000..b3e2637e --- /dev/null +++ b/website/src/docs/MicroRepl.jsx @@ -0,0 +1,91 @@ +import { useState, useRef, useCallback, useEffect } from 'react'; +import { Icon } from './Icon'; + +export function MicroRepl({ code, hideHeader = false }) { + /* const [ref, isVisible] = useInView({ + threshold: 0.01, + }); */ + const [replState, setReplState] = useState({}); + const { started, isDirty, error } = replState; + const wc = useRef(); + function togglePlay() { + if (wc.current) { + wc.current?.editor.evaluate(); + } + } + const listener = useCallback((e) => setReplState({ ...e.detail }), []); + useEffect(() => { + return () => { + wc.current.removeEventListener('update', listener); + }; + }, []); + return ( +
MicroRepl:
+