@pantoken/rust 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 +56 -0
- package/dist/index.d.mts +55 -0
- package/dist/index.mjs +110 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @pantoken/rust
|
|
2
|
+
|
|
3
|
+
Emit Instructure design tokens as Rust constants for the two mainstream native Rust GUIs. Colours
|
|
4
|
+
become the framework's colour type; dimensions become `f32` (px). Icons and non-colour/dimension
|
|
5
|
+
tokens are skipped.
|
|
6
|
+
|
|
7
|
+
| Format | Type | Framework |
|
|
8
|
+
| ------ | --------- | ------------------------------------- |
|
|
9
|
+
| `egui` | `Color32` | [egui](https://github.com/emilk/egui) |
|
|
10
|
+
| `iced` | `Color` | [iced](https://iced.rs) |
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm i @pantoken/rust
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { writeFileSync } from "node:fs";
|
|
22
|
+
import { generateRust } from "@pantoken/rust";
|
|
23
|
+
|
|
24
|
+
writeFileSync("tokens.rs", generateRust({ format: "egui", theme: "rebrand" }));
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or via the CLI:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pantoken generate rust --format egui --out tokens.rs
|
|
31
|
+
pantoken generate rust --format iced --out src/tokens.rs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Output
|
|
35
|
+
|
|
36
|
+
A single Rust module of `pub const`s — one per colour and dimension token, `SCREAMING_SNAKE_CASE`,
|
|
37
|
+
prefixed with `use egui::Color32;` or `use iced::Color;` to match the chosen format. Web-rendered
|
|
38
|
+
Rust UIs (Tauri, Dioxus-web, Leptos, Yew) render to HTML/CSS — use `@pantoken/css` there, not this
|
|
39
|
+
package.
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
- **`generateRust(options?): string`** — emit Rust for a named theme (from the vendored
|
|
44
|
+
`@pantoken/tokens` IR). Returns the source string.
|
|
45
|
+
- **`toRust(tokens, options?): string`** — same, but for an explicit token IR.
|
|
46
|
+
- **`RustFormat`** — `"egui" | "iced"`.
|
|
47
|
+
- **`RustOptions`** — the `format` and `mode` options (`generateRust` also takes `theme`).
|
|
48
|
+
|
|
49
|
+
## Related
|
|
50
|
+
|
|
51
|
+
- Uses `@pantoken/tokens` for the vendored token IR and `@pantoken/core` for the flattened Style
|
|
52
|
+
Dictionary values.
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Mode } from "@pantoken/core";
|
|
2
|
+
import { Theme, Token } from "@pantoken/model";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
/** The Rust GUI framework to target. */
|
|
6
|
+
type RustFormat = "egui" | "iced";
|
|
7
|
+
/** Options for {@link toRust} / {@link generateRust}. */
|
|
8
|
+
interface RustOptions {
|
|
9
|
+
/** The Rust GUI to target (default `"egui"`). */
|
|
10
|
+
format?: RustFormat;
|
|
11
|
+
/** Which `light-dark()` mode to resolve (default `"light"`). */
|
|
12
|
+
mode?: Mode;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Emit Rust constants for an explicit token IR.
|
|
16
|
+
*
|
|
17
|
+
* @param tokens - The IR.
|
|
18
|
+
* @param options - {@link RustOptions}.
|
|
19
|
+
* @returns The Rust source (a module of `pub const`s).
|
|
20
|
+
*
|
|
21
|
+
* @example egui (Color32), the default format
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { toRust } from "@pantoken/rust";
|
|
24
|
+
* import { byTheme } from "@pantoken/tokens";
|
|
25
|
+
*
|
|
26
|
+
* const source = toRust(byTheme("rebrand"));
|
|
27
|
+
* // "use egui::Color32;\npub const COLOR_BACKGROUND_BRAND: Color32 = Color32::from_rgb(…);"
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example iced (Color), dark mode
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { toRust } from "@pantoken/rust";
|
|
33
|
+
* import { byTheme } from "@pantoken/tokens";
|
|
34
|
+
*
|
|
35
|
+
* const source = toRust(byTheme("rebrand"), { format: "iced", mode: "dark" });
|
|
36
|
+
* // "use iced::Color;\npub const COLOR_BACKGROUND_BRAND: Color = Color { r: …, g: …, b: …, a: … };"
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function toRust(tokens: readonly Token[], options?: RustOptions): string;
|
|
40
|
+
/**
|
|
41
|
+
* Emit Rust constants for a named theme (using the vendored `@pantoken/tokens` IR).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { writeFileSync } from "node:fs";
|
|
46
|
+
* import { generateRust } from "@pantoken/rust";
|
|
47
|
+
*
|
|
48
|
+
* writeFileSync("tokens.rs", generateRust({ format: "egui", theme: "rebrand" }));
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare function generateRust(options?: RustOptions & {
|
|
52
|
+
theme?: Theme;
|
|
53
|
+
}): string;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { RustFormat, RustOptions, generateRust as default, generateRust, toRust };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { toStyleDictionary } from "@pantoken/core";
|
|
2
|
+
import { byTheme } from "@pantoken/tokens";
|
|
3
|
+
import { parseHexColor } from "@pantoken/utils";
|
|
4
|
+
//#region src/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* `@pantoken/rust` — emit Instructure design tokens as Rust constants for the two mainstream native
|
|
7
|
+
* Rust GUIs: **egui** (`Color32`) and **iced** (`Color`). Colours become the framework's colour
|
|
8
|
+
* type; dimensions become `f32` (px). Web-rendered Rust UIs (Tauri, Dioxus-web, Leptos, Yew) should
|
|
9
|
+
* use `@pantoken/css` instead.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
* @experimental
|
|
13
|
+
*/
|
|
14
|
+
function hexToRgb(hex) {
|
|
15
|
+
const c = parseHexColor(hex);
|
|
16
|
+
return c ? {
|
|
17
|
+
r: c.r,
|
|
18
|
+
g: c.g,
|
|
19
|
+
b: c.b,
|
|
20
|
+
a: Math.round(c.a * 255)
|
|
21
|
+
} : void 0;
|
|
22
|
+
}
|
|
23
|
+
function constName(tokenName) {
|
|
24
|
+
return tokenName.replace(/^--instui-/, "").replace(/-/g, "_").toUpperCase();
|
|
25
|
+
}
|
|
26
|
+
function dimension(value) {
|
|
27
|
+
const m = /^(-?\d*\.?\d+)(px|rem|em)?$/.exec(value.trim());
|
|
28
|
+
if (!m) return void 0;
|
|
29
|
+
return Number(m[1]) * (m[2] === "rem" || m[2] === "em" ? 16 : 1);
|
|
30
|
+
}
|
|
31
|
+
/** Format a number as a Rust `f32` literal (always with a decimal, so `1` → `1.0`). */
|
|
32
|
+
function f32lit(n) {
|
|
33
|
+
return Number.isInteger(n) ? `${n}.0` : `${n}`;
|
|
34
|
+
}
|
|
35
|
+
function colorLiteral(format, rgb) {
|
|
36
|
+
if (format === "egui") return {
|
|
37
|
+
ty: "Color32",
|
|
38
|
+
expr: rgb.a === 255 ? `Color32::from_rgb(${rgb.r}, ${rgb.g}, ${rgb.b})` : `Color32::from_rgba_unmultiplied(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`
|
|
39
|
+
};
|
|
40
|
+
const c = (n) => f32lit(n / 255);
|
|
41
|
+
return {
|
|
42
|
+
ty: "Color",
|
|
43
|
+
expr: `Color { r: ${c(rgb.r)}, g: ${c(rgb.g)}, b: ${c(rgb.b)}, a: ${c(rgb.a)} }`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Emit Rust constants for an explicit token IR.
|
|
48
|
+
*
|
|
49
|
+
* @param tokens - The IR.
|
|
50
|
+
* @param options - {@link RustOptions}.
|
|
51
|
+
* @returns The Rust source (a module of `pub const`s).
|
|
52
|
+
*
|
|
53
|
+
* @example egui (Color32), the default format
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { toRust } from "@pantoken/rust";
|
|
56
|
+
* import { byTheme } from "@pantoken/tokens";
|
|
57
|
+
*
|
|
58
|
+
* const source = toRust(byTheme("rebrand"));
|
|
59
|
+
* // "use egui::Color32;\npub const COLOR_BACKGROUND_BRAND: Color32 = Color32::from_rgb(…);"
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example iced (Color), dark mode
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { toRust } from "@pantoken/rust";
|
|
65
|
+
* import { byTheme } from "@pantoken/tokens";
|
|
66
|
+
*
|
|
67
|
+
* const source = toRust(byTheme("rebrand"), { format: "iced", mode: "dark" });
|
|
68
|
+
* // "use iced::Color;\npub const COLOR_BACKGROUND_BRAND: Color = Color { r: …, g: …, b: …, a: … };"
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
function toRust(tokens, options = {}) {
|
|
72
|
+
const format = options.format ?? "egui";
|
|
73
|
+
const dictionary = toStyleDictionary(tokens, options.mode ?? "light");
|
|
74
|
+
const lines = [
|
|
75
|
+
"// GENERATED by @pantoken/rust — do not edit by hand.",
|
|
76
|
+
"#![allow(dead_code)]",
|
|
77
|
+
format === "egui" ? "use egui::Color32;" : "use iced::Color;",
|
|
78
|
+
""
|
|
79
|
+
];
|
|
80
|
+
for (const [name, leaf] of Object.entries(dictionary)) {
|
|
81
|
+
const constant = constName(`--${name}`);
|
|
82
|
+
if (leaf.type === "color") {
|
|
83
|
+
const rgb = hexToRgb(leaf.value);
|
|
84
|
+
if (!rgb) continue;
|
|
85
|
+
const { ty, expr } = colorLiteral(format, rgb);
|
|
86
|
+
lines.push(`pub const ${constant}: ${ty} = ${expr};`);
|
|
87
|
+
} else if (leaf.type === "dimension" || leaf.type === "number") {
|
|
88
|
+
const n = dimension(leaf.value);
|
|
89
|
+
if (n === void 0) continue;
|
|
90
|
+
lines.push(`pub const ${constant}: f32 = ${f32lit(n)};`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return `${lines.join("\n")}\n`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Emit Rust constants for a named theme (using the vendored `@pantoken/tokens` IR).
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { writeFileSync } from "node:fs";
|
|
101
|
+
* import { generateRust } from "@pantoken/rust";
|
|
102
|
+
*
|
|
103
|
+
* writeFileSync("tokens.rs", generateRust({ format: "egui", theme: "rebrand" }));
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
function generateRust(options = {}) {
|
|
107
|
+
return toRust(byTheme(options.theme ?? "rebrand"), options);
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { generateRust as default, generateRust, toRust };
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pantoken/rust",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Emit Instructure design tokens as Rust constants for egui (Color32) or iced (Color).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.mjs",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@pantoken/core": "0.1.0",
|
|
19
|
+
"@pantoken/model": "0.1.0",
|
|
20
|
+
"@pantoken/tokens": "0.1.0",
|
|
21
|
+
"@pantoken/utils": "0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^24.13.3",
|
|
25
|
+
"typescript": "^6.0.3",
|
|
26
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.2.4",
|
|
27
|
+
"vite-plus": "0.2.4"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "vp pack",
|
|
31
|
+
"dev": "vp pack --watch",
|
|
32
|
+
"test": "vp test",
|
|
33
|
+
"check": "vp check"
|
|
34
|
+
}
|
|
35
|
+
}
|