mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-28 07:46:45 -04:00
127 lines
3.0 KiB
JavaScript
127 lines
3.0 KiB
JavaScript
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
import core from '@strudel.cycles/core';
|
|
const repl = core.repl
|
|
import { transpiler } from '@strudel.cycles/transpiler';
|
|
import usePostMessage from './usePostMessage.mjs';
|
|
|
|
function useStrudel({
|
|
defaultOutput,
|
|
interval,
|
|
getTime,
|
|
evalOnMount = false,
|
|
initialCode = '',
|
|
autolink = false,
|
|
beforeEval,
|
|
afterEval,
|
|
onEvalError,
|
|
onToggle,
|
|
}) {
|
|
const id = useMemo(() => s4(), []);
|
|
// scheduler
|
|
const [schedulerError, setSchedulerError] = useState();
|
|
const [evalError, setEvalError] = useState();
|
|
const [code, setCode] = useState(initialCode);
|
|
const [activeCode, setActiveCode] = useState();
|
|
const [pattern, setPattern] = useState();
|
|
const [started, setStarted] = useState(false);
|
|
const isDirty = code !== activeCode;
|
|
|
|
// TODO: make sure this hook reruns when scheduler.started changes
|
|
const { scheduler, evaluate, start, stop, pause } = useMemo(
|
|
() =>
|
|
repl({
|
|
interval,
|
|
defaultOutput,
|
|
onSchedulerError: setSchedulerError,
|
|
onEvalError: (err) => {
|
|
setEvalError(err);
|
|
onEvalError?.(err);
|
|
},
|
|
getTime,
|
|
transpiler,
|
|
beforeEval: ({ code }) => {
|
|
setCode(code);
|
|
beforeEval?.();
|
|
},
|
|
afterEval: ({ pattern: _pattern, code }) => {
|
|
setActiveCode(code);
|
|
setPattern(_pattern);
|
|
setEvalError();
|
|
setSchedulerError();
|
|
if (autolink) {
|
|
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
|
}
|
|
afterEval?.();
|
|
},
|
|
onToggle: (v) => {
|
|
setStarted(v);
|
|
onToggle?.(v);
|
|
},
|
|
}),
|
|
[defaultOutput, interval, getTime],
|
|
);
|
|
const broadcast = usePostMessage(({ data: { from, type } }) => {
|
|
if (type === 'start' && from !== id) {
|
|
// console.log('message', from, type);
|
|
stop();
|
|
}
|
|
});
|
|
const activateCode = useCallback(
|
|
async (autostart = true) => {
|
|
await evaluate(code, autostart);
|
|
broadcast({ type: 'start', from: id });
|
|
},
|
|
[evaluate, code],
|
|
);
|
|
|
|
const inited = useRef();
|
|
useEffect(() => {
|
|
if (!inited.current && evalOnMount && code) {
|
|
inited.current = true;
|
|
activateCode();
|
|
}
|
|
}, [activateCode, evalOnMount, code]);
|
|
|
|
// this will stop the scheduler when hot reloading in development
|
|
useEffect(() => {
|
|
return () => {
|
|
scheduler.stop();
|
|
};
|
|
}, [scheduler]);
|
|
|
|
const togglePlay = async () => {
|
|
if (started) {
|
|
scheduler.pause();
|
|
} else {
|
|
await activateCode();
|
|
}
|
|
};
|
|
const error = schedulerError || evalError;
|
|
return {
|
|
code,
|
|
setCode,
|
|
error,
|
|
schedulerError,
|
|
scheduler,
|
|
evalError,
|
|
evaluate,
|
|
activateCode,
|
|
activeCode,
|
|
isDirty,
|
|
pattern,
|
|
started,
|
|
start,
|
|
stop,
|
|
pause,
|
|
togglePlay,
|
|
};
|
|
}
|
|
|
|
export default useStrudel;
|
|
|
|
function s4() {
|
|
return Math.floor((1 + Math.random()) * 0x10000)
|
|
.toString(16)
|
|
.substring(1);
|
|
}
|