From a9dc0912d0f1857416518666092e926817674296 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 17:54:51 +0100 Subject: [PATCH 01/19] fix: drawer performance issue --- packages/codemirror/codemirror.mjs | 8 ++++++++ packages/core/draw.mjs | 3 +++ website/src/repl/Repl2.jsx | 5 +---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 29dca867..91f9fd8f 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -126,6 +126,7 @@ export class StrudelMirror { this.miniLocations = []; this.widgets = []; this.painters = []; + this.drawTime = drawTime; this.onDraw = onDraw; const self = this; this.id = id || s4(); @@ -151,6 +152,7 @@ export class StrudelMirror { onToggle: (started) => { replOptions?.onToggle?.(started); if (started) { + this.adjustDrawTime(); this.drawer.start(this.repl.scheduler); // stop other repls when this one is started document.dispatchEvent( @@ -177,6 +179,7 @@ export class StrudelMirror { updateWidgets(this.editor, this.widgets); updateMiniLocations(this.editor, this.miniLocations); replOptions?.afterEval?.(options); + this.adjustDrawTime(); this.drawer.invalidate(); }, }); @@ -212,6 +215,11 @@ export class StrudelMirror { }; 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; diff --git a/packages/core/draw.mjs b/packages/core/draw.mjs index ff5359e1..0d2796f1 100644 --- a/packages/core/draw.mjs +++ b/packages/core/draw.mjs @@ -145,6 +145,9 @@ export class Drawer { }, ); } + setDrawTime(drawTime) { + this.drawTime = drawTime; + } invalidate(scheduler = this.scheduler, t) { if (!scheduler) { return; diff --git a/website/src/repl/Repl2.jsx b/website/src/repl/Repl2.jsx index 641d2a91..fff89e52 100644 --- a/website/src/repl/Repl2.jsx +++ b/website/src/repl/Repl2.jsx @@ -57,10 +57,7 @@ export function Repl2({ embedded = false }) { const shouldDraw = true; const init = useCallback(({ shouldDraw }) => { - // TODO: find way to make spiral & punchcard work (if there's any) - // upping the 2nd value leads to slow eval times - // because Drawer.invalidate might query alot at one time - const drawTime = [0, 0]; + const drawTime = [-2, 2]; const drawContext = shouldDraw ? getDrawContext() : null; let onDraw; if (shouldDraw) { From f96827d052e3d96530e2fe71635eda4712448b09 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 18:03:50 +0100 Subject: [PATCH 02/19] fix: initial highlighting --- packages/core/draw.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/draw.mjs b/packages/core/draw.mjs index 0d2796f1..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 From 837f8f318d95b1f7af98bacb41e47bd1746846c7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 19:40:01 +0100 Subject: [PATCH 03/19] microrepl claviature support --- website/src/docs/MicroRepl.jsx | 43 +++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/website/src/docs/MicroRepl.jsx b/website/src/docs/MicroRepl.jsx index 72561669..7d2e260b 100644 --- a/website/src/docs/MicroRepl.jsx +++ b/website/src/docs/MicroRepl.jsx @@ -1,31 +1,35 @@ import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { Icon } from './Icon'; -import { silence, getPunchcardPainter } from '@strudel.cycles/core'; +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 Claviature from '@components/Claviature'; export function MicroRepl({ code, hideHeader = false, canvasHeight = 100, onTrigger, - onPaint, punchcard, punchcardLabels = true, + claviature, + claviatureLabels, }) { const id = useMemo(() => s4(), []); const canvasId = useMemo(() => `canvas-${id}`, [id]); - const shouldDraw = !!punchcard; + const shouldDraw = !!punchcard || !!claviature; + const shouldShowCanvas = !!punchcard; + const drawTime = punchcard ? [0, 4] : [0, 0]; + const [activeNotes, setActiveNotes] = useState([]); const init = useCallback(({ code, shouldDraw }) => { - const drawTime = [0, 4]; 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.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 }); @@ -48,10 +52,17 @@ export function MicroRepl({ if (onTrigger) { pat = pat.onTrigger(onTrigger, false); } - if (onPaint) { - editor.painters.push(onPaint); - } else if (punchcard) { - editor.painters.push(getPunchcardPainter({ labels: !!punchcardLabels })); + 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; }, @@ -78,7 +89,7 @@ export function MicroRepl({ }); } return () => { - editor.clear(); + editorRef.current?.clear(); }; }, []); @@ -116,7 +127,7 @@ export function MicroRepl({
{error &&
{error.message}
} - {shouldDraw && ( + {shouldShowCanvas && ( ) */} + {claviature && ( + + )} ); } From 975a198ee95f4f2e3f6424730c646ba6c4b55bd2 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 20:36:23 +0100 Subject: [PATCH 04/19] whole docs now run new repl - move MicroRepl code to MiniRepl - fix a ssr bug --- website/src/docs/MicroRepl.jsx | 172 ---------- website/src/docs/MiniRepl.jsx | 224 +++++++++---- website/src/pages/recipes/recipes-next.mdx | 312 ------------------ .../src/pages/technical-manual/patterns.mdx | 2 +- website/src/repl/idbutils.mjs | 3 + 5 files changed, 158 insertions(+), 555 deletions(-) delete mode 100644 website/src/docs/MicroRepl.jsx delete mode 100644 website/src/pages/recipes/recipes-next.mdx diff --git a/website/src/docs/MicroRepl.jsx b/website/src/docs/MicroRepl.jsx deleted file mode 100644 index 7d2e260b..00000000 --- a/website/src/docs/MicroRepl.jsx +++ /dev/null @@ -1,172 +0,0 @@ -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 Claviature from '@components/Claviature'; - -export function MicroRepl({ - code, - hideHeader = false, - canvasHeight = 100, - onTrigger, - punchcard, - punchcardLabels = true, - claviature, - claviatureLabels, -}) { - 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([]); - - 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, - 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, setClient] = useState(false); - useEffect(() => { - setClient(true); - if (!editorRef.current) { - setTimeout(() => { - init({ code, shouldDraw }); - }); - } - return () => { - editorRef.current?.clear(); - }; - }, []); - - if (!client) { - return
{code}
; - } - - return ( -
- {!hideHeader && ( -
-
- - -
-
- )} -
-
- {error &&
{error.message}
} -
- {shouldShowCanvas && ( - { - if (el && el.width !== el.clientWidth) { - el.width = el.clientWidth; - } - }} - > - )} - {/* !!log.length && ( -
- {log.map(({ message }, i) => ( -
{message}
- ))} -
- ) */} - {claviature && ( - - )} -
- ); -} - -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/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 4b37ebad..3a16f3dd 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,84 +1,159 @@ -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'; -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, setClient] = useState(false); + useEffect(() => { + setClient(true); + if (!editorRef.current) { + setTimeout(() => { + init({ code, shouldDraw }); + }); + } + return () => { + editorRef.current?.clear(); + }; + }, []); + + if (!client) { + return
{code}
; + } + + return ( +
+ {!hideHeader && ( +
+
+ + +
+
+ )} +
+
+ {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/recipes/recipes-next.mdx b/website/src/pages/recipes/recipes-next.mdx deleted file mode 100644 index 4230063f..00000000 --- a/website/src/pages/recipes/recipes-next.mdx +++ /dev/null @@ -1,312 +0,0 @@ ---- -title: Recipes -layout: ../../layouts/MainLayout.astro ---- - -import { MicroRepl } from '../../docs/MicroRepl'; - -# Recipes - -This page shows possible ways to achieve common (or not so common) musical goals. -There are often many ways to do a thing and there is no right or wrong. -The fun part is that each representation will give you different impulses when improvising. - -## Arpeggios - -An arpeggio is when the notes of a chord are played in sequence. -We can either write the notes by hand: - - - -...or use scales: - - - -...or chord symbols: - - - -...using off: - - - -## Chopping Breaks - -A sample can be looped and chopped like this: - - - -This fits the break into 8 cycles + chops it in 16 pieces. -The chops are not audible yet, because we're not doing any manipulation. -Let's add randmized doubling + reversing: - - - -If we want to specify the order of samples, we can replace `chop` with `slice`: - -") - .cut(1).rarely(ply(2))`} - punchcard -/> - -If we use `splice` instead of `slice`, the speed adjusts to the duration of the event: - -") - .cut(1).rarely(ply(2))`} - punchcard -/> - -Note that we don't need `fit`, because `splice` will do that by itself. - -## Filter Envelopes - -A minimal filter envelope looks like this: - - d2") - .s("sawtooth") - .lpf(400).lpa(.2).lpenv(4) - .scope()`} -/> - -We can flip the envelope by setting `lpenv` negative + add some resonance `lpq`: - - d2") - .s("sawtooth").lpq(8) - .lpf(400).lpa(.2).lpenv(-4) - .scope()`} -/> - -## Layering Sounds - -We can layer sounds by separating them with ",": - -") -.s("sawtooth, square") // <------ -.scope()`} -/> - -We can control the gain of individual sounds like this: - -") -.s("sawtooth, square:0:.5") // <--- "name:number:gain" -.scope()`} -/> - -For more control over each voice, we can use `layer`: - -").layer( - x=>x.s("sawtooth").vib(4), - x=>x.s("square").add(note(12)) -).scope()`} -/> - -Here, we give the sawtooth a vibrato and the square is moved an octave up. -With `layer`, you can use any pattern method available on each voice, so sky is the limit.. - -## Oscillator Detune - -We can fatten a sound by adding a detuned version to itself: - -") -.add(note("0,.1")) // <------ chorus -.s("sawtooth").scope()`} - punchcard -/> - -Try out different values, or add another voice! - -## Polyrhythms - -Here is a simple example of a polyrhythm: - - - -A polyrhythm is when 2 different tempos happen at the same time. - -## Polymeter - -This is a polymeter: - -,").fast(2)`} punchcard /> - -A polymeter is when 2 different bar lengths play at the same tempo. - -## Phasing - -This is a phasing: - -*[6,6.1]").piano()`} punchcard /> - -Phasing happens when the same sequence plays at slightly different tempos. - -## Running through samples - -Using `run` with `n`, we can rush through a sample bank: - - - -This works great with sample banks that contain similar sounds, like in this case different recordings of a tabla. -Often times, you'll hear the beginning of the phrase not where the pattern begins. -In this case, I hear the beginning at the third sample, which can be accounted for with `early`. - - - -Let's add some randomness: - - - -## Tape Warble - -We can emulate a pitch warbling effect like this: - - - -## Sound Duration - -There are a number of ways to change the sound duration. Using clip: - -/2")`} -/> - -The value of clip is relative to the duration of each event. -We can also create overlaps using release: - -/2")`} -/> - -This will smoothly fade out each sound for the given number of seconds. -We could also make the notes shorter with decay / sustain: - -/2").sustain(0)`} -/> - -For now, there is a limitation where decay values that exceed the event duration may cause little cracks, so use higher numbers with caution.. - -When using samples, we also have `.end` to cut relative to the sample length: - -")`} /> - -Compare that to clip: - -")`} /> - -or decay / sustain - -").sustain(0)`} /> - -## Wavetable Synthesis - -You can loop a sample with `loop` / `loopEnd`: - -").s("bd").loop(1).loopEnd(.05).gain(.2)`} /> - -This allows us to play the first 5% of the bass drum as a synth! -To simplify loading wavetables, any sample that starts with `wt_` will be looped automatically: - - - -Running through different wavetables can also give interesting variations: - - - -...adding a filter envelope + reverb: - - 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/repl/idbutils.mjs b/website/src/repl/idbutils.mjs index 18e15d20..7613b767 100644 --- a/website/src/repl/idbutils.mjs +++ b/website/src/repl/idbutils.mjs @@ -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; From 2ed3a5c582c14b570be3217ba0679c5e4dc296e1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 20:36:59 +0100 Subject: [PATCH 05/19] fix: lint --- packages/codemirror/codemirror.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index 91f9fd8f..8a887036 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -218,7 +218,7 @@ export class StrudelMirror { // 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]); + this.drawer.setDrawTime(this.painters.length ? this.drawTime : [0, 0]); } async drawFirstFrame() { if (!this.onDraw) { From 201fe726b614d93b721a3c25db425c9c54e431df Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 20:48:59 +0100 Subject: [PATCH 06/19] replace main repl --- website/src/pages/index.astro | 6 +- website/src/pages/next/index.astro | 14 -- website/src/repl/Repl.jsx | 259 ++++++++++++----------------- website/src/repl/Repl2.jsx | 231 ------------------------- 4 files changed, 113 insertions(+), 397 deletions(-) delete mode 100644 website/src/pages/next/index.astro delete mode 100644 website/src/repl/Repl2.jsx diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 40a1a03f..7db3da94 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -1,11 +1,11 @@ --- -import HeadCommon from '../components/HeadCommon.astro'; -import { Repl } from '../repl/Repl.jsx'; +import HeadCommonNext from '../components/HeadCommonNext.astro'; +import { Repl } from '../repl/Repl'; --- - + Strudel REPL diff --git a/website/src/pages/next/index.astro b/website/src/pages/next/index.astro deleted file mode 100644 index 0db75a1d..00000000 --- a/website/src/pages/next/index.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import HeadCommonNext from '../../components/HeadCommonNext.astro'; -import { Repl2 } from '../../repl/Repl2'; ---- - - - - - Strudel REPL - - - - - diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 65cc3473..4aa677ad 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -1,15 +1,17 @@ /* -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 PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon'; -import { cleanupDraw, cleanupUi, code2hash, getDrawContext, logger } from '@strudel.cycles/core'; -import { CodeMirror, cx, flash, useHighlighting, useKeydown, useStrudel } from '@strudel.cycles/react'; -import { useWidgets } from '@strudel.cycles/react/src/hooks/useWidgets.mjs'; -import { getAudioContext, initAudioOnFirstClick, resetLoadedSounds, webaudioOutput } from '@strudel.cycles/webaudio'; -import { createContext, useCallback, useEffect, useMemo, useState } from 'react'; +import { code2hash, getDrawContext, logger, silence } from '@strudel.cycles/core'; +import { cx } from '@strudel.cycles/react'; +import { transpiler } from '@strudel.cycles/transpiler'; +import { getAudioContext, initAudioOnFirstClick, webaudioOutput } from '@strudel.cycles/webaudio'; +import { StrudelMirror, defaultSettings } from '@strudel/codemirror'; +/* import { writeText } from '@tauri-apps/api/clipboard'; +import { nanoid } from 'nanoid'; */ +import { createContext, useCallback, useEffect, useRef, useState } from 'react'; import { initUserCode, setActivePattern, @@ -22,9 +24,14 @@ import { Header } from './Header'; import Loader from './Loader'; import './Repl.css'; import { Panel } from './panel/Panel'; -import { prebake } from './prebake.mjs'; -import { themes } from './themes.mjs'; +// import { prebake } from '@strudel/repl'; +import { useStore } from '@nanostores/react'; +import { prebake /* , resetSounds */ } from './prebake.mjs'; import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs'; +import './Repl.css'; + +const { code: randomTune, name } = getRandomTune(); +export const ReplContext = createContext(null); const { latestCode } = settingsMap.get(); @@ -39,185 +46,151 @@ if (typeof window !== 'undefined') { clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); } -const getTime = () => getAudioContext().currentTime; - -const { code: randomTune, name } = getRandomTune(); - -export const ReplContext = createContext(null); +// const getTime = () => getAudioContext().currentTime; export function Repl({ embedded = false }) { - const isEmbedded = embedded || window.location !== window.parent.location; - const [view, setView] = useState(); // codemirror view - const [pending, setPending] = useState(true); - const { - theme, - keybindings, - fontSize, - fontFamily, - isLineNumbersDisplayed, - isActiveLineHighlighted, - isAutoCompletionEnabled, - isTooltipEnabled, - isLineWrappingEnabled, - panelPosition, - isZen, - } = useSettings(); + //const isEmbedded = embedded || window.location !== window.parent.location; + const isEmbedded = false; + const { panelPosition, isZen } = useSettings(); + /* const replState = useStore($replstate); + const isDirty = useStore($repldirty); */ + const shouldDraw = true; - 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(({ shouldDraw }) => { + const drawTime = [-2, 2]; + const drawContext = shouldDraw ? getDrawContext() : 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({ 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); + editor.setCode(randomTune); msg = `A random code snippet named "${name}" has been loaded!`; } - //registers samples that have been saved to the index DB 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 } = replState; + const editorRef = useRef(); + const containerRef = useRef(); + const [client, setClient] = useState(false); + useEffect(() => { + setClient(true); + if (!editorRef.current) { + setTimeout(() => { + init({ shouldDraw }); + }); + } + return () => { + editorRef.current?.clear(); + delete editorRef.current; + }; + }, []); - // 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); + if (newCode || isDirty) { + editorRef.current.setCode(newCode); + editorRef.current.repl.evaluate(newCode); + } logger('[repl] code updated!'); }; - 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 () => shareCode(activeCode || code); + const handleShare = async () => shareCode(activeCode); + const pending = false; + //const error = undefined; + // const { started, activeCode } = replState; + const context = { - scheduler, + // scheduler, embedded, started, pending, isDirty, 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 @@ -231,7 +204,7 @@ export function Repl({ embedded = false }) { >
- {isEmbedded && !started && ( + {/* isEmbedded && !started && ( - )} + ) */}
-
- -
+
{panelPosition === 'right' && !isEmbedded && }
{error && ( diff --git a/website/src/repl/Repl2.jsx b/website/src/repl/Repl2.jsx deleted file mode 100644 index fff89e52..00000000 --- a/website/src/repl/Repl2.jsx +++ /dev/null @@ -1,231 +0,0 @@ -/* -App.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 . -*/ - -import { code2hash, getDrawContext, logger, silence } from '@strudel.cycles/core'; -import { cx } from '@strudel.cycles/react'; -import { transpiler } from '@strudel.cycles/transpiler'; -import { getAudioContext, initAudioOnFirstClick, webaudioOutput } from '@strudel.cycles/webaudio'; -import { StrudelMirror, defaultSettings } from '@strudel/codemirror'; -/* import { writeText } from '@tauri-apps/api/clipboard'; -import { nanoid } from 'nanoid'; */ -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 { prebake } from '@strudel/repl'; -import { useStore } from '@nanostores/react'; -import { prebake /* , resetSounds */ } from './prebake.mjs'; -import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs'; -import './Repl.css'; - -const { code: randomTune, name } = getRandomTune(); -export const ReplContext = createContext(null); - -const { latestCode } = settingsMap.get(); - -initAudioOnFirstClick(); - -const modulesLoading = loadModules(); -const presets = prebake(); - -let drawContext, clearCanvas; -if (typeof window !== 'undefined') { - drawContext = getDrawContext(); - clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); -} - -// const getTime = () => getAudioContext().currentTime; - -export function Repl2({ embedded = false }) { - //const isEmbedded = embedded || window.location !== window.parent.location; - const isEmbedded = false; - const { panelPosition, isZen } = useSettings(); - /* const replState = useStore($replstate); - const isDirty = useStore($repldirty); */ - const shouldDraw = true; - - const init = useCallback(({ shouldDraw }) => { - const drawTime = [-2, 2]; - const drawContext = shouldDraw ? getDrawContext() : 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({ - defaultOutput: webaudioOutput, - 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 }) => { - updateUserCode(code); - // setPending(false); - setLatestCode(code); - window.location.hash = '#' + code2hash(code); - }, - bgFill: false, - }); - // init settings - initCode().then((decoded) => { - let msg; - if (decoded) { - editor.setCode(decoded); - initUserCode(decoded); - msg = `I have loaded the code from the URL.`; - } else if (latestCode) { - editor.setCode(latestCode); - msg = `Your last session has been loaded!`; - } /* if(randomTune) */ else { - editor.setCode(randomTune); - msg = `A random code snippet named "${name}" has been loaded!`; - } - logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight'); - // setPending(false); - }); - - editorRef.current = editor; - }, []); - - const [replState, setReplState] = useState({}); - const { started, isDirty, error, activeCode } = replState; - const editorRef = useRef(); - const containerRef = useRef(); - const [client, setClient] = useState(false); - useEffect(() => { - setClient(true); - if (!editorRef.current) { - setTimeout(() => { - init({ shouldDraw }); - }); - } - return () => { - editorRef.current?.clear(); - delete editorRef.current; - }; - }, []); - - // 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 handleTogglePlay = async () => editorRef.current?.toggle(); - const handleUpdate = async (newCode, reset = false) => { - if (reset) { - clearCanvas(); - resetLoadedSounds(); - editorRef.current.repl.setCps(1); - await prebake(); // declare default samples - } - if (newCode || isDirty) { - editorRef.current.setCode(newCode); - editorRef.current.repl.evaluate(newCode); - } - logger('[repl] code updated!'); - }; - const handleShuffle = async () => { - // window.postMessage('strudel-shuffle'); - const { code, name } = getRandomTune(); - logger(`[repl] ✨ loading random tune "${name}"`); - setActivePattern(name); - clearCanvas(); - resetLoadedSounds(); - editorRef.current.repl.setCps(1); - await prebake(); // declare default samples - editorRef.current.setCode(code); - editorRef.current.repl.evaluate(code); - }; - - const handleShare = async () => shareCode(activeCode); - const pending = false; - //const error = undefined; - // const { started, activeCode } = replState; - - const context = { - // scheduler, - embedded, - started, - pending, - isDirty, - activeCode, - handleTogglePlay, - handleUpdate, - handleShuffle, - handleShare, - }; - - return ( - // bg-gradient-to-t from-blue-900 to-slate-900 - // bg-gradient-to-t from-green-900 to-slate-900 - -
- -
- {/* isEmbedded && !started && ( - - ) */} -
-
- {panelPosition === 'right' && !isEmbedded && } -
- {error && ( -
{error.message || 'Unknown Error :-/'}
- )} - {panelPosition === 'bottom' && !isEmbedded && } -
-
- ); -} From 014303b0d56b7d6bfd5692f18cd1a29d6437b8f5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Dec 2023 21:04:29 +0100 Subject: [PATCH 07/19] remove dependencies to @strudel.cycles/react --- pnpm-lock.yaml | 3 - website/package.json | 1 - website/public/dependencygraph.svg | 6 - website/src/components/HeadCommon.astro | 36 +- website/src/components/PitchSlider.jsx | 4 +- website/src/cx.mjs | 10 + website/src/pages/index.astro | 4 +- website/src/repl/Header.jsx | 3 +- website/src/repl/Loader.jsx | 3 +- website/src/repl/Repl.jsx | 2 +- website/src/repl/panel/ConsoleTab.jsx | 3 +- website/src/repl/panel/Forms.jsx | 3 +- website/src/repl/panel/Panel.jsx | 3 +- website/src/repl/panel/SettingsTab.jsx | 3 +- website/src/repl/panel/SoundsTab.jsx | 3 +- website/src/repl/panel/WelcomeTab.jsx | 4 +- website/src/repl/themes.mjs | 482 ------------------------ website/src/useEvent.mjs | 12 + website/src/useFrame.mjs | 43 +++ 19 files changed, 81 insertions(+), 547 deletions(-) create mode 100644 website/src/cx.mjs delete mode 100644 website/src/repl/themes.mjs create mode 100644 website/src/useEvent.mjs create mode 100644 website/src/useFrame.mjs diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5892af73..a9c5f796 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -669,9 +669,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 diff --git a/website/package.json b/website/package.json index b2c5f39a..7557bdc6 100644 --- a/website/package.json +++ b/website/package.json @@ -27,7 +27,6 @@ "@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:*", diff --git a/website/public/dependencygraph.svg b/website/public/dependencygraph.svg index 62fadac7..27f48833 100644 --- a/website/public/dependencygraph.svg +++ b/website/public/dependencygraph.svg @@ -96,12 +96,6 @@ - - @strudel.cycles/serial@0.6.0 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 && } - - 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/Icon.tsx b/packages/react/src/components/Icon.tsx deleted file mode 100644 index 24ec8786..00000000 --- a/packages/react/src/components/Icon.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; - -export function Icon({ type }) { - return ( - - { - { - refresh: ( - - ), - play: ( - - ), - pause: ( - - ), - stop: ( - - ), - }[type] - } - - ); -} 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/Tooltip.jsx b/packages/react/src/components/Tooltip.jsx deleted file mode 100644 index 43a53476..00000000 --- a/packages/react/src/components/Tooltip.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import { hoverTooltip } from '@codemirror/view'; -import jsdoc from '../../../../doc.json'; -import { Autocomplete } from './Autocomplete'; - -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, -); - -window.addEventListener( - 'keydown', - function (e) { - if (e.key == 'Control') { - ctrlDown = true; - } - }, - true, -); - -export const strudelTooltip = hoverTooltip( - (view, pos, side) => { - // Word selection from CodeMirror Hover Tooltip example https://codemirror.net/examples/tooltip/#hover-tooltips - if (!ctrlDown) { - return null; - } - let { from, to, text } = view.state.doc.lineAt(pos); - let start = pos, - end = pos; - while (start > from && /\w/.test(text[start - from - 1])) { - start--; - } - while (end < to && /\w/.test(text[end - from])) { - end++; - } - if ((start == pos && side < 0) || (end == pos && side > 0)) { - return null; - } - let word = text.slice(start - from, end - from); - // Get entry from Strudel documentation - let entry = jsdoc.docs.filter((doc) => getDocLabel(doc) === word)[0]; - if (!entry) { - // Try for synonyms - entry = jsdoc.docs.filter((doc) => doc.synonyms && doc.synonyms.includes(word))[0]; - if (!entry) { - return null; - } - } - - return { - pos: start, - end, - above: false, - arrow: true, - create(view) { - let dom = document.createElement('div'); - dom.className = 'strudel-tooltip'; - createRoot(dom).render(); - return { dom }; - }, - }; - }, - { hoverTime: 10 }, -); 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/cx.js b/packages/react/src/cx.js deleted file mode 100644 index 4e4aea08..00000000 --- a/packages/react/src/cx.js +++ /dev/null @@ -1,10 +0,0 @@ -/* -cx.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 . -*/ - -export default function cx(...classes) { - // : Array - return classes.filter(Boolean).join(' '); -} diff --git a/packages/react/src/hooks/useEvent.mjs b/packages/react/src/hooks/useEvent.mjs deleted file mode 100644 index f349c6e7..00000000 --- a/packages/react/src/hooks/useEvent.mjs +++ /dev/null @@ -1,12 +0,0 @@ -import { useEffect } from 'react'; - -function useEvent(name, onTrigger, useCapture = false) { - useEffect(() => { - document.addEventListener(name, onTrigger, useCapture); - return () => { - document.removeEventListener(name, onTrigger, useCapture); - }; - }, [onTrigger]); -} - -export default useEvent; diff --git a/packages/react/src/hooks/useFrame.mjs b/packages/react/src/hooks/useFrame.mjs deleted file mode 100644 index 130609d0..00000000 --- a/packages/react/src/hooks/useFrame.mjs +++ /dev/null @@ -1,43 +0,0 @@ -import { useEffect, useRef } from 'react'; - -function useFrame(callback, autostart = false) { - const requestRef = useRef(); - const previousTimeRef = useRef(); - - const animate = (time) => { - if (previousTimeRef.current !== undefined) { - const deltaTime = time - previousTimeRef.current; - callback(time, deltaTime); - } - previousTimeRef.current = time; - requestRef.current = requestAnimationFrame(animate); - }; - - const start = () => { - requestRef.current = requestAnimationFrame(animate); - }; - const stop = () => { - requestRef.current && cancelAnimationFrame(requestRef.current); - delete requestRef.current; - }; - useEffect(() => { - if (requestRef.current) { - stop(); - start(); - } - }, [callback]); - - useEffect(() => { - if (autostart) { - start(); - } - return stop; - }, []); // Make sure the effect only runs once - - return { - start, - stop, - }; -} - -export default useFrame; 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/src/themes/algoboy.js b/packages/react/src/themes/algoboy.js deleted file mode 100644 index 399370e1..00000000 --- a/packages/react/src/themes/algoboy.js +++ /dev/null @@ -1,41 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; -export const settings = { - background: '#9bbc0f', - foreground: '#0f380f', // whats that? - caret: '#0f380f', - selection: '#306230', - selectionMatch: '#ffffff26', - lineHighlight: '#8bac0f', - lineBackground: '#9bbc0f50', - //lineBackground: 'transparent', - gutterBackground: 'transparent', - gutterForeground: '#0f380f', - light: true, - customStyle: '.cm-line { line-height: 1 }', -}; -export default createTheme({ - theme: 'light', - settings, - styles: [ - { tag: t.keyword, color: '#0f380f' }, - { tag: t.operator, color: '#0f380f' }, - { tag: t.special(t.variableName), color: '#0f380f' }, - { tag: t.typeName, color: '#0f380f' }, - { tag: t.atom, color: '#0f380f' }, - { tag: t.number, color: '#0f380f' }, - { tag: t.definition(t.variableName), color: '#0f380f' }, - { tag: t.string, color: '#0f380f' }, - { tag: t.special(t.string), color: '#0f380f' }, - { tag: t.comment, color: '#0f380f' }, - { tag: t.variableName, color: '#0f380f' }, - { tag: t.tagName, color: '#0f380f' }, - { tag: t.bracket, color: '#0f380f' }, - { tag: t.meta, color: '#0f380f' }, - { tag: t.attributeName, color: '#0f380f' }, - { tag: t.propertyName, color: '#0f380f' }, - { tag: t.className, color: '#0f380f' }, - { tag: t.invalid, color: '#0f380f' }, - { tag: [t.unit, t.punctuation], color: '#0f380f' }, - ], -}); diff --git a/packages/react/src/themes/blackscreen.js b/packages/react/src/themes/blackscreen.js deleted file mode 100644 index 135285a3..00000000 --- a/packages/react/src/themes/blackscreen.js +++ /dev/null @@ -1,38 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; -export const settings = { - background: 'black', - foreground: 'white', // whats that? - caret: 'white', - selection: '#ffffff20', - selectionMatch: '#036dd626', - lineHighlight: '#ffffff10', - lineBackground: '#00000050', - gutterBackground: 'transparent', - gutterForeground: '#8a919966', -}; -export default createTheme({ - theme: 'dark', - settings, - styles: [ - { tag: t.keyword, color: 'white' }, - { tag: t.operator, color: 'white' }, - { tag: t.special(t.variableName), color: 'white' }, - { tag: t.typeName, color: 'white' }, - { tag: t.atom, color: 'white' }, - { tag: t.number, color: 'white' }, - { tag: t.definition(t.variableName), color: 'white' }, - { tag: t.string, color: 'white' }, - { tag: t.special(t.string), color: 'white' }, - { tag: t.comment, color: 'white' }, - { tag: t.variableName, color: 'white' }, - { tag: t.tagName, color: 'white' }, - { tag: t.bracket, color: 'white' }, - { tag: t.meta, color: 'white' }, - { tag: t.attributeName, color: 'white' }, - { tag: t.propertyName, color: 'white' }, - { tag: t.className, color: 'white' }, - { tag: t.invalid, color: 'white' }, - { tag: [t.unit, t.punctuation], color: 'white' }, - ], -}); diff --git a/packages/react/src/themes/bluescreen.js b/packages/react/src/themes/bluescreen.js deleted file mode 100644 index aa6489d6..00000000 --- a/packages/react/src/themes/bluescreen.js +++ /dev/null @@ -1,41 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; -export const settings = { - background: '#051DB5', - lineBackground: '#051DB550', - foreground: 'white', // whats that? - caret: 'white', - selection: 'rgba(128, 203, 196, 0.5)', - selectionMatch: '#036dd626', - // lineHighlight: '#8a91991a', // original - lineHighlight: '#00000050', - gutterBackground: 'transparent', - // gutterForeground: '#8a919966', - gutterForeground: '#8a919966', -}; - -export default createTheme({ - theme: 'dark', - settings, - styles: [ - { tag: t.keyword, color: 'white' }, - { tag: t.operator, color: 'white' }, - { tag: t.special(t.variableName), color: 'white' }, - { tag: t.typeName, color: 'white' }, - { tag: t.atom, color: 'white' }, - { tag: t.number, color: 'white' }, - { tag: t.definition(t.variableName), color: 'white' }, - { tag: t.string, color: 'white' }, - { tag: t.special(t.string), color: 'white' }, - { tag: t.comment, color: 'white' }, - { tag: t.variableName, color: 'white' }, - { tag: t.tagName, color: 'white' }, - { tag: t.bracket, color: 'white' }, - { tag: t.meta, color: 'white' }, - { tag: t.attributeName, color: 'white' }, - { tag: t.propertyName, color: 'white' }, - { tag: t.className, color: 'white' }, - { tag: t.invalid, color: 'white' }, - { tag: [t.unit, t.punctuation], color: 'white' }, - ], -}); diff --git a/packages/react/src/themes/strudel-theme.js b/packages/react/src/themes/strudel-theme.js deleted file mode 100644 index 4ae31060..00000000 --- a/packages/react/src/themes/strudel-theme.js +++ /dev/null @@ -1,45 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; -export default createTheme({ - theme: 'dark', - settings: { - background: '#222', - foreground: '#75baff', // whats that? - caret: '#ffcc00', - selection: 'rgba(128, 203, 196, 0.5)', - selectionMatch: '#036dd626', - // lineHighlight: '#8a91991a', // original - lineHighlight: '#00000050', - gutterBackground: 'transparent', - // gutterForeground: '#8a919966', - gutterForeground: '#8a919966', - }, - styles: [ - { tag: t.keyword, color: '#c792ea' }, - { tag: t.operator, color: '#89ddff' }, - { tag: t.special(t.variableName), color: '#eeffff' }, - // { tag: t.typeName, color: '#f07178' }, // original - { tag: t.typeName, color: '#c3e88d' }, - { tag: t.atom, color: '#f78c6c' }, - // { tag: t.number, color: '#ff5370' }, // original - { tag: t.number, color: '#c3e88d' }, - { tag: t.definition(t.variableName), color: '#82aaff' }, - { tag: t.string, color: '#c3e88d' }, - // { tag: t.special(t.string), color: '#f07178' }, // original - { tag: t.special(t.string), color: '#c3e88d' }, - { tag: t.comment, color: '#7d8799' }, - // { tag: t.variableName, color: '#f07178' }, // original - { tag: t.variableName, color: '#c792ea' }, - // { tag: t.tagName, color: '#ff5370' }, // original - { tag: t.tagName, color: '#c3e88d' }, - { tag: t.bracket, color: '#525154' }, - // { tag: t.bracket, color: '#a2a1a4' }, // original - { tag: t.meta, color: '#ffcb6b' }, - { tag: t.attributeName, color: '#c792ea' }, - { tag: t.propertyName, color: '#c792ea' }, - - { tag: t.className, color: '#decb6b' }, - { tag: t.invalid, color: '#ffffff' }, - { tag: [t.unit, t.punctuation], color: '#82aaff' }, - ], -}); diff --git a/packages/react/src/themes/teletext.js b/packages/react/src/themes/teletext.js deleted file mode 100644 index 5fd9a557..00000000 --- a/packages/react/src/themes/teletext.js +++ /dev/null @@ -1,50 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; - -let colorA = '#6edee4'; -//let colorB = 'magenta'; -let colorB = 'white'; -let colorC = 'red'; -let colorD = '#f8fc55'; - -export const settings = { - background: '#000000', - foreground: colorA, // whats that? - caret: colorC, - selection: colorD, - selectionMatch: colorA, - lineHighlight: '#6edee440', // panel bg - lineBackground: '#00000040', - gutterBackground: 'transparent', - gutterForeground: '#8a919966', - customStyle: '.cm-line { line-height: 1 }', -}; - -let punctuation = colorD; -let mini = colorB; - -export default createTheme({ - theme: 'dark', - settings, - styles: [ - { tag: t.keyword, color: colorA }, - { tag: t.operator, color: mini }, - { tag: t.special(t.variableName), color: colorA }, - { tag: t.typeName, color: colorA }, - { tag: t.atom, color: colorA }, - { tag: t.number, color: mini }, - { tag: t.definition(t.variableName), color: colorA }, - { tag: t.string, color: mini }, - { tag: t.special(t.string), color: mini }, - { tag: t.comment, color: punctuation }, - { tag: t.variableName, color: colorA }, - { tag: t.tagName, color: colorA }, - { tag: t.bracket, color: punctuation }, - { tag: t.meta, color: colorA }, - { tag: t.attributeName, color: colorA }, - { tag: t.propertyName, color: colorA }, // methods - { tag: t.className, color: colorA }, - { tag: t.invalid, color: colorC }, - { tag: [t.unit, t.punctuation], color: punctuation }, - ], -}); diff --git a/packages/react/src/themes/terminal.js b/packages/react/src/themes/terminal.js deleted file mode 100644 index 1374bb86..00000000 --- a/packages/react/src/themes/terminal.js +++ /dev/null @@ -1,36 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; -export const settings = { - background: 'black', - foreground: '#41FF00', // whats that? - caret: '#41FF00', - selection: '#ffffff20', - selectionMatch: '#036dd626', - lineHighlight: '#ffffff10', - gutterBackground: 'transparent', - gutterForeground: '#8a919966', -}; -export default createTheme({ - theme: 'dark', - settings, - styles: [ - { tag: t.keyword, color: '#41FF00' }, - { tag: t.operator, color: '#41FF00' }, - { tag: t.special(t.variableName), color: '#41FF00' }, - { tag: t.typeName, color: '#41FF00' }, - { tag: t.atom, color: '#41FF00' }, - { tag: t.number, color: '#41FF00' }, - { tag: t.definition(t.variableName), color: '#41FF00' }, - { tag: t.string, color: '#41FF00' }, - { tag: t.special(t.string), color: '#41FF00' }, - { tag: t.comment, color: '#41FF00' }, - { tag: t.variableName, color: '#41FF00' }, - { tag: t.tagName, color: '#41FF00' }, - { tag: t.bracket, color: '#41FF00' }, - { tag: t.meta, color: '#41FF00' }, - { tag: t.attributeName, color: '#41FF00' }, - { tag: t.propertyName, color: '#41FF00' }, - { tag: t.className, color: '#41FF00' }, - { tag: t.invalid, color: '#41FF00' }, - ], -}); diff --git a/packages/react/src/themes/whitescreen.js b/packages/react/src/themes/whitescreen.js deleted file mode 100644 index 22abad9e..00000000 --- a/packages/react/src/themes/whitescreen.js +++ /dev/null @@ -1,38 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from '@uiw/codemirror-themes'; -export const settings = { - background: 'white', - foreground: 'black', // whats that? - caret: 'black', - selection: 'rgba(128, 203, 196, 0.5)', - selectionMatch: '#ffffff26', - lineHighlight: '#cccccc50', - lineBackground: '#ffffff50', - gutterBackground: 'transparent', - gutterForeground: 'black', - light: true, -}; -export default createTheme({ - theme: 'light', - settings, - styles: [ - { tag: t.keyword, color: 'black' }, - { tag: t.operator, color: 'black' }, - { tag: t.special(t.variableName), color: 'black' }, - { tag: t.typeName, color: 'black' }, - { tag: t.atom, color: 'black' }, - { tag: t.number, color: 'black' }, - { tag: t.definition(t.variableName), color: 'black' }, - { tag: t.string, color: 'black' }, - { tag: t.special(t.string), color: 'black' }, - { tag: t.comment, color: 'black' }, - { tag: t.variableName, color: 'black' }, - { tag: t.tagName, color: 'black' }, - { tag: t.bracket, color: 'black' }, - { tag: t.meta, color: 'black' }, - { tag: t.attributeName, color: 'black' }, - { tag: t.propertyName, color: 'black' }, - { tag: t.className, color: 'black' }, - { tag: t.invalid, color: 'black' }, - ], -}); 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/pnpm-lock.yaml b/pnpm-lock.yaml index a9c5f796..f26dd289 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,149 +282,6 @@ importers: specifier: ^4.3.3 version: 4.3.3 - packages/react: - 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) - '@strudel.cycles/core': - specifier: workspace:* - version: link:../core - '@strudel.cycles/transpiler': - specifier: workspace:* - version: link:../transpiler - '@strudel.cycles/webaudio': - specifier: workspace:* - version: link:../webaudio - '@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) - 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 - packages/repl: dependencies: '@rollup/plugin-replace': @@ -930,6 +787,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==} @@ -1074,38 +932,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'} @@ -1137,16 +967,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'} @@ -1170,20 +990,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'} @@ -1240,11 +1046,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'} @@ -1256,14 +1057,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'} @@ -1271,13 +1064,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'} @@ -1291,35 +1077,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'} @@ -1340,11 +1103,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'} @@ -1378,13 +1136,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'} @@ -1398,13 +1149,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'} @@ -1432,11 +1176,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'} @@ -1453,17 +1192,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'} @@ -1513,6 +1241,7 @@ packages: hasBin: true dependencies: '@babel/types': 7.21.5 + dev: false /@babel/parser@7.23.0: resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} @@ -2150,16 +1879,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'} @@ -2170,16 +1889,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'} @@ -2401,15 +2110,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'} @@ -2418,24 +2118,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'} @@ -2553,15 +2235,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: @@ -4685,11 +4358,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==} @@ -4697,6 +4372,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==} @@ -4706,6 +4382,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==} @@ -4761,26 +4438,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: @@ -5056,33 +4713,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==} @@ -5096,21 +4726,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} @@ -5373,6 +4988,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==} @@ -5413,6 +5029,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==} @@ -5624,22 +5241,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} @@ -5792,17 +5393,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} @@ -5945,6 +5535,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==} @@ -5964,10 +5555,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==} @@ -6214,20 +5801,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'} @@ -6338,6 +5911,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==} @@ -6478,10 +6052,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==} @@ -6541,6 +6111,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==} @@ -6826,6 +6397,7 @@ packages: /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} @@ -6920,10 +6492,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==} @@ -7789,6 +7357,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==} @@ -8080,6 +7649,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==} @@ -9107,6 +8677,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==} @@ -9452,10 +9023,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==} @@ -9591,6 +9164,7 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 + dev: false /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} @@ -10617,6 +10191,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==} @@ -10761,10 +10336,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'} @@ -10848,6 +10419,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==} @@ -11123,6 +10695,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==} @@ -11609,6 +11182,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==} @@ -11679,6 +11253,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==} @@ -11688,6 +11263,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==} @@ -11704,6 +11280,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==} @@ -11730,6 +11307,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==} @@ -11748,6 +11326,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==} @@ -12049,6 +11628,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==} @@ -12067,17 +11647,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==} @@ -12695,6 +12278,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==} @@ -13267,6 +12851,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==} @@ -13325,6 +12910,7 @@ packages: sucrase: 3.32.0 transitivePeerDependencies: - ts-node + dev: false /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -13477,11 +13063,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==} @@ -13570,6 +13158,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==} @@ -13965,17 +13554,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 @@ -14815,6 +14393,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==} From b0bdd090322d3f4b845a1b9db697d8efabb39319 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 29 Dec 2023 00:30:46 +0100 Subject: [PATCH 09/19] fix: dumb react ssr workaround --- website/src/docs/MiniRepl.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 3a16f3dd..aab37fd2 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,4 +1,4 @@ -import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; +import { useState, useRef, useCallback, useMemo, useEffect, useLayoutEffect } from 'react'; import { Icon } from './Icon'; import { silence, getPunchcardPainter, noteToMidi } from '@strudel.cycles/core'; import { transpiler } from '@strudel.cycles/transpiler'; @@ -9,6 +9,9 @@ import { prebake } from '../repl/prebake.mjs'; import { loadModules } from '../repl/util.mjs'; import Claviature from '@components/Claviature'; +// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85 +export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; + let prebaked, modulesLoading; if (typeof window !== 'undefined') { prebaked = prebake(); @@ -89,7 +92,8 @@ export function MiniRepl({ const editorRef = useRef(); const containerRef = useRef(); const [client, setClient] = useState(false); - useEffect(() => { + + useIsomorphicLayoutEffect(() => { setClient(true); if (!editorRef.current) { setTimeout(() => { From 9974311344dd22ab6a5aeae942dba1695cea0d4f Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Fri, 29 Dec 2023 00:55:14 +0100 Subject: [PATCH 10/19] better repl init + a bit of ssr for main repl --- website/src/docs/MiniRepl.jsx | 29 +++++++++++------------------ website/src/pages/index.astro | 2 +- website/src/repl/Header.jsx | 2 +- website/src/repl/Repl.jsx | 32 ++++++++++---------------------- website/src/repl/panel/Panel.jsx | 16 ++++++++++++---- website/src/useClient.mjs | 9 +++++++++ 6 files changed, 44 insertions(+), 46 deletions(-) create mode 100644 website/src/useClient.mjs diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index aab37fd2..5e915641 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,4 +1,4 @@ -import { useState, useRef, useCallback, useMemo, useEffect, useLayoutEffect } from 'react'; +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'; @@ -8,9 +8,7 @@ import { StrudelMirror } from '@strudel/codemirror'; import { prebake } from '../repl/prebake.mjs'; import { loadModules } from '../repl/util.mjs'; import Claviature from '@components/Claviature'; - -// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85 -export const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; +import useClient from '@src/useClient.mjs'; let prebaked, modulesLoading; if (typeof window !== 'undefined') { @@ -91,19 +89,7 @@ export function MiniRepl({ const { started, isDirty, error } = replState; const editorRef = useRef(); const containerRef = useRef(); - const [client, setClient] = useState(false); - - useIsomorphicLayoutEffect(() => { - setClient(true); - if (!editorRef.current) { - setTimeout(() => { - init({ code, shouldDraw }); - }); - } - return () => { - editorRef.current?.clear(); - }; - }, []); + const client = useClient(); if (!client) { return
{code}
; @@ -136,7 +122,14 @@ export function MiniRepl({
)}
-
+
{ + if (!editorRef.current) { + containerRef.current = el; + init({ code, shouldDraw }); + } + }} + >
{error &&
{error.message}
}
{shouldShowCanvas && ( diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 6630ef32..53a522e8 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -9,6 +9,6 @@ import { Repl } from '../repl/Repl'; Strudel REPL - + diff --git a/website/src/repl/Header.jsx b/website/src/repl/Header.jsx index 9af54b12..774d0142 100644 --- a/website/src/repl/Header.jsx +++ b/website/src/repl/Header.jsx @@ -23,7 +23,7 @@ export function Header({ context }) { 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 ( diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 4a350d55..0ab201a1 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -35,19 +35,15 @@ export const ReplContext = createContext(null); const { latestCode } = settingsMap.get(); -initAudioOnFirstClick(); - -const modulesLoading = loadModules(); -const presets = prebake(); - -let drawContext, clearCanvas; +let modulesLoading, presets, drawContext, clearCanvas; if (typeof window !== 'undefined') { + initAudioOnFirstClick(); + modulesLoading = loadModules(); + presets = prebake(); drawContext = getDrawContext(); clearCanvas = () => drawContext.clearRect(0, 0, drawContext.canvas.height, drawContext.canvas.width); } -// const getTime = () => getAudioContext().currentTime; - export function Repl({ embedded = false }) { //const isEmbedded = embedded || window.location !== window.parent.location; const isEmbedded = false; @@ -116,19 +112,6 @@ export function Repl({ embedded = false }) { const { started, isDirty, error, activeCode } = replState; const editorRef = useRef(); const containerRef = useRef(); - const [client, setClient] = useState(false); - useEffect(() => { - setClient(true); - if (!editorRef.current) { - setTimeout(() => { - init({ shouldDraw }); - }); - } - return () => { - editorRef.current?.clear(); - delete editorRef.current; - }; - }, []); // 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 @@ -217,7 +200,12 @@ export function Repl({ embedded = false }) {
{ + containerRef.current = el; + if (!editorRef.current) { + init({ shouldDraw }); + } + }} >
{panelPosition === 'right' && !isEmbedded && }
diff --git a/website/src/repl/panel/Panel.jsx b/website/src/repl/panel/Panel.jsx index c2dbd443..8d824d26 100644 --- a/website/src/repl/panel/Panel.jsx +++ b/website/src/repl/panel/Panel.jsx @@ -3,7 +3,7 @@ import { logger } from '@strudel.cycles/core'; 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'; @@ -12,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; @@ -80,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 (