pull out repl utility functions
+ repl2 initCode / switching patterns works now
This commit is contained in:
parent
5c41d6789c
commit
8c0065f563
3 changed files with 157 additions and 146 deletions
|
|
@ -42,45 +42,13 @@ import { isTauri } from '../tauri.mjs';
|
||||||
import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs';
|
import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs';
|
||||||
import { writeText } from '@tauri-apps/api/clipboard';
|
import { writeText } from '@tauri-apps/api/clipboard';
|
||||||
import { registerSamplesFromDB, userSamplesDBConfig } from './idbutils.mjs';
|
import { registerSamplesFromDB, userSamplesDBConfig } from './idbutils.mjs';
|
||||||
|
import { getRandomTune, initCode, loadModules } from './util.mjs';
|
||||||
|
|
||||||
const { latestCode } = settingsMap.get();
|
const { latestCode } = settingsMap.get();
|
||||||
|
|
||||||
initAudioOnFirstClick();
|
initAudioOnFirstClick();
|
||||||
|
|
||||||
// Create a single supabase client for interacting with your database
|
const modulesLoading = loadModules();
|
||||||
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();
|
const presets = prebake();
|
||||||
|
|
||||||
let drawContext, clearCanvas;
|
let drawContext, clearCanvas;
|
||||||
|
|
@ -91,43 +59,6 @@ if (typeof window !== 'undefined') {
|
||||||
|
|
||||||
const getTime = () => getAudioContext().currentTime;
|
const getTime = () => getAudioContext().currentTime;
|
||||||
|
|
||||||
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();
|
const { code: randomTune, name } = getRandomTune();
|
||||||
|
|
||||||
export const ReplContext = createContext(null);
|
export const ReplContext = createContext(null);
|
||||||
|
|
@ -289,35 +220,7 @@ export function Repl({ embedded = false }) {
|
||||||
await evaluate(code, false);
|
await evaluate(code, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShare = async () => {
|
const handleShare = async () => shareCode(activeCode || code);
|
||||||
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 context = {
|
const context = {
|
||||||
scheduler,
|
scheduler,
|
||||||
embedded,
|
embedded,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
||||||
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 <https://www.gnu.org/licenses/>.
|
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 <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { logger, getDrawContext, silence, evalScope, controls } from '@strudel.cycles/core';
|
import { logger, getDrawContext, silence, code2hash } from '@strudel.cycles/core';
|
||||||
import { cx } from '@strudel.cycles/react';
|
import { cx } from '@strudel.cycles/react';
|
||||||
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
||||||
import { transpiler } from '@strudel.cycles/transpiler';
|
import { transpiler } from '@strudel.cycles/transpiler';
|
||||||
|
|
@ -34,47 +34,16 @@ import { useCallback, useRef, useEffect } from 'react';
|
||||||
import { prebake /* , resetSounds */ } from './prebake.mjs';
|
import { prebake /* , resetSounds */ } from './prebake.mjs';
|
||||||
import * as tunes from './tunes.mjs';
|
import * as tunes from './tunes.mjs';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
|
import { getRandomTune, loadModules, initCode } from './util.mjs';
|
||||||
|
|
||||||
|
const { code: randomTune, name } = getRandomTune();
|
||||||
export const ReplContext = createContext(null);
|
export const ReplContext = createContext(null);
|
||||||
|
|
||||||
const { latestCode } = settingsMap.get();
|
const { latestCode } = settingsMap.get();
|
||||||
|
|
||||||
initAudioOnFirstClick();
|
initAudioOnFirstClick();
|
||||||
|
|
||||||
// Create a single supabase client for interacting with your database
|
const modulesLoading = loadModules();
|
||||||
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();
|
const presets = prebake();
|
||||||
|
|
||||||
let drawContext, clearCanvas;
|
let drawContext, clearCanvas;
|
||||||
|
|
@ -94,7 +63,7 @@ export function Repl2({ embedded = false }) {
|
||||||
const isDirty = useStore($repldirty); */
|
const isDirty = useStore($repldirty); */
|
||||||
const shouldDraw = true;
|
const shouldDraw = true;
|
||||||
|
|
||||||
const init = useCallback(({ code, shouldDraw }) => {
|
const init = useCallback(({ shouldDraw }) => {
|
||||||
const drawTime = [0, 4];
|
const drawTime = [0, 4];
|
||||||
const drawContext = shouldDraw ? getDrawContext() : null;
|
const drawContext = shouldDraw ? getDrawContext() : null;
|
||||||
let onDraw;
|
let onDraw;
|
||||||
|
|
@ -121,9 +90,34 @@ export function Repl2({ embedded = false }) {
|
||||||
onUpdateState: (state) => {
|
onUpdateState: (state) => {
|
||||||
setReplState({ ...state });
|
setReplState({ ...state });
|
||||||
},
|
},
|
||||||
|
afterEval: ({ code }) => {
|
||||||
|
updateUserCode(code);
|
||||||
|
// setPending(false);
|
||||||
|
setLatestCode(code);
|
||||||
|
window.location.hash = '#' + code2hash(code);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
// init settings
|
// init settings
|
||||||
editor.setCode(code);
|
initCode().then((decoded) => {
|
||||||
|
console.log('init code');
|
||||||
|
let msg;
|
||||||
|
if (decoded) {
|
||||||
|
editor.setCode(decoded);
|
||||||
|
initUserCode(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!`;
|
||||||
|
}
|
||||||
|
//registers samples that have been saved to the index DB
|
||||||
|
// registerSamplesFromDB(userSamplesDBConfig);
|
||||||
|
logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight');
|
||||||
|
// setPending(false);
|
||||||
|
});
|
||||||
|
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -136,7 +130,7 @@ export function Repl2({ embedded = false }) {
|
||||||
setClient(true);
|
setClient(true);
|
||||||
if (!editorRef.current) {
|
if (!editorRef.current) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
init({ code: 's("bd")', shouldDraw });
|
init({ shouldDraw });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -162,7 +156,19 @@ export function Repl2({ embedded = false }) {
|
||||||
//
|
//
|
||||||
|
|
||||||
const handleTogglePlay = async () => editorRef.current?.toggle();
|
const handleTogglePlay = async () => editorRef.current?.toggle();
|
||||||
const handleUpdate = () => editorRef.current?.evaluate();
|
const handleUpdate = async (newCode, reset = false) => {
|
||||||
|
if (reset) {
|
||||||
|
clearCanvas();
|
||||||
|
resetLoadedSounds();
|
||||||
|
editorRef.current.repl.setCps(1);
|
||||||
|
await prebake(); // declare default samples
|
||||||
|
}
|
||||||
|
if (newCode || isDirty) {
|
||||||
|
editorRef.current.setCode(newCode);
|
||||||
|
editorRef.current.repl.evaluate(newCode);
|
||||||
|
}
|
||||||
|
logger('[repl] code updated!');
|
||||||
|
};
|
||||||
const handleShuffle = async () => {
|
const handleShuffle = async () => {
|
||||||
// window.postMessage('strudel-shuffle');
|
// window.postMessage('strudel-shuffle');
|
||||||
const { code, name } = getRandomTune();
|
const { code, name } = getRandomTune();
|
||||||
|
|
@ -262,10 +268,3 @@ export function Repl2({ embedded = false }) {
|
||||||
</ReplContext.Provider>
|
</ReplContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 };
|
|
||||||
}
|
|
||||||
|
|
|
||||||
109
website/src/repl/util.mjs
Normal file
109
website/src/repl/util.mjs
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { controls, evalScope, hash2code } from '@strudel.cycles/core';
|
||||||
|
import { settingPatterns } from '../settings.mjs';
|
||||||
|
import { isTauri } from '../tauri.mjs';
|
||||||
|
import './Repl.css';
|
||||||
|
import * as tunes from './tunes.mjs';
|
||||||
|
import { createClient } from '@supabase/supabase-js';
|
||||||
|
|
||||||
|
// Create a single supabase client for interacting with your database
|
||||||
|
const supabase = createClient(
|
||||||
|
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
||||||
|
);
|
||||||
|
|
||||||
|
export 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadModules() {
|
||||||
|
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')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return evalScope(
|
||||||
|
controls, // sadly, this cannot be exported from core direclty
|
||||||
|
settingPatterns,
|
||||||
|
...modules,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function shareCode(codeToShare) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue