Merge pull request #852 from tidalcycles/import-export-patterns

patterns tab: import patterns + style
This commit is contained in:
Felix Roos 2023-12-08 09:41:10 +01:00 committed by GitHub
commit c3e48f1b4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 49 deletions

View file

@ -247,9 +247,15 @@ export function Repl({ embedded = false }) {
stop(); stop();
} }
}; };
const handleUpdate = (newCode) => { const handleUpdate = async (newCode, reset = false) => {
if (reset) {
clearCanvas();
resetLoadedSounds();
scheduler.setCps(1);
await prebake(); // declare default samples
}
(newCode || isDirty) && activateCode(newCode); (newCode || isDirty) && activateCode(newCode);
logger('[repl] code updated! tip: you can also update the code by pressing ctrl+enter', 'highlight'); logger('[repl] code updated!');
}; };
const handleShuffle = async () => { const handleShuffle = async () => {

View file

@ -9,7 +9,9 @@ import {
duplicateActivePattern, duplicateActivePattern,
getUserPattern, getUserPattern,
renameActivePattern, renameActivePattern,
addUserPattern,
} from '../../settings.mjs'; } from '../../settings.mjs';
import { logger } from '@strudel.cycles/core';
function classNames(...classes) { function classNames(...classes) {
return classes.filter(Boolean).join(' '); return classes.filter(Boolean).join(' ');
@ -18,16 +20,15 @@ function classNames(...classes) {
export function PatternsTab({ context }) { export function PatternsTab({ context }) {
const { userPatterns, activePattern } = useSettings(); const { userPatterns, activePattern } = useSettings();
return ( return (
<div className="px-4 w-full text-foreground space-y-4"> <div className="px-4 w-full dark:text-white text-stone-900 space-y-4 pb-4">
<section> <section>
<h2 className="text-xl mb-2">Pattern Collection</h2> <div className="px-4 space-x-4 border-b border-foreground mb-2 h-8">
<div className="space-x-4 border-b border-foreground mb-1">
<button <button
className="hover:opacity-50" className="hover:opacity-50"
onClick={() => { onClick={() => {
const name = newUserPattern(); const name = newUserPattern();
const { code } = getUserPattern(name); const { code } = getUserPattern(name);
context.handleUpdate(code); context.handleUpdate(code, true);
}} }}
> >
new new
@ -44,50 +45,67 @@ export function PatternsTab({ context }) {
<button className="hover:opacity-50" onClick={() => clearUserPatterns()}> <button className="hover:opacity-50" onClick={() => clearUserPatterns()}>
clear clear
</button> </button>
<label className="hover:opacity-50 cursor-pointer">
<input
style={{ display: 'none' }}
type="file"
multiple
accept="text/plain"
onChange={async (e) => {
const files = Array.from(e.target.files);
await Promise.all(
files.map(async (file, i) => {
const code = await file.text();
const name = file.name.replace(/\.[^/.]+$/, '');
console.log(i, name, code);
addUserPattern(name, { code });
}),
);
logger(`import done!`);
}}
/>
import
</label>
</div>
<div className="font-mono text-sm">
{Object.entries(userPatterns).map(([key, up]) => (
<a
key={key}
className={classNames(
'mr-4 hover:opacity-50 cursor-pointer inline-block',
key === activePattern ? 'outline outline-1' : '',
)}
onClick={() => {
const { code } = up;
setActivePattern(key);
context.handleUpdate(code, true);
}}
>
{key}
</a>
))}
</div> </div>
{Object.entries(userPatterns).map(([key, up]) => (
<a
key={key}
className={classNames(
'mr-4 hover:opacity-50 cursor-pointer inline-block',
key === activePattern ? 'underline' : '',
)}
onClick={() => {
const { code } = up;
setActivePattern(key);
context.handleUpdate(code);
}}
>
{key}
</a>
))}
</section> </section>
<section> <section>
<h2 className="text-xl mb-2">Examples</h2> <h2 className="text-xl mb-2">Examples</h2>
{Object.entries(tunes).map(([key, tune]) => ( <div className="font-mono text-sm">
<a {Object.entries(tunes).map(([key, tune]) => (
key={key} <a
className={classNames( key={key}
'mr-4 hover:opacity-50 cursor-pointer inline-block', className={classNames(
key === activePattern ? 'underline' : '', 'mr-4 hover:opacity-50 cursor-pointer inline-block',
)} key === activePattern ? 'outline outline-1' : '',
onClick={() => { )}
setActivePattern(key); onClick={() => {
context.handleUpdate(tune); setActivePattern(key);
}} context.handleUpdate(tune, true);
> }}
{key} >
</a> {key}
))} </a>
))}
</div>
</section> </section>
</div> </div>
); );
} }
/*
selectable examples
if example selected
type character -> create new user pattern with exampleName_n
even if
clicking (+) opens the "new" example with same behavior as above
*/

View file

@ -42,8 +42,8 @@ export function SoundsTab() {
}); });
}); });
return ( return (
<div id="sounds-tab" className="flex flex-col w-full h-full dark:text-white text-stone-900"> <div id="sounds-tab" className="px-4 flex flex-col w-full h-full dark:text-white text-stone-900">
<div className="px-2 pb-2 flex-none"> <div className="pb-2 flex-none">
<ButtonGroup <ButtonGroup
value={soundsFilter} value={soundsFilter}
onChange={(value) => settingsMap.setKey('soundsFilter', value)} onChange={(value) => settingsMap.setKey('soundsFilter', value)}
@ -55,7 +55,7 @@ export function SoundsTab() {
}} }}
></ButtonGroup> ></ButtonGroup>
</div> </div>
<div className="p-2 min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal"> <div className="min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal">
{soundEntries.map(([name, { data, onTrigger }]) => ( {soundEntries.map(([name, { data, onTrigger }]) => (
<span <span
key={name} key={name}

View file

@ -187,6 +187,5 @@ export function duplicateActivePattern() {
} }
export function setActivePattern(key) { export function setActivePattern(key) {
console.log('set', key);
settingsMap.setKey('activePattern', key); settingsMap.setKey('activePattern', key);
} }