migrate repl + move imports out of minirepl

This commit is contained in:
Felix Roos 2022-05-17 22:27:22 +02:00
parent 0bcc01ed0e
commit ed35f967b0
11 changed files with 114 additions and 183 deletions

View File

@ -1 +1,3 @@
import './midi.mjs';
export * from './midi.mjs';

File diff suppressed because one or more lines are too long

View File

@ -1,14 +1,15 @@
import React$1, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { CodeMirror as CodeMirror$1 } from 'react-codemirror6';
import { EditorView, Decoration } from '@codemirror/view';
import { StateEffect, StateField } from '@codemirror/state';
import { javascript } from '@codemirror/lang-javascript';
import { HighlightStyle, tags } from '@codemirror/highlight';
import { useInView } from 'react-hook-inview';
import { evaluate, evalScope } from '@strudel.cycles/eval';
import { evaluate } from '@strudel.cycles/eval';
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
import { Tone } from '@strudel.cycles/tone';
import { TimeSpan, State } from '@strudel.cycles/core';
import { WebMidi, enableWebMidi } from '@strudel.cycles/midi';
/*
Credits for color palette:
@ -173,7 +174,7 @@ const highlightField = StateField.define({
provide: (f) => EditorView.decorations.from(f)
});
function CodeMirror({ value, onChange, onViewChanged, onCursor, options, editorDidMount }) {
return /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, /* @__PURE__ */ React$1.createElement(CodeMirror$1, {
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(CodeMirror$1, {
onViewChange: onViewChanged,
style: {
display: "flex",
@ -189,82 +190,6 @@ function CodeMirror({ value, onChange, onViewChanged, onCursor, options, editorD
]
}));
}
let parenMark;
const markParens = (editor, data) => {
const v = editor.getDoc().getValue();
const marked = getCurrentParenArea(v, data);
parenMark?.clear();
parenMark = editor.getDoc().markText(...marked, { css: "background-color: #00007720" });
};
function offsetToPosition(offset, code) {
const lines = code.split("\n");
let line = 0;
let ch = 0;
for (let i = 0; i < offset; i++) {
if (ch === lines[line].length) {
line++;
ch = 0;
} else {
ch++;
}
}
return { line, ch };
}
function positionToOffset(position, code) {
const lines = code.split("\n");
if (position.line > lines.length) {
return 0;
}
let offset = 0;
for (let i = 0; i < position.line; i++) {
offset += lines[i].length + 1;
}
offset += position.ch;
return offset;
}
function getCurrentParenArea(code, caretPosition) {
const caret = positionToOffset(caretPosition, code);
let open, i, begin, end;
i = caret;
open = 0;
while (i > 0) {
if (code[i - 1] === "(") {
open--;
} else if (code[i - 1] === ")") {
open++;
}
if (open === -1) {
break;
}
i--;
}
begin = i;
i = caret;
open = 0;
while (i < code.length) {
if (code[i] === "(") {
open--;
} else if (code[i] === ")") {
open++;
}
if (open === 1) {
break;
}
i++;
}
end = i;
return [begin, end].map((o) => offsetToPosition(o, code));
}
var CodeMirror6 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
setHighlights: setHighlights,
'default': CodeMirror,
markParens: markParens,
offsetToPosition: offsetToPosition,
positionToOffset: positionToOffset,
getCurrentParenArea: getCurrentParenArea
}, Symbol.toStringTag, { value: 'Module' }));
/*
useCycle.mjs - <short description TODO>
@ -607,14 +532,7 @@ function Icon({ type }) {
}[type]);
}
evalScope(Tone, import('@strudel.cycles/core'), import('@strudel.cycles/tone'), import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), import('@strudel.cycles/midi'), import('@strudel.cycles/xen'), import('@strudel.cycles/webaudio'));
const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
oscillator: { type: "triangle" },
envelope: {
release: 0.01
}
});
function MiniRepl({ tune }) {
function MiniRepl({ tune, defaultSynth }) {
const { code, setCode, pattern, activateCode, error, cycle, dirty, togglePlay } = useRepl({
tune,
defaultSynth,
@ -632,32 +550,71 @@ function MiniRepl({ tune }) {
return isVisible || wasVisible.current;
}, [isVisible]);
useHighlighting({ view, pattern, active: cycle.started });
return /* @__PURE__ */ React$1.createElement("div", {
return /* @__PURE__ */ React.createElement("div", {
className: styles.container,
ref
}, /* @__PURE__ */ React$1.createElement("div", {
}, /* @__PURE__ */ React.createElement("div", {
className: styles.header
}, /* @__PURE__ */ React$1.createElement("div", {
}, /* @__PURE__ */ React.createElement("div", {
className: styles.buttons
}, /* @__PURE__ */ React$1.createElement("button", {
}, /* @__PURE__ */ React.createElement("button", {
className: cx(styles.button, cycle.started ? "sc-animate-pulse" : ""),
onClick: () => togglePlay()
}, /* @__PURE__ */ React$1.createElement(Icon, {
}, /* @__PURE__ */ React.createElement(Icon, {
type: cycle.started ? "pause" : "play"
})), /* @__PURE__ */ React$1.createElement("button", {
})), /* @__PURE__ */ React.createElement("button", {
className: cx(dirty ? styles.button : styles.buttonDisabled),
onClick: () => activateCode()
}, /* @__PURE__ */ React$1.createElement(Icon, {
}, /* @__PURE__ */ React.createElement(Icon, {
type: "refresh"
}))), error && /* @__PURE__ */ React$1.createElement("div", {
}))), error && /* @__PURE__ */ React.createElement("div", {
className: styles.error
}, error.message)), /* @__PURE__ */ React$1.createElement("div", {
}, error.message)), /* @__PURE__ */ React.createElement("div", {
className: styles.body
}, show && /* @__PURE__ */ React$1.createElement(CodeMirror, {
}, show && /* @__PURE__ */ React.createElement(CodeMirror, {
value: code,
onChange: setCode,
onViewChanged: setView
})));
}
export { CodeMirror6 as CodeMirror, MiniRepl };
/*
useWebMidi.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useWebMidi.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/>.
*/
function useWebMidi(props) {
const { ready, connected, disconnected } = props;
const [loading, setLoading] = useState(true);
const [outputs, setOutputs] = useState(WebMidi?.outputs || []);
useEffect(() => {
enableWebMidi()
.then(() => {
// Reacting when a new device becomes available
WebMidi.addListener('connected', (e) => {
setOutputs([...WebMidi.outputs]);
connected?.(WebMidi, e);
});
// Reacting when a device becomes unavailable
WebMidi.addListener('disconnected', (e) => {
setOutputs([...WebMidi.outputs]);
disconnected?.(WebMidi, e);
});
ready?.(WebMidi);
setLoading(false);
})
.catch((err) => {
if (err) {
console.error(err);
//throw new Error("Web Midi could not be enabled...");
console.warn('Web Midi could not be enabled..');
return;
}
});
}, [ready, connected, disconnected, outputs]);
const outputByName = (name) => WebMidi.getOutputByName(name);
return { loading, outputs, outputByName };
}
export { CodeMirror, MiniRepl, cx, useCycle, useHighlighting, usePostMessage, useRepl, useWebMidi };

View File

@ -1,6 +1,5 @@
import React, { useState, useMemo, useRef } from 'react';
import { useInView } from 'react-hook-inview';
import useRepl from '../hooks/useRepl.mjs';
import cx from '../cx';
import useHighlighting from '../hooks/useHighlighting.mjs';
@ -9,27 +8,7 @@ import 'tailwindcss/tailwind.css';
import styles from './MiniRepl.module.css';
import { Icon } from './Icon';
import { Tone } from '@strudel.cycles/tone';
import { evalScope } from '@strudel.cycles/eval';
evalScope(
Tone,
import('@strudel.cycles/core'),
import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
);
const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
oscillator: { type: 'triangle' },
envelope: {
release: 0.01,
},
});
export function MiniRepl({ tune }) {
export function MiniRepl({ tune, defaultSynth }) {
const { code, setCode, pattern, activateCode, error, cycle, dirty, togglePlay } = useRepl({
tune,
defaultSynth,

View File

@ -1,4 +1,10 @@
// import 'tailwindcss/tailwind.css';
export * as CodeMirror from './components/CodeMirror6';
export { default as CodeMirror } from './components/CodeMirror6';
export * from './components/MiniRepl';
export { default as useCycle } from './hooks/useCycle';
export { default as useHighlighting } from './hooks/useHighlighting';
export { default as usePostMessage } from './hooks/usePostMessage';
export { default as useRepl } from './hooks/useRepl';
export { default as cx } from './cx';
export { useWebMidi } from './hooks/useWebMidi';

View File

@ -47,7 +47,7 @@ Pattern.prototype.draw = function (callback, cycleSpan, lookaheadCycles = 1) {
return this;
};
export const cleanup = () => {
export const cleanupDraw = () => {
const ctx = getDrawContext();
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
if (window.strudelAnimation) {

View File

@ -47,7 +47,7 @@ export const backgroundImage = function (src, animateOptions = {}) {
);
};
export const cleanup = () => {
export const cleanupUi = () => {
const container = document.getElementById('code');
if (container) {
container.style = '';

View File

@ -4,59 +4,24 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
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/>.
*/
import CodeMirror6, { setHighlights } from '@strudel.cycles/react/src/components/CodeMirror6';
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
import cx from '@strudel.cycles/react/src/cx';
import logo from './logo.svg';
// import playStatic from './static.mjs';
import { getDefaultSynth } from '@strudel.cycles/tone';
import * as tunes from './tunes.mjs';
import useRepl from '@strudel.cycles/react/src/hooks/useRepl.mjs';
import { useWebMidi } from '@strudel.cycles/react/src/hooks/useWebMidi.mjs';
import useHighlighting from '@strudel.cycles/react/src/hooks/useHighlighting';
import './App.css';
// eval stuff start
import { evaluate, extend } from '@strudel.cycles/eval';
import * as strudel from '@strudel.cycles/core';
import gist from '@strudel.cycles/core/gist.js';
import { mini } from '@strudel.cycles/mini/mini.mjs';
import { Tone } from '@strudel.cycles/tone';
import * as toneHelpers from '@strudel.cycles/tone/tone.mjs';
import * as voicingHelpers from '@strudel.cycles/tonal/voicings.mjs';
import * as uiHelpers from '@strudel.cycles/tone/ui.mjs';
import * as drawHelpers from '@strudel.cycles/tone/draw.mjs';
import euclid from '@strudel.cycles/core/euclid.mjs';
import '@strudel.cycles/tone/tone.mjs';
import '@strudel.cycles/midi/midi.mjs';
import '@strudel.cycles/tonal/voicings.mjs';
import '@strudel.cycles/tonal/tonal.mjs';
import '@strudel.cycles/xen/xen.mjs';
import '@strudel.cycles/xen/tune.mjs';
import '@strudel.cycles/core/euclid.mjs';
import '@strudel.cycles/core/speak.mjs';
import '@strudel.cycles/tone/pianoroll.mjs';
import '@strudel.cycles/tone/draw.mjs';
import '@strudel.cycles/osc/osc.mjs';
import '@strudel.cycles/webaudio/webaudio.mjs';
import '@strudel.cycles/serial/serial.mjs';
import controls from '@strudel.cycles/core/controls.mjs';
// TODO: refactor to evalScope
extend(
import { evalScope, evaluate } from '@strudel.cycles/eval';
import { CodeMirror, cx, useHighlighting, useRepl, useWebMidi } from '@strudel.cycles/react';
import { getDefaultSynth, cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
import './App.css';
import logo from './logo.svg';
import * as tunes from './tunes.mjs';
evalScope(
Tone,
strudel,
strudel.Pattern.prototype.bootstrap(),
controls,
toneHelpers,
voicingHelpers,
drawHelpers,
uiHelpers,
{
gist,
euclid,
mini,
Tone,
},
import('@strudel.cycles/core'),
import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
);
const initialUrl = window.location.href;
@ -195,8 +160,8 @@ function App() {
const _code = getRandomTune();
console.log('tune', _code); // uncomment this to debug when random code fails
setCode(_code);
drawHelpers.cleanup();
uiHelpers.cleanup();
cleanupDraw();
cleanupUi();
const parsed = await evaluate(_code);
setPattern(parsed.pattern);
setActiveCode(_code);
@ -235,7 +200,7 @@ function App() {
<section className="grow flex flex-col text-gray-100">
<div className="grow relative flex overflow-auto" id="code">
{/* onCursor={markParens} */}
<CodeMirror6 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">
{!cycle.started ? `press ctrl+enter to play\n` : dirty ? `ctrl+enter to update\n` : 'no changes\n'}
</span>

25
tutorial/MiniRepl.jsx Normal file
View File

@ -0,0 +1,25 @@
import { Tone } from '@strudel.cycles/tone';
import { evalScope } from '@strudel.cycles/eval';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
export const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
oscillator: { type: 'triangle' },
envelope: {
release: 0.01,
},
});
evalScope(
Tone,
import('@strudel.cycles/core'),
import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
);
export function MiniRepl({ tune }) {
return <_MiniRepl tune={tune} defaultSynth={defaultSynth} />;
}

View File

@ -10,7 +10,6 @@ import Tutorial from './tutorial.mdx';
import './style.css';
import '@strudel.cycles/react/dist/style.css';
ReactDOM.render(
<React.StrictMode>
<div className="min-h-screen">
@ -32,5 +31,5 @@ ReactDOM.render(
</main>
</div>
</React.StrictMode>,
document.getElementById('root')
document.getElementById('root'),
);

View File

@ -1,4 +1,4 @@
import { MiniRepl } from '@strudel.cycles/react';
import { MiniRepl } from './MiniRepl';
# What is Strudel?