@steffolino/diagram-kit-core 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @steffolino/diagram-kit-core
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@steffolino/diagram-kit-core.svg)](https://www.npmjs.com/package/@steffolino/diagram-kit-core)
4
+ [![license](https://img.shields.io/npm/l/@steffolino/diagram-kit-core.svg)](../../LICENSE)
5
+
3
6
  Normalized `{ nodes, edges }` graph model, `dagre`-based auto-layout, and
4
7
  light/dark theme tokens shared by every diagram-kit renderer and adapter.
5
8
  Framework-agnostic — no React, no DOM.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steffolino/diagram-kit-core",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Normalized graph model, auto-layout, and theme tokens shared by every diagram-kit renderer and adapter.",
5
5
  "license": "MIT",
6
6
  "author": "Stefan Stretz",
@@ -14,6 +14,9 @@
14
14
  "publishConfig": {
15
15
  "access": "public"
16
16
  },
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
17
20
  "keywords": [
18
21
  "diagram",
19
22
  "graph",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=graph.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph.test.d.ts","sourceRoot":"","sources":["../src/graph.test.ts"],"names":[],"mappings":""}
@@ -1,33 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createGraph, validateGraph, GraphValidationError } from './graph.js';
3
- describe('createGraph', () => {
4
- it('defaults to empty nodes/edges', () => {
5
- const graph = createGraph();
6
- expect(graph.nodes).toEqual([]);
7
- expect(graph.edges).toEqual([]);
8
- });
9
- it('carries through provided nodes/edges/metadata', () => {
10
- const graph = createGraph([{ id: 'a', label: 'A' }], [], { source: 'test' });
11
- expect(graph.nodes).toHaveLength(1);
12
- expect(graph.metadata).toEqual({ source: 'test' });
13
- });
14
- });
15
- describe('validateGraph', () => {
16
- it('accepts a graph with consistent nodes/edges', () => {
17
- const graph = createGraph([{ id: 'a', label: 'A' }, { id: 'b', label: 'B' }], [{ id: 'a->b', source: 'a', target: 'b' }]);
18
- expect(() => validateGraph(graph)).not.toThrow();
19
- });
20
- it('rejects duplicate node ids', () => {
21
- const graph = createGraph([{ id: 'a', label: 'A' }, { id: 'a', label: 'A2' }]);
22
- expect(() => validateGraph(graph)).toThrow(GraphValidationError);
23
- });
24
- it('rejects an edge referencing a missing source node', () => {
25
- const graph = createGraph([{ id: 'b', label: 'B' }], [{ id: 'a->b', source: 'a', target: 'b' }]);
26
- expect(() => validateGraph(graph)).toThrow(/missing source node "a"/);
27
- });
28
- it('rejects an edge referencing a missing target node', () => {
29
- const graph = createGraph([{ id: 'a', label: 'A' }], [{ id: 'a->b', source: 'a', target: 'b' }]);
30
- expect(() => validateGraph(graph)).toThrow(/missing target node "b"/);
31
- });
32
- });
33
- //# sourceMappingURL=graph.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph.test.js","sourceRoot":"","sources":["../src/graph.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE7E,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,KAAK,GAAG,WAAW,EAAE,CAAA;QAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;QAC5E,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QACnC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,WAAW,CACvB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAClD,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAC3C,CAAA;QACD,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAA;IAClD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QAC9E,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAClE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;QAChG,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAA;IACvE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;QAChG,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAA;IACvE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=layout.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"layout.test.d.ts","sourceRoot":"","sources":["../src/layout.test.ts"],"names":[],"mappings":""}
@@ -1,72 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createGraph } from './graph.js';
3
- import { layoutGraph } from './layout.js';
4
- describe('layoutGraph', () => {
5
- it('positions every node and preserves edge count', () => {
6
- const graph = createGraph([
7
- { id: 'a', label: 'A' },
8
- { id: 'b', label: 'B' },
9
- { id: 'c', label: 'C' },
10
- ], [
11
- { id: 'a->b', source: 'a', target: 'b' },
12
- { id: 'b->c', source: 'b', target: 'c' },
13
- ]);
14
- const positioned = layoutGraph(graph);
15
- expect(positioned.nodes).toHaveLength(3);
16
- expect(positioned.edges).toHaveLength(2);
17
- for (const node of positioned.nodes) {
18
- expect(Number.isFinite(node.x)).toBe(true);
19
- expect(Number.isFinite(node.y)).toBe(true);
20
- expect(node.width).toBeGreaterThan(0);
21
- expect(node.height).toBeGreaterThan(0);
22
- }
23
- expect(positioned.width).toBeGreaterThan(0);
24
- expect(positioned.height).toBeGreaterThan(0);
25
- });
26
- it('routes a top-to-bottom layout with later ranks below earlier ones', () => {
27
- const graph = createGraph([
28
- { id: 'a', label: 'A' },
29
- { id: 'b', label: 'B' },
30
- ], [{ id: 'a->b', source: 'a', target: 'b' }]);
31
- const positioned = layoutGraph(graph, { direction: 'TB' });
32
- const a = positioned.nodes.find((n) => n.id === 'a');
33
- const b = positioned.nodes.find((n) => n.id === 'b');
34
- expect(b.y).toBeGreaterThan(a.y);
35
- });
36
- it('handles a graph with no edges', () => {
37
- const graph = createGraph([{ id: 'solo', label: 'Solo' }]);
38
- const positioned = layoutGraph(graph);
39
- expect(positioned.nodes).toHaveLength(1);
40
- expect(positioned.edges).toHaveLength(0);
41
- });
42
- it('grows node width to fit a long label beyond the default minimum', () => {
43
- const graph = createGraph([
44
- { id: 'short', label: 'A' },
45
- { id: 'long', label: 'A Much Much Longer Node Label Than The Default Width' },
46
- ]);
47
- const positioned = layoutGraph(graph);
48
- const short = positioned.nodes.find((n) => n.id === 'short');
49
- const long = positioned.nodes.find((n) => n.id === 'long');
50
- expect(long.width).toBeGreaterThan(short.width);
51
- });
52
- it('clamps node width to nodeWidth in "fixed" sizing mode regardless of label length', () => {
53
- const graph = createGraph([
54
- { id: 'long', label: 'A Much Much Longer Node Label Than The Default Width' },
55
- ]);
56
- const positioned = layoutGraph(graph, { nodeSizing: 'fixed', nodeWidth: 160 });
57
- expect(positioned.nodes[0].width).toBe(160);
58
- });
59
- it('supports multiple parallel edges between the same two nodes (multigraph)', () => {
60
- const graph = createGraph([
61
- { id: 'a', label: 'A' },
62
- { id: 'b', label: 'B' },
63
- ], [
64
- { id: 'edge1', source: 'a', target: 'b' },
65
- { id: 'edge2', source: 'a', target: 'b' },
66
- ]);
67
- expect(() => layoutGraph(graph)).not.toThrow();
68
- const positioned = layoutGraph(graph);
69
- expect(positioned.edges).toHaveLength(2);
70
- });
71
- });
72
- //# sourceMappingURL=layout.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"layout.test.js","sourceRoot":"","sources":["../src/layout.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=stackLayout.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stackLayout.test.d.ts","sourceRoot":"","sources":["../src/stackLayout.test.ts"],"names":[],"mappings":""}
@@ -1,48 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createGraph } from './graph.js';
3
- import { layoutStack } from './stackLayout.js';
4
- describe('layoutStack', () => {
5
- it('produces no edges', () => {
6
- const graph = createGraph([
7
- { id: 'a', label: 'A' },
8
- { id: 'b', label: 'B' },
9
- ], [{ id: 'x', source: 'a', target: 'b' }]);
10
- expect(layoutStack(graph).edges).toEqual([]);
11
- });
12
- it('stacks nodes vertically in graph order with increasing y', () => {
13
- const graph = createGraph([
14
- { id: 'a', label: 'A' },
15
- { id: 'b', label: 'B' },
16
- { id: 'c', label: 'C' },
17
- ]);
18
- const positioned = layoutStack(graph, { direction: 'vertical' });
19
- const [a, b, c] = graph.nodes.map((n) => positioned.nodes.find((p) => p.id === n.id));
20
- expect(b.y).toBeGreaterThan(a.y);
21
- expect(c.y).toBeGreaterThan(b.y);
22
- });
23
- it('lays out nodes horizontally with increasing x in horizontal direction', () => {
24
- const graph = createGraph([
25
- { id: 'a', label: 'A' },
26
- { id: 'b', label: 'B' },
27
- ]);
28
- const positioned = layoutStack(graph, { direction: 'horizontal' });
29
- const a = positioned.nodes.find((n) => n.id === 'a');
30
- const b = positioned.nodes.find((n) => n.id === 'b');
31
- expect(b.x).toBeGreaterThan(a.x);
32
- });
33
- it('grows node width to fit a long label in responsive mode', () => {
34
- const graph = createGraph([{ id: 'long', label: 'A Much Much Longer Node Label Than Default' }]);
35
- const positioned = layoutStack(graph);
36
- expect(positioned.nodes[0].width).toBeGreaterThan(200);
37
- });
38
- it('clamps node width in fixed sizing mode', () => {
39
- const graph = createGraph([{ id: 'long', label: 'A Much Much Longer Node Label Than Default' }]);
40
- const positioned = layoutStack(graph, { nodeSizing: 'fixed', nodeWidth: 200 });
41
- expect(positioned.nodes[0].width).toBe(200);
42
- });
43
- it('handles an empty graph without throwing', () => {
44
- const graph = createGraph([]);
45
- expect(() => layoutStack(graph)).not.toThrow();
46
- });
47
- });
48
- //# sourceMappingURL=stackLayout.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stackLayout.test.js","sourceRoot":"","sources":["../src/stackLayout.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=structuralLayout.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"structuralLayout.test.d.ts","sourceRoot":"","sources":["../src/structuralLayout.test.ts"],"names":[],"mappings":""}
@@ -1,74 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createGraph } from './graph.js';
3
- import { layoutStructural } from './structuralLayout.js';
4
- describe('layoutStructural', () => {
5
- it('produces no edges regardless of the input graph having any', () => {
6
- const graph = createGraph([
7
- { id: 'parent', label: 'Parent' },
8
- { id: 'child', label: 'Child', parentId: 'parent' },
9
- ], [{ id: 'x', source: 'parent', target: 'child' }]);
10
- const positioned = layoutStructural(graph);
11
- expect(positioned.edges).toEqual([]);
12
- });
13
- it('sizes a container to enclose its children', () => {
14
- const graph = createGraph([
15
- { id: 'parent', label: 'Parent' },
16
- { id: 'a', label: 'A', parentId: 'parent' },
17
- { id: 'b', label: 'B', parentId: 'parent' },
18
- ]);
19
- const positioned = layoutStructural(graph);
20
- const parent = positioned.nodes.find((n) => n.id === 'parent');
21
- const a = positioned.nodes.find((n) => n.id === 'a');
22
- const b = positioned.nodes.find((n) => n.id === 'b');
23
- const parentLeft = parent.x - parent.width / 2;
24
- const parentRight = parent.x + parent.width / 2;
25
- const parentTop = parent.y - parent.height / 2;
26
- const parentBottom = parent.y + parent.height / 2;
27
- for (const child of [a, b]) {
28
- expect(child.x - child.width / 2).toBeGreaterThanOrEqual(parentLeft);
29
- expect(child.x + child.width / 2).toBeLessThanOrEqual(parentRight);
30
- expect(child.y - child.height / 2).toBeGreaterThanOrEqual(parentTop);
31
- expect(child.y + child.height / 2).toBeLessThanOrEqual(parentBottom);
32
- }
33
- });
34
- it('wraps children onto a new row once maxRowWidth is exceeded', () => {
35
- const graph = createGraph([
36
- { id: 'parent', label: 'Parent' },
37
- { id: 'a', label: 'A', parentId: 'parent' },
38
- { id: 'b', label: 'B', parentId: 'parent' },
39
- { id: 'c', label: 'C', parentId: 'parent' },
40
- ]);
41
- const positioned = layoutStructural(graph, { leafWidth: 100, maxRowWidth: 250, gap: 10 });
42
- const a = positioned.nodes.find((n) => n.id === 'a');
43
- const c = positioned.nodes.find((n) => n.id === 'c');
44
- // 3 leaves at 100 + 2 gaps of 10 = 320 > maxRowWidth 250, so the third
45
- // leaf should wrap onto a second row (different y than the first).
46
- expect(c.y).toBeGreaterThan(a.y);
47
- });
48
- it('sizes a leaf wide enough for a long label', () => {
49
- const graph = createGraph([
50
- { id: 'short', label: 'A' },
51
- { id: 'long', label: 'A Much Much Longer Node Label' },
52
- ]);
53
- const positioned = layoutStructural(graph);
54
- const short = positioned.nodes.find((n) => n.id === 'short');
55
- const long = positioned.nodes.find((n) => n.id === 'long');
56
- expect(long.width).toBeGreaterThan(short.width);
57
- });
58
- it('clamps leaf width in "fixed" sizing mode', () => {
59
- const graph = createGraph([{ id: 'long', label: 'A Much Much Longer Node Label Than Default' }]);
60
- const positioned = layoutStructural(graph, { nodeSizing: 'fixed', leafWidth: 140 });
61
- expect(positioned.nodes[0].width).toBe(140);
62
- });
63
- it('places multiple root nodes side by side without overlapping', () => {
64
- const graph = createGraph([
65
- { id: 'root1', label: 'Root 1' },
66
- { id: 'root2', label: 'Root 2' },
67
- ]);
68
- const positioned = layoutStructural(graph);
69
- const root1 = positioned.nodes.find((n) => n.id === 'root1');
70
- const root2 = positioned.nodes.find((n) => n.id === 'root2');
71
- expect(root2.x - root2.width / 2).toBeGreaterThanOrEqual(root1.x + root1.width / 2);
72
- });
73
- });
74
- //# sourceMappingURL=structuralLayout.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"structuralLayout.test.js","sourceRoot":"","sources":["../src/structuralLayout.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExD,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,KAAK,GAAG,WAAW,CACvB;YACE,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;YACjC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE;SACpD,EACD,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CACjD,CAAA;QACD,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC1C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,KAAK,GAAG,WAAW,CAAC;YACxB,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;YACjC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;YAC3C,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;SAC5C,CAAC,CAAA;QACF,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAE,CAAA;QAC/D,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAE,CAAA;QACrD,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAE,CAAA;QAErD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAA;QAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAA;QAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;QAC9C,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;QAEjD,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAA;YACpE,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAA;YAClE,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAA;YACpE,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAA;QACtE,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,KAAK,GAAG,WAAW,CAAC;YACxB,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;YACjC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;YAC3C,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;YAC3C,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;SAC5C,CAAC,CAAA;QACF,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAA;QACzF,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAE,CAAA;QACrD,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAE,CAAA;QACrD,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,KAAK,GAAG,WAAW,CAAC;YACxB,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE;YAC3B,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,+BAA+B,EAAE;SACvD,CAAC,CAAA;QACF,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAE,CAAA;QAC7D,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAE,CAAA;QAC3D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC,CAAC,CAAA;QAChG,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAA;QACnF,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,KAAK,GAAG,WAAW,CAAC;YACxB,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE;YAChC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE;SACjC,CAAC,CAAA;QACF,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAE,CAAA;QAC7D,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAE,CAAA;QAC7D,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;IACrF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}