This commit is contained in:
2023-08-28 15:15:33 +03:00
8 changed files with 961 additions and 1164 deletions

View File

@ -41,6 +41,7 @@ export class UserAPI {
*/
private variables: { [key: string]: any } = {};
public codeExamples: { [key: string]: string } = {};
private counters: { [key: string]: any } = {};
private _drunk: DrunkWalk = new DrunkWalk(-100, 100, false);
public randomGen = Math.random;
@ -52,9 +53,32 @@ export class UserAPI {
MidiConnection: MidiConnection = new MidiConnection();
load: samples;
constructor(public app: Editor) {
//this.load = samples("github:tidalcycles/Dirt-Samples/master");
}
constructor(public app: Editor) {}
_loadUniverseFromInterface = (universe: string) => {
this.app.loadUniverse(universe as string);
this.app.openBuffersModal();
}
_deleteUniverseFromInterface = (universe: string) => {
delete this.app.universes[universe];
this.app.settings.saveApplicationToLocalStorage(
this.app.universes,
this.app.settings
);
this.app.openBuffersModal();
}
_playDocExample = (code?: string) => {
this.play();
console.log("Executing documentation example: " + this.app.selectedExample);
this.app.universes[this.app.selected_universe as string].global.candidate =
code ? code : (this.app.selectedExample as string);
tryEvaluate(
this.app,
this.app.universes[this.app.selected_universe as string].global
);
};
_all_samples = (): object => {
return soundMap.get();
@ -103,6 +127,11 @@ export class UserAPI {
// Mouse functions
// =============================================================
onmousemove = (e: MouseEvent) => {
this.app._mouseX = e.clientX;
this.app._mouseY = e.clientY;
}
public mouseX = (): number => {
/**
* @returns The current x position of the mouse
@ -1225,4 +1254,36 @@ export class UserAPI {
// is evaluated. This is useful for slowing down the script, or speeding it up. The default
// would be 1.0, which is the current rate (very speedy).
};
// =============================================================
// Legacy functions
// =============================================================
public divseq = (...args: any): any => {
const chunk_size = args[0]; // Get the first argument (chunk size)
const elements = args.slice(1); // Get the rest of the arguments as an array
const timepos = this.epulse();
const slice_count = Math.floor(
timepos / Math.floor(chunk_size * this.ppqn())
);
return elements[slice_count % elements.length];
};
public seqbeat = <T>(...array: T[]): T => {
/**
* Returns an element from an array based on the current beat.
*
* @param array - The array of values to pick from
*/
return array[this.ebeat() % array.length];
};
public seqbar = <T>(...array: T[]): T => {
/**
* Returns an element from an array based on the current bar.
*
* @param array - The array of values to pick from
*/
return array[(this.app.clock.time_position.bar + 1) % array.length];
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
import {
uniqueNamesGenerator,
adjectives,
colors,
animals,
} from "unique-names-generator";
@ -33,16 +32,19 @@ import showdown from "showdown";
showdown.setFlavor("github");
import showdownHighlight from "showdown-highlight";
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",
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",
h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 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-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
ul: "text-underline pl-6",
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-6 mx-2 my-4 leading-normal",
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",
icode:
"lg:my-4 my-1 lg:text-xl sm:text-xs text-white font-mono bg-neutral-600",
"lg:my-1 my-1 lg:text-xl sm:text-xs text-white font-mono bg-neutral-600",
blockquote: "text-neutral-200 border-l-4 border-neutral-500 pl-4 my-4 mx-4",
details:
"lg:mx-12 py-2 px-6 lg:text-2xl text-white rounded-lg bg-neutral-600",
summary: "font-semibold text-xl",
table:
"justify-center lg:my-8 my-2 lg:mx-8 mx-2 lg:text-2xl text-base w-full text-left text-white border-collapse",
thead:
@ -58,8 +60,6 @@ const bindings = Object.keys(classMap).map((key) => ({
replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`,
}));
// Importing the documentation from separate files in the ./src/documentation/* folder
export class Editor {
universes: Universes = template_universes;
selected_universe: string;
@ -76,6 +76,7 @@ export class Editor {
userPlugins: Extension[] = [];
state: EditorState;
api: UserAPI;
selectedExample: string | null = "";
docs: { [key: string]: string } = {};
// Audio stuff
@ -83,6 +84,7 @@ export class Editor {
view: EditorView;
clock: Clock;
manualPlay: boolean = false;
isPlaying: boolean = false;
// Mouse position
public _mouseX: number = 0;
@ -91,11 +93,6 @@ export class Editor {
// Transport elements
play_buttons: HTMLButtonElement[] = [
document.getElementById("play-button-1") as HTMLButtonElement,
//document.getElementById("play-button-2") as HTMLButtonElement,
];
pause_buttons: HTMLButtonElement[] = [
document.getElementById("pause-button-1") as HTMLButtonElement,
//document.getElementById("pause-button-2") as HTMLButtonElement,
];
stop_buttons: HTMLButtonElement[] = [
document.getElementById("stop-button-1") as HTMLButtonElement,
@ -105,6 +102,8 @@ export class Editor {
document.getElementById("clear-button-1") as HTMLButtonElement,
//document.getElementById("clear-button-2") as HTMLButtonElement,
];
load_universe_button: HTMLButtonElement = document.getElementById("load-universe-button") as HTMLButtonElement;
documentation_button: HTMLButtonElement = document.getElementById(
"doc-button-1"
) as HTMLButtonElement;
@ -171,7 +170,7 @@ export class Editor {
// Share button
share_button: HTMLElement = document.getElementById(
"share_button"
"share-button"
) as HTMLElement;
// Error line
@ -238,9 +237,12 @@ export class Editor {
// ================================================================================
// Building the documentation
loadSamples().then(() => {
this.docs = documentation_factory(this);
});
// loadSamples().then(() => {
// this.docs = documentation_factory(this);
// });
let pre_loading = async () => { await loadSamples(); };
pre_loading();
this.docs = documentation_factory(this);
// ================================================================================
// ================================================================================
@ -305,6 +307,18 @@ export class Editor {
// This is the modal to switch between universes
if (event.ctrlKey && event.key === "b") {
this.hideDocumentation();
let existing_universes = document.getElementById("existing-universes");
let known_universes = Object.keys(this.universes);
let final_html = "<ul class='lg:h-80 lg:w-80 lg:pb-2 lg:pt-2 overflow-y-scroll text-white lg:mb-4 border rounded-lg bg-gray-800'>";
known_universes.forEach((name) => {
final_html += `
<li onclick="_loadUniverseFromInterface('${name}')" class="hover:fill-black hover:bg-white py-2 hover:text-black flex justify-between px-4">
<p >${name}</p>
<button onclick=_deleteUniverseFromInterface('${name}')>🗑</button>
</li>`;
});
final_html = final_html + "</ul>";
existing_universes!.innerHTML = final_html;
this.openBuffersModal();
}
@ -396,9 +410,16 @@ export class Editor {
this.play_buttons.forEach((button) => {
button.addEventListener("click", () => {
this.setButtonHighlighting("play", true);
this.clock.start();
});
if (this.isPlaying) {
this.setButtonHighlighting("pause", true);
this.isPlaying = !this.isPlaying;
this.clock.pause();
} else {
this.setButtonHighlighting("play", true);
this.isPlaying = !this.isPlaying;
this.clock.start();
}
});
});
this.clear_buttons.forEach((button) => {
@ -415,17 +436,23 @@ export class Editor {
this.showDocumentation();
});
this.load_universe_button.addEventListener("click", () => {
let query = this.buffer_search.value;
if (query.length > 2 && query.length < 20 && !query.includes(" ")) {
this.loadUniverse(query);
this.settings.selected_universe = query;
this.buffer_search.value = "";
this.closeBuffersModal();
this.view.focus();
}
});
this.eval_button.addEventListener("click", () => {
this.currentFile().candidate = this.view.state.doc.toString();
this.flashBackground("#2d313d", 200);
});
this.pause_buttons.forEach((button) => {
button.addEventListener("click", () => {
this.setButtonHighlighting("pause", true);
this.clock.pause();
});
});
this.stop_buttons.forEach((button) => {
button.addEventListener("click", () => {
@ -485,11 +512,6 @@ export class Editor {
});
this.share_button.addEventListener("click", () => {
this.share_button.classList.add("animate-spin");
setInterval(
() => this.share_button.classList.remove("animate-spin"),
1000
);
// trigger a manual save
this.currentFile().candidate = app.view.state.doc.toString();
this.currentFile().committed = app.view.state.doc.toString();
@ -550,9 +572,18 @@ export class Editor {
"about",
].forEach((e) => {
let name = `docs_` + e;
document.getElementById(name)!.addEventListener("click", () => {
this.currentDocumentationPane = e;
this.updateDocumentationContent();
document.getElementById(name)!.addEventListener("click", async () => {
if (name !== "docs_samples") {
this.currentDocumentationPane = e;
this.updateDocumentationContent();
} else {
console.log('Loading samples!');
await loadSamples().then(() => {
this.docs = documentation_factory(this)
this.currentDocumentationPane = e;
this.updateDocumentationContent();
});
}
});
});
@ -597,7 +628,8 @@ export class Editor {
if (universeParam !== null) {
new_universe = JSON.parse(atob(universeParam));
const randomName: string = uniqueNamesGenerator({
dictionaries: [adjectives, colors, animals],
length: 2, separator: '_',
dictionaries: [colors, animals],
});
this.loadUniverse(randomName, new_universe["universe"]);
this.emptyUrl();
@ -675,13 +707,8 @@ export class Editor {
const converted_markdown = converter.makeHtml(
this.docs[this.currentDocumentationPane]
);
function wrapCodeWithPre(inputString: string): string {
let newString = inputString.replace(/<code>/g, "<pre><code>");
newString = newString.replace(/<\/code>/g, "</code></pre>");
return newString;
}
document.getElementById("documentation-content")!.innerHTML =
wrapCodeWithPre(converted_markdown);
converted_markdown;
}
changeToLocalBuffer(i: number) {
@ -766,10 +793,26 @@ export class Editor {
button: "play" | "pause" | "stop" | "clear",
highlight: boolean
) {
document.getElementById('play-label')!.textContent = button !== "pause" ? "Pause" : "Play";
if (button !== "pause") {
document.getElementById('pause-icon')!.classList.remove('hidden');
document.getElementById('play-icon')!.classList.add('hidden');
} else {
document.getElementById('pause-icon')!.classList.add('hidden');
document.getElementById('play-icon')!.classList.remove('hidden');
}
if (button === "stop") {
this.isPlaying == false;
document.getElementById('play-label')!.textContent = "Play";
document.getElementById('pause-icon')!.classList.add('hidden');
document.getElementById('play-icon')!.classList.remove('hidden');
}
this.flashBackground("#2d313d", 200);
const possible_selectors = [
'[id^="play-button-"]',
'[id^="pause-button-"]',
'[id^="clear-button-"]',
'[id^="stop-button-"]',
];
@ -791,7 +834,6 @@ export class Editor {
document
.querySelectorAll(possible_selectors[selector])
.forEach((button) => {
if (highlight) button.children[0].classList.add("fill-orange-300");
if (highlight) button.children[0].classList.add("animate-pulse");
});
// All other buttons must lose the highlighting
@ -800,10 +842,8 @@ export class Editor {
possible_selectors.filter((_, index) => index != selector).join(",")
)
.forEach((button) => {
button.children[0].classList.remove("fill-orange-300");
button.children[0].classList.remove("text-orange-300");
button.children[0].classList.remove("bg-orange-300");
button.children[0].classList.remove("animate-pulse");
button.children[1].classList.remove("animate-pulse");
});
}
@ -900,7 +940,6 @@ export class Editor {
// @ts-ignore
document.getElementById("buffer-search")!.value = "";
document.getElementById("editor")!.classList.remove("invisible");
document.getElementById("modal")!.classList.add("invisible");
document.getElementById("modal-buffers")!.classList.add("invisible");
}
@ -931,45 +970,7 @@ export class Editor {
// Creating the application
const app = new Editor();
// Starting the clock after displaying a modal
function startClock() {
document.getElementById("editor")!.classList.remove("invisible");
document.getElementById("modal")!.classList.add("hidden");
document
.getElementById("modal-container")!
.classList.remove("motion-safe:animate-pulse");
document
.getElementById("start-button")!
.removeEventListener("click", startClock);
document.removeEventListener("click", startClock);
document.removeEventListener("keydown", startOnEnter);
document.removeEventListener("click", startOnClick);
app.clock.start();
app.view.focus();
app.setButtonHighlighting("play", true);
}
function startOnEnter(e: KeyboardEvent) {
if (e.code === "Enter" || e.code === "Space") startClock();
}
function startOnClick(e: MouseEvent) {
if (e.button === 0) startClock();
}
document.addEventListener("keydown", startOnEnter);
document.addEventListener("click", startOnClick);
// document.getElementById("start-button")!.addEventListener("click", startClock);
/**
* @param event The mouse event
*/
function reportMouseCoordinates(event: MouseEvent) {
app._mouseX = event.clientX;
app._mouseY = event.clientY;
}
window.addEventListener("mousemove", reportMouseCoordinates);
// When the user leaves the page, all the universes should be saved in the localStorage
window.addEventListener("beforeunload", () => {
@ -982,3 +983,11 @@ window.addEventListener("beforeunload", () => {
app.clock.stop();
return null;
});
// function reportMouseCoordinates(event: MouseEvent) {
// app._mouseX = event.clientX;
// app._mouseY = event.clientY;
// }
onmousemove = function(e){console.log("mouse location:", e.clientX, e.clientY)}

View File

@ -1,3 +1,10 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.striped .col-span-3, .striped .col-span-2 {
@apply bg-neutral-300
}
}

View File

@ -1,22 +1,10 @@
const global_text =`
// Global buffer: a central buffer to command them all.
// ====================================================
// The global buffer is a special buffer used to control
// the general behavior of your universe. It is meant to
// be used as a "control room" for your universe. You can
// make use of several commands to control the execution
// flow of all the files:
// - script(universe/universes: number): run script(s)
`
const local_buffer =`
// Local buffer: nine buffers to write your algorithms.
`
const init_buffer=`
// Init buffer: a buffer to initialize the universe.
// This universe is runned once when the universe is
// loaded!
`
const note_buffer='// Notes buffer: a buffer to write your notes.'
@ -36,4 +24,4 @@ export const tutorial_universe = {
},
init: { candidate: init_buffer, committed: init_buffer, evaluations: 0 },
notes: { candidate: note_buffer },
}
}