/* 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, initAudioOnFirstClick } from '@strudel.cycles/webaudio'; import { transpiler } from '@strudel.cycles/transpiler'; import { StrudelMirror } from '@strudel/codemirror'; import { createClient } from '@supabase/supabase-js'; /* import { writeText } from '@tauri-apps/api/clipboard'; import { nanoid } from 'nanoid'; */ import { createContext, useState } from 'react'; import { useSettings, settingsMap, setLatestCode, updateUserCode, setActivePattern, getActivePattern, getUserPattern, initUserCode, settingPatterns, } 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'; import * as tunes from './tunes.mjs'; export const ReplContext = createContext(null); const { latestCode } = settingsMap.get(); initAudioOnFirstClick(); // Create a single supabase client for interacting with your database const supabase = createClient( 'https://pidxdsxphlhzjnzmifth.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', ); 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, ); const presets = prebake(); let drawContext, clearCanvas; if (typeof window !== 'undefined') { drawContext = getDrawContext(); clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); } // const getTime = () => getAudioContext().currentTime; 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 () => Promise.all([modulesLoading, presets]), 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(); const handleUpdate = () => editorRef.current?.evaluate(); const handleShuffle = async () => { // window.postMessage('strudel-shuffle'); const { code, name } = getRandomTune(); logger(`[repl] ✨ loading random tune "${name}"`); setActivePattern(name); clearCanvas(); resetLoadedSounds(); editorRef.current.repl.setCps(1); await prebake(); // declare default samples editorRef.current.setCode(code); editorRef.current.repl.evaluate(code); }; /* 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 && }
); } 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 }; }