From 47d29bc6546d3c5812511fb1baa19a5edf802111 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 16 Feb 2022 20:12:22 +0100 Subject: [PATCH] eval only on ctrl+enter, not on every keystroke --- repl/src/App.tsx | 74 +++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/repl/src/App.tsx b/repl/src/App.tsx index 5cecfae9..de3b55ad 100644 --- a/repl/src/App.tsx +++ b/repl/src/App.tsx @@ -12,7 +12,7 @@ import { isNote } from 'tone'; import { useWebMidi } from './midi'; const [_, codeParam] = window.location.href.split('#'); -const decoded = atob(codeParam || ''); +const decoded = atob(decodeURIComponent(codeParam || '')); const getHotCode = async () => { return fetch('/hot.js') @@ -39,17 +39,37 @@ function getRandomTune() { const randomTune = getRandomTune(); function App() { - const [mode, setMode] = useState('javascript'); const [code, setCode] = useState(decoded || randomTune); + const [activeCode, setActiveCode] = useState(); const [log, setLog] = useState(''); const logBox = useRef(); const [error, setError] = useState(); const [pattern, setPattern] = useState(); const [activePattern, setActivePattern] = useState(); - const activatePattern = (_pattern = pattern) => { - setActivePattern(() => _pattern); - window.location.hash = '#' + btoa(code); - !cycle.started && cycle.start(); + const dirty = code !== activeCode; + const activateCode = (_code = code) => { + if (activeCode && !dirty) { + setError(undefined); + return; + } + try { + const parsed = evaluate(_code); + setPattern(() => parsed.pattern); + activatePattern(parsed.pattern); + setError(undefined); + setActiveCode(_code); + } catch (err: any) { + setError(err); + } + }; + const activatePattern = (_pattern) => { + try { + setActivePattern(() => _pattern); + window.location.hash = '#' + encodeURIComponent(btoa(code)); + !cycle.started && cycle.start(); + } catch (err: any) { + setError(err); + } }; const [isHot, setIsHot] = useState(false); // set to true to enable live coding in hot.js, using dev server const pushLog = (message: string) => setLog((log) => log + `${log ? '\n\n' : ''}${message}`); @@ -102,7 +122,7 @@ function App() { if (e.ctrlKey || e.altKey) { switch (e.code) { case 'Enter': - activatePattern(); + activateCode(); break; case 'Period': cycle.stop(); @@ -111,40 +131,22 @@ function App() { }; document.addEventListener('keypress', handleKeyPress); return () => document.removeEventListener('keypress', handleKeyPress); - }, [pattern]); + }, [pattern, code]); // parse pattern when code changes useEffect(() => { - let _code = code; - // handle hot mode if (isHot) { if (typeof hot !== 'string') { getHotCode().then((_code) => { - setCode(_code); - setMode('javascript'); + // setCode(_code); }); // if using HMR, just use changed file activatePattern(hot); return; } else { - _code = hot; - setCode(_code); + setCode(hot); + activateCode(hot); } } - // normal mode - try { - const parsed = evaluate(_code); - // need arrow function here! otherwise if user returns a function, react will think it's a state reducer - // only first time, then need ctrl+enter - setPattern(() => parsed.pattern); - if (isHot) { - activatePattern(parsed.pattern); - } - setMode(parsed.mode); - setError(undefined); - } catch (err: any) { - console.warn(err); - setError(err); - } }, [code, isHot]); // scroll log box to bottom when log changes @@ -175,7 +177,7 @@ function App() {