@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.
Files changed (496) hide show
  1. package/README.md +93 -0
  2. package/dist/components/ConfigurationSelector.d.ts +35 -0
  3. package/dist/components/ConfigurationSelector.d.ts.map +1 -0
  4. package/dist/components/ConfigurationSelector.js +63 -0
  5. package/dist/components/ConfigurationSelector.js.map +1 -0
  6. package/dist/components/GraphRenderer.d.ts +295 -0
  7. package/dist/components/GraphRenderer.d.ts.map +1 -0
  8. package/dist/components/GraphRenderer.js +2008 -0
  9. package/dist/components/GraphRenderer.js.map +1 -0
  10. package/dist/components/MultiCanvasRenderer.d.ts +75 -0
  11. package/dist/components/MultiCanvasRenderer.d.ts.map +1 -0
  12. package/dist/components/MultiCanvasRenderer.js +203 -0
  13. package/dist/components/MultiCanvasRenderer.js.map +1 -0
  14. package/dist/components/NodeTooltip.d.ts +24 -0
  15. package/dist/components/NodeTooltip.d.ts.map +1 -0
  16. package/dist/components/NodeTooltip.js +160 -0
  17. package/dist/components/NodeTooltip.js.map +1 -0
  18. package/dist/components/SelectionSidebar.d.ts +18 -0
  19. package/dist/components/SelectionSidebar.d.ts.map +1 -0
  20. package/dist/components/SelectionSidebar.js +190 -0
  21. package/dist/components/SelectionSidebar.js.map +1 -0
  22. package/dist/components/SequenceDiagramRenderer.d.ts +77 -0
  23. package/dist/components/SequenceDiagramRenderer.d.ts.map +1 -0
  24. package/dist/components/SequenceDiagramRenderer.js +604 -0
  25. package/dist/components/SequenceDiagramRenderer.js.map +1 -0
  26. package/dist/components/WorkflowSequenceDiagram.d.ts +63 -0
  27. package/dist/components/WorkflowSequenceDiagram.d.ts.map +1 -0
  28. package/dist/components/WorkflowSequenceDiagram.js +147 -0
  29. package/dist/components/WorkflowSequenceDiagram.js.map +1 -0
  30. package/dist/components/dashboard/DashboardRenderer.d.ts +10 -0
  31. package/dist/components/dashboard/DashboardRenderer.d.ts.map +1 -0
  32. package/dist/components/dashboard/DashboardRenderer.js +179 -0
  33. package/dist/components/dashboard/DashboardRenderer.js.map +1 -0
  34. package/dist/components/dashboard/MetricPanel.d.ts +10 -0
  35. package/dist/components/dashboard/MetricPanel.d.ts.map +1 -0
  36. package/dist/components/dashboard/MetricPanel.js +103 -0
  37. package/dist/components/dashboard/MetricPanel.js.map +1 -0
  38. package/dist/components/dashboard/MockDataProvider.d.ts +30 -0
  39. package/dist/components/dashboard/MockDataProvider.d.ts.map +1 -0
  40. package/dist/components/dashboard/MockDataProvider.js +270 -0
  41. package/dist/components/dashboard/MockDataProvider.js.map +1 -0
  42. package/dist/components/dashboard/components/BarChart.d.ts +10 -0
  43. package/dist/components/dashboard/components/BarChart.d.ts.map +1 -0
  44. package/dist/components/dashboard/components/BarChart.js +167 -0
  45. package/dist/components/dashboard/components/BarChart.js.map +1 -0
  46. package/dist/components/dashboard/components/LineChart.d.ts +10 -0
  47. package/dist/components/dashboard/components/LineChart.d.ts.map +1 -0
  48. package/dist/components/dashboard/components/LineChart.js +141 -0
  49. package/dist/components/dashboard/components/LineChart.js.map +1 -0
  50. package/dist/components/dashboard/components/MetricCard.d.ts +9 -0
  51. package/dist/components/dashboard/components/MetricCard.d.ts.map +1 -0
  52. package/dist/components/dashboard/components/MetricCard.js +163 -0
  53. package/dist/components/dashboard/components/MetricCard.js.map +1 -0
  54. package/dist/components/dashboard/components/SourceLink.d.ts +9 -0
  55. package/dist/components/dashboard/components/SourceLink.d.ts.map +1 -0
  56. package/dist/components/dashboard/components/SourceLink.js +39 -0
  57. package/dist/components/dashboard/components/SourceLink.js.map +1 -0
  58. package/dist/components/dashboard/components/TimeRangeSelector.d.ts +9 -0
  59. package/dist/components/dashboard/components/TimeRangeSelector.d.ts.map +1 -0
  60. package/dist/components/dashboard/components/TimeRangeSelector.js +167 -0
  61. package/dist/components/dashboard/components/TimeRangeSelector.js.map +1 -0
  62. package/dist/components/dashboard/components/index.d.ts +6 -0
  63. package/dist/components/dashboard/components/index.d.ts.map +1 -0
  64. package/dist/components/dashboard/components/index.js +6 -0
  65. package/dist/components/dashboard/components/index.js.map +1 -0
  66. package/dist/components/dashboard/index.d.ts +6 -0
  67. package/dist/components/dashboard/index.d.ts.map +1 -0
  68. package/dist/components/dashboard/index.js +8 -0
  69. package/dist/components/dashboard/index.js.map +1 -0
  70. package/dist/components/dashboard/types.d.ts +74 -0
  71. package/dist/components/dashboard/types.d.ts.map +1 -0
  72. package/dist/components/dashboard/types.js +8 -0
  73. package/dist/components/dashboard/types.js.map +1 -0
  74. package/dist/components/session-events/SessionEventFeed.d.ts +34 -0
  75. package/dist/components/session-events/SessionEventFeed.d.ts.map +1 -0
  76. package/dist/components/session-events/SessionEventFeed.js +1196 -0
  77. package/dist/components/session-events/SessionEventFeed.js.map +1 -0
  78. package/dist/components/session-events/index.d.ts +7 -0
  79. package/dist/components/session-events/index.d.ts.map +1 -0
  80. package/dist/components/session-events/index.js +6 -0
  81. package/dist/components/session-events/index.js.map +1 -0
  82. package/dist/components/state-view/PipelineView.d.ts +14 -0
  83. package/dist/components/state-view/PipelineView.d.ts.map +1 -0
  84. package/dist/components/state-view/PipelineView.js +195 -0
  85. package/dist/components/state-view/PipelineView.js.map +1 -0
  86. package/dist/components/state-view/index.d.ts +14 -0
  87. package/dist/components/state-view/index.d.ts.map +1 -0
  88. package/dist/components/state-view/index.js +12 -0
  89. package/dist/components/state-view/index.js.map +1 -0
  90. package/dist/components/state-view/types.d.ts +188 -0
  91. package/dist/components/state-view/types.d.ts.map +1 -0
  92. package/dist/components/state-view/types.js +10 -0
  93. package/dist/components/state-view/types.js.map +1 -0
  94. package/dist/components/state-view/useStateView.d.ts +32 -0
  95. package/dist/components/state-view/useStateView.d.ts.map +1 -0
  96. package/dist/components/state-view/useStateView.js +129 -0
  97. package/dist/components/state-view/useStateView.js.map +1 -0
  98. package/dist/contexts/GraphEditContext.d.ts +20 -0
  99. package/dist/contexts/GraphEditContext.d.ts.map +1 -0
  100. package/dist/contexts/GraphEditContext.js +8 -0
  101. package/dist/contexts/GraphEditContext.js.map +1 -0
  102. package/dist/contexts/TooltipPortalContext.d.ts +8 -0
  103. package/dist/contexts/TooltipPortalContext.d.ts.map +1 -0
  104. package/dist/contexts/TooltipPortalContext.js +8 -0
  105. package/dist/contexts/TooltipPortalContext.js.map +1 -0
  106. package/dist/edges/CustomEdge.d.ts +24 -0
  107. package/dist/edges/CustomEdge.d.ts.map +1 -0
  108. package/dist/edges/CustomEdge.js +222 -0
  109. package/dist/edges/CustomEdge.js.map +1 -0
  110. package/dist/edges/GenericEdge.d.ts +18 -0
  111. package/dist/edges/GenericEdge.d.ts.map +1 -0
  112. package/dist/edges/GenericEdge.js +10 -0
  113. package/dist/edges/GenericEdge.js.map +1 -0
  114. package/dist/graphify/anchor.d.ts +28 -0
  115. package/dist/graphify/anchor.d.ts.map +1 -0
  116. package/dist/graphify/anchor.js +132 -0
  117. package/dist/graphify/anchor.js.map +1 -0
  118. package/dist/graphify/consolidated.d.ts +210 -0
  119. package/dist/graphify/consolidated.d.ts.map +1 -0
  120. package/dist/graphify/consolidated.js +11 -0
  121. package/dist/graphify/consolidated.js.map +1 -0
  122. package/dist/graphify/ids.d.ts +19 -0
  123. package/dist/graphify/ids.d.ts.map +1 -0
  124. package/dist/graphify/ids.js +46 -0
  125. package/dist/graphify/ids.js.map +1 -0
  126. package/dist/graphify/index.d.ts +18 -0
  127. package/dist/graphify/index.d.ts.map +1 -0
  128. package/dist/graphify/index.js +12 -0
  129. package/dist/graphify/index.js.map +1 -0
  130. package/dist/graphify/kind.d.ts +23 -0
  131. package/dist/graphify/kind.d.ts.map +1 -0
  132. package/dist/graphify/kind.js +77 -0
  133. package/dist/graphify/kind.js.map +1 -0
  134. package/dist/graphify/resolve.d.ts +48 -0
  135. package/dist/graphify/resolve.d.ts.map +1 -0
  136. package/dist/graphify/resolve.js +83 -0
  137. package/dist/graphify/resolve.js.map +1 -0
  138. package/dist/graphify/signature.d.ts +87 -0
  139. package/dist/graphify/signature.d.ts.map +1 -0
  140. package/dist/graphify/signature.js +276 -0
  141. package/dist/graphify/signature.js.map +1 -0
  142. package/dist/graphify/types.d.ts +141 -0
  143. package/dist/graphify/types.d.ts.map +1 -0
  144. package/dist/graphify/types.js +8 -0
  145. package/dist/graphify/types.js.map +1 -0
  146. package/dist/hooks/useElkLayout.d.ts +66 -0
  147. package/dist/hooks/useElkLayout.d.ts.map +1 -0
  148. package/dist/hooks/useElkLayout.js +136 -0
  149. package/dist/hooks/useElkLayout.js.map +1 -0
  150. package/dist/hooks/usePathBasedEvents.d.ts +42 -0
  151. package/dist/hooks/usePathBasedEvents.d.ts.map +1 -0
  152. package/dist/hooks/usePathBasedEvents.js +119 -0
  153. package/dist/hooks/usePathBasedEvents.js.map +1 -0
  154. package/dist/hooks/useSequenceLayout.d.ts +186 -0
  155. package/dist/hooks/useSequenceLayout.d.ts.map +1 -0
  156. package/dist/hooks/useSequenceLayout.js +327 -0
  157. package/dist/hooks/useSequenceLayout.js.map +1 -0
  158. package/dist/hooks/useUndoRedo.d.ts +80 -0
  159. package/dist/hooks/useUndoRedo.d.ts.map +1 -0
  160. package/dist/hooks/useUndoRedo.js +70 -0
  161. package/dist/hooks/useUndoRedo.js.map +1 -0
  162. package/dist/index.d.ts +73 -0
  163. package/dist/index.d.ts.map +1 -0
  164. package/dist/index.js +51 -0
  165. package/dist/index.js.map +1 -0
  166. package/dist/nodes/CustomNode.d.ts +28 -0
  167. package/dist/nodes/CustomNode.d.ts.map +1 -0
  168. package/dist/nodes/CustomNode.js +820 -0
  169. package/dist/nodes/CustomNode.js.map +1 -0
  170. package/dist/nodes/GenericNode.d.ts +20 -0
  171. package/dist/nodes/GenericNode.d.ts.map +1 -0
  172. package/dist/nodes/GenericNode.js +20 -0
  173. package/dist/nodes/GenericNode.js.map +1 -0
  174. package/dist/nodes/otel/OtelBoundaryNode.d.ts +55 -0
  175. package/dist/nodes/otel/OtelBoundaryNode.d.ts.map +1 -0
  176. package/dist/nodes/otel/OtelBoundaryNode.js +81 -0
  177. package/dist/nodes/otel/OtelBoundaryNode.js.map +1 -0
  178. package/dist/nodes/otel/OtelEventNode.d.ts +59 -0
  179. package/dist/nodes/otel/OtelEventNode.d.ts.map +1 -0
  180. package/dist/nodes/otel/OtelEventNode.js +82 -0
  181. package/dist/nodes/otel/OtelEventNode.js.map +1 -0
  182. package/dist/nodes/otel/OtelResourceNode.d.ts +53 -0
  183. package/dist/nodes/otel/OtelResourceNode.d.ts.map +1 -0
  184. package/dist/nodes/otel/OtelResourceNode.js +105 -0
  185. package/dist/nodes/otel/OtelResourceNode.js.map +1 -0
  186. package/dist/nodes/otel/OtelScopeNode.d.ts +53 -0
  187. package/dist/nodes/otel/OtelScopeNode.d.ts.map +1 -0
  188. package/dist/nodes/otel/OtelScopeNode.js +81 -0
  189. package/dist/nodes/otel/OtelScopeNode.js.map +1 -0
  190. package/dist/nodes/otel/OtelSpanConventionNode.d.ts +61 -0
  191. package/dist/nodes/otel/OtelSpanConventionNode.d.ts.map +1 -0
  192. package/dist/nodes/otel/OtelSpanConventionNode.js +131 -0
  193. package/dist/nodes/otel/OtelSpanConventionNode.js.map +1 -0
  194. package/dist/nodes/otel/index.d.ts +17 -0
  195. package/dist/nodes/otel/index.d.ts.map +1 -0
  196. package/dist/nodes/otel/index.js +13 -0
  197. package/dist/nodes/otel/index.js.map +1 -0
  198. package/dist/nodes/otel/shared/NodeBadges.d.ts +10 -0
  199. package/dist/nodes/otel/shared/NodeBadges.d.ts.map +1 -0
  200. package/dist/nodes/otel/shared/NodeBadges.js +178 -0
  201. package/dist/nodes/otel/shared/NodeBadges.js.map +1 -0
  202. package/dist/nodes/otel/shared/NodeContent.d.ts +10 -0
  203. package/dist/nodes/otel/shared/NodeContent.d.ts.map +1 -0
  204. package/dist/nodes/otel/shared/NodeContent.js +96 -0
  205. package/dist/nodes/otel/shared/NodeContent.js.map +1 -0
  206. package/dist/nodes/otel/shared/index.d.ts +8 -0
  207. package/dist/nodes/otel/shared/index.d.ts.map +1 -0
  208. package/dist/nodes/otel/shared/index.js +8 -0
  209. package/dist/nodes/otel/shared/index.js.map +1 -0
  210. package/dist/nodes/otel/shared/types.d.ts +129 -0
  211. package/dist/nodes/otel/shared/types.d.ts.map +1 -0
  212. package/dist/nodes/otel/shared/types.js +5 -0
  213. package/dist/nodes/otel/shared/types.js.map +1 -0
  214. package/dist/nodes/otel/shared/useNodeBehavior.d.ts +42 -0
  215. package/dist/nodes/otel/shared/useNodeBehavior.d.ts.map +1 -0
  216. package/dist/nodes/otel/shared/useNodeBehavior.js +61 -0
  217. package/dist/nodes/otel/shared/useNodeBehavior.js.map +1 -0
  218. package/dist/pierre/PierreFileView.d.ts +17 -0
  219. package/dist/pierre/PierreFileView.d.ts.map +1 -0
  220. package/dist/pierre/PierreFileView.js +46 -0
  221. package/dist/pierre/PierreFileView.js.map +1 -0
  222. package/dist/pierre/PierreSnippetView.d.ts +28 -0
  223. package/dist/pierre/PierreSnippetView.d.ts.map +1 -0
  224. package/dist/pierre/PierreSnippetView.js +105 -0
  225. package/dist/pierre/PierreSnippetView.js.map +1 -0
  226. package/dist/pierre/PierreThroughlineCodeView.d.ts +22 -0
  227. package/dist/pierre/PierreThroughlineCodeView.d.ts.map +1 -0
  228. package/dist/pierre/PierreThroughlineCodeView.js +206 -0
  229. package/dist/pierre/PierreThroughlineCodeView.js.map +1 -0
  230. package/dist/pierre/constructColors.d.ts +24 -0
  231. package/dist/pierre/constructColors.d.ts.map +1 -0
  232. package/dist/pierre/constructColors.js +91 -0
  233. package/dist/pierre/constructColors.js.map +1 -0
  234. package/dist/pierre/index.d.ts +12 -0
  235. package/dist/pierre/index.d.ts.map +1 -0
  236. package/dist/pierre/index.js +7 -0
  237. package/dist/pierre/index.js.map +1 -0
  238. package/dist/pierre/pierreBackground.d.ts +15 -0
  239. package/dist/pierre/pierreBackground.d.ts.map +1 -0
  240. package/dist/pierre/pierreBackground.js +31 -0
  241. package/dist/pierre/pierreBackground.js.map +1 -0
  242. package/dist/pierre/pierreFileLang.d.ts +27 -0
  243. package/dist/pierre/pierreFileLang.d.ts.map +1 -0
  244. package/dist/pierre/pierreFileLang.js +52 -0
  245. package/dist/pierre/pierreFileLang.js.map +1 -0
  246. package/dist/pierre/pierreSyntaxTheme.d.ts +11 -0
  247. package/dist/pierre/pierreSyntaxTheme.d.ts.map +1 -0
  248. package/dist/pierre/pierreSyntaxTheme.js +20 -0
  249. package/dist/pierre/pierreSyntaxTheme.js.map +1 -0
  250. package/dist/pierre/scrollAnchor.d.ts +5 -0
  251. package/dist/pierre/scrollAnchor.d.ts.map +1 -0
  252. package/dist/pierre/scrollAnchor.js +9 -0
  253. package/dist/pierre/scrollAnchor.js.map +1 -0
  254. package/dist/pierre/sliceSnippet.d.ts +10 -0
  255. package/dist/pierre/sliceSnippet.d.ts.map +1 -0
  256. package/dist/pierre/sliceSnippet.js +16 -0
  257. package/dist/pierre/sliceSnippet.js.map +1 -0
  258. package/dist/pierre/syntaxTokens.d.ts +11 -0
  259. package/dist/pierre/syntaxTokens.d.ts.map +1 -0
  260. package/dist/pierre/syntaxTokens.js +51 -0
  261. package/dist/pierre/syntaxTokens.js.map +1 -0
  262. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +8 -0
  263. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -0
  264. package/dist/stories/Subsystem/ComponentGraph/fixtures.js +116 -0
  265. package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -0
  266. package/dist/subsystem/ComponentDeclaration.d.ts +87 -0
  267. package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -0
  268. package/dist/subsystem/ComponentDeclaration.js +374 -0
  269. package/dist/subsystem/ComponentDeclaration.js.map +1 -0
  270. package/dist/subsystem/FileDrawer.d.ts +17 -0
  271. package/dist/subsystem/FileDrawer.d.ts.map +1 -0
  272. package/dist/subsystem/FileDrawer.js +72 -0
  273. package/dist/subsystem/FileDrawer.js.map +1 -0
  274. package/dist/subsystem/GraphLayoutCover.d.ts +20 -0
  275. package/dist/subsystem/GraphLayoutCover.d.ts.map +1 -0
  276. package/dist/subsystem/GraphLayoutCover.js +42 -0
  277. package/dist/subsystem/GraphLayoutCover.js.map +1 -0
  278. package/dist/subsystem/SubsystemComponentGraph.d.ts +96 -0
  279. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -0
  280. package/dist/subsystem/SubsystemComponentGraph.js +1059 -0
  281. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -0
  282. package/dist/subsystem/SubsystemFileTree.d.ts +24 -0
  283. package/dist/subsystem/SubsystemFileTree.d.ts.map +1 -0
  284. package/dist/subsystem/SubsystemFileTree.js +150 -0
  285. package/dist/subsystem/SubsystemFileTree.js.map +1 -0
  286. package/dist/subsystem/declarationRef.d.ts +39 -0
  287. package/dist/subsystem/declarationRef.d.ts.map +1 -0
  288. package/dist/subsystem/declarationRef.js +32 -0
  289. package/dist/subsystem/declarationRef.js.map +1 -0
  290. package/dist/subsystem/formatDeclaration.d.ts +11 -0
  291. package/dist/subsystem/formatDeclaration.d.ts.map +1 -0
  292. package/dist/subsystem/formatDeclaration.js +146 -0
  293. package/dist/subsystem/formatDeclaration.js.map +1 -0
  294. package/dist/subsystem/model.d.ts +353 -0
  295. package/dist/subsystem/model.d.ts.map +1 -0
  296. package/dist/subsystem/model.js +503 -0
  297. package/dist/subsystem/model.js.map +1 -0
  298. package/dist/subsystem/nodes.d.ts +69 -0
  299. package/dist/subsystem/nodes.d.ts.map +1 -0
  300. package/dist/subsystem/nodes.js +289 -0
  301. package/dist/subsystem/nodes.js.map +1 -0
  302. package/dist/subsystem/paths.d.ts +63 -0
  303. package/dist/subsystem/paths.d.ts.map +1 -0
  304. package/dist/subsystem/paths.js +86 -0
  305. package/dist/subsystem/paths.js.map +1 -0
  306. package/dist/subsystem/tokenizeComponent.d.ts +20 -0
  307. package/dist/subsystem/tokenizeComponent.d.ts.map +1 -0
  308. package/dist/subsystem/tokenizeComponent.js +56 -0
  309. package/dist/subsystem/tokenizeComponent.js.map +1 -0
  310. package/dist/subsystem/tokenizeFormatted.d.ts +12 -0
  311. package/dist/subsystem/tokenizeFormatted.d.ts.map +1 -0
  312. package/dist/subsystem/tokenizeFormatted.js +13 -0
  313. package/dist/subsystem/tokenizeFormatted.js.map +1 -0
  314. package/dist/utils/animationMapping.d.ts +53 -0
  315. package/dist/utils/animationMapping.d.ts.map +1 -0
  316. package/dist/utils/animationMapping.js +126 -0
  317. package/dist/utils/animationMapping.js.map +1 -0
  318. package/dist/utils/canvasBounds.d.ts +50 -0
  319. package/dist/utils/canvasBounds.d.ts.map +1 -0
  320. package/dist/utils/canvasBounds.js +88 -0
  321. package/dist/utils/canvasBounds.js.map +1 -0
  322. package/dist/utils/elkLayout.d.ts +137 -0
  323. package/dist/utils/elkLayout.d.ts.map +1 -0
  324. package/dist/utils/elkLayout.js +596 -0
  325. package/dist/utils/elkLayout.js.map +1 -0
  326. package/dist/utils/graphConverter.d.ts +18 -0
  327. package/dist/utils/graphConverter.d.ts.map +1 -0
  328. package/dist/utils/graphConverter.js +114 -0
  329. package/dist/utils/graphConverter.js.map +1 -0
  330. package/dist/utils/iconResolver.d.ts +29 -0
  331. package/dist/utils/iconResolver.d.ts.map +1 -0
  332. package/dist/utils/iconResolver.js +30 -0
  333. package/dist/utils/iconResolver.js.map +1 -0
  334. package/dist/utils/orientationUtils.d.ts +19 -0
  335. package/dist/utils/orientationUtils.d.ts.map +1 -0
  336. package/dist/utils/orientationUtils.js +62 -0
  337. package/dist/utils/orientationUtils.js.map +1 -0
  338. package/package.json +80 -0
  339. package/src/components/ConfigurationSelector.tsx +133 -0
  340. package/src/components/GraphRenderer.tsx +3195 -0
  341. package/src/components/MultiCanvasRenderer.tsx +295 -0
  342. package/src/components/NodeTooltip.tsx +279 -0
  343. package/src/components/PendingChanges.test.tsx +375 -0
  344. package/src/components/SelectionSidebar.tsx +352 -0
  345. package/src/components/SequenceDiagramRenderer.tsx +1156 -0
  346. package/src/components/WorkflowSequenceDiagram.tsx +270 -0
  347. package/src/components/dashboard/DashboardRenderer.tsx +317 -0
  348. package/src/components/dashboard/MetricPanel.tsx +254 -0
  349. package/src/components/dashboard/MockDataProvider.ts +330 -0
  350. package/src/components/dashboard/components/BarChart.tsx +299 -0
  351. package/src/components/dashboard/components/LineChart.tsx +279 -0
  352. package/src/components/dashboard/components/MetricCard.tsx +270 -0
  353. package/src/components/dashboard/components/SourceLink.tsx +63 -0
  354. package/src/components/dashboard/components/TimeRangeSelector.tsx +280 -0
  355. package/src/components/dashboard/components/index.ts +5 -0
  356. package/src/components/dashboard/index.ts +47 -0
  357. package/src/components/dashboard/types.ts +126 -0
  358. package/src/components/session-events/SessionEventFeed.tsx +1797 -0
  359. package/src/components/session-events/index.ts +13 -0
  360. package/src/components/state-view/PipelineView.tsx +347 -0
  361. package/src/components/state-view/index.ts +14 -0
  362. package/src/components/state-view/types.ts +261 -0
  363. package/src/components/state-view/useStateView.ts +205 -0
  364. package/src/contexts/GraphEditContext.tsx +27 -0
  365. package/src/contexts/TooltipPortalContext.ts +8 -0
  366. package/src/edges/CustomEdge.tsx +402 -0
  367. package/src/edges/GenericEdge.tsx +36 -0
  368. package/src/graphify/anchor.test.ts +124 -0
  369. package/src/graphify/anchor.ts +160 -0
  370. package/src/graphify/consolidated.ts +246 -0
  371. package/src/graphify/ids.ts +48 -0
  372. package/src/graphify/index.ts +72 -0
  373. package/src/graphify/kind.test.ts +108 -0
  374. package/src/graphify/kind.ts +120 -0
  375. package/src/graphify/resolve.test.ts +89 -0
  376. package/src/graphify/resolve.ts +115 -0
  377. package/src/graphify/signature.test.ts +278 -0
  378. package/src/graphify/signature.ts +354 -0
  379. package/src/graphify/types.ts +164 -0
  380. package/src/hooks/useElkLayout.test.ts +134 -0
  381. package/src/hooks/useElkLayout.ts +191 -0
  382. package/src/hooks/usePathBasedEvents.ts +192 -0
  383. package/src/hooks/useSequenceLayout.ts +543 -0
  384. package/src/hooks/useUndoRedo.ts +144 -0
  385. package/src/index.ts +341 -0
  386. package/src/nodes/CustomNode.tsx +1231 -0
  387. package/src/nodes/GenericNode.tsx +55 -0
  388. package/src/nodes/otel/OtelBoundaryNode.tsx +224 -0
  389. package/src/nodes/otel/OtelEventNode.tsx +225 -0
  390. package/src/nodes/otel/OtelResourceNode.tsx +251 -0
  391. package/src/nodes/otel/OtelScopeNode.tsx +221 -0
  392. package/src/nodes/otel/OtelSpanConventionNode.tsx +295 -0
  393. package/src/nodes/otel/index.ts +23 -0
  394. package/src/nodes/otel/shared/NodeBadges.tsx +295 -0
  395. package/src/nodes/otel/shared/NodeContent.tsx +204 -0
  396. package/src/nodes/otel/shared/index.ts +8 -0
  397. package/src/nodes/otel/shared/types.ts +138 -0
  398. package/src/nodes/otel/shared/useNodeBehavior.ts +114 -0
  399. package/src/pierre/PierreFileView.tsx +81 -0
  400. package/src/pierre/PierreSnippetView.tsx +184 -0
  401. package/src/pierre/PierreThroughlineCodeView.tsx +304 -0
  402. package/src/pierre/constructColors.test.ts +52 -0
  403. package/src/pierre/constructColors.ts +112 -0
  404. package/src/pierre/index.ts +18 -0
  405. package/src/pierre/pierreBackground.ts +33 -0
  406. package/src/pierre/pierreFileLang.test.ts +26 -0
  407. package/src/pierre/pierreFileLang.ts +55 -0
  408. package/src/pierre/pierreSyntaxTheme.ts +23 -0
  409. package/src/pierre/scrollAnchor.test.ts +13 -0
  410. package/src/pierre/scrollAnchor.ts +11 -0
  411. package/src/pierre/sliceSnippet.test.ts +21 -0
  412. package/src/pierre/sliceSnippet.ts +31 -0
  413. package/src/pierre/syntaxTokens.test.ts +35 -0
  414. package/src/pierre/syntaxTokens.ts +60 -0
  415. package/src/stories/Audit/BoundaryNodes.stories.tsx +385 -0
  416. package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +1448 -0
  417. package/src/stories/Audit/CanvasNodeTypes.stories.tsx +1049 -0
  418. package/src/stories/Audit/ColorPriority.stories.tsx +895 -0
  419. package/src/stories/Audit/DiamondBadges.stories.tsx +629 -0
  420. package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +724 -0
  421. package/src/stories/Audit/NodeFieldsAudit.stories.tsx +996 -0
  422. package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +715 -0
  423. package/src/stories/Audit/NodeShapes.stories.tsx +1208 -0
  424. package/src/stories/Audit/WideNodesTesting.stories.tsx +1045 -0
  425. package/src/stories/Components/DashboardRenderer.stories.tsx +263 -0
  426. package/src/stories/Components/GraphRenderer.stories.tsx +3236 -0
  427. package/src/stories/Components/MultiCanvasRenderer.stories.tsx +370 -0
  428. package/src/stories/Components/SequenceDiagram.stories.tsx +255 -0
  429. package/src/stories/Components/StateView.stories.tsx +417 -0
  430. package/src/stories/Components/TraceViewer.stories.tsx +442 -0
  431. package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +802 -0
  432. package/src/stories/Features/AnimationWorkshop.stories.tsx +727 -0
  433. package/src/stories/Features/ChangeTypeVisual.stories.tsx +67 -0
  434. package/src/stories/Features/EventDrivenAnimations.stories.tsx +513 -0
  435. package/src/stories/Features/MultiConfig.stories.tsx +539 -0
  436. package/src/stories/Introduction.mdx +34 -0
  437. package/src/stories/Layout/ElkEdgeRouting.stories.tsx +539 -0
  438. package/src/stories/Layout/GraphOrientation.stories.tsx +500 -0
  439. package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +352 -0
  440. package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +560 -0
  441. package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +491 -0
  442. package/src/stories/Nodes/NumericColors.stories.tsx +440 -0
  443. package/src/stories/OTEL/EventNameDisplay.stories.tsx +694 -0
  444. package/src/stories/OTEL/EventNameFallback.stories.tsx +578 -0
  445. package/src/stories/OTEL/OtelComponents.stories.tsx +422 -0
  446. package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +806 -0
  447. package/src/stories/OTEL/RawEventCards.stories.tsx +57 -0
  448. package/src/stories/OTEL/SpanBadges.stories.tsx +840 -0
  449. package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +209 -0
  450. package/src/stories/Pierre/CodeView.stories.tsx +219 -0
  451. package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +721 -0
  452. package/src/stories/RealWorld/FileCitySequence.stories.tsx +516 -0
  453. package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +336 -0
  454. package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +321 -0
  455. package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +365 -0
  456. package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +137 -0
  457. package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +238 -0
  458. package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +315 -0
  459. package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +239 -0
  460. package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +216 -0
  461. package/src/stories/Subsystem/ComponentGraph/GraphTitle.stories.tsx +70 -0
  462. package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +68 -0
  463. package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +959 -0
  464. package/src/stories/Subsystem/ComponentGraph/Spotlights.stories.tsx +681 -0
  465. package/src/stories/Subsystem/ComponentGraph/fixtures.ts +129 -0
  466. package/src/stories/__traces__/test-run.canvas.json +241 -0
  467. package/src/stories/data/activity-feed-analytics.dashboard.json +300 -0
  468. package/src/stories/data/file-city-images-transformed.otel.canvas.json +295 -0
  469. package/src/stories/data/file-city-workflows.json +269 -0
  470. package/src/stories/data/graph-converter-test-execution.json +443 -0
  471. package/src/stories/data/graph-converter-test.workflow.json +105 -0
  472. package/src/stories/data/graph-converter-validated-execution.json +58 -0
  473. package/src/stories/data/opencode-session-raw-events.json +273396 -0
  474. package/src/subsystem/ComponentDeclaration.tsx +643 -0
  475. package/src/subsystem/FileDrawer.tsx +106 -0
  476. package/src/subsystem/GraphLayoutCover.tsx +69 -0
  477. package/src/subsystem/SubsystemComponentGraph.tsx +1644 -0
  478. package/src/subsystem/SubsystemFileTree.tsx +190 -0
  479. package/src/subsystem/declarationRef.test.ts +33 -0
  480. package/src/subsystem/declarationRef.ts +63 -0
  481. package/src/subsystem/formatDeclaration.ts +174 -0
  482. package/src/subsystem/model.test.ts +244 -0
  483. package/src/subsystem/model.ts +850 -0
  484. package/src/subsystem/nodes.test.ts +56 -0
  485. package/src/subsystem/nodes.tsx +443 -0
  486. package/src/subsystem/paths.test.ts +123 -0
  487. package/src/subsystem/paths.ts +127 -0
  488. package/src/subsystem/tokenizeComponent.ts +70 -0
  489. package/src/subsystem/tokenizeFormatted.ts +19 -0
  490. package/src/utils/animationMapping.ts +166 -0
  491. package/src/utils/canvasBounds.ts +146 -0
  492. package/src/utils/elkLayout.test.ts +240 -0
  493. package/src/utils/elkLayout.ts +779 -0
  494. package/src/utils/graphConverter.ts +148 -0
  495. package/src/utils/iconResolver.tsx +53 -0
  496. 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
+ });