mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 21:23:21 -04:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14c88b5017 | |||
| ab1d8b065a | |||
| dd953704bf | |||
| d8d349af48 | |||
| f3e0e5e980 | |||
| 23f186ba97 | |||
| 4ca890867d | |||
| 21a64c02e9 | |||
| 0ce1530692 | |||
| 012d5146cb | |||
| 12115e7068 | |||
| cd35a17904 | |||
| be3f68ed06 | |||
| 64b8fd9584 | |||
| b001d1d0f1 | |||
| 64ad75c79e | |||
| 12eb361d69 | |||
| dd51bd0a2f | |||
| 0fb478a7c8 | |||
| 9694ecf55b | |||
| fe80478b8a | |||
| a253c26bee | |||
| a500f983f5 | |||
| 3579b6f8f3 | |||
| 14cb954213 | |||
| 6170eef441 | |||
| ddb9af4d57 | |||
| 6db2c0bf93 | |||
| 46f70e6a5e | |||
| 0b994a1b0f | |||
| 0326244956 | |||
| ea1b2252fc | |||
| 0da25ea7cc | |||
| 3209afa4ff | |||
| 33e2631e5d | |||
| f02a41b675 | |||
| f497d7b212 | |||
| 0f18f5eb81 | |||
| 8da81104aa | |||
| 97cbdc6b0f | |||
| 7f53f4e36b | |||
| c350f262e6 | |||
| 3e727893bf | |||
| 2a2c7437e4 | |||
| 4f0885d3af | |||
| 5656cd2fc6 | |||
| b6e42876ad | |||
| 3671344867 | |||
| 30b149053c | |||
| cdd40df9d3 | |||
| 9d661808ba | |||
| 6a5d849b3c |
+3
-1
@@ -15,4 +15,6 @@ vite.config.js
|
||||
!**/*.mjs
|
||||
**/*.tsx
|
||||
**/*.ts
|
||||
**/*.json
|
||||
**/*.json
|
||||
**/dev-dist
|
||||
**/dist
|
||||
+2
-1
@@ -8,4 +8,5 @@ packages/mini/krill-parser.js
|
||||
packages/xen/tunejs.js
|
||||
paper
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
pnpm-workspace.yaml
|
||||
**/dev-dist
|
||||
@@ -15,3 +15,4 @@ export * from './packages/transpiler/index.mjs';
|
||||
export * from './packages/webaudio/index.mjs';
|
||||
export * from './packages/webdirt/index.mjs';
|
||||
export * from './packages/xen/index.mjs';
|
||||
export * from './packages/wam/index.mjs';
|
||||
|
||||
@@ -20,11 +20,8 @@ Example: <https://felixroos.github.io/strudel/swatch/>
|
||||
### 6. edit `website/astro.config.mjs` to use site: `https://<your-username>.github.io` and base `/strudel`, like this
|
||||
|
||||
```js
|
||||
export default defineConfig({
|
||||
/* ... rest of config ... */
|
||||
site: 'https://<your-username>.github.io',
|
||||
base: '/strudel',
|
||||
});
|
||||
const site = 'https://<your-username>.github.io';
|
||||
const base = '/strudel';
|
||||
```
|
||||
|
||||
### 7. commit & push the changes
|
||||
|
||||
+33
-18
@@ -11,15 +11,6 @@ const generic_params = [
|
||||
/**
|
||||
* Select a sound / sample by name.
|
||||
*
|
||||
* <details style={{display:'none'}}>
|
||||
* <summary>show all sounds</summary>
|
||||
*
|
||||
* 808 (6) 808bd (25) 808cy (25) 808hc (5) 808ht (5) 808lc (5) 808lt (5) 808mc (5) 808mt (5) 808oh (5) 808sd (25) 909 (1) ab (12) ade (10) ades2 (9) ades3 (7) ades4 (6) alex (2) alphabet (26) amencutup (32) armora (7) arp (2) arpy (11) auto (11) baa (7) baa2 (7) bass (4) bass0 (3) bass1 (30) bass2 (5) bass3 (11) bassdm (24) bassfoo (3) battles (2) bd (24) bend (4) bev (2) bin (2) birds (10) birds3 (19) bleep (13) blip (2) blue (2) bottle (13) breaks125 (2) breaks152 (1) breaks157 (1) breaks165 (1) breath (1) bubble (8) can (14) casio (3) cb (1) cc (6) chin (4) circus (3) clak (2) click (4) clubkick (5) co (4) coins (1) control (2) cosmicg (15) cp (2) cr (6) crow (4) d (4) db (13) diphone (38) diphone2 (12) dist (16) dork2 (4) dorkbot (2) dr (42) dr2 (6) dr55 (4) dr_few (8) drum (6) drumtraks (13) e (8) east (9) electro1 (13) em2 (6) erk (1) f (1) feel (7) feelfx (8) fest (1) fire (1) flick (17) fm (17) foo (27) future (17) gab (10) gabba (4) gabbaloud (4) gabbalouder (4) glasstap (3) glitch (8) glitch2 (8) gretsch (24) gtr (3) h (7) hand (17) hardcore (12) hardkick (6) haw (6) hc (6) hh (13) hh27 (13) hit (6) hmm (1) ho (6) hoover (6) house (8) ht (16) if (5) ifdrums (3) incoming (8) industrial (32) insect (3) invaders (18) jazz (8) jungbass (20) jungle (13) juno (12) jvbass (13) kicklinn (1) koy (2) kurt (7) latibro (8) led (1) less (4) lighter (33) linnhats (6) lt (16) made (7) made2 (1) mash (2) mash2 (4) metal (10) miniyeah (4) monsterb (6) moog (7) mouth (15) mp3 (4) msg (9) mt (16) mute (28) newnotes (15) noise (1) noise2 (8) notes (15) numbers (9) oc (4) odx (15) off (1) outdoor (6) pad (3) padlong (1) pebbles (1) perc (6) peri (15) pluck (17) popkick (10) print (11) proc (2) procshort (8) psr (30) rave (8) rave2 (4) ravemono (2) realclaps (4) reverbkick (1) rm (2) rs (1) sax (22) sd (2) seawolf (3) sequential (8) sf (18) sheffield (1) short (5) sid (12) sine (6) sitar (8) sn (52) space (18) speakspell (12) speech (7) speechless (10) speedupdown (9) stab (23) stomp (10) subroc3d (11) sugar (2) sundance (6) tabla (26) tabla2 (46) tablex (3) tacscan (22) tech (13) techno (7) tink (5) tok (4) toys (13) trump (11) ul (10) ulgab (5) uxay (3) v (6) voodoo (5) wind (10) wobble (1) world (3) xmas (1) yeah (31)
|
||||
*
|
||||
* <a href="https://tidalcycles.org/docs/configuration/Audio%20Samples/default_library" target="_blank">more info</a>
|
||||
*
|
||||
* </details>
|
||||
*
|
||||
* @name s
|
||||
* @param {string | Pattern} sound The sound / pattern of sounds to pick
|
||||
* @example
|
||||
@@ -28,20 +19,36 @@ const generic_params = [
|
||||
*/
|
||||
['s', 's', 'sound'],
|
||||
/**
|
||||
* The note or sample number to choose for a synth or sampleset
|
||||
* Note names currently not working yet, but will hopefully soon. Just stick to numbers for now
|
||||
* Selects the given index from the sample map.
|
||||
* Numbers too high will wrap around.
|
||||
* `n` can also be used to play midi numbers, but it is recommended to use `note` instead.
|
||||
*
|
||||
* @name n
|
||||
* @param {string | number | Pattern} value note name, note number or sample number
|
||||
* @param {number | Pattern} value sample index starting from 0
|
||||
* @example
|
||||
* s('superpiano').n("<0 1 2 3>").osc()
|
||||
* @example
|
||||
* s('superpiano').n("<c4 d4 e4 g4>").osc()
|
||||
* @example
|
||||
* n("0 1 2 3").s('east').osc()
|
||||
* s("bd sd,hh*3").n("<0 1>")
|
||||
*/
|
||||
// also see https://github.com/tidalcycles/strudel/pull/63
|
||||
['f', 'n', 'The note or sample number to choose for a synth or sampleset'],
|
||||
['f', 'n', 'The sample number to choose for a synth or sampleset'],
|
||||
/**
|
||||
* Plays the given note name or midi number. A note name consists of
|
||||
*
|
||||
* - a letter (a-g or A-G)
|
||||
* - optional accidentals (b or #)
|
||||
* - optional octave number (0-9). Defaults to 3
|
||||
*
|
||||
* Examples of valid note names: `c`, `bb`, `Bb`, `f#`, `c3`, `A4`, `Eb2`, `c#5`
|
||||
*
|
||||
* You can also use midi numbers instead of note names, where 69 is mapped to A4 440Hz in 12EDO.
|
||||
*
|
||||
* @name note
|
||||
* @example
|
||||
* note("c a f e")
|
||||
* @example
|
||||
* note("c4 a4 f4 e4")
|
||||
* @example
|
||||
* note("60 69 65 64")
|
||||
*/
|
||||
['f', 'note', 'The note or pitch to play a sound or synth with'],
|
||||
//['s', 'toArg', 'for internal sound routing'],
|
||||
// ["f", "from", "for internal sound routing"),
|
||||
@@ -386,6 +393,7 @@ const generic_params = [
|
||||
*
|
||||
* @name detune
|
||||
* @param {number | Pattern} amount between 0 and 1
|
||||
* @superdirtOnly
|
||||
* @example
|
||||
* n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc()
|
||||
*
|
||||
@@ -398,6 +406,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} dry 0 = wet, 1 = dry
|
||||
* @example
|
||||
* n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc()
|
||||
* @superdirtOnly
|
||||
*
|
||||
*/
|
||||
[
|
||||
@@ -456,6 +465,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} wet between 0 and 1
|
||||
* @example
|
||||
* n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc()
|
||||
* @superdirtOnly
|
||||
*
|
||||
*/
|
||||
['f', 'leslie', ''],
|
||||
@@ -466,6 +476,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} rate 6.7 for fast, 0.7 for slow
|
||||
* @example
|
||||
* n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc()
|
||||
* @superdirtOnly
|
||||
*
|
||||
*/
|
||||
// TODO: the rate seems to "lag" (in the example, 1 will be fast)
|
||||
@@ -477,6 +488,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} meters somewhere between 0 and 1
|
||||
* @example
|
||||
* n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc()
|
||||
* @superdirtOnly
|
||||
*
|
||||
*/
|
||||
['f', 'lsize', ''],
|
||||
@@ -512,6 +524,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} octave octave number
|
||||
* @example
|
||||
* n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc()
|
||||
* @superDirtOnly
|
||||
*/
|
||||
['i', 'octave', ''],
|
||||
['f', 'offset', ''], // TODO: what is this? not found in tidal doc
|
||||
@@ -676,6 +689,7 @@ const generic_params = [
|
||||
* @param {number | string | Pattern} unit see description above
|
||||
* @example
|
||||
* speed("1 2 .5 3").s("bd").unit("c").osc()
|
||||
* @superdirtOnly
|
||||
*
|
||||
*/
|
||||
[
|
||||
@@ -692,6 +706,7 @@ const generic_params = [
|
||||
* @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc.
|
||||
* @example
|
||||
* squiz("2 4/2 6 [8 16]").s("bd").osc()
|
||||
* @superdirtOnly
|
||||
*
|
||||
*/
|
||||
['f', 'squiz', ''],
|
||||
|
||||
@@ -27,6 +27,7 @@ export class Pattern {
|
||||
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
|
||||
*
|
||||
* @param {function} query - The function that maps a {@link State} to an array of {@link Hap}.
|
||||
* @noAutocomplete
|
||||
*/
|
||||
constructor(query) {
|
||||
this.query = query;
|
||||
@@ -51,6 +52,7 @@ export class Pattern {
|
||||
|
||||
/**
|
||||
* see {@link Pattern#withValue}
|
||||
* @noAutocomplete
|
||||
*/
|
||||
fmap(func) {
|
||||
return this.withValue(func);
|
||||
@@ -62,6 +64,7 @@ export class Pattern {
|
||||
* pattern of functions.
|
||||
* @param {Function} whole_func
|
||||
* @param {Function} func
|
||||
* @noAutocomplete
|
||||
* @returns Pattern
|
||||
*/
|
||||
appWhole(whole_func, pat_val) {
|
||||
@@ -97,6 +100,7 @@ export class Pattern {
|
||||
* are not the same but do intersect, the resulting hap has a timespan of the
|
||||
* intersection. This applies to both the part and the whole timespan.
|
||||
* @param {Pattern} pat_val
|
||||
* @noAutocomplete
|
||||
* @returns Pattern
|
||||
*/
|
||||
appBoth(pat_val) {
|
||||
@@ -117,6 +121,7 @@ export class Pattern {
|
||||
* are preserved from the pattern of functions (often referred to as the left
|
||||
* hand or inner pattern).
|
||||
* @param {Pattern} pat_val
|
||||
* @noAutocomplete
|
||||
* @returns Pattern
|
||||
*/
|
||||
appLeft(pat_val) {
|
||||
@@ -147,6 +152,7 @@ export class Pattern {
|
||||
* pattern of values, i.e. structure is preserved from the right hand/outer
|
||||
* pattern.
|
||||
* @param {Pattern} pat_val
|
||||
* @noAutocomplete
|
||||
* @returns Pattern
|
||||
*/
|
||||
appRight(pat_val) {
|
||||
@@ -332,6 +338,7 @@ export class Pattern {
|
||||
* const haps = pattern.queryArc(0, 1)
|
||||
* console.log(haps)
|
||||
* silence
|
||||
* @noAutocomplete
|
||||
*/
|
||||
queryArc(begin, end) {
|
||||
try {
|
||||
@@ -347,6 +354,7 @@ export class Pattern {
|
||||
* some calculations easier to express, as all haps are then constrained to
|
||||
* happen within a cycle.
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
splitQueries() {
|
||||
const pat = this;
|
||||
@@ -361,6 +369,7 @@ export class Pattern {
|
||||
* timespan before passing it to the original pattern.
|
||||
* @param {Function} func the function to apply
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withQuerySpan(func) {
|
||||
return new Pattern((state) => this.query(state.withSpan(func)));
|
||||
@@ -382,6 +391,7 @@ export class Pattern {
|
||||
* begin and end time of the query timespan.
|
||||
* @param {Function} func the function to apply
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withQueryTime(func) {
|
||||
return new Pattern((state) => this.query(state.withSpan((span) => span.withTime(func))));
|
||||
@@ -393,6 +403,7 @@ export class Pattern {
|
||||
* present, `whole` timespans).
|
||||
* @param {Function} func
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withHapSpan(func) {
|
||||
return new Pattern((state) => this.query(state).map((hap) => hap.withSpan(func)));
|
||||
@@ -403,6 +414,7 @@ export class Pattern {
|
||||
* begin and end time of the hap timespans.
|
||||
* @param {Function} func the function to apply
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withHapTime(func) {
|
||||
return this.withHapSpan((span) => span.withTime(func));
|
||||
@@ -412,6 +424,7 @@ export class Pattern {
|
||||
* Returns a new pattern with the given function applied to the list of haps returned by every query.
|
||||
* @param {Function} func
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withHaps(func) {
|
||||
return new Pattern((state) => func(this.query(state)));
|
||||
@@ -421,6 +434,7 @@ export class Pattern {
|
||||
* As with {@link Pattern#withHaps}, but applies the function to every hap, rather than every list of haps.
|
||||
* @param {Function} func
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withHap(func) {
|
||||
return this.withHaps((haps) => haps.map(func));
|
||||
@@ -430,6 +444,7 @@ export class Pattern {
|
||||
* Returns a new pattern with the context field set to every hap set to the given value.
|
||||
* @param {*} context
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
setContext(context) {
|
||||
return this.withHap((hap) => hap.setContext(context));
|
||||
@@ -439,6 +454,7 @@ export class Pattern {
|
||||
* Returns a new pattern with the given function applied to the context field of every hap.
|
||||
* @param {Function} func
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withContext(func) {
|
||||
return this.withHap((hap) => hap.setContext(func(hap.context)));
|
||||
@@ -447,6 +463,7 @@ export class Pattern {
|
||||
/**
|
||||
* Returns a new pattern with the context field of every hap set to an empty object.
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
stripContext() {
|
||||
return this.withHap((hap) => hap.setContext({}));
|
||||
@@ -458,6 +475,7 @@ export class Pattern {
|
||||
* @param {Number} start
|
||||
* @param {Number} end
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
withLocation(start, end) {
|
||||
const location = {
|
||||
@@ -500,6 +518,7 @@ export class Pattern {
|
||||
* Returns a new Pattern, which only returns haps that meet the given test.
|
||||
* @param {Function} hap_test - a function which returns false for haps to be removed from the pattern
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
filterHaps(hap_test) {
|
||||
return new Pattern((state) => this.query(state).filter(hap_test));
|
||||
@@ -510,6 +529,7 @@ export class Pattern {
|
||||
* inside haps.
|
||||
* @param {Function} value_test
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
filterValues(value_test) {
|
||||
return new Pattern((state) => this.query(state).filter((hap) => value_test(hap.value)));
|
||||
@@ -519,6 +539,7 @@ export class Pattern {
|
||||
* Returns a new pattern, with haps containing undefined values removed from
|
||||
* query results.
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
removeUndefineds() {
|
||||
return this.filterValues((val) => val != undefined);
|
||||
@@ -529,6 +550,7 @@ export class Pattern {
|
||||
* with an onset is one with a `whole` timespan that begins at the same time
|
||||
* as its `part` timespan.
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
onsetsOnly() {
|
||||
// Returns a new pattern that will only return haps where the start
|
||||
@@ -541,6 +563,7 @@ export class Pattern {
|
||||
* Returns a new pattern, with 'continuous' haps (those without 'whole'
|
||||
* timespans) removed from query results.
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
discreteOnly() {
|
||||
// removes continuous haps that don't have a 'whole' timespan
|
||||
@@ -550,6 +573,7 @@ export class Pattern {
|
||||
/**
|
||||
* Combines adjacent haps with the same value and whole. Only
|
||||
* intended for use in tests.
|
||||
* @noAutocomplete
|
||||
*/
|
||||
defragmentHaps() {
|
||||
// remove continuous haps
|
||||
@@ -604,6 +628,7 @@ export class Pattern {
|
||||
* @param {Boolean} with_context - set to true, otherwise the context field
|
||||
* will be stripped from the resulting haps.
|
||||
* @returns [Hap]
|
||||
* @noAutocomplete
|
||||
*/
|
||||
firstCycle(with_context = false) {
|
||||
var self = this;
|
||||
@@ -615,6 +640,7 @@ export class Pattern {
|
||||
|
||||
/**
|
||||
* Accessor for a list of values returned by querying the first cycle.
|
||||
* @noAutocomplete
|
||||
*/
|
||||
get firstCycleValues() {
|
||||
return this.firstCycle().map((hap) => hap.value);
|
||||
@@ -622,6 +648,7 @@ export class Pattern {
|
||||
|
||||
/**
|
||||
* More human-readable version of the {@link Pattern#firstCycleValues} accessor.
|
||||
* @noAutocomplete
|
||||
*/
|
||||
get showFirstCycle() {
|
||||
return this.firstCycle().map(
|
||||
@@ -633,6 +660,7 @@ export class Pattern {
|
||||
* Returns a new pattern, which returns haps sorted in temporal order. Mainly
|
||||
* of use when comparing two patterns for equality, in tests.
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
sortHapsByPart() {
|
||||
return this.withHaps((haps) =>
|
||||
@@ -792,6 +820,22 @@ export class Pattern {
|
||||
);
|
||||
}
|
||||
|
||||
addTrigger(onTrigger) {
|
||||
return this.withHap((hap) =>
|
||||
hap.setContext({
|
||||
...hap.context,
|
||||
onTrigger: (...args) => {
|
||||
if (hap.context.onTrigger) {
|
||||
hap.context.onTrigger(...args);
|
||||
}
|
||||
onTrigger(...args);
|
||||
},
|
||||
// this flag causes the default output to be ignored
|
||||
dominantTrigger: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
log(func = (_, hap) => `[hap] ${hap.showWhole(true)}`, getData = (_, hap) => ({ hap })) {
|
||||
return this.onTrigger((...args) => {
|
||||
logger(func(...args), undefined, getData(...args));
|
||||
@@ -1103,6 +1147,7 @@ export const silence = new Pattern(() => []);
|
||||
* @returns {Pattern}
|
||||
* @example
|
||||
* pure('e4') // "e4"
|
||||
* @noAutocomplete
|
||||
*/
|
||||
export function pure(value) {
|
||||
function query(state) {
|
||||
@@ -1343,6 +1388,7 @@ export const func = curry((a, b) => reify(b).func(a));
|
||||
*
|
||||
* @param {string} name name of the function
|
||||
* @param {function} func function with 1 or more params, where last is the current pattern
|
||||
* @noAutocomplete
|
||||
*
|
||||
*/
|
||||
export function register(name, func) {
|
||||
@@ -1448,15 +1494,17 @@ export const ceil = register('ceil', function (pat) {
|
||||
* Assumes a numerical pattern, containing unipolar values in the range 0 ..
|
||||
* 1. Returns a new pattern with values scaled to the bipolar range -1 .. 1
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
export const toBipolar = register('toBipolar', function (pat) {
|
||||
return pat.fmap((x) => x * 2 - 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Assumes a numerical pattern, containing bipolar values in the range -1 ..
|
||||
* 1. Returns a new pattern with values scaled to the unipolar range 0 .. 1
|
||||
* Assumes a numerical pattern, containing bipolar values in the range -1 .. 1
|
||||
* Returns a new pattern with values scaled to the unipolar range 0 .. 1
|
||||
* @returns Pattern
|
||||
* @noAutocomplete
|
||||
*/
|
||||
export const fromBipolar = register('fromBipolar', function (pat) {
|
||||
return pat.fmap((x) => (x + 1) / 2);
|
||||
|
||||
@@ -58,6 +58,7 @@ export const valueToMidi = (value, fallbackValue) => {
|
||||
|
||||
/**
|
||||
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
|
||||
* @noAutocomplete
|
||||
*/
|
||||
export const getFreq = (noteOrMidi) => {
|
||||
if (typeof noteOrMidi === 'number') {
|
||||
@@ -68,6 +69,7 @@ export const getFreq = (noteOrMidi) => {
|
||||
|
||||
/**
|
||||
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
|
||||
* @noAutocomplete
|
||||
*/
|
||||
export const midi2note = (n) => {
|
||||
const oct = Math.floor(n / 12) - 1;
|
||||
|
||||
@@ -39,6 +39,7 @@ let startedAt = -1;
|
||||
/**
|
||||
*
|
||||
* Sends each hap as an OSC message, which can be picked up by SuperCollider or any other OSC-enabled software.
|
||||
* For more info, read [MIDI & OSC in the docs](https://strudel.tidalcycles.org/learn/input-output)
|
||||
*
|
||||
* @name osc
|
||||
* @memberof Pattern
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.4.0",
|
||||
"@codemirror/lang-javascript": "^6.1.1",
|
||||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/view": "^6.7.3",
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import jsdoc from '../../../../doc.json';
|
||||
|
||||
const getDocLabel = (doc) => doc.name || doc.longname;
|
||||
const getInnerText = (html) => {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return div.textContent || div.innerText || '';
|
||||
};
|
||||
|
||||
export function Autocomplete({ doc }) {
|
||||
return (
|
||||
<div className="prose dark:prose-invert max-h-[400px] overflow-auto">
|
||||
<h3 className="pt-0 mt-0">{getDocLabel(doc)}</h3>
|
||||
<div dangerouslySetInnerHTML={{ __html: doc.description }} />
|
||||
<ul>
|
||||
{doc.params?.map(({ name, type, description }, i) => (
|
||||
<li key={i}>
|
||||
{name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div>
|
||||
{doc.examples?.map((example, i) => (
|
||||
<div key={i}>
|
||||
<pre
|
||||
className="cursor-pointer"
|
||||
onMouseDown={(e) => {
|
||||
console.log('ola!');
|
||||
navigator.clipboard.writeText(example);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{example}
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const jsdocCompletions = jsdoc.docs
|
||||
.filter(
|
||||
(doc) =>
|
||||
getDocLabel(doc) &&
|
||||
!getDocLabel(doc).startsWith('_') &&
|
||||
!['package'].includes(doc.kind) &&
|
||||
!['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)),
|
||||
)
|
||||
// https://codemirror.net/docs/ref/#autocomplete.Completion
|
||||
.map((doc) /*: Completion */ => ({
|
||||
label: getDocLabel(doc),
|
||||
// detail: 'xxx', // An optional short piece of information to show (with a different style) after the label.
|
||||
info: () => {
|
||||
const node = document.createElement('div');
|
||||
// if Autocomplete is non-interactive, it could also be rendered at build time..
|
||||
// .. using renderToStaticMarkup
|
||||
createRoot(node).render(<Autocomplete doc={doc} />);
|
||||
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' },
|
||||
], */
|
||||
};
|
||||
};
|
||||
@@ -6,6 +6,8 @@ import { javascript } from '@codemirror/lang-javascript';
|
||||
import strudelTheme from '../themes/strudel-theme';
|
||||
import './style.css';
|
||||
import { useCallback } from 'react';
|
||||
import { autocompletion } from '@codemirror/autocomplete';
|
||||
import { strudelAutocomplete } from './Autocomplete';
|
||||
|
||||
export const setFlash = StateEffect.define();
|
||||
const flashField = StateField.define({
|
||||
@@ -49,11 +51,12 @@ const highlightField = StateField.define({
|
||||
try {
|
||||
for (let e of tr.effects) {
|
||||
if (e.is(setHighlights)) {
|
||||
const { haps } = e.value;
|
||||
const marks =
|
||||
e.value
|
||||
haps
|
||||
.map((hap) =>
|
||||
(hap.context.locations || []).map(({ start, end }) => {
|
||||
const color = hap.context.color || '#FFCA28';
|
||||
const color = hap.context.color || e.value.color || '#FFCA28';
|
||||
let from = tr.newDoc.line(start.line).from + start.column;
|
||||
let to = tr.newDoc.line(end.line).from + end.column;
|
||||
const l = tr.newDoc.length;
|
||||
@@ -79,9 +82,24 @@ const highlightField = StateField.define({
|
||||
provide: (f) => EditorView.decorations.from(f),
|
||||
});
|
||||
|
||||
const extensions = [javascript(), strudelTheme, highlightField, flashField];
|
||||
const extensions = [
|
||||
javascript(),
|
||||
highlightField,
|
||||
flashField,
|
||||
// javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
|
||||
// autocompletion({ override: [strudelAutocomplete] }),
|
||||
autocompletion({ override: [] }), // wait for https://github.com/uiwjs/react-codemirror/pull/458
|
||||
];
|
||||
|
||||
export default function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) {
|
||||
export default function CodeMirror({
|
||||
value,
|
||||
onChange,
|
||||
onViewChanged,
|
||||
onSelectionChange,
|
||||
theme,
|
||||
options,
|
||||
editorDidMount,
|
||||
}) {
|
||||
const handleOnChange = useCallback(
|
||||
(value) => {
|
||||
onChange?.(value);
|
||||
@@ -106,6 +124,7 @@ export default function CodeMirror({ value, onChange, onViewChanged, onSelection
|
||||
<>
|
||||
<_CodeMirror
|
||||
value={value}
|
||||
theme={theme || strudelTheme}
|
||||
onChange={handleOnChange}
|
||||
onCreateEditor={handleOnCreateEditor}
|
||||
onUpdate={handleOnUpdate}
|
||||
|
||||
@@ -10,10 +10,21 @@ import { Icon } from './Icon';
|
||||
import styles from './MiniRepl.module.css';
|
||||
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, drawTime, punchcard, canvasHeight = 200 }) {
|
||||
export function MiniRepl({
|
||||
tune,
|
||||
hideOutsideView = false,
|
||||
enableKeyboard,
|
||||
drawTime,
|
||||
punchcard,
|
||||
canvasHeight = 200,
|
||||
theme,
|
||||
highlightColor,
|
||||
}) {
|
||||
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
|
||||
const evalOnMount = !!drawTime;
|
||||
const drawContext = useCallback(
|
||||
@@ -61,8 +72,30 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
||||
pattern,
|
||||
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
||||
getTime: () => scheduler.now(),
|
||||
color: highlightColor,
|
||||
});
|
||||
|
||||
// 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.code === 'Period') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[activateCode, stop, view],
|
||||
),
|
||||
);
|
||||
|
||||
// set active pattern on ctrl+enter
|
||||
useLayoutEffect(() => {
|
||||
if (enableKeyboard) {
|
||||
@@ -111,7 +144,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
||||
{error && <div className={styles.error}>{error.message}</div>}
|
||||
</div>
|
||||
<div className={styles.body}>
|
||||
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
|
||||
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} />}
|
||||
</div>
|
||||
{drawTime && (
|
||||
<canvas
|
||||
@@ -140,13 +173,3 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, drawTi
|
||||
function useLogger(onTrigger) {
|
||||
useEvent(logger.key, onTrigger);
|
||||
}
|
||||
|
||||
// TODO: dedupe
|
||||
function useEvent(name, onTrigger, useCapture = false) {
|
||||
useEffect(() => {
|
||||
document.addEventListener(name, onTrigger, useCapture);
|
||||
return () => {
|
||||
document.removeEventListener(name, onTrigger, useCapture);
|
||||
};
|
||||
}, [onTrigger]);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.container {
|
||||
@apply rounded-md overflow-hidden bg-[#222222];
|
||||
@apply overflow-hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
@apply flex justify-between bg-slate-700 border-t border-slate-500;
|
||||
@apply flex justify-between bg-lineHighlight border-t border-l border-r border-lineHighlight rounded-t-md overflow-hidden;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
@@ -11,11 +11,11 @@
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply cursor-pointer w-16 flex items-center justify-center p-1 bg-slate-700 border-r border-slate-500 text-white hover:bg-slate-600;
|
||||
@apply cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground hover:bg-background;
|
||||
}
|
||||
|
||||
.buttonDisabled {
|
||||
@apply w-16 flex items-center justify-center p-1 bg-slate-600 text-slate-400 cursor-not-allowed;
|
||||
@apply w-16 flex items-center justify-center p-1 opacity-50 cursor-not-allowed border-r border-lineHighlight;
|
||||
}
|
||||
|
||||
.error {
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.cm-theme-light {
|
||||
.cm-theme {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cm-line > * {
|
||||
background: #00000095;
|
||||
.cm-theme-light {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
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;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { setHighlights } from '../components/CodeMirror6';
|
||||
|
||||
function useHighlighting({ view, pattern, active, getTime }) {
|
||||
function useHighlighting({ view, pattern, active, getTime, color }) {
|
||||
const highlights = useRef([]);
|
||||
const lastEnd = useRef(0);
|
||||
useEffect(() => {
|
||||
@@ -19,9 +19,9 @@ function useHighlighting({ view, pattern, active, getTime }) {
|
||||
highlights.current = highlights.current.filter((hap) => hap.whole.end > 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
|
||||
view.dispatch({ effects: setHighlights.of(highlights.current) }); // highlight all still active + new active haps
|
||||
view.dispatch({ effects: setHighlights.of({ haps: highlights.current, color }) }); // highlight all still active + new active haps
|
||||
} catch (err) {
|
||||
view.dispatch({ effects: setHighlights.of([]) });
|
||||
view.dispatch({ effects: setHighlights.of({ haps: [] }) });
|
||||
}
|
||||
frame = requestAnimationFrame(updateHighlights);
|
||||
});
|
||||
@@ -30,10 +30,10 @@ function useHighlighting({ view, pattern, active, getTime }) {
|
||||
};
|
||||
} else {
|
||||
highlights.current = [];
|
||||
view.dispatch({ effects: setHighlights.of([]) });
|
||||
view.dispatch({ effects: setHighlights.of({ haps: [] }) });
|
||||
}
|
||||
}
|
||||
}, [pattern, active, view]);
|
||||
}, [pattern, active, view, color]);
|
||||
}
|
||||
|
||||
export default useHighlighting;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// import 'tailwindcss/tailwind.css';
|
||||
|
||||
export { default as CodeMirror, flash } from './components/CodeMirror6';
|
||||
export * from './components/MiniRepl';
|
||||
export { default as useHighlighting } from './hooks/useHighlighting';
|
||||
export { default as CodeMirror, flash } 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 useStrudel } from './hooks/useStrudel';
|
||||
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 cx } from './cx';
|
||||
|
||||
@@ -123,17 +123,18 @@ export const scaleTranspose = register('scaleTranspose', function (offset /* : n
|
||||
/**
|
||||
* Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
||||
*
|
||||
* The scale name has the form "TO? N" wher
|
||||
*
|
||||
* - T = Tonic
|
||||
* - O = Octave (optional, defaults to 3)
|
||||
* - N = Name of scale, available names can be found [here](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
|
||||
* A scale consists of a root note (e.g. `c4`, `c`, `f#`, `bb4`) followed by a [scale type](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
|
||||
* The root note defaults to octave 3, if no octave number is given.
|
||||
* Note that you currently cannot pattern `scale` with the mini notation, because the scale name includes a space.
|
||||
* This will be improved in the future. Until then, use a sequence function like `cat` or `seq`.
|
||||
*
|
||||
* @memberof Pattern
|
||||
* @name scale
|
||||
* @param {string} scale Name of scale
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* "0 2 4 6 4 2".scale('C2 major').note()
|
||||
* @example
|
||||
* "0 2 4 6 4 2"
|
||||
* .scale(seq('C2 major', 'C2 minor').slow(2))
|
||||
* .note()
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { register, Pattern, toMidi, valueToMidi } from '@strudel.cycles/core';
|
||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { initializeWamHost } from '@webaudiomodules/sdk';
|
||||
|
||||
// this is a map of all loaded WebAudioModules
|
||||
let wams = {};
|
||||
|
||||
// this is a map of all WebAudioModule instances (possibly more than one per WAM)
|
||||
let wamInstances = {};
|
||||
export const getWamInstances = () => wamInstances;
|
||||
export const getWamInstance = (key) => {
|
||||
if (!key) {
|
||||
throw new Error(`no .wam set`);
|
||||
}
|
||||
let instance = wamInstances[key];
|
||||
if (!instance) {
|
||||
throw new Error(`wam instance "${key}" not found`);
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
|
||||
// holds a promise of the wam host init
|
||||
let hostInit;
|
||||
// maps wam keys to init promises
|
||||
let instanceInit = {};
|
||||
// maps wam keys to a list of params that are automated
|
||||
let automatedWamParams = {};
|
||||
export const getAutomatedWamParams = () => automatedWamParams;
|
||||
|
||||
// host groups of WAMs can interact with one another, but not directly across groups
|
||||
const hostGroupId = 'strudel';
|
||||
|
||||
export const loadWAM = async function (name, url) {
|
||||
// memoized wam host init
|
||||
hostInit = hostInit || initializeWamHost(getAudioContext(), hostGroupId);
|
||||
await hostInit;
|
||||
// memoized wam file import
|
||||
wams[url] = wams[url] || import(/* @vite-ignore */ url);
|
||||
const { default: WAM } = await wams[url];
|
||||
|
||||
const instance = new WAM(hostGroupId, getAudioContext());
|
||||
// memoized instance ini
|
||||
instanceInit[name] =
|
||||
instanceInit[name] ||
|
||||
instance.initialize().then(() => {
|
||||
instance.audioNode.connect(getAudioContext().destination);
|
||||
return instance;
|
||||
});
|
||||
|
||||
// save loaded instance
|
||||
wamInstances[name] = await instanceInit[name];
|
||||
return wamInstances[name];
|
||||
};
|
||||
|
||||
export const loadwam = loadWAM;
|
||||
export const loadWam = loadWAM;
|
||||
|
||||
export const wam = register('wam', function (name, pat) {
|
||||
return pat.set({ wam: name }).addTrigger((time, hap) => {
|
||||
const i = getWamInstance(name);
|
||||
let note = toMidi(hap.value.note);
|
||||
let velocity = hap.context?.velocity ?? 0.75;
|
||||
let endTime = time + hap.duration.valueOf();
|
||||
|
||||
i.audioNode.scheduleEvents({
|
||||
type: 'wam-midi',
|
||||
data: { bytes: [0x90, note, velocity] },
|
||||
time: time,
|
||||
});
|
||||
|
||||
i.audioNode.scheduleEvents({
|
||||
type: 'wam-midi',
|
||||
data: { bytes: [0x80, note, 0] },
|
||||
time: endTime,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export const param = register('param', function (param, value, pat) {
|
||||
return pat.addTrigger((time, hap) => {
|
||||
const { wam } = hap.value;
|
||||
const i = getWamInstance(wam);
|
||||
|
||||
// save param automation
|
||||
if (!automatedWamParams[wam]) {
|
||||
automatedWamParams[wam] = {};
|
||||
}
|
||||
automatedWamParams[wam][param] = true;
|
||||
|
||||
i.audioNode.scheduleEvents({
|
||||
time: time,
|
||||
type: 'wam-automation',
|
||||
data: {
|
||||
id: param,
|
||||
normalized: false,
|
||||
value: value,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@strudel.cycles/wam",
|
||||
"version": "0.1.0",
|
||||
"description": "WAM2 API for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
"publishConfig": {
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||
},
|
||||
"keywords": [
|
||||
"tidalcycles",
|
||||
"strudel",
|
||||
"pattern",
|
||||
"livecoding",
|
||||
"algorave"
|
||||
],
|
||||
"author": "Tom Burns <tom@burns.ca>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"@strudel.cycles/core": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@webaudiomodules/sdk": "^0.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^3.2.2",
|
||||
"vitest": "^0.25.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { dependencies } from './package.json';
|
||||
import { resolve } from 'path';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'index.mjs'),
|
||||
formats: ['es', 'cjs'],
|
||||
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [...Object.keys(dependencies)],
|
||||
},
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
@@ -111,7 +111,9 @@ let sampleCache = { current: undefined };
|
||||
|
||||
/**
|
||||
* Loads a collection of samples to use with `s`
|
||||
*
|
||||
* @example
|
||||
* samples('github:tidalcycles/Dirt-Samples/master');
|
||||
* s("[bd ~]*2, [~ hh]*2, ~ sd")
|
||||
* @example
|
||||
* samples({
|
||||
* bd: '808bd/BD0000.WAV',
|
||||
|
||||
@@ -59,6 +59,7 @@ export function loadWebDirt(config) {
|
||||
* @returns Pattern
|
||||
* @example
|
||||
* s("bd*2 hh sd hh").n("<0 1>").webdirt()
|
||||
* @noAutocomplete
|
||||
*/
|
||||
Pattern.prototype.webdirt = function () {
|
||||
// create a WebDirt object and initialize Web Audio context
|
||||
|
||||
Generated
+301
-8
@@ -159,6 +159,7 @@ importers:
|
||||
|
||||
packages/react:
|
||||
specifiers:
|
||||
'@codemirror/autocomplete': ^6.4.0
|
||||
'@codemirror/lang-javascript': ^6.1.1
|
||||
'@codemirror/state': ^6.2.0
|
||||
'@codemirror/view': ^6.7.3
|
||||
@@ -179,6 +180,7 @@ importers:
|
||||
tailwindcss: ^3.0.24
|
||||
vite: ^3.2.2
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.4.0_a4vbhepr4qhxm5cldqd4jpyase
|
||||
'@codemirror/lang-javascript': 6.1.2
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
@@ -187,7 +189,7 @@ importers:
|
||||
'@strudel.cycles/transpiler': link:../transpiler
|
||||
'@strudel.cycles/webaudio': link:../webaudio
|
||||
'@uiw/codemirror-themes': 4.19.7_a4vbhepr4qhxm5cldqd4jpyase
|
||||
'@uiw/react-codemirror': 4.19.7_b6o5qp6ml4k7skggixohr5abde
|
||||
'@uiw/react-codemirror': 4.19.7_r3x7zzmc35ug7i3c2vv4bf5iey
|
||||
react-hook-inview: 4.5.0_sfoxds7t5ydpegc3knd667wn6m
|
||||
devDependencies:
|
||||
'@types/react': 17.0.53
|
||||
@@ -310,6 +312,21 @@ importers:
|
||||
vite: 3.2.5
|
||||
vitest: 0.25.8
|
||||
|
||||
packages/wam:
|
||||
specifiers:
|
||||
'@strudel.cycles/core': workspace:*
|
||||
'@strudel.cycles/webaudio': workspace:*
|
||||
'@webaudiomodules/sdk': ^0.0.10
|
||||
vite: ^3.2.2
|
||||
vitest: ^0.25.7
|
||||
dependencies:
|
||||
'@strudel.cycles/core': link:../core
|
||||
'@strudel.cycles/webaudio': link:../webaudio
|
||||
'@webaudiomodules/sdk': 0.0.10
|
||||
devDependencies:
|
||||
vite: 3.2.5
|
||||
vitest: 0.25.8
|
||||
|
||||
packages/webaudio:
|
||||
specifiers:
|
||||
'@strudel.cycles/core': workspace:*
|
||||
@@ -366,6 +383,7 @@ importers:
|
||||
'@strudel.cycles/soundfonts': workspace:*
|
||||
'@strudel.cycles/tonal': workspace:*
|
||||
'@strudel.cycles/transpiler': workspace:*
|
||||
'@strudel.cycles/wam': workspace:*
|
||||
'@strudel.cycles/webaudio': workspace:*
|
||||
'@strudel.cycles/xen': workspace:*
|
||||
'@supabase/supabase-js': ^1.35.3
|
||||
@@ -373,6 +391,7 @@ importers:
|
||||
'@types/node': ^18.0.0
|
||||
'@types/react': ^18.0.26
|
||||
'@types/react-dom': ^18.0.9
|
||||
'@uiw/codemirror-themes-all': ^4.19.8
|
||||
'@vite-pwa/astro': ^0.0.1
|
||||
astro: ^1.7.2
|
||||
canvas: ^2.11.0
|
||||
@@ -384,6 +403,7 @@ importers:
|
||||
react-dom: ^18.2.0
|
||||
rehype-autolink-headings: ^6.1.1
|
||||
rehype-slug: ^5.0.1
|
||||
rehype-urls: ^1.1.1
|
||||
remark-toc: ^8.0.1
|
||||
tailwindcss: ^3.2.4
|
||||
vite-plugin-pwa: ^0.14.1
|
||||
@@ -408,6 +428,7 @@ importers:
|
||||
'@strudel.cycles/soundfonts': link:../packages/soundfonts
|
||||
'@strudel.cycles/tonal': link:../packages/tonal
|
||||
'@strudel.cycles/transpiler': link:../packages/transpiler
|
||||
'@strudel.cycles/wam': link:../packages/wam
|
||||
'@strudel.cycles/webaudio': link:../packages/webaudio
|
||||
'@strudel.cycles/xen': link:../packages/xen
|
||||
'@supabase/supabase-js': 1.35.7
|
||||
@@ -415,6 +436,7 @@ importers:
|
||||
'@types/node': 18.11.18
|
||||
'@types/react': 18.0.27
|
||||
'@types/react-dom': 18.0.10
|
||||
'@uiw/codemirror-themes-all': 4.19.8
|
||||
astro: 1.9.2_@types+node@18.11.18
|
||||
canvas: 2.11.0
|
||||
fraction.js: 4.2.0
|
||||
@@ -424,6 +446,7 @@ importers:
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
rehype-autolink-headings: 6.1.1
|
||||
rehype-slug: 5.1.0
|
||||
rehype-urls: 1.1.1
|
||||
remark-toc: 8.0.1
|
||||
tailwindcss: 3.2.4
|
||||
devDependencies:
|
||||
@@ -3979,9 +4002,10 @@ packages:
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-extensions-basic-setup/4.19.7_a4vbhepr4qhxm5cldqd4jpyase:
|
||||
/@uiw/codemirror-extensions-basic-setup/4.19.7_cgfc5aojxuwjajwhkrgidrzxoa:
|
||||
resolution: {integrity: sha512-pk0JTFJAZOGxouBB1pdBtb59qKibO9DW4hER4VSFGBGW3TBDeXUwL/gKujhRpySHG2MtG09cgt4a3XOFIWwXTw==}
|
||||
peerDependencies:
|
||||
'@codemirror/autocomplete': '>=6.0.0'
|
||||
'@codemirror/state': '>=6.0.0'
|
||||
'@codemirror/view': '>=6.0.0'
|
||||
dependencies:
|
||||
@@ -3994,6 +4018,173 @@ packages:
|
||||
'@codemirror/view': 6.7.3
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-abcdef/4.19.8:
|
||||
resolution: {integrity: sha512-sR7srfMJRGTsAFQYEs+8IogUxUHDsMqNqZ4waIA6rNIvOH9Q1NYFEqTpqf4AeivkNeXm3N1LI+1nQAKhWzWpOA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-androidstudio/4.19.8:
|
||||
resolution: {integrity: sha512-xhvEPZ1NvihIJVx7MA1Z8A8NwaTbM6mtBsfwM3mKAmzW6K6JcIJftcKe5z5wSPZk4J3S66vq8RYEMOBygSOdEQ==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-atomone/4.19.8:
|
||||
resolution: {integrity: sha512-enZR8RPiKohyuEU4DOWA7A/xLs/+NsuvZAuGmg5PU43CQITzK1Wqri43/WxkGGsEIztejX439s1QuYxUwCCeUg==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-aura/4.19.8:
|
||||
resolution: {integrity: sha512-DLoi2XEPNBD6Cxsp3v/nYw7jJ7sDEXET9+C43KZnk1dQ9H5e0HSpZhOkw1xmaCGzoXn9q7HInPa7GgUEMv8NPA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-bbedit/4.19.8:
|
||||
resolution: {integrity: sha512-1vaVaVhMP2IB7H77W9yvPRhVr+8HGBWPDfFEVWAc7tfeU0xC2MFby05gO/7T6K2JWyj2lXnVw/i+oFNeDdcekQ==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-bespin/4.19.8:
|
||||
resolution: {integrity: sha512-JyzmgCloEWhduH5GVYzHQ3vPc3zC0YVRiu7lOjgdOVRhr49M6KmwTytmbmqXbaqmSZKFUJqiHjz8VswqcNC4dw==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-darcula/4.19.8:
|
||||
resolution: {integrity: sha512-fPKyzMqCtBfez2ubC/77AP0BfE+XOio+sy8oRYfX3HqRQEivnnEddQJnTZVz0kKSLYSryQHbOMw/qwgInPvBCA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-dracula/4.19.8:
|
||||
resolution: {integrity: sha512-YHQJUDnYKrew/YkEPQG3SQFcnKr6MzVbIXxmdKuxlCTrySNFNMK19dKrzY3fwc+Lgpp5r26QLLocTZyCvu1xeA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-duotone/4.19.8:
|
||||
resolution: {integrity: sha512-SJkjyKxU2Nazv6uEctvRdz62/ZQ8rBP4xtXrjF/zcjOdWmx2ecUAGn/j5Euw5V55Dr74yIQFs0L+GB5UTHCHDA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-eclipse/4.19.8:
|
||||
resolution: {integrity: sha512-6XXzYFC809e5MWT/NNTqoZ0N9H6P8mXwIEGDfigh33VU1TJPPpO1LCWdf59VcXx/xIY0ctDp7rU7hTy3f3tHkw==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-github/4.19.8:
|
||||
resolution: {integrity: sha512-RUAzHGkG2G2PXTxsH4KbKVCKqPs/+H6xvhCT3Q4YiPj1W2aGSfPwZZ8cLLCm9dYaIh/+H6L+AcoiUtvADjyKsQ==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-gruvbox-dark/4.19.8:
|
||||
resolution: {integrity: sha512-EQepnqhfcu7HY2uutigzflZ9OFzDcF9eq4xF53UylD+WZBdwq2qarZMQgBfOXZZKYI3XcTny064fa7Xxklhs1g==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-material/4.19.8:
|
||||
resolution: {integrity: sha512-dm7FtZkJSZ4AV+9vXxKUDnXLTHaqWnsmn9HOb6AmLiHmHcLFLDgeosSBnA80fwXBhd6QswJI0cEnKHAvcC/vFg==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-noctis-lilac/4.19.8:
|
||||
resolution: {integrity: sha512-vPH8KOiRgcBK1V7/BN04n8K03cfR4CEmOGZUMKpcLlSZwWRe0+JPk6S2qtjbJ072Fsn1ZrcLRYrqtaAJQNOVnw==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-nord/4.19.8:
|
||||
resolution: {integrity: sha512-qBqKJI9fa35rFefXqaiw3FCXWSpD+65+Weji3g/AgB0m0+MHG+3rkC+rLZ4PE2QpkcMz6wHj89EMwOW9W1i3cA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-okaidia/4.19.8:
|
||||
resolution: {integrity: sha512-gxEThgbbfIxKjk7UzD0D8gw5bVpgxDct3e6r9/WegZw7tTvTHMnz9wutGbJp2cYSwdgLHoQJZzZBi6jKhExZSg==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-solarized/4.19.8:
|
||||
resolution: {integrity: sha512-xHp2i6zWBMw2+kZX0GUYi7pCL3ZL1qWSO8OaleHCkpY3jn+lfLSJOAYYkajvNyu3tPsiKQuAgpCQ6AYFWOr6BQ==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-sublime/4.19.8:
|
||||
resolution: {integrity: sha512-tapkCty8lwpRw2CAY5bPiK8S2ptdTL4d1X/IkCVyHqbC0B8728iQJu3E4Rrka7yggFx4QGUpyZB9UTIFYURtsw==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-tokyo-night-day/4.19.8:
|
||||
resolution: {integrity: sha512-HDKp2KDjluubpe+7M31SohWUFCW7wGQIvoD60xr86OkPZDdqsCVUFmna6rqfttV6S2OsgVNJPBzZvOF398Eapg==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-tokyo-night-storm/4.19.8:
|
||||
resolution: {integrity: sha512-Kp8HIf1H+pgbRx/ZhRsqqN5qfyx3SVVfjnUPGltsdBymqh8QGdr7LC8Tp0P5+Yjb6CqF6Dgx7RnQDj1XZ94rbA==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-tokyo-night/4.19.8:
|
||||
resolution: {integrity: sha512-LrfB+EgZqBNlMF6Zqk8RrlsfVzKe6Ple7ckvREV6kSuwxncF5FlhzVL3cWgxNfNQ7TJ5ySVSvw4fWOcxIcc1LQ==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-vscode/4.19.8:
|
||||
resolution: {integrity: sha512-UdUuJfNjG2y88taP5gsEA9AQlhRTURnl+9kl+LOJkQtXavrqp3J8gUAHRhEWxN7k8q8YimsSVcQ4yw0FKAgXQQ==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-theme-xcode/4.19.8:
|
||||
resolution: {integrity: sha512-VYCRhStYujUNGIkiaZZ1o5OO2Z2edsKkYCCYSy0rwb4G17vT6sd4YDnmJQuF0gzqQkTIdDxSb6aCM9ISkPABlg==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-themes-all/4.19.8:
|
||||
resolution: {integrity: sha512-0kAWabe46Vkz+gwNfaXvn/0adeUSCTAuZC0d7Nu8ebcr1TjT1fDLFNZIXlwidn40suiXq8FDfZ9euArx3RPg0Q==}
|
||||
dependencies:
|
||||
'@uiw/codemirror-theme-abcdef': 4.19.8
|
||||
'@uiw/codemirror-theme-androidstudio': 4.19.8
|
||||
'@uiw/codemirror-theme-atomone': 4.19.8
|
||||
'@uiw/codemirror-theme-aura': 4.19.8
|
||||
'@uiw/codemirror-theme-bbedit': 4.19.8
|
||||
'@uiw/codemirror-theme-bespin': 4.19.8
|
||||
'@uiw/codemirror-theme-darcula': 4.19.8
|
||||
'@uiw/codemirror-theme-dracula': 4.19.8
|
||||
'@uiw/codemirror-theme-duotone': 4.19.8
|
||||
'@uiw/codemirror-theme-eclipse': 4.19.8
|
||||
'@uiw/codemirror-theme-github': 4.19.8
|
||||
'@uiw/codemirror-theme-gruvbox-dark': 4.19.8
|
||||
'@uiw/codemirror-theme-material': 4.19.8
|
||||
'@uiw/codemirror-theme-noctis-lilac': 4.19.8
|
||||
'@uiw/codemirror-theme-nord': 4.19.8
|
||||
'@uiw/codemirror-theme-okaidia': 4.19.8
|
||||
'@uiw/codemirror-theme-solarized': 4.19.8
|
||||
'@uiw/codemirror-theme-sublime': 4.19.8
|
||||
'@uiw/codemirror-theme-tokyo-night': 4.19.8
|
||||
'@uiw/codemirror-theme-tokyo-night-day': 4.19.8
|
||||
'@uiw/codemirror-theme-tokyo-night-storm': 4.19.8
|
||||
'@uiw/codemirror-theme-vscode': 4.19.8
|
||||
'@uiw/codemirror-theme-xcode': 4.19.8
|
||||
'@uiw/codemirror-themes': 4.19.8
|
||||
dev: false
|
||||
|
||||
/@uiw/codemirror-themes/4.19.7_a4vbhepr4qhxm5cldqd4jpyase:
|
||||
resolution: {integrity: sha512-M/42RkPI60ItlssmNuEoZO2MQvlY6fRmdX7XRUAhKjxczZoaq8xS6HIvv1whGf2zGsTrwdVTPCm6ls0l17dvPA==}
|
||||
peerDependencies:
|
||||
@@ -4005,7 +4196,15 @@ packages:
|
||||
'@codemirror/view': 6.7.3
|
||||
dev: false
|
||||
|
||||
/@uiw/react-codemirror/4.19.7_b6o5qp6ml4k7skggixohr5abde:
|
||||
/@uiw/codemirror-themes/4.19.8:
|
||||
resolution: {integrity: sha512-k+0molX6YuQZ7vYymS5DEl3NcHxVE4VP4J0nB7RBnzLrhDwT2K8R5ie7J2eH+4bpppIK1ZZdZ6Mie7U48dH5dQ==}
|
||||
dependencies:
|
||||
'@codemirror/language': 6.4.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.7.3
|
||||
dev: false
|
||||
|
||||
/@uiw/react-codemirror/4.19.7_r3x7zzmc35ug7i3c2vv4bf5iey:
|
||||
resolution: {integrity: sha512-IHvpYWVSdiaHX0Fk6oY6YyAJigDnyvSpWKNUTRzsMNxB+8/wqZ8lior4TprXH0zyLxW5F1+bTyifFFTeg+X3Sw==}
|
||||
peerDependencies:
|
||||
'@codemirror/state': '>=6.0.0'
|
||||
@@ -4018,10 +4217,12 @@ packages:
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/theme-one-dark': 6.1.0
|
||||
'@codemirror/view': 6.7.3
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.19.7_a4vbhepr4qhxm5cldqd4jpyase
|
||||
'@uiw/codemirror-extensions-basic-setup': 4.19.7_cgfc5aojxuwjajwhkrgidrzxoa
|
||||
codemirror: 6.0.1
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/autocomplete'
|
||||
dev: false
|
||||
|
||||
/@vite-pwa/astro/0.0.1:
|
||||
@@ -4065,6 +4266,10 @@ packages:
|
||||
resolution: {integrity: sha512-ukOMWnCg1tCvT7WnDfsUKQOFDQGsyR5tNgRpwmqi+5/vzU3ghdDXzvIM4IOPdSb3OeSsBNvmSL8nxIVOqi2WXA==}
|
||||
dev: false
|
||||
|
||||
/@webaudiomodules/sdk/0.0.10:
|
||||
resolution: {integrity: sha512-owzwJL3ETntGH325vxDZ5vJlrEIYlhqVjw9xu1z+M1zebX7vcWAzh/Bf6GxISFeKoVSH4te90q1BYPdzmh+PeA==}
|
||||
dev: false
|
||||
|
||||
/JSONStream/1.3.5:
|
||||
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
|
||||
hasBin: true
|
||||
@@ -7250,6 +7455,10 @@ packages:
|
||||
web-namespaces: 2.0.1
|
||||
dev: false
|
||||
|
||||
/hast-util-has-property/1.0.4:
|
||||
resolution: {integrity: sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==}
|
||||
dev: false
|
||||
|
||||
/hast-util-has-property/2.0.1:
|
||||
resolution: {integrity: sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==}
|
||||
dev: false
|
||||
@@ -7593,6 +7802,10 @@ packages:
|
||||
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
|
||||
dev: true
|
||||
|
||||
/is-arrayish/0.3.2:
|
||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
||||
dev: false
|
||||
|
||||
/is-bigint/1.0.4:
|
||||
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
|
||||
dependencies:
|
||||
@@ -10221,6 +10434,17 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/postcss-import/14.1.0:
|
||||
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.0.0
|
||||
dependencies:
|
||||
postcss-value-parser: 4.2.0
|
||||
read-cache: 1.0.0
|
||||
resolve: 1.22.1
|
||||
dev: false
|
||||
|
||||
/postcss-import/14.1.0_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -10231,6 +10455,16 @@ packages:
|
||||
postcss-value-parser: 4.2.0
|
||||
read-cache: 1.0.0
|
||||
resolve: 1.22.1
|
||||
dev: true
|
||||
|
||||
/postcss-js/4.0.0:
|
||||
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
|
||||
engines: {node: ^12 || ^14 || >= 16}
|
||||
peerDependencies:
|
||||
postcss: ^8.3.3
|
||||
dependencies:
|
||||
camelcase-css: 2.0.1
|
||||
dev: false
|
||||
|
||||
/postcss-js/4.0.0_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
|
||||
@@ -10240,6 +10474,23 @@ packages:
|
||||
dependencies:
|
||||
camelcase-css: 2.0.1
|
||||
postcss: 8.4.21
|
||||
dev: true
|
||||
|
||||
/postcss-load-config/3.1.4:
|
||||
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
|
||||
engines: {node: '>= 10'}
|
||||
peerDependencies:
|
||||
postcss: '>=8.0.9'
|
||||
ts-node: '>=9.0.0'
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
ts-node:
|
||||
optional: true
|
||||
dependencies:
|
||||
lilconfig: 2.0.6
|
||||
yaml: 1.10.2
|
||||
dev: false
|
||||
|
||||
/postcss-load-config/3.1.4_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
|
||||
@@ -10257,6 +10508,15 @@ packages:
|
||||
postcss: 8.4.21
|
||||
yaml: 1.10.2
|
||||
|
||||
/postcss-nested/6.0.0:
|
||||
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
|
||||
engines: {node: '>=12.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.14
|
||||
dependencies:
|
||||
postcss-selector-parser: 6.0.11
|
||||
dev: false
|
||||
|
||||
/postcss-nested/6.0.0_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
|
||||
engines: {node: '>=12.0'}
|
||||
@@ -10265,6 +10525,7 @@ packages:
|
||||
dependencies:
|
||||
postcss: 8.4.21
|
||||
postcss-selector-parser: 6.0.11
|
||||
dev: true
|
||||
|
||||
/postcss-selector-parser/6.0.10:
|
||||
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||
@@ -10880,6 +11141,14 @@ packages:
|
||||
unified: 10.1.2
|
||||
dev: false
|
||||
|
||||
/rehype-urls/1.1.1:
|
||||
resolution: {integrity: sha512-ct9Kb/nAL6oe/O5fDc0xjiqm8Z9xgXdorOdDhZAWx7awucyiuYXU7Dax+23Gu24nnGwtdaCW6zslKAYzlEW1lw==}
|
||||
dependencies:
|
||||
hast-util-has-property: 1.0.4
|
||||
stdopt: 2.2.0
|
||||
unist-util-visit: 1.4.1
|
||||
dev: false
|
||||
|
||||
/rehype/12.0.1:
|
||||
resolution: {integrity: sha512-ey6kAqwLM3X6QnMDILJthGvG1m1ULROS9NT4uG9IDCuv08SFyLlreSuvOa//DgEvbXx62DS6elGVqusWhRUbgw==}
|
||||
dependencies:
|
||||
@@ -11609,6 +11878,12 @@ packages:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/stdopt/2.2.0:
|
||||
resolution: {integrity: sha512-D/p41NgXOkcj1SeGhfXOwv9z1K6EV3sjAUY5aeepVbgEHv7DpKWLTjhjScyzMWAQCAgUQys1mjH0eArm4cjRGw==}
|
||||
dependencies:
|
||||
is-arrayish: 0.3.2
|
||||
dev: false
|
||||
|
||||
/stream-connect/1.0.2:
|
||||
resolution: {integrity: sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -11873,6 +12148,8 @@ packages:
|
||||
resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
postcss: ^8.0.9
|
||||
dependencies:
|
||||
arg: 5.0.2
|
||||
chokidar: 3.5.3
|
||||
@@ -11889,10 +12166,10 @@ packages:
|
||||
object-hash: 3.0.0
|
||||
picocolors: 1.0.0
|
||||
postcss: 8.4.21
|
||||
postcss-import: 14.1.0_postcss@8.4.21
|
||||
postcss-js: 4.0.0_postcss@8.4.21
|
||||
postcss-load-config: 3.1.4_postcss@8.4.21
|
||||
postcss-nested: 6.0.0_postcss@8.4.21
|
||||
postcss-import: 14.1.0
|
||||
postcss-js: 4.0.0
|
||||
postcss-load-config: 3.1.4
|
||||
postcss-nested: 6.0.0
|
||||
postcss-selector-parser: 6.0.11
|
||||
postcss-value-parser: 4.2.0
|
||||
quick-lru: 5.1.1
|
||||
@@ -12475,6 +12752,10 @@ packages:
|
||||
resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
|
||||
dev: false
|
||||
|
||||
/unist-util-is/3.0.0:
|
||||
resolution: {integrity: sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==}
|
||||
dev: false
|
||||
|
||||
/unist-util-is/5.2.0:
|
||||
resolution: {integrity: sha512-Glt17jWwZeyqrFqOK0pF1Ded5U3yzJnFr8CG1GMjCWTp9zDo2p+cmD6pWbZU8AgM5WU3IzRv6+rBwhzsGh6hBQ==}
|
||||
dev: false
|
||||
@@ -12523,6 +12804,12 @@ packages:
|
||||
'@types/unist': 2.0.6
|
||||
dev: false
|
||||
|
||||
/unist-util-visit-parents/2.1.2:
|
||||
resolution: {integrity: sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==}
|
||||
dependencies:
|
||||
unist-util-is: 3.0.0
|
||||
dev: false
|
||||
|
||||
/unist-util-visit-parents/5.1.3:
|
||||
resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
|
||||
dependencies:
|
||||
@@ -12530,6 +12817,12 @@ packages:
|
||||
unist-util-is: 5.2.0
|
||||
dev: false
|
||||
|
||||
/unist-util-visit/1.4.1:
|
||||
resolution: {integrity: sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==}
|
||||
dependencies:
|
||||
unist-util-visit-parents: 2.1.2
|
||||
dev: false
|
||||
|
||||
/unist-util-visit/4.1.2:
|
||||
resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
|
||||
dependencies:
|
||||
|
||||
@@ -2506,40 +2506,26 @@ exports[`runs examples > example "mul" example index 0 1`] = `
|
||||
|
||||
exports[`runs examples > example "n" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | s:superpiano n:0 ]",
|
||||
"[ 1/1 → 2/1 | s:superpiano n:1 ]",
|
||||
"[ 2/1 → 3/1 | s:superpiano n:2 ]",
|
||||
"[ 3/1 → 4/1 | s:superpiano n:3 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "n" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | s:superpiano n:c4 ]",
|
||||
"[ 1/1 → 2/1 | s:superpiano n:d4 ]",
|
||||
"[ 2/1 → 3/1 | s:superpiano n:e4 ]",
|
||||
"[ 3/1 → 4/1 | s:superpiano n:g4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "n" example index 2 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | n:0 s:east ]",
|
||||
"[ 1/4 → 1/2 | n:1 s:east ]",
|
||||
"[ 1/2 → 3/4 | n:2 s:east ]",
|
||||
"[ 3/4 → 1/1 | n:3 s:east ]",
|
||||
"[ 1/1 → 5/4 | n:0 s:east ]",
|
||||
"[ 5/4 → 3/2 | n:1 s:east ]",
|
||||
"[ 3/2 → 7/4 | n:2 s:east ]",
|
||||
"[ 7/4 → 2/1 | n:3 s:east ]",
|
||||
"[ 2/1 → 9/4 | n:0 s:east ]",
|
||||
"[ 9/4 → 5/2 | n:1 s:east ]",
|
||||
"[ 5/2 → 11/4 | n:2 s:east ]",
|
||||
"[ 11/4 → 3/1 | n:3 s:east ]",
|
||||
"[ 3/1 → 13/4 | n:0 s:east ]",
|
||||
"[ 13/4 → 7/2 | n:1 s:east ]",
|
||||
"[ 7/2 → 15/4 | n:2 s:east ]",
|
||||
"[ 15/4 → 4/1 | n:3 s:east ]",
|
||||
"[ 0/1 → 1/2 | s:bd n:0 ]",
|
||||
"[ 1/2 → 1/1 | s:sd n:0 ]",
|
||||
"[ 0/1 → 1/3 | s:hh n:0 ]",
|
||||
"[ 1/3 → 2/3 | s:hh n:0 ]",
|
||||
"[ 2/3 → 1/1 | s:hh n:0 ]",
|
||||
"[ 1/1 → 3/2 | s:bd n:1 ]",
|
||||
"[ 3/2 → 2/1 | s:sd n:1 ]",
|
||||
"[ 1/1 → 4/3 | s:hh n:1 ]",
|
||||
"[ 4/3 → 5/3 | s:hh n:1 ]",
|
||||
"[ 5/3 → 2/1 | s:hh n:1 ]",
|
||||
"[ 2/1 → 5/2 | s:bd n:0 ]",
|
||||
"[ 5/2 → 3/1 | s:sd n:0 ]",
|
||||
"[ 2/1 → 7/3 | s:hh n:0 ]",
|
||||
"[ 7/3 → 8/3 | s:hh n:0 ]",
|
||||
"[ 8/3 → 3/1 | s:hh n:0 ]",
|
||||
"[ 3/1 → 7/2 | s:bd n:1 ]",
|
||||
"[ 7/2 → 4/1 | s:sd n:1 ]",
|
||||
"[ 3/1 → 10/3 | s:hh n:1 ]",
|
||||
"[ 10/3 → 11/3 | s:hh n:1 ]",
|
||||
"[ 11/3 → 4/1 | s:hh n:1 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -2580,6 +2566,69 @@ exports[`runs examples > example "never" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "note" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c ]",
|
||||
"[ 1/4 → 1/2 | note:a ]",
|
||||
"[ 1/2 → 3/4 | note:f ]",
|
||||
"[ 3/4 → 1/1 | note:e ]",
|
||||
"[ 1/1 → 5/4 | note:c ]",
|
||||
"[ 5/4 → 3/2 | note:a ]",
|
||||
"[ 3/2 → 7/4 | note:f ]",
|
||||
"[ 7/4 → 2/1 | note:e ]",
|
||||
"[ 2/1 → 9/4 | note:c ]",
|
||||
"[ 9/4 → 5/2 | note:a ]",
|
||||
"[ 5/2 → 11/4 | note:f ]",
|
||||
"[ 11/4 → 3/1 | note:e ]",
|
||||
"[ 3/1 → 13/4 | note:c ]",
|
||||
"[ 13/4 → 7/2 | note:a ]",
|
||||
"[ 7/2 → 15/4 | note:f ]",
|
||||
"[ 15/4 → 4/1 | note:e ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "note" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:c4 ]",
|
||||
"[ 1/4 → 1/2 | note:a4 ]",
|
||||
"[ 1/2 → 3/4 | note:f4 ]",
|
||||
"[ 3/4 → 1/1 | note:e4 ]",
|
||||
"[ 1/1 → 5/4 | note:c4 ]",
|
||||
"[ 5/4 → 3/2 | note:a4 ]",
|
||||
"[ 3/2 → 7/4 | note:f4 ]",
|
||||
"[ 7/4 → 2/1 | note:e4 ]",
|
||||
"[ 2/1 → 9/4 | note:c4 ]",
|
||||
"[ 9/4 → 5/2 | note:a4 ]",
|
||||
"[ 5/2 → 11/4 | note:f4 ]",
|
||||
"[ 11/4 → 3/1 | note:e4 ]",
|
||||
"[ 3/1 → 13/4 | note:c4 ]",
|
||||
"[ 13/4 → 7/2 | note:a4 ]",
|
||||
"[ 7/2 → 15/4 | note:f4 ]",
|
||||
"[ 15/4 → 4/1 | note:e4 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "note" example index 2 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | note:60 ]",
|
||||
"[ 1/4 → 1/2 | note:69 ]",
|
||||
"[ 1/2 → 3/4 | note:65 ]",
|
||||
"[ 3/4 → 1/1 | note:64 ]",
|
||||
"[ 1/1 → 5/4 | note:60 ]",
|
||||
"[ 5/4 → 3/2 | note:69 ]",
|
||||
"[ 3/2 → 7/4 | note:65 ]",
|
||||
"[ 7/4 → 2/1 | note:64 ]",
|
||||
"[ 2/1 → 9/4 | note:60 ]",
|
||||
"[ 9/4 → 5/2 | note:69 ]",
|
||||
"[ 5/2 → 11/4 | note:65 ]",
|
||||
"[ 11/4 → 3/1 | note:64 ]",
|
||||
"[ 3/1 → 13/4 | note:60 ]",
|
||||
"[ 13/4 → 7/2 | note:69 ]",
|
||||
"[ 7/2 → 15/4 | note:65 ]",
|
||||
"[ 15/4 → 4/1 | note:64 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "octave" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/1 | n:0 s:supersquare octave:3 ]",
|
||||
@@ -3280,6 +3329,31 @@ exports[`runs examples > example "samples" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "samples" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd ]",
|
||||
"[ 1/2 → 3/4 | s:bd ]",
|
||||
"[ 1/4 → 1/2 | s:hh ]",
|
||||
"[ 3/4 → 1/1 | s:hh ]",
|
||||
"[ 1/2 → 1/1 | s:sd ]",
|
||||
"[ 1/1 → 5/4 | s:bd ]",
|
||||
"[ 3/2 → 7/4 | s:bd ]",
|
||||
"[ 5/4 → 3/2 | s:hh ]",
|
||||
"[ 7/4 → 2/1 | s:hh ]",
|
||||
"[ 3/2 → 2/1 | s:sd ]",
|
||||
"[ 2/1 → 9/4 | s:bd ]",
|
||||
"[ 5/2 → 11/4 | s:bd ]",
|
||||
"[ 9/4 → 5/2 | s:hh ]",
|
||||
"[ 11/4 → 3/1 | s:hh ]",
|
||||
"[ 5/2 → 3/1 | s:sd ]",
|
||||
"[ 3/1 → 13/4 | s:bd ]",
|
||||
"[ 7/2 → 15/4 | s:bd ]",
|
||||
"[ 13/4 → 7/2 | s:hh ]",
|
||||
"[ 15/4 → 4/1 | s:hh ]",
|
||||
"[ 7/2 → 4/1 | s:sd ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "saw" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/32 | note:c3 ]",
|
||||
@@ -3323,6 +3397,35 @@ exports[`runs examples > example "saw" example index 1 1`] = `
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "scale" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:C2 ]",
|
||||
"[ 1/6 → 1/3 | note:E2 ]",
|
||||
"[ 1/3 → 1/2 | note:G2 ]",
|
||||
"[ 1/2 → 2/3 | note:B2 ]",
|
||||
"[ 2/3 → 5/6 | note:G2 ]",
|
||||
"[ 5/6 → 1/1 | note:E2 ]",
|
||||
"[ 1/1 → 7/6 | note:C2 ]",
|
||||
"[ 7/6 → 4/3 | note:E2 ]",
|
||||
"[ 4/3 → 3/2 | note:G2 ]",
|
||||
"[ 3/2 → 5/3 | note:B2 ]",
|
||||
"[ 5/3 → 11/6 | note:G2 ]",
|
||||
"[ 11/6 → 2/1 | note:E2 ]",
|
||||
"[ 2/1 → 13/6 | note:C2 ]",
|
||||
"[ 13/6 → 7/3 | note:E2 ]",
|
||||
"[ 7/3 → 5/2 | note:G2 ]",
|
||||
"[ 5/2 → 8/3 | note:B2 ]",
|
||||
"[ 8/3 → 17/6 | note:G2 ]",
|
||||
"[ 17/6 → 3/1 | note:E2 ]",
|
||||
"[ 3/1 → 19/6 | note:C2 ]",
|
||||
"[ 19/6 → 10/3 | note:E2 ]",
|
||||
"[ 10/3 → 7/2 | note:G2 ]",
|
||||
"[ 7/2 → 11/3 | note:B2 ]",
|
||||
"[ 11/3 → 23/6 | note:G2 ]",
|
||||
"[ 23/6 → 4/1 | note:E2 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "scale" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | note:C2 ]",
|
||||
"[ 1/6 → 1/3 | note:E2 ]",
|
||||
|
||||
+55
-15
@@ -1,24 +1,47 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
import preact from '@astrojs/preact';
|
||||
import react from '@astrojs/react';
|
||||
|
||||
import mdx from '@astrojs/mdx';
|
||||
|
||||
import remarkToc from 'remark-toc';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
import rehypeUrls from 'rehype-urls';
|
||||
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import AstroPWA from '@vite-pwa/astro';
|
||||
// import { visualizer } from 'rollup-plugin-visualizer';
|
||||
|
||||
const site = `https://strudel.tidalcycles.org`; // root url without a path
|
||||
const base = '/'; // base path of the strudel site
|
||||
|
||||
// this rehype plugin converts relative anchor links to absolute ones
|
||||
// it wokrs by prepending the absolute page path to anchor links
|
||||
// example: #gain -> /learn/effects/#gain
|
||||
// this is necessary when using a base href like <base href={base} />
|
||||
// in this setup, relative anchor links will always link to base, instead of the current page
|
||||
function absoluteAnchors() {
|
||||
return (tree, file) => {
|
||||
const chunks = file.history[0].split('/src/pages/'); // file.history[0] is the file path
|
||||
const path = chunks[chunks.length - 1].slice(0, -4); // only path inside src/pages, without .mdx
|
||||
return rehypeUrls((url) => {
|
||||
if (!url.href.startsWith('#')) {
|
||||
return;
|
||||
}
|
||||
const baseWithSlash = base.endsWith('/') ? base : base + '/';
|
||||
const absoluteUrl = baseWithSlash + path + url.href;
|
||||
// console.log(url.href + ' -> ', absoluteUrl);
|
||||
return absoluteUrl;
|
||||
})(tree);
|
||||
};
|
||||
}
|
||||
const options = {
|
||||
// See https://mdxjs.com/advanced/plugins
|
||||
remarkPlugins: [
|
||||
remarkToc,
|
||||
// E.g. `remark-frontmatter`
|
||||
],
|
||||
rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings],
|
||||
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }], absoluteAnchors],
|
||||
};
|
||||
|
||||
// https://astro.build/config
|
||||
@@ -31,8 +54,35 @@ export default defineConfig({
|
||||
mdx(options),
|
||||
tailwind(),
|
||||
AstroPWA({
|
||||
registerType: 'autoUpdate',
|
||||
injectRegister: 'auto',
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,json,wav,mp3,ogg}'],
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: ({ url }) =>
|
||||
[
|
||||
/^https:\/\/raw\.githubusercontent\.com\/.*/i,
|
||||
/^https:\/\/freesound\.org\/.*/i,
|
||||
/^https:\/\/cdn\.freesound\.org\/.*/i,
|
||||
/^https:\/\/shabda\.ndre\.gr\/.*/i,
|
||||
].some((regex) => regex.test(url)),
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'external-samples',
|
||||
expiration: {
|
||||
maxEntries: 5000,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 30, // <== 14 days
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
},
|
||||
manifest: {
|
||||
includeAssets: ['favicon.ico', 'icons/apple-icon-180.png', 'favicon.svg'],
|
||||
@@ -70,8 +120,8 @@ export default defineConfig({
|
||||
},
|
||||
}),
|
||||
],
|
||||
site: `https://strudel.tidalcycles.org`,
|
||||
base: '/',
|
||||
site,
|
||||
base,
|
||||
vite: {
|
||||
ssr: {
|
||||
// Example: Force a broken package to skip SSR processing, if needed
|
||||
@@ -79,13 +129,3 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
build: {
|
||||
outDir: '../out',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
plugins: [visualizer({ template: 'treemap' })],
|
||||
},
|
||||
},
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@strudel.cycles/soundfonts": "workspace:*",
|
||||
"@strudel.cycles/tonal": "workspace:*",
|
||||
"@strudel.cycles/transpiler": "workspace:*",
|
||||
"@strudel.cycles/wam": "workspace:*",
|
||||
"@strudel.cycles/webaudio": "workspace:*",
|
||||
"@strudel.cycles/xen": "workspace:*",
|
||||
"@supabase/supabase-js": "^1.35.3",
|
||||
@@ -38,6 +39,7 @@
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@uiw/codemirror-themes-all": "^4.19.8",
|
||||
"astro": "^1.7.2",
|
||||
"canvas": "^2.11.0",
|
||||
"fraction.js": "^4.2.0",
|
||||
@@ -47,6 +49,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-slug": "^5.0.1",
|
||||
"rehype-urls": "^1.1.1",
|
||||
"remark-toc": "^8.0.1",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
---
|
||||
import { pwaInfo } from 'virtual:pwa-info';
|
||||
import '../styles/index.css';
|
||||
import { settings } from '../repl/themes.mjs';
|
||||
|
||||
const { BASE_URL } = import.meta.env;
|
||||
const base = BASE_URL;
|
||||
|
||||
const { strudelTheme } = settings;
|
||||
---
|
||||
|
||||
<!-- Global Metadata -->
|
||||
@@ -27,4 +30,55 @@ const base = BASE_URL;
|
||||
<script src="./make-scrollable-code-focusable.js" is:inline></script>
|
||||
|
||||
<script src="/src/pwa.ts"></script>
|
||||
<!-- this does not work for some reason: -->
|
||||
<!-- <style is:global define:vars={strudelTheme}></style> -->
|
||||
<!-- the following variables are just a fallback to make sure everything is readable without JS -->
|
||||
<style is:global>
|
||||
:root {
|
||||
--background: #222;
|
||||
--lineBackground: #22222250;
|
||||
--foreground: #fff;
|
||||
--caret: #ffcc00;
|
||||
--selection: rgba(128, 203, 196, 0.5);
|
||||
--selectionMatch: #036dd626;
|
||||
--lineHighlight: #00000050;
|
||||
--gutterBackground: transparent;
|
||||
--gutterForeground: #8a919966;
|
||||
}
|
||||
</style>
|
||||
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
|
||||
|
||||
<script define:vars={{ settings, strudelTheme }} is:inline>
|
||||
const themeStyle = document.createElement('style');
|
||||
themeStyle.id = 'strudel-theme';
|
||||
document.head.append(themeStyle);
|
||||
function getTheme(name) {
|
||||
if (!settings[name]) {
|
||||
console.warn('theme', name, 'has no settings');
|
||||
}
|
||||
return {
|
||||
name,
|
||||
settings: settings[name] || settings.strudelTheme,
|
||||
};
|
||||
}
|
||||
function setTheme(name) {
|
||||
const { settings } = getTheme(name);
|
||||
// set css variables
|
||||
themeStyle.innerHTML = `:root {
|
||||
${Object.entries(settings)
|
||||
// important to override fallback
|
||||
.map(([key, value]) => `--${key}: ${value} !important;`)
|
||||
.join('\n')}
|
||||
}`;
|
||||
// tailwind dark mode
|
||||
if (settings.light) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
// persist theme name
|
||||
localStorage.setItem('strudel-theme', name || 'strudelTheme');
|
||||
}
|
||||
setTheme(localStorage.getItem('strudel-theme'));
|
||||
document.addEventListener('strudel-theme', (e) => setTheme(e.detail));
|
||||
</script>
|
||||
|
||||
@@ -19,13 +19,13 @@ const langCode = 'en'; // getLanguageFromURL(currentPage);
|
||||
const sidebar = SIDEBAR[langCode];
|
||||
---
|
||||
|
||||
<nav class="flex justify-between py-2 px-4 items-center h-14 max-h-14 bg-[#161616]" title="Top Navigation">
|
||||
<nav class="flex justify-between py-2 px-4 items-center h-14 max-h-14 bg-lineHighlight text-foreground" title="Top Navigation">
|
||||
<!-- <div class="menu-toggle">
|
||||
<SidebarToggle client:idle />
|
||||
</div> -->
|
||||
<div class="flex overflow-visible items-center grow" style="overflow:visible">
|
||||
<a href="/" class="flex items-center text-2xl space-x-2">
|
||||
<h1 class="text-white font-bold flex space-x-2 items-baseline text-xl">
|
||||
<h1 class="font-bold flex space-x-2 items-baseline text-xl">
|
||||
<span>🌀</span>
|
||||
<div class="flex space-x-1 items-baseline">
|
||||
<span class="">strudel</span>
|
||||
|
||||
@@ -14,7 +14,7 @@ const langCode = 'en'; // getLanguageFromURL(currentPage);
|
||||
const sidebar = SIDEBAR[langCode];
|
||||
---
|
||||
|
||||
<nav aria-labelledby="grid-left" class="max-h-full overflow-auto pb-20">
|
||||
<nav aria-labelledby="grid-left" class="max-h-full overflow-auto pb-20 text-foreground">
|
||||
<ul>
|
||||
{
|
||||
Object.entries(sidebar).map(([header, children]) => (
|
||||
@@ -27,8 +27,8 @@ const sidebar = SIDEBAR[langCode];
|
||||
return (
|
||||
<li class="">
|
||||
<a
|
||||
class={`pl-4 py-0.5 w-full hover:bg-header block${
|
||||
currentPageMatch === child.link ? ' bg-header' : ''
|
||||
class={`pl-4 py-0.5 w-full hover:bg-lineHighlight block${
|
||||
currentPageMatch === child.link ? ' bg-lineHighlight' : ''
|
||||
}`}
|
||||
href={url}
|
||||
aria-current={currentPageMatch === child.link ? 'page' : false}
|
||||
|
||||
@@ -22,7 +22,7 @@ const currentPage = Astro.url.pathname;
|
||||
<span>On this Page:</span>
|
||||
<TableOfContents client:media="(max-width: 50em)" headings={headings} currentPage={currentPage} />
|
||||
</nav> -->
|
||||
<div class="prose prose-invert max-w-full pb-8">
|
||||
<div class="prose dark:prose-invert max-w-full pb-8">
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -9,10 +9,12 @@ type Props = {
|
||||
};
|
||||
|
||||
const { headings, githubEditUrl } = Astro.props as Props;
|
||||
const currentPage = Astro.url.pathname;
|
||||
let currentPage = Astro.url.pathname;
|
||||
// remove slash before #
|
||||
currentPage = currentPage.endsWith('/') ? currentPage.slice(0, -1) : currentPage;
|
||||
---
|
||||
|
||||
<nav aria-labelledby="grid-right" class="w-64">
|
||||
<nav aria-labelledby="grid-right" class="w-64 text-foreground">
|
||||
<TableOfContents client:media="(min-width: 50em)" headings={headings} currentPage={currentPage} />
|
||||
<MoreMenu editHref={githubEditUrl} />
|
||||
</nav>
|
||||
|
||||
@@ -78,9 +78,9 @@ const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[]; curren
|
||||
<a
|
||||
href={`${currentPage}#${heading.slug}`}
|
||||
onClick={onLinkClick}
|
||||
className={`py-0.5 block cursor-pointer w-full border-l-4 border-header hover:bg-header ${
|
||||
className={`py-0.5 block cursor-pointer w-full border-l-4 border-lineHighlight hover:bg-lineHighlight ${
|
||||
['pl-4', 'pl-9', 'pl-12'][heading.depth - minDepth]
|
||||
} ${currentID === heading.slug ? 'bg-header' : ''}`.trim()}
|
||||
} ${currentID === heading.slug ? 'bg-lineHighlight' : ''}`.trim()}
|
||||
>
|
||||
{unescape(heading.text)}
|
||||
</a>
|
||||
|
||||
@@ -65,16 +65,17 @@ export const SIDEBAR: Sidebar = {
|
||||
{ text: 'Tonal Modifiers', link: 'learn/tonal' },
|
||||
],
|
||||
More: [
|
||||
{ text: 'MIDI & OSC', link: 'learn/input-output' },
|
||||
{ text: 'Offline', link: 'learn/pwa' },
|
||||
{ text: 'Patterns', link: 'technical-manual/patterns' },
|
||||
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
|
||||
{ text: 'MIDI & OSC', link: 'learn/input-output' },
|
||||
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
|
||||
],
|
||||
Development: [
|
||||
{ text: 'REPL', link: 'technical-manual/repl' },
|
||||
{ text: 'Docs', link: 'technical-manual/docs' },
|
||||
{ text: 'Testing', link: 'technical-manual/testing' },
|
||||
{ text: 'Packages', link: 'technical-manual/packages' },
|
||||
// { text: 'Packages', link: 'technical-manual/packages' },
|
||||
// { text: 'Internals', link: 'technical-manual/internals' },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.cm-activeLine {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.cm-theme {
|
||||
background-color: var(--background);
|
||||
border: 1px solid var(--lineHighlight);
|
||||
padding: 2px;
|
||||
}
|
||||
@@ -2,6 +2,10 @@ import { evalScope, controls } 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';
|
||||
|
||||
const theme = localStorage.getItem('strudel-theme') || 'strudelTheme';
|
||||
|
||||
let modules;
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -32,9 +36,18 @@ export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
|
||||
.then(([res]) => setRepl(() => res.MiniRepl))
|
||||
.catch((err) => console.error(err));
|
||||
}, []);
|
||||
// const { settings } = useTheme();
|
||||
return Repl ? (
|
||||
<div className="mb-4">
|
||||
<Repl tune={tune} hideOutsideView={true} drawTime={drawTime} punchcard={punchcard} canvasHeight={canvasHeight} />
|
||||
<Repl
|
||||
tune={tune}
|
||||
hideOutsideView={true}
|
||||
drawTime={drawTime}
|
||||
punchcard={punchcard}
|
||||
canvasHeight={canvasHeight}
|
||||
theme={themes[theme]}
|
||||
highlightColor={settings[theme]?.foreground}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<pre>{tune}</pre>
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function MobileNav({ sidebar }) {
|
||||
<div className="space-y-1 px-4 py-4 bg-[#161616]">
|
||||
<a
|
||||
href=".."
|
||||
className="py-2 flex cursor-pointer items-center space-x-1 hover:bg-bg hover:px-2 rounded-md"
|
||||
className="py-2 flex cursor-pointer items-center space-x-1 hover:bg-background hover:px-2 rounded-md"
|
||||
>
|
||||
<span>go to REPL</span>
|
||||
</a>
|
||||
@@ -36,7 +36,9 @@ export default function MobileNav({ sidebar }) {
|
||||
as="a"
|
||||
href={`/${item.link}`}
|
||||
className={classNames(
|
||||
item.current ? 'bg-bg text-white' : 'text-gray-300 hover:bg-bg hover:text-white',
|
||||
item.current
|
||||
? 'bg-background text-white'
|
||||
: 'text-gray-300 hover:bg-lineHighlight hover:text-white',
|
||||
'block px-3 py-2 rounded-md text-base font-medium',
|
||||
)}
|
||||
aria-current={item.current ? 'page' : undefined}
|
||||
|
||||
+25
-19
@@ -10,27 +10,33 @@
|
||||
height: 70px;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.prose h1:hover .icon-link,
|
||||
.prose h2:hover .icon-link,
|
||||
.prose h3:hover .icon-link,
|
||||
.prose h4:hover .icon-link,
|
||||
.prose h5:hover .icon-link,
|
||||
.prose h6:hover .icon-link,
|
||||
.icon.icon-link:hover {
|
||||
.icon.icon-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.prose h1:hover .icon.icon-link,
|
||||
.prose h2:hover .icon.icon-link,
|
||||
.prose h3:hover .icon.icon-link,
|
||||
.prose h4:hover .icon.icon-link,
|
||||
.prose h5:hover .icon.icon-link,
|
||||
.prose h6:hover .icon.icon-link {
|
||||
visibility: visible;
|
||||
}
|
||||
.icon.icon-link {
|
||||
background-image: url(./link.svg);
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
margin-left: -20px;
|
||||
width: 20px;
|
||||
margin-top: 8px;
|
||||
|
||||
.prose h1 > a,
|
||||
.prose h2 > a,
|
||||
.prose h3 > a,
|
||||
.prose h4 > a,
|
||||
.prose h5 > a,
|
||||
.prose h6 > a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.icon.icon-link::after {
|
||||
content: '#';
|
||||
margin-left: 8px;
|
||||
font-size: 0.9em;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const currentFile = `src/pages${currentPage.replace(/\/$/, '')}.mdx`;
|
||||
const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
|
||||
---
|
||||
|
||||
<html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en'} class="initial">
|
||||
<html dir={frontmatter.dir ?? 'ltr'} lang={frontmatter.lang ?? 'en'} class="initial dark">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<HeadSEO frontmatter={frontmatter} canonicalUrl={canonicalURL} />
|
||||
@@ -31,7 +31,7 @@ const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`;
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body class="h-screen text-gray-50">
|
||||
<body class="h-screen text-gray-50 bg-background">
|
||||
<div class="w-full h-full space-y-4 flex flex-col">
|
||||
<header class="max-w-full fixed top-0 w-full z-[100]">
|
||||
<Header currentPage={currentPage} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Introduction
|
||||
title: JavaScript API
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import HeadCommon from '../components/HeadCommon.astro';
|
||||
import { Repl } from '../repl/Repl.jsx';
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<HeadCommon />
|
||||
<title>Strudel REPL</title>
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-background">
|
||||
<Repl client:only="react" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Accumulation Modifiers
|
||||
description: Strudel Tutorial - Coding syntax
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Coding syntax
|
||||
description: Strudel Tutorial - Coding syntax
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Audio effects
|
||||
description: Strudel Tutorial - Audio effects
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Pattern Constructors
|
||||
description: Strudel Tutorial
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Getting Started
|
||||
description: Strudel Tutorial - Getting Started
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Other Outptuts
|
||||
description: Strudel Tutorial
|
||||
title: MIDI & OSC
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Notes
|
||||
description: Strudel Tutorial - Notes
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Offline
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
# Using Strudel Offline
|
||||
|
||||
You can use Strudel even without a network! When you first visit the [Strudel REPL](strudel.tidalcycles.org/),
|
||||
your browser will download the whole web app including documentation.
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Notes
|
||||
description: Strudel Tutorial - Notes
|
||||
title: Samples
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: What is Strudel?
|
||||
description: Strudel Tutorial
|
||||
title: Signals
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Sounds
|
||||
description: Strudel Tutorial - Sounds
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Synths
|
||||
description: Strudel Tutorial - Synths
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
|
||||
+107
-13
@@ -1,15 +1,18 @@
|
||||
import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon';
|
||||
import { logger } from '@strudel.cycles/core';
|
||||
import { cx } from '@strudel.cycles/react';
|
||||
import { useEvent, cx } from '@strudel.cycles/react';
|
||||
// import { cx } from '@strudel.cycles/react';
|
||||
import { nanoid } from 'nanoid';
|
||||
import React, { useContext, useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useEvent, loadedSamples, ReplContext } from './Repl';
|
||||
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { loadedSamples } from './Repl';
|
||||
import { Reference } from './Reference';
|
||||
import { themes, themeColors } from './themes.mjs';
|
||||
import { getAutomatedWamParams, getWamInstances } from '@strudel.cycles/wam';
|
||||
|
||||
export function Footer({ context }) {
|
||||
// const [activeFooter, setActiveFooter] = useState('console');
|
||||
// const { activeFooter, setActiveFooter, isZen } = useContext?.(ReplContext);
|
||||
const { activeFooter, setActiveFooter, isZen } = context;
|
||||
const { activeFooter, setActiveFooter, isZen, theme, setTheme } = context;
|
||||
const footerContent = useRef();
|
||||
const [log, setLog] = useState([]);
|
||||
|
||||
@@ -54,8 +57,8 @@ export function Footer({ context }) {
|
||||
<div
|
||||
onClick={() => setActiveFooter(name)}
|
||||
className={cx(
|
||||
'h-8 px-2 text-white cursor-pointer hover:text-tertiary flex items-center space-x-1 border-b',
|
||||
activeFooter === name ? 'border-white hover:border-tertiary' : 'border-transparent',
|
||||
'h-8 px-2 text-foreground cursor-pointer hover:text-tertiary flex items-center space-x-1 border-b',
|
||||
activeFooter === name ? 'border-foreground hover:border-tertiary' : 'border-transparent',
|
||||
)}
|
||||
>
|
||||
{label || name}
|
||||
@@ -66,17 +69,20 @@ export function Footer({ context }) {
|
||||
if (isZen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<footer className="bg-footer z-[20]">
|
||||
<footer className="bg-lineHighlight z-[20]">
|
||||
<div className="flex justify-between px-2">
|
||||
<div className={cx('flex select-none', activeFooter && 'pb-2')}>
|
||||
<div className={cx('flex select-none max-w-full overflow-auto', activeFooter && 'pb-2')}>
|
||||
<FooterTab name="intro" label="welcome" />
|
||||
<FooterTab name="samples" />
|
||||
<FooterTab name="console" />
|
||||
<FooterTab name="reference" />
|
||||
<FooterTab name="theme" />
|
||||
<FooterTab name="wams" />
|
||||
</div>
|
||||
{activeFooter !== '' && (
|
||||
<button onClick={() => setActiveFooter('')} className="text-white" aria-label="Close Panel">
|
||||
<button onClick={() => setActiveFooter('')} className="text-foreground" aria-label="Close Panel">
|
||||
<XMarkIcon className="w-5 h-5" />
|
||||
</button>
|
||||
)}
|
||||
@@ -87,7 +93,7 @@ export function Footer({ context }) {
|
||||
ref={footerContent}
|
||||
>
|
||||
{activeFooter === 'intro' && (
|
||||
<div className="prose prose-invert max-w-[600px] pt-2 font-sans pb-8 px-4">
|
||||
<div className="prose dark:prose-invert max-w-[600px] pt-2 font-sans pb-8 px-4">
|
||||
<h3>
|
||||
<span className={cx('animate-spin inline-block select-none')}>🌀</span> welcome
|
||||
</h3>
|
||||
@@ -132,7 +138,7 @@ export function Footer({ context }) {
|
||||
</div>
|
||||
)}
|
||||
{activeFooter === 'console' && (
|
||||
<div className="break-all px-4">
|
||||
<div className="break-all px-4 dark:text-white text-stone-900">
|
||||
{log.map((l, i) => {
|
||||
const message = linkify(l.message);
|
||||
return (
|
||||
@@ -148,8 +154,8 @@ export function Footer({ context }) {
|
||||
</div>
|
||||
)}
|
||||
{activeFooter === 'samples' && (
|
||||
<div className="break-normal w-full px-4">
|
||||
<span className="text-white">{loadedSamples.length} banks loaded:</span>
|
||||
<div className="break-normal w-full px-4 dark:text-white text-stone-900">
|
||||
<span>{loadedSamples.length} banks loaded:</span>
|
||||
{loadedSamples.map(([name, samples]) => (
|
||||
<span key={name} className="cursor-pointer hover:text-tertiary" onClick={() => {}}>
|
||||
{' '}
|
||||
@@ -165,6 +171,47 @@ export function Footer({ context }) {
|
||||
</div>
|
||||
)}
|
||||
{activeFooter === 'reference' && <Reference />}
|
||||
{activeFooter === 'theme' && (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2 p-2">
|
||||
{Object.entries(themes).map(([k, t]) => (
|
||||
<div
|
||||
key={k}
|
||||
className={cx(
|
||||
'border-2 border-transparent cursor-pointer p-4 bg-background bg-opacity-25 rounded-md',
|
||||
theme === k ? '!border-foreground' : '',
|
||||
)}
|
||||
onClick={() => {
|
||||
setTheme(k);
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('strudel-theme', {
|
||||
detail: k,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="mb-2 w-full text-center text-foreground">{k}</div>
|
||||
<div className="flex justify-stretch overflow-hidden rounded-md">
|
||||
{themeColors(t).map((c, i) => (
|
||||
<div key={i} className="grow h-6" style={{ background: c }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{activeFooter === 'wams' && (
|
||||
<div className="break-normal w-full px-4 dark:text-white text-stone-900">
|
||||
<span>{Object.keys(getWamInstances()).length} loaded:</span>
|
||||
<select onChange={(e) => showWAM(e)}>
|
||||
<option key="--">--</option>
|
||||
{Object.keys(getWamInstances()).map((w) => (
|
||||
<option key={w}>{w}</option>
|
||||
))}
|
||||
</select>
|
||||
<div id="wam-gui"></div>
|
||||
<button onClick={() => wamPreset()}>preset</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</footer>
|
||||
@@ -195,3 +242,50 @@ function linkify(inputText) {
|
||||
|
||||
return replacedText;
|
||||
}
|
||||
|
||||
let wamGui = null;
|
||||
let displayedWam = null;
|
||||
|
||||
const showWAM = async (e) => {
|
||||
if (displayedWam !== null && wamGui !== null) {
|
||||
displayedWam.destroyGui(wamGui);
|
||||
}
|
||||
|
||||
const wam = getWamInstances()[e.target.value];
|
||||
displayedWam = e.target.value;
|
||||
|
||||
const gui = await wam.createGui();
|
||||
const guiDiv = document.getElementById('wam-gui');
|
||||
guiDiv.innerHTML = '';
|
||||
guiDiv.appendChild(gui);
|
||||
|
||||
const params = await wam.audioNode.getParameterInfo();
|
||||
|
||||
for (let id of Object.keys(params)) {
|
||||
const param = params[id];
|
||||
const input = document.createElement('div');
|
||||
input.innerHTML = `<label>${id}</label>: type ${param.type}, min ${param.minValue}, max ${param.maxValue}`;
|
||||
guiDiv.appendChild(input);
|
||||
}
|
||||
};
|
||||
|
||||
const wamPreset = async () => {
|
||||
const wam = getWamInstances()[displayedWam];
|
||||
|
||||
const params = await wam.audioNode.getParameterInfo();
|
||||
const values = await wam.audioNode.getParameterValues();
|
||||
|
||||
let preset = {}
|
||||
const automatedParams = getAutomatedWamParams()[displayedWam];
|
||||
|
||||
for (let id of Object.keys(params)) {
|
||||
if (automatedParams[id]) {
|
||||
continue
|
||||
}
|
||||
if (values[id].value.toFixed(6) == params[id].defaultValue.toFixed(6)) {
|
||||
continue
|
||||
}
|
||||
preset[id] = values[id].value;
|
||||
}
|
||||
console.log("preset", preset);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export function Header({ context }) {
|
||||
id="header"
|
||||
className={cx(
|
||||
'py-1 flex-none w-full text-black justify-between z-[100] text-lg select-none sticky top-0',
|
||||
!isZen && !isEmbedded && 'bg-header',
|
||||
!isZen && !isEmbedded && 'bg-lineHighlight',
|
||||
isEmbedded ? 'flex' : 'md:flex',
|
||||
)}
|
||||
>
|
||||
@@ -48,8 +48,7 @@ export function Header({ context }) {
|
||||
}}
|
||||
className={cx(
|
||||
isEmbedded ? 'text-l cursor-pointer' : 'text-xl',
|
||||
// 'bg-clip-text bg-gradient-to-r from-primary to-secondary text-transparent font-bold',
|
||||
'text-white font-bold flex space-x-2 items-center',
|
||||
'text-foreground font-bold flex space-x-2 items-center',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -66,7 +65,7 @@ export function Header({ context }) {
|
||||
</h1>
|
||||
</div>
|
||||
{!isZen && (
|
||||
<div className="flex max-w-full overflow-auto text-white ">
|
||||
<div className="flex max-w-full overflow-auto text-foreground">
|
||||
<button
|
||||
onClick={handleTogglePlay}
|
||||
title={started ? 'stop' : 'play'}
|
||||
|
||||
@@ -5,16 +5,16 @@ const visibleFunctions = jsdocJson.docs
|
||||
|
||||
export function Reference() {
|
||||
return (
|
||||
<div className="flex h-full w-full pt-2">
|
||||
<div className="flex h-full w-full pt-2 text-foreground">
|
||||
<div className="w-64 flex-none h-full overflow-y-auto overflow-x-hidden pr-4">
|
||||
{visibleFunctions.map((entry, i) => (
|
||||
<a key={i} className="cursor-pointer block hover:bg-linegray py-1 px-4" href={`#doc-${i}`}>
|
||||
<a key={i} className="cursor-pointer block hover:bg-lineHighlight py-1 px-4" href={`#doc-${i}`}>
|
||||
{entry.name} {/* <span className="text-gray-600">{entry.meta.filename}</span> */}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="break-normal w-full h-full overflow-auto pl-4 flex relative">
|
||||
<div className="prose prose-invert">
|
||||
<div className="prose dark:prose-invert">
|
||||
<h2>API Reference</h2>
|
||||
<p>
|
||||
This is the long list functions you can use! Remember that you don't need to remember all of those and that
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cm-gutters {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cm-content {
|
||||
#code .cm-content {
|
||||
padding-top: 12px !important;
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
|
||||
#code .cm-scroller {
|
||||
padding-bottom: 50vh;
|
||||
}
|
||||
#code .cm-line > * {
|
||||
background: var(--lineBackground);
|
||||
}
|
||||
|
||||
+14
-14
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import { cleanupDraw, cleanupUi, controls, evalScope, getDrawContext, logger } from '@strudel.cycles/core';
|
||||
import { CodeMirror, cx, flash, useHighlighting, useStrudel } from '@strudel.cycles/react';
|
||||
import { CodeMirror, cx, flash, useHighlighting, useStrudel, useKeydown } from '@strudel.cycles/react';
|
||||
import {
|
||||
getAudioContext,
|
||||
getLoadedSamples,
|
||||
@@ -22,6 +22,10 @@ import { Header } from './Header';
|
||||
import { prebake } from './prebake.mjs';
|
||||
import * as tunes from './tunes.mjs';
|
||||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||
import { themes } from './themes.mjs';
|
||||
import useTheme from '../useTheme';
|
||||
|
||||
const initialTheme = localStorage.getItem('strudel-theme') || 'strudelTheme';
|
||||
|
||||
initAudioOnFirstClick();
|
||||
|
||||
@@ -42,6 +46,7 @@ const modules = [
|
||||
import('@strudel.cycles/serial'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
import('@strudel.cycles/csound'),
|
||||
import('@strudel.cycles/wam'),
|
||||
];
|
||||
|
||||
evalScope(
|
||||
@@ -109,6 +114,7 @@ export const ReplContext = createContext(null);
|
||||
export function Repl({ embedded = false }) {
|
||||
const isEmbedded = embedded || window.location !== window.parent.location;
|
||||
const [view, setView] = useState(); // codemirror view
|
||||
const [theme, setTheme] = useState(initialTheme);
|
||||
const [lastShared, setLastShared] = useState();
|
||||
const [activeFooter, setActiveFooter] = useState('');
|
||||
const [isZen, setIsZen] = useState(false);
|
||||
@@ -167,12 +173,15 @@ export function Repl({ embedded = false }) {
|
||||
),
|
||||
);
|
||||
|
||||
const { settings } = useTheme();
|
||||
|
||||
// highlighting
|
||||
useHighlighting({
|
||||
view,
|
||||
pattern,
|
||||
active: started && !activeCode?.includes('strudel disable-highlighting'),
|
||||
getTime: () => scheduler.now(),
|
||||
color: settings?.foreground,
|
||||
});
|
||||
|
||||
//
|
||||
@@ -255,6 +264,8 @@ export function Repl({ embedded = false }) {
|
||||
handleShare,
|
||||
isZen,
|
||||
setIsZen,
|
||||
theme,
|
||||
setTheme,
|
||||
};
|
||||
return (
|
||||
// bg-gradient-to-t from-blue-900 to-slate-900
|
||||
@@ -269,6 +280,7 @@ export function Repl({ embedded = false }) {
|
||||
<Header context={context} />
|
||||
<section className="grow flex text-gray-100 relative overflow-auto cursor-text pb-0" id="code">
|
||||
<CodeMirror
|
||||
theme={themes[theme] || themes.strudelTheme}
|
||||
value={code}
|
||||
onChange={handleChangeCode}
|
||||
onViewChanged={setView}
|
||||
@@ -276,7 +288,7 @@ export function Repl({ embedded = false }) {
|
||||
/>
|
||||
</section>
|
||||
{error && (
|
||||
<div className="text-red-500 p-4 bg-lineblack animate-pulse">{error.message || 'Unknown Error :-/'}</div>
|
||||
<div className="text-red-500 p-4 bg-lineHighlight animate-pulse">{error.message || 'Unknown Error :-/'}</div>
|
||||
)}
|
||||
{isEmbedded && !started && (
|
||||
<button
|
||||
@@ -292,15 +304,3 @@ export function Repl({ embedded = false }) {
|
||||
</ReplContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useEvent(name, onTrigger, useCapture = false) {
|
||||
useEffect(() => {
|
||||
document.addEventListener(name, onTrigger, useCapture);
|
||||
return () => {
|
||||
document.removeEventListener(name, onTrigger, useCapture);
|
||||
};
|
||||
}, [onTrigger]);
|
||||
}
|
||||
function useKeydown(onTrigger) {
|
||||
useEvent('keydown', onTrigger, true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,457 @@
|
||||
import {
|
||||
abcdef,
|
||||
androidstudio,
|
||||
atomone,
|
||||
aura,
|
||||
bespin,
|
||||
darcula,
|
||||
dracula,
|
||||
duotoneDark,
|
||||
eclipse,
|
||||
githubDark,
|
||||
gruvboxDark,
|
||||
materialDark,
|
||||
nord,
|
||||
okaidia,
|
||||
solarizedDark,
|
||||
sublime,
|
||||
tokyoNight,
|
||||
tokyoNightStorm,
|
||||
vscodeDark,
|
||||
xcodeDark,
|
||||
bbedit,
|
||||
duotoneLight,
|
||||
githubLight,
|
||||
gruvboxLight,
|
||||
materialLight,
|
||||
noctisLilac,
|
||||
solarizedLight,
|
||||
tokyoNightDay,
|
||||
xcodeLight,
|
||||
} from '@uiw/codemirror-themes-all';
|
||||
|
||||
import strudelTheme from '@strudel.cycles/react/src/themes/strudel-theme';
|
||||
|
||||
export const themes = {
|
||||
strudelTheme,
|
||||
abcdef,
|
||||
androidstudio,
|
||||
atomone,
|
||||
aura,
|
||||
bespin,
|
||||
darcula,
|
||||
dracula,
|
||||
duotoneDark,
|
||||
eclipse,
|
||||
githubDark,
|
||||
gruvboxDark,
|
||||
materialDark,
|
||||
nord,
|
||||
okaidia,
|
||||
solarizedDark,
|
||||
sublime,
|
||||
tokyoNight,
|
||||
tokyoNightStorm,
|
||||
vscodeDark,
|
||||
xcodeDark,
|
||||
bbedit,
|
||||
duotoneLight,
|
||||
githubLight,
|
||||
gruvboxLight,
|
||||
materialLight,
|
||||
noctisLilac,
|
||||
solarizedLight,
|
||||
tokyoNightDay,
|
||||
xcodeLight,
|
||||
};
|
||||
|
||||
// lineBackground is background with 50% opacity, to make sure the selection below is visible
|
||||
|
||||
export const settings = {
|
||||
strudelTheme: {
|
||||
background: '#222',
|
||||
lineBackground: '#22222250',
|
||||
foreground: '#fff',
|
||||
// foreground: '#75baff',
|
||||
caret: '#ffcc00',
|
||||
selection: 'rgba(128, 203, 196, 0.5)',
|
||||
selectionMatch: '#036dd626',
|
||||
// lineHighlight: '#8a91991a', // original
|
||||
lineHighlight: '#00000050',
|
||||
gutterBackground: 'transparent',
|
||||
// gutterForeground: '#8a919966',
|
||||
gutterForeground: '#8a919966',
|
||||
},
|
||||
abcdef: {
|
||||
background: '#0f0f0f',
|
||||
lineBackground: '#0f0f0f50',
|
||||
foreground: '#defdef',
|
||||
caret: '#00FF00',
|
||||
selection: '#515151',
|
||||
selectionMatch: '#515151',
|
||||
gutterBackground: '#555',
|
||||
gutterForeground: '#FFFFFF',
|
||||
lineHighlight: '#314151',
|
||||
},
|
||||
androidstudio: {
|
||||
background: '#282b2e',
|
||||
lineBackground: '#282b2e50',
|
||||
foreground: '#a9b7c6',
|
||||
caret: '#00FF00',
|
||||
selection: '#343739',
|
||||
selectionMatch: '#343739',
|
||||
lineHighlight: '#343739',
|
||||
},
|
||||
atomone: {
|
||||
background: '#272C35',
|
||||
lineBackground: '#272C3550',
|
||||
foreground: '#9d9b97',
|
||||
caret: '#797977',
|
||||
selection: '#ffffff30',
|
||||
selectionMatch: '#2B323D',
|
||||
gutterBackground: '#272C35',
|
||||
gutterForeground: '#465063',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#2B323D',
|
||||
},
|
||||
aura: {
|
||||
background: '#21202e',
|
||||
lineBackground: '#21202e50',
|
||||
foreground: '#edecee',
|
||||
caret: '#a277ff',
|
||||
selection: '#3d375e7f',
|
||||
selectionMatch: '#3d375e7f',
|
||||
gutterBackground: '#21202e',
|
||||
gutterForeground: '#edecee',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#a394f033',
|
||||
},
|
||||
bbedit: {
|
||||
light: true,
|
||||
background: '#FFFFFF',
|
||||
lineBackground: '#FFFFFF50',
|
||||
foreground: '#000000',
|
||||
caret: '#FBAC52',
|
||||
selection: '#FFD420',
|
||||
selectionMatch: '#FFD420',
|
||||
gutterBackground: '#f5f5f5',
|
||||
gutterForeground: '#4D4D4C',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#00000012',
|
||||
},
|
||||
bespin: {
|
||||
background: '#28211c',
|
||||
lineBackground: '#28211c50',
|
||||
foreground: '#9d9b97',
|
||||
caret: '#797977',
|
||||
selection: '#36312e',
|
||||
selectionMatch: '#4f382b',
|
||||
gutterBackground: '#28211c',
|
||||
gutterForeground: '#666666',
|
||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
darcula: {
|
||||
background: '#2B2B2B',
|
||||
lineBackground: '#2B2B2B50',
|
||||
foreground: '#f8f8f2',
|
||||
caret: '#FFFFFF',
|
||||
selection: 'rgba(255, 255, 255, 0.1)',
|
||||
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
||||
gutterBackground: 'rgba(255, 255, 255, 0.1)',
|
||||
gutterForeground: '#999',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
dracula: {
|
||||
background: '#282a36',
|
||||
lineBackground: '#282a3650',
|
||||
foreground: '#f8f8f2',
|
||||
caret: '#f8f8f0',
|
||||
selection: 'rgba(255, 255, 255, 0.1)',
|
||||
selectionMatch: 'rgba(255, 255, 255, 0.2)',
|
||||
gutterBackground: '#282a36',
|
||||
gutterForeground: '#6D8A88',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
duotoneLight: {
|
||||
light: true,
|
||||
background: '#faf8f5',
|
||||
lineBackground: '#faf8f550',
|
||||
foreground: '#b29762',
|
||||
caret: '#93abdc',
|
||||
selection: '#e3dcce',
|
||||
selectionMatch: '#e3dcce',
|
||||
gutterBackground: '#faf8f5',
|
||||
gutterForeground: '#cdc4b1',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#EFEFEF',
|
||||
},
|
||||
duotoneDark: {
|
||||
background: '#2a2734',
|
||||
lineBackground: '#2a273450',
|
||||
foreground: '#6c6783',
|
||||
caret: '#ffad5c',
|
||||
selection: 'rgba(255, 255, 255, 0.1)',
|
||||
gutterBackground: '#2a2734',
|
||||
gutterForeground: '#545167',
|
||||
lineHighlight: '#36334280',
|
||||
},
|
||||
eclipse: {
|
||||
light: true,
|
||||
background: '#fff',
|
||||
lineBackground: '#ffffff50',
|
||||
foreground: '#000',
|
||||
caret: '#FFFFFF',
|
||||
selection: '#d7d4f0',
|
||||
selectionMatch: '#d7d4f0',
|
||||
gutterBackground: '#f7f7f7',
|
||||
gutterForeground: '#999',
|
||||
lineHighlight: '#e8f2ff',
|
||||
gutterBorder: 'transparent',
|
||||
},
|
||||
githubLight: {
|
||||
light: true,
|
||||
background: '#fff',
|
||||
lineBackground: '#ffffff50',
|
||||
foreground: '#24292e',
|
||||
selection: '#BBDFFF',
|
||||
selectionMatch: '#BBDFFF',
|
||||
gutterBackground: '#fff',
|
||||
gutterForeground: '#6e7781',
|
||||
},
|
||||
githubDark: {
|
||||
background: '#0d1117',
|
||||
lineBackground: '#0d111750',
|
||||
foreground: '#c9d1d9',
|
||||
caret: '#c9d1d9',
|
||||
selection: '#003d73',
|
||||
selectionMatch: '#003d73',
|
||||
lineHighlight: '#36334280',
|
||||
},
|
||||
gruvboxDark: {
|
||||
background: '#282828',
|
||||
lineBackground: '#28282850',
|
||||
foreground: '#ebdbb2',
|
||||
caret: '#ebdbb2',
|
||||
selection: '#bdae93',
|
||||
selectionMatch: '#bdae93',
|
||||
lineHighlight: '#3c3836',
|
||||
gutterBackground: '#282828',
|
||||
gutterForeground: '#7c6f64',
|
||||
},
|
||||
gruvboxLight: {
|
||||
light: true,
|
||||
background: '#fbf1c7',
|
||||
lineBackground: '#fbf1c750',
|
||||
foreground: '#3c3836',
|
||||
caret: '#af3a03',
|
||||
selection: '#ebdbb2',
|
||||
selectionMatch: '#bdae93',
|
||||
lineHighlight: '#ebdbb2',
|
||||
gutterBackground: '#ebdbb2',
|
||||
gutterForeground: '#665c54',
|
||||
gutterBorder: 'transparent',
|
||||
},
|
||||
materialDark: {
|
||||
background: '#2e3235',
|
||||
lineBackground: '#2e323550',
|
||||
foreground: '#bdbdbd',
|
||||
caret: '#a0a4ae',
|
||||
selection: '#d7d4f0',
|
||||
selectionMatch: '#d7d4f0',
|
||||
gutterBackground: '#2e3235',
|
||||
gutterForeground: '#999',
|
||||
gutterActiveForeground: '#4f5b66',
|
||||
lineHighlight: '#545b61',
|
||||
},
|
||||
materialLight: {
|
||||
light: true,
|
||||
background: '#FAFAFA',
|
||||
lineBackground: '#FAFAFA50',
|
||||
foreground: '#90A4AE',
|
||||
caret: '#272727',
|
||||
selection: '#80CBC440',
|
||||
selectionMatch: '#FAFAFA',
|
||||
gutterBackground: '#FAFAFA',
|
||||
gutterForeground: '#90A4AE',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#CCD7DA50',
|
||||
},
|
||||
noctisLilac: {
|
||||
light: true,
|
||||
background: '#f2f1f8',
|
||||
lineBackground: '#f2f1f850',
|
||||
foreground: '#0c006b',
|
||||
caret: '#5c49e9',
|
||||
selection: '#d5d1f2',
|
||||
selectionMatch: '#d5d1f2',
|
||||
gutterBackground: '#f2f1f8',
|
||||
gutterForeground: '#0c006b70',
|
||||
lineHighlight: '#e1def3',
|
||||
},
|
||||
nord: {
|
||||
background: '#2e3440',
|
||||
lineBackground: '#2e344050',
|
||||
foreground: '#FFFFFF',
|
||||
caret: '#FFFFFF',
|
||||
selection: '#3b4252',
|
||||
selectionMatch: '#e5e9f0',
|
||||
gutterBackground: '#2e3440',
|
||||
gutterForeground: '#4c566a',
|
||||
gutterActiveForeground: '#d8dee9',
|
||||
lineHighlight: '#4c566a',
|
||||
},
|
||||
okaidia: {
|
||||
background: '#272822',
|
||||
lineBackground: '#27282250',
|
||||
foreground: '#FFFFFF',
|
||||
caret: '#FFFFFF',
|
||||
selection: '#49483E',
|
||||
selectionMatch: '#49483E',
|
||||
gutterBackground: '#272822',
|
||||
gutterForeground: '#FFFFFF70',
|
||||
lineHighlight: '#00000059',
|
||||
},
|
||||
solarizedLight: {
|
||||
light: true,
|
||||
background: '#fdf6e3',
|
||||
lineBackground: '#fdf6e350',
|
||||
foreground: '#657b83',
|
||||
caret: '#586e75',
|
||||
selection: '#dfd9c8',
|
||||
selectionMatch: '#dfd9c8',
|
||||
gutterBackground: '#00000010',
|
||||
gutterForeground: '#657b83',
|
||||
lineHighlight: '#dfd9c8',
|
||||
},
|
||||
solarizedDark: {
|
||||
background: '#002b36',
|
||||
lineBackground: '#002b3650',
|
||||
foreground: '#93a1a1',
|
||||
caret: '#839496',
|
||||
selection: '#173541',
|
||||
selectionMatch: '#aafe661a',
|
||||
gutterBackground: '#00252f',
|
||||
gutterForeground: '#839496',
|
||||
lineHighlight: '#173541',
|
||||
},
|
||||
sublime: {
|
||||
background: '#303841',
|
||||
lineBackground: '#30384150',
|
||||
foreground: '#FFFFFF',
|
||||
caret: '#FBAC52',
|
||||
selection: '#4C5964',
|
||||
selectionMatch: '#3A546E',
|
||||
gutterBackground: '#303841',
|
||||
gutterForeground: '#FFFFFF70',
|
||||
lineHighlight: '#00000059',
|
||||
},
|
||||
tokyoNightDay: {
|
||||
light: true,
|
||||
background: '#e1e2e7',
|
||||
lineBackground: '#e1e2e750',
|
||||
foreground: '#3760bf',
|
||||
caret: '#3760bf',
|
||||
selection: '#99a7df',
|
||||
selectionMatch: '#99a7df',
|
||||
gutterBackground: '#e1e2e7',
|
||||
gutterForeground: '#3760bf',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#5f5faf11',
|
||||
},
|
||||
tokyoNightStorm: {
|
||||
background: '#24283b',
|
||||
lineBackground: '#24283b50',
|
||||
foreground: '#7982a9',
|
||||
caret: '#c0caf5',
|
||||
selection: '#6f7bb630',
|
||||
selectionMatch: '#1f2335',
|
||||
gutterBackground: '#24283b',
|
||||
gutterForeground: '#7982a9',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#292e42',
|
||||
},
|
||||
tokyoNight: {
|
||||
background: '#1a1b26',
|
||||
lineBackground: '#1a1b2650',
|
||||
foreground: '#787c99',
|
||||
caret: '#c0caf5',
|
||||
selection: '#515c7e40',
|
||||
selectionMatch: '#16161e',
|
||||
gutterBackground: '#1a1b26',
|
||||
gutterForeground: '#787c99',
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: '#1e202e',
|
||||
},
|
||||
vscodeDark: {
|
||||
background: '#1e1e1e',
|
||||
lineBackground: '#1e1e1e50',
|
||||
foreground: '#9cdcfe',
|
||||
caret: '#c6c6c6',
|
||||
selection: '#6199ff2f',
|
||||
selectionMatch: '#72a1ff59',
|
||||
lineHighlight: '#ffffff0f',
|
||||
gutterBackground: '#1e1e1e',
|
||||
gutterForeground: '#838383',
|
||||
gutterActiveForeground: '#fff',
|
||||
fontFamily: 'Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
|
||||
},
|
||||
xcodeLight: {
|
||||
light: true,
|
||||
background: '#fff',
|
||||
lineBackground: '#ffffff50',
|
||||
foreground: '#3D3D3D',
|
||||
selection: '#BBDFFF',
|
||||
selectionMatch: '#BBDFFF',
|
||||
gutterBackground: '#fff',
|
||||
gutterForeground: '#AFAFAF',
|
||||
lineHighlight: '#EDF4FF',
|
||||
},
|
||||
xcodeDark: {
|
||||
background: '#292A30',
|
||||
lineBackground: '#292A3050',
|
||||
foreground: '#CECFD0',
|
||||
caret: '#fff',
|
||||
selection: '#727377',
|
||||
selectionMatch: '#727377',
|
||||
lineHighlight: '#2F3239',
|
||||
},
|
||||
};
|
||||
|
||||
function getColors(str) {
|
||||
const colorRegex = /#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})/g;
|
||||
const colors = [];
|
||||
|
||||
let match;
|
||||
while ((match = colorRegex.exec(str)) !== null) {
|
||||
const color = match[0];
|
||||
if (!colors.includes(color)) {
|
||||
colors.push(color);
|
||||
}
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
export function themeColors(theme) {
|
||||
return getColors(stringifySafe(theme));
|
||||
}
|
||||
|
||||
function getCircularReplacer() {
|
||||
const seen = new WeakSet();
|
||||
return (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.has(value)) {
|
||||
return;
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
function stringifySafe(json) {
|
||||
return JSON.stringify(json, getCircularReplacer());
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
body {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.cm-gutters {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useState } from 'react';
|
||||
import { settings } from './repl/themes.mjs';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
function useTheme() {
|
||||
const [theme, setTheme] = useState(localStorage.getItem('strudel-theme'));
|
||||
useEvent('strudel-theme', (e) => setTheme(e.detail));
|
||||
const themeSettings = settings[theme || 'strudelTheme'];
|
||||
return {
|
||||
theme,
|
||||
setTheme,
|
||||
settings: themeSettings,
|
||||
isDark: !themeSettings.light,
|
||||
isLight: !!themeSettings.light,
|
||||
};
|
||||
}
|
||||
// TODO: dedupe
|
||||
function useEvent(name, onTrigger, useCapture = false) {
|
||||
useEffect(() => {
|
||||
document.addEventListener(name, onTrigger, useCapture);
|
||||
return () => {
|
||||
document.removeEventListener(name, onTrigger, useCapture);
|
||||
};
|
||||
}, [onTrigger]);
|
||||
}
|
||||
|
||||
export default useTheme;
|
||||
+13
-11
@@ -3,6 +3,7 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
|
||||
'../packages/react/src/**/*.{html,js,jsx,md,mdx,ts,tsx}',
|
||||
@@ -10,18 +11,19 @@ module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#c792ea',
|
||||
secondary: '#c3e88d',
|
||||
tertiary: '#82aaff',
|
||||
highlight: '#ffcc00',
|
||||
linegray: '#8a91991a',
|
||||
lineblack: '#00000095',
|
||||
bg: '#222222',
|
||||
// header: '#8a91991a',
|
||||
// footer: '#8a91991a',
|
||||
header: '#00000050',
|
||||
// header: 'transparent',
|
||||
footer: '#00000050',
|
||||
highlight: '#bb8800',
|
||||
// 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)',
|
||||
},
|
||||
typography(theme) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user