cleaning up

This commit is contained in:
Jade (Rose) Rowland 2024-01-18 20:01:54 -05:00
parent 7adfe6f2df
commit 6cb156d876
6 changed files with 249 additions and 390 deletions

View File

@ -12,18 +12,17 @@ import { defaultAudioDeviceName } from '../settings.mjs';
import { getAudioDevices, setAudioDevice } from './util.mjs';
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
import { useCallback, useEffect, useRef, useState } from 'react';
import { settingsMap, useSettings } from '../settings.mjs';
import {
initUserCode,
setActivePattern,
setLatestCode,
settingsMap,
useSettings,
getViewingPattern,
setViewingPattern,
createPatternID,
userPattern,
getNextCloneID,
} from '../settings.mjs';
getViewingPatternData,
setViewingPatternData,
} from '../user_pattern_utils.mjs';
import { Header } from './Header';
import Loader from './Loader';
import { Panel } from './panel/Panel';
@ -32,12 +31,8 @@ import { prebake } from './prebake.mjs';
import { getRandomTune, initCode, loadModules, shareCode, ReplContext } from './util.mjs';
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
import './Repl.css';
import { useExamplePatterns } from './useExamplePatterns';
const { code: randomTune, name } = getRandomTune();
//
const { latestCode } = settingsMap.get();
let modulesLoading, presets, drawContext, clearCanvas, isIframe;
@ -50,8 +45,6 @@ if (typeof window !== 'undefined') {
isIframe = window.location !== window.parent.location;
}
let viewingPatternData = { id: '', code: null, collection: userPattern.source };
export function Repl({ embedded = false }) {
const isEmbedded = embedded || isIframe;
const { panelPosition, isZen } = useSettings();
@ -83,23 +76,24 @@ export function Repl({ embedded = false }) {
const { code } = all;
setLatestCode(code);
window.location.hash = '#' + code2hash(code);
const viewingPatternData = getViewingPatternData();
const data = { ...viewingPatternData, code };
let id = getViewingPattern();
const isExamplePattern = viewingPatternData.collection != userPattern.source;
let id = data.id;
const isExamplePattern = viewingPatternData.collection !== userPattern.collection;
if (isExamplePattern) {
const codeHasChanged = code !== viewingPatternData.code;
if (codeHasChanged) {
// fork example
id = getNextCloneID(id);
id = createPatternID();
setViewingPattern(id);
viewingPatternData = userPattern.update(id, data).data;
setViewingPatternData(userPattern.update(id, data).data);
}
} else {
id = id == null ? createPatternID() : id;
setViewingPattern(id);
viewingPatternData = userPattern.update(id, data).data;
setViewingPatternData(userPattern.update(id, data).data);
}
setActivePattern(id);
},
@ -176,7 +170,7 @@ export function Repl({ embedded = false }) {
};
const handleUpdate = async (id, data, reset = false) => {
viewingPatternData = data;
setViewingPatternData(data);
if (reset) {
await resetEditor();
}

View File

@ -1,20 +1,13 @@
import { DocumentDuplicateIcon, PencilSquareIcon, TrashIcon } from '@heroicons/react/20/solid';
import { DocumentDuplicateIcon, TrashIcon } from '@heroicons/react/20/solid';
import { useSettings } from '../../settings.mjs';
import {
$featuredPatterns,
$publicPatterns,
exportPatterns,
importPatterns,
useActivePattern,
useViewingPattern,
useSettings,
userPattern,
examplePattern,
} from '../../settings.mjs';
} from '../../user_pattern_utils.mjs';
import { useMemo } from 'react';
import { useStore } from '@nanostores/react';
import { getMetadata } from '../../metadata_parser';
import { useExamplePatterns } from '../useExamplePatterns';
@ -24,19 +17,14 @@ function classNames(...classes) {
function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) {
const meta = useMemo(() => getMetadata(pattern.code), [pattern]);
return (
<>{`${pattern.id}: ${meta.title ?? pattern.hash ?? 'unnamed'} by ${
Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous'
}`}</>
<>{`${pattern.id}: ${
meta.title ?? pattern.hash ?? new Date(pattern.created_at).toLocaleDateString() ?? 'unnamed'
} by ${Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous'}`}</>
);
}
const getPatternLabel = (pattern) => {
return `${pattern.id}: ${meta.title ?? pattern.hash ?? 'unnamed'} by ${
Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous'
}`;
};
function PatternButton({ showOutline, onClick, pattern, showHiglight }) {
return (
<a
@ -52,24 +40,6 @@ function PatternButton({ showOutline, onClick, pattern, showHiglight }) {
);
}
// function ListButton() {
// return (
// <a
// key={pattern.id}
// className={classNames(
// 'mr-4 hover:opacity-50 cursor-pointer block',
// pattern.hash === activePattern ? 'outline outline-1' : '',
// )}
// onClick={() => {
// setActivePattern(pattern.hash);
// context.handleUpdate(pattern.code, true);
// }}
// >
// <PatternLabel pattern={pattern} />
// </a>
// );
// }
function PatternButtons({ patterns, activePattern, onClick, viewingPattern, started }) {
return (
<div className="font-mono text-sm">
@ -89,28 +59,27 @@ function PatternButtons({ patterns, activePattern, onClick, viewingPattern, star
);
}
function ActionButton({ children, onClick, label, labelIsHidden }) {
return (
<button className="hover:opacity-50" onClick={onClick} title={label}>
{labelIsHidden !== true && label}
{children}
</button>
);
}
export function PatternsTab({ context }) {
const activePattern = useActivePattern();
const viewingPattern = useViewingPattern();
const { userPatterns } = useSettings();
const examplePatterns = useExamplePatterns();
const collections = examplePatterns.collections;
const examplesData = examplePatterns.patterns;
const updateCodeWindow = (id, data, reset = false) => {
context.handleUpdate(id, data, reset);
// if (patternSource === userPattern.source) {
// } else {
// const source = otherPatterns.get(patternSource);
// const data = source[id];
// }
};
const isUserPattern = userPatterns[viewingPattern] != null;
// const isExample = useMemo(() => activePattern && !!tunes[activePattern], [activePattern]);
return (
<div className="px-4 w-full dark:text-white text-stone-900 space-y-4 pb-4">
<section>
@ -118,69 +87,56 @@ export function PatternsTab({ context }) {
<div className="flex items-center mb-2 space-x-2 overflow-auto">
<h1 className="text-xl">{`${viewingPattern}`}</h1>
<div className="space-x-4 flex w-min">
{/* {!isExample && (
<button
className="hover:opacity-50"
onClick={() => {
const { id, data } = userPattern.rename(viewingPattern);
updateCodeWindow(id, data.code);
}}
title="Rename"
>
<PencilSquareIcon className="w-5 h-5" />
</button>
)} */}
<button
className="hover:opacity-50"
<ActionButton
label="Duplicate"
onClick={() => {
const { id, data } = userPattern.duplicate(viewingPattern);
updateCodeWindow(id, data.code);
const { id, data } = userPattern.duplicate(
userPattern.getPatternData(id) ?? examplePatterns.patterns[id],
);
updateCodeWindow(id, data);
}}
title="Duplicate"
labelIsHidden
>
<DocumentDuplicateIcon className="w-5 h-5" />
</button>
</ActionButton>
{isUserPattern && (
<button
className="hover:opacity-50"
<ActionButton
label="Delete"
onClick={() => {
const { id, data } = userPattern.delete(viewingPattern);
updateCodeWindow(id, { ...data, collection: userPattern.source });
updateCodeWindow(id, { ...data, collection: userPattern.collection });
}}
title="Delete"
labelIsHidden
>
<TrashIcon className="w-5 h-5" />
</button>
</ActionButton>
)}
</div>
</div>
)}
<PatternButtons
onClick={(id) => updateCodeWindow(id, { ...userPatterns[id], collection: userPattern.source }, false)}
onClick={(id) => updateCodeWindow(id, { ...userPatterns[id], collection: userPattern.collection }, false)}
patterns={userPatterns}
started={context.started}
activePattern={activePattern}
viewingPattern={viewingPattern}
/>
<div className="pr-4 space-x-4 border-b border-foreground mb-2 h-8 flex overflow-auto max-w-full items-center">
<button
className="hover:opacity-50"
<ActionButton
label="new"
onClick={() => {
const { id, data } = userPattern.create();
updateCodeWindow(id, data.code);
const { id, data } = userPattern.createAndAddToDB();
updateCodeWindow(id, data);
}}
>
new
</button>
<button
className="hover:opacity-50"
/>
<ActionButton
label="clear"
onClick={() => {
const { id, data } = userPattern.clearAll();
updateCodeWindow(id, data.code);
updateCodeWindow(id, data);
}}
>
clear
</button>
/>
<label className="hover:opacity-50 cursor-pointer">
<input
style={{ display: 'none' }}
@ -191,14 +147,11 @@ export function PatternsTab({ context }) {
/>
import
</label>
<button className="hover:opacity-50" onClick={() => exportPatterns()}>
export
</button>
<ActionButton label="export" onClick={exportPatterns} />
</div>
</section>
{Array.from(collections.keys()).map((collection) => {
const patterns = collections.get(collection);
return (
<section key={collection}>
<h2 className="text-xl mb-2">{collection}</h2>

View File

@ -1,34 +1,23 @@
import { examplePattern, $featuredPatterns, $publicPatterns } from '../settings.mjs';
import { $featuredPatterns, $publicPatterns } from '../user_pattern_utils.mjs';
import { useStore } from '@nanostores/react';
import { useCallback, useMemo } from 'react';
import { useMemo } from 'react';
import * as tunes from '../repl/tunes.mjs';
export const useExamplePatterns = () => {
const featuredPatterns = useStore($featuredPatterns);
const publicPatterns = useStore($publicPatterns);
const collections = useMemo(() => {
const stockPatterns = Object.fromEntries(Object.entries(tunes).map(([key, code], i) => [i, { id: i, code }]));
const pats = new Map();
pats.set('Featured', featuredPatterns);
pats.set('Last Creations', publicPatterns);
pats.set(examplePattern.source, examplePattern.getAll());
pats.set('Stock Examples', stockPatterns);
return pats;
}, [featuredPatterns, publicPatterns]);
const patterns = useMemo(() => {
const allPatterns = Object.assign({}, ...collections.values());
return allPatterns;
return Object.assign({}, ...collections.values());
}, [collections]);
// const examplePatterns = examplePattern.getAll();
// const collections = new Map();
// collections.set('Featured', featuredPatterns);
// collections.set('Last Creations', publicPatterns);
// collections.set(examplePattern.source, examplePatterns);
// const patterns = {
// ...examplePatterns,
// ...publicPatterns,
// ...examplePatterns,
// };
return { patterns, collections };
};

View File

@ -9,7 +9,6 @@ import { createClient } from '@supabase/supabase-js';
import { nanoid } from 'nanoid';
import { writeText } from '@tauri-apps/api/clipboard';
import { createContext } from 'react';
import { $publicPatterns, $featuredPatterns } from '../settings.mjs';
// Create a single supabase client for interacting with your database
export const supabase = createClient(
@ -17,33 +16,6 @@ export const supabase = createClient(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
);
export function loadPublicPatterns() {
return supabase.from('code').select().eq('public', true).limit(20).order('id', { ascending: false });
}
export function loadFeaturedPatterns() {
return supabase.from('code').select().eq('featured', true).limit(20).order('id', { ascending: false });
}
async function loadDBPatterns() {
try {
const { data: publicPatterns } = await loadPublicPatterns();
const { data: featuredPatterns } = await loadFeaturedPatterns();
const featured = {};
const pub = {};
publicPatterns?.forEach((data, key) => (pub[data.id ?? key] = data));
featuredPatterns?.forEach((data, key) => (featured[data.id ?? key] = data));
$publicPatterns.set(pub);
$featuredPatterns.set(featured);
} catch (err) {
console.error('error loading patterns');
}
}
if (typeof window !== 'undefined') {
loadDBPatterns();
}
export async function initCode() {
// load code from url hash (either short hash from database or decode long hash)
try {

View File

@ -1,12 +1,6 @@
import { atom } from 'nanostores';
import { persistentMap, persistentAtom } from '@nanostores/persistent';
import { persistentMap } from '@nanostores/persistent';
import { useStore } from '@nanostores/react';
import { register } from '@strudel.cycles/core';
import * as tunes from './repl/tunes.mjs';
import { logger } from '@strudel.cycles/core';
import { nanoid } from 'nanoid';
export let $publicPatterns = atom([]);
export let $featuredPatterns = atom([]);
export const defaultAudioDeviceName = 'System Standard';
@ -33,54 +27,6 @@ export const defaultSettings = {
export const settingsMap = persistentMap('strudel-settings', defaultSettings);
const defaultCode = '';
//pattern that the use is currently viewing in the window
const $viewingPattern = persistentAtom('viewingPattern', '', { listen: false });
export function setViewingPattern(key) {
$viewingPattern.set(key);
}
export function getViewingPattern() {
return $viewingPattern.get();
}
export function useViewingPattern() {
return useStore($viewingPattern);
}
// const $viewingCollection = persistentAtom('viewingCollection', '', { listen: false });
// export function setViewingCollection(key) {
// $viewingCollection.set(key);
// }
// export function getViewingCollection() {
// return $viewingCollection.get();
// }
// export function useViewingCollection() {
// return useStore($viewingCollection);
// }
// active pattern is separate, because it shouldn't sync state across tabs
// reason: https://github.com/tidalcycles/strudel/issues/857
const $activePattern = persistentAtom('activePattern', '', { listen: false });
export function setActivePattern(key) {
$activePattern.set(key);
}
export function getActivePattern() {
return $activePattern.get();
}
export function useActivePattern() {
return useStore($activePattern);
}
export function initUserCode(code) {
const patterns = { ...userPattern.getAll(), ...examplePattern.getAll() };
const match = Object.entries(patterns).find(([_, pat]) => pat.code === code);
const id = match?.[0];
if (id != null) {
setActivePattern(id);
setViewingPattern(id);
}
}
export function useSettings() {
const state = useStore(settingsMap);
@ -88,7 +34,6 @@ export function useSettings() {
Object.keys(userPatterns).forEach((key) => {
const data = userPatterns[key];
data.id = data.id ?? key;
data.date = data.date ?? 0;
userPatterns[key] = data;
});
return {
@ -109,7 +54,6 @@ export function useSettings() {
export const setActiveFooter = (tab) => settingsMap.setKey('activeFooter', tab);
export const setLatestCode = (code) => settingsMap.setKey('latestCode', code);
export const setIsZen = (active) => settingsMap.setKey('isZen', !!active);
const patternSetting = (key) =>
@ -128,186 +72,3 @@ export const fontFamily = patternSetting('fontFamily');
export const fontSize = patternSetting('fontSize');
export const settingPatterns = { theme, fontFamily, fontSize };
function getUserPatterns() {
return JSON.parse(settingsMap.get().userPatterns);
}
function setUserPatterns(obj) {
return settingsMap.setKey('userPatterns', JSON.stringify(obj));
}
export const createPatternID = () => {
return nanoid(12);
};
export const getNextCloneID = (id) => {
return createPatternID();
};
const examplePatterns = Object.fromEntries(Object.entries(tunes).map(([key, code], i) => [i, { id: i, code }]));
export const examplePattern = {
source: 'Stock Examples',
getAll() {
return examplePatterns;
},
getPatternData(id) {
const pats = this.getAll();
return pats[id];
},
exists(id) {
return this.getPatternData(id) != null;
},
};
// break
export const userPattern = {
source: 'user',
collection: 'user',
getAll() {
const patterns = JSON.parse(settingsMap.get().userPatterns);
return patterns;
},
getPatternData(id) {
const userPatterns = this.getAll();
return userPatterns[id];
},
exists(id) {
return this.getPatternData(id) != null;
},
create() {
const newID = createPatternID();
const code = defaultCode;
const data = { code, created_at: Date.now(), id: newID, collection: this.collection };
return this.update(newID, data);
},
update(id, data) {
const userPatterns = this.getAll();
data = { ...data, id, collection: this.collection };
setUserPatterns({ ...userPatterns, [id]: data });
return { id, data };
},
duplicate(id) {
const examplePatternData = examplePattern.getPatternData(id);
const data = examplePatternData != null ? examplePatternData : this.getPatternData(id);
const newID = getNextCloneID(id);
return this.update(newID, data);
},
clearAll() {
if (!confirm(`This will delete all your patterns. Are you really sure?`)) {
return;
}
const viewingPattern = getViewingPattern();
const examplePatternData = examplePattern.getPatternData(viewingPattern);
setUserPatterns({});
if (examplePatternData != null) {
return { id: viewingPattern, data: examplePatternData };
}
// setViewingPattern(null);
setActivePattern(null);
return { id: null, data: { code: defaultCode, id: null, collection: this.collection } };
},
delete(id) {
const userPatterns = this.getAll();
delete userPatterns[id];
if (getActivePattern() === id) {
setActivePattern(null);
}
setUserPatterns(userPatterns);
const viewingPattern = getViewingPattern();
if (viewingPattern === id) {
return { id: null, data: { code: defaultCode } };
}
return { id: viewingPattern, data: userPatterns[viewingPattern] };
},
rename(id) {
const userPatterns = this.getAll();
const newID = prompt('Enter new name', id);
const data = userPatterns[id];
if (newID === null) {
// canceled
return { id, data };
}
if (userPatterns[newID]) {
alert('Name already taken!');
return { id, data };
}
userPatterns[newID] = data; // copy code
delete userPatterns[id];
setUserPatterns({ ...userPatterns });
if (id === getActivePattern()) {
setActivePattern(newID);
}
return { id: newID, data };
},
};
// export function updateUserCode(code) {
// const userPatterns = getUserPatterns();
// let activePattern = getActivePattern();
// // check if code is that of an example tune
// const [example] = Object.entries(tunes).find(([_, tune]) => tune === code) || [];
// if (example && (!activePattern || activePattern === example)) {
// // select example
// setActivePattern(example);
// return;
// }
// const publicPattern = $publicPatterns.get().find((pat) => pat.code === code);
// if (publicPattern) {
// setActivePattern(publicPattern.hash);
// return;
// }
// const featuredPattern = $featuredPatterns.get().find((pat) => pat.code === code);
// if (featuredPattern) {
// setActivePattern(featuredPattern.hash);
// return;
// }
// if (!activePattern) {
// // create new user pattern
// activePattern = newUserPattern();
// setActivePattern(activePattern);
// } else if (
// (!!tunes[activePattern] && code !== tunes[activePattern]) || // fork example tune?
// $publicPatterns.get().find((p) => p.hash === activePattern) || // fork public pattern?
// $featuredPatterns.get().find((p) => p.hash === activePattern) // fork featured pattern?
// ) {
// // fork example
// activePattern = getNextCloneName(activePattern);
// setActivePattern(activePattern);
// }
// setUserPatterns({ ...userPatterns, [activePattern]: { code } });
// }
export async function importPatterns(fileList) {
const files = Array.from(fileList);
await Promise.all(
files.map(async (file, i) => {
const content = await file.text();
if (file.type === 'application/json') {
const userPatterns = getUserPatterns() || {};
setUserPatterns({ ...userPatterns, ...JSON.parse(content) });
} else if (file.type === 'text/plain') {
const id = file.name.replace(/\.[^/.]+$/, '');
userPattern.update(id, { code: content });
}
}),
);
logger(`import done!`);
}
export async function exportPatterns() {
const userPatterns = getUserPatterns() || {};
const blob = new Blob([JSON.stringify(userPatterns)], { type: 'application/json' });
const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(blob);
const date = new Date().toISOString().split('T')[0];
downloadLink.download = `strudel_patterns_${date}.json`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}

View File

@ -0,0 +1,190 @@
import { atom } from 'nanostores';
import { persistentAtom } from '@nanostores/persistent';
import { useStore } from '@nanostores/react';
import { logger } from '@strudel.cycles/core';
import { nanoid } from 'nanoid';
import { settingsMap } from './settings.mjs';
import { supabase } from './repl/util.mjs';
export let $publicPatterns = atom([]);
export let $featuredPatterns = atom([]);
const userPatternCollectionName = 'user';
export let $viewingPatternData = atom({ id: null, code: null, collection: userPatternCollectionName });
export const getViewingPatternData = () => {
return $viewingPatternData.get();
};
export const setViewingPatternData = (data) => {
$viewingPatternData.set(data);
};
export function loadPublicPatterns() {
return supabase.from('code').select().eq('public', true).limit(20).order('id', { ascending: false });
}
export function loadFeaturedPatterns() {
return supabase.from('code').select().eq('featured', true).limit(20).order('id', { ascending: false });
}
async function loadDBPatterns() {
try {
const { data: publicPatterns } = await loadPublicPatterns();
const { data: featuredPatterns } = await loadFeaturedPatterns();
const featured = {};
const pub = {};
publicPatterns?.forEach((data, key) => (pub[data.id ?? key] = data));
featuredPatterns?.forEach((data, key) => (featured[data.id ?? key] = data));
$publicPatterns.set(pub);
$featuredPatterns.set(featured);
} catch (err) {
console.error('error loading patterns');
}
}
if (typeof window !== 'undefined') {
loadDBPatterns();
}
//pattern that the use is currently viewing in the window
const $viewingPattern = persistentAtom('viewingPattern', '', { listen: false });
export function setViewingPattern(key) {
$viewingPattern.set(key);
}
export function getViewingPattern() {
return $viewingPattern.get();
}
export function useViewingPattern() {
return useStore($viewingPattern);
}
// active pattern is separate, because it shouldn't sync state across tabs
// reason: https://github.com/tidalcycles/strudel/issues/857
const $activePattern = persistentAtom('activePattern', '', { listen: false });
export function setActivePattern(key) {
$activePattern.set(key);
}
export function getActivePattern() {
return $activePattern.get();
}
export function useActivePattern() {
return useStore($activePattern);
}
export function initUserCode(code) {
const patterns = { ...userPattern.getAll() };
const match = Object.entries(patterns).find(([_, pat]) => pat.code === code);
const id = match?.[0];
if (id != null) {
setActivePattern(id);
setViewingPattern(id);
}
}
export const setLatestCode = (code) => settingsMap.setKey('latestCode', code);
const defaultCode = '';
export const userPattern = {
collection: userPatternCollectionName,
getAll() {
const patterns = JSON.parse(settingsMap.get().userPatterns);
return patterns ?? {};
},
getPatternData(id) {
const userPatterns = this.getAll();
return userPatterns[id];
},
exists(id) {
return this.getPatternData(id) != null;
},
create() {
const newID = createPatternID();
const code = defaultCode;
const data = { code, created_at: Date.now(), id: newID, collection: this.collection };
return { id: newID, data };
},
createAndAddToDB() {
const newPattern = this.create();
this.update(newPattern.id, newPattern.data);
},
update(id, data) {
const userPatterns = this.getAll();
data = { ...data, id, collection: this.collection };
setUserPatterns({ ...userPatterns, [id]: data });
return { id, data };
},
duplicate(data) {
const newID = createPatternID();
return this.update(newID, data);
},
clearAll() {
if (!confirm(`This will delete all your patterns. Are you really sure?`)) {
return;
}
const viewingPatternData = getViewingPatternData();
setUserPatterns({});
if (viewingPatternData.collection !== this.collection) {
return { id: viewingPatternData.id, data: viewingPatternData };
}
setActivePattern(null);
return this.create();
},
delete(id) {
const userPatterns = this.getAll();
delete userPatterns[id];
if (getActivePattern() === id) {
setActivePattern(null);
}
setUserPatterns(userPatterns);
const viewingPattern = getViewingPattern();
if (viewingPattern === id) {
return { id: null, data: { code: defaultCode } };
}
return { id: viewingPattern, data: userPatterns[viewingPattern] };
},
};
function setUserPatterns(obj) {
return settingsMap.setKey('userPatterns', JSON.stringify(obj));
}
export const createPatternID = () => {
return nanoid(12);
};
export const getNextCloneID = (id) => {
return createPatternID();
};
export async function importPatterns(fileList) {
const files = Array.from(fileList);
await Promise.all(
files.map(async (file, i) => {
const content = await file.text();
if (file.type === 'application/json') {
const userPatterns = userPattern.getAll();
setUserPatterns({ ...userPatterns, ...JSON.parse(content) });
} else if (file.type === 'text/plain') {
const id = file.name.replace(/\.[^/.]+$/, '');
userPattern.update(id, { code: content });
}
}),
);
logger(`import done!`);
}
export async function exportPatterns() {
const userPatterns = userPattern.getAll();
const blob = new Blob([JSON.stringify(userPatterns)], { type: 'application/json' });
const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(blob);
const date = new Date().toISOString().split('T')[0];
downloadLink.download = `strudel_patterns_${date}.json`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}