@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.
Files changed (98) hide show
  1. package/dist/index.d.ts +7 -44
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -32
  4. package/dist/index.js.map +1 -1
  5. package/package.json +2 -2
  6. package/src/index.ts +9 -199
  7. package/src/components/ConfigurationSelector.tsx +0 -133
  8. package/src/components/GraphRenderer.tsx +0 -3195
  9. package/src/components/MultiCanvasRenderer.tsx +0 -295
  10. package/src/components/NodeTooltip.tsx +0 -279
  11. package/src/components/PendingChanges.test.tsx +0 -375
  12. package/src/components/SelectionSidebar.tsx +0 -352
  13. package/src/components/SequenceDiagramRenderer.tsx +0 -1156
  14. package/src/components/WorkflowSequenceDiagram.tsx +0 -270
  15. package/src/components/dashboard/DashboardRenderer.tsx +0 -317
  16. package/src/components/dashboard/MetricPanel.tsx +0 -254
  17. package/src/components/dashboard/MockDataProvider.ts +0 -330
  18. package/src/components/dashboard/components/BarChart.tsx +0 -299
  19. package/src/components/dashboard/components/LineChart.tsx +0 -279
  20. package/src/components/dashboard/components/MetricCard.tsx +0 -270
  21. package/src/components/dashboard/components/SourceLink.tsx +0 -63
  22. package/src/components/dashboard/components/TimeRangeSelector.tsx +0 -280
  23. package/src/components/dashboard/components/index.ts +0 -5
  24. package/src/components/dashboard/index.ts +0 -47
  25. package/src/components/dashboard/types.ts +0 -126
  26. package/src/components/state-view/PipelineView.tsx +0 -347
  27. package/src/components/state-view/index.ts +0 -14
  28. package/src/components/state-view/types.ts +0 -261
  29. package/src/components/state-view/useStateView.ts +0 -205
  30. package/src/contexts/GraphEditContext.tsx +0 -27
  31. package/src/contexts/TooltipPortalContext.ts +0 -8
  32. package/src/edges/CustomEdge.tsx +0 -402
  33. package/src/edges/GenericEdge.tsx +0 -36
  34. package/src/hooks/usePathBasedEvents.ts +0 -192
  35. package/src/hooks/useSequenceLayout.ts +0 -543
  36. package/src/hooks/useUndoRedo.ts +0 -144
  37. package/src/nodes/CustomNode.tsx +0 -1231
  38. package/src/nodes/GenericNode.tsx +0 -55
  39. package/src/nodes/otel/OtelBoundaryNode.tsx +0 -224
  40. package/src/nodes/otel/OtelEventNode.tsx +0 -225
  41. package/src/nodes/otel/OtelResourceNode.tsx +0 -251
  42. package/src/nodes/otel/OtelScopeNode.tsx +0 -221
  43. package/src/nodes/otel/OtelSpanConventionNode.tsx +0 -295
  44. package/src/nodes/otel/index.ts +0 -23
  45. package/src/nodes/otel/shared/NodeBadges.tsx +0 -295
  46. package/src/nodes/otel/shared/NodeContent.tsx +0 -204
  47. package/src/nodes/otel/shared/index.ts +0 -8
  48. package/src/nodes/otel/shared/types.ts +0 -138
  49. package/src/nodes/otel/shared/useNodeBehavior.ts +0 -114
  50. package/src/stories/Audit/BoundaryNodes.stories.tsx +0 -385
  51. package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +0 -1448
  52. package/src/stories/Audit/CanvasNodeTypes.stories.tsx +0 -1049
  53. package/src/stories/Audit/ColorPriority.stories.tsx +0 -895
  54. package/src/stories/Audit/DiamondBadges.stories.tsx +0 -629
  55. package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +0 -724
  56. package/src/stories/Audit/NodeFieldsAudit.stories.tsx +0 -996
  57. package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +0 -715
  58. package/src/stories/Audit/NodeShapes.stories.tsx +0 -1208
  59. package/src/stories/Audit/WideNodesTesting.stories.tsx +0 -1045
  60. package/src/stories/Components/DashboardRenderer.stories.tsx +0 -263
  61. package/src/stories/Components/GraphRenderer.stories.tsx +0 -3236
  62. package/src/stories/Components/MultiCanvasRenderer.stories.tsx +0 -370
  63. package/src/stories/Components/SequenceDiagram.stories.tsx +0 -255
  64. package/src/stories/Components/StateView.stories.tsx +0 -417
  65. package/src/stories/Components/TraceViewer.stories.tsx +0 -442
  66. package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +0 -802
  67. package/src/stories/Features/AnimationWorkshop.stories.tsx +0 -727
  68. package/src/stories/Features/ChangeTypeVisual.stories.tsx +0 -67
  69. package/src/stories/Features/EventDrivenAnimations.stories.tsx +0 -513
  70. package/src/stories/Features/MultiConfig.stories.tsx +0 -539
  71. package/src/stories/Layout/ElkEdgeRouting.stories.tsx +0 -539
  72. package/src/stories/Layout/GraphOrientation.stories.tsx +0 -500
  73. package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +0 -352
  74. package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +0 -560
  75. package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +0 -491
  76. package/src/stories/Nodes/NumericColors.stories.tsx +0 -440
  77. package/src/stories/OTEL/EventNameDisplay.stories.tsx +0 -694
  78. package/src/stories/OTEL/EventNameFallback.stories.tsx +0 -578
  79. package/src/stories/OTEL/OtelComponents.stories.tsx +0 -422
  80. package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +0 -806
  81. package/src/stories/OTEL/RawEventCards.stories.tsx +0 -57
  82. package/src/stories/OTEL/SpanBadges.stories.tsx +0 -840
  83. package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +0 -209
  84. package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +0 -721
  85. package/src/stories/RealWorld/FileCitySequence.stories.tsx +0 -516
  86. package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +0 -336
  87. package/src/stories/__traces__/test-run.canvas.json +0 -241
  88. package/src/stories/data/activity-feed-analytics.dashboard.json +0 -300
  89. package/src/stories/data/file-city-images-transformed.otel.canvas.json +0 -295
  90. package/src/stories/data/file-city-workflows.json +0 -269
  91. package/src/stories/data/graph-converter-test-execution.json +0 -443
  92. package/src/stories/data/graph-converter-test.workflow.json +0 -105
  93. package/src/stories/data/graph-converter-validated-execution.json +0 -58
  94. package/src/utils/animationMapping.ts +0 -166
  95. package/src/utils/canvasBounds.ts +0 -146
  96. package/src/utils/graphConverter.ts +0 -148
  97. package/src/utils/iconResolver.tsx +0 -53
  98. package/src/utils/orientationUtils.ts +0 -73
@@ -1,402 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
- import { BaseEdge, EdgeLabelRenderer, getSmoothStepPath } from '@xyflow/react';
3
- import type { EdgeProps, Edge } from '@xyflow/react';
4
- import type { EdgeTypeDefinition } from '@principal-ai/subsystems-core';
5
- import { useTheme } from '@principal-ade/industry-theme';
6
-
7
- export interface CustomEdgeData extends Record<string, unknown> {
8
- typeDefinition: EdgeTypeDefinition;
9
- hasViolations?: boolean;
10
- data?: Record<string, unknown>;
11
- edgeType?: string;
12
- // Animation control
13
- animationType?: 'flow' | 'particle' | 'pulse' | 'glow' | null;
14
- animationDuration?: number;
15
- animationDirection?: 'forward' | 'backward' | 'bidirectional';
16
- // Whether tooltips are enabled (defaults to true)
17
- tooltipsEnabled?: boolean;
18
- // Whether shift key is currently pressed (for tooltip control)
19
- shiftKeyPressed?: boolean;
20
- // ELK-computed path (circuit-board style routing)
21
- elkPath?: string;
22
- // ELK-computed label position
23
- elkLabelPosition?: { x: number; y: number };
24
- }
25
-
26
- /**
27
- * Custom edge component for xyflow that renders based on EdgeTypeDefinition
28
- */
29
- export const CustomEdge: React.FC<EdgeProps<Edge<CustomEdgeData>>> = ({
30
- id,
31
- sourceX,
32
- sourceY,
33
- targetX,
34
- targetY,
35
- sourcePosition,
36
- targetPosition,
37
- data,
38
- markerEnd,
39
- selected,
40
- }) => {
41
- const { theme } = useTheme();
42
- const edgeProps = data;
43
- const {
44
- typeDefinition,
45
- hasViolations,
46
- data: edgeData,
47
- edgeType,
48
- animationType,
49
- animationDuration = 1000,
50
- animationDirection = 'forward',
51
- tooltipsEnabled = true,
52
- shiftKeyPressed = false,
53
- elkPath,
54
- elkLabelPosition,
55
- } = edgeProps || ({} as CustomEdgeData);
56
-
57
- const [particlePosition, setParticlePosition] = useState(0);
58
- const [isHovered, setIsHovered] = useState(false);
59
- const pathRef = useRef<SVGPathElement>(null);
60
-
61
- // Particle animation effect
62
- useEffect(() => {
63
- if (animationType !== 'particle') return;
64
-
65
- const animate = () => {
66
- setParticlePosition((prev) => {
67
- const next = prev + (100 / animationDuration) * 16; // ~60fps
68
- return next >= 100 ? 0 : next;
69
- });
70
- };
71
-
72
- const intervalId = setInterval(animate, 16);
73
- return () => clearInterval(intervalId);
74
- }, [animationType, animationDuration]);
75
-
76
- // Early return after hooks
77
- if (!typeDefinition) {
78
- return null;
79
- }
80
-
81
- // Color priority: violations > edge data color > type definition color > default
82
- const edgeColor = edgeData?.color as string | undefined;
83
- const color = hasViolations ? '#D0021B' : edgeColor || typeDefinition.color || '#888';
84
- const width = typeDefinition.width || 2;
85
-
86
- // Get edge path - use ELK path if available, otherwise fall back to SmoothStep
87
- const [defaultPath, defaultLabelX, defaultLabelY] = getSmoothStepPath({
88
- sourceX,
89
- sourceY,
90
- sourcePosition,
91
- targetX,
92
- targetY,
93
- targetPosition,
94
- borderRadius: 0,
95
- offset: 20,
96
- });
97
-
98
- // Use ELK-computed path for circuit-board style routing when available
99
- const edgePath = elkPath || defaultPath;
100
- const labelX = elkLabelPosition?.x ?? defaultLabelX;
101
- const labelY = elkLabelPosition?.y ?? defaultLabelY;
102
-
103
- // Style based on edge type
104
- const getStrokeStyle = () => {
105
- switch (typeDefinition.style) {
106
- case 'dashed':
107
- return '5 5';
108
- case 'dotted':
109
- return '2 2';
110
- default:
111
- return 'none';
112
- }
113
- };
114
-
115
- // Label configuration
116
- const labelConfig = typeDefinition.label;
117
- const labelField = labelConfig?.field;
118
- const labelText = labelField && edgeData?.[labelField] ? String(edgeData[labelField]) : '';
119
-
120
- // Animation-specific rendering helpers
121
- const getAnimationClass = () => {
122
- if (!animationType) {
123
- return typeDefinition.style === 'animated' ? 'edge-flow-forward' : '';
124
- }
125
-
126
- switch (animationType) {
127
- case 'flow':
128
- return animationDirection === 'backward'
129
- ? 'edge-flow-backward'
130
- : animationDirection === 'bidirectional'
131
- ? 'edge-flow-bidirectional'
132
- : 'edge-flow-forward';
133
- case 'pulse':
134
- return 'edge-pulse';
135
- case 'glow':
136
- return 'edge-glow';
137
- default:
138
- return '';
139
- }
140
- };
141
-
142
- const getAnimationDurationStyle = () => {
143
- if (!animationType) return {};
144
- return {
145
- animationDuration: `${animationDuration}ms`,
146
- };
147
- };
148
-
149
- // Calculate particle position along path using SVG path methods
150
- const getParticleTransform = () => {
151
- if (!pathRef.current) {
152
- // Fallback to linear interpolation if path ref not available yet
153
- const progress =
154
- animationDirection === 'backward' ? 1 - particlePosition / 100 : particlePosition / 100;
155
- const x = sourceX + (targetX - sourceX) * progress;
156
- const y = sourceY + (targetY - sourceY) * progress;
157
- return { x, y };
158
- }
159
-
160
- // Use actual path to get point along the curve
161
- const pathLength = pathRef.current.getTotalLength();
162
- const progress =
163
- animationDirection === 'backward' ? 1 - particlePosition / 100 : particlePosition / 100;
164
- const distance = pathLength * progress;
165
- const point = pathRef.current.getPointAtLength(distance);
166
-
167
- return { x: point.x, y: point.y };
168
- };
169
-
170
- const particlePos = animationType === 'particle' ? getParticleTransform() : null;
171
-
172
- return (
173
- <g
174
- onMouseEnter={() => setIsHovered(true)}
175
- onMouseLeave={() => setIsHovered(false)}
176
- style={{ cursor: 'pointer' }}
177
- >
178
- {/* Hidden path for particle position calculation */}
179
- <path
180
- ref={pathRef}
181
- d={edgePath}
182
- fill="none"
183
- stroke="transparent"
184
- strokeWidth={0}
185
- style={{ pointerEvents: 'none' }}
186
- />
187
-
188
- {/* Invisible wide path for easier clicking */}
189
- <path
190
- d={edgePath}
191
- fill="none"
192
- stroke="transparent"
193
- strokeWidth={Math.max(width + 10, 20)}
194
- style={{ pointerEvents: 'stroke' }}
195
- />
196
-
197
- <BaseEdge
198
- id={id}
199
- path={edgePath}
200
- markerEnd={markerEnd as string}
201
- style={{
202
- stroke: color,
203
- strokeWidth: selected ? width + 2 : width,
204
- strokeDasharray: getStrokeStyle(),
205
- opacity: animationType ? 0.7 : 1,
206
- cursor: 'pointer',
207
- }}
208
- />
209
-
210
- {/* Flow Animation - Animated dashed overlay */}
211
- {animationType === 'flow' && (
212
- <path
213
- d={edgePath}
214
- fill="none"
215
- stroke={typeDefinition.animation?.color || color}
216
- strokeWidth={width}
217
- strokeDasharray="10 5"
218
- className={getAnimationClass()}
219
- style={{
220
- ...getAnimationDurationStyle(),
221
- opacity: 0.8,
222
- }}
223
- />
224
- )}
225
-
226
- {/* Pulse Animation - Wave effect */}
227
- {animationType === 'pulse' && (
228
- <path
229
- d={edgePath}
230
- fill="none"
231
- stroke={typeDefinition.animation?.color || color}
232
- strokeWidth={width + 2}
233
- className={getAnimationClass()}
234
- style={{
235
- ...getAnimationDurationStyle(),
236
- }}
237
- />
238
- )}
239
-
240
- {/* Glow Animation - Brief highlight */}
241
- {animationType === 'glow' && (
242
- <path
243
- d={edgePath}
244
- fill="none"
245
- stroke={typeDefinition.animation?.color || color}
246
- strokeWidth={width + 4}
247
- className={getAnimationClass()}
248
- style={{
249
- ...getAnimationDurationStyle(),
250
- filter: 'blur(3px)',
251
- }}
252
- />
253
- )}
254
-
255
- {/* Particle Animation - Traveling dot */}
256
- {animationType === 'particle' && particlePos && (
257
- <circle
258
- cx={particlePos.x}
259
- cy={particlePos.y}
260
- r={width * 1.5}
261
- fill={typeDefinition.animation?.color || color}
262
- style={{
263
- filter: 'drop-shadow(0 0 3px rgba(0,0,0,0.3))',
264
- }}
265
- />
266
- )}
267
-
268
- {/* Fallback: Legacy animated style support */}
269
- {!animationType && typeDefinition.style === 'animated' && (
270
- <path
271
- d={edgePath}
272
- fill="none"
273
- stroke={typeDefinition.animation?.color || color}
274
- strokeWidth={width}
275
- strokeDasharray="5 5"
276
- className="edge-flow-forward"
277
- style={{
278
- animationDuration: '500ms',
279
- }}
280
- />
281
- )}
282
-
283
- {/* Label */}
284
- {labelText && (
285
- <EdgeLabelRenderer>
286
- <div
287
- style={{
288
- position: 'absolute',
289
- transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
290
- backgroundColor: 'white',
291
- padding: '2px 6px',
292
- borderRadius: '4px',
293
- fontSize: theme.fontSizes[0],
294
- fontWeight: theme.fontWeights.medium,
295
- fontFamily: theme.fonts.body,
296
- border: `1px solid ${color}`,
297
- pointerEvents: 'all',
298
- zIndex: 1000,
299
- }}
300
- className="nodrag nopan"
301
- >
302
- {labelText}
303
- </div>
304
- </EdgeLabelRenderer>
305
- )}
306
-
307
- {/* Hover tooltip showing edge type - only shown when shift key is pressed */}
308
- {tooltipsEnabled && isHovered && shiftKeyPressed && (
309
- <EdgeLabelRenderer>
310
- <div
311
- style={{
312
- position: 'absolute',
313
- transform: `translate(-50%, -100%) translate(${labelX}px,${labelY - 12}px)`,
314
- backgroundColor: 'rgba(0, 0, 0, 0.85)',
315
- color: 'white',
316
- padding: '4px 8px',
317
- borderRadius: '4px',
318
- fontSize: theme.fontSizes[0],
319
- fontWeight: theme.fontWeights.medium,
320
- fontFamily: theme.fonts.body,
321
- whiteSpace: 'nowrap',
322
- pointerEvents: 'none',
323
- zIndex: 1000,
324
- boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
325
- }}
326
- >
327
- {edgeType || 'edge'}
328
- </div>
329
- </EdgeLabelRenderer>
330
- )}
331
-
332
- {/* CSS animations for all edge animation types */}
333
- <style>{`
334
- /* Flow animation - forward direction */
335
- .edge-flow-forward {
336
- animation: flow-forward linear infinite;
337
- }
338
-
339
- @keyframes flow-forward {
340
- to {
341
- stroke-dashoffset: -15;
342
- }
343
- }
344
-
345
- /* Flow animation - backward direction */
346
- .edge-flow-backward {
347
- animation: flow-backward linear infinite;
348
- }
349
-
350
- @keyframes flow-backward {
351
- to {
352
- stroke-dashoffset: 15;
353
- }
354
- }
355
-
356
- /* Flow animation - bidirectional (alternating) */
357
- .edge-flow-bidirectional {
358
- animation: flow-bidirectional linear infinite alternate;
359
- }
360
-
361
- @keyframes flow-bidirectional {
362
- 0% {
363
- stroke-dashoffset: -15;
364
- }
365
- 100% {
366
- stroke-dashoffset: 15;
367
- }
368
- }
369
-
370
- /* Pulse animation - wave effect */
371
- .edge-pulse {
372
- animation: pulse ease-in-out infinite;
373
- }
374
-
375
- @keyframes pulse {
376
- 0%, 100% {
377
- opacity: 0.3;
378
- stroke-width: inherit;
379
- }
380
- 50% {
381
- opacity: 1;
382
- stroke-width: calc(inherit + 2);
383
- }
384
- }
385
-
386
- /* Glow animation - brief highlight */
387
- .edge-glow {
388
- animation: glow ease-out forwards;
389
- }
390
-
391
- @keyframes glow {
392
- 0% {
393
- opacity: 1;
394
- }
395
- 100% {
396
- opacity: 0;
397
- }
398
- }
399
- `}</style>
400
- </g>
401
- );
402
- };
@@ -1,36 +0,0 @@
1
- import React from 'react';
2
- import type { EdgeTypeDefinition } from '@principal-ai/subsystems-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> = ({ id, typeDefinition, hasViolations }) => {
23
- const color = hasViolations ? 'red' : typeDefinition.color || '#888';
24
-
25
- return (
26
- <div>
27
- {/* This is a placeholder - actual edge rendering happens in xyflow */}
28
- <div style={{ fontSize: '10px', color }}>
29
- Edge: {id} ({typeDefinition.style})
30
- </div>
31
- <div style={{ fontSize: '8px' }}>
32
- <strong>TODO:</strong> Render in xyflow with proper styling
33
- </div>
34
- </div>
35
- );
36
- };
@@ -1,192 +0,0 @@
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/subsystems-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(
61
- (event: ComponentActivityEvent) => {
62
- // Map log level to animation
63
- const animation = logLevelToNodeAnimation(event.level);
64
-
65
- // Trigger node animation
66
- onNodeAnimation(event.componentId, {
67
- ...animation,
68
- timestamp: event.timestamp,
69
- });
70
-
71
- onEventProcessed?.(event);
72
- },
73
- [onNodeAnimation, onEventProcessed]
74
- );
75
-
76
- // Process component action events (Milestone 2)
77
- const processActionEvent = useCallback(
78
- (event: ComponentActionEvent) => {
79
- // Map action/state to animation
80
- const animation = actionToNodeAnimation(event.action, event.state);
81
-
82
- // Trigger node animation
83
- onNodeAnimation(event.componentId, {
84
- ...animation,
85
- timestamp: event.timestamp,
86
- });
87
-
88
- onEventProcessed?.(event);
89
- },
90
- [onNodeAnimation, onEventProcessed]
91
- );
92
-
93
- // Process edge animation events (Milestone 2)
94
- const processEdgeAnimationEvent = useCallback(
95
- (event: EdgeAnimationEvent) => {
96
- const animation = actionToEdgeAnimation(event.triggeredBy?.action || 'unknown', {
97
- type: event.animation,
98
- duration: event.duration,
99
- direction: event.direction,
100
- });
101
-
102
- onEdgeAnimation(event.edgeId, {
103
- ...animation,
104
- timestamp: event.timestamp,
105
- });
106
-
107
- onEventProcessed?.(event);
108
- },
109
- [onEdgeAnimation, onEventProcessed]
110
- );
111
-
112
- // Process events when they change
113
- useEffect(() => {
114
- if (events.length === 0) return;
115
-
116
- // Process the latest event
117
- const latestEvent = events[events.length - 1];
118
-
119
- switch (latestEvent.type) {
120
- case 'component-activity': {
121
- // Milestone 1: Component activity from log
122
- // Check if log level is high enough to trigger animation
123
- const levels = ['debug', 'info', 'warn', 'error'];
124
- if (levels.indexOf(latestEvent.level) >= levels.indexOf(minLogLevel)) {
125
- processActivityEvent(latestEvent);
126
- }
127
- break;
128
- }
129
-
130
- case 'component-action':
131
- // Milestone 2: Specific action from pattern match
132
- processActionEvent(latestEvent);
133
- break;
134
-
135
- case 'edge-animation':
136
- // Milestone 2: Edge activation from action trigger
137
- processEdgeAnimationEvent(latestEvent);
138
- break;
139
- }
140
- }, [events, processActivityEvent, processActionEvent, processEdgeAnimationEvent, minLogLevel]);
141
- }
142
-
143
- /**
144
- * Process a batch of path-based events
145
- * Useful for processing historical logs or bulk updates
146
- */
147
- export function processBatchEvents(
148
- events: PathBasedEvent[],
149
- callbacks: AnimationCallbacks,
150
- minLogLevel: 'debug' | 'info' | 'warn' | 'error' = 'info'
151
- ): void {
152
- const levels = ['debug', 'info', 'warn', 'error'];
153
- const minLevelIndex = levels.indexOf(minLogLevel);
154
-
155
- for (const event of events) {
156
- switch (event.type) {
157
- case 'component-activity':
158
- if (levels.indexOf(event.level) >= minLevelIndex) {
159
- const animation = logLevelToNodeAnimation(event.level);
160
- callbacks.onNodeAnimation(event.componentId, {
161
- ...animation,
162
- timestamp: event.timestamp,
163
- });
164
- }
165
- break;
166
-
167
- case 'component-action':
168
- {
169
- const animation = actionToNodeAnimation(event.action, event.state);
170
- callbacks.onNodeAnimation(event.componentId, {
171
- ...animation,
172
- timestamp: event.timestamp,
173
- });
174
- }
175
- break;
176
-
177
- case 'edge-animation':
178
- {
179
- const animation = actionToEdgeAnimation(event.triggeredBy?.action || 'unknown', {
180
- type: event.animation,
181
- duration: event.duration,
182
- direction: event.direction,
183
- });
184
- callbacks.onEdgeAnimation(event.edgeId, {
185
- ...animation,
186
- timestamp: event.timestamp,
187
- });
188
- }
189
- break;
190
- }
191
- }
192
- }