@xenolithengine/graph-theme-liquid-glass 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,42 @@
1
+ # @xenolithengine/graph-theme-liquid-glass
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
+ Liquid Glass theme for XenolithGraph — Apple WWDC25-inspired translucent material with per-node backdrop refraction, gaussian blur, and a vertical tint, sitting over a radial-gradient navy canvas.
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
+ Part of [XenolithGraph](https://github.com/XenolithEngine/xenolith-graph) — an AI-native, embeddable node-graph editor for the web with its own visual design language (Xen).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @xenolithengine/graph-theme-liquid-glass pixi.js
16
+ ```
17
+
18
+ Peer dependency: `pixi.js@^8.6.0`.
19
+
20
+ ## Usage
21
+
22
+ ```ts
23
+ import { XenolithEditor } from '@xenolithengine/graph-editor'
24
+ import { liquidGlassTheme } from '@xenolithengine/graph-theme-liquid-glass'
25
+
26
+ const editor = await XenolithEditor.init('#graph', { theme: liquidGlassTheme })
27
+
28
+ // or swap at runtime
29
+ editor.setTheme(liquidGlassTheme)
30
+ ```
31
+
32
+ ## What's exported
33
+
34
+ - `liquidGlassTheme` — drop-in `XenolithTheme`
35
+ - `liquidGlassTokens` — token map for further overrides via `mergeTheme`
36
+
37
+ ## Docs
38
+
39
+ - [Theming guide](https://xenolithengine.github.io/xenolith-graph/guides/api/)
40
+ - [GitHub](https://github.com/XenolithEngine/xenolith-graph)
41
+
42
+ MIT © XenolithEngine
@@ -0,0 +1,14 @@
1
+ import { Container } from 'pixi.js';
2
+ import type { XenTokens } from '@xenolithengine/graph-theme-xen';
3
+ /**
4
+ * Liquid Glass canvas backdrop — a radial gradient rendered into a Canvas2D ImageBitmap and
5
+ * wrapped as a PIXI Sprite. The shader-driven node bodies sample this through the editor's
6
+ * per-frame backdrop RenderTexture, so a gradient backdrop gives the glass a natural cool/warm
7
+ * play instead of a uniform dark slab.
8
+ *
9
+ * Canvas2D path is used (rather than PIXI FillGradient + Graphics.rect) because the latter's
10
+ * radial fills don't reliably cover an 8000×8000 quad in PIXI v8 — the texture-space gradient
11
+ * renders empty/clipped in that case. A pre-baked Canvas2D bitmap is bulletproof.
12
+ */
13
+ export declare function createLiquidGlassBackdrop(tokens: XenTokens): Container;
14
+ //# sourceMappingURL=backdrop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backdrop.d.ts","sourceRoot":"","sources":["../src/backdrop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAA;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAEhE;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAmCtE"}
@@ -0,0 +1,42 @@
1
+ import { Container, Sprite, Texture } from 'pixi.js';
2
+ import { createGridSprite } from '@xenolithengine/graph-render-pixi';
3
+ /**
4
+ * Liquid Glass canvas backdrop — a radial gradient rendered into a Canvas2D ImageBitmap and
5
+ * wrapped as a PIXI Sprite. The shader-driven node bodies sample this through the editor's
6
+ * per-frame backdrop RenderTexture, so a gradient backdrop gives the glass a natural cool/warm
7
+ * play instead of a uniform dark slab.
8
+ *
9
+ * Canvas2D path is used (rather than PIXI FillGradient + Graphics.rect) because the latter's
10
+ * radial fills don't reliably cover an 8000×8000 quad in PIXI v8 — the texture-space gradient
11
+ * renders empty/clipped in that case. A pre-baked Canvas2D bitmap is bulletproof.
12
+ */
13
+ export function createLiquidGlassBackdrop(tokens) {
14
+ const root = new Container({ label: 'liquid-glass-backdrop' });
15
+ // Bake a 2048-px radial gradient bitmap once at theme creation. Stretched to 8000 px in
16
+ // world space; smoothing keeps the gradient soft even when zoomed in.
17
+ const BAKE_SIZE = 2048;
18
+ const cnv = document.createElement('canvas');
19
+ cnv.width = BAKE_SIZE;
20
+ cnv.height = BAKE_SIZE;
21
+ const ctx = cnv.getContext('2d');
22
+ const grad = ctx.createRadialGradient(BAKE_SIZE / 2, BAKE_SIZE / 2, 0, BAKE_SIZE / 2, BAKE_SIZE / 2, BAKE_SIZE * 0.55);
23
+ grad.addColorStop(0, '#1D3573');
24
+ grad.addColorStop(1, '#112558');
25
+ ctx.fillStyle = grad;
26
+ ctx.fillRect(0, 0, BAKE_SIZE, BAKE_SIZE);
27
+ const texture = Texture.from(cnv);
28
+ const sprite = new Sprite(texture);
29
+ const WORLD_SIZE = 8000;
30
+ sprite.width = WORLD_SIZE;
31
+ sprite.height = WORLD_SIZE;
32
+ sprite.anchor.set(0.5);
33
+ sprite.position.set(0, 0);
34
+ root.addChild(sprite);
35
+ // Dot grid overlaid on the gradient — preserves the "graph editor canvas" feel while still
36
+ // letting the radial backdrop show through. Uses the same `createGridSprite` machinery as the
37
+ // Xen theme so the dot spacing/size is consistent.
38
+ const grid = createGridSprite(tokens);
39
+ root.addChild(grid);
40
+ return root;
41
+ }
42
+ //# sourceMappingURL=backdrop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backdrop.js","sourceRoot":"","sources":["../src/backdrop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AAGpE;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAiB;IACzD,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAA;IAE9D,wFAAwF;IACxF,sEAAsE;IACtE,MAAM,SAAS,GAAG,IAAI,CAAA;IACtB,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC5C,GAAG,CAAC,KAAK,GAAG,SAAS,CAAA;IACrB,GAAG,CAAC,MAAM,GAAG,SAAS,CAAA;IACtB,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAE,CAAA;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,oBAAoB,CACnC,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,CAAC,EAC/B,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,IAAI,CAC/C,CAAA;IACD,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;IAC/B,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;IAC/B,GAAG,CAAC,SAAS,GAAG,IAAI,CAAA;IACpB,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;IAExC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,UAAU,GAAG,IAAI,CAAA;IACvB,MAAM,CAAC,KAAK,GAAG,UAAU,CAAA;IACzB,MAAM,CAAC,MAAM,GAAG,UAAU,CAAA;IAC1B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACtB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACzB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAErB,2FAA2F;IAC3F,8FAA8F;IAC9F,mDAAmD;IACnD,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAEnB,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Liquid Glass body shader — direct port of dashersw/liquid-glass-js's fragment shader, adapted
3
+ * to PIXI v8 Mesh rendering. Their algorithm + parameter set 1:1; only the texture-coord
4
+ * derivation differs because PIXI Mesh draws at world position (not as a full-screen quad).
5
+ *
6
+ * Algorithm (per fragment):
7
+ * 1. Compute textureCoord = stage_pixel_position / backdrop_size (the dashersw equivalent of
8
+ * `(container_center + (uv - 0.5) * container_size) / texture_size`).
9
+ * 2. Rounded-rect SDF → distFromEdgeShape + shape normal.
10
+ * 3. Three exponential-falloff refraction bands (base/edge/rim) blended by per-band intensity,
11
+ * plus a corner-boost spike and a ripple effect for life. Sum → refraction offset in UV.
12
+ * 4. 13×13 circular gaussian blur on the backdrop, centred at the refracted UV.
13
+ * 5. Vertical white→grey gradient tint mixed into the blurred backdrop at `tintOpacity`.
14
+ * 6. Rounded-rect AA mask written to alpha.
15
+ *
16
+ * Parameters match dashersw's tuned defaults (Edge Intensity 0.01, Rim Intensity 0.05, etc.) —
17
+ * those landed Apple-grade on a forest backdrop, so we use them verbatim and only tune the
18
+ * canvas later for our darker context.
19
+ */
20
+ export declare const glassVertexGLSL = "\nin vec2 aPosition;\nout vec2 vLocal;\nout vec2 vStagePixel;\n\nuniform mat3 uProjectionMatrix;\nuniform mat3 uWorldTransformMatrix;\nuniform mat3 uTransformMatrix;\n\nvoid main() {\n vLocal = aPosition;\n mat3 stage = uWorldTransformMatrix * uTransformMatrix;\n vStagePixel = (stage * vec3(aPosition, 1.0)).xy;\n mat3 mvp = uProjectionMatrix * stage;\n gl_Position = vec4((mvp * vec3(aPosition, 1.0)).xy, 0.0, 1.0);\n}\n";
21
+ export declare const glassFragmentGLSL = "\nin vec2 vLocal;\nin vec2 vStagePixel;\n\nuniform sampler2D uBackdropTex;\nuniform vec2 uBackdropSize; // dashersw u_textureSize\nuniform vec2 uSize; // dashersw u_resolution (container px size)\nuniform float uBorderRadius;\nuniform float uBlurRadius;\nuniform float uEdgeIntensity;\nuniform float uRimIntensity;\nuniform float uBaseIntensity;\nuniform float uEdgeDistance;\nuniform float uRimDistance;\nuniform float uBaseDistance;\nuniform float uCornerBoost;\nuniform float uRippleEffect;\nuniform float uTintOpacity;\nuniform float uWarp;\n\nfloat roundedRectDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = size * 0.5;\n vec2 pixelCoord = coord * size;\n vec2 toCorner = abs(pixelCoord - center) - (center - radius);\n float outsideCorner = length(max(toCorner, 0.0));\n float insideCorner = min(max(toCorner.x, toCorner.y), 0.0);\n return outsideCorner + insideCorner - radius;\n}\n\nvoid main() {\n // Mesh-local UV in [0,1] for the rounded-rect math (dashersw equivalent: coord).\n vec2 coord = vLocal / uSize;\n\n // PIXI port of dashersw textureCoord = pagePixel / textureSize. PIXI Mesh pipeline already\n // gives us the fragment stage-pixel position via worldTransform * aPosition.\n vec2 textureCoord = vStagePixel / uBackdropSize;\n\n // ----- shape + normal -------------------------------------------------------------------\n float distFromEdgeShape = -roundedRectDistance(coord, uSize, uBorderRadius);\n distFromEdgeShape = max(distFromEdgeShape, 0.0);\n vec2 shapeNormal = normalize(coord - vec2(0.5));\n\n float distFromLeft = coord.x;\n float distFromRight = 1.0 - coord.x;\n float distFromTop = coord.y;\n float distFromBottom = 1.0 - coord.y;\n float distFromEdge = distFromEdgeShape / min(uSize.x, uSize.y);\n\n // ----- refraction (3 exponential bands + corner boost + ripple) -------------------------\n float normalizedDistance = distFromEdge * min(uSize.x, uSize.y);\n float baseIntensity = 1.0 - exp(-normalizedDistance * uBaseDistance);\n float edgeIntensity = exp(-normalizedDistance * uEdgeDistance);\n float rimIntensity = exp(-normalizedDistance * uRimDistance);\n\n float baseComponent = uWarp > 0.5 ? baseIntensity * uBaseIntensity : 0.0;\n float totalIntensity = baseComponent + edgeIntensity * uEdgeIntensity + rimIntensity * uRimIntensity;\n vec2 baseRefraction = shapeNormal * totalIntensity;\n\n float cornerProximityX = min(distFromLeft, distFromRight);\n float cornerProximityY = min(distFromTop, distFromBottom);\n float cornerDistance = max(cornerProximityX, cornerProximityY);\n float cornerNormalized = cornerDistance * min(uSize.x, uSize.y);\n float cornerBoost = exp(-cornerNormalized * 0.3) * uCornerBoost;\n vec2 cornerRefraction = shapeNormal * cornerBoost;\n\n vec2 perpendicular = vec2(-shapeNormal.y, shapeNormal.x);\n float ripple = sin(distFromEdge * 25.0) * uRippleEffect * rimIntensity;\n vec2 textureRefraction = perpendicular * ripple;\n\n vec2 totalRefraction = baseRefraction + cornerRefraction + textureRefraction;\n textureCoord += totalRefraction;\n\n // ----- 13x13 circular gaussian blur on backdrop ----------------------------------------\n vec4 color = vec4(0.0);\n vec2 texelSize = 1.0 / uBackdropSize;\n float sigma = uBlurRadius / 2.0;\n vec2 blurStep = texelSize * sigma;\n float totalWeight = 0.0;\n\n for (float i = -6.0; i <= 6.0; i += 1.0) {\n for (float j = -6.0; j <= 6.0; j += 1.0) {\n float dist = length(vec2(i, j));\n if (dist > 6.0) continue;\n float weight = exp(-(dist * dist) / (2.0 * sigma * sigma));\n vec2 offset = vec2(i, j) * blurStep;\n color += texture2D(uBackdropTex, textureCoord + offset) * weight;\n totalWeight += weight;\n }\n }\n color /= totalWeight;\n\n // ----- vertical gradient tint -----------------------------------------------------------\n // Cool-shifted tint so the glass picks up the navy backdrop's character. Top is near-white\n // (slightly cool), bottom drifts toward a desaturated blue-grey \u2014 gives the body the WWDC25\n // \"cool glass over blue\" look without overpowering the actual backdrop sample beneath.\n float gradientPosition = coord.y;\n vec3 topTint = vec3(0.96, 0.98, 1.0);\n vec3 bottomTint = vec3(0.62, 0.72, 0.88);\n vec3 gradientTint = mix(topTint, bottomTint, gradientPosition);\n color.rgb = mix(color.rgb, gradientTint, uTintOpacity);\n\n // ----- rounded-rect AA mask -------------------------------------------------------------\n // Discard fragments well outside the rounded silhouette so the mesh quad doesn't leave\n // a faint rectangular halo from accumulated blur/refraction samples blending under alpha=0.\n float maskDistance = roundedRectDistance(coord, uSize, uBorderRadius);\n if (maskDistance > 1.0) discard;\n float mask = 1.0 - smoothstep(-1.0, 1.0, maskDistance);\n\n gl_FragColor = vec4(color.rgb, mask);\n}\n";
22
+ //# sourceMappingURL=glass-shader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glass-shader.d.ts","sourceRoot":"","sources":["../src/glass-shader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,eAAO,MAAM,eAAe,gbAgB3B,CAAA;AAED,eAAO,MAAM,iBAAiB,8zJA8G7B,CAAA"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Liquid Glass body shader — direct port of dashersw/liquid-glass-js's fragment shader, adapted
3
+ * to PIXI v8 Mesh rendering. Their algorithm + parameter set 1:1; only the texture-coord
4
+ * derivation differs because PIXI Mesh draws at world position (not as a full-screen quad).
5
+ *
6
+ * Algorithm (per fragment):
7
+ * 1. Compute textureCoord = stage_pixel_position / backdrop_size (the dashersw equivalent of
8
+ * `(container_center + (uv - 0.5) * container_size) / texture_size`).
9
+ * 2. Rounded-rect SDF → distFromEdgeShape + shape normal.
10
+ * 3. Three exponential-falloff refraction bands (base/edge/rim) blended by per-band intensity,
11
+ * plus a corner-boost spike and a ripple effect for life. Sum → refraction offset in UV.
12
+ * 4. 13×13 circular gaussian blur on the backdrop, centred at the refracted UV.
13
+ * 5. Vertical white→grey gradient tint mixed into the blurred backdrop at `tintOpacity`.
14
+ * 6. Rounded-rect AA mask written to alpha.
15
+ *
16
+ * Parameters match dashersw's tuned defaults (Edge Intensity 0.01, Rim Intensity 0.05, etc.) —
17
+ * those landed Apple-grade on a forest backdrop, so we use them verbatim and only tune the
18
+ * canvas later for our darker context.
19
+ */
20
+ export const glassVertexGLSL = /* glsl */ `
21
+ in vec2 aPosition;
22
+ out vec2 vLocal;
23
+ out vec2 vStagePixel;
24
+
25
+ uniform mat3 uProjectionMatrix;
26
+ uniform mat3 uWorldTransformMatrix;
27
+ uniform mat3 uTransformMatrix;
28
+
29
+ void main() {
30
+ vLocal = aPosition;
31
+ mat3 stage = uWorldTransformMatrix * uTransformMatrix;
32
+ vStagePixel = (stage * vec3(aPosition, 1.0)).xy;
33
+ mat3 mvp = uProjectionMatrix * stage;
34
+ gl_Position = vec4((mvp * vec3(aPosition, 1.0)).xy, 0.0, 1.0);
35
+ }
36
+ `;
37
+ export const glassFragmentGLSL = /* glsl */ `
38
+ in vec2 vLocal;
39
+ in vec2 vStagePixel;
40
+
41
+ uniform sampler2D uBackdropTex;
42
+ uniform vec2 uBackdropSize; // dashersw u_textureSize
43
+ uniform vec2 uSize; // dashersw u_resolution (container px size)
44
+ uniform float uBorderRadius;
45
+ uniform float uBlurRadius;
46
+ uniform float uEdgeIntensity;
47
+ uniform float uRimIntensity;
48
+ uniform float uBaseIntensity;
49
+ uniform float uEdgeDistance;
50
+ uniform float uRimDistance;
51
+ uniform float uBaseDistance;
52
+ uniform float uCornerBoost;
53
+ uniform float uRippleEffect;
54
+ uniform float uTintOpacity;
55
+ uniform float uWarp;
56
+
57
+ float roundedRectDistance(vec2 coord, vec2 size, float radius) {
58
+ vec2 center = size * 0.5;
59
+ vec2 pixelCoord = coord * size;
60
+ vec2 toCorner = abs(pixelCoord - center) - (center - radius);
61
+ float outsideCorner = length(max(toCorner, 0.0));
62
+ float insideCorner = min(max(toCorner.x, toCorner.y), 0.0);
63
+ return outsideCorner + insideCorner - radius;
64
+ }
65
+
66
+ void main() {
67
+ // Mesh-local UV in [0,1] for the rounded-rect math (dashersw equivalent: coord).
68
+ vec2 coord = vLocal / uSize;
69
+
70
+ // PIXI port of dashersw textureCoord = pagePixel / textureSize. PIXI Mesh pipeline already
71
+ // gives us the fragment stage-pixel position via worldTransform * aPosition.
72
+ vec2 textureCoord = vStagePixel / uBackdropSize;
73
+
74
+ // ----- shape + normal -------------------------------------------------------------------
75
+ float distFromEdgeShape = -roundedRectDistance(coord, uSize, uBorderRadius);
76
+ distFromEdgeShape = max(distFromEdgeShape, 0.0);
77
+ vec2 shapeNormal = normalize(coord - vec2(0.5));
78
+
79
+ float distFromLeft = coord.x;
80
+ float distFromRight = 1.0 - coord.x;
81
+ float distFromTop = coord.y;
82
+ float distFromBottom = 1.0 - coord.y;
83
+ float distFromEdge = distFromEdgeShape / min(uSize.x, uSize.y);
84
+
85
+ // ----- refraction (3 exponential bands + corner boost + ripple) -------------------------
86
+ float normalizedDistance = distFromEdge * min(uSize.x, uSize.y);
87
+ float baseIntensity = 1.0 - exp(-normalizedDistance * uBaseDistance);
88
+ float edgeIntensity = exp(-normalizedDistance * uEdgeDistance);
89
+ float rimIntensity = exp(-normalizedDistance * uRimDistance);
90
+
91
+ float baseComponent = uWarp > 0.5 ? baseIntensity * uBaseIntensity : 0.0;
92
+ float totalIntensity = baseComponent + edgeIntensity * uEdgeIntensity + rimIntensity * uRimIntensity;
93
+ vec2 baseRefraction = shapeNormal * totalIntensity;
94
+
95
+ float cornerProximityX = min(distFromLeft, distFromRight);
96
+ float cornerProximityY = min(distFromTop, distFromBottom);
97
+ float cornerDistance = max(cornerProximityX, cornerProximityY);
98
+ float cornerNormalized = cornerDistance * min(uSize.x, uSize.y);
99
+ float cornerBoost = exp(-cornerNormalized * 0.3) * uCornerBoost;
100
+ vec2 cornerRefraction = shapeNormal * cornerBoost;
101
+
102
+ vec2 perpendicular = vec2(-shapeNormal.y, shapeNormal.x);
103
+ float ripple = sin(distFromEdge * 25.0) * uRippleEffect * rimIntensity;
104
+ vec2 textureRefraction = perpendicular * ripple;
105
+
106
+ vec2 totalRefraction = baseRefraction + cornerRefraction + textureRefraction;
107
+ textureCoord += totalRefraction;
108
+
109
+ // ----- 13x13 circular gaussian blur on backdrop ----------------------------------------
110
+ vec4 color = vec4(0.0);
111
+ vec2 texelSize = 1.0 / uBackdropSize;
112
+ float sigma = uBlurRadius / 2.0;
113
+ vec2 blurStep = texelSize * sigma;
114
+ float totalWeight = 0.0;
115
+
116
+ for (float i = -6.0; i <= 6.0; i += 1.0) {
117
+ for (float j = -6.0; j <= 6.0; j += 1.0) {
118
+ float dist = length(vec2(i, j));
119
+ if (dist > 6.0) continue;
120
+ float weight = exp(-(dist * dist) / (2.0 * sigma * sigma));
121
+ vec2 offset = vec2(i, j) * blurStep;
122
+ color += texture2D(uBackdropTex, textureCoord + offset) * weight;
123
+ totalWeight += weight;
124
+ }
125
+ }
126
+ color /= totalWeight;
127
+
128
+ // ----- vertical gradient tint -----------------------------------------------------------
129
+ // Cool-shifted tint so the glass picks up the navy backdrop's character. Top is near-white
130
+ // (slightly cool), bottom drifts toward a desaturated blue-grey — gives the body the WWDC25
131
+ // "cool glass over blue" look without overpowering the actual backdrop sample beneath.
132
+ float gradientPosition = coord.y;
133
+ vec3 topTint = vec3(0.96, 0.98, 1.0);
134
+ vec3 bottomTint = vec3(0.62, 0.72, 0.88);
135
+ vec3 gradientTint = mix(topTint, bottomTint, gradientPosition);
136
+ color.rgb = mix(color.rgb, gradientTint, uTintOpacity);
137
+
138
+ // ----- rounded-rect AA mask -------------------------------------------------------------
139
+ // Discard fragments well outside the rounded silhouette so the mesh quad doesn't leave
140
+ // a faint rectangular halo from accumulated blur/refraction samples blending under alpha=0.
141
+ float maskDistance = roundedRectDistance(coord, uSize, uBorderRadius);
142
+ if (maskDistance > 1.0) discard;
143
+ float mask = 1.0 - smoothstep(-1.0, 1.0, maskDistance);
144
+
145
+ gl_FragColor = vec4(color.rgb, mask);
146
+ }
147
+ `;
148
+ //# sourceMappingURL=glass-shader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glass-shader.js","sourceRoot":"","sources":["../src/glass-shader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;CAgBzC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8G3C,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { XenolithTheme } from '@xenolithengine/graph-render-pixi';
2
+ /**
3
+ * Liquid Glass theme — Apple WWDC25 material aesthetic applied to nodes.
4
+ *
5
+ * Custom PIXI v8 Mesh + GLSL material per node body (see `render-node.ts` + `glass-shader.ts`).
6
+ * Each frame the editor renders the world (minus the nodes layer) into a backdrop RenderTexture
7
+ * via `needsBackdrop: true`; every glass body samples that texture through its shader with
8
+ * edge-localised refraction, gaussian blur, and a vertical tint. Sits over a radial-gradient
9
+ * navy canvas with a soft dot grid (`createGrid` override).
10
+ */
11
+ export declare const liquidGlassTheme: XenolithTheme;
12
+ export { liquidGlassTokens } from './tokens.js';
13
+ export declare const VERSION = "0.7.0-beta.0";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAA;AAWtE;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAqC9B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/C,eAAO,MAAM,OAAO,iBAAiB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ import { liquidGlassTokens } from './tokens.js';
2
+ import { renderNodeLiquidGlass, renderRerouteLiquidGlass, renderRerouteNodeBoxLiquidGlass, syncLiquidGlassBackdropSize, syncLiquidGlassBackdropTexture, } from './render-node.js';
3
+ import { createLiquidGlassBackdrop } from './backdrop.js';
4
+ /**
5
+ * Liquid Glass theme — Apple WWDC25 material aesthetic applied to nodes.
6
+ *
7
+ * Custom PIXI v8 Mesh + GLSL material per node body (see `render-node.ts` + `glass-shader.ts`).
8
+ * Each frame the editor renders the world (minus the nodes layer) into a backdrop RenderTexture
9
+ * via `needsBackdrop: true`; every glass body samples that texture through its shader with
10
+ * edge-localised refraction, gaussian blur, and a vertical tint. Sits over a radial-gradient
11
+ * navy canvas with a soft dot grid (`createGrid` override).
12
+ */
13
+ export const liquidGlassTheme = {
14
+ id: 'liquid-glass',
15
+ tokens: liquidGlassTokens,
16
+ needsBackdrop: true,
17
+ // Disabled for now: the freeze/unfreeze at gesture start/end causes a hitch that hurts
18
+ // smoothness more than the per-frame glass cost. Revisit with proper LOD/virtualization (#59).
19
+ freezeOnNavigate: false,
20
+ // Glass nodes are far heavier per node than flat Xen ones (per-node backdrop RTs + the refraction
21
+ // shader), so the GPU ceiling is hit at fewer nodes — virtualize earlier than the 300 default.
22
+ virtualizeThreshold: 150,
23
+ // Comment header mirrors the LG node header: a flat accent tint over the (non-glass) comment body.
24
+ commentHeaderStyle: 'tint',
25
+ // CSS frosted-glass approximation for DOM chrome (insert palette). Translucent cool-white
26
+ // panel + heavy backdrop blur + luminous 1px rim + soft inner highlight — the WWDC25 look
27
+ // without the backdrop-sampling shader (overkill for chrome).
28
+ paletteStyle: {
29
+ backdropFilter: 'blur(18px) saturate(160%)',
30
+ panelBackground: 'rgba(28, 42, 74, 0.55)',
31
+ panelBorder: 'rgba(255, 255, 255, 0.28)',
32
+ panelShadow: '0 16px 50px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.35)',
33
+ panelRadius: '16px',
34
+ textColor: 'rgba(255, 255, 255, 0.95)',
35
+ mutedColor: 'rgba(220, 232, 255, 0.6)',
36
+ accent: '#9AD6E3',
37
+ rowSelectedBackground: 'rgba(255, 255, 255, 0.16)',
38
+ inputBackground: 'rgba(255, 255, 255, 0.1)',
39
+ inputBorder: 'rgba(255, 255, 255, 0.22)',
40
+ },
41
+ renderNode: (node, opts, ctx) => renderNodeLiquidGlass(node, liquidGlassTokens, opts, ctx),
42
+ renderReroute: (node, opts, ctx) => renderRerouteLiquidGlass(node, liquidGlassTokens, opts, ctx),
43
+ renderRerouteNode: (node, opts, ctx) => renderRerouteNodeBoxLiquidGlass(node, liquidGlassTokens, opts, ctx),
44
+ createGrid: () => createLiquidGlassBackdrop(liquidGlassTokens),
45
+ onFrame: (ctx) => {
46
+ const tex = ctx.backdropTexture;
47
+ if (tex)
48
+ syncLiquidGlassBackdropSize(tex.width, tex.height);
49
+ },
50
+ onNodeBackdrop: (nodeId, texture) => syncLiquidGlassBackdropTexture(nodeId, texture),
51
+ };
52
+ export { liquidGlassTokens } from './tokens.js';
53
+ export const VERSION = '0.7.0-beta.0';
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,+BAA+B,EAC/B,2BAA2B,EAC3B,8BAA8B,GAC/B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAA;AAEzD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAkB;IAC7C,EAAE,EAAE,cAAc;IAClB,MAAM,EAAE,iBAAiB;IACzB,aAAa,EAAE,IAAI;IACnB,uFAAuF;IACvF,+FAA+F;IAC/F,gBAAgB,EAAE,KAAK;IACvB,kGAAkG;IAClG,+FAA+F;IAC/F,mBAAmB,EAAE,GAAG;IACxB,mGAAmG;IACnG,kBAAkB,EAAE,MAAM;IAC1B,0FAA0F;IAC1F,0FAA0F;IAC1F,8DAA8D;IAC9D,YAAY,EAAE;QACZ,cAAc,EAAS,2BAA2B;QAClD,eAAe,EAAQ,wBAAwB;QAC/C,WAAW,EAAY,2BAA2B;QAClD,WAAW,EAAY,0EAA0E;QACjG,WAAW,EAAY,MAAM;QAC7B,SAAS,EAAc,2BAA2B;QAClD,UAAU,EAAa,0BAA0B;QACjD,MAAM,EAAiB,SAAS;QAChC,qBAAqB,EAAE,2BAA2B;QAClD,eAAe,EAAQ,0BAA0B;QACjD,WAAW,EAAY,2BAA2B;KACnD;IACD,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,GAAG,CAAC;IAC1F,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,GAAG,CAAC;IAChG,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,+BAA+B,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,GAAG,CAAC;IAC3G,UAAU,EAAE,GAAG,EAAE,CAAC,yBAAyB,CAAC,iBAAiB,CAAC;IAC9D,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACf,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAA;QAC/B,IAAI,GAAG;YAAE,2BAA2B,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC7D,CAAC;IACD,cAAc,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,8BAA8B,CAAC,MAAM,EAAE,OAAO,CAAC;CACrF,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { type TextureSource } from 'pixi.js';
2
+ import type { Node } from '@xenolithengine/graph-core';
3
+ import type { XenTokens } from '@xenolithengine/graph-theme-xen';
4
+ import { type NodeView, type RenderNodeOptions, type ThemeRenderContext } from '@xenolithengine/graph-render-pixi';
5
+ export declare function syncLiquidGlassBackdropSize(w: number, h: number): void;
6
+ /** Editor's painter's-order pass calls this per overlapping node to swap that node's mesh
7
+ * shader resources to point at the freshly rendered personal backdrop. `source = null` reverts
8
+ * to whatever the per-frame `onFrame` hook would otherwise sync (shared backdrop). */
9
+ export declare function syncLiquidGlassBackdropTexture(nodeId: string, source: TextureSource | null): void;
10
+ export declare function renderNodeLiquidGlass(node: Node, tokens: XenTokens, opts?: RenderNodeOptions, ctx?: ThemeRenderContext): NodeView;
11
+ /** Liquid Glass reroute knot — a glass disc. The glass shader draws a rounded rect; with a square
12
+ * the size of the dot and a corner radius equal to half its side, that rounded rect is a perfect
13
+ * circle that refracts the backdrop just like a full node. */
14
+ export declare function renderRerouteLiquidGlass(node: Node, tokens: XenTokens, opts?: RenderNodeOptions, ctx?: ThemeRenderContext): NodeView;
15
+ /** Liquid Glass palette Reroute node — a compact glass body with pullable In/Out pins. Mirrors the
16
+ * Xen `renderRerouteNodeBox` but with the glass material. */
17
+ export declare function renderRerouteNodeBoxLiquidGlass(node: Node, tokens: XenTokens, opts?: RenderNodeOptions, ctx?: ThemeRenderContext): NodeView;
18
+ //# sourceMappingURL=render-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-node.d.ts","sourceRoot":"","sources":["../src/render-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAcL,KAAK,aAAa,EACnB,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAChE,OAAO,EAWL,KAAK,QAAQ,EAEb,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAExB,MAAM,mCAAmC,CAAA;AAe1C,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAQtE;AAED;;uFAEuF;AACvF,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,GAAG,IAAI,GAC3B,IAAI,CAeN;AAiFD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,iBAAsB,EAC5B,GAAG,GAAE,kBAA8C,GAClD,QAAQ,CAySV;AAED;;+DAE+D;AAC/D,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,iBAAsB,EAC5B,GAAG,GAAE,kBAA8C,GAClD,QAAQ,CAsDV;AAED;8DAC8D;AAC9D,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,iBAAsB,EAC5B,GAAG,GAAE,kBAA8C,GAClD,QAAQ,CA0DV"}
@@ -0,0 +1,532 @@
1
+ import { CanvasTextMetrics, Circle, Container, Geometry, Graphics, Mesh, Rectangle, Shader, BitmapText, Text, TextStyle, Texture, } from 'pixi.js';
2
+ import { computeNodeLayout, makeHeaderIcon, buildPinShape, markPinInteractive, renderWidgets, rerouteStateColor, resolveCategoryAccent, resolvePinFill, resolvePinShape, resolvePinStroke, } from '@xenolithengine/graph-render-pixi';
3
+ import { glassFragmentGLSL, glassVertexGLSL } from './glass-shader.js';
4
+ const liveGlassMeshes = new Set();
5
+ const meshesByNode = new Map();
6
+ export function syncLiquidGlassBackdropSize(w, h) {
7
+ for (const entry of liveGlassMeshes) {
8
+ const group = entry.shader.resources['glassUniforms'];
9
+ if (!group)
10
+ continue;
11
+ const arr = group.uniforms.uBackdropSize;
12
+ if (arr && arr[0] === w && arr[1] === h)
13
+ continue;
14
+ group.uniforms.uBackdropSize = new Float32Array([w, h]);
15
+ }
16
+ }
17
+ /** Editor's painter's-order pass calls this per overlapping node to swap that node's mesh
18
+ * shader resources to point at the freshly rendered personal backdrop. `source = null` reverts
19
+ * to whatever the per-frame `onFrame` hook would otherwise sync (shared backdrop). */
20
+ export function syncLiquidGlassBackdropTexture(nodeId, source) {
21
+ const set = meshesByNode.get(nodeId);
22
+ if (!set)
23
+ return;
24
+ for (const entry of set) {
25
+ entry.shader.resources['uBackdropTex'] = source ?? Texture.WHITE.source;
26
+ if (source) {
27
+ const group = entry.shader.resources['glassUniforms'];
28
+ if (group) {
29
+ const arr = group.uniforms.uBackdropSize;
30
+ if (!arr || arr[0] !== source.width || arr[1] !== source.height) {
31
+ group.uniforms.uBackdropSize = new Float32Array([source.width, source.height]);
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ /** Build one glass-material mesh sized to (w, h). The fragment shader samples
38
+ * `ctx.backdropTexture` (a per-frame snapshot of the world minus nodes) via screen UV, with
39
+ * refraction-offset taps, to give the body a real "see through frosted glass" feel. */
40
+ function createGlassMesh(nodeId, w, h, radius, _tokens, backdropTexture) {
41
+ const geometry = new Geometry({
42
+ attributes: {
43
+ aPosition: [0, 0, w, 0, w, h, 0, h],
44
+ },
45
+ indexBuffer: [0, 1, 2, 0, 2, 3],
46
+ });
47
+ // Backdrop texture must be a Texture instance (not raw TextureSource) for PIXI's resource
48
+ // binding to wrap it correctly. Fall back to white pixel if no backdrop is available yet.
49
+ const backdrop = backdropTexture
50
+ ? new Texture({ source: backdropTexture })
51
+ : Texture.WHITE;
52
+ const bdWidth = backdropTexture ? backdropTexture.width : 1;
53
+ const bdHeight = backdropTexture ? backdropTexture.height : 1;
54
+ // dashersw/liquid-glass-js defaults (their tuned values from container.js):
55
+ // blurRadius: 5.0, edgeIntensity: 0.01, rimIntensity: 0.05, baseIntensity: 0.01,
56
+ // edgeDistance: 0.15, rimDistance: 0.8, baseDistance: 0.1, cornerBoost: 0.02,
57
+ // rippleEffect: 0.1, tintOpacity: 0.2, warp: 0 (off)
58
+ const shader = Shader.from({
59
+ gl: { vertex: glassVertexGLSL, fragment: glassFragmentGLSL },
60
+ resources: {
61
+ uBackdropTex: backdrop.source,
62
+ glassUniforms: {
63
+ uSize: { value: new Float32Array([w, h]), type: 'vec2<f32>' },
64
+ uBorderRadius: { value: radius, type: 'f32' },
65
+ uBackdropSize: { value: new Float32Array([bdWidth, bdHeight]), type: 'vec2<f32>' },
66
+ uBlurRadius: { value: 5.0, type: 'f32' },
67
+ uEdgeIntensity: { value: 0.01, type: 'f32' },
68
+ uRimIntensity: { value: 0.05, type: 'f32' },
69
+ uBaseIntensity: { value: 0.01, type: 'f32' },
70
+ uEdgeDistance: { value: 0.15, type: 'f32' },
71
+ uRimDistance: { value: 0.8, type: 'f32' },
72
+ uBaseDistance: { value: 0.1, type: 'f32' },
73
+ uCornerBoost: { value: 0.02, type: 'f32' },
74
+ uRippleEffect: { value: 0.1, type: 'f32' },
75
+ uTintOpacity: { value: 0.2, type: 'f32' },
76
+ uWarp: { value: 0.0, type: 'f32' },
77
+ },
78
+ },
79
+ });
80
+ const mesh = new Mesh({ geometry, shader });
81
+ mesh.label = `liquid-glass-${w}x${h}`;
82
+ mesh.eventMode = 'static';
83
+ mesh.cursor = 'pointer';
84
+ mesh.hitArea = new Rectangle(0, 0, w, h);
85
+ const entry = { shader, nodeId };
86
+ liveGlassMeshes.add(entry);
87
+ let nodeSet = meshesByNode.get(nodeId);
88
+ if (!nodeSet) {
89
+ nodeSet = new Set();
90
+ meshesByNode.set(nodeId, nodeSet);
91
+ }
92
+ nodeSet.add(entry);
93
+ mesh.on('destroyed', () => {
94
+ liveGlassMeshes.delete(entry);
95
+ const s = meshesByNode.get(nodeId);
96
+ if (s) {
97
+ s.delete(entry);
98
+ if (s.size === 0)
99
+ meshesByNode.delete(nodeId);
100
+ }
101
+ });
102
+ return mesh;
103
+ }
104
+ export function renderNodeLiquidGlass(node, tokens, opts = {}, ctx = { backdropTexture: null }) {
105
+ const geo = tokens.geometry;
106
+ const layout = computeNodeLayout(node, {
107
+ node: {
108
+ minWidth: geo.node.minWidth,
109
+ headerHeight: geo.node.headerHeight,
110
+ headerPadding: geo.node.headerPadding,
111
+ innerPaddingX: geo.node.innerPaddingX,
112
+ innerPaddingY: geo.node.innerPaddingY,
113
+ },
114
+ pin: {
115
+ diameter: geo.pin.diameter,
116
+ rowSpacing: geo.pin.rowSpacing,
117
+ rowHeight: geo.pin.rowHeight,
118
+ },
119
+ header: { toPinsGap: geo.header.toPinsGap },
120
+ });
121
+ const expandedW = layout.body.width;
122
+ const expandedH = layout.body.height;
123
+ const expandedRadius = geo.node.radius;
124
+ const pillH = geo.node.pillHeight;
125
+ const pillR = geo.node.pillRadius;
126
+ // Pill width must fit the title (it sits at x=34) plus a trailing cap, not a fixed minimum —
127
+ // otherwise long titles like "Workflow Information" overflow the capsule.
128
+ const titleText = opts.title ?? node.type;
129
+ const titleW = CanvasTextMetrics.measureText(titleText, new TextStyle({ fontFamily: tokens.typography.fontFamily, fontSize: tokens.typography.heading.size, fontWeight: '700' })).width;
130
+ // A header glyph sits left or right of the title; the pill shifts/widens to fit it.
131
+ const glyph = opts.glyph;
132
+ const glyphSize = glyph ? Math.round(tokens.typography.heading.size + 1) : 0;
133
+ const leftGlyph = glyph?.side === 'left';
134
+ const rightGlyph = glyph?.side === 'right';
135
+ const pillTitleX = leftGlyph ? 34 + glyphSize + geo.header.titleGap : 34;
136
+ const pillTitleEndX = pillTitleX + titleW + (rightGlyph ? geo.header.titleGap + glyphSize : 0);
137
+ const pillW = Math.max(geo.node.pillMinWidth, pillTitleEndX + pillR);
138
+ // Xen canon: pill is vertically centred within the expanded body's bounds when collapsed.
139
+ // Canon: collapse UP to the header (pill at top), not to the vertical centre.
140
+ const pillOffsetY = 0;
141
+ const container = new Container({ label: `node-liquid-glass:${node.id}` });
142
+ container.position.set(node.position.x, node.position.y);
143
+ container.eventMode = 'static';
144
+ // ===== Two body meshes — expanded (full size) and pill (compact, vertically centred) =======
145
+ const nodeIdStr = String(node.id);
146
+ const expandedBody = createGlassMesh(nodeIdStr, expandedW, expandedH, expandedRadius, tokens, ctx.backdropTexture);
147
+ const pillBody = createGlassMesh(nodeIdStr, pillW, pillH, pillR, tokens, ctx.backdropTexture);
148
+ pillBody.position.set(0, pillOffsetY);
149
+ container.addChild(expandedBody);
150
+ container.addChild(pillBody);
151
+ // Clip each glass mesh to its exact rounded-rect silhouette. The shader's rounded-rect AA can
152
+ // leave the quad corners faintly square ("clipped" look); a hard mask guarantees crisp rounded
153
+ // corners — same fix proven on the reroute knots.
154
+ const expandedMask = new Graphics().roundRect(0, 0, expandedW, expandedH, expandedRadius).fill({ color: 0xffffff });
155
+ container.addChild(expandedMask);
156
+ expandedBody.mask = expandedMask;
157
+ const pillMask = new Graphics().roundRect(0, pillOffsetY, pillW, pillH, pillR).fill({ color: 0xffffff });
158
+ container.addChild(pillMask);
159
+ pillBody.mask = pillMask;
160
+ // ===== Inner content (header tint + pin labels + chevron+title for expanded form) ==========
161
+ const expandedInner = new Container({ label: 'expanded-inner' });
162
+ container.addChild(expandedInner);
163
+ const accent = opts.color ?? resolveCategoryAccent(opts.category, tokens, opts.categoryPalette);
164
+ const headerHeight = geo.node.headerHeight;
165
+ const headerRadius = Math.max(0, geo.node.radius - 1);
166
+ const headerTint = new Graphics()
167
+ .roundRect(2, 2, expandedW - 4, headerHeight - 1, headerRadius)
168
+ .fill({ color: accent, alpha: 0.45 });
169
+ expandedInner.addChild(headerTint);
170
+ // Collapsed pill carries the category accent across its whole body (the glass alone reads as
171
+ // "some node" — this is the type cue, mirroring the header tint of the expanded form). Only
172
+ // visible in pill form; sits above the glass, below the pill title.
173
+ const pillTint = new Graphics()
174
+ .roundRect(0, pillOffsetY, pillW, pillH, pillR)
175
+ .fill({ color: accent, alpha: 0.4 });
176
+ pillTint.visible = false;
177
+ container.addChild(pillTint);
178
+ const title = new BitmapText({
179
+ text: titleText,
180
+ style: {
181
+ fontFamily: tokens.typography.fontFamily,
182
+ fontSize: tokens.typography.heading.size,
183
+ fontWeight: '700',
184
+ fill: '#FFFFFF',
185
+ },
186
+ });
187
+ // Header glyph left or right of the title — same icons as Xen. Lives in expandedInner so it toggles
188
+ // away with the title in pill form.
189
+ const baseTitleX = geo.node.headerPadding + 8 + geo.header.chevronSize / 2 + geo.header.titleGap;
190
+ const titleX = leftGlyph ? baseTitleX + glyphSize + geo.header.titleGap : baseTitleX;
191
+ title.position.set(titleX, 4);
192
+ expandedInner.addChild(title);
193
+ if (glyph) {
194
+ // Centre the glyph's box on the title's optical middle so the glyph + text read on one line.
195
+ const icon = makeHeaderIcon(glyph.svg, '#FFFFFF', glyphSize);
196
+ const iconX = leftGlyph ? baseTitleX : expandedW - geo.node.headerPadding - glyphSize;
197
+ icon.position.set(iconX, title.position.y + title.height / 2 - glyphSize / 2);
198
+ expandedInner.addChild(icon);
199
+ }
200
+ // ===== Pill title — separate label only visible in collapsed form =========================
201
+ const pillTitle = new BitmapText({
202
+ text: titleText,
203
+ style: {
204
+ fontFamily: tokens.typography.fontFamily,
205
+ fontSize: tokens.typography.heading.size,
206
+ fontWeight: '700',
207
+ fill: '#FFFFFF',
208
+ },
209
+ });
210
+ pillTitle.position.set(pillTitleX, pillOffsetY + (pillH - tokens.typography.heading.lineHeight) / 2);
211
+ pillTitle.visible = false;
212
+ container.addChild(pillTitle);
213
+ // Pill glyph — same icon as the expanded header, centred on the pill title. Toggled with the pill
214
+ // form in applyForm (only present when this node has a glyph).
215
+ const pillIcon = glyph ? makeHeaderIcon(glyph.svg, '#FFFFFF', glyphSize) : null;
216
+ if (pillIcon) {
217
+ const pillIconX = leftGlyph ? 34 : pillTitleX + pillTitle.width + geo.header.titleGap;
218
+ pillIcon.position.set(pillIconX, pillTitle.position.y + pillTitle.height / 2 - glyphSize / 2);
219
+ pillIcon.visible = false;
220
+ container.addChild(pillIcon);
221
+ }
222
+ // ===== Chevron (toggle button) — drawn at origin, positioned per form. Matches Xen's
223
+ // `buildChevron` proportions (halfW = size*0.22, halfH = size*0.11) for a thin, crisp ‘v’.
224
+ const chevron = new Graphics();
225
+ const cs = geo.header.chevronSize;
226
+ const chevronHalfW = cs * 0.22;
227
+ const chevronHalfH = cs * 0.11;
228
+ chevron
229
+ .moveTo(-chevronHalfW, -chevronHalfH)
230
+ .lineTo(0, chevronHalfH)
231
+ .lineTo(chevronHalfW, -chevronHalfH)
232
+ .stroke({ color: '#FFFFFF', width: 1.2, cap: 'round', join: 'round' });
233
+ chevron.cursor = 'pointer';
234
+ chevron.eventMode = 'static';
235
+ chevron.hitArea = {
236
+ contains: (px, py) => Math.abs(px) <= cs / 2 && Math.abs(py) <= cs / 2,
237
+ };
238
+ container.addChild(chevron);
239
+ // ===== Pins + labels — built for expanded form; rebuilt on form change =====================
240
+ const expandedPinPositions = new Map();
241
+ const pinGraphics = [];
242
+ for (const pin of node.pins) {
243
+ const layoutPin = layout.pins.find((p) => p.id === pin.id);
244
+ if (!layoutPin)
245
+ continue;
246
+ const localX = layoutPin.x - node.position.x;
247
+ const localY = layoutPin.y - node.position.y;
248
+ expandedPinPositions.set(pin.id, { x: localX, y: localY });
249
+ const r = geo.pin.diameter / 2;
250
+ const g = buildPinShape(new Graphics(), resolvePinShape(String(pin.type), pin.kind, opts.types), localX, localY, r)
251
+ .fill({ color: resolvePinFill(String(pin.type), tokens, opts.types) })
252
+ .stroke({ color: resolvePinStroke(String(pin.type), tokens), width: geo.pin.stroke });
253
+ markPinInteractive(g, pin, String(node.id), localX, localY, r);
254
+ pinGraphics.push(g);
255
+ container.addChild(g);
256
+ if (pin.label) {
257
+ const lab = new BitmapText({
258
+ text: pin.label,
259
+ style: {
260
+ fontFamily: tokens.typography.fontFamily,
261
+ fontSize: tokens.typography.label.size,
262
+ fontWeight: String(tokens.typography.label.weight),
263
+ fill: 'rgba(255, 255, 255, 0.95)',
264
+ },
265
+ });
266
+ if (layoutPin.side === 'left') {
267
+ lab.anchor.set(0, 0.5);
268
+ lab.position.set(localX + r + geo.pin.labelGap, localY);
269
+ }
270
+ else {
271
+ lab.anchor.set(1, 0.5);
272
+ lab.position.set(localX - r - geo.pin.labelGap, localY);
273
+ }
274
+ expandedInner.addChild(lab);
275
+ }
276
+ }
277
+ // ===== Pill pin positions (computed once; applied on collapse) ============================
278
+ const pillPinPositions = computePillPinPositions(node, pillW, pillH, pillR, pillOffsetY);
279
+ // ===== Selection / hover ring =============================================================
280
+ const selectionRim = new Graphics();
281
+ selectionRim.alpha = 0;
282
+ container.addChild(selectionRim);
283
+ let visualState = opts.state ?? 'default';
284
+ const applyVisualState = () => {
285
+ switch (visualState) {
286
+ case 'hover':
287
+ selectionRim.tint = 0xFFFFFF;
288
+ selectionRim.alpha = 0.45;
289
+ break;
290
+ case 'selected':
291
+ selectionRim.tint = 0xFFFFFF;
292
+ selectionRim.alpha = 0.85;
293
+ break;
294
+ case 'active':
295
+ selectionRim.tint = 0xFFDF67;
296
+ selectionRim.alpha = 0.95;
297
+ break;
298
+ default: selectionRim.alpha = 0;
299
+ }
300
+ };
301
+ // ===== Form swap (expanded ↔ pill) =========================================================
302
+ let collapsed = !!opts.collapsed;
303
+ function rebuildPinGeometry(positions) {
304
+ let i = 0;
305
+ for (const pin of node.pins) {
306
+ const pos = positions.get(pin.id);
307
+ if (!pos)
308
+ continue;
309
+ const g = pinGraphics[i++];
310
+ if (!g)
311
+ continue;
312
+ const r = geo.pin.diameter / 2;
313
+ g.clear();
314
+ buildPinShape(g, resolvePinShape(String(pin.type), pin.kind, opts.types), pos.x, pos.y, r)
315
+ .fill({ color: resolvePinFill(String(pin.type), tokens, opts.types) })
316
+ .stroke({ color: resolvePinStroke(String(pin.type), tokens), width: geo.pin.stroke });
317
+ markPinInteractive(g, pin, String(node.id), pos.x, pos.y, r);
318
+ }
319
+ }
320
+ function applyForm(c) {
321
+ if (c) {
322
+ expandedBody.visible = false;
323
+ pillBody.visible = true;
324
+ expandedInner.visible = false;
325
+ pillTitle.visible = true;
326
+ if (pillIcon)
327
+ pillIcon.visible = true;
328
+ pillTint.visible = true;
329
+ selectionRim.clear()
330
+ .roundRect(0, pillOffsetY, pillW, pillH, pillR)
331
+ .stroke({ color: '#FFFFFF', width: 1.5, alignment: 0.5 });
332
+ rebuildPinGeometry(pillPinPositions);
333
+ chevron.rotation = -Math.PI / 2;
334
+ chevron.position.set(18, pillOffsetY + pillH / 2);
335
+ }
336
+ else {
337
+ expandedBody.visible = true;
338
+ pillBody.visible = false;
339
+ expandedInner.visible = true;
340
+ pillTitle.visible = false;
341
+ if (pillIcon)
342
+ pillIcon.visible = false;
343
+ pillTint.visible = false;
344
+ selectionRim.clear()
345
+ .roundRect(0, 0, expandedW, expandedH, expandedRadius)
346
+ .stroke({ color: '#FFFFFF', width: 1.5, alignment: 0.5 });
347
+ rebuildPinGeometry(expandedPinPositions);
348
+ chevron.rotation = 0;
349
+ chevron.position.set(geo.node.headerPadding + 8 + cs / 2 - 4, 2 + headerHeight / 2 - 0.5);
350
+ }
351
+ applyVisualState();
352
+ }
353
+ chevron.on('pointerdown', (e) => {
354
+ collapsed = !collapsed;
355
+ applyForm(collapsed);
356
+ e.stopPropagation();
357
+ });
358
+ // Widget rows (expanded form only) — reuse the shared renderer; LG inherits widget tokens.
359
+ const widgetsView = node.widgets && node.widgets.length > 0
360
+ ? renderWidgets(node, expandedW, tokens, {
361
+ node: { headerHeight: geo.node.headerHeight },
362
+ pin: { rowSpacing: geo.pin.rowSpacing, rowHeight: geo.pin.rowHeight },
363
+ header: { toPinsGap: geo.header.toPinsGap },
364
+ widget: { rowHeight: geo.widget.rowHeight, gap: geo.widget.gap, paddingX: geo.widget.paddingX },
365
+ }, opts.customWidgets)
366
+ : null;
367
+ if (widgetsView)
368
+ expandedInner.addChild(widgetsView.container);
369
+ applyForm(collapsed);
370
+ const view = {
371
+ container,
372
+ setVisualState(state) {
373
+ visualState = state;
374
+ applyVisualState();
375
+ },
376
+ setCollapsed(c, _animated) {
377
+ if (c === collapsed)
378
+ return;
379
+ collapsed = c;
380
+ applyForm(collapsed);
381
+ },
382
+ isCollapsed() { return collapsed; },
383
+ pinLocalPosition(pinId) {
384
+ return (collapsed ? pillPinPositions : expandedPinPositions).get(pinId) ?? null;
385
+ },
386
+ collapsedRect: { x: 0, y: pillOffsetY, w: pillW, h: pillH, r: pillR },
387
+ };
388
+ if (widgetsView) {
389
+ view.widgetHit = (x, y) => (collapsed ? null : widgetsView.widgetHit(x, y));
390
+ view.updateWidget = (id, value) => widgetsView.update(id, value);
391
+ }
392
+ return view;
393
+ }
394
+ /** Liquid Glass reroute knot — a glass disc. The glass shader draws a rounded rect; with a square
395
+ * the size of the dot and a corner radius equal to half its side, that rounded rect is a perfect
396
+ * circle that refracts the backdrop just like a full node. */
397
+ export function renderRerouteLiquidGlass(node, tokens, opts = {}, ctx = { backdropTexture: null }) {
398
+ const r = tokens.geometry.reroute.radius;
399
+ const d = 2 * r;
400
+ const cy = r;
401
+ const container = new Container({ label: `reroute-liquid-glass:${node.id}` });
402
+ container.position.set(node.position.x, node.position.y);
403
+ container.eventMode = 'static';
404
+ const disc = createGlassMesh(String(node.id), d, d, r, tokens, ctx.backdropTexture);
405
+ disc.hitArea = new Circle(r, r, r);
406
+ container.addChild(disc);
407
+ // Clip the glass mesh to a hard circle so the knot is unambiguously round (the shader's
408
+ // rounded-rect AA can leave the quad corners faintly visible at this small size).
409
+ const discMask = new Graphics().circle(r, r, r).fill({ color: 0xffffff });
410
+ container.addChild(discMask);
411
+ disc.mask = discMask;
412
+ const outPin = node.pins.find((p) => p.direction === 'out');
413
+ const inPin = node.pins.find((p) => p.direction === 'in');
414
+ const wireType = String(outPin?.type ?? inPin?.type ?? 'any');
415
+ // Tint the whole glass disc with a soft type-coloured fill — the same treatment a collapsed node
416
+ // gets (a wash over the glass, not a bright inner ring), so the knot reads as glass first and its
417
+ // wire type second. Clipped to the same circle so it never bleeds past the disc edge.
418
+ const tint = new Graphics()
419
+ .circle(r, r, r)
420
+ .fill({ color: resolvePinFill(wireType, tokens), alpha: 0.4 });
421
+ container.addChild(tint);
422
+ const rim = new Graphics()
423
+ .circle(r, r, r + 1)
424
+ .stroke({ color: 0xffffff, width: 1.5, alignment: 0.5 });
425
+ rim.alpha = 0;
426
+ container.addChild(rim);
427
+ // Inline reroute: anchor points only, no interactive (pullable) pin handles — the glass disc
428
+ // body stays the grab target and new wires can't be started from the knot.
429
+ const pinLocal = new Map();
430
+ for (const pin of node.pins) {
431
+ pinLocal.set(String(pin.id), { x: pin.direction === 'in' ? 0 : d, y: cy });
432
+ }
433
+ return {
434
+ container,
435
+ setVisualState(state) {
436
+ const c = rerouteStateColor(state, tokens);
437
+ if (!c) {
438
+ rim.alpha = 0;
439
+ return;
440
+ }
441
+ rim.tint = c;
442
+ rim.alpha = state === 'hover' ? 0.6 : 0.95;
443
+ },
444
+ setCollapsed: () => { },
445
+ isCollapsed: () => false,
446
+ pinLocalPosition: (pinId) => pinLocal.get(pinId) ?? null,
447
+ };
448
+ }
449
+ /** Liquid Glass palette Reroute node — a compact glass body with pullable In/Out pins. Mirrors the
450
+ * Xen `renderRerouteNodeBox` but with the glass material. */
451
+ export function renderRerouteNodeBoxLiquidGlass(node, tokens, opts = {}, ctx = { backdropTexture: null }) {
452
+ const h = tokens.geometry.pin.diameter + 16;
453
+ const w = 56;
454
+ const radius = Math.min(tokens.geometry.node.radius, h / 2);
455
+ const cy = h / 2;
456
+ const container = new Container({ label: `reroute-node-liquid-glass:${node.id}` });
457
+ container.position.set(node.position.x, node.position.y);
458
+ container.eventMode = 'static';
459
+ const body = createGlassMesh(String(node.id), w, h, radius, tokens, ctx.backdropTexture);
460
+ body.hitArea = new Rectangle(0, 0, w, h);
461
+ container.addChild(body);
462
+ const bodyMask = new Graphics().roundRect(0, 0, w, h, radius).fill({ color: 0xffffff });
463
+ container.addChild(bodyMask);
464
+ body.mask = bodyMask;
465
+ // Tint the glass relay by the type flowing through it (resolved from the out pin, falling back to
466
+ // the in pin) — the same soft wash the inline knot and collapsed pills carry. Itself a rounded
467
+ // rect of the body shape, so it stays within the glass edge without a shared mask.
468
+ const inPin = node.pins.find((p) => p.direction === 'in');
469
+ const outPin = node.pins.find((p) => p.direction === 'out');
470
+ const wireType = String(outPin?.type ?? inPin?.type ?? 'any');
471
+ container.addChild(new Graphics().roundRect(0, 0, w, h, radius).fill({ color: resolvePinFill(wireType, tokens), alpha: 0.4 }));
472
+ const rim = new Graphics()
473
+ .roundRect(-1, -1, w + 2, h + 2, radius + 1)
474
+ .stroke({ color: 0xffffff, width: 1.5, alignment: 0.5 });
475
+ rim.alpha = 0;
476
+ container.addChild(rim);
477
+ const pinLocal = new Map();
478
+ const pinRadius = tokens.geometry.pin.diameter / 2;
479
+ for (const pin of node.pins) {
480
+ const px = pin.direction === 'in' ? 0 : w;
481
+ pinLocal.set(String(pin.id), { x: px, y: cy });
482
+ const g = new Graphics()
483
+ .circle(px, cy, pinRadius)
484
+ .fill({ color: resolvePinFill(String(pin.type), tokens) })
485
+ .stroke({ color: resolvePinStroke(String(pin.type), tokens), width: tokens.geometry.pin.stroke });
486
+ markPinInteractive(g, pin, String(node.id), px, cy, pinRadius);
487
+ container.addChild(g);
488
+ }
489
+ return {
490
+ container,
491
+ setVisualState(state) {
492
+ const c = rerouteStateColor(state, tokens);
493
+ if (!c) {
494
+ rim.alpha = 0;
495
+ return;
496
+ }
497
+ rim.tint = c;
498
+ rim.alpha = state === 'hover' ? 0.6 : 0.95;
499
+ },
500
+ setCollapsed: () => { },
501
+ isCollapsed: () => false,
502
+ pinLocalPosition: (pinId) => pinLocal.get(pinId) ?? null,
503
+ };
504
+ }
505
+ /** Distribute pins onto the rounded end-caps of the pill so each sits on the silhouette, in
506
+ * container-local coordinates. The pill is offset vertically by `pillOffsetY` to centre it
507
+ * within the original expanded body's bounds (Xen canon — collapse to centre, not to header). */
508
+ function computePillPinPositions(node, pillW, pillH, pillR, pillOffsetY) {
509
+ const inputs = node.pins.filter((p) => p.direction === 'in');
510
+ const outputs = node.pins.filter((p) => p.direction === 'out');
511
+ const out = new Map();
512
+ const cy = pillOffsetY + pillH / 2;
513
+ const idealStep = Math.PI / 3.6;
514
+ const maxTotal = Math.PI * 0.94;
515
+ const arcAngle = (count, idx) => {
516
+ if (count <= 1)
517
+ return 0;
518
+ const total = Math.min(maxTotal, (count - 1) * idealStep);
519
+ const step = total / (count - 1);
520
+ return -total / 2 + idx * step;
521
+ };
522
+ inputs.forEach((p, i) => {
523
+ const a = arcAngle(inputs.length, i);
524
+ out.set(p.id, { x: pillR - pillR * Math.cos(a), y: cy + pillR * Math.sin(a) });
525
+ });
526
+ outputs.forEach((p, i) => {
527
+ const a = arcAngle(outputs.length, i);
528
+ out.set(p.id, { x: pillW - pillR + pillR * Math.cos(a), y: cy + pillR * Math.sin(a) });
529
+ });
530
+ return out;
531
+ }
532
+ //# sourceMappingURL=render-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-node.js","sourceRoot":"","sources":["../src/render-node.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,IAAI,EACJ,SAAS,EACT,OAAO,GAGR,MAAM,SAAS,CAAA;AAGhB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,gBAAgB,GAMjB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAWtE,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAA;AACjD,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+B,CAAA;AAE3D,MAAM,UAAU,2BAA2B,CAAC,CAAS,EAAE,CAAS;IAC9D,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;QACrD,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,aAA6B,CAAA;QACxD,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,SAAQ;QACjD,KAAK,CAAC,QAAQ,CAAC,aAAa,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACzD,CAAC;AACH,CAAC;AAED;;uFAEuF;AACvF,MAAM,UAAU,8BAA8B,CAC5C,MAAc,EACd,MAA4B;IAE5B,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACpC,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,GAAG,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAA;QACvE,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;YACrD,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,aAA6B,CAAA;gBACxD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;oBAChE,KAAK,CAAC,QAAQ,CAAC,aAAa,GAAG,IAAI,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;gBAChF,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAGD;;wFAEwF;AACxF,SAAS,eAAe,CACtB,MAAc,EACd,CAAS,EACT,CAAS,EACT,MAAc,EACd,OAAkB,EAClB,eAAqC;IAErC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;QAC5B,UAAU,EAAE;YACV,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;SACpC;QACD,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;KAChC,CAAC,CAAA;IAEF,0FAA0F;IAC1F,0FAA0F;IAC1F,MAAM,QAAQ,GAAG,eAAe;QAC9B,CAAC,CAAC,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QAC1C,CAAC,CAAC,OAAO,CAAC,KAAK,CAAA;IACjB,MAAM,OAAO,GAAI,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAE,CAAC,CAAC,CAAC,CAAA;IAC7D,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAE7D,4EAA4E;IAC5E,mFAAmF;IACnF,gFAAgF;IAChF,uDAAuD;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,EAAE,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,iBAAiB,EAAE;QAC5D,SAAS,EAAE;YACT,YAAY,EAAE,QAAQ,CAAC,MAAM;YAC7B,aAAa,EAAE;gBACb,KAAK,EAAU,EAAE,KAAK,EAAE,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAe,IAAI,EAAE,WAAW,EAAE;gBAClF,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAiC,IAAI,EAAE,KAAK,EAAE;gBAC5E,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,YAAY,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE;gBAClF,WAAW,EAAK,EAAE,KAAK,EAAE,GAAG,EAAK,IAAI,EAAE,KAAK,EAAE;gBAC9C,cAAc,EAAE,EAAE,KAAK,EAAE,IAAI,EAAI,IAAI,EAAE,KAAK,EAAE;gBAC9C,aAAa,EAAG,EAAE,KAAK,EAAE,IAAI,EAAI,IAAI,EAAE,KAAK,EAAE;gBAC9C,cAAc,EAAE,EAAE,KAAK,EAAE,IAAI,EAAI,IAAI,EAAE,KAAK,EAAE;gBAC9C,aAAa,EAAG,EAAE,KAAK,EAAE,IAAI,EAAI,IAAI,EAAE,KAAK,EAAE;gBAC9C,YAAY,EAAI,EAAE,KAAK,EAAE,GAAG,EAAK,IAAI,EAAE,KAAK,EAAE;gBAC9C,aAAa,EAAG,EAAE,KAAK,EAAE,GAAG,EAAK,IAAI,EAAE,KAAK,EAAE;gBAC9C,YAAY,EAAI,EAAE,KAAK,EAAE,IAAI,EAAI,IAAI,EAAE,KAAK,EAAE;gBAC9C,aAAa,EAAG,EAAE,KAAK,EAAE,GAAG,EAAK,IAAI,EAAE,KAAK,EAAE;gBAC9C,YAAY,EAAI,EAAE,KAAK,EAAE,GAAG,EAAK,IAAI,EAAE,KAAK,EAAE;gBAC9C,KAAK,EAAW,EAAE,KAAK,EAAE,GAAG,EAAK,IAAI,EAAE,KAAK,EAAE;aAC/C;SACF;KACF,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAmB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;IAC7D,IAAI,CAAC,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAA;IACrC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;IACzB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;IACvB,IAAI,CAAC,OAAO,GAAG,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAExC,MAAM,KAAK,GAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;IAChD,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC1B,IAAI,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,GAAG,EAAE,CAAA;QACnB,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAClB,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;QACxB,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC7B,MAAM,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAClC,IAAI,CAAC,EAAE,CAAC;YACN,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACf,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;gBAAE,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,IAAU,EACV,MAAiB,EACjB,OAA0B,EAAE,EAC5B,MAA0B,EAAE,eAAe,EAAE,IAAI,EAAE;IAEnD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAA;IAC3B,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,EAAE;QACrC,IAAI,EAAE;YACJ,QAAQ,EAAO,GAAG,CAAC,IAAI,CAAC,QAAQ;YAChC,YAAY,EAAG,GAAG,CAAC,IAAI,CAAC,YAAY;YACpC,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa;YACrC,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa;YACrC,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa;SACtC;QACD,GAAG,EAAE;YACH,QAAQ,EAAI,GAAG,CAAC,GAAG,CAAC,QAAQ;YAC5B,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,UAAU;YAC9B,SAAS,EAAG,GAAG,CAAC,GAAG,CAAC,SAAS;SAC9B;QACD,MAAM,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE;KAC5C,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAA;IACnC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAA;IACpC,MAAM,cAAc,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAA;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAA;IACjC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAA;IACjC,6FAA6F;IAC7F,0EAA0E;IAC1E,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAA;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,CAC1C,SAAS,EACT,IAAI,SAAS,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CACzH,CAAC,KAAK,CAAA;IACP,oFAAoF;IACpF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IACxB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5E,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,KAAK,MAAM,CAAA;IACxC,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,KAAK,OAAO,CAAA;IAC1C,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;IACxE,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9F,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,KAAK,CAAC,CAAA;IACpE,0FAA0F;IAC1F,8EAA8E;IAC9E,MAAM,WAAW,GAAG,CAAC,CAAA;IAErB,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,qBAAqB,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IAC1E,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IACxD,SAAS,CAAC,SAAS,GAAG,QAAQ,CAAA;IAE9B,8FAA8F;IAC9F,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACjC,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC,eAAe,CAAC,CAAA;IAClH,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,eAAe,CAAC,CAAA;IAC7F,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAA;IACrC,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IAChC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC5B,8FAA8F;IAC9F,+FAA+F;IAC/F,kDAAkD;IAClD,MAAM,YAAY,GAAG,IAAI,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IACnH,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IAChC,YAAY,CAAC,IAAI,GAAG,YAAY,CAAA;IAChC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IACxG,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC5B,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAA;IAExB,8FAA8F;IAC9F,MAAM,aAAa,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAA;IAChE,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAA;IAEjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,qBAAqB,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,CAAA;IAC/F,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAA;IAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACrD,MAAM,UAAU,GAAG,IAAI,QAAQ,EAAE;SAC9B,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,YAAY,GAAG,CAAC,EAAE,YAAY,CAAC;SAC9D,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACvC,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;IAElC,6FAA6F;IAC7F,4FAA4F;IAC5F,oEAAoE;IACpE,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE;SAC5B,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;SAC9C,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IACtC,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAA;IACxB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAE5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC;QAC3B,IAAI,EAAE,SAAS;QACf,KAAK,EAAE;YACL,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU;YACxC,QAAQ,EAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI;YAC1C,UAAU,EAAE,KAA4B;YACxC,IAAI,EAAQ,SAAS;SACtB;KACF,CAAC,CAAA;IACF,oGAAoG;IACpG,oCAAoC;IACpC,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAA;IAChG,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAA;IACpF,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC7B,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7B,IAAI,KAAK,EAAE,CAAC;QACV,6FAA6F;QAC7F,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;QAC5D,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAA;QACrF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC,CAAA;QAC7E,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAED,6FAA6F;IAC7F,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC;QAC/B,IAAI,EAAE,SAAS;QACf,KAAK,EAAE;YACL,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU;YACxC,QAAQ,EAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI;YAC1C,UAAU,EAAE,KAA4B;YACxC,IAAI,EAAQ,SAAS;SACtB;KACF,CAAC,CAAA;IACF,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;IACpG,SAAS,CAAC,OAAO,GAAG,KAAK,CAAA;IACzB,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;IAE7B,kGAAkG;IAClG,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC/E,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAA;QACrF,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC,CAAA;QAC7F,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAA;QACxB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC;IAED,sFAAsF;IACtF,2FAA2F;IAC3F,MAAM,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC9B,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAA;IACjC,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,CAAA;IAC9B,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,CAAA;IAC9B,OAAO;SACJ,MAAM,CAAC,CAAC,YAAY,EAAE,CAAC,YAAY,CAAC;SACpC,MAAM,CAAC,CAAC,EAAe,YAAY,CAAC;SACpC,MAAM,CAAE,YAAY,EAAE,CAAC,YAAY,CAAC;SACpC,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;IACxE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAA;IAC1B,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAA;IAC5B,OAAO,CAAC,OAAO,GAAG;QAChB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;KACvE,CAAA;IACD,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;IAE3B,8FAA8F;IAC9F,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAoC,CAAA;IACxE,MAAM,WAAW,GAAe,EAAE,CAAA;IAClC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;QAC1D,IAAI,CAAC,SAAS;YAAE,SAAQ;QACxB,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC5C,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;QAE1D,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAA;QAC9B,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,QAAQ,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;aAChH,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;aACrE,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;QACvF,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;QAC9D,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACnB,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QAErB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC;gBACzB,IAAI,EAAE,GAAG,CAAC,KAAK;gBACf,KAAK,EAAE;oBACL,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU;oBACxC,QAAQ,EAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI;oBACxC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAwB;oBACzE,IAAI,EAAQ,2BAA2B;iBACxC;aACF,CAAC,CAAA;YACF,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;gBACtB,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACzD,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;gBACtB,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACzD,CAAC;YACD,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,6FAA6F;IAC7F,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,CAAA;IAExF,6FAA6F;IAC7F,MAAM,YAAY,GAAG,IAAI,QAAQ,EAAE,CAAA;IACnC,YAAY,CAAC,KAAK,GAAG,CAAC,CAAA;IACtB,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IAEhC,IAAI,WAAW,GAAoB,IAAI,CAAC,KAAK,IAAI,SAAS,CAAA;IAC1D,MAAM,gBAAgB,GAAG,GAAG,EAAE;QAC5B,QAAQ,WAAW,EAAE,CAAC;YACpB,KAAK,OAAO;gBAAK,YAAY,CAAC,IAAI,GAAG,QAAQ,CAAC;gBAAC,YAAY,CAAC,KAAK,GAAG,IAAI,CAAC;gBAAC,MAAK;YAC/E,KAAK,UAAU;gBAAE,YAAY,CAAC,IAAI,GAAG,QAAQ,CAAC;gBAAC,YAAY,CAAC,KAAK,GAAG,IAAI,CAAC;gBAAC,MAAK;YAC/E,KAAK,QAAQ;gBAAI,YAAY,CAAC,IAAI,GAAG,QAAQ,CAAC;gBAAC,YAAY,CAAC,KAAK,GAAG,IAAI,CAAC;gBAAC,MAAK;YAC/E,OAAO,CAAC,CAAS,YAAY,CAAC,KAAK,GAAG,CAAC,CAAA;QACzC,CAAC;IACH,CAAC,CAAA;IAED,8FAA8F;IAC9F,IAAI,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAA;IAEhC,SAAS,kBAAkB,CAAC,SAAgD;QAC1E,IAAI,CAAC,GAAG,CAAC,CAAA;QACT,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACjC,IAAI,CAAC,GAAG;gBAAE,SAAQ;YAClB,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAA;YAC1B,IAAI,CAAC,CAAC;gBAAE,SAAQ;YAChB,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAA;YAC9B,CAAC,CAAC,KAAK,EAAE,CAAA;YACT,aAAa,CAAC,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;iBACvF,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;iBACrE,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;YACvF,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAC9D,CAAC;IACH,CAAC;IAED,SAAS,SAAS,CAAC,CAAU;QAC3B,IAAI,CAAC,EAAE,CAAC;YACN,YAAY,CAAC,OAAO,GAAG,KAAK,CAAA;YAC5B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAA;YACvB,aAAa,CAAC,OAAO,GAAG,KAAK,CAAA;YAC7B,SAAS,CAAC,OAAO,GAAG,IAAI,CAAA;YACxB,IAAI,QAAQ;gBAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAA;YACrC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAA;YACvB,YAAY,CAAC,KAAK,EAAE;iBACjB,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;iBAC9C,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;YAC3D,kBAAkB,CAAC,gBAAgB,CAAC,CAAA;YACpC,OAAO,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAA;YAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,GAAG,KAAK,GAAG,CAAC,CAAC,CAAA;QACnD,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,OAAO,GAAG,IAAI,CAAA;YAC3B,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAA;YACxB,aAAa,CAAC,OAAO,GAAG,IAAI,CAAA;YAC5B,SAAS,CAAC,OAAO,GAAG,KAAK,CAAA;YACzB,IAAI,QAAQ;gBAAE,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAA;YACtC,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAA;YACxB,YAAY,CAAC,KAAK,EAAE;iBACjB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC;iBACrD,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;YAC3D,kBAAkB,CAAC,oBAAoB,CAAC,CAAA;YACxC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;YACpB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;QAC3F,CAAC;QACD,gBAAgB,EAAE,CAAA;IACpB,CAAC;IAED,OAAO,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;QAC9B,SAAS,GAAG,CAAC,SAAS,CAAA;QACtB,SAAS,CAAC,SAAS,CAAC,CAAA;QACpB,CAAC,CAAC,eAAe,EAAE,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,2FAA2F;IAC3F,MAAM,WAAW,GACf,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QACrC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE;YACrC,IAAI,EAAI,EAAE,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE;YAC/C,GAAG,EAAK,EAAE,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE;YACxE,MAAM,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE;YAC3C,MAAM,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE;SAChG,EAAE,IAAI,CAAC,aAAa,CAAC;QACxB,CAAC,CAAC,IAAI,CAAA;IACV,IAAI,WAAW;QAAE,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;IAE9D,SAAS,CAAC,SAAS,CAAC,CAAA;IAEpB,MAAM,IAAI,GAAa;QACrB,SAAS;QACT,cAAc,CAAC,KAAK;YAClB,WAAW,GAAG,KAAK,CAAA;YACnB,gBAAgB,EAAE,CAAA;QACpB,CAAC;QACD,YAAY,CAAC,CAAC,EAAE,SAAS;YACvB,IAAI,CAAC,KAAK,SAAS;gBAAE,OAAM;YAC3B,SAAS,GAAG,CAAC,CAAA;YACb,SAAS,CAAC,SAAS,CAAC,CAAA;QACtB,CAAC;QACD,WAAW,KAAK,OAAO,SAAS,CAAA,CAAC,CAAC;QAClC,gBAAgB,CAAC,KAAK;YACpB,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAA;QACjF,CAAC;QACD,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE;KACtE,CAAA;IACD,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC,EAAoB,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC7F,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE,EAAE,KAAK,EAAQ,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;+DAE+D;AAC/D,MAAM,UAAU,wBAAwB,CACtC,IAAU,EACV,MAAiB,EACjB,OAA0B,EAAE,EAC5B,MAA0B,EAAE,eAAe,EAAE,IAAI,EAAE;IAEnD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAA;IACxC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACf,MAAM,EAAE,GAAG,CAAC,CAAA;IAEZ,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,wBAAwB,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IAC7E,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IACxD,SAAS,CAAC,SAAS,GAAG,QAAQ,CAAA;IAE9B,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,eAAe,CAAC,CAAA;IACnF,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACxB,wFAAwF;IACxF,kFAAkF;IAClF,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IACzE,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC5B,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAA;IAEpB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAA;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAA;IACzD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,CAAA;IAC7D,iGAAiG;IACjG,kGAAkG;IAClG,sFAAsF;IACtF,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE;SACxB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;SACf,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IAChE,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAExB,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE;SACvB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;SACnB,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1D,GAAG,CAAC,KAAK,GAAG,CAAC,CAAA;IACb,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IAEvB,6FAA6F;IAC7F,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoC,CAAA;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IAC5E,CAAC;IAED,OAAO;QACL,SAAS;QACT,cAAc,CAAC,KAAK;YAClB,MAAM,CAAC,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;YAC1C,IAAI,CAAC,CAAC,EAAE,CAAC;gBAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;gBAAC,OAAM;YAAC,CAAC;YACjC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAA;YACZ,GAAG,CAAC,KAAK,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5C,CAAC;QACD,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;QACtB,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;QACxB,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI;KACzD,CAAA;AACH,CAAC;AAED;8DAC8D;AAC9D,MAAM,UAAU,+BAA+B,CAC7C,IAAU,EACV,MAAiB,EACjB,OAA0B,EAAE,EAC5B,MAA0B,EAAE,eAAe,EAAE,IAAI,EAAE;IAEnD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAA;IAC3C,MAAM,CAAC,GAAG,EAAE,CAAA;IACZ,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IAC3D,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;IAEhB,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,6BAA6B,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IAClF,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IACxD,SAAS,CAAC,SAAS,GAAG,QAAQ,CAAA;IAE9B,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,eAAe,CAAC,CAAA;IACxF,IAAI,CAAC,OAAO,GAAG,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACxC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACxB,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;IACvF,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC5B,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAA;IAEpB,kGAAkG;IAClG,+FAA+F;IAC/F,mFAAmF;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAA;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAA;IAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,CAAA;IAC7D,SAAS,CAAC,QAAQ,CAChB,IAAI,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAC3G,CAAA;IAED,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAE;SACvB,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC;SAC3C,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1D,GAAG,CAAC,KAAK,GAAG,CAAC,CAAA;IACb,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IAEvB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoC,CAAA;IAC5D,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAA;IAClD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACzC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAC9C,MAAM,CAAC,GAAG,IAAI,QAAQ,EAAE;aACrB,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC;aACzB,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;aACzD,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;QACnG,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;QAC9D,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IACvB,CAAC;IAED,OAAO;QACL,SAAS;QACT,cAAc,CAAC,KAAK;YAClB,MAAM,CAAC,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;YAC1C,IAAI,CAAC,CAAC,EAAE,CAAC;gBAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;gBAAC,OAAM;YAAC,CAAC;YACjC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAA;YACZ,GAAG,CAAC,KAAK,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5C,CAAC;QACD,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;QACtB,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;QACxB,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI;KACzD,CAAA;AACH,CAAC;AAED;;kGAEkG;AAClG,SAAS,uBAAuB,CAC9B,IAAU,EACV,KAAa,EACb,KAAa,EACb,KAAa,EACb,WAAmB;IAEnB,MAAM,MAAM,GAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAA;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAA;IAC9D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAoC,CAAA;IACvD,MAAM,EAAE,GAAG,WAAW,GAAG,KAAK,GAAG,CAAC,CAAA;IAElC,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,GAAG,GAAG,CAAA;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;IAC/B,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,GAAW,EAAU,EAAE;QACtD,IAAI,KAAK,IAAI,CAAC;YAAE,OAAO,CAAC,CAAA;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;QACzD,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAChC,OAAO,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,CAAA;IAChC,CAAC,CAAA;IAED,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QACpC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAChF,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QACrC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACxF,CAAC,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type XenTokens } from '@xenolithengine/graph-theme-xen';
2
+ export declare const liquidGlassTokens: XenTokens;
3
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,SAAS,EAAoB,MAAM,iCAAiC,CAAA;AA+GzG,eAAO,MAAM,iBAAiB,EAAE,SAAsD,CAAA"}
package/dist/tokens.js ADDED
@@ -0,0 +1,109 @@
1
+ import { xenTokens, mergeTheme } from '@xenolithengine/graph-theme-xen';
2
+ /**
3
+ * Liquid Glass — Apple WWDC25-inspired material. Translucent body fills, luminous white rims
4
+ * and top highlights, neutral cool backdrop. Geometry, typography, and pin/category colours are
5
+ * inherited from Xen so the graph reads identically — only the *material* of each node changes.
6
+ */
7
+ const liquidGlassOverride = {
8
+ category: {
9
+ logic: { accent: '#4D9C75' },
10
+ data: { accent: '#1B76C9' },
11
+ macro: { accent: '#5B3FDA' },
12
+ utility: { accent: '#8291B7' },
13
+ },
14
+ color: {
15
+ surface: {
16
+ // Cool, slightly blue dark canvas — gives the glass a subtle warm/cool play.
17
+ canvas: '#112558',
18
+ // Translucent white — the centrepiece of the look. Reads as frosted card over the canvas.
19
+ node: 'rgba(255, 255, 255, 0.07)',
20
+ panel: 'rgba(255, 255, 255, 0.05)',
21
+ headerEnd: 'rgba(255, 255, 255, 0.18)',
22
+ },
23
+ minimap: {
24
+ background: 'rgba(17, 37, 88, 0.6)',
25
+ border: 'rgba(255, 255, 255, 0.28)',
26
+ node: 'rgba(255, 255, 255, 0.42)',
27
+ frame: 'rgba(154, 214, 227, 0.16)',
28
+ frameBorder: 'rgba(154, 214, 227, 0.9)',
29
+ },
30
+ // Frosted insets over the glass body — mirrors the palette's translucent input fields.
31
+ widget: {
32
+ bg: 'rgba(255, 255, 255, 0.1)',
33
+ bgHover: 'rgba(255, 255, 255, 0.18)',
34
+ bgFocused: 'rgba(17, 37, 88, 0.72)',
35
+ track: 'rgba(255, 255, 255, 0.16)',
36
+ fill: '#9AD6E3',
37
+ fillAlpha: 0.6,
38
+ text: 'rgba(255, 255, 255, 0.95)',
39
+ label: 'rgba(220, 232, 255, 0.65)',
40
+ placeholder: 'rgba(220, 232, 255, 0.4)',
41
+ border: 'rgba(255, 255, 255, 0.24)',
42
+ borderFocused: 'rgba(154, 214, 227, 0.9)',
43
+ selection: 'rgba(154, 214, 227, 0.35)',
44
+ knob: '#FFFFFF',
45
+ },
46
+ },
47
+ // Brighter rims and halos read as light bouncing off glass edges.
48
+ state: {
49
+ hover: {
50
+ border: 'rgba(255, 255, 255, 0.85)',
51
+ borderWidth: 1,
52
+ glow: 'rgba(255, 255, 255, 0.55)',
53
+ glowBlur: 6,
54
+ glowWidth: 3,
55
+ },
56
+ selected: {
57
+ border: 'rgba(255, 255, 255, 1)',
58
+ borderWidth: 1.2,
59
+ glow: 'rgba(255, 255, 255, 0.6)',
60
+ glowBlur: 7,
61
+ glowWidth: 3,
62
+ },
63
+ active: {
64
+ border: 'rgba(255, 223, 103, 0.95)',
65
+ borderWidth: 1.2,
66
+ glow: 'rgba(252, 180, 0, 0.75)',
67
+ glowBlur: 8,
68
+ glowWidth: 3,
69
+ },
70
+ },
71
+ effect: {
72
+ // Stronger top-rim highlight — the signature wet-glass shine.
73
+ headerInnerShadow: {
74
+ offsetX: 0,
75
+ offsetY: 3,
76
+ blur: 6,
77
+ color: 'rgba(255, 255, 255, 0.75)',
78
+ },
79
+ headerHighlightGradient: 'linear-gradient(180deg, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0) 60%)',
80
+ headerHighlightStroke: 'linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 100%)',
81
+ // Drop shadow tuned darker to push the glass surface forward against the cool canvas.
82
+ nodeDropShadow: {
83
+ offsetX: 0,
84
+ offsetY: 10,
85
+ blur: 24,
86
+ color: 'rgba(0, 0, 0, 0.55)',
87
+ },
88
+ },
89
+ geometry: {
90
+ node: {
91
+ // Liquid Glass reads as more "soft" with chunkier corners — Apple's WWDC25 sample buttons
92
+ // use ~50% radius on small pills; for our larger node bodies a moderate bump lands well.
93
+ radius: 14,
94
+ pillRadius: 22,
95
+ },
96
+ },
97
+ background: {
98
+ color: '#112558',
99
+ grid: {
100
+ // Subtler dots — they're meant to read *through* the frosted body, not compete with it.
101
+ kind: 'dots',
102
+ spacing: 24,
103
+ size: 1,
104
+ color: 'rgba(255, 255, 255, 0.18)',
105
+ },
106
+ },
107
+ };
108
+ export const liquidGlassTokens = mergeTheme(xenTokens, liquidGlassOverride);
109
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAoC,MAAM,iCAAiC,CAAA;AAEzG;;;;GAIG;AACH,MAAM,mBAAmB,GAA2B;IAClD,QAAQ,EAAE;QACR,KAAK,EAAI,EAAE,MAAM,EAAE,SAAS,EAAE;QAC9B,IAAI,EAAK,EAAE,MAAM,EAAE,SAAS,EAAE;QAC9B,KAAK,EAAI,EAAE,MAAM,EAAE,SAAS,EAAE;QAC9B,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;KAC/B;IACD,KAAK,EAAE;QACL,OAAO,EAAE;YACP,6EAA6E;YAC7E,MAAM,EAAK,SAAS;YACpB,0FAA0F;YAC1F,IAAI,EAAO,2BAA2B;YACtC,KAAK,EAAM,2BAA2B;YACtC,SAAS,EAAE,2BAA2B;SACvC;QACD,OAAO,EAAE;YACP,UAAU,EAAG,uBAAuB;YACpC,MAAM,EAAO,2BAA2B;YACxC,IAAI,EAAS,2BAA2B;YACxC,KAAK,EAAQ,2BAA2B;YACxC,WAAW,EAAE,0BAA0B;SACxC;QACD,uFAAuF;QACvF,MAAM,EAAE;YACN,EAAE,EAAa,0BAA0B;YACzC,OAAO,EAAQ,2BAA2B;YAC1C,SAAS,EAAM,wBAAwB;YACvC,KAAK,EAAU,2BAA2B;YAC1C,IAAI,EAAW,SAAS;YACxB,SAAS,EAAM,GAAG;YAClB,IAAI,EAAW,2BAA2B;YAC1C,KAAK,EAAU,2BAA2B;YAC1C,WAAW,EAAI,0BAA0B;YACzC,MAAM,EAAS,2BAA2B;YAC1C,aAAa,EAAE,0BAA0B;YACzC,SAAS,EAAM,2BAA2B;YAC1C,IAAI,EAAW,SAAS;SACzB;KACF;IACD,kEAAkE;IAClE,KAAK,EAAE;QACL,KAAK,EAAE;YACL,MAAM,EAAO,2BAA2B;YACxC,WAAW,EAAE,CAAC;YACd,IAAI,EAAS,2BAA2B;YACxC,QAAQ,EAAK,CAAC;YACd,SAAS,EAAI,CAAC;SACf;QACD,QAAQ,EAAE;YACR,MAAM,EAAO,wBAAwB;YACrC,WAAW,EAAE,GAAG;YAChB,IAAI,EAAS,0BAA0B;YACvC,QAAQ,EAAK,CAAC;YACd,SAAS,EAAI,CAAC;SACf;QACD,MAAM,EAAE;YACN,MAAM,EAAO,2BAA2B;YACxC,WAAW,EAAE,GAAG;YAChB,IAAI,EAAS,yBAAyB;YACtC,QAAQ,EAAK,CAAC;YACd,SAAS,EAAI,CAAC;SACf;KACF;IACD,MAAM,EAAE;QACN,8DAA8D;QAC9D,iBAAiB,EAAE;YACjB,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;YACV,IAAI,EAAK,CAAC;YACV,KAAK,EAAI,2BAA2B;SACrC;QACD,uBAAuB,EACrB,mFAAmF;QACrF,qBAAqB,EACnB,mFAAmF;QACrF,sFAAsF;QACtF,cAAc,EAAE;YACd,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,EAAE;YACX,IAAI,EAAK,EAAE;YACX,KAAK,EAAI,qBAAqB;SAC/B;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE;YACJ,0FAA0F;YAC1F,yFAAyF;YACzF,MAAM,EAAM,EAAE;YACd,UAAU,EAAE,EAAE;SACf;KACF;IACD,UAAU,EAAE;QACV,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE;YACJ,wFAAwF;YACxF,IAAI,EAAK,MAAM;YACf,OAAO,EAAE,EAAE;YACX,IAAI,EAAK,CAAC;YACV,KAAK,EAAI,2BAA2B;SACrC;KACF;CACF,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAc,UAAU,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@xenolithengine/graph-theme-liquid-glass",
3
+ "version": "0.7.0-beta.0",
4
+ "description": "Liquid Glass theme for XenolithGraph — Apple WWDC25-inspired translucent material.",
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
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "sideEffects": false,
21
+ "dependencies": {
22
+ "@xenolithengine/graph-core": "0.7.0-beta.0",
23
+ "@xenolithengine/graph-render-pixi": "0.7.0-beta.0",
24
+ "@xenolithengine/graph-theme-xen": "0.7.0-beta.0"
25
+ },
26
+ "peerDependencies": {
27
+ "pixi.js": "^8.6.0"
28
+ },
29
+ "devDependencies": {
30
+ "pixi.js": "^8.6.0",
31
+ "typescript": "^5.7.0",
32
+ "vitest": "^3.0.0"
33
+ },
34
+ "keywords": [
35
+ "theme",
36
+ "glass",
37
+ "frosted",
38
+ "node-editor",
39
+ "liquid-glass"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/XenolithEngine/xenolith-graph.git",
47
+ "directory": "packages/theme-liquid-glass"
48
+ },
49
+ "homepage": "https://xenolithengine.github.io/xenolith-graph/",
50
+ "bugs": {
51
+ "url": "https://github.com/XenolithEngine/xenolith-graph/issues"
52
+ },
53
+ "author": "XenolithEngine",
54
+ "scripts": {
55
+ "build": "tsc -b",
56
+ "dev": "tsc -b --watch",
57
+ "test": "vitest run --passWithNoTests",
58
+ "lint": "eslint src",
59
+ "typecheck": "tsc --noEmit"
60
+ }
61
+ }