lint
This commit is contained in:
17
.github/workflows/build_docker.yml
vendored
17
.github/workflows/build_docker.yml
vendored
@ -3,28 +3,23 @@ name: Build and Push Docker Images
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- "main"
|
||||
jobs:
|
||||
topos:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,2 +1 @@
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
</p>
|
||||
</p>
|
||||
|
||||
Topos is a web-based live coding environment. It lives [here](https://topos.live). Documentation is directly embedded in the application itself. Topos is an emulation and extension of the [Monome Teletype](https://monome.org/docs/teletype/) that gradually evolved into something a bit more personal.
|
||||
Topos is a web-based live coding environment. It lives [here](https://topos.live). Documentation is directly embedded in the application itself. Topos is an emulation and extension of the [Monome Teletype](https://monome.org/docs/teletype/) that gradually evolved into something a bit more personal.
|
||||
|
||||

|
||||
|
||||
@ -46,15 +46,18 @@ The `tauri` version is only here to quickstart future developments but nothing
|
||||
## Docker
|
||||
|
||||
### Run the application
|
||||
|
||||
`docker run -p 8001:80 yassinsiouda/topos:latest`
|
||||
|
||||
### Build and run the prod image
|
||||
|
||||
`docker compose --profile prod up`
|
||||
|
||||
### Build and run the dev image
|
||||
|
||||
**First installation**
|
||||
First you need to map node_modules to your local machine for your ide intellisense to work properly
|
||||
|
||||
```bash
|
||||
docker compose --profile dev up -d
|
||||
docker cp topos-dev:/app/node_modules .
|
||||
@ -62,7 +65,7 @@ docker compose --profile dev down
|
||||
```
|
||||
|
||||
**Then**
|
||||
|
||||
```bash
|
||||
docker compose --profile dev up
|
||||
```
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
version: '3.7'
|
||||
version: "3.7"
|
||||
services:
|
||||
topos-dev:
|
||||
container_name: topos-dev
|
||||
profiles: ["dev"]
|
||||
build:
|
||||
build:
|
||||
context: .
|
||||
target: "dev"
|
||||
volumes:
|
||||
@ -21,8 +21,8 @@ services:
|
||||
topos-prod:
|
||||
container_name: topos-prod
|
||||
profiles: ["prod"]
|
||||
build:
|
||||
build:
|
||||
context: .
|
||||
target: "prod"
|
||||
ports:
|
||||
- "8001:80"
|
||||
- "8001:80"
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
||||
135
fonts/index.css
135
fonts/index.css
@ -1,7 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "IBM Plex Mono";
|
||||
src: url("woff2/IBMPlexMono-Regular.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-Regular.woff") format("woff");
|
||||
src:
|
||||
url("woff2/IBMPlexMono-Regular.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-Regular.woff") format("woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
@ -9,8 +10,9 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "IBM PLex Mono";
|
||||
src: url("woff2/IBMPlexMono-Italic.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-Italic.woff") format("woff");
|
||||
src:
|
||||
url("woff2/IBMPlexMono-Italic.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-Italic.woff") format("woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
@ -18,8 +20,9 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "IBM PLex Mono";
|
||||
src: url("woff2/IBMPlexMono-Bold.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-Bold.woff") format("woff");
|
||||
src:
|
||||
url("woff2/IBMPlexMono-Bold.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-Bold.woff") format("woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
@ -27,8 +30,9 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "IBM Plex Mono";
|
||||
src: url("woff2/IBMPlexMono-BoldItalic.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-BoldItalic.woff") format("woff");
|
||||
src:
|
||||
url("woff2/IBMPlexMono-BoldItalic.woff2") format("woff2"),
|
||||
url("woff/IBMPlexMono-BoldItalic.woff") format("woff");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
@ -37,84 +41,85 @@
|
||||
@font-face {
|
||||
font-family: "Comic Mono";
|
||||
font-weight: normal;
|
||||
src: url(./woff/ComicMono.woff) format("woff"),
|
||||
url(./woff2/ComicMono.woff2) format("wooff2");
|
||||
src:
|
||||
url(./woff/ComicMono.woff) format("woff"),
|
||||
url(./woff2/ComicMono.woff2) format("wooff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Comic Mono";
|
||||
font-weight: bold;
|
||||
src: url(./woff/ComicMono-Bold.woff) format("woff"),
|
||||
url(./woff/ComicMono-Bold.woff2) format("woff2"),
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'jgs7';
|
||||
src: url('./woff2/jgs7.woff2') format('woff2'),
|
||||
url('./woff/jgs7.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src:
|
||||
url(./woff/ComicMono-Bold.woff) format("woff"),
|
||||
url(./woff/ComicMono-Bold.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'jgs5';
|
||||
src: url('./woff2/jgs5.woff2') format('woff2'),
|
||||
url('./woff/jgs5.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-family: "jgs7";
|
||||
src:
|
||||
url("./woff2/jgs7.woff2") format("woff2"),
|
||||
url("./woff/jgs7.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'jgs9';
|
||||
src: url('./woff2/jgs9.woff2') format('woff2'),
|
||||
url('./woff/jgs9.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'jgs_vecto';
|
||||
src: url('./woff2/jgs_vecto.woff2') format('woff2');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-family: "jgs5";
|
||||
src:
|
||||
url("./woff2/jgs5.woff2") format("woff2"),
|
||||
url("./woff/jgs5.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Steps Mono';
|
||||
src: url('./woff2/Steps-Mono.woff2') format('woff2');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-family: "jgs9";
|
||||
src:
|
||||
url("./woff2/jgs9.woff2") format("woff2"),
|
||||
url("./woff/jgs9.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Steps Mono Thin';
|
||||
src: url('./woff2/Steps-Mono-Thin.woff2') format('woff2');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-family: "jgs_vecto";
|
||||
src: url("./woff2/jgs_vecto.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
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-family: "Steps Mono";
|
||||
src: url("./woff2/Steps-Mono.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Jet Brains';
|
||||
src: url('./woff2/JetBrainsMono-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-family: "Steps Mono Thin";
|
||||
src: url("./woff2/Steps-Mono-Thin.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
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-family: "Jet Brains";
|
||||
src: url("./woff2/JetBrainsMono-Bold.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@ -3,4 +3,4 @@ export default {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
74
src/API.ts
74
src/API.ts
@ -111,7 +111,7 @@ export class UserAPI {
|
||||
}
|
||||
this.app.settings.saveApplicationToLocalStorage(
|
||||
this.app.universes,
|
||||
this.app.settings
|
||||
this.app.settings,
|
||||
);
|
||||
this.app.updateKnownUniversesView();
|
||||
};
|
||||
@ -203,7 +203,7 @@ export class UserAPI {
|
||||
// @ts-ignore
|
||||
this.errorTimeoutID = setTimeout(
|
||||
() => this.app.interface.error_line.classList.add("hidden"),
|
||||
2000
|
||||
2000,
|
||||
);
|
||||
};
|
||||
|
||||
@ -217,7 +217,7 @@ export class UserAPI {
|
||||
// @ts-ignore
|
||||
this.printTimeoutID = setTimeout(
|
||||
() => 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.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);
|
||||
tryEvaluate(
|
||||
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];
|
||||
this.app.settings.saveApplicationToLocalStorage(
|
||||
this.app.universes,
|
||||
this.app.settings
|
||||
this.app.settings,
|
||||
);
|
||||
this.app.updateKnownUniversesView();
|
||||
};
|
||||
@ -388,7 +388,7 @@ export class UserAPI {
|
||||
};
|
||||
this.app.settings.saveApplicationToLocalStorage(
|
||||
this.app.universes,
|
||||
this.app.settings
|
||||
this.app.settings,
|
||||
);
|
||||
}
|
||||
this.app.selected_universe = "Default";
|
||||
@ -425,7 +425,7 @@ export class UserAPI {
|
||||
value: number | number[] = 60,
|
||||
velocity?: number | number[],
|
||||
channel?: number | number[],
|
||||
port?: number | string | number[] | string[]
|
||||
port?: number | string | number[] | string[],
|
||||
): MidiEvent => {
|
||||
/**
|
||||
* Sends a MIDI note to the current MIDI output.
|
||||
@ -500,7 +500,7 @@ export class UserAPI {
|
||||
};
|
||||
|
||||
public active_note_events = (
|
||||
channel?: number
|
||||
channel?: number,
|
||||
): MidiNoteEvent[] | undefined => {
|
||||
/**
|
||||
* @returns A list of currently active MIDI notes
|
||||
@ -637,7 +637,7 @@ export class UserAPI {
|
||||
scale: number | string,
|
||||
channel: number = 0,
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0,
|
||||
soundOff: boolean = false
|
||||
soundOff: boolean = false,
|
||||
): void => {
|
||||
/**
|
||||
* Sends given scale to midi output for visual aid
|
||||
@ -661,7 +661,7 @@ export class UserAPI {
|
||||
// @ts-ignore
|
||||
scale: number | string = 0,
|
||||
channel: number = 0,
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0,
|
||||
): void => {
|
||||
/**
|
||||
* Hides all notes by sending all notes off to midi output
|
||||
@ -676,7 +676,7 @@ export class UserAPI {
|
||||
|
||||
midi_notes_off = (
|
||||
channel: number = 0,
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0,
|
||||
): void => {
|
||||
/**
|
||||
* Sends all notes off to midi output
|
||||
@ -686,7 +686,7 @@ export class UserAPI {
|
||||
|
||||
midi_sound_off = (
|
||||
channel: number = 0,
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0
|
||||
port: number | string = this.MidiConnection.currentOutputIndex || 0,
|
||||
): void => {
|
||||
/**
|
||||
* Sends all sound off to midi output
|
||||
@ -713,7 +713,7 @@ export class UserAPI {
|
||||
public z = (
|
||||
input: string | Generator<number>,
|
||||
options: InputOptions = {},
|
||||
id: number | string = ""
|
||||
id: number | string = "",
|
||||
): Player => {
|
||||
const zid = "z" + id.toString();
|
||||
const key = id === "" ? this.generateCacheKey(input, options) : zid;
|
||||
@ -790,7 +790,7 @@ export class UserAPI {
|
||||
public counter = (
|
||||
name: string | number,
|
||||
limit?: number,
|
||||
step?: number
|
||||
step?: number,
|
||||
): number => {
|
||||
/**
|
||||
* Returns the current value of a counter, and increments it by the step value.
|
||||
@ -1297,8 +1297,8 @@ export class UserAPI {
|
||||
const results: boolean[] = nArray.map(
|
||||
(value) =>
|
||||
(this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) %
|
||||
Math.floor(value * this.ppqn()) ===
|
||||
0
|
||||
Math.floor(value * this.ppqn()) ===
|
||||
0,
|
||||
);
|
||||
return results.some((value) => value === true);
|
||||
};
|
||||
@ -1317,8 +1317,8 @@ export class UserAPI {
|
||||
const results: boolean[] = nArray.map(
|
||||
(value) =>
|
||||
(this.app.clock.pulses_since_origin - nudgeInPulses) %
|
||||
Math.floor(value * barLength) ===
|
||||
0
|
||||
Math.floor(value * barLength) ===
|
||||
0,
|
||||
);
|
||||
return results.some((value) => value === true);
|
||||
};
|
||||
@ -1333,7 +1333,7 @@ export class UserAPI {
|
||||
*/
|
||||
const nArray = Array.isArray(n) ? n : [n];
|
||||
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);
|
||||
};
|
||||
@ -1342,7 +1342,7 @@ export class UserAPI {
|
||||
public tick = (tick: number | number[], offset: number = 0): boolean => {
|
||||
const nArray = Array.isArray(tick) ? tick : [tick];
|
||||
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);
|
||||
};
|
||||
@ -1391,7 +1391,7 @@ export class UserAPI {
|
||||
|
||||
public onbar = (
|
||||
bars: number[] | number,
|
||||
n: number = this.app.clock.time_signature[0]
|
||||
n: number = this.app.clock.time_signature[0],
|
||||
): boolean => {
|
||||
let current_bar = (this.app.clock.time_position.bar % n) + 1;
|
||||
return typeof bars === "number"
|
||||
@ -1419,7 +1419,7 @@ export class UserAPI {
|
||||
if (decimal_part <= 0)
|
||||
decimal_part = decimal_part + this.ppqn() * this.nominator();
|
||||
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);
|
||||
@ -1501,7 +1501,7 @@ export class UserAPI {
|
||||
iterator: number,
|
||||
pulses: number,
|
||||
length: number,
|
||||
rotate: number = 0
|
||||
rotate: number = 0,
|
||||
): boolean => {
|
||||
/**
|
||||
* Returns a euclidean cycle of size length, with n pulses, rotated or not.
|
||||
@ -1520,7 +1520,7 @@ export class UserAPI {
|
||||
div: number,
|
||||
pulses: number,
|
||||
length: number,
|
||||
rotate: number = 0
|
||||
rotate: number = 0,
|
||||
): boolean => {
|
||||
return (
|
||||
this.beat(div) && this._euclidean_cycle(pulses, length, rotate).beat(div)
|
||||
@ -1530,7 +1530,7 @@ export class UserAPI {
|
||||
_euclidean_cycle(
|
||||
pulses: number,
|
||||
length: number,
|
||||
rotate: number = 0
|
||||
rotate: number = 0,
|
||||
): boolean[] {
|
||||
if (pulses == length) return Array.from({ length }, () => true);
|
||||
function startsDescent(list: number[], i: number): boolean {
|
||||
@ -1541,7 +1541,7 @@ export class UserAPI {
|
||||
if (pulses >= length) return [true];
|
||||
const resList = Array.from(
|
||||
{ length },
|
||||
(_, i) => (((pulses * (i - 1)) % length) + length) % length
|
||||
(_, i) => (((pulses * (i - 1)) % length) + length) % length,
|
||||
);
|
||||
let cycle = resList.map((_, i) => startsDescent(resList, i));
|
||||
if (rotate != 0) {
|
||||
@ -1660,7 +1660,7 @@ export class UserAPI {
|
||||
triangle = (
|
||||
freq: number = 1,
|
||||
times: number = 1,
|
||||
offset: number = 0
|
||||
offset: number = 0,
|
||||
): number => {
|
||||
/**
|
||||
* Returns a triangle wave between -1 and 1.
|
||||
@ -1677,7 +1677,7 @@ export class UserAPI {
|
||||
utriangle = (
|
||||
freq: number = 1,
|
||||
times: number = 1,
|
||||
offset: number = 0
|
||||
offset: number = 0,
|
||||
): number => {
|
||||
/**
|
||||
* Returns a triangle wave between 0 and 1.
|
||||
@ -1694,7 +1694,7 @@ export class UserAPI {
|
||||
freq: number = 1,
|
||||
times: number = 1,
|
||||
offset: number = 0,
|
||||
duty: number = 0.5
|
||||
duty: number = 0.5,
|
||||
): number => {
|
||||
/**
|
||||
* Returns a square wave with a specified duty cycle between -1 and 1.
|
||||
@ -1714,7 +1714,7 @@ export class UserAPI {
|
||||
freq: number = 1,
|
||||
times: number = 1,
|
||||
offset: number = 0,
|
||||
duty: number = 0.5
|
||||
duty: number = 0.5,
|
||||
): number => {
|
||||
/**
|
||||
* Returns a square wave between 0 and 1.
|
||||
@ -1774,7 +1774,7 @@ export class UserAPI {
|
||||
*/
|
||||
const sum = values.reduce(
|
||||
(accumulator, currentValue) => accumulator + currentValue,
|
||||
0
|
||||
0,
|
||||
);
|
||||
return sum / values.length;
|
||||
};
|
||||
@ -1784,7 +1784,7 @@ export class UserAPI {
|
||||
yMin: number,
|
||||
yMax: number,
|
||||
xMin: number,
|
||||
xMax: number
|
||||
xMax: number,
|
||||
): number => {
|
||||
const percent = (inputY - yMin) / (yMax - yMin);
|
||||
const outputX = percent * (xMax - xMin) + xMin;
|
||||
@ -1814,7 +1814,7 @@ export class UserAPI {
|
||||
lang: string = "en-US",
|
||||
voice: number = 0,
|
||||
rate: number = 1,
|
||||
pitch: number = 1
|
||||
pitch: number = 1,
|
||||
): void => {
|
||||
/*
|
||||
* 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 timepos = this.app.clock.pulses_since_origin;
|
||||
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];
|
||||
};
|
||||
@ -1917,7 +1917,7 @@ export class UserAPI {
|
||||
// =============================================================
|
||||
|
||||
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
|
||||
AbstractEvent.prototype[name] = function(
|
||||
AbstractEvent.prototype[name] = function (
|
||||
this: AbstractEvent,
|
||||
...args: any[]
|
||||
) {
|
||||
@ -2024,7 +2024,7 @@ export class UserAPI {
|
||||
".cm-comment": {
|
||||
fontFamily: commentFont,
|
||||
},
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@ export const drawCircle = (
|
||||
x: number,
|
||||
y: number,
|
||||
radius: number,
|
||||
color: string
|
||||
color: string,
|
||||
): void => {
|
||||
// @ts-ignore
|
||||
const canvas: HTMLCanvasElement = app.interface.feedback;
|
||||
@ -36,7 +36,7 @@ export const blinkScript = (
|
||||
*/
|
||||
app: Editor,
|
||||
script: "local" | "global" | "init",
|
||||
no?: number
|
||||
no?: number,
|
||||
) => {
|
||||
if (no !== undefined && no < 1 && no > 9) return;
|
||||
const blinkDuration =
|
||||
@ -55,7 +55,7 @@ export const blinkScript = (
|
||||
horizontalOffset + shift,
|
||||
app.interface.feedback.clientHeight - 15,
|
||||
8,
|
||||
"#fdba74"
|
||||
"#fdba74",
|
||||
);
|
||||
};
|
||||
|
||||
@ -91,7 +91,7 @@ export const blinkScript = (
|
||||
0,
|
||||
0,
|
||||
(app.interface.feedback as HTMLCanvasElement).width,
|
||||
(app.interface.feedback as HTMLCanvasElement).height
|
||||
(app.interface.feedback as HTMLCanvasElement).height,
|
||||
);
|
||||
}, blinkDuration);
|
||||
}
|
||||
@ -137,7 +137,7 @@ let lastRenderTime: number = 0;
|
||||
|
||||
export const runOscilloscope = (
|
||||
canvas: HTMLCanvasElement,
|
||||
app: Editor
|
||||
app: Editor,
|
||||
): void => {
|
||||
/**
|
||||
* Runs the oscilloscope visualization on the provided canvas element.
|
||||
@ -157,7 +157,7 @@ export const runOscilloscope = (
|
||||
width: number,
|
||||
height: number,
|
||||
offset_height: number,
|
||||
offset_width: number
|
||||
offset_width: number,
|
||||
) {
|
||||
const maxFPS = 30;
|
||||
const now = performance.now();
|
||||
@ -172,11 +172,11 @@ export const runOscilloscope = (
|
||||
|
||||
const performanceFactor = 1;
|
||||
const reducedDataSize = Math.floor(
|
||||
freqDataArray.length * performanceFactor
|
||||
freqDataArray.length * performanceFactor,
|
||||
);
|
||||
const numBars = Math.min(
|
||||
reducedDataSize,
|
||||
app.osc.orientation === "horizontal" ? width : height
|
||||
app.osc.orientation === "horizontal" ? width : height,
|
||||
);
|
||||
const barWidth =
|
||||
app.osc.orientation === "horizontal" ? width / numBars : height / numBars;
|
||||
@ -189,7 +189,7 @@ export const runOscilloscope = (
|
||||
for (let i = 0; i < numBars; i++) {
|
||||
barHeight = Math.floor(
|
||||
freqDataArray[Math.floor((i * freqDataArray.length) / numBars)] *
|
||||
((height / 256) * app.osc.size)
|
||||
((height / 256) * app.osc.size),
|
||||
);
|
||||
|
||||
if (app.osc.orientation === "horizontal") {
|
||||
@ -197,7 +197,7 @@ export const runOscilloscope = (
|
||||
x + offset_width,
|
||||
(height - barHeight) / 2 + offset_height,
|
||||
barWidth + 1,
|
||||
barHeight
|
||||
barHeight,
|
||||
);
|
||||
x += barWidth;
|
||||
} else {
|
||||
@ -205,7 +205,7 @@ export const runOscilloscope = (
|
||||
(width - barHeight) / 2 + offset_width,
|
||||
y + offset_height,
|
||||
barHeight,
|
||||
barWidth + 1
|
||||
barWidth + 1,
|
||||
);
|
||||
y += barWidth;
|
||||
}
|
||||
@ -234,7 +234,7 @@ export const runOscilloscope = (
|
||||
-OFFSET_WIDTH,
|
||||
-OFFSET_HEIGHT,
|
||||
WIDTH + 2 * OFFSET_WIDTH,
|
||||
HEIGHT + 2 * OFFSET_HEIGHT
|
||||
HEIGHT + 2 * OFFSET_HEIGHT,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -261,7 +261,7 @@ export const runOscilloscope = (
|
||||
-OFFSET_WIDTH,
|
||||
-OFFSET_HEIGHT,
|
||||
WIDTH + 2 * OFFSET_WIDTH,
|
||||
HEIGHT + 2 * OFFSET_HEIGHT
|
||||
HEIGHT + 2 * OFFSET_HEIGHT,
|
||||
);
|
||||
}
|
||||
canvasCtx.lineWidth = app.osc.thickness;
|
||||
|
||||
@ -48,7 +48,10 @@ export class Clock {
|
||||
lastPlayPressTime: 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_signature = [4, 4];
|
||||
this.logicalTime = 0;
|
||||
|
||||
@ -47,7 +47,7 @@ export const makeExampleFactory = (application: Editor): Function => {
|
||||
const make_example = (
|
||||
description: string,
|
||||
code: string,
|
||||
open: boolean = false
|
||||
open: boolean = false,
|
||||
) => {
|
||||
const codeId = `codeExample${application.exampleCounter++}`;
|
||||
// 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],
|
||||
});
|
||||
const converted_markdown = converter.makeHtml(
|
||||
app.docs[app.currentDocumentationPane]
|
||||
app.docs[app.currentDocumentationPane],
|
||||
);
|
||||
document.getElementById("documentation-content")!.innerHTML =
|
||||
converted_markdown;
|
||||
|
||||
@ -8,7 +8,7 @@ const codeReplace = (code: string): string => {
|
||||
|
||||
const tryCatchWrapper = async (
|
||||
application: Editor,
|
||||
code: string
|
||||
code: string,
|
||||
): Promise<boolean> => {
|
||||
/**
|
||||
* Wraps the provided code in a try-catch block and executes it.
|
||||
@ -20,7 +20,7 @@ const tryCatchWrapper = async (
|
||||
|
||||
try {
|
||||
await new Function(`"use strict"; ${codeReplace(code)}`).call(
|
||||
application.api
|
||||
application.api,
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
@ -48,7 +48,7 @@ const addFunctionToCache = (code: string, fn: Function) => {
|
||||
export const tryEvaluate = async (
|
||||
application: Editor,
|
||||
code: File,
|
||||
timeout = 5000
|
||||
timeout = 5000,
|
||||
): Promise<void> => {
|
||||
/**
|
||||
* Tries to evaluate the provided code within a specified timeout period.
|
||||
@ -77,7 +77,7 @@ export const tryEvaluate = async (
|
||||
if (isCodeValid) {
|
||||
code.committed = code.candidate;
|
||||
const newFunction = new Function(
|
||||
`"use strict"; ${codeReplace(wrappedCode)}`
|
||||
`"use strict"; ${codeReplace(wrappedCode)}`,
|
||||
);
|
||||
addFunctionToCache(candidateCode, newFunction);
|
||||
} else {
|
||||
@ -93,7 +93,7 @@ export const tryEvaluate = async (
|
||||
export const evaluate = async (
|
||||
application: Editor,
|
||||
code: File,
|
||||
timeout = 1000
|
||||
timeout = 1000,
|
||||
): Promise<void> => {
|
||||
/**
|
||||
* Evaluates the given code using the provided application and timeout.
|
||||
@ -117,7 +117,7 @@ export const evaluate = async (
|
||||
|
||||
export const evaluateOnce = async (
|
||||
application: Editor,
|
||||
code: string
|
||||
code: string,
|
||||
): Promise<void> => {
|
||||
/**
|
||||
* Evaluates the code once without any caching or error-handling mechanisms besides the tryCatchWrapper.
|
||||
|
||||
@ -154,7 +154,7 @@ export class AppSettings {
|
||||
|
||||
constructor() {
|
||||
const settingsFromStorage = JSON.parse(
|
||||
localStorage.getItem("topos") || "{}"
|
||||
localStorage.getItem("topos") || "{}",
|
||||
);
|
||||
|
||||
if (settingsFromStorage && Object.keys(settingsFromStorage).length !== 0) {
|
||||
@ -210,7 +210,7 @@ export class AppSettings {
|
||||
|
||||
saveApplicationToLocalStorage(
|
||||
universes: Universes,
|
||||
settings: Settings
|
||||
settings: Settings,
|
||||
): void {
|
||||
/**
|
||||
* Main method to store the application to local storage.
|
||||
@ -328,7 +328,7 @@ export const loadUniverserFromUrl = (app: Editor): void => {
|
||||
export const loadUniverse = (
|
||||
app: Editor,
|
||||
universeName: string,
|
||||
universe: Universe = template_universe
|
||||
universe: Universe = template_universe,
|
||||
): void => {
|
||||
/**
|
||||
* Loads a universe into the application.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export function objectWithArraysToArrayOfObjects(
|
||||
input: Record<string, any>,
|
||||
arraysToArrays: string[]
|
||||
arraysToArrays: string[],
|
||||
): Record<string, any>[] {
|
||||
/*
|
||||
* Transforms object with arrays into array of objects
|
||||
@ -24,7 +24,7 @@ export function objectWithArraysToArrayOfObjects(
|
||||
acc.keys.push(key);
|
||||
return acc;
|
||||
},
|
||||
{ keys: [] as string[], maxLength: 0 }
|
||||
{ keys: [] as string[], maxLength: 0 },
|
||||
);
|
||||
|
||||
const output: Record<string, any>[] = [];
|
||||
@ -44,7 +44,7 @@ export function objectWithArraysToArrayOfObjects(
|
||||
|
||||
export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
|
||||
array: T[],
|
||||
mergeObject: Record<string, any> = {}
|
||||
mergeObject: Record<string, any> = {},
|
||||
): Record<string, any> {
|
||||
/*
|
||||
* Transforms array of objects into object with arrays
|
||||
@ -54,21 +54,24 @@ export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
|
||||
* @returns {object} Merged object with arrays
|
||||
*
|
||||
*/
|
||||
return array.reduce((acc, obj) => {
|
||||
const mergedObj = { ...obj, ...mergeObject };
|
||||
Object.keys(mergedObj).forEach((key) => {
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(mergedObj[key]);
|
||||
});
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
return array.reduce(
|
||||
(acc, obj) => {
|
||||
const mergedObj = { ...obj, ...mergeObject };
|
||||
Object.keys(mergedObj).forEach((key) => {
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(mergedObj[key]);
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
);
|
||||
}
|
||||
|
||||
export function filterObject(
|
||||
obj: Record<string, any>,
|
||||
filter: string[]
|
||||
filter: string[],
|
||||
): Record<string, any> {
|
||||
/*
|
||||
* Filter certain keys from object
|
||||
@ -79,6 +82,6 @@ export function filterObject(
|
||||
*
|
||||
*/
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([key]) => filter.includes(key))
|
||||
Object.entries(obj).filter(([key]) => filter.includes(key)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -32,13 +32,13 @@ export const saveBeforeExit = (app: Editor): null => {
|
||||
export const installWindowBehaviors = (
|
||||
app: Editor,
|
||||
window: Window,
|
||||
preventMultipleTabs: boolean = false
|
||||
preventMultipleTabs: boolean = false,
|
||||
) => {
|
||||
window.addEventListener("resize", () =>
|
||||
handleResize(app.interface.scope as HTMLCanvasElement)
|
||||
handleResize(app.interface.scope as HTMLCanvasElement),
|
||||
);
|
||||
window.addEventListener("resize", () =>
|
||||
handleResize(app.interface.feedback as HTMLCanvasElement)
|
||||
handleResize(app.interface.feedback as HTMLCanvasElement),
|
||||
);
|
||||
window.addEventListener("beforeunload", (event) => {
|
||||
event.preventDefault();
|
||||
@ -61,11 +61,11 @@ export const installWindowBehaviors = (
|
||||
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."
|
||||
"Topos is already opened in another tab. Close this tab now to prevent data loss.",
|
||||
);
|
||||
}
|
||||
},
|
||||
false
|
||||
false,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,7 +38,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
|
||||
public updateValue<T>(
|
||||
key: string,
|
||||
value: T | T[] | SoundParams[] | null
|
||||
value: T | T[] | SoundParams[] | null,
|
||||
): this {
|
||||
if (value == null) return this;
|
||||
this.values[key] = value;
|
||||
@ -82,7 +82,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
a: number,
|
||||
d: number,
|
||||
s: number,
|
||||
r: number
|
||||
r: number,
|
||||
) {
|
||||
self.updateValue("fmattack", a);
|
||||
self.updateValue("fmdecay", d);
|
||||
@ -106,7 +106,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
a: number,
|
||||
d: number,
|
||||
s: number,
|
||||
r: number
|
||||
r: number,
|
||||
) {
|
||||
self.updateValue("attack", a);
|
||||
self.updateValue("decay", d);
|
||||
@ -152,7 +152,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
a: number,
|
||||
d: number,
|
||||
s: number,
|
||||
r: number
|
||||
r: number,
|
||||
) {
|
||||
self.updateValue("lpenv", depth);
|
||||
self.updateValue("lpattack", a);
|
||||
@ -198,7 +198,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
a: number,
|
||||
d: number,
|
||||
s: number,
|
||||
r: number
|
||||
r: number,
|
||||
) {
|
||||
self.updateValue("hpenv", depth);
|
||||
self.updateValue("hpattack", a);
|
||||
@ -241,7 +241,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
a: number,
|
||||
d: number,
|
||||
s: number,
|
||||
r: number
|
||||
r: number,
|
||||
) {
|
||||
self.updateValue("bpenv", depth);
|
||||
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);
|
||||
this.nudge = app.dough_nudge / 100;
|
||||
|
||||
@ -367,7 +370,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
}
|
||||
|
||||
private processSound = (
|
||||
sound: string | string[] | SoundParams | SoundParams[]
|
||||
sound: string | string[] | SoundParams | SoundParams[],
|
||||
): SoundParams => {
|
||||
if (Array.isArray(sound) && typeof sound[0] === "string") {
|
||||
const s: string[] = [];
|
||||
@ -438,7 +441,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
(event.key as number) || "C4",
|
||||
(event.pitch as number) || 0,
|
||||
(event.parsedScale as number[]) || event.scale || "MAJOR",
|
||||
(event.octave as number) || 0
|
||||
(event.octave as number) || 0,
|
||||
);
|
||||
event.note = note;
|
||||
event.freq = midiToFreq(note);
|
||||
@ -458,7 +461,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
public invert = (howMany: number = 0) => {
|
||||
if (this.values.chord) {
|
||||
let notes = this.values.chord.map(
|
||||
(obj: { [key: string]: number }) => obj.note
|
||||
(obj: { [key: string]: number }) => obj.note,
|
||||
);
|
||||
notes = howMany < 0 ? [...notes].reverse() : notes;
|
||||
for (let i = 0; i < Math.abs(howMany); i++) {
|
||||
@ -500,7 +503,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
superdough(
|
||||
filteredEvent,
|
||||
this.nudge - this.app.clock.deviation,
|
||||
filteredEvent.dur
|
||||
filteredEvent.dur,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ export class Player extends AbstractEvent {
|
||||
input: string | number | Generator<number>,
|
||||
options: InputOptions,
|
||||
public app: Editor,
|
||||
zid: string = ""
|
||||
zid: string = "",
|
||||
) {
|
||||
super(app);
|
||||
this.options = options;
|
||||
@ -159,7 +159,7 @@ export class Player extends AbstractEvent {
|
||||
if (this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch | Chord | ZRest;
|
||||
const noteLengthInSeconds = this.app.clock.convertPulseToSecond(
|
||||
event.duration * 4 * this.app.clock.ppqn
|
||||
event.duration * 4 * this.app.clock.ppqn,
|
||||
);
|
||||
if (event instanceof Pitch) {
|
||||
const obj = event.getExisting(
|
||||
@ -169,7 +169,7 @@ export class Player extends AbstractEvent {
|
||||
"key",
|
||||
"scale",
|
||||
"octave",
|
||||
"parsedScale"
|
||||
"parsedScale",
|
||||
) as SoundParams;
|
||||
if (event.sound) name = event.sound as string;
|
||||
if (event.soundIndex) obj.n = event.soundIndex as number;
|
||||
@ -184,14 +184,14 @@ export class Player extends AbstractEvent {
|
||||
"key",
|
||||
"scale",
|
||||
"octave",
|
||||
"parsedScale"
|
||||
"parsedScale",
|
||||
);
|
||||
}) as SoundParams[];
|
||||
const add = { dur: noteLengthInSeconds } as SoundParams;
|
||||
if (name) add.s = name;
|
||||
let sound = arrayOfObjectsToObjectWithArrays(
|
||||
pitches,
|
||||
add
|
||||
add,
|
||||
) as SoundParams;
|
||||
return new SoundEvent(sound, this.app);
|
||||
} else if (event instanceof ZRest) {
|
||||
@ -212,7 +212,7 @@ export class Player extends AbstractEvent {
|
||||
"key",
|
||||
"scale",
|
||||
"octave",
|
||||
"parsedScale"
|
||||
"parsedScale",
|
||||
) as MidiParams;
|
||||
if (event instanceof Pitch) {
|
||||
if (event.soundIndex) obj.channel = event.soundIndex as number;
|
||||
|
||||
@ -20,11 +20,11 @@ Some features have been included as a bonus. These features are often about patt
|
||||
${makeExample(
|
||||
"Hydra integration",
|
||||
`beat(4) :: hydra.osc(3, 0.5, 2).out()`,
|
||||
true
|
||||
true,
|
||||
)}
|
||||
|
||||
Close the documentation to see the effect: ${key_shortcut(
|
||||
"Ctrl+D"
|
||||
"Ctrl+D",
|
||||
)}! **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.
|
||||
@ -37,7 +37,7 @@ ${makeExample(
|
||||
beat(4) :: stop_hydra() // 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(
|
||||
"Changing Hydra resolution",
|
||||
`hydra.setResolution(1024, 768)`,
|
||||
true
|
||||
true,
|
||||
)}
|
||||
|
||||
### Documentation
|
||||
@ -82,7 +82,7 @@ beat(0.25)::gif({
|
||||
posX: ir(1,1200), // CSS Horizontal Position
|
||||
posY: ir(1, 800), // CSS Vertical Position
|
||||
`,
|
||||
true
|
||||
true,
|
||||
)}
|
||||
`;
|
||||
};
|
||||
|
||||
@ -65,12 +65,12 @@ On this page, you will find an exhaustive list of all the samples currently load
|
||||
A very large collection of wavetables for wavetable synthesis. This collection has been released by Kristoffer Ekstrand: [AKWF Waveforms](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/). Every sound sample that starts with <ic>wt_</ic> will be looped. Look at this demo:
|
||||
|
||||
${makeExample(
|
||||
"Wavetable synthesis made easy :)",
|
||||
`
|
||||
"Wavetable synthesis made easy :)",
|
||||
`
|
||||
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.
|
||||
@ -84,12 +84,12 @@ ${samples_to_markdown(application, "Waveforms")}
|
||||
A set of 72 classic drum machines created by **Geikha**: [Geikha Drum Machines](https://github.com/geikha/tidal-drum-machines). To use them efficiently, it is best to use the <ic>.bank()</ic> parameter like so:
|
||||
|
||||
${makeExample(
|
||||
"Using a classic drum machine",
|
||||
`
|
||||
"Using a classic drum machine",
|
||||
`
|
||||
beat(0.5)::sound(['bd', 'cp'].pick()).bank("AkaiLinn").out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
Here is the complete list of available machines:
|
||||
|
||||
@ -119,12 +119,12 @@ ${samples_to_markdown(application, "Amiga")}
|
||||
A collection of many different amen breaks. Use <ic>.stretch()</ic> to play with these:
|
||||
|
||||
${makeExample(
|
||||
"Stretching an amen break",
|
||||
`
|
||||
"Stretching an amen break",
|
||||
`
|
||||
beat(4)::sound('amen1').stretch(4).out()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
true,
|
||||
)}
|
||||
|
||||
The stretch should be adapted based on the length of each amen break.
|
||||
|
||||
|
||||
26
src/main.ts
26
src/main.ts
@ -246,7 +246,7 @@ export class Editor {
|
||||
* If any required elements or templates are missing, warning messages are logged and the function returns early.
|
||||
*/
|
||||
let itemTemplate = document.getElementById(
|
||||
"ui-known-universe-item-template"
|
||||
"ui-known-universe-item-template",
|
||||
) as HTMLTemplateElement;
|
||||
if (!itemTemplate) {
|
||||
console.warn("Missing template #ui-known-universe-item-template");
|
||||
@ -274,10 +274,10 @@ export class Editor {
|
||||
item
|
||||
.querySelector(".delete-universe")
|
||||
?.addEventListener("click", () =>
|
||||
api._deleteUniverseFromInterface(it)
|
||||
api._deleteUniverseFromInterface(it),
|
||||
);
|
||||
return item;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
existing_universes.innerHTML = "";
|
||||
@ -369,7 +369,7 @@ export class Editor {
|
||||
|
||||
this.view.dispatch({
|
||||
effects: this.chosenLanguage.reconfigure(
|
||||
this.editor_mode == "notes" ? [markdown()] : [javascript()]
|
||||
this.editor_mode == "notes" ? [markdown()] : [javascript()],
|
||||
),
|
||||
});
|
||||
|
||||
@ -378,7 +378,7 @@ export class Editor {
|
||||
|
||||
setButtonHighlighting(
|
||||
button: "play" | "pause" | "stop" | "clear",
|
||||
highlight: boolean
|
||||
highlight: boolean,
|
||||
) {
|
||||
/**
|
||||
* Sets the highlighting for a specific button.
|
||||
@ -432,7 +432,7 @@ export class Editor {
|
||||
// All other buttons must lose the highlighting
|
||||
document
|
||||
.querySelectorAll(
|
||||
possible_selectors.filter((_, index) => index != selector).join(",")
|
||||
possible_selectors.filter((_, index) => index != selector).join(","),
|
||||
)
|
||||
.forEach((button) => {
|
||||
button.children[0].classList.remove("animate-pulse");
|
||||
@ -478,28 +478,28 @@ export class Editor {
|
||||
*/
|
||||
const domElement = this.view.dom;
|
||||
const gutters = domElement.getElementsByClassName(
|
||||
"cm-gutter"
|
||||
"cm-gutter",
|
||||
) as HTMLCollectionOf<HTMLElement>;
|
||||
|
||||
domElement.classList.add("fluid-bg-transition");
|
||||
Array.from(gutters).forEach((gutter) =>
|
||||
gutter.classList.add("fluid-bg-transition")
|
||||
gutter.classList.add("fluid-bg-transition"),
|
||||
);
|
||||
|
||||
domElement.style.backgroundColor = color;
|
||||
Array.from(gutters).forEach(
|
||||
(gutter) => (gutter.style.backgroundColor = color)
|
||||
(gutter) => (gutter.style.backgroundColor = color),
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
domElement.style.backgroundColor = "";
|
||||
Array.from(gutters).forEach(
|
||||
(gutter) => (gutter.style.backgroundColor = "")
|
||||
(gutter) => (gutter.style.backgroundColor = ""),
|
||||
);
|
||||
|
||||
domElement.classList.remove("fluid-bg-transition");
|
||||
Array.from(gutters).forEach((gutter) =>
|
||||
gutter.classList.remove("fluid-bg-transition")
|
||||
gutter.classList.remove("fluid-bg-transition"),
|
||||
);
|
||||
}, duration);
|
||||
}
|
||||
@ -507,7 +507,7 @@ export class Editor {
|
||||
private initializeElements(): void {
|
||||
for (const [key, value] of Object.entries(singleElements)) {
|
||||
this.interface[key] = document.getElementById(
|
||||
value
|
||||
value,
|
||||
) as ElementMap[keyof ElementMap];
|
||||
}
|
||||
}
|
||||
@ -515,7 +515,7 @@ export class Editor {
|
||||
private initializeButtonGroups(): void {
|
||||
for (const [key, ids] of Object.entries(buttonGroups)) {
|
||||
this.buttonElements[key] = ids.map(
|
||||
(id) => document.getElementById(id) as HTMLButtonElement
|
||||
(id) => document.getElementById(id) as HTMLButtonElement,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
|
||||
Reference in New Issue
Block a user