1import { code2hash, errorLogger, evalScope, hash2code, logger } from '@strudel/core'; 2import { settingPatterns } from '../settings.mjs'; 3import { setVersionDefaults } from '@strudel/webaudio'; 4import { getMetadata } from '../metadata_parser'; 5import { isTauri } from '../tauri.mjs'; 6import './Repl.css'; 7import { createClient } from '@supabase/supabase-js'; 8import { writeText } from '@tauri-apps/plugin-clipboard-manager'; 9import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs';
Create a single supabase client for interacting with your database
12export const supabase = createClient( 13 'https://pidxdsxphlhzjnzmifth.supabase.co', 14 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', 15);
17let dbLoaded;
if (typeof window !== 'undefined') { dbLoaded = loadDBPatterns(); }
22export async function initCode() { 23 // load code from url hash (either short hash from database or decode long hash) 24 try { 25 const initialUrl = window.location.href; 26 const hash = initialUrl.split('?')[1]?.split('#')?.[0]?.split('&')[0]; 27 const codeParam = window.location.href.split('#')[1] || ''; 28 if (codeParam) { 29 // looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length) 30 return hash2code(codeParam); 31 } else if (hash) { 32 // looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length) 33 return supabase 34 .from('code_v1') 35 .select('code') 36 .eq('hash', hash) 37 .then(({ data, error }) => { 38 if (error) { 39 console.warn('failed to load hash', error); 40 } 41 if (data.length) { 42 //console.log('load hash from database', hash); 43 return data[0].code; 44 } 45 }); 46 } 47 } catch (err) { 48 console.warn('failed to decode', err); 49 } 50}
52export const parseJSON = (json) => { 53 json = json != null && json.length ? json : '{}'; 54 try { 55 return JSON.parse(json); 56 } catch { 57 return '{}'; 58 } 59}; 60 61export async function getRandomTune() { 62 await dbLoaded; 63 const featuredTunes = Object.entries($featuredPatterns.get()); 64 const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; 65 const [_, data] = randomItem(featuredTunes); 66 return data; 67} 68 69export function loadModules() { 70 let modules = [ 71 import('@strudel/core'), 72 import('@strudel/draw'), 73 import('@strudel/edo'), 74 import('@strudel/tonal'), 75 import('@strudel/mini'), 76 import('@strudel/xen'), 77 import('@strudel/webaudio'), 78 import('@strudel/codemirror'), 79 import('@strudel/hydra'), 80 import('@strudel/serial'), 81 import('@strudel/soundfonts'), 82 import('@strudel/csound'), 83 import('@strudel/tidal'), 84 import('@strudel/gamepad'), 85 import('@strudel/motion'), 86 import('@strudel/mqtt'), 87 import('@strudel/mondo'), 88 import('@strudel/dough'), 89 // jevstrudel: `jev({...})` and TypeSafe's choice/score/noul 90 import('../jev/jev.mjs'), 91 ]; 92 if (isTauri()) { 93 modules = modules.concat([ 94 import('@strudel/desktopbridge/loggerbridge.mjs'), 95 import('@strudel/desktopbridge/midibridge.mjs'), 96 import('@strudel/desktopbridge/oscbridge.mjs'), 97 ]); 98 } else { 99 modules = modules.concat([import('@strudel/midi'), import('@strudel/osc')]); 100 } 101 102 return evalScope(settingPatterns, ...modules); 103}
confirm dialog is a promise in webkit and a boolean in other browsers... normalize it to be a promise everywhere
105export function confirmDialog(msg) { 106 const confirmed = confirm(msg); 107 if (confirmed instanceof Promise) { 108 return confirmed; 109 } 110 return new Promise((resolve) => { 111 resolve(confirmed); 112 }); 113} 114export const SETTING_CHANGE_RELOAD_MSG = 'Changing this setting requires the window to reload itself. OK?';
RIP due to SPAM
let lastShared;
export async function shareCode(codeToShare) {
// const codeToShare = activeCode || code;
if (lastShared === codeToShare) {
logger(Link already generated!, 'error');
return;
}
confirmDialog(
'Do you want your pattern to be public? If no, press cancel and you will get just a private link.',
).then(async (isPublic) => {
const hash = nanoid(12);
const shareUrl = window.location.origin + window.location.pathname + '?' + hash;
const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]);
if (!error) {
lastShared = codeToShare;
// 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);
}
});
}
164export async function shareCode(codeToShare) { 165 try { 166 const hash = '#' + code2hash(codeToShare); 167 const shareUrl = window.location.origin + window.location.pathname + hash; 168 if (isTauri()) { 169 await writeText(shareUrl); 170 } else { 171 await navigator.clipboard.writeText(shareUrl); 172 } 173 const message = `Link copied to clipboard!`; 174 alert(message); 175 logger(message, 'highlight'); 176 } catch (e) { 177 console.error(e); 178 } 179}
181export const isIframe = () => window.location !== window.parent.location; 182function isCrossOriginFrame() { 183 try { 184 return !window.top.location.hostname; 185 } catch (e) { 186 return true; 187 } 188} 189 190export const isUdels = () => { 191 if (isCrossOriginFrame()) { 192 return false; 193 } 194 return window.top?.location?.pathname.includes('udels'); 195}; 196 197export function setVersionDefaultsFrom(code) { 198 try { 199 const metadata = getMetadata(code); 200 setVersionDefaults(metadata.version); 201 } catch (err) { 202 console.error('Error parsing metadata..'); 203 console.error(err); 204 } 205}