@principal-ai/principal-view-react 0.6.6

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.
Files changed (96) hide show
  1. package/README.md +111 -0
  2. package/dist/components/ConfigurationSelector.d.ts +37 -0
  3. package/dist/components/ConfigurationSelector.d.ts.map +1 -0
  4. package/dist/components/ConfigurationSelector.js +67 -0
  5. package/dist/components/ConfigurationSelector.js.map +1 -0
  6. package/dist/components/EdgeInfoPanel.d.ts +16 -0
  7. package/dist/components/EdgeInfoPanel.d.ts.map +1 -0
  8. package/dist/components/EdgeInfoPanel.js +85 -0
  9. package/dist/components/EdgeInfoPanel.js.map +1 -0
  10. package/dist/components/EventLog.d.ts +20 -0
  11. package/dist/components/EventLog.d.ts.map +1 -0
  12. package/dist/components/EventLog.js +13 -0
  13. package/dist/components/EventLog.js.map +1 -0
  14. package/dist/components/EventLog.test.d.ts +2 -0
  15. package/dist/components/EventLog.test.d.ts.map +1 -0
  16. package/dist/components/EventLog.test.js +73 -0
  17. package/dist/components/EventLog.test.js.map +1 -0
  18. package/dist/components/GraphRenderer.d.ts +121 -0
  19. package/dist/components/GraphRenderer.d.ts.map +1 -0
  20. package/dist/components/GraphRenderer.js +809 -0
  21. package/dist/components/GraphRenderer.js.map +1 -0
  22. package/dist/components/GraphRenderer.test.d.ts +2 -0
  23. package/dist/components/GraphRenderer.test.d.ts.map +1 -0
  24. package/dist/components/GraphRenderer.test.js +88 -0
  25. package/dist/components/GraphRenderer.test.js.map +1 -0
  26. package/dist/components/MetricsDashboard.d.ts +14 -0
  27. package/dist/components/MetricsDashboard.d.ts.map +1 -0
  28. package/dist/components/MetricsDashboard.js +13 -0
  29. package/dist/components/MetricsDashboard.js.map +1 -0
  30. package/dist/components/NodeInfoPanel.d.ts +21 -0
  31. package/dist/components/NodeInfoPanel.d.ts.map +1 -0
  32. package/dist/components/NodeInfoPanel.js +217 -0
  33. package/dist/components/NodeInfoPanel.js.map +1 -0
  34. package/dist/edges/CustomEdge.d.ts +16 -0
  35. package/dist/edges/CustomEdge.d.ts.map +1 -0
  36. package/dist/edges/CustomEdge.js +200 -0
  37. package/dist/edges/CustomEdge.js.map +1 -0
  38. package/dist/edges/GenericEdge.d.ts +18 -0
  39. package/dist/edges/GenericEdge.d.ts.map +1 -0
  40. package/dist/edges/GenericEdge.js +14 -0
  41. package/dist/edges/GenericEdge.js.map +1 -0
  42. package/dist/hooks/usePathBasedEvents.d.ts +42 -0
  43. package/dist/hooks/usePathBasedEvents.d.ts.map +1 -0
  44. package/dist/hooks/usePathBasedEvents.js +122 -0
  45. package/dist/hooks/usePathBasedEvents.js.map +1 -0
  46. package/dist/index.d.ts +33 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +41 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/nodes/CustomNode.d.ts +18 -0
  51. package/dist/nodes/CustomNode.d.ts.map +1 -0
  52. package/dist/nodes/CustomNode.js +298 -0
  53. package/dist/nodes/CustomNode.js.map +1 -0
  54. package/dist/nodes/GenericNode.d.ts +20 -0
  55. package/dist/nodes/GenericNode.d.ts.map +1 -0
  56. package/dist/nodes/GenericNode.js +24 -0
  57. package/dist/nodes/GenericNode.js.map +1 -0
  58. package/dist/utils/animationMapping.d.ts +53 -0
  59. package/dist/utils/animationMapping.d.ts.map +1 -0
  60. package/dist/utils/animationMapping.js +133 -0
  61. package/dist/utils/animationMapping.js.map +1 -0
  62. package/dist/utils/graphConverter.d.ts +22 -0
  63. package/dist/utils/graphConverter.d.ts.map +1 -0
  64. package/dist/utils/graphConverter.js +176 -0
  65. package/dist/utils/graphConverter.js.map +1 -0
  66. package/dist/utils/iconResolver.d.ts +29 -0
  67. package/dist/utils/iconResolver.d.ts.map +1 -0
  68. package/dist/utils/iconResolver.js +68 -0
  69. package/dist/utils/iconResolver.js.map +1 -0
  70. package/package.json +61 -0
  71. package/src/components/ConfigurationSelector.tsx +147 -0
  72. package/src/components/EdgeInfoPanel.tsx +198 -0
  73. package/src/components/EventLog.test.tsx +85 -0
  74. package/src/components/EventLog.tsx +51 -0
  75. package/src/components/GraphRenderer.test.tsx +118 -0
  76. package/src/components/GraphRenderer.tsx +1222 -0
  77. package/src/components/MetricsDashboard.tsx +40 -0
  78. package/src/components/NodeInfoPanel.tsx +425 -0
  79. package/src/edges/CustomEdge.tsx +344 -0
  80. package/src/edges/GenericEdge.tsx +40 -0
  81. package/src/hooks/usePathBasedEvents.ts +182 -0
  82. package/src/index.ts +67 -0
  83. package/src/nodes/CustomNode.tsx +432 -0
  84. package/src/nodes/GenericNode.tsx +54 -0
  85. package/src/stories/AnimationWorkshop.stories.tsx +608 -0
  86. package/src/stories/EventDrivenAnimations.stories.tsx +499 -0
  87. package/src/stories/EventLog.stories.tsx +161 -0
  88. package/src/stories/GraphRenderer.stories.tsx +628 -0
  89. package/src/stories/Introduction.mdx +51 -0
  90. package/src/stories/MetricsDashboard.stories.tsx +227 -0
  91. package/src/stories/MultiConfig.stories.tsx +531 -0
  92. package/src/stories/MultiDirectionalConnections.stories.tsx +345 -0
  93. package/src/stories/NodeShapes.stories.tsx +769 -0
  94. package/src/utils/animationMapping.ts +170 -0
  95. package/src/utils/graphConverter.ts +218 -0
  96. package/src/utils/iconResolver.tsx +49 -0
@@ -0,0 +1,344 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@xyflow/react';
3
+ import type { EdgeProps } from '@xyflow/react';
4
+ import type { EdgeTypeDefinition } from '@principal-ai/principal-view-core';
5
+
6
+ export interface CustomEdgeData extends Record<string, unknown> {
7
+ typeDefinition: EdgeTypeDefinition;
8
+ hasViolations?: boolean;
9
+ data?: Record<string, unknown>;
10
+ // Animation control
11
+ animationType?: 'flow' | 'particle' | 'pulse' | 'glow' | null;
12
+ animationDuration?: number;
13
+ animationDirection?: 'forward' | 'backward' | 'bidirectional';
14
+ }
15
+
16
+ /**
17
+ * Custom edge component for xyflow that renders based on EdgeTypeDefinition
18
+ */
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ export const CustomEdge: React.FC<EdgeProps<any>> = ({
21
+ id,
22
+ sourceX,
23
+ sourceY,
24
+ targetX,
25
+ targetY,
26
+ sourcePosition,
27
+ targetPosition,
28
+ data,
29
+ markerEnd,
30
+ selected,
31
+ }) => {
32
+ const edgeProps = data as CustomEdgeData | undefined;
33
+ const {
34
+ typeDefinition,
35
+ hasViolations,
36
+ data: edgeData,
37
+ animationType,
38
+ animationDuration = 1000,
39
+ animationDirection = 'forward'
40
+ } = edgeProps || ({} as CustomEdgeData);
41
+
42
+ const [particlePosition, setParticlePosition] = useState(0);
43
+ const pathRef = useRef<SVGPathElement>(null);
44
+
45
+ // Particle animation effect
46
+ useEffect(() => {
47
+ if (animationType !== 'particle') return;
48
+
49
+ const animate = () => {
50
+ setParticlePosition((prev) => {
51
+ const next = prev + (100 / animationDuration) * 16; // ~60fps
52
+ return next >= 100 ? 0 : next;
53
+ });
54
+ };
55
+
56
+ const intervalId = setInterval(animate, 16);
57
+ return () => clearInterval(intervalId);
58
+ }, [animationType, animationDuration]);
59
+
60
+ // Early return after hooks
61
+ if (!typeDefinition) {
62
+ return null;
63
+ }
64
+
65
+ const color = hasViolations ? '#D0021B' : (typeDefinition.color || '#888');
66
+ const width = typeDefinition.width || 2;
67
+
68
+ // Get Bezier path
69
+ const [edgePath, labelX, labelY] = getBezierPath({
70
+ sourceX,
71
+ sourceY,
72
+ sourcePosition,
73
+ targetX,
74
+ targetY,
75
+ targetPosition,
76
+ });
77
+
78
+ // Style based on edge type
79
+ const getStrokeStyle = () => {
80
+ switch (typeDefinition.style) {
81
+ case 'dashed':
82
+ return '5 5';
83
+ case 'dotted':
84
+ return '2 2';
85
+ default:
86
+ return 'none';
87
+ }
88
+ };
89
+
90
+ // Label configuration
91
+ const labelConfig = typeDefinition.label;
92
+ const labelField = labelConfig?.field;
93
+ const labelText = labelField && edgeData?.[labelField] ? String(edgeData[labelField]) : '';
94
+
95
+ // Animation-specific rendering helpers
96
+ const getAnimationClass = () => {
97
+ if (!animationType) {
98
+ return typeDefinition.style === 'animated' ? 'edge-flow-forward' : '';
99
+ }
100
+
101
+ switch (animationType) {
102
+ case 'flow':
103
+ return animationDirection === 'backward'
104
+ ? 'edge-flow-backward'
105
+ : animationDirection === 'bidirectional'
106
+ ? 'edge-flow-bidirectional'
107
+ : 'edge-flow-forward';
108
+ case 'pulse':
109
+ return 'edge-pulse';
110
+ case 'glow':
111
+ return 'edge-glow';
112
+ default:
113
+ return '';
114
+ }
115
+ };
116
+
117
+ const getAnimationDurationStyle = () => {
118
+ if (!animationType) return {};
119
+ return {
120
+ animationDuration: `${animationDuration}ms`,
121
+ };
122
+ };
123
+
124
+ // Calculate particle position along path using SVG path methods
125
+ const getParticleTransform = () => {
126
+ if (!pathRef.current) {
127
+ // Fallback to linear interpolation if path ref not available yet
128
+ const progress = animationDirection === 'backward' ? 1 - particlePosition / 100 : particlePosition / 100;
129
+ const x = sourceX + (targetX - sourceX) * progress;
130
+ const y = sourceY + (targetY - sourceY) * progress;
131
+ return { x, y };
132
+ }
133
+
134
+ // Use actual path to get point along the curve
135
+ const pathLength = pathRef.current.getTotalLength();
136
+ const progress = animationDirection === 'backward' ? 1 - particlePosition / 100 : particlePosition / 100;
137
+ const distance = pathLength * progress;
138
+ const point = pathRef.current.getPointAtLength(distance);
139
+
140
+ return { x: point.x, y: point.y };
141
+ };
142
+
143
+ const particlePos = animationType === 'particle' ? getParticleTransform() : null;
144
+
145
+ return (
146
+ <>
147
+ {/* Hidden path for particle position calculation */}
148
+ <path
149
+ ref={pathRef}
150
+ d={edgePath}
151
+ fill="none"
152
+ stroke="transparent"
153
+ strokeWidth={0}
154
+ style={{ pointerEvents: 'none' }}
155
+ />
156
+
157
+ {/* Invisible wide path for easier clicking */}
158
+ <path
159
+ d={edgePath}
160
+ fill="none"
161
+ stroke="transparent"
162
+ strokeWidth={Math.max(width + 10, 20)}
163
+ style={{ cursor: 'pointer' }}
164
+ />
165
+
166
+ <BaseEdge
167
+ id={id}
168
+ path={edgePath}
169
+ markerEnd={markerEnd as string}
170
+ style={{
171
+ stroke: color,
172
+ strokeWidth: selected ? width + 2 : width,
173
+ strokeDasharray: getStrokeStyle(),
174
+ opacity: animationType ? 0.7 : 1,
175
+ cursor: 'pointer',
176
+ }}
177
+ />
178
+
179
+ {/* Flow Animation - Animated dashed overlay */}
180
+ {animationType === 'flow' && (
181
+ <path
182
+ d={edgePath}
183
+ fill="none"
184
+ stroke={typeDefinition.animation?.color || color}
185
+ strokeWidth={width}
186
+ strokeDasharray="10 5"
187
+ className={getAnimationClass()}
188
+ style={{
189
+ ...getAnimationDurationStyle(),
190
+ opacity: 0.8,
191
+ }}
192
+ />
193
+ )}
194
+
195
+ {/* Pulse Animation - Wave effect */}
196
+ {animationType === 'pulse' && (
197
+ <path
198
+ d={edgePath}
199
+ fill="none"
200
+ stroke={typeDefinition.animation?.color || color}
201
+ strokeWidth={width + 2}
202
+ className={getAnimationClass()}
203
+ style={{
204
+ ...getAnimationDurationStyle(),
205
+ }}
206
+ />
207
+ )}
208
+
209
+ {/* Glow Animation - Brief highlight */}
210
+ {animationType === 'glow' && (
211
+ <path
212
+ d={edgePath}
213
+ fill="none"
214
+ stroke={typeDefinition.animation?.color || color}
215
+ strokeWidth={width + 4}
216
+ className={getAnimationClass()}
217
+ style={{
218
+ ...getAnimationDurationStyle(),
219
+ filter: 'blur(3px)',
220
+ }}
221
+ />
222
+ )}
223
+
224
+ {/* Particle Animation - Traveling dot */}
225
+ {animationType === 'particle' && particlePos && (
226
+ <circle
227
+ cx={particlePos.x}
228
+ cy={particlePos.y}
229
+ r={width * 1.5}
230
+ fill={typeDefinition.animation?.color || color}
231
+ style={{
232
+ filter: 'drop-shadow(0 0 3px rgba(0,0,0,0.3))',
233
+ }}
234
+ />
235
+ )}
236
+
237
+ {/* Fallback: Legacy animated style support */}
238
+ {!animationType && typeDefinition.style === 'animated' && (
239
+ <path
240
+ d={edgePath}
241
+ fill="none"
242
+ stroke={typeDefinition.animation?.color || color}
243
+ strokeWidth={width}
244
+ strokeDasharray="5 5"
245
+ className="edge-flow-forward"
246
+ style={{
247
+ animationDuration: '500ms',
248
+ }}
249
+ />
250
+ )}
251
+
252
+ {/* Label */}
253
+ {labelText && (
254
+ <EdgeLabelRenderer>
255
+ <div
256
+ style={{
257
+ position: 'absolute',
258
+ transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
259
+ backgroundColor: 'white',
260
+ padding: '2px 6px',
261
+ borderRadius: '4px',
262
+ fontSize: '10px',
263
+ fontWeight: 500,
264
+ border: `1px solid ${color}`,
265
+ pointerEvents: 'all',
266
+ }}
267
+ className="nodrag nopan"
268
+ >
269
+ {labelText}
270
+ </div>
271
+ </EdgeLabelRenderer>
272
+ )}
273
+
274
+ {/* CSS animations for all edge animation types */}
275
+ <style>{`
276
+ /* Flow animation - forward direction */
277
+ .edge-flow-forward {
278
+ animation: flow-forward linear infinite;
279
+ }
280
+
281
+ @keyframes flow-forward {
282
+ to {
283
+ stroke-dashoffset: -15;
284
+ }
285
+ }
286
+
287
+ /* Flow animation - backward direction */
288
+ .edge-flow-backward {
289
+ animation: flow-backward linear infinite;
290
+ }
291
+
292
+ @keyframes flow-backward {
293
+ to {
294
+ stroke-dashoffset: 15;
295
+ }
296
+ }
297
+
298
+ /* Flow animation - bidirectional (alternating) */
299
+ .edge-flow-bidirectional {
300
+ animation: flow-bidirectional linear infinite alternate;
301
+ }
302
+
303
+ @keyframes flow-bidirectional {
304
+ 0% {
305
+ stroke-dashoffset: -15;
306
+ }
307
+ 100% {
308
+ stroke-dashoffset: 15;
309
+ }
310
+ }
311
+
312
+ /* Pulse animation - wave effect */
313
+ .edge-pulse {
314
+ animation: pulse ease-in-out infinite;
315
+ }
316
+
317
+ @keyframes pulse {
318
+ 0%, 100% {
319
+ opacity: 0.3;
320
+ stroke-width: inherit;
321
+ }
322
+ 50% {
323
+ opacity: 1;
324
+ stroke-width: calc(inherit + 2);
325
+ }
326
+ }
327
+
328
+ /* Glow animation - brief highlight */
329
+ .edge-glow {
330
+ animation: glow ease-out forwards;
331
+ }
332
+
333
+ @keyframes glow {
334
+ 0% {
335
+ opacity: 1;
336
+ }
337
+ 100% {
338
+ opacity: 0;
339
+ }
340
+ }
341
+ `}</style>
342
+ </>
343
+ );
344
+ };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import type { EdgeTypeDefinition } from '@principal-ai/principal-view-core';
3
+
4
+ export interface GenericEdgeProps {
5
+ /** Edge ID */
6
+ id: string;
7
+
8
+ /** Edge type definition from configuration */
9
+ typeDefinition: EdgeTypeDefinition;
10
+
11
+ /** Edge data */
12
+ data?: Record<string, unknown>;
13
+
14
+ /** Whether this edge has violations */
15
+ hasViolations?: boolean;
16
+ }
17
+
18
+ /**
19
+ * Generic edge renderer that adapts based on EdgeTypeDefinition
20
+ * TODO: Implement different styles, animations, and labels
21
+ */
22
+ export const GenericEdge: React.FC<GenericEdgeProps> = ({
23
+ id,
24
+ typeDefinition,
25
+ hasViolations,
26
+ }) => {
27
+ const color = hasViolations ? 'red' : (typeDefinition.color || '#888');
28
+
29
+ return (
30
+ <div>
31
+ {/* This is a placeholder - actual edge rendering happens in xyflow */}
32
+ <div style={{ fontSize: '10px', color }}>
33
+ Edge: {id} ({typeDefinition.style})
34
+ </div>
35
+ <div style={{ fontSize: '8px' }}>
36
+ <strong>TODO:</strong> Render in xyflow with proper styling
37
+ </div>
38
+ </div>
39
+ );
40
+ };
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Hook for processing path-based events and mapping them to animations
3
+ *
4
+ * Handles ComponentActivityEvent and ComponentActionEvent from
5
+ * PathBasedEventProcessor (Milestone 1 & 2)
6
+ */
7
+
8
+ import { useEffect, useCallback } from 'react';
9
+ import type {
10
+ ComponentActivityEvent,
11
+ ComponentActionEvent,
12
+ EdgeAnimationEvent,
13
+ PathBasedEvent
14
+ } from '@principal-ai/principal-view-core';
15
+ import {
16
+ logLevelToNodeAnimation,
17
+ actionToNodeAnimation,
18
+ actionToEdgeAnimation,
19
+ type NodeAnimation,
20
+ type EdgeAnimation
21
+ } from '../utils/animationMapping';
22
+
23
+ /**
24
+ * Animation update callbacks
25
+ */
26
+ export interface AnimationCallbacks {
27
+ onNodeAnimation: (nodeId: string, animation: NodeAnimation & { timestamp: number }) => void;
28
+ onEdgeAnimation: (edgeId: string, animation: EdgeAnimation & { timestamp: number }) => void;
29
+ }
30
+
31
+ /**
32
+ * Hook options
33
+ */
34
+ export interface UsePathBasedEventsOptions {
35
+ /** Path-based events to process */
36
+ events: PathBasedEvent[];
37
+
38
+ /** Callbacks for triggering animations */
39
+ callbacks: AnimationCallbacks;
40
+
41
+ /** Optional callback when event is processed */
42
+ onEventProcessed?: (event: PathBasedEvent) => void;
43
+
44
+ /** Minimum log level to trigger animations (default: 'info') */
45
+ minLogLevel?: 'debug' | 'info' | 'warn' | 'error';
46
+ }
47
+
48
+ /**
49
+ * Process path-based events and trigger appropriate animations
50
+ */
51
+ export function usePathBasedEvents({
52
+ events,
53
+ callbacks,
54
+ onEventProcessed,
55
+ minLogLevel = 'info'
56
+ }: UsePathBasedEventsOptions): void {
57
+ const { onNodeAnimation, onEdgeAnimation } = callbacks;
58
+
59
+ // Process component activity events (Milestone 1)
60
+ const processActivityEvent = useCallback((event: ComponentActivityEvent) => {
61
+ // Map log level to animation
62
+ const animation = logLevelToNodeAnimation(event.level);
63
+
64
+ // Trigger node animation
65
+ onNodeAnimation(event.componentId, {
66
+ ...animation,
67
+ timestamp: event.timestamp
68
+ });
69
+
70
+ onEventProcessed?.(event);
71
+ }, [onNodeAnimation, onEventProcessed]);
72
+
73
+ // Process component action events (Milestone 2)
74
+ const processActionEvent = useCallback((event: ComponentActionEvent) => {
75
+ // Map action/state to animation
76
+ const animation = actionToNodeAnimation(event.action, event.state);
77
+
78
+ // Trigger node animation
79
+ onNodeAnimation(event.componentId, {
80
+ ...animation,
81
+ timestamp: event.timestamp
82
+ });
83
+
84
+ onEventProcessed?.(event);
85
+ }, [onNodeAnimation, onEventProcessed]);
86
+
87
+ // Process edge animation events (Milestone 2)
88
+ const processEdgeAnimationEvent = useCallback((event: EdgeAnimationEvent) => {
89
+ const animation = actionToEdgeAnimation(event.triggeredBy?.action || 'unknown', {
90
+ type: event.animation,
91
+ duration: event.duration,
92
+ direction: event.direction
93
+ });
94
+
95
+ onEdgeAnimation(event.edgeId, {
96
+ ...animation,
97
+ timestamp: event.timestamp
98
+ });
99
+
100
+ onEventProcessed?.(event);
101
+ }, [onEdgeAnimation, onEventProcessed]);
102
+
103
+ // Process events when they change
104
+ useEffect(() => {
105
+ if (events.length === 0) return;
106
+
107
+ // Process the latest event
108
+ const latestEvent = events[events.length - 1];
109
+
110
+ switch (latestEvent.type) {
111
+ case 'component-activity':
112
+ // Milestone 1: Component activity from log
113
+ // Check if log level is high enough to trigger animation
114
+ const levels = ['debug', 'info', 'warn', 'error'];
115
+ if (levels.indexOf(latestEvent.level) >= levels.indexOf(minLogLevel)) {
116
+ processActivityEvent(latestEvent);
117
+ }
118
+ break;
119
+
120
+ case 'component-action':
121
+ // Milestone 2: Specific action from pattern match
122
+ processActionEvent(latestEvent);
123
+ break;
124
+
125
+ case 'edge-animation':
126
+ // Milestone 2: Edge activation from action trigger
127
+ processEdgeAnimationEvent(latestEvent);
128
+ break;
129
+ }
130
+ }, [events, processActivityEvent, processActionEvent, processEdgeAnimationEvent, minLogLevel]);
131
+ }
132
+
133
+ /**
134
+ * Process a batch of path-based events
135
+ * Useful for processing historical logs or bulk updates
136
+ */
137
+ export function processBatchEvents(
138
+ events: PathBasedEvent[],
139
+ callbacks: AnimationCallbacks,
140
+ minLogLevel: 'debug' | 'info' | 'warn' | 'error' = 'info'
141
+ ): void {
142
+ const levels = ['debug', 'info', 'warn', 'error'];
143
+ const minLevelIndex = levels.indexOf(minLogLevel);
144
+
145
+ for (const event of events) {
146
+ switch (event.type) {
147
+ case 'component-activity':
148
+ if (levels.indexOf(event.level) >= minLevelIndex) {
149
+ const animation = logLevelToNodeAnimation(event.level);
150
+ callbacks.onNodeAnimation(event.componentId, {
151
+ ...animation,
152
+ timestamp: event.timestamp
153
+ });
154
+ }
155
+ break;
156
+
157
+ case 'component-action':
158
+ {
159
+ const animation = actionToNodeAnimation(event.action, event.state);
160
+ callbacks.onNodeAnimation(event.componentId, {
161
+ ...animation,
162
+ timestamp: event.timestamp
163
+ });
164
+ }
165
+ break;
166
+
167
+ case 'edge-animation':
168
+ {
169
+ const animation = actionToEdgeAnimation(event.triggeredBy?.action || 'unknown', {
170
+ type: event.animation,
171
+ duration: event.duration,
172
+ direction: event.direction
173
+ });
174
+ callbacks.onEdgeAnimation(event.edgeId, {
175
+ ...animation,
176
+ timestamp: event.timestamp
177
+ });
178
+ }
179
+ break;
180
+ }
181
+ }
182
+ }
package/src/index.ts ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @principal-ai/visual-validation-react
3
+ * React components for graph-based visual validation framework
4
+ *
5
+ * This library provides UI building blocks for creating graph visualization panels.
6
+ * The actual "panel" application should be built separately using these components.
7
+ */
8
+
9
+ // Re-export types from core
10
+ export type {
11
+ GraphConfiguration,
12
+ GraphEvent,
13
+ GraphMetrics,
14
+ Violation,
15
+ Warning,
16
+ ValidationResult,
17
+ EventStream,
18
+ NodeTypeDefinition,
19
+ EdgeTypeDefinition,
20
+ ConnectionRule,
21
+ NodeState,
22
+ EdgeState,
23
+ ConfigurationFile,
24
+ ConfigurationLoadResult,
25
+ // Library types for loading .vgc/library.yaml
26
+ ComponentLibrary,
27
+ LibraryNodeComponent,
28
+ LibraryEdgeComponent,
29
+ } from '@principal-ai/principal-view-core';
30
+
31
+ // Export components
32
+ export { GraphRenderer } from './components/GraphRenderer';
33
+ export type { GraphRendererProps, GraphRendererHandle, NodePositionChange, PendingChanges } from './components/GraphRenderer';
34
+
35
+ export { EventLog } from './components/EventLog';
36
+ export type { EventLogProps } from './components/EventLog';
37
+
38
+ export { MetricsDashboard } from './components/MetricsDashboard';
39
+ export type { MetricsDashboardProps } from './components/MetricsDashboard';
40
+
41
+ export { EdgeInfoPanel } from './components/EdgeInfoPanel';
42
+ export type { EdgeInfoPanelProps } from './components/EdgeInfoPanel';
43
+
44
+ export { NodeInfoPanel } from './components/NodeInfoPanel';
45
+ export type { NodeInfoPanelProps } from './components/NodeInfoPanel';
46
+
47
+ export { ConfigurationSelector } from './components/ConfigurationSelector';
48
+ export type { ConfigurationSelectorProps } from './components/ConfigurationSelector';
49
+
50
+ // Export node/edge renderers
51
+ export { GenericNode } from './nodes/GenericNode';
52
+ export type { GenericNodeProps } from './nodes/GenericNode';
53
+
54
+ export { CustomNode } from './nodes/CustomNode';
55
+ export type { CustomNodeData } from './nodes/CustomNode';
56
+
57
+ export { GenericEdge } from './edges/GenericEdge';
58
+ export type { GenericEdgeProps } from './edges/GenericEdge';
59
+
60
+ export { CustomEdge } from './edges/CustomEdge';
61
+ export type { CustomEdgeData } from './edges/CustomEdge';
62
+
63
+ // Export utilities
64
+ export { convertToXYFlowNodes, convertToXYFlowEdges, autoLayoutNodes } from './utils/graphConverter';
65
+ export type { EdgeStateWithHandles } from './utils/graphConverter';
66
+ export { Icon, resolveIcon } from './utils/iconResolver';
67
+ export type { IconProps } from './utils/iconResolver';