Merge pull request #43 from Bubobubobubobubo/autocompletion
Autocompletion
This commit is contained in:
@ -230,6 +230,10 @@
|
||||
<div class="flex items-center mb-4 ml-8">
|
||||
<input id="show-time-position" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||||
<label for="default-checkbox" class="ml-2 text-sm font-medium text-dark">Show Time Position</label>
|
||||
</div>
|
||||
<div class="flex items-center mb-4 ml-8">
|
||||
<input id="show-tips" type="checkbox" value="" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||||
<label for="default-checkbox" class="ml-2 text-sm font-medium text-dark">Show Hovering Tips</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Information card -->
|
||||
|
||||
15
src/API.ts
15
src/API.ts
@ -1072,7 +1072,7 @@ export class UserAPI {
|
||||
integral_part = integral_part == 0 ? this.nominator() : integral_part;
|
||||
let decimal_part = Math.floor((beat - integral_part) * this.ppqn() + 1);
|
||||
// This was once revelead to me in a dream
|
||||
if (decimal_part <= 0)
|
||||
if (decimal_part <= 0)
|
||||
decimal_part = decimal_part + this.ppqn() * this.nominator();
|
||||
final_pulses.push(
|
||||
integral_part === this.beat() && this.pulse() === decimal_part
|
||||
@ -1219,6 +1219,19 @@ export class UserAPI {
|
||||
return tobin[iterator % tobin.length];
|
||||
};
|
||||
|
||||
public binrhythm = (div: number, n: number): boolean => {
|
||||
/**
|
||||
* Returns a binary cycle of size n, divided by div.
|
||||
*
|
||||
* @param div - The divisor of the binary cycle
|
||||
* @param n - The number to convert to binary
|
||||
* @returns boolean value based on the binary sequence
|
||||
*/
|
||||
let convert: string = n.toString(2);
|
||||
let tobin: boolean[] = convert.split("").map((x: string) => x === "1");
|
||||
return this.mod(div) && tobin.div(div);
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Low Frequency Oscillators
|
||||
// =============================================================
|
||||
|
||||
@ -42,6 +42,8 @@ export interface Settings {
|
||||
* @param universes - The set universes to use (e.g. saved files)
|
||||
* @param selected_universe - The name of the selected universe
|
||||
* @param line_numbers - Whether or not to show line numbers
|
||||
* @param time_position - Whether or not to show time position
|
||||
* @param tips - Whether or not to show tips
|
||||
*/
|
||||
vimMode: boolean;
|
||||
theme: string;
|
||||
@ -50,7 +52,8 @@ export interface Settings {
|
||||
universes: Universes;
|
||||
selected_universe: string;
|
||||
line_numbers: boolean;
|
||||
time_position: boolean;
|
||||
time_position: boolean;
|
||||
tips: boolean;
|
||||
}
|
||||
|
||||
export const template_universe = {
|
||||
@ -105,6 +108,9 @@ export class AppSettings {
|
||||
* @param universes - The set universes to use (e.g. saved files)
|
||||
* @param selected_universe - The name of the selected universe
|
||||
* @param line_numbers - Whether or not to show line numbers
|
||||
* @param time_position - Whether or not to show time position
|
||||
* @param tips - Whether or not to show tips
|
||||
|
||||
*/
|
||||
|
||||
public vimMode: boolean = false;
|
||||
@ -114,7 +120,8 @@ export class AppSettings {
|
||||
public universes: Universes;
|
||||
public selected_universe: string = "Default";
|
||||
public line_numbers: boolean = true;
|
||||
public time_position: boolean = true;
|
||||
public time_position: boolean = true;
|
||||
public tips: boolean = true;
|
||||
|
||||
constructor() {
|
||||
const settingsFromStorage = JSON.parse(
|
||||
@ -131,6 +138,7 @@ export class AppSettings {
|
||||
this.selected_universe = settingsFromStorage.selected_universe;
|
||||
this.line_numbers = settingsFromStorage.line_numbers;
|
||||
this.time_position = settingsFromStorage.time_position;
|
||||
this.tips = settingsFromStorage.tips;
|
||||
} else {
|
||||
this.universes = template_universes;
|
||||
}
|
||||
@ -152,7 +160,8 @@ export class AppSettings {
|
||||
universes: this.universes,
|
||||
selected_universe: this.selected_universe,
|
||||
line_numbers: this.line_numbers,
|
||||
time_position: this.time_position
|
||||
time_position: this.time_position,
|
||||
tips: this.tips,
|
||||
};
|
||||
}
|
||||
|
||||
@ -173,6 +182,7 @@ export class AppSettings {
|
||||
this.selected_universe = settings.selected_universe;
|
||||
this.line_numbers = settings.line_numbers;
|
||||
this.time_position = settings.time_position;
|
||||
this.tips = settings.tips;
|
||||
localStorage.setItem("topos", JSON.stringify(this.data));
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@ import {
|
||||
drawSelection,
|
||||
highlightActiveLine,
|
||||
dropCursor,
|
||||
rectangularSelection,
|
||||
crosshairCursor,
|
||||
// rectangularSelection,
|
||||
// crosshairCursor,
|
||||
highlightActiveLineGutter,
|
||||
} from "@codemirror/view";
|
||||
import { Extension, EditorState } from "@codemirror/state";
|
||||
@ -24,45 +24,6 @@ import {
|
||||
} from "@codemirror/autocomplete";
|
||||
import { lintKeymap } from "@codemirror/lint";
|
||||
|
||||
// (The superfluous function calls around the list of extensions work
|
||||
// around current limitations in tree-shaking software.)
|
||||
|
||||
/// This is an extension value that just pulls together a number of
|
||||
/// extensions that you might want in a basic editor. It is meant as a
|
||||
/// convenient helper to quickly set up CodeMirror without installing
|
||||
/// and importing a lot of separate packages.
|
||||
///
|
||||
/// Specifically, it includes...
|
||||
///
|
||||
/// - [the default command bindings](#commands.defaultKeymap)
|
||||
/// - [line numbers](#view.lineNumbers)
|
||||
/// - [special character highlighting](#view.highlightSpecialChars)
|
||||
/// - [the undo history](#commands.history)
|
||||
/// - [a fold gutter](#language.foldGutter)
|
||||
/// - [custom selection drawing](#view.drawSelection)
|
||||
/// - [drop cursor](#view.dropCursor)
|
||||
/// - [multiple selections](#state.EditorState^allowMultipleSelections)
|
||||
/// - [reindentation on input](#language.indentOnInput)
|
||||
/// - [the default highlight style](#language.defaultHighlightStyle) (as fallback)
|
||||
/// - [bracket matching](#language.bracketMatching)
|
||||
/// - [bracket closing](#autocomplete.closeBrackets)
|
||||
/// - [autocompletion](#autocomplete.autocompletion)
|
||||
/// - [rectangular selection](#view.rectangularSelection) and [crosshair cursor](#view.crosshairCursor)
|
||||
/// - [active line highlighting](#view.highlightActiveLine)
|
||||
/// - [active line gutter highlighting](#view.highlightActiveLineGutter)
|
||||
/// - [selection match highlighting](#search.highlightSelectionMatches)
|
||||
/// - [search](#search.searchKeymap)
|
||||
/// - [linting](#lint.lintKeymap)
|
||||
///
|
||||
/// (You'll probably want to add some language package to your setup
|
||||
/// too.)
|
||||
///
|
||||
/// This extension does not allow customization. The idea is that,
|
||||
/// once you decide you want to configure your editor more precisely,
|
||||
/// you take this package's source (which is just a bunch of imports
|
||||
/// and an array literal), copy it into your own code, and adjust it
|
||||
/// as desired.
|
||||
|
||||
export const editorSetup: Extension = (() => [
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
@ -75,8 +36,8 @@ export const editorSetup: Extension = (() => [
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
autocompletion(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
// rectangularSelection(),
|
||||
// crosshairCursor(),
|
||||
highlightActiveLine(),
|
||||
highlightSelectionMatches(),
|
||||
keymap.of([
|
||||
|
||||
681
src/documentation/inlineHelp.ts
Normal file
681
src/documentation/inlineHelp.ts
Normal file
@ -0,0 +1,681 @@
|
||||
import { hoverTooltip } from "@codemirror/view";
|
||||
import { type EditorView } from "@codemirror/view";
|
||||
|
||||
interface InlineCompletion {
|
||||
name: string;
|
||||
category: string;
|
||||
description: string;
|
||||
example: string;
|
||||
}
|
||||
|
||||
type CompletionDatabase = {
|
||||
[key: string]: InlineCompletion;
|
||||
};
|
||||
|
||||
const completionDatabase: CompletionDatabase = {
|
||||
attack: {
|
||||
name: "attack",
|
||||
category: "synthesis",
|
||||
description: "ADSR envelope attack time (in seconds)",
|
||||
example: "sound('sawtooth').attack(.5).out()",
|
||||
},
|
||||
decay: {
|
||||
name: "decay",
|
||||
category: "synthesis",
|
||||
description: "ADSR envelope decay time (in seconds)",
|
||||
example: "sound('sawtooth').decay(.5).out()",
|
||||
},
|
||||
sustain: {
|
||||
name: "sustain",
|
||||
category: "synthesis",
|
||||
description: "ADSR envelope sustain level (0-1)",
|
||||
example: "sound('sawtooth').sustain(.5).out()",
|
||||
},
|
||||
release: {
|
||||
name: "release",
|
||||
category: "synthesis",
|
||||
description: "ADSR envelope release time (in seconds)",
|
||||
example: "sound('sawtooth').release(.5).out()",
|
||||
},
|
||||
fmwave: {
|
||||
name: "fmwave",
|
||||
category: "synthesis",
|
||||
description: "FM synth modulator waveform",
|
||||
example: "sound('fm').fmwave('sine').out()",
|
||||
},
|
||||
fmi: {
|
||||
name: "fmi",
|
||||
category: "audio",
|
||||
description: "FM synth modulator index",
|
||||
example: "sound('fm').fmi([1,2].beat()).out()",
|
||||
},
|
||||
fmh: {
|
||||
name: "fmh",
|
||||
category: "audio",
|
||||
description: "FM synth modulator ratio",
|
||||
example: "sound('fm').fmi(2).fmh(2).out()",
|
||||
},
|
||||
fmattack: {
|
||||
name: "fmattack",
|
||||
category: "synthesis",
|
||||
description: "FM synth modulator ADSR envelope attack time (in seconds)",
|
||||
example: "sound('sine').fmi(2).fmattack(.5).out()",
|
||||
},
|
||||
fmdecay: {
|
||||
name: "fmdecay",
|
||||
category: "synthesis",
|
||||
description: "FM synth modulator ADSR envelope decay time (in seconds)",
|
||||
example: "sound('sine').fmi(2).fmdecay(.5).out()",
|
||||
},
|
||||
fmsustain: {
|
||||
name: "fmsustain",
|
||||
category: "synthesis",
|
||||
description: "FM synth modulator ADSR envelope sustain level (0-1)",
|
||||
example: "sound('sine').fmi(2).fmsustain(.5).out()",
|
||||
},
|
||||
fmrelease: {
|
||||
name: "fmrelease",
|
||||
category: "synthesis",
|
||||
description: "FM synth modulator ADSR envelope release time (in seconds)",
|
||||
example: "sound('sine').fmi(2).fmrelease(.5).out()",
|
||||
},
|
||||
repeatAll: {
|
||||
name: "repeatAll",
|
||||
category: "patterns",
|
||||
description: "Repeat every array elements <i>n</i> times",
|
||||
example: "[0,1,2,3].repeatAll(2)",
|
||||
},
|
||||
quant: {
|
||||
name: "quant",
|
||||
category: "functions",
|
||||
description: "Quantize a value in the given array",
|
||||
example: "quant(30, [0,1,2,3])",
|
||||
},
|
||||
log: {
|
||||
name: "log",
|
||||
category: "javascript",
|
||||
description: "Log a value in the console",
|
||||
example: "log('Hello, world')",
|
||||
},
|
||||
div: {
|
||||
name: "div",
|
||||
category: "patterns",
|
||||
description:
|
||||
"Returns next value every <i>n</i> beats or true and false alternatively",
|
||||
example: "div(4, 50) // 2 beats of true, 2 beats of false, 50/50.",
|
||||
},
|
||||
n: {
|
||||
name: "n",
|
||||
category: "audio",
|
||||
description: "Sample number or synth oscillator partials count",
|
||||
example: "sound('dr').n([1,2].beat()).out()",
|
||||
},
|
||||
note: {
|
||||
name: "note",
|
||||
category: "patterns",
|
||||
description: "MIDI note number (0-127)",
|
||||
example: "sound('jvbass').note(50).out()",
|
||||
},
|
||||
vel: {
|
||||
name: "vel",
|
||||
category: "audio",
|
||||
description: "Velocity or sound volume (0-1)",
|
||||
example: "sound('cp').vel(.5).out()",
|
||||
},
|
||||
palindrome: {
|
||||
name: "palindrome",
|
||||
category: "patterns",
|
||||
description: "Returns palindrome of the current array",
|
||||
example: "[0,1,2,3].palindrome()",
|
||||
},
|
||||
cutoff: {
|
||||
name: "cutoff",
|
||||
category: "filter",
|
||||
description: "Lowpass filter cutoff frequency",
|
||||
example: "sound('cp').cutoff(1000).out()",
|
||||
},
|
||||
resonance: {
|
||||
name: "resonance",
|
||||
category: "filter",
|
||||
description: "Lowpass filter resonance",
|
||||
example: "sound('cp').resonance(1).out()",
|
||||
},
|
||||
hcutoff: {
|
||||
name: "hcutoff",
|
||||
category: "filter",
|
||||
description: "Highpass filter cutoff frequency",
|
||||
example: "sound('cp').hcutoff(1000).out()",
|
||||
},
|
||||
hresonance: {
|
||||
name: "hresonance",
|
||||
category: "filter",
|
||||
description: "Highpass filter resonance",
|
||||
example: "sound('cp').hresonance(1).out()",
|
||||
},
|
||||
bandf: {
|
||||
name: "bandf",
|
||||
category: "filter",
|
||||
description: "Bandpass filter cutoff frequency",
|
||||
example: "sound('cp').bandf(1000).out()",
|
||||
},
|
||||
bandq: {
|
||||
name: "bandq",
|
||||
category: "filter",
|
||||
description: "Bandpass filter resonance",
|
||||
example: "sound('cp').bandq(1).out()",
|
||||
},
|
||||
vowel: {
|
||||
name: "vowel",
|
||||
category: "filter",
|
||||
description: "Vowel filter type",
|
||||
example: "sound('cp').vowel('a').out()",
|
||||
},
|
||||
coarse: {
|
||||
name: "coarse",
|
||||
category: "synthesis",
|
||||
description: "Artificial sample-rate lowering",
|
||||
example: "mod(.5)::snd('pad').coarse($(1) % 16).clip(.5).out();",
|
||||
},
|
||||
crush: {
|
||||
name: "crush",
|
||||
category: "synthesis",
|
||||
description:
|
||||
"Bitcrushing effect. <i>1</i> is extreme, superior values are more subtle.",
|
||||
example: "",
|
||||
},
|
||||
speed: {
|
||||
name: "speed",
|
||||
category: "sampling",
|
||||
description: "Sample playback speed",
|
||||
example: "sound('cp').speed(.5).out()",
|
||||
},
|
||||
shape: {
|
||||
name: "shape",
|
||||
category: "synthesis",
|
||||
description: "Waveshaping distorsion",
|
||||
example: "sound('cp').shape(.5).out()",
|
||||
},
|
||||
delay: {
|
||||
name: "delay",
|
||||
category: "effect",
|
||||
description: "Delay effect dry/wet",
|
||||
example: "sound('cp').delay(.5).out()",
|
||||
},
|
||||
delayfb: {
|
||||
name: "delayfb",
|
||||
category: "effect",
|
||||
description: "Delay effect feedback amount (0-1)",
|
||||
example: "sound('cp').delay(0.2).delayfb(.5).out()",
|
||||
},
|
||||
delaytime: {
|
||||
name: "delaytime",
|
||||
category: "effect",
|
||||
description: "Delay effect delay time (in seconds)",
|
||||
example: "sound('cp').delay(0.2).delaytime(.5).out()",
|
||||
},
|
||||
gain: {
|
||||
name: "gain",
|
||||
category: "audio",
|
||||
description: "Playback volume",
|
||||
example: "sound('cp').gain(.5).out()",
|
||||
},
|
||||
bar: {
|
||||
name: "bar",
|
||||
category: "patterns",
|
||||
description: "Returns list index for the current bar (with wrapping)",
|
||||
example: "[0,1,2,3].bar()",
|
||||
},
|
||||
beat: {
|
||||
name: "beat",
|
||||
category: "patterns",
|
||||
description: "Returns list index for the current beat (with wrapping)",
|
||||
example: "[0,1,2,3].beat()",
|
||||
},
|
||||
room: {
|
||||
name: "room",
|
||||
category: "effect",
|
||||
description: "Reverb effect room amount",
|
||||
example: "sound('cp').room(.5).out()",
|
||||
},
|
||||
size: {
|
||||
name: "size",
|
||||
category: "effect",
|
||||
description: "Reverb effect room size",
|
||||
example: "sound('cp').size(.5).out()",
|
||||
},
|
||||
usine: {
|
||||
name: "usine",
|
||||
category: "modulation",
|
||||
description: "Unipolar sinusoïdal low-frequency oscillator",
|
||||
example: "usine(5) // 5 hz oscillation",
|
||||
},
|
||||
sine: {
|
||||
name: "usine",
|
||||
category: "modulation",
|
||||
description: "Sinusoïdal low-frequency oscillator",
|
||||
example: "usine(5) // 5 hz oscillation",
|
||||
},
|
||||
utriangle: {
|
||||
name: "utriangle",
|
||||
category: "modulation",
|
||||
description: "Unipolar triangular low-frequency oscillator",
|
||||
example: "utriangle(5) // 5 hz oscillation",
|
||||
},
|
||||
triangle: {
|
||||
name: "triangle",
|
||||
category: "modulation",
|
||||
description: "Triangular low-frequency oscillator",
|
||||
example: "triangle(5) // 5 hz oscillation",
|
||||
},
|
||||
usaw: {
|
||||
name: "usaw",
|
||||
category: "modulation",
|
||||
description: "Unipolar sawtooth low-frequency oscillator",
|
||||
example: "usaw(5) // 5 hz oscillation",
|
||||
},
|
||||
saw: {
|
||||
name: "saw",
|
||||
category: "modulation",
|
||||
description: "Sawtooth low-frequency oscillator",
|
||||
example: "saw(5) // 5 hz oscillation",
|
||||
},
|
||||
square: {
|
||||
name: "square",
|
||||
category: "modulation",
|
||||
description: "Square low-frequency oscillator",
|
||||
example: "square(5) // 5 hz oscillation",
|
||||
},
|
||||
usquare: {
|
||||
name: "usquare",
|
||||
category: "modulation",
|
||||
description: "Unipolar square low-frequency oscillator",
|
||||
example: "usquare(5) // 5 hz oscillation",
|
||||
},
|
||||
rhythm: {
|
||||
name: "rhythm",
|
||||
category: "rhythm",
|
||||
description: "Variant of the euclidian algorithm function",
|
||||
example: "rhythm(.5, 3, 8) // time, pulses, steps",
|
||||
},
|
||||
let: {
|
||||
name: "let",
|
||||
category: "javascript",
|
||||
description: "Variable assignation",
|
||||
example: "let baba = 10",
|
||||
},
|
||||
onbeat: {
|
||||
name: "onbeat",
|
||||
category: "rhythm",
|
||||
description: "Return true when targetted beat(s) is/are reached",
|
||||
example: "onbeat([1,2,3]) // true on beats 1, 2 and 3",
|
||||
},
|
||||
oncount: {
|
||||
name: "oncount",
|
||||
category: "rhythm",
|
||||
description:
|
||||
"Return true when targetted beat(s) is/are reached in the given period",
|
||||
example:
|
||||
"oncount([1,2,3], 4) // true on beats 1, 2 and 3 in a 4 beats period",
|
||||
},
|
||||
mod: {
|
||||
name: "mod",
|
||||
category: "rhythm",
|
||||
description: "return true every <i>n</i> pulsations.",
|
||||
example: "mod(1) :: log(rand(1,5))",
|
||||
},
|
||||
modp: {
|
||||
name: "modp",
|
||||
category: "rhythm",
|
||||
description: "return true every <i>n</i> ticks.",
|
||||
example: "modp(8) :: log(rand(1,5))",
|
||||
},
|
||||
euclid: {
|
||||
name: "euclid",
|
||||
category: "rhythm",
|
||||
description: "Iterator-based euclidian rhythm generator",
|
||||
example: "euclid($(1), 3, 8) // iterator, pulses",
|
||||
},
|
||||
oneuclid: {
|
||||
name: "oneuclid",
|
||||
category: "rhythm",
|
||||
description: "Variant of the euclidian rhythm generator",
|
||||
example: "oneuclid(3, 8) // iterator, pulses",
|
||||
},
|
||||
bin: {
|
||||
name: "bin",
|
||||
category: "rhythm",
|
||||
description: "Convert a decimal number to binary rhythm generator",
|
||||
example: "bin($(1), 9223) // iterator, number to convert",
|
||||
},
|
||||
binrhythm: {
|
||||
name: "binrhythm",
|
||||
category: "rhythm",
|
||||
description: "Binary rhythm generator",
|
||||
example: "binrhythm(9223) :: sound('cp').out()",
|
||||
},
|
||||
prob: {
|
||||
name: "prob",
|
||||
category: "randomness",
|
||||
description: "Return true with a probability of <i>n</i> %",
|
||||
example: "prob(50) // 50% probability",
|
||||
},
|
||||
rand: {
|
||||
name: "rand",
|
||||
category: "randomness",
|
||||
description: "random floating point number between x and y",
|
||||
example: "rand(1, 10) // between 1 and 10",
|
||||
},
|
||||
irand: {
|
||||
name: "irand",
|
||||
category: "randomness",
|
||||
description: "random integer number between x and y",
|
||||
example: "irand(1, 10) // between 1 and 10",
|
||||
},
|
||||
pick: {
|
||||
name: "pick",
|
||||
category: "randomness",
|
||||
description: "Pick a value in the given array",
|
||||
example: "[1,4,10].pick()",
|
||||
},
|
||||
sound: {
|
||||
name: "sound",
|
||||
category: "audio",
|
||||
description: "Base function to play audio (samples / synths)",
|
||||
example: "sound('bd').out()",
|
||||
},
|
||||
snd: {
|
||||
name: "snd",
|
||||
category: "audio",
|
||||
description:
|
||||
"Base function to play audio (samples / synths). Alias for <code>sound<code>.",
|
||||
example: "sound('bd').out()",
|
||||
},
|
||||
bpm: {
|
||||
name: "bpm",
|
||||
category: "time",
|
||||
description: "Get or set the current beats per minute.",
|
||||
example: "bpm(135) // set the bpm to 135",
|
||||
},
|
||||
out: {
|
||||
name: "out",
|
||||
category: "audio",
|
||||
description: "Connect the <code>sound()</code> chain to the output",
|
||||
example: "sound('clap').out()",
|
||||
},
|
||||
script: {
|
||||
name: "script",
|
||||
category: "core",
|
||||
description: "Execute one or more local scripts",
|
||||
example: "mod(1) :: script(1)",
|
||||
},
|
||||
warp: {
|
||||
name: "warp",
|
||||
category: "core",
|
||||
description: "jumps to the <i>n</i> tick of the clock.",
|
||||
example: "warp(1) :: log('back to the big bang!')",
|
||||
},
|
||||
beat_warp: {
|
||||
name: "beat_warp",
|
||||
category: "core",
|
||||
description: "jumps to the <i>n</i> beat of the clock.",
|
||||
example: "beat_warp(1) :: log('back to the first beat!')",
|
||||
},
|
||||
divbar: {
|
||||
name: "divbar",
|
||||
category: "time",
|
||||
description:
|
||||
"works just like <i>div</i> but at the level of bars instead of beats",
|
||||
example: "divbar(2)::mod(1)::snd('kick').out()",
|
||||
},
|
||||
onbar: {
|
||||
name: "onbar",
|
||||
category: "time",
|
||||
description: "return true when targetted bar(s) is/are reached in period",
|
||||
example: "onbar(4, 4)::mod(.5)::snd('hh').out();",
|
||||
},
|
||||
begin: {
|
||||
name: "begin",
|
||||
category: "sampling",
|
||||
description: "Audio playback start time (0-1)",
|
||||
example: "sound('cp').begin(.5).out()",
|
||||
},
|
||||
end: {
|
||||
name: "end",
|
||||
category: "sampling",
|
||||
description: "Audio playback end time (0-1)",
|
||||
example: "sound('cp').end(.5).out()",
|
||||
},
|
||||
mouseX: {
|
||||
name: "mouseX",
|
||||
category: "mouse",
|
||||
description: "Mouse X position (big float)",
|
||||
example: "log(mouseX())",
|
||||
},
|
||||
mouseY: {
|
||||
name: "mouseY",
|
||||
category: "mouse",
|
||||
description: "Mouse Y position (big float)",
|
||||
example: "log(mouseY())",
|
||||
},
|
||||
noteX: {
|
||||
name: "noteX",
|
||||
category: "mouse",
|
||||
description: "Mouse X position (as MIDI note)",
|
||||
example: "log(noteX())",
|
||||
},
|
||||
noteY: {
|
||||
name: "noteY",
|
||||
category: "mouse",
|
||||
description: "Mouse Y position (as MIDI note)",
|
||||
example: "log(noteY())",
|
||||
},
|
||||
cut: {
|
||||
name: "cut",
|
||||
category: "sampling",
|
||||
description: "Cutting sample when other sample met on same orbit (0 or 1)",
|
||||
example: "sound('cp').cut(1).out()",
|
||||
},
|
||||
pan: {
|
||||
name: "pan",
|
||||
category: "audio",
|
||||
description: "Stereo panning (-1 to 1)",
|
||||
example: "sound('cp').pan(-1).out()",
|
||||
},
|
||||
zrand: {
|
||||
name: "zrand",
|
||||
category: "synthesis",
|
||||
description: "ZzFX randomisation factor",
|
||||
example: "sound('zzfx').zrand(.5).out()",
|
||||
},
|
||||
curve: {
|
||||
name: "curve",
|
||||
category: "synthesis",
|
||||
description: "ZzFX waveshaping (0-3)",
|
||||
example: "sound('zzfx').curve(1).out()",
|
||||
},
|
||||
slide: {
|
||||
name: "slide",
|
||||
category: "synthesis",
|
||||
description: "ZzFX pitch slide",
|
||||
example: "sound('zzfx').slide(1).out()",
|
||||
},
|
||||
deltaSlide: {
|
||||
name: "deltaSlide",
|
||||
category: "synthesis",
|
||||
description: "ZzFX pitch delta slide",
|
||||
example: "sound('zzfx').deltaSlide(1).out()",
|
||||
},
|
||||
pitchJump: {
|
||||
name: "pitchJump",
|
||||
category: "synthesis",
|
||||
description: "ZzFX pitch jump",
|
||||
example: "sound('zzfx').pitchJump(1).out()",
|
||||
},
|
||||
pitchJumpTime: {
|
||||
name: "pitchJumpTime",
|
||||
category: "synthesis",
|
||||
description: "ZzFX pitch jump time (time before jump)",
|
||||
example: "sound('zzfx').pitchJumpTime(1).out()",
|
||||
},
|
||||
zcrush: {
|
||||
name: "zcrush",
|
||||
category: "synthesis",
|
||||
description: "ZzFX bitcrushing",
|
||||
example: "sound('zzfx').zcrush(1).out()",
|
||||
},
|
||||
zdelay: {
|
||||
name: "zdelay",
|
||||
category: "synthesis",
|
||||
description: "ZzFX delay",
|
||||
example: "sound('zzfx').zdelay(1).out()",
|
||||
},
|
||||
tremolo: {
|
||||
name: "tremolo",
|
||||
category: "synthesis",
|
||||
description: "ZzFX weird tremolo effect",
|
||||
example: "sound('zzfx').tremolo(1).out()",
|
||||
},
|
||||
speak: {
|
||||
name: "speak",
|
||||
category: "synthesis",
|
||||
description: "Text to speech synthesizer",
|
||||
example: "mod(2) :: speak('Topos!','fr',irand(0,5))",
|
||||
},
|
||||
midi_outputs: {
|
||||
name: "midi_outputs",
|
||||
category: "midi",
|
||||
description: "List of available MIDI outputs",
|
||||
example: "midi_outputs()",
|
||||
},
|
||||
midi_output: {
|
||||
name: "midi_output",
|
||||
category: "midi",
|
||||
description: "Set the current MIDI output",
|
||||
example: "midi_output('IAC Driver Bus 1')",
|
||||
},
|
||||
midi: {
|
||||
name: "midi",
|
||||
category: "midi",
|
||||
description: "Send a MIDI message",
|
||||
example: "midi(144, 60, 100)",
|
||||
},
|
||||
control_change: {
|
||||
name: "control_change",
|
||||
category: "midi",
|
||||
description: "Send a MIDI control change message",
|
||||
example: "control_change({control: 1, value: 60, channel: 10})",
|
||||
},
|
||||
program_change: {
|
||||
name: "program_change",
|
||||
category: "midi",
|
||||
description: "Send a MIDI program change message",
|
||||
example: "program_change(1, 10)",
|
||||
},
|
||||
sysex: {
|
||||
name: "sysex",
|
||||
category: "midi",
|
||||
description: "Send a MIDI sysex message",
|
||||
example: "sysex(0xF0, 0x7D, 0x00, 0x06, 0x01, 0xF7)",
|
||||
},
|
||||
midi_clock: {
|
||||
name: "midi_clock",
|
||||
category: "midi",
|
||||
description: "Send a MIDI clock message",
|
||||
example: "midi_clock()",
|
||||
},
|
||||
degrade: {
|
||||
name: "degrade",
|
||||
category: "patterns",
|
||||
description: "Removes <i>n</i>% of the given array randomly",
|
||||
example: "[0,1,2,3].degrade(20)",
|
||||
},
|
||||
loop: {
|
||||
name: "loop",
|
||||
category: "patterns",
|
||||
description: "Loop over the given array using an iterator",
|
||||
example: "[0,1,2,3].loop($(1))",
|
||||
},
|
||||
$: {
|
||||
name: "$",
|
||||
category: "patterns",
|
||||
description: "Iterator",
|
||||
example: "[0,1,2,3].loop($(1))",
|
||||
},
|
||||
shuffle: {
|
||||
name: "shuffle",
|
||||
category: "patterns",
|
||||
description: "Shuffle the given array",
|
||||
example: "[0,1,2,3].shuffle()",
|
||||
},
|
||||
rotate: {
|
||||
name: "rotate",
|
||||
category: "patterns",
|
||||
description: "Rotate the given array to the right for <i>n</i> indexes",
|
||||
example: "[0,1,2,3].rotate(2)",
|
||||
},
|
||||
unique: {
|
||||
name: "unique",
|
||||
category: "patterns",
|
||||
description: "Remove duplicates from the given array",
|
||||
example: "[0,1,2,3,3,3].unique()",
|
||||
},
|
||||
add: {
|
||||
name: "add",
|
||||
category: "patterns",
|
||||
description: "Add a value to each element of the given array",
|
||||
example: "[0,1,2,3].add(1)",
|
||||
},
|
||||
sub: {
|
||||
name: "sub",
|
||||
category: "patterns",
|
||||
description: "Substract a value to each element of the given array",
|
||||
example: "[0,1,2,3].sub(1)",
|
||||
},
|
||||
mul: {
|
||||
name: "mul",
|
||||
category: "patterns",
|
||||
description: "Multiply each element of the given array by a value",
|
||||
example: "[0,1,2,3].mul(2)",
|
||||
},
|
||||
division: {
|
||||
name: "div",
|
||||
category: "patterns",
|
||||
description: "Divide each element of the given array by a value",
|
||||
example: "[0,1,2,3].division(2)",
|
||||
},
|
||||
};
|
||||
|
||||
export const inlineHoveringTips = hoverTooltip(
|
||||
(view: any, pos: any, side: any) => {
|
||||
let { from, to, text } = view.state.doc.lineAt(pos);
|
||||
let start = pos,
|
||||
end = pos;
|
||||
while (start > from && /\w/.test(text[start - from - 1])) start--;
|
||||
while (end < to && /\w/.test(text[end - from])) end++;
|
||||
if ((start == pos && side < 0) || (end == pos && side > 0)) return null;
|
||||
return {
|
||||
pos: start,
|
||||
end,
|
||||
above: true,
|
||||
create(view: EditorView) {
|
||||
if (
|
||||
text.slice(start - from, end - from) in completionDatabase ===
|
||||
false
|
||||
) {
|
||||
return { dom: document.createElement("div") };
|
||||
}
|
||||
let completion =
|
||||
completionDatabase[text.slice(start - from, end - from)] || {};
|
||||
let divContent = `
|
||||
<h1 class="text-orange-300 text-base pb-1">${completion.name} [<em class="text-white">${completion.category}</em>]</h1>
|
||||
<p class="text-base pl-4">${completion.description}</p>
|
||||
<pre class="-mt-2"><code class="pl-4 text-base">${completion.example}</code></pre></div>
|
||||
`;
|
||||
let dom = document.createElement("div");
|
||||
dom.classList.add("px-4", "py-2", "bg-neutral-700", "rounded-lg");
|
||||
dom.innerHTML = divContent;
|
||||
return { dom };
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -182,6 +182,7 @@ rhythm(speed, 7, 12) :: snd('east').n(9).out()
|
||||
|
||||
|
||||
- <ic>bin(iterator: number, n: number): boolean</ic>: a binary rhythm generator. It transforms the given number into its binary representation (_e.g_ <ic>34</ic> becomes <ic>100010</ic>). It then returns a boolean value based on the iterator in order to generate a rhythm.
|
||||
- <ic>binrhythm(divisor: number, n: number): boolean: boolean</ic>: iterator-less version of the binary rhythm generator.
|
||||
|
||||
${makeExample(
|
||||
"Change the integers for a surprise rhythm!",
|
||||
@ -192,6 +193,15 @@ mod(.5) && bin($(2), 48) && snd('sd').out()
|
||||
true
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"binrhythm for fast cool binary rhythms!",
|
||||
`
|
||||
binrhythm(.5, 13) && snd('kick').out()
|
||||
binrhythm(.5, 18) && snd('sd').out()
|
||||
`,
|
||||
true
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Calling 911",
|
||||
`
|
||||
|
||||
38
src/main.ts
38
src/main.ts
@ -3,6 +3,7 @@ import { examples } from "./examples/excerpts";
|
||||
import { EditorState, Compartment } from "@codemirror/state";
|
||||
import { ViewUpdate, lineNumbers, keymap } from "@codemirror/view";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { inlineHoveringTips } from "./documentation/inlineHelp";
|
||||
import { toposTheme } from "./themes/toposTheme";
|
||||
import { markdown } from "@codemirror/lang-markdown";
|
||||
import { Extension, Prec } from "@codemirror/state";
|
||||
@ -72,6 +73,7 @@ export class Editor {
|
||||
fontSize: Compartment;
|
||||
withLineNumbers: Compartment;
|
||||
vimModeCompartment: Compartment;
|
||||
hoveringCompartment: Compartment;
|
||||
chosenLanguage: Compartment;
|
||||
currentDocumentationPane: string = "introduction";
|
||||
exampleCounter: number = 0;
|
||||
@ -185,6 +187,10 @@ export class Editor {
|
||||
"show-time-position"
|
||||
) as HTMLInputElement;
|
||||
|
||||
// Hovering tips checkbox
|
||||
tips_checkbox: HTMLInputElement = document.getElementById(
|
||||
"show-tips"
|
||||
) as HTMLInputElement;
|
||||
|
||||
// Editor mode selection
|
||||
normal_mode_button: HTMLButtonElement = document.getElementById(
|
||||
@ -231,12 +237,12 @@ export class Editor {
|
||||
this.universes[this.selected_universe].global.committed = random_example;
|
||||
this.universes[this.selected_universe].global.candidate = random_example;
|
||||
|
||||
this.line_numbers_checkbox.checked = this.settings.line_numbers;
|
||||
this.time_position_checkbox.checked = this.settings.time_position;
|
||||
if (!this.settings.time_position) {
|
||||
document.getElementById('timeviewer')!.classList.add('hidden');
|
||||
}
|
||||
|
||||
this.line_numbers_checkbox.checked = this.settings.line_numbers;
|
||||
this.time_position_checkbox.checked = this.settings.time_position;
|
||||
this.tips_checkbox.checked = this.settings.tips;
|
||||
if (!this.settings.time_position) {
|
||||
document.getElementById("timeviewer")!.classList.add("hidden");
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
// Audio context and clock
|
||||
@ -258,6 +264,7 @@ export class Editor {
|
||||
// ================================================================================
|
||||
|
||||
this.vimModeCompartment = new Compartment();
|
||||
this.hoveringCompartment = new Compartment();
|
||||
this.withLineNumbers = new Compartment();
|
||||
this.chosenLanguage = new Compartment();
|
||||
this.fontSize = new Compartment();
|
||||
@ -280,6 +287,7 @@ export class Editor {
|
||||
this.withLineNumbers.of(lines),
|
||||
this.fontSize.of(fontModif),
|
||||
this.vimModeCompartment.of(vimPlugin),
|
||||
this.hoveringCompartment.of(this.settings.tips ? inlineHoveringTips : []),
|
||||
editorSetup,
|
||||
toposTheme,
|
||||
this.chosenLanguage.of(javascript()),
|
||||
@ -547,6 +555,7 @@ export class Editor {
|
||||
);
|
||||
this.line_numbers_checkbox.checked = this.settings.line_numbers;
|
||||
this.time_position_checkbox.checked = this.settings.time_position;
|
||||
this.tips_checkbox.checked = this.settings.tips;
|
||||
let modal_settings = document.getElementById("modal-settings");
|
||||
let editor = document.getElementById("editor");
|
||||
modal_settings?.classList.remove("invisible");
|
||||
@ -605,12 +614,23 @@ export class Editor {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.time_position_checkbox.addEventListener("change", () => {
|
||||
let timeviewer = document.getElementById("timeviewer") as HTMLElement;
|
||||
let timeviewer = document.getElementById("timeviewer") as HTMLElement;
|
||||
let checked = this.time_position_checkbox.checked ? true : false;
|
||||
this.settings.time_position = checked;
|
||||
checked ? timeviewer.classList.remove('hidden') : timeviewer.classList.add('hidden');
|
||||
checked
|
||||
? timeviewer.classList.remove("hidden")
|
||||
: timeviewer.classList.add("hidden");
|
||||
});
|
||||
|
||||
this.tips_checkbox.addEventListener("change", () => {
|
||||
let checked = this.tips_checkbox.checked ? true : false;
|
||||
this.settings.tips = checked;
|
||||
this.view.dispatch({
|
||||
effects: this.hoveringCompartment.reconfigure(
|
||||
checked ? inlineHoveringTips : []
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
this.vim_mode_button.addEventListener("click", () => {
|
||||
|
||||
Reference in New Issue
Block a user