mirror of
https://codeberg.org/uzu/strudel
synced 2026-09-05 06:57:35 -04:00
intermediate /vanilla repl
+ react is now only used for ui around editor! + ui still not fully functional
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { EditorState, Compartment } from '@codemirror/state';
|
||||
import { EditorView, highlightActiveLineGutter, keymap, lineNumbers } from '@codemirror/view';
|
||||
import { Drawer, repl } from '@strudel.cycles/core';
|
||||
import { flashField, flash } from './flash.mjs';
|
||||
import { highlightExtension, highlightMiniLocations } from './highlight.mjs';
|
||||
import { highlightExtension, highlightMiniLocations, updateMiniLocations } from './highlight.mjs';
|
||||
import { oneDark } from './themes/one-dark';
|
||||
|
||||
const themeComparment = new Compartment();
|
||||
|
||||
// https://codemirror.net/docs/guide/
|
||||
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, theme = oneDark, root }) {
|
||||
let state = EditorState.create({
|
||||
doc: initialCode,
|
||||
extensions: [
|
||||
theme,
|
||||
themeComparment.of(theme),
|
||||
javascript(),
|
||||
lineNumbers(),
|
||||
highlightExtension,
|
||||
@@ -43,7 +45,7 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, the
|
||||
|
||||
export class StrudelMirror {
|
||||
constructor(options) {
|
||||
const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, ...replOptions } = options;
|
||||
const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, theme, ...replOptions } = options;
|
||||
this.code = initialCode;
|
||||
|
||||
this.drawer = new Drawer((haps, time) => {
|
||||
@@ -81,12 +83,14 @@ export class StrudelMirror {
|
||||
await prebaked;
|
||||
},
|
||||
afterEval: (options) => {
|
||||
updateMiniLocations(this.editor, options.meta?.miniLocations);
|
||||
replOptions?.afterEval?.(options);
|
||||
this.drawer.invalidate();
|
||||
},
|
||||
});
|
||||
this.editor = initEditor({
|
||||
root,
|
||||
theme,
|
||||
initialCode,
|
||||
onChange: (v) => {
|
||||
this.code = v.state.doc.toString();
|
||||
@@ -108,6 +112,15 @@ export class StrudelMirror {
|
||||
flash(this.editor, ms);
|
||||
}
|
||||
highlight(haps, time) {
|
||||
highlightMiniLocations(this.editor.view, time, haps);
|
||||
highlightMiniLocations(this.editor, time, haps);
|
||||
}
|
||||
setTheme(theme) {
|
||||
this.editor.dispatch({
|
||||
effects: themeComparment.reconfigure(theme),
|
||||
});
|
||||
}
|
||||
setCode(code) {
|
||||
const changes = { from: 0, to: this.editor.state.doc.length, insert: code };
|
||||
this.editor.dispatch({ changes });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
},
|
||||
"homepage": "https://strudel.cc",
|
||||
"dependencies": {
|
||||
"fraction.js": "^4.2.0"
|
||||
"fraction.js": "^4.2.0",
|
||||
"nanostores": "^0.8.1"
|
||||
},
|
||||
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
|
||||
"devDependencies": {
|
||||
|
||||
+28
-1
@@ -4,6 +4,19 @@ import { logger } from './logger.mjs';
|
||||
import { setTime } from './time.mjs';
|
||||
import { evalScope } from './evaluate.mjs';
|
||||
import { register } from './pattern.mjs';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const $replstate = atom({
|
||||
schedulerError: undefined,
|
||||
evalError: undefined,
|
||||
code: '// LOADING',
|
||||
activeCode: '// LOADING',
|
||||
pattern: undefined,
|
||||
miniLocations: [],
|
||||
widgets: [],
|
||||
pending: true,
|
||||
});
|
||||
export const setReplState = (key, value) => $replstate.set({ ...$replstate.get(), [key]: value });
|
||||
|
||||
export function repl({
|
||||
interval,
|
||||
@@ -22,7 +35,10 @@ export function repl({
|
||||
onTrigger: getTrigger({ defaultOutput, getTime }),
|
||||
onError: onSchedulerError,
|
||||
getTime,
|
||||
onToggle,
|
||||
onToggle: (started) => {
|
||||
setReplState('started', started);
|
||||
onToggle?.(started);
|
||||
},
|
||||
});
|
||||
let playPatterns = [];
|
||||
const setPattern = (pattern, autostart = true) => {
|
||||
@@ -35,6 +51,8 @@ export function repl({
|
||||
throw new Error('no code to evaluate');
|
||||
}
|
||||
try {
|
||||
setReplState('code', code);
|
||||
setReplState('pending', true);
|
||||
await beforeEval?.({ code });
|
||||
playPatterns = [];
|
||||
let { pattern, meta } = await _evaluate(code, transpiler);
|
||||
@@ -43,11 +61,20 @@ export function repl({
|
||||
}
|
||||
logger(`[eval] code updated`);
|
||||
setPattern(pattern, autostart);
|
||||
setReplState('miniLocations', meta?.miniLocations || []);
|
||||
setReplState('widgets', meta?.widgets || []);
|
||||
setReplState('activeCode', code);
|
||||
setReplState('pattern', pattern);
|
||||
setReplState('evalError', undefined);
|
||||
setReplState('schedulerError', undefined);
|
||||
setReplState('pending', false);
|
||||
afterEval?.({ code, pattern, meta });
|
||||
return pattern;
|
||||
} catch (err) {
|
||||
// console.warn(`[repl] eval error: ${err.message}`);
|
||||
logger(`[eval] error: ${err.message}`, 'error');
|
||||
setReplState('evalError', err);
|
||||
setReplState('pending', false);
|
||||
onEvalError?.(err);
|
||||
}
|
||||
};
|
||||
|
||||
Generated
+3
@@ -102,6 +102,9 @@ importers:
|
||||
fraction.js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
nanostores:
|
||||
specifier: ^0.8.1
|
||||
version: 0.8.1
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
|
||||
@@ -28,7 +28,7 @@ export function MiniRepl({
|
||||
useEffect(() => {
|
||||
// we have to load this package on the client
|
||||
// because codemirror throws an error on the server
|
||||
Promise.all([import('@strudel.cycles/react')])
|
||||
Promise.all([import('@strudel.cycles/react'), init])
|
||||
.then(([res]) => setRepl(() => res.MiniRepl))
|
||||
.catch((err) => console.error(err));
|
||||
}, []);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
import HeadCommon from '../components/HeadCommon.astro';
|
||||
import VanillaRepl from '../repl/VanillaRepl.astro';
|
||||
---
|
||||
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<title>Strudel Vanilla REPL</title>
|
||||
</head>
|
||||
<body class="h-app-height bg-background">
|
||||
<VanillaRepl />
|
||||
</body>
|
||||
</html>
|
||||
+30
-109
@@ -5,32 +5,20 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||
import { getDrawContext, logger } from '@strudel.cycles/core';
|
||||
import { CodeMirror, cx } from '@strudel.cycles/react';
|
||||
import { getAudioContext, resetLoadedSounds } from '@strudel.cycles/webaudio';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import { getDrawContext, logger, $replstate } from '@strudel.cycles/core';
|
||||
import { cx } from '@strudel.cycles/react';
|
||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { writeText } from '@tauri-apps/api/clipboard';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { createContext, useEffect, useMemo, useState } from 'react';
|
||||
import { settingsMap, useSettings } from '../settings.mjs';
|
||||
import { createContext, useState } from 'react';
|
||||
import { useSettings } from '../settings.mjs';
|
||||
import { isTauri } from '../tauri.mjs';
|
||||
import { Footer } from './Footer';
|
||||
import { Header } from './Header';
|
||||
import Loader from './Loader';
|
||||
import './Repl.css';
|
||||
import { hash2code, code2hash } from './helpers.mjs';
|
||||
import * as tunes from './tunes.mjs';
|
||||
import { useRepl } from './useRepl';
|
||||
import { setLatestCode } from '../settings.mjs';
|
||||
import { resetSounds } from './prebake.mjs';
|
||||
|
||||
const { latestCode } = settingsMap.get();
|
||||
|
||||
// Create a single supabase client for interacting with your database
|
||||
const supabase = createClient(
|
||||
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
||||
);
|
||||
import { useStore } from '@nanostores/react';
|
||||
|
||||
let clearCanvas;
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -38,106 +26,30 @@ if (typeof window !== 'undefined') {
|
||||
clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width);
|
||||
}
|
||||
|
||||
async function initCode() {
|
||||
// load code from url hash (either short hash from database or decode long hash)
|
||||
try {
|
||||
const initialUrl = window.location.href;
|
||||
const hash = initialUrl.split('?')[1]?.split('#')?.[0];
|
||||
const codeParam = window.location.href.split('#')[1] || '';
|
||||
// looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length)
|
||||
if (codeParam) {
|
||||
// looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length)
|
||||
return hash2code(codeParam);
|
||||
} else if (hash) {
|
||||
return supabase
|
||||
.from('code')
|
||||
.select('code')
|
||||
.eq('hash', hash)
|
||||
.then(({ data, error }) => {
|
||||
if (error) {
|
||||
console.warn('failed to load hash', err);
|
||||
}
|
||||
if (data.length) {
|
||||
//console.log('load hash from database', hash);
|
||||
return data[0].code;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomTune() {
|
||||
const allTunes = Object.entries(tunes);
|
||||
const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
||||
const [name, code] = randomItem(allTunes);
|
||||
return { name, code };
|
||||
}
|
||||
|
||||
const { code: randomTune, name } = getRandomTune();
|
||||
|
||||
export const ReplContext = createContext(null);
|
||||
|
||||
export function Repl({ embedded = false }) {
|
||||
const isEmbedded = embedded || window.location !== window.parent.location;
|
||||
//const isEmbedded = embedded || window.location !== window.parent.location;
|
||||
const isEmbedded = false;
|
||||
const [lastShared, setLastShared] = useState();
|
||||
const { panelPosition, isZen } = useSettings();
|
||||
|
||||
const {
|
||||
codemirror,
|
||||
code,
|
||||
setCode,
|
||||
scheduler,
|
||||
evaluate,
|
||||
activateCode,
|
||||
isDirty,
|
||||
activeCode,
|
||||
pattern,
|
||||
started,
|
||||
stop,
|
||||
error,
|
||||
pending,
|
||||
setPending,
|
||||
} = useRepl({
|
||||
afterEval: ({ code }) => {
|
||||
setLatestCode(code);
|
||||
window.location.hash = '#' + code2hash(code);
|
||||
},
|
||||
});
|
||||
|
||||
// init code
|
||||
useEffect(() => {
|
||||
initCode().then((decoded) => {
|
||||
let msg;
|
||||
if (decoded) {
|
||||
setCode(decoded);
|
||||
msg = `I have loaded the code from the URL.`;
|
||||
} else if (latestCode) {
|
||||
setCode(latestCode);
|
||||
msg = `Your last session has been loaded!`;
|
||||
} /* if(randomTune) */ else {
|
||||
setCode(randomTune);
|
||||
msg = `A random code snippet named "${name}" has been loaded!`;
|
||||
}
|
||||
logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight');
|
||||
setPending(false);
|
||||
});
|
||||
}, []);
|
||||
const replState = useStore($replstate);
|
||||
|
||||
//
|
||||
// UI Actions
|
||||
//
|
||||
|
||||
const handleTogglePlay = async () => {
|
||||
await getAudioContext().resume(); // fixes no sound in ios webkit
|
||||
console.log('toggle.');
|
||||
window.postMessage('strudel-toggle-play');
|
||||
/* await getAudioContext().resume(); // fixes no sound in ios webkit
|
||||
if (!started) {
|
||||
logger('[repl] started. tip: you can also start by pressing ctrl+enter', 'highlight');
|
||||
activateCode();
|
||||
} else {
|
||||
logger('[repl] stopped. tip: you can also stop by pressing ctrl+dot', 'highlight');
|
||||
stop();
|
||||
}
|
||||
} */
|
||||
};
|
||||
const handleUpdate = () => {
|
||||
isDirty && activateCode();
|
||||
@@ -149,7 +61,7 @@ export function Repl({ embedded = false }) {
|
||||
logger(`[repl] ✨ loading random tune "${name}"`);
|
||||
clearCanvas();
|
||||
await resetSounds();
|
||||
scheduler.setCps(1);
|
||||
// scheduler.setCps(1);
|
||||
await evaluate(code, false);
|
||||
};
|
||||
|
||||
@@ -182,15 +94,18 @@ export function Repl({ embedded = false }) {
|
||||
logger(message);
|
||||
}
|
||||
};
|
||||
const pending = false;
|
||||
const error = undefined;
|
||||
const { started, activeCode } = replState;
|
||||
const context = {
|
||||
scheduler,
|
||||
// scheduler,
|
||||
embedded,
|
||||
started,
|
||||
pending,
|
||||
isDirty,
|
||||
isDirty: false,
|
||||
lastShared,
|
||||
activeCode,
|
||||
handleChangeCode: codemirror.handleChangeCode,
|
||||
// handleChangeCode: codemirror.handleChangeCode,
|
||||
handleTogglePlay,
|
||||
handleUpdate,
|
||||
handleShuffle,
|
||||
@@ -209,7 +124,7 @@ export function Repl({ embedded = false }) {
|
||||
>
|
||||
<Loader active={pending} />
|
||||
<Header context={context} />
|
||||
{isEmbedded && !started && (
|
||||
{/* isEmbedded && !started && (
|
||||
<button
|
||||
onClick={() => handleTogglePlay()}
|
||||
className="text-white text-2xl fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-[1000] m-auto p-4 bg-black rounded-md flex items-center space-x-2"
|
||||
@@ -217,10 +132,16 @@ export function Repl({ embedded = false }) {
|
||||
<PlayCircleIcon className="w-6 h-6" />
|
||||
<span>play</span>
|
||||
</button>
|
||||
)}
|
||||
) */}
|
||||
<div className="grow flex relative overflow-hidden">
|
||||
<section className={'text-gray-100 cursor-text pb-0 overflow-auto grow' + (isZen ? ' px-10' : '')} id="code">
|
||||
<CodeMirror {...codemirror} />
|
||||
<section
|
||||
className={'text-gray-100 cursor-text pb-0 overflow-auto grow' + (isZen ? ' px-10' : '')}
|
||||
id="code"
|
||||
ref={() => {
|
||||
window.postMessage('strudel-container');
|
||||
}}
|
||||
>
|
||||
{/* <CodeMirror {...codemirror} /> */}
|
||||
</section>
|
||||
{panelPosition === 'right' && !isEmbedded && <Footer context={context} />}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
import HeadCommon from '../components/HeadCommon.astro';
|
||||
import { Repl } from '../repl/Repl.jsx';
|
||||
---
|
||||
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<title>Strudel REPL</title>
|
||||
</head>
|
||||
<body class="h-app-height bg-background">
|
||||
<Repl client:only="react" />
|
||||
<script src="./vanillarepl.mjs"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -150,7 +150,7 @@ export async function prebake() {
|
||||
...modules,
|
||||
);
|
||||
// register sounds and samples
|
||||
return Promise.all([initAudio, modulesLoading, registerStockSounds()]);
|
||||
return Promise.all([/* initAudio, */ modulesLoading, registerStockSounds()]);
|
||||
// await samples('github:tidalcycles/Dirt-Samples/master');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import { StrudelMirror } from '@strudel/codemirror';
|
||||
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { prebake } from './prebake.mjs';
|
||||
import { settingsMap } from '@src/settings.mjs';
|
||||
import { themes } from '@src/repl/themes.mjs';
|
||||
import { setLatestCode } from '../settings.mjs';
|
||||
import { hash2code, code2hash } from './helpers.mjs';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
import * as tunes from './tunes.mjs';
|
||||
|
||||
const onEvent = (key, callback) => {
|
||||
const listener = (e) => {
|
||||
if (e.data === key) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', listener);
|
||||
return () => window.removeEventListener('message', listener);
|
||||
};
|
||||
|
||||
function run() {
|
||||
const { latestCode } = settingsMap.get();
|
||||
|
||||
/* let clearCanvas;
|
||||
if (typeof window !== 'undefined') {
|
||||
const drawContext = getDrawContext();
|
||||
clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width);
|
||||
} */
|
||||
|
||||
// Create a single supabase client for interacting with your database
|
||||
const supabase = createClient(
|
||||
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
||||
);
|
||||
async function initCode() {
|
||||
// load code from url hash (either short hash from database or decode long hash)
|
||||
try {
|
||||
const initialUrl = window.location.href;
|
||||
const hash = initialUrl.split('?')[1]?.split('#')?.[0];
|
||||
const codeParam = window.location.href.split('#')[1] || '';
|
||||
// looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length)
|
||||
if (codeParam) {
|
||||
// looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length)
|
||||
return hash2code(codeParam);
|
||||
} else if (hash) {
|
||||
return supabase
|
||||
.from('code')
|
||||
.select('code')
|
||||
.eq('hash', hash)
|
||||
.then(({ data, error }) => {
|
||||
if (error) {
|
||||
console.warn('failed to load hash', error);
|
||||
}
|
||||
if (data.length) {
|
||||
//console.log('load hash from database', hash);
|
||||
return data[0].code;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('failed to decode', err);
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.getElementById('code');
|
||||
const setFontSize = (size) => (container.style.fontSize = size + 'px');
|
||||
const setFontFamily = (family) => (container.style.fontFamily = family);
|
||||
|
||||
/* const drawContext = getDrawContext()
|
||||
const drawTime = [-2, 2]; */
|
||||
console.log('container', container);
|
||||
const editor = new StrudelMirror({
|
||||
theme: themes.strudelTheme,
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => getAudioContext().currentTime,
|
||||
transpiler,
|
||||
root: container,
|
||||
initialCode: '// LOADING',
|
||||
/* drawTime,
|
||||
onDraw: (haps, time) =>
|
||||
drawPianoroll({ haps, time, ctx: drawContext, drawTime, fold: 1 }), */
|
||||
prebake: () => prebake(),
|
||||
afterEval: ({ code }) => {
|
||||
setLatestCode(code);
|
||||
window.location.hash = '#' + code2hash(code);
|
||||
},
|
||||
});
|
||||
|
||||
function getRandomTune() {
|
||||
const allTunes = Object.entries(tunes);
|
||||
const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
||||
const [name, code] = randomItem(allTunes);
|
||||
return { name, code };
|
||||
}
|
||||
|
||||
const { code: randomTune, name } = getRandomTune();
|
||||
|
||||
function init() {
|
||||
if (!container) {
|
||||
console.warn('could not init: no container found');
|
||||
return;
|
||||
}
|
||||
const settings = settingsMap.get();
|
||||
setFontSize(settings.fontSize);
|
||||
setFontFamily(settings.fontFamily);
|
||||
initCode().then((decoded) => {
|
||||
let msg;
|
||||
if (decoded) {
|
||||
editor.setCode(decoded);
|
||||
msg = `I have loaded the code from the URL.`;
|
||||
} else if (latestCode) {
|
||||
editor.setCode(latestCode);
|
||||
msg = `Your last session has been loaded!`;
|
||||
} /* if(randomTune) */ else {
|
||||
editor.setCode(randomTune);
|
||||
msg = `A random code snippet named "${name}" has been loaded!`;
|
||||
}
|
||||
console.log('msg', msg);
|
||||
/* logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight');
|
||||
setPending(false); */
|
||||
});
|
||||
|
||||
editor.setTheme(themes[settings.theme || 'strudelTheme']);
|
||||
}
|
||||
init();
|
||||
|
||||
settingsMap.listen((settings, key) => {
|
||||
const value = settings[key];
|
||||
if (key === 'theme') {
|
||||
editor.setTheme(themes[value]);
|
||||
} else if (key === 'fontFamily') {
|
||||
// console.log('change fontFamily', value);
|
||||
setFontFamily(value);
|
||||
} else if (key === 'fontSize') {
|
||||
// console.log('change fontSize', value);
|
||||
setFontSize(value);
|
||||
}
|
||||
});
|
||||
|
||||
onEvent('strudel-toggle-play', () => {
|
||||
console.log('toggle-play');
|
||||
editor.evaluate();
|
||||
});
|
||||
|
||||
// const isEmbedded = embedded || window.location !== window.parent.location;
|
||||
}
|
||||
|
||||
let inited = false;
|
||||
onEvent('strudel-container', () => {
|
||||
if (!inited) {
|
||||
inited = true;
|
||||
run();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user