commit
688a3d29fd
15 changed files with 406 additions and 291 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -35,3 +35,4 @@ tutorial.rendered.mdx
|
||||||
doc.json
|
doc.json
|
||||||
talk/public/EmuSP12
|
talk/public/EmuSP12
|
||||||
talk/public/samples
|
talk/public/samples
|
||||||
|
server/samples/old
|
||||||
|
|
@ -1046,6 +1046,22 @@ export class Pattern {
|
||||||
onTrigger(onTrigger) {
|
onTrigger(onTrigger) {
|
||||||
return this._withHap((hap) => hap.setContext({ ...hap.context, onTrigger }));
|
return this._withHap((hap) => hap.setContext({ ...hap.context, onTrigger }));
|
||||||
}
|
}
|
||||||
|
log(func = id) {
|
||||||
|
return this._withHap((hap) =>
|
||||||
|
hap.setContext({
|
||||||
|
...hap.context,
|
||||||
|
onTrigger: (...args) => {
|
||||||
|
if (hap.context.onTrigger) {
|
||||||
|
hap.context.onTrigger(...args);
|
||||||
|
}
|
||||||
|
console.log(func(...args));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
logValues(func = id) {
|
||||||
|
return this.log((_, hap) => func(hap.value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - adopt value.mjs fully..
|
// TODO - adopt value.mjs fully..
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,19 @@ export const fromMidi = (n) => {
|
||||||
return Math.pow(2, (n - 69) / 12) * 440;
|
return Math.pow(2, (n - 69) / 12) * 440;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getFreq = (noteOrMidi) => {
|
||||||
|
if (typeof noteOrMidi === 'number') {
|
||||||
|
return fromMidi(noteOrMidi);
|
||||||
|
}
|
||||||
|
return fromMidi(toMidi(noteOrMidi));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const midi2note = (n) => {
|
||||||
|
const oct = Math.floor(n / 12) - 1;
|
||||||
|
const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12];
|
||||||
|
return pc + oct;
|
||||||
|
};
|
||||||
|
|
||||||
// modulo that works with negative numbers e.g. mod(-1, 3) = 2
|
// modulo that works with negative numbers e.g. mod(-1, 3) = 2
|
||||||
// const mod = (n: number, m: number): number => (n < 0 ? mod(n + m, m) : n % m);
|
// const mod = (n: number, m: number): number => (n < 0 ? mod(n + m, m) : n % m);
|
||||||
export const mod = (n, m) => ((n % m) + m) % m;
|
export const mod = (n, m) => ((n % m) + m) % m;
|
||||||
|
|
|
||||||
4
packages/react/dist/index.cjs.js
vendored
4
packages/react/dist/index.cjs.js
vendored
File diff suppressed because one or more lines are too long
63
packages/react/dist/index.es.js
vendored
63
packages/react/dist/index.es.js
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo, useRef, useLayoutEffect } from 'react';
|
||||||
import { CodeMirror as CodeMirror$1 } from 'react-codemirror6';
|
import { CodeMirror as CodeMirror$1 } from 'react-codemirror6';
|
||||||
import { EditorView, Decoration } from '@codemirror/view';
|
import { EditorView, Decoration } from '@codemirror/view';
|
||||||
import { StateEffect, StateField } from '@codemirror/state';
|
import { StateEffect, StateField } from '@codemirror/state';
|
||||||
|
|
@ -46,7 +46,13 @@ const materialPalenightTheme = EditorView.theme(
|
||||||
lineHeight: '22px',
|
lineHeight: '22px',
|
||||||
},
|
},
|
||||||
'.cm-line': {
|
'.cm-line': {
|
||||||
background: '#2C323699',
|
// background: '#2C323699',
|
||||||
|
background: 'transparent',
|
||||||
|
},
|
||||||
|
'.cm-line > *': {
|
||||||
|
// background: '#2C323699',
|
||||||
|
background: '#00000090',
|
||||||
|
// background: 'transparent',
|
||||||
},
|
},
|
||||||
// done
|
// done
|
||||||
'&.cm-focused .cm-cursor': {
|
'&.cm-focused .cm-cursor': {
|
||||||
|
|
@ -71,7 +77,8 @@ const materialPalenightTheme = EditorView.theme(
|
||||||
backgroundColor: '#6199ff2f',
|
backgroundColor: '#6199ff2f',
|
||||||
},
|
},
|
||||||
|
|
||||||
'.cm-activeLine': { backgroundColor: highlightBackground },
|
// commented out because it looks bad in mini repl one liners
|
||||||
|
//'.cm-activeLine': { backgroundColor: cursor + '50' },
|
||||||
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
||||||
|
|
||||||
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
||||||
|
|
@ -193,7 +200,7 @@ const highlightField = StateField.define({
|
||||||
if (from > l || to > l) {
|
if (from > l || to > l) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const mark = Decoration.mark({ attributes: { style: `outline: 1px solid ${color}` } });
|
const mark = Decoration.mark({ attributes: { style: `outline: 1.5px solid ${color};` } });
|
||||||
return mark.range(from, to);
|
return mark.range(from, to);
|
||||||
})).filter(Boolean), true);
|
})).filter(Boolean), true);
|
||||||
}
|
}
|
||||||
|
|
@ -351,6 +358,8 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
||||||
}
|
}
|
||||||
}, [activeCode, onDrawProp]);
|
}, [activeCode, onDrawProp]);
|
||||||
|
|
||||||
|
const hideHeader = useMemo(() => activeCode && activeCode.includes('strudel hide-header'), [activeCode]);
|
||||||
|
const hideConsole = useMemo(() => activeCode && activeCode.includes('strudel hide-console'), [activeCode]);
|
||||||
// cycle hook to control scheduling
|
// cycle hook to control scheduling
|
||||||
const cycle = useCycle({
|
const cycle = useCycle({
|
||||||
onDraw,
|
onDraw,
|
||||||
|
|
@ -449,6 +458,8 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
hideHeader,
|
||||||
|
hideConsole,
|
||||||
pending,
|
pending,
|
||||||
code,
|
code,
|
||||||
setCode,
|
setCode,
|
||||||
|
|
@ -521,13 +532,13 @@ var tailwind = '';
|
||||||
|
|
||||||
var style = '';
|
var style = '';
|
||||||
|
|
||||||
const container = "_container_10e1g_1";
|
const container = "_container_xpa19_1";
|
||||||
const header = "_header_10e1g_5";
|
const header = "_header_xpa19_5";
|
||||||
const buttons = "_buttons_10e1g_9";
|
const buttons = "_buttons_xpa19_9";
|
||||||
const button = "_button_10e1g_9";
|
const button = "_button_xpa19_9";
|
||||||
const buttonDisabled = "_buttonDisabled_10e1g_17";
|
const buttonDisabled = "_buttonDisabled_xpa19_17";
|
||||||
const error = "_error_10e1g_21";
|
const error = "_error_xpa19_21";
|
||||||
const body = "_body_10e1g_25";
|
const body = "_body_xpa19_25";
|
||||||
var styles = {
|
var styles = {
|
||||||
container: container,
|
container: container,
|
||||||
header: header,
|
header: header,
|
||||||
|
|
@ -563,12 +574,16 @@ function Icon({ type }) {
|
||||||
}[type]);
|
}[type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function MiniRepl({ tune, defaultSynth, hideOutsideView = false }) {
|
function MiniRepl({ tune, defaultSynth, hideOutsideView = false, theme, init, onEvent, enableKeyboard }) {
|
||||||
const { code, setCode, pattern, activateCode, error, cycle, dirty, togglePlay } = useRepl({
|
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } = useRepl({
|
||||||
tune,
|
tune,
|
||||||
defaultSynth,
|
defaultSynth,
|
||||||
autolink: false
|
autolink: false,
|
||||||
|
onEvent
|
||||||
});
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
init && evaluateOnly();
|
||||||
|
}, [tune, init]);
|
||||||
const [view, setView] = useState();
|
const [view, setView] = useState();
|
||||||
const [ref, isVisible] = useInView({
|
const [ref, isVisible] = useInView({
|
||||||
threshold: 0.01
|
threshold: 0.01
|
||||||
|
|
@ -580,7 +595,25 @@ function MiniRepl({ tune, defaultSynth, hideOutsideView = false }) {
|
||||||
}
|
}
|
||||||
return isVisible || wasVisible.current;
|
return isVisible || wasVisible.current;
|
||||||
}, [isVisible, hideOutsideView]);
|
}, [isVisible, hideOutsideView]);
|
||||||
useHighlighting({ view, pattern, active: cycle.started });
|
useHighlighting({ view, pattern, active: cycle.started && !activeCode?.includes("strudel disable-highlighting") });
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (enableKeyboard) {
|
||||||
|
const handleKeyPress = async (e) => {
|
||||||
|
if (e.ctrlKey || e.altKey) {
|
||||||
|
if (e.code === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
flash(view);
|
||||||
|
await activateCode();
|
||||||
|
} else if (e.code === "Period") {
|
||||||
|
cycle.stop();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("keydown", handleKeyPress, true);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyPress, true);
|
||||||
|
}
|
||||||
|
}, [enableKeyboard, pattern, code, activateCode, cycle, view]);
|
||||||
return /* @__PURE__ */ React.createElement("div", {
|
return /* @__PURE__ */ React.createElement("div", {
|
||||||
className: styles.container,
|
className: styles.container,
|
||||||
ref
|
ref
|
||||||
|
|
|
||||||
2
packages/react/dist/style.css
vendored
2
packages/react/dist/style.css
vendored
|
|
@ -1 +1 @@
|
||||||
*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cm-editor{background-color:transparent!important}._container_10e1g_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(68 76 87 / var(--tw-bg-opacity))}._header_10e1g_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_10e1g_9{display:flex}._button_10e1g_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_10e1g_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_10e1g_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_10e1g_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_10e1g_25{position:relative;overflow:auto}
|
*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cm-editor{background-color:transparent!important}._container_xpa19_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(17 17 17 / var(--tw-bg-opacity))}._header_xpa19_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_xpa19_9{display:flex}._button_xpa19_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_xpa19_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_xpa19_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_xpa19_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_xpa19_25{position:relative;overflow:auto}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ const highlightField = StateField.define({
|
||||||
if (from > l || to > l) {
|
if (from > l || to > l) {
|
||||||
return; // dont mark outside of range, as it will throw an error
|
return; // dont mark outside of range, as it will throw an error
|
||||||
}
|
}
|
||||||
const mark = Decoration.mark({ attributes: { style: `outline: 1px solid ${color}` } });
|
// const mark = Decoration.mark({ attributes: { style: `outline: 1px solid ${color}` } });
|
||||||
|
const mark = Decoration.mark({ attributes: { style: `outline: 1.5px solid ${color};` } });
|
||||||
return mark.range(from, to);
|
return mark.range(from, to);
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
import React, { useState, useMemo, useRef } from 'react';
|
import React, { useState, useMemo, useRef, useEffect, useLayoutEffect } from 'react';
|
||||||
import { useInView } from 'react-hook-inview';
|
import { useInView } from 'react-hook-inview';
|
||||||
import useRepl from '../hooks/useRepl.mjs';
|
import useRepl from '../hooks/useRepl.mjs';
|
||||||
import cx from '../cx';
|
import cx from '../cx';
|
||||||
import useHighlighting from '../hooks/useHighlighting.mjs';
|
import useHighlighting from '../hooks/useHighlighting.mjs';
|
||||||
import CodeMirror6 from './CodeMirror6';
|
import CodeMirror6, { flash } from './CodeMirror6';
|
||||||
import 'tailwindcss/tailwind.css';
|
import 'tailwindcss/tailwind.css';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
import styles from './MiniRepl.module.css';
|
import styles from './MiniRepl.module.css';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
|
|
||||||
export function MiniRepl({ tune, defaultSynth, hideOutsideView = false }) {
|
export function MiniRepl({ tune, defaultSynth, hideOutsideView = false, theme, init, onEvent, enableKeyboard }) {
|
||||||
const { code, setCode, pattern, activateCode, error, cycle, dirty, togglePlay } = useRepl({
|
const { code, setCode, pattern, activeCode, activateCode, evaluateOnly, error, cycle, dirty, togglePlay, stop } =
|
||||||
|
useRepl({
|
||||||
tune,
|
tune,
|
||||||
defaultSynth,
|
defaultSynth,
|
||||||
autolink: false,
|
autolink: false,
|
||||||
|
onEvent,
|
||||||
});
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
init && evaluateOnly();
|
||||||
|
}, [tune, init]);
|
||||||
const [view, setView] = useState();
|
const [view, setView] = useState();
|
||||||
const [ref, isVisible] = useInView({
|
const [ref, isVisible] = useInView({
|
||||||
threshold: 0.01,
|
threshold: 0.01,
|
||||||
|
|
@ -26,7 +31,28 @@ export function MiniRepl({ tune, defaultSynth, hideOutsideView = false }) {
|
||||||
}
|
}
|
||||||
return isVisible || wasVisible.current;
|
return isVisible || wasVisible.current;
|
||||||
}, [isVisible, hideOutsideView]);
|
}, [isVisible, hideOutsideView]);
|
||||||
useHighlighting({ view, pattern, active: cycle.started });
|
useHighlighting({ view, pattern, active: cycle.started && !activeCode?.includes('strudel disable-highlighting') });
|
||||||
|
|
||||||
|
// set active pattern on ctrl+enter
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (enableKeyboard) {
|
||||||
|
const handleKeyPress = async (e) => {
|
||||||
|
if (e.ctrlKey || e.altKey) {
|
||||||
|
if (e.code === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
flash(view);
|
||||||
|
await activateCode();
|
||||||
|
} else if (e.code === 'Period') {
|
||||||
|
cycle.stop();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('keydown', handleKeyPress, true);
|
||||||
|
return () => window.removeEventListener('keydown', handleKeyPress, true);
|
||||||
|
}
|
||||||
|
}, [enableKeyboard, pattern, code, activateCode, cycle, view]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} ref={ref}>
|
<div className={styles.container} ref={ref}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
|
|
@ -40,7 +66,7 @@ export function MiniRepl({ tune, defaultSynth, hideOutsideView = false }) {
|
||||||
</div>
|
</div>
|
||||||
{error && <div className={styles.error}>{error.message}</div>}
|
{error && <div className={styles.error}>{error.message}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.body} >
|
<div className={styles.body}>
|
||||||
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
.container {
|
.container {
|
||||||
@apply sc-rounded-md sc-overflow-hidden sc-bg-[#444C57];
|
@apply sc-rounded-md sc-overflow-hidden sc-bg-[#111111];
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
||||||
}
|
}
|
||||||
}, [activeCode, onDrawProp]);
|
}, [activeCode, onDrawProp]);
|
||||||
|
|
||||||
|
const hideHeader = useMemo(() => activeCode && activeCode.includes('strudel hide-header'), [activeCode]);
|
||||||
|
const hideConsole = useMemo(() => activeCode && activeCode.includes('strudel hide-console'), [activeCode]);
|
||||||
// cycle hook to control scheduling
|
// cycle hook to control scheduling
|
||||||
const cycle = useCycle({
|
const cycle = useCycle({
|
||||||
onDraw,
|
onDraw,
|
||||||
|
|
@ -136,6 +138,8 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw: onDrawP
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
hideHeader,
|
||||||
|
hideConsole,
|
||||||
pending,
|
pending,
|
||||||
code,
|
code,
|
||||||
setCode,
|
setCode,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,13 @@ export const materialPalenightTheme = EditorView.theme(
|
||||||
lineHeight: '22px',
|
lineHeight: '22px',
|
||||||
},
|
},
|
||||||
'.cm-line': {
|
'.cm-line': {
|
||||||
background: '#2C323699',
|
// background: '#2C323699',
|
||||||
|
background: 'transparent',
|
||||||
|
},
|
||||||
|
'.cm-line > *': {
|
||||||
|
// background: '#2C323699',
|
||||||
|
background: '#00000090',
|
||||||
|
// background: 'transparent',
|
||||||
},
|
},
|
||||||
// done
|
// done
|
||||||
'&.cm-focused .cm-cursor': {
|
'&.cm-focused .cm-cursor': {
|
||||||
|
|
@ -61,7 +67,8 @@ export const materialPalenightTheme = EditorView.theme(
|
||||||
backgroundColor: '#6199ff2f',
|
backgroundColor: '#6199ff2f',
|
||||||
},
|
},
|
||||||
|
|
||||||
'.cm-activeLine': { backgroundColor: highlightBackground },
|
// commented out because it looks bad in mini repl one liners
|
||||||
|
//'.cm-activeLine': { backgroundColor: cursor + '50' },
|
||||||
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
|
||||||
|
|
||||||
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ const splitSN = (s, n) => {
|
||||||
|
|
||||||
Pattern.prototype.out = function () {
|
Pattern.prototype.out = function () {
|
||||||
return this.onTrigger(async (t, hap, ct) => {
|
return this.onTrigger(async (t, hap, ct) => {
|
||||||
|
try {
|
||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
// calculate correct time (tone.js workaround)
|
// calculate correct time (tone.js workaround)
|
||||||
t = ac.currentTime + t - ct;
|
t = ac.currentTime + t - ct;
|
||||||
|
|
@ -267,5 +268,8 @@ Pattern.prototype.out = function () {
|
||||||
chain.push(ac.destination);
|
chain.push(ac.destination);
|
||||||
// connect chain elements together
|
// connect chain elements together
|
||||||
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('.out error:', e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
1
repl/.gitignore
vendored
1
repl/.gitignore
vendored
|
|
@ -24,3 +24,4 @@ dist-ssr
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
oldtunes.mjs
|
oldtunes.mjs
|
||||||
|
public/samples/EMU World/
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #2a3236;
|
background-color: #111;
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-codemirror2,
|
.react-codemirror2,
|
||||||
|
|
@ -15,12 +15,12 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-line > span {
|
.CodeMirror-line > span {
|
||||||
background-color: #2a323699;
|
background-color: #11111190;
|
||||||
}
|
}
|
||||||
|
|
||||||
.darken::before {
|
.darken::before {
|
||||||
content: ' ';
|
content: ' ';
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,8 @@ function App() {
|
||||||
pattern,
|
pattern,
|
||||||
pushLog,
|
pushLog,
|
||||||
pending,
|
pending,
|
||||||
|
hideHeader,
|
||||||
|
hideConsole,
|
||||||
} = useRepl({
|
} = useRepl({
|
||||||
tune: '// LOADING...',
|
tune: '// LOADING...',
|
||||||
defaultSynth,
|
defaultSynth,
|
||||||
|
|
@ -167,6 +169,7 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
|
{!hideHeader && (
|
||||||
<header
|
<header
|
||||||
id="header"
|
id="header"
|
||||||
className={cx(
|
className={cx(
|
||||||
|
|
@ -289,20 +292,26 @@ function App() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
)}
|
||||||
<section className="grow flex flex-col text-gray-100">
|
<section className="grow flex flex-col text-gray-100">
|
||||||
<div className="grow relative flex overflow-auto" id="code">
|
<div className="grow relative flex overflow-auto pb-8 cursor-text" id="code">
|
||||||
{/* onCursor={markParens} */}
|
{/* onCursor={markParens} */}
|
||||||
<CodeMirror value={code} onChange={setCode} onViewChanged={setView} />
|
<CodeMirror value={code} onChange={setCode} onViewChanged={setView} />
|
||||||
<span className="z-[20] py-1 px-2 absolute top-0 right-0 text-xs whitespace-pre text-right pointer-events-none">
|
<span className="z-[20] bg-black rounded-t-md py-1 px-2 fixed bottom-0 right-1 text-xs whitespace-pre text-right pointer-events-none">
|
||||||
{!cycle.started ? `press ctrl+enter to play\n` : dirty ? `ctrl+enter to update\n` : 'no changes\n'}
|
{!cycle.started ? `press ctrl+enter to play\n` : dirty ? `ctrl+enter to update\n` : 'no changes\n'}
|
||||||
</span>
|
</span>
|
||||||
{error && (
|
{error && (
|
||||||
<div className={cx('absolute right-2 bottom-2 px-2', 'text-red-500')}>
|
<div
|
||||||
|
className={cx(
|
||||||
|
'rounded-md fixed pointer-events-none left-2 bottom-1 text-xs bg-black px-2 z-[20]',
|
||||||
|
'text-red-500',
|
||||||
|
)}
|
||||||
|
>
|
||||||
{error?.message || 'unknown error'}
|
{error?.message || 'unknown error'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isEmbedded && (
|
{!isEmbedded && !hideConsole && (
|
||||||
<textarea
|
<textarea
|
||||||
className="z-[10] h-16 border-0 text-xs bg-[transparent] border-t border-slate-600 resize-none"
|
className="z-[10] h-16 border-0 text-xs bg-[transparent] border-t border-slate-600 resize-none"
|
||||||
value={log}
|
value={log}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue