proteus-sdk 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,70 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ichellin1/proteus/main/brand/logo/assets/lockup-color-dark.png">
4
+ <img alt="Proteus" src="https://raw.githubusercontent.com/ichellin1/proteus/main/brand/logo/assets/lockup-color-light.png" width="210">
5
+ </picture>
6
+ </p>
2
7
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
8
+ # proteus-sdk
9
+
10
+ The TypeScript SDK for [Proteus](https://github.com/ichellin1/proteus), a UI framework whose components transform into one
11
+ another instead of loading new screens: a button can become the panel it opens. It draws on a
12
+ `<canvas>` with WebGPU, or WebGL2 where WebGPU isn't available. Its core is written in Rust and
13
+ compiled to WebAssembly.
14
+
15
+ ```bash
16
+ npm install proteus-sdk
17
+ npm install --save-dev vite-plugin-wasm vite-plugin-top-level-await@1.5.0 @swc/core@1.10.16
18
+ ```
19
+
20
+ With Vite, add both plugins to `vite.config.ts`: `plugins: [wasm(), topLevelAwait()]`. Then, with
21
+ a `<canvas id="app">` on the page, a button that turns into a panel when it's clicked, and back
22
+ when the panel is clicked:
23
+
24
+ ```ts
25
+ import { colorFrom, mount, type Geometry } from "proteus-sdk";
26
+
27
+ function centered(width: number, height: number, cornerRadius: number): Geometry {
28
+ return {
29
+ position: { x: 0, y: 0, z: 0 },
30
+ size: { width, height },
31
+ rotation: 0,
32
+ scale: 1,
33
+ anchor: { x: 0.5, y: 0.5 },
34
+ color: colorFrom("#7359cc"),
35
+ cornerRadius,
36
+ };
37
+ }
38
+
39
+ await mount("app", {
40
+ setup(app) {
41
+ const button = app.component({
42
+ geometry: centered(200, 60, 30),
43
+ text: { content: "Open", sizePx: 24 },
44
+ });
45
+ const panel = app.component({
46
+ geometry: centered(480, 320, 16),
47
+ text: { content: "Close", sizePx: 24 },
48
+ visible: false,
49
+ });
50
+ const channel = app.transitionChannel();
51
+ button.onClick(() => channel.set(panel, button, { duration: 0.4 }));
52
+ panel.onClick(() => channel.set(button, panel, { duration: 0.4 }));
53
+ },
54
+ });
55
+ ```
56
+
57
+ ## Learn more
58
+
59
+ - [Getting started in TypeScript](https://github.com/ichellin1/proteus/blob/main/docs/getting-started/typescript.md),
60
+ step by step.
61
+ - [Guides](https://github.com/ichellin1/proteus/tree/main/docs): the concepts, and how-tos.
62
+ - [API reference](https://ichellin1.github.io/proteus/api/ts/).
63
+ - [Examples](https://github.com/ichellin1/proteus/tree/main/examples), and
64
+ [the reference demo](https://ichellin1.github.io/proteus/), running in your browser.
65
+ - Proteus is written in Rust, and its [Rust crates](https://crates.io/crates/proteus-sdk) can be
66
+ used directly.
67
+
68
+ ## License
69
+
70
+ MIT or Apache-2.0, at your option.
@@ -0,0 +1,24 @@
1
+ import type { Color, Vec2 } from "./types.js";
2
+ /** Converts degrees to radians, the unit {@link Geometry.rotation} uses. */
3
+ export declare function degreesToRadians(degrees: number): number;
4
+ /** Converts radians to degrees. */
5
+ export declare function radiansToDegrees(radians: number): number;
6
+ /**
7
+ * Converts a hex color or a color name to a {@link Color}.
8
+ *
9
+ * Accepts `#rgb`, `#rrggbb` and `#rrggbbaa`, with or without the `#`, and the
10
+ * names black, white, red, green, blue, yellow, orange, purple, gray (or grey)
11
+ * and transparent.
12
+ *
13
+ * @throws if `input` isn't one of those.
14
+ */
15
+ export declare function colorFrom(input: string): Color;
16
+ /**
17
+ * Converts a position measured from the canvas's top-left corner, with y
18
+ * pointing down (the usual page and canvas convention), to world units, with
19
+ * the origin at the center and y pointing up. `viewportWidth` and
20
+ * `viewportHeight` are the canvas's size in the same units as `x` and `y`.
21
+ */
22
+ export declare function topLeftToWorld(x: number, y: number, viewportWidth: number, viewportHeight: number): Vec2;
23
+ /** Converts world units back to a position from the top-left; the inverse of {@link topLeftToWorld}. */
24
+ export declare function worldToTopLeft(x: number, y: number, viewportWidth: number, viewportHeight: number): Vec2;
@@ -0,0 +1,76 @@
1
+ // Conversions from familiar web conventions (degrees, hex and named colors,
2
+ // page coordinates) to the values the SDK takes (radians, RGBA from 0 to 1,
3
+ // world units with the origin at the viewport center and y up).
4
+ /** Converts degrees to radians, the unit {@link Geometry.rotation} uses. */
5
+ export function degreesToRadians(degrees) {
6
+ return (degrees * Math.PI) / 180;
7
+ }
8
+ /** Converts radians to degrees. */
9
+ export function radiansToDegrees(radians) {
10
+ return (radians * 180) / Math.PI;
11
+ }
12
+ // A small set of CSS color names for quick prototyping. Deliberately not the
13
+ // full CSS list.
14
+ const NAMED_COLORS = {
15
+ black: "#000000",
16
+ white: "#ffffff",
17
+ red: "#ff0000",
18
+ green: "#008000",
19
+ blue: "#0000ff",
20
+ yellow: "#ffff00",
21
+ orange: "#ffa500",
22
+ purple: "#800080",
23
+ gray: "#808080",
24
+ grey: "#808080",
25
+ transparent: "#00000000",
26
+ };
27
+ /**
28
+ * Converts a hex color or a color name to a {@link Color}.
29
+ *
30
+ * Accepts `#rgb`, `#rrggbb` and `#rrggbbaa`, with or without the `#`, and the
31
+ * names black, white, red, green, blue, yellow, orange, purple, gray (or grey)
32
+ * and transparent.
33
+ *
34
+ * @throws if `input` isn't one of those.
35
+ */
36
+ export function colorFrom(input) {
37
+ const named = NAMED_COLORS[input.toLowerCase()];
38
+ const hex = (named ?? input).replace(/^#/, "");
39
+ let r;
40
+ let g;
41
+ let b;
42
+ let a = 1;
43
+ if (hex.length === 3) {
44
+ r = parseInt(hex[0] + hex[0], 16);
45
+ g = parseInt(hex[1] + hex[1], 16);
46
+ b = parseInt(hex[2] + hex[2], 16);
47
+ }
48
+ else if (hex.length === 6 || hex.length === 8) {
49
+ r = parseInt(hex.slice(0, 2), 16);
50
+ g = parseInt(hex.slice(2, 4), 16);
51
+ b = parseInt(hex.slice(4, 6), 16);
52
+ if (hex.length === 8) {
53
+ a = parseInt(hex.slice(6, 8), 16) / 255;
54
+ }
55
+ }
56
+ else {
57
+ throw new Error(`colorFrom: unrecognized color "${input}"`);
58
+ }
59
+ if ([r, g, b].some((c) => Number.isNaN(c))) {
60
+ throw new Error(`colorFrom: unrecognized color "${input}"`);
61
+ }
62
+ return { r: r / 255, g: g / 255, b: b / 255, a };
63
+ }
64
+ /**
65
+ * Converts a position measured from the canvas's top-left corner, with y
66
+ * pointing down (the usual page and canvas convention), to world units, with
67
+ * the origin at the center and y pointing up. `viewportWidth` and
68
+ * `viewportHeight` are the canvas's size in the same units as `x` and `y`.
69
+ */
70
+ export function topLeftToWorld(x, y, viewportWidth, viewportHeight) {
71
+ return { x: x - viewportWidth / 2, y: viewportHeight / 2 - y };
72
+ }
73
+ /** Converts world units back to a position from the top-left; the inverse of {@link topLeftToWorld}. */
74
+ export function worldToTopLeft(x, y, viewportWidth, viewportHeight) {
75
+ return { x: x + viewportWidth / 2, y: viewportHeight / 2 - y };
76
+ }