This commit is contained in:
2023-11-26 23:06:49 +01:00
parent fc47d598ac
commit 22508acb9f
21 changed files with 243 additions and 232 deletions

View File

@ -3,28 +3,23 @@ name: Build and Push Docker Images
on: on:
push: push:
branches: branches:
- 'main' - "main"
jobs: jobs:
topos: topos:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- - name: Checkout
name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- - name: Set up QEMU
name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
- - name: Set up Docker Buildx
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- - name: Login to Docker Hub
name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Build and push
name: Build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
push: true push: true

View File

@ -1,2 +1 @@
{ {}
}

View File

@ -46,15 +46,18 @@ The `tauri` version is only here to quickstart future developments but nothing
## Docker ## Docker
### Run the application ### Run the application
`docker run -p 8001:80 yassinsiouda/topos:latest` `docker run -p 8001:80 yassinsiouda/topos:latest`
### Build and run the prod image ### Build and run the prod image
`docker compose --profile prod up` `docker compose --profile prod up`
### Build and run the dev image ### Build and run the dev image
**First installation** **First installation**
First you need to map node_modules to your local machine for your ide intellisense to work properly First you need to map node_modules to your local machine for your ide intellisense to work properly
```bash ```bash
docker compose --profile dev up -d docker compose --profile dev up -d
docker cp topos-dev:/app/node_modules . docker cp topos-dev:/app/node_modules .
@ -62,7 +65,7 @@ docker compose --profile dev down
``` ```
**Then** **Then**
```bash ```bash
docker compose --profile dev up docker compose --profile dev up
``` ```

View File

@ -1,4 +1,4 @@
version: '3.7' version: "3.7"
services: services:
topos-dev: topos-dev:
container_name: topos-dev container_name: topos-dev

View File

@ -1,6 +1,7 @@
@font-face { @font-face {
font-family: "IBM Plex Mono"; font-family: "IBM Plex Mono";
src: url("woff2/IBMPlexMono-Regular.woff2") format("woff2"), src:
url("woff2/IBMPlexMono-Regular.woff2") format("woff2"),
url("woff/IBMPlexMono-Regular.woff") format("woff"); url("woff/IBMPlexMono-Regular.woff") format("woff");
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
@ -9,7 +10,8 @@
@font-face { @font-face {
font-family: "IBM PLex Mono"; font-family: "IBM PLex Mono";
src: url("woff2/IBMPlexMono-Italic.woff2") format("woff2"), src:
url("woff2/IBMPlexMono-Italic.woff2") format("woff2"),
url("woff/IBMPlexMono-Italic.woff") format("woff"); url("woff/IBMPlexMono-Italic.woff") format("woff");
font-weight: 400; font-weight: 400;
font-style: italic; font-style: italic;
@ -18,7 +20,8 @@
@font-face { @font-face {
font-family: "IBM PLex Mono"; font-family: "IBM PLex Mono";
src: url("woff2/IBMPlexMono-Bold.woff2") format("woff2"), src:
url("woff2/IBMPlexMono-Bold.woff2") format("woff2"),
url("woff/IBMPlexMono-Bold.woff") format("woff"); url("woff/IBMPlexMono-Bold.woff") format("woff");
font-weight: 700; font-weight: 700;
font-style: normal; font-style: normal;
@ -27,7 +30,8 @@
@font-face { @font-face {
font-family: "IBM Plex Mono"; font-family: "IBM Plex Mono";
src: url("woff2/IBMPlexMono-BoldItalic.woff2") format("woff2"), src:
url("woff2/IBMPlexMono-BoldItalic.woff2") format("woff2"),
url("woff/IBMPlexMono-BoldItalic.woff") format("woff"); url("woff/IBMPlexMono-BoldItalic.woff") format("woff");
font-weight: 700; font-weight: 700;
font-style: italic; font-style: italic;
@ -37,83 +41,84 @@
@font-face { @font-face {
font-family: "Comic Mono"; font-family: "Comic Mono";
font-weight: normal; font-weight: normal;
src: url(./woff/ComicMono.woff) format("woff"), src:
url(./woff/ComicMono.woff) format("woff"),
url(./woff2/ComicMono.woff2) format("wooff2"); url(./woff2/ComicMono.woff2) format("wooff2");
} }
@font-face { @font-face {
font-family: "Comic Mono"; font-family: "Comic Mono";
font-weight: bold; font-weight: bold;
src: url(./woff/ComicMono-Bold.woff) format("woff"), src:
url(./woff/ComicMono-Bold.woff2) format("woff2"), url(./woff/ComicMono-Bold.woff) format("woff"),
url(./woff/ComicMono-Bold.woff2) format("woff2");
} }
@font-face { @font-face {
font-family: 'jgs7'; font-family: "jgs7";
src: url('./woff2/jgs7.woff2') format('woff2'), src:
url('./woff/jgs7.woff') format('woff'); url("./woff2/jgs7.woff2") format("woff2"),
url("./woff/jgs7.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: 'jgs5'; font-family: "jgs5";
src: url('./woff2/jgs5.woff2') format('woff2'), src:
url('./woff/jgs5.woff') format('woff'); url("./woff2/jgs5.woff2") format("woff2"),
url("./woff/jgs5.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: 'jgs9'; font-family: "jgs9";
src: url('./woff2/jgs9.woff2') format('woff2'), src:
url('./woff/jgs9.woff') format('woff'); url("./woff2/jgs9.woff2") format("woff2"),
font-weight: normal; url("./woff/jgs9.woff") format("woff");
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'jgs_vecto';
src: url('./woff2/jgs_vecto.woff2') format('woff2');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: 'Steps Mono'; font-family: "jgs_vecto";
src: url('./woff2/Steps-Mono.woff2') format('woff2'); src: url("./woff2/jgs_vecto.woff2") format("woff2");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: 'Steps Mono Thin'; font-family: "Steps Mono";
src: url('./woff2/Steps-Mono-Thin.woff2') format('woff2'); src: url("./woff2/Steps-Mono.woff2") format("woff2");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face { @font-face {
font-family: 'Jet Brains'; font-family: "Steps Mono Thin";
src: url('./woff2/JetBrainsMono-Regular.woff2') format('woff2'); src: url("./woff2/Steps-Mono-Thin.woff2") format("woff2");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
@font-face {
font-family: "Jet Brains";
src: url("./woff2/JetBrainsMono-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face { @font-face {
font-family: 'Jet Brains'; font-family: "Jet Brains";
src: url('./woff2/JetBrainsMono-Bold.woff2') format('woff2'); src: url("./woff2/JetBrainsMono-Bold.woff2") format("woff2");
font-weight: 700; font-weight: 700;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;

View File

@ -3,4 +3,4 @@ export default {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
} };

View File

@ -111,7 +111,7 @@ export class UserAPI {
} }
this.app.settings.saveApplicationToLocalStorage( this.app.settings.saveApplicationToLocalStorage(
this.app.universes, this.app.universes,
this.app.settings this.app.settings,
); );
this.app.updateKnownUniversesView(); this.app.updateKnownUniversesView();
}; };
@ -203,7 +203,7 @@ export class UserAPI {
// @ts-ignore // @ts-ignore
this.errorTimeoutID = setTimeout( this.errorTimeoutID = setTimeout(
() => this.app.interface.error_line.classList.add("hidden"), () => this.app.interface.error_line.classList.add("hidden"),
2000 2000,
); );
}; };
@ -217,7 +217,7 @@ export class UserAPI {
// @ts-ignore // @ts-ignore
this.printTimeoutID = setTimeout( this.printTimeoutID = setTimeout(
() => this.app.interface.error_line.classList.add("hidden"), () => this.app.interface.error_line.classList.add("hidden"),
4000 4000,
); );
}; };
@ -268,7 +268,7 @@ export class UserAPI {
*/ */
this.app.clock.tick = beat * this.app.clock.ppqn; this.app.clock.tick = beat * this.app.clock.ppqn;
this.app.clock.time_position = this.app.clock.convertTicksToTimeposition( this.app.clock.time_position = this.app.clock.convertTicksToTimeposition(
beat * this.app.clock.ppqn beat * this.app.clock.ppqn,
); );
}; };
@ -325,7 +325,7 @@ export class UserAPI {
blinkScript(this.app, "local", arg); blinkScript(this.app, "local", arg);
tryEvaluate( tryEvaluate(
this.app, this.app,
this.app.universes[this.app.selected_universe].locals[arg] this.app.universes[this.app.selected_universe].locals[arg],
); );
} }
}); });
@ -372,7 +372,7 @@ export class UserAPI {
delete this.app.universes[universe]; delete this.app.universes[universe];
this.app.settings.saveApplicationToLocalStorage( this.app.settings.saveApplicationToLocalStorage(
this.app.universes, this.app.universes,
this.app.settings this.app.settings,
); );
this.app.updateKnownUniversesView(); this.app.updateKnownUniversesView();
}; };
@ -388,7 +388,7 @@ export class UserAPI {
}; };
this.app.settings.saveApplicationToLocalStorage( this.app.settings.saveApplicationToLocalStorage(
this.app.universes, this.app.universes,
this.app.settings this.app.settings,
); );
} }
this.app.selected_universe = "Default"; this.app.selected_universe = "Default";
@ -425,7 +425,7 @@ export class UserAPI {
value: number | number[] = 60, value: number | number[] = 60,
velocity?: number | number[], velocity?: number | number[],
channel?: number | number[], channel?: number | number[],
port?: number | string | number[] | string[] port?: number | string | number[] | string[],
): MidiEvent => { ): MidiEvent => {
/** /**
* Sends a MIDI note to the current MIDI output. * Sends a MIDI note to the current MIDI output.
@ -500,7 +500,7 @@ export class UserAPI {
}; };
public active_note_events = ( public active_note_events = (
channel?: number channel?: number,
): MidiNoteEvent[] | undefined => { ): MidiNoteEvent[] | undefined => {
/** /**
* @returns A list of currently active MIDI notes * @returns A list of currently active MIDI notes
@ -637,7 +637,7 @@ export class UserAPI {
scale: number | string, scale: number | string,
channel: number = 0, channel: number = 0,
port: number | string = this.MidiConnection.currentOutputIndex || 0, port: number | string = this.MidiConnection.currentOutputIndex || 0,
soundOff: boolean = false soundOff: boolean = false,
): void => { ): void => {
/** /**
* Sends given scale to midi output for visual aid * Sends given scale to midi output for visual aid
@ -661,7 +661,7 @@ export class UserAPI {
// @ts-ignore // @ts-ignore
scale: number | string = 0, scale: number | string = 0,
channel: number = 0, channel: number = 0,
port: number | string = this.MidiConnection.currentOutputIndex || 0 port: number | string = this.MidiConnection.currentOutputIndex || 0,
): void => { ): void => {
/** /**
* Hides all notes by sending all notes off to midi output * Hides all notes by sending all notes off to midi output
@ -676,7 +676,7 @@ export class UserAPI {
midi_notes_off = ( midi_notes_off = (
channel: number = 0, channel: number = 0,
port: number | string = this.MidiConnection.currentOutputIndex || 0 port: number | string = this.MidiConnection.currentOutputIndex || 0,
): void => { ): void => {
/** /**
* Sends all notes off to midi output * Sends all notes off to midi output
@ -686,7 +686,7 @@ export class UserAPI {
midi_sound_off = ( midi_sound_off = (
channel: number = 0, channel: number = 0,
port: number | string = this.MidiConnection.currentOutputIndex || 0 port: number | string = this.MidiConnection.currentOutputIndex || 0,
): void => { ): void => {
/** /**
* Sends all sound off to midi output * Sends all sound off to midi output
@ -713,7 +713,7 @@ export class UserAPI {
public z = ( public z = (
input: string | Generator<number>, input: string | Generator<number>,
options: InputOptions = {}, options: InputOptions = {},
id: number | string = "" id: number | string = "",
): Player => { ): Player => {
const zid = "z" + id.toString(); const zid = "z" + id.toString();
const key = id === "" ? this.generateCacheKey(input, options) : zid; const key = id === "" ? this.generateCacheKey(input, options) : zid;
@ -790,7 +790,7 @@ export class UserAPI {
public counter = ( public counter = (
name: string | number, name: string | number,
limit?: number, limit?: number,
step?: number step?: number,
): number => { ): number => {
/** /**
* Returns the current value of a counter, and increments it by the step value. * Returns the current value of a counter, and increments it by the step value.
@ -1298,7 +1298,7 @@ export class UserAPI {
(value) => (value) =>
(this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) % (this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) %
Math.floor(value * this.ppqn()) === Math.floor(value * this.ppqn()) ===
0 0,
); );
return results.some((value) => value === true); return results.some((value) => value === true);
}; };
@ -1318,7 +1318,7 @@ export class UserAPI {
(value) => (value) =>
(this.app.clock.pulses_since_origin - nudgeInPulses) % (this.app.clock.pulses_since_origin - nudgeInPulses) %
Math.floor(value * barLength) === Math.floor(value * barLength) ===
0 0,
); );
return results.some((value) => value === true); return results.some((value) => value === true);
}; };
@ -1333,7 +1333,7 @@ export class UserAPI {
*/ */
const nArray = Array.isArray(n) ? n : [n]; const nArray = Array.isArray(n) ? n : [n];
const results: boolean[] = nArray.map( const results: boolean[] = nArray.map(
(value) => (this.app.clock.pulses_since_origin - nudge) % value === 0 (value) => (this.app.clock.pulses_since_origin - nudge) % value === 0,
); );
return results.some((value) => value === true); return results.some((value) => value === true);
}; };
@ -1342,7 +1342,7 @@ export class UserAPI {
public tick = (tick: number | number[], offset: number = 0): boolean => { public tick = (tick: number | number[], offset: number = 0): boolean => {
const nArray = Array.isArray(tick) ? tick : [tick]; const nArray = Array.isArray(tick) ? tick : [tick];
const results: boolean[] = nArray.map( const results: boolean[] = nArray.map(
(value) => this.app.clock.time_position.pulse === value + offset (value) => this.app.clock.time_position.pulse === value + offset,
); );
return results.some((value) => value === true); return results.some((value) => value === true);
}; };
@ -1391,7 +1391,7 @@ export class UserAPI {
public onbar = ( public onbar = (
bars: number[] | number, bars: number[] | number,
n: number = this.app.clock.time_signature[0] n: number = this.app.clock.time_signature[0],
): boolean => { ): boolean => {
let current_bar = (this.app.clock.time_position.bar % n) + 1; let current_bar = (this.app.clock.time_position.bar % n) + 1;
return typeof bars === "number" return typeof bars === "number"
@ -1419,7 +1419,7 @@ export class UserAPI {
if (decimal_part <= 0) if (decimal_part <= 0)
decimal_part = decimal_part + this.ppqn() * this.nominator(); decimal_part = decimal_part + this.ppqn() * this.nominator();
final_pulses.push( final_pulses.push(
integral_part === this.cbeat() && this.cpulse() === decimal_part integral_part === this.cbeat() && this.cpulse() === decimal_part,
); );
}); });
return final_pulses.some((p) => p == true); return final_pulses.some((p) => p == true);
@ -1501,7 +1501,7 @@ export class UserAPI {
iterator: number, iterator: number,
pulses: number, pulses: number,
length: number, length: number,
rotate: number = 0 rotate: number = 0,
): boolean => { ): boolean => {
/** /**
* Returns a euclidean cycle of size length, with n pulses, rotated or not. * Returns a euclidean cycle of size length, with n pulses, rotated or not.
@ -1520,7 +1520,7 @@ export class UserAPI {
div: number, div: number,
pulses: number, pulses: number,
length: number, length: number,
rotate: number = 0 rotate: number = 0,
): boolean => { ): boolean => {
return ( return (
this.beat(div) && this._euclidean_cycle(pulses, length, rotate).beat(div) this.beat(div) && this._euclidean_cycle(pulses, length, rotate).beat(div)
@ -1530,7 +1530,7 @@ export class UserAPI {
_euclidean_cycle( _euclidean_cycle(
pulses: number, pulses: number,
length: number, length: number,
rotate: number = 0 rotate: number = 0,
): boolean[] { ): boolean[] {
if (pulses == length) return Array.from({ length }, () => true); if (pulses == length) return Array.from({ length }, () => true);
function startsDescent(list: number[], i: number): boolean { function startsDescent(list: number[], i: number): boolean {
@ -1541,7 +1541,7 @@ export class UserAPI {
if (pulses >= length) return [true]; if (pulses >= length) return [true];
const resList = Array.from( const resList = Array.from(
{ length }, { length },
(_, i) => (((pulses * (i - 1)) % length) + length) % length (_, i) => (((pulses * (i - 1)) % length) + length) % length,
); );
let cycle = resList.map((_, i) => startsDescent(resList, i)); let cycle = resList.map((_, i) => startsDescent(resList, i));
if (rotate != 0) { if (rotate != 0) {
@ -1660,7 +1660,7 @@ export class UserAPI {
triangle = ( triangle = (
freq: number = 1, freq: number = 1,
times: number = 1, times: number = 1,
offset: number = 0 offset: number = 0,
): number => { ): number => {
/** /**
* Returns a triangle wave between -1 and 1. * Returns a triangle wave between -1 and 1.
@ -1677,7 +1677,7 @@ export class UserAPI {
utriangle = ( utriangle = (
freq: number = 1, freq: number = 1,
times: number = 1, times: number = 1,
offset: number = 0 offset: number = 0,
): number => { ): number => {
/** /**
* Returns a triangle wave between 0 and 1. * Returns a triangle wave between 0 and 1.
@ -1694,7 +1694,7 @@ export class UserAPI {
freq: number = 1, freq: number = 1,
times: number = 1, times: number = 1,
offset: number = 0, offset: number = 0,
duty: number = 0.5 duty: number = 0.5,
): number => { ): number => {
/** /**
* Returns a square wave with a specified duty cycle between -1 and 1. * Returns a square wave with a specified duty cycle between -1 and 1.
@ -1714,7 +1714,7 @@ export class UserAPI {
freq: number = 1, freq: number = 1,
times: number = 1, times: number = 1,
offset: number = 0, offset: number = 0,
duty: number = 0.5 duty: number = 0.5,
): number => { ): number => {
/** /**
* Returns a square wave between 0 and 1. * Returns a square wave between 0 and 1.
@ -1774,7 +1774,7 @@ export class UserAPI {
*/ */
const sum = values.reduce( const sum = values.reduce(
(accumulator, currentValue) => accumulator + currentValue, (accumulator, currentValue) => accumulator + currentValue,
0 0,
); );
return sum / values.length; return sum / values.length;
}; };
@ -1784,7 +1784,7 @@ export class UserAPI {
yMin: number, yMin: number,
yMax: number, yMax: number,
xMin: number, xMin: number,
xMax: number xMax: number,
): number => { ): number => {
const percent = (inputY - yMin) / (yMax - yMin); const percent = (inputY - yMin) / (yMax - yMin);
const outputX = percent * (xMax - xMin) + xMin; const outputX = percent * (xMax - xMin) + xMin;
@ -1814,7 +1814,7 @@ export class UserAPI {
lang: string = "en-US", lang: string = "en-US",
voice: number = 0, voice: number = 0,
rate: number = 1, rate: number = 1,
pitch: number = 1 pitch: number = 1,
): void => { ): void => {
/* /*
* Speaks the given text using the browser's speech synthesis API. * Speaks the given text using the browser's speech synthesis API.
@ -1889,7 +1889,7 @@ export class UserAPI {
const elements = args.slice(1); // Get the rest of the arguments as an array const elements = args.slice(1); // Get the rest of the arguments as an array
const timepos = this.app.clock.pulses_since_origin; const timepos = this.app.clock.pulses_since_origin;
const slice_count = Math.floor( const slice_count = Math.floor(
timepos / Math.floor(chunk_size * this.ppqn()) timepos / Math.floor(chunk_size * this.ppqn()),
); );
return elements[slice_count % elements.length]; return elements[slice_count % elements.length];
}; };
@ -1917,7 +1917,7 @@ export class UserAPI {
// ============================================================= // =============================================================
register = (name: string, operation: EventOperation<AbstractEvent>): void => { register = (name: string, operation: EventOperation<AbstractEvent>): void => {
AbstractEvent.prototype[name] = function( AbstractEvent.prototype[name] = function (
this: AbstractEvent, this: AbstractEvent,
...args: any[] ...args: any[]
) { ) {
@ -2024,7 +2024,7 @@ export class UserAPI {
".cm-comment": { ".cm-comment": {
fontFamily: commentFont, fontFamily: commentFont,
}, },
}) }),
), ),
}); });
}; };

View File

@ -14,7 +14,7 @@ export const drawCircle = (
x: number, x: number,
y: number, y: number,
radius: number, radius: number,
color: string color: string,
): void => { ): void => {
// @ts-ignore // @ts-ignore
const canvas: HTMLCanvasElement = app.interface.feedback; const canvas: HTMLCanvasElement = app.interface.feedback;
@ -36,7 +36,7 @@ export const blinkScript = (
*/ */
app: Editor, app: Editor,
script: "local" | "global" | "init", script: "local" | "global" | "init",
no?: number no?: number,
) => { ) => {
if (no !== undefined && no < 1 && no > 9) return; if (no !== undefined && no < 1 && no > 9) return;
const blinkDuration = const blinkDuration =
@ -55,7 +55,7 @@ export const blinkScript = (
horizontalOffset + shift, horizontalOffset + shift,
app.interface.feedback.clientHeight - 15, app.interface.feedback.clientHeight - 15,
8, 8,
"#fdba74" "#fdba74",
); );
}; };
@ -91,7 +91,7 @@ export const blinkScript = (
0, 0,
0, 0,
(app.interface.feedback as HTMLCanvasElement).width, (app.interface.feedback as HTMLCanvasElement).width,
(app.interface.feedback as HTMLCanvasElement).height (app.interface.feedback as HTMLCanvasElement).height,
); );
}, blinkDuration); }, blinkDuration);
} }
@ -137,7 +137,7 @@ let lastRenderTime: number = 0;
export const runOscilloscope = ( export const runOscilloscope = (
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
app: Editor app: Editor,
): void => { ): void => {
/** /**
* Runs the oscilloscope visualization on the provided canvas element. * Runs the oscilloscope visualization on the provided canvas element.
@ -157,7 +157,7 @@ export const runOscilloscope = (
width: number, width: number,
height: number, height: number,
offset_height: number, offset_height: number,
offset_width: number offset_width: number,
) { ) {
const maxFPS = 30; const maxFPS = 30;
const now = performance.now(); const now = performance.now();
@ -172,11 +172,11 @@ export const runOscilloscope = (
const performanceFactor = 1; const performanceFactor = 1;
const reducedDataSize = Math.floor( const reducedDataSize = Math.floor(
freqDataArray.length * performanceFactor freqDataArray.length * performanceFactor,
); );
const numBars = Math.min( const numBars = Math.min(
reducedDataSize, reducedDataSize,
app.osc.orientation === "horizontal" ? width : height app.osc.orientation === "horizontal" ? width : height,
); );
const barWidth = const barWidth =
app.osc.orientation === "horizontal" ? width / numBars : height / numBars; app.osc.orientation === "horizontal" ? width / numBars : height / numBars;
@ -189,7 +189,7 @@ export const runOscilloscope = (
for (let i = 0; i < numBars; i++) { for (let i = 0; i < numBars; i++) {
barHeight = Math.floor( barHeight = Math.floor(
freqDataArray[Math.floor((i * freqDataArray.length) / numBars)] * freqDataArray[Math.floor((i * freqDataArray.length) / numBars)] *
((height / 256) * app.osc.size) ((height / 256) * app.osc.size),
); );
if (app.osc.orientation === "horizontal") { if (app.osc.orientation === "horizontal") {
@ -197,7 +197,7 @@ export const runOscilloscope = (
x + offset_width, x + offset_width,
(height - barHeight) / 2 + offset_height, (height - barHeight) / 2 + offset_height,
barWidth + 1, barWidth + 1,
barHeight barHeight,
); );
x += barWidth; x += barWidth;
} else { } else {
@ -205,7 +205,7 @@ export const runOscilloscope = (
(width - barHeight) / 2 + offset_width, (width - barHeight) / 2 + offset_width,
y + offset_height, y + offset_height,
barHeight, barHeight,
barWidth + 1 barWidth + 1,
); );
y += barWidth; y += barWidth;
} }
@ -234,7 +234,7 @@ export const runOscilloscope = (
-OFFSET_WIDTH, -OFFSET_WIDTH,
-OFFSET_HEIGHT, -OFFSET_HEIGHT,
WIDTH + 2 * OFFSET_WIDTH, WIDTH + 2 * OFFSET_WIDTH,
HEIGHT + 2 * OFFSET_HEIGHT HEIGHT + 2 * OFFSET_HEIGHT,
); );
return; return;
} }
@ -261,7 +261,7 @@ export const runOscilloscope = (
-OFFSET_WIDTH, -OFFSET_WIDTH,
-OFFSET_HEIGHT, -OFFSET_HEIGHT,
WIDTH + 2 * OFFSET_WIDTH, WIDTH + 2 * OFFSET_WIDTH,
HEIGHT + 2 * OFFSET_HEIGHT HEIGHT + 2 * OFFSET_HEIGHT,
); );
} }
canvasCtx.lineWidth = app.osc.thickness; canvasCtx.lineWidth = app.osc.thickness;

View File

@ -48,7 +48,10 @@ export class Clock {
lastPlayPressTime: number; lastPlayPressTime: number;
totalPauseTime: number; totalPauseTime: number;
constructor(public app: Editor, ctx: AudioContext) { constructor(
public app: Editor,
ctx: AudioContext,
) {
this.time_position = { bar: 0, beat: 0, pulse: 0 }; this.time_position = { bar: 0, beat: 0, pulse: 0 };
this.time_signature = [4, 4]; this.time_signature = [4, 4];
this.logicalTime = 0; this.logicalTime = 0;

View File

@ -47,7 +47,7 @@ export const makeExampleFactory = (application: Editor): Function => {
const make_example = ( const make_example = (
description: string, description: string,
code: string, code: string,
open: boolean = false open: boolean = false,
) => { ) => {
const codeId = `codeExample${application.exampleCounter++}`; const codeId = `codeExample${application.exampleCounter++}`;
// Store the code snippet in the data structure // Store the code snippet in the data structure
@ -160,7 +160,7 @@ export const updateDocumentationContent = (app: Editor, bindings: any) => {
extensions: [showdownHighlight({ auto_detection: true }), ...bindings], extensions: [showdownHighlight({ auto_detection: true }), ...bindings],
}); });
const converted_markdown = converter.makeHtml( const converted_markdown = converter.makeHtml(
app.docs[app.currentDocumentationPane] app.docs[app.currentDocumentationPane],
); );
document.getElementById("documentation-content")!.innerHTML = document.getElementById("documentation-content")!.innerHTML =
converted_markdown; converted_markdown;

View File

@ -8,7 +8,7 @@ const codeReplace = (code: string): string => {
const tryCatchWrapper = async ( const tryCatchWrapper = async (
application: Editor, application: Editor,
code: string code: string,
): Promise<boolean> => { ): Promise<boolean> => {
/** /**
* Wraps the provided code in a try-catch block and executes it. * Wraps the provided code in a try-catch block and executes it.
@ -20,7 +20,7 @@ const tryCatchWrapper = async (
try { try {
await new Function(`"use strict"; ${codeReplace(code)}`).call( await new Function(`"use strict"; ${codeReplace(code)}`).call(
application.api application.api,
); );
return true; return true;
} catch (error) { } catch (error) {
@ -48,7 +48,7 @@ const addFunctionToCache = (code: string, fn: Function) => {
export const tryEvaluate = async ( export const tryEvaluate = async (
application: Editor, application: Editor,
code: File, code: File,
timeout = 5000 timeout = 5000,
): Promise<void> => { ): Promise<void> => {
/** /**
* Tries to evaluate the provided code within a specified timeout period. * Tries to evaluate the provided code within a specified timeout period.
@ -77,7 +77,7 @@ export const tryEvaluate = async (
if (isCodeValid) { if (isCodeValid) {
code.committed = code.candidate; code.committed = code.candidate;
const newFunction = new Function( const newFunction = new Function(
`"use strict"; ${codeReplace(wrappedCode)}` `"use strict"; ${codeReplace(wrappedCode)}`,
); );
addFunctionToCache(candidateCode, newFunction); addFunctionToCache(candidateCode, newFunction);
} else { } else {
@ -93,7 +93,7 @@ export const tryEvaluate = async (
export const evaluate = async ( export const evaluate = async (
application: Editor, application: Editor,
code: File, code: File,
timeout = 1000 timeout = 1000,
): Promise<void> => { ): Promise<void> => {
/** /**
* Evaluates the given code using the provided application and timeout. * Evaluates the given code using the provided application and timeout.
@ -117,7 +117,7 @@ export const evaluate = async (
export const evaluateOnce = async ( export const evaluateOnce = async (
application: Editor, application: Editor,
code: string code: string,
): Promise<void> => { ): Promise<void> => {
/** /**
* Evaluates the code once without any caching or error-handling mechanisms besides the tryCatchWrapper. * Evaluates the code once without any caching or error-handling mechanisms besides the tryCatchWrapper.

View File

@ -154,7 +154,7 @@ export class AppSettings {
constructor() { constructor() {
const settingsFromStorage = JSON.parse( const settingsFromStorage = JSON.parse(
localStorage.getItem("topos") || "{}" localStorage.getItem("topos") || "{}",
); );
if (settingsFromStorage && Object.keys(settingsFromStorage).length !== 0) { if (settingsFromStorage && Object.keys(settingsFromStorage).length !== 0) {
@ -210,7 +210,7 @@ export class AppSettings {
saveApplicationToLocalStorage( saveApplicationToLocalStorage(
universes: Universes, universes: Universes,
settings: Settings settings: Settings,
): void { ): void {
/** /**
* Main method to store the application to local storage. * Main method to store the application to local storage.
@ -328,7 +328,7 @@ export const loadUniverserFromUrl = (app: Editor): void => {
export const loadUniverse = ( export const loadUniverse = (
app: Editor, app: Editor,
universeName: string, universeName: string,
universe: Universe = template_universe universe: Universe = template_universe,
): void => { ): void => {
/** /**
* Loads a universe into the application. * Loads a universe into the application.

View File

@ -1,6 +1,6 @@
export function objectWithArraysToArrayOfObjects( export function objectWithArraysToArrayOfObjects(
input: Record<string, any>, input: Record<string, any>,
arraysToArrays: string[] arraysToArrays: string[],
): Record<string, any>[] { ): Record<string, any>[] {
/* /*
* Transforms object with arrays into array of objects * Transforms object with arrays into array of objects
@ -24,7 +24,7 @@ export function objectWithArraysToArrayOfObjects(
acc.keys.push(key); acc.keys.push(key);
return acc; return acc;
}, },
{ keys: [] as string[], maxLength: 0 } { keys: [] as string[], maxLength: 0 },
); );
const output: Record<string, any>[] = []; const output: Record<string, any>[] = [];
@ -44,7 +44,7 @@ export function objectWithArraysToArrayOfObjects(
export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>( export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
array: T[], array: T[],
mergeObject: Record<string, any> = {} mergeObject: Record<string, any> = {},
): Record<string, any> { ): Record<string, any> {
/* /*
* Transforms array of objects into object with arrays * Transforms array of objects into object with arrays
@ -54,7 +54,8 @@ export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
* @returns {object} Merged object with arrays * @returns {object} Merged object with arrays
* *
*/ */
return array.reduce((acc, obj) => { return array.reduce(
(acc, obj) => {
const mergedObj = { ...obj, ...mergeObject }; const mergedObj = { ...obj, ...mergeObject };
Object.keys(mergedObj).forEach((key) => { Object.keys(mergedObj).forEach((key) => {
if (!acc[key]) { if (!acc[key]) {
@ -63,12 +64,14 @@ export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
acc[key].push(mergedObj[key]); acc[key].push(mergedObj[key]);
}); });
return acc; return acc;
}, {} as Record<string, any>); },
{} as Record<string, any>,
);
} }
export function filterObject( export function filterObject(
obj: Record<string, any>, obj: Record<string, any>,
filter: string[] filter: string[],
): Record<string, any> { ): Record<string, any> {
/* /*
* Filter certain keys from object * Filter certain keys from object
@ -79,6 +82,6 @@ export function filterObject(
* *
*/ */
return Object.fromEntries( return Object.fromEntries(
Object.entries(obj).filter(([key]) => filter.includes(key)) Object.entries(obj).filter(([key]) => filter.includes(key)),
); );
} }

View File

@ -32,13 +32,13 @@ export const saveBeforeExit = (app: Editor): null => {
export const installWindowBehaviors = ( export const installWindowBehaviors = (
app: Editor, app: Editor,
window: Window, window: Window,
preventMultipleTabs: boolean = false preventMultipleTabs: boolean = false,
) => { ) => {
window.addEventListener("resize", () => window.addEventListener("resize", () =>
handleResize(app.interface.scope as HTMLCanvasElement) handleResize(app.interface.scope as HTMLCanvasElement),
); );
window.addEventListener("resize", () => window.addEventListener("resize", () =>
handleResize(app.interface.feedback as HTMLCanvasElement) handleResize(app.interface.feedback as HTMLCanvasElement),
); );
window.addEventListener("beforeunload", (event) => { window.addEventListener("beforeunload", (event) => {
event.preventDefault(); event.preventDefault();
@ -61,11 +61,11 @@ export const installWindowBehaviors = (
if (e.key == "page_available") { if (e.key == "page_available") {
document.getElementById("all")!.classList.add("invisible"); document.getElementById("all")!.classList.add("invisible");
alert( alert(
"Topos is already opened in another tab. Close this tab now to prevent data loss." "Topos is already opened in another tab. Close this tab now to prevent data loss.",
); );
} }
}, },
false false,
); );
} }
}; };

View File

@ -38,7 +38,7 @@ export class SoundEvent extends AudibleEvent {
public updateValue<T>( public updateValue<T>(
key: string, key: string,
value: T | T[] | SoundParams[] | null value: T | T[] | SoundParams[] | null,
): this { ): this {
if (value == null) return this; if (value == null) return this;
this.values[key] = value; this.values[key] = value;
@ -82,7 +82,7 @@ export class SoundEvent extends AudibleEvent {
a: number, a: number,
d: number, d: number,
s: number, s: number,
r: number r: number,
) { ) {
self.updateValue("fmattack", a); self.updateValue("fmattack", a);
self.updateValue("fmdecay", d); self.updateValue("fmdecay", d);
@ -106,7 +106,7 @@ export class SoundEvent extends AudibleEvent {
a: number, a: number,
d: number, d: number,
s: number, s: number,
r: number r: number,
) { ) {
self.updateValue("attack", a); self.updateValue("attack", a);
self.updateValue("decay", d); self.updateValue("decay", d);
@ -152,7 +152,7 @@ export class SoundEvent extends AudibleEvent {
a: number, a: number,
d: number, d: number,
s: number, s: number,
r: number r: number,
) { ) {
self.updateValue("lpenv", depth); self.updateValue("lpenv", depth);
self.updateValue("lpattack", a); self.updateValue("lpattack", a);
@ -198,7 +198,7 @@ export class SoundEvent extends AudibleEvent {
a: number, a: number,
d: number, d: number,
s: number, s: number,
r: number r: number,
) { ) {
self.updateValue("hpenv", depth); self.updateValue("hpenv", depth);
self.updateValue("hpattack", a); self.updateValue("hpattack", a);
@ -241,7 +241,7 @@ export class SoundEvent extends AudibleEvent {
a: number, a: number,
d: number, d: number,
s: number, s: number,
r: number r: number,
) { ) {
self.updateValue("bpenv", depth); self.updateValue("bpenv", depth);
self.updateValue("bpattack", a); self.updateValue("bpattack", a);
@ -336,7 +336,10 @@ export class SoundEvent extends AudibleEvent {
}, },
}; };
constructor(sound: string | string[] | SoundParams, public app: Editor) { constructor(
sound: string | string[] | SoundParams,
public app: Editor,
) {
super(app); super(app);
this.nudge = app.dough_nudge / 100; this.nudge = app.dough_nudge / 100;
@ -367,7 +370,7 @@ export class SoundEvent extends AudibleEvent {
} }
private processSound = ( private processSound = (
sound: string | string[] | SoundParams | SoundParams[] sound: string | string[] | SoundParams | SoundParams[],
): SoundParams => { ): SoundParams => {
if (Array.isArray(sound) && typeof sound[0] === "string") { if (Array.isArray(sound) && typeof sound[0] === "string") {
const s: string[] = []; const s: string[] = [];
@ -438,7 +441,7 @@ export class SoundEvent extends AudibleEvent {
(event.key as number) || "C4", (event.key as number) || "C4",
(event.pitch as number) || 0, (event.pitch as number) || 0,
(event.parsedScale as number[]) || event.scale || "MAJOR", (event.parsedScale as number[]) || event.scale || "MAJOR",
(event.octave as number) || 0 (event.octave as number) || 0,
); );
event.note = note; event.note = note;
event.freq = midiToFreq(note); event.freq = midiToFreq(note);
@ -458,7 +461,7 @@ export class SoundEvent extends AudibleEvent {
public invert = (howMany: number = 0) => { public invert = (howMany: number = 0) => {
if (this.values.chord) { if (this.values.chord) {
let notes = this.values.chord.map( let notes = this.values.chord.map(
(obj: { [key: string]: number }) => obj.note (obj: { [key: string]: number }) => obj.note,
); );
notes = howMany < 0 ? [...notes].reverse() : notes; notes = howMany < 0 ? [...notes].reverse() : notes;
for (let i = 0; i < Math.abs(howMany); i++) { for (let i = 0; i < Math.abs(howMany); i++) {
@ -500,7 +503,7 @@ export class SoundEvent extends AudibleEvent {
superdough( superdough(
filteredEvent, filteredEvent,
this.nudge - this.app.clock.deviation, this.nudge - this.app.clock.deviation,
filteredEvent.dur filteredEvent.dur,
); );
} }
}; };

View File

@ -29,7 +29,7 @@ export class Player extends AbstractEvent {
input: string | number | Generator<number>, input: string | number | Generator<number>,
options: InputOptions, options: InputOptions,
public app: Editor, public app: Editor,
zid: string = "" zid: string = "",
) { ) {
super(app); super(app);
this.options = options; this.options = options;
@ -159,7 +159,7 @@ export class Player extends AbstractEvent {
if (this.areWeThereYet()) { if (this.areWeThereYet()) {
const event = this.next() as Pitch | Chord | ZRest; const event = this.next() as Pitch | Chord | ZRest;
const noteLengthInSeconds = this.app.clock.convertPulseToSecond( const noteLengthInSeconds = this.app.clock.convertPulseToSecond(
event.duration * 4 * this.app.clock.ppqn event.duration * 4 * this.app.clock.ppqn,
); );
if (event instanceof Pitch) { if (event instanceof Pitch) {
const obj = event.getExisting( const obj = event.getExisting(
@ -169,7 +169,7 @@ export class Player extends AbstractEvent {
"key", "key",
"scale", "scale",
"octave", "octave",
"parsedScale" "parsedScale",
) as SoundParams; ) as SoundParams;
if (event.sound) name = event.sound as string; if (event.sound) name = event.sound as string;
if (event.soundIndex) obj.n = event.soundIndex as number; if (event.soundIndex) obj.n = event.soundIndex as number;
@ -184,14 +184,14 @@ export class Player extends AbstractEvent {
"key", "key",
"scale", "scale",
"octave", "octave",
"parsedScale" "parsedScale",
); );
}) as SoundParams[]; }) as SoundParams[];
const add = { dur: noteLengthInSeconds } as SoundParams; const add = { dur: noteLengthInSeconds } as SoundParams;
if (name) add.s = name; if (name) add.s = name;
let sound = arrayOfObjectsToObjectWithArrays( let sound = arrayOfObjectsToObjectWithArrays(
pitches, pitches,
add add,
) as SoundParams; ) as SoundParams;
return new SoundEvent(sound, this.app); return new SoundEvent(sound, this.app);
} else if (event instanceof ZRest) { } else if (event instanceof ZRest) {
@ -212,7 +212,7 @@ export class Player extends AbstractEvent {
"key", "key",
"scale", "scale",
"octave", "octave",
"parsedScale" "parsedScale",
) as MidiParams; ) as MidiParams;
if (event instanceof Pitch) { if (event instanceof Pitch) {
if (event.soundIndex) obj.channel = event.soundIndex as number; if (event.soundIndex) obj.channel = event.soundIndex as number;

View File

@ -20,11 +20,11 @@ Some features have been included as a bonus. These features are often about patt
${makeExample( ${makeExample(
"Hydra integration", "Hydra integration",
`beat(4) :: hydra.osc(3, 0.5, 2).out()`, `beat(4) :: hydra.osc(3, 0.5, 2).out()`,
true true,
)} )}
Close the documentation to see the effect: ${key_shortcut( Close the documentation to see the effect: ${key_shortcut(
"Ctrl+D" "Ctrl+D",
)}! **Boom, all shiny!** )}! **Boom, all shiny!**
Be careful not to call <ic>hydra</ic> too often as it can impact performances. You can use any rhythmical function like <ic>beat()</ic> function to limit the number of function calls. You can write any Topos code like <ic>[1,2,3].beat()</ic> to bring some life and movement in your Hydra sketches. Be careful not to call <ic>hydra</ic> too often as it can impact performances. You can use any rhythmical function like <ic>beat()</ic> function to limit the number of function calls. You can write any Topos code like <ic>[1,2,3].beat()</ic> to bring some life and movement in your Hydra sketches.
@ -37,7 +37,7 @@ ${makeExample(
beat(4) :: stop_hydra() // this one beat(4) :: stop_hydra() // this one
beat(4) :: hydra.hush() // or this one beat(4) :: hydra.hush() // or this one
`, `,
true true,
)} )}
@ -48,7 +48,7 @@ You can change Hydra resolution using this simple method:
${makeExample( ${makeExample(
"Changing Hydra resolution", "Changing Hydra resolution",
`hydra.setResolution(1024, 768)`, `hydra.setResolution(1024, 768)`,
true true,
)} )}
### Documentation ### Documentation
@ -82,7 +82,7 @@ beat(0.25)::gif({
posX: ir(1,1200), // CSS Horizontal Position posX: ir(1,1200), // CSS Horizontal Position
posY: ir(1, 800), // CSS Vertical Position posY: ir(1, 800), // CSS Vertical Position
`, `,
true true,
)} )}
`; `;
}; };

View File

@ -70,7 +70,7 @@ ${makeExample(
beat(0.5)::sound('wt_stereo').n([0, 1].pick()).ad(0, .25).out() beat(0.5)::sound('wt_stereo').n([0, 1].pick()).ad(0, .25).out()
`, `,
true, true,
)} )}
Pick one folder and spend some time exploring it. There is a lot of different waveforms. Pick one folder and spend some time exploring it. There is a lot of different waveforms.
@ -89,7 +89,7 @@ ${makeExample(
beat(0.5)::sound(['bd', 'cp'].pick()).bank("AkaiLinn").out() beat(0.5)::sound(['bd', 'cp'].pick()).bank("AkaiLinn").out()
`, `,
true, true,
)} )}
Here is the complete list of available machines: Here is the complete list of available machines:
@ -124,7 +124,7 @@ ${makeExample(
beat(4)::sound('amen1').stretch(4).out() beat(4)::sound('amen1').stretch(4).out()
`, `,
true, true,
)} )}
The stretch should be adapted based on the length of each amen break. The stretch should be adapted based on the length of each amen break.

View File

@ -246,7 +246,7 @@ export class Editor {
* If any required elements or templates are missing, warning messages are logged and the function returns early. * If any required elements or templates are missing, warning messages are logged and the function returns early.
*/ */
let itemTemplate = document.getElementById( let itemTemplate = document.getElementById(
"ui-known-universe-item-template" "ui-known-universe-item-template",
) as HTMLTemplateElement; ) as HTMLTemplateElement;
if (!itemTemplate) { if (!itemTemplate) {
console.warn("Missing template #ui-known-universe-item-template"); console.warn("Missing template #ui-known-universe-item-template");
@ -274,10 +274,10 @@ export class Editor {
item item
.querySelector(".delete-universe") .querySelector(".delete-universe")
?.addEventListener("click", () => ?.addEventListener("click", () =>
api._deleteUniverseFromInterface(it) api._deleteUniverseFromInterface(it),
); );
return item; return item;
}) }),
); );
existing_universes.innerHTML = ""; existing_universes.innerHTML = "";
@ -369,7 +369,7 @@ export class Editor {
this.view.dispatch({ this.view.dispatch({
effects: this.chosenLanguage.reconfigure( effects: this.chosenLanguage.reconfigure(
this.editor_mode == "notes" ? [markdown()] : [javascript()] this.editor_mode == "notes" ? [markdown()] : [javascript()],
), ),
}); });
@ -378,7 +378,7 @@ export class Editor {
setButtonHighlighting( setButtonHighlighting(
button: "play" | "pause" | "stop" | "clear", button: "play" | "pause" | "stop" | "clear",
highlight: boolean highlight: boolean,
) { ) {
/** /**
* Sets the highlighting for a specific button. * Sets the highlighting for a specific button.
@ -432,7 +432,7 @@ export class Editor {
// All other buttons must lose the highlighting // All other buttons must lose the highlighting
document document
.querySelectorAll( .querySelectorAll(
possible_selectors.filter((_, index) => index != selector).join(",") possible_selectors.filter((_, index) => index != selector).join(","),
) )
.forEach((button) => { .forEach((button) => {
button.children[0].classList.remove("animate-pulse"); button.children[0].classList.remove("animate-pulse");
@ -478,28 +478,28 @@ export class Editor {
*/ */
const domElement = this.view.dom; const domElement = this.view.dom;
const gutters = domElement.getElementsByClassName( const gutters = domElement.getElementsByClassName(
"cm-gutter" "cm-gutter",
) as HTMLCollectionOf<HTMLElement>; ) as HTMLCollectionOf<HTMLElement>;
domElement.classList.add("fluid-bg-transition"); domElement.classList.add("fluid-bg-transition");
Array.from(gutters).forEach((gutter) => Array.from(gutters).forEach((gutter) =>
gutter.classList.add("fluid-bg-transition") gutter.classList.add("fluid-bg-transition"),
); );
domElement.style.backgroundColor = color; domElement.style.backgroundColor = color;
Array.from(gutters).forEach( Array.from(gutters).forEach(
(gutter) => (gutter.style.backgroundColor = color) (gutter) => (gutter.style.backgroundColor = color),
); );
setTimeout(() => { setTimeout(() => {
domElement.style.backgroundColor = ""; domElement.style.backgroundColor = "";
Array.from(gutters).forEach( Array.from(gutters).forEach(
(gutter) => (gutter.style.backgroundColor = "") (gutter) => (gutter.style.backgroundColor = ""),
); );
domElement.classList.remove("fluid-bg-transition"); domElement.classList.remove("fluid-bg-transition");
Array.from(gutters).forEach((gutter) => Array.from(gutters).forEach((gutter) =>
gutter.classList.remove("fluid-bg-transition") gutter.classList.remove("fluid-bg-transition"),
); );
}, duration); }, duration);
} }
@ -507,7 +507,7 @@ export class Editor {
private initializeElements(): void { private initializeElements(): void {
for (const [key, value] of Object.entries(singleElements)) { for (const [key, value] of Object.entries(singleElements)) {
this.interface[key] = document.getElementById( this.interface[key] = document.getElementById(
value value,
) as ElementMap[keyof ElementMap]; ) as ElementMap[keyof ElementMap];
} }
} }
@ -515,7 +515,7 @@ export class Editor {
private initializeButtonGroups(): void { private initializeButtonGroups(): void {
for (const [key, ids] of Object.entries(buttonGroups)) { for (const [key, ids] of Object.entries(buttonGroups)) {
this.buttonElements[key] = ids.map( this.buttonElements[key] = ids.map(
(id) => document.getElementById(id) as HTMLButtonElement (id) => document.getElementById(id) as HTMLButtonElement,
); );
} }
} }