@principal-ai/subsystems-react 0.17.0 → 0.19.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 (108) hide show
  1. package/README.md +11 -85
  2. package/dist/index.d.ts +7 -44
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -32
  5. package/dist/index.js.map +1 -1
  6. package/dist/subsystem/SubsystemCarousel.d.ts +36 -0
  7. package/dist/subsystem/SubsystemCarousel.d.ts.map +1 -0
  8. package/dist/subsystem/SubsystemCarousel.js +163 -0
  9. package/dist/subsystem/SubsystemCarousel.js.map +1 -0
  10. package/dist/subsystem/SubsystemComponentGraph.d.ts +25 -0
  11. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  12. package/dist/subsystem/SubsystemComponentGraph.js +329 -68
  13. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  14. package/package.json +4 -4
  15. package/src/index.ts +9 -199
  16. package/src/subsystem/SubsystemComponentGraph.tsx +396 -50
  17. package/src/components/ConfigurationSelector.tsx +0 -133
  18. package/src/components/GraphRenderer.tsx +0 -3195
  19. package/src/components/MultiCanvasRenderer.tsx +0 -295
  20. package/src/components/NodeTooltip.tsx +0 -279
  21. package/src/components/PendingChanges.test.tsx +0 -375
  22. package/src/components/SelectionSidebar.tsx +0 -352
  23. package/src/components/SequenceDiagramRenderer.tsx +0 -1156
  24. package/src/components/WorkflowSequenceDiagram.tsx +0 -270
  25. package/src/components/dashboard/DashboardRenderer.tsx +0 -317
  26. package/src/components/dashboard/MetricPanel.tsx +0 -254
  27. package/src/components/dashboard/MockDataProvider.ts +0 -330
  28. package/src/components/dashboard/components/BarChart.tsx +0 -299
  29. package/src/components/dashboard/components/LineChart.tsx +0 -279
  30. package/src/components/dashboard/components/MetricCard.tsx +0 -270
  31. package/src/components/dashboard/components/SourceLink.tsx +0 -63
  32. package/src/components/dashboard/components/TimeRangeSelector.tsx +0 -280
  33. package/src/components/dashboard/components/index.ts +0 -5
  34. package/src/components/dashboard/index.ts +0 -47
  35. package/src/components/dashboard/types.ts +0 -126
  36. package/src/components/state-view/PipelineView.tsx +0 -347
  37. package/src/components/state-view/index.ts +0 -14
  38. package/src/components/state-view/types.ts +0 -261
  39. package/src/components/state-view/useStateView.ts +0 -205
  40. package/src/contexts/GraphEditContext.tsx +0 -27
  41. package/src/contexts/TooltipPortalContext.ts +0 -8
  42. package/src/edges/CustomEdge.tsx +0 -402
  43. package/src/edges/GenericEdge.tsx +0 -36
  44. package/src/hooks/usePathBasedEvents.ts +0 -192
  45. package/src/hooks/useSequenceLayout.ts +0 -543
  46. package/src/hooks/useUndoRedo.ts +0 -144
  47. package/src/nodes/CustomNode.tsx +0 -1231
  48. package/src/nodes/GenericNode.tsx +0 -55
  49. package/src/nodes/otel/OtelBoundaryNode.tsx +0 -224
  50. package/src/nodes/otel/OtelEventNode.tsx +0 -225
  51. package/src/nodes/otel/OtelResourceNode.tsx +0 -251
  52. package/src/nodes/otel/OtelScopeNode.tsx +0 -221
  53. package/src/nodes/otel/OtelSpanConventionNode.tsx +0 -295
  54. package/src/nodes/otel/index.ts +0 -23
  55. package/src/nodes/otel/shared/NodeBadges.tsx +0 -295
  56. package/src/nodes/otel/shared/NodeContent.tsx +0 -204
  57. package/src/nodes/otel/shared/index.ts +0 -8
  58. package/src/nodes/otel/shared/types.ts +0 -138
  59. package/src/nodes/otel/shared/useNodeBehavior.ts +0 -114
  60. package/src/stories/Audit/BoundaryNodes.stories.tsx +0 -385
  61. package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +0 -1448
  62. package/src/stories/Audit/CanvasNodeTypes.stories.tsx +0 -1049
  63. package/src/stories/Audit/ColorPriority.stories.tsx +0 -895
  64. package/src/stories/Audit/DiamondBadges.stories.tsx +0 -629
  65. package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +0 -724
  66. package/src/stories/Audit/NodeFieldsAudit.stories.tsx +0 -996
  67. package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +0 -715
  68. package/src/stories/Audit/NodeShapes.stories.tsx +0 -1208
  69. package/src/stories/Audit/WideNodesTesting.stories.tsx +0 -1045
  70. package/src/stories/Components/DashboardRenderer.stories.tsx +0 -263
  71. package/src/stories/Components/GraphRenderer.stories.tsx +0 -3236
  72. package/src/stories/Components/MultiCanvasRenderer.stories.tsx +0 -370
  73. package/src/stories/Components/SequenceDiagram.stories.tsx +0 -255
  74. package/src/stories/Components/StateView.stories.tsx +0 -417
  75. package/src/stories/Components/TraceViewer.stories.tsx +0 -442
  76. package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +0 -802
  77. package/src/stories/Features/AnimationWorkshop.stories.tsx +0 -727
  78. package/src/stories/Features/ChangeTypeVisual.stories.tsx +0 -67
  79. package/src/stories/Features/EventDrivenAnimations.stories.tsx +0 -513
  80. package/src/stories/Features/MultiConfig.stories.tsx +0 -539
  81. package/src/stories/Layout/ElkEdgeRouting.stories.tsx +0 -539
  82. package/src/stories/Layout/GraphOrientation.stories.tsx +0 -500
  83. package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +0 -352
  84. package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +0 -560
  85. package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +0 -491
  86. package/src/stories/Nodes/NumericColors.stories.tsx +0 -440
  87. package/src/stories/OTEL/EventNameDisplay.stories.tsx +0 -694
  88. package/src/stories/OTEL/EventNameFallback.stories.tsx +0 -578
  89. package/src/stories/OTEL/OtelComponents.stories.tsx +0 -422
  90. package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +0 -806
  91. package/src/stories/OTEL/RawEventCards.stories.tsx +0 -57
  92. package/src/stories/OTEL/SpanBadges.stories.tsx +0 -840
  93. package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +0 -209
  94. package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +0 -721
  95. package/src/stories/RealWorld/FileCitySequence.stories.tsx +0 -516
  96. package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +0 -336
  97. package/src/stories/__traces__/test-run.canvas.json +0 -241
  98. package/src/stories/data/activity-feed-analytics.dashboard.json +0 -300
  99. package/src/stories/data/file-city-images-transformed.otel.canvas.json +0 -295
  100. package/src/stories/data/file-city-workflows.json +0 -269
  101. package/src/stories/data/graph-converter-test-execution.json +0 -443
  102. package/src/stories/data/graph-converter-test.workflow.json +0 -105
  103. package/src/stories/data/graph-converter-validated-execution.json +0 -58
  104. package/src/utils/animationMapping.ts +0 -166
  105. package/src/utils/canvasBounds.ts +0 -146
  106. package/src/utils/graphConverter.ts +0 -148
  107. package/src/utils/iconResolver.tsx +0 -53
  108. package/src/utils/orientationUtils.ts +0 -73
@@ -1,295 +0,0 @@
1
- /**
2
- * OTEL Span Convention Node
3
- *
4
- * Renders span convention nodes as hexagons with:
5
- * - Span pattern identifier
6
- * - Workflow chips showing which workflows use this span
7
- * - Status, sources, and references badges
8
- */
9
-
10
- import React from 'react';
11
- import { Handle, Position, NodeResizer } from '@xyflow/react';
12
- import type { NodeProps, Node } from '@xyflow/react';
13
- import { useTheme } from '@principal-ade/industry-theme';
14
- import { NodeTooltip } from '../../components/NodeTooltip';
15
- import type { OtelInfo } from '../../components/NodeTooltip';
16
- import { NodeBadges } from './shared/NodeBadges';
17
- import { NodeContent } from './shared/NodeContent';
18
- import { useNodeBehavior } from './shared/useNodeBehavior';
19
- import type { WorkflowChip } from './shared/types';
20
-
21
- export interface OtelSpanConventionNodeData extends Record<string, unknown> {
22
- name: string;
23
- typeDefinition: {
24
- color?: string;
25
- icon?: string;
26
- states?: Record<string, { color?: string; label?: string; icon?: string }>;
27
- };
28
- state?: string;
29
- hasViolations?: boolean;
30
- data: {
31
- color?: string;
32
- scopeColor?: string;
33
- spanColor?: string;
34
- stroke?: string;
35
- icon?: string;
36
- status?: 'draft' | 'approved' | 'implemented';
37
- description?: string;
38
- sources?: string[];
39
- references?: string[];
40
- states?: Record<string, { color?: string; label?: string; icon?: string }>;
41
- otel?: {
42
- spanPattern?: string;
43
- files?: string[];
44
- };
45
- /** Workflow chips to display */
46
- workflowChips?: WorkflowChip[];
47
- /** Callback when workflow chip is clicked */
48
- onWorkflowChipClick?: (chipId: string) => void;
49
- /** Currently selected workflow ID */
50
- selectedWorkflowId?: string;
51
- };
52
- editable?: boolean;
53
- tooltipsEnabled?: boolean;
54
- shiftKeyPressed?: boolean;
55
- isHighlighted?: boolean;
56
- isActive?: boolean;
57
- isHidden?: boolean;
58
- animationType?: 'pulse' | 'flash' | 'shake' | 'entry' | null;
59
- animationDuration?: number;
60
- }
61
-
62
- export const OtelSpanConventionNode: React.FC<
63
- NodeProps<Node<OtelSpanConventionNodeData>>
64
- > = ({ data, selected, dragging }) => {
65
- const { theme } = useTheme();
66
- const nodeProps = data;
67
- const {
68
- typeDefinition,
69
- state,
70
- hasViolations,
71
- data: nodeData,
72
- editable = false,
73
- tooltipsEnabled = true,
74
- shiftKeyPressed = false,
75
- isHighlighted = false,
76
- isActive = true,
77
- isHidden = false,
78
- animationType,
79
- animationDuration: _animationDuration = 1000,
80
- } = nodeProps;
81
-
82
- const {
83
- nodeRef,
84
- showTooltip,
85
- handleMouseDown,
86
- handleMouseEnter,
87
- handleMouseLeave,
88
- handleResizeEnd,
89
- } = useNodeBehavior({
90
- editable,
91
- tooltipsEnabled,
92
- shiftKeyPressed,
93
- selected,
94
- dragging,
95
- });
96
-
97
- // Calculate opacity based on node state
98
- const nodeOpacity = isHidden ? 0.4 : isActive ? 1 : 0.1;
99
-
100
- // Color resolution
101
- const scopeColor = nodeData.scopeColor as string | undefined;
102
- const spanColor = nodeData.spanColor as string | undefined;
103
- const nodeDataColor = nodeData.color as string | undefined;
104
- // Fill color priority: explicit color > scope color > type definition color > default purple
105
- const baseFillColor = nodeDataColor || scopeColor || typeDefinition.color || '#8b5cf6';
106
- const fillColor = baseFillColor;
107
- // Stroke color priority: explicit stroke > span color (workflow context) > fill color
108
- const nodeDataStroke = nodeData.stroke as string | undefined;
109
- const strokeColor = nodeDataStroke || spanColor || fillColor;
110
-
111
- // Get display info
112
- const displayName = nodeProps.name;
113
- const otelData = nodeData.otel;
114
- const identifier = otelData?.spanPattern;
115
-
116
- // Extract badge data
117
- const status = nodeData.status;
118
- const sourceFiles = otelData?.files || nodeData.sources;
119
- const references = nodeData.references;
120
- const description = nodeData.description;
121
-
122
- // Icon priority
123
- const icon =
124
- (nodeData.icon as string) ||
125
- (state && nodeData.states?.[state]?.icon) ||
126
- (state && typeDefinition.states?.[state]?.icon) ||
127
- typeDefinition.icon;
128
-
129
- // State definitions for label lookup
130
- const stateDefinitions = nodeData.states || typeDefinition.states;
131
-
132
- // Workflow chips
133
- const workflowChips = nodeData?.workflowChips;
134
- const onWorkflowChipClick = nodeData?.onWorkflowChipClick;
135
- const selectedWorkflowId = nodeData?.selectedWorkflowId;
136
-
137
- // Animation class
138
- const getAnimationClass = () => {
139
- switch (animationType) {
140
- case 'pulse':
141
- return 'node-pulse';
142
- case 'flash':
143
- return 'node-flash';
144
- case 'shake':
145
- return 'node-shake';
146
- case 'entry':
147
- return 'node-entry';
148
- default:
149
- return '';
150
- }
151
- };
152
-
153
- // Hexagon styles
154
- const hexagonClipPath = 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)';
155
- const hexagonBorderWidth = 2;
156
-
157
- const hexagonBorderStyle: React.CSSProperties = {
158
- position: 'relative',
159
- clipPath: hexagonClipPath,
160
- backgroundColor: hasViolations ? '#D0021B' : strokeColor,
161
- width: '100%',
162
- height: '100%',
163
- minWidth: 20,
164
- minHeight: 20,
165
- boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
166
- opacity: nodeOpacity,
167
- transition: 'opacity 0.3s ease',
168
- boxSizing: 'border-box',
169
- };
170
-
171
- const hexagonInnerStyle: React.CSSProperties = {
172
- position: 'absolute',
173
- top: hexagonBorderWidth,
174
- left: hexagonBorderWidth,
175
- right: hexagonBorderWidth,
176
- bottom: hexagonBorderWidth,
177
- clipPath: hexagonClipPath,
178
- backgroundColor: fillColor,
179
- color: '#000',
180
- display: 'flex',
181
- flexDirection: 'column',
182
- alignItems: 'center',
183
- justifyContent: 'center',
184
- fontSize: theme.fontSizes[0],
185
- fontWeight: theme.fontWeights.medium,
186
- fontFamily: theme.fonts.body,
187
- gap: '4px',
188
- padding: '8px 20px',
189
- };
190
-
191
- // Handle styles
192
- const handleStyle = editable
193
- ? {
194
- background: fillColor,
195
- width: 12,
196
- height: 12,
197
- border: '2px solid white',
198
- boxShadow: '0 0 0 1px ' + fillColor,
199
- zIndex: 10,
200
- }
201
- : {
202
- background: fillColor,
203
- width: 8,
204
- height: 8,
205
- opacity: 0,
206
- pointerEvents: 'none' as const,
207
- zIndex: 10,
208
- };
209
-
210
- return (
211
- <>
212
- {/* Node Resizer */}
213
- {editable && (
214
- <NodeResizer
215
- color={strokeColor}
216
- isVisible={selected}
217
- minWidth={40}
218
- minHeight={30}
219
- onResizeEnd={handleResizeEnd}
220
- handleStyle={{ width: 8, height: 8, borderRadius: 2, zIndex: 20 }}
221
- lineStyle={{ borderWidth: 1, zIndex: 20 }}
222
- />
223
- )}
224
-
225
- {/* Input handles */}
226
- <Handle type="target" position={Position.Top} id="top" style={handleStyle} />
227
- <Handle type="target" position={Position.Bottom} id="bottom" style={handleStyle} />
228
- <Handle type="target" position={Position.Left} id="left" style={handleStyle} />
229
- <Handle type="target" position={Position.Right} id="right" style={handleStyle} />
230
-
231
- {/* Hexagon node */}
232
- <div
233
- ref={nodeRef}
234
- style={{
235
- position: 'relative',
236
- width: '100%',
237
- height: '100%',
238
- boxShadow: isHighlighted
239
- ? '0 0 0 3px #3b82f6, 0 0 20px rgba(59, 130, 246, 0.5)'
240
- : selected
241
- ? `0 0 0 2px ${strokeColor}`
242
- : 'none',
243
- transition: 'box-shadow 0.2s ease',
244
- }}
245
- onMouseDown={handleMouseDown}
246
- onMouseEnter={handleMouseEnter}
247
- onMouseLeave={handleMouseLeave}
248
- >
249
- {/* Badges */}
250
- <NodeBadges
251
- shape="hexagon"
252
- status={status}
253
- sourceFiles={sourceFiles}
254
- references={references}
255
- opacity={nodeOpacity}
256
- />
257
-
258
- {/* Hexagon shape */}
259
- <div style={hexagonBorderStyle} className={getAnimationClass()}>
260
- <div style={hexagonInnerStyle}>
261
- <NodeContent
262
- displayName={displayName}
263
- identifier={identifier}
264
- icon={icon}
265
- state={state}
266
- stateDefinitions={stateDefinitions}
267
- hasViolations={hasViolations}
268
- workflowChips={workflowChips}
269
- onWorkflowChipClick={onWorkflowChipClick}
270
- selectedWorkflowId={selectedWorkflowId}
271
- />
272
- </div>
273
- </div>
274
-
275
- {/* Tooltip */}
276
- {tooltipsEnabled && (
277
- <NodeTooltip
278
- description={description}
279
- otel={nodeData.otel as OtelInfo}
280
- sources={nodeData.sources}
281
- references={references}
282
- visible={showTooltip}
283
- nodeRef={nodeRef}
284
- />
285
- )}
286
- </div>
287
-
288
- {/* Output handles */}
289
- <Handle type="source" position={Position.Top} id="top-out" style={handleStyle} />
290
- <Handle type="source" position={Position.Bottom} id="bottom-out" style={handleStyle} />
291
- <Handle type="source" position={Position.Left} id="left-out" style={handleStyle} />
292
- <Handle type="source" position={Position.Right} id="right-out" style={handleStyle} />
293
- </>
294
- );
295
- };
@@ -1,23 +0,0 @@
1
- /**
2
- * OTEL Node Components
3
- *
4
- * Specialized node components for different OpenTelemetry concepts.
5
- */
6
-
7
- export { OtelSpanConventionNode } from './OtelSpanConventionNode';
8
- export type { OtelSpanConventionNodeData } from './OtelSpanConventionNode';
9
-
10
- export { OtelEventNode } from './OtelEventNode';
11
- export type { OtelEventNodeData } from './OtelEventNode';
12
-
13
- export { OtelScopeNode } from './OtelScopeNode';
14
- export type { OtelScopeNodeData } from './OtelScopeNode';
15
-
16
- export { OtelResourceNode } from './OtelResourceNode';
17
- export type { OtelResourceNodeData } from './OtelResourceNode';
18
-
19
- export { OtelBoundaryNode } from './OtelBoundaryNode';
20
- export type { OtelBoundaryNodeData } from './OtelBoundaryNode';
21
-
22
- // Re-export shared types
23
- export * from './shared/types';
@@ -1,295 +0,0 @@
1
- /**
2
- * Node badges component for status, sources, references, and boundary indicators
3
- */
4
-
5
- import React from 'react';
6
- import { useTheme } from '@principal-ade/industry-theme';
7
- import type { NodeBadgesProps, BadgePosition, BoundaryData } from './types';
8
-
9
- /**
10
- * Get badge shape styles based on node shape
11
- */
12
- function getBadgeShapeStyles(shape: string): React.CSSProperties {
13
- const baseSize = 18;
14
-
15
- switch (shape) {
16
- case 'circle':
17
- return {
18
- width: baseSize,
19
- height: baseSize,
20
- borderRadius: '50%',
21
- };
22
- case 'diamond':
23
- return {
24
- width: baseSize - 4,
25
- height: baseSize - 4,
26
- borderRadius: 0,
27
- transform: 'rotate(45deg)',
28
- };
29
- case 'hexagon':
30
- return {
31
- width: baseSize,
32
- height: baseSize,
33
- borderRadius: 0,
34
- clipPath: 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)',
35
- };
36
- case 'rectangle':
37
- default:
38
- return {
39
- width: baseSize,
40
- height: baseSize,
41
- borderRadius: 0,
42
- };
43
- }
44
- }
45
-
46
- /**
47
- * Get badge position based on shape - diamonds need badges at their points, not bounding box corners
48
- */
49
- function getBadgePosition(shape: string, position: BadgePosition): React.CSSProperties {
50
- const isDiamondShape = shape === 'diamond';
51
-
52
- if (isDiamondShape) {
53
- // Diamond points are at the middle of each edge of the bounding box
54
- switch (position) {
55
- case 'top-left':
56
- case 'left':
57
- return { top: '50%', left: 0, transform: 'translate(-50%, -50%)' };
58
- case 'top-right':
59
- case 'right':
60
- return { top: '50%', right: 0, transform: 'translate(50%, -50%)' };
61
- case 'top':
62
- return { top: 0, left: '50%', transform: 'translate(-50%, -50%)' };
63
- case 'bottom':
64
- case 'bottom-left':
65
- case 'bottom-right':
66
- return { bottom: 0, left: '50%', transform: 'translate(-50%, 50%)' };
67
- }
68
- }
69
-
70
- // Default positioning for rectangles, circles, hexagons (bounding box corners)
71
- switch (position) {
72
- case 'top-left':
73
- return { top: -6, left: -6 };
74
- case 'top-right':
75
- return { top: -6, right: -6 };
76
- case 'bottom-left':
77
- return { bottom: -6, left: -6 };
78
- case 'bottom-right':
79
- return { bottom: -6, right: -6 };
80
- case 'left':
81
- return { top: -6, left: -6 };
82
- case 'right':
83
- return { top: -6, right: -6 };
84
- case 'top':
85
- return { top: -6, left: '50%', transform: 'translateX(-50%)' };
86
- case 'bottom':
87
- return { bottom: -6, left: '50%', transform: 'translateX(-50%)' };
88
- }
89
- }
90
-
91
- interface BadgeProps {
92
- shape: string;
93
- position: BadgePosition;
94
- backgroundColor: string;
95
- label: string;
96
- title: string;
97
- opacity: number;
98
- }
99
-
100
- const Badge: React.FC<BadgeProps> = ({
101
- shape,
102
- position,
103
- backgroundColor,
104
- label,
105
- title,
106
- opacity,
107
- }) => {
108
- const { theme } = useTheme();
109
- const shapeStyles = getBadgeShapeStyles(shape);
110
- const positionStyles = getBadgePosition(shape, position);
111
-
112
- return (
113
- <div
114
- style={{
115
- position: 'absolute',
116
- ...positionStyles,
117
- ...shapeStyles,
118
- // Override transform if shape has rotation but we already have a position transform
119
- ...(shape === 'diamond'
120
- ? { transform: `${positionStyles.transform} rotate(45deg)` }
121
- : {}),
122
- backgroundColor,
123
- color: 'white',
124
- fontSize: theme.fontSizes[0],
125
- fontWeight: theme.fontWeights.bold,
126
- fontFamily: theme.fonts.body,
127
- display: 'flex',
128
- alignItems: 'center',
129
- justifyContent: 'center',
130
- boxShadow: '0 1px 3px rgba(0,0,0,0.3)',
131
- zIndex: 10,
132
- opacity,
133
- }}
134
- title={title}
135
- >
136
- <span style={{ transform: shapeStyles.transform ? 'rotate(-45deg)' : undefined }}>
137
- {label}
138
- </span>
139
- </div>
140
- );
141
- };
142
-
143
- /**
144
- * Status badge component (top-left)
145
- */
146
- const StatusBadge: React.FC<{
147
- shape: string;
148
- status: 'draft' | 'approved' | 'implemented';
149
- opacity: number;
150
- }> = ({ shape, status, opacity }) => {
151
- const statusColors = {
152
- draft: '#6b7280', // Gray
153
- approved: '#3b82f6', // Blue
154
- implemented: '#10b981', // Green
155
- };
156
-
157
- const statusLabels = {
158
- draft: 'D',
159
- approved: 'A',
160
- implemented: 'I',
161
- };
162
-
163
- const statusTitles = {
164
- draft: 'Draft - Design phase',
165
- approved: 'Approved - Ready for implementation',
166
- implemented: 'Implemented - Code exists',
167
- };
168
-
169
- return (
170
- <Badge
171
- shape={shape}
172
- position="top-left"
173
- backgroundColor={statusColors[status]}
174
- label={statusLabels[status]}
175
- title={statusTitles[status]}
176
- opacity={opacity}
177
- />
178
- );
179
- };
180
-
181
- /**
182
- * Sources badge component (top-right)
183
- */
184
- const SourcesBadge: React.FC<{
185
- shape: string;
186
- sourceFiles: string[];
187
- opacity: number;
188
- }> = ({ shape, sourceFiles, opacity }) => {
189
- return (
190
- <Badge
191
- shape={shape}
192
- position="top-right"
193
- backgroundColor="#10b981" // Green
194
- label="S"
195
- title={`Source files: ${sourceFiles.join(', ')}`}
196
- opacity={opacity}
197
- />
198
- );
199
- };
200
-
201
- /**
202
- * References badge component (bottom-left)
203
- */
204
- const ReferencesBadge: React.FC<{
205
- shape: string;
206
- references: string[];
207
- opacity: number;
208
- }> = ({ shape, references, opacity }) => {
209
- return (
210
- <Badge
211
- shape={shape}
212
- position="bottom-left"
213
- backgroundColor="#8b5cf6" // Purple
214
- label="R"
215
- title={`References: ${references.join(', ')}`}
216
- opacity={opacity}
217
- />
218
- );
219
- };
220
-
221
- /**
222
- * Boundary badge component (right or left based on direction)
223
- */
224
- const BoundaryBadge: React.FC<{
225
- boundary: BoundaryData;
226
- opacity: number;
227
- }> = ({ boundary, opacity }) => {
228
- const { theme } = useTheme();
229
- const direction = boundary.direction || 'outbound';
230
- const isOutbound = direction === 'outbound';
231
- const directionIcon = isOutbound ? '↗' : '↙';
232
- const directionTitle = isOutbound
233
- ? 'Outbound boundary (calls external system)'
234
- : 'Inbound boundary (called by external system)';
235
-
236
- const positionStyles = getBadgePosition('rectangle', isOutbound ? 'right' : 'left');
237
-
238
- return (
239
- <div
240
- style={{
241
- position: 'absolute',
242
- ...positionStyles,
243
- width: 18,
244
- height: 18,
245
- borderRadius: '50%', // Always circular for boundary badge
246
- backgroundColor: '#06b6d4', // Cyan
247
- color: 'white',
248
- fontSize: 12,
249
- fontWeight: theme.fontWeights.bold,
250
- fontFamily: theme.fonts.body,
251
- display: 'flex',
252
- alignItems: 'center',
253
- justifyContent: 'center',
254
- boxShadow: '0 1px 3px rgba(0,0,0,0.3)',
255
- zIndex: 10,
256
- opacity,
257
- }}
258
- title={directionTitle}
259
- >
260
- {directionIcon}
261
- </div>
262
- );
263
- };
264
-
265
- /**
266
- * Node badges component - renders status, sources, references, and boundary badges
267
- */
268
- export const NodeBadges: React.FC<NodeBadgesProps> = ({
269
- shape,
270
- status,
271
- sourceFiles,
272
- references,
273
- boundary,
274
- opacity = 1,
275
- }) => {
276
- const isBoundaryNode = !!boundary;
277
-
278
- return (
279
- <>
280
- {status && <StatusBadge shape={shape} status={status} opacity={opacity} />}
281
- {isBoundaryNode ? (
282
- <BoundaryBadge boundary={boundary} opacity={opacity} />
283
- ) : (
284
- <>
285
- {sourceFiles && sourceFiles.length > 0 && (
286
- <SourcesBadge shape={shape} sourceFiles={sourceFiles} opacity={opacity} />
287
- )}
288
- {references && references.length > 0 && (
289
- <ReferencesBadge shape={shape} references={references} opacity={opacity} />
290
- )}
291
- </>
292
- )}
293
- </>
294
- );
295
- };