mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-27 21:48:27 +00:00
eval only on ctrl+enter, not on every keystroke
This commit is contained in:
parent
70f858361f
commit
47d29bc654
@ -12,7 +12,7 @@ import { isNote } from 'tone';
|
|||||||
import { useWebMidi } from './midi';
|
import { useWebMidi } from './midi';
|
||||||
|
|
||||||
const [_, codeParam] = window.location.href.split('#');
|
const [_, codeParam] = window.location.href.split('#');
|
||||||
const decoded = atob(codeParam || '');
|
const decoded = atob(decodeURIComponent(codeParam || ''));
|
||||||
|
|
||||||
const getHotCode = async () => {
|
const getHotCode = async () => {
|
||||||
return fetch('/hot.js')
|
return fetch('/hot.js')
|
||||||
@ -39,17 +39,37 @@ function getRandomTune() {
|
|||||||
const randomTune = getRandomTune();
|
const randomTune = getRandomTune();
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [mode, setMode] = useState<string>('javascript');
|
|
||||||
const [code, setCode] = useState<string>(decoded || randomTune);
|
const [code, setCode] = useState<string>(decoded || randomTune);
|
||||||
|
const [activeCode, setActiveCode] = useState<string>();
|
||||||
const [log, setLog] = useState('');
|
const [log, setLog] = useState('');
|
||||||
const logBox = useRef<any>();
|
const logBox = useRef<any>();
|
||||||
const [error, setError] = useState<Error>();
|
const [error, setError] = useState<Error>();
|
||||||
const [pattern, setPattern] = useState<Pattern>();
|
const [pattern, setPattern] = useState<Pattern>();
|
||||||
const [activePattern, setActivePattern] = useState<Pattern>();
|
const [activePattern, setActivePattern] = useState<Pattern>();
|
||||||
const activatePattern = (_pattern = pattern) => {
|
const dirty = code !== activeCode;
|
||||||
setActivePattern(() => _pattern);
|
const activateCode = (_code = code) => {
|
||||||
window.location.hash = '#' + btoa(code);
|
if (activeCode && !dirty) {
|
||||||
!cycle.started && cycle.start();
|
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 [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}`);
|
const pushLog = (message: string) => setLog((log) => log + `${log ? '\n\n' : ''}${message}`);
|
||||||
@ -102,7 +122,7 @@ function App() {
|
|||||||
if (e.ctrlKey || e.altKey) {
|
if (e.ctrlKey || e.altKey) {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
activatePattern();
|
activateCode();
|
||||||
break;
|
break;
|
||||||
case 'Period':
|
case 'Period':
|
||||||
cycle.stop();
|
cycle.stop();
|
||||||
@ -111,40 +131,22 @@ function App() {
|
|||||||
};
|
};
|
||||||
document.addEventListener('keypress', handleKeyPress);
|
document.addEventListener('keypress', handleKeyPress);
|
||||||
return () => document.removeEventListener('keypress', handleKeyPress);
|
return () => document.removeEventListener('keypress', handleKeyPress);
|
||||||
}, [pattern]);
|
}, [pattern, code]);
|
||||||
|
|
||||||
// parse pattern when code changes
|
// parse pattern when code changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let _code = code;
|
|
||||||
// handle hot mode
|
|
||||||
if (isHot) {
|
if (isHot) {
|
||||||
if (typeof hot !== 'string') {
|
if (typeof hot !== 'string') {
|
||||||
getHotCode().then((_code) => {
|
getHotCode().then((_code) => {
|
||||||
setCode(_code);
|
// setCode(_code);
|
||||||
setMode('javascript');
|
|
||||||
}); // if using HMR, just use changed file
|
}); // if using HMR, just use changed file
|
||||||
activatePattern(hot);
|
activatePattern(hot);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
_code = hot;
|
setCode(hot);
|
||||||
setCode(_code);
|
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]);
|
}, [code, isHot]);
|
||||||
|
|
||||||
// scroll log box to bottom when log changes
|
// scroll log box to bottom when log changes
|
||||||
@ -175,7 +177,7 @@ function App() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const _code = getRandomTune();
|
const _code = getRandomTune();
|
||||||
console.log('tune',_code); // uncomment this to debug when random code fails
|
console.log('tune', _code); // uncomment this to debug when random code fails
|
||||||
setCode(_code);
|
setCode(_code);
|
||||||
const parsed = evaluate(_code);
|
const parsed = evaluate(_code);
|
||||||
// Tone.Transport.cancel(Tone.Transport.seconds);
|
// Tone.Transport.cancel(Tone.Transport.seconds);
|
||||||
@ -204,7 +206,7 @@ function App() {
|
|||||||
value={code}
|
value={code}
|
||||||
readOnly={isHot}
|
readOnly={isHot}
|
||||||
options={{
|
options={{
|
||||||
mode,
|
mode: 'javascript',
|
||||||
theme: 'material',
|
theme: 'material',
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
}}
|
}}
|
||||||
@ -218,20 +220,22 @@ function App() {
|
|||||||
<span className="p-4 absolute top-0 right-0 text-xs whitespace-pre text-right">
|
<span className="p-4 absolute top-0 right-0 text-xs whitespace-pre text-right">
|
||||||
{!cycle.started
|
{!cycle.started
|
||||||
? `press ctrl+enter to play\n`
|
? `press ctrl+enter to play\n`
|
||||||
: !isHot && activePattern !== pattern
|
: !isHot && code !== activeCode
|
||||||
? `ctrl+enter to update\n`
|
? `ctrl+enter to update\n`
|
||||||
: 'no changes\n'}
|
: 'no changes\n'}
|
||||||
{!isHot && <>{{ pegjs: 'mini' }[mode] || mode} mode</>}
|
|
||||||
{isHot && '🔥 hot mode: go to hot.js to edit pattern, then save'}
|
{isHot && '🔥 hot mode: go to hot.js to edit pattern, then save'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{error && <div className="absolute right-2 bottom-2 text-red-500">{error?.message || 'unknown error'}</div>}
|
{error && (
|
||||||
|
<div className={cx('absolute right-2 bottom-2', 'text-red-500')}>{error?.message || 'unknown error'}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="flex-none w-full border border-gray-700 p-2 bg-slate-700 hover:bg-slate-500"
|
className="flex-none w-full border border-gray-700 p-2 bg-slate-700 hover:bg-slate-500"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!cycle.started) {
|
if (!cycle.started) {
|
||||||
activatePattern();
|
// activatePattern();
|
||||||
|
activateCode();
|
||||||
} else {
|
} else {
|
||||||
cycle.stop();
|
cycle.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user