Merge pull request #427 from tidalcycles/docs

autocomplete preparations
This commit is contained in:
Felix Roos 2023-02-11 21:02:11 +01:00 committed by GitHub
commit dd51bd0a2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 333 additions and 82 deletions

View File

@ -11,15 +11,6 @@ const generic_params = [
/** /**
* Select a sound / sample by name. * 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 * @name s
* @param {string | Pattern} sound The sound / pattern of sounds to pick * @param {string | Pattern} sound The sound / pattern of sounds to pick
* @example * @example
@ -28,20 +19,36 @@ const generic_params = [
*/ */
['s', 's', 'sound'], ['s', 's', 'sound'],
/** /**
* The note or sample number to choose for a synth or sampleset * Selects the given index from the sample map.
* Note names currently not working yet, but will hopefully soon. Just stick to numbers for now * 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 * @name n
* @param {string | number | Pattern} value note name, note number or sample number * @param {number | Pattern} value sample index starting from 0
* @example * @example
* s('superpiano').n("<0 1 2 3>").osc() * s("bd sd,hh*3").n("<0 1>")
* @example
* s('superpiano').n("<c4 d4 e4 g4>").osc()
* @example
* n("0 1 2 3").s('east').osc()
*/ */
// also see https://github.com/tidalcycles/strudel/pull/63 // 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'], ['f', 'note', 'The note or pitch to play a sound or synth with'],
//['s', 'toArg', 'for internal sound routing'], //['s', 'toArg', 'for internal sound routing'],
// ["f", "from", "for internal sound routing"), // ["f", "from", "for internal sound routing"),
@ -386,6 +393,7 @@ const generic_params = [
* *
* @name detune * @name detune
* @param {number | Pattern} amount between 0 and 1 * @param {number | Pattern} amount between 0 and 1
* @superdirtOnly
* @example * @example
* n("0 3 7").s('superzow').octave(3).detune("<0 .25 .5 1 2>").osc() * 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 * @param {number | Pattern} dry 0 = wet, 1 = dry
* @example * @example
* n("[0,3,7](3,8)").s("superpiano").room(.7).dry("<0 .5 .75 1>").osc() * 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 * @param {number | Pattern} wet between 0 and 1
* @example * @example
* n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc() * n("0,4,7").s("supersquare").leslie("<0 .4 .6 1>").osc()
* @superdirtOnly
* *
*/ */
['f', 'leslie', ''], ['f', 'leslie', ''],
@ -466,6 +476,7 @@ const generic_params = [
* @param {number | Pattern} rate 6.7 for fast, 0.7 for slow * @param {number | Pattern} rate 6.7 for fast, 0.7 for slow
* @example * @example
* n("0,4,7").s("supersquare").leslie(1).lrate("<1 2 4 8>").osc() * 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) // 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 * @param {number | Pattern} meters somewhere between 0 and 1
* @example * @example
* n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc() * n("0,4,7").s("supersquare").leslie(1).lrate(2).lsize("<.1 .5 1>").osc()
* @superdirtOnly
* *
*/ */
['f', 'lsize', ''], ['f', 'lsize', ''],
@ -512,6 +524,7 @@ const generic_params = [
* @param {number | Pattern} octave octave number * @param {number | Pattern} octave octave number
* @example * @example
* n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc() * n("0,4,7").s('supersquare').octave("<3 4 5 6>").osc()
* @superDirtOnly
*/ */
['i', 'octave', ''], ['i', 'octave', ''],
['f', 'offset', ''], // TODO: what is this? not found in tidal doc ['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 * @param {number | string | Pattern} unit see description above
* @example * @example
* speed("1 2 .5 3").s("bd").unit("c").osc() * 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. * @param {number | Pattern} squiz Try passing multiples of 2 to it - 2, 4, 8 etc.
* @example * @example
* squiz("2 4/2 6 [8 16]").s("bd").osc() * squiz("2 4/2 6 [8 16]").s("bd").osc()
* @superdirtOnly
* *
*/ */
['f', 'squiz', ''], ['f', 'squiz', ''],

View File

@ -27,6 +27,7 @@ export class Pattern {
* Create a pattern. As an end user, you will most likely not create a Pattern directly. * 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}. * @param {function} query - The function that maps a {@link State} to an array of {@link Hap}.
* @noAutocomplete
*/ */
constructor(query) { constructor(query) {
this.query = query; this.query = query;
@ -51,6 +52,7 @@ export class Pattern {
/** /**
* see {@link Pattern#withValue} * see {@link Pattern#withValue}
* @noAutocomplete
*/ */
fmap(func) { fmap(func) {
return this.withValue(func); return this.withValue(func);
@ -62,6 +64,7 @@ export class Pattern {
* pattern of functions. * pattern of functions.
* @param {Function} whole_func * @param {Function} whole_func
* @param {Function} func * @param {Function} func
* @noAutocomplete
* @returns Pattern * @returns Pattern
*/ */
appWhole(whole_func, pat_val) { 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 * 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. * intersection. This applies to both the part and the whole timespan.
* @param {Pattern} pat_val * @param {Pattern} pat_val
* @noAutocomplete
* @returns Pattern * @returns Pattern
*/ */
appBoth(pat_val) { appBoth(pat_val) {
@ -117,6 +121,7 @@ export class Pattern {
* are preserved from the pattern of functions (often referred to as the left * are preserved from the pattern of functions (often referred to as the left
* hand or inner pattern). * hand or inner pattern).
* @param {Pattern} pat_val * @param {Pattern} pat_val
* @noAutocomplete
* @returns Pattern * @returns Pattern
*/ */
appLeft(pat_val) { appLeft(pat_val) {
@ -147,6 +152,7 @@ export class Pattern {
* pattern of values, i.e. structure is preserved from the right hand/outer * pattern of values, i.e. structure is preserved from the right hand/outer
* pattern. * pattern.
* @param {Pattern} pat_val * @param {Pattern} pat_val
* @noAutocomplete
* @returns Pattern * @returns Pattern
*/ */
appRight(pat_val) { appRight(pat_val) {
@ -332,6 +338,7 @@ export class Pattern {
* const haps = pattern.queryArc(0, 1) * const haps = pattern.queryArc(0, 1)
* console.log(haps) * console.log(haps)
* silence * silence
* @noAutocomplete
*/ */
queryArc(begin, end) { queryArc(begin, end) {
try { try {
@ -347,6 +354,7 @@ export class Pattern {
* some calculations easier to express, as all haps are then constrained to * some calculations easier to express, as all haps are then constrained to
* happen within a cycle. * happen within a cycle.
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
splitQueries() { splitQueries() {
const pat = this; const pat = this;
@ -361,6 +369,7 @@ export class Pattern {
* timespan before passing it to the original pattern. * timespan before passing it to the original pattern.
* @param {Function} func the function to apply * @param {Function} func the function to apply
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withQuerySpan(func) { withQuerySpan(func) {
return new Pattern((state) => this.query(state.withSpan(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. * begin and end time of the query timespan.
* @param {Function} func the function to apply * @param {Function} func the function to apply
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withQueryTime(func) { withQueryTime(func) {
return new Pattern((state) => this.query(state.withSpan((span) => span.withTime(func)))); return new Pattern((state) => this.query(state.withSpan((span) => span.withTime(func))));
@ -393,6 +403,7 @@ export class Pattern {
* present, `whole` timespans). * present, `whole` timespans).
* @param {Function} func * @param {Function} func
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withHapSpan(func) { withHapSpan(func) {
return new Pattern((state) => this.query(state).map((hap) => hap.withSpan(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. * begin and end time of the hap timespans.
* @param {Function} func the function to apply * @param {Function} func the function to apply
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withHapTime(func) { withHapTime(func) {
return this.withHapSpan((span) => span.withTime(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. * Returns a new pattern with the given function applied to the list of haps returned by every query.
* @param {Function} func * @param {Function} func
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withHaps(func) { withHaps(func) {
return new Pattern((state) => func(this.query(state))); 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. * As with {@link Pattern#withHaps}, but applies the function to every hap, rather than every list of haps.
* @param {Function} func * @param {Function} func
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withHap(func) { withHap(func) {
return this.withHaps((haps) => haps.map(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. * Returns a new pattern with the context field set to every hap set to the given value.
* @param {*} context * @param {*} context
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
setContext(context) { setContext(context) {
return this.withHap((hap) => hap.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. * Returns a new pattern with the given function applied to the context field of every hap.
* @param {Function} func * @param {Function} func
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withContext(func) { withContext(func) {
return this.withHap((hap) => hap.setContext(func(hap.context))); 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 a new pattern with the context field of every hap set to an empty object.
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
stripContext() { stripContext() {
return this.withHap((hap) => hap.setContext({})); return this.withHap((hap) => hap.setContext({}));
@ -458,6 +475,7 @@ export class Pattern {
* @param {Number} start * @param {Number} start
* @param {Number} end * @param {Number} end
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
withLocation(start, end) { withLocation(start, end) {
const location = { const location = {
@ -500,6 +518,7 @@ export class Pattern {
* Returns a new Pattern, which only returns haps that meet the given test. * 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 * @param {Function} hap_test - a function which returns false for haps to be removed from the pattern
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
filterHaps(hap_test) { filterHaps(hap_test) {
return new Pattern((state) => this.query(state).filter(hap_test)); return new Pattern((state) => this.query(state).filter(hap_test));
@ -510,6 +529,7 @@ export class Pattern {
* inside haps. * inside haps.
* @param {Function} value_test * @param {Function} value_test
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
filterValues(value_test) { filterValues(value_test) {
return new Pattern((state) => this.query(state).filter((hap) => value_test(hap.value))); 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 * Returns a new pattern, with haps containing undefined values removed from
* query results. * query results.
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
removeUndefineds() { removeUndefineds() {
return this.filterValues((val) => val != undefined); 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 * with an onset is one with a `whole` timespan that begins at the same time
* as its `part` timespan. * as its `part` timespan.
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
onsetsOnly() { onsetsOnly() {
// Returns a new pattern that will only return haps where the start // 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' * Returns a new pattern, with 'continuous' haps (those without 'whole'
* timespans) removed from query results. * timespans) removed from query results.
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
discreteOnly() { discreteOnly() {
// removes continuous haps that don't have a 'whole' timespan // 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 * Combines adjacent haps with the same value and whole. Only
* intended for use in tests. * intended for use in tests.
* @noAutocomplete
*/ */
defragmentHaps() { defragmentHaps() {
// remove continuous haps // remove continuous haps
@ -604,6 +628,7 @@ export class Pattern {
* @param {Boolean} with_context - set to true, otherwise the context field * @param {Boolean} with_context - set to true, otherwise the context field
* will be stripped from the resulting haps. * will be stripped from the resulting haps.
* @returns [Hap] * @returns [Hap]
* @noAutocomplete
*/ */
firstCycle(with_context = false) { firstCycle(with_context = false) {
var self = this; var self = this;
@ -615,6 +640,7 @@ export class Pattern {
/** /**
* Accessor for a list of values returned by querying the first cycle. * Accessor for a list of values returned by querying the first cycle.
* @noAutocomplete
*/ */
get firstCycleValues() { get firstCycleValues() {
return this.firstCycle().map((hap) => hap.value); return this.firstCycle().map((hap) => hap.value);
@ -622,6 +648,7 @@ export class Pattern {
/** /**
* More human-readable version of the {@link Pattern#firstCycleValues} accessor. * More human-readable version of the {@link Pattern#firstCycleValues} accessor.
* @noAutocomplete
*/ */
get showFirstCycle() { get showFirstCycle() {
return this.firstCycle().map( return this.firstCycle().map(
@ -633,6 +660,7 @@ export class Pattern {
* Returns a new pattern, which returns haps sorted in temporal order. Mainly * Returns a new pattern, which returns haps sorted in temporal order. Mainly
* of use when comparing two patterns for equality, in tests. * of use when comparing two patterns for equality, in tests.
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
sortHapsByPart() { sortHapsByPart() {
return this.withHaps((haps) => return this.withHaps((haps) =>
@ -1103,6 +1131,7 @@ export const silence = new Pattern(() => []);
* @returns {Pattern} * @returns {Pattern}
* @example * @example
* pure('e4') // "e4" * pure('e4') // "e4"
* @noAutocomplete
*/ */
export function pure(value) { export function pure(value) {
function query(state) { function query(state) {
@ -1343,6 +1372,7 @@ export const func = curry((a, b) => reify(b).func(a));
* *
* @param {string} name name of the function * @param {string} name name of the function
* @param {function} func function with 1 or more params, where last is the current pattern * @param {function} func function with 1 or more params, where last is the current pattern
* @noAutocomplete
* *
*/ */
export function register(name, func) { export function register(name, func) {
@ -1448,15 +1478,17 @@ export const ceil = register('ceil', function (pat) {
* Assumes a numerical pattern, containing unipolar values in the range 0 .. * 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 * 1. Returns a new pattern with values scaled to the bipolar range -1 .. 1
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
export const toBipolar = register('toBipolar', function (pat) { export const toBipolar = register('toBipolar', function (pat) {
return pat.fmap((x) => x * 2 - 1); return pat.fmap((x) => x * 2 - 1);
}); });
/** /**
* Assumes a numerical pattern, containing bipolar values in the range -1 .. * Assumes a numerical pattern, containing bipolar values in the range -1 .. 1
* 1. Returns a new pattern with values scaled to the unipolar range 0 .. 1 * Returns a new pattern with values scaled to the unipolar range 0 .. 1
* @returns Pattern * @returns Pattern
* @noAutocomplete
*/ */
export const fromBipolar = register('fromBipolar', function (pat) { export const fromBipolar = register('fromBipolar', function (pat) {
return pat.fmap((x) => (x + 1) / 2); return pat.fmap((x) => (x + 1) / 2);

View File

@ -58,6 +58,7 @@ export const valueToMidi = (value, fallbackValue) => {
/** /**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase * @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/ */
export const getFreq = (noteOrMidi) => { export const getFreq = (noteOrMidi) => {
if (typeof noteOrMidi === 'number') { 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 * @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/ */
export const midi2note = (n) => { export const midi2note = (n) => {
const oct = Math.floor(n / 12) - 1; const oct = Math.floor(n / 12) - 1;

View File

@ -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. * 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 * @name osc
* @memberof Pattern * @memberof Pattern

View File

@ -32,6 +32,7 @@
}, },
"homepage": "https://github.com/tidalcycles/strudel#readme", "homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.4.0",
"@codemirror/lang-javascript": "^6.1.1", "@codemirror/lang-javascript": "^6.1.1",
"@codemirror/state": "^6.2.0", "@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.7.3", "@codemirror/view": "^6.7.3",

View File

@ -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' },
], */
};
};

View File

@ -6,6 +6,8 @@ import { javascript } from '@codemirror/lang-javascript';
import strudelTheme from '../themes/strudel-theme'; import strudelTheme from '../themes/strudel-theme';
import './style.css'; import './style.css';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { autocompletion } from '@codemirror/autocomplete';
import { strudelAutocomplete } from './Autocomplete';
export const setFlash = StateEffect.define(); export const setFlash = StateEffect.define();
const flashField = StateField.define({ const flashField = StateField.define({
@ -80,7 +82,14 @@ const highlightField = StateField.define({
provide: (f) => EditorView.decorations.from(f), provide: (f) => EditorView.decorations.from(f),
}); });
const extensions = [javascript(), 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({ export default function CodeMirror({
value, value,

View File

@ -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}. * 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 * 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.
* - T = Tonic * Note that you currently cannot pattern `scale` with the mini notation, because the scale name includes a space.
* - O = Octave (optional, defaults to 3) * This will be improved in the future. Until then, use a sequence function like `cat` or `seq`.
* - N = Name of scale, available names can be found [here](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
* *
* @memberof Pattern * @memberof Pattern
* @name scale * @name scale
* @param {string} scale Name of scale * @param {string} scale Name of scale
* @returns Pattern * @returns Pattern
* @example * @example
* "0 2 4 6 4 2".scale('C2 major').note()
* @example
* "0 2 4 6 4 2" * "0 2 4 6 4 2"
* .scale(seq('C2 major', 'C2 minor').slow(2)) * .scale(seq('C2 major', 'C2 minor').slow(2))
* .note() * .note()

View File

@ -111,7 +111,9 @@ let sampleCache = { current: undefined };
/** /**
* Loads a collection of samples to use with `s` * Loads a collection of samples to use with `s`
* * @example
* samples('github:tidalcycles/Dirt-Samples/master');
* s("[bd ~]*2, [~ hh]*2, ~ sd")
* @example * @example
* samples({ * samples({
* bd: '808bd/BD0000.WAV', * bd: '808bd/BD0000.WAV',

View File

@ -59,6 +59,7 @@ export function loadWebDirt(config) {
* @returns Pattern * @returns Pattern
* @example * @example
* s("bd*2 hh sd hh").n("<0 1>").webdirt() * s("bd*2 hh sd hh").n("<0 1>").webdirt()
* @noAutocomplete
*/ */
Pattern.prototype.webdirt = function () { Pattern.prototype.webdirt = function () {
// create a WebDirt object and initialize Web Audio context // create a WebDirt object and initialize Web Audio context

13
pnpm-lock.yaml generated
View File

@ -159,6 +159,7 @@ importers:
packages/react: packages/react:
specifiers: specifiers:
'@codemirror/autocomplete': ^6.4.0
'@codemirror/lang-javascript': ^6.1.1 '@codemirror/lang-javascript': ^6.1.1
'@codemirror/state': ^6.2.0 '@codemirror/state': ^6.2.0
'@codemirror/view': ^6.7.3 '@codemirror/view': ^6.7.3
@ -179,6 +180,7 @@ importers:
tailwindcss: ^3.0.24 tailwindcss: ^3.0.24
vite: ^3.2.2 vite: ^3.2.2
dependencies: dependencies:
'@codemirror/autocomplete': 6.4.0_a4vbhepr4qhxm5cldqd4jpyase
'@codemirror/lang-javascript': 6.1.2 '@codemirror/lang-javascript': 6.1.2
'@codemirror/state': 6.2.0 '@codemirror/state': 6.2.0
'@codemirror/view': 6.7.3 '@codemirror/view': 6.7.3
@ -187,7 +189,7 @@ importers:
'@strudel.cycles/transpiler': link:../transpiler '@strudel.cycles/transpiler': link:../transpiler
'@strudel.cycles/webaudio': link:../webaudio '@strudel.cycles/webaudio': link:../webaudio
'@uiw/codemirror-themes': 4.19.7_a4vbhepr4qhxm5cldqd4jpyase '@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 react-hook-inview: 4.5.0_sfoxds7t5ydpegc3knd667wn6m
devDependencies: devDependencies:
'@types/react': 17.0.53 '@types/react': 17.0.53
@ -3981,9 +3983,10 @@ packages:
eslint-visitor-keys: 3.3.0 eslint-visitor-keys: 3.3.0
dev: false 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==} resolution: {integrity: sha512-pk0JTFJAZOGxouBB1pdBtb59qKibO9DW4hER4VSFGBGW3TBDeXUwL/gKujhRpySHG2MtG09cgt4a3XOFIWwXTw==}
peerDependencies: peerDependencies:
'@codemirror/autocomplete': '>=6.0.0'
'@codemirror/state': '>=6.0.0' '@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0' '@codemirror/view': '>=6.0.0'
dependencies: dependencies:
@ -4182,7 +4185,7 @@ packages:
'@codemirror/view': 6.7.3 '@codemirror/view': 6.7.3
dev: false dev: false
/@uiw/react-codemirror/4.19.7_b6o5qp6ml4k7skggixohr5abde: /@uiw/react-codemirror/4.19.7_r3x7zzmc35ug7i3c2vv4bf5iey:
resolution: {integrity: sha512-IHvpYWVSdiaHX0Fk6oY6YyAJigDnyvSpWKNUTRzsMNxB+8/wqZ8lior4TprXH0zyLxW5F1+bTyifFFTeg+X3Sw==} resolution: {integrity: sha512-IHvpYWVSdiaHX0Fk6oY6YyAJigDnyvSpWKNUTRzsMNxB+8/wqZ8lior4TprXH0zyLxW5F1+bTyifFFTeg+X3Sw==}
peerDependencies: peerDependencies:
'@codemirror/state': '>=6.0.0' '@codemirror/state': '>=6.0.0'
@ -4195,10 +4198,12 @@ packages:
'@codemirror/state': 6.2.0 '@codemirror/state': 6.2.0
'@codemirror/theme-one-dark': 6.1.0 '@codemirror/theme-one-dark': 6.1.0
'@codemirror/view': 6.7.3 '@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 codemirror: 6.0.1
react: 17.0.2 react: 17.0.2
react-dom: 17.0.2_react@17.0.2 react-dom: 17.0.2_react@17.0.2
transitivePeerDependencies:
- '@codemirror/autocomplete'
dev: false dev: false
/@vite-pwa/astro/0.0.1: /@vite-pwa/astro/0.0.1:

View File

@ -2506,40 +2506,26 @@ exports[`runs examples > example "mul" example index 0 1`] = `
exports[`runs examples > example "n" example index 0 1`] = ` exports[`runs examples > example "n" example index 0 1`] = `
[ [
"[ 0/1 → 1/1 | s:superpiano n:0 ]", "[ 0/1 → 1/2 | s:bd n:0 ]",
"[ 1/1 → 2/1 | s:superpiano n:1 ]", "[ 1/2 → 1/1 | s:sd n:0 ]",
"[ 2/1 → 3/1 | s:superpiano n:2 ]", "[ 0/1 → 1/3 | s:hh n:0 ]",
"[ 3/1 → 4/1 | s:superpiano n:3 ]", "[ 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 ]",
exports[`runs examples > example "n" example index 1 1`] = ` "[ 1/1 → 4/3 | s:hh n:1 ]",
[ "[ 4/3 → 5/3 | s:hh n:1 ]",
"[ 0/1 → 1/1 | s:superpiano n:c4 ]", "[ 5/3 → 2/1 | s:hh n:1 ]",
"[ 1/1 → 2/1 | s:superpiano n:d4 ]", "[ 2/1 → 5/2 | s:bd n:0 ]",
"[ 2/1 → 3/1 | s:superpiano n:e4 ]", "[ 5/2 → 3/1 | s:sd n:0 ]",
"[ 3/1 → 4/1 | s:superpiano n:g4 ]", "[ 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 ]",
exports[`runs examples > example "n" example index 2 1`] = ` "[ 7/2 → 4/1 | s:sd n:1 ]",
[ "[ 3/1 → 10/3 | s:hh n:1 ]",
"[ 0/1 → 1/4 | n:0 s:east ]", "[ 10/3 → 11/3 | s:hh n:1 ]",
"[ 1/4 → 1/2 | n:1 s:east ]", "[ 11/3 → 4/1 | s:hh n:1 ]",
"[ 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 ]",
] ]
`; `;
@ -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`] = ` exports[`runs examples > example "octave" example index 0 1`] = `
[ [
"[ 0/1 → 1/1 | n:0 s:supersquare octave:3 ]", "[ 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`] = ` exports[`runs examples > example "saw" example index 0 1`] = `
[ [
"[ 0/1 → 1/32 | note:c3 ]", "[ 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`] = ` 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 ]", "[ 0/1 → 1/6 | note:C2 ]",
"[ 1/6 → 1/3 | note:E2 ]", "[ 1/6 → 1/3 | note:E2 ]",

View File

@ -65,16 +65,17 @@ export const SIDEBAR: Sidebar = {
{ text: 'Tonal Modifiers', link: 'learn/tonal' }, { text: 'Tonal Modifiers', link: 'learn/tonal' },
], ],
More: [ More: [
{ text: 'MIDI & OSC', link: 'learn/input-output' },
{ text: 'Offline', link: 'learn/pwa' },
{ text: 'Patterns', link: 'technical-manual/patterns' }, { text: 'Patterns', link: 'technical-manual/patterns' },
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' }, { text: 'Pattern Alignment', link: 'technical-manual/alignment' },
{ text: 'MIDI & OSC', link: 'learn/input-output' },
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' }, { text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
], ],
Development: [ Development: [
{ text: 'REPL', link: 'technical-manual/repl' }, { text: 'REPL', link: 'technical-manual/repl' },
{ text: 'Docs', link: 'technical-manual/docs' }, { text: 'Docs', link: 'technical-manual/docs' },
{ text: 'Testing', link: 'technical-manual/testing' }, { text: 'Testing', link: 'technical-manual/testing' },
{ text: 'Packages', link: 'technical-manual/packages' }, // { text: 'Packages', link: 'technical-manual/packages' },
// { text: 'Internals', link: 'technical-manual/internals' }, // { text: 'Internals', link: 'technical-manual/internals' },
], ],
}, },

View File

@ -1,5 +1,5 @@
--- ---
title: Introduction title: JavaScript API
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Accumulation Modifiers title: Accumulation Modifiers
description: Strudel Tutorial - Coding syntax
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Coding syntax title: Coding syntax
description: Strudel Tutorial - Coding syntax
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Audio effects title: Audio effects
description: Strudel Tutorial - Audio effects
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Pattern Constructors title: Pattern Constructors
description: Strudel Tutorial
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Getting Started title: Getting Started
description: Strudel Tutorial - Getting Started
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Other Outptuts title: MIDI & OSC
description: Strudel Tutorial
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Notes title: Notes
description: Strudel Tutorial - Notes
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

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

View File

@ -1,6 +1,5 @@
--- ---
title: Notes title: Samples
description: Strudel Tutorial - Notes
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: What is Strudel? title: Signals
description: Strudel Tutorial
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Sounds title: Sounds
description: Strudel Tutorial - Sounds
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -1,6 +1,5 @@
--- ---
title: Synths title: Synths
description: Strudel Tutorial - Synths
layout: ../../layouts/MainLayout.astro layout: ../../layouts/MainLayout.astro
--- ---

View File

@ -15,6 +15,9 @@
padding-left: 8px !important; padding-left: 8px !important;
} }
.cm-scroller {
padding-bottom: 50vh;
}
.cm-line > * { .cm-line > * {
background: var(--lineBackground); background: var(--lineBackground);
} }