This commit is contained in:
2023-12-19 22:41:38 +02:00
36 changed files with 370 additions and 257 deletions

View File

@ -47,9 +47,6 @@ jobs:
with:
path: "main"
- name: Copy favicon folder
run: cp -r main/favicon ./dist/
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:

View File

@ -19,52 +19,53 @@
---
Topos is a web based live coding environment. Topos is capable of many things:
Topos is a web based live coding environment designed to be installation-free, independant and fun. Topos is loosely based on the [Monome Teletype](https://monome.org/docs/teletype/). The application follows the same operating principle, but adapts it to the rich multimedia context offered by web browsers. Topos is capable of many things:
- it is a music sequencer made for improvisation and composition alike
- it is a synthesizer capable of additive, substractive, FM and wavetable
synthesis, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/) and
custom oscilloscopes, frequency visualizers and image sequencing capabilities
- it can be used to sequence other MIDI devices (and soon.. OSC!)
- it is a generative/algorithmic music sequencer made for **improvisation** and **composition** alike
- it is a synthesizer capable of _additive_, _substractive_, _FM_ and _wavetable
synthesis_, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/),
oscilloscopes, frequency visualizers and image/canvas sequencing capabilities
- it can be used to sequence other MIDI and OSC devices (the latter using a **NodeJS** script)
- it is made to be used without the need of installing anything, always ready at
[https://topos.live](https://topos.live)
- Topos is also an emulation and personal extension of the [Monome Teletype](https://monome.org/docs/teletype/)
---
![Screenshot](https://github.com/Bubobubobubobubo/Topos/blob/main/img/topos_gif.gif)
![Screenshot](https://github.com/Bubobubobubobubo/Topos/blob/main/src/assets/topos_gif.gif)
## Disclaimer
**Topos** is still a young project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it.
**Topos** is still a young and experimental project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it. Note that most features are rather experimental and that we don't really have any classical training in web development.
## Installation (for devs and contributors)
## Local Installation (for devs and contributors)
To run the application, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
- `yarn install`
- `yarn run dev`
To build the application for production, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
You are good to go. The application will update itself automatically with every change to the codebase. To test the production version of the applicationn, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
- `yarn run build`
- `yarn run start`
Always run a build before committing to check for compiler errors. The automatic deployment on the `main` branch will not accept compiler errors!
If the build passes, you can be sure that it will also pass our **CI** pipeline that deploys the application to [https://topos.live](https://topos.live). Always run a build before committing to check for compiler errors. The automatic deployment on the `main` branch will not accept compiler errors!
To build a standalone browser application using [Tauri](https://tauri.app/), you will need to have [Node.js](https://nodejs.org/en/), [Yarn](https://yarnpkg.com/en/) and [Rust](https://www.rust-lang.org/) installed. Then, clone the repository and run:
## Tauri version
Topos can also be compiled as a standalone application using [Tauri](https://tauri.app/). You will need [Node.js](https://nodejs.org/en/), [Yarn](https://yarnpkg.com/en/) and [Rust](https://www.rust-lang.org/) to be installed on your computer. Then, clone the repository and run:
- `yarn tauri build`
- `yarn tauri dev`
The `tauri` version is only here to quickstart future developments but nothing has been done yet.
The `tauri` version has never been fleshed out. It's a template for later developments if Topos ever wants to escape from the web :)
## Docker
### Run the application
To run the **Docker** version, run the following command:
`docker run -p 8001:80 yassinsiouda/topos:latest`
`docker run -p 8001:80 bubobubobubo/topos:latest`
### Build and run the prod image
@ -72,8 +73,7 @@ The `tauri` version is only here to quickstart future developments but nothing
### 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
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
@ -81,8 +81,21 @@ docker cp topos-dev:/app/node_modules .
docker compose --profile dev down
```
**Then**
then run the following command:
```bash
docker compose --profile dev up
```
Note that a Docker version of Topos is automatically generated everytime a commit is done on the `main` branch.
## Credits
- Felix Roos for the [SuperDough](https://www.npmjs.com/package/superdough) audio engine.
- Frank Force for the [ZzFX](https://github.com/KilledByAPixel/ZzFX) synthesizer.
- Kristoffer Ekstrand for the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) waveforms.
- Ryan Kirkbride for some of the audio samples in the [Dough-Fox](https://github.com/Bubobubobubobubo/Dough-Fox) sample pack, taken from [here](https://github.com/Qirky/FoxDot/tree/master/FoxDot/snd).
- Adel Faure for the [JGS](https://adelfaure.net/https://adelfaure.net/) font.
- Raphaël Bastide for the [Steps Mono](https://github.com/raphaelbastide/steps-mono/) font.
Many thanks to all the contributors and folks who tried the software already :)

View File

@ -1,19 +0,0 @@
{
"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"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

View File

@ -1,13 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Topos is a live coding environment inspired by the Monome Teletype.">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Topos</title>
<meta name="description" content="Topos is a live coding environment inspired by the Monome Teletype.">
<meta charset="UTF-8" />
<link rel="apple-touch-icon" sizes="180x180" href="favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon/favicon-16x16.png">
<link rel="icon" href="/favicon/favicon.ico" sizes="48x48" ><!-- REVISED (Aug 11, 2023)! -->
<link rel="icon" href="/favicon/favicon.svg" sizes="any" type="image/svg+xml"><!-- REVISED (Aug 11, 2023)! -->
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"/>
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="mask-icon" href="favicon/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">

View File

@ -1,24 +0,0 @@
{
"name": "Topos",
"short_name": "Topos",
"description": "Live coding environment",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "./favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "./favicon/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}

View File

@ -14,7 +14,7 @@
"typescript": "^5.2.2",
"vite": "^4.4.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^0.16.7"
"vite-plugin-pwa": "^0.17.4"
},
"dependencies": {
"@codemirror/lang-javascript": "^6.1.9",

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="48.000000pt" height="48.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M163 470 c-13 -5 -23 -12 -23 -15 0 -3 46 -5 102 -5 80 0 99 3 90 12
-15 15 -139 21 -169 8z"/>
<path d="M91 391 c-16 -16 -19 -32 -18 -84 1 -53 -2 -69 -18 -87 -15 -16 -20
-38 -22 -83 0 -33 2 -56 5 -50 9 13 44 13 39 -1 -2 -6 -18 -11 -34 -11 -39 0
-50 -10 -33 -30 8 -10 30 -15 60 -15 26 0 64 -7 83 -15 43 -18 125 -20 164 -3
15 6 57 14 93 17 73 7 87 24 51 60 -12 12 -21 17 -21 13 0 -4 5 -13 12 -20 8
-8 8 -12 1 -12 -18 0 -25 34 -9 45 10 7 12 16 6 25 -5 8 -7 24 -4 35 3 13 -2
28 -15 39 -12 11 -21 27 -21 36 0 9 -5 21 -11 27 -8 8 -8 17 0 31 17 32 13 56
-12 80 -31 29 -63 28 -91 -3 -16 -17 -34 -25 -56 -25 -22 0 -40 8 -56 25 -28
30 -67 32 -93 6z m89 -16 c19 -23 5 -29 -24 -10 -16 10 -33 14 -47 10 -14 -5
-19 -4 -15 4 10 16 72 13 86 -4z m211 -7 c12 -22 11 -22 -8 -5 -25 21 -41 22
-65 0 -22 -19 -36 -10 -18 12 20 24 77 19 91 -7z m-264 -30 c-3 -7 -5 -2 -5
12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m229 -5 c-11 -11 -19 6 -11 24 8 17 8 17
12 0 3 -10 2 -21 -1 -24z m-80 -8 c4 -8 10 -12 15 -9 5 3 9 0 9 -6 0 -14 60
-40 77 -33 7 3 13 -2 13 -11 0 -13 -6 -15 -27 -9 -36 9 -210 9 -245 0 -22 -6
-28 -4 -28 9 0 8 6 14 13 11 6 -2 25 2 40 10 15 8 32 11 37 8 6 -4 7 1 3 11
-4 12 -3 15 5 10 7 -4 15 -1 18 8 8 21 63 21 70 1z m83 -91 c10 -9 -37 -33
-77 -39 -55 -8 -117 2 -155 26 l-30 18 54 4 c56 4 201 -2 208 -9z m-286 -30
c-15 -7 21 -44 42 -44 8 0 15 -4 15 -10 0 -16 -33 -11 -59 9 -25 19 -24 52 1
50 9 0 10 -2 1 -5z m355 -21 c2 -14 -4 -23 -17 -28 -12 -3 -21 -13 -21 -21 0
-19 -16 -18 -24 1 -5 15 -31 16 -53 2 -8 -5 -13 -4 -13 2 0 6 5 12 10 12 6 1
15 3 20 4 6 1 18 3 28 4 25 1 66 37 52 44 -6 3 -5 4 2 3 7 -1 14 -12 16 -23z
m-255 -37 c4 -10 1 -13 -9 -9 -7 3 -14 9 -14 14 0 14 17 10 23 -5z m42 -6 c3
-5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m35 0 c0
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-170
-10 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
m215 -39 c-6 -5 -25 10 -25 20 0 5 6 4 14 -3 8 -7 12 -15 11 -17z m45 8 c0
-14 -16 -11 -29 5 -10 12 -8 13 8 9 12 -3 21 -9 21 -14z m-220 1 c0 -5 -5 -10
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m40 0 c0 -5 -4 -10
-10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m215 0 c3 -5 2
-10 -4 -10 -5 0 -13 5 -16 10 -3 6 -2 10 4 10 5 0 13 -4 16 -10z m43 -10 c7
-11 10 -20 6 -20 -7 0 -34 27 -34 34 0 13 16 5 28 -14z m-160 -17 c-10 -2 -26
-2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z m99 1 c-3 -3 -12 -4 -19 -1 -8 3
-5 6 6 6 11 1 17 -2 13 -5z m40 0 c-3 -3 -12 -4 -19 -1 -8 3 -5 6 6 6 11 1 17
-2 13 -5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,37 @@
{
"name": "Topos",
"short_name": "Topos",
"icons": [
{
"src": "favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "favicon/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"display": "standalone",
"start_url": "/",
"scope": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"description": "Topos is a web based live coding platform",
"screenshots": [
{
"src": "favicon/screenshot_miniature.png",
"sizes": "640x320",
"type": "image/gif",
"form_factor": "wide",
"label": "Topos application"
},
{
"src": "favicon/topos_code.png",
"sizes": "1280x768",
"type": "image/gif",
"label": "Topos code"
}
]
}

View File

@ -121,7 +121,7 @@ export class UserAPI {
public currentSeed: string | undefined = undefined;
public localSeeds = new Map<string, Function>();
public patternCache = new LRUCache({ max: 10000, ttl: 10000 * 60 * 5 });
public invalidPatterns: {[key: string]: boolean} = {};
public invalidPatterns: { [key: string]: boolean } = {};
public cueTimes: { [key: string]: number } = {};
private errorTimeoutID: number = 0;
private printTimeoutID: number = 0;
@ -757,15 +757,15 @@ export class UserAPI {
this.patternCache.delete(id);
};
maybeToNumber = (something: any): number|any => {
maybeToNumber = (something: any): number | any => {
// If something is BigInt
if(typeof something === "bigint") {
if (typeof something === "bigint") {
return Number(something);
} else {
return something;
}
}
cache = (key: string, value: any) => {
/**
* Gets or sets a value in the cache.
@ -774,40 +774,40 @@ export class UserAPI {
* @param value - The value to set
* @returns The value of the key
*/
if(value !== undefined) {
if(isGenerator(value)) {
if(this.patternCache.has(key)) {
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value
if(cachedValue!==0 && !cachedValue) {
const generator = value as unknown as Generator<any>
this.patternCache.set(key, generator);
return this.maybeToNumber(generator.next().value);
}
return this.maybeToNumber(cachedValue);
} else {
if (value !== undefined) {
if (isGenerator(value)) {
if (this.patternCache.has(key)) {
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value
if (cachedValue !== 0 && !cachedValue) {
const generator = value as unknown as Generator<any>
this.patternCache.set(key, generator);
return this.maybeToNumber(generator.next().value);
}
} else if(isGeneratorFunction(value)) {
if(this.patternCache.has(key)) {
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value;
if(cachedValue || cachedValue===0 || cachedValue===0n) {
return this.maybeToNumber(cachedValue);
} else {
const generator = value();
this.patternCache.set(key, generator);
return this.maybeToNumber(generator.next().value);
}
return this.maybeToNumber(cachedValue);
} else {
const generator = value as unknown as Generator<any>
this.patternCache.set(key, generator);
return this.maybeToNumber(generator.next().value);
}
} else if (isGeneratorFunction(value)) {
if (this.patternCache.has(key)) {
const cachedValue = (this.patternCache.get(key) as Generator<any>).next().value;
if (cachedValue || cachedValue === 0 || cachedValue === 0n) {
return this.maybeToNumber(cachedValue);
} else {
const generator = value();
this.patternCache.set(key, generator);
return this.maybeToNumber(generator.next().value);
}
} else {
this.patternCache.set(key, value);
return this.maybeToNumber(value);
const generator = value();
this.patternCache.set(key, generator);
return this.maybeToNumber(generator.next().value);
}
} else {
this.patternCache.set(key, value);
return this.maybeToNumber(value);
}
} else {
return this.maybeToNumber(this.patternCache.get(key));
}
@ -833,27 +833,27 @@ export class UserAPI {
if (this.app.api.patternCache.has(key)) {
player = this.app.api.patternCache.get(key) as Player;
if (typeof input === "string" &&
player.input !== input &&
player.atTheBeginning()) {
replace = true;
if (typeof input === "string" &&
player.input !== input &&
player.atTheBeginning()) {
replace = true;
}
}
if ((typeof input !== "string" || validSyntax) && (!player || replace)) {
const newPlayer = new Player(input, options, this.app, zid);
if(newPlayer.isValid()) {
if (newPlayer.isValid()) {
player = newPlayer
this.patternCache.set(key, player);
} else if(typeof input === "string") {
} else if (typeof input === "string") {
this.invalidPatterns[input] = true;
}
}
if(player) {
if (player) {
if(player.atTheBeginning()) {
if(typeof input === "string" && !validSyntax) this.app.api.log(`Invalid syntax: ${input}`);
if (player.atTheBeginning()) {
if (typeof input === "string" && !validSyntax) this.app.api.log(`Invalid syntax: ${input}`);
}
if (player.ziffers.generator && player.ziffers.generatorDone) {
@ -920,7 +920,7 @@ export class UserAPI {
*
* @returns True if the code is being evaluated for the first time
*/
const firstTime = this.app.api.onceEvaluator;
const firstTime = this.app.api.onceEvaluator;
this.app.api.onceEvaluator = false;
return firstTime;
@ -1395,7 +1395,7 @@ export class UserAPI {
/**
* Returns the number of pulses in a given bar
*/
return (this.tempo()*this.ppqn()*this.nominator())/60;
return (this.tempo() * this.ppqn() * this.nominator()) / 60;
}
// =============================================================
@ -1444,7 +1444,7 @@ 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()) ===
Math.floor(value * this.ppqn()) ===
0,
);
return results.some((value) => value === true);
@ -1464,7 +1464,7 @@ export class UserAPI {
const results: boolean[] = nArray.map(
(value) =>
(this.app.clock.pulses_since_origin - nudgeInPulses) %
Math.floor(value * barLength) ===
Math.floor(value * barLength) ===
0,
);
return results.some((value) => value === true);
@ -2071,7 +2071,7 @@ export class UserAPI {
// =============================================================
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
AbstractEvent.prototype[name] = function (
AbstractEvent.prototype[name] = function(
this: AbstractEvent,
...args: any[]
) {
@ -2259,7 +2259,7 @@ export class UserAPI {
* Returns the current pulse location in the current bar.
* @returns The current pulse location in the current bar
*/
return ((this.epulse() / this.pulsesForBar())*this.w())%this.w()
return ((this.epulse() / this.pulsesForBar()) * this.w()) % this.w()
}
public clear = (): boolean => {
@ -2268,9 +2268,9 @@ export class UserAPI {
* @param timeout - The timeout in seconds
*/
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
ctx.clearRect(0, 0, canvas.width, canvas.height);
return true;
const ctx = canvas.getContext("2d")!;
ctx.clearRect(0, 0, canvas.width, canvas.height);
return true;
}
public w = (): number => {
@ -2307,21 +2307,21 @@ export class UserAPI {
return this.w() / 2;
}
public background = (color: string|number, ...gb:number[]): boolean => {
public background = (color: string | number, ...gb: number[]): boolean => {
/**
* Set background color of the canvas.
* @param color - The color to set. String or 3 numbers representing RGB values.
*/
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
if(typeof color === "number") color = `rgb(${color},${gb[0]},${gb[1]})`;
if (typeof color === "number") color = `rgb(${color},${gb[0]},${gb[1]})`;
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
return true;
}
bg = this.background;
public linearGradient = (x1: number, y1: number, x2: number, y2: number, ...stops: (number|string)[]) => {
public linearGradient = (x1: number, y1: number, x2: number, y2: number, ...stops: (number | string)[]) => {
/**
* Set linear gradient on the canvas.
* @param x1 - The x-coordinate of the start point
@ -2334,15 +2334,15 @@ export class UserAPI {
const ctx = canvas.getContext("2d")!;
const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
// Parse pairs of values from stops
for(let i=0; i<stops.length; i+=2) {
let color = stops[i+1];
if(typeof color === "number") color = `rgb(${color},${stops[i+2]},${stops[i+3]})`;
for (let i = 0; i < stops.length; i += 2) {
let color = stops[i + 1];
if (typeof color === "number") color = `rgb(${color},${stops[i + 2]},${stops[i + 3]})`;
gradient.addColorStop((stops[i] as number), color);
}
return gradient;
}
public radialGradient = (x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, ...stops: (number|string)[]) => {
public radialGradient = (x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, ...stops: (number | string)[]) => {
/**
* Set radial gradient on the canvas.
* @param x1 - The x-coordinate of the start circle
@ -2356,15 +2356,15 @@ export class UserAPI {
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
const gradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2);
for(let i=0; i<stops.length; i+=2) {
let color = stops[i+1];
if(typeof color === "number") color = `rgb(${color},${stops[i+2]},${stops[i+3]})`;
for (let i = 0; i < stops.length; i += 2) {
let color = stops[i + 1];
if (typeof color === "number") color = `rgb(${color},${stops[i + 2]},${stops[i + 3]})`;
gradient.addColorStop((stops[i] as number), color);
}
return gradient;
}
public conicGradient = (x: number, y: number, angle: number, ...stops: (number|string)[]) => {
public conicGradient = (x: number, y: number, angle: number, ...stops: (number | string)[]) => {
/**
* Set conic gradient on the canvas.
* @param x - The x-coordinate of the center of the gradient
@ -2375,9 +2375,9 @@ export class UserAPI {
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
const gradient = ctx.createConicGradient(x, y, angle);
for(let i=0; i<stops.length; i+=2) {
let color = stops[i+1];
if(typeof color === "number") color = `rgb(${color},${stops[i+2]},${stops[i+3]})`;
for (let i = 0; i < stops.length; i += 2) {
let color = stops[i + 1];
if (typeof color === "number") color = `rgb(${color},${stops[i + 2]},${stops[i + 3]})`;
gradient.addColorStop((stops[i] as number), color);
}
return gradient;
@ -2388,8 +2388,8 @@ export class UserAPI {
* Draws on the canvas.
* @param func - The function to execute
*/
if(typeof func === "string") {
this.drawText (func);
if (typeof func === "string") {
this.drawText(func);
} else {
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
@ -2399,29 +2399,29 @@ export class UserAPI {
}
public balloid = (
curves: number|ShapeObject = 6,
radius: number = this.hc()/2,
curves: number | ShapeObject = 6,
radius: number = this.hc() / 2,
curve: number = 1.5,
fillStyle: string = "white",
secondary: string = "black",
x: number = this.wc(),
y: number = this.hc(),
): boolean => {
if(typeof curves === "object") {
if (typeof curves === "object") {
fillStyle = curves.fillStyle || "white";
x = curves.x || this.wc();
y = curves.y || this.hc();
curve = curves.curve || 1.5;
radius = curves.radius || this.hc()/2;
radius = curves.radius || this.hc() / 2;
curves = curves.curves || 6;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
// Draw the shape using quadratic Bézier curves
ctx.beginPath();
ctx.fillStyle = fillStyle;
if (curves === 0) {
// Draw a circle if curves = 0
ctx.arc(x, y, radius, 0, 2 * Math.PI);
@ -2429,7 +2429,7 @@ export class UserAPI {
ctx.fill();
} else if (curves === 1) {
// Draw a single curve (ellipse) if curves = 1
ctx.ellipse(x, y, radius*0.8, (radius* curve)*0.7, 0, 0, 2 * Math.PI);
ctx.ellipse(x, y, radius * 0.8, (radius * curve) * 0.7, 0, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
} else if (curves === 2) {
@ -2438,20 +2438,20 @@ export class UserAPI {
// First curve
ctx.quadraticCurveTo(x + radius * curve, y, x, y + radius);
// Second symmetric curve
ctx.quadraticCurveTo(x - radius * curve, y, x, y - radius);
ctx.closePath();
ctx.fill();
} else {
} else {
// Draw the curved shape with the specified number of curves
ctx.moveTo(x, y - radius);
let points = [];
for (let i = 0; i < curves; i++) {
const startAngle = (i / curves) * 2 * Math.PI;
const endAngle = startAngle + (2 * Math.PI) / curves;
const controlX = x + radius * curve * Math.cos(startAngle + Math.PI / curves);
const controlY = y + radius * curve * Math.sin(startAngle + Math.PI / curves);
points.push([x + radius * Math.cos(startAngle), y + radius * Math.sin(startAngle)]);
@ -2466,28 +2466,28 @@ export class UserAPI {
ctx.fillStyle = secondary;
// Form the shape from points with straight lines and fill it
ctx.moveTo(points[0][0], points[0][1]);
for(let point of points) ctx.lineTo(point[0], point[1]);
for (let point of points) ctx.lineTo(point[0], point[1]);
// Close and fill
ctx.closePath();
ctx.fill();
}
return true;
};
public equilateral = (
radius: number|ShapeObject = this.hc()/3,
radius: number | ShapeObject = this.hc() / 3,
fillStyle: string = "white",
rotation: number = 0,
x: number = this.wc(),
y: number = this.hc(),
): boolean => {
if(typeof radius === "object") {
if (typeof radius === "object") {
fillStyle = radius.fillStyle || "white";
x = radius.x || this.wc();
y = radius.y || this.hc();
rotation = radius.rotation || 0;
radius = radius.radius || this.hc()/3;
radius = radius.radius || this.hc() / 3;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
@ -2506,20 +2506,20 @@ export class UserAPI {
}
public triangular = (
width: number|ShapeObject = this.hc()/3,
height: number = this.hc()/3,
width: number | ShapeObject = this.hc() / 3,
height: number = this.hc() / 3,
fillStyle: string = "white",
rotation: number = 0,
x: number = this.wc(),
y: number = this.hc(),
): boolean => {
if(typeof width === "object") {
if (typeof width === "object") {
fillStyle = width.fillStyle || "white";
x = width.x || this.wc();
y = width.y || this.hc();
rotation = width.rotation || 0;
height = width.height || this.hc()/3;
width = width.width || this.hc()/3;
height = width.height || this.hc() / 3;
width = width.width || this.hc() / 3;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
@ -2539,16 +2539,16 @@ export class UserAPI {
pointy = this.triangular;
public ball = (
radius: number|ShapeObject = this.hc()/3,
radius: number | ShapeObject = this.hc() / 3,
fillStyle: string = "white",
x: number = this.wc(),
y: number = this.hc(),
): boolean => {
if(typeof radius === "object") {
if (typeof radius === "object") {
fillStyle = radius.fillStyle || "white";
x = radius.x || this.wc();
y = radius.y || this.hc();
radius = radius.radius || this.hc()/3;
radius = radius.radius || this.hc() / 3;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
@ -2585,26 +2585,26 @@ export class UserAPI {
stroke = slices.stroke || "black";
slices = slices.slices || 3;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
ctx.save();
ctx.translate(x, y);
ctx.rotate((rotation * Math.PI) / 180);
if(slices<2) {
if (slices < 2) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = slices<1 ? secondary : fillStyle;
ctx.fillStyle = slices < 1 ? secondary : fillStyle;
ctx.fill();
ctx.beginPath();
ctx.arc(0, 0, hole, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = secondary;
ctx.fill();
ctx.restore();
return true;
}
@ -2615,17 +2615,17 @@ export class UserAPI {
for (let i = 0; i < totalSlices; i++) {
const startAngle = i * sliceAngle;
const endAngle = (i + 1) * sliceAngle;
// Calculate the position of the outer arc
const outerStartX = hole * Math.cos(startAngle);
const outerStartY = hole * Math.sin(startAngle);
ctx.beginPath();
ctx.moveTo(outerStartX, outerStartY);
ctx.arc(0, 0, radius, startAngle, endAngle);
ctx.arc(0, 0, hole, endAngle, startAngle, true);
ctx.closePath();
// Fill and stroke the slices with the specified fill style
if (i < slices - eaten) {
// Regular slices are white
@ -2639,14 +2639,14 @@ export class UserAPI {
ctx.strokeStyle = stroke;
ctx.stroke();
}
ctx.restore();
return true;
};
public pie = (
slices: number|ShapeObject = 3,
slices: number | ShapeObject = 3,
eaten: number = 0,
radius: number = this.hc() / 3,
fillStyle: string = "white",
@ -2667,18 +2667,18 @@ export class UserAPI {
eaten = slices.eaten || 0;
slices = slices.slices || 3;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
ctx.save();
ctx.translate(x, y);
ctx.rotate((rotation * Math.PI) / 180);
if(slices<2) {
if (slices < 2) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = slices<1 ? secondary : fillStyle;
ctx.fillStyle = slices < 1 ? secondary : fillStyle;
ctx.fill();
ctx.restore();
return true;
@ -2695,7 +2695,7 @@ export class UserAPI {
ctx.arc(0, 0, radius, startAngle, endAngle);
ctx.lineTo(0, 0); // Connect to center
ctx.closePath();
// Fill and stroke the slices with the specified fill style
if (i < slices - eaten) {
// Regular slices are white
@ -2709,67 +2709,67 @@ export class UserAPI {
ctx.strokeStyle = stroke;
ctx.stroke();
}
ctx.restore();
return true;
};
public star = (
points: number|ShapeObject = 5,
radius: number = this.hc()/3,
points: number | ShapeObject = 5,
radius: number = this.hc() / 3,
fillStyle: string = "white",
rotation: number = 0,
outerRadius: number = radius/100,
outerRadius: number = radius / 100,
x: number = this.wc(),
y: number = this.hc(),
): boolean => {
if(typeof points === "object") {
radius = points.radius || this.hc()/3;
if (typeof points === "object") {
radius = points.radius || this.hc() / 3;
fillStyle = points.fillStyle || "white";
x = points.x || this.wc();
y = points.y || this.hc();
rotation = points.rotation || 0;
outerRadius = points.outerRadius || radius/100;
outerRadius = points.outerRadius || radius / 100;
points = points.points || 5;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
if(points<1) return this.ball(radius, fillStyle, x, y);
if(points==1) return this.equilateral(radius, fillStyle, 0, x, y);
const ctx = canvas.getContext("2d")!;
ctx.save();
ctx.translate(x, y);
ctx.rotate((rotation * Math.PI) / 180);
ctx.beginPath();
ctx.moveTo(0, -radius);
for (let i = 0; i < points; i++) {
ctx.rotate(Math.PI / points);
ctx.lineTo(0, -(radius * outerRadius));
ctx.rotate(Math.PI / points);
ctx.lineTo(0, -radius);
}
ctx.closePath();
ctx.fillStyle = fillStyle;
ctx.fill();
ctx.restore();
return true;
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
if (points < 1) return this.ball(radius, fillStyle, x, y);
if (points == 1) return this.equilateral(radius, fillStyle, 0, x, y);
const ctx = canvas.getContext("2d")!;
ctx.save();
ctx.translate(x, y);
ctx.rotate((rotation * Math.PI) / 180);
ctx.beginPath();
ctx.moveTo(0, -radius);
for (let i = 0; i < points; i++) {
ctx.rotate(Math.PI / points);
ctx.lineTo(0, -(radius * outerRadius));
ctx.rotate(Math.PI / points);
ctx.lineTo(0, -radius);
}
ctx.closePath();
ctx.fillStyle = fillStyle;
ctx.fill();
ctx.restore();
return true;
};
public stroke = (
width: number|ShapeObject = 1,
width: number | ShapeObject = 1,
strokeStyle: string = "white",
rotation: number = 0,
x1: number = this.wc()-this.wc()/10,
x1: number = this.wc() - this.wc() / 10,
y1: number = this.hc(),
x2: number = this.wc()+this.wc()/5,
x2: number = this.wc() + this.wc() / 5,
y2: number = this.hc(),
): boolean => {
if(typeof width === "object") {
if (typeof width === "object") {
strokeStyle = width.strokeStyle || "white";
x1 = width.x1 || this.wc()-this.wc()/10;
x1 = width.x1 || this.wc() - this.wc() / 10;
y1 = width.y1 || this.hc();
x2 = width.x2 || this.wc()+this.wc()/5;
x2 = width.x2 || this.wc() + this.wc() / 5;
y2 = width.y2 || this.hc();
rotation = width.rotation || 0;
width = width.width || 1;
@ -2781,7 +2781,7 @@ export class UserAPI {
ctx.rotate((rotation * Math.PI) / 180);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(x2-x1, y2-y1);
ctx.lineTo(x2 - x1, y2 - y1);
ctx.lineWidth = width;
ctx.strokeStyle = strokeStyle;
ctx.stroke();
@ -2790,20 +2790,20 @@ export class UserAPI {
};
public box = (
width: number|ShapeObject = this.wc()/4,
height: number = this.wc()/4,
width: number | ShapeObject = this.wc() / 4,
height: number = this.wc() / 4,
fillStyle: string = "white",
rotation: number = 0,
x: number = this.wc()-this.wc()/8,
y: number = this.hc()-this.hc()/8,
x: number = this.wc() - this.wc() / 8,
y: number = this.hc() - this.hc() / 8,
): boolean => {
if(typeof width === "object") {
if (typeof width === "object") {
fillStyle = width.fillStyle || "white";
x = width.x || this.wc()-this.wc()/4;
y = width.y || this.hc()-this.hc()/2;
x = width.x || this.wc() - this.wc() / 4;
y = width.y || this.hc() - this.hc() / 2;
rotation = width.rotation || 0;
height = width.height || this.wc()/4;
width = width.width || this.wc()/4;
height = width.height || this.wc() / 4;
width = width.width || this.wc() / 4;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
@ -2817,27 +2817,27 @@ export class UserAPI {
}
public smiley = (
happiness: number|ShapeObject = 0,
radius: number = this.hc()/3,
happiness: number | ShapeObject = 0,
radius: number = this.hc() / 3,
eyeSize: number = 3.0,
fillStyle: string = "yellow",
rotation: number = 0,
x: number = this.wc(),
y: number = this.hc(),
): boolean => {
if(typeof happiness === "object") {
if (typeof happiness === "object") {
fillStyle = happiness.fillStyle || "yellow";
x = happiness.x || this.wc();
y = happiness.y || this.hc();
rotation = happiness.rotation || 0;
eyeSize = happiness.eyeSize || 3.0;
radius = happiness.radius || this.hc()/3;
radius = happiness.radius || this.hc() / 3;
happiness = happiness.happiness || 0;
}
const canvas: HTMLCanvasElement = this.app.interface.drawings as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
// Map the rotation value to an angle within the range of -PI to PI
const rotationAngle = rotation/100 * Math.PI;
const rotationAngle = rotation / 100 * Math.PI;
ctx.save();
ctx.translate(x, y);
ctx.rotate(rotationAngle);
@ -2871,16 +2871,16 @@ export class UserAPI {
const mouthY = radius / 2;
const mouthLength = radius * 0.9;
const smileFactor = 0.25; // Adjust for the smile curvature
let controlPointX = 0;
let controlPointY = 0;
if (happiness >= 0) {
controlPointY = mouthY + happiness * smileFactor * radius / 2;
} else {
controlPointY = mouthY + happiness * smileFactor * radius / 2;
}
ctx.beginPath();
ctx.moveTo(-mouthLength / 2, mouthY);
ctx.quadraticCurveTo(controlPointX, controlPointY, mouthLength / 2, mouthY);
@ -2892,7 +2892,7 @@ export class UserAPI {
}
drawText = (
text: string|ShapeObject,
text: string | ShapeObject,
fontSize: number = 24,
rotation: number = 0,
font: string = "Arial",
@ -2901,7 +2901,7 @@ export class UserAPI {
fillStyle: string = "white",
filter: string = "none",
): boolean => {
if(typeof text === "object") {
if (typeof text === "object") {
fillStyle = text.fillStyle || "white";
x = text.x || this.wc();
y = text.y || this.hc();
@ -2925,16 +2925,16 @@ export class UserAPI {
}
image = (
url: string|ShapeObject,
width: number = this.wc()/2,
height: number = this.hc()/2,
url: string | ShapeObject,
width: number = this.wc() / 2,
height: number = this.hc() / 2,
rotation: number = 0,
x: number = this.wc(),
y: number = this.hc(),
filter: string = "none",
): boolean => {
if(typeof url === "object") {
if(!url.url) return true;
if (typeof url === "object") {
if (!url.url) return true;
x = url.x || this.wc();
y = url.y || this.hc();
rotation = url.rotation || 0;
@ -2951,12 +2951,12 @@ export class UserAPI {
ctx.filter = filter;
const image = new Image();
image.src = url;
ctx.drawImage(image, -width/2, -height/2, width, height);
ctx.drawImage(image, -width / 2, -height / 2, width, height);
ctx.restore();
return true;
}
randomChar = (length: number= 1, min: number = 0, max: number = 65536): string => {
randomChar = (length: number = 1, min: number = 0, max: number = 65536): string => {
return Array.from(
{ length }, () => String.fromCodePoint(Math.floor(Math.random() * (max - min) + min))
@ -2967,7 +2967,7 @@ export class UserAPI {
const codePoint = Math.floor(Math.random() * (max - min) + min);
return String.fromCodePoint(codePoint);
};
emoji = (n: number = 1): string => {
return this.randomChar(n, 0x1f600, 0x1f64f);
};
@ -3068,7 +3068,7 @@ export class UserAPI {
this.app.clock.time_signature = [numerator, denominator];
};
public cue = (functionName: string|Function): void => {
public cue = (functionName: string | Function): void => {
functionName = typeof functionName === "function" ? functionName.name : functionName;
this.cueTimes[functionName] = this.app.clock.pulses_since_origin;
};
@ -3086,7 +3086,6 @@ export class UserAPI {
let theme_names = this.getThemes();
let selected_theme = theme_names[Math.floor(Math.random() * theme_names.length)];
this.app.readTheme(selected_theme);
this.app.api.log(selected_theme);
}
public nextTheme = (): void => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

46
src/assets/favicon.svg Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="48.000000pt" height="48.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M163 470 c-13 -5 -23 -12 -23 -15 0 -3 46 -5 102 -5 80 0 99 3 90 12
-15 15 -139 21 -169 8z"/>
<path d="M91 391 c-16 -16 -19 -32 -18 -84 1 -53 -2 -69 -18 -87 -15 -16 -20
-38 -22 -83 0 -33 2 -56 5 -50 9 13 44 13 39 -1 -2 -6 -18 -11 -34 -11 -39 0
-50 -10 -33 -30 8 -10 30 -15 60 -15 26 0 64 -7 83 -15 43 -18 125 -20 164 -3
15 6 57 14 93 17 73 7 87 24 51 60 -12 12 -21 17 -21 13 0 -4 5 -13 12 -20 8
-8 8 -12 1 -12 -18 0 -25 34 -9 45 10 7 12 16 6 25 -5 8 -7 24 -4 35 3 13 -2
28 -15 39 -12 11 -21 27 -21 36 0 9 -5 21 -11 27 -8 8 -8 17 0 31 17 32 13 56
-12 80 -31 29 -63 28 -91 -3 -16 -17 -34 -25 -56 -25 -22 0 -40 8 -56 25 -28
30 -67 32 -93 6z m89 -16 c19 -23 5 -29 -24 -10 -16 10 -33 14 -47 10 -14 -5
-19 -4 -15 4 10 16 72 13 86 -4z m211 -7 c12 -22 11 -22 -8 -5 -25 21 -41 22
-65 0 -22 -19 -36 -10 -18 12 20 24 77 19 91 -7z m-264 -30 c-3 -7 -5 -2 -5
12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m229 -5 c-11 -11 -19 6 -11 24 8 17 8 17
12 0 3 -10 2 -21 -1 -24z m-80 -8 c4 -8 10 -12 15 -9 5 3 9 0 9 -6 0 -14 60
-40 77 -33 7 3 13 -2 13 -11 0 -13 -6 -15 -27 -9 -36 9 -210 9 -245 0 -22 -6
-28 -4 -28 9 0 8 6 14 13 11 6 -2 25 2 40 10 15 8 32 11 37 8 6 -4 7 1 3 11
-4 12 -3 15 5 10 7 -4 15 -1 18 8 8 21 63 21 70 1z m83 -91 c10 -9 -37 -33
-77 -39 -55 -8 -117 2 -155 26 l-30 18 54 4 c56 4 201 -2 208 -9z m-286 -30
c-15 -7 21 -44 42 -44 8 0 15 -4 15 -10 0 -16 -33 -11 -59 9 -25 19 -24 52 1
50 9 0 10 -2 1 -5z m355 -21 c2 -14 -4 -23 -17 -28 -12 -3 -21 -13 -21 -21 0
-19 -16 -18 -24 1 -5 15 -31 16 -53 2 -8 -5 -13 -4 -13 2 0 6 5 12 10 12 6 1
15 3 20 4 6 1 18 3 28 4 25 1 66 37 52 44 -6 3 -5 4 2 3 7 -1 14 -12 16 -23z
m-255 -37 c4 -10 1 -13 -9 -9 -7 3 -14 9 -14 14 0 14 17 10 23 -5z m42 -6 c3
-5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m35 0 c0
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-170
-10 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
m215 -39 c-6 -5 -25 10 -25 20 0 5 6 4 14 -3 8 -7 12 -15 11 -17z m45 8 c0
-14 -16 -11 -29 5 -10 12 -8 13 8 9 12 -3 21 -9 21 -14z m-220 1 c0 -5 -5 -10
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m40 0 c0 -5 -4 -10
-10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m215 0 c3 -5 2
-10 -4 -10 -5 0 -13 5 -16 10 -3 6 -2 10 4 10 5 0 13 -4 16 -10z m43 -10 c7
-11 10 -20 6 -20 -7 0 -34 27 -34 34 0 13 16 5 28 -14z m-160 -17 c-10 -2 -26
-2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z m99 1 c-3 -3 -12 -4 -19 -1 -8 3
-5 6 6 6 11 1 17 -2 13 -5z m40 0 c-3 -3 -12 -4 -19 -1 -8 3 -5 6 6 6 11 1 17
-2 13 -5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="850.000000pt" height="850.000000pt" viewBox="0 0 850.000000 850.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,850.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M0 4250 l0 -3770 4250 0 4250 0 0 3770 0 3770 -4250 0 -4250 0 0
-3770z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 603 B

BIN
src/assets/topos_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

Before

Width:  |  Height:  |  Size: 427 KiB

After

Width:  |  Height:  |  Size: 427 KiB

BIN
src/assets/topos_gif.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

View File

@ -31,13 +31,12 @@ import { makeStringExtensions } from "./extensions/StringExtensions";
import { installInterfaceLogic } from "./InterfaceLogic";
import { installWindowBehaviors } from "./WindowBehavior";
import { makeNumberExtensions } from "./extensions/NumberExtensions";
// @ts-ignore
import { registerSW } from "virtual:pwa-register";
import colors from "./colors.json";
// @ts-ignore
const images = import.meta.glob("./assets/*")
if ("serviceWorker" in navigator) {
registerSW();
}
export class Editor {
// Universes and settings

View File

@ -4,15 +4,16 @@ import viteCompression from "vite-plugin-compression";
const vitePWAconfiguration = {
devOptions: {
enabled: true,
enabled: false,
suppressWarnings: true,
},
workbox: {
sourcemap: false,
cleanupOutdatedCaches: false,
maximumFileSizeToCacheInBytes: 10000000,
globPatterns: [
"**/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
"favicon/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
],
runtimeCaching: [
{
@ -35,14 +36,9 @@ const vitePWAconfiguration = {
},
],
},
includeAssets: [
"favicon/favicon.icon",
"favicon/apple-touch-icon.png",
"mask-icon.svg",
],
manifest: "manifest.webmanifest",
manifest: false,
registerType: "autoUpdate",
injectRegister: "auto",
injectRegister: "script-defer",
};
export default defineConfig(({ command, mode, ssrBuild }) => {
@ -54,6 +50,13 @@ export default defineConfig(({ command, mode, ssrBuild }) => {
port: 8000,
strictPort: true,
},
build: {
outDir: "dist",
emptyOutDir: true,
cssCodeSplit: true,
cssMinify: true,
minify: true,
}
};
} else {
return {

View File

@ -2203,7 +2203,7 @@ fast-glob@^3.2.12:
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-glob@^3.3.1:
fast-glob@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
@ -3774,13 +3774,13 @@ vite-plugin-markdown@^2.1.0:
htmlparser2 "^6.0.0"
markdown-it "^12.0.0"
vite-plugin-pwa@^0.16.7:
version "0.16.7"
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.16.7.tgz#3dcacc342766ff3598472ac7d5e0782d14e2853e"
integrity sha512-4WMA5unuKlHs+koNoykeuCfTcqEGbiTRr8sVYUQMhc6tWxZpSRnv9Ojk4LKmqVhoPGHfBVCdGaMo8t9Qidkc1Q==
vite-plugin-pwa@^0.17.4:
version "0.17.4"
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.17.4.tgz#be3b3714d4148681bc73e8e0b1e6ce1a71fa79f2"
integrity sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==
dependencies:
debug "^4.3.4"
fast-glob "^3.3.1"
fast-glob "^3.3.2"
pretty-bytes "^6.1.1"
workbox-build "^7.0.0"
workbox-window "^7.0.0"