move repl web component to new repl package

+ use it in /vanilla + /vanilla/mini
This commit is contained in:
Felix Roos 2023-12-15 10:12:47 +01:00
parent d4afbc63e2
commit f0c3d38ea7
12 changed files with 204 additions and 164 deletions

View File

@ -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';
}

3
packages/repl/README.md Normal file
View File

@ -0,0 +1,3 @@
# @strudel/repl
The Strudel REPL as a web component.

1
packages/repl/index.mjs Normal file
View File

@ -0,0 +1 @@
export * from './repl-component.mjs';

View File

@ -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 <flix91@gmail.com>",
"contributors": [
"Alex McLean <alex@slab.org>"
],
"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"
}
}

34
packages/repl/prebake.mjs Normal file
View File

@ -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`),
]);
}

View File

@ -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);

72
pnpm-lock.yaml generated
View File

@ -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}

View File

@ -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",

View File

@ -90,7 +90,8 @@ import HeadCommonNew from '../../components/HeadCommonNew.astro';
<!-- <label><input type="checkbox" name="isTooltipEnabled" />isTooltipEnabled</label> -->
</form>
</div>
<div id="code"></div>
<strudel-editor id="editor"></strudel-editor>
<script src="../../repl/vanilla/vanilla.mjs"></script>
<script src="@strudel/repl"></script>
</body>
</html>

View File

@ -2,13 +2,29 @@
import HeadCommonNew from '../../components/HeadCommonNew.astro';
---
<html lang="en" class="dark">
<head>
<HeadCommonNew />
<!-- <HeadCommonNew /> -->
<style>
:root {
--background: #cd8b8b;
--lineBackground: #22222299;
--foreground: #fff;
--caret: #ffcc00;
--selection: rgba(128, 203, 196, 0.5);
--selectionMatch: #036dd626;
--lineHighlight: #00000050;
--gutterBackground: transparent;
--gutterForeground: #8a919966;
}
</style>
<title>Strudel Vanilla REPL</title>
<script src="@strudel/repl"></script>
</head>
<body class="h-app-height">
<h1>vanilli repl</h1>
<p>This is a REPL:</p>
<strudel-editor
keybindings="emacs"
is-line-numbers-displayed="1"
@ -21,45 +37,9 @@ import HeadCommonNew from '../../components/HeadCommonNew.astro';
theme="teletext"
font-family="monospace"
font-size="18"
code={`// @date 23-11-30
// "teigrührgerät" @by froos
stack(
stack(
s("bd(<3!3 5>,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("<g1(<3 4>,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("<Gm9 Gm11>/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)))
)`}></strudel-editor>
<script src="../../repl/vanilla/strudel-editor.mjs"></script>
code={`s("bd")`}></strudel-editor>
<p>This is another REPL:</p>
<strudel-editor code={`s("hh*4")`}></strudel-editor>
</body>
</html>

View File

@ -12,7 +12,7 @@ select {
html,
body,
#code,
#editor,
.cm-editor,
.cm-scroller {
padding: 0;

View File

@ -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);
});