adding a new code section

This commit is contained in:
2023-08-19 15:06:32 +02:00
parent fc4107ec02
commit eb6fa8b385
3 changed files with 280 additions and 212 deletions

View File

@ -81,6 +81,7 @@
<div class="flex flex-col"> <div class="flex flex-col">
<p rel="noopener noreferrer" id="docs_introduction" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Introduction </p> <p rel="noopener noreferrer" id="docs_introduction" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Introduction </p>
<p rel="noopener noreferrer" id="docs_interface" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Interface</p> <p rel="noopener noreferrer" id="docs_interface" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Interface</p>
<p rel="noopener noreferrer" id="docs_code" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Code</p>
<p rel="noopener noreferrer" id="docs_time" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Time</p> <p rel="noopener noreferrer" id="docs_time" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Time</p>
<p rel="noopener noreferrer" id="docs_sound" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Sound</p> <p rel="noopener noreferrer" id="docs_sound" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">Sound</p>
<p rel="noopener noreferrer" id="docs_midi" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">MIDI</p> <p rel="noopener noreferrer" id="docs_midi" class="pl-2 pr-2 text-xl hover:bg-neutral-800 py-1 my-1 rounded-lg">MIDI</p>

View File

@ -1,11 +1,13 @@
const key_shortcut = (shortcut: string): string => { const key_shortcut = (shortcut: string): string => {
return `<kbd class="px-2 py-1.5 text-sm font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">${shortcut}</kbd>` return `<kbd class="px-2 py-1.5 text-sm font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">${shortcut}</kbd>`;
} };
const introduction: string = ` const introduction: string = `
# Welcome # Welcome
Welcome to the Topos documentation. This documentation companion is made to help you understand the software and the ideas behind Topos. You can summon it anytime by pressing ${key_shortcut('Ctrl + D')}. Press again to make the documentation disappear. Welcome to the Topos documentation. This documentation companion is made to help you understand the software and the ideas behind Topos. You can summon it anytime by pressing ${key_shortcut(
"Ctrl + D"
)}. Press again to make the documentation disappear.
## What is Topos? ## What is Topos?
@ -16,7 +18,11 @@ Topos is deeply inspired by the [Monome Teletype](https://monome.org/). The Tele
## Example ## Example
Press ${key_shortcut('Ctrl + G')} to switch to the global file. This is where everything starts! Evaluate the following script there by pasting and pressing ${key_shortcut('Ctrl + Enter')}: Press ${key_shortcut(
"Ctrl + G"
)} to switch to the global file. This is where everything starts! Evaluate the following script there by pasting and pressing ${key_shortcut(
"Ctrl + Enter"
)}:
<pre><code class="language-javascript"> <pre><code class="language-javascript">
if (bar() % 4 > 2 ) { if (bar() % 4 > 2 ) {
@ -32,7 +38,7 @@ if (bar() % 4 > 2 ) {
mod(24) && euclid($('ba'), 5, 8) && sound('cp').out() mod(24) && euclid($('ba'), 5, 8) && sound('cp').out()
} }
</code></pre> </code></pre>
` `;
const software_interface: string = ` const software_interface: string = `
# Interface # Interface
@ -43,19 +49,29 @@ The Topos interface is molded around the core concepts at play: _scripts_ and _u
Topos works by linking together several scripts into what is called a _universe_: Topos works by linking together several scripts into what is called a _universe_:
- the global script (${key_shortcut('Ctrl + G')}): Evaluated for every clock pulse. - the global script (${key_shortcut(
- the local scripts (${key_shortcut('Ctrl + L')}): Evaluated _on demand_. Local scripts are storing musical parts, logic or whatever you need! "Ctrl + G"
- the init script (${key_shortcut('Ctrl + I')}): Evaluated on program load. Used to set up the software (_bpm_, etc...). )}): Evaluated for every clock pulse.
- the note file (${key_shortcut('Ctrl + N')}): Not evaluated. Used to store thoughts and ideas about the music you are making. - the local scripts (${key_shortcut(
"Ctrl + L"
)}): Evaluated _on demand_. Local scripts are storing musical parts, logic or whatever you need!
- the init script (${key_shortcut(
"Ctrl + I"
)}): Evaluated on program load. Used to set up the software (_bpm_, etc...).
- the note file (${key_shortcut(
"Ctrl + N"
)}): Not evaluated. Used to store thoughts and ideas about the music you are making.
## Universes ## Universes
A set of files is called a _universe_. Topos can store several universes and switch immediately from one to another. You can switch between universes by pressing ${key_shortcut('Ctrl + B')}. You can also create a new universe by entering a name that has never been used before. _Universes_ are only known by their names. A set of files is called a _universe_. Topos can store several universes and switch immediately from one to another. You can switch between universes by pressing ${key_shortcut(
"Ctrl + B"
)}. You can also create a new universe by entering a name that has never been used before. _Universes_ are only known by their names.
Switching between universes will not stop the transport nor reset the clock. You are switching the context but time keeps flowing. This can be useful to prepare immediate transitions between songs and parts. Think of universes as an algorithmic set of music. All scripts in a given universe are aware about how many times they have been runned already. You can reset that value programatically. Switching between universes will not stop the transport nor reset the clock. You are switching the context but time keeps flowing. This can be useful to prepare immediate transitions between songs and parts. Think of universes as an algorithmic set of music. All scripts in a given universe are aware about how many times they have been runned already. You can reset that value programatically.
You can clear the current universe by pressing the flame button on the top right corner of the interface. This will clear all the scripts and the note file. **Note:** there is no shortcut for clearing a universe. We do not want to loose your work by mistake! You can clear the current universe by pressing the flame button on the top right corner of the interface. This will clear all the scripts and the note file. **Note:** there is no shortcut for clearing a universe. We do not want to loose your work by mistake!
` `;
const time: string = ` const time: string = `
# Time # Time
@ -122,7 +138,7 @@ You can use the time functions as conditionals. The following example will play
mod(24) && sound('jvbass').freq(250).out() mod(24) && sound('jvbass').freq(250).out()
} }
\`\`\` \`\`\`
` `;
const midi: string = ` const midi: string = `
# MIDI # MIDI
@ -173,17 +189,21 @@ You can use Topos to play MIDI thanks to the [WebMIDI API](https://developer.moz
## MIDI Output Selection ## MIDI Output Selection
- <icode>midi_outputs()</icode>: Prints a list of available MIDI outputs. You can then use any output name to select the MIDI output you wish to use. **Note:** this function will print to the console. You can open the console by pressing ${key_shortcut('Ctrl + Shift + I')} in many web browsers. - <icode>midi_outputs()</icode>: Prints a list of available MIDI outputs. You can then use any output name to select the MIDI output you wish to use. **Note:** this function will print to the console. You can open the console by pressing ${key_shortcut(
"Ctrl + Shift + I"
)} in many web browsers.
- <icode>midi_output(output_name: string)</icode>: Selects the MIDI output to use. You can use the <icode>midi_outputs()</icode> function to get a list of available MIDI outputs first. If the MIDI output is not available, the function will do nothing and keep on with the currently selected MIDI Port. - <icode>midi_output(output_name: string)</icode>: Selects the MIDI output to use. You can use the <icode>midi_outputs()</icode> function to get a list of available MIDI outputs first. If the MIDI output is not available, the function will do nothing and keep on with the currently selected MIDI Port.
` `;
const sound: string = ` const sound: string = `
# Sample playback # Sample playback
The Topos audio engine is based on the [SuperDough](https://www.npmjs.com/package/superdough) audio backend. It is a very powerful and flexible audio backend. It is based on the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and is capable of playing samples, synths, and effects. It is also capable of playing samples and synths in a polyphonic way. It is a very powerful tool to create complex sounds and textures. A set of default sounds are already provided by default but you can also load your own audio samples. They will be loaded through a special URL scheme using the <icode>sample</icode> function. The Topos audio engine is based on the [SuperDough](https://www.npmjs.com/package/superdough) audio backend. It is a very powerful and flexible audio backend. It is based on the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and is capable of playing samples, synths, and effects. It is also capable of playing samples and synths in a polyphonic way. It is a very powerful tool to create complex sounds and textures. A set of default sounds are already provided by default but you can also load your own audio samples. They will be loaded through a special URL scheme using the <icode>sample</icode> function.
I recommended you to run the following scripts in the global script (${key_shortcut('Ctrl + G')}). I recommended you to run the following scripts in the global script (${key_shortcut(
"Ctrl + G"
)}).
## Audio Engine ## Audio Engine
@ -257,8 +277,7 @@ No sound will play until you add <icode>.out()</icode> at the end of the chain.
| <icode>size(value: number)</icode>| Sets reverb size. | | <icode>size(value: number)</icode>| Sets reverb size. |
| <icode>velocity(value: number)</icode>| Sets velocity. | | <icode>velocity(value: number)</icode>| Sets velocity. |
| <icode>out()</icode> | Returns an object processed by the <icode>superdough</icode> function, using the current values in the <icode>values</icode> object and the <icode>pulse_duration</icode> from the <icode>app.clock</icode>. | | <icode>out()</icode> | Returns an object processed by the <icode>superdough</icode> function, using the current values in the <icode>values</icode> object and the <icode>pulse_duration</icode> from the <icode>app.clock</icode>. |
` `;
const about: string = ` const about: string = `
# About Topos # About Topos
@ -280,14 +299,36 @@ We welcome all contributions and ideas. You can find the source code on [GitHub]
You can also join us on [Discord](https://discord.gg/8Q2QV6Z6) to discuss about the project and live coding in general. You can also join us on [Discord](https://discord.gg/8Q2QV6Z6) to discuss about the project and live coding in general.
**Have fun!** **Have fun!**
` `;
const code: string = `
# Code
Topos is using the [JavaScript](https://en.wikipedia.org/wiki/JavaScript) syntax because it lives in a web browser where JS is the default programming language. It is also a language that you can learn to speak quite fast if you are already familiar with other programming lanugages. You are not going to write a lot of code anyway but familiarity with the language can help. Here are some good resources:
- [MDN (Mozilla Web Docs)](https://developer.mozilla.org/): it covers pretty much anything and is considered to be a reliable source to learn how the web currently works. We use it quite a lot to develop Topos.
- [Learn JS in Y Minutes](https://learnxinyminutes.com/docs/javascript/): a good tour of the language. Can be useful as a refresher.
- [The Modern JavaScript Tutorial](https://javascript.info/): another well known source to learn the language.
You **do not need to have any prior knowledge of programming** to use Topos. It can also be used as a **valuable resource** to learn some basic programming.
## How is the code evaluated?
The code you enter in any of the scripts is evaluated in strict mode. This tells your browser that the code you run can be optimized quite agressively. We need this because by default, **the global script is evaluated 48 times per beat**. It also means that you can crash at the speed of light :smile:. The local and initialisation scripts are evaluated on demand, one run at a time. There are some things to keep in mind:
- **about variables:** the state of your variables is not kept between iterations. If you write <icode>let a = 2</icode> and change the value later on, the value will be reset to <icode>2</icode> after each run! There are other ways to deal with variables and to share variables between scripts! Some variables like **iterators** can keep their state between iterations because they are saved **with the file itself**.
- **about errors and printing:** your code will crash! Don't worry, it will hopefully try to crash in the most gracious way possible. To check if your code is erroring, you will have to open the dev console with ${key_shortcut( "Ctrl + Shift + I")}. You cannot directly use <icode>console.log('hello, world')</icode> in the interface. You will have to open the console as well to see your messages being printed there!
`;
const functions: string = ` const functions: string = `
# Functions # Functions
` `;
const reference: string = ` const reference: string = `
# Reference # Reference
` `;
const shortcuts: string = ` const shortcuts: string = `
# Keybindings # Keybindings
@ -296,39 +337,45 @@ Topos is made to be controlled entirely with a keyboard. It is recommanded to st
## Transport ## Transport
- **Start** the transport: ${key_shortcut('Ctrl + P')}. - **Start** the transport: ${key_shortcut("Ctrl + P")}.
- **Pause** the transport: ${key_shortcut('Ctrl + S')}. - **Pause** the transport: ${key_shortcut("Ctrl + S")}.
- **Rewind** the transport: ${key_shortcut('Ctrl + R')}. - **Rewind** the transport: ${key_shortcut("Ctrl + R")}.
## Moving in the interface ## Moving in the interface
- Switch to a different universe: ${key_shortcut('Ctrl + B')}. - Switch to a different universe: ${key_shortcut("Ctrl + B")}.
- Switch to the global script: ${key_shortcut('Ctrl + G')} or ${key_shortcut('F10')}. - Switch to the global script: ${key_shortcut("Ctrl + G")} or ${key_shortcut(
- Switch to the local scripts: ${key_shortcut('Ctrl + L')} or ${key_shortcut('F11')}. "F10"
- Switch to the init script: ${key_shortcut('Ctrl + L')}. )}.
- Switch to the note file: ${key_shortcut('Ctrl + N')}. - Switch to the local scripts: ${key_shortcut("Ctrl + L")} or ${key_shortcut(
- Switch to a local file: ${key_shortcut('F1')} to ${key_shortcut('F9')}. "F11"
- Toggle the documentation: ${key_shortcut('Ctrl + D')}. )}.
- Switch to the init script: ${key_shortcut("Ctrl + L")}.
- Switch to the note file: ${key_shortcut("Ctrl + N")}.
- Switch to a local file: ${key_shortcut("F1")} to ${key_shortcut("F9")}.
- Toggle the documentation: ${key_shortcut("Ctrl + D")}.
## Evaluating code ## Evaluating code
- Evaluate the current script: ${key_shortcut('Ctrl + Enter')}. - Evaluate the current script: ${key_shortcut("Ctrl + Enter")}.
- Evaluate a local script: ${key_shortcut('Ctrl + F1')} to ${key_shortcut('Ctrl + F9')}. - Evaluate a local script: ${key_shortcut("Ctrl + F1")} to ${key_shortcut(
"Ctrl + F9"
)}.
## Special ## Special
- Switch the editor to Vim Mode: ${key_shortcut('Ctrl + V')}. - Switch the editor to Vim Mode: ${key_shortcut("Ctrl + V")}.
` `;
export const documentation = { export const documentation = {
introduction: introduction, introduction: introduction,
interface: software_interface, interface: software_interface,
time: time, code: code,
sound: sound, time: time,
midi: midi, sound: sound,
functions: functions, midi: midi,
reference: reference, functions: functions,
shortcuts: shortcuts, reference: reference,
about: about, shortcuts: shortcuts,
} about: about,
};

View File

@ -7,7 +7,7 @@ import { Extension } from "@codemirror/state";
import { vim } from "@replit/codemirror-vim"; import { vim } from "@replit/codemirror-vim";
import { AppSettings } from "./AppSettings"; import { AppSettings } from "./AppSettings";
import { editorSetup } from "./EditorSetup"; import { editorSetup } from "./EditorSetup";
import { documentation} from './Documentation'; import { documentation } from "./Documentation";
import { EditorView } from "codemirror"; import { EditorView } from "codemirror";
import { Clock } from "./Clock"; import { Clock } from "./Clock";
import { UserAPI } from "./API"; import { UserAPI } from "./API";
@ -24,46 +24,46 @@ type Documentation = { [key: string]: string };
const Docs: Documentation = documentation; 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");
import showdownHighlight from 'showdown-highlight'; import showdownHighlight from "showdown-highlight";
const classMap = { const classMap = {
h1: 'text-4xl text-white ml-4 mx-4 my-4 mb-8', h1: "text-4xl text-white ml-4 mx-4 my-4 mb-8",
h2: 'text-3xl text-white mx-4 my-4 mt-12 mb-6', h2: "text-3xl text-white mx-4 my-4 mt-12 mb-6",
ul: 'text-underline', ul: "text-underline",
li: 'ml-12 list-disc text-2xl text-white mx-4 my-4 leading-normal', li: "ml-12 list-disc text-2xl text-white mx-4 my-4 leading-normal",
p: 'text-2xl text-white mx-4 my-4 leading-normal', p: "text-2xl text-white mx-4 my-4 leading-normal",
a: 'text-2xl text-orange-300', a: "text-2xl text-orange-300",
code: "my-4 block whitespace-pre overflow-x-scroll", code: "my-4 block whitespace-pre overflow-x-scroll",
icode: "my-4 text-white font-mono bg-neutral-600", icode: "my-4 text-white font-mono bg-neutral-600",
blockquote: "text-neutral-200 border-l-4 border-neutral-500 pl-4 my-4 mx-4", blockquote: "text-neutral-200 border-l-4 border-neutral-500 pl-4 my-4 mx-4",
table: "justify-center my-8 mx-8 text-2xl w-full text-left text-white border-collapse", table:
thead: "text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400", "justify-center my-8 mx-8 text-2xl w-full text-left text-white border-collapse",
thead:
"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400",
th: "px-6 py-6", th: "px-6 py-6",
td: "py-2", td: "py-2",
tr: "py-0", tr: "py-0",
} };
const bindings = Object.keys(classMap) const bindings = Object.keys(classMap).map((key) => ({
.map(key => ({ type: "output",
type: 'output', regex: new RegExp(`<${key}([^>]*)>`, "g"),
regex: new RegExp(`<${key}([^>]*)>`, 'g'), //@ts-ignore
//@ts-ignore replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`,
replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>` }));
}));
// Importing the documentation from separate files in the ./src/documentation/* folder // Importing the documentation from separate files in the ./src/documentation/* folder
export class Editor { 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" = "local";
fontSize: Compartment; fontSize: Compartment;
withLineNumbers: Compartment; withLineNumbers: Compartment;
vimModeCompartment : Compartment; vimModeCompartment: Compartment;
chosenLanguage: Compartment chosenLanguage: Compartment;
currentDocumentationPane: string = "introduction" currentDocumentationPane: string = "introduction";
settings = new AppSettings(); settings = new AppSettings();
editorExtensions: Extension[] = []; editorExtensions: Extension[] = [];
@ -98,7 +98,9 @@ export class Editor {
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("doc-button-1") as HTMLButtonElement; documentation_button: HTMLButtonElement = document.getElementById(
"doc-button-1"
) as HTMLButtonElement;
// Script selection elements // Script selection elements
local_button: HTMLButtonElement = document.getElementById( local_button: HTMLButtonElement = document.getElementById(
@ -117,7 +119,7 @@ export class Editor {
"settings-button" "settings-button"
) as HTMLButtonElement; ) as HTMLButtonElement;
close_settings_button: HTMLButtonElement = document.getElementById( close_settings_button: HTMLButtonElement = document.getElementById(
'close-settings-button' "close-settings-button"
) as HTMLButtonElement; ) as HTMLButtonElement;
universe_viewer: HTMLDivElement = document.getElementById( universe_viewer: HTMLDivElement = document.getElementById(
"universe-viewer" "universe-viewer"
@ -137,15 +139,25 @@ export class Editor {
) as HTMLDivElement; ) as HTMLDivElement;
// Font Size Slider // Font Size Slider
font_size_slider: HTMLInputElement = document.getElementById('font-size-slider') as HTMLInputElement; font_size_slider: HTMLInputElement = document.getElementById(
font_size_witness: HTMLSpanElement = document.getElementById('font-size-witness') as HTMLSpanElement; "font-size-slider"
) as HTMLInputElement;
font_size_witness: HTMLSpanElement = document.getElementById(
"font-size-witness"
) as HTMLSpanElement;
// Line Numbers checkbox // Line Numbers checkbox
line_numbers_checkbox: HTMLInputElement = document.getElementById('show-line-numbers') as HTMLInputElement; line_numbers_checkbox: HTMLInputElement = document.getElementById(
"show-line-numbers"
) as HTMLInputElement;
// Editor mode selection // Editor mode selection
normal_mode_button: HTMLButtonElement = document.getElementById('normal-mode') as HTMLButtonElement; normal_mode_button: HTMLButtonElement = document.getElementById(
vim_mode_button: HTMLButtonElement = document.getElementById('vim-mode') as HTMLButtonElement; "normal-mode"
) as HTMLButtonElement;
vim_mode_button: HTMLButtonElement = document.getElementById(
"vim-mode"
) as HTMLButtonElement;
constructor() { constructor() {
// ================================================================================ // ================================================================================
@ -178,22 +190,22 @@ export class Editor {
this.chosenLanguage = new Compartment(); this.chosenLanguage = new Compartment();
this.fontSize = new Compartment(); this.fontSize = new Compartment();
const vimPlugin = this.settings.vimMode ? vim() : []; const vimPlugin = this.settings.vimMode ? vim() : [];
const lines = this.settings.line_numbers? lineNumbers() : []; const lines = this.settings.line_numbers ? lineNumbers() : [];
const fontSizeModif = EditorView.theme( { const fontSizeModif = EditorView.theme({
"&": { "&": {
fontSize: `${this.settings.font_size}px`, fontSize: `${this.settings.font_size}px`,
}, },
".cm-gutters": { ".cm-gutters": {
fontSize: `${this.settings.font_size}px`, fontSize: `${this.settings.font_size}px`,
} },
}) });
this.editorExtensions = [ this.editorExtensions = [
this.withLineNumbers.of(lines), this.withLineNumbers.of(lines),
this.fontSize.of(fontSizeModif), this.fontSize.of(fontSizeModif),
this.vimModeCompartment.of(vimPlugin), this.vimModeCompartment.of(vimPlugin),
editorSetup, editorSetup,
oneDark, oneDark,
this.chosenLanguage.of(javascript()), this.chosenLanguage.of(javascript()),
EditorView.updateListener.of((v: ViewUpdate) => { EditorView.updateListener.of((v: ViewUpdate) => {
v; v;
@ -238,7 +250,6 @@ export class Editor {
this.clock.stop(); this.clock.stop();
} }
if (event.ctrlKey && event.key === "p") { if (event.ctrlKey && event.key === "p") {
event.preventDefault(); event.preventDefault();
this.setButtonHighlighting("play", true); this.setButtonHighlighting("play", true);
@ -263,7 +274,7 @@ export class Editor {
if ((event.key === "Enter" || event.key === "Return") && event.ctrlKey) { if ((event.key === "Enter" || event.key === "Return") && event.ctrlKey) {
event.preventDefault(); event.preventDefault();
this.currentFile().candidate = this.view.state.doc.toString(); this.currentFile().candidate = this.view.state.doc.toString();
this.flashBackground('#2d313d', 200) this.flashBackground("#2d313d", 200);
} }
// Shift + Enter or Ctrl + E: evaluate the line // Shift + Enter or Ctrl + E: evaluate the line
@ -273,7 +284,7 @@ export class Editor {
) { ) {
event.preventDefault(); // Prevents the addition of a new line event.preventDefault(); // Prevents the addition of a new line
this.currentFile().candidate = this.view.state.doc.toString(); this.currentFile().candidate = this.view.state.doc.toString();
this.flashBackground('#2d313d', 200) this.flashBackground("#2d313d", 200);
} }
// This is the modal to switch between universes // This is the modal to switch between universes
@ -325,8 +336,8 @@ export class Editor {
(keycode, index) => { (keycode, index) => {
if (event.keyCode === keycode) { if (event.keyCode === keycode) {
event.preventDefault(); event.preventDefault();
if (event.ctrlKey) { if (event.ctrlKey) {
this.api.script(keycode - 111) this.api.script(keycode - 111);
} else { } else {
this.changeModeFromInterface("local"); this.changeModeFromInterface("local");
this.changeToLocalBuffer(index); this.changeToLocalBuffer(index);
@ -336,14 +347,14 @@ export class Editor {
} }
); );
if (event.keyCode == 121) { if (event.keyCode == 121) {
this.changeModeFromInterface("global"); this.changeModeFromInterface("global");
this.hideDocumentation(); this.hideDocumentation();
} }
if (event.keyCode == 122) { if (event.keyCode == 122) {
this.changeModeFromInterface("init"); this.changeModeFromInterface("init");
this.hideDocumentation(); this.hideDocumentation();
} }
}); });
// ================================================================================ // ================================================================================
@ -387,7 +398,7 @@ export class Editor {
this.documentation_button.addEventListener("click", () => { this.documentation_button.addEventListener("click", () => {
this.showDocumentation(); this.showDocumentation();
}) });
this.pause_buttons.forEach((button) => { this.pause_buttons.forEach((button) => {
button.addEventListener("click", () => { button.addEventListener("click", () => {
@ -416,24 +427,26 @@ export class Editor {
this.changeModeFromInterface("notes") this.changeModeFromInterface("notes")
); );
this.settings_button.addEventListener("click", () => { this.settings_button.addEventListener("click", () => {
this.font_size_slider.value = this.settings.font_size.toString(); this.font_size_slider.value = this.settings.font_size.toString();
this.font_size_witness.innerHTML = `Font Size: ${this.settings.font_size}px` this.font_size_witness.innerHTML = `Font Size: ${this.settings.font_size}px`;
this.font_size_witness?.setAttribute('style', `font-size: ${this.settings.font_size}px;`) this.font_size_witness?.setAttribute(
"style",
`font-size: ${this.settings.font_size}px;`
);
this.line_numbers_checkbox.checked = this.settings.line_numbers; this.line_numbers_checkbox.checked = this.settings.line_numbers;
let modal_settings = document.getElementById('modal-settings'); let modal_settings = document.getElementById("modal-settings");
let editor = document.getElementById('editor'); let editor = document.getElementById("editor");
modal_settings?.classList.remove('invisible') modal_settings?.classList.remove("invisible");
editor?.classList.add('invisible') editor?.classList.add("invisible");
}) });
this.close_settings_button.addEventListener("click", () => { this.close_settings_button.addEventListener("click", () => {
let modal_settings = document.getElementById('modal-settings'); let modal_settings = document.getElementById("modal-settings");
let editor = document.getElementById('editor'); let editor = document.getElementById("editor");
modal_settings?.classList.add('invisible') modal_settings?.classList.add("invisible");
editor?.classList.remove('invisible') editor?.classList.remove("invisible");
}) });
this.font_size_slider.addEventListener("input", () => { this.font_size_slider.addEventListener("input", () => {
const new_value = this.font_size_slider.value; const new_value = this.font_size_slider.value;
@ -442,19 +455,19 @@ export class Editor {
this.font_size_witness.innerHTML = `Font Size: ${new_value}px`; this.font_size_witness.innerHTML = `Font Size: ${new_value}px`;
let new_font_size = EditorView.theme({ let new_font_size = EditorView.theme({
"&": { fontSize : new_value + "px", }, "&": { fontSize: new_value + "px" },
".cm-gutters": { fontSize : new_value + "px", }, ".cm-gutters": { fontSize: new_value + "px" },
}); });
this.view.dispatch({ this.view.dispatch({
effects: this.fontSize.reconfigure(new_font_size) effects: this.fontSize.reconfigure(new_font_size),
}); });
this.settings.font_size = parseInt(new_value); this.settings.font_size = parseInt(new_value);
}) });
this.normal_mode_button.addEventListener("click", () => { this.normal_mode_button.addEventListener("click", () => {
this.settings.vimMode = false; this.settings.vimMode = false;
this.view.dispatch({effects: this.vimModeCompartment.reconfigure([])}); this.view.dispatch({ effects: this.vimModeCompartment.reconfigure([]) });
}) });
this.line_numbers_checkbox.addEventListener("change", () => { this.line_numbers_checkbox.addEventListener("change", () => {
let checked = this.line_numbers_checkbox.checked ? true : false; let checked = this.line_numbers_checkbox.checked ? true : false;
@ -462,14 +475,16 @@ export class Editor {
this.view.dispatch({ this.view.dispatch({
effects: this.withLineNumbers.reconfigure( effects: this.withLineNumbers.reconfigure(
checked ? [lineNumbers()] : [] checked ? [lineNumbers()] : []
) ),
}); });
}); });
this.vim_mode_button.addEventListener("click", () => { this.vim_mode_button.addEventListener("click", () => {
this.settings.vimMode = true; this.settings.vimMode = true;
this.view.dispatch({effects: this.vimModeCompartment.reconfigure(vim())}); this.view.dispatch({
}) effects: this.vimModeCompartment.reconfigure(vim()),
});
});
this.buffer_search.addEventListener("keydown", (event) => { this.buffer_search.addEventListener("keydown", (event) => {
if (event.key === "Enter") { if (event.key === "Enter") {
@ -483,53 +498,52 @@ export class Editor {
} }
} }
}); });
tryEvaluate( tryEvaluate(this, this.universes[this.selected_universe.toString()].init);
this,
this.universes[this.selected_universe.toString()].init,
)
// Setting up the documentation page // Setting up the documentation page
document.getElementById('docs_introduction')!.addEventListener('click', () => { document
this.currentDocumentationPane = 'introduction'; .getElementById("docs_introduction")!
.addEventListener("click", () => {
this.currentDocumentationPane = "introduction";
this.updateDocumentationContent();
});
document.getElementById("docs_interface")!.addEventListener("click", () => {
this.currentDocumentationPane = "interface";
this.updateDocumentationContent(); this.updateDocumentationContent();
}); });
document.getElementById('docs_interface')!.addEventListener('click', () => { document.getElementById("docs_code")!.addEventListener("click", () => {
this.currentDocumentationPane = 'interface'; this.currentDocumentationPane = "code";
this.updateDocumentationContent(); this.updateDocumentationContent();
}); });
document.getElementById('docs_time')!.addEventListener('click', () => { document.getElementById("docs_time")!.addEventListener("click", () => {
this.currentDocumentationPane = 'time'; this.currentDocumentationPane = "time";
this.updateDocumentationContent(); this.updateDocumentationContent();
}); });
document.getElementById('docs_sound')!.addEventListener('click', () => { document.getElementById("docs_sound")!.addEventListener("click", () => {
this.currentDocumentationPane = 'sound'; this.currentDocumentationPane = "sound";
this.updateDocumentationContent(); this.updateDocumentationContent();
}); });
document.getElementById('docs_midi')!.addEventListener('click', () => { document.getElementById("docs_midi")!.addEventListener("click", () => {
this.currentDocumentationPane = 'midi'; this.currentDocumentationPane = "midi";
this.updateDocumentationContent();
});
document.getElementById('docs_functions')!.addEventListener('click', () => {
this.currentDocumentationPane = 'functions';
this.updateDocumentationContent();
});
document.getElementById('docs_reference')!.addEventListener('click', () => {
this.currentDocumentationPane = 'reference';
this.updateDocumentationContent();
});
document.getElementById('docs_shortcuts')!.addEventListener('click', () => {
this.currentDocumentationPane = 'shortcuts';
this.updateDocumentationContent();
});
document.getElementById('docs_about')!.addEventListener('click', () => {
this.currentDocumentationPane = 'about';
this.updateDocumentationContent(); this.updateDocumentationContent();
}); });
document.getElementById("docs_functions")!.addEventListener("click", () => {
this.currentDocumentationPane = "functions";
this.updateDocumentationContent();
});
document.getElementById("docs_reference")!.addEventListener("click", () => {
this.currentDocumentationPane = "reference";
this.updateDocumentationContent();
});
document.getElementById("docs_shortcuts")!.addEventListener("click", () => {
this.currentDocumentationPane = "shortcuts";
this.updateDocumentationContent();
});
document.getElementById("docs_about")!.addEventListener("click", () => {
this.currentDocumentationPane = "about";
this.updateDocumentationContent();
});
// Passing the API to the User // Passing the API to the User
Object.entries(this.api).forEach(([name, value]) => { Object.entries(this.api).forEach(([name, value]) => {
@ -550,46 +564,47 @@ export class Editor {
} }
get local_buffer() { get local_buffer() {
return this.universes[this.selected_universe.toString()].locals[this.local_index]; return this.universes[this.selected_universe.toString()].locals[
this.local_index
];
} }
showDocumentation() { showDocumentation() {
if (document.getElementById("app")?.classList.contains("hidden")) { if (document.getElementById("app")?.classList.contains("hidden")) {
document.getElementById('app')?.classList.remove('hidden'); document.getElementById("app")?.classList.remove("hidden");
document.getElementById('documentation')?.classList.add('hidden'); document.getElementById("documentation")?.classList.add("hidden");
} else { } else {
document.getElementById('app')?.classList.add('hidden'); document.getElementById("app")?.classList.add("hidden");
document.getElementById('documentation')?.classList.remove('hidden'); document.getElementById("documentation")?.classList.remove("hidden");
// Load and convert Markdown content from the documentation file // Load and convert Markdown content from the documentation file
this.updateDocumentationContent(); this.updateDocumentationContent();
} }
} }
hideDocumentation() { hideDocumentation() {
if (document.getElementById("app")?.classList.contains("hidden")) { if (document.getElementById("app")?.classList.contains("hidden")) {
document.getElementById('app')?.classList.remove('hidden'); document.getElementById("app")?.classList.remove("hidden");
document.getElementById('documentation')?.classList.add('hidden'); document.getElementById("documentation")?.classList.add("hidden");
} }
} }
updateDocumentationContent() { updateDocumentationContent() {
const converter = new showdown.Converter({ const converter = new showdown.Converter({
emoji: true,
moreStyling: true, moreStyling: true,
extensions: [ extensions: [showdownHighlight({ auto_detection: true }), ...bindings],
showdownHighlight({auto_detection: true}),
...bindings,
]
}); });
const converted_markdown = converter.makeHtml( const converted_markdown = converter.makeHtml(
Docs[this.currentDocumentationPane] 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>");
newString = newString.replace(/<\/code>/g, '</code></pre>'); newString = newString.replace(/<\/code>/g, "</code></pre>");
return newString; return newString;
} }
document.getElementById('documentation-content')!.innerHTML = wrapCodeWithPre(converted_markdown); document.getElementById("documentation-content")!.innerHTML =
wrapCodeWithPre(converted_markdown);
} }
changeToLocalBuffer(i: number) { changeToLocalBuffer(i: number) {
@ -606,10 +621,11 @@ export class Editor {
} }
changeModeFromInterface(mode: "global" | "local" | "init" | "notes") { changeModeFromInterface(mode: "global" | "local" | "init" | "notes") {
const interface_buttons: HTMLElement[] = [ const interface_buttons: HTMLElement[] = [
this.local_button, this.global_button, this.local_button,
this.init_button, this.note_button, this.global_button,
this.init_button,
this.note_button,
]; ];
let changeColor = (button: HTMLElement) => { let changeColor = (button: HTMLElement) => {
@ -620,18 +636,17 @@ export class Editor {
button.classList.remove("text-orange-300"); button.classList.remove("text-orange-300");
} }
}); });
button.children[0].classList.remove('text-white'); button.children[0].classList.remove("text-white");
button.children[0].classList.add("text-orange-300"); button.children[0].classList.add("text-orange-300");
button.classList.add("text-orange-300"); button.classList.add("text-orange-300");
}; };
switch (mode) { switch (mode) {
case "local": case "local":
if (this.local_script_tabs.classList.contains("hidden")) { if (this.local_script_tabs.classList.contains("hidden")) {
this.local_script_tabs.classList.remove("hidden"); this.local_script_tabs.classList.remove("hidden");
} }
this.editor_mode = 'local'; this.editor_mode = "local";
this.local_index = 0; this.local_index = 0;
this.changeToLocalBuffer(this.local_index); this.changeToLocalBuffer(this.local_index);
changeColor(this.local_button); changeColor(this.local_button);
@ -640,21 +655,21 @@ export class Editor {
if (!this.local_script_tabs.classList.contains("hidden")) { if (!this.local_script_tabs.classList.contains("hidden")) {
this.local_script_tabs.classList.add("hidden"); this.local_script_tabs.classList.add("hidden");
} }
this.editor_mode = "global" this.editor_mode = "global";
changeColor(this.global_button); changeColor(this.global_button);
break; break;
case "init": case "init":
if (!this.local_script_tabs.classList.contains("hidden")) { if (!this.local_script_tabs.classList.contains("hidden")) {
this.local_script_tabs.classList.add("hidden"); this.local_script_tabs.classList.add("hidden");
} }
this.editor_mode = "init" this.editor_mode = "init";
changeColor(this.init_button); changeColor(this.init_button);
break; break;
case "notes": case "notes":
if (!this.local_script_tabs.classList.contains("hidden")) { if (!this.local_script_tabs.classList.contains("hidden")) {
this.local_script_tabs.classList.add("hidden"); this.local_script_tabs.classList.add("hidden");
} }
this.editor_mode = "notes" this.editor_mode = "notes";
changeColor(this.note_button); changeColor(this.note_button);
break; break;
} }
@ -662,8 +677,10 @@ export class Editor {
// If the editor is in notes mode, we need to update the selectedLanguage // If the editor is in notes mode, we need to update the selectedLanguage
this.view.dispatch({ this.view.dispatch({
effects: this.chosenLanguage.reconfigure(this.editor_mode == "notes" ? [markdown()] : [javascript()]) effects: this.chosenLanguage.reconfigure(
}) this.editor_mode == "notes" ? [markdown()] : [javascript()]
),
});
this.updateEditorView(); this.updateEditorView();
} }
@ -672,10 +689,12 @@ export class Editor {
button: "play" | "pause" | "stop" | "clear", button: "play" | "pause" | "stop" | "clear",
highlight: boolean highlight: boolean
) { ) {
this.flashBackground('#2d313d', 200) this.flashBackground("#2d313d", 200);
const possible_selectors = [ const possible_selectors = [
'[id^="play-button-"]', '[id^="pause-button-"]', '[id^="play-button-"]',
'[id^="clear-button-"]', '[id^="stop-button-"]', '[id^="pause-button-"]',
'[id^="clear-button-"]',
'[id^="stop-button-"]',
]; ];
let selector: number; let selector: number;
switch (button) { switch (button) {
@ -688,7 +707,7 @@ export class Editor {
case "clear": case "clear":
selector = 2; selector = 2;
break; break;
case "stop": case "stop":
selector = 3; selector = 3;
break; break;
} }
@ -716,10 +735,9 @@ export class Editor {
} }
updateEditorView(): void { updateEditorView(): void {
this.view.dispatch({ this.view.dispatch({
changes: { changes: {
from: 0, from: 0,
to: this.view.state.doc.toString().length, to: this.view.state.doc.toString().length,
insert: this.currentFile().candidate, insert: this.currentFile().candidate,
}, },
@ -731,7 +749,7 @@ export class Editor {
*/ */
currentFile(): File { currentFile(): File {
switch (this.editor_mode) { switch (this.editor_mode) {
case "global": case "global":
return this.global_buffer; return this.global_buffer;
case "local": case "local":
return this.local_buffer; return this.local_buffer;
@ -743,10 +761,9 @@ export class Editor {
} }
/** /**
* @param universeName: The name of the universe to load * @param universeName: The name of the universe to load
*/ */
loadUniverse(universeName: string): void { loadUniverse(universeName: string): void {
// Saving the current file before initiating the switch logic // Saving the current file before initiating the switch logic
this.currentFile().candidate = this.view.state.doc.toString(); this.currentFile().candidate = this.view.state.doc.toString();
@ -763,9 +780,7 @@ export class Editor {
this.updateEditorView(); this.updateEditorView();
// Evaluating the initialisation script for the selected universe // Evaluating the initialisation script for the selected universe
tryEvaluate(this, tryEvaluate(this, this.universes[this.selected_universe.toString()].init);
this.universes[this.selected_universe.toString()].init,
)
} }
openSettingsModal(): void { openSettingsModal(): void {
@ -784,7 +799,7 @@ export class Editor {
document.getElementById("modal-settings")!.classList.add("invisible"); document.getElementById("modal-settings")!.classList.add("invisible");
} }
openBuffersModal():void { openBuffersModal(): void {
// If the modal is hidden, unhide it and hide the editor // If the modal is hidden, unhide it and hide the editor
if ( if (
document.getElementById("modal-buffers")!.classList.contains("invisible") document.getElementById("modal-buffers")!.classList.contains("invisible")
@ -797,7 +812,7 @@ export class Editor {
} }
} }
closeBuffersModal():void { closeBuffersModal(): void {
// @ts-ignore // @ts-ignore
document.getElementById("buffer-search")!.value = ""; document.getElementById("buffer-search")!.value = "";
document.getElementById("editor")!.classList.remove("invisible"); document.getElementById("editor")!.classList.remove("invisible");
@ -812,13 +827,19 @@ export class Editor {
flashBackground(color: string, duration: number): void { flashBackground(color: string, duration: number): void {
// Set the flashing color // Set the flashing color
this.view.dom.style.backgroundColor = color; this.view.dom.style.backgroundColor = color;
const gutters = this.view.dom.getElementsByClassName("cm-gutter") as HTMLCollectionOf<HTMLElement>; const gutters = this.view.dom.getElementsByClassName(
Array.from(gutters).forEach(gutter => gutter.style.backgroundColor = color); "cm-gutter"
) as HTMLCollectionOf<HTMLElement>;
Array.from(gutters).forEach(
(gutter) => (gutter.style.backgroundColor = color)
);
// Reset to original color after duration // Reset to original color after duration
setTimeout(() => { setTimeout(() => {
this.view.dom.style.backgroundColor = ""; this.view.dom.style.backgroundColor = "";
Array.from(gutters).forEach(gutter => gutter.style.backgroundColor = ""); Array.from(gutters).forEach(
(gutter) => (gutter.style.backgroundColor = "")
);
}, duration); }, duration);
} }
} }
@ -836,10 +857,9 @@ function startClock() {
document document
.getElementById("start-button")! .getElementById("start-button")!
.removeEventListener("click", startClock); .removeEventListener("click", startClock);
document document.removeEventListener("click", startClock);
.removeEventListener("click", startClock);
document.removeEventListener("keydown", startOnEnter); document.removeEventListener("keydown", startOnEnter);
document.removeEventListener("click", startOnClick) document.removeEventListener("click", startOnClick);
app.clock.start(); app.clock.start();
app.view.focus(); app.view.focus();
app.setButtonHighlighting("play", true); app.setButtonHighlighting("play", true);
@ -864,8 +884,8 @@ function reportMouseCoordinates(event: MouseEvent) {
app._mouseX = event.clientX; app._mouseX = event.clientX;
app._mouseY = event.clientY; app._mouseY = event.clientY;
} }
window.addEventListener('mousemove', reportMouseCoordinates); window.addEventListener("mousemove", reportMouseCoordinates);
// When the user leaves the page, all the universes should be saved in the localStorage // When the user leaves the page, all the universes should be saved in the localStorage
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
@ -875,6 +895,6 @@ window.addEventListener("beforeunload", () => {
app.currentFile().candidate = app.view.state.doc.toString(); app.currentFile().candidate = app.view.state.doc.toString();
app.currentFile().committed = app.view.state.doc.toString(); app.currentFile().committed = app.view.state.doc.toString();
app.settings.saveApplicationToLocalStorage(app.universes, app.settings); app.settings.saveApplicationToLocalStorage(app.universes, app.settings);
app.clock.stop() app.clock.stop();
return null; return null;
}); });