diff --git a/website/src/pages/vanilla/2.astro b/website/src/pages/vanilla/2.astro new file mode 100644 index 00000000..543d5fed --- /dev/null +++ b/website/src/pages/vanilla/2.astro @@ -0,0 +1,16 @@ +--- +import HeadCommon from '../../components/HeadCommon.astro'; +// import { Repl } from '../repl/Repl.jsx'; + +import { Repl2 } from '../../repl/Repl2'; +--- + + + + + Strudel REPL + + + + + diff --git a/website/src/repl/Repl2.jsx b/website/src/repl/Repl2.jsx new file mode 100644 index 00000000..c4241a49 --- /dev/null +++ b/website/src/repl/Repl2.jsx @@ -0,0 +1,224 @@ +/* +App.js - +Copyright (C) 2022 Strudel contributors - see +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ + +import { logger, getDrawContext, silence, evalScope, controls } from '@strudel.cycles/core'; +import { cx } from '@strudel.cycles/react'; +import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; +import { transpiler } from '@strudel.cycles/transpiler'; +import { StrudelMirror } from '@strudel/codemirror'; +/* import { writeText } from '@tauri-apps/api/clipboard'; +import { nanoid } from 'nanoid'; */ +import { createContext, useState } from 'react'; +import { useSettings } from '../settings.mjs'; +import { isTauri } from '../tauri.mjs'; +import { Panel } from './panel/Panel'; +import { Header } from './Header'; +import Loader from './Loader'; +import './Repl.css'; +import { useCallback, useRef, useEffect } from 'react'; +// import { prebake } from '@strudel/repl'; +import { prebake /* , resetSounds */ } from './prebake.mjs'; + +export const ReplContext = createContext(null); + +export function Repl2({ embedded = false }) { + //const isEmbedded = embedded || window.location !== window.parent.location; + const isEmbedded = false; + const [lastShared, setLastShared] = useState(); + const { panelPosition, isZen } = useSettings(); + /* const replState = useStore($replstate); + const isDirty = useStore($repldirty); */ + const shouldDraw = true; + + const init = useCallback(({ code, shouldDraw }) => { + const drawTime = [0, 4]; + const drawContext = shouldDraw ? getDrawContext() : null; + let onDraw; + if (shouldDraw) { + onDraw = (haps, time, frame, painters) => { + painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); + painters?.forEach((painter) => { + // ctx time haps drawTime paintOptions + painter(drawContext, time, haps, drawTime, { clear: false }); + }); + }; + } + const editor = new StrudelMirror({ + defaultOutput: webaudioOutput, + getTime: () => getAudioContext().currentTime, + transpiler, + autodraw: false, + root: containerRef.current, + initialCode: '// LOADING', + pattern: silence, + drawTime, + onDraw, + prebake: async () => { + let modules = [ + import('@strudel.cycles/core'), + import('@strudel.cycles/tonal'), + import('@strudel.cycles/mini'), + import('@strudel.cycles/xen'), + import('@strudel.cycles/webaudio'), + import('@strudel/codemirror'), + import('@strudel/hydra'), + import('@strudel.cycles/serial'), + import('@strudel.cycles/soundfonts'), + import('@strudel.cycles/csound'), + ]; + if (isTauri()) { + modules = modules.concat([ + import('@strudel/desktopbridge/loggerbridge.mjs'), + import('@strudel/desktopbridge/midibridge.mjs'), + import('@strudel/desktopbridge/oscbridge.mjs'), + ]); + } else { + modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); + } + + const modulesLoading = evalScope( + controls, // sadly, this cannot be exported from core direclty + // settingPatterns, + ...modules, + ); + await Promise.all([modulesLoading, prebake()]); + }, + onUpdateState: (state) => { + setReplState({ ...state }); + }, + }); + // init settings + editor.setCode(code); + editorRef.current = editor; + }, []); + + const [replState, setReplState] = useState({}); + const { started, isDirty, error, activeCode } = replState; + const editorRef = useRef(); + const containerRef = useRef(); + const [client, setClient] = useState(false); + useEffect(() => { + setClient(true); + if (!editorRef.current) { + setTimeout(() => { + init({ code: 's("bd")', shouldDraw }); + }); + } + return () => { + editor.clear(); + }; + }, []); + + // + // UI Actions + // + + const handleTogglePlay = async () => { + editorRef.current?.toggle(); + /* 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(); + logger('[repl] code updated! tip: you can also update the code by pressing ctrl+enter', 'highlight'); + }; + + const handleShuffle = async () => { + // window.postMessage('strudel-shuffle'); + }; + + /* const handleShare = async () => { + const codeToShare = activeCode || code; + if (lastShared === codeToShare) { + logger(`Link already generated!`, 'error'); + return; + } + // generate uuid in the browser + const hash = nanoid(12); + const shareUrl = window.location.origin + window.location.pathname + '?' + hash; + const { data, error } = await supabase.from('code').insert([{ code: codeToShare, hash }]); + if (!error) { + setLastShared(activeCode || code); + // copy shareUrl to clipboard + if (isTauri()) { + await writeText(shareUrl); + } else { + await navigator.clipboard.writeText(shareUrl); + } + const message = `Link copied to clipboard: ${shareUrl}`; + alert(message); + // alert(message); + logger(message, 'highlight'); + } else { + console.log('error', error); + const message = `Error: ${error.message}`; + // alert(message); + logger(message); + } + }; */ + const pending = false; + //const error = undefined; + // const { started, activeCode } = replState; + + const context = { + // scheduler, + embedded, + started, + pending, + isDirty, + lastShared, + activeCode, + // handleChangeCode: codemirror.handleChangeCode, + handleTogglePlay, + handleUpdate, + handleShuffle, + /* handleShare, */ + handleShare: () => {}, + }; + + return ( + // bg-gradient-to-t from-blue-900 to-slate-900 + // bg-gradient-to-t from-green-900 to-slate-900 + +
+ +
+ {/* isEmbedded && !started && ( + + ) */} +
+
+ {panelPosition === 'right' && !isEmbedded && } +
+ {error && ( +
{error.message || 'Unknown Error :-/'}
+ )} + {panelPosition === 'bottom' && !isEmbedded && } +
+
+ ); +}