@@ -295,12 +308,17 @@
-
-
-
+
+
+
+
+
diff --git a/src/API.ts b/src/API.ts
index 87da292..0b4e679 100644
--- a/src/API.ts
+++ b/src/API.ts
@@ -7,6 +7,7 @@ import { SoundEvent } from "./classes/SoundEvent";
import { MidiEvent } from "./classes/MidiEvent";
import { LRUCache } from "lru-cache";
import { InputOptions, Player } from "./classes/ZPlayer";
+import { template_universes } from "./AppSettings";
import {
samples,
initAudioOnFirstClick,
@@ -277,7 +278,7 @@ export class UserAPI {
evaluations: 0,
};
};
- cs = this.clear_script;
+ cs = this.delete_script;
copy_script = (from: number, to: number): void => {
/**
@@ -310,6 +311,24 @@ export class UserAPI {
this.app.updateKnownUniversesView();
};
+ big_bang = (): void => {
+ /**
+ * Clears all universes
+ * TODO: add documentation. This doesn't work super well.
+ */
+ if (confirm("Are you sure you want to delete all universes?")) {
+ this.app.universes = {
+ ...template_universes,
+ };
+ this.app.settings.saveApplicationToLocalStorage(
+ this.app.universes,
+ this.app.settings
+ );
+ }
+ this.app.selected_universe = "Default";
+ this.app.updateKnownUniversesView();
+ };
+
// =============================================================
// MIDI related functions
// =============================================================
diff --git a/src/main.ts b/src/main.ts
index 1f3f7e8..3db261d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -33,19 +33,24 @@ showdown.setFlavor("github");
import showdownHighlight from "showdown-highlight";
import { makeStringExtensions } from "./StringExtensions";
-
// Broadcast that you're opening a page.
localStorage.openpages = Date.now();
-window.addEventListener('storage', function(e) {
- if (e.key == "openpages") {
- // Listen if anybody else is opening the same page!
- localStorage.page_available = Date.now();
- }
- if (e.key == "page_available") {
- document.getElementById("all")!.classList.add("invisible")
- alert("Topos is already opened in another tab. Close this tab now to prevent data loss.");
- }
-}, false);
+window.addEventListener(
+ "storage",
+ function (e) {
+ if (e.key == "openpages") {
+ // Listen if anybody else is opening the same page!
+ localStorage.page_available = Date.now();
+ }
+ if (e.key == "page_available") {
+ document.getElementById("all")!.classList.add("invisible");
+ alert(
+ "Topos is already opened in another tab. Close this tab now to prevent data loss."
+ );
+ }
+ },
+ false
+);
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-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
@@ -135,6 +140,14 @@ export class Editor {
"load-universe-button"
) as HTMLButtonElement;
+ download_universe_button: HTMLButtonElement = document.getElementById(
+ "download-universes"
+ ) as HTMLButtonElement;
+
+ upload_universe_button: HTMLButtonElement = document.getElementById(
+ "upload-universes"
+ ) as HTMLButtonElement;
+
documentation_button: HTMLButtonElement = document.getElementById(
"doc-button-1"
) as HTMLButtonElement;
@@ -186,12 +199,19 @@ export class Editor {
) as HTMLDivElement;
// Font Size Slider
- font_size_slider: HTMLInputElement = document.getElementById(
- "font-size-slider"
+ font_size_input: HTMLInputElement = document.getElementById(
+ "font-size-input"
+ ) as HTMLInputElement;
+
+ // Font Family Selector
+ font_family_selector: HTMLSelectElement = document.getElementById(
+ "font-family"
+ ) as HTMLSelectElement;
+
+ // Vim mode checkbox
+ vim_mode_checkbox: HTMLInputElement = document.getElementById(
+ "vim-mode"
) as HTMLInputElement;
- font_size_witness: HTMLSpanElement = document.getElementById(
- "font-size-witness"
- ) as HTMLSpanElement;
// Line Numbers checkbox
line_numbers_checkbox: HTMLInputElement = document.getElementById(
@@ -245,7 +265,6 @@ export class Editor {
public hydra: any = this.hydra_backend.synth;
constructor() {
-
// ================================================================================
// Loading the settings
// ================================================================================
@@ -264,22 +283,23 @@ export class Editor {
this.universes = {
...this.settings.universes,
- ...template_universes
+ ...template_universes,
};
if (this.settings.load_demo_songs) {
- let random_example = examples[Math.floor(Math.random() * examples.length)];
- this.selected_universe = "Welcome"
+ let random_example =
+ examples[Math.floor(Math.random() * examples.length)];
+ this.selected_universe = "Welcome";
this.universes[this.selected_universe].global.committed = random_example;
this.universes[this.selected_universe].global.candidate = random_example;
} else {
this.selected_universe = this.settings.selected_universe;
if (this.universes[this.selected_universe] === undefined)
- this.universes[this.selected_universe] = structuredClone(template_universe)
+ this.universes[this.selected_universe] =
+ structuredClone(template_universe);
}
this.universe_viewer.innerHTML = `Topos: ${this.selected_universe}`;
-
// ================================================================================
// Audio context and clock
// ================================================================================
@@ -534,7 +554,8 @@ export class Editor {
button.addEventListener("click", () => {
this.setButtonHighlighting("clear", true);
if (confirm("Do you want to reset the current universe?")) {
- this.universes[this.selected_universe] = structuredClone(template_universe);
+ this.universes[this.selected_universe] =
+ structuredClone(template_universe);
this.updateEditorView();
}
});
@@ -544,6 +565,63 @@ export class Editor {
this.showDocumentation();
});
+ this.upload_universe_button.addEventListener("click", () => {
+ const fileInput = document.createElement("input");
+ fileInput.type = "file";
+ fileInput.accept = ".json";
+
+ fileInput.addEventListener("change", (event) => {
+ const input = event.target as HTMLInputElement;
+ const file = input.files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.readAsText(file, "UTF-8");
+
+ reader.onload = (evt) => {
+ const data = JSON.parse(evt.target!.result as string);
+ for (const [key, value] of Object.entries(data)) {
+ this.universes[key] = value as Universe;
+ }
+ };
+ reader.onerror = (evt) => {
+ console.error("An error occurred reading the file:", evt);
+ };
+ }
+ });
+
+ document.body.appendChild(fileInput);
+ fileInput.click();
+ document.body.removeChild(fileInput);
+ });
+
+ this.download_universe_button.addEventListener("click", () => {
+ // Trigger save of the universe before downloading
+ this.settings.saveApplicationToLocalStorage(
+ this.universes,
+ this.settings
+ );
+
+ // Generate a file name based on timestamp
+ let fileName = `topos-universes-${Date.now()}.json`;
+
+ // Create Blob and Object URL
+ const blob = new Blob([JSON.stringify(this.settings.universes)], {
+ type: "application/json",
+ });
+ const url = URL.createObjectURL(blob);
+
+ // Create a temporary anchor and trigger download
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = fileName;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+
+ // Revoke the Object URL to free resources
+ URL.revokeObjectURL(url);
+ });
+
this.load_universe_button.addEventListener("click", () => {
let query = this.buffer_search.value;
if (query.length > 2 && query.length < 20 && !query.includes(" ")) {
@@ -582,35 +660,55 @@ export class Editor {
this.changeModeFromInterface("notes")
);
+ this.font_family_selector.addEventListener("change", () => {
+ let new_font = this.font_family_selector.value;
+ this.settings.font = new_font;
+ let new_font_size = EditorView.theme({
+ "&": { fontSize: this.settings.font_size + "px" },
+ "&content": {
+ fontFamily: new_font,
+ fontSize: this.settings.font_size + "px",
+ },
+ ".cm-gutters": { fontSize: this.settings.font_size + "px" },
+ });
+ this.view.dispatch({
+ effects: this.fontSize.reconfigure(new_font_size),
+ });
+ });
+
+ this.font_size_input.addEventListener("input", () => {
+ let new_value: string | number = this.font_size_input.value;
+ this.settings.font_size = Math.max(8, Math.min(48, parseInt(new_value)));
+
+ let new_font_size = EditorView.theme({
+ "&": { fontSize: new_value + "px" },
+ "&content": {
+ fontFamily: this.settings.font,
+ fontSize: new_value + "px",
+ },
+ ".cm-gutters": { fontSize: new_value + "px" },
+ });
+ this.view.dispatch({
+ effects: this.fontSize.reconfigure(new_font_size),
+ });
+ this.settings.font_size = parseInt(new_value);
+ });
+
this.settings_button.addEventListener("click", () => {
- 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?.setAttribute(
- "style",
- `font-size: ${this.settings.font_size}px;`
- );
+ // Populate the font family selector
+ this.font_family_selector.value = this.settings.font;
+
+ if (this.settings.font_size === null) {
+ this.settings.font_size = 12;
+ }
+ this.font_size_input.value = this.settings.font_size.toString();
// Get the right value to update graphical widgets
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;
this.load_demo_songs.checked = this.settings.load_demo_songs;
-
- if (this.settings.vimMode) {
- let vim = document.getElementById("vim-mode-radio") as HTMLInputElement;
- let normal = document.getElementById(
- "normal-mode-radio"
- ) as HTMLInputElement;
- vim.checked = true;
- normal.checked = false;
- } else {
- let vim = document.getElementById("vim-mode-radio") as HTMLInputElement;
- let normal = document.getElementById(
- "normal-mode-radio"
- ) as HTMLInputElement;
- normal.checked = true;
- vim.checked = false;
- }
+ this.vim_mode_checkbox.checked = this.settings.vimMode;
let modal_settings = document.getElementById("modal-settings");
let editor = document.getElementById("editor");
@@ -624,29 +722,25 @@ export class Editor {
let editor = document.getElementById("editor");
modal_settings?.classList.add("invisible");
editor?.classList.remove("invisible");
+ // Update the font size once again
+ this.view.dispatch({
+ effects: this.fontSize.reconfigure(
+ EditorView.theme({
+ "&": { fontSize: this.settings.font_size + "px" },
+ "&content": {
+ fontFamily: this.settings.font,
+ fontSize: this.settings.font_size + "px",
+ },
+ ".cm-gutters": { fontSize: this.settings.font_size + "px" },
+ })
+ ),
+ });
});
this.close_universes_button.addEventListener("click", () => {
this.openBuffersModal();
});
- this.font_size_slider.addEventListener("input", () => {
- const new_value = this.font_size_slider.value;
- this.settings.font_size = parseInt(new_value);
- this.font_size_witness.style.fontSize = `${new_value}px`;
- this.font_size_witness.innerHTML = `Font Size: ${new_value}px`;
-
- let new_font_size = EditorView.theme({
- "&": { fontSize: new_value + "px" },
- "&content": { fontFamily: this.settings.font },
- ".cm-gutters": { fontSize: new_value + "px" },
- });
- this.view.dispatch({
- effects: this.fontSize.reconfigure(new_font_size),
- });
- this.settings.font_size = parseInt(new_value);
- });
-
this.share_button.addEventListener("click", async () => {
// trigger a manual save
this.currentFile().candidate = app.view.state.doc.toString();
@@ -656,9 +750,12 @@ export class Editor {
await this.share();
});
- this.normal_mode_button.addEventListener("click", () => {
- this.settings.vimMode = false;
- this.view.dispatch({ effects: this.vimModeCompartment.reconfigure([]) });
+ this.vim_mode_checkbox.addEventListener("change", () => {
+ let checked = this.vim_mode_checkbox.checked ? true : false;
+ this.settings.vimMode = checked;
+ this.view.dispatch({
+ effects: this.vimModeCompartment.reconfigure(checked ? vim() : []),
+ });
});
this.line_numbers_checkbox.addEventListener("change", () => {
@@ -695,14 +792,6 @@ export class Editor {
this.settings.load_demo_songs = checked;
});
-
- this.vim_mode_button.addEventListener("click", () => {
- this.settings.vimMode = true;
- this.view.dispatch({
- effects: this.vimModeCompartment.reconfigure(vim()),
- });
- });
-
this.universe_creator.addEventListener("submit", (event) => {
event.preventDefault();
@@ -1187,4 +1276,3 @@ window.addEventListener("beforeunload", () => {
app.clock.stop();
return null;
});
-