From f0c3d38ea7e089d3f6ba6c8a54cb4bef0cba1157 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 15 Dec 2023 10:12:47 +0100 Subject: [PATCH] move repl web component to new repl package + use it in /vanilla + /vanilla/mini --- packages/codemirror/codemirror.mjs | 1 + packages/repl/README.md | 3 + packages/repl/index.mjs | 1 + packages/repl/package.json | 49 ++++++++++++ packages/repl/prebake.mjs | 34 ++++++++ .../repl/repl-component.mjs | 62 ++++----------- pnpm-lock.yaml | 72 +++++++++++++++++ website/package.json | 1 + website/src/pages/vanilla/index.astro | 3 +- website/src/pages/vanilla/mini.astro | 62 +++++---------- website/src/repl/vanilla/vanilla.css | 2 +- website/src/repl/vanilla/vanilla.mjs | 78 ++----------------- 12 files changed, 204 insertions(+), 164 deletions(-) create mode 100644 packages/repl/README.md create mode 100644 packages/repl/index.mjs create mode 100644 packages/repl/package.json create mode 100644 packages/repl/prebake.mjs rename website/src/repl/vanilla/strudel-editor.mjs => packages/repl/repl-component.mjs (60%) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 5ceb9619..43b74ef8 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -154,6 +154,7 @@ export class StrudelMirror { }); const cmEditor = this.root.querySelector('.cm-editor'); if (cmEditor) { + this.root.style.display = 'block'; this.root.style.backgroundColor = 'var(--background)'; cmEditor.style.backgroundColor = 'transparent'; } 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..330cd77d --- /dev/null +++ b/packages/repl/index.mjs @@ -0,0 +1 @@ +export * from './repl-component.mjs'; diff --git a/packages/repl/package.json b/packages/repl/package.json new file mode 100644 index 00000000..08e3fbcf --- /dev/null +++ b/packages/repl/package.json @@ -0,0 +1,49 @@ +{ + "name": "@strudel/repl", + "version": "0.9.0", + "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": { + "@strudel.cycles/core": "workspace:*", + "@strudel.cycles/mini": "workspace:*", + "@strudel.cycles/tonal": "workspace:*", + "@strudel.cycles/transpiler": "workspace:*", + "@strudel.cycles/webaudio": "workspace:*", + "@strudel/codemirror": "workspace:*", + "@strudel/hydra": "workspace:*", + "@strudel.cycles/soundfonts": "workspace:*", + "@strudel.cycles/midi": "workspace:*" + }, + "devDependencies": { + "vite": "^4.3.3" + } +} diff --git a/packages/repl/prebake.mjs b/packages/repl/prebake.mjs new file mode 100644 index 00000000..65e2d96f --- /dev/null +++ b/packages/repl/prebake.mjs @@ -0,0 +1,34 @@ +import { controls, evalScope } from '@strudel.cycles/core'; +import { registerSoundfonts } from '@strudel.cycles/soundfonts'; +import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio'; + +export async function prebake() { + const modulesLoading = evalScope( + import('@strudel.cycles/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(), + samples(`${ds}/tidal-drum-machines.json`), + samples(`${ds}/piano.json`), + samples(`${ds}/Dirt-Samples.json`), + samples(`${ds}/EmuSP12.json`), + samples(`${ds}/vcsl.json`), + ]); +} diff --git a/website/src/repl/vanilla/strudel-editor.mjs b/packages/repl/repl-component.mjs similarity index 60% rename from website/src/repl/vanilla/strudel-editor.mjs rename to packages/repl/repl-component.mjs index 85acc72e..44220766 100644 --- a/website/src/repl/vanilla/strudel-editor.mjs +++ b/packages/repl/repl-component.mjs @@ -1,14 +1,8 @@ -import { getDrawContext, silence, controls, evalScope, hash2code, code2hash } from '@strudel.cycles/core'; -import { StrudelMirror } from '@strudel/codemirror'; +import { getDrawContext, silence } from '@strudel.cycles/core'; import { transpiler } from '@strudel.cycles/transpiler'; -import { registerSoundfonts } from '@strudel.cycles/soundfonts'; -import { - getAudioContext, - webaudioOutput, - registerSynthSounds, - registerZZFXSounds, - samples, -} from '@strudel.cycles/webaudio'; +import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio'; +import { StrudelMirror } from '@strudel/codemirror'; +import { prebake } from './prebake.mjs'; function camelToKebab(camelCaseString) { return camelCaseString.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); @@ -46,13 +40,16 @@ const parseAttribute = (name, value) => { return value; }; // console.log('attributes', settingAttributes); - -class StrudelEditor extends HTMLElement { +class StrudelRepl extends HTMLElement { static observedAttributes = ['code', ...settingAttributes]; settings = initialSettings; + editor = null; constructor() { super(); } + onReady(listener) { + this.readyListener = listener; + } attributeChangedCallback(name, oldValue, newValue) { if (name === 'code') { this.code = newValue; @@ -67,13 +64,11 @@ class StrudelEditor extends HTMLElement { connectedCallback() { const drawContext = getDrawContext(); const drawTime = [-2, 2]; - this.container = document.createElement('div'); - this.appendChild(this.container); this.editor = new StrudelMirror({ defaultOutput: webaudioOutput, getTime: () => getAudioContext().currentTime, transpiler, - root: this.container, + root: this, initialCode: '// LOADING', pattern: silence, settings: this.settings, @@ -85,48 +80,19 @@ class StrudelEditor extends HTMLElement { painter(drawContext, time, haps, drawTime, { clear: false }); }); }, - prebake: async () => { - // populate scope / lazy load modules - const modulesLoading = evalScope( - import('@strudel.cycles/core'), - import('@strudel.cycles/tonal'), - import('@strudel.cycles/mini'), - import('@strudel.cycles/webaudio'), - import('@strudel/codemirror'), - import('@strudel/hydra'), - import('@strudel.cycles/soundfonts'), - // import('@strudel.cycles/xen'), - // import('@strudel.cycles/serial'), - // import('@strudel.cycles/csound'), - /* import('@strudel.cycles/midi'), */ - // 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(), - samples(`${ds}/tidal-drum-machines.json`), - samples(`${ds}/piano.json`), - samples(`${ds}/Dirt-Samples.json`), - samples(`${ds}/EmuSP12.json`), - samples(`${ds}/vcsl.json`), - ]); - }, + prebake, afterEval: ({ code }) => { - window.location.hash = '#' + code2hash(code); + // window.location.hash = '#' + code2hash(code); }, }); // init settings this.editor.updateSettings(this.settings); this.editor.setCode(this.code); + this.readyListener?.(this); // settingsMap.listen((settings, key) => editor.changeSetting(key, settings[key])); // onEvent('strudel-toggle-play', () => this.editor.toggle()); } // Element functionality written in here } -customElements.define('strudel-editor', StrudelEditor); +customElements.define('strudel-editor', StrudelRepl); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2416a62..3fa26a48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -468,6 +468,40 @@ importers: specifier: ^4.3.3 version: 4.3.3 + packages/repl: + dependencies: + '@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 + '@strudel.cycles/webaudio': + specifier: workspace:* + version: link:../webaudio + '@strudel/codemirror': + specifier: workspace:* + version: link:../codemirror + '@strudel/hydra': + specifier: workspace:* + version: link:../hydra + devDependencies: + vite: + specifier: ^4.3.3 + version: 4.5.0 + packages/serial: dependencies: '@strudel.cycles/core': @@ -702,6 +736,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 @@ -14331,6 +14368,41 @@ packages: fsevents: 2.3.3 dev: true + /vite@4.5.0: + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.18.20 + postcss: 8.4.32 + rollup: 3.28.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vite@4.5.0(@types/node@18.16.3): resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/website/package.json b/website/package.json index f5354842..b2267a69 100644 --- a/website/package.json +++ b/website/package.json @@ -37,6 +37,7 @@ "@strudel/hydra": "workspace:*", "@strudel/codemirror": "workspace:*", "@strudel/desktopbridge": "workspace:*", + "@strudel/repl": "workspace:*", "@supabase/supabase-js": "^2.21.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.8", diff --git a/website/src/pages/vanilla/index.astro b/website/src/pages/vanilla/index.astro index 44144bb6..d5c41d6d 100644 --- a/website/src/pages/vanilla/index.astro +++ b/website/src/pages/vanilla/index.astro @@ -90,7 +90,8 @@ import HeadCommonNew from '../../components/HeadCommonNew.astro'; -
+ + diff --git a/website/src/pages/vanilla/mini.astro b/website/src/pages/vanilla/mini.astro index 77dd58b6..68dccb8a 100644 --- a/website/src/pages/vanilla/mini.astro +++ b/website/src/pages/vanilla/mini.astro @@ -2,13 +2,29 @@ import HeadCommonNew from '../../components/HeadCommonNew.astro'; --- + - + + Strudel Vanilla REPL +

vanilli repl

+

This is a REPL:

,6)/2").bank('RolandTR707') - , - s("~ sd:<0 1>").bank('RolandTR707').room("<0 .5>") - .lastOf(8, x=>x.segment("12").end(.2).gain(isaw)) - , - s("[tb ~ tb]").bank('RolandTR707') - .clip(0).release(.08).room(.2) - ).off(-1/6, x=>x.speed(.7).gain(.2).degrade()) - , - stack( - note(",6) ~!2 [f1?]*2>") - .s("sawtooth").lpf(perlin.range(400,1000)) - .lpa(.1).lpenv(-3).room(.2) - .lpq(8).noise(.2) - .add(note("0,.1")) - , - chord("<~ Gm9 ~!2>") - .dict('ireal').voicing() - .s("sawtooth").vib("2:.1") - .lpf(1000).lpa(.1).lpenv(-4) - .room(.5) - , - n(run(3)).chord("/8") - .dict('ireal-ext') - .off(1/2, add(n(4))) - .voicing() - .clip(.1).release(.05) - .s("sine").jux(rev) - .sometimesBy(sine.slow(16), add(note(12))) - .room(.75) - .lpf(sine.range(200,2000).slow(16)) - .gain(saw.slow(4).div(2)) - ).add(note(perlin.range(0,.5))) - )`}> - + code={`s("bd")`}> + +

This is another REPL:

+ diff --git a/website/src/repl/vanilla/vanilla.css b/website/src/repl/vanilla/vanilla.css index 584dd924..5387fb04 100644 --- a/website/src/repl/vanilla/vanilla.css +++ b/website/src/repl/vanilla/vanilla.css @@ -12,7 +12,7 @@ select { html, body, -#code, +#editor, .cm-editor, .cm-scroller { padding: 0; diff --git a/website/src/repl/vanilla/vanilla.mjs b/website/src/repl/vanilla/vanilla.mjs index 488fcf8c..721b745c 100644 --- a/website/src/repl/vanilla/vanilla.mjs +++ b/website/src/repl/vanilla/vanilla.mjs @@ -1,13 +1,5 @@ -import { logger, getDrawContext, silence, controls, evalScope, hash2code, code2hash } from '@strudel.cycles/core'; -import { StrudelMirror, initTheme, activateTheme } from '@strudel/codemirror'; -import { transpiler } from '@strudel.cycles/transpiler'; -import { - getAudioContext, - webaudioOutput, - registerSynthSounds, - registerZZFXSounds, - samples, -} from '@strudel.cycles/webaudio'; +import { hash2code, logger } from '@strudel.cycles/core'; +import { activateTheme, initTheme } from '@strudel/codemirror'; import './vanilla.css'; let editor; @@ -27,68 +19,9 @@ const initialSettings = { initTheme(initialSettings.theme); async function run() { - const container = document.getElementById('code'); - if (!container) { - console.warn('could not init: no container found'); - return; - } - - const drawContext = getDrawContext(); - const drawTime = [-2, 2]; - editor = new StrudelMirror({ - defaultOutput: webaudioOutput, - getTime: () => getAudioContext().currentTime, - transpiler, - root: container, - initialCode: '// LOADING', - pattern: silence, - settings: initialSettings, - 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: async () => { - // populate scope / lazy load modules - const modulesLoading = evalScope( - 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'), - /* import('@strudel.cycles/midi'), */ - // 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(), - samples(`${ds}/tidal-drum-machines.json`), - samples(`${ds}/piano.json`), - samples(`${ds}/Dirt-Samples.json`), - samples(`${ds}/EmuSP12.json`), - samples(`${ds}/vcsl.json`), - ]); - }, - afterEval: ({ code }) => { - window.location.hash = '#' + code2hash(code); - }, - }); - - // init settings + const repl = document.getElementById('editor'); + editor = repl.editor; editor.updateSettings(initialSettings); - logger(`Welcome to Strudel! Click into the editor and then hit ctrl+enter to run the code!`, 'highlight'); const codeParam = window.location.href.split('#')[1] || ''; @@ -195,8 +128,7 @@ const form = document.querySelector('form[name=settings]'); setFormValues(form, initialSettings); form.addEventListener('change', () => { const values = getFormValues(form, initialSettings); - // console.log('values', values); - editor.updateSettings(values); + editor?.updateSettings(values); // TODO: only activateTheme when it changes activateTheme(values.theme); });