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'; 10 11// 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); 16 17let dbLoaded; 18/* if (typeof window !== 'undefined') { 19 dbLoaded = loadDBPatterns(); 20} */ 21 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} 51 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} 104// 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?'; 115 116export function confirmAndReloadPage(onSuccess) { 117 confirmDialog(SETTING_CHANGE_RELOAD_MSG).then((r) => { 118 if (r == true) { 119 try { 120 onSuccess(); 121 return window.location.reload(); 122 } catch (e) { 123 errorLogger(e); 124 } 125 } 126 }); 127} 128//RIP due to SPAM 129// let lastShared; 130// export async function shareCode(codeToShare) { 131// // const codeToShare = activeCode || code; 132// if (lastShared === codeToShare) { 133// logger(`Link already generated!`, 'error'); 134// return; 135// } 136 137// confirmDialog( 138// 'Do you want your pattern to be public? If no, press cancel and you will get just a private link.', 139// ).then(async (isPublic) => { 140// const hash = nanoid(12); 141// const shareUrl = window.location.origin + window.location.pathname + '?' + hash; 142// const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]); 143// if (!error) { 144// lastShared = codeToShare; 145// // copy shareUrl to clipboard 146// if (isTauri()) { 147// await writeText(shareUrl); 148// } else { 149// await navigator.clipboard.writeText(shareUrl); 150// } 151// const message = `Link copied to clipboard: ${shareUrl}`; 152// alert(message); 153// // alert(message); 154// logger(message, 'highlight'); 155// } else { 156// console.log('error', error); 157// const message = `Error: ${error.message}`; 158// // alert(message); 159// logger(message); 160// } 161// }); 162// } 163 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} 180 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}