@xenolithengine/graph-theme-xen 0.7.0-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 strelok2012
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @xenolithengine/graph-theme-xen
2
+
3
+ [![BETA](https://img.shields.io/badge/status-BETA-FCB400?style=flat-square)](https://github.com/XenolithEngine/xenolith-graph#status)
4
+ [![MIT](https://img.shields.io/badge/license-MIT-FCB400?style=flat-square)](https://github.com/XenolithEngine/xenolith-graph/blob/main/LICENSE)
5
+
6
+ **Xen** — the default original design system for XenolithGraph.
7
+
8
+ > **Beta** — public API in `STABLE-API.md` is the surface we plan to freeze, but it is **NOT frozen yet** — breaking changes can land at any point before v1.0. If you adopt now, pin an exact version.
9
+
10
+ A dark, gold-accented visual language with typed pins (circle / empty-circle / chevron), four category-coloured headers (logic green / data blue / macro purple / utility white), and glassmorphic header effects.
11
+
12
+ The Figma source is the canonical authority for all visuals. Static reference assets — including the SVG export and screenshots of all node variants — live under `reference/`.
13
+
14
+ ## Usage
15
+
16
+ ```ts
17
+ import { xenTokens } from '@xenolithengine/graph-theme-xen'
18
+ import { Editor } from '@xenolithengine/graph-editor'
19
+
20
+ const editor = new Editor({ theme: xenTokens })
21
+ ```
22
+
23
+ Partial overrides:
24
+
25
+ ```ts
26
+ const editor = new Editor({
27
+ theme: {
28
+ ...xenTokens,
29
+ pinType: {
30
+ ...xenTokens.pinType,
31
+ float: { ...xenTokens.pinType.float, color: '#7FE067' },
32
+ },
33
+ },
34
+ })
35
+ ```
36
+
37
+ ## Design rules
38
+
39
+ - **Edge colour follows the source pin's type colour.** A green-out pin produces a green wire. This is an invariant of the Xen system.
40
+ - **Selected nodes** wear a 1 px white border with a soft white glow. **Hovered nodes** wear a 1 px brand-yellow border with a soft yellow glow.
41
+ - **Pin shapes carry meaning.** Filled circle — typed value. Empty circle — typed but unconnected (`wildcard`). Chevron — `exec` flow pin.
42
+ - **Header backdrop blur** is rendered to a cached texture at theme load — it is not recomputed per frame. See ADR-0004.
43
+ - **Pill nodes** are the collapsed-summary form of any standard node, carrying only essential pins and a horizontal accent glow.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Register Inter (Regular / SemiBold / Bold) with the document's font set from the WOFF2 files
3
+ * shipped inside this package. Call once at application start, before rendering any text.
4
+ *
5
+ * Bundlers that understand `new URL(..., import.meta.url)` (Vite ≥ 4, webpack 5, esbuild, Rollup
6
+ * with `@rollup/plugin-url`) emit the WOFF2 as a static asset and rewrite the URL. No external
7
+ * CDN is contacted; the fonts ride in the npm package.
8
+ *
9
+ * Subsequent calls return the same promise — safe to invoke multiple times.
10
+ *
11
+ * In non-DOM environments (Node, SSR) this resolves immediately as a no-op.
12
+ */
13
+ export declare function loadXenFonts(): Promise<void>;
14
+ //# sourceMappingURL=fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA4B5C"}
package/dist/fonts.js ADDED
@@ -0,0 +1,43 @@
1
+ const VARIANTS = [
2
+ { weight: 400, style: 'normal', file: 'Inter-Regular.woff2' },
3
+ { weight: 600, style: 'normal', file: 'Inter-SemiBold.woff2' },
4
+ { weight: 700, style: 'normal', file: 'Inter-Bold.woff2' },
5
+ ];
6
+ let loadPromise = null;
7
+ /**
8
+ * Register Inter (Regular / SemiBold / Bold) with the document's font set from the WOFF2 files
9
+ * shipped inside this package. Call once at application start, before rendering any text.
10
+ *
11
+ * Bundlers that understand `new URL(..., import.meta.url)` (Vite ≥ 4, webpack 5, esbuild, Rollup
12
+ * with `@rollup/plugin-url`) emit the WOFF2 as a static asset and rewrite the URL. No external
13
+ * CDN is contacted; the fonts ride in the npm package.
14
+ *
15
+ * Subsequent calls return the same promise — safe to invoke multiple times.
16
+ *
17
+ * In non-DOM environments (Node, SSR) this resolves immediately as a no-op.
18
+ */
19
+ export function loadXenFonts() {
20
+ if (typeof document === 'undefined' || typeof FontFace === 'undefined') {
21
+ return Promise.resolve();
22
+ }
23
+ if (loadPromise)
24
+ return loadPromise;
25
+ loadPromise = (async () => {
26
+ await Promise.all(VARIANTS.map(async ({ weight, style, file }) => {
27
+ const url = new URL(`./fonts/${file}`, import.meta.url).href;
28
+ const face = new FontFace('Inter', `url(${url}) format("woff2")`, {
29
+ weight: String(weight),
30
+ style,
31
+ display: 'swap',
32
+ });
33
+ await face.load();
34
+ document.fonts.add(face);
35
+ }));
36
+ // `face.load()` resolves when the binary is available, but Canvas2D text measurement only
37
+ // picks up the new face after `document.fonts.ready` resolves. Without this await PIXI Text
38
+ // can fall back to the next family in the list during the first measure pass.
39
+ await document.fonts.ready;
40
+ })();
41
+ return loadPromise;
42
+ }
43
+ //# sourceMappingURL=fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAMA,MAAM,QAAQ,GAA2B;IACvC,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,EAAE;IAC7D,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,sBAAsB,EAAE;IAC9D,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,EAAE;CAC3D,CAAA;AAED,IAAI,WAAW,GAAyB,IAAI,CAAA;AAE5C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACvE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IACD,IAAI,WAAW;QAAE,OAAO,WAAW,CAAA;IAEnC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;QACxB,MAAM,OAAO,CAAC,GAAG,CACf,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE;YAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAA;YAC5D,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,EAAE;gBAChE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;gBACtB,KAAK;gBACL,OAAO,EAAE,MAAM;aAChB,CAAC,CAAA;YACF,MAAM,IAAI,CAAC,IAAI,EAAE,CAGhB;YAAC,QAAQ,CAAC,KAA+C,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACtE,CAAC,CAAC,CACH,CAAA;QACD,0FAA0F;QAC1F,4FAA4F;QAC5F,8EAA8E;QAC9E,MAAM,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAA;IAC5B,CAAC,CAAC,EAAE,CAAA;IAEJ,OAAO,WAAW,CAAA;AACpB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { XenTokens } from './types.js';
2
+ export declare const xenTokens: XenTokens;
3
+ export declare const VERSION: string;
4
+ export { loadXenFonts } from './fonts.js';
5
+ export { mergeTheme } from './merge.js';
6
+ export type { DeepPartial } from './merge.js';
7
+ export type { XenTokens, XenCategoryMap, XenPinTypeMap, XenSurfaceColors, PinTypeToken, CategoryToken, StateStyle, PinShape, TypographyStyle, ShadowStyle, } from './types.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C,eAAO,MAAM,SAAS,EAAwB,SAAS,CAAA;AAEvD,eAAO,MAAM,OAAO,QAAoB,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,YAAY,EACV,SAAS,EACT,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import tokens from './tokens.json' with { type: 'json' };
2
+ export const xenTokens = tokens;
3
+ export const VERSION = xenTokens.version;
4
+ export { loadXenFonts } from './fonts.js';
5
+ export { mergeTheme } from './merge.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,eAAe,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAGxD,MAAM,CAAC,MAAM,SAAS,GAAG,MAA8B,CAAA;AAEvD,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { XenTokens } from './types.js';
2
+ /** Recursively-optional version of `XenTokens` for partial overrides. */
3
+ export type DeepPartial<T> = T extends ReadonlyArray<infer _> ? T : T extends object ? {
4
+ [K in keyof T]?: DeepPartial<T[K]>;
5
+ } : T;
6
+ /**
7
+ * Produce a new `XenTokens` with `override` deep-merged into `base`. Plain-object branches are
8
+ * merged recursively; arrays, strings, and numbers in `override` replace whole values. Always
9
+ * returns a new object — the base is never mutated.
10
+ */
11
+ export declare function mergeTheme(base: XenTokens, override: DeepPartial<XenTokens>): XenTokens;
12
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C,yEAAyE;AACzE,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GACzD,CAAC,GACD,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtC,CAAC,CAAA;AA0BP;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC,GAAG,SAAS,CAKvF"}
package/dist/merge.js ADDED
@@ -0,0 +1,31 @@
1
+ function isPlainObject(value) {
2
+ return (typeof value === 'object' &&
3
+ value !== null &&
4
+ !Array.isArray(value) &&
5
+ (value.constructor === Object || value.constructor === undefined));
6
+ }
7
+ function mergeDeep(base, override) {
8
+ const result = { ...base };
9
+ for (const key of Object.keys(override)) {
10
+ const o = override[key];
11
+ if (o === undefined)
12
+ continue;
13
+ const b = base[key];
14
+ if (isPlainObject(o) && isPlainObject(b)) {
15
+ result[key] = mergeDeep(b, o);
16
+ }
17
+ else {
18
+ result[key] = o;
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+ /**
24
+ * Produce a new `XenTokens` with `override` deep-merged into `base`. Plain-object branches are
25
+ * merged recursively; arrays, strings, and numbers in `override` replace whole values. Always
26
+ * returns a new object — the base is never mutated.
27
+ */
28
+ export function mergeTheme(base, override) {
29
+ return mergeDeep(base, override);
30
+ }
31
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AASA,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,CAAC,KAAK,CAAC,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAClE,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAoC,IAAO,EAAE,QAAiC;IAC9F,MAAM,MAAM,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAA;IACnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,CAAC,KAAK,SAAS;YAAE,SAAQ;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACnB,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAC/B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IACD,OAAO,MAAW,CAAA;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe,EAAE,QAAgC;IAC1E,OAAO,SAAS,CACd,IAA0C,EAC1C,QAA8C,CACvB,CAAA;AAC3B,CAAC"}
@@ -0,0 +1,288 @@
1
+ {
2
+ "$schema": "./tokens.schema.json",
3
+ "name": "xen",
4
+ "version": "0.7.0-beta.0",
5
+ "color": {
6
+ "brand": {
7
+ "primary": "#FCB400",
8
+ "primaryDark": "#C28B00",
9
+ "primaryDeep": "#68531D",
10
+ "primaryLight": "#FFDF67",
11
+ "primaryPale": "#FFEDA8",
12
+ "primaryAlpha20": "rgba(252, 180, 0, 0.2)",
13
+ "primaryAlpha30": "rgba(252, 180, 0, 0.3)",
14
+ "primaryAlpha50": "rgba(252, 180, 0, 0.5)",
15
+ "primaryAlpha60": "rgba(255, 237, 168, 0.6)"
16
+ },
17
+ "surface": {
18
+ "canvas": "#1A1A1A",
19
+ "node": "#0F110E",
20
+ "panel": "#191919",
21
+ "elevated": "#232323",
22
+ "muted": "#292929",
23
+ "subtle": "#363636",
24
+ "outline": "#424242",
25
+ "divider": "#363636",
26
+ "headerEnd": "#30342E"
27
+ },
28
+ "text": {
29
+ "primary": "#FFFFFF",
30
+ "secondary": "#B8B8B8",
31
+ "muted": "rgba(255, 255, 255, 0.5)",
32
+ "disabled": "rgba(255, 255, 255, 0.3)"
33
+ },
34
+ "alpha": {
35
+ "white10": "rgba(255, 255, 255, 0.1)",
36
+ "white20": "rgba(255, 255, 255, 0.2)",
37
+ "white30": "rgba(255, 255, 255, 0.3)",
38
+ "white50": "rgba(255, 255, 255, 0.5)",
39
+ "dark50": "rgba(71, 71, 71, 0.5)"
40
+ },
41
+ "minimap": {
42
+ "background": "rgba(20, 20, 20, 0.85)",
43
+ "border": "#424242",
44
+ "node": "rgba(255, 255, 255, 0.32)",
45
+ "frame": "rgba(252, 180, 0, 0.14)",
46
+ "frameBorder": "#FCB400"
47
+ },
48
+ "widget": {
49
+ "bg": "rgba(255, 255, 255, 0.045)",
50
+ "bgHover": "rgba(255, 255, 255, 0.09)",
51
+ "bgFocused": "#0F110E",
52
+ "track": "rgba(255, 255, 255, 0.08)",
53
+ "fill": "#FCB400",
54
+ "fillAlpha": 0.55,
55
+ "text": "#FFFFFF",
56
+ "label": "#B8B8B8",
57
+ "placeholder": "rgba(255, 255, 255, 0.35)",
58
+ "border": "rgba(255, 255, 255, 0.12)",
59
+ "borderFocused": "#FCB400",
60
+ "selection": "rgba(252, 180, 0, 0.35)",
61
+ "knob": "#FFFFFF"
62
+ }
63
+ },
64
+ "pinType": {
65
+ "exec": {
66
+ "label": "Exec",
67
+ "color": "#FCB400",
68
+ "shape": "chevron",
69
+ "edgeColor": "#FCB400",
70
+ "edgeWidth": 3,
71
+ "note": "Imperative flow. Carries no value, only ordering."
72
+ },
73
+ "float": {
74
+ "label": "Float",
75
+ "color": "#85C244",
76
+ "shape": "circle",
77
+ "edgeColor": "#85C244",
78
+ "edgeWidth": 2
79
+ },
80
+ "object": {
81
+ "label": "Object",
82
+ "color": "#3E95B9",
83
+ "shape": "circle",
84
+ "edgeColor": "#3E95B9",
85
+ "edgeWidth": 2
86
+ },
87
+ "string": {
88
+ "label": "String",
89
+ "color": "#FF5622",
90
+ "shape": "circle",
91
+ "edgeColor": "#FF5622",
92
+ "edgeWidth": 2
93
+ },
94
+ "any": {
95
+ "label": "Any",
96
+ "color": "#FFFFFF",
97
+ "shape": "circle",
98
+ "edgeColor": "#FFFFFF",
99
+ "edgeWidth": 2
100
+ },
101
+ "wildcard": {
102
+ "label": "Wildcard",
103
+ "color": "#9AD6E3",
104
+ "shape": "circle-empty",
105
+ "edgeColor": "#9AD6E3",
106
+ "edgeWidth": 2,
107
+ "note": "Typed but unconnected; renders with outline only, no fill."
108
+ }
109
+ },
110
+ "category": {
111
+ "logic": {
112
+ "label": "Logic",
113
+ "accent": "#85C244",
114
+ "gradient": "linear-gradient(90deg, rgba(133, 194, 68, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
115
+ },
116
+ "data": {
117
+ "label": "Data",
118
+ "accent": "#3E95B9",
119
+ "gradient": "linear-gradient(90deg, rgba(62, 149, 185, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
120
+ },
121
+ "macro": {
122
+ "label": "Macro",
123
+ "accent": "#8A38F5",
124
+ "gradient": "linear-gradient(90deg, rgba(138, 56, 245, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
125
+ },
126
+ "utility": {
127
+ "label": "Utility",
128
+ "accent": "#FFFFFF",
129
+ "gradient": "linear-gradient(90deg, rgba(255, 255, 255, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
130
+ }
131
+ },
132
+ "pill": {
133
+ "orange": "linear-gradient(90deg, rgba(15, 17, 14, 0.5) 12.63%, rgba(255, 86, 34, 0.3) 51.57%, rgba(15, 17, 14, 0.5) 90.91%), #0F110E",
134
+ "green": "linear-gradient(90deg, rgba(15, 17, 14, 0.5) 12.63%, rgba(133, 194, 68, 0.3) 51.57%, rgba(15, 17, 14, 0.5) 90.91%), #0F110E"
135
+ },
136
+ "state": {
137
+ "hover": {
138
+ "border": "#FCB400",
139
+ "borderWidth": 1,
140
+ "glow": "rgba(252, 180, 0, 0.52)",
141
+ "glowBlur": 4
142
+ },
143
+ "selected": {
144
+ "border": "#FFFFFF",
145
+ "borderWidth": 1,
146
+ "glow": "rgba(255, 255, 255, 0.4)",
147
+ "glowBlur": 5
148
+ },
149
+ "active": {
150
+ "border": "#FFDF67",
151
+ "borderWidth": 1,
152
+ "glow": "rgba(252, 180, 0, 0.64)",
153
+ "glowBlur": 6
154
+ },
155
+ "disabled": {
156
+ "opacity": 0.4
157
+ }
158
+ },
159
+ "geometry": {
160
+ "node": {
161
+ "radius": 7,
162
+ "minWidth": 150,
163
+ "headerHeight": 21,
164
+ "headerPadding": 2,
165
+ "innerPaddingX": 4,
166
+ "innerPaddingY": 4,
167
+ "pillRadius": 20,
168
+ "pillHeight": 40,
169
+ "pillMinWidth": 140
170
+ },
171
+ "pin": {
172
+ "diameter": 8,
173
+ "stroke": 1,
174
+ "strokeColor": "#1A1A1A",
175
+ "labelGap": 7,
176
+ "rowSpacing": 10,
177
+ "rowHeight": 11,
178
+ "hitPadding": 4,
179
+ "chevronExecWidth": 10,
180
+ "chevronExecHeight": 8
181
+ },
182
+ "header": {
183
+ "toPinsGap": 15,
184
+ "chevronSize": 16,
185
+ "titleGap": 5
186
+ },
187
+ "edge": {
188
+ "width": 2,
189
+ "execWidth": 3,
190
+ "bezierTension": 0.5,
191
+ "minHorizontalSpread": 50,
192
+ "hitPadding": 6,
193
+ "pulseEnabled": true,
194
+ "midpointRadius": 3.5,
195
+ "arrowSize": 9
196
+ },
197
+ "comment": {
198
+ "headerHeight": 21,
199
+ "minWidth": 120,
200
+ "minHeight": 80
201
+ },
202
+ "reroute": {
203
+ "radius": 11,
204
+ "ringWidth": 2
205
+ },
206
+ "widget": {
207
+ "rowHeight": 22,
208
+ "gap": 6,
209
+ "controlMinWidth": 64,
210
+ "radius": 5,
211
+ "paddingX": 8,
212
+ "paddingY": 4,
213
+ "borderWidth": 1,
214
+ "toggleWidth": 30,
215
+ "toggleHeight": 16
216
+ },
217
+ "minimap": {
218
+ "width": 210,
219
+ "height": 150,
220
+ "margin": 14,
221
+ "radius": 10,
222
+ "padding": 10,
223
+ "borderWidth": 1,
224
+ "nodeRadius": 1.5
225
+ }
226
+ },
227
+ "typography": {
228
+ "fontFamily": "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
229
+ "heading": {
230
+ "size": 12,
231
+ "weight": 700,
232
+ "lineHeight": 14,
233
+ "color": "#FFFFFF",
234
+ "tracking": 0
235
+ },
236
+ "label": {
237
+ "size": 10,
238
+ "weight": 400,
239
+ "lineHeight": 11,
240
+ "color": "#FFFFFF",
241
+ "tracking": 0
242
+ },
243
+ "comment": {
244
+ "size": 14,
245
+ "weight": 600,
246
+ "lineHeight": 18,
247
+ "color": "#FFFFFF",
248
+ "tracking": 0
249
+ }
250
+ },
251
+ "effect": {
252
+ "headerInnerShadow": {
253
+ "offsetX": 0,
254
+ "offsetY": 3,
255
+ "blur": 6,
256
+ "color": "rgba(255, 255, 255, 0.5)"
257
+ },
258
+ "pillInnerShadow": {
259
+ "offsetX": 0,
260
+ "offsetY": 4,
261
+ "blur": 4,
262
+ "color": "rgba(255, 255, 255, 0.15)"
263
+ },
264
+ "headerBackdropBlur": 4,
265
+ "pillBackdropBlur": 2,
266
+ "headerHighlightGradient": "linear-gradient(180deg, #FFFFFF 14.29%, rgba(153, 153, 153, 0) 57.14%)",
267
+ "headerHighlightStroke": "linear-gradient(180deg, #FFFFFF 0%, rgba(153, 153, 153, 0) 100%)",
268
+ "nodeDropShadow": {
269
+ "offsetX": 0,
270
+ "offsetY": 8,
271
+ "blur": 18,
272
+ "color": "rgba(0, 0, 0, 0.85)"
273
+ }
274
+ },
275
+ "background": {
276
+ "color": "#1A1A1A",
277
+ "grid": {
278
+ "kind": "dots",
279
+ "spacing": 24,
280
+ "size": 1,
281
+ "color": "rgba(255, 255, 255, 0.3)"
282
+ }
283
+ },
284
+ "edgeRouting": {
285
+ "rule": "edgeColorFollowsSourcePinType",
286
+ "note": "Invariant: every edge inherits the colour of its source pin's type. Per-edge overrides are allowed but discouraged outside special UI states (e.g. error highlighting)."
287
+ }
288
+ }
@@ -0,0 +1,208 @@
1
+ export type PinShape = 'circle' | 'circle-empty' | 'chevron';
2
+ export interface PinTypeToken {
3
+ label: string;
4
+ color: string;
5
+ shape: PinShape;
6
+ edgeColor: string;
7
+ edgeWidth: number;
8
+ note?: string;
9
+ }
10
+ export interface CategoryToken {
11
+ label: string;
12
+ accent: string;
13
+ gradient: string;
14
+ }
15
+ export interface StateStyle {
16
+ border?: string;
17
+ borderWidth?: number;
18
+ glow?: string;
19
+ glowBlur?: number;
20
+ /** Width of the stroke that seeds the blurred glow halo. Wider stroke = brighter halo at
21
+ * equal alpha (more source energy under the gaussian). Default 3. */
22
+ glowWidth?: number;
23
+ opacity?: number;
24
+ }
25
+ export interface XenSurfaceColors {
26
+ canvas: string;
27
+ node: string;
28
+ panel: string;
29
+ elevated: string;
30
+ muted: string;
31
+ subtle: string;
32
+ outline: string;
33
+ divider: string;
34
+ headerEnd: string;
35
+ }
36
+ export interface XenPinTypeMap {
37
+ exec: PinTypeToken;
38
+ float: PinTypeToken;
39
+ object: PinTypeToken;
40
+ string: PinTypeToken;
41
+ any: PinTypeToken;
42
+ wildcard: PinTypeToken;
43
+ }
44
+ export interface XenCategoryMap {
45
+ logic: CategoryToken;
46
+ data: CategoryToken;
47
+ macro: CategoryToken;
48
+ utility: CategoryToken;
49
+ }
50
+ export interface XenTokens {
51
+ name: 'xen';
52
+ version: string;
53
+ color: {
54
+ brand: Record<string, string>;
55
+ surface: XenSurfaceColors;
56
+ text: {
57
+ primary: string;
58
+ secondary: string;
59
+ muted: string;
60
+ disabled: string;
61
+ };
62
+ alpha: Record<string, string>;
63
+ minimap: {
64
+ background: string;
65
+ border: string;
66
+ node: string;
67
+ frame: string;
68
+ frameBorder: string;
69
+ };
70
+ widget: {
71
+ bg: string;
72
+ bgHover: string;
73
+ bgFocused: string;
74
+ track: string;
75
+ fill: string;
76
+ fillAlpha: number;
77
+ text: string;
78
+ label: string;
79
+ placeholder: string;
80
+ border: string;
81
+ borderFocused: string;
82
+ selection: string;
83
+ knob: string;
84
+ };
85
+ };
86
+ pinType: XenPinTypeMap;
87
+ category: XenCategoryMap;
88
+ pill: Record<string, string>;
89
+ state: {
90
+ hover: StateStyle;
91
+ selected: StateStyle;
92
+ active: StateStyle;
93
+ disabled: StateStyle;
94
+ };
95
+ geometry: {
96
+ node: {
97
+ radius: number;
98
+ minWidth: number;
99
+ headerHeight: number;
100
+ headerPadding: number;
101
+ innerPaddingX: number;
102
+ innerPaddingY: number;
103
+ pillRadius: number;
104
+ pillHeight: number;
105
+ pillMinWidth: number;
106
+ };
107
+ pin: {
108
+ diameter: number;
109
+ stroke: number;
110
+ strokeColor: string;
111
+ labelGap: number;
112
+ rowSpacing: number;
113
+ rowHeight: number;
114
+ hitPadding: number;
115
+ chevronExecWidth: number;
116
+ chevronExecHeight: number;
117
+ };
118
+ header: {
119
+ toPinsGap: number;
120
+ chevronSize: number;
121
+ titleGap: number;
122
+ };
123
+ edge: {
124
+ width: number;
125
+ execWidth: number;
126
+ bezierTension: number;
127
+ minHorizontalSpread: number;
128
+ hitPadding: number;
129
+ pulseEnabled: boolean;
130
+ midpointRadius: number;
131
+ arrowSize: number;
132
+ };
133
+ comment: {
134
+ /** Corner radius. Omit to inherit the node radius (so a comment frame reads 1:1 with a node
135
+ * header in any theme); set it to deliberately diverge. */
136
+ radius?: number;
137
+ headerHeight: number;
138
+ minWidth: number;
139
+ minHeight: number;
140
+ };
141
+ reroute: {
142
+ radius: number;
143
+ ringWidth: number;
144
+ };
145
+ widget: {
146
+ rowHeight: number;
147
+ gap: number;
148
+ controlMinWidth: number;
149
+ radius: number;
150
+ paddingX: number;
151
+ paddingY: number;
152
+ borderWidth: number;
153
+ toggleWidth: number;
154
+ toggleHeight: number;
155
+ };
156
+ minimap: {
157
+ width: number;
158
+ height: number;
159
+ margin: number;
160
+ radius: number;
161
+ padding: number;
162
+ borderWidth: number;
163
+ nodeRadius: number;
164
+ };
165
+ };
166
+ typography: {
167
+ fontFamily: string;
168
+ heading: TypographyStyle;
169
+ label: TypographyStyle;
170
+ comment: TypographyStyle;
171
+ };
172
+ effect: {
173
+ headerInnerShadow: ShadowStyle;
174
+ pillInnerShadow: ShadowStyle;
175
+ headerBackdropBlur: number;
176
+ pillBackdropBlur: number;
177
+ headerHighlightGradient: string;
178
+ headerHighlightStroke: string;
179
+ nodeDropShadow: ShadowStyle;
180
+ };
181
+ background: {
182
+ color: string;
183
+ grid: {
184
+ kind: 'dots' | 'lines';
185
+ spacing: number;
186
+ size: number;
187
+ color: string;
188
+ };
189
+ };
190
+ edgeRouting: {
191
+ rule: string;
192
+ note: string;
193
+ };
194
+ }
195
+ export interface TypographyStyle {
196
+ size: number;
197
+ weight: number;
198
+ lineHeight: number;
199
+ color: string;
200
+ tracking: number;
201
+ }
202
+ export interface ShadowStyle {
203
+ offsetX: number;
204
+ offsetY: number;
205
+ blur: number;
206
+ color: string;
207
+ }
208
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@xenolithengine/graph-theme-xen",
3
+ "version": "0.7.0-beta.0",
4
+ "description": "Xen — the default original design system for XenolithGraph. Dark / gold palette with typed pins, categorical headers, and glassmorphic effects.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./tokens.json": "./src/tokens.json"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "src/tokens.json",
20
+ "src/fonts",
21
+ "reference",
22
+ "README.md"
23
+ ],
24
+ "sideEffects": false,
25
+ "dependencies": {
26
+ "@xenolithengine/graph-core": "0.7.0-beta.0"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "^5.7.0",
30
+ "vitest": "^3.0.0"
31
+ },
32
+ "keywords": [
33
+ "xenolith",
34
+ "theme",
35
+ "design-system",
36
+ "node-editor"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/XenolithEngine/xenolith-graph.git",
44
+ "directory": "packages/theme-xen"
45
+ },
46
+ "homepage": "https://xenolithengine.github.io/xenolith-graph/",
47
+ "bugs": {
48
+ "url": "https://github.com/XenolithEngine/xenolith-graph/issues"
49
+ },
50
+ "author": "XenolithEngine",
51
+ "scripts": {
52
+ "build": "tsc -b",
53
+ "dev": "tsc -b --watch",
54
+ "test": "vitest run",
55
+ "lint": "eslint src",
56
+ "typecheck": "tsc --noEmit"
57
+ }
58
+ }
@@ -0,0 +1,12 @@
1
+ # Xen — reference assets
2
+
3
+ Static references for the Xen design system. These files are **read-only ground truth** — when in doubt how something should look, compare to these.
4
+
5
+ | File | What it shows |
6
+ |---|---|
7
+ | `nodes.svg` | All six node variants exported from Figma at 1:1 scale: default with both pins, only-inputs, three-inputs-two-outputs, without-output, rolled-up (pill, orange variant), rolled-up-without-input (pill, green variant). |
8
+ | `node-variants.png` | Same set rendered as PNG for quick visual scan. |
9
+ | `canvas-150.png` | Sample graph at 150% zoom showing edge colour-by-source-type and category-coloured headers. |
10
+ | `context-menu.png` | Context menu with "Группа нодов" / "Рефакторинг" entries and keyboard hints. |
11
+
12
+ When adding interactions or visual states that aren't documented here, capture a Figma frame, export PNG, and add it with a row in this table.
Binary file
Binary file
Binary file
@@ -0,0 +1,124 @@
1
+ <!--
2
+ Xen design system — node variant reference, 1:1 from Figma.
3
+ Six variants: default · only-inputs · 3-in-2-out · without-output · pill-orange · pill-green.
4
+ Do not edit. Replace as a whole when Figma source updates.
5
+ -->
6
+ <svg width="198" height="558" viewBox="0 0 198 558" fill="none" xmlns="http://www.w3.org/2000/svg">
7
+ <rect x="0.5" y="0.5" width="197" height="557" rx="4.5" stroke="#FCB400" stroke-dasharray="10 5"/>
8
+ <g clip-path="url(#clip0_155_7675)">
9
+ <rect x="24" y="20" width="150" height="70" rx="7" fill="#0F110E"/>
10
+ <foreignObject x="18" y="14" width="163" height="37"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(4px);clip-path:url(#bgblur_1_155_7675_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter0_i_155_7675)" data-figma-bg-blur-radius="8">
11
+ <path d="M26 27C26 24.2386 28.2386 22 31 22H168C170.761 22 173 24.2386 173 27V43H26V27Z" fill="url(#paint0_linear_155_7675)" fill-opacity="0.6"/>
12
+ </g>
13
+ <path d="M26.5 29L26.3826 27.3562C26.1758 24.4619 28.4682 22 31.3699 22H167.735C170.778 22 173.115 24.6949 172.685 27.7071L172.5 29" stroke="url(#paint1_linear_155_7675)" stroke-width="0.5"/>
14
+ <path d="M40.5 30.25L37 33.75L33.5 30.25" stroke="white" stroke-linecap="round"/>
15
+ <g clip-path="url(#clip2_155_7675)">
16
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 56.5)" fill="#85C244" stroke="#1A1A1A"/>
17
+ </g>
18
+ <g clip-path="url(#clip3_155_7675)">
19
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 178 56.5)" fill="#85C244" stroke="#1A1A1A"/>
20
+ </g>
21
+ </g>
22
+ <g clip-path="url(#clip4_155_7675)">
23
+ <rect x="24.5" y="106.5" width="149" height="84" rx="6.5" fill="#0F110E"/>
24
+ <rect x="24.5" y="106.5" width="149" height="84" rx="6.5" stroke="white"/>
25
+ <foreignObject x="18" y="100" width="163" height="37"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(4px);clip-path:url(#bgblur_5_155_7675_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter1_i_155_7675)" data-figma-bg-blur-radius="8">
26
+ <path d="M26 113C26 110.239 28.2386 108 31 108H168C170.761 108 173 110.239 173 113V129H26V113Z" fill="url(#paint2_linear_155_7675)" fill-opacity="0.6"/>
27
+ </g>
28
+ <path d="M38.5 116.25L35 119.75L31.5 116.25" stroke="white" stroke-linecap="round"/>
29
+ <g clip-path="url(#clip6_155_7675)">
30
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 142.5)" fill="#3E95B9" stroke="#1A1A1A"/>
31
+ </g>
32
+ <g clip-path="url(#clip7_155_7675)">
33
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 163.5)" fill="#3E95B9" stroke="#1A1A1A"/>
34
+ </g>
35
+ </g>
36
+ <g clip-path="url(#clip8_155_7675)">
37
+ <rect x="24" y="215" width="150" height="105" rx="7" fill="#0F110E"/>
38
+ <foreignObject x="18" y="209" width="162" height="37"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(4px);clip-path:url(#bgblur_9_155_7675_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter2_i_155_7675)" data-figma-bg-blur-radius="8">
39
+ <path d="M26 222C26 219.239 28.2386 217 31 217H167C169.761 217 172 219.239 172 222V238H26V222Z" fill="url(#paint3_linear_155_7675)" fill-opacity="0.6"/>
40
+ </g>
41
+ <path d="M39.5 226.25L36 229.75L32.5 226.25" stroke="white" stroke-linecap="round"/>
42
+ <g clip-path="url(#clip10_155_7675)">
43
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 252.5)" fill="#85C244" stroke="#1A1A1A"/>
44
+ </g>
45
+ <g clip-path="url(#clip11_155_7675)">
46
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 273.5)" fill="white" stroke="#1A1A1A"/>
47
+ </g>
48
+ <g clip-path="url(#clip12_155_7675)">
49
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 294.5)" stroke="#9AD6E3"/>
50
+ </g>
51
+ <g clip-path="url(#clip13_155_7675)">
52
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 178 253)" fill="#3E95B9" stroke="#1A1A1A"/>
53
+ </g>
54
+ <g clip-path="url(#clip14_155_7675)">
55
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 178 275)" fill="#3E95B9" stroke="#1A1A1A"/>
56
+ </g>
57
+ </g>
58
+ <g clip-path="url(#clip15_155_7675)">
59
+ <rect x="24" y="336" width="150" height="70" rx="7" fill="#0F110E"/>
60
+ <foreignObject x="18" y="330" width="162" height="37"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(4px);clip-path:url(#bgblur_16_155_7675_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter3_i_155_7675)" data-figma-bg-blur-radius="8">
61
+ <path d="M26 343C26 340.239 28.2386 338 31 338H167C169.761 338 172 340.239 172 343V359H26V343Z" fill="url(#paint4_linear_155_7675)" fill-opacity="0.6"/>
62
+ </g>
63
+ <path d="M42.5 347.25L39 350.75L35.5 347.25" stroke="white" stroke-linecap="round"/>
64
+ <g clip-path="url(#clip17_155_7675)">
65
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 373.5)" fill="#FF5622" stroke="#1A1A1A"/>
66
+ </g>
67
+ <g clip-path="url(#clip18_155_7675)">
68
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 178 373.5)" fill="#3E95B9" stroke="#1A1A1A"/>
69
+ </g>
70
+ </g>
71
+ <g clip-path="url(#clip19_155_7675)">
72
+ <foreignObject x="20" y="428" width="148" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(2px);clip-path:url(#bgblur_20_155_7675_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter4_i_155_7675)" data-figma-bg-blur-radius="4">
73
+ <rect x="24" y="432" width="140" height="40" rx="20" fill="#0F110E"/>
74
+ <rect x="24" y="432" width="140" height="40" rx="20" fill="url(#paint5_linear_155_7675)" fill-opacity="0.5"/>
75
+ </g>
76
+ <g clip-path="url(#clip21_155_7675)">
77
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 37 432)" fill="#FF5622" stroke="#1A1A1A"/>
78
+ </g>
79
+ <g clip-path="url(#clip22_155_7675)">
80
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 28 442)" fill="#85C244" stroke="#1A1A1A"/>
81
+ </g>
82
+ <g clip-path="url(#clip23_155_7675)">
83
+ <circle cx="33" cy="468" r="3.5" transform="rotate(-180 33 468)" fill="white" stroke="#1A1A1A"/>
84
+ </g>
85
+ <g clip-path="url(#clip24_155_7675)">
86
+ <circle cx="24" cy="458" r="3.5" transform="rotate(-180 24 458)" fill="white" stroke="#1A1A1A"/>
87
+ </g>
88
+ <path d="M48.25 448.5L51.75 452L48.25 455.5" stroke="white" stroke-linecap="round"/>
89
+ <g clip-path="url(#clip25_155_7675)">
90
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 167 448)" fill="white" stroke="#1A1A1A"/>
91
+ </g>
92
+ </g>
93
+ <foreignObject x="11" y="486" width="148" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(2px);clip-path:url(#bgblur_26_155_7675_clip_path);height:100%;width:100%"></div></foreignObject><g filter="url(#filter5_i_155_7675)" data-figma-bg-blur-radius="4">
94
+ <rect x="15" y="490" width="140" height="40" rx="20" fill="#0F110E"/>
95
+ <rect x="15" y="490" width="140" height="40" rx="20" fill="url(#paint6_linear_155_7675)" fill-opacity="0.5"/>
96
+ </g>
97
+ <path d="M32.25 506.5L35.75 510L32.25 513.5" stroke="white" stroke-linecap="round"/>
98
+ <g clip-path="url(#clip27_155_7675)">
99
+ <circle cx="4" cy="4" r="3.5" transform="matrix(-1 0 0 1 158 506)" fill="#FF5622" stroke="#1A1A1A"/>
100
+ </g>
101
+ <defs>
102
+ <linearGradient id="paint0_linear_155_7675" x1="26" y1="32.5" x2="173" y2="32.5" gradientUnits="userSpaceOnUse">
103
+ <stop stop-color="#85C244"/><stop offset="1" stop-color="#30342E"/>
104
+ </linearGradient>
105
+ <linearGradient id="paint1_linear_155_7675" x1="103.5" y1="23" x2="103.5" y2="26" gradientUnits="userSpaceOnUse">
106
+ <stop stop-color="white"/><stop offset="1" stop-color="#999999" stop-opacity="0"/>
107
+ </linearGradient>
108
+ <linearGradient id="paint2_linear_155_7675" x1="26" y1="118.5" x2="173" y2="118.5" gradientUnits="userSpaceOnUse">
109
+ <stop stop-color="#3E95B9"/><stop offset="1" stop-color="#30342E"/>
110
+ </linearGradient>
111
+ <linearGradient id="paint3_linear_155_7675" x1="26" y1="227.5" x2="172" y2="227.5" gradientUnits="userSpaceOnUse">
112
+ <stop stop-color="#8A38F5"/><stop offset="1" stop-color="#30342E"/>
113
+ </linearGradient>
114
+ <linearGradient id="paint4_linear_155_7675" x1="26" y1="348.5" x2="172" y2="348.5" gradientUnits="userSpaceOnUse">
115
+ <stop stop-color="white"/><stop offset="1" stop-color="#30342E"/>
116
+ </linearGradient>
117
+ <linearGradient id="paint5_linear_155_7675" x1="36.7273" y1="452.513" x2="151.273" y2="452.513" gradientUnits="userSpaceOnUse">
118
+ <stop offset="0.0432692" stop-color="#0F110E"/><stop offset="0.519231" stop-color="#FF5622" stop-opacity="0.6"/><stop offset="1" stop-color="#0F110E"/>
119
+ </linearGradient>
120
+ <linearGradient id="paint6_linear_155_7675" x1="27.7273" y1="510.513" x2="142.273" y2="510.513" gradientUnits="userSpaceOnUse">
121
+ <stop offset="0.0432692" stop-color="#0F110E"/><stop offset="0.519231" stop-color="#85C244" stop-opacity="0.6"/><stop offset="1" stop-color="#0F110E"/>
122
+ </linearGradient>
123
+ </defs>
124
+ </svg>
Binary file
Binary file
Binary file
@@ -0,0 +1,288 @@
1
+ {
2
+ "$schema": "./tokens.schema.json",
3
+ "name": "xen",
4
+ "version": "0.7.0-beta.0",
5
+ "color": {
6
+ "brand": {
7
+ "primary": "#FCB400",
8
+ "primaryDark": "#C28B00",
9
+ "primaryDeep": "#68531D",
10
+ "primaryLight": "#FFDF67",
11
+ "primaryPale": "#FFEDA8",
12
+ "primaryAlpha20": "rgba(252, 180, 0, 0.2)",
13
+ "primaryAlpha30": "rgba(252, 180, 0, 0.3)",
14
+ "primaryAlpha50": "rgba(252, 180, 0, 0.5)",
15
+ "primaryAlpha60": "rgba(255, 237, 168, 0.6)"
16
+ },
17
+ "surface": {
18
+ "canvas": "#1A1A1A",
19
+ "node": "#0F110E",
20
+ "panel": "#191919",
21
+ "elevated": "#232323",
22
+ "muted": "#292929",
23
+ "subtle": "#363636",
24
+ "outline": "#424242",
25
+ "divider": "#363636",
26
+ "headerEnd": "#30342E"
27
+ },
28
+ "text": {
29
+ "primary": "#FFFFFF",
30
+ "secondary": "#B8B8B8",
31
+ "muted": "rgba(255, 255, 255, 0.5)",
32
+ "disabled": "rgba(255, 255, 255, 0.3)"
33
+ },
34
+ "alpha": {
35
+ "white10": "rgba(255, 255, 255, 0.1)",
36
+ "white20": "rgba(255, 255, 255, 0.2)",
37
+ "white30": "rgba(255, 255, 255, 0.3)",
38
+ "white50": "rgba(255, 255, 255, 0.5)",
39
+ "dark50": "rgba(71, 71, 71, 0.5)"
40
+ },
41
+ "minimap": {
42
+ "background": "rgba(20, 20, 20, 0.85)",
43
+ "border": "#424242",
44
+ "node": "rgba(255, 255, 255, 0.32)",
45
+ "frame": "rgba(252, 180, 0, 0.14)",
46
+ "frameBorder": "#FCB400"
47
+ },
48
+ "widget": {
49
+ "bg": "rgba(255, 255, 255, 0.045)",
50
+ "bgHover": "rgba(255, 255, 255, 0.09)",
51
+ "bgFocused": "#0F110E",
52
+ "track": "rgba(255, 255, 255, 0.08)",
53
+ "fill": "#FCB400",
54
+ "fillAlpha": 0.55,
55
+ "text": "#FFFFFF",
56
+ "label": "#B8B8B8",
57
+ "placeholder": "rgba(255, 255, 255, 0.35)",
58
+ "border": "rgba(255, 255, 255, 0.12)",
59
+ "borderFocused": "#FCB400",
60
+ "selection": "rgba(252, 180, 0, 0.35)",
61
+ "knob": "#FFFFFF"
62
+ }
63
+ },
64
+ "pinType": {
65
+ "exec": {
66
+ "label": "Exec",
67
+ "color": "#FCB400",
68
+ "shape": "chevron",
69
+ "edgeColor": "#FCB400",
70
+ "edgeWidth": 3,
71
+ "note": "Imperative flow. Carries no value, only ordering."
72
+ },
73
+ "float": {
74
+ "label": "Float",
75
+ "color": "#85C244",
76
+ "shape": "circle",
77
+ "edgeColor": "#85C244",
78
+ "edgeWidth": 2
79
+ },
80
+ "object": {
81
+ "label": "Object",
82
+ "color": "#3E95B9",
83
+ "shape": "circle",
84
+ "edgeColor": "#3E95B9",
85
+ "edgeWidth": 2
86
+ },
87
+ "string": {
88
+ "label": "String",
89
+ "color": "#FF5622",
90
+ "shape": "circle",
91
+ "edgeColor": "#FF5622",
92
+ "edgeWidth": 2
93
+ },
94
+ "any": {
95
+ "label": "Any",
96
+ "color": "#FFFFFF",
97
+ "shape": "circle",
98
+ "edgeColor": "#FFFFFF",
99
+ "edgeWidth": 2
100
+ },
101
+ "wildcard": {
102
+ "label": "Wildcard",
103
+ "color": "#9AD6E3",
104
+ "shape": "circle-empty",
105
+ "edgeColor": "#9AD6E3",
106
+ "edgeWidth": 2,
107
+ "note": "Typed but unconnected; renders with outline only, no fill."
108
+ }
109
+ },
110
+ "category": {
111
+ "logic": {
112
+ "label": "Logic",
113
+ "accent": "#85C244",
114
+ "gradient": "linear-gradient(90deg, rgba(133, 194, 68, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
115
+ },
116
+ "data": {
117
+ "label": "Data",
118
+ "accent": "#3E95B9",
119
+ "gradient": "linear-gradient(90deg, rgba(62, 149, 185, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
120
+ },
121
+ "macro": {
122
+ "label": "Macro",
123
+ "accent": "#8A38F5",
124
+ "gradient": "linear-gradient(90deg, rgba(138, 56, 245, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
125
+ },
126
+ "utility": {
127
+ "label": "Utility",
128
+ "accent": "#FFFFFF",
129
+ "gradient": "linear-gradient(90deg, rgba(255, 255, 255, 0.6) 0%, rgba(48, 52, 46, 0.6) 100%)"
130
+ }
131
+ },
132
+ "pill": {
133
+ "orange": "linear-gradient(90deg, rgba(15, 17, 14, 0.5) 12.63%, rgba(255, 86, 34, 0.3) 51.57%, rgba(15, 17, 14, 0.5) 90.91%), #0F110E",
134
+ "green": "linear-gradient(90deg, rgba(15, 17, 14, 0.5) 12.63%, rgba(133, 194, 68, 0.3) 51.57%, rgba(15, 17, 14, 0.5) 90.91%), #0F110E"
135
+ },
136
+ "state": {
137
+ "hover": {
138
+ "border": "#FCB400",
139
+ "borderWidth": 1,
140
+ "glow": "rgba(252, 180, 0, 0.52)",
141
+ "glowBlur": 4
142
+ },
143
+ "selected": {
144
+ "border": "#FFFFFF",
145
+ "borderWidth": 1,
146
+ "glow": "rgba(255, 255, 255, 0.4)",
147
+ "glowBlur": 5
148
+ },
149
+ "active": {
150
+ "border": "#FFDF67",
151
+ "borderWidth": 1,
152
+ "glow": "rgba(252, 180, 0, 0.64)",
153
+ "glowBlur": 6
154
+ },
155
+ "disabled": {
156
+ "opacity": 0.4
157
+ }
158
+ },
159
+ "geometry": {
160
+ "node": {
161
+ "radius": 7,
162
+ "minWidth": 150,
163
+ "headerHeight": 21,
164
+ "headerPadding": 2,
165
+ "innerPaddingX": 4,
166
+ "innerPaddingY": 4,
167
+ "pillRadius": 20,
168
+ "pillHeight": 40,
169
+ "pillMinWidth": 140
170
+ },
171
+ "pin": {
172
+ "diameter": 8,
173
+ "stroke": 1,
174
+ "strokeColor": "#1A1A1A",
175
+ "labelGap": 7,
176
+ "rowSpacing": 10,
177
+ "rowHeight": 11,
178
+ "hitPadding": 4,
179
+ "chevronExecWidth": 10,
180
+ "chevronExecHeight": 8
181
+ },
182
+ "header": {
183
+ "toPinsGap": 15,
184
+ "chevronSize": 16,
185
+ "titleGap": 5
186
+ },
187
+ "edge": {
188
+ "width": 2,
189
+ "execWidth": 3,
190
+ "bezierTension": 0.5,
191
+ "minHorizontalSpread": 50,
192
+ "hitPadding": 6,
193
+ "pulseEnabled": true,
194
+ "midpointRadius": 3.5,
195
+ "arrowSize": 9
196
+ },
197
+ "comment": {
198
+ "headerHeight": 21,
199
+ "minWidth": 120,
200
+ "minHeight": 80
201
+ },
202
+ "reroute": {
203
+ "radius": 11,
204
+ "ringWidth": 2
205
+ },
206
+ "widget": {
207
+ "rowHeight": 22,
208
+ "gap": 6,
209
+ "controlMinWidth": 64,
210
+ "radius": 5,
211
+ "paddingX": 8,
212
+ "paddingY": 4,
213
+ "borderWidth": 1,
214
+ "toggleWidth": 30,
215
+ "toggleHeight": 16
216
+ },
217
+ "minimap": {
218
+ "width": 210,
219
+ "height": 150,
220
+ "margin": 14,
221
+ "radius": 10,
222
+ "padding": 10,
223
+ "borderWidth": 1,
224
+ "nodeRadius": 1.5
225
+ }
226
+ },
227
+ "typography": {
228
+ "fontFamily": "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
229
+ "heading": {
230
+ "size": 12,
231
+ "weight": 700,
232
+ "lineHeight": 14,
233
+ "color": "#FFFFFF",
234
+ "tracking": 0
235
+ },
236
+ "label": {
237
+ "size": 10,
238
+ "weight": 400,
239
+ "lineHeight": 11,
240
+ "color": "#FFFFFF",
241
+ "tracking": 0
242
+ },
243
+ "comment": {
244
+ "size": 14,
245
+ "weight": 600,
246
+ "lineHeight": 18,
247
+ "color": "#FFFFFF",
248
+ "tracking": 0
249
+ }
250
+ },
251
+ "effect": {
252
+ "headerInnerShadow": {
253
+ "offsetX": 0,
254
+ "offsetY": 3,
255
+ "blur": 6,
256
+ "color": "rgba(255, 255, 255, 0.5)"
257
+ },
258
+ "pillInnerShadow": {
259
+ "offsetX": 0,
260
+ "offsetY": 4,
261
+ "blur": 4,
262
+ "color": "rgba(255, 255, 255, 0.15)"
263
+ },
264
+ "headerBackdropBlur": 4,
265
+ "pillBackdropBlur": 2,
266
+ "headerHighlightGradient": "linear-gradient(180deg, #FFFFFF 14.29%, rgba(153, 153, 153, 0) 57.14%)",
267
+ "headerHighlightStroke": "linear-gradient(180deg, #FFFFFF 0%, rgba(153, 153, 153, 0) 100%)",
268
+ "nodeDropShadow": {
269
+ "offsetX": 0,
270
+ "offsetY": 8,
271
+ "blur": 18,
272
+ "color": "rgba(0, 0, 0, 0.85)"
273
+ }
274
+ },
275
+ "background": {
276
+ "color": "#1A1A1A",
277
+ "grid": {
278
+ "kind": "dots",
279
+ "spacing": 24,
280
+ "size": 1,
281
+ "color": "rgba(255, 255, 255, 0.3)"
282
+ }
283
+ },
284
+ "edgeRouting": {
285
+ "rule": "edgeColorFollowsSourcePinType",
286
+ "note": "Invariant: every edge inherits the colour of its source pin's type. Per-edge overrides are allowed but discouraged outside special UI states (e.g. error highlighting)."
287
+ }
288
+ }