Merge branch 'main' of https://github.com/Bubobubobubobubo/Topos into logicaltime

This commit is contained in:
2023-08-26 11:10:32 +03:00
7 changed files with 778 additions and 503 deletions

View File

@ -65,7 +65,7 @@
</svg> </svg>
</a> </a>
<a title="Eval button" id="eval-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg"> <a title="Eval button" id="eval-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
<svg class="w-8 h-8 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20"> <svg class="w-8 h-8 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/>
</svg> </svg>
</a> </a>
@ -76,7 +76,7 @@
</svg> </svg>
</a> </a>
<a title="Open Documentation" id="doc-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg"> <a title="Open Documentation" id="doc-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
<svg class="w-8 h-8 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18"> <svg class="w-8 h-8 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
<path d="M9 1.334C7.06.594 1.646-.84.293.653a1.158 1.158 0 0 0-.293.77v13.973c0 .193.046.383.134.55.088.167.214.306.366.403a.932.932 0 0 0 .5.147c.176 0 .348-.05.5-.147 1.059-.32 6.265.851 7.5 1.65V1.334ZM19.707.653C18.353-.84 12.94.593 11 1.333V18c1.234-.799 6.436-1.968 7.5-1.65a.931.931 0 0 0 .5.147.931.931 0 0 0 .5-.148c.152-.096.279-.235.366-.403.088-.167.134-.357.134-.55V1.423a1.158 1.158 0 0 0-.293-.77Z"/> <path d="M9 1.334C7.06.594 1.646-.84.293.653a1.158 1.158 0 0 0-.293.77v13.973c0 .193.046.383.134.55.088.167.214.306.366.403a.932.932 0 0 0 .5.147c.176 0 .348-.05.5-.147 1.059-.32 6.265.851 7.5 1.65V1.334ZM19.707.653C18.353-.84 12.94.593 11 1.333V18c1.234-.799 6.436-1.968 7.5-1.65a.931.931 0 0 0 .5.147.931.931 0 0 0 .5-.148c.152-.096.279-.235.366-.403.088-.167.134-.357.134-.55V1.423a1.158 1.158 0 0 0-.293-.77Z"/>
</svg> </svg>
</a> </a>
@ -214,8 +214,8 @@
<!-- This is a lateral bar that will inherit the header buttons if the window is too small. --> <!-- This is a lateral bar that will inherit the header buttons if the window is too small. -->
<aside class=" <aside class="
flex flex-col items-center w-14 flex flex-col items-center w-14
h-screen py-2 overflow-y-hidden h-screen py-2 border-r
border-r rtl:border-l max-h-fit rtl:border-l max-h-fit
rtl:border-r-0 bg-neutral-900 rtl:border-r-0 bg-neutral-900
dark:border-neutral-700 border-none" dark:border-neutral-700 border-none"
> >
@ -255,6 +255,7 @@
<!-- Other buttons --> <!-- Other buttons -->
<!--
<a title="Play button" id="play-button-2" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800 sm:landscape:hidden"> <a title="Play button" id="play-button-2" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800 sm:landscape:hidden">
<svg class="w-8 h-8 text-white block xl:hidden" fill="currentColor" viewBox="0 0 14 16"> <svg class="w-8 h-8 text-white block xl:hidden" fill="currentColor" viewBox="0 0 14 16">
<path d="M0 .984v14.032a1 1 0 0 0 1.506.845l12.006-7.016a.974.974 0 0 0 0-1.69L1.506.139A1 1 0 0 0 0 .984Z"/> <path d="M0 .984v14.032a1 1 0 0 0 1.506.845l12.006-7.016a.974.974 0 0 0 0-1.69L1.506.139A1 1 0 0 0 0 .984Z"/>
@ -273,11 +274,13 @@
</svg> </svg>
</a> </a>
<a title="Clear button" id="clear-button-2" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800"> <a title="Clear button" id="clear-button-2" class="hidden lg:block pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg text-white hover:bg-gray-800">
<svg class="w-8 h-8 text-white block xl:hidden" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 17 20"> <svg class="w-8 h-8 text-white block xl:hidden" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 17 20">
<path d="M7.958 19.393a7.7 7.7 0 0 1-6.715-3.439c-2.868-4.832 0-9.376.944-10.654l.091-.122a3.286 3.286 0 0 0 .765-3.288A1 1 0 0 1 4.6.8c.133.1.313.212.525.347A10.451 10.451 0 0 1 10.6 9.3c.5-1.06.772-2.213.8-3.385a1 1 0 0 1 1.592-.758c1.636 1.205 4.638 6.081 2.019 10.441a8.177 8.177 0 0 1-7.053 3.795Z"/> <path d="M7.958 19.393a7.7 7.7 0 0 1-6.715-3.439c-2.868-4.832 0-9.376.944-10.654l.091-.122a3.286 3.286 0 0 0 .765-3.288A1 1 0 0 1 4.6.8c.133.1.313.212.525.347A10.451 10.451 0 0 1 10.6 9.3c.5-1.06.772-2.213.8-3.385a1 1 0 0 1 1.592-.758c1.636 1.205 4.638 6.081 2.019 10.441a8.177 8.177 0 0 1-7.053 3.795Z"/>
</svg> </svg>
</a> </a>
-->
<a title="Open settings" id="settings-button" class="lg:block sm:hidden p-2 text-gray-200 ml-1 absolute bottom-2 focus:outline-nones transition-colors duration-200 rounded-lg text-gray-200 bg-gray-800"> <a title="Open settings" id="settings-button" class="lg:block sm:hidden p-2 text-gray-200 ml-1 absolute bottom-2 focus:outline-nones transition-colors duration-200 rounded-lg text-gray-200 bg-gray-800">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
@ -289,7 +292,7 @@
<!-- Tabs for local files --> <!-- Tabs for local files -->
<div class="min-w-screen flex grow flex-col"> <div class="min-w-screen flex grow flex-col">
<ul id="local-script-tabs" class="text-xl flex text-sm font-medium text-center text-white dark:text-white bg-neutral-900 space-x-2 lg:space-x-8"> <ul id="local-script-tabs" class="text-xl flex text-sm font-medium text-center text-white bg-neutral-900 space-x-2 lg:space-x-8">
<li class="pl-5"> <li class="pl-5">
<a title="Local Script 1" id="tab-1" class="bg-orange-300 inline-block px-4 py-1 text-white hover:bg-gray-800">1</a> <a title="Local Script 1" id="tab-1" class="bg-orange-300 inline-block px-4 py-1 text-white hover:bg-gray-800">1</a>
</li> </li>

View File

@ -22,21 +22,230 @@ interface ControlChange {
value: number; value: number;
} }
// ======================================================================
// Array prototype extensions: easier work with lists
// ======================================================================
declare global {
interface Array<T> {
palindrome(): T[];
random(index: number): T;
rand(index: number): T;
degrade(amount: number): T;
repeatAll(amount: number): T;
repeatPair(amount: number): T;
repeatOdd(amount: number): T;
loop(index: number): T;
div(division: number): T;
shuffle(): this;
rotate(steps: number): this;
unique(): this;
}
}
Array.prototype.shuffle = function () {
/**
* Shuffles the array in place.
*
* @returns The shuffled array
*/
let currentIndex = this.length,
randomIndex;
while (currentIndex !== 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[this[currentIndex], this[randomIndex]] = [
this[randomIndex],
this[currentIndex],
];
}
return this;
};
Array.prototype.rotate = function (steps: number) {
/**
* Rotates the array in place.
*
* @param steps - The number of steps to rotate the array by
* @returns The rotated array
*/
const length = this.length;
if (steps < 0) {
steps = length + (steps % length);
} else if (steps > 0) {
steps = steps % length;
} else {
return this;
}
const rotated = this.splice(-steps, steps);
this.unshift(...rotated);
return this;
};
Array.prototype.unique = function () {
/**
* Removes duplicate elements from the array in place.
*
* @returns The array without duplicates
*/
const seen = new Set();
let writeIndex = 0;
for (let readIndex = 0; readIndex < this.length; readIndex++) {
const value = this[readIndex];
if (!seen.has(value)) {
seen.add(value);
this[writeIndex++] = value;
}
}
this.length = writeIndex;
return this;
};
Array.prototype.degrade = function <T>(this: T[], amount: number) {
/**
* Removes elements from the array at random. If the array has
* only one element left, it will not be removed.
*
* @param amount - The amount of elements to remove
* @returns The degraded array
*/
if (amount < 0 || amount > 100) {
throw new Error("Amount should be between 0 and 100");
}
if (this.length <= 1) {
return this;
}
for (let i = 0; i < this.length; ) {
const rand = Math.random() * 100;
if (rand < amount) {
if (this.length > 1) {
this.splice(i, 1);
} else {
return this;
}
} else {
i++;
}
}
return this;
};
Array.prototype.repeatAll = function <T>(this: T[], amount: number) {
/**
* Repeats all elements in the array n times.
*
* @param amount - The amount of times to repeat the elements
* @returns The repeated array
*/
if (amount < 1) {
throw new Error("Amount should be at least 1");
}
let result = [];
for (let i = 0; i < this.length; i++) {
for (let j = 0; j < amount; j++) {
result.push(this[i]);
}
}
this.length = 0;
this.push(...result);
return this;
};
Array.prototype.repeatPair = function <T>(this: T[], amount: number) {
/**
* Repeats all elements in the array n times, except for the
* elements at odd indexes.
*
* @param amount - The amount of times to repeat the elements
* @returns The repeated array
*/
if (amount < 1) {
throw new Error("Amount should be at least 1");
}
let result = [];
for (let i = 0; i < this.length; i++) {
// If the index is even, repeat the element
if (i % 2 === 0) {
for (let j = 0; j < amount; j++) {
result.push(this[i]);
}
} else {
result.push(this[i]);
}
}
this.length = 0;
this.push(...result);
return this;
};
Array.prototype.repeatOdd = function <T>(this: T[], amount: number) {
/**
* Repeats all elements in the array n times, except for the
* elements at even indexes.
*
* @param amount - The amount of times to repeat the elements
* @returns The repeated array
*
* @remarks
* This function is the opposite of repeatPair.
*/
if (amount < 1) {
throw new Error("Amount should be at least 1");
}
let result = [];
for (let i = 0; i < this.length; i++) {
// If the index is odd, repeat the element
if (i % 2 !== 0) {
for (let j = 0; j < amount; j++) {
result.push(this[i]);
}
} else {
result.push(this[i]);
}
}
// Update the original array
this.length = 0;
this.push(...result);
return this;
};
// @ts-ignore // @ts-ignore
Array.prototype.palindrome = function (index) { Array.prototype.palindrome = function <T>() {
/**
* Returns a palindrome of the array.
*
* @returns The palindrome of the array
*/
let left_to_right = Array.from(this); let left_to_right = Array.from(this);
let right_to_left = Array.from(this.reverse()); let right_to_left = Array.from(this.reverse());
return left_to_right.concat(right_to_left); return left_to_right.concat(right_to_left);
}; };
// @ts-ignore
Array.prototype.random = function (index) { Array.prototype.loop = function <T>(this: T[], index: number): T {
return this[Math.floor(Math.random() * this.length)]; /**
}; * Returns an element from the array based on the index.
// @ts-ignore * The index will wrap over the array.
Array.prototype.loop = function (index) { *
* @param index - The index of the element to return
* @returns The element at the given index
*/
return this[index % this.length]; return this[index % this.length];
}; };
// @ts-ignore
Array.prototype.random = function () {
/**
* Returns a random element from the array.
*
* @returns A random element from the array
*/
return this[Math.floor(Math.random() * this.length)];
};
Array.prototype.rand = Array.prototype.random; Array.prototype.rand = Array.prototype.random;
/** /**
@ -51,19 +260,16 @@ Array.prototype.in = function <T>(this: T[], value: T): boolean {
return this.includes(value); return this.includes(value);
}; };
async function loadSamples() { export async function loadSamples() {
// const ds = "https://raw.githubusercontent.com/felixroos/dough-samples/main/";
return Promise.all([ return Promise.all([
initAudioOnFirstClick(), initAudioOnFirstClick(),
samples("github:Bubobubobubobubo/Topos-Samples/main"),
samples("github:tidalcycles/Dirt-Samples/master").then(() => samples("github:tidalcycles/Dirt-Samples/master").then(() =>
registerSynthSounds() registerSynthSounds()
), ),
samples("github:Bubobubobubobubo/Topos-Samples/main"),
]); ]);
} }
loadSamples();
export const generateCacheKey = (...args: any[]): string => { export const generateCacheKey = (...args: any[]): string => {
return args.map((arg) => JSON.stringify(arg)).join(","); return args.map((arg) => JSON.stringify(arg)).join(",");
}; };
@ -92,6 +298,10 @@ export class UserAPI {
//this.load = samples("github:tidalcycles/Dirt-Samples/master"); //this.load = samples("github:tidalcycles/Dirt-Samples/master");
} }
_all_samples = (): object => {
return soundMap.get();
};
_reportError = (error: any): void => { _reportError = (error: any): void => {
console.log(error); console.log(error);
clearTimeout(this.errorTimeoutID); clearTimeout(this.errorTimeoutID);
@ -236,7 +446,11 @@ export class UserAPI {
} }
}; };
public midi = (value: number|object = 60): NoteEvent => { public midi = (
value: number | object = 60,
velocity?: number,
channel?: number
): NoteEvent => {
/** /**
* Sends a MIDI note to the current MIDI output. * Sends a MIDI note to the current MIDI output.
* *
@ -244,6 +458,24 @@ export class UserAPI {
* @param options - an object containing options for that note * @param options - an object containing options for that note
* { channel: 0, velocity: 100, duration: 0.5 } * { channel: 0, velocity: 100, duration: 0.5 }
*/ */
if (velocity !== undefined) {
// Check if value is of type number
if (typeof value === "number") {
value = { note: value };
}
// @ts-ignore
value["velocity"] = velocity;
}
if (channel !== undefined) {
if (typeof value === "number") {
value = { note: value };
}
// @ts-ignore
value["channel"] = channel;
}
return new NoteEvent(value, this.app); return new NoteEvent(value, this.app);
}; };
@ -323,8 +555,8 @@ export class UserAPI {
this.app.api.patternCache.set(key, player); this.app.api.patternCache.set(key, player);
} }
if ((player && player.notStarted()) || player.played) { if ((player && player.notStarted()) || player.played) {
player.callTime = this.epulse(); player.callTime = this.epulse();
player.played = false; player.played = false;
} }
return player; return player;
}; };
@ -702,6 +934,30 @@ export class UserAPI {
// Probability functions // Probability functions
// ============================================================= // =============================================================
public prob = (p: number): boolean => {
/**
* Returns true p% of the time.
*
* @param p - The probability of returning true
* @returns True p% of the time
*/
return this.randomGen() * 100 < p;
};
public toss = (): boolean => {
/**
* Returns true 50% of the time.
*
* @returns True 50% of the time
* @see sometimes
* @see rarely
* @see often
* @see almostAlways
* @see almostNever
*/
return this.randomGen() > 0.5;
};
public odds = (n: number, sec: number = 15): boolean => { public odds = (n: number, sec: number = 15): boolean => {
/** /**
* Returns true n% of the time. * Returns true n% of the time.
@ -865,6 +1121,10 @@ export class UserAPI {
return this.app.clock.pulses_since_origin; return this.app.clock.pulses_since_origin;
}; };
// =============================================================
// Time Filters
// =============================================================
onbar = (n: number, ...bar: number[]): boolean => { onbar = (n: number, ...bar: number[]): boolean => {
// n is acting as a modulo on the bar number // n is acting as a modulo on the bar number
const bar_list = [...Array(n).keys()].map((i) => i + 1); const bar_list = [...Array(n).keys()].map((i) => i + 1);
@ -889,7 +1149,7 @@ export class UserAPI {
let integral_part = Math.floor(b); let integral_part = Math.floor(b);
let decimal_part = b - integral_part; let decimal_part = b - integral_part;
final_pulses.push( final_pulses.push(
integral_part === this.app.clock.time_position.beat && integral_part === this.app.clock.time_position.beat &&
this.app.clock.time_position.pulse === this.app.clock.time_position.pulse ===
decimal_part * this.app.clock.ppqn decimal_part * this.app.clock.ppqn
); );
@ -897,30 +1157,6 @@ export class UserAPI {
return final_pulses.some((p) => p == true); return final_pulses.some((p) => p == true);
}; };
prob = (p: number): boolean => {
/**
* Returns true p% of the time.
*
* @param p - The probability of returning true
* @returns True p% of the time
*/
return this.randomGen() * 100 < p;
};
toss = (): boolean => {
/**
* Returns true 50% of the time.
*
* @returns True 50% of the time
* @see sometimes
* @see rarely
* @see often
* @see almostAlways
* @see almostNever
*/
return this.randomGen() > 0.5;
};
public min = (...values: number[]): number => { public min = (...values: number[]): number => {
/** /**
* Returns the minimum value of a list of numbers. * Returns the minimum value of a list of numbers.
@ -1235,13 +1471,12 @@ export class UserAPI {
// Trivial functions // Trivial functions
// ============================================================= // =============================================================
sound = (sound: string|object) => { sound = (sound: string | object) => {
return new SoundEvent(sound, this.app); return new SoundEvent(sound, this.app);
}; };
snd = this.sound; snd = this.sound;
samples = samples; samples = samples;
soundMap = soundMap;
log = console.log; log = console.log;

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,10 @@ export class RestEvent extends Event {
} }
_fallbackMethod = (): Event => { _fallbackMethod = (): Event => {
return this; return RestEvent.createRestProxy(this.values["duration"], this.app);
} }
public static createRestProxy = (duration: number, app: Editor) => { public static createRestProxy = (duration: number, app: Editor): RestEvent => {
const instance = new RestEvent(duration, app); const instance = new RestEvent(duration, app);
return new Proxy(instance, { return new Proxy(instance, {
// @ts-ignore // @ts-ignore

View File

@ -1,10 +1,10 @@
export class SkipEvent { export class SkipEvent {
_fallbackMethod = (): SkipEvent => { _fallbackMethod = (): SkipEvent => {
return this; return SkipEvent.createSkipProxy();
} }
public static createSkipProxy = () => { public static createSkipProxy = (): SkipEvent => {
const instance = new SkipEvent(); const instance = new SkipEvent();
return new Proxy(instance, { return new Proxy(instance, {
// @ts-ignore // @ts-ignore

View File

@ -1,6 +1,6 @@
import { type Editor } from '../main'; import { type Editor } from "../main";
import { AudibleEvent } from './AbstractEvents'; import { AudibleEvent } from "./AbstractEvents";
import { midiToFreq, noteFromPc } from 'zifferjs'; import { midiToFreq, noteFromPc } from "zifferjs";
import { import {
superdough, superdough,
@ -8,7 +8,6 @@ import {
} from "superdough"; } from "superdough";
export class SoundEvent extends AudibleEvent { export class SoundEvent extends AudibleEvent {
constructor(sound: string | object, public app: Editor) { constructor(sound: string | object, public app: Editor) {
super(app); super(app);
if (typeof sound === "string") this.values = { s: sound, dur: 0.5 }; if (typeof sound === "string") this.values = { s: sound, dur: 0.5 };

View File

@ -14,10 +14,10 @@ import { indentWithTab } from "@codemirror/commands";
import { vim } from "@replit/codemirror-vim"; import { vim } from "@replit/codemirror-vim";
import { AppSettings, Universe } from "./AppSettings"; import { AppSettings, Universe } from "./AppSettings";
import { editorSetup } from "./EditorSetup"; import { editorSetup } from "./EditorSetup";
import { documentation } from "./Documentation"; import { documentation_factory } from "./Documentation";
import { EditorView } from "codemirror"; import { EditorView } from "codemirror";
import { Clock } from "./Clock"; import { Clock } from "./Clock";
import { UserAPI } from "./API"; import { loadSamples, UserAPI } from "./API";
import "./style.css"; import "./style.css";
import { import {
Universes, Universes,
@ -27,9 +27,6 @@ import {
} from "./AppSettings"; } from "./AppSettings";
import { tryEvaluate } from "./Evaluator"; import { tryEvaluate } from "./Evaluator";
type Documentation = { [key: string]: string };
const Docs: Documentation = documentation;
// Importing showdown and setting up the markdown converter // Importing showdown and setting up the markdown converter
import showdown from "showdown"; import showdown from "showdown";
showdown.setFlavor("github"); showdown.setFlavor("github");
@ -37,8 +34,8 @@ import showdownHighlight from "showdown-highlight";
const classMap = { const classMap = {
h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-8 mb-4 bg-neutral-900 rounded-lg py-2 px-2", h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-8 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
h2: "text-white lg:text-3xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-8 mb-4 bg-neutral-900 rounded-lg py-2 px-2", h2: "text-white lg:text-3xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-8 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
ul: "text-underline", ul: "text-underline pl-6",
li: "ml-12 list-disc lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 lg:pl-4 my-2 leading-normal", li: "list-disc lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 my-2 leading-normal",
p: "lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 leading-normal", p: "lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 leading-normal",
a: "lg:text-2xl text-base text-orange-300", a: "lg:text-2xl text-base text-orange-300",
code: "lg:my-4 sm:my-1 text-base lg:text-xl block whitespace-pre overflow-x-hidden", code: "lg:my-4 sm:my-1 text-base lg:text-xl block whitespace-pre overflow-x-hidden",
@ -66,7 +63,7 @@ export class Editor {
universes: Universes = template_universes; universes: Universes = template_universes;
selected_universe: string; selected_universe: string;
local_index: number = 1; local_index: number = 1;
editor_mode: "global" | "local" | "init" | "notes" = "local"; editor_mode: "global" | "local" | "init" | "notes" = "global";
fontSize: Compartment; fontSize: Compartment;
withLineNumbers: Compartment; withLineNumbers: Compartment;
vimModeCompartment: Compartment; vimModeCompartment: Compartment;
@ -78,6 +75,7 @@ export class Editor {
userPlugins: Extension[] = []; userPlugins: Extension[] = [];
state: EditorState; state: EditorState;
api: UserAPI; api: UserAPI;
docs: { [key: string]: string } = {};
// Audio stuff // Audio stuff
audioContext: AudioContext; audioContext: AudioContext;
@ -92,19 +90,19 @@ export class Editor {
// Transport elements // Transport elements
play_buttons: HTMLButtonElement[] = [ play_buttons: HTMLButtonElement[] = [
document.getElementById("play-button-1") as HTMLButtonElement, document.getElementById("play-button-1") as HTMLButtonElement,
document.getElementById("play-button-2") as HTMLButtonElement, //document.getElementById("play-button-2") as HTMLButtonElement,
]; ];
pause_buttons: HTMLButtonElement[] = [ pause_buttons: HTMLButtonElement[] = [
document.getElementById("pause-button-1") as HTMLButtonElement, document.getElementById("pause-button-1") as HTMLButtonElement,
document.getElementById("pause-button-2") as HTMLButtonElement, //document.getElementById("pause-button-2") as HTMLButtonElement,
]; ];
stop_buttons: HTMLButtonElement[] = [ stop_buttons: HTMLButtonElement[] = [
document.getElementById("stop-button-1") as HTMLButtonElement, document.getElementById("stop-button-1") as HTMLButtonElement,
document.getElementById("stop-button-2") as HTMLButtonElement, //document.getElementById("stop-button-2") as HTMLButtonElement,
]; ];
clear_buttons: HTMLButtonElement[] = [ clear_buttons: HTMLButtonElement[] = [
document.getElementById("clear-button-1") as HTMLButtonElement, document.getElementById("clear-button-1") as HTMLButtonElement,
document.getElementById("clear-button-2") as HTMLButtonElement, //document.getElementById("clear-button-2") as HTMLButtonElement,
]; ];
documentation_button: HTMLButtonElement = document.getElementById( documentation_button: HTMLButtonElement = document.getElementById(
"doc-button-1" "doc-button-1"
@ -235,31 +233,13 @@ export class Editor {
]; ];
let dynamicPlugins = new Compartment(); let dynamicPlugins = new Compartment();
this.state = EditorState.create({
extensions: [
...this.editorExtensions,
EditorView.lineWrapping,
dynamicPlugins.of(this.userPlugins),
Prec.highest(
keymap.of([
{
key: "Ctrl-Enter",
run: () => {
return true;
},
},
])
),
keymap.of([indentWithTab]),
],
doc: this.universes[this.selected_universe].locals[this.local_index]
.candidate,
});
this.view = new EditorView({ // ================================================================================
parent: document.getElementById("editor") as HTMLElement, // Building the documentation
state: this.state, loadSamples().then(() => {
this.docs = documentation_factory(this);
}); });
// ================================================================================
// ================================================================================ // ================================================================================
// Application event listeners // Application event listeners
@ -579,6 +559,33 @@ export class Editor {
(globalThis as Record<string, any>)[name] = value; (globalThis as Record<string, any>)[name] = value;
}); });
this.state = EditorState.create({
extensions: [
...this.editorExtensions,
EditorView.lineWrapping,
dynamicPlugins.of(this.userPlugins),
Prec.highest(
keymap.of([
{
key: "Ctrl-Enter",
run: () => {
return true;
},
},
])
),
keymap.of([indentWithTab]),
],
doc: this.universes[this.selected_universe].global.candidate,
});
this.view = new EditorView({
parent: document.getElementById("editor") as HTMLElement,
state: this.state,
});
this.changeModeFromInterface("global");
// Loading from URL bar // Loading from URL bar
let url = new URLSearchParams(window.location.search); let url = new URLSearchParams(window.location.search);
if (url !== undefined) { if (url !== undefined) {
@ -660,10 +667,11 @@ export class Editor {
const converter = new showdown.Converter({ const converter = new showdown.Converter({
emoji: true, emoji: true,
moreStyling: true, moreStyling: true,
backslashEscapesHTMLTags: true,
extensions: [showdownHighlight({ auto_detection: true }), ...bindings], extensions: [showdownHighlight({ auto_detection: true }), ...bindings],
}); });
const converted_markdown = converter.makeHtml( const converted_markdown = converter.makeHtml(
Docs[this.currentDocumentationPane] this.docs[this.currentDocumentationPane]
); );
function wrapCodeWithPre(inputString: string): string { function wrapCodeWithPre(inputString: string): string {
let newString = inputString.replace(/<code>/g, "<pre><code>"); let newString = inputString.replace(/<code>/g, "<pre><code>");