diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index ad521112..4e230240 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -11,15 +11,6 @@ const generic_params = [ /** * Select a sound / sample by name. * - *
- * show all sounds - * - * 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) - * - * more info - * - *
- * * @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("").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', ''], diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index dd02e529..b9c9e24c 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -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) => @@ -1103,6 +1131,7 @@ export const silence = new Pattern(() => []); * @returns {Pattern} * @example * pure('e4') // "e4" + * @noAutocomplete */ export function pure(value) { 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 {function} func function with 1 or more params, where last is the current pattern + * @noAutocomplete * */ 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 .. * 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); diff --git a/packages/core/util.mjs b/packages/core/util.mjs index 19ff7c12..3127e0d1 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -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; diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index 15909203..234bdfce 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -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 diff --git a/packages/react/package.json b/packages/react/package.json index 886da009..adfdc9bf 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -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", diff --git a/packages/react/src/components/Autocomplete.jsx b/packages/react/src/components/Autocomplete.jsx new file mode 100644 index 00000000..7ec81d2d --- /dev/null +++ b/packages/react/src/components/Autocomplete.jsx @@ -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 ( +
+

{getDocLabel(doc)}

+
+
    + {doc.params?.map(({ name, type, description }, i) => ( +
  • + {name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)} : ''} +
  • + ))} +
+
+ {doc.examples?.map((example, i) => ( +
+
 {
+                console.log('ola!');
+                navigator.clipboard.writeText(example);
+                e.stopPropagation();
+              }}
+            >
+              {example}
+            
+
+ ))} +
+
+ ); +} + +const jsdocCompletions = jsdoc.docs + .filter( + (doc) => + getDocLabel(doc) && + !getDocLabel(doc).startsWith('_') && + !['package'].includes(doc.kind) && + !['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)), + ) + // https://codemirror.net/docs/ref/#autocomplete.Completion + .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(); + return node; + }, + type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type + })); + +export const strudelAutocomplete = (context /* : CompletionContext */) => { + let word = context.matchBefore(/\w*/); + if (word.from == word.to && !context.explicit) return null; + return { + from: word.from, + options: jsdocCompletions, + /* options: [ + { label: 'match', type: 'keyword' }, + { label: 'hello', type: 'variable', info: '(World)' }, + { label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' }, + ], */ + }; +}; diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx index eb02004a..06b95de0 100644 --- a/packages/react/src/components/CodeMirror6.jsx +++ b/packages/react/src/components/CodeMirror6.jsx @@ -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({ @@ -80,7 +82,14 @@ const highlightField = StateField.define({ 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({ value, diff --git a/packages/tonal/tonal.mjs b/packages/tonal/tonal.mjs index 9e47d4a2..206ea840 100644 --- a/packages/tonal/tonal.mjs +++ b/packages/tonal/tonal.mjs @@ -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() diff --git a/packages/webaudio/sampler.mjs b/packages/webaudio/sampler.mjs index f94b6b5a..40a69376 100644 --- a/packages/webaudio/sampler.mjs +++ b/packages/webaudio/sampler.mjs @@ -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', diff --git a/packages/webdirt/webdirt.mjs b/packages/webdirt/webdirt.mjs index 500d7373..6ae2ff9f 100644 --- a/packages/webdirt/webdirt.mjs +++ b/packages/webdirt/webdirt.mjs @@ -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 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c7090c2..f61104a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 @@ -3981,9 +3983,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: @@ -4182,7 +4185,7 @@ packages: '@codemirror/view': 6.7.3 dev: false - /@uiw/react-codemirror/4.19.7_b6o5qp6ml4k7skggixohr5abde: + /@uiw/react-codemirror/4.19.7_r3x7zzmc35ug7i3c2vv4bf5iey: resolution: {integrity: sha512-IHvpYWVSdiaHX0Fk6oY6YyAJigDnyvSpWKNUTRzsMNxB+8/wqZ8lior4TprXH0zyLxW5F1+bTyifFFTeg+X3Sw==} peerDependencies: '@codemirror/state': '>=6.0.0' @@ -4195,10 +4198,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: diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index e52a5932..e061d105 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -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 ]", diff --git a/website/src/config.ts b/website/src/config.ts index 6fe76052..26273dba 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -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' }, ], }, diff --git a/website/src/pages/functions/intro.mdx b/website/src/pages/functions/intro.mdx index bed3cb37..6bbd26b9 100644 --- a/website/src/pages/functions/intro.mdx +++ b/website/src/pages/functions/intro.mdx @@ -1,5 +1,5 @@ --- -title: Introduction +title: JavaScript API layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/accumulation.mdx b/website/src/pages/learn/accumulation.mdx index 71761387..c442e87b 100644 --- a/website/src/pages/learn/accumulation.mdx +++ b/website/src/pages/learn/accumulation.mdx @@ -1,6 +1,5 @@ --- title: Accumulation Modifiers -description: Strudel Tutorial - Coding syntax layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/code.mdx b/website/src/pages/learn/code.mdx index 7be95ed3..d10e1f64 100644 --- a/website/src/pages/learn/code.mdx +++ b/website/src/pages/learn/code.mdx @@ -1,6 +1,5 @@ --- title: Coding syntax -description: Strudel Tutorial - Coding syntax layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index afe1c63c..7fc0a77c 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -1,6 +1,5 @@ --- title: Audio effects -description: Strudel Tutorial - Audio effects layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/factories.mdx b/website/src/pages/learn/factories.mdx index 7a7256ec..86598cf2 100644 --- a/website/src/pages/learn/factories.mdx +++ b/website/src/pages/learn/factories.mdx @@ -1,6 +1,5 @@ --- title: Pattern Constructors -description: Strudel Tutorial layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/getting-started.mdx b/website/src/pages/learn/getting-started.mdx index 3f2868f1..3f41c859 100644 --- a/website/src/pages/learn/getting-started.mdx +++ b/website/src/pages/learn/getting-started.mdx @@ -1,6 +1,5 @@ --- title: Getting Started -description: Strudel Tutorial - Getting Started layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/input-output.mdx b/website/src/pages/learn/input-output.mdx index b7766719..68233159 100644 --- a/website/src/pages/learn/input-output.mdx +++ b/website/src/pages/learn/input-output.mdx @@ -1,6 +1,5 @@ --- -title: Other Outptuts -description: Strudel Tutorial +title: MIDI & OSC layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/notes.mdx b/website/src/pages/learn/notes.mdx index e2d2c7bb..42c54174 100644 --- a/website/src/pages/learn/notes.mdx +++ b/website/src/pages/learn/notes.mdx @@ -1,6 +1,5 @@ --- title: Notes -description: Strudel Tutorial - Notes layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/pwa.mdx b/website/src/pages/learn/pwa.mdx new file mode 100644 index 00000000..ab2c68f3 --- /dev/null +++ b/website/src/pages/learn/pwa.mdx @@ -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. diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index 5ad0c0e0..4720e5fa 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -1,6 +1,5 @@ --- -title: Notes -description: Strudel Tutorial - Notes +title: Samples layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/signals.mdx b/website/src/pages/learn/signals.mdx index 0cf0f58e..79f255b0 100644 --- a/website/src/pages/learn/signals.mdx +++ b/website/src/pages/learn/signals.mdx @@ -1,6 +1,5 @@ --- -title: What is Strudel? -description: Strudel Tutorial +title: Signals layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/sounds.mdx b/website/src/pages/learn/sounds.mdx index f80f51be..a17c2d0c 100644 --- a/website/src/pages/learn/sounds.mdx +++ b/website/src/pages/learn/sounds.mdx @@ -1,6 +1,5 @@ --- title: Sounds -description: Strudel Tutorial - Sounds layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 69c45394..9b5b1c28 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -1,6 +1,5 @@ --- title: Synths -description: Strudel Tutorial - Synths layout: ../../layouts/MainLayout.astro --- diff --git a/website/src/repl/Repl.css b/website/src/repl/Repl.css index 0c5cfecc..124fe4e4 100644 --- a/website/src/repl/Repl.css +++ b/website/src/repl/Repl.css @@ -15,6 +15,9 @@ padding-left: 8px !important; } +.cm-scroller { + padding-bottom: 50vh; +} .cm-line > * { background: var(--lineBackground); }