@principal-ai/subsystems-react 0.17.0 → 0.18.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/dist/index.d.ts +7 -44
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -32
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +9 -199
- package/src/components/ConfigurationSelector.tsx +0 -133
- package/src/components/GraphRenderer.tsx +0 -3195
- package/src/components/MultiCanvasRenderer.tsx +0 -295
- package/src/components/NodeTooltip.tsx +0 -279
- package/src/components/PendingChanges.test.tsx +0 -375
- package/src/components/SelectionSidebar.tsx +0 -352
- package/src/components/SequenceDiagramRenderer.tsx +0 -1156
- package/src/components/WorkflowSequenceDiagram.tsx +0 -270
- package/src/components/dashboard/DashboardRenderer.tsx +0 -317
- package/src/components/dashboard/MetricPanel.tsx +0 -254
- package/src/components/dashboard/MockDataProvider.ts +0 -330
- package/src/components/dashboard/components/BarChart.tsx +0 -299
- package/src/components/dashboard/components/LineChart.tsx +0 -279
- package/src/components/dashboard/components/MetricCard.tsx +0 -270
- package/src/components/dashboard/components/SourceLink.tsx +0 -63
- package/src/components/dashboard/components/TimeRangeSelector.tsx +0 -280
- package/src/components/dashboard/components/index.ts +0 -5
- package/src/components/dashboard/index.ts +0 -47
- package/src/components/dashboard/types.ts +0 -126
- package/src/components/state-view/PipelineView.tsx +0 -347
- package/src/components/state-view/index.ts +0 -14
- package/src/components/state-view/types.ts +0 -261
- package/src/components/state-view/useStateView.ts +0 -205
- package/src/contexts/GraphEditContext.tsx +0 -27
- package/src/contexts/TooltipPortalContext.ts +0 -8
- package/src/edges/CustomEdge.tsx +0 -402
- package/src/edges/GenericEdge.tsx +0 -36
- package/src/hooks/usePathBasedEvents.ts +0 -192
- package/src/hooks/useSequenceLayout.ts +0 -543
- package/src/hooks/useUndoRedo.ts +0 -144
- package/src/nodes/CustomNode.tsx +0 -1231
- package/src/nodes/GenericNode.tsx +0 -55
- package/src/nodes/otel/OtelBoundaryNode.tsx +0 -224
- package/src/nodes/otel/OtelEventNode.tsx +0 -225
- package/src/nodes/otel/OtelResourceNode.tsx +0 -251
- package/src/nodes/otel/OtelScopeNode.tsx +0 -221
- package/src/nodes/otel/OtelSpanConventionNode.tsx +0 -295
- package/src/nodes/otel/index.ts +0 -23
- package/src/nodes/otel/shared/NodeBadges.tsx +0 -295
- package/src/nodes/otel/shared/NodeContent.tsx +0 -204
- package/src/nodes/otel/shared/index.ts +0 -8
- package/src/nodes/otel/shared/types.ts +0 -138
- package/src/nodes/otel/shared/useNodeBehavior.ts +0 -114
- package/src/stories/Audit/BoundaryNodes.stories.tsx +0 -385
- package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +0 -1448
- package/src/stories/Audit/CanvasNodeTypes.stories.tsx +0 -1049
- package/src/stories/Audit/ColorPriority.stories.tsx +0 -895
- package/src/stories/Audit/DiamondBadges.stories.tsx +0 -629
- package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +0 -724
- package/src/stories/Audit/NodeFieldsAudit.stories.tsx +0 -996
- package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +0 -715
- package/src/stories/Audit/NodeShapes.stories.tsx +0 -1208
- package/src/stories/Audit/WideNodesTesting.stories.tsx +0 -1045
- package/src/stories/Components/DashboardRenderer.stories.tsx +0 -263
- package/src/stories/Components/GraphRenderer.stories.tsx +0 -3236
- package/src/stories/Components/MultiCanvasRenderer.stories.tsx +0 -370
- package/src/stories/Components/SequenceDiagram.stories.tsx +0 -255
- package/src/stories/Components/StateView.stories.tsx +0 -417
- package/src/stories/Components/TraceViewer.stories.tsx +0 -442
- package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +0 -802
- package/src/stories/Features/AnimationWorkshop.stories.tsx +0 -727
- package/src/stories/Features/ChangeTypeVisual.stories.tsx +0 -67
- package/src/stories/Features/EventDrivenAnimations.stories.tsx +0 -513
- package/src/stories/Features/MultiConfig.stories.tsx +0 -539
- package/src/stories/Layout/ElkEdgeRouting.stories.tsx +0 -539
- package/src/stories/Layout/GraphOrientation.stories.tsx +0 -500
- package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +0 -352
- package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +0 -560
- package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +0 -491
- package/src/stories/Nodes/NumericColors.stories.tsx +0 -440
- package/src/stories/OTEL/EventNameDisplay.stories.tsx +0 -694
- package/src/stories/OTEL/EventNameFallback.stories.tsx +0 -578
- package/src/stories/OTEL/OtelComponents.stories.tsx +0 -422
- package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +0 -806
- package/src/stories/OTEL/RawEventCards.stories.tsx +0 -57
- package/src/stories/OTEL/SpanBadges.stories.tsx +0 -840
- package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +0 -209
- package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +0 -721
- package/src/stories/RealWorld/FileCitySequence.stories.tsx +0 -516
- package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +0 -336
- package/src/stories/__traces__/test-run.canvas.json +0 -241
- package/src/stories/data/activity-feed-analytics.dashboard.json +0 -300
- package/src/stories/data/file-city-images-transformed.otel.canvas.json +0 -295
- package/src/stories/data/file-city-workflows.json +0 -269
- package/src/stories/data/graph-converter-test-execution.json +0 -443
- package/src/stories/data/graph-converter-test.workflow.json +0 -105
- package/src/stories/data/graph-converter-validated-execution.json +0 -58
- package/src/utils/animationMapping.ts +0 -166
- package/src/utils/canvasBounds.ts +0 -146
- package/src/utils/graphConverter.ts +0 -148
- package/src/utils/iconResolver.tsx +0 -53
- package/src/utils/orientationUtils.ts +0 -73
|
@@ -1,895 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import { GraphRenderer } from '../../components/GraphRenderer';
|
|
4
|
-
import type { ExtendedCanvas, GraphEvent, ComponentLibrary } from '@principal-ai/subsystems-core';
|
|
5
|
-
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
6
|
-
|
|
7
|
-
// Helper component that sets initial node states via events
|
|
8
|
-
const GraphWithInitialStates: React.FC<{
|
|
9
|
-
canvas: ExtendedCanvas;
|
|
10
|
-
width: number;
|
|
11
|
-
height: number;
|
|
12
|
-
showMinimap: boolean;
|
|
13
|
-
initialStates?: Record<string, string>;
|
|
14
|
-
}> = ({ canvas, width, height, showMinimap, initialStates }) => {
|
|
15
|
-
const [events, setEvents] = useState<GraphEvent[]>([]);
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (initialStates) {
|
|
19
|
-
const stateEvents: GraphEvent[] = Object.entries(initialStates).map(
|
|
20
|
-
([nodeId, newState], idx) => ({
|
|
21
|
-
id: `init-state-${idx}`,
|
|
22
|
-
type: 'state_changed',
|
|
23
|
-
timestamp: Date.now(),
|
|
24
|
-
category: 'state' as const,
|
|
25
|
-
payload: { nodeId, newState },
|
|
26
|
-
})
|
|
27
|
-
);
|
|
28
|
-
setEvents(stateEvents);
|
|
29
|
-
}
|
|
30
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally run only on mount
|
|
31
|
-
}, []);
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<GraphRenderer
|
|
35
|
-
canvas={canvas}
|
|
36
|
-
width={width}
|
|
37
|
-
height={height}
|
|
38
|
-
showMinimap={showMinimap}
|
|
39
|
-
events={events}
|
|
40
|
-
/>
|
|
41
|
-
);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const meta = {
|
|
45
|
-
title: 'Audit/ColorPriority',
|
|
46
|
-
component: GraphRenderer,
|
|
47
|
-
parameters: {
|
|
48
|
-
layout: 'centered',
|
|
49
|
-
},
|
|
50
|
-
tags: ['autodocs'],
|
|
51
|
-
decorators: [
|
|
52
|
-
(Story) => (
|
|
53
|
-
<ThemeProvider theme={defaultEditorTheme}>
|
|
54
|
-
<Story />
|
|
55
|
-
</ThemeProvider>
|
|
56
|
-
),
|
|
57
|
-
],
|
|
58
|
-
} satisfies Meta<typeof GraphRenderer>;
|
|
59
|
-
|
|
60
|
-
export default meta;
|
|
61
|
-
type Story = StoryObj<typeof meta>;
|
|
62
|
-
|
|
63
|
-
// ============================================================================
|
|
64
|
-
// Node Color Priority Story
|
|
65
|
-
// Priority: state color > node data color (pv.fill) > node.color > type definition color > default (#888)
|
|
66
|
-
// ============================================================================
|
|
67
|
-
|
|
68
|
-
const nodeColorPriorityCanvas: ExtendedCanvas = {
|
|
69
|
-
nodes: [
|
|
70
|
-
// 1. Default color only (no color specified anywhere)
|
|
71
|
-
{
|
|
72
|
-
id: 'default-only',
|
|
73
|
-
type: 'text',
|
|
74
|
-
x: 100,
|
|
75
|
-
y: 100,
|
|
76
|
-
width: 180,
|
|
77
|
-
height: 100,
|
|
78
|
-
text: 'SOURCE: Default\nEXPECT: Gray',
|
|
79
|
-
pv: {
|
|
80
|
-
nodeType: 'default-demo',
|
|
81
|
-
shape: 'rectangle',
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
// 2. Type definition color via node.color
|
|
85
|
-
{
|
|
86
|
-
id: 'node-color',
|
|
87
|
-
type: 'text',
|
|
88
|
-
x: 320,
|
|
89
|
-
y: 100,
|
|
90
|
-
width: 180,
|
|
91
|
-
height: 100,
|
|
92
|
-
text: 'SOURCE: node.color\nEXPECT: BLUE',
|
|
93
|
-
color: '#0000FF',
|
|
94
|
-
pv: {
|
|
95
|
-
nodeType: 'color-demo',
|
|
96
|
-
shape: 'rectangle',
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
// 3. pv.fill overrides node.color
|
|
100
|
-
{
|
|
101
|
-
id: 'pv-fill',
|
|
102
|
-
type: 'text',
|
|
103
|
-
x: 540,
|
|
104
|
-
y: 100,
|
|
105
|
-
width: 180,
|
|
106
|
-
height: 100,
|
|
107
|
-
text: 'SOURCE: pv.fill\nEXPECT: GREEN\n(node.color=BLUE ignored)',
|
|
108
|
-
color: '#0000FF', // This should be overridden
|
|
109
|
-
pv: {
|
|
110
|
-
nodeType: 'fill-demo',
|
|
111
|
-
shape: 'rectangle',
|
|
112
|
-
fill: '#00FF00', // This wins
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
// 4. State color overrides everything (idle state)
|
|
116
|
-
{
|
|
117
|
-
id: 'state-color-idle',
|
|
118
|
-
type: 'text',
|
|
119
|
-
x: 760,
|
|
120
|
-
y: 100,
|
|
121
|
-
width: 180,
|
|
122
|
-
height: 100,
|
|
123
|
-
text: 'SOURCE: state.idle.color\nEXPECT: RED\n(pv.fill & node.color ignored)',
|
|
124
|
-
color: '#0000FF', // Overridden
|
|
125
|
-
pv: {
|
|
126
|
-
nodeType: 'state-demo',
|
|
127
|
-
shape: 'rectangle',
|
|
128
|
-
fill: '#00FF00', // Overridden
|
|
129
|
-
states: {
|
|
130
|
-
idle: { label: 'Idle', color: '#FF0000' },
|
|
131
|
-
active: { label: 'Active', color: '#FFFF00' },
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
edges: [],
|
|
137
|
-
pv: {
|
|
138
|
-
version: '1.0.0',
|
|
139
|
-
name: 'Node Color Priority Demo',
|
|
140
|
-
description: 'Shows how node colors are prioritized',
|
|
141
|
-
edgeTypes: {},
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export const NodeColorPriority: Story = {
|
|
146
|
-
render: () => (
|
|
147
|
-
<GraphWithInitialStates
|
|
148
|
-
canvas={nodeColorPriorityCanvas}
|
|
149
|
-
width={1050}
|
|
150
|
-
height={320}
|
|
151
|
-
showMinimap={false}
|
|
152
|
-
initialStates={{ 'state-color-idle': 'idle' }}
|
|
153
|
-
/>
|
|
154
|
-
),
|
|
155
|
-
parameters: {
|
|
156
|
-
docs: {
|
|
157
|
-
description: {
|
|
158
|
-
story: `
|
|
159
|
-
**Node Fill Color Priority** (highest to lowest):
|
|
160
|
-
|
|
161
|
-
1. **State color** - When a node has an active state with a color defined
|
|
162
|
-
2. **pv.fill** - Explicit fill color in the PV extension
|
|
163
|
-
3. **node.color** - The standard canvas node color property
|
|
164
|
-
4. **Type definition color** - Color defined in the node type
|
|
165
|
-
5. **Default** - Falls back to #888
|
|
166
|
-
|
|
167
|
-
In this demo:
|
|
168
|
-
- Node 1: No color specified → uses default #888
|
|
169
|
-
- Node 2: Only node.color → shows blue
|
|
170
|
-
- Node 3: Both node.color AND pv.fill → pv.fill (green) wins
|
|
171
|
-
- Node 4: Has state "idle" with purple color → state color wins over everything
|
|
172
|
-
`,
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
// ============================================================================
|
|
179
|
-
// Node Stroke Color Priority Story
|
|
180
|
-
// Priority: pv.stroke > type definition stroke > fill color
|
|
181
|
-
// ============================================================================
|
|
182
|
-
|
|
183
|
-
const strokeColorPriorityCanvas: ExtendedCanvas = {
|
|
184
|
-
nodes: [
|
|
185
|
-
// 1. Stroke defaults to fill color
|
|
186
|
-
{
|
|
187
|
-
id: 'stroke-default',
|
|
188
|
-
type: 'text',
|
|
189
|
-
x: 100,
|
|
190
|
-
y: 100,
|
|
191
|
-
width: 200,
|
|
192
|
-
height: 100,
|
|
193
|
-
text: 'SOURCE: Default (=fill)\nEXPECT BORDER: BLUE\n(same as fill color)',
|
|
194
|
-
color: '#0000FF',
|
|
195
|
-
pv: {
|
|
196
|
-
nodeType: 'stroke-default-demo',
|
|
197
|
-
shape: 'rectangle',
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
// 2. Explicit stroke color
|
|
201
|
-
{
|
|
202
|
-
id: 'stroke-explicit',
|
|
203
|
-
type: 'text',
|
|
204
|
-
x: 340,
|
|
205
|
-
y: 100,
|
|
206
|
-
width: 200,
|
|
207
|
-
height: 100,
|
|
208
|
-
text: 'SOURCE: pv.stroke\nEXPECT BORDER: RED\n(fill is BLUE)',
|
|
209
|
-
color: '#0000FF', // Fill is blue
|
|
210
|
-
pv: {
|
|
211
|
-
nodeType: 'stroke-explicit-demo',
|
|
212
|
-
shape: 'rectangle',
|
|
213
|
-
stroke: '#FF0000', // But stroke is red
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
// 3. Different fill and stroke
|
|
217
|
-
{
|
|
218
|
-
id: 'fill-and-stroke',
|
|
219
|
-
type: 'text',
|
|
220
|
-
x: 580,
|
|
221
|
-
y: 100,
|
|
222
|
-
width: 200,
|
|
223
|
-
height: 100,
|
|
224
|
-
text: 'SOURCE: pv.fill + pv.stroke\nEXPECT BORDER: YELLOW\nEXPECT FILL: GREEN',
|
|
225
|
-
pv: {
|
|
226
|
-
nodeType: 'both-demo',
|
|
227
|
-
shape: 'rectangle',
|
|
228
|
-
fill: '#00FF00',
|
|
229
|
-
stroke: '#FFFF00',
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
],
|
|
233
|
-
edges: [],
|
|
234
|
-
pv: {
|
|
235
|
-
version: '1.0.0',
|
|
236
|
-
name: 'Stroke Color Priority Demo',
|
|
237
|
-
description: 'Shows how stroke colors are prioritized',
|
|
238
|
-
edgeTypes: {},
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
export const StrokeColorPriority: Story = {
|
|
243
|
-
args: {
|
|
244
|
-
canvas: strokeColorPriorityCanvas,
|
|
245
|
-
width: 880,
|
|
246
|
-
height: 320,
|
|
247
|
-
showMinimap: false,
|
|
248
|
-
},
|
|
249
|
-
parameters: {
|
|
250
|
-
docs: {
|
|
251
|
-
description: {
|
|
252
|
-
story: `
|
|
253
|
-
**Node Stroke Color Priority** (highest to lowest):
|
|
254
|
-
|
|
255
|
-
1. **pv.stroke** - Explicit stroke color in the PV extension
|
|
256
|
-
2. **Type definition stroke** - Stroke defined in node type
|
|
257
|
-
3. **Fill color** - Falls back to the resolved fill color
|
|
258
|
-
|
|
259
|
-
In this demo:
|
|
260
|
-
- Node 1: No stroke specified → border matches fill color (blue)
|
|
261
|
-
- Node 2: Blue fill but red pv.stroke → red border
|
|
262
|
-
- Node 3: Green fill with orange stroke → independent colors
|
|
263
|
-
`,
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
// ============================================================================
|
|
270
|
-
// Edge Color Priority Story
|
|
271
|
-
// Priority: edge.color > edge type definition color > default
|
|
272
|
-
// ============================================================================
|
|
273
|
-
|
|
274
|
-
const edgeColorPriorityCanvas: ExtendedCanvas = {
|
|
275
|
-
nodes: [
|
|
276
|
-
{
|
|
277
|
-
id: 'source-1',
|
|
278
|
-
type: 'text',
|
|
279
|
-
x: 100,
|
|
280
|
-
y: 100,
|
|
281
|
-
width: 180,
|
|
282
|
-
height: 70,
|
|
283
|
-
text: 'SOURCE: edgeType color\nEXPECT EDGE: BLUE',
|
|
284
|
-
color: '#888888',
|
|
285
|
-
pv: { nodeType: 'source', shape: 'rectangle' },
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
id: 'target-1',
|
|
289
|
-
type: 'text',
|
|
290
|
-
x: 450,
|
|
291
|
-
y: 100,
|
|
292
|
-
width: 100,
|
|
293
|
-
height: 70,
|
|
294
|
-
text: 'Target',
|
|
295
|
-
color: '#888888',
|
|
296
|
-
pv: { nodeType: 'target', shape: 'rectangle' },
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
id: 'source-2',
|
|
300
|
-
type: 'text',
|
|
301
|
-
x: 100,
|
|
302
|
-
y: 220,
|
|
303
|
-
width: 180,
|
|
304
|
-
height: 70,
|
|
305
|
-
text: 'SOURCE: edge.color\nEXPECT EDGE: RED\n(edgeType=BLUE ignored)',
|
|
306
|
-
color: '#888888',
|
|
307
|
-
pv: { nodeType: 'source', shape: 'rectangle' },
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
id: 'target-2',
|
|
311
|
-
type: 'text',
|
|
312
|
-
x: 450,
|
|
313
|
-
y: 220,
|
|
314
|
-
width: 100,
|
|
315
|
-
height: 70,
|
|
316
|
-
text: 'Target',
|
|
317
|
-
color: '#888888',
|
|
318
|
-
pv: { nodeType: 'target', shape: 'rectangle' },
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
id: 'source-3',
|
|
322
|
-
type: 'text',
|
|
323
|
-
x: 100,
|
|
324
|
-
y: 340,
|
|
325
|
-
width: 180,
|
|
326
|
-
height: 70,
|
|
327
|
-
text: 'SOURCE: Default\nEXPECT EDGE: Gray',
|
|
328
|
-
color: '#888888',
|
|
329
|
-
pv: { nodeType: 'source', shape: 'rectangle' },
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
id: 'target-3',
|
|
333
|
-
type: 'text',
|
|
334
|
-
x: 450,
|
|
335
|
-
y: 340,
|
|
336
|
-
width: 100,
|
|
337
|
-
height: 70,
|
|
338
|
-
text: 'Target',
|
|
339
|
-
color: '#888888',
|
|
340
|
-
pv: { nodeType: 'target', shape: 'rectangle' },
|
|
341
|
-
},
|
|
342
|
-
],
|
|
343
|
-
edges: [
|
|
344
|
-
// 1. Edge type color only (from edgeTypes definition)
|
|
345
|
-
{
|
|
346
|
-
id: 'edge-type-color',
|
|
347
|
-
fromNode: 'source-1',
|
|
348
|
-
toNode: 'target-1',
|
|
349
|
-
pv: {
|
|
350
|
-
edgeType: 'dataflow', // Uses edgeTypes.dataflow.color = BLUE
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
// 2. Edge's own color overrides edge type
|
|
354
|
-
{
|
|
355
|
-
id: 'edge-own-color',
|
|
356
|
-
fromNode: 'source-2',
|
|
357
|
-
toNode: 'target-2',
|
|
358
|
-
color: '#FF0000', // RED - overrides the edge type color
|
|
359
|
-
pv: {
|
|
360
|
-
edgeType: 'dataflow', // Would be blue, but overridden
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
// 3. No color specified (default)
|
|
364
|
-
{
|
|
365
|
-
id: 'edge-default',
|
|
366
|
-
fromNode: 'source-3',
|
|
367
|
-
toNode: 'target-3',
|
|
368
|
-
pv: {
|
|
369
|
-
edgeType: 'uncolored', // No color in edge type
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
],
|
|
373
|
-
pv: {
|
|
374
|
-
version: '1.0.0',
|
|
375
|
-
name: 'Edge Color Priority Demo',
|
|
376
|
-
description: 'Shows how edge colors are prioritized',
|
|
377
|
-
edgeTypes: {
|
|
378
|
-
dataflow: {
|
|
379
|
-
style: 'solid',
|
|
380
|
-
color: '#0000FF', // BLUE
|
|
381
|
-
directed: true,
|
|
382
|
-
},
|
|
383
|
-
uncolored: {
|
|
384
|
-
style: 'dashed',
|
|
385
|
-
directed: true,
|
|
386
|
-
// No color defined
|
|
387
|
-
},
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
export const EdgeColorPriority: Story = {
|
|
393
|
-
args: {
|
|
394
|
-
canvas: edgeColorPriorityCanvas,
|
|
395
|
-
width: 650,
|
|
396
|
-
height: 520,
|
|
397
|
-
showMinimap: false,
|
|
398
|
-
},
|
|
399
|
-
parameters: {
|
|
400
|
-
docs: {
|
|
401
|
-
description: {
|
|
402
|
-
story: `
|
|
403
|
-
**Edge Color Priority** (highest to lowest):
|
|
404
|
-
|
|
405
|
-
1. **edge.color** - The edge's own color property
|
|
406
|
-
2. **Edge type definition color** - Color from pv.edgeTypes[type].color
|
|
407
|
-
3. **Default** - System default edge color
|
|
408
|
-
|
|
409
|
-
In this demo:
|
|
410
|
-
- Edge 1: Uses "dataflow" type → shows cyan from edgeTypes
|
|
411
|
-
- Edge 2: Has edge.color AND uses "dataflow" type → purple (edge.color) wins
|
|
412
|
-
- Edge 3: Uses "uncolored" type with no color → default styling
|
|
413
|
-
`,
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
// ============================================================================
|
|
420
|
-
// Icon Priority Story
|
|
421
|
-
// Priority: node data icon > state icon > type definition icon
|
|
422
|
-
// ============================================================================
|
|
423
|
-
|
|
424
|
-
const iconPriorityCanvas: ExtendedCanvas = {
|
|
425
|
-
nodes: [
|
|
426
|
-
// 1. Type definition icon only
|
|
427
|
-
{
|
|
428
|
-
id: 'icon-type',
|
|
429
|
-
type: 'text',
|
|
430
|
-
x: 100,
|
|
431
|
-
y: 100,
|
|
432
|
-
width: 180,
|
|
433
|
-
height: 100,
|
|
434
|
-
text: 'SOURCE: pv.icon\nEXPECT ICON: Settings',
|
|
435
|
-
color: '#0000FF',
|
|
436
|
-
pv: {
|
|
437
|
-
nodeType: 'icon-type-demo',
|
|
438
|
-
shape: 'rectangle',
|
|
439
|
-
icon: 'Settings',
|
|
440
|
-
},
|
|
441
|
-
},
|
|
442
|
-
// 2. State icon (when in a state)
|
|
443
|
-
{
|
|
444
|
-
id: 'icon-state',
|
|
445
|
-
type: 'text',
|
|
446
|
-
x: 320,
|
|
447
|
-
y: 100,
|
|
448
|
-
width: 180,
|
|
449
|
-
height: 100,
|
|
450
|
-
text: 'SOURCE: state.icon\nEXPECT ICON: Check\n(pv.icon=settings ignored)',
|
|
451
|
-
color: '#00FF00',
|
|
452
|
-
pv: {
|
|
453
|
-
nodeType: 'icon-state-demo',
|
|
454
|
-
shape: 'rectangle',
|
|
455
|
-
icon: 'Settings', // Type icon - should be overridden
|
|
456
|
-
states: {
|
|
457
|
-
complete: { label: 'Complete', color: '#00FF00', icon: 'Check' },
|
|
458
|
-
},
|
|
459
|
-
},
|
|
460
|
-
},
|
|
461
|
-
],
|
|
462
|
-
edges: [],
|
|
463
|
-
pv: {
|
|
464
|
-
version: '1.0.0',
|
|
465
|
-
name: 'Icon Priority Demo',
|
|
466
|
-
description: 'Shows how icons are prioritized',
|
|
467
|
-
edgeTypes: {},
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
export const IconPriority: Story = {
|
|
472
|
-
render: () => (
|
|
473
|
-
<GraphWithInitialStates
|
|
474
|
-
canvas={iconPriorityCanvas}
|
|
475
|
-
width={600}
|
|
476
|
-
height={320}
|
|
477
|
-
showMinimap={false}
|
|
478
|
-
initialStates={{ 'icon-state': 'complete' }}
|
|
479
|
-
/>
|
|
480
|
-
),
|
|
481
|
-
parameters: {
|
|
482
|
-
docs: {
|
|
483
|
-
description: {
|
|
484
|
-
story: `
|
|
485
|
-
**Icon Priority** (highest to lowest):
|
|
486
|
-
|
|
487
|
-
1. **Node data icon override** - Explicitly set icon in node data
|
|
488
|
-
2. **State icon** - Icon from the current state definition
|
|
489
|
-
3. **Type definition icon** - Icon from pv.icon
|
|
490
|
-
|
|
491
|
-
In this demo:
|
|
492
|
-
- Node 1: Only has type icon (settings) → shows settings
|
|
493
|
-
- Node 2: Has type icon (settings) BUT is in "complete" state with check icon → shows check
|
|
494
|
-
`,
|
|
495
|
-
},
|
|
496
|
-
},
|
|
497
|
-
},
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
// ============================================================================
|
|
501
|
-
// Combined Priority Demo - All in One
|
|
502
|
-
// ============================================================================
|
|
503
|
-
|
|
504
|
-
const combinedPriorityCanvas: ExtendedCanvas = {
|
|
505
|
-
nodes: [
|
|
506
|
-
// Row 1 label
|
|
507
|
-
{
|
|
508
|
-
id: 'label-row1',
|
|
509
|
-
type: 'text',
|
|
510
|
-
x: 50,
|
|
511
|
-
y: 50,
|
|
512
|
-
width: 700,
|
|
513
|
-
height: 40,
|
|
514
|
-
text: 'FILL COLOR PRIORITY (low to high): Default → node.color → pv.fill → state.color',
|
|
515
|
-
pv: { nodeType: 'label', shape: 'rectangle' },
|
|
516
|
-
},
|
|
517
|
-
// Lowest priority
|
|
518
|
-
{
|
|
519
|
-
id: 'fill-1',
|
|
520
|
-
type: 'text',
|
|
521
|
-
x: 50,
|
|
522
|
-
y: 120,
|
|
523
|
-
width: 150,
|
|
524
|
-
height: 80,
|
|
525
|
-
text: 'SOURCE: Default\nEXPECT: Gray',
|
|
526
|
-
pv: { nodeType: 'fill-demo', shape: 'rectangle' },
|
|
527
|
-
},
|
|
528
|
-
// node.color
|
|
529
|
-
{
|
|
530
|
-
id: 'fill-2',
|
|
531
|
-
type: 'text',
|
|
532
|
-
x: 230,
|
|
533
|
-
y: 120,
|
|
534
|
-
width: 150,
|
|
535
|
-
height: 80,
|
|
536
|
-
text: 'SOURCE: node.color\nEXPECT: BLUE',
|
|
537
|
-
color: '#0000FF',
|
|
538
|
-
pv: { nodeType: 'fill-demo', shape: 'rectangle' },
|
|
539
|
-
},
|
|
540
|
-
// pv.fill
|
|
541
|
-
{
|
|
542
|
-
id: 'fill-3',
|
|
543
|
-
type: 'text',
|
|
544
|
-
x: 410,
|
|
545
|
-
y: 120,
|
|
546
|
-
width: 150,
|
|
547
|
-
height: 80,
|
|
548
|
-
text: 'SOURCE: pv.fill\nEXPECT: GREEN',
|
|
549
|
-
color: '#0000FF',
|
|
550
|
-
pv: { nodeType: 'fill-demo', shape: 'rectangle', fill: '#00FF00' },
|
|
551
|
-
},
|
|
552
|
-
// state color
|
|
553
|
-
{
|
|
554
|
-
id: 'fill-4',
|
|
555
|
-
type: 'text',
|
|
556
|
-
x: 590,
|
|
557
|
-
y: 120,
|
|
558
|
-
width: 150,
|
|
559
|
-
height: 80,
|
|
560
|
-
text: 'SOURCE: state.color\nEXPECT: RED',
|
|
561
|
-
color: '#0000FF',
|
|
562
|
-
pv: {
|
|
563
|
-
nodeType: 'fill-demo',
|
|
564
|
-
shape: 'rectangle',
|
|
565
|
-
fill: '#00FF00',
|
|
566
|
-
states: { error: { label: 'Error', color: '#FF0000' } },
|
|
567
|
-
},
|
|
568
|
-
},
|
|
569
|
-
// Row 2 label
|
|
570
|
-
{
|
|
571
|
-
id: 'label-row2',
|
|
572
|
-
type: 'text',
|
|
573
|
-
x: 50,
|
|
574
|
-
y: 240,
|
|
575
|
-
width: 500,
|
|
576
|
-
height: 40,
|
|
577
|
-
text: 'STROKE PRIORITY (low to high): fill color → pv.stroke',
|
|
578
|
-
pv: { nodeType: 'label', shape: 'rectangle' },
|
|
579
|
-
},
|
|
580
|
-
// Stroke = fill
|
|
581
|
-
{
|
|
582
|
-
id: 'stroke-1',
|
|
583
|
-
type: 'text',
|
|
584
|
-
x: 50,
|
|
585
|
-
y: 310,
|
|
586
|
-
width: 180,
|
|
587
|
-
height: 80,
|
|
588
|
-
text: 'SOURCE: Default (=fill)\nEXPECT BORDER: BLUE',
|
|
589
|
-
color: '#0000FF',
|
|
590
|
-
pv: { nodeType: 'stroke-demo', shape: 'rectangle' },
|
|
591
|
-
},
|
|
592
|
-
// pv.stroke
|
|
593
|
-
{
|
|
594
|
-
id: 'stroke-2',
|
|
595
|
-
type: 'text',
|
|
596
|
-
x: 260,
|
|
597
|
-
y: 310,
|
|
598
|
-
width: 180,
|
|
599
|
-
height: 80,
|
|
600
|
-
text: 'SOURCE: pv.stroke\nEXPECT BORDER: YELLOW',
|
|
601
|
-
color: '#0000FF',
|
|
602
|
-
pv: { nodeType: 'stroke-demo', shape: 'rectangle', stroke: '#FFFF00' },
|
|
603
|
-
},
|
|
604
|
-
],
|
|
605
|
-
edges: [],
|
|
606
|
-
pv: {
|
|
607
|
-
version: '1.0.0',
|
|
608
|
-
name: 'Combined Color Priority Demo',
|
|
609
|
-
description: 'Overview of all color priorities',
|
|
610
|
-
edgeTypes: {},
|
|
611
|
-
},
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
export const CombinedPriorityOverview: Story = {
|
|
615
|
-
render: () => (
|
|
616
|
-
<GraphWithInitialStates
|
|
617
|
-
canvas={combinedPriorityCanvas}
|
|
618
|
-
width={850}
|
|
619
|
-
height={480}
|
|
620
|
-
showMinimap={false}
|
|
621
|
-
initialStates={{ 'fill-4': 'error' }}
|
|
622
|
-
/>
|
|
623
|
-
),
|
|
624
|
-
parameters: {
|
|
625
|
-
docs: {
|
|
626
|
-
description: {
|
|
627
|
-
story: `
|
|
628
|
-
**Complete Color Priority Reference**
|
|
629
|
-
|
|
630
|
-
This story shows all color priority chains at a glance:
|
|
631
|
-
|
|
632
|
-
**Fill Color:** Default (#888) → node.color → pv.fill → State color
|
|
633
|
-
|
|
634
|
-
**Stroke Color:** Fill color → pv.stroke
|
|
635
|
-
|
|
636
|
-
Higher priority always wins when multiple values are specified.
|
|
637
|
-
`,
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
},
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
// ============================================================================
|
|
644
|
-
// Library Color Priority Story
|
|
645
|
-
// Priority: library.nodeComponents.color → node.color → pv.fill → state.color
|
|
646
|
-
// ============================================================================
|
|
647
|
-
|
|
648
|
-
const libraryColorTestLibrary: ComponentLibrary = {
|
|
649
|
-
version: '1.0.0',
|
|
650
|
-
name: 'Color Test Library',
|
|
651
|
-
description: 'Library for testing color inheritance',
|
|
652
|
-
resources: {},
|
|
653
|
-
nodeComponents: {
|
|
654
|
-
'service-red': {
|
|
655
|
-
description: 'Service with red color from library',
|
|
656
|
-
shape: 'rectangle',
|
|
657
|
-
color: '#FF0000', // RED from library
|
|
658
|
-
icon: 'Server',
|
|
659
|
-
},
|
|
660
|
-
'service-green': {
|
|
661
|
-
description: 'Service with green color from library',
|
|
662
|
-
shape: 'rectangle',
|
|
663
|
-
color: '#00FF00', // GREEN from library
|
|
664
|
-
icon: 'Database',
|
|
665
|
-
},
|
|
666
|
-
'service-blue': {
|
|
667
|
-
description: 'Service with blue color from library',
|
|
668
|
-
shape: 'rectangle',
|
|
669
|
-
color: '#0000FF', // BLUE from library
|
|
670
|
-
icon: 'Settings',
|
|
671
|
-
},
|
|
672
|
-
'service-purple': {
|
|
673
|
-
description: 'Service with purple color from library',
|
|
674
|
-
shape: 'rectangle',
|
|
675
|
-
color: '#8B00FF', // PURPLE from library
|
|
676
|
-
icon: 'Zap',
|
|
677
|
-
},
|
|
678
|
-
},
|
|
679
|
-
edgeComponents: {
|
|
680
|
-
'flow-orange': {
|
|
681
|
-
description: 'Flow edge with orange color from library',
|
|
682
|
-
style: 'solid',
|
|
683
|
-
color: '#FFA500', // ORANGE from library
|
|
684
|
-
directed: true,
|
|
685
|
-
},
|
|
686
|
-
'flow-cyan': {
|
|
687
|
-
description: 'Flow edge with cyan color from library',
|
|
688
|
-
style: 'solid',
|
|
689
|
-
color: '#00FFFF', // CYAN from library
|
|
690
|
-
directed: true,
|
|
691
|
-
},
|
|
692
|
-
},
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
const libraryColorPriorityCanvas: ExtendedCanvas = {
|
|
696
|
-
nodes: [
|
|
697
|
-
// 1. Library color only (nodeType references library, no other colors)
|
|
698
|
-
{
|
|
699
|
-
id: 'library-only',
|
|
700
|
-
type: 'text',
|
|
701
|
-
x: 100,
|
|
702
|
-
y: 100,
|
|
703
|
-
width: 180,
|
|
704
|
-
height: 100,
|
|
705
|
-
text: 'SOURCE: library\nEXPECT: RED\n(from service-red)',
|
|
706
|
-
pv: {
|
|
707
|
-
nodeType: 'service-red', // Should get RED from library
|
|
708
|
-
shape: 'rectangle',
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
// 2. Library color overridden by node.color
|
|
712
|
-
{
|
|
713
|
-
id: 'library-vs-node-color',
|
|
714
|
-
type: 'text',
|
|
715
|
-
x: 320,
|
|
716
|
-
y: 100,
|
|
717
|
-
width: 180,
|
|
718
|
-
height: 100,
|
|
719
|
-
text: 'SOURCE: node.color\nEXPECT: YELLOW\n(library GREEN ignored)',
|
|
720
|
-
color: '#FFFF00', // YELLOW - should override library GREEN
|
|
721
|
-
pv: {
|
|
722
|
-
nodeType: 'service-green', // Would be GREEN, but overridden
|
|
723
|
-
shape: 'rectangle',
|
|
724
|
-
},
|
|
725
|
-
},
|
|
726
|
-
// 3. Library color overridden by pv.fill
|
|
727
|
-
{
|
|
728
|
-
id: 'library-vs-pv-fill',
|
|
729
|
-
type: 'text',
|
|
730
|
-
x: 540,
|
|
731
|
-
y: 100,
|
|
732
|
-
width: 180,
|
|
733
|
-
height: 100,
|
|
734
|
-
text: 'SOURCE: pv.fill\nEXPECT: CYAN\n(library BLUE ignored)',
|
|
735
|
-
pv: {
|
|
736
|
-
nodeType: 'service-blue', // Would be BLUE, but overridden
|
|
737
|
-
shape: 'rectangle',
|
|
738
|
-
fill: '#00FFFF', // CYAN - should override library BLUE
|
|
739
|
-
},
|
|
740
|
-
},
|
|
741
|
-
// 4. Library color overridden by state color
|
|
742
|
-
{
|
|
743
|
-
id: 'library-vs-state',
|
|
744
|
-
type: 'text',
|
|
745
|
-
x: 760,
|
|
746
|
-
y: 100,
|
|
747
|
-
width: 180,
|
|
748
|
-
height: 100,
|
|
749
|
-
text: 'SOURCE: state.color\nEXPECT: ORANGE\n(library PURPLE ignored)',
|
|
750
|
-
pv: {
|
|
751
|
-
nodeType: 'service-purple', // Would be PURPLE, but overridden by state
|
|
752
|
-
shape: 'rectangle',
|
|
753
|
-
states: {
|
|
754
|
-
active: { label: 'Active', color: '#FFA500' }, // ORANGE
|
|
755
|
-
},
|
|
756
|
-
},
|
|
757
|
-
},
|
|
758
|
-
// 5. Another library color node for comparison
|
|
759
|
-
{
|
|
760
|
-
id: 'library-green',
|
|
761
|
-
type: 'text',
|
|
762
|
-
x: 100,
|
|
763
|
-
y: 250,
|
|
764
|
-
width: 180,
|
|
765
|
-
height: 100,
|
|
766
|
-
text: 'SOURCE: library\nEXPECT: GREEN\n(from service-green)',
|
|
767
|
-
pv: {
|
|
768
|
-
nodeType: 'service-green',
|
|
769
|
-
shape: 'rectangle',
|
|
770
|
-
},
|
|
771
|
-
},
|
|
772
|
-
// 6. Library blue for comparison
|
|
773
|
-
{
|
|
774
|
-
id: 'library-blue',
|
|
775
|
-
type: 'text',
|
|
776
|
-
x: 320,
|
|
777
|
-
y: 250,
|
|
778
|
-
width: 180,
|
|
779
|
-
height: 100,
|
|
780
|
-
text: 'SOURCE: library\nEXPECT: BLUE\n(from service-blue)',
|
|
781
|
-
pv: {
|
|
782
|
-
nodeType: 'service-blue',
|
|
783
|
-
shape: 'rectangle',
|
|
784
|
-
},
|
|
785
|
-
},
|
|
786
|
-
],
|
|
787
|
-
edges: [
|
|
788
|
-
// Edge using library color
|
|
789
|
-
{
|
|
790
|
-
id: 'edge-library-orange',
|
|
791
|
-
fromNode: 'library-green',
|
|
792
|
-
toNode: 'library-blue',
|
|
793
|
-
pv: {
|
|
794
|
-
edgeType: 'flow-orange', // Should get ORANGE from library
|
|
795
|
-
},
|
|
796
|
-
},
|
|
797
|
-
// Edge with library color overridden
|
|
798
|
-
{
|
|
799
|
-
id: 'edge-override',
|
|
800
|
-
fromNode: 'library-only',
|
|
801
|
-
toNode: 'library-vs-node-color',
|
|
802
|
-
color: '#FF00FF', // MAGENTA - overrides library
|
|
803
|
-
pv: {
|
|
804
|
-
edgeType: 'flow-cyan', // Would be CYAN, but overridden
|
|
805
|
-
},
|
|
806
|
-
},
|
|
807
|
-
],
|
|
808
|
-
pv: {
|
|
809
|
-
version: '1.0.0',
|
|
810
|
-
name: 'Library Color Priority Demo',
|
|
811
|
-
description: 'Shows how library.yaml colors are applied and overridden',
|
|
812
|
-
edgeTypes: {},
|
|
813
|
-
},
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
// Helper component for library color test
|
|
817
|
-
const GraphWithLibrary: React.FC<{
|
|
818
|
-
canvas: ExtendedCanvas;
|
|
819
|
-
library: ComponentLibrary;
|
|
820
|
-
width: number;
|
|
821
|
-
height: number;
|
|
822
|
-
showMinimap: boolean;
|
|
823
|
-
initialStates?: Record<string, string>;
|
|
824
|
-
}> = ({ canvas, library, width, height, showMinimap, initialStates }) => {
|
|
825
|
-
const [events, setEvents] = useState<GraphEvent[]>([]);
|
|
826
|
-
|
|
827
|
-
useEffect(() => {
|
|
828
|
-
if (initialStates) {
|
|
829
|
-
const stateEvents: GraphEvent[] = Object.entries(initialStates).map(
|
|
830
|
-
([nodeId, newState], idx) => ({
|
|
831
|
-
id: `init-state-${idx}`,
|
|
832
|
-
type: 'state_changed',
|
|
833
|
-
timestamp: Date.now(),
|
|
834
|
-
category: 'state' as const,
|
|
835
|
-
payload: { nodeId, newState },
|
|
836
|
-
})
|
|
837
|
-
);
|
|
838
|
-
setEvents(stateEvents);
|
|
839
|
-
}
|
|
840
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally run only on mount
|
|
841
|
-
}, []);
|
|
842
|
-
|
|
843
|
-
return (
|
|
844
|
-
<GraphRenderer
|
|
845
|
-
canvas={canvas}
|
|
846
|
-
library={library}
|
|
847
|
-
width={width}
|
|
848
|
-
height={height}
|
|
849
|
-
showMinimap={showMinimap}
|
|
850
|
-
events={events}
|
|
851
|
-
/>
|
|
852
|
-
);
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
export const LibraryColorPriority: Story = {
|
|
856
|
-
render: () => (
|
|
857
|
-
<GraphWithLibrary
|
|
858
|
-
canvas={libraryColorPriorityCanvas}
|
|
859
|
-
library={libraryColorTestLibrary}
|
|
860
|
-
width={1050}
|
|
861
|
-
height={450}
|
|
862
|
-
showMinimap={false}
|
|
863
|
-
initialStates={{ 'library-vs-state': 'active' }}
|
|
864
|
-
/>
|
|
865
|
-
),
|
|
866
|
-
parameters: {
|
|
867
|
-
docs: {
|
|
868
|
-
description: {
|
|
869
|
-
story: `
|
|
870
|
-
**Library Color Priority** - Tests colors from library.yaml nodeComponents
|
|
871
|
-
|
|
872
|
-
This story validates that the \`library\` prop correctly provides colors to nodes via nodeType lookup.
|
|
873
|
-
|
|
874
|
-
**Full Fill Color Priority Chain** (lowest to highest):
|
|
875
|
-
1. **Default** - Falls back to #888 gray
|
|
876
|
-
2. **Library nodeComponent color** - Color from library.yaml nodeComponents[nodeType].color
|
|
877
|
-
3. **node.color** - The standard canvas node color property
|
|
878
|
-
4. **pv.fill** - Explicit fill color in the PV extension
|
|
879
|
-
5. **state.color** - When a node has an active state with a color defined
|
|
880
|
-
|
|
881
|
-
**In this demo:**
|
|
882
|
-
- Row 1 Node 1: Uses \`service-red\` nodeType → gets RED from library
|
|
883
|
-
- Row 1 Node 2: Uses \`service-green\` nodeType but has node.color=YELLOW → YELLOW wins
|
|
884
|
-
- Row 1 Node 3: Uses \`service-blue\` nodeType but has pv.fill=CYAN → CYAN wins
|
|
885
|
-
- Row 1 Node 4: Uses \`service-purple\` nodeType but is in "active" state → ORANGE wins
|
|
886
|
-
- Row 2: Pure library colors (GREEN, BLUE) for visual comparison
|
|
887
|
-
|
|
888
|
-
**Edge Colors:**
|
|
889
|
-
- Bottom edge: Uses \`flow-orange\` edgeType → gets ORANGE from library
|
|
890
|
-
- Top edge: Uses \`flow-cyan\` but has edge.color=MAGENTA → MAGENTA wins
|
|
891
|
-
`,
|
|
892
|
-
},
|
|
893
|
-
},
|
|
894
|
-
},
|
|
895
|
-
};
|