diff --git a/dependencies.svg b/dependencies.svg deleted file mode 100644 index 2d2fc61d..00000000 --- a/dependencies.svg +++ /dev/null @@ -1,1806 +0,0 @@ - - - - - - -dependency-cruiser output - - -cluster_packages - -packages - - -cluster_packages/core - -core - - -cluster_packages/core/test - -test - - -cluster_packages/embed - -embed - - -cluster_packages/eval - -eval - - -cluster_packages/eval/shift-traverser - -shift-traverser - - -cluster_packages/eval/test - -test - - -cluster_packages/midi - -midi - - -cluster_packages/mini - -mini - - -cluster_packages/mini/test - -test - - -cluster_packages/osc - -osc - - -cluster_packages/react - -react - - -cluster_packages/react/dist - -dist - - -cluster_packages/react/src - -src - - -cluster_packages/react/src/components - -components - - -cluster_packages/react/src/hooks - -hooks - - -cluster_packages/react/src/themes - -themes - - -cluster_packages/serial - -serial - - -cluster_packages/soundfonts - -soundfonts - - -cluster_packages/tonal - -tonal - - -cluster_packages/tonal/test - -test - - -cluster_packages/tone - -tone - - -cluster_packages/tone/test - -test - - -cluster_packages/webaudio - -webaudio - - -cluster_packages/webdirt - -webdirt - - -cluster_packages/xen - -xen - - -cluster_packages/xen/test - -test - - - -packages/core/controls.mjs - - -controls.mjs - - - - - -packages/core/pattern.mjs - - -pattern.mjs - - - - - -packages/core/controls.mjs->packages/core/pattern.mjs - - - - - -packages/core/drawLine.mjs - - -drawLine.mjs - - - - - -packages/core/pattern.mjs->packages/core/drawLine.mjs - - - - - -packages/core/fraction.mjs - - -fraction.mjs - - - - - -packages/core/pattern.mjs->packages/core/fraction.mjs - - - - - -packages/core/util.mjs - - -util.mjs - - - - - -packages/core/pattern.mjs->packages/core/util.mjs - - - - - -packages/core/timespan.mjs - - -timespan.mjs - - - - - -packages/core/pattern.mjs->packages/core/timespan.mjs - - - - - -packages/core/hap.mjs - - -hap.mjs - - - - - -packages/core/pattern.mjs->packages/core/hap.mjs - - - - - -packages/core/state.mjs - - -state.mjs - - - - - -packages/core/pattern.mjs->packages/core/state.mjs - - - - - -packages/core/value.mjs - - -value.mjs - - - - - -packages/core/pattern.mjs->packages/core/value.mjs - - - - - -packages/core/drawLine.mjs->packages/core/fraction.mjs - - - - - -packages/core/fraction.mjs->packages/core/timespan.mjs - - - - - - - -packages/core/euclid.mjs - - -euclid.mjs - - - - - -packages/core/euclid.mjs->packages/core/pattern.mjs - - - - - -packages/core/euclid.mjs->packages/core/fraction.mjs - - - - - -packages/core/euclid.mjs->packages/core/util.mjs - - - - - -packages/core/timespan.mjs->packages/core/fraction.mjs - - - - - - - -packages/core/gist.js - - -gist.js - - - - - -packages/core/index.mjs - - -index.mjs - - - - - -packages/core/index.mjs->packages/core/controls.mjs - - - - - -packages/core/index.mjs->packages/core/pattern.mjs - - - - - -packages/core/index.mjs->packages/core/fraction.mjs - - - - - -packages/core/index.mjs->packages/core/euclid.mjs - - - - - -packages/core/index.mjs->packages/core/util.mjs - - - - - -packages/core/index.mjs->packages/core/timespan.mjs - - - - - -packages/core/index.mjs->packages/core/gist.js - - - - - -packages/core/index.mjs->packages/core/hap.mjs - - - - - -packages/core/signal.mjs - - -signal.mjs - - - - - -packages/core/index.mjs->packages/core/signal.mjs - - - - - -packages/core/speak.mjs - - -speak.mjs - - - - - -packages/core/index.mjs->packages/core/speak.mjs - - - - - - - -packages/core/index.mjs->packages/core/state.mjs - - - - - -packages/core/signal.mjs->packages/core/pattern.mjs - - - - - -packages/core/signal.mjs->packages/core/fraction.mjs - - - - - -packages/core/signal.mjs->packages/core/util.mjs - - - - - -packages/core/signal.mjs->packages/core/hap.mjs - - - - - -packages/core/speak.mjs->packages/core/index.mjs - - - - - - - -packages/core/value.mjs->packages/core/util.mjs - - - - - -packages/core/test/drawLine.test.mjs - - -drawLine.test.mjs - - - - - -packages/core/test/drawLine.test.mjs->packages/core/pattern.mjs - - - - - -packages/core/test/drawLine.test.mjs->packages/core/drawLine.mjs - - - - - -packages/core/test/fraction.test.mjs - - -fraction.test.mjs - - - - - -packages/core/test/fraction.test.mjs->packages/core/fraction.mjs - - - - - -packages/core/test/pattern.test.mjs - - -pattern.test.mjs - - - - - -packages/core/test/pattern.test.mjs->packages/core/index.mjs - - - - - -packages/core/test/pattern.test.mjs->packages/core/signal.mjs - - - - - -packages/core/test/util.test.mjs - - -util.test.mjs - - - - - -packages/core/test/util.test.mjs->packages/core/pattern.mjs - - - - - -packages/core/test/util.test.mjs->packages/core/util.mjs - - - - - -packages/core/test/value.test.mjs - - -value.test.mjs - - - - - -packages/core/test/value.test.mjs->packages/core/value.mjs - - - - - -packages/embed/embed.js - - -embed.js - - - - - -packages/eval/evaluate.mjs - - -evaluate.mjs - - - - - -packages/eval/evaluate.mjs->packages/core/index.mjs - - - - - -packages/eval/shapeshifter.mjs - - -shapeshifter.mjs - - - - - -packages/eval/evaluate.mjs->packages/eval/shapeshifter.mjs - - - - - -packages/eval/shapeshifter.mjs->packages/core/index.mjs - - - - - -packages/eval/shift-traverser/index.js - - -index.js - - - - - -packages/eval/shapeshifter.mjs->packages/eval/shift-traverser/index.js - - - - - -packages/eval/index.mjs - - -index.mjs - - - - - -packages/eval/index.mjs->packages/eval/evaluate.mjs - - - - - -packages/eval/test/evaluate.test.mjs - - -evaluate.test.mjs - - - - - -packages/eval/test/evaluate.test.mjs->packages/core/index.mjs - - - - - -packages/eval/test/evaluate.test.mjs->packages/eval/evaluate.mjs - - - - - -packages/mini/index.mjs - - -index.mjs - - - - - -packages/eval/test/evaluate.test.mjs->packages/mini/index.mjs - - - - - -packages/mini/krill-parser.js - - -krill-parser.js - - - - - -packages/mini/index.mjs->packages/mini/krill-parser.js - - - - - -packages/mini/mini.mjs - - -mini.mjs - - - - - -packages/mini/index.mjs->packages/mini/mini.mjs - - - - - -packages/eval/test/shapeshifter.test.mjs - - -shapeshifter.test.mjs - - - - - -packages/eval/test/shapeshifter.test.mjs->packages/eval/shapeshifter.mjs - - - - - -packages/midi/index.mjs - - -index.mjs - - - - - -packages/midi/midi.mjs - - -midi.mjs - - - - - -packages/midi/index.mjs->packages/midi/midi.mjs - - - - - -packages/midi/midi.mjs->packages/core/index.mjs - - - - - -packages/tone/index.mjs - - -index.mjs - - - - - -packages/midi/midi.mjs->packages/tone/index.mjs - - - - - -packages/tone/draw.mjs - - -draw.mjs - - - - - -packages/tone/index.mjs->packages/tone/draw.mjs - - - - - -packages/tone/tone.mjs - - -tone.mjs - - - - - -packages/tone/index.mjs->packages/tone/tone.mjs - - - - - -packages/tone/pianoroll.mjs - - -pianoroll.mjs - - - - - -packages/tone/index.mjs->packages/tone/pianoroll.mjs - - - - - -packages/tone/ui.mjs - - -ui.mjs - - - - - -packages/tone/index.mjs->packages/tone/ui.mjs - - - - - -packages/mini/mini.mjs->packages/core/index.mjs - - - - - -packages/mini/mini.mjs->packages/eval/shapeshifter.mjs - - - - - -packages/mini/mini.mjs->packages/mini/krill-parser.js - - - - - -packages/mini/test/mini.test.mjs - - -mini.test.mjs - - - - - -packages/mini/test/mini.test.mjs->packages/core/euclid.mjs - - - - - -packages/mini/test/mini.test.mjs->packages/mini/mini.mjs - - - - - -packages/osc/osc.mjs - - -osc.mjs - - - - - -packages/osc/osc.mjs->packages/core/index.mjs - - - - - -packages/osc/server.js - - -server.js - - - - - -packages/osc/tidal-sniffer.js - - -tidal-sniffer.js - - - - - -packages/react/dist/index.cjs.js - - -index.cjs.js - - - - - -packages/react/dist/index.cjs.js->packages/core/util.mjs - - - - - -packages/react/dist/index.cjs.js->packages/core/index.mjs - - - - - -packages/react/dist/index.cjs.js->packages/eval/index.mjs - - - - - -packages/react/dist/index.cjs.js->packages/midi/index.mjs - - - - - -packages/react/dist/index.cjs.js->packages/tone/index.mjs - - - - - -packages/react/dist/index.es.js - - -index.es.js - - - - - -packages/react/dist/index.es.js->packages/core/util.mjs - - - - - -packages/react/dist/index.es.js->packages/core/index.mjs - - - - - -packages/react/dist/index.es.js->packages/eval/index.mjs - - - - - -packages/react/dist/index.es.js->packages/midi/index.mjs - - - - - -packages/react/dist/index.es.js->packages/tone/index.mjs - - - - - -packages/react/dist/index.es.js->packages/react/dist/index.cjs.js - - - - - -packages/react/package.json - - -package.json - - - - - -packages/react/postcss.config.js - - -postcss.config.js - - - - - -packages/react/src/App.jsx - - -App.jsx - - - - - -packages/react/src/App.jsx->packages/core/index.mjs - - - - - -packages/react/src/App.jsx->packages/eval/index.mjs - - - - - -packages/react/src/App.jsx->packages/mini/index.mjs - - - - - -packages/react/src/App.jsx->packages/midi/index.mjs - - - - - -packages/react/src/App.jsx->packages/tone/index.mjs - - - - - -packages/react/src/App.jsx->packages/react/dist/index.cjs.js - - - - - -packages/react/src/components/MiniRepl.jsx - - -MiniRepl.jsx - - - - - -packages/react/src/App.jsx->packages/react/src/components/MiniRepl.jsx - - - - - -packages/tonal/index.mjs - - -index.mjs - - - - - -packages/react/src/App.jsx->packages/tonal/index.mjs - - - - - -packages/webaudio/index.mjs - - -index.mjs - - - - - -packages/react/src/App.jsx->packages/webaudio/index.mjs - - - - - -packages/xen/index.mjs - - -index.mjs - - - - - -packages/react/src/App.jsx->packages/xen/index.mjs - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/dist/index.cjs.js - - - - - -packages/react/src/components/CodeMirror6.jsx - - -CodeMirror6.jsx - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/src/components/CodeMirror6.jsx - - - - - -packages/react/src/cx.js - - -cx.js - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/src/cx.js - - - - - -packages/react/src/hooks/useHighlighting.mjs - - -useHighlighting.mjs - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/src/hooks/useHighlighting.mjs - - - - - -packages/react/src/hooks/useRepl.mjs - - -useRepl.mjs - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/src/hooks/useRepl.mjs - - - - - -packages/react/src/components/MiniRepl.module.css - - -MiniRepl.module.css - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/src/components/MiniRepl.module.css - - - - - -packages/react/src/components/style.css - - -style.css - - - - - -packages/react/src/components/MiniRepl.jsx->packages/react/src/components/style.css - - - - - -packages/tonal/tonal.mjs - - -tonal.mjs - - - - - -packages/tonal/index.mjs->packages/tonal/tonal.mjs - - - - - -packages/tonal/voicings.mjs - - -voicings.mjs - - - - - -packages/tonal/index.mjs->packages/tonal/voicings.mjs - - - - - -packages/webaudio/clockworker.mjs - - -clockworker.mjs - - - - - -packages/webaudio/index.mjs->packages/webaudio/clockworker.mjs - - - - - -packages/webaudio/sampler.mjs - - -sampler.mjs - - - - - -packages/webaudio/index.mjs->packages/webaudio/sampler.mjs - - - - - -packages/webaudio/scheduler.mjs - - -scheduler.mjs - - - - - -packages/webaudio/index.mjs->packages/webaudio/scheduler.mjs - - - - - -packages/webaudio/webaudio.mjs - - -webaudio.mjs - - - - - -packages/webaudio/index.mjs->packages/webaudio/webaudio.mjs - - - - - -packages/xen/tune.mjs - - -tune.mjs - - - - - -packages/xen/index.mjs->packages/xen/tune.mjs - - - - - -packages/xen/xen.mjs - - -xen.mjs - - - - - -packages/xen/index.mjs->packages/xen/xen.mjs - - - - - -packages/react/src/components/CodeMirror6.jsx->packages/react/dist/index.cjs.js - - - - - -packages/react/src/themes/material-palenight.js - - -material-palenight.js - - - - - -packages/react/src/components/CodeMirror6.jsx->packages/react/src/themes/material-palenight.js - - - - - -packages/react/src/hooks/useHighlighting.mjs->packages/tone/index.mjs - - - - - -packages/react/src/hooks/useHighlighting.mjs->packages/react/dist/index.cjs.js - - - - - -packages/react/src/hooks/useHighlighting.mjs->packages/react/src/components/CodeMirror6.jsx - - - - - -packages/react/src/hooks/useRepl.mjs->packages/core/util.mjs - - - - - -packages/react/src/hooks/useRepl.mjs->packages/eval/index.mjs - - - - - -packages/react/src/hooks/useRepl.mjs->packages/react/dist/index.cjs.js - - - - - -packages/react/src/hooks/useCycle.mjs - - -useCycle.mjs - - - - - -packages/react/src/hooks/useRepl.mjs->packages/react/src/hooks/useCycle.mjs - - - - - -packages/react/src/hooks/usePostMessage.mjs - - -usePostMessage.mjs - - - - - -packages/react/src/hooks/useRepl.mjs->packages/react/src/hooks/usePostMessage.mjs - - - - - -packages/react/src/hooks/useCycle.mjs->packages/core/index.mjs - - - - - -packages/react/src/hooks/useCycle.mjs->packages/tone/index.mjs - - - - - -packages/react/src/hooks/useCycle.mjs->packages/react/dist/index.cjs.js - - - - - -packages/react/src/hooks/usePostMessage.mjs->packages/react/dist/index.cjs.js - - - - - -packages/react/src/hooks/useWebMidi.mjs - - -useWebMidi.mjs - - - - - -packages/react/src/hooks/useWebMidi.mjs->packages/midi/index.mjs - - - - - -packages/react/src/hooks/useWebMidi.mjs->packages/react/dist/index.cjs.js - - - - - -packages/react/src/index.js - - -index.js - - - - - -packages/react/src/index.js->packages/react/src/components/MiniRepl.jsx - - - - - -packages/react/src/index.js->packages/react/src/components/CodeMirror6.jsx - - - - - -packages/react/src/index.js->packages/react/src/cx.js - - - - - -packages/react/src/index.js->packages/react/src/hooks/useHighlighting.mjs - - - - - -packages/react/src/index.js->packages/react/src/hooks/useRepl.mjs - - - - - -packages/react/src/index.js->packages/react/src/hooks/useCycle.mjs - - - - - -packages/react/src/index.js->packages/react/src/hooks/usePostMessage.mjs - - - - - -packages/react/src/index.js->packages/react/src/hooks/useWebMidi.mjs - - - - - -packages/react/src/main.jsx - - -main.jsx - - - - - -packages/react/src/main.jsx->packages/react/dist/index.cjs.js - - - - - -packages/react/src/main.jsx->packages/react/src/App.jsx - - - - - -packages/react/tailwind.config.js - - -tailwind.config.js - - - - - -packages/react/vite.config.js - - -vite.config.js - - - - - -packages/react/vite.config.js->packages/react/package.json - - - - - -packages/serial/serial.mjs - - -serial.mjs - - - - - -packages/serial/serial.mjs->packages/core/index.mjs - - - - - -packages/soundfonts/convert.js - - -convert.js - - - - - -packages/soundfonts/fontloader.mjs - - -fontloader.mjs - - - - - -packages/soundfonts/index.mjs - - -index.mjs - - - - - -packages/soundfonts/index.mjs->packages/soundfonts/fontloader.mjs - - - - - -packages/soundfonts/list.mjs - - -list.mjs - - - - - -packages/soundfonts/index.mjs->packages/soundfonts/list.mjs - - - - - -packages/tonal/tonal.mjs->packages/core/index.mjs - - - - - -packages/tonal/voicings.mjs->packages/core/index.mjs - - - - - -packages/tonal/test/tonal.test.mjs - - -tonal.test.mjs - - - - - -packages/tonal/test/tonal.test.mjs->packages/core/index.mjs - - - - - -packages/tonal/test/tonal.test.mjs->packages/tonal/tonal.mjs - - - - - -packages/tone/draw.mjs->packages/core/index.mjs - - - - - -packages/tone/draw.mjs->packages/tone/tone.mjs - - - - - -packages/tone/tone.mjs->packages/core/util.mjs - - - - - -packages/tone/tone.mjs->packages/core/index.mjs - - - - - -packages/tone/pianoroll.mjs->packages/core/index.mjs - - - - - -packages/tone/ui.mjs->packages/tone/tone.mjs - - - - - -packages/tone/test/tone.test.mjs - - -tone.test.mjs - - - - - -packages/tone/test/tone.test.mjs->packages/core/index.mjs - - - - - -packages/tone/test/tone.test.mjs->packages/tone/tone.mjs - - - - - -packages/webaudio/scheduler.mjs->packages/core/index.mjs - - - - - -packages/webaudio/scheduler.mjs->packages/webaudio/clockworker.mjs - - - - - -packages/webaudio/webaudio.mjs->packages/core/index.mjs - - - - - -packages/webaudio/webaudio.mjs->packages/webaudio/sampler.mjs - - - - - -packages/webdirt/index.mjs - - -index.mjs - - - - - -packages/webdirt/webdirt.mjs - - -webdirt.mjs - - - - - -packages/webdirt/index.mjs->packages/webdirt/webdirt.mjs - - - - - -packages/webdirt/webdirt.mjs->packages/core/index.mjs - - - - - -packages/webdirt/webdirt.mjs->packages/webaudio/index.mjs - - - - - -packages/xen/tune.mjs->packages/core/index.mjs - - - - - -packages/xen/tunejs.js - - -tunejs.js - - - - - -packages/xen/tune.mjs->packages/xen/tunejs.js - - - - - -packages/xen/xen.mjs->packages/core/index.mjs - - - - - -packages/xen/test/xen.test.mjs - - -xen.test.mjs - - - - - -packages/xen/test/xen.test.mjs->packages/xen/xen.mjs - - - - - diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..c35b8ef4 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,3 @@ +# examples + +This folder contains usage examples for different scenarios. diff --git a/examples/buildless/README.md b/examples/buildless/README.md new file mode 100644 index 00000000..0ecdd083 --- /dev/null +++ b/examples/buildless/README.md @@ -0,0 +1,5 @@ +# buildless examples + +These examples show you how strudel can be used in a regular html file, without the need for a build tool. + +Most examples are using [skypack](https://www.skypack.dev/) diff --git a/packages/core/examples/basic.html b/examples/buildless/basic.html similarity index 100% rename from packages/core/examples/basic.html rename to examples/buildless/basic.html diff --git a/packages/core/examples/canvas.html b/examples/buildless/canvas.html similarity index 100% rename from packages/core/examples/canvas.html rename to examples/buildless/canvas.html diff --git a/packages/web/examples/evaluate.html b/examples/buildless/headless-simple.html similarity index 100% rename from packages/web/examples/evaluate.html rename to examples/buildless/headless-simple.html diff --git a/packages/web/examples/headless-serverless-buildless.html b/examples/buildless/headless-with-samples.html similarity index 100% rename from packages/web/examples/headless-serverless-buildless.html rename to examples/buildless/headless-with-samples.html diff --git a/packages/core/examples/vanilla.html b/examples/buildless/minimal-repl.html similarity index 100% rename from packages/core/examples/vanilla.html rename to examples/buildless/minimal-repl.html diff --git a/packages/embed/example.html b/examples/buildless/web-component-iframe.html similarity index 100% rename from packages/embed/example.html rename to examples/buildless/web-component-iframe.html diff --git a/examples/buildless/web-component-no-iframe.html b/examples/buildless/web-component-no-iframe.html new file mode 100644 index 00000000..6c86d282 --- /dev/null +++ b/examples/buildless/web-component-no-iframe.html @@ -0,0 +1,31 @@ + + + + diff --git a/packages/core/examples/vite-vanilla-repl-cm6/.gitignore b/examples/codemirror-repl/.gitignore similarity index 100% rename from packages/core/examples/vite-vanilla-repl-cm6/.gitignore rename to examples/codemirror-repl/.gitignore diff --git a/examples/codemirror-repl/README.md b/examples/codemirror-repl/README.md new file mode 100644 index 00000000..0ebff4ed --- /dev/null +++ b/examples/codemirror-repl/README.md @@ -0,0 +1,8 @@ +# codemirror-repl example + +This folder demonstrates how to set up a full strudel repl with the `@strudel/codemirror` package. Run it using: + +```sh +pnpm i +pnpm dev +``` diff --git a/packages/core/examples/vite-vanilla-repl-cm6/index.html b/examples/codemirror-repl/index.html similarity index 100% rename from packages/core/examples/vite-vanilla-repl-cm6/index.html rename to examples/codemirror-repl/index.html diff --git a/packages/core/examples/vite-vanilla-repl-cm6/main.js b/examples/codemirror-repl/main.js similarity index 100% rename from packages/core/examples/vite-vanilla-repl-cm6/main.js rename to examples/codemirror-repl/main.js diff --git a/packages/core/examples/vite-vanilla-repl-cm6/package.json b/examples/codemirror-repl/package.json similarity index 100% rename from packages/core/examples/vite-vanilla-repl-cm6/package.json rename to examples/codemirror-repl/package.json diff --git a/packages/core/examples/vite-vanilla-repl-cm6/style.css b/examples/codemirror-repl/style.css similarity index 100% rename from packages/core/examples/vite-vanilla-repl-cm6/style.css rename to examples/codemirror-repl/style.css diff --git a/packages/core/examples/vite-vanilla-repl-cm6/tunes.mjs b/examples/codemirror-repl/tunes.mjs similarity index 100% rename from packages/core/examples/vite-vanilla-repl-cm6/tunes.mjs rename to examples/codemirror-repl/tunes.mjs diff --git a/packages/react/examples/nano-repl/.gitignore b/examples/headless-repl/.gitignore similarity index 100% rename from packages/react/examples/nano-repl/.gitignore rename to examples/headless-repl/.gitignore diff --git a/examples/headless-repl/README.md b/examples/headless-repl/README.md new file mode 100644 index 00000000..a8ad651d --- /dev/null +++ b/examples/headless-repl/README.md @@ -0,0 +1,13 @@ +# headless-repl demo + +This demo shows how to use strudel in "headless mode". +Buttons A / B / C will switch between different patterns. +It showcases the usage of the `@strudel/web` package, using [vite](https://vitejs.dev/) as the dev server. + +## Running + +```sh +pnpm i && cd examples/headless-repl +pnpm dev +# open http://localhost:5173/ +``` diff --git a/packages/web/examples/repl-example/index.html b/examples/headless-repl/index.html similarity index 100% rename from packages/web/examples/repl-example/index.html rename to examples/headless-repl/index.html diff --git a/packages/web/examples/repl-example/package.json b/examples/headless-repl/package.json similarity index 100% rename from packages/web/examples/repl-example/package.json rename to examples/headless-repl/package.json diff --git a/examples/minimal-repl/.gitignore b/examples/minimal-repl/.gitignore new file mode 100644 index 00000000..53c37a16 --- /dev/null +++ b/examples/minimal-repl/.gitignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/examples/minimal-repl/README.md b/examples/minimal-repl/README.md new file mode 100644 index 00000000..1ad16f64 --- /dev/null +++ b/examples/minimal-repl/README.md @@ -0,0 +1,10 @@ +# minimal repl + +This folder demonstrates how to set up a minimal strudel repl using vite and vanilla JS. Run it using: + +```sh +npm i +npm run dev +``` + +If you're looking for a more feature rich alternative, have a look at the [../codemirror-repl](codemirror-repl example) diff --git a/packages/core/examples/vite-vanilla-repl/index.html b/examples/minimal-repl/index.html similarity index 100% rename from packages/core/examples/vite-vanilla-repl/index.html rename to examples/minimal-repl/index.html diff --git a/packages/core/examples/vite-vanilla-repl/main.js b/examples/minimal-repl/main.js similarity index 100% rename from packages/core/examples/vite-vanilla-repl/main.js rename to examples/minimal-repl/main.js diff --git a/packages/core/examples/vite-vanilla-repl/package.json b/examples/minimal-repl/package.json similarity index 100% rename from packages/core/examples/vite-vanilla-repl/package.json rename to examples/minimal-repl/package.json diff --git a/packages/core/examples/vite-vanilla-repl/tune.mjs b/examples/minimal-repl/tune.mjs similarity index 100% rename from packages/core/examples/vite-vanilla-repl/tune.mjs rename to examples/minimal-repl/tune.mjs diff --git a/packages/superdough/example/.gitignore b/examples/superdough/.gitignore similarity index 100% rename from packages/superdough/example/.gitignore rename to examples/superdough/.gitignore diff --git a/examples/superdough/README.md b/examples/superdough/README.md new file mode 100644 index 00000000..85945983 --- /dev/null +++ b/examples/superdough/README.md @@ -0,0 +1,11 @@ +# superdough demo + +This demo shows how to use [superdough](https://www.npmjs.com/package/superdough) with [vite](https://vitejs.dev/). + +## Running + +```sh +pnpm i && cd examples/headless-repl +pnpm dev +# open http://localhost:5173/ +``` diff --git a/examples/superdough/index.html b/examples/superdough/index.html new file mode 100644 index 00000000..7a0bb88d --- /dev/null +++ b/examples/superdough/index.html @@ -0,0 +1,38 @@ + + + + Superdough Example + + + + + + + + diff --git a/packages/superdough/example/package.json b/examples/superdough/package.json similarity index 100% rename from packages/superdough/example/package.json rename to examples/superdough/package.json diff --git a/packages/README.md b/packages/README.md index 5eaa07c6..71aba9d5 100644 --- a/packages/README.md +++ b/packages/README.md @@ -3,13 +3,3 @@ Each folder represents one of the @strudel.cycles/* packages [published to npm](https://www.npmjs.com/org/strudel.cycles). To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs. - -This is a graphical view of all the packages: [full screen](https://raw.githubusercontent.com/tidalcycles/strudel/main/dependencies.svg) - -![dependencies](https://raw.githubusercontent.com/tidalcycles/strudel/main/dependencies.svg) - -Generated with - -```sh -npx depcruise --include-only "^packages" -X "node_modules" --output-type dot packages | dot -T svg > dependencygraph.svg -``` diff --git a/packages/codemirror/autocomplete.mjs b/packages/codemirror/autocomplete.mjs new file mode 100644 index 00000000..c9ec3d7a --- /dev/null +++ b/packages/codemirror/autocomplete.mjs @@ -0,0 +1,78 @@ +import jsdoc from '../../doc.json'; +// import { javascriptLanguage } from '@codemirror/lang-javascript'; +import { autocompletion } from '@codemirror/autocomplete'; +import { h } from './html'; + +const getDocLabel = (doc) => doc.name || doc.longname; +const getInnerText = (html) => { + var div = document.createElement('div'); + div.innerHTML = html; + return div.textContent || div.innerText || ''; +}; + +export function Autocomplete({ doc, label }) { + return h`
+

${label || getDocLabel(doc)}

+${doc.description} + +
+ ${doc.examples?.map((example) => `
${example}
`)} +
+
`[0]; + /* +
 {
+  console.log('ola!');
+  navigator.clipboard.writeText(example);
+  e.stopPropagation();
+}}
+>
+{example}
+
+*/ +} + +const jsdocCompletions = jsdoc.docs + .filter( + (doc) => + getDocLabel(doc) && + !getDocLabel(doc).startsWith('_') && + !['package'].includes(doc.kind) && + !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)), + ) + // https://codemirror.net/docs/ref/#autocomplete.Completion + .map((doc) /*: Completion */ => ({ + label: getDocLabel(doc), + // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. + info: () => Autocomplete({ doc }), + type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type + })); + +export const strudelAutocomplete = (context /* : CompletionContext */) => { + let word = context.matchBefore(/\w*/); + if (word.from == word.to && !context.explicit) return null; + return { + from: word.from, + options: jsdocCompletions, + /* options: [ + { label: 'match', type: 'keyword' }, + { label: 'hello', type: 'variable', info: '(World)' }, + { label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' }, + ], */ + }; +}; + +export function isAutoCompletionEnabled(on) { + return on + ? [ + autocompletion({ override: [strudelAutocomplete] }), + //javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }), + ] + : []; // autocompletion({ override: [] }) +} diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 2094416e..8a887036 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -1,37 +1,104 @@ -import { defaultKeymap } from '@codemirror/commands'; +import { closeBrackets } from '@codemirror/autocomplete'; +// import { search, highlightSelectionMatches } from '@codemirror/search'; +import { history } from '@codemirror/commands'; import { javascript } from '@codemirror/lang-javascript'; import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language'; -import { EditorState } from '@codemirror/state'; -import { EditorView, highlightActiveLineGutter, keymap, lineNumbers } from '@codemirror/view'; -import { Drawer, repl } from '@strudel.cycles/core'; -import { flashField, flash } from './flash.mjs'; -import { highlightExtension, highlightMiniLocations } from './highlight.mjs'; -import { oneDark } from './themes/one-dark'; +import { Compartment, EditorState, Prec } from '@codemirror/state'; +import { EditorView, highlightActiveLineGutter, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view'; +import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core'; +import { isAutoCompletionEnabled } from './autocomplete.mjs'; +import { isTooltipEnabled } from './tooltip.mjs'; +import { flash, isFlashEnabled } from './flash.mjs'; +import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs'; +import { keybindings } from './keybindings.mjs'; +import { initTheme, activateTheme, theme } from './themes.mjs'; +import { updateWidgets, sliderPlugin } from './slider.mjs'; +import { persistentAtom } from '@nanostores/persistent'; + +const extensions = { + isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []), + isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []), + theme, + isAutoCompletionEnabled, + isTooltipEnabled, + isPatternHighlightingEnabled, + isActiveLineHighlighted: (on) => (on ? [highlightActiveLine(), highlightActiveLineGutter()] : []), + isFlashEnabled, + keybindings, +}; +const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()])); + +export const defaultSettings = { + keybindings: 'codemirror', + isLineNumbersDisplayed: true, + isActiveLineHighlighted: false, + isAutoCompletionEnabled: false, + isPatternHighlightingEnabled: true, + isFlashEnabled: true, + isTooltipEnabled: false, + isLineWrappingEnabled: false, + theme: 'strudelTheme', + fontFamily: 'monospace', + fontSize: 18, +}; + +export const codemirrorSettings = persistentAtom('codemirror-settings', defaultSettings, { + encode: JSON.stringify, + decode: JSON.parse, +}); // https://codemirror.net/docs/guide/ -export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, theme = oneDark, root }) { +export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root }) { + const settings = codemirrorSettings.get(); + const initialSettings = Object.keys(compartments).map((key) => + compartments[key].of(extensions[key](parseBooleans(settings[key]))), + ); + initTheme(settings.theme); let state = EditorState.create({ doc: initialCode, extensions: [ - theme, + /* search(), + highlightSelectionMatches(), */ + ...initialSettings, javascript(), - lineNumbers(), - highlightExtension, - highlightActiveLineGutter(), + sliderPlugin, + // indentOnInput(), // works without. already brought with javascript extension? + // bracketMatching(), // does not do anything + closeBrackets(), syntaxHighlighting(defaultHighlightStyle), - keymap.of(defaultKeymap), - flashField, + history(), EditorView.updateListener.of((v) => onChange(v)), - keymap.of([ - { - key: 'Ctrl-Enter', - run: () => onEvaluate(), + Prec.highest( + keymap.of([ + { + key: 'Ctrl-Enter', + run: () => onEvaluate?.(), + }, + { + key: 'Alt-Enter', + run: () => onEvaluate?.(), + }, + { + key: 'Ctrl-.', + run: () => onStop?.(), + }, + { + key: 'Alt-.', + run: (_, e) => { + e.preventDefault(); + onStop?.(); + }, + }, + /* { + key: 'Ctrl-Shift-.', + run: () => (onPanic ? onPanic() : onStop?.()), }, { - key: 'Ctrl-.', - run: () => onStop(), - }, - ]), + key: 'Ctrl-Shift-Enter', + run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()), + }, */ + ]), + ), ], }); @@ -43,45 +110,76 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, the export class StrudelMirror { constructor(options) { - const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, ...replOptions } = options; + const { + root, + id, + initialCode = '', + onDraw, + drawTime = [0, 0], + autodraw, + prebake, + bgFill = true, + ...replOptions + } = options; this.code = initialCode; + this.root = root; + this.miniLocations = []; + this.widgets = []; + this.painters = []; + this.drawTime = drawTime; + this.onDraw = onDraw; + const self = this; + this.id = id || s4(); this.drawer = new Drawer((haps, time) => { const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped); this.highlight(currentFrame, time); - onDraw?.(haps, time, currentFrame); + this.onDraw?.(haps, time, currentFrame, this.painters); }, drawTime); - const prebaked = prebake(); - prebaked.then(async () => { - if (!onDraw) { - return; - } - const { scheduler, evaluate } = await this.repl; - // draw first frame instantly - prebaked.then(async () => { - await evaluate(this.code, false); - this.drawer.invalidate(scheduler); - onDraw?.(this.drawer.visibleHaps, 0, []); - }); - }); + // this approach does not work with multiple repls on screen + // TODO: refactor onPaint usages + find fix, maybe remove painters here? + Pattern.prototype.onPaint = function (onPaint) { + self.painters.push(onPaint); + return this; + }; + + this.prebaked = prebake(); + autodraw && this.drawFirstFrame(); this.repl = repl({ ...replOptions, - onToggle: async (started) => { + onToggle: (started) => { replOptions?.onToggle?.(started); - const { scheduler } = await this.repl; if (started) { - this.drawer.start(scheduler); + this.adjustDrawTime(); + this.drawer.start(this.repl.scheduler); + // stop other repls when this one is started + document.dispatchEvent( + new CustomEvent('start-repl', { + detail: this.id, + }), + ); } else { this.drawer.stop(); + updateMiniLocations(this.editor, []); + cleanupDraw(false); } }, beforeEval: async () => { - await prebaked; + cleanupDraw(); + this.painters = []; + await this.prebaked; + await replOptions?.beforeEval?.(); }, afterEval: (options) => { + // remember for when highlighting is toggled on + this.miniLocations = options.meta?.miniLocations; + this.widgets = options.meta?.widgets; + updateWidgets(this.editor, this.widgets); + updateMiniLocations(this.editor, this.miniLocations); replOptions?.afterEval?.(options); + this.adjustDrawTime(); this.drawer.invalidate(); }, }); @@ -89,25 +187,145 @@ export class StrudelMirror { root, initialCode, onChange: (v) => { - this.code = v.state.doc.toString(); + if (v.docChanged) { + this.code = v.state.doc.toString(); + this.repl.setCode?.(this.code); + } }, onEvaluate: () => this.evaluate(), onStop: () => this.stop(), }); + const cmEditor = this.root.querySelector('.cm-editor'); + if (cmEditor) { + this.root.style.display = 'block'; + if (bgFill) { + this.root.style.backgroundColor = 'var(--background)'; + } + cmEditor.style.backgroundColor = 'transparent'; + } + const settings = codemirrorSettings.get(); + this.setFontSize(settings.fontSize); + this.setFontFamily(settings.fontFamily); + + // stop this repl when another repl is started + this.onStartRepl = (e) => { + if (e.detail !== this.id) { + this.stop(); + } + }; + document.addEventListener('start-repl', this.onStartRepl); + } + // adjusts draw time depending on if there are painters + adjustDrawTime() { + // when no painters are set, [0,0] is enough (just highlighting) + this.drawer.setDrawTime(this.painters.length ? this.drawTime : [0, 0]); + } + async drawFirstFrame() { + if (!this.onDraw) { + return; + } + // draw first frame instantly + await this.prebaked; + try { + await this.repl.evaluate(this.code, false); + this.drawer.invalidate(this.repl.scheduler, -0.001); + // draw at -0.001 to avoid haps at 0 to be visualized as active + this.onDraw?.(this.drawer.visibleHaps, -0.001, [], this.painters); + } catch (err) { + console.warn('first frame could not be painted'); + } } async evaluate() { - const { evaluate } = await this.repl; this.flash(); - await evaluate(this.code); + await this.repl.evaluate(this.code); } async stop() { - const { scheduler } = await this.repl; - scheduler.stop(); + this.repl.scheduler.stop(); + } + async toggle() { + if (this.repl.scheduler.started) { + this.repl.stop(); + } else { + this.evaluate(); + } } flash(ms) { flash(this.editor, ms); } highlight(haps, time) { - highlightMiniLocations(this.editor.view, time, haps); + highlightMiniLocations(this.editor, time, haps); + } + setFontSize(size) { + this.root.style.fontSize = size + 'px'; + } + setFontFamily(family) { + this.root.style.fontFamily = family; + const scroller = this.root.querySelector('.cm-scroller'); + if (scroller) { + scroller.style.fontFamily = family; + } + } + reconfigureExtension(key, value) { + if (!extensions[key]) { + console.warn(`extension ${key} is not known`); + return; + } + value = parseBooleans(value); + const newValue = extensions[key](value, this); + this.editor.dispatch({ + effects: compartments[key].reconfigure(newValue), + }); + if (key === 'theme') { + activateTheme(value); + } + } + setLineWrappingEnabled(enabled) { + this.reconfigureExtension('isLineWrappingEnabled', enabled); + } + setLineNumbersDisplayed(enabled) { + this.reconfigureExtension('isLineNumbersDisplayed', enabled); + } + setTheme(theme) { + this.reconfigureExtension('theme', theme); + } + setAutocompletionEnabled(enabled) { + this.reconfigureExtension('isAutoCompletionEnabled', enabled); + } + updateSettings(settings) { + this.setFontSize(settings.fontSize); + this.setFontFamily(settings.fontFamily); + for (let key in extensions) { + this.reconfigureExtension(key, settings[key]); + } + const updated = { ...codemirrorSettings.get(), ...settings }; + codemirrorSettings.set(updated); + } + changeSetting(key, value) { + if (extensions[key]) { + this.reconfigureExtension(key, value); + return; + } else if (key === 'fontFamily') { + this.setFontFamily(value); + } else if (key === 'fontSize') { + this.setFontSize(value); + } + } + setCode(code) { + const changes = { from: 0, to: this.editor.state.doc.length, insert: code }; + this.editor.dispatch({ changes }); + } + clear() { + this.onStartRepl && document.removeEventListener('start-repl', this.onStartRepl); } } + +function parseBooleans(value) { + return { true: true, false: false }[value] ?? value; +} + +// helper function to generate repl ids +function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); +} diff --git a/packages/codemirror/flash.mjs b/packages/codemirror/flash.mjs index 9bc5c593..6b37038f 100644 --- a/packages/codemirror/flash.mjs +++ b/packages/codemirror/flash.mjs @@ -33,3 +33,5 @@ export const flash = (view, ms = 200) => { view.dispatch({ effects: setFlash.of(false) }); }, ms); }; + +export const isFlashEnabled = (on) => (on ? flashField : []); diff --git a/packages/codemirror/highlight.mjs b/packages/codemirror/highlight.mjs index 317c5fdf..79724f8f 100644 --- a/packages/codemirror/highlight.mjs +++ b/packages/codemirror/highlight.mjs @@ -124,3 +124,12 @@ const miniLocationHighlights = EditorView.decorations.compute([miniLocations, vi }); export const highlightExtension = [miniLocations, visibleMiniLocations, miniLocationHighlights]; + +export const isPatternHighlightingEnabled = (on, config) => { + on && + config && + setTimeout(() => { + updateMiniLocations(config.editor, config.miniLocations); + }, 100); + return on ? highlightExtension : []; +}; diff --git a/packages/codemirror/html.mjs b/packages/codemirror/html.mjs new file mode 100644 index 00000000..527275ef --- /dev/null +++ b/packages/codemirror/html.mjs @@ -0,0 +1,17 @@ +const parser = typeof DOMParser !== 'undefined' ? new DOMParser() : null; +export let html = (string) => { + return parser?.parseFromString(string, 'text/html').querySelectorAll('*'); +}; +let parseChunk = (chunk) => { + if (Array.isArray(chunk)) return chunk.flat().join(''); + if (chunk === undefined) return ''; + return chunk; +}; +export let h = (strings, ...vars) => { + let string = ''; + for (let i in strings) { + string += parseChunk(strings[i]); + string += parseChunk(vars[i]); + } + return html(string); +}; diff --git a/packages/codemirror/index.mjs b/packages/codemirror/index.mjs index c847c32c..8f2d1630 100644 --- a/packages/codemirror/index.mjs +++ b/packages/codemirror/index.mjs @@ -2,3 +2,4 @@ export * from './codemirror.mjs'; export * from './highlight.mjs'; export * from './flash.mjs'; export * from './slider.mjs'; +export * from './themes.mjs'; diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs new file mode 100644 index 00000000..6fe00eda --- /dev/null +++ b/packages/codemirror/keybindings.mjs @@ -0,0 +1,31 @@ +import { Prec } from '@codemirror/state'; +import { keymap, ViewPlugin } from '@codemirror/view'; +// import { searchKeymap } from '@codemirror/search'; +import { emacs } from '@replit/codemirror-emacs'; +import { vim } from '@replit/codemirror-vim'; +import { vscodeKeymap } from '@replit/codemirror-vscode-keymap'; +import { defaultKeymap, historyKeymap } from '@codemirror/commands'; + +const vscodePlugin = ViewPlugin.fromClass( + class { + constructor() {} + }, + { + provide: () => { + return Prec.highest(keymap.of([...vscodeKeymap])); + }, + }, +); +const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []); + +const keymaps = { + vim, + emacs, + vscode: vscodeExtension, +}; + +export function keybindings(name) { + const active = keymaps[name]; + return [keymap.of(defaultKeymap), keymap.of(historyKeymap), active ? active() : []]; + // keymap.of(searchKeymap), +} diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 4e443648..8810a607 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -33,13 +33,22 @@ }, "homepage": "https://github.com/tidalcycles/strudel#readme", "dependencies": { + "@codemirror/autocomplete": "^6.6.0", "@codemirror/commands": "^6.2.4", "@codemirror/lang-javascript": "^6.1.7", "@codemirror/language": "^6.6.0", + "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.2.0", "@codemirror/view": "^6.10.0", "@lezer/highlight": "^1.1.4", - "@strudel.cycles/core": "workspace:*" + "@replit/codemirror-emacs": "^6.0.1", + "@replit/codemirror-vim": "^6.0.14", + "@replit/codemirror-vscode-keymap": "^6.0.2", + "@strudel.cycles/core": "workspace:*", + "@uiw/codemirror-themes": "^4.19.16", + "@uiw/codemirror-themes-all": "^4.19.16", + "nanostores": "^0.8.1", + "@nanostores/persistent": "^0.8.0" }, "devDependencies": { "vite": "^4.3.3" diff --git a/website/src/repl/themes.mjs b/packages/codemirror/themes.mjs similarity index 86% rename from website/src/repl/themes.mjs rename to packages/codemirror/themes.mjs index bee95fd1..ee3e05bf 100644 --- a/website/src/repl/themes.mjs +++ b/packages/codemirror/themes.mjs @@ -30,13 +30,13 @@ import { xcodeLight, } from '@uiw/codemirror-themes-all'; -import strudelTheme from '@strudel.cycles/react/src/themes/strudel-theme'; -import bluescreen, { settings as bluescreenSettings } from '@strudel.cycles/react/src/themes/bluescreen'; -import blackscreen, { settings as blackscreenSettings } from '@strudel.cycles/react/src/themes/blackscreen'; -import whitescreen, { settings as whitescreenSettings } from '@strudel.cycles/react/src/themes/whitescreen'; -import teletext, { settings as teletextSettings } from '@strudel.cycles/react/src/themes/teletext'; -import algoboy, { settings as algoboySettings } from '@strudel.cycles/react/src/themes/algoboy'; -import terminal, { settings as terminalSettings } from '@strudel.cycles/react/src/themes/terminal'; +import strudelTheme from './themes/strudel-theme'; +import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen'; +import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen'; +import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen'; +import teletext, { settings as teletextSettings } from './themes/teletext'; +import algoboy, { settings as algoboySettings } from './themes/algoboy'; +import terminal, { settings as terminalSettings } from './themes/terminal'; export const themes = { strudelTheme, @@ -473,6 +473,9 @@ function stringifySafe(json) { return JSON.stringify(json, getCircularReplacer()); } +export const theme = (theme) => themes[theme] || themes.strudelTheme; + +// css style injection helpers export function injectStyle(rule) { const newStyle = document.createElement('style'); document.head.appendChild(newStyle); @@ -480,3 +483,45 @@ export function injectStyle(rule) { const ruleIndex = styleSheet.insertRule(rule, 0); return () => styleSheet.deleteRule(ruleIndex); } + +let currentTheme, + resetThemeStyle, + themeStyle, + styleID = 'strudel-theme-vars'; +export function initTheme(theme) { + if (!document.getElementById(styleID)) { + themeStyle = document.createElement('style'); + themeStyle.id = styleID; + document.head.append(themeStyle); + } + activateTheme(theme); +} + +export function activateTheme(name) { + if (currentTheme === name) { + return; + } + currentTheme = name; + if (!settings[name]) { + console.warn('theme', name, 'has no settings.. defaulting to strudelTheme settings'); + } + const themeSettings = settings[name] || settings.strudelTheme; + // set css variables + themeStyle.innerHTML = `:root { + ${Object.entries(themeSettings) + // important to override fallback + .map(([key, value]) => `--${key}: ${value} !important;`) + .join('\n')} + }`; + // tailwind dark mode + if (themeSettings.light) { + document.documentElement.classList.remove('dark'); + } else { + document.documentElement.classList.add('dark'); + } + resetThemeStyle?.(); + resetThemeStyle = undefined; + if (themeSettings.customStyle) { + resetThemeStyle = injectStyle(themeSettings.customStyle); + } +} diff --git a/packages/react/src/themes/algoboy.js b/packages/codemirror/themes/algoboy.mjs similarity index 100% rename from packages/react/src/themes/algoboy.js rename to packages/codemirror/themes/algoboy.mjs diff --git a/packages/react/src/themes/blackscreen.js b/packages/codemirror/themes/blackscreen.mjs similarity index 100% rename from packages/react/src/themes/blackscreen.js rename to packages/codemirror/themes/blackscreen.mjs diff --git a/packages/react/src/themes/bluescreen.js b/packages/codemirror/themes/bluescreen.mjs similarity index 100% rename from packages/react/src/themes/bluescreen.js rename to packages/codemirror/themes/bluescreen.mjs diff --git a/packages/codemirror/themes/one-dark.mjs b/packages/codemirror/themes/one-dark.mjs deleted file mode 100644 index cce83699..00000000 --- a/packages/codemirror/themes/one-dark.mjs +++ /dev/null @@ -1,139 +0,0 @@ -import { EditorView } from '@codemirror/view'; -import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'; -import { tags as t } from '@lezer/highlight'; - -// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors - -const chalky = '#e5c07b', - coral = '#e06c75', - cyan = '#56b6c2', - invalid = '#ffffff', - ivory = '#abb2bf', - stone = '#7d8799', // Brightened compared to original to increase contrast - malibu = '#61afef', - sage = '#98c379', - whiskey = '#d19a66', - violet = '#c678dd', - darkBackground = '#21252b', - highlightBackground = '#2c313a', - background = '#282c34', - tooltipBackground = '#353a42', - selection = '#3E4451', - cursor = '#528bff'; - -/// The colors used in the theme, as CSS color strings. -export const color = { - chalky, - coral, - cyan, - invalid, - ivory, - stone, - malibu, - sage, - whiskey, - violet, - darkBackground, - highlightBackground, - background, - tooltipBackground, - selection, - cursor, -}; - -/// The editor theme styles for One Dark. -export const oneDarkTheme = EditorView.theme( - { - '&': { - color: ivory, - backgroundColor: background, - }, - - '.cm-content': { - caretColor: cursor, - }, - - '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor }, - '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': - { backgroundColor: selection }, - - '.cm-panels': { backgroundColor: darkBackground, color: ivory }, - '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' }, - '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' }, - - '.cm-searchMatch': { - backgroundColor: '#72a1ff59', - outline: '1px solid #457dff', - }, - '.cm-searchMatch.cm-searchMatch-selected': { - backgroundColor: '#6199ff2f', - }, - - '.cm-activeLine': { backgroundColor: '#6699ff0b' }, - '.cm-selectionMatch': { backgroundColor: '#aafe661a' }, - - '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': { - backgroundColor: '#bad0f847', - }, - - '.cm-gutters': { - backgroundColor: background, - color: stone, - border: 'none', - }, - - '.cm-activeLineGutter': { - backgroundColor: highlightBackground, - }, - - '.cm-foldPlaceholder': { - backgroundColor: 'transparent', - border: 'none', - color: '#ddd', - }, - - '.cm-tooltip': { - border: 'none', - backgroundColor: tooltipBackground, - }, - '.cm-tooltip .cm-tooltip-arrow:before': { - borderTopColor: 'transparent', - borderBottomColor: 'transparent', - }, - '.cm-tooltip .cm-tooltip-arrow:after': { - borderTopColor: tooltipBackground, - borderBottomColor: tooltipBackground, - }, - '.cm-tooltip-autocomplete': { - '& > ul > li[aria-selected]': { - backgroundColor: highlightBackground, - color: ivory, - }, - }, - }, - { dark: true }, -); - -/// The highlighting style for code in the One Dark theme. -export const oneDarkHighlightStyle = HighlightStyle.define([ - { tag: t.keyword, color: violet }, - { tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: coral }, - { tag: [t.function(t.variableName), t.labelName], color: malibu }, - { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey }, - { tag: [t.definition(t.name), t.separator], color: ivory }, - { tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: chalky }, - { tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: cyan }, - { tag: [t.meta, t.comment], color: stone }, - { tag: t.strong, fontWeight: 'bold' }, - { tag: t.emphasis, fontStyle: 'italic' }, - { tag: t.strikethrough, textDecoration: 'line-through' }, - { tag: t.link, color: stone, textDecoration: 'underline' }, - { tag: t.heading, fontWeight: 'bold', color: coral }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey }, - { tag: [t.processingInstruction, t.string, t.inserted], color: sage }, - { tag: t.invalid, color: invalid }, -]); - -/// Extension to enable the One Dark theme (both the editor theme and -/// the highlight style). -export const oneDark = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)]; diff --git a/packages/react/src/themes/strudel-theme.js b/packages/codemirror/themes/strudel-theme.mjs similarity index 100% rename from packages/react/src/themes/strudel-theme.js rename to packages/codemirror/themes/strudel-theme.mjs diff --git a/packages/react/src/themes/teletext.js b/packages/codemirror/themes/teletext.mjs similarity index 100% rename from packages/react/src/themes/teletext.js rename to packages/codemirror/themes/teletext.mjs diff --git a/packages/react/src/themes/terminal.js b/packages/codemirror/themes/terminal.mjs similarity index 100% rename from packages/react/src/themes/terminal.js rename to packages/codemirror/themes/terminal.mjs diff --git a/packages/react/src/themes/whitescreen.js b/packages/codemirror/themes/whitescreen.mjs similarity index 100% rename from packages/react/src/themes/whitescreen.js rename to packages/codemirror/themes/whitescreen.mjs diff --git a/packages/react/src/components/Tooltip.jsx b/packages/codemirror/tooltip.mjs similarity index 66% rename from packages/react/src/components/Tooltip.jsx rename to packages/codemirror/tooltip.mjs index 43a53476..f67e6d14 100644 --- a/packages/react/src/components/Tooltip.jsx +++ b/packages/codemirror/tooltip.mjs @@ -1,32 +1,33 @@ -import { createRoot } from 'react-dom/client'; import { hoverTooltip } from '@codemirror/view'; -import jsdoc from '../../../../doc.json'; -import { Autocomplete } from './Autocomplete'; +import jsdoc from '../../doc.json'; +import { Autocomplete } from './autocomplete.mjs'; const getDocLabel = (doc) => doc.name || doc.longname; let ctrlDown = false; -// Record Control key event to trigger or block the tooltip depending on the state -window.addEventListener( - 'keyup', - function (e) { - if (e.key == 'Control') { - ctrlDown = false; - } - }, - true, -); +if (typeof window !== 'undefined') { + // Record Control key event to trigger or block the tooltip depending on the state + window.addEventListener( + 'keyup', + function (e) { + if (e.key == 'Control') { + ctrlDown = false; + } + }, + true, + ); -window.addEventListener( - 'keydown', - function (e) { - if (e.key == 'Control') { - ctrlDown = true; - } - }, - true, -); + window.addEventListener( + 'keydown', + function (e) { + if (e.key == 'Control') { + ctrlDown = true; + } + }, + true, + ); +} export const strudelTooltip = hoverTooltip( (view, pos, side) => { @@ -65,10 +66,13 @@ export const strudelTooltip = hoverTooltip( create(view) { let dom = document.createElement('div'); dom.className = 'strudel-tooltip'; - createRoot(dom).render(); + const ac = Autocomplete({ doc: entry, label: word }); + dom.appendChild(ac); return { dom }; }, }; }, { hoverTime: 10 }, ); + +export const isTooltipEnabled = (on) => (on ? strudelTooltip : []); diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index 303525a2..c835ca76 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -11,13 +11,14 @@ export class Cyclist { constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) { this.started = false; this.cps = 1; + this.num_ticks_since_cps_change = 0; this.lastTick = 0; // absolute time when last tick (clock callback) happened this.lastBegin = 0; // query begin of last tick this.lastEnd = 0; // query end of last tick this.getTime = getTime; // get absolute time + this.num_cycles_since_last_cps_change = 0; this.onToggle = onToggle; this.latency = latency; // fixed trigger time offset - const round = (x) => Math.round(x * 1000) / 1000; this.clock = createClock( getTime, // called slightly before each cycle @@ -25,14 +26,24 @@ export class Cyclist { if (tick === 0) { this.origin = phase; } + if (this.num_ticks_since_cps_change === 0) { + this.num_cycles_since_last_cps_change = this.lastEnd; + } + this.num_ticks_since_cps_change++; try { const time = getTime(); const begin = this.lastEnd; this.lastBegin = begin; - const end = round(begin + duration * this.cps); + + //convert ticks to cycles, so you can query the pattern for events + const eventLength = duration * this.cps; + const end = this.num_cycles_since_last_cps_change + this.num_ticks_since_cps_change * eventLength; this.lastEnd = end; + + // query the pattern for events const haps = this.pattern.queryArc(begin, end); - const tickdeadline = phase - time; // time left till phase begins + + const tickdeadline = phase - time; // time left until the phase is a whole number this.lastTick = time + tickdeadline; haps.forEach((hap) => { @@ -59,6 +70,8 @@ export class Cyclist { this.onToggle?.(v); } start() { + this.num_ticks_since_cps_change = 0; + this.num_cycles_since_last_cps_change = 0; if (!this.pattern) { throw new Error('Scheduler: no pattern set! call .setPattern first.'); } @@ -84,7 +97,11 @@ export class Cyclist { } } setCps(cps = 1) { + if (this.cps === cps) { + return; + } this.cps = cps; + this.num_ticks_since_cps_change = 0; } log(begin, end, haps) { const onsets = haps.filter((h) => h.hasOnset()); diff --git a/packages/core/draw.mjs b/packages/core/draw.mjs index c57baa63..941401da 100644 --- a/packages/core/draw.mjs +++ b/packages/core/draw.mjs @@ -111,8 +111,6 @@ export class Framer { // see vite-vanilla-repl-cm6 for an example export class Drawer { constructor(onDraw, drawTime) { - let [lookbehind, lookahead] = drawTime; // e.g. [-2, 2] - lookbehind = Math.abs(lookbehind); this.visibleHaps = []; this.lastFrame = null; this.drawTime = drawTime; @@ -122,6 +120,8 @@ export class Drawer { console.warn('Drawer: no scheduler'); return; } + const lookbehind = Math.abs(this.drawTime[0]); + const lookahead = this.drawTime[1]; // calculate current frame time (think right side of screen for pianoroll) const phase = this.scheduler.now() + lookahead; // first frame just captures the phase @@ -145,12 +145,16 @@ export class Drawer { }, ); } - invalidate(scheduler = this.scheduler) { + setDrawTime(drawTime) { + this.drawTime = drawTime; + } + invalidate(scheduler = this.scheduler, t) { if (!scheduler) { return; } + // TODO: scheduler.now() seems to move even when it's stopped, this hints at a bug... + t = t ?? scheduler.now(); this.scheduler = scheduler; - const t = scheduler.now(); let [_, lookahead] = this.drawTime; const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1]; // remove all future haps diff --git a/packages/core/examples/vite-vanilla-repl-cm6/README.md b/packages/core/examples/vite-vanilla-repl-cm6/README.md deleted file mode 100644 index 4d99d4e6..00000000 --- a/packages/core/examples/vite-vanilla-repl-cm6/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# vite-vanilla-repl-cm6 - -This folder demonstrates how to set up a strudel repl using vite and vanilla JS + codemirror. Run it using: - -```sh -pnpm i -pnpm dev -``` diff --git a/packages/core/examples/vite-vanilla-repl/.gitignore b/packages/core/examples/vite-vanilla-repl/.gitignore deleted file mode 100644 index 586e3d7a..00000000 --- a/packages/core/examples/vite-vanilla-repl/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!dist \ No newline at end of file diff --git a/packages/core/examples/vite-vanilla-repl/README.md b/packages/core/examples/vite-vanilla-repl/README.md deleted file mode 100644 index d7f65720..00000000 --- a/packages/core/examples/vite-vanilla-repl/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# vite-vanilla-repl - -This folder demonstrates how to set up a strudel repl using vite and vanilla JS. Run it using: - -```sh -npm i -npm run dev -``` diff --git a/packages/core/examples/vite-vanilla-repl/dist/assets/index.211bbc68.js b/packages/core/examples/vite-vanilla-repl/dist/assets/index.211bbc68.js deleted file mode 100644 index c71a2c36..00000000 --- a/packages/core/examples/vite-vanilla-repl/dist/assets/index.211bbc68.js +++ /dev/null @@ -1 +0,0 @@ -import{b as s,h as i,m,a as n,c as p,p as t}from"./index.4cbc0a10.js";export{s as SyntaxError,i as h,m as mini,n as minify,p as parse,t as patternifyAST}; diff --git a/packages/core/examples/vite-vanilla-repl/dist/assets/index.4cbc0a10.js b/packages/core/examples/vite-vanilla-repl/dist/assets/index.4cbc0a10.js deleted file mode 100644 index d34a6d2d..00000000 --- a/packages/core/examples/vite-vanilla-repl/dist/assets/index.4cbc0a10.js +++ /dev/null @@ -1,24 +0,0 @@ -var tn=Object.defineProperty;var rn=(r,t,e)=>t in r?tn(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var K=(r,t,e)=>(rn(r,typeof t!="symbol"?t+"":t,e),e);const strudel=Object.freeze(Object.defineProperty({__proto__:null,get Fraction(){return fraction},get controls(){return controls},get drawLine(){return drawLine},get gist(){return gist},get Hap(){return Hap},get setStringParser(){return setStringParser},get Pattern(){return Pattern$3},get polyrhythm(){return polyrhythm},get pr(){return pr},get silence(){return silence$1},get pure(){return pure$1},get isPattern(){return isPattern$1},get reify(){return reify$2},get stack(){return stack$1},get slowcat(){return slowcat$1},get slowcatPrime(){return slowcatPrime},get fastcat(){return fastcat},get cat(){return cat},get timeCat(){return timeCat$1},get sequence(){return sequence$1},get seq(){return seq},get polymeterSteps(){return polymeterSteps},get polymeter(){return polymeter},get pm(){return pm},get add(){return add},get chop(){return chop},get chunk(){return chunk},get chunkBack(){return chunkBack},get div(){return div},get early(){return early},get echo(){return echo},get every(){return every},get fast(){return fast},get inv(){return inv},get invert(){return invert},get iter(){return iter},get iterBack(){return iterBack},get jux(){return jux},get juxBy(){return juxBy},get late(){return late},get linger(){return linger},get mask(){return mask},get mul(){return mul},get off(){return off},get ply(){return ply},get range(){return range},get rangex(){return rangex},get range2(){return range2},get rev(){return rev},get slow(){return slow},get struct(){return struct},get sub(){return sub},get superimpose(){return superimpose},get set(){return set},get when(){return when},get makeComposable(){return makeComposable},get patternify2(){return patternify2},get patternify3(){return patternify3},get patternify4(){return patternify4},get steady(){return steady},get signal(){return signal},get isaw(){return isaw},get isaw2(){return isaw2},get saw(){return saw},get saw2(){return saw2},get sine2(){return sine2},get sine(){return sine},get cosine(){return cosine},get cosine2(){return cosine2},get square(){return square},get square2(){return square2},get tri(){return tri},get tri2(){return tri2},get time(){return time},get rand(){return rand},get rand2(){return rand2},get _brandBy(){return _brandBy},get brandBy(){return brandBy},get brand(){return brand},get _irand(){return _irand},get irand(){return irand},get __chooseWith(){return __chooseWith},get chooseWith(){return chooseWith},get chooseInWith(){return chooseInWith},get choose(){return choose},get chooseCycles(){return chooseCycles},get randcat(){return randcat},get wchoose(){return wchoose},get wchooseCycles(){return wchooseCycles},get perlinWith(){return perlinWith},get perlin(){return perlin},get State(){return State},get TimeSpan(){return TimeSpan},get isNote(){return isNote},get tokenizeNote(){return tokenizeNote},get toMidi(){return toMidi},get fromMidi(){return fromMidi},get getFreq(){return getFreq},get midi2note(){return midi2note},get mod(){return mod},get getPlayableNoteValue(){return getPlayableNoteValue},get getFrequency(){return getFrequency},get rotate(){return rotate},get pipe(){return pipe},get compose(){return compose},get removeUndefineds(){return removeUndefineds},get flatten(){return flatten},get id(){return id},get constant(){return constant},get listRange(){return listRange},get curry(){return curry},get parseNumeral(){return parseNumeral},get mapArgs(){return mapArgs},get numeralArgs(){return numeralArgs},get parseFractional(){return parseFractional},get fractionalArgs(){return fractionalArgs},get evalScope(){return evalScope},get evaluate(){return evaluate$1},get repl(){return repl}},Symbol.toStringTag,{value:"Module"}));(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))o(s);new MutationObserver(s=>{for(const i of s)if(i.type==="childList")for(const f of i.addedNodes)f.tagName==="LINK"&&f.rel==="modulepreload"&&o(f)}).observe(document,{childList:!0,subtree:!0});function e(s){const i={};return s.integrity&&(i.integrity=s.integrity),s.referrerpolicy&&(i.referrerPolicy=s.referrerpolicy),s.crossorigin==="use-credentials"?i.credentials="include":s.crossorigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function o(s){if(s.ep)return;s.ep=!0;const i=e(s);fetch(s.href,i)}})();const scriptRel="modulepreload",assetsURL=function(r){return"/tidalcycles/strudel/use-acorn/packages/core/examples/vite-vanilla-repl/dist/"+r},seen={},__vitePreload=function(t,e,o){if(!e||e.length===0)return t();const s=document.getElementsByTagName("link");return Promise.all(e.map(i=>{if(i=assetsURL(i),i in seen)return;seen[i]=!0;const f=i.endsWith(".css"),l=f?'[rel="stylesheet"]':"";if(!!o)for(let y=s.length-1;y>=0;y--){const $=s[y];if($.href===i&&(!f||$.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${l}`))return;const w=document.createElement("link");if(w.rel=f?"stylesheet":scriptRel,f||(w.as="script",w.crossOrigin=""),w.href=i,document.head.appendChild(w),f)return new Promise((y,$)=>{w.addEventListener("load",y),w.addEventListener("error",()=>$(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t())};var commonjsGlobal=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function getDefaultExportFromCjs(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}function getAugmentedNamespace(r){var t=r.default;if(typeof t=="function"){var e=function(){return t.apply(this,arguments)};e.prototype=t.prototype}else e={};return Object.defineProperty(e,"__esModule",{value:!0}),Object.keys(r).forEach(function(o){var s=Object.getOwnPropertyDescriptor(r,o);Object.defineProperty(e,o,s.get?s:{enumerable:!0,get:function(){return r[o]}})}),e}var fraction$1={exports:{}};/** - * @license Fraction.js v4.2.0 05/03/2022 - * https://www.xarg.org/2014/03/rational-numbers-in-javascript/ - * - * Copyright (c) 2021, Robert Eisele (robert@xarg.org) - * Dual licensed under the MIT or GPL Version 2 licenses. - **/(function(r,t){(function(e){var o=2e3,s={s:1,n:0,d:1};function i(p,d){if(isNaN(p=parseInt(p,10)))throw C.InvalidParameter;return p*d}function f(p,d){if(d===0)throw C.DivisionByZero;var m=Object.create(C.prototype);m.s=p<0?-1:1,p=p<0?-p:p;var b=S(p,d);return m.n=p/b,m.d=d/b,m}function l(p){for(var d={},m=p,b=2,I=4;I<=m;){for(;m%b===0;)m/=b,d[b]=(d[b]||0)+1;I+=1+2*b++}return m!==p?m>1&&(d[m]=(d[m]||0)+1):d[p]=(d[p]||0)+1,d}var g=function(p,d){var m=0,b=1,I=1,T=0,G=0,Y=0,D=1,W=1,P=0,B=1,z=1,O=1,Z=1e7,L;if(p!=null)if(d!==void 0){if(m=p,b=d,I=m*b,m%1!==0||b%1!==0)throw C.NonIntegerParameter}else switch(typeof p){case"object":{if("d"in p&&"n"in p)m=p.n,b=p.d,"s"in p&&(m*=p.s);else if(0 in p)m=p[0],1 in p&&(b=p[1]);else throw C.InvalidParameter;I=m*b;break}case"number":{if(p<0&&(I=p,p=-p),p%1===0)m=p;else if(p>0){for(p>=1&&(W=Math.pow(10,Math.floor(1+Math.log(p)/Math.LN10)),p/=W);B<=Z&&O<=Z;)if(L=(P+z)/(B+O),p===L){B+O<=Z?(m=P+z,b=B+O):O>B?(m=z,b=O):(m=P,b=B);break}else p>L?(P+=z,B+=O):(z+=P,O+=B),B>Z?(m=z,b=O):(m=P,b=B);m*=W}else(isNaN(p)||isNaN(d))&&(b=m=NaN);break}case"string":{if(B=p.match(/\d+|./g),B===null)throw C.InvalidParameter;if(B[P]==="-"?(I=-1,P++):B[P]==="+"&&P++,B.length===P+1?G=i(B[P++],I):B[P+1]==="."||B[P]==="."?(B[P]!=="."&&(T=i(B[P++],I)),P++,(P+1===B.length||B[P+1]==="("&&B[P+3]===")"||B[P+1]==="'"&&B[P+3]==="'")&&(G=i(B[P],I),D=Math.pow(10,B[P].length),P++),(B[P]==="("&&B[P+2]===")"||B[P]==="'"&&B[P+2]==="'")&&(Y=i(B[P+1],I),W=Math.pow(10,B[P+1].length)-1,P+=3)):B[P+1]==="/"||B[P+1]===":"?(G=i(B[P],I),D=i(B[P+2],1),P+=3):B[P+3]==="/"&&B[P+1]===" "&&(T=i(B[P],I),G=i(B[P+2],I),D=i(B[P+4],1),P+=5),B.length<=P){b=D*W,I=m=Y+b*T+W*G;break}}default:throw C.InvalidParameter}if(b===0)throw C.DivisionByZero;s.s=I<0?-1:1,s.n=Math.abs(m),s.d=Math.abs(b)};function w(p,d,m){for(var b=1;d>0;p=p*p%m,d>>=1)d&1&&(b=b*p%m);return b}function y(p,d){for(;d%2===0;d/=2);for(;d%5===0;d/=5);if(d===1)return 0;for(var m=10%d,b=1;m!==1;b++)if(m=m*10%d,b>o)return 0;return b}function $(p,d,m){for(var b=1,I=w(10,m,d),T=0;T<300;T++){if(b===I)return T;b=b*10%d,I=I*10%d}return 0}function S(p,d){if(!p)return d;if(!d)return p;for(;;){if(p%=d,!p)return d;if(d%=p,!d)return p}}function C(p,d){if(g(p,d),this instanceof C)p=S(s.d,s.n),this.s=s.s,this.n=s.n/p,this.d=s.d/p;else return f(s.s*s.n,s.d)}C.DivisionByZero=new Error("Division by Zero"),C.InvalidParameter=new Error("Invalid argument"),C.NonIntegerParameter=new Error("Parameters must be integer"),C.prototype={s:1,n:0,d:1,abs:function(){return f(this.n,this.d)},neg:function(){return f(-this.s*this.n,this.d)},add:function(p,d){return g(p,d),f(this.s*this.n*s.d+s.s*this.d*s.n,this.d*s.d)},sub:function(p,d){return g(p,d),f(this.s*this.n*s.d-s.s*this.d*s.n,this.d*s.d)},mul:function(p,d){return g(p,d),f(this.s*s.s*this.n*s.n,this.d*s.d)},div:function(p,d){return g(p,d),f(this.s*s.s*this.n*s.d,this.d*s.n)},clone:function(){return f(this.s*this.n,this.d)},mod:function(p,d){if(isNaN(this.n)||isNaN(this.d))return new C(NaN);if(p===void 0)return f(this.s*this.n%this.d,1);if(g(p,d),s.n===0&&this.d===0)throw C.DivisionByZero;return f(this.s*(s.d*this.n)%(s.n*this.d),s.d*this.d)},gcd:function(p,d){return g(p,d),f(S(s.n,this.n)*S(s.d,this.d),s.d*this.d)},lcm:function(p,d){return g(p,d),s.n===0&&this.n===0?f(0,1):f(s.n*this.n,S(s.n,this.n)*S(s.d,this.d))},ceil:function(p){return p=Math.pow(10,p||0),isNaN(this.n)||isNaN(this.d)?new C(NaN):f(Math.ceil(p*this.s*this.n/this.d),p)},floor:function(p){return p=Math.pow(10,p||0),isNaN(this.n)||isNaN(this.d)?new C(NaN):f(Math.floor(p*this.s*this.n/this.d),p)},round:function(p){return p=Math.pow(10,p||0),isNaN(this.n)||isNaN(this.d)?new C(NaN):f(Math.round(p*this.s*this.n/this.d),p)},inverse:function(){return f(this.s*this.d,this.n)},pow:function(p,d){if(g(p,d),s.d===1)return s.s<0?f(Math.pow(this.s*this.d,s.n),Math.pow(this.n,s.n)):f(Math.pow(this.s*this.n,s.n),Math.pow(this.d,s.n));if(this.s<0)return null;var m=l(this.n),b=l(this.d),I=1,T=1;for(var G in m)if(G!=="1"){if(G==="0"){I=0;break}if(m[G]*=s.n,m[G]%s.d===0)m[G]/=s.d;else return null;I*=Math.pow(G,m[G])}for(var G in b)if(G!=="1"){if(b[G]*=s.n,b[G]%s.d===0)b[G]/=s.d;else return null;T*=Math.pow(G,b[G])}return s.s<0?f(T,I):f(I,T)},equals:function(p,d){return g(p,d),this.s*this.n*s.d===s.s*s.n*this.d},compare:function(p,d){g(p,d);var m=this.s*this.n*s.d-s.s*s.n*this.d;return(0=0;T--)I=I.inverse().add(m[T]);if(I.sub(d).abs().valueOf()0&&(m+=d,m+=" ",b%=I),m+=b,m+="/",m+=I),m},toLatex:function(p){var d,m="",b=this.n,I=this.d;return this.s<0&&(m+="-"),I===1?m+=b:(p&&(d=Math.floor(b/I))>0&&(m+=d,b%=I),m+="\\frac{",m+=b,m+="}{",m+=I,m+="}"),m},toContinued:function(){var p,d=this.n,m=this.d,b=[];if(isNaN(d)||isNaN(m))return b;do b.push(Math.floor(d/m)),p=d%m,d=m,m=p;while(d!==1);return b},toString:function(p){var d=this.n,m=this.d;if(isNaN(d)||isNaN(m))return"NaN";p=p||15;var b=y(d,m),I=$(d,m,b),T=this.s<0?"-":"";if(T+=d/m|0,d%=m,d*=10,d&&(T+="."),b){for(var G=I;G--;)T+=d/m|0,d%=m,d*=10;T+="(";for(var G=b;G--;)T+=d/m|0,d%=m,d*=10;T+=")"}else for(var G=p;d&&G--;)T+=d/m|0,d%=m,d*=10;return T}},Object.defineProperty(C,"__esModule",{value:!0}),C.default=C,C.Fraction=C,r.exports=C})()})(fraction$1);const Fraction$1=getDefaultExportFromCjs(fraction$1.exports);Fraction$1.prototype.sam=function(){return this.floor()};Fraction$1.prototype.nextSam=function(){return this.sam().add(1)};Fraction$1.prototype.wholeCycle=function(){return new TimeSpan(this.sam(),this.nextSam())};Fraction$1.prototype.cyclePos=function(){return this.sub(this.sam())};Fraction$1.prototype.lt=function(r){return this.compare(r)<0};Fraction$1.prototype.gt=function(r){return this.compare(r)>0};Fraction$1.prototype.lte=function(r){return this.compare(r)<=0};Fraction$1.prototype.gte=function(r){return this.compare(r)>=0};Fraction$1.prototype.eq=function(r){return this.compare(r)==0};Fraction$1.prototype.max=function(r){return this.gt(r)?this:r};Fraction$1.prototype.min=function(r){return this.lt(r)?this:r};Fraction$1.prototype.show=function(){return this.s*this.n+"/"+this.d};Fraction$1.prototype.or=function(r){return this.eq(0)?r:this};const fraction=r=>(typeof r=="number"&&(r=String(r)),Fraction$1(r)),gcd=(...r)=>r.reduce((t,e)=>t.gcd(e),fraction(1));fraction._original=Fraction$1;class TimeSpan{constructor(t,e){this.begin=fraction(t),this.end=fraction(e)}get spanCycles(){const t=[];var e=this.begin;const o=this.end,s=o.sam();if(e.equals(o))return[new TimeSpan(e,o)];for(;o.gt(e);){if(e.sam().equals(s)){t.push(new TimeSpan(e,this.end));break}const i=e.nextSam();t.push(new TimeSpan(e,i)),e=i}return t}get duration(){return this.end.sub(this.begin)}cycleArc(){const t=this.begin.cyclePos(),e=t.add(this.duration);return new TimeSpan(t,e)}withTime(t){return new TimeSpan(t(this.begin),t(this.end))}withEnd(t){return new TimeSpan(this.begin,t(this.end))}withCycle(t){const e=this.begin.sam(),o=e.add(t(this.begin.sub(e))),s=e.add(t(this.end.sub(e)));return new TimeSpan(o,s)}intersection(t){const e=this.begin.max(t.begin),o=this.end.min(t.end);if(!e.gt(o)&&!(e.equals(o)&&(e.equals(this.end)&&this.begin.lt(this.end)||e.equals(t.end)&&t.begin.lt(t.end))))return new TimeSpan(e,o)}intersection_e(t){const e=this.intersection(t);if(e==null)throw"TimeSpans do not intersect";return e}midpoint(){return this.begin.add(this.duration.div(fraction(2)))}equals(t){return this.begin.equals(t.begin)&&this.end.equals(t.end)}show(){return this.begin.show()+" -> "+this.end.show()}}class Hap{constructor(t,e,o,s={},i=!1){this.whole=t,this.part=e,this.value=o,this.context=s,this.stateful=i,i&&console.assert(typeof this.value=="function","Stateful values must be functions")}get duration(){return this.whole.end.sub(this.whole.begin)}wholeOrPart(){return this.whole?this.whole:this.part}withSpan(t){const e=this.whole?t(this.whole):void 0;return new Hap(e,t(this.part),this.value,this.context)}withValue(t){return new Hap(this.whole,this.part,t(this.value),this.context)}hasOnset(){return this.whole!=null&&this.whole.begin.equals(this.part.begin)}resolveState(t){if(this.stateful&&this.hasOnset()){console.log("stateful");const e=this.value,[o,s]=e(t);return[o,new Hap(this.whole,this.part,s,this.context,!1)]}return[t,this]}spanEquals(t){return this.whole==null&&t.whole==null||this.whole.equals(t.whole)}equals(t){return this.spanEquals(t)&&this.part.equals(t.part)&&this.value===t.value}show(){return"("+(this.whole==null?"~":this.whole.show())+", "+this.part.show()+", "+this.value+")"}showWhole(){return`${this.whole==null?"~":this.whole.show()}: ${typeof this.value=="object"?JSON.stringify(this.value):this.value}`}combineContext(t){const e=this;return{...e.context,...t.context,locations:(e.context.locations||[]).concat(t.context.locations||[])}}setContext(t){return new Hap(this.whole,this.part,this.value,t)}}class State{constructor(t,e={}){this.span=t,this.controls=e}setSpan(t){return new State(t,this.controls)}withSpan(t){return this.setSpan(t(this.span))}setControls(t){return new State(this.span,t)}}const isNote=r=>/^[a-gA-G][#b]*[0-9]$/.test(r),tokenizeNote=r=>{var s;if(typeof r!="string")return[];const[t,e="",o]=((s=r.match(/^([a-gA-G])([#bs]*)([0-9])?$/))==null?void 0:s.slice(1))||[];return t?[t,e,o?Number(o):void 0]:[]},toMidi=r=>{const[t,e,o]=tokenizeNote(r);if(!t)throw new Error('not a note: "'+r+'"');const s={c:0,d:2,e:4,f:5,g:7,a:9,b:11}[t.toLowerCase()],i=(e==null?void 0:e.split("").reduce((f,l)=>f+{"#":1,b:-1,s:1}[l],0))||0;return(Number(o)+1)*12+s+i},fromMidi=r=>Math.pow(2,(r-69)/12)*440,getFreq=r=>fromMidi(typeof r=="number"?r:toMidi(r)),midi2note=r=>{const t=Math.floor(r/12)-1;return["C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B"][r%12]+t},mod=(r,t)=>(r%t+t)%t,getPlayableNoteValue=r=>{let{value:t,context:e}=r;if(typeof t=="object"&&!Array.isArray(t)&&(t=t.note||t.n||t.value),typeof t=="number"&&e.type!=="frequency")t=fromMidi(r.value);else if(typeof t=="number"&&e.type==="frequency")t=r.value;else if(typeof t!="string"||!isNote(t))throw new Error("not a note: "+JSON.stringify(t));return t},getFrequency=r=>{let{value:t,context:e}=r;if(typeof t=="object")return t.freq?t.freq:getFreq(t.note||t.n||t.value);if(typeof t=="number"&&e.type!=="frequency")t=fromMidi(r.value);else if(typeof t=="string"&&isNote(t))t=fromMidi(toMidi(r.value));else if(typeof t!="number")throw new Error("not a note or frequency: "+t);return t},rotate=(r,t)=>r.slice(t).concat(r.slice(0,t)),pipe=(...r)=>r.reduce((t,e)=>(...o)=>t(e(...o)),t=>t),compose=(...r)=>pipe(...r.reverse()),removeUndefineds=r=>r.filter(t=>t!=null),flatten=r=>[].concat(...r),id=r=>r,constant=(r,t)=>r,listRange=(r,t)=>Array.from({length:t-r+1},(e,o)=>o+r);function curry(r,t){const e=function o(...s){if(s.length>=r.length)return r.apply(this,s);{const i=function(...f){return o.apply(this,s.concat(f))};return t&&t(i,s),i}};return t&&t(e,[]),e}function parseNumeral(r){const t=Number(r);if(!isNaN(t))return t;if(isNote(r))return toMidi(r);throw new Error(`cannot parse as numeral: "${r}"`)}function mapArgs(r,t){return(...e)=>r(...e.map(t))}function numeralArgs(r){return mapArgs(r,parseNumeral)}function parseFractional(r){const t=Number(r);if(!isNaN(t))return t;const e={pi:Math.PI,w:1,h:.5,q:.25,e:.125,s:.0625,t:1/3,f:.2,x:1/6}[r];if(typeof e<"u")return e;throw new Error(`cannot parse as fractional: "${r}"`)}const fractionalArgs=r=>mapArgs(r,parseFractional);function unionWithObj(r,t,e){const o=Object.keys(r).filter(s=>Object.keys(t).includes(s));return Object.assign({},r,t,Object.fromEntries(o.map(s=>[s,e(r[s],t[s])])))}curry((r,t)=>r*t);curry((r,t)=>t.map(r));function drawLine(r,t=60){let e=0,o=fraction(0),s=[""],i="";for(;s[0].lengthy.hasOnset()).map(y=>y.duration),g=gcd(...l),w=g.inverse();s=s.map(y=>y+"|"),i+="|";for(let y=0;yd.whole.begin.lte($)&&d.whole.end.gte(S)),p=C.length-s.length;p>0&&(s=s.concat(Array(p).fill(i))),s=s.map((d,m)=>{const b=C[m];if(b){const T=b.whole.begin.eq($)?""+b.value:"-";return d+T}return d+"."}),i+=".",o=o.add(g)}e++}return s.join(` -`)}let stringParser;const setStringParser=r=>stringParser=r;class Pattern$3{constructor(t){K(this,"_Pattern",!0);this.query=t}queryArc(t,e){return this.query(new State(new TimeSpan(t,e)))}_splitQueries(){const t=this,e=o=>flatten(o.span.spanCycles.map(s=>t.query(o.setSpan(s))));return new Pattern$3(e)}withQuerySpan(t){return new Pattern$3(e=>this.query(e.withSpan(t)))}withQuerySpanMaybe(t){const e=this;return new Pattern$3(o=>{const s=o.withSpan(t);return s.span?e.query(s):[]})}withQueryTime(t){return new Pattern$3(e=>this.query(e.withSpan(o=>o.withTime(t))))}withHapSpan(t){return new Pattern$3(e=>this.query(e).map(o=>o.withSpan(t)))}withHapTime(t){return this.withHapSpan(e=>e.withTime(t))}_withHaps(t){return new Pattern$3(e=>t(this.query(e)))}_withHap(t){return this._withHaps(e=>e.map(t))}_setContext(t){return this._withHap(e=>e.setContext(t))}_withContext(t){return this._withHap(e=>e.setContext(t(e.context)))}_stripContext(){return this._withHap(t=>t.setContext({}))}withLocation(t,e){const o={start:{line:t[0],column:t[1],offset:t[2]},end:{line:e[0],column:e[1],offset:e[2]}};return this._withContext(s=>{const i=(s.locations||[]).concat([o]);return{...s,locations:i}})}withMiniLocation(t,e){const o={start:{line:t[0],column:t[1],offset:t[2]},end:{line:e[0],column:e[1],offset:e[2]}};return this._withContext(s=>{let i=s.locations||[];return i=i.map(({start:f,end:l})=>{const g=f.line===1?o.start.column:0;return{start:{...f,line:f.line-1+(o.start.line-1)+1,column:f.column-1+g},end:{...l,line:l.line-1+(o.start.line-1)+1,column:l.column-1+g}}}),{...s,locations:i}})}withValue(t){return new Pattern$3(e=>this.query(e).map(o=>o.withValue(t)))}fmap(t){return this.withValue(t)}_filterHaps(t){return new Pattern$3(e=>this.query(e).filter(t))}_filterValues(t){return new Pattern$3(e=>this.query(e).filter(o=>t(o.value)))}_removeUndefineds(){return this._filterValues(t=>t!=null)}onsetsOnly(){return this._filterHaps(t=>t.hasOnset())}discreteOnly(){return this._filterHaps(t=>t.whole)}_appWhole(t,e){const o=this,s=function(i){const f=o.query(i),l=e.query(i),g=function(w,y){const $=w.part.intersection(y.part);if($!=null)return new Hap(t(w.whole,y.whole),$,w.value(y.value),y.combineContext(w))};return flatten(f.map(w=>removeUndefineds(l.map(y=>g(w,y)))))};return new Pattern$3(s)}appBoth(t){const e=function(o,s){if(!(o==null||s==null))return o.intersection_e(s)};return this._appWhole(e,t)}appLeft(t){const e=this,o=function(s){const i=[];for(const f of e.query(s)){const l=t.query(s.setSpan(f.wholeOrPart()));for(const g of l){const w=f.whole,y=f.part.intersection(g.part);if(y){const $=f.value(g.value),S=g.combineContext(f),C=new Hap(w,y,$,S);i.push(C)}}}return i};return new Pattern$3(o)}appRight(t){const e=this,o=function(s){const i=[];for(const f of t.query(s)){const l=e.query(s.setSpan(f.wholeOrPart()));for(const g of l){const w=f.whole,y=g.part.intersection(f.part);if(y){const $=g.value(f.value),S=f.combineContext(g),C=new Hap(w,y,$,S);i.push(C)}}}return i};return new Pattern$3(o)}firstCycle(t=!1){var e=this;return t||(e=e._stripContext()),e.query(new State(new TimeSpan(fraction(0),fraction(1))))}get _firstCycleValues(){return this.firstCycle().map(t=>t.value)}get _showFirstCycle(){return this.firstCycle().map(t=>`${t.value}: ${t.whole.begin.toFraction()} - ${t.whole.end.toFraction()}`)}_sortHapsByPart(){return this._withHaps(t=>t.sort((e,o)=>e.part.begin.sub(o.part.begin).or(e.part.end.sub(o.part.end)).or(e.whole.begin.sub(o.whole.begin).or(e.whole.end.sub(o.whole.end)))))}_opIn(t,e){return this.fmap(e).appLeft(reify$2(t))}_opOut(t,e){return this.fmap(e).appRight(reify$2(t))}_opMix(t,e){return this.fmap(e).appBoth(reify$2(t))}_opSqueeze(t,e){const o=reify$2(t);return this.fmap(s=>o.fmap(i=>e(s)(i)))._squeezeJoin()}_opSqueezeOut(t,e){const o=this;return reify$2(t).fmap(i=>o.fmap(f=>e(f)(i)))._squeezeJoin()}_opTrig(t,e){return reify$2(t).fmap(s=>this.fmap(i=>e(i)(s)))._trigJoin()}_opTrigzero(t,e){return reify$2(t).fmap(s=>this.fmap(i=>e(i)(s)))._TrigzeroJoin()}_asNumber(){return this.fmap(parseNumeral)}round(){return this._asNumber().fmap(t=>Math.round(t))}floor(){return this._asNumber().fmap(t=>Math.floor(t))}ceil(){return this._asNumber().fmap(t=>Math.ceil(t))}_toBipolar(){return this.fmap(t=>t*2-1)}_fromBipolar(){return this.fmap(t=>(t+1)/2)}_range(t,e){return this.mul(e-t).add(t)}_rangex(t,e){return this._range(Math.log(t),Math.log(e)).fmap(Math.exp)}_range2(t,e){return this._fromBipolar()._range(t,e)}_bindWhole(t,e){const o=this,s=function(i){const f=function(g,w){return new Hap(t(g.whole,w.whole),w.part,w.value,Object.assign({},g.context,w.context,{locations:(g.context.locations||[]).concat(w.context.locations||[])}))},l=function(g){return e(g.value).query(i.setSpan(g.part)).map(w=>f(g,w))};return flatten(o.query(i).map(g=>l(g)))};return new Pattern$3(s)}bind(t){const e=function(o,s){if(!(o==null||s==null))return o.intersection_e(s)};return this._bindWhole(e,t)}join(){return this.bind(id)}outerBind(t){return this._bindWhole((e,o)=>e,t)}outerJoin(){return this.outerBind(id)}innerBind(t){return this._bindWhole((e,o)=>o,t)}innerJoin(){return this.innerBind(id)}_trigJoin(t=!1){const e=this;return new Pattern$3(o=>e.discreteOnly().query(o).map(s=>s.value.late(t?s.whole.begin:s.whole.begin.cyclePos()).query(o).map(i=>new Hap(i.whole?i.whole.intersection(s.whole):void 0,i.part.intersection(s.part),i.value).setContext(s.combineContext(i))).filter(i=>i.part)).flat())}_TrigzeroJoin(){return this._trigJoin(!0)}_squeezeJoin(){const t=this;function e(o){const s=t.discreteOnly().query(o);function i(l){const w=l.value._focusSpan(l.wholeOrPart()).query(o.setSpan(l.part));function y($,S){let C;if(S.whole&&$.whole&&(C=S.whole.intersection($.whole),!C))return;const p=S.part.intersection($.part);if(!p)return;const d=S.combineContext($);return new Hap(C,p,S.value,d)}return w.map($=>y(l,$))}return flatten(s.map(i)).filter(l=>l)}return new Pattern$3(e)}_squeezeBind(t){return this.fmap(t)._squeezeJoin()}_apply(t){return t(this)}layer(...t){return stack$1(...t.map(e=>e(this)))}_patternify(t){const e=this;return function(...s){return s=s.map(f=>isPattern$1(f)?f.fmap(l=>l.value||l):f),sequence$1(...s).fmap(f=>t.call(e,f)).innerJoin()}}_fastGap(t){const e=function(s){const i=s.begin.sam(),f=s.begin.sub(i).mul(t).min(1),l=s.end.sub(i).mul(t).min(1);if(!(f>=1))return new TimeSpan(i.add(f),i.add(l))},o=function(s){const i=s.part.begin,f=s.part.end,l=i.sam(),g=i.sub(l).div(t).min(1),w=f.sub(l).div(t).min(1),y=new TimeSpan(l.add(g),l.add(w)),$=s.whole?new TimeSpan(y.begin.sub(i.sub(s.whole.begin).div(t)),y.end.add(s.whole.end.sub(f).div(t))):void 0;return new Hap($,y,s.value,s.context)};return this.withQuerySpanMaybe(e)._withHap(o)._splitQueries()}_compress(t,e){return t.gt(e)||t.gt(1)||e.gt(1)||t.lt(0)||e.lt(0)?silence$1:this._fastGap(fraction(1).div(e.sub(t)))._late(t)}_compressSpan(t){return this._compress(t.begin,t.end)}_focus(t,e){return this._fast(fraction(1).div(e.sub(t))).late(t.cyclePos())}_focusSpan(t){return this._focus(t.begin,t.end)}_fast(t){return this.withQueryTime(o=>o.mul(t)).withHapTime(o=>o.div(t))}_slow(t){return this._fast(fraction(1).div(t))}_inside(t,e){return e(this._slow(t))._fast(t)}_outside(t,e){return e(this._fast(t))._slow(t)}_ply(t){return this.fmap(e=>pure$1(e)._fast(t))._squeezeJoin()}_chop(t){const o=Array.from({length:t},(i,f)=>f).map(i=>({begin:i/t,end:(i+1)/t})),s=function(i){return sequence$1(o.map(f=>Object.assign({},i,f)))};return this._squeezeBind(s)}_striate(t){const o=Array.from({length:t},(i,f)=>f).map(i=>({begin:i/t,end:(i+1)/t})),s=slowcat$1(...o);return this.set(s)._fast(t)}_cpm(t){return this._fast(t/60)}_early(t){return t=fraction(t),this.withQueryTime(e=>e.add(t)).withHapTime(e=>e.sub(t))}_late(t){return t=fraction(t),this._early(fraction(0).sub(t))}_zoom(t,e){e=fraction(e),t=fraction(t);const o=e.sub(t);return this.withQuerySpan(s=>s.withCycle(i=>i.mul(o).add(t))).withHapSpan(s=>s.withCycle(i=>i.sub(t).div(o)))._splitQueries()}_zoomArc(t){return this.zoom(t.begin,t.end)}_linger(t){return t==0?silence$1:t<0?this._zoom(t.add(1),1)._slow(t):this._zoom(0,t)._slow(t)}_color(t){return this._withContext(e=>({...e,color:t}))}log(){return this._withHap(t=>{var e;return t.setContext({...t.context,logs:(((e=t.context)==null?void 0:e.logs)||[]).concat([t.show()])})})}drawLine(){return console.log(drawLine(this)),this}_segment(t){return this.struct(pure$1(!0)._fast(t))}invert(){return this.fmap(t=>!t)}inv(){return this.invert()}when(t,e){const o=t._filterValues(id),s=t._filterValues(l=>!l),i=o.fmap(l=>g=>g).appRight(e(this)),f=s.fmap(l=>g=>g).appRight(this);return stack$1(i,f)}off(t,e){return stack$1(this,e(this.late(t)))}every(t,e){const o=this,s=Array(t-1).fill(o);return s.push(e(o)),slowcatPrime(...s)}every(t,e){const o=this,s=Array(t-1).fill(o);return s.unshift(e(o)),slowcatPrime(...s)}each(t,e){const o=this,s=Array(t-1).fill(o);return s.push(e(o)),slowcatPrime(...s)}brak(){return this.when(slowcat$1(!1,!0),t=>fastcat(t,silence$1)._late(.25))}rev(){const t=this,e=function(o){const s=o.span,i=s.begin.sam(),f=s.begin.nextSam(),l=function(w){const y=w.withTime(S=>i.add(f.sub(S))),$=y.begin;return y.begin=y.end,y.end=$,y};return t.query(o.setSpan(l(s))).map(w=>w.withSpan(l))};return new Pattern$3(e)._splitQueries()}palindrome(){return this.every(2,rev)}juxBy(t,e){t/=2;const o=function(f,l,g){return l in f?f[l]:g},s=this.withValue(f=>Object.assign({},f,{pan:o(f,"pan",.5)-t})),i=this.withValue(f=>Object.assign({},f,{pan:o(f,"pan",.5)+t}));return stack$1(s,e(i))}_jux(t){return this.juxBy(1,t)}stack(...t){return stack$1(this,...t)}sequence(...t){return sequence$1(this,...t)}seq(...t){return sequence$1(this,...t)}cat(...t){return cat(this,...t)}fastcat(...t){return fastcat(this,...t)}slowcat(...t){return slowcat$1(this,...t)}superimpose(...t){return this.stack(...t.map(e=>e(this)))}stutWith(t,e,o){return stack$1(...listRange(0,t-1).map(s=>o(this.late(fraction(e).mul(s)),s)))}stut(t,e,o){return this.stutWith(t,o,(s,i)=>s.velocity(Math.pow(e,i)))}_echoWith(t,e,o){return stack$1(...listRange(0,t-1).map(s=>o(this.late(fraction(e).mul(s)),s)))}_echo(t,e,o){return this._echoWith(t,e,(s,i)=>s.velocity(Math.pow(o,i)))}iter(t,e=!1){return slowcat$1(...listRange(0,t-1).map(o=>e?this.late(o/t):this.early(o/t)))}iterBack(t){return this.iter(t,!0)}_chunk(t,e,o=!1){const s=Array(t-1).fill(!1);s.unshift(!0);const i=sequence$1(...s).iter(t,o);return this.when(i,e)}_chunkBack(t,e){return this._chunk(t,e,!0)}_bypass(t){return t=Boolean(parseInt(t)),t?silence$1:this}hush(){return silence$1}_duration(t){return this.withHapSpan(e=>new TimeSpan(e.begin,e.begin.add(t)))}_legato(t){return this.withHapSpan(e=>new TimeSpan(e.begin,e.begin.add(e.end.sub(e.begin).mul(t))))}_velocity(t){return this._withContext(e=>({...e,velocity:(e.velocity||1)*t}))}_loopAt(t,e=1){return this.speed(1/t*e).unit("c").slow(t)}onTrigger(t){return this._withHap(e=>e.setContext({...e.context,onTrigger:t}))}log(t=id){return this._withHap(e=>e.setContext({...e.context,onTrigger:(...o)=>{e.context.onTrigger&&e.context.onTrigger(...o),console.log(t(...o))}}))}logValues(t=id){return this.log((e,o)=>t(o.value))}}function _composeOp(r,t,e){function o(s){return s instanceof Object&&!(s instanceof Function)}return o(r)||o(t)?(o(r)||(r={value:r}),o(t)||(t={value:t}),unionWithObj(r,t,e)):e(r,t)}(function(){const r={set:[(t,e)=>e],keep:[(t,e)=>t],keepif:[(t,e)=>e?t:void 0],add:[numeralArgs((t,e)=>t+e)],sub:[numeralArgs((t,e)=>t-e)],mul:[numeralArgs((t,e)=>t*e)],div:[numeralArgs((t,e)=>t/e)],mod:[numeralArgs(mod)],pow:[numeralArgs(Math.pow)],_and:[numeralArgs((t,e)=>t&e)],_or:[numeralArgs((t,e)=>t|e)],_xor:[numeralArgs((t,e)=>t^e)],_lshift:[numeralArgs((t,e)=>t<t>>e)],lt:[(t,e)=>tt>e],lte:[(t,e)=>t<=e],gte:[(t,e)=>t>=e],eq:[(t,e)=>t==e],eqt:[(t,e)=>t===e],ne:[(t,e)=>t!=e],net:[(t,e)=>t!==e],and:[(t,e)=>t&&e],or:[(t,e)=>t||e],func:[(t,e)=>e(t)]};for(const[t,[e,o]]of Object.entries(r))for(const s of["In","Out","Mix","Squeeze","SqueezeOut","Trig","Trigzero"])Pattern$3.prototype[t+s]=function(...i){var f=this;i=sequence$1(i),o&&(f=o(f),i=o(i));var l;return t==="keepif"?(l=f["_op"+s](i,g=>w=>e(g,w)),l=l._removeUndefineds()):l=f["_op"+s](i,g=>w=>_composeOp(g,w,e)),l},s==="Squeeze"&&(Pattern$3.prototype[t+"SqueezeIn"]=Pattern$3.prototype[t+s]),s==="In"?Pattern$3.prototype[t]=Pattern$3.prototype[t+s]:t==="set"&&(Pattern$3.prototype[s.toLowerCase()]=Pattern$3.prototype[t+s]);Pattern$3.prototype.struct=Pattern$3.prototype.keepifOut,Pattern$3.prototype.structAll=Pattern$3.prototype.keepOut,Pattern$3.prototype.mask=Pattern$3.prototype.keepifIn,Pattern$3.prototype.maskAll=Pattern$3.prototype.keepIn,Pattern$3.prototype.reset=Pattern$3.prototype.keepifTrig,Pattern$3.prototype.resetAll=Pattern$3.prototype.keepTrig,Pattern$3.prototype.restart=Pattern$3.prototype.keepifTrigzero,Pattern$3.prototype.restartAll=Pattern$3.prototype.keepTrigzero})();Pattern$3.prototype.patternified=["apply","chop","color","cpm","duration","early","fast","jux","late","legato","linger","ply","segment","striate","slow","velocity"];const polyrhythm=stack$1,pr=stack$1;Pattern$3.prototype.factories={pure:pure$1,stack:stack$1,slowcat:slowcat$1,fastcat,cat,timeCat:timeCat$1,sequence:sequence$1,seq,polymeter,pm,polyrhythm,pr};const silence$1=new Pattern$3(r=>[]);function pure$1(r){function t(e){return e.span.spanCycles.map(o=>new Hap(fraction(o.begin).wholeCycle(),o,r))}return new Pattern$3(t)}function isPattern$1(r){const t=r instanceof Pattern$3||(r==null?void 0:r._Pattern);return!r instanceof Pattern$3&&console.warn(`Found Pattern that fails "instanceof Pattern" check. - This may happen if you are using multiple versions of @strudel.cycles/core. - Please check by running "npm ls @strudel.cycles/core".`),t}function reify$2(r){return isPattern$1(r)?r:stringParser&&typeof r=="string"?stringParser(r):pure$1(r)}function stack$1(...r){r=r.map(e=>Array.isArray(e)?sequence$1(...e):reify$2(e));const t=e=>flatten(r.map(o=>o.query(e)));return new Pattern$3(t)}function slowcat$1(...r){r=r.map(e=>Array.isArray(e)?sequence$1(...e):reify$2(e));const t=function(e){const o=e.span,s=mod(o.begin.sam(),r.length),i=r[s];if(!i)return[];const f=o.begin.floor().sub(o.begin.div(r.length).floor());return i.withHapTime(l=>l.add(f)).query(e.setSpan(o.withTime(l=>l.sub(f))))};return new Pattern$3(t)._splitQueries()}function slowcatPrime(...r){r=r.map(reify$2);const t=function(e){const o=Math.floor(e.span.begin)%r.length,s=r[o];return(s==null?void 0:s.query(e))||[]};return new Pattern$3(t)._splitQueries()}function fastcat(...r){return slowcat$1(...r)._fast(r.length)}function cat(...r){return slowcat$1(...r)}function timeCat$1(...r){const t=r.map(s=>s[0]).reduce((s,i)=>s.add(i),fraction(0));let e=fraction(0);const o=[];for(const[s,i]of r){const f=e.add(s);o.push(reify$2(i)._compress(e.div(t),f.div(t))),e=f}return stack$1(...o)}function sequence$1(...r){return fastcat(...r)}function seq(...r){return fastcat(...r)}function _sequenceCount(r){return Array.isArray(r)?r.length==0?[silence$1,0]:r.length==1?_sequenceCount(r[0]):[fastcat(...r.map(t=>_sequenceCount(t)[0])),r.length]:[reify$2(r),1]}function polymeterSteps(r,...t){const e=t.map(s=>_sequenceCount(s));if(e.length==0)return silence$1;r==0&&(r=e[0][1]);const o=[];for(const s of e)s[1]==0&&next,r==s[1]?o.push(s[0]):o.push(s[0]._fast(fraction(r).div(fraction(s[1]))));return stack$1(...o)}function polymeter(...r){return polymeterSteps(0,...r)}function pm(...r){polymeter(...r)}const add=curry((r,t)=>t.add(r)),chop=curry((r,t)=>t.chop(r)),chunk=curry((r,t)=>t.chunk(r)),chunkBack=curry((r,t)=>t.chunkBack(r)),div=curry((r,t)=>t.div(r)),early=curry((r,t)=>t.early(r)),echo=curry((r,t,e,o)=>o.echo(r,t,e)),every=curry((r,t,e)=>e.every(r,t)),fast=curry((r,t)=>t.fast(r)),inv=r=>r.inv(),invert=r=>r.invert(),iter=curry((r,t)=>t.iter(r)),iterBack=curry((r,t)=>t.iter(r)),jux=curry((r,t)=>t.jux(r)),juxBy=curry((r,t,e)=>e.juxBy(r,t)),late=curry((r,t)=>t.late(r)),linger=curry((r,t)=>t.linger(r)),mask=curry((r,t)=>t.mask(r)),mul=curry((r,t)=>t.mul(r)),off=curry((r,t,e)=>e.off(r,t)),ply=curry((r,t)=>t.ply(r)),range=curry((r,t,e)=>e.range(r,t)),rangex=curry((r,t,e)=>e.rangex(r,t)),range2=curry((r,t,e)=>e.range2(r,t)),rev=r=>r.rev(),slow=curry((r,t)=>t.slow(r)),struct=curry((r,t)=>t.struct(r)),sub=curry((r,t)=>t.sub(r)),superimpose=curry((r,t)=>t.superimpose(...r)),set=curry((r,t)=>t.set(r)),when=curry((r,t,e)=>e.when(r,t));Pattern$3.prototype.composable={fast,slow,early,late,superimpose};function makeComposable(r){return Object.entries(Pattern$3.prototype.composable).forEach(([t,e])=>{r[t]=(...o)=>{const s=compose(r,e(...o));return makeComposable(s)}}),r}const patternify2=r=>(t,e,o)=>t.fmap(s=>i=>r.call(o,s,i)).appLeft(e).innerJoin(),patternify3=r=>(t,e,o,s)=>t.fmap(i=>f=>l=>r.call(s,i,f,l)).appLeft(e).appLeft(o).innerJoin(),patternify4=r=>(t,e,o,s,i)=>t.fmap(f=>l=>g=>w=>r.call(i,f,l,g,w)).appLeft(e).appLeft(o).appLeft(s).innerJoin();Pattern$3.prototype.echo=function(...r){return r=r.map(reify$2),patternify3(Pattern$3.prototype._echo)(...r,this)};Pattern$3.prototype.echoWith=function(...r){return r=r.map(reify$2),patternify3(Pattern$3.prototype._echoWith)(...r,this)};Pattern$3.prototype.chunk=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._chunk)(...r,this)};Pattern$3.prototype.chunkBack=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._chunkBack)(...r,this)};Pattern$3.prototype.loopAt=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._loopAt)(...r,this)};Pattern$3.prototype.zoom=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._zoom)(...r,this)};Pattern$3.prototype.compress=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._compress)(...r,this)};Pattern$3.prototype.outside=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._outside)(...r,this)};Pattern$3.prototype.inside=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._inside)(...r,this)};Pattern$3.prototype.range=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._range)(...r,this)};Pattern$3.prototype.rangex=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._rangex)(...r,this)};Pattern$3.prototype.range2=function(...r){return r=r.map(reify$2),patternify2(Pattern$3.prototype._range2)(...r,this)};Pattern$3.prototype.bootstrap=function(){const r=Object.fromEntries(Object.entries(Pattern$3.prototype.composable).map(([t,e])=>(Pattern$3.prototype[t]&&(Pattern$3.prototype[t]=makeComposable(Pattern$3.prototype[t])),[t,curry(e,makeComposable)])));return this.patternified.forEach(t=>{Pattern$3.prototype[t]=function(...e){return this._patternify(Pattern$3.prototype["_"+t])(...e)}}),r};Pattern$3.prototype.define=(r,t,e={})=>{e.composable&&(Pattern$3.prototype.composable[r]=t),e.patternified&&(Pattern$3.prototype.patternified=Pattern$3.prototype.patternified.concat([r])),Pattern$3.prototype.bootstrap()};Pattern$3.prototype.define("hush",r=>r.hush(),{patternified:!1,composable:!0});Pattern$3.prototype.define("bypass",r=>r.bypass(on),{patternified:!0,composable:!0});const controls={},generic_params=[["s","s","sound"],["f","n","The note or sample number to choose for a synth or sampleset"],["f","note","The note or pitch to play a sound or synth with"],["f","accelerate","a pattern of numbers that speed up (or slow down) samples while they play."],["f","gain","a pattern of numbers that specify volume. Values less than 1 make the sound quieter. Values greater than 1 make the sound louder. For the linear equivalent, see @amp@."],["f","amp","like @gain@, but linear."],["f","attack","a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample."],["f","bank","selects sound bank to use"],["f","decay",""],["f","sustain",""],["f","release","a pattern of numbers to specify the release time (in seconds) of an envelope applied to each sample."],["f","hold","a pattern of numbers to specify the hold time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` and `release` are also specified."],["f","bandf","A pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter."],["f","bandq","a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter."],["f","begin","a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample."],["f","end","the same as `begin`, but cuts the end off samples, shortening them; e.g. `0.75` to cut off the last quarter of each sample."],["f","loop","loops the sample (from `begin` to `end`) the specified number of times."],["f","legato","controls the amount of overlap between two adjacent sounds"],["f","crush","bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction)."],["f","coarse","fake-resampling, a pattern of numbers for lowering the sample rate, i.e. 1 for original 2 for half, 3 for a third and so on."],["i","channel","choose the channel the pattern is sent to in superdirt"],["i","cut","In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open."],["f","cutoff","a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter."],["f","hcutoff","a pattern of numbers from 0 to 1. Applies the cutoff frequency of the high-pass filter. Also has alias @hpf@"],["f","hresonance","a pattern of numbers from 0 to 1. Applies the resonance of the high-pass filter. Has alias @hpq@"],["f","resonance","a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter."],["f","djf","DJ filter, below 0.5 is low pass filter, above is high pass filter."],["f","delay","a pattern of numbers from 0 to 1. Sets the level of the delay signal."],["f","delayfeedback","a pattern of numbers from 0 to 1. Sets the amount of delay feedback."],["f","delaytime","a pattern of numbers from 0 to 1. Sets the length of the delay."],["f","lock","A pattern of numbers. Specifies whether delaytime is calculated relative to cps. When set to 1, delaytime is a direct multiple of a cycle."],["f","detune",""],["f","dry","when set to `1` will disable all reverb for this pattern. See `room` and `size` for more information about reverb."],["f","fadeTime","Used when using begin/end or chop/striate and friends, to change the fade out time of the 'grain' envelope."],["f","fadeInTime","As with fadeTime, but controls the fade in time of the grain envelope. Not used if the grain begins at position 0 in the sample."],["f","freq",""],["f","gate",""],["f","leslie",""],["f","lrate",""],["f","lsize",""],["f","degree",""],["f","mtranspose",""],["f","ctranspose",""],["f","harmonic",""],["f","stepsPerOctave",""],["f","octaveR",""],["f","nudge","Nudges events into the future by the specified number of seconds. Negative numbers work up to a point as well (due to internal latency)"],["i","octave",""],["f","offset",""],["i","orbit","a pattern of numbers. An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share hardware output bus offset and global effects, e.g. reverb and delay. The maximum number of orbits is specified in the superdirt startup, numbers higher than maximum will wrap around."],["f","overgain",""],["f","overshape",""],["f","pan","a pattern of numbers between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)"],["f","panspan","a pattern of numbers between -inf and inf, which controls how much multichannel output is fanned out (negative is backwards ordering)"],["f","pansplay","a pattern of numbers between 0.0 and 1.0, which controls the multichannel spread range (multichannel only)"],["f","panwidth","a pattern of numbers between 0.0 and inf, which controls how much each channel is distributed over neighbours (multichannel only)"],["f","panorient","a pattern of numbers between -1.0 and 1.0, which controls the relative position of the centre pan in a pair of adjacent speakers (multichannel only)"],["f","rate","used in SuperDirt softsynths as a control rate or 'speed'"],["f","slide",""],["f","semitone",""],["f","velocity",""],["f","voice",""],["f","room","a pattern of numbers from 0 to 1. Sets the level of reverb."],["f","size","a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb."],["f","roomsize","a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb."],["f","shape","wave shaping distortion, a pattern of numbers from 0 for no distortion up to 1 for loads of distortion."],["f","speed","a pattern of numbers which changes the speed of sample playback, i.e. a cheap way of changing pitch. Negative values will play the sample backwards!"],["s","unit",'used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.'],["f","squiz",""],["f","stutterdepth",""],["f","stuttertime",""],["f","timescale",""],["f","timescalewin",""],["s","vowel","formant filter to make things sound like vowels, a pattern of either `a`, `e`, `i`, `o` or `u`. Use a rest (`~`) for no effect."],["f","waveloss",""],["f","dur",""],["f","expression",""],["f","sustainpedal",""],["f","tremolodepth","Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"],["f","tremolorate","Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"],["f","phaserdepth","Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"],["f","phaserrate","Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"],["f","fshift","frequency shifter"],["f","fshiftnote","frequency shifter"],["f","fshiftphase","frequency shifter"],["f","triode","tube distortion"],["f","krush","shape/bass enhancer"],["f","kcutoff",""],["f","octer","octaver effect"],["f","octersub","octaver effect"],["f","octersubsub","octaver effect"],["f","ring","ring modulation"],["f","ringf","ring modulation"],["f","ringdf","ring modulation"],["f","distort","noisy fuzzy distortion"],["f","freeze","Spectral freeze"],["f","xsdelay",""],["f","tsdelay",""],["f","real","Spectral conform"],["f","imag",""],["f","enhance","Spectral enhance"],["f","partials",""],["f","comb","Spectral comb"],["f","smear","Spectral smear"],["f","scram","Spectral scramble"],["f","binshift","Spectral binshift"],["f","hbrick","High pass sort of spectral filter"],["f","lbrick","Low pass sort of spectral filter"],["f","midichan",""],["f","control",""],["f","ccn",""],["f","ccv",""],["f","polyTouch",""],["f","midibend",""],["f","miditouch",""],["f","ctlNum",""],["f","frameRate",""],["f","frames",""],["f","hours",""],["s","midicmd",""],["f","minutes",""],["f","progNum",""],["f","seconds",""],["f","songPtr",""],["f","uid",""],["f","val",""],["f","cps",""],["f","clip",""]],_name=(r,...t)=>sequence$1(...t).withValue(e=>({[r]:e})),_setter=(r,t)=>function(...e){return e.length?this.set(r(...e)):this.fmap(o=>({[t]:o}))};generic_params.forEach(([r,t,e])=>{controls[t]=(...o)=>_name(t,...o),Pattern$3.prototype[t]=_setter(controls[t],t)});controls.createParam=r=>{const t=(...e)=>_name(r,...e);return Pattern$3.prototype[r]=_setter(t,r),(...e)=>_name(r,...e)};controls.createParams=(...r)=>r.reduce((t,e)=>Object.assign(t,{[e]:createParam(e)}),{});function bjorklund(r,t){for(var e=[],o=[],s=[t],i=r-t,f=0,l=function(g){if(g==-1)e.push(0);else if(g==-2)e.push(1);else{for(var w=0;w1;)o.push(Math.floor(i/s[f])),s.push(i%s[f]),i=s[f],f++;return o.push(i),l(f),e.reverse()}var bjork=function(r,t){return r>t?bjorklund(r,t):bjorklund(t,r)};const euclid=(r,t,e=0)=>{const o=bjork(t,r);return e?rotate(o,-e):o};Pattern$3.prototype.euclid=function(r,t,e=0){return this.struct(euclid(r,t,e))};Pattern$3.prototype.euclidLegato=function(r,t,e=0){const o=euclid(r,t,e),s=o.indexOf(1),i=rotate(o,s).join("").split("1").slice(1).map(f=>[f.length+1,!0]);return this.struct(timeCat$1(...i)).late(fraction(s).div(t))};function steady(r){return new Pattern$3(t=>[new Hap(void 0,t.span,r)])}const signal=r=>{const t=e=>[new Hap(void 0,e.span,r(e.span.midpoint()))];return new Pattern$3(t)},isaw=signal(r=>1-r%1),isaw2=isaw._toBipolar(),saw=signal(r=>r%1),saw2=saw._toBipolar(),sine2=signal(r=>Math.sin(Math.PI*2*r)),sine=sine2._fromBipolar(),cosine=sine._early(fraction(1).div(4)),cosine2=sine2._early(fraction(1).div(4)),square=signal(r=>Math.floor(r*2%2)),square2=square._toBipolar(),tri=fastcat(isaw,saw),tri2=fastcat(isaw2,saw2),time=signal(id),xorwise=r=>{const t=r<<13^r,e=t>>17^t;return e<<5^e},_frac=r=>r-Math.trunc(r),timeToIntSeed=r=>xorwise(Math.trunc(_frac(r/300)*536870912)),intSeedToRand=r=>r%536870912/536870912,timeToRand=r=>Math.abs(intSeedToRand(timeToIntSeed(r))),rand=signal(timeToRand),rand2=rand._toBipolar(),_brandBy=r=>rand.fmap(t=>treify$2(r).fmap(_brandBy).innerJoin(),brand=_brandBy(.5),_irand=r=>rand.fmap(t=>Math.trunc(t*r)),irand=r=>reify$2(r).fmap(_irand).innerJoin(),__chooseWith=(r,t)=>(t=t.map(reify$2),t.length==0?silence$1:r.range(0,t.length).fmap(e=>t[Math.floor(e)])),chooseWith=(r,t)=>__chooseWith(r,t).outerJoin(),chooseInWith=(r,t)=>__chooseWith(r,t).innerJoin(),choose=(...r)=>chooseWith(rand,r);Pattern$3.prototype.choose=function(...r){return chooseWith(this,r)};Pattern$3.prototype.choose2=function(...r){return chooseWith(this._fromBipolar(),r)};const chooseCycles=(...r)=>chooseInWith(rand.segment(1),r),randcat=chooseCycles,_wchooseWith=function(r,...t){const e=t.map(l=>reify$2(l[0])),o=[];let s=0;for(const l of t)s+=l[1],o.push(s);const i=s,f=function(l){const g=l*i;return e[o.findIndex(w=>w>g,o)]};return r.fmap(f)},wchooseWith=(...r)=>_wchooseWith(...r).outerJoin(),wchoose=(...r)=>wchooseWith(rand,...r),wchooseCycles=(...r)=>_wchooseWith(rand,...r).innerJoin(),perlinWith=r=>{const t=r.fmap(Math.floor),e=r.fmap(i=>Math.floor(i)+1),o=i=>6*i**5-15*i**4+10*i**3,s=i=>f=>l=>f+o(i)*(l-f);return r.sub(t).fmap(s).appBoth(t.fmap(timeToRand)).appBoth(e.fmap(timeToRand))},perlin=perlinWith(time.fmap(r=>Number(r)));Pattern$3.prototype._degradeByWith=function(r,t){return this.fmap(e=>o=>e).appLeft(r._filterValues(e=>e>t))};Pattern$3.prototype._degradeBy=function(r){return this._degradeByWith(rand,r)};Pattern$3.prototype.degrade=function(){return this._degradeBy(.5)};Pattern$3.prototype._undegradeBy=function(r){return this._degradeByWith(rand.fmap(t=>1-t),r)};Pattern$3.prototype.undegrade=function(){return this._undegradeBy(.5)};Pattern$3.prototype._sometimesBy=function(r,t){return stack$1(this._degradeBy(r),t(this._undegradeBy(1-r)))};Pattern$3.prototype.sometimesBy=function(r,t){const e=this;return reify$2(r).fmap(o=>e._sometimesBy(o,t)).innerJoin()};Pattern$3.prototype._sometimesByPre=function(r,t){return stack$1(this._degradeBy(r),t(this).undegradeBy(1-r))};Pattern$3.prototype.sometimesByPre=function(r,t){const e=this;return reify$2(r).fmap(o=>e._sometimesByPre(o,t)).innerJoin()};Pattern$3.prototype.sometimes=function(r){return this._sometimesBy(.5,r)};Pattern$3.prototype.sometimesPre=function(r){return this._sometimesByPre(.5,r)};Pattern$3.prototype._someCyclesBy=function(r,t){return stack$1(this._degradeByWith(rand._segment(1),r),t(this._degradeByWith(rand.fmap(e=>1-e)._segment(1),1-r)))};Pattern$3.prototype.someCyclesBy=function(r,t){const e=this;return reify$2(r).fmap(o=>e._someCyclesBy(o,t)).innerJoin()};Pattern$3.prototype.someCycles=function(r){return this._someCyclesBy(.5,r)};Pattern$3.prototype.often=function(r){return this.sometimesBy(.75,r)};Pattern$3.prototype.rarely=function(r){return this.sometimesBy(.25,r)};Pattern$3.prototype.almostNever=function(r){return this.sometimesBy(.1,r)};Pattern$3.prototype.almostAlways=function(r){return this.sometimesBy(.9,r)};Pattern$3.prototype.never=function(r){return this};Pattern$3.prototype.always=function(r){return r(this)};Pattern$3.prototype.patternified.push("degradeBy","undegradeBy");let synth;try{synth=window==null?void 0:window.speechSynthesis}catch{console.warn("cannot use window: not in browser?")}let allVoices=synth==null?void 0:synth.getVoices();function speak(r,t,e){synth.cancel();const o=new SpeechSynthesisUtterance(r);o.lang=t,allVoices=synth.getVoices();const s=allVoices.filter(i=>i.lang.includes(t));typeof e=="number"?o.voice=s[e%s.length]:typeof e=="string"&&(o.voice=s.find(i=>i.name===i)),speechSynthesis.speak(o)}Pattern$3.prototype._speak=function(r,t){return this._withHap(e=>{const o=(s,i)=>{speak(i.value,r,t)};return e.setContext({...e.context,onTrigger:o})})};Pattern$3.prototype.speak=function(r,t){return patternify2(Pattern$3.prototype._speak)(reify(r),reify(t),this)};const{isPattern,Pattern:Pattern$2}=strudel;let scoped=!1;const evalScope=async(...r)=>{scoped&&console.warn("evalScope was called more than once."),scoped=!0;const t=await Promise.allSettled(r),e=t.filter(o=>o.status==="fulfilled").map(o=>o.value);t.forEach((o,s)=>{o.status==="rejected"&&console.warn(`evalScope: module with index ${s} could not be loaded:`,o.reason)}),Object.assign(globalThis,...e,Pattern$2.prototype.bootstrap())};function safeEval(r,t={}){const{wrapExpression:e=!0,wrapAsync:o=!0}=t;e&&(r=`{${r}}`),o&&(r=`(async ()=>${r})()`);const s=`"use strict";return (${r})`;return Function(s)()}const evaluate$1=async(r,t)=>{scoped||await evalScope(),t&&(r=t(r));let o=await safeEval(r,{wrapExpression:!!t});if(!isPattern(o)){console.log("evaluated",o);const s=`got "${typeof o}" instead of pattern`;throw new Error(s+(typeof o=="function"?", did you forget to call a function?":"."))}return{mode:"javascript",pattern:o}};function createClock(r,t,e=.05,o=.1,s=.1){let i=0,f=0,l=10**4,g=.01;const w=b=>e=b(e);s=s||o/2;const y=()=>{const b=r(),I=b+o+s;for(f===0&&(f=b+g);f=b&&t(f,e,i),f{y(),$=setInterval(y,o*1e3)},C=()=>clearInterval($);return{setDuration:w,start:S,stop:()=>{i=0,f=0,C()},pause:()=>C(),duration:e,getPhase:()=>f}}class Cyclist{constructor({interval:t,onTrigger:e,onError:o,getTime:s,latency:i=.1}){K(this,"worker");K(this,"pattern");K(this,"started",!1);K(this,"cps",1);K(this,"getTime");K(this,"phase",0);this.getTime=s;const f=l=>Math.round(l*1e3)/1e3;this.clock=createClock(s,(l,g,w)=>{w===0&&(this.origin=l);const y=f(l-this.origin);this.phase=y-i;const $=f(y+g),S=s();try{this.pattern.queryArc(y,$).forEach(p=>{if(p.part.begin.equals(p.whole.begin)){const d=p.whole.begin+this.origin-S+i,m=p.duration*1;e==null||e(p,d,m)}})}catch(C){console.warn("scheduler error",C),o==null||o(C)}},t)}getPhase(){return this.phase}start(){if(!this.pattern)throw new Error("Scheduler: no pattern set! call .setPattern first.");this.clock.start(),this.started=!0}pause(){this.clock.stop(),delete this.origin,this.started=!1}stop(){delete this.origin,this.clock.stop(),this.started=!1}setPattern(t,e=!1){this.pattern=t,e&&!this.started&&this.start()}setCps(t=1){this.cps=t}log(t,e,o){const s=o.filter(i=>i.hasOnset());console.log(`${t.toFixed(4)} - ${e.toFixed(4)} ${Array(s.length).fill("I").join("")}`)}}function repl({interval:r,defaultOutput:t,onSchedulerError:e,onEvalError:o,onEval:s,getTime:i,transpiler:f}){const l=new Cyclist({interval:r,onTrigger:t,onError:e,getTime:i});return{scheduler:l,evaluate:async w=>{if(!w)throw new Error("no code to evaluate");try{const{pattern:y}=await evaluate$1(w,f);l.setPattern(y,!0),s==null||s({pattern:y,code:w})}catch(y){console.warn(`eval error: ${y.message}`),o==null||o(y)}}}}const gist=(route,cache=!0)=>fetch(`https://gist.githubusercontent.com/${route}?cachebust=${cache?"":Date.now()}`).then(r=>r.text()).then(code=>eval(code));console.log("%c // \u{1F300} @strudel.cycles/core loaded \u{1F300}","background-color: black;color:white;padding:4px;border-radius:15px");globalThis._strudelLoaded&&console.warn(`@strudel.cycles/core was loaded more than once... -This might happen when you have multiple versions of strudel installed. -Please check with "npm ls @strudel.cycles/core".`);globalThis._strudelLoaded=!0;function peg$subclass(r,t){function e(){this.constructor=r}e.prototype=t.prototype,r.prototype=new e}function peg$SyntaxError(r,t,e,o){var s=Error.call(this,r);return Object.setPrototypeOf&&Object.setPrototypeOf(s,peg$SyntaxError.prototype),s.expected=t,s.found=e,s.location=o,s.name="SyntaxError",s}peg$subclass(peg$SyntaxError,Error);function peg$padEnd(r,t,e){return e=e||" ",r.length>t?r:(t-=r.length,e+=e.repeat(t),r+e.slice(0,t))}peg$SyntaxError.prototype.format=function(r){var t="Error: "+this.message;if(this.location){var e=null,o;for(o=0;o `+i+` -`+l+` | -`+s.line+" | "+g+` -`+l+" | "+peg$padEnd("",s.column-1," ")+peg$padEnd("",y,"^")}else t+=` - at `+i}return t};peg$SyntaxError.buildMessage=function(r,t){var e={literal:function(w){return'"'+s(w.text)+'"'},class:function(w){var y=w.parts.map(function($){return Array.isArray($)?i($[0])+"-"+i($[1]):i($)});return"["+(w.inverted?"^":"")+y.join("")+"]"},any:function(){return"any character"},end:function(){return"end of input"},other:function(w){return w.description}};function o(w){return w.charCodeAt(0).toString(16).toUpperCase()}function s(w){return w.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\0/g,"\\0").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/[\x00-\x0F]/g,function(y){return"\\x0"+o(y)}).replace(/[\x10-\x1F\x7F-\x9F]/g,function(y){return"\\x"+o(y)})}function i(w){return w.replace(/\\/g,"\\\\").replace(/\]/g,"\\]").replace(/\^/g,"\\^").replace(/-/g,"\\-").replace(/\0/g,"\\0").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/[\x00-\x0F]/g,function(y){return"\\x0"+o(y)}).replace(/[\x10-\x1F\x7F-\x9F]/g,function(y){return"\\x"+o(y)})}function f(w){return e[w.type](w)}function l(w){var y=w.map(f),$,S;if(y.sort(),y.length>0){for($=1,S=1;$",D="@",W="!",P="(",B=")",z="/",O="*",Z="%",L="?",fe="struct",we="target",be="euclid",te="slow",le="rotL",pe="rotR",he="fast",ve="scale",de="//",$e="cat",F="$",Q="setcps",ge="setbpm",me="hush",M=/^[1-9]/,U=/^[eE]/,ye=/^[0-9]/,J=/^[ \n\r\t]/,ee=/^[0-9a-zA-Z~]/,R=/^[^\n]/,X=He("number"),re=x(".",!1),Pe=ne([["1","9"]],!1,!1),Be=ne(["e","E"],!1,!1),Te=x("-",!1),Ze=x("+",!1),Le=x("0",!1),Je=ne([["0","9"]],!1,!1),Ke=He("whitespace"),Ge=ne([" ",` -`,"\r"," "],!1,!1),Ye=x(",",!1),Qe=x("|",!1),Ue=x('"',!1),et=x("'",!1),tt=ne([["0","9"],["a","z"],["A","Z"],"~"],!1,!1),rt=x("#",!1),nt=x("^",!1),st=x("_",!1),ot=x(":",!1),Re=x("[",!1),je=x("]",!1),it=x("<",!1),at=x(">",!1),ct=x("@",!1),ut=x("!",!1),ft=x("(",!1),lt=x(")",!1),pt=x("/",!1),ht=x("*",!1),dt=x("%",!1),gt=x("?",!1),mt=x("struct",!1),yt=x("target",!1),wt=x("euclid",!1),bt=x("slow",!1),vt=x("rotL",!1),$t=x("rotR",!1),_t=x("fast",!1),Ct=x("scale",!1),At=x("//",!1),Fe=ne([` -`],!0,!1),It=x("cat",!1),Pt=x("$",!1),Bt=x("setcps",!1),St=x("setbpm",!1),kt=x("hush",!1),Nt=function(){return parseFloat(ur())},xt=function(n){return n.join("")},qt=function(n){return n},Tt=function(n){return n.arguments_.alignment="t",n},Gt=function(n){return{weight:n}},Rt=function(n){return{replicate:n}},jt=function(n,c,u){return{operator:{type_:"bjorklund",arguments_:{pulse:n,step:c,rotation:u||0}}}},Ft=function(n){return{operator:{type_:"stretch",arguments_:{amount:n}}}},Mt=function(n){return{operator:{type_:"stretch",arguments_:{amount:"1/"+n}}}},Ht=function(n){return{operator:{type_:"fixed-step",arguments_:{amount:n}}}},Ot=function(n){return{operator:{type_:"degradeBy",arguments_:{amount:n||.5}}}},Wt=function(n,c){return new en(n,c)},Vt=function(n){return new xe(n,"h")},zt=function(n){return{alignment:"v",list:n}},Xt=function(n){return{alignment:"r",list:n}},Et=function(n,c){return c&&c.list.length>0?new xe([n,...c.list],c.alignment):n},Dt=function(n){return n},Zt=function(n){return{name:"struct",args:{sequence:n}}},Lt=function(n){return{name:"target",args:{name:n}}},Jt=function(n,c,u){return{name:"bjorklund",args:{pulse:parseInt(n),step:parseInt(c)}}},Kt=function(n){return{name:"stretch",args:{amount:n}}},Yt=function(n){return{name:"shift",args:{amount:"-"+n}}},Qt=function(n){return{name:"shift",args:{amount:n}}},Ut=function(n){return{name:"stretch",args:{amount:"1/"+n}}},er=function(n){return{name:"scale",args:{scale:n.join("")}}},Me=function(n,c){return c},tr=function(n,c){return c.unshift(n),new xe(c,"t")},rr=function(n){return n},nr=function(n,c){return new Ur(n.name,n.args,c)},sr=function(n){return n},or=function(n){return n},ir=function(n){return new qe("setcps",{value:n})},ar=function(n){return new qe("setcps",{value:n/120/2})},cr=function(){return new qe("hush")},a=0,q=0,_e=[{line:1,column:1}],E=0,Se=[],_=0,Ce;if("startRule"in t){if(!(t.startRule in s))throw new Error(`Can't start parsing from rule "`+t.startRule+'".');i=s[t.startRule]}function ur(){return r.substring(q,a)}function fr(){return ke(q,a)}function x(n,c){return{type:"literal",text:n,ignoreCase:c}}function ne(n,c,u){return{type:"class",parts:n,inverted:c,ignoreCase:u}}function lr(){return{type:"end"}}function He(n){return{type:"other",description:n}}function Oe(n){var c=_e[n],u;if(c)return c;for(u=n-1;!_e[u];)u--;for(c=_e[u],c={line:c.line,column:c.column};uE&&(E=a,Se=[]),Se.push(n))}function hr(n,c,u){return new peg$SyntaxError(peg$SyntaxError.buildMessage(n,c),n,c,u)}function We(){var n;return n=Qr(),n}function H(){var n,c;return _++,n=a,Ve(),c=Ae(),c!==e?(wr(),yr(),q=n,n=Nt()):(a=n,n=e),_--,n===e&&_===0&&A(X),n}function dr(){var n;return r.charCodeAt(a)===46?(n=f,a++):(n=e,_===0&&A(re)),n}function gr(){var n;return M.test(r.charAt(a))?(n=r.charAt(a),a++):(n=e,_===0&&A(Pe)),n}function mr(){var n;return U.test(r.charAt(a))?(n=r.charAt(a),a++):(n=e,_===0&&A(Be)),n}function yr(){var n,c,u,v,k;if(n=a,c=mr(),c!==e){if(u=Ve(),u===e&&(u=br()),u===e&&(u=null),v=[],k=se(),k!==e)for(;k!==e;)v.push(k),k=se();else v=e;v!==e?(c=[c,u,v],n=c):(a=n,n=e)}else a=n,n=e;return n}function wr(){var n,c,u,v;if(n=a,c=dr(),c!==e){if(u=[],v=se(),v!==e)for(;v!==e;)u.push(v),v=se();else u=e;u!==e?(c=[c,u],n=c):(a=n,n=e)}else a=n,n=e;return n}function Ae(){var n,c,u,v;if(n=vr(),n===e)if(n=a,c=gr(),c!==e){for(u=[],v=se();v!==e;)u.push(v),v=se();c=[c,u],n=c}else a=n,n=e;return n}function Ve(){var n;return r.charCodeAt(a)===45?(n=l,a++):(n=e,_===0&&A(Te)),n}function br(){var n;return r.charCodeAt(a)===43?(n=g,a++):(n=e,_===0&&A(Ze)),n}function vr(){var n;return r.charCodeAt(a)===48?(n=w,a++):(n=e,_===0&&A(Le)),n}function se(){var n;return ye.test(r.charAt(a))?(n=r.charAt(a),a++):(n=e,_===0&&A(Je)),n}function N(){var n,c;for(_++,n=[],J.test(r.charAt(a))?(c=r.charAt(a),a++):(c=e,_===0&&A(Ge));c!==e;)n.push(c),J.test(r.charAt(a))?(c=r.charAt(a),a++):(c=e,_===0&&A(Ge));return _--,c=e,_===0&&A(Ke),n}function oe(){var n,c,u,v;return n=a,c=N(),r.charCodeAt(a)===44?(u=y,a++):(u=e,_===0&&A(Ye)),u!==e?(v=N(),c=[c,u,v],n=c):(a=n,n=e),n}function ze(){var n,c,u,v;return n=a,c=N(),r.charCodeAt(a)===124?(u=$,a++):(u=e,_===0&&A(Qe)),u!==e?(v=N(),c=[c,u,v],n=c):(a=n,n=e),n}function ie(){var n;return r.charCodeAt(a)===34?(n=S,a++):(n=e,_===0&&A(Ue)),n===e&&(r.charCodeAt(a)===39?(n=C,a++):(n=e,_===0&&A(et))),n}function Ie(){var n;return ee.test(r.charAt(a))?(n=r.charAt(a),a++):(n=e,_===0&&A(tt)),n===e&&(r.charCodeAt(a)===45?(n=l,a++):(n=e,_===0&&A(Te)),n===e&&(r.charCodeAt(a)===35?(n=p,a++):(n=e,_===0&&A(rt)),n===e&&(r.charCodeAt(a)===46?(n=f,a++):(n=e,_===0&&A(re)),n===e&&(r.charCodeAt(a)===94?(n=d,a++):(n=e,_===0&&A(nt)),n===e&&(r.charCodeAt(a)===95?(n=m,a++):(n=e,_===0&&A(st)),n===e&&(r.charCodeAt(a)===58?(n=b,a++):(n=e,_===0&&A(ot)))))))),n}function Xe(){var n,c,u;if(n=a,N(),c=[],u=Ie(),u!==e)for(;u!==e;)c.push(u),u=Ie();else c=e;return c!==e?(u=N(),q=n,n=xt(c)):(a=n,n=e),n}function $r(){var n,c,u,v;return n=a,N(),r.charCodeAt(a)===91?(c=I,a++):(c=e,_===0&&A(Re)),c!==e?(N(),u=De(),u!==e?(N(),r.charCodeAt(a)===93?(v=T,a++):(v=e,_===0&&A(je)),v!==e?(N(),q=n,n=qt(u)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e),n}function _r(){var n,c,u,v;return n=a,N(),r.charCodeAt(a)===60?(c=G,a++):(c=e,_===0&&A(it)),c!==e?(N(),u=ae(),u!==e?(N(),r.charCodeAt(a)===62?(v=Y,a++):(v=e,_===0&&A(at)),v!==e?(N(),q=n,n=Tt(u)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e),n}function Cr(){var n;return n=Xe(),n===e&&(n=$r(),n===e&&(n=_r())),n}function Ar(){var n;return n=Ir(),n===e&&(n=Br(),n===e&&(n=Sr(),n===e&&(n=kr(),n===e&&(n=Nr(),n===e&&(n=Pr(),n===e&&(n=xr())))))),n}function Ir(){var n,c,u;return n=a,r.charCodeAt(a)===64?(c=D,a++):(c=e,_===0&&A(ct)),c!==e?(u=H(),u!==e?(q=n,n=Gt(u)):(a=n,n=e)):(a=n,n=e),n}function Pr(){var n,c,u;return n=a,r.charCodeAt(a)===33?(c=W,a++):(c=e,_===0&&A(ut)),c!==e?(u=H(),u!==e?(q=n,n=Rt(u)):(a=n,n=e)):(a=n,n=e),n}function Br(){var n,c,u,v,k,j,V;return n=a,r.charCodeAt(a)===40?(c=P,a++):(c=e,_===0&&A(ft)),c!==e?(N(),u=H(),u!==e?(N(),v=oe(),v!==e?(N(),k=H(),k!==e?(N(),oe(),N(),j=H(),j===e&&(j=null),N(),r.charCodeAt(a)===41?(V=B,a++):(V=e,_===0&&A(lt)),V!==e?(q=n,n=jt(u,k,j)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e),n}function Sr(){var n,c,u;return n=a,r.charCodeAt(a)===47?(c=z,a++):(c=e,_===0&&A(pt)),c!==e?(u=H(),u!==e?(q=n,n=Ft(u)):(a=n,n=e)):(a=n,n=e),n}function kr(){var n,c,u;return n=a,r.charCodeAt(a)===42?(c=O,a++):(c=e,_===0&&A(ht)),c!==e?(u=H(),u!==e?(q=n,n=Mt(u)):(a=n,n=e)):(a=n,n=e),n}function Nr(){var n,c,u;return n=a,r.charCodeAt(a)===37?(c=Z,a++):(c=e,_===0&&A(dt)),c!==e?(u=H(),u!==e?(q=n,n=Ht(u)):(a=n,n=e)):(a=n,n=e),n}function xr(){var n,c,u;return n=a,r.charCodeAt(a)===63?(c=L,a++):(c=e,_===0&&A(gt)),c!==e?(u=H(),u===e&&(u=null),q=n,n=Ot(u)):(a=n,n=e),n}function Ee(){var n,c,u;return n=a,c=Cr(),c!==e?(u=Ar(),u===e&&(u=null),q=n,n=Wt(c,u)):(a=n,n=e),n}function ae(){var n,c,u;if(n=a,c=[],u=Ee(),u!==e)for(;u!==e;)c.push(u),u=Ee();else c=e;return c!==e&&(q=n,c=Vt(c)),n=c,n}function qr(){var n,c,u,v,k;if(n=a,c=[],u=a,v=oe(),v!==e?(k=ae(),k!==e?u=k:(a=u,u=e)):(a=u,u=e),u!==e)for(;u!==e;)c.push(u),u=a,v=oe(),v!==e?(k=ae(),k!==e?u=k:(a=u,u=e)):(a=u,u=e);else c=e;return c!==e&&(q=n,c=zt(c)),n=c,n}function Tr(){var n,c,u,v,k;if(n=a,c=[],u=a,v=ze(),v!==e?(k=ae(),k!==e?u=k:(a=u,u=e)):(a=u,u=e),u!==e)for(;u!==e;)c.push(u),u=a,v=ze(),v!==e?(k=ae(),k!==e?u=k:(a=u,u=e)):(a=u,u=e);else c=e;return c!==e&&(q=n,c=Xt(c)),n=c,n}function De(){var n,c,u;return n=a,c=ae(),c!==e?(u=qr(),u===e&&(u=Tr()),u===e&&(u=null),q=n,n=Et(c,u)):(a=n,n=e),n}function Gr(){var n,c,u,v;return n=a,N(),c=ie(),c!==e?(u=De(),u!==e?(v=ie(),v!==e?(q=n,n=Dt(u)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e),n}function Rr(){var n;return n=zr(),n===e&&(n=Hr(),n===e&&(n=Vr(),n===e&&(n=Fr(),n===e&&(n=Mr(),n===e&&(n=jr(),n===e&&(n=Wr(),n===e&&(n=Or()))))))),n}function jr(){var n,c,u;return n=a,r.substr(a,6)===fe?(c=fe,a+=6):(c=e,_===0&&A(mt)),c!==e?(N(),u=ce(),u!==e?(q=n,n=Zt(u)):(a=n,n=e)):(a=n,n=e),n}function Fr(){var n,c,u,v,k;return n=a,r.substr(a,6)===we?(c=we,a+=6):(c=e,_===0&&A(yt)),c!==e?(N(),u=ie(),u!==e?(v=Xe(),v!==e?(k=ie(),k!==e?(q=n,n=Lt(v)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e),n}function Mr(){var n,c,u,v;return n=a,r.substr(a,6)===be?(c=be,a+=6):(c=e,_===0&&A(wt)),c!==e?(N(),u=Ae(),u!==e?(N(),v=Ae(),v!==e?(N(),Ae(),q=n,n=Jt(u,v)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e),n}function Hr(){var n,c,u;return n=a,r.substr(a,4)===te?(c=te,a+=4):(c=e,_===0&&A(bt)),c!==e?(N(),u=H(),u!==e?(q=n,n=Kt(u)):(a=n,n=e)):(a=n,n=e),n}function Or(){var n,c,u;return n=a,r.substr(a,4)===le?(c=le,a+=4):(c=e,_===0&&A(vt)),c!==e?(N(),u=H(),u!==e?(q=n,n=Yt(u)):(a=n,n=e)):(a=n,n=e),n}function Wr(){var n,c,u;return n=a,r.substr(a,4)===pe?(c=pe,a+=4):(c=e,_===0&&A($t)),c!==e?(N(),u=H(),u!==e?(q=n,n=Qt(u)):(a=n,n=e)):(a=n,n=e),n}function Vr(){var n,c,u;return n=a,r.substr(a,4)===he?(c=he,a+=4):(c=e,_===0&&A(_t)),c!==e?(N(),u=H(),u!==e?(q=n,n=Ut(u)):(a=n,n=e)):(a=n,n=e),n}function zr(){var n,c,u,v,k;if(n=a,r.substr(a,5)===ve?(c=ve,a+=5):(c=e,_===0&&A(Ct)),c!==e)if(N(),u=ie(),u!==e){if(v=[],k=Ie(),k!==e)for(;k!==e;)v.push(k),k=Ie();else v=e;v!==e?(k=ie(),k!==e?(q=n,n=er(v)):(a=n,n=e)):(a=n,n=e)}else a=n,n=e;else a=n,n=e;return n}function Ne(){var n,c,u,v;if(n=a,r.substr(a,2)===de?(c=de,a+=2):(c=e,_===0&&A(At)),c!==e){for(u=[],R.test(r.charAt(a))?(v=r.charAt(a),a++):(v=e,_===0&&A(Fe));v!==e;)u.push(v),R.test(r.charAt(a))?(v=r.charAt(a),a++):(v=e,_===0&&A(Fe));c=[c,u],n=c}else a=n,n=e;return n}function Xr(){var n,c,u,v,k,j,V,ue;if(n=a,r.substr(a,3)===$e?(c=$e,a+=3):(c=e,_===0&&A(It)),c!==e)if(N(),r.charCodeAt(a)===91?(u=I,a++):(u=e,_===0&&A(Re)),u!==e)if(N(),v=ce(),v!==e){for(k=[],j=a,V=oe(),V!==e?(ue=ce(),ue!==e?(q=j,j=Me(v,ue)):(a=j,j=e)):(a=j,j=e);j!==e;)k.push(j),j=a,V=oe(),V!==e?(ue=ce(),ue!==e?(q=j,j=Me(v,ue)):(a=j,j=e)):(a=j,j=e);j=N(),r.charCodeAt(a)===93?(V=T,a++):(V=e,_===0&&A(je)),V!==e?(q=n,n=tr(v,k)):(a=n,n=e)}else a=n,n=e;else a=n,n=e;else a=n,n=e;return n}function Er(){var n;return n=Xr(),n===e&&(n=Gr()),n}function ce(){var n,c,u,v,k;if(n=a,c=Er(),c!==e){for(N(),u=[],v=Ne();v!==e;)u.push(v),v=Ne();q=n,n=rr(c)}else a=n,n=e;return n===e&&(n=a,c=Rr(),c!==e?(N(),r.charCodeAt(a)===36?(u=F,a++):(u=e,_===0&&A(Pt)),u!==e?(v=N(),k=ce(),k!==e?(q=n,n=nr(c,k)):(a=n,n=e)):(a=n,n=e)):(a=n,n=e)),n}function Dr(){var n,c;return n=a,c=ce(),c!==e&&(q=n,c=sr(c)),n=c,n===e&&(n=Ne()),n}function Zr(){var n;return n=Dr(),n}function Lr(){var n,c;return n=a,N(),c=Jr(),c===e&&(c=Kr(),c===e&&(c=Yr())),c!==e?(N(),q=n,n=or(c)):(a=n,n=e),n}function Jr(){var n,c,u;return n=a,r.substr(a,6)===Q?(c=Q,a+=6):(c=e,_===0&&A(Bt)),c!==e?(N(),u=H(),u!==e?(q=n,n=ir(u)):(a=n,n=e)):(a=n,n=e),n}function Kr(){var n,c,u;return n=a,r.substr(a,6)===ge?(c=ge,a+=6):(c=e,_===0&&A(St)),c!==e?(N(),u=H(),u!==e?(q=n,n=ar(u)):(a=n,n=e)):(a=n,n=e),n}function Yr(){var n,c;return n=a,r.substr(a,4)===me?(c=me,a+=4):(c=e,_===0&&A(kt)),c!==e&&(q=n,c=cr()),n=c,n}function Qr(){var n;return n=Zr(),n===e&&(n=Lr()),n}var xe=function(n,c){this.type_="pattern",this.arguments_={alignment:c},this.source_=n},Ur=function(n,c,u){this.type_=n,this.arguments_=c,this.source_=u},en=function(n,c){this.type_="element",this.source_=n,this.options_=c,this.location_=fr()},qe=function(n,c){this.type_="command",this.name_=n,this.options_=c};if(Ce=i(),Ce!==e&&a===r.length)return Ce;throw Ce!==e&&a(t,e)=>{const s=r.source_[e].options_,i=s==null?void 0:s.operator;if(i){switch(i.type_){case"stretch":const l=Fraction(i.arguments_.amount).inverse();return reify$1(t).fast(l);case"bjorklund":return t.euclid(i.arguments_.pulse,i.arguments_.step,i.arguments_.rotation);case"degradeBy":return reify$1(t)._degradeByWith(rand.early(randOffset*_nextSeed()).segment(1),i.arguments_.amount)}console.warn(`operator "${i.type_}" not implemented`)}if(s!=null&&s.weight)return t;const f=Object.keys(s||{}).filter(l=>l!=="operator");return f.length&&console.warn(`option${f.length>1?"s":""} ${f.map(l=>`"${l}"`).join(", ")} not implemented`),t};function resolveReplications(r){r.source_=r.source_.map(t=>{const{replicate:e,...o}=t.options_||{};return e?{...t,options_:{...o,weight:e},source_:{type_:"pattern",arguments_:{alignment:"h"},source_:[{type_:"element",source_:t.source_,location_:t.location_,options_:{operator:{type_:"stretch",arguments_:{amount:Fraction(e).inverse().toString()}}}}]}}:t})}function patternifyAST(r){switch(r.type_){case"pattern":resolveReplications(r);const t=r.source_.map(patternifyAST).map(applyOptions(r)),e=r.arguments_.alignment;if(e==="v")return stack(...t);if(e==="r")return chooseInWith(rand.early(randOffset*_nextSeed()).segment(1),t);const o=r.source_.some(s=>{var i;return!!((i=s.options_)!=null&&i.weight)});if(!o&&e==="t")return slowcat(...t);if(o){const s=timeCat(...r.source_.map((i,f)=>{var l;return[((l=i.options_)==null?void 0:l.weight)||1,t[f]]}));if(e==="t"){const i=r.source_.reduce((f,l)=>{var g;return f+(((g=l.options_)==null?void 0:g.weight)||1)},0);return s._slow(i)}return s}return sequence(...t);case"element":if(r.source_==="~")return silence;if(typeof r.source_!="object"){if(!r.location_)return console.warn("no location for",r),r.source_;const{start:s,end:i}=r.location_,f=isNaN(Number(r.source_))?r.source_:Number(r.source_);return pure(f).withLocation([s.line,s.column,s.offset],[i.line,i.column,i.offset])}return patternifyAST(r.source_);case"stretch":return patternifyAST(r.source_).slow(r.arguments_.amount);default:return console.warn(`node type "${r.type_}" not implemented -> returning silence`),silence}}const mini=(...r)=>{const t=r.map(e=>{const o=peg$parse(`"${e}"`);return patternifyAST(o)});return sequence(...t)},h=r=>{const t=peg$parse(r);return patternifyAST(t)};Pattern$1.prototype.define("mini",mini,{composable:!0});Pattern$1.prototype.define("m",mini,{composable:!0});Pattern$1.prototype.define("h",h,{composable:!0});function minify(r){return typeof r=="string"?mini(r):reify$1(r)}if(typeof DelayNode<"u"){class r extends DelayNode{constructor(e,o,s,i){super(e),o=Math.abs(o),this.delayTime.value=s;const f=e.createGain();f.gain.value=Math.min(Math.abs(i),.995),this.feedback=f.gain;const l=e.createGain();return l.gain.value=o,this.delayGain=l,this.connect(f),this.connect(l),f.connect(this),this.connect=g=>l.connect(g),this}start(e){this.delayGain.gain.setValueAtTime(this.delayGain.gain.value,e+this.delayTime.value)}}AudioContext.prototype.createFeedbackDelay=function(t,e,o){return new r(this,t,e,o)}}typeof AudioContext<"u"&&(AudioContext.prototype.impulseResponse=function(r,t=1){const e=this.sampleRate*r,o=this.createBuffer(t,e,this.sampleRate),s=o.getChannelData(0);for(let i=0;i(t.buffer=this.impulseResponse(e),t.duration=r,t),t.setDuration(r),t});const bufferCache={},loadCache={},getCachedBuffer=r=>bufferCache[r],loadBuffer=(r,t)=>(loadCache[r]||(loadCache[r]=fetch(r).then(e=>e.arrayBuffer()).then(async e=>{const o=await t.decodeAudioData(e);return bufferCache[r]=o,o})),loadCache[r]);function reverseBuffer(r){const t=getAudioContext(),e=t.createBuffer(r.numberOfChannels,r.length,t.sampleRate);for(let o=0;obufferCache[r],githubCache={};let sampleCache={current:void 0};const loadGithubSamples=async(r,t)=>{const e="loadGithubSamples "+r,o=localStorage.getItem(e);if(o&&(console.log("[sampler]: loaded sample list from localstorage",r),githubCache[r]=JSON.parse(o)),githubCache[r])return sampleCache.current=githubCache[r],githubCache[r];console.log("[sampler]: fetching sample list from github",r);try{const[s,i,...f]=r.split("/"),l=`https://api.github.com/repos/${s}/${i}/contents`,g=await fetch(`${l}/${f.join("/")}`).then(w=>w.json());githubCache[r]=(await Promise.all(g.map(async({name:w,path:y})=>({name:w,content:await fetch(`${l}/${y}`).then($=>$.json()).catch($=>{console.error("could not load path",$)})})))).filter(({content:w})=>!!w).reduce((w,{name:y,content:$})=>({...w,[(t==null?void 0:t(y))||y]:$.map(({download_url:S})=>S)}),{})}catch(s){console.error("[sampler]: failed to fetch sample list from github",s);return}return sampleCache.current=githubCache[r],localStorage.setItem(e,JSON.stringify(sampleCache.current)),console.log("[sampler]: loaded samples:",sampleCache.current),githubCache[r]},samples=async(r,t=r._base||"")=>{if(typeof r=="string"){if(r.startsWith("github:")){const[o,s]=r.split("github:");r=`https://raw.githubusercontent.com/${s}/strudel.json`}if(typeof fetch!="function")return;const e=r.split("/").slice(0,-1).join("/");return typeof fetch>"u"?void 0:fetch(r).then(o=>o.json()).then(o=>samples(o,t||o._base||e)).catch(o=>{throw console.error(o),new Error(`error loading "${r}"`)})}sampleCache.current={...sampleCache.current,...Object.fromEntries(Object.entries(r).map(([e,o])=>{if(typeof o=="string"&&(o=[o]),typeof o!="object")throw new Error("wrong sample map format for "+e);t=o._base||t;const s=i=>(t+i).replace("github:","https://raw.githubusercontent.com/");return Array.isArray(o)?[e,o.map(s)]:[e,Object.fromEntries(Object.entries(o).map(([i,f])=>[i,(typeof f=="string"?[f]:f).map(s)]))]}))}},resetLoadedSamples=()=>{sampleCache.current=void 0},getLoadedSamples$1=()=>sampleCache.current;var vowelFormant={a:{freqs:[660,1120,2750,3e3,3350],gains:[1,.5012,.0708,.0631,.0126],qs:[80,90,120,130,140]},e:{freqs:[440,1800,2700,3e3,3300],gains:[1,.1995,.1259,.1,.1],qs:[70,80,100,120,120]},i:{freqs:[270,1850,2900,3350,3590],gains:[1,.0631,.0631,.0158,.0158],qs:[40,90,100,120,120]},o:{freqs:[430,820,2700,3e3,3300],gains:[1,.3162,.0501,.0794,.01995],qs:[40,80,100,120,120]},u:{freqs:[370,630,2750,3e3,3400],gains:[1,.1,.0708,.0316,.01995],qs:[40,60,100,120,120]}};if(typeof GainNode<"u"){class r extends GainNode{constructor(e,o){if(super(e),!vowelFormant[o])throw new Error("vowel: unknown vowel "+o);const{gains:s,qs:i,freqs:f}=vowelFormant[o],l=e.createGain();for(let g=0;g<5;g++){const w=e.createGain();w.gain.value=s[g];const y=e.createBiquadFilter();y.type="bandpass",y.Q.value=i[g],y.frequency.value=f[g],this.connect(y),y.connect(w),w.connect(l)}return l.gain.value=8,this.connect=g=>l.connect(g),this}}AudioContext.prototype.createVowelFilter=function(t){return new r(this,t)}}const workletsUrl="data:application/javascript;base64,Ly8gTElDRU5TRSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSB2My4wIHNlZSBodHRwczovL2dpdGh1Yi5jb20vZGt0cjAvV2ViRGlydC9ibG9iL21haW4vTElDRU5TRQovLyBhbGwgdGhlIGNyZWRpdCBnb2VzIHRvIGRrdHIwJ3Mgd2ViZGlydDogaHR0cHM6Ly9naXRodWIuY29tL2RrdHIwL1dlYkRpcnQvYmxvYi81Y2UzZDY5ODM2MmM1NGQ2ZTFiNjhhY2M0N2ViMjk1NWFjNjJjNzkzL2Rpc3QvQXVkaW9Xb3JrbGV0cy5qcwovLyA8MwoKY2xhc3MgQ29hcnNlUHJvY2Vzc29yIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICBzdGF0aWMgZ2V0IHBhcmFtZXRlckRlc2NyaXB0b3JzKCkgewogICAgcmV0dXJuIFt7IG5hbWU6ICdjb2Fyc2UnLCBkZWZhdWx0VmFsdWU6IDEgfV07CiAgfQoKICBjb25zdHJ1Y3RvcigpIHsKICAgIHN1cGVyKCk7CiAgICB0aGlzLm5vdFN0YXJ0ZWQgPSB0cnVlOwogIH0KCiAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKICAgIGNvbnN0IGlucHV0ID0gaW5wdXRzWzBdOwogICAgY29uc3Qgb3V0cHV0ID0gb3V0cHV0c1swXTsKICAgIGNvbnN0IGNvYXJzZSA9IHBhcmFtZXRlcnMuY29hcnNlOwogICAgY29uc3QgYmxvY2tTaXplID0gMTI4OwogICAgY29uc3QgaGFzSW5wdXQgPSAhKGlucHV0WzBdID09PSB1bmRlZmluZWQpOwogICAgaWYgKGhhc0lucHV0KSB7CiAgICAgIHRoaXMubm90U3RhcnRlZCA9IGZhbHNlOwogICAgICBvdXRwdXRbMF1bMF0gPSBpbnB1dFswXVswXTsKICAgICAgZm9yIChsZXQgbiA9IDE7IG4gPCBibG9ja1NpemU7IG4rKykgewogICAgICAgIGZvciAobGV0IG8gPSAwOyBvIDwgb3V0cHV0Lmxlbmd0aDsgbysrKSB7CiAgICAgICAgICBvdXRwdXRbb11bbl0gPSBuICUgY29hcnNlID09IDAgPyBpbnB1dFswXVtuXSA6IG91dHB1dFtvXVtuIC0gMV07CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4gdGhpcy5ub3RTdGFydGVkIHx8IGhhc0lucHV0OwogIH0KfQoKcmVnaXN0ZXJQcm9jZXNzb3IoJ2NvYXJzZS1wcm9jZXNzb3InLCBDb2Fyc2VQcm9jZXNzb3IpOwoKY2xhc3MgQ3J1c2hQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICByZXR1cm4gW3sgbmFtZTogJ2NydXNoJywgZGVmYXVsdFZhbHVlOiAwIH1dOwogIH0KCiAgY29uc3RydWN0b3IoKSB7CiAgICBzdXBlcigpOwogICAgdGhpcy5ub3RTdGFydGVkID0gdHJ1ZTsKICB9CgogIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CiAgICBjb25zdCBjcnVzaCA9IHBhcmFtZXRlcnMuY3J1c2g7CiAgICBjb25zdCBibG9ja1NpemUgPSAxMjg7CiAgICBjb25zdCBoYXNJbnB1dCA9ICEoaW5wdXRbMF0gPT09IHVuZGVmaW5lZCk7CiAgICBpZiAoaGFzSW5wdXQpIHsKICAgICAgdGhpcy5ub3RTdGFydGVkID0gZmFsc2U7CiAgICAgIGlmIChjcnVzaC5sZW5ndGggPT09IDEpIHsKICAgICAgICBjb25zdCB4ID0gTWF0aC5wb3coMiwgY3J1c2hbMF0gLSAxKTsKICAgICAgICBmb3IgKGxldCBuID0gMDsgbiA8IGJsb2NrU2l6ZTsgbisrKSB7CiAgICAgICAgICBjb25zdCB2YWx1ZSA9IE1hdGgucm91bmQoaW5wdXRbMF1bbl0gKiB4KSAvIHg7CiAgICAgICAgICBmb3IgKGxldCBvID0gMDsgbyA8IG91dHB1dC5sZW5ndGg7IG8rKykgewogICAgICAgICAgICBvdXRwdXRbb11bbl0gPSB2YWx1ZTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZm9yIChsZXQgbiA9IDA7IG4gPCBibG9ja1NpemU7IG4rKykgewogICAgICAgICAgbGV0IHggPSBNYXRoLnBvdygyLCBjcnVzaFtuXSAtIDEpOwogICAgICAgICAgY29uc3QgdmFsdWUgPSBNYXRoLnJvdW5kKGlucHV0WzBdW25dICogeCkgLyB4OwogICAgICAgICAgZm9yIChsZXQgbyA9IDA7IG8gPCBvdXRwdXQubGVuZ3RoOyBvKyspIHsKICAgICAgICAgICAgb3V0cHV0W29dW25dID0gdmFsdWU7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4gdGhpcy5ub3RTdGFydGVkIHx8IGhhc0lucHV0OwogIH0KfQpyZWdpc3RlclByb2Nlc3NvcignY3J1c2gtcHJvY2Vzc29yJywgQ3J1c2hQcm9jZXNzb3IpOwoKY2xhc3MgU2hhcGVQcm9jZXNzb3IgZXh0ZW5kcyBBdWRpb1dvcmtsZXRQcm9jZXNzb3IgewogIHN0YXRpYyBnZXQgcGFyYW1ldGVyRGVzY3JpcHRvcnMoKSB7CiAgICByZXR1cm4gW3sgbmFtZTogJ3NoYXBlJywgZGVmYXVsdFZhbHVlOiAwIH1dOwogIH0KCiAgY29uc3RydWN0b3IoKSB7CiAgICBzdXBlcigpOwogICAgdGhpcy5ub3RTdGFydGVkID0gdHJ1ZTsKICB9CgogIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzLCBwYXJhbWV0ZXJzKSB7CiAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CiAgICBjb25zdCBzaGFwZTAgPSBwYXJhbWV0ZXJzLnNoYXBlWzBdOwogICAgY29uc3Qgc2hhcGUxID0gc2hhcGUwIDwgMSA/IHNoYXBlMCA6IDEuMCAtIDRlLTEwOwogICAgY29uc3Qgc2hhcGUgPSAoMi4wICogc2hhcGUxKSAvICgxLjAgLSBzaGFwZTEpOwogICAgY29uc3QgYmxvY2tTaXplID0gMTI4OwogICAgY29uc3QgaGFzSW5wdXQgPSAhKGlucHV0WzBdID09PSB1bmRlZmluZWQpOwogICAgaWYgKGhhc0lucHV0KSB7CiAgICAgIHRoaXMubm90U3RhcnRlZCA9IGZhbHNlOwogICAgICBmb3IgKGxldCBuID0gMDsgbiA8IGJsb2NrU2l6ZTsgbisrKSB7CiAgICAgICAgY29uc3QgdmFsdWUgPSAoKDEgKyBzaGFwZSkgKiBpbnB1dFswXVtuXSkgLyAoMSArIHNoYXBlICogTWF0aC5hYnMoaW5wdXRbMF1bbl0pKTsKICAgICAgICBmb3IgKGxldCBvID0gMDsgbyA8IG91dHB1dC5sZW5ndGg7IG8rKykgewogICAgICAgICAgb3V0cHV0W29dW25dID0gdmFsdWU7CiAgICAgICAgfQogICAgICB9CiAgICB9CiAgICByZXR1cm4gdGhpcy5ub3RTdGFydGVkIHx8IGhhc0lucHV0OwogIH0KfQoKcmVnaXN0ZXJQcm9jZXNzb3IoJ3NoYXBlLXByb2Nlc3NvcicsIFNoYXBlUHJvY2Vzc29yKTsK",{Pattern}=strudel;let audioContext;const getAudioContext$1=()=>(audioContext||(audioContext=new AudioContext),audioContext);let destination;const getDestination=()=>{const r=getAudioContext$1();return destination||(destination=r.createGain(),destination.connect(r.destination)),destination},panic=()=>{getDestination().gain.linearRampToValueAtTime(0,getAudioContext$1().currentTime+.01),destination=null},getFilter=(r,t,e)=>{const o=getAudioContext$1().createBiquadFilter();return o.type=r,o.frequency.value=t,o.Q.value=e,o},getADSR=(r,t,e,o,s,i,f)=>{const l=getAudioContext$1().createGain();return l.gain.setValueAtTime(0,i),l.gain.linearRampToValueAtTime(s,i+r),l.gain.linearRampToValueAtTime(e*s,i+r+t),l.gain.setValueAtTime(e*s,f),l.gain.linearRampToValueAtTime(0,f+o),l},getOscillator=({s:r,freq:t,t:e,duration:o,release:s})=>{const i=getAudioContext$1().createOscillator();return i.type=r||"triangle",i.frequency.value=Number(t),i.start(e),i.stop(e+o+s),i},getSoundfontKey=r=>{var o,s,i,f;if(!globalThis.soundfontList)return!1;if((s=(o=globalThis.soundfontList)==null?void 0:o.instruments)!=null&&s.includes(r))return r;const t=(f=(i=globalThis.soundfontList)==null?void 0:i.instrumentNames)==null?void 0:f.indexOf(r),e=t<10?`00${t}`:t<100?`0${t}`:t;if(t!==-1)return globalThis.soundfontList.instruments.find(l=>l.startsWith(e))},getSampleBufferSource=async(r,t,e,o)=>{let s=0,i=typeof e=="string"?toMidi(e):e||36;s=i-36;const f=getAudioContext$1(),l=getLoadedSamples();if(!l)throw new Error("no samples loaded");const g=l==null?void 0:l[r];if(!g)throw new Error(`sample not found: "${r}", try one of ${Object.keys(l).map(C=>`"${C}"`).join(", ")}.`);if(typeof g!="object")throw new Error("wrong format for sample bank:",r);let w;if(Array.isArray(g))w=g[t%g.length];else{const C=d=>toMidi(d)-i,p=Object.keys(g).filter(d=>!d.startsWith("_")).reduce((d,m,b)=>!d||Math.abs(C(m)){if(!r.includes(":"))return[r,t];let[e,o]=r.split(":");return isNaN(Number(o))?[r,t]:[e,o]};let workletsLoading;function loadWorklets(){return workletsLoading||(workletsLoading=getAudioContext$1().audioWorklet.addModule(workletsUrl),workletsLoading)}function getWorklet(r,t,e){const o=new AudioWorkletNode(r,t);return Object.entries(e).forEach(([s,i])=>{o.parameters.get(s).value=i}),o}if(typeof window<"u")try{loadWorklets()}catch(r){console.warn("could not load AudioWorklet effects coarse, crush and shape",r)}function gainNode(r){const t=getAudioContext$1().createGain();return t.gain.value=r,t}const cutGroups=[];let delays={};function getDelay(r,t,e,o){if(!delays[r]){const i=getAudioContext$1().createFeedbackDelay(1,t,e);i.start(o),i.connect(getDestination()),delays[r]=i}return delays[r].delayTime.value!==t&&delays[r].delayTime.setValueAtTime(t,o),delays[r].feedback.value!==e&&delays[r].feedback.setValueAtTime(e,o),delays[r]}let reverbs={};function getReverb(r,t=2){if(!reverbs[r]){const o=getAudioContext$1().createReverb(t);o.connect(getDestination()),reverbs[r]=o}return reverbs[r].duration!==t&&(reverbs[r]=reverbs[r].setDuration(t),reverbs[r].duration=t),reverbs[r]}function effectSend(r,t,e){const o=gainNode(e);return r.connect(o),o.connect(t),o}const webaudioOutput=async(r,t,e)=>{var o;try{const s=getAudioContext$1();if(typeof r.value!="object")throw new Error(`hap.value ${r.value} is not supported by webaudio output. Hint: append .note() or .s() to the end`);let i=s.currentTime+t,{freq:f,s:l,bank:g,sf:w,clip:y=0,n:$=0,note:S,gain:C=.8,cutoff:p,resonance:d=1,hcutoff:m,hresonance:b=1,bandf:I,bandq:T=1,coarse:G,crush:Y,shape:D,pan:W,speed:P=1,begin:B=0,end:z=1,vowel:O,delay:Z=0,delayfeedback:L=.5,delaytime:fe=.25,unit:we,nudge:be=0,cut:te,loop:le,orbit:pe=1,room:he,size:ve=2,roomsize:de=ve}=r.value;const{velocity:$e=1}=r.context;C*=$e;const F=[];if(g&&l&&(l=`${g}_${l}`),typeof l=="string"&&([l,$]=splitSN(l,$)),typeof S=="string"&&([S,$]=splitSN(S,$)),!l||["sine","square","triangle","sawtooth"].includes(l)){const{attack:M=.001,decay:U=.05,sustain:ye=.6,release:J=.01}=r.value;$=S||$||36,typeof $=="string"&&($=toMidi($)),!f&&typeof $=="number"&&(f=fromMidi($));const ee=getOscillator({t:i,s:l,freq:f,duration:e,release:J});F.push(ee),F.push(gainNode(.3));const R=getADSR(M,U,ye,J,1,i,i+e);F.push(R)}else{const{attack:M=.001,decay:U=.001,sustain:ye=1,release:J=.001}=r.value;if(P===0)return;if(!l){console.warn("no sample specified");return}const ee=getSoundfontKey(l);let R;try{ee?R=await globalThis.getFontBufferSource(ee,S||$,s):R=await getSampleBufferSource(l,$,S,P)}catch(Be){console.warn(Be);return}if(s.currentTime>i){console.warn("sample still loading:",l,$);return}if(!R){console.warn("no buffer source");return}R.playbackRate.value=Math.abs(P)*R.playbackRate.value,we==="c"&&(R.playbackRate.value=R.playbackRate.value*R.buffer.duration);let X=ee||y?e:R.buffer.duration/R.playbackRate.value;const re=B*X*R.playbackRate.value;X=(z-B)*X,le&&(R.loop=!0,R.loopStart=re,R.loopEnd=re+X,X=le*X),i+=be,R.start(i,re),te!==void 0&&((o=cutGroups[te])==null||o.stop(i),cutGroups[te]=R),F.push(R),R.stop(i+X+J);const Pe=getADSR(M,U,ye,J,1,i,i+X);F.push(Pe)}if(F.push(gainNode(C)),p!==void 0&&F.push(getFilter("lowpass",p,d)),m!==void 0&&F.push(getFilter("highpass",m,b)),I!==void 0&&F.push(getFilter("bandpass",I,T)),O!==void 0&&F.push(s.createVowelFilter(O)),G!==void 0&&F.push(getWorklet(s,"coarse-processor",{coarse:G})),Y!==void 0&&F.push(getWorklet(s,"crush-processor",{crush:Y})),D!==void 0&&F.push(getWorklet(s,"shape-processor",{shape:D})),W!==void 0){const M=s.createStereoPanner();M.pan.value=2*W-1,F.push(M)}const Q=gainNode(1);F.push(Q),Q.connect(getDestination());let ge;if(Z>0&&fe>0&&L>0){const M=getDelay(pe,fe,L,i);ge=effectSend(Q,M,Z)}let me;if(he>0&&de>0){const M=getReverb(pe,de);me=effectSend(Q,M,he)}F.slice(1).reduce((M,U)=>M.connect(U),F[0]),F[0].onended=()=>F.concat([ge,me]).forEach(M=>M==null?void 0:M.disconnect())}catch(s){console.warn(".out error:",s)}},webaudioOutputTrigger=(r,t,e,o)=>webaudioOutput(t,r-e,t.duration/o);Pattern.prototype.out=function(){return this.onTrigger(webaudioOutputTrigger)};const tune=`stack( - n("c3 [eb3,g3]") - .delay("<0 .5>") - .delaytime(".16 | .33") - .delayfeedback(".6 | .8") -).sometimes(x=>x.speed("-1"))`,ctx=getAudioContext$1(),input=document.getElementById("text");input.innerHTML=tune;evalScope(controls,__vitePreload(()=>Promise.resolve().then(()=>strudel),void 0),__vitePreload(()=>import("./index.211bbc68.js"),[]),__vitePreload(()=>import("./index.ecddf978.js"),[]),__vitePreload(()=>import("./index.63aa02b5.js"),[]));setStringParser(mini);const{evaluate}=repl({defaultOutput:webaudioOutput,getTime:()=>ctx.currentTime});document.getElementById("start").addEventListener("click",()=>{ctx.resume(),evaluate(input.value)});export{Hap as H,Pattern$3 as P,minify as a,peg$SyntaxError as b,peg$parse as c,panic as d,webaudioOutputTrigger as e,getCachedBuffer as f,getAudioContext$1 as g,h,getLoadedBuffer as i,loadGithubSamples as j,resetLoadedSamples as k,loadBuffer as l,mini as m,getLoadedSamples$1 as n,mod as o,patternifyAST as p,getAugmentedNamespace as q,reverseBuffer as r,samples as s,commonjsGlobal as t,getDefaultExportFromCjs as u,reify$2 as v,webaudioOutput as w,stack$1 as x}; diff --git a/packages/core/examples/vite-vanilla-repl/dist/assets/index.63aa02b5.js b/packages/core/examples/vite-vanilla-repl/dist/assets/index.63aa02b5.js deleted file mode 100644 index dfde3ae1..00000000 --- a/packages/core/examples/vite-vanilla-repl/dist/assets/index.63aa02b5.js +++ /dev/null @@ -1 +0,0 @@ -import{P as _,o as Lt,q as Ut,t as Y,u as Xt,v as Kt,H as Qt,x as Yt}from"./index.4cbc0a10.js";const C=(n,t)=>Array(Math.abs(t)+1).join(n);function g(n,t,e){return function(...o){return console.warn(`${n} is deprecated. Use ${t}.`),e.apply(this,o)}}function z(n){return n!==null&&typeof n=="object"&&typeof n.name=="string"}function q(n){return n!==null&&typeof n=="object"&&typeof n.step=="number"&&typeof n.alt=="number"}const Qn=[0,2,4,-1,1,3,5],Yn=Qn.map(n=>Math.floor(n*7/12));function en(n){const{step:t,alt:e,oct:o,dir:r=1}=n,i=Qn[t]+7*e;if(o===void 0)return[r*i];const a=o-Yn[t]-4*e;return[r*i,r*a]}const Zt=[3,0,4,1,5,2,6];function on(n){const[t,e,o]=n,r=Zt[Jt(t)],i=Math.floor((t+1)/7);if(e===void 0)return{step:r,alt:i,dir:o};const a=e+4*i+Yn[r];return{step:r,alt:i,oct:a,dir:o}}function Jt(n){const t=(n+1)%7;return t<0?7+t:t}const Zn={empty:!0,name:"",pc:"",acc:""},Dn=new Map,vn=n=>"CDEFGAB".charAt(n),E=n=>n<0?C("b",-n):C("#",n),G=n=>n[0]==="b"?-n.length:n.length;function m(n){const t=Dn.get(n);if(t)return t;const e=typeof n=="string"?te(n):q(n)?m(ee(n)):z(n)?m(n.name):Zn;return Dn.set(n,e),e}const Wt=/^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/;function rn(n){const t=Wt.exec(n);return[t[1].toUpperCase(),t[2].replace(/x/g,"##"),t[3],t[4]]}function F(n){return m(on(n))}const ne=(n,t)=>(n%t+t)%t,pn=[0,2,4,5,7,9,11];function te(n){const t=rn(n);if(t[0]===""||t[3]!=="")return Zn;const e=t[0],o=t[1],r=t[2],i=(e.charCodeAt(0)+3)%7,a=G(o),s=r.length?+r:void 0,c=en({step:i,alt:a,oct:s}),l=e+o+r,u=e+o,d=(pn[i]+a+120)%12,M=s===void 0?ne(pn[i]+a,12)-12*99:pn[i]+a+12*(s+1),h=M>=0&&M<=127?M:null,A=s===void 0?null:Math.pow(2,(M-69)/12)*440;return{empty:!1,acc:o,alt:a,chroma:d,coord:c,freq:A,height:M,letter:e,midi:h,name:l,oct:s,pc:u,step:i}}function ee(n){const{step:t,alt:e,oct:o}=n,r=vn(t);if(!r)return"";const i=r+E(e);return o||o===0?i+o:i}const yn={empty:!0,name:"",acc:""},oe="([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})",re="(AA|A|P|M|m|d|dd)([-+]?\\d+)",ie=new RegExp("^"+oe+"|"+re+"$");function $n(n){const t=ie.exec(`${n}`);return t===null?["",""]:t[1]?[t[1],t[2]]:[t[4],t[3]]}const Rn={};function f(n){return typeof n=="string"?Rn[n]||(Rn[n]=ae(n)):q(n)?f(ce(n)):z(n)?f(n.name):yn}const kn=[0,2,4,5,7,9,11],Jn="PMMPPMM";function ae(n){const t=$n(n);if(t[0]==="")return yn;const e=+t[0],o=t[1],r=(Math.abs(e)-1)%7,i=Jn[r];if(i==="M"&&o==="P")return yn;const a=i==="M"?"majorable":"perfectable",s=""+e+o,c=e<0?-1:1,l=e===8||e===-8?e:c*(r+1),u=se(a,o),d=Math.floor((Math.abs(e)-1)/7),M=c*(kn[r]+u+12*d),h=(c*(kn[r]+u)%12+12)%12,A=en({step:r,alt:u,oct:d,dir:c});return{empty:!1,name:s,num:e,q:o,step:r,alt:u,dir:c,type:a,simple:l,semitones:M,chroma:h,coord:A,oct:d}}function B(n,t){const[e,o=0]=n,r=e*7+o*12<0,i=t||r?[-e,-o,-1]:[e,o,1];return f(on(i))}function se(n,t){return t==="M"&&n==="majorable"||t==="P"&&n==="perfectable"?0:t==="m"&&n==="majorable"?-1:/^A+$/.test(t)?t.length:/^d+$/.test(t)?-1*(n==="perfectable"?t.length:t.length+1):0}function ce(n){const{step:t,alt:e,oct:o=0,dir:r}=n;if(!r)return"";const i=t+1+7*o,a=i===0?t+1:i,s=r<0?"-":"",c=Jn[t]==="M"?"majorable":"perfectable";return s+a+me(c,e)}function me(n,t){return t===0?n==="majorable"?"M":"P":t===-1&&n==="majorable"?"m":t>0?C("A",t):C("d",n==="perfectable"?t:t+1)}function P(n,t){const e=m(n),o=f(t);if(e.empty||o.empty)return"";const r=e.coord,i=o.coord,a=r.length===1?[r[0]+i[0]]:[r[0]+i[0],r[1]+i[1]];return F(a).name}function D(n,t){const e=m(n),o=m(t);if(e.empty||o.empty)return"";const r=e.coord,i=o.coord,a=i[0]-r[0],s=r.length===2&&i.length===2?i[1]-r[1]:-Math.floor(a*7/12),c=o.height===e.height&&o.midi!==null&&e.midi!==null&&e.step>o.step;return B([a,s],c).name}const Wn=Object.freeze(Object.defineProperty({__proto__:null,accToAlt:G,altToAcc:E,coordToInterval:B,coordToNote:F,decode:on,deprecate:g,distance:D,encode:en,fillStr:C,interval:f,isNamed:z,isPitch:q,note:m,stepToLetter:vn,tokenizeInterval:$n,tokenizeNote:rn,transpose:P},Symbol.toStringTag,{value:"Module"})),On=(n,t)=>Array(t+1).join(n),ue=/^(_{1,}|=|\^{1,}|)([abcdefgABCDEFG])([,']*)$/;function nt(n){const t=ue.exec(n);return t?[t[1],t[2],t[3]]:["","",""]}function W(n){const[t,e,o]=nt(n);if(e==="")return"";let r=4;for(let a=0;a96?e.toUpperCase()+i+(r+1):e+i+r}function tt(n){const t=m(n);if(t.empty||!t.oct&&t.oct!==0)return"";const{letter:e,acc:o,oct:r}=t,i=o[0]==="b"?o.replace(/b/g,"_"):o.replace(/#/g,"^"),a=r>4?e.toLowerCase():e,s=r===5?"":r>4?On("'",r-5):On(",",4-r);return i+a+s}function le(n,t){return tt(P(W(n),t))}function de(n,t){return D(W(n),W(t))}var Me={abcToScientificNotation:W,scientificToAbcNotation:tt,tokenize:nt,transpose:le,distance:de};function fe(n,t){const e=[];for(;t--;e[t]=t+n);return e}function Pe(n,t){const e=[];for(;t--;e[t]=n-t);return e}function pe(n,t){return nt===0||t)}function et(n){return n.map(e=>m(e)).filter(e=>!e.empty).sort((e,o)=>e.height-o.height).map(e=>e.name)}function ge(n){return et(n).filter((t,e,o)=>e===0||t!==o[e-1])}function be(n,t=Math.random){let e,o,r=n.length;for(;r;)e=Math.floor(t()*r--),o=n[r],n[r]=n[e],n[e]=o;return n}function ot(n){return n.length===0?[[]]:ot(n.slice(1)).reduce((t,e)=>t.concat(n.map((o,r)=>{const i=e.slice();return i.splice(r,0,n[0]),i})),[])}const Ae=Object.freeze(Object.defineProperty({__proto__:null,compact:ye,permutations:ot,range:pe,rotate:he,shuffle:be,sortedNoteNames:et,sortedUniqNoteNames:ge},Symbol.toStringTag,{value:"Module"}));function ve(n,t){const e=[];for(;t--;e[t]=t+n);return e}function $e(n,t){const e=[];for(;t--;e[t]=n-t);return e}function an(n,t){return nt===0||t)}function Ne(n,t=Math.random){let e,o,r=n.length;for(;r;)e=Math.floor(t()*r--),o=n[r],n[r]=n[e],n[e]=o;return n}function rt(n){return n.length===0?[[]]:rt(n.slice(1)).reduce((t,e)=>t.concat(n.map((o,r)=>{const i=e.slice();return i.splice(r,0,n[0]),i})),[])}var Ie={compact:Nn,permutations:rt,range:an,rotate:H,shuffle:Ne};const T={empty:!0,name:"",setNum:0,chroma:"000000000000",normalized:"000000000000",intervals:[]},In=n=>Number(n).toString(2),Fn=n=>parseInt(n,2),je=/^[01]{12}$/;function it(n){return je.test(n)}const Se=n=>typeof n=="number"&&n>=0&&n<=4095,Te=n=>n&&it(n.chroma),zn={[T.chroma]:T};function p(n){const t=it(n)?n:Se(n)?In(n):Array.isArray(n)?ze(n):Te(n)?n.chroma:T.chroma;return zn[t]=zn[t]||Fe(t)}const Ve=g("Pcset.pcset","Pcset.get",p),_e=n=>p(n).chroma,Ce=n=>p(n).intervals,we=n=>p(n).setNum,xe=["1P","2m","2M","3m","3M","4P","5d","5P","6m","6M","7m","7M"];function Ee(n){const t=[];for(let e=0;e<12;e++)n.charAt(e)==="1"&&t.push(xe[e]);return t}function De(){return an(2048,4095).map(In)}function jn(n,t=!0){const o=p(n).chroma.split("");return Nn(o.map((r,i)=>{const a=H(i,o);return t&&a[0]==="0"?null:a.join("")}))}function Re(n,t){return p(n).setNum===p(t).setNum}function sn(n){const t=p(n).setNum;return e=>{const o=p(e).setNum;return t&&t!==o&&(o&t)===o}}function cn(n){const t=p(n).setNum;return e=>{const o=p(e).setNum;return t&&t!==o&&(o|t)===o}}function at(n){const t=p(n);return e=>{const o=m(e);return t&&!o.empty&&t.chroma.charAt(o.chroma)==="1"}}function ke(n){const t=at(n);return e=>e.filter(t)}var st={get:p,chroma:_e,num:we,intervals:Ce,chromas:De,isSupersetOf:cn,isSubsetOf:sn,isNoteIncludedIn:at,isEqual:Re,filter:ke,modes:jn,pcset:Ve};function Oe(n){const t=n.split("");return t.map((e,o)=>H(o,t).join(""))}function Fe(n){const t=Fn(n),e=Oe(n).map(Fn).filter(i=>i>=2048).sort()[0],o=In(e),r=Ee(n);return{empty:!1,name:"",setNum:t,chroma:n,normalized:o,intervals:r}}function ze(n){if(n.length===0)return T.chroma;let t;const e=[0,0,0,0,0,0,0,0,0,0,0,0];for(let o=0;on.name).filter(n=>n)}function Le(){return R.map(n=>n.aliases[0]).filter(n=>n)}function Ue(){return Object.keys(j)}function k(){return R.slice()}const Xe=g("ChordType.entries","ChordType.all",k);function Ke(){R=[],j={}}function ct(n,t,e){const o=Ye(n),r={...p(n),name:e||"",quality:o,intervals:n,aliases:t};R.push(r),r.name&&(j[r.name]=r),j[r.setNum]=r,j[r.chroma]=r,r.aliases.forEach(i=>Qe(r,i))}function Qe(n,t){j[t]=n}function Ye(n){const t=e=>n.indexOf(e)!==-1;return t("5A")?"Augmented":t("3M")?"Major":t("5d")?"Diminished":t("3m")?"Minor":"Unknown"}qe.forEach(([n,t,e])=>ct(n.split(" "),e.split(" "),t));R.sort((n,t)=>n.setNum-t.setNum);var mt={names:He,symbols:Le,get:Sn,all:k,add:ct,removeAll:Ke,keys:Ue,entries:Xe,chordType:Be};const Ze=n=>{const t=n.reduce((e,o)=>{const r=m(o).chroma;return r!==void 0&&(e[r]=e[r]||m(o).name),e},{});return e=>t[e]};function Je(n){const t=n.map(o=>m(o).pc).filter(o=>o);return m.length===0?[]:We(t,1).filter(o=>o.weight).sort((o,r)=>r.weight-o.weight).map(o=>o.name)}function We(n,t){const e=n[0],o=m(e).chroma,r=Ze(n),i=jn(n,!1),a=[];return i.forEach((s,c)=>{k().filter(u=>u.chroma===s).forEach(u=>{const d=u.aliases[0],M=r(c);c!==o?a.push({weight:.5*t,name:`${M}${d}/${e}`}):a.push({weight:1*t,name:`${M}${d}`})})}),a}const no=[["1P 2M 3M 5P 6M","major pentatonic","pentatonic"],["1P 3M 4P 5P 7M","ionian pentatonic"],["1P 3M 4P 5P 7m","mixolydian pentatonic","indian"],["1P 2M 4P 5P 6M","ritusen"],["1P 2M 4P 5P 7m","egyptian"],["1P 3M 4P 5d 7m","neopolitan major pentatonic"],["1P 3m 4P 5P 6m","vietnamese 1"],["1P 2m 3m 5P 6m","pelog"],["1P 2m 4P 5P 6m","kumoijoshi"],["1P 2M 3m 5P 6m","hirajoshi"],["1P 2m 4P 5d 7m","iwato"],["1P 2m 4P 5P 7m","in-sen"],["1P 3M 4A 5P 7M","lydian pentatonic","chinese"],["1P 3m 4P 6m 7m","malkos raga"],["1P 3m 4P 5d 7m","locrian pentatonic","minor seven flat five pentatonic"],["1P 3m 4P 5P 7m","minor pentatonic","vietnamese 2"],["1P 3m 4P 5P 6M","minor six pentatonic"],["1P 2M 3m 5P 6M","flat three pentatonic","kumoi"],["1P 2M 3M 5P 6m","flat six pentatonic"],["1P 2m 3M 5P 6M","scriabin"],["1P 3M 5d 6m 7m","whole tone pentatonic"],["1P 3M 4A 5A 7M","lydian #5P pentatonic"],["1P 3M 4A 5P 7m","lydian dominant pentatonic"],["1P 3m 4P 5P 7M","minor #7M pentatonic"],["1P 3m 4d 5d 7m","super locrian pentatonic"],["1P 2M 3m 4P 5P 7M","minor hexatonic"],["1P 2A 3M 5P 5A 7M","augmented"],["1P 2M 3m 3M 5P 6M","major blues"],["1P 2M 4P 5P 6M 7m","piongio"],["1P 2m 3M 4A 6M 7m","prometheus neopolitan"],["1P 2M 3M 4A 6M 7m","prometheus"],["1P 2m 3M 5d 6m 7m","mystery #1"],["1P 2m 3M 4P 5A 6M","six tone symmetric"],["1P 2M 3M 4A 5A 7m","whole tone","messiaen's mode #1"],["1P 2m 4P 4A 5P 7M","messiaen's mode #5"],["1P 3m 4P 5d 5P 7m","minor blues","blues"],["1P 2M 3M 4P 5d 6m 7m","locrian major","arabian"],["1P 2m 3M 4A 5P 6m 7M","double harmonic lydian"],["1P 2M 3m 4P 5P 6m 7M","harmonic minor"],["1P 2m 2A 3M 4A 6m 7m","altered","super locrian","diminished whole tone","pomeroy"],["1P 2M 3m 4P 5d 6m 7m","locrian #2","half-diminished","aeolian b5"],["1P 2M 3M 4P 5P 6m 7m","mixolydian b6","melodic minor fifth mode","hindu"],["1P 2M 3M 4A 5P 6M 7m","lydian dominant","lydian b7","overtone"],["1P 2M 3M 4A 5P 6M 7M","lydian"],["1P 2M 3M 4A 5A 6M 7M","lydian augmented"],["1P 2m 3m 4P 5P 6M 7m","dorian b2","phrygian #6","melodic minor second mode"],["1P 2M 3m 4P 5P 6M 7M","melodic minor"],["1P 2m 3m 4P 5d 6m 7m","locrian"],["1P 2m 3m 4d 5d 6m 7d","ultralocrian","superlocrian bb7","superlocrian diminished"],["1P 2m 3m 4P 5d 6M 7m","locrian 6","locrian natural 6","locrian sharp 6"],["1P 2A 3M 4P 5P 5A 7M","augmented heptatonic"],["1P 2M 3m 4A 5P 6M 7m","dorian #4","ukrainian dorian","romanian minor","altered dorian"],["1P 2M 3m 4A 5P 6M 7M","lydian diminished"],["1P 2m 3m 4P 5P 6m 7m","phrygian"],["1P 2M 3M 4A 5A 7m 7M","leading whole tone"],["1P 2M 3M 4A 5P 6m 7m","lydian minor"],["1P 2m 3M 4P 5P 6m 7m","phrygian dominant","spanish","phrygian major"],["1P 2m 3m 4P 5P 6m 7M","balinese"],["1P 2m 3m 4P 5P 6M 7M","neopolitan major"],["1P 2M 3m 4P 5P 6m 7m","aeolian","minor"],["1P 2M 3M 4P 5P 6m 7M","harmonic major"],["1P 2m 3M 4P 5P 6m 7M","double harmonic major","gypsy"],["1P 2M 3m 4P 5P 6M 7m","dorian"],["1P 2M 3m 4A 5P 6m 7M","hungarian minor"],["1P 2A 3M 4A 5P 6M 7m","hungarian major"],["1P 2m 3M 4P 5d 6M 7m","oriental"],["1P 2m 3m 3M 4A 5P 7m","flamenco"],["1P 2m 3m 4A 5P 6m 7M","todi raga"],["1P 2M 3M 4P 5P 6M 7m","mixolydian","dominant"],["1P 2m 3M 4P 5d 6m 7M","persian"],["1P 2M 3M 4P 5P 6M 7M","major","ionian"],["1P 2m 3M 5d 6m 7m 7M","enigmatic"],["1P 2M 3M 4P 5A 6M 7M","major augmented","major #5","ionian augmented","ionian #5"],["1P 2A 3M 4A 5P 6M 7M","lydian #9"],["1P 2m 2M 4P 4A 5P 6m 7M","messiaen's mode #4"],["1P 2m 3M 4P 4A 5P 6m 7M","purvi raga"],["1P 2m 3m 3M 4P 5P 6m 7m","spanish heptatonic"],["1P 2M 3M 4P 5P 6M 7m 7M","bebop"],["1P 2M 3m 3M 4P 5P 6M 7m","bebop minor"],["1P 2M 3M 4P 5P 5A 6M 7M","bebop major"],["1P 2m 3m 4P 5d 5P 6m 7m","bebop locrian"],["1P 2M 3m 4P 5P 6m 7m 7M","minor bebop"],["1P 2M 3m 4P 5d 6m 6M 7M","diminished","whole-half diminished"],["1P 2M 3M 4P 5d 5P 6M 7M","ichikosucho"],["1P 2M 3m 4P 5P 6m 6M 7M","minor six diminished"],["1P 2m 3m 3M 4A 5P 6M 7m","half-whole diminished","dominant diminished","messiaen's mode #2"],["1P 3m 3M 4P 5P 6M 7m 7M","kafi raga"],["1P 2M 3M 4P 4A 5A 6A 7M","messiaen's mode #6"],["1P 2M 3m 3M 4P 5d 5P 6M 7m","composite blues"],["1P 2M 3m 3M 4A 5P 6m 7m 7M","messiaen's mode #3"],["1P 2m 2M 3m 4P 4A 5P 6m 6M 7M","messiaen's mode #7"],["1P 2m 2M 3m 3M 4P 5d 5P 6m 6M 7m 7M","chromatic"]],to={...T,intervals:[],aliases:[]};let mn=[],S={};function ut(){return mn.map(n=>n.name)}function un(n){return S[n]||to}const eo=g("ScaleDictionary.scaleType","ScaleType.get",un);function L(){return mn.slice()}const oo=g("ScaleDictionary.entries","ScaleType.all",L);function ro(){return Object.keys(S)}function io(){mn=[],S={}}function lt(n,t,e=[]){const o={...p(n),name:t,intervals:n,aliases:e};return mn.push(o),S[o.name]=o,S[o.setNum]=o,S[o.chroma]=o,o.aliases.forEach(r=>ao(o,r)),o}function ao(n,t){S[t]=n}no.forEach(([n,t,...e])=>lt(n.split(" "),t,e));var dt={names:ut,get:un,all:L,add:lt,removeAll:io,keys:ro,entries:oo,scaleType:eo};const gn={empty:!0,name:"",symbol:"",root:"",rootDegree:0,type:"",tonic:null,setNum:NaN,quality:"Unknown",chroma:"",normalized:"",aliases:[],notes:[],intervals:[]},so=/^(6|64|7|9|11|13)$/;function ln(n){const[t,e,o,r]=rn(n);return t===""?["",n]:t==="A"&&r==="ug"?["","aug"]:!r&&(o==="4"||o==="5")?[t+e,o]:so.test(o)?[t+e,o+r]:[t+e+o,r]}function U(n){if(n==="")return gn;if(Array.isArray(n)&&n.length===2)return Z(n[1],n[0]);{const[t,e]=ln(n),o=Z(e,t);return o.empty?Z(n):o}}function Z(n,t,e){const o=Sn(n),r=m(t||""),i=m(e||"");if(o.empty||t&&r.empty||e&&i.empty)return gn;const a=D(r.pc,i.pc),s=o.intervals.indexOf(a)+1;if(!i.empty&&!s)return gn;const c=Array.from(o.intervals);for(let M=1;MP(r,M));n=o.aliases.indexOf(n)!==-1?n:o.aliases[0];const u=`${r.empty?"":r.pc}${n}${i.empty||s<=1?"":"/"+i.pc}`,d=`${t?r.pc+" ":""}${o.name}${s>1&&e?" over "+i.pc:""}`;return{...o,name:d,symbol:u,type:o.name,root:i.name,intervals:c,rootDegree:s,tonic:r.name,notes:l}}const co=g("Chord.chord","Chord.get",U);function mo(n,t){const[e,o]=ln(n);return e?P(e,t)+o:n}function uo(n){const t=U(n),e=cn(t.chroma);return L().filter(o=>e(o.chroma)).map(o=>o.name)}function lo(n){const t=U(n),e=cn(t.chroma);return k().filter(o=>e(o.chroma)).map(o=>t.tonic+o.aliases[0])}function Mo(n){const t=U(n),e=sn(t.chroma);return k().filter(o=>e(o.chroma)).map(o=>t.tonic+o.aliases[0])}var fo={getChord:Z,get:U,detect:Je,chordScales:uo,extended:lo,reduced:Mo,tokenize:ln,transpose:mo,chord:co};const Po=[[.125,"dl",["large","duplex longa","maxima","octuple","octuple whole"]],[.25,"l",["long","longa"]],[.5,"d",["double whole","double","breve"]],[1,"w",["whole","semibreve"]],[2,"h",["half","minim"]],[4,"q",["quarter","crotchet"]],[8,"e",["eighth","quaver"]],[16,"s",["sixteenth","semiquaver"]],[32,"t",["thirty-second","demisemiquaver"]],[64,"sf",["sixty-fourth","hemidemisemiquaver"]],[128,"h",["hundred twenty-eighth"]],[256,"th",["two hundred fifty-sixth"]]],dn=[];Po.forEach(([n,t,e])=>$o(n,t,e));const po={empty:!0,name:"",value:0,fraction:[0,0],shorthand:"",dots:"",names:[]};function ho(){return dn.reduce((n,t)=>(t.names.forEach(e=>n.push(e)),n),[])}function yo(){return dn.map(n=>n.shorthand)}const go=/^([^.]+)(\.*)$/;function Tn(n){const[t,e,o]=go.exec(n)||[],r=dn.find(s=>s.shorthand===e||s.names.includes(e));if(!r)return po;const i=No(r.fraction,o.length),a=i[0]/i[1];return{...r,name:n,dots:o,value:a,fraction:i}}const bo=n=>Tn(n).value,Ao=n=>Tn(n).fraction;var vo={names:ho,shorthands:yo,get:Tn,value:bo,fraction:Ao};function $o(n,t,e){dn.push({empty:!1,dots:"",name:"",value:1/n,fraction:n<1?[1/n,1]:[1,n],shorthand:t,names:e})}function No(n,t){const e=Math.pow(2,t);let o=n[0]*e,r=n[1]*e;const i=o;for(let a=0;af(n).name,So=n=>f(n).semitones,To=n=>f(n).q,Vo=n=>f(n).num;function ft(n){const t=f(n);return t.empty?"":t.simple+t.q}function _o(n){const t=f(n);if(t.empty)return"";const e=(7-t.step)%7,o=t.type==="perfectable"?-t.alt:-(t.alt+1);return f({step:e,alt:o,oct:t.oct,dir:t.dir}).name}const Co=[1,2,2,3,3,4,5,5,6,6,7,7],wo="P m M m M P d P m M m M".split(" ");function xo(n){const t=n<0?-1:1,e=Math.abs(n),o=e%12,r=Math.floor(e/12);return t*(Co[o]+7*r)+wo[o]}const Eo=D,Pt=ht((n,t)=>[n[0]+t[0],n[1]+t[1]]),Do=n=>t=>Pt(n,t),Ro=ht((n,t)=>[n[0]-t[0],n[1]-t[1]]);function pt(n,t){const e=Mt(n);if(e.empty)return"";const[o,r,i]=e.coord;return B([o+t,r,i]).name}var bn={names:Io,get:Mt,name:jo,num:Vo,semitones:So,quality:To,fromSemitones:xo,distance:Eo,invert:_o,simplify:ft,add:Pt,addTo:Do,substract:Ro,transposeFifths:pt};function ht(n){return(t,e)=>{const o=f(t).coord,r=f(e).coord;if(o&&r){const i=n(o,r);return B(i).name}}}function yt(n){return+n>=0&&+n<=127}function gt(n){if(yt(n))return+n;const t=m(n);return t.empty?null:t.midi}function ko(n,t=440){return Math.pow(2,(n-69)/12)*t}const Oo=Math.log(2),Fo=Math.log(440);function Vn(n){const t=12*(Math.log(n)-Fo)/Oo+69;return Math.round(t*100)/100}const zo="C C# D D# E F F# G G# A A# B".split(" "),qo="C Db D Eb E F Gb G Ab A Bb B".split(" ");function I(n,t={}){if(isNaN(n)||n===-1/0||n===1/0)return"";n=Math.round(n);const o=(t.sharps===!0?zo:qo)[n%12];if(t.pitchClass)return o;const r=Math.floor(n/12)-1;return o+r}var Go={isMidi:yt,toMidi:gt,midiToFreq:ko,midiToNoteName:I,freqToMidi:Vn};const Bo=["C","D","E","F","G","A","B"],bt=n=>n.name,At=n=>n.map(m).filter(t=>!t.empty);function Ho(n){return n===void 0?Bo.slice():Array.isArray(n)?At(n).map(bt):[]}const y=m,Lo=n=>y(n).name,Uo=n=>y(n).pc,Xo=n=>y(n).acc,Ko=n=>y(n).oct,Qo=n=>y(n).midi,Yo=n=>y(n).freq,Zo=n=>y(n).chroma;function vt(n){return I(n)}function Jo(n){return I(Vn(n))}function Wo(n){return I(Vn(n),{sharps:!0})}function nr(n){return I(n,{sharps:!0})}const _n=P,tr=P,$t=n=>t=>_n(t,n),er=$t,Nt=n=>t=>_n(n,t),or=Nt;function nn(n,t){const e=y(n);if(e.empty)return"";const[o,r]=e.coord;return F(r===void 0?[o+t]:[o+t,r]).name}const rr=nn,Cn=(n,t)=>n.height-t.height,ir=(n,t)=>t.height-n.height;function It(n,t){return t=t||Cn,At(n).sort(t).map(bt)}function jt(n){return It(n,Cn).filter((t,e,o)=>e===0||t!==o[e-1])}const ar=n=>{const t=y(n);return t.empty?"":I(t.midi||t.chroma,{sharps:t.alt>0,pitchClass:t.midi===null})};function St(n,t){const e=y(n);if(e.empty)return"";const o=y(t||I(e.midi||e.chroma,{sharps:e.alt<0,pitchClass:!0}));if(o.empty||o.chroma!==e.chroma)return"";if(e.oct===void 0)return o.pc;const r=e.chroma-e.alt,i=o.chroma-o.alt,a=r>11||i<0?-1:r<0||i>11?1:0,s=e.oct+a;return o.pc+s}var w={names:Ho,get:y,name:Lo,pitchClass:Uo,accidentals:Xo,octave:Ko,midi:Qo,ascending:Cn,descending:ir,sortedNames:It,sortedUniqNames:jt,fromMidi:vt,fromMidiSharps:nr,freq:Yo,fromFreq:Jo,fromFreqSharps:Wo,chroma:Zo,transpose:_n,tr,transposeBy:$t,trBy:er,transposeFrom:Nt,trFrom:or,transposeFifths:nn,trFifths:rr,simplify:ar,enharmonic:St};const Tt={empty:!0,name:"",chordType:""},qn={};function $(n){return typeof n=="string"?qn[n]||(qn[n]=Mr(n)):typeof n=="number"?$(Mn[n]||""):q(n)?mr(n):z(n)?$(n.name):Tt}const sr=g("RomanNumeral.romanNumeral","RomanNumeral.get",$);function cr(n=!0){return(n?Mn:dr).slice()}function mr(n){return $(E(n.alt)+Mn[n.step])}const ur=/^(#{1,}|b{1,}|x{1,}|)(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2})([^IViv]*)$/;function lr(n){return ur.exec(n)||["","","",""]}const Vt="I II III IV V VI VII",Mn=Vt.split(" "),dr=Vt.toLowerCase().split(" ");function Mr(n){const[t,e,o,r]=lr(n);if(!o)return Tt;const i=o.toUpperCase(),a=Mn.indexOf(i),s=G(e),c=1;return{empty:!1,name:t,roman:o,interval:f({step:a,alt:s,dir:c}).name,acc:e,chordType:r,alt:s,step:a,major:o===i,oct:0,dir:c}}var fr={names:cr,get:$,romanNumeral:sr};const b=Object.freeze([]),_t={type:"major",tonic:"",alteration:0,keySignature:""},J={tonic:"",grades:b,intervals:b,scale:b,chords:b,chordsHarmonicFunction:b,chordScales:b},Pr={..._t,...J,type:"major",minorRelative:"",scale:b,secondaryDominants:b,secondaryDominantsMinorRelative:b,substituteDominants:b,substituteDominantsMinorRelative:b},pr={..._t,type:"minor",relativeMajor:"",natural:J,harmonic:J,melodic:J},Gn=(n,t,e="")=>t.map((o,r)=>`${n[r]}${e}${o}`);function fn(n,t,e,o){return r=>{const i=n.map(s=>$(s).interval||""),a=i.map(s=>P(r,s));return{tonic:r,grades:n,intervals:i,scale:a,chords:Gn(a,t),chordsHarmonicFunction:e.slice(),chordScales:Gn(a,o," ")}}}const Ct=(n,t)=>{const e=m(n),o=m(t);return e.empty||o.empty?0:o.coord[0]-e.coord[0]},hr=fn("I II III IV V VI VII".split(" "),"maj7 m7 m7 maj7 7 m7 m7b5".split(" "),"T SD T SD D T D".split(" "),"major,dorian,phrygian,lydian,mixolydian,minor,locrian".split(",")),yr=fn("I II bIII IV V bVI bVII".split(" "),"m7 m7b5 maj7 m7 m7 maj7 7".split(" "),"T SD T SD D SD SD".split(" "),"minor,locrian,major,dorian,phrygian,lydian,mixolydian".split(",")),gr=fn("I II bIII IV V bVI VII".split(" "),"mMaj7 m7b5 +maj7 m7 7 maj7 o7".split(" "),"T SD T SD D SD D".split(" "),"harmonic minor,locrian 6,major augmented,lydian diminished,phrygian dominant,lydian #9,ultralocrian".split(",")),br=fn("I II bIII IV V VI VII".split(" "),"m6 m7 +maj7 7 7 m7b5 m7b5".split(" "),"T SD T SD D ".split(" "),"melodic minor,dorian b2,lydian augmented,lydian dominant,mixolydian b6,locrian #2,altered".split(","));function Ar(n){const t=m(n).pc;if(!t)return Pr;const e=hr(t),o=Ct("C",t),r=i=>{const a=$(i);return a.empty?"":P(n,a.interval)+a.chordType};return{...e,type:"major",minorRelative:P(t,"-3m"),alteration:o,keySignature:E(o),secondaryDominants:"- VI7 VII7 I7 II7 III7 -".split(" ").map(r),secondaryDominantsMinorRelative:"- IIIm7b5 IV#m7 Vm7 VIm7 VIIm7b5 -".split(" ").map(r),substituteDominants:"- bIII7 IV7 bV7 bVI7 bVII7 -".split(" ").map(r),substituteDominantsMinorRelative:"- IIIm7 Im7 IIbm7 VIm7 IVm7 -".split(" ").map(r)}}function vr(n){const t=m(n).pc;if(!t)return pr;const e=Ct("C",t)-3;return{type:"minor",tonic:t,relativeMajor:P(t,"3m"),alteration:e,keySignature:E(e),natural:yr(t),harmonic:gr(t),melodic:br(t)}}function $r(n){return typeof n=="number"?nn("C",n):typeof n=="string"&&/^b+|#+$/.test(n)?nn("C",G(n)):null}var Nr={majorKey:Ar,majorTonicFromKeySignature:$r,minorKey:vr};const wn=[[0,2773,0,"ionian","","Maj7","major"],[1,2902,2,"dorian","m","m7"],[2,3418,4,"phrygian","m","m7"],[3,2741,-1,"lydian","","Maj7"],[4,2774,1,"mixolydian","","7"],[5,2906,3,"aeolian","m","m7","minor"],[6,3434,5,"locrian","dim","m7b5"]],Bn={...T,name:"",alt:0,modeNum:NaN,triad:"",seventh:"",aliases:[]},xn=wn.map(Tr),An={};xn.forEach(n=>{An[n.name]=n,n.aliases.forEach(t=>{An[t]=n})});function V(n){return typeof n=="string"?An[n.toLowerCase()]||Bn:n&&n.name?V(n.name):Bn}const Ir=g("Mode.mode","Mode.get",V);function wt(){return xn.slice()}const jr=g("Mode.mode","Mode.all",wt);function Sr(){return xn.map(n=>n.name)}function Tr(n){const[t,e,o,r,i,a,s]=n,c=s?[s]:[],l=Number(e).toString(2);return{empty:!1,intervals:un(r).intervals,modeNum:t,chroma:l,normalized:l,name:r,setNum:e,alt:o,triad:i,seventh:a,aliases:c}}function Vr(n,t){return V(n).intervals.map(e=>P(t,e))}function xt(n){return(t,e)=>{const o=V(t);if(o.empty)return[];const r=H(o.modeNum,n),i=o.intervals.map(a=>P(e,a));return r.map((a,s)=>i[s]+a)}}const _r=xt(wn.map(n=>n[4])),Cr=xt(wn.map(n=>n[5]));function Et(n,t){const e=V(t),o=V(n);return e.empty||o.empty?"":ft(pt("1P",o.alt-e.alt))}function wr(n,t,e){return P(e,Et(n,t))}var xr={get:V,names:Sr,all:wt,distance:Et,relativeTonic:wr,notes:Vr,triads:_r,seventhChords:Cr,entries:jr,mode:Ir};function Er(n,t){return t.map($).map(o=>P(n,f(o))+o.chordType)}function Dr(n,t){return t.map(e=>{const[o,r]=ln(e),i=D(n,o);return $(f(i)).name+r})}var Rr={fromRomanNumerals:Er,toRomanNumerals:Dr};function Dt(n){const t=Nn(n.map(gt));return!n.length||t.length!==n.length?[]:t.reduce((e,o)=>{const r=e[e.length-1];return e.concat(an(r,o).slice(1))},[t[0]])}function kr(n,t){return Dt(n).map(e=>I(e,t))}var Or={numeric:Dt,chromatic:kr};const Fr={empty:!0,name:"",type:"",tonic:null,setNum:NaN,chroma:"",normalized:"",aliases:[],notes:[],intervals:[]};function Rt(n){if(typeof n!="string")return["",""];const t=n.indexOf(" "),e=m(n.substring(0,t));if(e.empty){const r=m(n);return r.empty?["",n]:[r.name,""]}const o=n.substring(e.name.length+1);return[e.name,o.length?o:""]}const zr=ut;function N(n){const t=Array.isArray(n)?n:Rt(n),e=m(t[0]).name,o=un(t[1]);if(o.empty)return Fr;const r=o.name,i=e?o.intervals.map(s=>P(e,s)):[],a=e?e+" "+r:r;return{...o,name:a,type:r,tonic:e,notes:i}}const qr=g("Scale.scale","Scale.get",N);function Gr(n){const t=N(n),e=sn(t.chroma);return k().filter(o=>e(o.chroma)).map(o=>o.aliases[0])}function Br(n){const t=N(n),e=cn(t.chroma);return L().filter(o=>e(o.chroma)).map(o=>o.name)}function Hr(n){const t=sn(N(n).chroma);return L().filter(e=>t(e.chroma)).map(e=>e.name)}function kt(n){const t=n.map(r=>m(r).pc).filter(r=>r),e=t[0],o=jt(t);return H(o.indexOf(e),o)}function Lr(n){const t=N(n);if(t.empty)return[];const e=t.tonic?t.notes:t.intervals;return jn(t.chroma).map((o,r)=>{const i=N(o).name;return i?[e[r],i]:["",""]}).filter(o=>o[0])}function Ur(n){const t=Array.isArray(n)?kt(n):N(n).notes,e=t.map(o=>m(o).chroma);return o=>{const r=m(typeof o=="number"?vt(o):o),i=r.height;if(i===void 0)return;const a=i%12,s=e.indexOf(a);if(s!==-1)return St(r.name,t[s])}}function Xr(n){const t=Ur(n);return(e,o)=>{const r=m(e).height,i=m(o).height;return r===void 0||i===void 0?[]:an(r,i).map(t).filter(a=>a)}}var tn={get:N,names:zr,extended:Br,modeNames:Lr,reduced:Hr,scaleChords:Gr,scaleNotes:kt,tokenize:Rt,rangeOf:Xr,scale:qr};const Kr={empty:!0,name:"",upper:void 0,lower:void 0,type:void 0,additive:[]},Qr=["4/4","3/4","2/4","2/2","12/8","9/8","6/8","3/8"];function Yr(){return Qr.slice()}const Zr=/^(\d?\d(?:\+\d)*)\/(\d)$/,Hn=new Map;function Jr(n){const t=Hn.get(n);if(t)return t;const e=ni(En(n));return Hn.set(n,e),e}function En(n){if(typeof n=="string"){const[i,a,s]=Zr.exec(n)||[];return En([a,s])}const[t,e]=n,o=+e;if(typeof t=="number")return[t,o];const r=t.split("+").map(i=>+i);return r.length===1?[r[0],o]:[r,o]}var Wr={names:Yr,parse:En,get:Jr};function ni([n,t]){const e=Array.isArray(n)?n.reduce((s,c)=>s+c,0):n,o=t;if(e===0||o===0)return Kr;const r=Array.isArray(n)?`${n.join("+")}/${t}`:`${n}/${t}`,i=Array.isArray(n)?n:[],a=o===4||o===2?"simple":o===8&&e%3===0?"compound":"irregular";return{empty:!1,name:r,type:a,upper:e,lower:o,additive:i}}const ti=Wn,ei=st,oi=mt,ri=dt,ii=Object.freeze(Object.defineProperty({__proto__:null,Array:Ae,Core:Wn,ChordDictionary:oi,PcSet:ei,ScaleDictionary:ri,Tonal:ti,AbcNotation:Me,Chord:fo,ChordType:mt,Collection:Ie,DurationValue:vo,Interval:bn,Key:Nr,Midi:Go,Mode:xr,Note:w,Pcset:st,Progression:Rr,Range:Or,RomanNumeral:fr,Scale:tn,ScaleType:dt,TimeSignature:Wr,accToAlt:G,altToAcc:E,coordToInterval:B,coordToNote:F,decode:on,deprecate:g,distance:D,encode:en,fillStr:C,interval:f,isNamed:z,isPitch:q,note:m,stepToLetter:vn,tokenizeInterval:$n,tokenizeNote:rn,transpose:P},Symbol.toStringTag,{value:"Module"}));function Ot(n,t,e){let[o,r]=tn.tokenize(n),{notes:i}=tn.get(`${o} ${r}`);if(i=i.map(h=>w.get(h).pc),t=Number(t),isNaN(t))throw new Error(`scale offset "${t}" not a number`);const{pc:a,oct:s=3}=w.get(e),c=i.indexOf(a);if(c===-1)throw new Error(`note "${e}" is not in scale "${n}"`);let l=c,u=s,d=a;const M=Math.sign(t);for(;Math.abs(l-c)0&&d[0]==="C"&&(u+=M)}return d+u}_.prototype._transpose=function(n){return this._withHap(t=>{const e=isNaN(Number(n))?String(n):bn.fromSemitones(n);if(typeof t.value=="number"){const o=typeof e=="string"?bn.semitones(e)||0:e;return t.withValue(()=>t.value+o)}return t.withValue(()=>w.simplify(w.transpose(t.value,e)))})};_.prototype._scaleTranspose=function(n){return this._withHap(t=>{if(!t.context.scale)throw new Error("can only use scaleTranspose after .scale");if(typeof t.value!="string")throw new Error("can only use scaleTranspose with notes");return t.withValue(()=>Ot(t.context.scale,Number(n),t.value))})};_.prototype._scale=function(n){return this._withHap(t=>{let e=t.value;const o=Number(e);if(!isNaN(o)){let[r,i]=tn.tokenize(n);const{pc:a,oct:s=3}=w.get(r);e=Ot(a+" "+i,o,a+s)}return t.withValue(()=>e).setContext({...t.context,scale:n})})};_.prototype.define("transpose",(n,t)=>t.transpose(n),{composable:!0,patternified:!0});_.prototype.define("scale",(n,t)=>t.scale(n),{composable:!0,patternified:!0});_.prototype.define("scaleTranspose",(n,t)=>t.scaleTranspose(n),{composable:!0,patternified:!0});var Ft={},hn={},X={};X.__esModule=!0;X.getBestVoicing=void 0;function ai(n){var t=n.chord,e=n.range,o=n.finder,r=n.picker,i=n.lastVoicing,a=o(t,e);return a.length?r(a,i):[]}X.getBestVoicing=ai;var O={};const zt=Ut(ii);var K={};K.__esModule=!0;K.tokenizeChord=void 0;function si(n){var t=(n||"").match(/^([A-G][b#]*)([^\/]*)[\/]?([A-G][b#]*)?$/);return t?t.slice(1):[]}K.tokenizeChord=si;var Ln;function ci(){if(Ln)return O;Ln=1,O.__esModule=!0,O.voicingsInRange=void 0;var n=zt,t=qt(),e=K;function o(r,i,a){i===void 0&&(i=t.lefthand),a===void 0&&(a=["D3","A4"]);var s=(0,e.tokenizeChord)(r),c=s[0],l=s[1];if(!i[l])return[];var u=i[l].map(function(M){return M.split(" ")}),d=n.Range.chromatic(a);return u.reduce(function(M,h){var A=h.map(function(v){return n.Interval.substract(v,h[0])}),Q=n.Note.transpose(c,h[0]),Gt=d.filter(function(v){return n.Note.chroma(v)===n.Note.chroma(Q)}).filter(function(v){return n.Note.midi(n.Note.transpose(v,A[A.length-1]))<=n.Note.midi(a[1])}).map(function(v){return n.Note.enharmonic(v,Q)}),Bt=Gt.map(function(v){return A.map(function(Ht){return n.Note.transpose(v,Ht)})});return M.concat(Bt)},[])}return O.voicingsInRange=o,O}var Un;function qt(){return Un||(Un=1,function(n){var t=Y&&Y.__assign||function(){return t=Object.assign||function(s){for(var c,l=1,u=arguments.length;lui({chord:n,dictionary:di,range:e,picker:li,lastVoicing:t}),x=_;x.prototype.fmapNested=function(n){return new x(t=>this.query(t).map(e=>Kt(n(e)).query(t).map(o=>new Qt(e.whole,e.part,o.value,o.context))).flat())};x.prototype.voicings=function(n){let t;return n!=null&&n.length||(n=["F3","A4"]),this.fmapNested(e=>(t=Mi(e.value,t,n),Yt(...t)._withContext(()=>({locations:e.context.locations||[]}))))};x.prototype._rootNotes=function(n=2){return this.fmap(t=>{const[e,o]=t.match(/^([a-gA-G][b#]?).*$/);return o+n})};x.prototype.define("voicings",(n,t)=>t.voicings(n),{composable:!0});x.prototype.define("rootNotes",(n,t)=>t.rootNotes(n),{composable:!0,patternified:!0}); diff --git a/packages/core/examples/vite-vanilla-repl/dist/assets/index.ecddf978.js b/packages/core/examples/vite-vanilla-repl/dist/assets/index.ecddf978.js deleted file mode 100644 index 7e53038e..00000000 --- a/packages/core/examples/vite-vanilla-repl/dist/assets/index.ecddf978.js +++ /dev/null @@ -1 +0,0 @@ -import{g as s,f as d,i as t,n as r,l as u,j as o,d as f,k as p,r as g,s as i,w as l,e as m}from"./index.4cbc0a10.js";export{s as getAudioContext,d as getCachedBuffer,t as getLoadedBuffer,r as getLoadedSamples,u as loadBuffer,o as loadGithubSamples,f as panic,p as resetLoadedSamples,g as reverseBuffer,i as samples,l as webaudioOutput,m as webaudioOutputTrigger}; diff --git a/packages/core/examples/vite-vanilla-repl/dist/index.html b/packages/core/examples/vite-vanilla-repl/dist/index.html deleted file mode 100644 index 614a6b6c..00000000 --- a/packages/core/examples/vite-vanilla-repl/dist/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Vite Vanilla Strudel REPL - - - -
- -
- -
- - - diff --git a/packages/core/gist.js b/packages/core/gist.js deleted file mode 100644 index 9df2dda2..00000000 --- a/packages/core/gist.js +++ /dev/null @@ -1,12 +0,0 @@ -/* -gist.js - -Copyright (C) 2022 Strudel contributors - see -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 . -*/ - -// this is a shortcut to eval code from a gist -// why? to be able to shorten strudel code + e.g. be able to change instruments after links have been generated -export default (route, cache = true) => - fetch(`https://gist.githubusercontent.com/${route}?cachebust=${cache ? '' : Date.now()}`) - .then((res) => res.text()) - .then((code) => eval(code)); diff --git a/packages/core/index.mjs b/packages/core/index.mjs index bed63f9a..f4598f11 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -27,7 +27,6 @@ export * from './pianoroll.mjs'; export * from './spiral.mjs'; export * from './ui.mjs'; export { default as drawLine } from './drawLine.mjs'; -export { default as gist } from './gist.js'; // below won't work with runtime.mjs (json import fails) /* import * as p from './package.json'; export const version = p.version; */ diff --git a/packages/core/pianoroll.mjs b/packages/core/pianoroll.mjs index 254dd94a..4a8fd8db 100644 --- a/packages/core/pianoroll.mjs +++ b/packages/core/pianoroll.mjs @@ -256,10 +256,13 @@ export function getDrawOptions(drawTime, options = {}) { return { fold: 1, ...options, cycles, playhead }; } +export const getPunchcardPainter = + (options = {}) => + (ctx, time, haps, drawTime, paintOptions = {}) => + pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) }); + Pattern.prototype.punchcard = function (options) { - return this.onPaint((ctx, time, haps, drawTime, paintOptions = {}) => - pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) }), - ); + return this.onPaint(getPunchcardPainter(options)); }; /** diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index 0bd5229d..9fb6b4b9 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -16,13 +16,36 @@ export function repl({ transpiler, onToggle, editPattern, + onUpdateState, }) { + const state = { + schedulerError: undefined, + evalError: undefined, + code: '// LOADING', + activeCode: '// LOADING', + pattern: undefined, + miniLocations: [], + widgets: [], + pending: false, + started: false, + }; + + const updateState = (update) => { + Object.assign(state, update); + state.isDirty = state.code !== state.activeCode; + state.error = state.evalError || state.schedulerError; + onUpdateState?.(state); + }; + const scheduler = new Cyclist({ interval, onTrigger: getTrigger({ defaultOutput, getTime }), onError: onSchedulerError, getTime, - onToggle, + onToggle: (started) => { + updateState({ started }); + onToggle?.(started); + }, }); let pPatterns = {}; let allTransform; @@ -43,6 +66,7 @@ export function repl({ throw new Error('no code to evaluate'); } try { + updateState({ code, pending: true }); await beforeEval?.({ code }); shouldHush && hush(); let { pattern, meta } = await _evaluate(code, transpiler); @@ -58,17 +82,28 @@ export function repl({ } logger(`[eval] code updated`); setPattern(pattern, autostart); + updateState({ + miniLocations: meta?.miniLocations || [], + widgets: meta?.widgets || [], + activeCode: code, + pattern, + evalError: undefined, + schedulerError: undefined, + pending: false, + }); afterEval?.({ code, pattern, meta }); return pattern; } catch (err) { // console.warn(`[repl] eval error: ${err.message}`); logger(`[eval] error: ${err.message}`, 'error'); + updateState({ evalError: err, pending: false }); onEvalError?.(err); } }; const stop = () => scheduler.stop(); const start = () => scheduler.start(); const pause = () => scheduler.pause(); + const toggle = () => scheduler.toggle(); const setCps = (cps) => scheduler.setCps(cps); const setCpm = (cpm) => scheduler.setCps(cpm / 60); @@ -127,8 +162,8 @@ export function repl({ setCpm, setcpm: setCpm, }); - - return { scheduler, evaluate, start, stop, pause, setCps, setPattern }; + const setCode = (code) => updateState({ code }); + return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state }; } export const getTrigger = diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 695eaacf..2b190c82 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -274,3 +274,31 @@ export const sol2note = (n, notation = 'letters') => { const oct = Math.floor(n / 12) - 1; return note + oct; }; + +// code hashing helpers + +export function unicodeToBase64(text) { + const utf8Bytes = new TextEncoder().encode(text); + const base64String = btoa(String.fromCharCode(...utf8Bytes)); + return base64String; +} + +export function base64ToUnicode(base64String) { + const utf8Bytes = new Uint8Array( + atob(base64String) + .split('') + .map((char) => char.charCodeAt(0)), + ); + const decodedText = new TextDecoder().decode(utf8Bytes); + return decodedText; +} + +export function code2hash(code) { + return encodeURIComponent(unicodeToBase64(code)); + //return '#' + encodeURIComponent(btoa(code)); +} + +export function hash2code(hash) { + return base64ToUnicode(decodeURIComponent(hash)); + //return atob(decodeURIComponent(codeParam || '')); +} diff --git a/packages/react/.gitignore b/packages/react/.gitignore deleted file mode 100644 index 251ce6d2..00000000 --- a/packages/react/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/packages/react/.npmignore b/packages/react/.npmignore deleted file mode 100644 index b7265688..00000000 --- a/packages/react/.npmignore +++ /dev/null @@ -1 +0,0 @@ -examples \ No newline at end of file diff --git a/packages/react/README.md b/packages/react/README.md deleted file mode 100644 index 4b55adbb..00000000 --- a/packages/react/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# @strudel.cycles/react - -This package contains react hooks and components for strudel. It is used internally by the Strudel REPL. - -## Install - -```js -npm i @strudel.cycles/react -``` - -## Usage - -Here is a minimal example of how to set up a MiniRepl: - -```jsx -import * as React from 'react'; -import '@strudel.cycles/react/dist/style.css'; -import { MiniRepl } from '@strudel.cycles/react'; -import { evalScope, controls } from '@strudel.cycles/core'; -import { samples, initAudioOnFirstClick } from '@strudel.cycles/webaudio'; - -async function prebake() { - await samples( - 'https://strudel.cc/tidal-drum-machines.json', - 'github:ritchse/tidal-drum-machines/main/machines/' - ); - await samples( - 'https://strudel.cc/EmuSP12.json', - 'https://strudel.cc/EmuSP12/' - ); -} - -async function init() { - await evalScope( - controls, - import('@strudel.cycles/core'), - import('@strudel.cycles/mini'), - import('@strudel.cycles/webaudio'), - import('@strudel.cycles/tonal') - ); - await prebake(); - initAudioOnFirstClick(); -} - -if (typeof window !== 'undefined') { - init(); -} - -export default function App() { - return ; -} -``` - -- Open [example on stackblitz](https://stackblitz.com/edit/react-ts-saaair?file=tune.tsx,App.tsx) -- Also check out the [nano-repl](./examples/nano-repl/) for a more sophisticated example diff --git a/packages/react/examples/nano-repl/README.md b/packages/react/examples/nano-repl/README.md deleted file mode 100644 index 5a8de496..00000000 --- a/packages/react/examples/nano-repl/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# nano-repl - -this is an example of how to create a repl with strudel and react. - -## Usage - -after cloning the strudel repo: - -```sh -pnpm i -cd packages/react/examples/nano-repl -pnpm dev -``` - -you should now have a repl running at `http://localhost:5173/` diff --git a/packages/react/examples/nano-repl/index.html b/packages/react/examples/nano-repl/index.html deleted file mode 100644 index 4d5e8670..00000000 --- a/packages/react/examples/nano-repl/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Strudel Nano REPL - - -
- - - diff --git a/packages/react/examples/nano-repl/package.json b/packages/react/examples/nano-repl/package.json deleted file mode 100644 index c837e4b7..00000000 --- a/packages/react/examples/nano-repl/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@strudel.cycles/nano-repl", - "private": true, - "version": "0.6.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0", - "@strudel.cycles/core": "workspace:*", - "@strudel.cycles/osc": "workspace:*", - "@strudel.cycles/mini": "workspace:*", - "@strudel.cycles/transpiler": "workspace:*", - "@strudel.cycles/soundfonts": "workspace:*", - "@strudel.cycles/webaudio": "workspace:*", - "@strudel.cycles/tonal": "workspace:*", - "@strudel.cycles/react": "workspace:*" - }, - "devDependencies": { - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.1", - "@vitejs/plugin-react": "^4.0.0", - "autoprefixer": "^10.4.14", - "postcss": "^8.4.23", - "tailwindcss": "^3.3.2", - "vite": "^4.3.3" - } -} diff --git a/packages/react/examples/nano-repl/src/App.jsx b/packages/react/examples/nano-repl/src/App.jsx deleted file mode 100644 index 086923ac..00000000 --- a/packages/react/examples/nano-repl/src/App.jsx +++ /dev/null @@ -1,144 +0,0 @@ -import { controls, evalScope } from '@strudel.cycles/core'; -import { CodeMirror, useHighlighting, useKeydown, useStrudel, flash } from '@strudel.cycles/react'; -import { - getAudioContext, - initAudioOnFirstClick, - panic, - webaudioOutput, - registerSynthSounds, -} from '@strudel.cycles/webaudio'; -import { registerSoundfonts } from '@strudel.cycles/soundfonts'; -import { useCallback, useState } from 'react'; -import './style.css'; -// import { prebake } from '../../../../../repl/src/prebake.mjs'; - -initAudioOnFirstClick(); - -async function init() { - // TODO: only import stuff when play is pressed? - const loadModules = evalScope( - controls, - import('@strudel.cycles/core'), - import('@strudel.cycles/tonal'), - import('@strudel.cycles/mini'), - import('@strudel.cycles/xen'), - import('@strudel.cycles/webaudio'), - import('@strudel.cycles/osc'), - ); - - await Promise.all([loadModules, registerSynthSounds(), registerSoundfonts()]); -} -init(); - -const defaultTune = `samples({ - bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'], - sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'], - hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'], -}, 'github:tidalcycles/Dirt-Samples/master/'); -stack( - s("bd,[~ ],hh*8") // drums - .speed(perlin.range(.7,.9)) // random sample speed variation - //.hush() - ,"" // bassline - .off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps - .add(perlin.range(0,.5)) // random pitch variation - .superimpose(add(.05)) // add second, slightly detuned voice - .note() // wrap in "note" - .decay(.15).sustain(0) // make each note of equal length - .s('sawtooth') // waveform - .gain(.4) // turn down - .cutoff(sine.slow(7).range(300,5000)) // automate cutoff - //.hush() - ,">".voicings('lefthand') // chords - .superimpose(x=>x.add(.04)) // add second, slightly detuned voice - .add(perlin.range(0,.5)) // random pitch variation - .note() // wrap in "n" - .s('square') // waveform - .gain(.16) // turn down - .cutoff(500) // fixed cutoff - .attack(1) // slowly fade in - //.hush() - ,"a4 c5 ".struct("x(5,8)") - .superimpose(x=>x.add(.04)) // add second, slightly detuned voice - .add(perlin.range(0,.5)) // random pitch variation - .note() // wrap in "note" - .decay(.1).sustain(0) // make notes short - .s('triangle') // waveform - .degradeBy(perlin.range(0,.5)) // randomly controlled random removal :) - .echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes - //.hush() -) -.fast(2/3)`; - -// await prebake(); - -const ctx = getAudioContext(); -const getTime = () => ctx.currentTime; -function App() { - const [code, setCode] = useState(defaultTune); - const [view, setView] = useState(); - // const [code, setCode] = useState(`"c3".note().slow(2)`); - const { scheduler, evaluate, schedulerError, evalError, isDirty, activeCode, pattern, started } = useStrudel({ - code, - defaultOutput: webaudioOutput, - getTime, - afterEval: ({ meta }) => setMiniLocations(meta.miniLocations), - }); - - const { setMiniLocations } = useHighlighting({ - view, - pattern, - active: started && !activeCode?.includes('strudel disable-highlighting'), - getTime: () => scheduler.now(), - }); - - const error = evalError || schedulerError; - useKeydown( - useCallback( - async (e) => { - if (e.ctrlKey || e.altKey) { - if (e.code === 'Enter') { - e.preventDefault(); - flash(view); - await evaluate(code); - if (e.shiftKey) { - panic(); - scheduler.stop(); - scheduler.start(); - } - if (!scheduler.started) { - scheduler.start(); - } - } else if (e.code === 'Period') { - scheduler.stop(); - panic(); - e.preventDefault(); - } - } - }, - [scheduler, evaluate, view, code], - ), - ); - return ( -
- - -
- ); -} - -export default App; diff --git a/packages/react/examples/nano-repl/src/main.jsx b/packages/react/examples/nano-repl/src/main.jsx deleted file mode 100644 index 1b794581..00000000 --- a/packages/react/examples/nano-repl/src/main.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App'; - -ReactDOM.createRoot(document.getElementById('root')).render( - - - , -); diff --git a/packages/react/examples/nano-repl/src/style.css b/packages/react/examples/nano-repl/src/style.css deleted file mode 100644 index e01c06ef..00000000 --- a/packages/react/examples/nano-repl/src/style.css +++ /dev/null @@ -1,19 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --background: #222; - --lineBackground: #22222250; - --foreground: #fff; - --caret: #ffcc00; - --selection: rgba(128, 203, 196, 0.5); - --selectionMatch: #036dd626; - --lineHighlight: #00000050; - --gutterBackground: transparent; - --gutterForeground: #8a919966; -} - -body { - background: #123; -} diff --git a/packages/react/examples/nano-repl/tailwind.config.cjs b/packages/react/examples/nano-repl/tailwind.config.cjs deleted file mode 100644 index b2a25c69..00000000 --- a/packages/react/examples/nano-repl/tailwind.config.cjs +++ /dev/null @@ -1,28 +0,0 @@ -/* -tailwind.config.js - -Copyright (C) 2022 Strudel contributors - see -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 . -*/ - -module.exports = { - // TODO: find out if leaving out tutorial path works now - content: ['./src/**/*.{js,jsx,ts,tsx}', '../../src/**/*.{html,js,jsx,md,mdx,ts,tsx}'], - theme: { - extend: { - colors: { - // codemirror-theme settings - background: 'var(--background)', - lineBackground: 'var(--lineBackground)', - foreground: 'var(--foreground)', - caret: 'var(--caret)', - selection: 'var(--selection)', - selectionMatch: 'var(--selectionMatch)', - gutterBackground: 'var(--gutterBackground)', - gutterForeground: 'var(--gutterForeground)', - gutterBorder: 'var(--gutterBorder)', - lineHighlight: 'var(--lineHighlight)', - }, - }, - }, - plugins: [], -}; diff --git a/packages/react/examples/nano-repl/vite.config.js b/packages/react/examples/nano-repl/vite.config.js deleted file mode 100644 index 627a3196..00000000 --- a/packages/react/examples/nano-repl/vite.config.js +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}); diff --git a/packages/react/index.html b/packages/react/index.html deleted file mode 100644 index 83d2bd57..00000000 --- a/packages/react/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Strudel React Components - - -
- - - diff --git a/packages/react/package.json b/packages/react/package.json deleted file mode 100644 index df017d91..00000000 --- a/packages/react/package.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "name": "@strudel.cycles/react", - "version": "0.9.0", - "description": "React components for strudel", - "main": "src/index.js", - "publishConfig": { - "main": "dist/index.js", - "module": "dist/index.mjs" - }, - "scripts": { - "dev": "vite", - "build": "vite build", - "watch": "vite build --watch", - "preview": "vite preview", - "prepublishOnly": "npm run build" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" - }, - "keywords": [ - "tidalcycles", - "strudel", - "pattern", - "livecoding", - "algorave" - ], - "author": "Felix Roos ", - "license": "AGPL-3.0-or-later", - "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" - }, - "homepage": "https://github.com/tidalcycles/strudel#readme", - "dependencies": { - "@codemirror/autocomplete": "^6.6.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/lang-javascript": "^6.1.7", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.10.0", - "@lezer/highlight": "^1.1.4", - "@replit/codemirror-emacs": "^6.0.1", - "@replit/codemirror-vim": "^6.0.14", - "@replit/codemirror-vscode-keymap": "^6.0.2", - "@strudel.cycles/core": "workspace:*", - "@strudel.cycles/transpiler": "workspace:*", - "@strudel.cycles/webaudio": "workspace:*", - "@strudel/codemirror": "workspace:*", - "@uiw/codemirror-themes": "^4.19.16", - "@uiw/react-codemirror": "^4.19.16", - "react-hook-inview": "^4.5.0" - }, - "peerDependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.1", - "@vitejs/plugin-react": "^4.0.0", - "autoprefixer": "^10.4.14", - "postcss": "^8.4.23", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "tailwindcss": "^3.3.2", - "vite": "^4.3.3" - } -} diff --git a/packages/react/postcss.config.js b/packages/react/postcss.config.js deleted file mode 100644 index b77b9fe4..00000000 --- a/packages/react/postcss.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/* -postcss.config.js - -Copyright (C) 2022 Strudel contributors - see -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 . -*/ - -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/packages/react/src/App.jsx b/packages/react/src/App.jsx deleted file mode 100644 index ba7b1da4..00000000 --- a/packages/react/src/App.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { MiniRepl } from './components/MiniRepl'; -import 'tailwindcss/tailwind.css'; -import { controls, evalScope } from '@strudel.cycles/core'; - -evalScope( - controls, - import('@strudel.cycles/core'), - import('@strudel.cycles/tonal'), - import('@strudel.cycles/mini'), - import('@strudel.cycles/webaudio'), -); - -function App() { - return ( -
- -
- ); -} - -export default App; diff --git a/packages/react/src/components/Autocomplete.jsx b/packages/react/src/components/Autocomplete.jsx deleted file mode 100644 index 9a0b9de4..00000000 --- a/packages/react/src/components/Autocomplete.jsx +++ /dev/null @@ -1,89 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import jsdoc from '../../../../doc.json'; - -const getDocLabel = (doc) => doc.name || doc.longname; -const getDocSynonyms = (doc) => [getDocLabel(doc), ...(doc.synonyms || [])]; -const getInnerText = (html) => { - var div = document.createElement('div'); - div.innerHTML = html; - return div.textContent || div.innerText || ''; -}; - -export function Autocomplete({ doc, label = getDocLabel(doc) }) { - const synonyms = getDocSynonyms(doc).filter((a) => a !== label); - return ( -
-

{label}

{' '} - {!!synonyms.length && ( - - Synonyms: {synonyms.join(', ')} - - )} -
-
    - {doc.params?.map(({ name, type, description }, i) => ( -
  • - {name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)} : ''} -
  • - ))} -
-
- {doc.examples?.map((example, i) => ( -
-
 {
-                navigator.clipboard.writeText(example);
-                e.stopPropagation();
-              }}
-            >
-              {example}
-            
-
- ))} -
-
- ); -} - -const jsdocCompletions = jsdoc.docs - .filter( - (doc) => - getDocLabel(doc) && - !getDocLabel(doc).startsWith('_') && - !['package'].includes(doc.kind) && - !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)), - ) - // https://codemirror.net/docs/ref/#autocomplete.Completion - .reduce( - (acc, doc) /*: Completion */ => - acc.concat( - [getDocLabel(doc), ...(doc.synonyms || [])].map((label) => ({ - label, - // detail: 'xxx', // An optional short piece of information to show (with a different style) after the label. - info: () => { - const node = document.createElement('div'); - // if Autocomplete is non-interactive, it could also be rendered at build time.. - // .. using renderToStaticMarkup - createRoot(node).render(); - return node; - }, - type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type - })), - ), - [], - ); - -export const strudelAutocomplete = (context /* : CompletionContext */) => { - let word = context.matchBefore(/\w*/); - if (word.from == word.to && !context.explicit) return null; - return { - from: word.from, - options: jsdocCompletions, - /* options: [ - { label: 'match', type: 'keyword' }, - { label: 'hello', type: 'variable', info: '(World)' }, - { label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' }, - ], */ - }; -}; diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx deleted file mode 100644 index 4852b287..00000000 --- a/packages/react/src/components/CodeMirror6.jsx +++ /dev/null @@ -1,134 +0,0 @@ -import { autocompletion } from '@codemirror/autocomplete'; -import { Prec } from '@codemirror/state'; -import { javascript, javascriptLanguage } from '@codemirror/lang-javascript'; -import { ViewPlugin, EditorView, keymap } from '@codemirror/view'; -import { emacs } from '@replit/codemirror-emacs'; -import { vim } from '@replit/codemirror-vim'; -import { vscodeKeymap } from '@replit/codemirror-vscode-keymap'; -import _CodeMirror from '@uiw/react-codemirror'; -import React, { useCallback, useMemo } from 'react'; -import strudelTheme from '../themes/strudel-theme'; -import { strudelAutocomplete } from './Autocomplete'; -import { strudelTooltip } from './Tooltip'; -import { - highlightExtension, - flashField, - flash, - highlightMiniLocations, - updateMiniLocations, -} from '@strudel/codemirror'; -import './style.css'; -import { sliderPlugin } from '@strudel/codemirror/slider.mjs'; - -export { flash, highlightMiniLocations, updateMiniLocations }; - -const staticExtensions = [javascript(), flashField, highlightExtension, sliderPlugin]; - -export default function CodeMirror({ - value, - onChange, - onViewChanged, - onSelectionChange, - onDocChange, - theme, - keybindings, - isLineNumbersDisplayed, - isActiveLineHighlighted, - isAutoCompletionEnabled, - isTooltipEnabled, - isLineWrappingEnabled, - fontSize = 18, - fontFamily = 'monospace', -}) { - const handleOnChange = useCallback( - (value) => { - onChange?.(value); - }, - [onChange], - ); - - const handleOnCreateEditor = useCallback( - (view) => { - onViewChanged?.(view); - }, - [onViewChanged], - ); - - const handleOnUpdate = useCallback( - (viewUpdate) => { - if (viewUpdate.docChanged && onDocChange) { - onDocChange?.(viewUpdate); - } - if (viewUpdate.selectionSet && onSelectionChange) { - onSelectionChange?.(viewUpdate.state.selection); - } - }, - [onSelectionChange], - ); - - const vscodePlugin = ViewPlugin.fromClass( - class { - constructor(view) {} - }, - { - provide: (plugin) => { - return Prec.highest(keymap.of([...vscodeKeymap])); - }, - }, - ); - - const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []); - - const extensions = useMemo(() => { - let _extensions = [...staticExtensions]; - let bindings = { - vim, - emacs, - vscode: vscodeExtension, - }; - - if (bindings[keybindings]) { - _extensions.push(bindings[keybindings]()); - } - - if (isAutoCompletionEnabled) { - _extensions.push(javascriptLanguage.data.of({ autocomplete: strudelAutocomplete })); - } else { - _extensions.push(autocompletion({ override: [] })); - } - - if (isTooltipEnabled) { - _extensions.push(strudelTooltip); - } - - _extensions.push([keymap.of({})]); - - if (isLineWrappingEnabled) { - _extensions.push(EditorView.lineWrapping); - } - - return _extensions; - }, [keybindings, isAutoCompletionEnabled, isTooltipEnabled, isLineWrappingEnabled]); - - const basicSetup = useMemo( - () => ({ - lineNumbers: isLineNumbersDisplayed, - highlightActiveLine: isActiveLineHighlighted, - }), - [isLineNumbersDisplayed, isActiveLineHighlighted], - ); - - return ( -
- <_CodeMirror - value={value} - theme={theme || strudelTheme} - onChange={handleOnChange} - onCreateEditor={handleOnCreateEditor} - onUpdate={handleOnUpdate} - extensions={extensions} - basicSetup={basicSetup} - /> -
- ); -} diff --git a/packages/react/src/components/MiniRepl.jsx b/packages/react/src/components/MiniRepl.jsx deleted file mode 100644 index c7f70907..00000000 --- a/packages/react/src/components/MiniRepl.jsx +++ /dev/null @@ -1,199 +0,0 @@ -import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; -import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react'; -import { useInView } from 'react-hook-inview'; -import 'tailwindcss/tailwind.css'; -import cx from '../cx'; -import useHighlighting from '../hooks/useHighlighting.mjs'; -import useStrudel from '../hooks/useStrudel.mjs'; -import CodeMirror6, { flash } from './CodeMirror6'; -import { Icon } from './Icon'; -import './style.css'; -import { logger } from '@strudel.cycles/core'; -import useEvent from '../hooks/useEvent.mjs'; -import useKeydown from '../hooks/useKeydown.mjs'; - -const getTime = () => getAudioContext().currentTime; - -export function MiniRepl({ - tune, - hideOutsideView = false, - enableKeyboard, - onTrigger, - drawTime, - punchcard, - punchcardLabels, - onPaint, - canvasHeight = 200, - fontSize = 18, - fontFamily, - hideHeader = false, - theme, - keybindings, - isLineNumbersDisplayed, - isActiveLineHighlighted, -}) { - drawTime = drawTime || (punchcard ? [0, 4] : undefined); - const evalOnMount = !!drawTime; - const drawContext = useCallback( - punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null, - [punchcard], - ); - const { - code, - setCode, - evaluate, - activateCode, - error, - isDirty, - activeCode, - pattern, - started, - scheduler, - togglePlay, - stop, - canvasId, - id: replId, - } = useStrudel({ - initialCode: tune, - defaultOutput: webaudioOutput, - editPattern: (pat, id) => { - //pat = pat.withContext((ctx) => ({ ...ctx, id })); - if (onTrigger) { - pat = pat.onTrigger(onTrigger, false); - } - if (onPaint) { - pat = pat.onPaint(onPaint); - } else if (punchcard) { - pat = pat.punchcard({ labels: punchcardLabels }); - } - return pat; - }, - getTime, - evalOnMount, - drawContext, - drawTime, - afterEval: ({ meta }) => setMiniLocations(meta.miniLocations), - }); - - const [view, setView] = useState(); - const [ref, isVisible] = useInView({ - threshold: 0.01, - }); - const wasVisible = useRef(); - const show = useMemo(() => { - if (isVisible || !hideOutsideView) { - wasVisible.current = true; - } - return isVisible || wasVisible.current; - }, [isVisible, hideOutsideView]); - const { setMiniLocations } = useHighlighting({ - view, - pattern, - active: started && !activeCode?.includes('strudel disable-highlighting'), - getTime: () => scheduler.now(), - }); - - // keyboard shortcuts - useKeydown( - useCallback( - async (e) => { - if (view?.hasFocus) { - if (e.ctrlKey || e.altKey) { - if (e.code === 'Enter') { - e.preventDefault(); - flash(view); - await activateCode(); - } else if (e.key === '.' || e.code === 'Period') { - stop(); - e.preventDefault(); - } - } - } - }, - [activateCode, stop, view], - ), - ); - - const [log, setLog] = useState([]); - useLogger( - useCallback((e) => { - const { data } = e.detail; - const logId = data?.hap?.context?.id; - // const logId = data?.pattern?.meta?.id; - if (logId === replId) { - setLog((l) => { - return l.concat([e.detail]).slice(-8); - }); - } - }, []), - ); - - return ( -
- {!hideHeader && ( -
-
- - -
-
- )} -
- {show && ( - - )} - {error &&
{error.message}
} -
- {punchcard && ( - { - if (el && el.width !== el.clientWidth) { - el.width = el.clientWidth; - } - }} - > - )} - {!!log.length && ( -
- {log.map(({ message }, i) => ( -
{message}
- ))} -
- )} -
- ); -} - -// TODO: dedupe -function useLogger(onTrigger) { - useEvent(logger.key, onTrigger); -} diff --git a/packages/react/src/components/style.css b/packages/react/src/components/style.css deleted file mode 100644 index 6336bba3..00000000 --- a/packages/react/src/components/style.css +++ /dev/null @@ -1,34 +0,0 @@ -:root { - --background: #222; - --lineBackground: #22222299; - --foreground: #fff; - --caret: #ffcc00; - --selection: rgba(128, 203, 196, 0.5); - --selectionMatch: #036dd626; - --lineHighlight: #00000050; - --gutterBackground: transparent; - --gutterForeground: #8a919966; -} - -.cm-editor { - background-color: transparent !important; - height: 100%; - z-index: 11; -} - -.cm-theme { - width: 100%; - height: 100%; -} - -.cm-theme-light { - width: 100%; -} - -footer { - z-index: 0 !important; -} - -.strudel-tooltip { - padding: 5px; -} diff --git a/packages/react/src/hooks/useHighlighting.mjs b/packages/react/src/hooks/useHighlighting.mjs deleted file mode 100644 index cb2ca114..00000000 --- a/packages/react/src/hooks/useHighlighting.mjs +++ /dev/null @@ -1,50 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; -import { highlightMiniLocations, updateMiniLocations } from '../components/CodeMirror6'; -const round = (x) => Math.round(x * 1000) / 1000; - -function useHighlighting({ view, pattern, active, getTime }) { - const highlights = useRef([]); - const lastEnd = useRef(0); - - const [miniLocations, setMiniLocations] = useState([]); - useEffect(() => { - if (view) { - updateMiniLocations(view, miniLocations); - } - }, [view, miniLocations]); - - useEffect(() => { - if (view) { - if (pattern && active) { - lastEnd.current = 0; - let frame = requestAnimationFrame(function updateHighlights() { - try { - const audioTime = getTime(); - // force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away - // see https://github.com/tidalcycles/strudel/issues/108 - const begin = Math.max(lastEnd.current ?? audioTime, audioTime - 1 / 10, -0.01); // negative time seems buggy - const span = [round(begin), round(audioTime + 1 / 60)]; - lastEnd.current = span[1]; - highlights.current = highlights.current.filter((hap) => hap.endClipped > audioTime); // keep only highlights that are still active - const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset()); - highlights.current = highlights.current.concat(haps); // add potential new onsets - highlightMiniLocations(view, begin, highlights.current); - } catch (err) { - highlightMiniLocations(view, 0, []); - } - frame = requestAnimationFrame(updateHighlights); - }); - return () => { - cancelAnimationFrame(frame); - }; - } else { - highlights.current = []; - highlightMiniLocations(view, 0, highlights.current); - } - } - }, [pattern, active, view]); - - return { setMiniLocations }; -} - -export default useHighlighting; diff --git a/packages/react/src/hooks/useKeydown.mjs b/packages/react/src/hooks/useKeydown.mjs deleted file mode 100644 index 88d1cb94..00000000 --- a/packages/react/src/hooks/useKeydown.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import { useLayoutEffect } from 'react'; - -// set active pattern on ctrl+enter -const useKeydown = (callback) => - useLayoutEffect(() => { - window.addEventListener('keydown', callback, true); - return () => window.removeEventListener('keydown', callback, true); - }, [callback]); - -export default useKeydown; diff --git a/packages/react/src/hooks/usePatternFrame.mjs b/packages/react/src/hooks/usePatternFrame.mjs deleted file mode 100644 index 725fe0a3..00000000 --- a/packages/react/src/hooks/usePatternFrame.mjs +++ /dev/null @@ -1,48 +0,0 @@ -import { useCallback, useEffect, useRef } from 'react'; -import 'tailwindcss/tailwind.css'; -import useFrame from '../hooks/useFrame.mjs'; - -function usePatternFrame({ pattern, started, getTime, onDraw, drawTime = [-2, 2] }) { - let [lookbehind, lookahead] = drawTime; - lookbehind = Math.abs(lookbehind); - let visibleHaps = useRef([]); - let lastFrame = useRef(null); - useEffect(() => { - if (pattern && started) { - const t = getTime(); - const futureHaps = pattern.queryArc(Math.max(t, 0), t + lookahead + 0.1); // +0.1 = workaround for weird holes in query.. - visibleHaps.current = visibleHaps.current.filter((h) => h.whole.begin < t); - visibleHaps.current = visibleHaps.current.concat(futureHaps); - } - }, [pattern, started]); - const { start: startFrame, stop: stopFrame } = useFrame( - useCallback(() => { - const phase = getTime() + lookahead; - if (lastFrame.current === null) { - lastFrame.current = phase; - return; - } - const haps = pattern.queryArc(Math.max(lastFrame.current, phase - 1 / 10), phase); - lastFrame.current = phase; - visibleHaps.current = (visibleHaps.current || []) - .filter((h) => h.endClipped >= phase - lookbehind - lookahead) // in frame - .concat(haps.filter((h) => h.hasOnset())); - onDraw(pattern, phase - lookahead, visibleHaps.current, drawTime); - }, [pattern, onDraw]), - ); - useEffect(() => { - if (started) { - startFrame(); - } else { - visibleHaps.current = []; - stopFrame(); - } - }, [started]); - return { - clear: () => { - visibleHaps.current = []; - }, - }; -} - -export default usePatternFrame; diff --git a/packages/react/src/hooks/usePostMessage.mjs b/packages/react/src/hooks/usePostMessage.mjs deleted file mode 100644 index 9d3bc8e7..00000000 --- a/packages/react/src/hooks/usePostMessage.mjs +++ /dev/null @@ -1,17 +0,0 @@ -/* -usePostMessage.mjs - -Copyright (C) 2022 Strudel contributors - see -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 . -*/ - -import { useEffect, useCallback } from 'react'; - -function usePostMessage(listener) { - useEffect(() => { - window.addEventListener('message', listener); - return () => window.removeEventListener('message', listener); - }, [listener]); - return useCallback((data) => window.postMessage(data, '*'), []); -} - -export default usePostMessage; diff --git a/packages/react/src/hooks/useStrudel.mjs b/packages/react/src/hooks/useStrudel.mjs deleted file mode 100644 index dbf2269e..00000000 --- a/packages/react/src/hooks/useStrudel.mjs +++ /dev/null @@ -1,171 +0,0 @@ -import { useRef, useCallback, useEffect, useMemo, useState } from 'react'; -import { repl } from '@strudel.cycles/core'; -import { transpiler } from '@strudel.cycles/transpiler'; -import usePatternFrame from './usePatternFrame'; -import usePostMessage from './usePostMessage.mjs'; - -function useStrudel({ - defaultOutput, - interval, - getTime, - evalOnMount = false, - initialCode = '', - beforeEval, - afterEval, - editPattern, - onEvalError, - onToggle, - canvasId, - drawContext, - drawTime = [-2, 2], - paintOptions = {}, -}) { - const id = useMemo(() => s4(), []); - canvasId = canvasId || `canvas-${id}`; - // scheduler - const [schedulerError, setSchedulerError] = useState(); - const [evalError, setEvalError] = useState(); - const [code, setCode] = useState(initialCode); - const [activeCode, setActiveCode] = useState(); - const [pattern, setPattern] = useState(); - const [started, setStarted] = useState(false); - const isDirty = code !== activeCode; - //const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]); - const shouldPaint = useCallback((pat) => !!pat?.context?.onPaint, []); - - // TODO: make sure this hook reruns when scheduler.started changes - const { scheduler, evaluate, start, stop, pause, setCps } = useMemo( - () => - repl({ - interval, - defaultOutput, - onSchedulerError: setSchedulerError, - onEvalError: (err) => { - setEvalError(err); - onEvalError?.(err); - }, - getTime, - drawContext, - transpiler, - editPattern, - beforeEval: async ({ code }) => { - setCode(code); - await beforeEval?.(); - }, - afterEval: (res) => { - const { pattern: _pattern, code } = res; - setActiveCode(code); - setPattern(_pattern); - setEvalError(); - setSchedulerError(); - afterEval?.(res); - }, - onToggle: (v) => { - setStarted(v); - onToggle?.(v); - }, - }), - [defaultOutput, interval, getTime], - ); - const broadcast = usePostMessage(({ data: { from, type } }) => { - if (type === 'start' && from !== id) { - // console.log('message', from, type); - stop(); - } - }); - const activateCode = useCallback( - async (newCode, autostart = true) => { - if (newCode) { - setCode(code); - } - const res = await evaluate(newCode || code, autostart); - broadcast({ type: 'start', from: id }); - return res; - }, - [evaluate, code], - ); - - const onDraw = useCallback( - (pattern, time, haps, drawTime) => { - const { onPaint } = pattern.context || {}; - const ctx = typeof drawContext === 'function' ? drawContext(canvasId) : drawContext; - onPaint?.(ctx, time, haps, drawTime, paintOptions); - }, - [drawContext, canvasId, paintOptions], - ); - - const drawFirstFrame = useCallback( - (pat) => { - if (shouldPaint(pat)) { - const [_, lookahead] = drawTime; - const haps = pat.queryArc(0, lookahead); - // draw at -0.001 to avoid activating haps at 0 - onDraw(pat, -0.001, haps, drawTime); - } - }, - [drawTime, onDraw, shouldPaint], - ); - - const inited = useRef(); - useEffect(() => { - if (!inited.current && evalOnMount && code) { - inited.current = true; - evaluate(code, false).then((pat) => drawFirstFrame(pat)); - } - }, [evalOnMount, code, evaluate, drawFirstFrame]); - - // this will stop the scheduler when hot reloading in development - useEffect(() => { - return () => { - scheduler.stop(); - }; - }, [scheduler]); - - const togglePlay = async () => { - if (started) { - scheduler.stop(); - drawFirstFrame(pattern); - } else { - await activateCode(); - } - }; - const error = schedulerError || evalError; - - usePatternFrame({ - pattern, - started: shouldPaint(pattern) && started, - getTime: () => scheduler.now(), - drawTime, - onDraw, - }); - - return { - id, - canvasId, - code, - setCode, - error, - schedulerError, - scheduler, - evalError, - evaluate, - activateCode, - activeCode, - isDirty, - pattern, - started, - start, - stop, - pause, - togglePlay, - setCps, - }; -} - -export default useStrudel; - -function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); -} diff --git a/packages/react/src/hooks/useWidgets.mjs b/packages/react/src/hooks/useWidgets.mjs deleted file mode 100644 index e7ca136a..00000000 --- a/packages/react/src/hooks/useWidgets.mjs +++ /dev/null @@ -1,13 +0,0 @@ -import { useEffect, useState } from 'react'; -import { updateWidgets } from '@strudel/codemirror'; - -// i know this is ugly.. in the future, repl needs to run without react -export function useWidgets(view) { - const [widgets, setWidgets] = useState([]); - useEffect(() => { - if (view) { - updateWidgets(view, widgets); - } - }, [view, widgets]); - return { widgets, setWidgets }; -} diff --git a/packages/react/src/index.js b/packages/react/src/index.js deleted file mode 100644 index 818d9c95..00000000 --- a/packages/react/src/index.js +++ /dev/null @@ -1,12 +0,0 @@ -// import 'tailwindcss/tailwind.css'; - -export { default as CodeMirror, flash, updateMiniLocations, highlightMiniLocations } from './components/CodeMirror6'; // !SSR -export * from './components/MiniRepl'; // !SSR -export { default as useHighlighting } from './hooks/useHighlighting'; // !SSR -export { default as useStrudel } from './hooks/useStrudel'; // !SSR -export { default as usePostMessage } from './hooks/usePostMessage'; -export { default as useKeydown } from './hooks/useKeydown'; -export { default as useEvent } from './hooks/useEvent'; -export { default as strudelTheme } from './themes/strudel-theme'; -export { default as teletext } from './themes/teletext'; -export { default as cx } from './cx'; diff --git a/packages/react/src/main.jsx b/packages/react/src/main.jsx deleted file mode 100644 index fe0fabf3..00000000 --- a/packages/react/src/main.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import App from './App'; -import { createRoot } from 'react-dom/client'; - -createRoot(document.getElementById('root')).render( - - - , -); diff --git a/packages/react/tailwind.config.js b/packages/react/tailwind.config.js deleted file mode 100644 index c5a940fc..00000000 --- a/packages/react/tailwind.config.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = { - content: ['./src/**/*.{js,jsx,ts,tsx}'], - theme: { - extend: { - colors: { - // codemirror-theme settings - background: 'var(--background)', - lineBackground: 'var(--lineBackground)', - foreground: 'var(--foreground)', - caret: 'var(--caret)', - selection: 'var(--selection)', - selectionMatch: 'var(--selectionMatch)', - gutterBackground: 'var(--gutterBackground)', - gutterForeground: 'var(--gutterForeground)', - gutterBorder: 'var(--gutterBorder)', - lineHighlight: 'var(--lineHighlight)', - }, - }, - }, - plugins: [], - corePlugins: { - preflight: false, - }, -}; diff --git a/packages/react/vite.config.js b/packages/react/vite.config.js deleted file mode 100644 index be289dab..00000000 --- a/packages/react/vite.config.js +++ /dev/null @@ -1,47 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import { peerDependencies, dependencies } from './package.json'; -import { resolve } from 'path'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - react({ - jsxRuntime: 'classic', - }), - ], - build: { - lib: { - entry: resolve(__dirname, 'src', 'index.js'), - formats: ['es', 'cjs'], - fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]), - // for UMD name: 'GlobalName' - }, - rollupOptions: { - external: [ - ...Object.keys(peerDependencies), - ...Object.keys(dependencies), - // TODO: find out which of below names are obsolete now - '@strudel.cycles/transpiler', - 'acorn', - '@strudel.cycles/core', - '@strudel.cycles/mini', - '@strudel.cycles/tonal', - '@strudel.cycles/midi', - '@strudel.cycles/xen', - '@strudel.cycles/serial', - '@strudel.cycles/webaudio', - '@codemirror/view', - '@codemirror/lang-javascript', - '@codemirror/state', - '@codemirror/commands', - '@lezer/highlight', - '@codemirror/language', - '@uiw/codemirror-themes', - '@uiw/react-codemirror', - '@lezer/highlight', - ], - }, - target: 'esnext', - }, -}); diff --git a/packages/repl/.gitignore b/packages/repl/.gitignore new file mode 100644 index 00000000..10dcd67f --- /dev/null +++ b/packages/repl/.gitignore @@ -0,0 +1 @@ +stats.html \ No newline at end of file diff --git a/packages/repl/README.md b/packages/repl/README.md new file mode 100644 index 00000000..ff310948 --- /dev/null +++ b/packages/repl/README.md @@ -0,0 +1,3 @@ +# @strudel/repl + +The Strudel REPL as a web component. diff --git a/packages/repl/index.mjs b/packages/repl/index.mjs new file mode 100644 index 00000000..4119059d --- /dev/null +++ b/packages/repl/index.mjs @@ -0,0 +1,2 @@ +export * from './repl-component.mjs'; +export * from './prebake.mjs'; diff --git a/packages/repl/package.json b/packages/repl/package.json new file mode 100644 index 00000000..aaa95983 --- /dev/null +++ b/packages/repl/package.json @@ -0,0 +1,51 @@ +{ + "name": "@strudel/repl", + "version": "0.9.4", + "description": "Strudel REPL as a Web Component", + "main": "index.mjs", + "publishConfig": { + "main": "dist/index.js", + "module": "dist/index.mjs" + }, + "scripts": { + "build": "vite build", + "prepublishOnly": "npm run build" + }, + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "contributors": [ + "Alex McLean " + ], + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + "@rollup/plugin-replace": "^5.0.5", + "@strudel.cycles/core": "workspace:*", + "@strudel.cycles/midi": "workspace:*", + "@strudel.cycles/mini": "workspace:*", + "@strudel.cycles/soundfonts": "workspace:*", + "@strudel.cycles/tonal": "workspace:*", + "@strudel.cycles/transpiler": "workspace:*", + "@strudel.cycles/webaudio": "workspace:*", + "@strudel/codemirror": "workspace:*", + "@strudel/hydra": "workspace:*", + "rollup-plugin-visualizer": "^5.8.1" + }, + "devDependencies": { + "vite": "^4.3.3" + } +} diff --git a/packages/repl/prebake.mjs b/packages/repl/prebake.mjs new file mode 100644 index 00000000..80d2c3bd --- /dev/null +++ b/packages/repl/prebake.mjs @@ -0,0 +1,54 @@ +import { controls, noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel.cycles/core'; +import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio'; +import * as core from '@strudel.cycles/core'; + +export async function prebake() { + const modulesLoading = evalScope( + // import('@strudel.cycles/core'), + core, + import('@strudel.cycles/mini'), + import('@strudel.cycles/tonal'), + import('@strudel.cycles/webaudio'), + import('@strudel/codemirror'), + import('@strudel/hydra'), + import('@strudel.cycles/soundfonts'), + import('@strudel.cycles/midi'), + // import('@strudel.cycles/xen'), + // import('@strudel.cycles/serial'), + // import('@strudel.cycles/csound'), + // import('@strudel.cycles/osc'), + controls, // sadly, this cannot be exported from core directly (yet) + ); + // load samples + const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main/'; + await Promise.all([ + modulesLoading, + registerSynthSounds(), + registerZZFXSounds(), + //registerSoundfonts(), + // need dynamic import here, because importing @strudel.cycles/soundfonts fails on server: + // => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically + // seems to be a problem with soundfont2 + import('@strudel.cycles/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()), + samples(`${ds}/tidal-drum-machines.json`), + samples(`${ds}/piano.json`), + samples(`${ds}/Dirt-Samples.json`), + samples(`${ds}/EmuSP12.json`), + samples(`${ds}/vcsl.json`), + ]); +} + +const maxPan = noteToMidi('C8'); +const panwidth = (pan, width) => pan * width + (1 - width) / 2; + +Pattern.prototype.piano = function () { + return this.fmap((v) => ({ ...v, clip: v.clip ?? 1 })) // set clip if not already set.. + .s('piano') + .release(0.1) + .fmap((value) => { + const midi = valueToMidi(value); + // pan by pitch + const pan = panwidth(Math.min(Math.round(midi) / maxPan, 1), 0.5); + return { ...value, pan: (value.pan || 1) * pan }; + }); +}; diff --git a/packages/repl/repl-component.mjs b/packages/repl/repl-component.mjs new file mode 100644 index 00000000..1aece8a4 --- /dev/null +++ b/packages/repl/repl-component.mjs @@ -0,0 +1,69 @@ +import { getDrawContext, silence } from '@strudel.cycles/core'; +import { transpiler } from '@strudel.cycles/transpiler'; +import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; +import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror'; +import { prebake } from './prebake.mjs'; + +if (typeof HTMLElement !== 'undefined') { + class StrudelRepl extends HTMLElement { + static observedAttributes = ['code']; + settings = codemirrorSettings.get(); + editor = null; + constructor() { + super(); + } + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'code') { + this.code = newValue; + this.editor?.setCode(newValue); + } + } + connectedCallback() { + // setTimeout makes sure the dom is ready + setTimeout(() => { + const code = (this.innerHTML + '').replace('', '').trim(); + if (code) { + // use comment code in element body if present + this.setAttribute('code', code); + } + }); + // use a separate container for the editor, to make sure the innerHTML stays as is + const container = document.createElement('div'); + this.parentElement.insertBefore(container, this.nextSibling); + const drawContext = getDrawContext(); + const drawTime = [-2, 2]; + this.editor = new StrudelMirror({ + defaultOutput: webaudioOutput, + getTime: () => getAudioContext().currentTime, + transpiler, + root: container, + initialCode: '// LOADING', + pattern: silence, + drawTime, + onDraw: (haps, time, frame, painters) => { + painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); + painters?.forEach((painter) => { + // ctx time haps drawTime paintOptions + painter(drawContext, time, haps, drawTime, { clear: false }); + }); + }, + prebake, + afterEval: ({ code }) => { + // window.location.hash = '#' + code2hash(code); + }, + onUpdateState: (state) => { + const event = new CustomEvent('update', { + detail: state, + }); + this.dispatchEvent(event); + }, + }); + // init settings + this.editor.updateSettings(this.settings); + this.editor.setCode(this.code); + } + // Element functionality written in here + } + + customElements.define('strudel-editor', StrudelRepl); +} diff --git a/packages/repl/vite.config.js b/packages/repl/vite.config.js new file mode 100644 index 00000000..49391bf3 --- /dev/null +++ b/packages/repl/vite.config.js @@ -0,0 +1,28 @@ +import { defineConfig } from 'vite'; +import { dependencies } from './package.json'; +import { resolve } from 'path'; +// import { visualizer } from 'rollup-plugin-visualizer'; +import replace from '@rollup/plugin-replace'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [], + build: { + lib: { + entry: resolve(__dirname, 'index.mjs'), + name: 'strudel', + formats: ['es', 'iife'], + fileName: (ext) => ({ es: 'index.mjs', iife: 'index.js' }[ext]), + }, + rollupOptions: { + // external: [...Object.keys(dependencies)], + plugins: [ + replace({ + 'process.env.NODE_ENV': JSON.stringify('production'), + preventAssignment: true, + }), + ], + }, + target: 'esnext', + }, +}); diff --git a/packages/superdough/example/index.html b/packages/superdough/example/index.html deleted file mode 100644 index 806cb2f1..00000000 --- a/packages/superdough/example/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Superdough Example - - - - - - - - diff --git a/packages/superdough/example/main.js b/packages/superdough/example/main.js deleted file mode 100644 index 7b74fb40..00000000 --- a/packages/superdough/example/main.js +++ /dev/null @@ -1,25 +0,0 @@ -import { superdough, samples, initAudioOnFirstClick, registerSynthSounds } from 'superdough'; - -const init = Promise.all([ - initAudioOnFirstClick(), - samples('github:tidalcycles/Dirt-Samples/master'), - registerSynthSounds(), -]); - -const loop = (t = 0) => { - // superdough(value, time, duration) - superdough({ s: 'bd', delay: 0.5 }, t); - superdough({ note: 'g1', s: 'sawtooth', cutoff: 600, resonance: 8 }, t, 0.125); - superdough({ note: 'g2', s: 'sawtooth', cutoff: 600, resonance: 8 }, t + 0.25, 0.125); - superdough({ s: 'hh' }, t + 0.25); - superdough({ s: 'sd', room: 0.5 }, t + 0.5); - superdough({ s: 'hh' }, t + 0.75); -}; - -document.getElementById('play').addEventListener('click', async () => { - await init; - let t = 0.1; - while (t < 16) { - loop(t++); - } -}); diff --git a/packages/web/examples/minimal.html b/packages/web/examples/minimal.html deleted file mode 100644 index 58487009..00000000 --- a/packages/web/examples/minimal.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/web/examples/repl-example/.gitignore b/packages/web/examples/repl-example/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/packages/web/examples/repl-example/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/packages/web/examples/samples.html b/packages/web/examples/samples.html deleted file mode 100644 index 8b967ec2..00000000 --- a/packages/web/examples/samples.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db0a9dce..44efa683 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,8 +73,81 @@ importers: specifier: ^0.33.0 version: 0.33.0(@vitest/ui@0.28.0) + examples/codemirror-repl: + dependencies: + '@strudel.cycles/core': + specifier: workspace:* + version: link:../../packages/core + '@strudel.cycles/mini': + specifier: workspace:* + version: link:../../packages/mini + '@strudel.cycles/soundfonts': + specifier: workspace:* + version: link:../../packages/soundfonts + '@strudel.cycles/tonal': + specifier: workspace:* + version: link:../../packages/tonal + '@strudel.cycles/transpiler': + specifier: workspace:* + version: link:../../packages/transpiler + '@strudel.cycles/webaudio': + specifier: workspace:* + version: link:../../packages/webaudio + '@strudel/codemirror': + specifier: workspace:* + version: link:../../packages/codemirror + devDependencies: + vite: + specifier: ^4.3.2 + version: 4.5.0(@types/node@18.16.3) + + examples/headless-repl: + dependencies: + '@strudel/web': + specifier: workspace:* + version: link:../../packages/web + devDependencies: + vite: + specifier: ^4.3.2 + version: 4.5.0(@types/node@18.16.3) + + examples/minimal-repl: + dependencies: + '@strudel.cycles/core': + specifier: workspace:* + version: link:../../packages/core + '@strudel.cycles/mini': + specifier: workspace:* + version: link:../../packages/mini + '@strudel.cycles/tonal': + specifier: workspace:* + version: link:../../packages/tonal + '@strudel.cycles/transpiler': + specifier: workspace:* + version: link:../../packages/transpiler + '@strudel.cycles/webaudio': + specifier: workspace:* + version: link:../../packages/webaudio + devDependencies: + vite: + specifier: ^4.3.3 + version: 4.5.0(@types/node@18.16.3) + + examples/superdough: + dependencies: + superdough: + specifier: workspace:* + version: link:../../packages/superdough + devDependencies: + vite: + specifier: ^4.4.5 + version: 4.5.0(@types/node@18.16.3) + packages/codemirror: dependencies: + '@codemirror/autocomplete': + specifier: ^6.6.0 + version: 6.6.0(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0)(@lezer/common@1.0.2) '@codemirror/commands': specifier: ^6.2.4 version: 6.2.4 @@ -84,6 +157,9 @@ importers: '@codemirror/language': specifier: ^6.6.0 version: 6.6.0 + '@codemirror/search': + specifier: ^6.0.0 + version: 6.2.3 '@codemirror/state': specifier: ^6.2.0 version: 6.2.0 @@ -93,9 +169,30 @@ importers: '@lezer/highlight': specifier: ^1.1.4 version: 1.1.4 + '@nanostores/persistent': + specifier: ^0.8.0 + version: 0.8.0(nanostores@0.8.1) + '@replit/codemirror-emacs': + specifier: ^6.0.1 + version: 6.0.1(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@replit/codemirror-vim': + specifier: ^6.0.14 + version: 6.0.14(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@replit/codemirror-vscode-keymap': + specifier: ^6.0.2 + version: 6.0.2(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) '@strudel.cycles/core': specifier: workspace:* version: link:../core + '@uiw/codemirror-themes': + specifier: ^4.19.16 + version: 4.19.16(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@uiw/codemirror-themes-all': + specifier: ^4.19.16 + version: 4.19.16(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + nanostores: + specifier: ^0.8.1 + version: 0.8.1 devDependencies: vite: specifier: ^4.3.3 @@ -114,56 +211,6 @@ importers: specifier: ^0.33.0 version: 0.33.0(@vitest/ui@0.28.0) - packages/core/examples/vite-vanilla-repl: - dependencies: - '@strudel.cycles/core': - specifier: workspace:* - version: link:../.. - '@strudel.cycles/mini': - specifier: workspace:* - version: link:../../../mini - '@strudel.cycles/tonal': - specifier: workspace:* - version: link:../../../tonal - '@strudel.cycles/transpiler': - specifier: workspace:* - version: link:../../../transpiler - '@strudel.cycles/webaudio': - specifier: workspace:* - version: link:../../../webaudio - devDependencies: - vite: - specifier: ^4.3.3 - version: 4.3.3 - - packages/core/examples/vite-vanilla-repl-cm6: - dependencies: - '@strudel.cycles/core': - specifier: workspace:* - version: link:../.. - '@strudel.cycles/mini': - specifier: workspace:* - version: link:../../../mini - '@strudel.cycles/soundfonts': - specifier: workspace:* - version: link:../../../soundfonts - '@strudel.cycles/tonal': - specifier: workspace:* - version: link:../../../tonal - '@strudel.cycles/transpiler': - specifier: workspace:* - version: link:../../../transpiler - '@strudel.cycles/webaudio': - specifier: workspace:* - version: link:../../../webaudio - '@strudel/codemirror': - specifier: workspace:* - version: link:../../../codemirror - devDependencies: - vite: - specifier: ^4.3.2 - version: 4.3.3 - packages/csound: dependencies: '@csound/browser': @@ -271,47 +318,26 @@ importers: specifier: ^4.3.3 version: 4.3.3 - packages/react: + packages/repl: dependencies: - '@codemirror/autocomplete': - specifier: ^6.6.0 - version: 6.6.0(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0)(@lezer/common@1.0.2) - '@codemirror/commands': - specifier: ^6.0.0 - version: 6.2.4 - '@codemirror/lang-javascript': - specifier: ^6.1.7 - version: 6.1.7 - '@codemirror/language': - specifier: ^6.0.0 - version: 6.6.0 - '@codemirror/lint': - specifier: ^6.0.0 - version: 6.1.0 - '@codemirror/search': - specifier: ^6.0.0 - version: 6.2.3 - '@codemirror/state': - specifier: ^6.2.0 - version: 6.2.0 - '@codemirror/view': - specifier: ^6.10.0 - version: 6.10.0 - '@lezer/highlight': - specifier: ^1.1.4 - version: 1.1.4 - '@replit/codemirror-emacs': - specifier: ^6.0.1 - version: 6.0.1(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) - '@replit/codemirror-vim': - specifier: ^6.0.14 - version: 6.0.14(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) - '@replit/codemirror-vscode-keymap': - specifier: ^6.0.2 - version: 6.0.2(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) + '@rollup/plugin-replace': + specifier: ^5.0.5 + version: 5.0.5 '@strudel.cycles/core': specifier: workspace:* version: link:../core + '@strudel.cycles/midi': + specifier: workspace:* + version: link:../midi + '@strudel.cycles/mini': + specifier: workspace:* + version: link:../mini + '@strudel.cycles/soundfonts': + specifier: workspace:* + version: link:../soundfonts + '@strudel.cycles/tonal': + specifier: workspace:* + version: link:../tonal '@strudel.cycles/transpiler': specifier: workspace:* version: link:../transpiler @@ -321,98 +347,16 @@ importers: '@strudel/codemirror': specifier: workspace:* version: link:../codemirror - '@uiw/codemirror-themes': - specifier: ^4.19.16 - version: 4.19.16(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) - '@uiw/react-codemirror': - specifier: ^4.19.16 - version: 4.19.16(@babel/runtime@7.20.13)(@codemirror/autocomplete@6.6.0)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/theme-one-dark@6.1.0)(@codemirror/view@6.10.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0) - react-hook-inview: - specifier: ^4.5.0 - version: 4.5.0(react-dom@18.2.0)(react@18.2.0) + '@strudel/hydra': + specifier: workspace:* + version: link:../hydra + rollup-plugin-visualizer: + specifier: ^5.8.1 + version: 5.9.0 devDependencies: - '@types/react': - specifier: ^18.2.0 - version: 18.2.0 - '@types/react-dom': - specifier: ^18.2.1 - version: 18.2.1 - '@vitejs/plugin-react': - specifier: ^4.0.0 - version: 4.0.0(vite@4.3.3) - autoprefixer: - specifier: ^10.4.14 - version: 10.4.14(postcss@8.4.23) - postcss: - specifier: ^8.4.23 - version: 8.4.23 - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - tailwindcss: - specifier: ^3.3.2 - version: 3.3.2 vite: specifier: ^4.3.3 - version: 4.3.3 - - packages/react/examples/nano-repl: - dependencies: - '@strudel.cycles/core': - specifier: workspace:* - version: link:../../../core - '@strudel.cycles/mini': - specifier: workspace:* - version: link:../../../mini - '@strudel.cycles/osc': - specifier: workspace:* - version: link:../../../osc - '@strudel.cycles/react': - specifier: workspace:* - version: link:../.. - '@strudel.cycles/soundfonts': - specifier: workspace:* - version: link:../../../soundfonts - '@strudel.cycles/tonal': - specifier: workspace:* - version: link:../../../tonal - '@strudel.cycles/transpiler': - specifier: workspace:* - version: link:../../../transpiler - '@strudel.cycles/webaudio': - specifier: workspace:* - version: link:../../../webaudio - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - devDependencies: - '@types/react': - specifier: ^18.2.0 - version: 18.2.0 - '@types/react-dom': - specifier: ^18.2.1 - version: 18.2.1 - '@vitejs/plugin-react': - specifier: ^4.0.0 - version: 4.0.0(vite@4.3.3) - autoprefixer: - specifier: ^10.4.14 - version: 10.4.14(postcss@8.4.23) - postcss: - specifier: ^8.4.23 - version: 8.4.23 - tailwindcss: - specifier: ^3.3.2 - version: 3.3.2 - vite: - specifier: ^4.3.3 - version: 4.3.3 + version: 4.5.0(@types/node@18.16.3) packages/serial: dependencies: @@ -456,16 +400,6 @@ importers: specifier: ^4.3.3 version: 4.3.3 - packages/superdough/example: - dependencies: - superdough: - specifier: workspace:* - version: link:.. - devDependencies: - vite: - specifier: ^4.4.5 - version: 4.4.5(@types/node@18.16.3) - packages/tonal: dependencies: '@strudel.cycles/core': @@ -535,16 +469,6 @@ importers: specifier: ^4.3.3 version: 4.3.3 - packages/web/examples/repl-example: - dependencies: - '@strudel/web': - specifier: workspace:* - version: link:../.. - devDependencies: - vite: - specifier: ^4.3.2 - version: 4.3.3 - packages/webaudio: dependencies: '@strudel.cycles/core': @@ -618,9 +542,6 @@ importers: '@strudel.cycles/osc': specifier: workspace:* version: link:../packages/osc - '@strudel.cycles/react': - specifier: workspace:* - version: link:../packages/react '@strudel.cycles/serial': specifier: workspace:* version: link:../packages/serial @@ -648,6 +569,9 @@ importers: '@strudel/hydra': specifier: workspace:* version: link:../packages/hydra + '@strudel/repl': + specifier: workspace:* + version: link:../packages/repl '@supabase/supabase-js': specifier: ^2.21.0 version: 2.21.0 @@ -696,6 +620,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-hook-inview: + specifier: ^4.5.0 + version: 4.5.0(react-dom@18.2.0)(react@18.2.0) rehype-autolink-headings: specifier: ^6.1.1 version: 6.1.1 @@ -876,6 +803,7 @@ packages: /@alloc/quick-lru@5.2.0: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + dev: false /@ampproject/remapping@2.2.0: resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} @@ -1020,38 +948,10 @@ packages: '@babel/highlight': 7.22.20 chalk: 2.4.2 - /@babel/compat-data@7.21.5: - resolution: {integrity: sha512-M+XAiQ7GzQ3FDPf0KOLkugzptnIypt0X0ma0wmlTKPR3IchgNFdx2JXxZdvd18JY5s7QkaFD/qyX0dsMpog/Ug==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/compat-data@7.23.2: resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} engines: {node: '>=6.9.0'} - /@babel/core@7.21.5: - resolution: {integrity: sha512-9M398B/QH5DlfCOTKDZT1ozXr0x8uBEeFd+dJraGUZGiaNpGCDVGCc14hZexsMblw3XxltJ+6kSvogp9J+5a9g==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.5 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.5) - '@babel/helper-module-transforms': 7.21.5 - '@babel/helpers': 7.21.5 - '@babel/parser': 7.21.5 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/core@7.23.2: resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} engines: {node: '>=6.9.0'} @@ -1083,16 +983,6 @@ packages: jsesc: 2.5.2 dev: true - /@babel/generator@7.21.5: - resolution: {integrity: sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 - jsesc: 2.5.2 - dev: true - /@babel/generator@7.23.0: resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} engines: {node: '>=6.9.0'} @@ -1116,20 +1006,6 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-compilation-targets@7.21.5(@babel/core@7.21.5): - resolution: {integrity: sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.21.5 - '@babel/core': 7.21.5 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.5 - lru-cache: 5.1.1 - semver: 6.3.0 - dev: true - /@babel/helper-compilation-targets@7.22.15: resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} engines: {node: '>=6.9.0'} @@ -1186,11 +1062,6 @@ packages: - supports-color dev: true - /@babel/helper-environment-visitor@7.21.5: - resolution: {integrity: sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-environment-visitor@7.22.20: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} @@ -1202,14 +1073,6 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-function-name@7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.5 - dev: true - /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} @@ -1217,13 +1080,6 @@ packages: '@babel/template': 7.22.15 '@babel/types': 7.23.0 - /@babel/helper-hoist-variables@7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - dev: true - /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} @@ -1237,35 +1093,12 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-module-imports@7.21.4: - resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - dev: true - /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.23.0 - /@babel/helper-module-transforms@7.21.5: - resolution: {integrity: sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.21.5 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-simple-access': 7.21.5 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} engines: {node: '>=6.9.0'} @@ -1286,11 +1119,6 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-plugin-utils@7.20.2: - resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} engines: {node: '>=6.9.0'} @@ -1324,13 +1152,6 @@ packages: - supports-color dev: true - /@babel/helper-simple-access@7.21.5: - resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - dev: true - /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} @@ -1344,13 +1165,6 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - dev: true - /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} @@ -1378,11 +1192,6 @@ packages: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-option@7.22.15: resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} engines: {node: '>=6.9.0'} @@ -1399,17 +1208,6 @@ packages: - supports-color dev: true - /@babel/helpers@7.21.5: - resolution: {integrity: sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.5 - '@babel/types': 7.21.5 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helpers@7.23.2: resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} engines: {node: '>=6.9.0'} @@ -1459,6 +1257,7 @@ packages: hasBin: true dependencies: '@babel/types': 7.21.5 + dev: false /@babel/parser@7.23.0: resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} @@ -2096,16 +1895,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.5): - resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.5 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - /@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==} engines: {node: '>=6.9.0'} @@ -2116,16 +1905,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.21.5): - resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.5 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - /@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==} engines: {node: '>=6.9.0'} @@ -2347,15 +2126,6 @@ packages: dependencies: regenerator-runtime: 0.13.11 - /@babel/template@7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.5 - '@babel/types': 7.21.5 - dev: true - /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -2364,24 +2134,6 @@ packages: '@babel/parser': 7.23.0 '@babel/types': 7.23.0 - /@babel/traverse@7.21.5: - resolution: {integrity: sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.5 - '@babel/helper-environment-visitor': 7.21.5 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.5 - '@babel/types': 7.21.5 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/traverse@7.23.2: resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} engines: {node: '>=6.9.0'} @@ -2499,15 +2251,6 @@ packages: resolution: {integrity: sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==} dev: false - /@codemirror/theme-one-dark@6.1.0: - resolution: {integrity: sha512-AiTHtFRu8+vWT9wWUWDM+cog6ZwgivJogB1Tm/g40NIpLwph7AnmxrSzWfvJN5fBVufsuwBxecQCNmdcR5D7Aw==} - dependencies: - '@codemirror/language': 6.6.0 - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.10.0 - '@lezer/highlight': 1.1.4 - dev: false - /@codemirror/view@6.10.0: resolution: {integrity: sha512-Oea3rvE4JQLMmLsy2b54yxXQJgJM9xKpUQIpF/LGgKUTH2lA06GAmEtKKWn5OUnbW3jrH1hHeUd8DJEgePMOeQ==} dependencies: @@ -4061,6 +3804,19 @@ packages: rollup: 2.79.1 dev: true + /@rollup/plugin-replace@5.0.5: + resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0 + magic-string: 0.30.5 + dev: false + /@rollup/pluginutils@3.1.0(rollup@2.79.1): resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} @@ -4073,104 +3829,118 @@ packages: rollup: 2.79.1 dev: true - /@rollup/rollup-android-arm-eabi@4.9.1: - resolution: {integrity: sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==} + /@rollup/pluginutils@5.1.0: + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.0 + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: false + + /@rollup/rollup-android-arm-eabi@4.9.2: + resolution: {integrity: sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.9.1: - resolution: {integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==} + /@rollup/rollup-android-arm64@4.9.2: + resolution: {integrity: sha512-yZ+MUbnwf3SHNWQKJyWh88ii2HbuHCFQnAYTeeO1Nb8SyEiWASEi5dQUygt3ClHWtA9My9RQAYkjvrsZ0WK8Xg==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.9.1: - resolution: {integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==} + /@rollup/rollup-darwin-arm64@4.9.2: + resolution: {integrity: sha512-vqJ/pAUh95FLc/G/3+xPqlSBgilPnauVf2EXOQCZzhZJCXDXt/5A8mH/OzU6iWhb3CNk5hPJrh8pqJUPldN5zw==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.9.1: - resolution: {integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==} + /@rollup/rollup-darwin-x64@4.9.2: + resolution: {integrity: sha512-otPHsN5LlvedOprd3SdfrRNhOahhVBwJpepVKUN58L0RnC29vOAej1vMEaVU6DadnpjivVsNTM5eNt0CcwTahw==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.9.1: - resolution: {integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==} + /@rollup/rollup-linux-arm-gnueabihf@4.9.2: + resolution: {integrity: sha512-ewG5yJSp+zYKBYQLbd1CUA7b1lSfIdo9zJShNTyc2ZP1rcPrqyZcNlsHgs7v1zhgfdS+kW0p5frc0aVqhZCiYQ==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.9.1: - resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==} + /@rollup/rollup-linux-arm64-gnu@4.9.2: + resolution: {integrity: sha512-pL6QtV26W52aCWTG1IuFV3FMPL1m4wbsRG+qijIvgFO/VBsiXJjDPE/uiMdHBAO6YcpV4KvpKtd0v3WFbaxBtg==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.9.1: - resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==} + /@rollup/rollup-linux-arm64-musl@4.9.2: + resolution: {integrity: sha512-On+cc5EpOaTwPSNetHXBuqylDW+765G/oqB9xGmWU3npEhCh8xu0xqHGUA+4xwZLqBbIZNcBlKSIYfkBm6ko7g==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.9.1: - resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==} + /@rollup/rollup-linux-riscv64-gnu@4.9.2: + resolution: {integrity: sha512-Wnx/IVMSZ31D/cO9HSsU46FjrPWHqtdF8+0eyZ1zIB5a6hXaZXghUKpRrC4D5DcRTZOjml2oBhXoqfGYyXKipw==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.9.1: - resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==} + /@rollup/rollup-linux-x64-gnu@4.9.2: + resolution: {integrity: sha512-ym5x1cj4mUAMBummxxRkI4pG5Vht1QMsJexwGP8547TZ0sox9fCLDHw9KCH9c1FO5d9GopvkaJsBIOkTKxksdw==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.9.1: - resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==} + /@rollup/rollup-linux-x64-musl@4.9.2: + resolution: {integrity: sha512-m0hYELHGXdYx64D6IDDg/1vOJEaiV8f1G/iO+tejvRCJNSwK4jJ15e38JQy5Q6dGkn1M/9KcyEOwqmlZ2kqaZg==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.9.1: - resolution: {integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==} + /@rollup/rollup-win32-arm64-msvc@4.9.2: + resolution: {integrity: sha512-x1CWburlbN5JjG+juenuNa4KdedBdXLjZMp56nHFSHTOsb/MI2DYiGzLtRGHNMyydPGffGId+VgjOMrcltOksA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.9.1: - resolution: {integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==} + /@rollup/rollup-win32-ia32-msvc@4.9.2: + resolution: {integrity: sha512-VVzCB5yXR1QlfsH1Xw1zdzQ4Pxuzv+CPr5qpElpKhVxlxD3CRdfubAG9mJROl6/dmj5gVYDDWk8sC+j9BI9/kQ==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.9.1: - resolution: {integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==} + /@rollup/rollup-win32-x64-msvc@4.9.2: + resolution: {integrity: sha512-SYRedJi+mweatroB+6TTnJYLts0L0bosg531xnQWtklOI6dezEagx4Q0qDyvRdK+qgdA3YZpjjGuPFtxBmddBA==} cpu: [x64] os: [win32] requiresBuild: true @@ -4708,11 +4478,13 @@ packages: /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + dev: false /@types/react-dom@18.2.1: resolution: {integrity: sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==} dependencies: '@types/react': 18.2.0 + dev: false /@types/react@18.2.0: resolution: {integrity: sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==} @@ -4720,6 +4492,7 @@ packages: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 csstype: 3.1.1 + dev: false /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -4729,6 +4502,7 @@ packages: /@types/scheduler@0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + dev: false /@types/trusted-types@2.0.2: resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} @@ -4784,26 +4558,6 @@ packages: eslint-visitor-keys: 3.3.0 dev: false - /@uiw/codemirror-extensions-basic-setup@4.19.16(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0): - resolution: {integrity: sha512-Xm0RDpyYVZ/8hWqaBs3+wZwi4uLwZUBwp/uCt89X80FeR6mr3BFuC+a+gcDO4dBu3l+WQE3jJdhjKjB2TCY/PQ==} - peerDependencies: - '@codemirror/autocomplete': '>=6.0.0' - '@codemirror/commands': '>=6.0.0' - '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' - '@codemirror/search': '>=6.0.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - dependencies: - '@codemirror/autocomplete': 6.6.0(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0)(@lezer/common@1.0.2) - '@codemirror/commands': 6.2.4 - '@codemirror/language': 6.6.0 - '@codemirror/lint': 6.1.0 - '@codemirror/search': 6.2.3 - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.10.0 - dev: false - /@uiw/codemirror-theme-abcdef@4.19.16(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0): resolution: {integrity: sha512-vZHLg35Rhz39FF3HgAeHSZxIOV3/PG8q8v/0dcywCvt1FG9J6OuAifXzePo2nrT/P/qkienbehxzF+DyHHzV5g==} dependencies: @@ -5079,33 +4833,6 @@ packages: '@codemirror/view': 6.10.0 dev: false - /@uiw/react-codemirror@4.19.16(@babel/runtime@7.20.13)(@codemirror/autocomplete@6.6.0)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/theme-one-dark@6.1.0)(@codemirror/view@6.10.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-uElraR7Mvwz2oZKrmtF5hmIB8dAlIiU65nfg484e/V9k4PV6/5KtPUQL3JPO4clH2pcd+TQqRTQrFFkP/D25ew==} - peerDependencies: - '@babel/runtime': '>=7.11.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/theme-one-dark': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - codemirror: '>=6.0.0' - react: '>=16.8.0' - react-dom: '>=16.8.0' - dependencies: - '@babel/runtime': 7.20.13 - '@codemirror/commands': 6.2.4 - '@codemirror/state': 6.2.0 - '@codemirror/theme-one-dark': 6.1.0 - '@codemirror/view': 6.10.0 - '@uiw/codemirror-extensions-basic-setup': 4.19.16(@codemirror/autocomplete@6.6.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.6.0)(@codemirror/lint@6.1.0)(@codemirror/search@6.2.3)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0) - codemirror: 6.0.1(@lezer/common@1.0.2) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - '@codemirror/autocomplete' - - '@codemirror/language' - - '@codemirror/lint' - - '@codemirror/search' - dev: false - /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -5119,21 +4846,6 @@ packages: vite-plugin-pwa: 0.16.5(vite@4.5.0)(workbox-build@7.0.0)(workbox-window@7.0.0) dev: true - /@vitejs/plugin-react@4.0.0(vite@4.3.3): - resolution: {integrity: sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.2.0 - dependencies: - '@babel/core': 7.21.5 - '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5) - '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5) - react-refresh: 0.14.0 - vite: 4.3.3 - transitivePeerDependencies: - - supports-color - dev: true - /@vitejs/plugin-react@4.1.0(vite@4.5.0): resolution: {integrity: sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -5396,6 +5108,7 @@ packages: /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -5436,6 +5149,7 @@ packages: /arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: false /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -5647,22 +5361,6 @@ packages: tslib: 2.5.0 dev: false - /autoprefixer@10.4.14(postcss@8.4.23): - resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001481 - fraction.js: 4.2.0 - normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.23 - postcss-value-parser: 4.2.0 - dev: true - /autoprefixer@10.4.16(postcss@8.4.31): resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} engines: {node: ^10 || ^12 || >=14} @@ -5815,17 +5513,6 @@ packages: dependencies: fill-range: 7.0.1 - /browserslist@4.21.5: - resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001481 - electron-to-chromium: 1.4.284 - node-releases: 2.0.8 - update-browserslist-db: 1.0.10(browserslist@4.21.5) - dev: true - /browserslist@4.22.1: resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5968,6 +5655,7 @@ packages: /camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} + dev: false /camelcase-keys@6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} @@ -5987,10 +5675,6 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - /caniuse-lite@1.0.30001481: - resolution: {integrity: sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==} - dev: true - /caniuse-lite@1.0.30001559: resolution: {integrity: sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==} @@ -6093,7 +5777,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /chord-voicings@0.0.1: resolution: {integrity: sha512-SutgB/4ynkkuiK6qdQ/k3QvCFcH0Vj8Ch4t6LbRyRQbVzP/TOztiCk3kvXd516UZ6fqk7ijDRELEFcKN+6V8sA==} @@ -6180,7 +5864,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone-buffer@1.0.0: resolution: {integrity: sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==} @@ -6238,20 +5921,6 @@ packages: resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} engines: {node: '>=0.10.0'} - /codemirror@6.0.1(@lezer/common@1.0.2): - resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} - dependencies: - '@codemirror/autocomplete': 6.6.0(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.10.0)(@lezer/common@1.0.2) - '@codemirror/commands': 6.2.4 - '@codemirror/language': 6.6.0 - '@codemirror/lint': 6.1.0 - '@codemirror/search': 6.2.3 - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.10.0 - transitivePeerDependencies: - - '@lezer/common' - dev: false - /collect-all@1.0.4: resolution: {integrity: sha512-RKZhRwJtJEP5FWul+gkSMEnaK6H3AGPTTWOiRimCcs+rc/OmQE3Yhy1Q7A7KsdkG3ZXVdZq68Y6ONSdvkeEcKA==} engines: {node: '>=0.10.0'} @@ -6362,6 +6031,7 @@ packages: /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + dev: false /commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} @@ -6502,10 +6172,6 @@ packages: q: 1.5.1 dev: true - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: true - /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -6565,6 +6231,7 @@ packages: /csstype@3.1.1: resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + dev: false /d@1.0.1: resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} @@ -6685,7 +6352,6 @@ packages: /define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} - dev: true /define-properties@1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} @@ -6851,6 +6517,7 @@ packages: /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} @@ -6945,10 +6612,6 @@ packages: jake: 10.8.5 dev: true - /electron-to-chromium@1.4.284: - resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} - dev: true - /electron-to-chromium@1.4.574: resolution: {integrity: sha512-bg1m8L0n02xRzx4LsTTMbBPiUd9yIR+74iPtS/Ao65CuXvhVZHP0ym1kSdDG3yHFDXqHQQBKujlN1AQ8qZnyFg==} @@ -7493,6 +7156,10 @@ packages: resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} dev: true + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: false + /estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: @@ -7810,6 +7477,7 @@ packages: /fraction.js@4.2.0: resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: false /fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -7865,19 +7533,11 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - optional: true - /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind@1.1.1: @@ -7957,7 +7617,6 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-func-name@2.0.0: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} @@ -8110,6 +7769,7 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 + dev: false /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -8852,7 +8512,6 @@ packages: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true - dev: true /is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} @@ -9079,7 +8738,6 @@ packages: engines: {node: '>=8'} dependencies: is-docker: 2.2.1 - dev: true /is-wsl@3.1.0: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} @@ -9139,6 +8797,7 @@ packages: /jiti@1.18.2: resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} hasBin: true + dev: false /js-sdsl@4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} @@ -9484,10 +9143,12 @@ packages: /lilconfig@2.0.6: resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} engines: {node: '>=10'} + dev: false /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} + dev: false /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -9623,6 +9284,7 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 + dev: false /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} @@ -10649,6 +10311,7 @@ packages: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 + dev: false /nan@2.17.0: resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} @@ -10793,10 +10456,6 @@ packages: /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - /node-releases@2.0.8: - resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} - dev: true - /node-source-walk@4.3.0: resolution: {integrity: sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==} engines: {node: '>=6.0'} @@ -10880,6 +10539,7 @@ packages: /normalize-range@0.1.2: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + dev: false /npm-bundled@1.1.2: resolution: {integrity: sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==} @@ -11155,6 +10815,7 @@ packages: /object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + dev: false /object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} @@ -11213,7 +10874,6 @@ packages: define-lazy-prop: 2.0.0 is-docker: 2.2.1 is-wsl: 2.2.0 - dev: true /optionator@0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} @@ -11642,6 +11302,7 @@ packages: /pirates@4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} + dev: false /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} @@ -11712,6 +11373,7 @@ packages: postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.2 + dev: false /postcss-js@4.0.1(postcss@8.4.23): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} @@ -11721,6 +11383,7 @@ packages: dependencies: camelcase-css: 2.0.1 postcss: 8.4.23 + dev: false /postcss-load-config@4.0.1(postcss@8.4.23): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} @@ -11737,6 +11400,7 @@ packages: lilconfig: 2.0.6 postcss: 8.4.23 yaml: 2.2.2 + dev: false /postcss-load-config@4.0.1(postcss@8.4.31): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} @@ -11763,6 +11427,7 @@ packages: dependencies: postcss: 8.4.23 postcss-selector-parser: 6.0.11 + dev: false /postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} @@ -11781,6 +11446,7 @@ packages: /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false /postcss-values-parser@6.0.2(postcss@8.4.23): resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} @@ -11809,7 +11475,6 @@ packages: nanoid: 3.3.6 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: true /postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} @@ -11818,6 +11483,7 @@ packages: nanoid: 3.3.6 picocolors: 1.0.0 source-map-js: 1.0.2 + dev: false /postcss@8.4.32: resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} @@ -12082,6 +11748,7 @@ packages: loose-envify: 1.4.0 react: 18.2.0 scheduler: 0.23.0 + dev: false /react-hook-inview@4.5.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Hm61BK32/K2Cc3bjBe2bQkndHbQP6NhHvWVX7zYitaitB6T28uUV+wlgxbXU9twxUt7+17HyHq6aezpMUCijQQ==} @@ -12100,17 +11767,20 @@ packages: /react-refresh@0.14.0: resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} engines: {node: '>=0.10.0'} + dev: false /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 + dev: false /read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} dependencies: pify: 2.3.0 + dev: false /read-cmd-shim@3.0.0: resolution: {integrity: sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog==} @@ -12479,7 +12149,6 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -12651,14 +12320,13 @@ packages: picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.6.2 - dev: true /rollup@2.79.1: resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} engines: {node: '>=10.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rollup@3.21.0: @@ -12666,7 +12334,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rollup@3.28.0: @@ -12674,26 +12342,26 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 - /rollup@4.9.1: - resolution: {integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==} + /rollup@4.9.2: + resolution: {integrity: sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.9.1 - '@rollup/rollup-android-arm64': 4.9.1 - '@rollup/rollup-darwin-arm64': 4.9.1 - '@rollup/rollup-darwin-x64': 4.9.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.9.1 - '@rollup/rollup-linux-arm64-gnu': 4.9.1 - '@rollup/rollup-linux-arm64-musl': 4.9.1 - '@rollup/rollup-linux-riscv64-gnu': 4.9.1 - '@rollup/rollup-linux-x64-gnu': 4.9.1 - '@rollup/rollup-linux-x64-musl': 4.9.1 - '@rollup/rollup-win32-arm64-msvc': 4.9.1 - '@rollup/rollup-win32-ia32-msvc': 4.9.1 - '@rollup/rollup-win32-x64-msvc': 4.9.1 + '@rollup/rollup-android-arm-eabi': 4.9.2 + '@rollup/rollup-android-arm64': 4.9.2 + '@rollup/rollup-darwin-arm64': 4.9.2 + '@rollup/rollup-darwin-x64': 4.9.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.9.2 + '@rollup/rollup-linux-arm64-gnu': 4.9.2 + '@rollup/rollup-linux-arm64-musl': 4.9.2 + '@rollup/rollup-linux-riscv64-gnu': 4.9.2 + '@rollup/rollup-linux-x64-gnu': 4.9.2 + '@rollup/rollup-linux-x64-musl': 4.9.2 + '@rollup/rollup-win32-arm64-msvc': 4.9.2 + '@rollup/rollup-win32-ia32-msvc': 4.9.2 + '@rollup/rollup-win32-x64-msvc': 4.9.2 fsevents: 2.3.3 dev: true @@ -12751,6 +12419,7 @@ packages: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 + dev: false /section-matter@1.0.0: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} @@ -13323,6 +12992,7 @@ packages: mz: 2.7.0 pirates: 4.0.5 ts-interface-checker: 0.1.13 + dev: false /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} @@ -13381,6 +13051,7 @@ packages: sucrase: 3.32.0 transitivePeerDependencies: - ts-node + dev: false /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -13533,11 +13204,13 @@ packages: engines: {node: '>=0.8'} dependencies: thenify: 3.3.1 + dev: false /thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} dependencies: any-promise: 1.3.0 + dev: false /through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -13639,6 +13312,7 @@ packages: /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: false /tsconfck@3.0.0(typescript@4.9.4): resolution: {integrity: sha512-w3wnsIrJNi7avf4Zb0VjOoodoO0woEqGgZGQm+LHH9przdUI+XDKsWAXwxHA1DaRTjeuZNcregSzr7RaA8zG9A==} @@ -14034,17 +13708,6 @@ packages: engines: {node: '>=4'} dev: true - /update-browserslist-db@1.0.10(browserslist@4.21.5): - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.5 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - /update-browserslist-db@1.0.13(browserslist@4.22.1): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -14200,7 +13863,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.5(@types/node@18.16.3) + vite: 4.5.0(@types/node@18.16.3) transitivePeerDependencies: - '@types/node' - less @@ -14259,7 +13922,7 @@ packages: postcss: 8.4.23 rollup: 3.21.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vite@4.4.5(@types/node@18.16.3): @@ -14295,7 +13958,7 @@ packages: postcss: 8.4.27 rollup: 3.28.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vite@4.5.0(@types/node@18.16.3): @@ -14328,10 +13991,10 @@ packages: dependencies: '@types/node': 18.16.3 esbuild: 0.18.20 - postcss: 8.4.31 + postcss: 8.4.27 rollup: 3.28.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /vite@5.0.10: resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} @@ -14363,7 +14026,7 @@ packages: dependencies: esbuild: 0.19.5 postcss: 8.4.32 - rollup: 4.9.1 + rollup: 4.9.2 optionalDependencies: fsevents: 2.3.3 dev: true @@ -14786,7 +14449,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} @@ -14869,7 +14531,6 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yaeti@0.0.6: resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} @@ -14890,6 +14551,7 @@ packages: /yaml@2.2.2: resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==} engines: {node: '>= 14'} + dev: false /yargs-parser@20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} @@ -14929,7 +14591,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 615475e4..ea7b6879 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,9 +1,5 @@ packages: # all packages in direct subdirs of packages/ - "packages/*" + - "examples/*" - "website/" - - "packages/core/examples/vite-vanilla-repl" - - "packages/core/examples/vite-vanilla-repl-cm6" - - "packages/react/examples/nano-repl" - - "packages/web/examples/repl-example" - - "packages/superdough/example" diff --git a/undocumented.json b/undocumented.json index 0a807c90..8a5641ad 100644 --- a/undocumented.json +++ b/undocumented.json @@ -178,7 +178,6 @@ "backgroundImage", "cleanupUi" ], - "/packages/core/gist.js": [], "/packages/core/index.mjs": [], "/packages/csound/index.mjs": [ "loadCSound", diff --git a/website/package.json b/website/package.json index f5354842..7557bdc6 100644 --- a/website/package.json +++ b/website/package.json @@ -27,16 +27,16 @@ "@strudel.cycles/midi": "workspace:*", "@strudel.cycles/mini": "workspace:*", "@strudel.cycles/osc": "workspace:*", - "@strudel.cycles/react": "workspace:*", "@strudel.cycles/serial": "workspace:*", "@strudel.cycles/soundfonts": "workspace:*", "@strudel.cycles/tonal": "workspace:*", "@strudel.cycles/transpiler": "workspace:*", "@strudel.cycles/webaudio": "workspace:*", "@strudel.cycles/xen": "workspace:*", - "@strudel/hydra": "workspace:*", "@strudel/codemirror": "workspace:*", "@strudel/desktopbridge": "workspace:*", + "@strudel/hydra": "workspace:*", + "@strudel/repl": "workspace:*", "@supabase/supabase-js": "^2.21.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.8", @@ -53,6 +53,7 @@ "nanostores": "^0.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-inview": "^4.5.0", "rehype-autolink-headings": "^6.1.1", "rehype-slug": "^5.0.1", "rehype-urls": "^1.1.1", diff --git a/website/public/dependencygraph.svg b/website/public/dependencygraph.svg deleted file mode 100644 index 62fadac7..00000000 --- a/website/public/dependencygraph.svg +++ /dev/null @@ -1,326 +0,0 @@ - - - - - - - - -@strudel.cycles/core@0.6.8 - -@strudel.cycles/core@0.6.8 - - - -fraction.js@4.2.0 - -fraction.js@4.2.0 - - - - - -@strudel.cycles/csound@0.6.2 - -@strudel.cycles/csound@0.6.2 - - - - - -@strudel.cycles/webaudio@0.6.1 - -@strudel.cycles/webaudio@0.6.1 - - - - - - - - - -@strudel.cycles/embed@0.2.0 - -@strudel.cycles/embed@0.2.0 - - - -@strudel.cycles/midi@0.6.0 - -@strudel.cycles/midi@0.6.0 - - - - - -@strudel.cycles/webaudio@0.6.0 - -@strudel.cycles/webaudio@0.6.0 - - - - - - - - - -@strudel.cycles/mini@0.6.0 - -@strudel.cycles/mini@0.6.0 - - - - - -@strudel.cycles/osc@0.6.0 - -@strudel.cycles/osc@0.6.0 - - - - - - - - - - - -@strudel.cycles/serial@0.6.0 - -@strudel.cycles/serial@0.6.0 - - - - - -@strudel.cycles/soundfonts@0.6.0 - -@strudel.cycles/soundfonts@0.6.0 - - - - - - - -sfumato@0.1.2 - -sfumato@0.1.2 - - - - - -soundfont2@0.4.0 - -soundfont2@0.4.0 - - - - - -@strudel.cycles/tonal@0.6.0 - -@strudel.cycles/tonal@0.6.0 - - - - - - - - - -chord-voicings@0.0.1 - -chord-voicings@0.0.1 - - - - - - - -@strudel.cycles/transpiler@0.6.0 - -@strudel.cycles/transpiler@0.6.0 - - - - - - - - - - - - - - - - - - - -@strudel.cycles/xen@0.6.0 - -@strudel.cycles/xen@0.6.0 - - - - - - - -chord-voicings@0.0.1->@tonaljs/tonal@4.10.0 - - - - - -sfumato@0.1.2->soundfont2@0.4.0 - - - - -@strudel.cycles/core@0.6.8->fraction.js@4.2.0 - - - -@strudel.cycles/csound@0.6.2->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/csound@0.6.2->@strudel.cycles/webaudio@0.6.1 - - - -@strudel.cycles/midi@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/midi@0.6.0->@strudel.cycles/webaudio@0.6.0 - - - -@strudel.cycles/midi@0.6.0->webmidi@3.1.4 - - - -@strudel.cycles/mini@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/osc@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/osc@0.6.0->osc-js@2.4.0 - - - -@strudel.cycles/serial@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/soundfonts@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/soundfonts@0.6.0->@strudel.cycles/webaudio@0.6.0 - - - -@strudel.cycles/soundfonts@0.6.0->sfumato@0.1.2 - - - -@strudel.cycles/soundfonts@0.6.0->soundfont2@0.4.0 - - - -@strudel.cycles/tonal@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/tonal@0.6.0->@tonaljs/tonal@4.10.0 - - - -@strudel.cycles/tonal@0.6.0->chord-voicings@0.0.1 - - - -@strudel.cycles/tonal@0.6.0->webmidi@3.1.4 - - - -@strudel.cycles/transpiler@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/transpiler@0.6.0->acorn@8.8.2 - - - -@strudel.cycles/transpiler@0.6.0->escodegen@2.0.0 - - - -@strudel.cycles/transpiler@0.6.0->estree-walker@3.0.3 - - - -@strudel.cycles/webaudio@0.6.1->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/xen@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/webaudio@0.6.0->@strudel.cycles/core@0.6.8 - - - -@strudel.cycles/csound@0.6.2->@csound/browser@6.18.5 - - - - \ No newline at end of file diff --git a/website/src/components/HeadCommon.astro b/website/src/components/HeadCommon.astro index ab18500f..cb3e605a 100644 --- a/website/src/components/HeadCommon.astro +++ b/website/src/components/HeadCommon.astro @@ -46,41 +46,9 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL {pwaInfo && } + + + + + + +{pwaInfo && } + + diff --git a/website/src/components/PitchSlider.jsx b/website/src/components/PitchSlider.jsx index b3c2b920..41b215fb 100644 --- a/website/src/components/PitchSlider.jsx +++ b/website/src/components/PitchSlider.jsx @@ -1,5 +1,5 @@ -import useEvent from '@strudel.cycles/react/src/hooks/useEvent.mjs'; -import useFrame from '@strudel.cycles/react/src/hooks/useFrame.mjs'; +import useEvent from '@src/useEvent.mjs'; +import useFrame from '@src/useFrame.mjs'; import { getAudioContext } from '@strudel.cycles/webaudio'; import { midi2note } from '@strudel.cycles/core'; import { useState, useRef, useEffect } from 'react'; diff --git a/packages/react/src/cx.js b/website/src/cx.mjs similarity index 100% rename from packages/react/src/cx.js rename to website/src/cx.mjs diff --git a/packages/react/src/components/Icon.tsx b/website/src/docs/Icon.jsx similarity index 98% rename from packages/react/src/components/Icon.tsx rename to website/src/docs/Icon.jsx index 24ec8786..64d5f88a 100644 --- a/packages/react/src/components/Icon.tsx +++ b/website/src/docs/Icon.jsx @@ -1,5 +1,3 @@ -import React from 'react'; - export function Icon({ type }) { return ( diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 4b37ebad..5e915641 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,84 +1,156 @@ -import { evalScope, controls, noteToMidi } from '@strudel.cycles/core'; -import { initAudioOnFirstClick } from '@strudel.cycles/webaudio'; -import { useEffect, useState } from 'react'; -import { prebake } from '../repl/prebake'; -import { themes, settings } from '../repl/themes.mjs'; -import './MiniRepl.css'; -import { useSettings } from '../settings.mjs'; +import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; +import { Icon } from './Icon'; +import { silence, getPunchcardPainter, noteToMidi } from '@strudel.cycles/core'; +import { transpiler } from '@strudel.cycles/transpiler'; +import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; +import { StrudelMirror } from '@strudel/codemirror'; +// import { prebake } from '@strudel/repl'; +import { prebake } from '../repl/prebake.mjs'; +import { loadModules } from '../repl/util.mjs'; import Claviature from '@components/Claviature'; +import useClient from '@src/useClient.mjs'; -let modules; +let prebaked, modulesLoading; if (typeof window !== 'undefined') { - modules = evalScope( - controls, - import('@strudel.cycles/core'), - import('@strudel.cycles/tonal'), - import('@strudel.cycles/mini'), - import('@strudel.cycles/midi'), - import('@strudel.cycles/xen'), - import('@strudel.cycles/webaudio'), - import('@strudel.cycles/osc'), - import('@strudel.cycles/csound'), - import('@strudel.cycles/soundfonts'), - import('@strudel/hydra'), - ); -} - -if (typeof window !== 'undefined') { - initAudioOnFirstClick(); - prebake(); + prebaked = prebake(); + modulesLoading = loadModules(); } export function MiniRepl({ - tune, - drawTime, + tune: code, + hideHeader = false, + canvasHeight = 100, + onTrigger, punchcard, punchcardLabels = true, - span = [0, 4], - canvasHeight = 100, - hideHeader, claviature, claviatureLabels, }) { - const [Repl, setRepl] = useState(); - const { theme, keybindings, fontSize, fontFamily, isLineNumbersDisplayed, isActiveLineHighlighted } = useSettings(); + const id = useMemo(() => s4(), []); + const canvasId = useMemo(() => `canvas-${id}`, [id]); + const shouldDraw = !!punchcard || !!claviature; + const shouldShowCanvas = !!punchcard; + const drawTime = punchcard ? [0, 4] : [0, 0]; const [activeNotes, setActiveNotes] = useState([]); - useEffect(() => { - // we have to load this package on the client - // because codemirror throws an error on the server - Promise.all([import('@strudel.cycles/react'), modules]) - .then(([res]) => setRepl(() => res.MiniRepl)) - .catch((err) => console.error(err)); - }, []); - return Repl ? ( -
- { - const active = haps - .map((hap) => hap.value.note) - .filter(Boolean) - .map((n) => (typeof n === 'string' ? noteToMidi(n) : n)); - setActiveNotes(active); - } - : undefined + + const init = useCallback(({ code, shouldDraw }) => { + const drawContext = shouldDraw ? document.querySelector('#' + canvasId)?.getContext('2d') : null; + let onDraw; + if (shouldDraw) { + onDraw = (haps, time, frame, painters) => { + painters.length && drawContext?.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); + painters?.forEach((painter) => { + // ctx time haps drawTime paintOptions + painter(drawContext, time, haps, drawTime, { clear: false }); + }); + }; + } + + const editor = new StrudelMirror({ + id, + defaultOutput: webaudioOutput, + getTime: () => getAudioContext().currentTime, + transpiler, + autodraw: !!shouldDraw, + root: containerRef.current, + initialCode: '// LOADING', + pattern: silence, + drawTime, + onDraw, + editPattern: (pat, id) => { + if (onTrigger) { + pat = pat.onTrigger(onTrigger, false); } - /> + if (claviature) { + editor?.painters.push((ctx, time, haps, drawTime) => { + const active = haps + .map((hap) => hap.value.note) + .filter(Boolean) + .map((n) => (typeof n === 'string' ? noteToMidi(n) : n)); + setActiveNotes(active); + }); + } + if (punchcard) { + editor?.painters.push(getPunchcardPainter({ labels: !!punchcardLabels })); + } + return pat; + }, + prebake: async () => Promise.all([modulesLoading, prebaked]), + onUpdateState: (state) => { + setReplState({ ...state }); + }, + }); + // init settings + editor.setCode(code); + editorRef.current = editor; + }, []); + + const [replState, setReplState] = useState({}); + const { started, isDirty, error } = replState; + const editorRef = useRef(); + const containerRef = useRef(); + const client = useClient(); + + if (!client) { + return
{code}
; + } + + return ( +
+ {!hideHeader && ( +
+
+ + +
+
+ )} +
+
{ + if (!editorRef.current) { + containerRef.current = el; + init({ code, shouldDraw }); + } + }} + >
+ {error &&
{error.message}
} +
+ {shouldShowCanvas && ( + { + if (el && el.width !== el.clientWidth) { + el.width = el.clientWidth; + } + }} + > + )} + {/* !!log.length && ( +
+ {log.map(({ message }, i) => ( +
{message}
+ ))} +
+ ) */} {claviature && ( )}
- ) : ( -
{tune}
); } + +function cx(...classes) { + // : Array + return classes.filter(Boolean).join(' '); +} + +function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); +} diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 40a1a03f..53a522e8 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -1,6 +1,6 @@ --- import HeadCommon from '../components/HeadCommon.astro'; -import { Repl } from '../repl/Repl.jsx'; +import { Repl } from '../repl/Repl'; --- @@ -9,6 +9,6 @@ import { Repl } from '../repl/Repl.jsx'; Strudel REPL - + diff --git a/website/src/pages/technical-manual/docs.mdx b/website/src/pages/technical-manual/docs.mdx index aa18eab4..386de6ad 100644 --- a/website/src/pages/technical-manual/docs.mdx +++ b/website/src/pages/technical-manual/docs.mdx @@ -9,7 +9,7 @@ The docs page is built ontop of astro's [docs site](https://github.com/withastro ## Adding a new Docs Page -1. add a `.mdx` file in a path under `website/src/pages/`, e.g. [website/src/pages/learn/code.mdx](https://raw.githubusercontent.com/tidalcycles/strudel/main/website/src/pages/learn/code.mdx) will be available under https://strudel.cc/learn/code (or locally under `http://localhost:4321/learn/code`) +1. add a `.mdx` file in a path under `website/src/pages/`, e.g. [website/src/pages/learn/code.mdx](https://raw.githubusercontent.com/tidalcycles/strudel/main/website/src/pages/learn/code.mdx) will be available under https://strudel.cc/learn/code/ (or locally under `http://localhost:4321/learn/code/`) 2. make sure to copy the top part of another existing docs page. Adjust the title accordingly 3. To add a link to the sidebar, add a new entry to `SIDEBAR` to [`config.ts`](https://github.com/tidalcycles/strudel/blob/main/website/src/config.ts) diff --git a/website/src/pages/technical-manual/patterns.mdx b/website/src/pages/technical-manual/patterns.mdx index 45664ad7..49ede526 100644 --- a/website/src/pages/technical-manual/patterns.mdx +++ b/website/src/pages/technical-manual/patterns.mdx @@ -14,7 +14,7 @@ Example: e.show())) silence`} diff --git a/website/src/pages/workshop/first-notes.mdx b/website/src/pages/workshop/first-notes.mdx index 330dd072..ab11ed90 100644 --- a/website/src/pages/workshop/first-notes.mdx +++ b/website/src/pages/workshop/first-notes.mdx @@ -83,7 +83,6 @@ Can you find melodies that are actual words? Hint: ☕ 😉 ⚪ client:visible tune={`note("c2 e3 g4 b5").sound("piano")`} claviature - claviatureLabels={Object.fromEntries(['c1', 'c2', 'c3', 'c4', 'c5'].map((n) => [n, n]))} claviatureLabels={Object.fromEntries( Array(49) .fill() diff --git a/website/src/repl/Header.jsx b/website/src/repl/Header.jsx index b6f13060..774d0142 100644 --- a/website/src/repl/Header.jsx +++ b/website/src/repl/Header.jsx @@ -4,8 +4,7 @@ import LinkIcon from '@heroicons/react/20/solid/LinkIcon'; import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon'; import SparklesIcon from '@heroicons/react/20/solid/SparklesIcon'; import StopCircleIcon from '@heroicons/react/20/solid/StopCircleIcon'; -import { cx } from '@strudel.cycles/react'; -import React, { useContext } from 'react'; +import cx from '@src/cx.mjs'; import { useSettings, setIsZen } from '../settings.mjs'; // import { ReplContext } from './Repl'; import './Repl.css'; @@ -18,14 +17,13 @@ export function Header({ context }) { started, pending, isDirty, - lastShared, activeCode, handleTogglePlay, handleUpdate, handleShuffle, handleShare, } = context; - const isEmbedded = embedded || window.location !== window.parent.location; + const isEmbedded = typeof window !== 'undefined' && (embedded || window.location !== window.parent.location); const { isZen } = useSettings(); return ( @@ -119,7 +117,7 @@ export function Header({ context }) { onClick={handleShare} > - share{lastShared && lastShared === (activeCode || code) ? 'd!' : ''} + share )} {!isEmbedded && ( diff --git a/website/src/repl/Loader.jsx b/website/src/repl/Loader.jsx index 0c629402..a942ec2d 100644 --- a/website/src/repl/Loader.jsx +++ b/website/src/repl/Loader.jsx @@ -1,5 +1,4 @@ -import { cx } from '@strudel.cycles/react'; -import React from 'react'; +import cx from '@src/cx.mjs'; function Loader({ active }) { return ( diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index a606d67a..72f57113 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -1,344 +1,171 @@ /* -App.js - +Repl.jsx - Copyright (C) 2022 Strudel contributors - see 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 . */ -import { cleanupDraw, cleanupUi, controls, evalScope, getDrawContext, logger } from '@strudel.cycles/core'; -import { CodeMirror, cx, flash, useHighlighting, useStrudel, useKeydown } from '@strudel.cycles/react'; -import { getAudioContext, initAudioOnFirstClick, resetLoadedSounds, webaudioOutput } from '@strudel.cycles/webaudio'; -import { createClient } from '@supabase/supabase-js'; -import { nanoid } from 'nanoid'; -import React, { createContext, useCallback, useEffect, useState, useMemo } from 'react'; +import { code2hash, getDrawContext, logger, silence } from '@strudel.cycles/core'; +import cx from '@src/cx.mjs'; +import { transpiler } from '@strudel.cycles/transpiler'; +import { getAudioContext, initAudioOnFirstClick, webaudioOutput } from '@strudel.cycles/webaudio'; +import { StrudelMirror, defaultSettings } from '@strudel/codemirror'; +import { createContext, useCallback, useEffect, useRef, useState } from 'react'; +import { + initUserCode, + setActivePattern, + setLatestCode, + settingsMap, + updateUserCode, + useSettings, +} from '../settings.mjs'; +import { Header } from './Header'; +import Loader from './Loader'; import './Repl.css'; import { Panel } from './panel/Panel'; -import { Header } from './Header'; +import { useStore } from '@nanostores/react'; import { prebake } from './prebake.mjs'; -import * as tunes from './tunes.mjs'; +import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs'; import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon'; -import { themes } from './themes.mjs'; -import { - settingsMap, - useSettings, - setLatestCode, - updateUserCode, - setActivePattern, - getActivePattern, - getUserPattern, - initUserCode, -} from '../settings.mjs'; -import Loader from './Loader'; -import { settingPatterns } from '../settings.mjs'; -import { code2hash, hash2code } from './helpers.mjs'; -import { isTauri } from '../tauri.mjs'; -import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs'; -import { writeText } from '@tauri-apps/api/clipboard'; -import { registerSamplesFromDB, userSamplesDBConfig } from './idbutils.mjs'; +import './Repl.css'; + +const { code: randomTune, name } = getRandomTune(); +export const ReplContext = createContext(null); const { latestCode } = settingsMap.get(); -initAudioOnFirstClick(); - -// Create a single supabase client for interacting with your database -const supabase = createClient( - 'https://pidxdsxphlhzjnzmifth.supabase.co', - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', -); - -let modules = [ - import('@strudel.cycles/core'), - import('@strudel.cycles/tonal'), - import('@strudel.cycles/mini'), - import('@strudel.cycles/xen'), - import('@strudel.cycles/webaudio'), - import('@strudel/codemirror'), - import('@strudel/hydra'), - import('@strudel.cycles/serial'), - import('@strudel.cycles/soundfonts'), - import('@strudel.cycles/csound'), -]; -if (isTauri()) { - modules = modules.concat([ - import('@strudel/desktopbridge/loggerbridge.mjs'), - import('@strudel/desktopbridge/midibridge.mjs'), - import('@strudel/desktopbridge/oscbridge.mjs'), - ]); -} else { - modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); -} - -const modulesLoading = evalScope( - controls, // sadly, this cannot be exported from core direclty - settingPatterns, - ...modules, -); - -const presets = prebake(); - -let drawContext, clearCanvas; +let modulesLoading, presets, drawContext, clearCanvas, isIframe; if (typeof window !== 'undefined') { + initAudioOnFirstClick(); + modulesLoading = loadModules(); + presets = prebake(); drawContext = getDrawContext(); clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); + isIframe = window.location !== window.parent.location; } -const getTime = () => getAudioContext().currentTime; - -async function initCode() { - // load code from url hash (either short hash from database or decode long hash) - try { - const initialUrl = window.location.href; - const hash = initialUrl.split('?')[1]?.split('#')?.[0]; - const codeParam = window.location.href.split('#')[1] || ''; - // looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length) - if (codeParam) { - // looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length) - return hash2code(codeParam); - } else if (hash) { - return supabase - .from('code') - .select('code') - .eq('hash', hash) - .then(({ data, error }) => { - if (error) { - console.warn('failed to load hash', err); - } - if (data.length) { - //console.log('load hash from database', hash); - return data[0].code; - } - }); - } - } catch (err) { - console.warn('failed to decode', err); - } -} - -function getRandomTune() { - const allTunes = Object.entries(tunes); - const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; - const [name, code] = randomItem(allTunes); - return { name, code }; -} - -const { code: randomTune, name } = getRandomTune(); - -export const ReplContext = createContext(null); - export function Repl({ embedded = false }) { - const isEmbedded = embedded || window.location !== window.parent.location; - const [view, setView] = useState(); // codemirror view - const [lastShared, setLastShared] = useState(); - const [pending, setPending] = useState(true); - const { - theme, - keybindings, - fontSize, - fontFamily, - isLineNumbersDisplayed, - isActiveLineHighlighted, - isAutoCompletionEnabled, - isTooltipEnabled, - isLineWrappingEnabled, - panelPosition, - isZen, - } = useSettings(); + const isEmbedded = embedded || isIframe; + const { panelPosition, isZen } = useSettings(); - const paintOptions = useMemo(() => ({ fontFamily }), [fontFamily]); - const { setWidgets } = useWidgets(view); - const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop, error } = - useStrudel({ - initialCode: '// LOADING...', + const init = useCallback(() => { + const drawTime = [-2, 2]; + const drawContext = getDrawContext(); + const onDraw = (haps, time, frame, painters) => { + painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2); + painters?.forEach((painter) => { + // ctx time haps drawTime paintOptions + painter(drawContext, time, haps, drawTime, { clear: false }); + }); + }; + const editor = new StrudelMirror({ defaultOutput: webaudioOutput, - getTime, - beforeEval: async () => { - setPending(true); - await modulesLoading; - cleanupUi(); - cleanupDraw(); + getTime: () => getAudioContext().currentTime, + transpiler, + autodraw: false, + root: containerRef.current, + initialCode: '// LOADING', + pattern: silence, + drawTime, + onDraw, + prebake: async () => Promise.all([modulesLoading, presets]), + onUpdateState: (state) => { + setReplState({ ...state }); }, - afterEval: ({ code, meta }) => { + afterEval: ({ code }) => { updateUserCode(code); - setMiniLocations(meta.miniLocations); - setWidgets(meta.widgets); - setPending(false); + // setPending(false); setLatestCode(code); window.location.hash = '#' + code2hash(code); }, - onEvalError: (err) => { - setPending(false); - }, - onToggle: (play) => { - if (!play) { - cleanupDraw(false); - window.postMessage('strudel-stop'); - } else { - window.postMessage('strudel-start'); - } - }, - drawContext, - // drawTime: [0, 6], - paintOptions, + bgFill: false, }); - - // init code - useEffect(() => { + // init settings initCode().then((decoded) => { let msg; if (decoded) { - setCode(decoded); + editor.setCode(decoded); initUserCode(decoded); msg = `I have loaded the code from the URL.`; } else if (latestCode) { - setCode(latestCode); + editor.setCode(latestCode); msg = `Your last session has been loaded!`; - } /* if(randomTune) */ else { - setCode(randomTune); + } else { + editor.setCode(randomTune); msg = `A random code snippet named "${name}" has been loaded!`; } - //registers samples that have been saved to the index DB - registerSamplesFromDB(userSamplesDBConfig); logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight'); - setPending(false); + // setPending(false); }); + + editorRef.current = editor; }, []); - // keyboard shortcuts - useKeydown( - useCallback( - async (e) => { - if (e.ctrlKey || e.altKey) { - if (e.code === 'Enter') { - if (getAudioContext().state !== 'running') { - alert('please click play to initialize the audio. you can use shortcuts after that!'); - return; - } - e.preventDefault(); - flash(view); - await activateCode(); - } else if (e.key === '.' || e.code === 'Period') { - stop(); - e.preventDefault(); - } - } - }, - [activateCode, stop, view], - ), - ); + const [replState, setReplState] = useState({}); + const { started, isDirty, error, activeCode, pending } = replState; + const editorRef = useRef(); + const containerRef = useRef(); - // highlighting - const { setMiniLocations } = useHighlighting({ - view, - pattern, - active: started && !activeCode?.includes('strudel disable-highlighting'), - getTime: () => scheduler.now(), - }); + // this can be simplified once SettingsTab has been refactored to change codemirrorSettings directly! + // this will be the case when the main repl is being replaced + const _settings = useStore(settingsMap, { keys: Object.keys(defaultSettings) }); + useEffect(() => { + let editorSettings = {}; + Object.keys(defaultSettings).forEach((key) => { + if (_settings.hasOwnProperty(key)) { + editorSettings[key] = _settings[key]; + } + }); + editorRef.current?.updateSettings(editorSettings); + }, [_settings]); // // UI Actions // - const handleChangeCode = useCallback( - (c) => { - setCode(c); - // started && logger('[edit] code changed. hit ctrl+enter to update'); - }, - [started], - ); - const handleSelectionChange = useCallback((selection) => { - // TODO: scroll to selected function in reference - // console.log('selectino change', selection.ranges[0].from); - }, []); - - const handleTogglePlay = async () => { - await getAudioContext().resume(); // fixes no sound in ios webkit - if (!started) { - logger('[repl] started. tip: you can also start by pressing ctrl+enter', 'highlight'); - activateCode(); - } else { - logger('[repl] stopped. tip: you can also stop by pressing ctrl+dot', 'highlight'); - stop(); - } - }; + const handleTogglePlay = async () => editorRef.current?.toggle(); const handleUpdate = async (newCode, reset = false) => { if (reset) { clearCanvas(); resetLoadedSounds(); - scheduler.setCps(1); + editorRef.current.repl.setCps(1); await prebake(); // declare default samples } - (newCode || isDirty) && activateCode(newCode); - logger('[repl] code updated!'); + if (newCode) { + editorRef.current.setCode(newCode); + editorRef.current.repl.evaluate(newCode); + } else if (isDirty) { + editorRef.current.evaluate(); + } }; - const handleShuffle = async () => { + // window.postMessage('strudel-shuffle'); const { code, name } = getRandomTune(); logger(`[repl] ✨ loading random tune "${name}"`); setActivePattern(name); clearCanvas(); resetLoadedSounds(); - scheduler.setCps(1); + editorRef.current.repl.setCps(1); await prebake(); // declare default samples - await evaluate(code, false); + editorRef.current.setCode(code); + editorRef.current.repl.evaluate(code); }; - const handleShare = async () => { - const codeToShare = activeCode || code; - if (lastShared === codeToShare) { - logger(`Link already generated!`, 'error'); - return; - } - // generate uuid in the browser - const hash = nanoid(12); - const shareUrl = window.location.origin + window.location.pathname + '?' + hash; - const { data, error } = await supabase.from('code').insert([{ code: codeToShare, hash }]); - if (!error) { - setLastShared(activeCode || code); - // 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); - } - }; + const handleShare = async () => shareCode(activeCode); const context = { - scheduler, embedded, started, pending, isDirty, - lastShared, activeCode, - handleChangeCode, handleTogglePlay, handleUpdate, handleShuffle, handleShare, }; - const currentTheme = useMemo(() => themes[theme] || themes.strudelTheme, [theme]); - const handleViewChanged = useCallback((v) => { - setView(v); - }, []); return ( - // bg-gradient-to-t from-blue-900 to-slate-900 - // bg-gradient-to-t from-green-900 to-slate-900 -
+
{isEmbedded && !started && ( @@ -351,23 +178,16 @@ export function Repl({ embedded = false }) { )}
-
- -
+
{ + containerRef.current = el; + if (!editorRef.current) { + init(); + } + }} + >
{panelPosition === 'right' && !isEmbedded && }
{error && ( diff --git a/website/src/repl/helpers.mjs b/website/src/repl/helpers.mjs deleted file mode 100644 index b86e76f1..00000000 --- a/website/src/repl/helpers.mjs +++ /dev/null @@ -1,25 +0,0 @@ -export function unicodeToBase64(text) { - const utf8Bytes = new TextEncoder().encode(text); - const base64String = btoa(String.fromCharCode(...utf8Bytes)); - return base64String; -} - -export function base64ToUnicode(base64String) { - const utf8Bytes = new Uint8Array( - atob(base64String) - .split('') - .map((char) => char.charCodeAt(0)), - ); - const decodedText = new TextDecoder().decode(utf8Bytes); - return decodedText; -} - -export function code2hash(code) { - return encodeURIComponent(unicodeToBase64(code)); - //return '#' + encodeURIComponent(btoa(code)); -} - -export function hash2code(hash) { - return base64ToUnicode(decodeURIComponent(hash)); - //return atob(decodeURIComponent(codeParam || '')); -} diff --git a/website/src/repl/idbutils.mjs b/website/src/repl/idbutils.mjs index 414006b9..7613b767 100644 --- a/website/src/repl/idbutils.mjs +++ b/website/src/repl/idbutils.mjs @@ -24,7 +24,7 @@ const clearIDB = () => { }; // queries the DB, and registers the sounds so they can be played -export const registerSamplesFromDB = (config, onComplete = () => {}) => { +export const registerSamplesFromDB = (config = userSamplesDBConfig, onComplete = () => {}) => { openDB(config, (objectStore) => { let query = objectStore.getAll(); query.onsuccess = (event) => { @@ -77,6 +77,9 @@ async function bufferToDataUrl(buf) { //open db and initialize it if necessary const openDB = (config, onOpened) => { const { dbName, version, table, columns } = config; + if (typeof window === 'undefined') { + return; + } if (!('indexedDB' in window)) { console.log('IndexedDB is not supported.'); return; diff --git a/website/src/repl/panel/ConsoleTab.jsx b/website/src/repl/panel/ConsoleTab.jsx index 4472fd4c..b5681ab1 100644 --- a/website/src/repl/panel/ConsoleTab.jsx +++ b/website/src/repl/panel/ConsoleTab.jsx @@ -1,5 +1,4 @@ -import { cx } from '@strudel.cycles/react'; -import React from 'react'; +import cx from '@src/cx.mjs'; export function ConsoleTab({ log }) { return ( diff --git a/website/src/repl/panel/Forms.jsx b/website/src/repl/panel/Forms.jsx index 49d0e913..c5b22002 100644 --- a/website/src/repl/panel/Forms.jsx +++ b/website/src/repl/panel/Forms.jsx @@ -1,5 +1,4 @@ -import { cx } from '@strudel.cycles/react'; -import React from 'react'; +import cx from '@src/cx.mjs'; export function ButtonGroup({ value, onChange, items }) { return ( diff --git a/website/src/repl/panel/Panel.jsx b/website/src/repl/panel/Panel.jsx index b815770c..eac31720 100644 --- a/website/src/repl/panel/Panel.jsx +++ b/website/src/repl/panel/Panel.jsx @@ -1,8 +1,9 @@ import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon'; import { logger } from '@strudel.cycles/core'; -import { cx, useEvent } from '@strudel.cycles/react'; +import useEvent from '@src/useEvent.mjs'; +import cx from '@src/cx.mjs'; import { nanoid } from 'nanoid'; -import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'; +import { useCallback, useLayoutEffect, useEffect, useRef, useState } from 'react'; import { setActiveFooter, useSettings } from '../../settings.mjs'; import { ConsoleTab } from './ConsoleTab'; import { FilesTab } from './FilesTab'; @@ -11,21 +12,25 @@ import { SettingsTab } from './SettingsTab'; import { SoundsTab } from './SoundsTab'; import { WelcomeTab } from './WelcomeTab'; import { PatternsTab } from './PatternsTab'; +import useClient from '@src/useClient.mjs'; -const TAURI = window.__TAURI__; +// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85 +export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; + +const TAURI = typeof window !== 'undefined' && window.__TAURI__; export function Panel({ context }) { const footerContent = useRef(); const [log, setLog] = useState([]); const { activeFooter, isZen, panelPosition } = useSettings(); - useLayoutEffect(() => { + useIsomorphicLayoutEffect(() => { if (footerContent.current && activeFooter === 'console') { // scroll log box to bottom when log changes footerContent.current.scrollTop = footerContent.current?.scrollHeight; } }, [log, activeFooter]); - useLayoutEffect(() => { + useIsomorphicLayoutEffect(() => { if (!footerContent.current) { } else if (activeFooter === 'console') { footerContent.current.scrollTop = footerContent.current?.scrollHeight; @@ -79,6 +84,10 @@ export function Panel({ context }) { right: cx('max-w-full flex-grow-0 flex-none overflow-hidden', isActive ? 'w-[600px] h-full' : 'absolute right-0'), bottom: cx('relative', isActive ? 'h-[360px] min-h-[360px]' : ''), }; + const client = useClient(); + if (!client) { + return null; + } return (
diff --git a/website/src/repl/panel/SettingsTab.jsx b/website/src/repl/panel/SettingsTab.jsx index cee5b286..9297b0e4 100644 --- a/website/src/repl/panel/SettingsTab.jsx +++ b/website/src/repl/panel/SettingsTab.jsx @@ -1,6 +1,5 @@ -import React from 'react'; import { defaultSettings, settingsMap, useSettings } from '../../settings.mjs'; -import { themes } from '../themes.mjs'; +import { themes } from '@strudel/codemirror'; import { ButtonGroup } from './Forms.jsx'; function Checkbox({ label, value, onChange }) { @@ -78,9 +77,11 @@ export function SettingsTab() { theme, keybindings, isLineNumbersDisplayed, + isPatternHighlightingEnabled, isActiveLineHighlighted, isAutoCompletionEnabled, isTooltipEnabled, + isFlashEnabled, isLineWrappingEnabled, fontSize, fontFamily, @@ -89,24 +90,6 @@ export function SettingsTab() { return (
- {/* -
- - -
-
*/} settingsMap.setKey('theme', theme)} /> @@ -153,6 +136,11 @@ export function SettingsTab() { onChange={(cbEvent) => settingsMap.setKey('isActiveLineHighlighted', cbEvent.target.checked)} value={isActiveLineHighlighted} /> + settingsMap.setKey('isPatternHighlightingEnabled', cbEvent.target.checked)} + value={isPatternHighlightingEnabled} + /> settingsMap.setKey('isAutoCompletionEnabled', cbEvent.target.checked)} @@ -168,6 +156,11 @@ export function SettingsTab() { onChange={(cbEvent) => settingsMap.setKey('isLineWrappingEnabled', cbEvent.target.checked)} value={isLineWrappingEnabled} /> + settingsMap.setKey('isFlashEnabled', cbEvent.target.checked)} + value={isFlashEnabled} + /> Try clicking the logo in the top left! diff --git a/website/src/repl/panel/SoundsTab.jsx b/website/src/repl/panel/SoundsTab.jsx index 976361d0..a3639dfb 100644 --- a/website/src/repl/panel/SoundsTab.jsx +++ b/website/src/repl/panel/SoundsTab.jsx @@ -1,5 +1,4 @@ -import { useEvent } from '@strudel.cycles/react'; -// import { cx } from '@strudel.cycles/react'; +import useEvent from '@src/useEvent.mjs'; import { useStore } from '@nanostores/react'; import { getAudioContext, soundMap, connectToDestination } from '@strudel.cycles/webaudio'; import React, { useMemo, useRef } from 'react'; diff --git a/website/src/repl/panel/WelcomeTab.jsx b/website/src/repl/panel/WelcomeTab.jsx index feb0a583..bda6c3f5 100644 --- a/website/src/repl/panel/WelcomeTab.jsx +++ b/website/src/repl/panel/WelcomeTab.jsx @@ -1,6 +1,4 @@ -import { cx } from '@strudel.cycles/react'; -import React from 'react'; -import * as tunes from '../tunes.mjs'; +import cx from '@src/cx.mjs'; const { BASE_URL } = import.meta.env; const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL; diff --git a/website/src/repl/prebake.mjs b/website/src/repl/prebake.mjs index 68f6f8a1..96a484e7 100644 --- a/website/src/repl/prebake.mjs +++ b/website/src/repl/prebake.mjs @@ -1,5 +1,6 @@ import { Pattern, noteToMidi, valueToMidi } from '@strudel.cycles/core'; import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio'; +import { registerSamplesFromDB } from './idbutils.mjs'; import './piano.mjs'; import './files.mjs'; @@ -12,6 +13,7 @@ export async function prebake() { await Promise.all([ registerSynthSounds(), registerZZFXSounds(), + registerSamplesFromDB(), //registerSoundfonts(), // need dynamic import here, because importing @strudel.cycles/soundfonts fails on server: // => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs new file mode 100644 index 00000000..2b93b619 --- /dev/null +++ b/website/src/repl/util.mjs @@ -0,0 +1,112 @@ +import { controls, evalScope, hash2code, logger } from '@strudel.cycles/core'; +import { settingPatterns } from '../settings.mjs'; +import { isTauri } from '../tauri.mjs'; +import './Repl.css'; +import * as tunes from './tunes.mjs'; +import { createClient } from '@supabase/supabase-js'; +import { nanoid } from 'nanoid'; +import { writeText } from '@tauri-apps/api/clipboard'; + +// Create a single supabase client for interacting with your database +const supabase = createClient( + 'https://pidxdsxphlhzjnzmifth.supabase.co', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', +); + +export async function initCode() { + // load code from url hash (either short hash from database or decode long hash) + try { + const initialUrl = window.location.href; + const hash = initialUrl.split('?')[1]?.split('#')?.[0]; + const codeParam = window.location.href.split('#')[1] || ''; + // looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length) + if (codeParam) { + // looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length) + return hash2code(codeParam); + } else if (hash) { + return supabase + .from('code') + .select('code') + .eq('hash', hash) + .then(({ data, error }) => { + if (error) { + console.warn('failed to load hash', error); + } + if (data.length) { + //console.log('load hash from database', hash); + return data[0].code; + } + }); + } + } catch (err) { + console.warn('failed to decode', err); + } +} + +export function getRandomTune() { + const allTunes = Object.entries(tunes); + const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; + const [name, code] = randomItem(allTunes); + return { name, code }; +} + +export function loadModules() { + let modules = [ + import('@strudel.cycles/core'), + import('@strudel.cycles/tonal'), + import('@strudel.cycles/mini'), + import('@strudel.cycles/xen'), + import('@strudel.cycles/webaudio'), + import('@strudel/codemirror'), + import('@strudel/hydra'), + import('@strudel.cycles/serial'), + import('@strudel.cycles/soundfonts'), + import('@strudel.cycles/csound'), + ]; + if (isTauri()) { + modules = modules.concat([ + import('@strudel/desktopbridge/loggerbridge.mjs'), + import('@strudel/desktopbridge/midibridge.mjs'), + import('@strudel/desktopbridge/oscbridge.mjs'), + ]); + } else { + modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); + } + + return evalScope( + controls, // sadly, this cannot be exported from core direclty + settingPatterns, + ...modules, + ); +} + +let lastShared; +export async function shareCode(codeToShare) { + // const codeToShare = activeCode || code; + if (lastShared === codeToShare) { + logger(`Link already generated!`, 'error'); + return; + } + // generate uuid in the browser + const hash = nanoid(12); + const shareUrl = window.location.origin + window.location.pathname + '?' + hash; + const { data, error } = await supabase.from('code').insert([{ code: codeToShare, hash }]); + 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); + } +} diff --git a/website/src/settings.mjs b/website/src/settings.mjs index 570b6446..f3ceeb6d 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -11,14 +11,16 @@ export const defaultSettings = { isActiveLineHighlighted: true, isAutoCompletionEnabled: false, isTooltipEnabled: false, + isFlashEnabled: true, isLineWrappingEnabled: false, + isPatternHighlightingEnabled: true, theme: 'strudelTheme', fontFamily: 'monospace', fontSize: 18, latestCode: '', isZen: false, soundsFilter: 'all', - panelPosition: 'bottom', + panelPosition: 'right', userPatterns: '{}', }; @@ -50,10 +52,12 @@ export function useSettings() { isLineNumbersDisplayed: [true, 'true'].includes(state.isLineNumbersDisplayed) ? true : false, isActiveLineHighlighted: [true, 'true'].includes(state.isActiveLineHighlighted) ? true : false, isAutoCompletionEnabled: [true, 'true'].includes(state.isAutoCompletionEnabled) ? true : false, + isPatternHighlightingEnabled: [true, 'true'].includes(state.isPatternHighlightingEnabled) ? true : false, isTooltipEnabled: [true, 'true'].includes(state.isTooltipEnabled) ? true : false, isLineWrappingEnabled: [true, 'true'].includes(state.isLineWrappingEnabled) ? true : false, + isFlashEnabled: [true, 'true'].includes(state.isFlashEnabled) ? true : false, fontSize: Number(state.fontSize), - panelPosition: state.activeFooter !== '' ? state.panelPosition : 'bottom', + panelPosition: state.activeFooter !== '' ? state.panelPosition : 'bottom', // <-- keep this 'bottom' where it is! userPatterns: JSON.parse(state.userPatterns), }; } diff --git a/website/src/useClient.mjs b/website/src/useClient.mjs new file mode 100644 index 00000000..b695a36c --- /dev/null +++ b/website/src/useClient.mjs @@ -0,0 +1,9 @@ +import { useEffect, useState } from 'react'; + +export default function useClient() { + const [client, setClient] = useState(false); + useEffect(() => { + setClient(true); + }, []); + return client; +} diff --git a/packages/react/src/hooks/useEvent.mjs b/website/src/useEvent.mjs similarity index 100% rename from packages/react/src/hooks/useEvent.mjs rename to website/src/useEvent.mjs diff --git a/packages/react/src/hooks/useFrame.mjs b/website/src/useFrame.mjs similarity index 100% rename from packages/react/src/hooks/useFrame.mjs rename to website/src/useFrame.mjs