Merge branch 'main' into uzu
This commit is contained in:
commit
28cd7c1681
186 changed files with 3188 additions and 2138 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Repl.jsx - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/App.js>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/repl/src/App.js>
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export function Header({ context, embedded = false }) {
|
|||
>
|
||||
{!isEmbedded && <span>update</span>}
|
||||
</button>
|
||||
{!isEmbedded && (
|
||||
{/* !isEmbedded && (
|
||||
<button
|
||||
title="shuffle"
|
||||
className="hover:opacity-50 p-2 flex items-center space-x-1"
|
||||
|
|
@ -101,7 +101,7 @@ export function Header({ context, embedded = false }) {
|
|||
>
|
||||
<span> shuffle</span>
|
||||
</button>
|
||||
)}
|
||||
) */}
|
||||
{!isEmbedded && (
|
||||
<button
|
||||
title="share"
|
||||
|
|
|
|||
|
|
@ -142,21 +142,21 @@ function UserPatterns({ context }) {
|
|||
</div>
|
||||
|
||||
<div className="overflow-auto h-full bg-background p-2 rounded-md">
|
||||
{patternFilter === patternFilterName.user && (
|
||||
<PatternButtons
|
||||
onClick={(id) =>
|
||||
updateCodeWindow(
|
||||
context,
|
||||
{ ...userPatterns[id], collection: userPattern.collection },
|
||||
autoResetPatternOnChange,
|
||||
)
|
||||
}
|
||||
patterns={userPatterns}
|
||||
started={context.started}
|
||||
activePattern={activePattern}
|
||||
viewingPatternID={viewingPatternID}
|
||||
/>
|
||||
)}
|
||||
{/* {patternFilter === patternFilterName.user && ( */}
|
||||
<PatternButtons
|
||||
onClick={(id) =>
|
||||
updateCodeWindow(
|
||||
context,
|
||||
{ ...userPatterns[id], collection: userPattern.collection },
|
||||
autoResetPatternOnChange,
|
||||
)
|
||||
}
|
||||
patterns={userPatterns}
|
||||
started={context.started}
|
||||
activePattern={activePattern}
|
||||
viewingPatternID={viewingPatternID}
|
||||
/>
|
||||
{/* )} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -250,6 +250,11 @@ export function PatternsTab({ context }) {
|
|||
const { patternFilter } = useSettings();
|
||||
|
||||
return (
|
||||
<div className="px-4 w-full text-foreground space-y-2 flex flex-col overflow-hidden max-h-full h-full">
|
||||
<UserPatterns context={context} />
|
||||
</div>
|
||||
);
|
||||
/* return (
|
||||
<div className="px-4 w-full text-foreground space-y-2 flex flex-col overflow-hidden max-h-full h-full">
|
||||
<ButtonGroup
|
||||
value={patternFilter}
|
||||
|
|
@ -263,5 +268,5 @@ export function PatternsTab({ context }) {
|
|||
<PublicPatterns context={context} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
); */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export function Reference() {
|
|||
<ul>
|
||||
{entry.params?.map(({ name, type, description }, i) => (
|
||||
<li key={i}>
|
||||
{name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
|
||||
{name} : {type?.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { ButtonGroup } from './Forms.jsx';
|
|||
import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
||||
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
||||
import { confirmDialog } from '../../util.mjs';
|
||||
import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony } from '@strudel/webaudio';
|
||||
import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony, setMultiChannelOrbits } from '@strudel/webaudio';
|
||||
|
||||
function Checkbox({ label, value, onChange, disabled = false }) {
|
||||
return (
|
||||
|
|
@ -108,6 +108,7 @@ export function SettingsTab({ started }) {
|
|||
audioEngineTarget,
|
||||
togglePanelTrigger,
|
||||
maxPolyphony,
|
||||
multiChannelOrbits,
|
||||
} = useSettings();
|
||||
const shouldAlwaysSync = isUdels();
|
||||
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
||||
|
|
@ -162,6 +163,22 @@ export function SettingsTab({ started }) {
|
|||
value={maxPolyphony ?? ''}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Checkbox
|
||||
label="Multi Channel Orbits"
|
||||
onChange={(cbEvent) => {
|
||||
const val = cbEvent.target.checked;
|
||||
confirmDialog(RELOAD_MSG).then((r) => {
|
||||
if (r == true) {
|
||||
settingsMap.setKey('multiChannelOrbits', val);
|
||||
setMultiChannelOrbits(val);
|
||||
return window.location.reload();
|
||||
}
|
||||
});
|
||||
}}
|
||||
value={multiChannelOrbits}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="Theme">
|
||||
<SelectInput options={themeOptions} value={theme} onChange={(theme) => settingsMap.setKey('theme', theme)} />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -15,17 +15,18 @@ export function WelcomeTab({ context }) {
|
|||
<br />
|
||||
<span className="underline">1. hit play</span> - <span className="underline">2. change something</span> -{' '}
|
||||
<span className="underline">3. hit update</span>
|
||||
<br />
|
||||
If you don't like what you hear, try <span className="underline">shuffle</span>!
|
||||
{/* <br />
|
||||
If you don't like what you hear, try <span className="underline">shuffle</span>! */}
|
||||
</p>
|
||||
<p>
|
||||
To learn more about what this all means, check out the{' '}
|
||||
{/* To learn more about what this all means, check out the{' '} */}
|
||||
To get started, check out the
|
||||
<a href={`${baseNoTrailing}/workshop/getting-started/`} target="_blank">
|
||||
interactive tutorial
|
||||
</a>
|
||||
. Also feel free to join the{' '}
|
||||
<a href="https://discord.com/invite/HGEdXmRkzT" target="_blank">
|
||||
tidalcycles discord channel
|
||||
discord channel
|
||||
</a>{' '}
|
||||
to ask any questions, give feedback or just say hello.
|
||||
</p>
|
||||
|
|
@ -37,11 +38,11 @@ export function WelcomeTab({ context }) {
|
|||
</a>
|
||||
, which is a popular live coding language for music, written in Haskell. Strudel is free/open source software:
|
||||
you can redistribute and/or modify it under the terms of the{' '}
|
||||
<a href="https://github.com/tidalcycles/strudel/blob/main/LICENSE" target="_blank">
|
||||
<a href="https://codeberg.org/uzu/strudel/src/branch/main/LICENSE" target="_blank">
|
||||
GNU Affero General Public License
|
||||
</a>
|
||||
. You can find the source code at{' '}
|
||||
<a href="https://github.com/tidalcycles/strudel" target="_blank">
|
||||
<a href="https://codeberg.org/uzu/strudel" target="_blank">
|
||||
github
|
||||
</a>
|
||||
. You can also find <a href="https://github.com/felixroos/dough-samples/blob/main/README.md">licensing info</a>{' '}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
tunes.mjs - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/tunes.mjs>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/repl/src/tunes.mjs>
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Repl.jsx - <short description TODO>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/App.js>
|
||||
Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/repl/src/App.js>
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ import { setVersionDefaultsFrom } from './util.mjs';
|
|||
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
|
||||
import { clearHydra } from '@strudel/hydra';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { settingsMap, useSettings } from '../settings.mjs';
|
||||
import { parseBoolean, settingsMap, useSettings } from '../settings.mjs';
|
||||
import {
|
||||
setActivePattern,
|
||||
setLatestCode,
|
||||
|
|
@ -36,11 +36,15 @@ import './Repl.css';
|
|||
import { setInterval, clearInterval } from 'worker-timers';
|
||||
import { getMetadata } from '../metadata_parser';
|
||||
|
||||
const { latestCode, maxPolyphony, audioDeviceName } = settingsMap.get();
|
||||
const { latestCode, maxPolyphony, audioDeviceName, multiChannelOrbits } = settingsMap.get();
|
||||
let modulesLoading, presets, drawContext, clearCanvas, audioReady;
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
audioReady = initAudioOnFirstClick({ maxPolyphony, audioDeviceName });
|
||||
audioReady = initAudioOnFirstClick({
|
||||
maxPolyphony,
|
||||
audioDeviceName,
|
||||
multiChannelOrbits: parseBoolean(multiChannelOrbits),
|
||||
});
|
||||
modulesLoading = loadModules();
|
||||
presets = prebake();
|
||||
drawContext = getDrawContext();
|
||||
|
|
@ -55,6 +59,8 @@ async function getModule(name) {
|
|||
return modules.find((m) => m.packageName === name);
|
||||
}
|
||||
|
||||
const initialCode = `// LOADING`;
|
||||
|
||||
export function useReplContext() {
|
||||
const { isSyncEnabled, audioEngineTarget } = useSettings();
|
||||
const shouldUseWebaudio = audioEngineTarget !== audioEngineTargets.osc;
|
||||
|
|
@ -73,7 +79,7 @@ export function useReplContext() {
|
|||
transpiler,
|
||||
autodraw: false,
|
||||
root: containerRef.current,
|
||||
initialCode: '// LOADING',
|
||||
initialCode,
|
||||
pattern: silence,
|
||||
drawTime,
|
||||
drawContext,
|
||||
|
|
@ -129,9 +135,10 @@ export function useReplContext() {
|
|||
code = latestCode;
|
||||
msg = `Your last session has been loaded!`;
|
||||
} else {
|
||||
const { code: randomTune, name } = await getRandomTune();
|
||||
code = randomTune;
|
||||
msg = `A random code snippet named "${name}" has been loaded!`;
|
||||
/* const { code: randomTune, name } = await getRandomTune();
|
||||
code = randomTune; */
|
||||
code = '$: s("[bd <hh oh>]*2").bank("tr909").dec(.4)';
|
||||
msg = `Default code has been loaded`;
|
||||
}
|
||||
editor.setCode(code);
|
||||
setDocumentTitle(code);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import './Repl.css';
|
|||
import { createClient } from '@supabase/supabase-js';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import { $featuredPatterns, loadDBPatterns } from '@src/user_pattern_utils.mjs';
|
||||
import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs';
|
||||
|
||||
// Create a single supabase client for interacting with your database
|
||||
export const supabase = createClient(
|
||||
|
|
@ -16,9 +16,9 @@ export const supabase = createClient(
|
|||
);
|
||||
|
||||
let dbLoaded;
|
||||
if (typeof window !== 'undefined') {
|
||||
/* if (typeof window !== 'undefined') {
|
||||
dbLoaded = loadDBPatterns();
|
||||
}
|
||||
} */
|
||||
|
||||
export async function initCode() {
|
||||
// load code from url hash (either short hash from database or decode long hash)
|
||||
|
|
@ -110,38 +110,56 @@ export function confirmDialog(msg) {
|
|||
}
|
||||
|
||||
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');
|
||||
//RIP due to SPAM
|
||||
// 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);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
export async function shareCode() {
|
||||
try {
|
||||
const shareUrl = window.location.href;
|
||||
if (isTauri()) {
|
||||
await writeText(shareUrl);
|
||||
} else {
|
||||
console.log('error', error);
|
||||
const message = `Error: ${error.message}`;
|
||||
// alert(message);
|
||||
logger(message);
|
||||
await navigator.clipboard.writeText(shareUrl);
|
||||
}
|
||||
});
|
||||
const message = `Link copied to clipboard!`;
|
||||
alert(message);
|
||||
logger(message, 'highlight');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export const isIframe = () => window.location !== window.parent.location;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue