@principal-ai/subsystems-react 0.17.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/README.md +93 -0
- package/dist/components/ConfigurationSelector.d.ts +35 -0
- package/dist/components/ConfigurationSelector.d.ts.map +1 -0
- package/dist/components/ConfigurationSelector.js +63 -0
- package/dist/components/ConfigurationSelector.js.map +1 -0
- package/dist/components/GraphRenderer.d.ts +295 -0
- package/dist/components/GraphRenderer.d.ts.map +1 -0
- package/dist/components/GraphRenderer.js +2008 -0
- package/dist/components/GraphRenderer.js.map +1 -0
- package/dist/components/MultiCanvasRenderer.d.ts +75 -0
- package/dist/components/MultiCanvasRenderer.d.ts.map +1 -0
- package/dist/components/MultiCanvasRenderer.js +203 -0
- package/dist/components/MultiCanvasRenderer.js.map +1 -0
- package/dist/components/NodeTooltip.d.ts +24 -0
- package/dist/components/NodeTooltip.d.ts.map +1 -0
- package/dist/components/NodeTooltip.js +160 -0
- package/dist/components/NodeTooltip.js.map +1 -0
- package/dist/components/SelectionSidebar.d.ts +18 -0
- package/dist/components/SelectionSidebar.d.ts.map +1 -0
- package/dist/components/SelectionSidebar.js +190 -0
- package/dist/components/SelectionSidebar.js.map +1 -0
- package/dist/components/SequenceDiagramRenderer.d.ts +77 -0
- package/dist/components/SequenceDiagramRenderer.d.ts.map +1 -0
- package/dist/components/SequenceDiagramRenderer.js +604 -0
- package/dist/components/SequenceDiagramRenderer.js.map +1 -0
- package/dist/components/WorkflowSequenceDiagram.d.ts +63 -0
- package/dist/components/WorkflowSequenceDiagram.d.ts.map +1 -0
- package/dist/components/WorkflowSequenceDiagram.js +147 -0
- package/dist/components/WorkflowSequenceDiagram.js.map +1 -0
- package/dist/components/dashboard/DashboardRenderer.d.ts +10 -0
- package/dist/components/dashboard/DashboardRenderer.d.ts.map +1 -0
- package/dist/components/dashboard/DashboardRenderer.js +179 -0
- package/dist/components/dashboard/DashboardRenderer.js.map +1 -0
- package/dist/components/dashboard/MetricPanel.d.ts +10 -0
- package/dist/components/dashboard/MetricPanel.d.ts.map +1 -0
- package/dist/components/dashboard/MetricPanel.js +103 -0
- package/dist/components/dashboard/MetricPanel.js.map +1 -0
- package/dist/components/dashboard/MockDataProvider.d.ts +30 -0
- package/dist/components/dashboard/MockDataProvider.d.ts.map +1 -0
- package/dist/components/dashboard/MockDataProvider.js +270 -0
- package/dist/components/dashboard/MockDataProvider.js.map +1 -0
- package/dist/components/dashboard/components/BarChart.d.ts +10 -0
- package/dist/components/dashboard/components/BarChart.d.ts.map +1 -0
- package/dist/components/dashboard/components/BarChart.js +167 -0
- package/dist/components/dashboard/components/BarChart.js.map +1 -0
- package/dist/components/dashboard/components/LineChart.d.ts +10 -0
- package/dist/components/dashboard/components/LineChart.d.ts.map +1 -0
- package/dist/components/dashboard/components/LineChart.js +141 -0
- package/dist/components/dashboard/components/LineChart.js.map +1 -0
- package/dist/components/dashboard/components/MetricCard.d.ts +9 -0
- package/dist/components/dashboard/components/MetricCard.d.ts.map +1 -0
- package/dist/components/dashboard/components/MetricCard.js +163 -0
- package/dist/components/dashboard/components/MetricCard.js.map +1 -0
- package/dist/components/dashboard/components/SourceLink.d.ts +9 -0
- package/dist/components/dashboard/components/SourceLink.d.ts.map +1 -0
- package/dist/components/dashboard/components/SourceLink.js +39 -0
- package/dist/components/dashboard/components/SourceLink.js.map +1 -0
- package/dist/components/dashboard/components/TimeRangeSelector.d.ts +9 -0
- package/dist/components/dashboard/components/TimeRangeSelector.d.ts.map +1 -0
- package/dist/components/dashboard/components/TimeRangeSelector.js +167 -0
- package/dist/components/dashboard/components/TimeRangeSelector.js.map +1 -0
- package/dist/components/dashboard/components/index.d.ts +6 -0
- package/dist/components/dashboard/components/index.d.ts.map +1 -0
- package/dist/components/dashboard/components/index.js +6 -0
- package/dist/components/dashboard/components/index.js.map +1 -0
- package/dist/components/dashboard/index.d.ts +6 -0
- package/dist/components/dashboard/index.d.ts.map +1 -0
- package/dist/components/dashboard/index.js +8 -0
- package/dist/components/dashboard/index.js.map +1 -0
- package/dist/components/dashboard/types.d.ts +74 -0
- package/dist/components/dashboard/types.d.ts.map +1 -0
- package/dist/components/dashboard/types.js +8 -0
- package/dist/components/dashboard/types.js.map +1 -0
- package/dist/components/session-events/SessionEventFeed.d.ts +34 -0
- package/dist/components/session-events/SessionEventFeed.d.ts.map +1 -0
- package/dist/components/session-events/SessionEventFeed.js +1196 -0
- package/dist/components/session-events/SessionEventFeed.js.map +1 -0
- package/dist/components/session-events/index.d.ts +7 -0
- package/dist/components/session-events/index.d.ts.map +1 -0
- package/dist/components/session-events/index.js +6 -0
- package/dist/components/session-events/index.js.map +1 -0
- package/dist/components/state-view/PipelineView.d.ts +14 -0
- package/dist/components/state-view/PipelineView.d.ts.map +1 -0
- package/dist/components/state-view/PipelineView.js +195 -0
- package/dist/components/state-view/PipelineView.js.map +1 -0
- package/dist/components/state-view/index.d.ts +14 -0
- package/dist/components/state-view/index.d.ts.map +1 -0
- package/dist/components/state-view/index.js +12 -0
- package/dist/components/state-view/index.js.map +1 -0
- package/dist/components/state-view/types.d.ts +188 -0
- package/dist/components/state-view/types.d.ts.map +1 -0
- package/dist/components/state-view/types.js +10 -0
- package/dist/components/state-view/types.js.map +1 -0
- package/dist/components/state-view/useStateView.d.ts +32 -0
- package/dist/components/state-view/useStateView.d.ts.map +1 -0
- package/dist/components/state-view/useStateView.js +129 -0
- package/dist/components/state-view/useStateView.js.map +1 -0
- package/dist/contexts/GraphEditContext.d.ts +20 -0
- package/dist/contexts/GraphEditContext.d.ts.map +1 -0
- package/dist/contexts/GraphEditContext.js +8 -0
- package/dist/contexts/GraphEditContext.js.map +1 -0
- package/dist/contexts/TooltipPortalContext.d.ts +8 -0
- package/dist/contexts/TooltipPortalContext.d.ts.map +1 -0
- package/dist/contexts/TooltipPortalContext.js +8 -0
- package/dist/contexts/TooltipPortalContext.js.map +1 -0
- package/dist/edges/CustomEdge.d.ts +24 -0
- package/dist/edges/CustomEdge.d.ts.map +1 -0
- package/dist/edges/CustomEdge.js +222 -0
- package/dist/edges/CustomEdge.js.map +1 -0
- package/dist/edges/GenericEdge.d.ts +18 -0
- package/dist/edges/GenericEdge.d.ts.map +1 -0
- package/dist/edges/GenericEdge.js +10 -0
- package/dist/edges/GenericEdge.js.map +1 -0
- package/dist/graphify/anchor.d.ts +28 -0
- package/dist/graphify/anchor.d.ts.map +1 -0
- package/dist/graphify/anchor.js +132 -0
- package/dist/graphify/anchor.js.map +1 -0
- package/dist/graphify/consolidated.d.ts +210 -0
- package/dist/graphify/consolidated.d.ts.map +1 -0
- package/dist/graphify/consolidated.js +11 -0
- package/dist/graphify/consolidated.js.map +1 -0
- package/dist/graphify/ids.d.ts +19 -0
- package/dist/graphify/ids.d.ts.map +1 -0
- package/dist/graphify/ids.js +46 -0
- package/dist/graphify/ids.js.map +1 -0
- package/dist/graphify/index.d.ts +18 -0
- package/dist/graphify/index.d.ts.map +1 -0
- package/dist/graphify/index.js +12 -0
- package/dist/graphify/index.js.map +1 -0
- package/dist/graphify/kind.d.ts +23 -0
- package/dist/graphify/kind.d.ts.map +1 -0
- package/dist/graphify/kind.js +77 -0
- package/dist/graphify/kind.js.map +1 -0
- package/dist/graphify/resolve.d.ts +48 -0
- package/dist/graphify/resolve.d.ts.map +1 -0
- package/dist/graphify/resolve.js +83 -0
- package/dist/graphify/resolve.js.map +1 -0
- package/dist/graphify/signature.d.ts +87 -0
- package/dist/graphify/signature.d.ts.map +1 -0
- package/dist/graphify/signature.js +276 -0
- package/dist/graphify/signature.js.map +1 -0
- package/dist/graphify/types.d.ts +141 -0
- package/dist/graphify/types.d.ts.map +1 -0
- package/dist/graphify/types.js +8 -0
- package/dist/graphify/types.js.map +1 -0
- package/dist/hooks/useElkLayout.d.ts +66 -0
- package/dist/hooks/useElkLayout.d.ts.map +1 -0
- package/dist/hooks/useElkLayout.js +136 -0
- package/dist/hooks/useElkLayout.js.map +1 -0
- package/dist/hooks/usePathBasedEvents.d.ts +42 -0
- package/dist/hooks/usePathBasedEvents.d.ts.map +1 -0
- package/dist/hooks/usePathBasedEvents.js +119 -0
- package/dist/hooks/usePathBasedEvents.js.map +1 -0
- package/dist/hooks/useSequenceLayout.d.ts +186 -0
- package/dist/hooks/useSequenceLayout.d.ts.map +1 -0
- package/dist/hooks/useSequenceLayout.js +327 -0
- package/dist/hooks/useSequenceLayout.js.map +1 -0
- package/dist/hooks/useUndoRedo.d.ts +80 -0
- package/dist/hooks/useUndoRedo.d.ts.map +1 -0
- package/dist/hooks/useUndoRedo.js +70 -0
- package/dist/hooks/useUndoRedo.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/CustomNode.d.ts +28 -0
- package/dist/nodes/CustomNode.d.ts.map +1 -0
- package/dist/nodes/CustomNode.js +820 -0
- package/dist/nodes/CustomNode.js.map +1 -0
- package/dist/nodes/GenericNode.d.ts +20 -0
- package/dist/nodes/GenericNode.d.ts.map +1 -0
- package/dist/nodes/GenericNode.js +20 -0
- package/dist/nodes/GenericNode.js.map +1 -0
- package/dist/nodes/otel/OtelBoundaryNode.d.ts +55 -0
- package/dist/nodes/otel/OtelBoundaryNode.d.ts.map +1 -0
- package/dist/nodes/otel/OtelBoundaryNode.js +81 -0
- package/dist/nodes/otel/OtelBoundaryNode.js.map +1 -0
- package/dist/nodes/otel/OtelEventNode.d.ts +59 -0
- package/dist/nodes/otel/OtelEventNode.d.ts.map +1 -0
- package/dist/nodes/otel/OtelEventNode.js +82 -0
- package/dist/nodes/otel/OtelEventNode.js.map +1 -0
- package/dist/nodes/otel/OtelResourceNode.d.ts +53 -0
- package/dist/nodes/otel/OtelResourceNode.d.ts.map +1 -0
- package/dist/nodes/otel/OtelResourceNode.js +105 -0
- package/dist/nodes/otel/OtelResourceNode.js.map +1 -0
- package/dist/nodes/otel/OtelScopeNode.d.ts +53 -0
- package/dist/nodes/otel/OtelScopeNode.d.ts.map +1 -0
- package/dist/nodes/otel/OtelScopeNode.js +81 -0
- package/dist/nodes/otel/OtelScopeNode.js.map +1 -0
- package/dist/nodes/otel/OtelSpanConventionNode.d.ts +61 -0
- package/dist/nodes/otel/OtelSpanConventionNode.d.ts.map +1 -0
- package/dist/nodes/otel/OtelSpanConventionNode.js +131 -0
- package/dist/nodes/otel/OtelSpanConventionNode.js.map +1 -0
- package/dist/nodes/otel/index.d.ts +17 -0
- package/dist/nodes/otel/index.d.ts.map +1 -0
- package/dist/nodes/otel/index.js +13 -0
- package/dist/nodes/otel/index.js.map +1 -0
- package/dist/nodes/otel/shared/NodeBadges.d.ts +10 -0
- package/dist/nodes/otel/shared/NodeBadges.d.ts.map +1 -0
- package/dist/nodes/otel/shared/NodeBadges.js +178 -0
- package/dist/nodes/otel/shared/NodeBadges.js.map +1 -0
- package/dist/nodes/otel/shared/NodeContent.d.ts +10 -0
- package/dist/nodes/otel/shared/NodeContent.d.ts.map +1 -0
- package/dist/nodes/otel/shared/NodeContent.js +96 -0
- package/dist/nodes/otel/shared/NodeContent.js.map +1 -0
- package/dist/nodes/otel/shared/index.d.ts +8 -0
- package/dist/nodes/otel/shared/index.d.ts.map +1 -0
- package/dist/nodes/otel/shared/index.js +8 -0
- package/dist/nodes/otel/shared/index.js.map +1 -0
- package/dist/nodes/otel/shared/types.d.ts +129 -0
- package/dist/nodes/otel/shared/types.d.ts.map +1 -0
- package/dist/nodes/otel/shared/types.js +5 -0
- package/dist/nodes/otel/shared/types.js.map +1 -0
- package/dist/nodes/otel/shared/useNodeBehavior.d.ts +42 -0
- package/dist/nodes/otel/shared/useNodeBehavior.d.ts.map +1 -0
- package/dist/nodes/otel/shared/useNodeBehavior.js +61 -0
- package/dist/nodes/otel/shared/useNodeBehavior.js.map +1 -0
- package/dist/pierre/PierreFileView.d.ts +17 -0
- package/dist/pierre/PierreFileView.d.ts.map +1 -0
- package/dist/pierre/PierreFileView.js +46 -0
- package/dist/pierre/PierreFileView.js.map +1 -0
- package/dist/pierre/PierreSnippetView.d.ts +28 -0
- package/dist/pierre/PierreSnippetView.d.ts.map +1 -0
- package/dist/pierre/PierreSnippetView.js +105 -0
- package/dist/pierre/PierreSnippetView.js.map +1 -0
- package/dist/pierre/PierreThroughlineCodeView.d.ts +22 -0
- package/dist/pierre/PierreThroughlineCodeView.d.ts.map +1 -0
- package/dist/pierre/PierreThroughlineCodeView.js +206 -0
- package/dist/pierre/PierreThroughlineCodeView.js.map +1 -0
- package/dist/pierre/constructColors.d.ts +24 -0
- package/dist/pierre/constructColors.d.ts.map +1 -0
- package/dist/pierre/constructColors.js +91 -0
- package/dist/pierre/constructColors.js.map +1 -0
- package/dist/pierre/index.d.ts +12 -0
- package/dist/pierre/index.d.ts.map +1 -0
- package/dist/pierre/index.js +7 -0
- package/dist/pierre/index.js.map +1 -0
- package/dist/pierre/pierreBackground.d.ts +15 -0
- package/dist/pierre/pierreBackground.d.ts.map +1 -0
- package/dist/pierre/pierreBackground.js +31 -0
- package/dist/pierre/pierreBackground.js.map +1 -0
- package/dist/pierre/pierreFileLang.d.ts +27 -0
- package/dist/pierre/pierreFileLang.d.ts.map +1 -0
- package/dist/pierre/pierreFileLang.js +52 -0
- package/dist/pierre/pierreFileLang.js.map +1 -0
- package/dist/pierre/pierreSyntaxTheme.d.ts +11 -0
- package/dist/pierre/pierreSyntaxTheme.d.ts.map +1 -0
- package/dist/pierre/pierreSyntaxTheme.js +20 -0
- package/dist/pierre/pierreSyntaxTheme.js.map +1 -0
- package/dist/pierre/scrollAnchor.d.ts +5 -0
- package/dist/pierre/scrollAnchor.d.ts.map +1 -0
- package/dist/pierre/scrollAnchor.js +9 -0
- package/dist/pierre/scrollAnchor.js.map +1 -0
- package/dist/pierre/sliceSnippet.d.ts +10 -0
- package/dist/pierre/sliceSnippet.d.ts.map +1 -0
- package/dist/pierre/sliceSnippet.js +16 -0
- package/dist/pierre/sliceSnippet.js.map +1 -0
- package/dist/pierre/syntaxTokens.d.ts +11 -0
- package/dist/pierre/syntaxTokens.d.ts.map +1 -0
- package/dist/pierre/syntaxTokens.js +51 -0
- package/dist/pierre/syntaxTokens.js.map +1 -0
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +8 -0
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -0
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +116 -0
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -0
- package/dist/subsystem/ComponentDeclaration.d.ts +87 -0
- package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -0
- package/dist/subsystem/ComponentDeclaration.js +374 -0
- package/dist/subsystem/ComponentDeclaration.js.map +1 -0
- package/dist/subsystem/FileDrawer.d.ts +17 -0
- package/dist/subsystem/FileDrawer.d.ts.map +1 -0
- package/dist/subsystem/FileDrawer.js +72 -0
- package/dist/subsystem/FileDrawer.js.map +1 -0
- package/dist/subsystem/GraphLayoutCover.d.ts +20 -0
- package/dist/subsystem/GraphLayoutCover.d.ts.map +1 -0
- package/dist/subsystem/GraphLayoutCover.js +42 -0
- package/dist/subsystem/GraphLayoutCover.js.map +1 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts +96 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -0
- package/dist/subsystem/SubsystemComponentGraph.js +1059 -0
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -0
- package/dist/subsystem/SubsystemFileTree.d.ts +24 -0
- package/dist/subsystem/SubsystemFileTree.d.ts.map +1 -0
- package/dist/subsystem/SubsystemFileTree.js +150 -0
- package/dist/subsystem/SubsystemFileTree.js.map +1 -0
- package/dist/subsystem/declarationRef.d.ts +39 -0
- package/dist/subsystem/declarationRef.d.ts.map +1 -0
- package/dist/subsystem/declarationRef.js +32 -0
- package/dist/subsystem/declarationRef.js.map +1 -0
- package/dist/subsystem/formatDeclaration.d.ts +11 -0
- package/dist/subsystem/formatDeclaration.d.ts.map +1 -0
- package/dist/subsystem/formatDeclaration.js +146 -0
- package/dist/subsystem/formatDeclaration.js.map +1 -0
- package/dist/subsystem/model.d.ts +353 -0
- package/dist/subsystem/model.d.ts.map +1 -0
- package/dist/subsystem/model.js +503 -0
- package/dist/subsystem/model.js.map +1 -0
- package/dist/subsystem/nodes.d.ts +69 -0
- package/dist/subsystem/nodes.d.ts.map +1 -0
- package/dist/subsystem/nodes.js +289 -0
- package/dist/subsystem/nodes.js.map +1 -0
- package/dist/subsystem/paths.d.ts +63 -0
- package/dist/subsystem/paths.d.ts.map +1 -0
- package/dist/subsystem/paths.js +86 -0
- package/dist/subsystem/paths.js.map +1 -0
- package/dist/subsystem/tokenizeComponent.d.ts +20 -0
- package/dist/subsystem/tokenizeComponent.d.ts.map +1 -0
- package/dist/subsystem/tokenizeComponent.js +56 -0
- package/dist/subsystem/tokenizeComponent.js.map +1 -0
- package/dist/subsystem/tokenizeFormatted.d.ts +12 -0
- package/dist/subsystem/tokenizeFormatted.d.ts.map +1 -0
- package/dist/subsystem/tokenizeFormatted.js +13 -0
- package/dist/subsystem/tokenizeFormatted.js.map +1 -0
- package/dist/utils/animationMapping.d.ts +53 -0
- package/dist/utils/animationMapping.d.ts.map +1 -0
- package/dist/utils/animationMapping.js +126 -0
- package/dist/utils/animationMapping.js.map +1 -0
- package/dist/utils/canvasBounds.d.ts +50 -0
- package/dist/utils/canvasBounds.d.ts.map +1 -0
- package/dist/utils/canvasBounds.js +88 -0
- package/dist/utils/canvasBounds.js.map +1 -0
- package/dist/utils/elkLayout.d.ts +137 -0
- package/dist/utils/elkLayout.d.ts.map +1 -0
- package/dist/utils/elkLayout.js +596 -0
- package/dist/utils/elkLayout.js.map +1 -0
- package/dist/utils/graphConverter.d.ts +18 -0
- package/dist/utils/graphConverter.d.ts.map +1 -0
- package/dist/utils/graphConverter.js +114 -0
- package/dist/utils/graphConverter.js.map +1 -0
- package/dist/utils/iconResolver.d.ts +29 -0
- package/dist/utils/iconResolver.d.ts.map +1 -0
- package/dist/utils/iconResolver.js +30 -0
- package/dist/utils/iconResolver.js.map +1 -0
- package/dist/utils/orientationUtils.d.ts +19 -0
- package/dist/utils/orientationUtils.d.ts.map +1 -0
- package/dist/utils/orientationUtils.js +62 -0
- package/dist/utils/orientationUtils.js.map +1 -0
- package/package.json +80 -0
- package/src/components/ConfigurationSelector.tsx +133 -0
- package/src/components/GraphRenderer.tsx +3195 -0
- package/src/components/MultiCanvasRenderer.tsx +295 -0
- package/src/components/NodeTooltip.tsx +279 -0
- package/src/components/PendingChanges.test.tsx +375 -0
- package/src/components/SelectionSidebar.tsx +352 -0
- package/src/components/SequenceDiagramRenderer.tsx +1156 -0
- package/src/components/WorkflowSequenceDiagram.tsx +270 -0
- package/src/components/dashboard/DashboardRenderer.tsx +317 -0
- package/src/components/dashboard/MetricPanel.tsx +254 -0
- package/src/components/dashboard/MockDataProvider.ts +330 -0
- package/src/components/dashboard/components/BarChart.tsx +299 -0
- package/src/components/dashboard/components/LineChart.tsx +279 -0
- package/src/components/dashboard/components/MetricCard.tsx +270 -0
- package/src/components/dashboard/components/SourceLink.tsx +63 -0
- package/src/components/dashboard/components/TimeRangeSelector.tsx +280 -0
- package/src/components/dashboard/components/index.ts +5 -0
- package/src/components/dashboard/index.ts +47 -0
- package/src/components/dashboard/types.ts +126 -0
- package/src/components/session-events/SessionEventFeed.tsx +1797 -0
- package/src/components/session-events/index.ts +13 -0
- package/src/components/state-view/PipelineView.tsx +347 -0
- package/src/components/state-view/index.ts +14 -0
- package/src/components/state-view/types.ts +261 -0
- package/src/components/state-view/useStateView.ts +205 -0
- package/src/contexts/GraphEditContext.tsx +27 -0
- package/src/contexts/TooltipPortalContext.ts +8 -0
- package/src/edges/CustomEdge.tsx +402 -0
- package/src/edges/GenericEdge.tsx +36 -0
- package/src/graphify/anchor.test.ts +124 -0
- package/src/graphify/anchor.ts +160 -0
- package/src/graphify/consolidated.ts +246 -0
- package/src/graphify/ids.ts +48 -0
- package/src/graphify/index.ts +72 -0
- package/src/graphify/kind.test.ts +108 -0
- package/src/graphify/kind.ts +120 -0
- package/src/graphify/resolve.test.ts +89 -0
- package/src/graphify/resolve.ts +115 -0
- package/src/graphify/signature.test.ts +278 -0
- package/src/graphify/signature.ts +354 -0
- package/src/graphify/types.ts +164 -0
- package/src/hooks/useElkLayout.test.ts +134 -0
- package/src/hooks/useElkLayout.ts +191 -0
- package/src/hooks/usePathBasedEvents.ts +192 -0
- package/src/hooks/useSequenceLayout.ts +543 -0
- package/src/hooks/useUndoRedo.ts +144 -0
- package/src/index.ts +341 -0
- package/src/nodes/CustomNode.tsx +1231 -0
- package/src/nodes/GenericNode.tsx +55 -0
- package/src/nodes/otel/OtelBoundaryNode.tsx +224 -0
- package/src/nodes/otel/OtelEventNode.tsx +225 -0
- package/src/nodes/otel/OtelResourceNode.tsx +251 -0
- package/src/nodes/otel/OtelScopeNode.tsx +221 -0
- package/src/nodes/otel/OtelSpanConventionNode.tsx +295 -0
- package/src/nodes/otel/index.ts +23 -0
- package/src/nodes/otel/shared/NodeBadges.tsx +295 -0
- package/src/nodes/otel/shared/NodeContent.tsx +204 -0
- package/src/nodes/otel/shared/index.ts +8 -0
- package/src/nodes/otel/shared/types.ts +138 -0
- package/src/nodes/otel/shared/useNodeBehavior.ts +114 -0
- package/src/pierre/PierreFileView.tsx +81 -0
- package/src/pierre/PierreSnippetView.tsx +184 -0
- package/src/pierre/PierreThroughlineCodeView.tsx +304 -0
- package/src/pierre/constructColors.test.ts +52 -0
- package/src/pierre/constructColors.ts +112 -0
- package/src/pierre/index.ts +18 -0
- package/src/pierre/pierreBackground.ts +33 -0
- package/src/pierre/pierreFileLang.test.ts +26 -0
- package/src/pierre/pierreFileLang.ts +55 -0
- package/src/pierre/pierreSyntaxTheme.ts +23 -0
- package/src/pierre/scrollAnchor.test.ts +13 -0
- package/src/pierre/scrollAnchor.ts +11 -0
- package/src/pierre/sliceSnippet.test.ts +21 -0
- package/src/pierre/sliceSnippet.ts +31 -0
- package/src/pierre/syntaxTokens.test.ts +35 -0
- package/src/pierre/syntaxTokens.ts +60 -0
- package/src/stories/Audit/BoundaryNodes.stories.tsx +385 -0
- package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +1448 -0
- package/src/stories/Audit/CanvasNodeTypes.stories.tsx +1049 -0
- package/src/stories/Audit/ColorPriority.stories.tsx +895 -0
- package/src/stories/Audit/DiamondBadges.stories.tsx +629 -0
- package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +724 -0
- package/src/stories/Audit/NodeFieldsAudit.stories.tsx +996 -0
- package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +715 -0
- package/src/stories/Audit/NodeShapes.stories.tsx +1208 -0
- package/src/stories/Audit/WideNodesTesting.stories.tsx +1045 -0
- package/src/stories/Components/DashboardRenderer.stories.tsx +263 -0
- package/src/stories/Components/GraphRenderer.stories.tsx +3236 -0
- package/src/stories/Components/MultiCanvasRenderer.stories.tsx +370 -0
- package/src/stories/Components/SequenceDiagram.stories.tsx +255 -0
- package/src/stories/Components/StateView.stories.tsx +417 -0
- package/src/stories/Components/TraceViewer.stories.tsx +442 -0
- package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +802 -0
- package/src/stories/Features/AnimationWorkshop.stories.tsx +727 -0
- package/src/stories/Features/ChangeTypeVisual.stories.tsx +67 -0
- package/src/stories/Features/EventDrivenAnimations.stories.tsx +513 -0
- package/src/stories/Features/MultiConfig.stories.tsx +539 -0
- package/src/stories/Introduction.mdx +34 -0
- package/src/stories/Layout/ElkEdgeRouting.stories.tsx +539 -0
- package/src/stories/Layout/GraphOrientation.stories.tsx +500 -0
- package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +352 -0
- package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +560 -0
- package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +491 -0
- package/src/stories/Nodes/NumericColors.stories.tsx +440 -0
- package/src/stories/OTEL/EventNameDisplay.stories.tsx +694 -0
- package/src/stories/OTEL/EventNameFallback.stories.tsx +578 -0
- package/src/stories/OTEL/OtelComponents.stories.tsx +422 -0
- package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +806 -0
- package/src/stories/OTEL/RawEventCards.stories.tsx +57 -0
- package/src/stories/OTEL/SpanBadges.stories.tsx +840 -0
- package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +209 -0
- package/src/stories/Pierre/CodeView.stories.tsx +219 -0
- package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +721 -0
- package/src/stories/RealWorld/FileCitySequence.stories.tsx +516 -0
- package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +336 -0
- package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +321 -0
- package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +365 -0
- package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +137 -0
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +238 -0
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +315 -0
- package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +239 -0
- package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +216 -0
- package/src/stories/Subsystem/ComponentGraph/GraphTitle.stories.tsx +70 -0
- package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +68 -0
- package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +959 -0
- package/src/stories/Subsystem/ComponentGraph/Spotlights.stories.tsx +681 -0
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +129 -0
- package/src/stories/__traces__/test-run.canvas.json +241 -0
- package/src/stories/data/activity-feed-analytics.dashboard.json +300 -0
- package/src/stories/data/file-city-images-transformed.otel.canvas.json +295 -0
- package/src/stories/data/file-city-workflows.json +269 -0
- package/src/stories/data/graph-converter-test-execution.json +443 -0
- package/src/stories/data/graph-converter-test.workflow.json +105 -0
- package/src/stories/data/graph-converter-validated-execution.json +58 -0
- package/src/stories/data/opencode-session-raw-events.json +273396 -0
- package/src/subsystem/ComponentDeclaration.tsx +643 -0
- package/src/subsystem/FileDrawer.tsx +106 -0
- package/src/subsystem/GraphLayoutCover.tsx +69 -0
- package/src/subsystem/SubsystemComponentGraph.tsx +1644 -0
- package/src/subsystem/SubsystemFileTree.tsx +190 -0
- package/src/subsystem/declarationRef.test.ts +33 -0
- package/src/subsystem/declarationRef.ts +63 -0
- package/src/subsystem/formatDeclaration.ts +174 -0
- package/src/subsystem/model.test.ts +244 -0
- package/src/subsystem/model.ts +850 -0
- package/src/subsystem/nodes.test.ts +56 -0
- package/src/subsystem/nodes.tsx +443 -0
- package/src/subsystem/paths.test.ts +123 -0
- package/src/subsystem/paths.ts +127 -0
- package/src/subsystem/tokenizeComponent.ts +70 -0
- package/src/subsystem/tokenizeFormatted.ts +19 -0
- package/src/utils/animationMapping.ts +166 -0
- package/src/utils/canvasBounds.ts +146 -0
- package/src/utils/elkLayout.test.ts +240 -0
- package/src/utils/elkLayout.ts +779 -0
- package/src/utils/graphConverter.ts +148 -0
- package/src/utils/iconResolver.tsx +53 -0
- package/src/utils/orientationUtils.ts +73 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { EDGE_DIM_ALPHA, fileMatchForNode, flowElementVisibility, hexWithAlpha } from './nodes';
|
|
3
|
+
|
|
4
|
+
describe('hexWithAlpha', () => {
|
|
5
|
+
test('appends a two-digit alpha to #rrggbb', () => {
|
|
6
|
+
expect(hexWithAlpha('#4ec9b0', 1)).toBe('#4ec9b0ff');
|
|
7
|
+
expect(hexWithAlpha('#4ec9b0', EDGE_DIM_ALPHA)).toBe('#4ec9b026');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('expands #rgb', () => {
|
|
11
|
+
expect(hexWithAlpha('#abc', 1)).toBe('#aabbccff');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('leaves non-hex values alone', () => {
|
|
15
|
+
expect(hexWithAlpha('teal', 0.15)).toBe('teal');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('fileMatchForNode', () => {
|
|
20
|
+
test('no open file → neutral', () => {
|
|
21
|
+
expect(fileMatchForNode('src/a.ts', null, false)).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('open file spotlights matches and dims others', () => {
|
|
25
|
+
expect(fileMatchForNode('src/a.ts', 'src/a.ts', false)).toBe(true);
|
|
26
|
+
expect(fileMatchForNode('src/b.ts', 'src/a.ts', false)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('focused-edge endpoint is not dimmed when it lives in another file', () => {
|
|
30
|
+
expect(fileMatchForNode('src/target.ts', 'src/source.ts', true)).toBeUndefined();
|
|
31
|
+
expect(fileMatchForNode('src/source.ts', 'src/source.ts', true)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('flowElementVisibility', () => {
|
|
36
|
+
test('nothing open or selected → everything full', () => {
|
|
37
|
+
expect(flowElementVisibility({ inOpened: false, inSelected: false, anyOpened: false, anySelected: false }))
|
|
38
|
+
.toEqual({ hidden: false, dimmed: false });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('opened, nothing selected → opened members full, rest hidden', () => {
|
|
42
|
+
expect(flowElementVisibility({ inOpened: true, inSelected: false, anyOpened: true, anySelected: false }))
|
|
43
|
+
.toEqual({ hidden: false, dimmed: false });
|
|
44
|
+
expect(flowElementVisibility({ inOpened: false, inSelected: false, anyOpened: true, anySelected: false }))
|
|
45
|
+
.toEqual({ hidden: true, dimmed: false });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('selected flow or step full; other opened members dimmed; rest hidden', () => {
|
|
49
|
+
expect(flowElementVisibility({ inOpened: true, inSelected: true, anyOpened: true, anySelected: true }))
|
|
50
|
+
.toEqual({ hidden: false, dimmed: false });
|
|
51
|
+
expect(flowElementVisibility({ inOpened: true, inSelected: false, anyOpened: true, anySelected: true }))
|
|
52
|
+
.toEqual({ hidden: false, dimmed: true });
|
|
53
|
+
expect(flowElementVisibility({ inOpened: false, inSelected: false, anyOpened: true, anySelected: true }))
|
|
54
|
+
.toEqual({ hidden: true, dimmed: false });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subsystem component/group node + edge renderers.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight, purpose-built for the subsystem component graph: a component
|
|
5
|
+
* renders its name (kind-tagged, colored by package) plus its symbol/purpose.
|
|
6
|
+
* Groups render as package containers. Clicking
|
|
7
|
+
* a component calls `onSelect` (to open the entry point / file).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
Handle,
|
|
13
|
+
Position,
|
|
14
|
+
type Node,
|
|
15
|
+
type NodeProps,
|
|
16
|
+
type EdgeProps,
|
|
17
|
+
} from '@xyflow/react';
|
|
18
|
+
import { useTheme } from '@principal-ade/industry-theme';
|
|
19
|
+
import {
|
|
20
|
+
MECHANISM_COLOR,
|
|
21
|
+
MECHANISM_STYLE,
|
|
22
|
+
ROLE_COLOR,
|
|
23
|
+
ROLE_LABEL,
|
|
24
|
+
constructBadgeLabel,
|
|
25
|
+
deriveNameFromSymbol,
|
|
26
|
+
BADGE_EDGE_INSET,
|
|
27
|
+
nodeMinWidthForBadges,
|
|
28
|
+
packageColor,
|
|
29
|
+
type SubsystemGraphNodeData,
|
|
30
|
+
type SubsystemGroupNodeData,
|
|
31
|
+
type SubsystemGraphEdge,
|
|
32
|
+
} from './model';
|
|
33
|
+
import { constructColorsFromPierreTheme } from '../pierre/constructColors';
|
|
34
|
+
import { resolvePierreSyntaxThemeName } from '../pierre/pierreSyntaxTheme';
|
|
35
|
+
|
|
36
|
+
export const CONSTRUCT_LABEL: Record<string, string> = {
|
|
37
|
+
class: 'class',
|
|
38
|
+
function: 'function',
|
|
39
|
+
method: 'method',
|
|
40
|
+
interface: 'interface',
|
|
41
|
+
type_alias: 'type alias',
|
|
42
|
+
enum: 'enum',
|
|
43
|
+
module: 'module',
|
|
44
|
+
store: 'store',
|
|
45
|
+
external: 'external',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Insert zero-width spaces at identifier word boundaries so long names wrap
|
|
49
|
+
* on naming conventions (snake_case `foo_`|`bar`, camelCase `foo`|`Bar`,
|
|
50
|
+
* PascalCase `Foo`|`Bar`, acronym `ABC`|`Def`) instead of mid-character. */
|
|
51
|
+
function breakWords(s: string): string {
|
|
52
|
+
const zwsp = '\u200b';
|
|
53
|
+
return s
|
|
54
|
+
// camelCase: lower/digit → Upper (and the boundary before it holds)
|
|
55
|
+
.replace(/([a-z0-9])([A-Z])/g, `$1${zwsp}$2`)
|
|
56
|
+
// acronym → word: `ABC`|`Def` (two Uppercase then a lowercase)
|
|
57
|
+
.replace(/([A-Z])([A-Z][a-z])/g, `$1${zwsp}$2`)
|
|
58
|
+
// separators: break after `_`, `-`, `.`
|
|
59
|
+
.replace(/([_\-.])([^_\-.\u200b])/g, `$1${zwsp}$2`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface SubsystemGraphCallbacks {
|
|
63
|
+
/** Click a component — open its file/entry point. */
|
|
64
|
+
onSelect?: (componentId: string) => void;
|
|
65
|
+
/** Click the filename badge — open that component's file in the drawer
|
|
66
|
+
* (same path as clicking the file link in the declaration panel). */
|
|
67
|
+
onOpenFile?: (componentId: string) => void;
|
|
68
|
+
/** Click an edge (or its label) — select the relationship. */
|
|
69
|
+
onEdgeSelect?: (edgeId: string) => void;
|
|
70
|
+
/** Hover a component (null on leave) — associates it with the file tree. */
|
|
71
|
+
onHover?: (componentId: string | null) => void;
|
|
72
|
+
/** Upper bound for node width; nodes grow with content up to this, then wrap. */
|
|
73
|
+
maxNodeWidth?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Root callbacks carried through node data (injected by the graph component). */
|
|
77
|
+
export const SUBSYSTEM_CALLBACKS: SubsystemGraphCallbacks = {};
|
|
78
|
+
|
|
79
|
+
export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeData, 'subsystem-component'>>) {
|
|
80
|
+
const { theme, mode } = useTheme();
|
|
81
|
+
const { data, selected, width: nodeWidth, height: nodeHeight } = props;
|
|
82
|
+
const c = data.component;
|
|
83
|
+
// Construct owns node color, derived from the active Pierre syntax theme —
|
|
84
|
+
// the same palette the declaration panel and file drawer render with. Role
|
|
85
|
+
// shows as the hover badge, not as color — for now; a role glyph/accent may
|
|
86
|
+
// come later.
|
|
87
|
+
const color = constructColorsFromPierreTheme(resolvePierreSyntaxThemeName(mode))[c.construct];
|
|
88
|
+
const [hover, setHover] = useState(false);
|
|
89
|
+
const configuredMax = SUBSYSTEM_CALLBACKS.maxNodeWidth;
|
|
90
|
+
const maxWidth = configuredMax ?? 300;
|
|
91
|
+
// `symbol` is the source of truth; `name` is derived from it consistently.
|
|
92
|
+
const displayName = deriveNameFromSymbol(c.symbol, c.construct, c.name, c.file, c.stereotype);
|
|
93
|
+
// Top badges are absolutely positioned — widen the node so they nowrap
|
|
94
|
+
// instead of wrapping, including when construct + role badges share the top.
|
|
95
|
+
const badgeMinWidth = nodeMinWidthForBadges(c);
|
|
96
|
+
// Set while a file is open in the drawer: true → spotlight, false → dim,
|
|
97
|
+
// absent (no file open) → neutral.
|
|
98
|
+
const fileMatch = data.fileMatch as boolean | undefined;
|
|
99
|
+
// Selection is stamped into data by the graph component — React Flow's own
|
|
100
|
+
// `selected` never updates because the node stops click propagation.
|
|
101
|
+
const isSelected = selected || (data.isSelected as boolean | undefined) === true;
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div
|
|
105
|
+
onMouseEnter={() => {
|
|
106
|
+
setHover(true);
|
|
107
|
+
SUBSYSTEM_CALLBACKS.onHover?.(c.id);
|
|
108
|
+
}}
|
|
109
|
+
onMouseLeave={() => {
|
|
110
|
+
setHover(false);
|
|
111
|
+
SUBSYSTEM_CALLBACKS.onHover?.(null);
|
|
112
|
+
}}
|
|
113
|
+
onClick={(e) => {
|
|
114
|
+
e.stopPropagation();
|
|
115
|
+
SUBSYSTEM_CALLBACKS.onSelect?.(c.id);
|
|
116
|
+
}}
|
|
117
|
+
style={{
|
|
118
|
+
position: 'relative',
|
|
119
|
+
display: 'flex',
|
|
120
|
+
flexDirection: 'column',
|
|
121
|
+
justifyContent: 'center',
|
|
122
|
+
alignItems: 'center',
|
|
123
|
+
boxSizing: 'border-box',
|
|
124
|
+
width: nodeWidth,
|
|
125
|
+
height: nodeHeight,
|
|
126
|
+
minWidth: badgeMinWidth,
|
|
127
|
+
maxWidth,
|
|
128
|
+
padding: '6px 10px',
|
|
129
|
+
borderRadius: 8,
|
|
130
|
+
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
131
|
+
// Selected / file-matched nodes get a thicker border; kind colors are
|
|
132
|
+
// kept for selection (thickness only). The file-open spotlight keeps
|
|
133
|
+
// its primary tint.
|
|
134
|
+
border: `${isSelected || fileMatch ? 4 : 2}px solid ${fileMatch ? theme.colors.primary : color}`,
|
|
135
|
+
boxShadow: fileMatch
|
|
136
|
+
? `0 1px 4px rgba(0,0,0,0.25), 0 0 12px ${theme.colors.primary}55`
|
|
137
|
+
: '0 1px 4px rgba(0,0,0,0.25)',
|
|
138
|
+
opacity: fileMatch === false || data.dimmed === true ? 0.18 : 1,
|
|
139
|
+
transition: 'opacity 150ms ease',
|
|
140
|
+
cursor: 'pointer',
|
|
141
|
+
fontFamily: theme.fonts.body,
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
{/* Construct / stereotype badge — prefers framework stereotype so a
|
|
145
|
+
React UI unit reads as "react · component" instead of "function".
|
|
146
|
+
Persistent; pointer-events none so clicks pass through to the node. */}
|
|
147
|
+
<div
|
|
148
|
+
style={{
|
|
149
|
+
position: 'absolute',
|
|
150
|
+
top: -9,
|
|
151
|
+
left: BADGE_EDGE_INSET,
|
|
152
|
+
zIndex: 1,
|
|
153
|
+
fontFamily: theme.fonts.monospace,
|
|
154
|
+
fontSize: theme.fontSizes[0] * 1.1,
|
|
155
|
+
letterSpacing: 0.5,
|
|
156
|
+
textTransform: 'uppercase',
|
|
157
|
+
lineHeight: '17px',
|
|
158
|
+
whiteSpace: 'nowrap',
|
|
159
|
+
color,
|
|
160
|
+
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
161
|
+
border: `1px solid ${color}`,
|
|
162
|
+
borderRadius: 4,
|
|
163
|
+
padding: '0 5px',
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{constructBadgeLabel(c)}
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
{/* Role badge — top-right, only when the node carries a topology role.
|
|
170
|
+
Role color (entry orange / service blue) on the badge; the node
|
|
171
|
+
border stays construct-colored. */}
|
|
172
|
+
{c.role != null && (
|
|
173
|
+
<div
|
|
174
|
+
style={{
|
|
175
|
+
position: 'absolute',
|
|
176
|
+
top: -9,
|
|
177
|
+
right: BADGE_EDGE_INSET,
|
|
178
|
+
zIndex: 1,
|
|
179
|
+
fontFamily: theme.fonts.monospace,
|
|
180
|
+
fontSize: theme.fontSizes[0] * 1.1,
|
|
181
|
+
letterSpacing: 0.5,
|
|
182
|
+
textTransform: 'uppercase',
|
|
183
|
+
lineHeight: '17px',
|
|
184
|
+
whiteSpace: 'nowrap',
|
|
185
|
+
color: ROLE_COLOR[c.role],
|
|
186
|
+
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
187
|
+
border: `1px solid ${ROLE_COLOR[c.role]}`,
|
|
188
|
+
borderRadius: 4,
|
|
189
|
+
padding: '0 5px',
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
{ROLE_LABEL[c.role]}
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{/* Filename badge — a strip across the bottom border, shown on hover
|
|
197
|
+
and pinned while this node's file is open in the drawer (fileMatch).
|
|
198
|
+
Mirrors the construct/role tab badges; hidden for fileless nodes. */}
|
|
199
|
+
{(hover || fileMatch === true) && c.file ? (
|
|
200
|
+
<div
|
|
201
|
+
style={{
|
|
202
|
+
position: 'absolute',
|
|
203
|
+
bottom: -13,
|
|
204
|
+
left: '50%',
|
|
205
|
+
transform: 'translateX(-50%)',
|
|
206
|
+
zIndex: 1,
|
|
207
|
+
fontFamily: theme.fonts.monospace,
|
|
208
|
+
fontSize: theme.fontSizes[0] * 1.1,
|
|
209
|
+
letterSpacing: 0.5,
|
|
210
|
+
lineHeight: '17px',
|
|
211
|
+
whiteSpace: 'nowrap',
|
|
212
|
+
// filename stays neutral — the construct color belongs to the
|
|
213
|
+
// borders and badges, not the file name
|
|
214
|
+
color: theme.colors.text ?? theme.colors.textSecondary,
|
|
215
|
+
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
216
|
+
border: `1px solid ${color}`,
|
|
217
|
+
borderRadius: 4,
|
|
218
|
+
padding: '0 5px',
|
|
219
|
+
cursor: 'pointer',
|
|
220
|
+
}}
|
|
221
|
+
onClick={(e) => {
|
|
222
|
+
// Open the file directly; don't also toggle node selection.
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
SUBSYSTEM_CALLBACKS.onOpenFile?.(c.id);
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
{c.file.split('/').pop()}
|
|
228
|
+
</div>
|
|
229
|
+
) : null}
|
|
230
|
+
|
|
231
|
+
{/* Purpose is no longer shown as a hover tooltip below the node — it
|
|
232
|
+
lives in the declaration panel and the graphify detail payload. */}
|
|
233
|
+
|
|
234
|
+
<div
|
|
235
|
+
style={{
|
|
236
|
+
fontSize: theme.fontSizes[2],
|
|
237
|
+
fontWeight: 600,
|
|
238
|
+
color: theme.colors.text,
|
|
239
|
+
lineHeight: 1.2,
|
|
240
|
+
textAlign: 'center',
|
|
241
|
+
// Let long names wrap within the node's capped width (instead of
|
|
242
|
+
// truncating), but cap each line so the node doesn't grow unbounded.
|
|
243
|
+
whiteSpace: 'normal',
|
|
244
|
+
overflowWrap: 'anywhere',
|
|
245
|
+
maxWidth: '100%',
|
|
246
|
+
fontFamily: theme.fonts.body,
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
{breakWords(displayName)}
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
{/* Hide the identity line when the symbol is just the title without its
|
|
253
|
+
decoration (`()`, ` {}`, or `<>`) — only show it when it adds
|
|
254
|
+
information (e.g. the dotted host on methods, or a different code
|
|
255
|
+
identity). */}
|
|
256
|
+
{c.symbol &&
|
|
257
|
+
c.symbol !==
|
|
258
|
+
displayName
|
|
259
|
+
.replace(/^<(.+)>$/, '$1')
|
|
260
|
+
.replace(/ ?\{\}$/, '')
|
|
261
|
+
.replace(/\(\)$/, '') && (
|
|
262
|
+
<div
|
|
263
|
+
style={{
|
|
264
|
+
fontSize: theme.fontSizes[0] * 0.82,
|
|
265
|
+
fontFamily: theme.fonts.monospace,
|
|
266
|
+
color: color,
|
|
267
|
+
marginTop: 1,
|
|
268
|
+
textAlign: 'center',
|
|
269
|
+
whiteSpace: 'nowrap',
|
|
270
|
+
overflow: 'hidden',
|
|
271
|
+
textOverflow: 'ellipsis',
|
|
272
|
+
maxWidth: '100%',
|
|
273
|
+
}}
|
|
274
|
+
title={c.symbol}
|
|
275
|
+
>
|
|
276
|
+
{c.symbol}
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
<Handle type="target" position={Position.Left} style={{ opacity: 0 }} />
|
|
281
|
+
<Handle type="source" position={Position.Right} style={{ opacity: 0 }} />
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Process boundary frame — a React Flow parent node. Members render inside
|
|
288
|
+
* via `parentId`; this draws the labeled container only (no handles, no
|
|
289
|
+
* selection). The border color derives deterministically from the process key
|
|
290
|
+
* so each deployment unit reads as its own region.
|
|
291
|
+
*/
|
|
292
|
+
export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData, 'subsystem-group'>>) {
|
|
293
|
+
const { theme } = useTheme();
|
|
294
|
+
const { data, width, height, selected } = props as unknown as {
|
|
295
|
+
data: SubsystemGroupNodeData;
|
|
296
|
+
width?: number;
|
|
297
|
+
height?: number;
|
|
298
|
+
selected?: boolean;
|
|
299
|
+
};
|
|
300
|
+
const region = data.region;
|
|
301
|
+
const color = packageColor(region?.key ?? 'process');
|
|
302
|
+
const dimmed = data.dimmed === true;
|
|
303
|
+
const hidden = (data as { hidden?: boolean }).hidden === true;
|
|
304
|
+
|
|
305
|
+
if (!region) return null;
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<div
|
|
309
|
+
style={{
|
|
310
|
+
position: 'relative',
|
|
311
|
+
width: width ?? 400,
|
|
312
|
+
height: height ?? 300,
|
|
313
|
+
boxSizing: 'border-box',
|
|
314
|
+
borderRadius: 12,
|
|
315
|
+
border: `2px ${selected ? 'solid' : 'dashed'} ${color}`,
|
|
316
|
+
background: `${color}14`,
|
|
317
|
+
opacity: hidden ? 0 : dimmed ? 0.35 : 1,
|
|
318
|
+
transition: 'opacity 150ms ease',
|
|
319
|
+
pointerEvents: 'none',
|
|
320
|
+
}}
|
|
321
|
+
>
|
|
322
|
+
<div
|
|
323
|
+
style={{
|
|
324
|
+
position: 'absolute',
|
|
325
|
+
top: -19,
|
|
326
|
+
left: 12,
|
|
327
|
+
fontFamily: theme.fonts.monospace,
|
|
328
|
+
fontSize: theme.fontSizes[3],
|
|
329
|
+
fontWeight: 700,
|
|
330
|
+
letterSpacing: 0.6,
|
|
331
|
+
color,
|
|
332
|
+
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
333
|
+
border: `1px solid ${color}`,
|
|
334
|
+
borderRadius: 4,
|
|
335
|
+
padding: '1px 7px',
|
|
336
|
+
whiteSpace: 'nowrap',
|
|
337
|
+
}}
|
|
338
|
+
>
|
|
339
|
+
{region.label}
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** `#rrggbb` + alpha → `#rrggbbaa`. Used to dim a stroke/marker by color so
|
|
346
|
+
* each opacity gets its own SVG marker id — path `opacity` leaks across every
|
|
347
|
+
* edge that shares a `url(#marker)` (the focused edge's arrowhead dims). */
|
|
348
|
+
export function hexWithAlpha(hex: string, alpha: number): string {
|
|
349
|
+
const raw = hex.replace('#', '');
|
|
350
|
+
const full = raw.length === 3 ? [...raw].map((c) => c + c).join('') : raw;
|
|
351
|
+
if (!/^[0-9a-fA-F]{6}$/.test(full)) return hex;
|
|
352
|
+
const a = Math.round(Math.min(1, Math.max(0, alpha)) * 255)
|
|
353
|
+
.toString(16)
|
|
354
|
+
.padStart(2, '0');
|
|
355
|
+
return `#${full}${a}`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* File-open spotlight flag for a node. `true` = lives in the open file,
|
|
360
|
+
* `false` = dim, `undefined` = render neutrally.
|
|
361
|
+
*
|
|
362
|
+
* Focused-edge endpoints stay neutral when they don't live in the open file
|
|
363
|
+
* (the target of a focused edge must not dim with the rest).
|
|
364
|
+
*/
|
|
365
|
+
export function fileMatchForNode(
|
|
366
|
+
nodeFile: string | undefined,
|
|
367
|
+
openFile: string | null,
|
|
368
|
+
isFocusEndpoint: boolean,
|
|
369
|
+
): boolean | undefined {
|
|
370
|
+
if (!openFile) return undefined;
|
|
371
|
+
if (nodeFile === openFile) return true;
|
|
372
|
+
if (isFocusEndpoint) return undefined;
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Hide / dim a node or edge while flows are open.
|
|
378
|
+
* - not in any opened flow → hidden
|
|
379
|
+
* - in an opened flow, but not the selected flow/step → dimmed
|
|
380
|
+
* - in the selected flow or step (or opened with nothing selected) → full
|
|
381
|
+
*/
|
|
382
|
+
export function flowElementVisibility(opts: {
|
|
383
|
+
inOpened: boolean;
|
|
384
|
+
inSelected: boolean;
|
|
385
|
+
anyOpened: boolean;
|
|
386
|
+
anySelected: boolean;
|
|
387
|
+
}): { hidden: boolean; dimmed: boolean } {
|
|
388
|
+
const { inOpened, inSelected, anyOpened, anySelected } = opts;
|
|
389
|
+
if (!anyOpened && !anySelected) return { hidden: false, dimmed: false };
|
|
390
|
+
if (!inOpened && !inSelected) return { hidden: true, dimmed: false };
|
|
391
|
+
if (anySelected && !inSelected) return { hidden: false, dimmed: true };
|
|
392
|
+
return { hidden: false, dimmed: false };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export const EDGE_DIM_ALPHA = 0.15;
|
|
396
|
+
|
|
397
|
+
/** Subsystem edge — SVG path only. The mechanism label is rendered as an
|
|
398
|
+
* absolutely-positioned HTML overlay OUTSIDE the ReactFlow tree (by the
|
|
399
|
+
* parent Inner component) so it sits above the pane and receives pointer
|
|
400
|
+
* events. */
|
|
401
|
+
export function SubsystemEdge({
|
|
402
|
+
data,
|
|
403
|
+
markerEnd,
|
|
404
|
+
}: EdgeProps<SubsystemGraphEdge>) {
|
|
405
|
+
const path = data?.elkPath ?? '';
|
|
406
|
+
const mechanism = data?.mechanism ?? 'imports';
|
|
407
|
+
const color = MECHANISM_COLOR[mechanism] ?? '#888';
|
|
408
|
+
// Dash style comes from the mechanism table (dashed = inverted-control or
|
|
409
|
+
// observational relationships: hierarchy, registration, watches).
|
|
410
|
+
const isDashed = MECHANISM_STYLE[mechanism] === 'dashed';
|
|
411
|
+
const dimmed = data?.dimmed === true;
|
|
412
|
+
// Dim the stroke by color, never via path `opacity`. SVG markers are shared
|
|
413
|
+
// by id; opacity on the referencing path paints every arrowhead that uses
|
|
414
|
+
// the same marker — including the focused edge's target.
|
|
415
|
+
const stroke = dimmed ? hexWithAlpha(color, EDGE_DIM_ALPHA) : color;
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
<>
|
|
419
|
+
{/* Invisible wide interaction path — React Flow's pane uses this for
|
|
420
|
+
hit-testing onEdgeClick. Must be pointer-events:stroke so the pane
|
|
421
|
+
delegates the click to onEdgeClick. */}
|
|
422
|
+
<path
|
|
423
|
+
d={path}
|
|
424
|
+
fill="none"
|
|
425
|
+
stroke="transparent"
|
|
426
|
+
strokeWidth={20}
|
|
427
|
+
className="react-flow__edge-interaction"
|
|
428
|
+
/>
|
|
429
|
+
{/* Visible edge line — pointer-events:none so it never intercepts the
|
|
430
|
+
HTML label overlay rendered by the parent component. */}
|
|
431
|
+
<path
|
|
432
|
+
d={path}
|
|
433
|
+
fill="none"
|
|
434
|
+
stroke={stroke}
|
|
435
|
+
strokeWidth={1.5}
|
|
436
|
+
strokeDasharray={isDashed ? '6 4' : undefined}
|
|
437
|
+
markerEnd={markerEnd}
|
|
438
|
+
style={{ pointerEvents: 'none' }}
|
|
439
|
+
/>
|
|
440
|
+
</>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
buildRepoGroups,
|
|
4
|
+
buildTreePathMapping,
|
|
5
|
+
purlOwnerName,
|
|
6
|
+
purlRepoKey,
|
|
7
|
+
repoAvatarUrl,
|
|
8
|
+
} from './paths';
|
|
9
|
+
|
|
10
|
+
describe('purlRepoKey', () => {
|
|
11
|
+
test('strips fragments and whitespace', () => {
|
|
12
|
+
expect(purlRepoKey('pkg:github/acme/widget#src/x.ts')).toBe('pkg:github/acme/widget');
|
|
13
|
+
expect(purlRepoKey(' pkg:github/acme/widget ')).toBe('pkg:github/acme/widget');
|
|
14
|
+
expect(purlRepoKey(undefined)).toBeUndefined();
|
|
15
|
+
expect(purlRepoKey('#only-a-fragment')).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('purlOwnerName', () => {
|
|
20
|
+
test('extracts the last two path segments', () => {
|
|
21
|
+
expect(purlOwnerName('pkg:github/acme/widget#src/x.ts')).toBe('acme/widget');
|
|
22
|
+
expect(purlOwnerName('pkg:npm/@scope/lib')).toBe('@scope/lib');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('buildTreePathMapping', () => {
|
|
27
|
+
test('single-repo graphs keep bare file paths', () => {
|
|
28
|
+
const m = buildTreePathMapping([
|
|
29
|
+
{ file: 'src/a.ts', purl: 'pkg:github/acme/widget' },
|
|
30
|
+
{ file: 'src/b.ts', purl: 'pkg:github/acme/widget' },
|
|
31
|
+
]);
|
|
32
|
+
expect(m.multiRepo).toBe(false);
|
|
33
|
+
expect(m.entries.map((e) => e.displayPath)).toEqual(['src/a.ts', 'src/b.ts']);
|
|
34
|
+
expect(m.toFile.get('src/a.ts')).toBe('src/a.ts');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('multi-repo graphs prefix entries with owner/name', () => {
|
|
38
|
+
const m = buildTreePathMapping([
|
|
39
|
+
{ file: 'graphify/ids.py', purl: 'pkg:github/Graphify-Labs/graphify' },
|
|
40
|
+
{ file: 'packages/subsystems-react/src/x.ts', purl: 'pkg:github/principal-ai/core-lib#packages/subsystems-react/src/x.ts' },
|
|
41
|
+
]);
|
|
42
|
+
expect(m.multiRepo).toBe(true);
|
|
43
|
+
expect(m.toFile.get('Graphify-Labs/graphify/graphify/ids.py')).toBe('graphify/ids.py');
|
|
44
|
+
expect(m.toDisplay.get('packages/subsystems-react/src/x.ts')).toBe(
|
|
45
|
+
'principal-ai/core-lib/packages/subsystems-react/src/x.ts',
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('same-named files in different repos stay distinct', () => {
|
|
50
|
+
const m = buildTreePathMapping([
|
|
51
|
+
{ file: 'README.md', purl: 'pkg:github/acme/widget' },
|
|
52
|
+
{ file: 'README.md', purl: 'pkg:github/acme/other' },
|
|
53
|
+
]);
|
|
54
|
+
expect(m.entries).toHaveLength(2);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('components without files are skipped; purl-less components stay unprefixed', () => {
|
|
58
|
+
const m = buildTreePathMapping([
|
|
59
|
+
{ file: 'a.ts', purl: 'pkg:github/acme/widget' },
|
|
60
|
+
{ file: '', purl: 'pkg:github/acme/widget' },
|
|
61
|
+
{ purl: 'pkg:github/acme/widget' },
|
|
62
|
+
{ file: 'loose.ts' },
|
|
63
|
+
]);
|
|
64
|
+
// Two repos' worth of purls? No — the purl-less one has no repo key.
|
|
65
|
+
expect(m.multiRepo).toBe(false);
|
|
66
|
+
expect(m.toFile.get('loose.ts')).toBe('loose.ts');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('buildRepoGroups', () => {
|
|
71
|
+
test('groups by repo preserving first-appearance order', () => {
|
|
72
|
+
const { multiRepo, groups } = buildRepoGroups([
|
|
73
|
+
{ file: 'a.ts', purl: 'pkg:github/acme/widget' },
|
|
74
|
+
{ file: 'g/x.py', purl: 'pkg:github/Graphify-Labs/graphify' },
|
|
75
|
+
{ file: 'b.ts', purl: 'pkg:github/acme/widget' },
|
|
76
|
+
]);
|
|
77
|
+
expect(multiRepo).toBe(true);
|
|
78
|
+
expect(groups.map((g) => g.repoKey)).toEqual(['pkg:github/acme/widget', 'pkg:github/Graphify-Labs/graphify']);
|
|
79
|
+
expect(groups[0]!.owner).toBe('acme');
|
|
80
|
+
expect(groups[0]!.repo).toBe('widget');
|
|
81
|
+
expect(groups[0]!.entries.map((e) => e.file)).toEqual(['a.ts', 'b.ts']);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('entries carry bare files (no synthetic prefixes)', () => {
|
|
85
|
+
const { groups } = buildRepoGroups([
|
|
86
|
+
{ file: 'deep/nested/x.ts', purl: 'pkg:github/acme/widget#deep/nested/x.ts' },
|
|
87
|
+
]);
|
|
88
|
+
expect(groups[0]!.entries[0]).toEqual({ displayPath: 'deep/nested/x.ts', file: 'deep/nested/x.ts' });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('purl-less components land in a trailing unbadged group', () => {
|
|
92
|
+
const { multiRepo, groups } = buildRepoGroups([
|
|
93
|
+
{ file: 'loose.ts' },
|
|
94
|
+
{ file: 'a.ts', purl: 'pkg:github/acme/widget' },
|
|
95
|
+
]);
|
|
96
|
+
expect(multiRepo).toBe(false);
|
|
97
|
+
expect(groups).toHaveLength(2);
|
|
98
|
+
expect(groups[0]!.repoKey).toBeUndefined();
|
|
99
|
+
expect(groups[1]!.repoKey).toBe('pkg:github/acme/widget');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('single-repo graphs yield one group and multiRepo=false', () => {
|
|
103
|
+
const { multiRepo, groups } = buildRepoGroups([
|
|
104
|
+
{ file: 'a.ts', purl: 'pkg:github/acme/widget' },
|
|
105
|
+
{ file: 'b.ts', purl: 'pkg:github/acme/widget' },
|
|
106
|
+
]);
|
|
107
|
+
expect(multiRepo).toBe(false);
|
|
108
|
+
expect(groups).toHaveLength(1);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('repoAvatarUrl', () => {
|
|
113
|
+
test('github purls map to the owner avatar', () => {
|
|
114
|
+
expect(repoAvatarUrl('pkg:github/Graphify-Labs/graphify#x/y.py')).toBe(
|
|
115
|
+
'https://github.com/Graphify-Labs.png?size=64',
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('non-github purls have no avatar', () => {
|
|
120
|
+
expect(repoAvatarUrl('pkg:npm/@scope/lib')).toBeUndefined();
|
|
121
|
+
expect(repoAvatarUrl(undefined)).toBeUndefined();
|
|
122
|
+
});
|
|
123
|
+
});
|