diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 9d8562d..5327201 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -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:
diff --git a/README.md b/README.md
index 2098947..08d99f5 100644
--- a/README.md
+++ b/README.md
@@ -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/)
---
-
+
## 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 :)
\ No newline at end of file
diff --git a/favicon/site.webmanifest b/favicon/site.webmanifest
deleted file mode 100644
index fa99de7..0000000
--- a/favicon/site.webmanifest
+++ /dev/null
@@ -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"
-}
diff --git a/img/screnshot.png b/img/screnshot.png
deleted file mode 100644
index 3a21862..0000000
Binary files a/img/screnshot.png and /dev/null differ
diff --git a/index.html b/index.html
index 83b5b39..bba9383 100644
--- a/index.html
+++ b/index.html
@@ -1,13 +1,14 @@
-
-
+
Topos
+
-
-
-
+
+
+
+
diff --git a/manifest.webmanifest b/manifest.webmanifest
deleted file mode 100644
index 8fb49fd..0000000
--- a/manifest.webmanifest
+++ /dev/null
@@ -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"
- }
- ]
-}
diff --git a/package.json b/package.json
index 520f9f0..045dced 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/favicon/android-chrome-192x192.png b/public/favicon/android-chrome-192x192.png
similarity index 100%
rename from favicon/android-chrome-192x192.png
rename to public/favicon/android-chrome-192x192.png
diff --git a/favicon/android-chrome-512x512.png b/public/favicon/android-chrome-512x512.png
similarity index 100%
rename from favicon/android-chrome-512x512.png
rename to public/favicon/android-chrome-512x512.png
diff --git a/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png
similarity index 100%
rename from favicon/apple-touch-icon.png
rename to public/favicon/apple-touch-icon.png
diff --git a/favicon/browserconfig.xml b/public/favicon/browserconfig.xml
similarity index 100%
rename from favicon/browserconfig.xml
rename to public/favicon/browserconfig.xml
diff --git a/favicon/favicon-16x16.png b/public/favicon/favicon-16x16.png
similarity index 100%
rename from favicon/favicon-16x16.png
rename to public/favicon/favicon-16x16.png
diff --git a/favicon/favicon-32x32.png b/public/favicon/favicon-32x32.png
similarity index 100%
rename from favicon/favicon-32x32.png
rename to public/favicon/favicon-32x32.png
diff --git a/favicon/favicon.ico b/public/favicon/favicon.ico
similarity index 100%
rename from favicon/favicon.ico
rename to public/favicon/favicon.ico
diff --git a/public/favicon/favicon.svg b/public/favicon/favicon.svg
new file mode 100644
index 0000000..b152ce8
--- /dev/null
+++ b/public/favicon/favicon.svg
@@ -0,0 +1,46 @@
+
+
+
diff --git a/favicon/mstile-150x150.png b/public/favicon/mstile-150x150.png
similarity index 100%
rename from favicon/mstile-150x150.png
rename to public/favicon/mstile-150x150.png
diff --git a/favicon/safari-pinned-tab.svg b/public/favicon/safari-pinned-tab.svg
similarity index 100%
rename from favicon/safari-pinned-tab.svg
rename to public/favicon/safari-pinned-tab.svg
diff --git a/public/favicon/screenshot_miniature.png b/public/favicon/screenshot_miniature.png
new file mode 100644
index 0000000..1f6bc9e
Binary files /dev/null and b/public/favicon/screenshot_miniature.png differ
diff --git a/public/favicon/topos_code.png b/public/favicon/topos_code.png
new file mode 100644
index 0000000..1f6bc9e
Binary files /dev/null and b/public/favicon/topos_code.png differ
diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest
new file mode 100644
index 0000000..e0633b8
--- /dev/null
+++ b/public/manifest.webmanifest
@@ -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"
+ }
+ ]
+}
diff --git a/src/API.ts b/src/API.ts
index f5ec3dd..856e697 100644
--- a/src/API.ts
+++ b/src/API.ts
@@ -121,7 +121,7 @@ export class UserAPI {
public currentSeed: string | undefined = undefined;
public localSeeds = new Map();
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).next().value
- if(cachedValue!==0 && !cachedValue) {
- const generator = value as unknown as Generator
- 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).next().value
+ if (cachedValue !== 0 && !cachedValue) {
const generator = value as unknown as Generator
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).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
+ 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).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): 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 {
+ 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 {
+ 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 {
- 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 => {
diff --git a/src/assets/android-chrome-192x192.png b/src/assets/android-chrome-192x192.png
new file mode 100644
index 0000000..6425053
Binary files /dev/null and b/src/assets/android-chrome-192x192.png differ
diff --git a/src/assets/android-chrome-512x512.png b/src/assets/android-chrome-512x512.png
new file mode 100644
index 0000000..4254207
Binary files /dev/null and b/src/assets/android-chrome-512x512.png differ
diff --git a/src/assets/apple-touch-icon.png b/src/assets/apple-touch-icon.png
new file mode 100644
index 0000000..979536b
Binary files /dev/null and b/src/assets/apple-touch-icon.png differ
diff --git a/src/assets/favicon-16x16.png b/src/assets/favicon-16x16.png
new file mode 100644
index 0000000..b79c6b8
Binary files /dev/null and b/src/assets/favicon-16x16.png differ
diff --git a/src/assets/favicon-32x32.png b/src/assets/favicon-32x32.png
new file mode 100644
index 0000000..2f344cd
Binary files /dev/null and b/src/assets/favicon-32x32.png differ
diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico
new file mode 100644
index 0000000..19cdd93
Binary files /dev/null and b/src/assets/favicon.ico differ
diff --git a/src/assets/favicon.svg b/src/assets/favicon.svg
new file mode 100644
index 0000000..b152ce8
--- /dev/null
+++ b/src/assets/favicon.svg
@@ -0,0 +1,46 @@
+
+
+
diff --git a/src/assets/mstile-150x150.png b/src/assets/mstile-150x150.png
new file mode 100644
index 0000000..bef2f21
Binary files /dev/null and b/src/assets/mstile-150x150.png differ
diff --git a/src/assets/safari-pinned-tab.svg b/src/assets/safari-pinned-tab.svg
new file mode 100644
index 0000000..c46073d
--- /dev/null
+++ b/src/assets/safari-pinned-tab.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/src/assets/topos_code.png b/src/assets/topos_code.png
new file mode 100644
index 0000000..95ba295
Binary files /dev/null and b/src/assets/topos_code.png differ
diff --git a/topos_frog.png b/src/assets/topos_frog.png
similarity index 100%
rename from topos_frog.png
rename to src/assets/topos_frog.png
diff --git a/src/assets/topos_gif.gif b/src/assets/topos_gif.gif
new file mode 100644
index 0000000..ad26885
Binary files /dev/null and b/src/assets/topos_gif.gif differ
diff --git a/src/main.ts b/src/main.ts
index e0581f8..8d165d6 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -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
diff --git a/vite.config.js b/vite.config.js
index 22d1e67..f53522b 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -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 {
diff --git a/yarn.lock b/yarn.lock
index d32d3cc..7c847aa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"