@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,779 @@
1
+ /**
2
+ * ELK (Eclipse Layout Kernel) Layout Utility
3
+ *
4
+ * Provides sophisticated edge routing with orthogonal (circuit-board style) paths
5
+ * that don't overlap and run parallel to each other.
6
+ */
7
+
8
+ import ELK, { type ElkNode, type ElkExtendedEdge, type LayoutOptions } from 'elkjs/lib/elk.bundled.js';
9
+ import type { Node, Edge } from '@xyflow/react';
10
+
11
+ /** ELK layout options for different routing styles */
12
+ export type ElkRoutingStyle = 'orthogonal' | 'splines' | 'polyline';
13
+
14
+ /** Options for ELK layout */
15
+ export interface ElkLayoutOptions {
16
+ /**
17
+ * Edge routing style
18
+ * - 'orthogonal': Circuit-board style with 90-degree angles (default)
19
+ * - 'splines': Smooth curved edges
20
+ * - 'polyline': Straight line segments
21
+ */
22
+ routingStyle?: ElkRoutingStyle;
23
+
24
+ /**
25
+ * Whether to preserve manual node positions
26
+ * If true, only edge routing is computed
27
+ * If false, ELK will also position nodes
28
+ * @default true
29
+ */
30
+ preserveNodePositions?: boolean;
31
+
32
+ /**
33
+ * Minimum spacing between nodes
34
+ * @default 50
35
+ */
36
+ nodeSpacing?: number;
37
+
38
+ /**
39
+ * Minimum spacing between edges
40
+ * @default 8
41
+ */
42
+ edgeSpacing?: number;
43
+
44
+ /**
45
+ * Spacing between edge and node
46
+ * @default 10
47
+ */
48
+ edgeNodeSpacing?: number;
49
+
50
+ /**
51
+ * Minimum horizontal distance between layers (node-to-node across layers).
52
+ * Controls the gap that prevents nodes in adjacent layers from overlapping.
53
+ * @default 0
54
+ */
55
+ interLayerSpacing?: number;
56
+
57
+ /**
58
+ * Reserve space along edges for inline labels so they don't overlap nodes
59
+ * or other edges. When enabled ELK places labels inline on the edge with the
60
+ * given margin model.
61
+ */
62
+ edgeLabels?: {
63
+ /** Whether to reserve label space in the layout. @default true */
64
+ enabled?: boolean;
65
+ /** Placement of inline labels. @default 'CENTER' */
66
+ placement?: 'CENTER' | 'TAIL' | 'HEAD';
67
+ };
68
+
69
+ /**
70
+ * Layout direction
71
+ * @default 'RIGHT'
72
+ */
73
+ direction?: 'RIGHT' | 'LEFT' | 'DOWN' | 'UP';
74
+
75
+ /**
76
+ * Compound groups — each becomes a nested ELK parent whose `memberIds`
77
+ * are laid out inside it. Members reference the group via React Flow
78
+ * `parentId`; ELK returns parent-relative child positions which this
79
+ * module flattens back to absolute flow coordinates.
80
+ */
81
+ groups?: Array<{ id: string; memberIds: string[] }>;
82
+ }
83
+
84
+ /** Result of ELK layout computation */
85
+ export interface ElkLayoutResult {
86
+ /** Nodes with updated positions (if preserveNodePositions is false) */
87
+ nodes: Node[];
88
+ /** Edge paths as SVG path strings, keyed by edge ID */
89
+ edgePaths: Map<string, string>;
90
+ /** Edge label positions, keyed by edge ID */
91
+ edgeLabelPositions: Map<string, { x: number; y: number }>;
92
+ /** Raw ELK path points per edge (for debugging). */
93
+ edgePathPoints: Map<string, Point[]>;
94
+ /** Compound parent bounds from ELK (absolute flow coords), keyed by group id. */
95
+ groupBounds: Map<string, { x: number; y: number; width: number; height: number }>;
96
+ }
97
+
98
+ /** Point in 2D space */
99
+ export interface Point {
100
+ x: number;
101
+ y: number;
102
+ }
103
+
104
+ /** ELK edge section with bend points */
105
+ interface ElkEdgeSection {
106
+ id: string;
107
+ startPoint: Point;
108
+ endPoint: Point;
109
+ bendPoints?: Point[];
110
+ }
111
+
112
+ /** Extended ELK edge with sections */
113
+ interface ElkEdgeWithSections extends ElkExtendedEdge {
114
+ sections?: ElkEdgeSection[];
115
+ /** Id of the compound node whose coordinate frame sections/labels use. */
116
+ container?: string;
117
+ }
118
+
119
+ // Create ELK instance lazily to avoid issues in test environments
120
+ let elkInstance: InstanceType<typeof ELK> | null = null;
121
+
122
+ function getElkInstance(): InstanceType<typeof ELK> {
123
+ if (!elkInstance) {
124
+ elkInstance = new ELK();
125
+ }
126
+ return elkInstance;
127
+ }
128
+
129
+ /**
130
+ * Convert bend points to SVG path string
131
+ * @public Exported for testing
132
+ */
133
+ export function pointsToPath(points: Point[]): string {
134
+ if (points.length === 0) return '';
135
+ if (points.length === 1) return `M ${points[0].x} ${points[0].y}`;
136
+
137
+ let path = `M ${points[0].x} ${points[0].y}`;
138
+ for (let i = 1; i < points.length; i++) {
139
+ path += ` L ${points[i].x} ${points[i].y}`;
140
+ }
141
+ return path;
142
+ }
143
+
144
+ /**
145
+ * Convert bend points to smooth orthogonal path with rounded corners
146
+ * @public Exported for testing
147
+ */
148
+ export function pointsToSmoothPath(points: Point[], cornerRadius: number = 8): string {
149
+ if (points.length === 0) return '';
150
+ if (points.length === 1) return `M ${points[0].x} ${points[0].y}`;
151
+ if (points.length === 2) {
152
+ return `M ${points[0].x} ${points[0].y} L ${points[1].x} ${points[1].y}`;
153
+ }
154
+
155
+ let path = `M ${points[0].x} ${points[0].y}`;
156
+
157
+ for (let i = 1; i < points.length - 1; i++) {
158
+ const prev = points[i - 1];
159
+ const curr = points[i];
160
+ const next = points[i + 1];
161
+
162
+ // Calculate distances
163
+ const d1 = Math.sqrt(Math.pow(curr.x - prev.x, 2) + Math.pow(curr.y - prev.y, 2));
164
+ const d2 = Math.sqrt(Math.pow(next.x - curr.x, 2) + Math.pow(next.y - curr.y, 2));
165
+
166
+ // Limit corner radius to half the shorter segment
167
+ const maxRadius = Math.min(d1, d2) / 2;
168
+ const radius = Math.min(cornerRadius, maxRadius);
169
+
170
+ if (radius < 1) {
171
+ // Too short for curve, just draw line
172
+ path += ` L ${curr.x} ${curr.y}`;
173
+ continue;
174
+ }
175
+
176
+ // Calculate direction vectors
177
+ const dir1 = { x: (curr.x - prev.x) / d1, y: (curr.y - prev.y) / d1 };
178
+ const dir2 = { x: (next.x - curr.x) / d2, y: (next.y - curr.y) / d2 };
179
+
180
+ // Calculate arc start and end points
181
+ const arcStart = {
182
+ x: curr.x - dir1.x * radius,
183
+ y: curr.y - dir1.y * radius,
184
+ };
185
+ const arcEnd = {
186
+ x: curr.x + dir2.x * radius,
187
+ y: curr.y + dir2.y * radius,
188
+ };
189
+
190
+ // Draw line to arc start, then quadratic curve to arc end
191
+ path += ` L ${arcStart.x} ${arcStart.y}`;
192
+ path += ` Q ${curr.x} ${curr.y} ${arcEnd.x} ${arcEnd.y}`;
193
+ }
194
+
195
+ // Final line to last point
196
+ const last = points[points.length - 1];
197
+ path += ` L ${last.x} ${last.y}`;
198
+
199
+ return path;
200
+ }
201
+
202
+ /**
203
+ * Calculate the midpoint of a path for label positioning
204
+ * @public Exported for testing
205
+ */
206
+ export function calculatePathMidpoint(points: Point[]): Point {
207
+ if (points.length === 0) return { x: 0, y: 0 };
208
+ if (points.length === 1) return points[0];
209
+
210
+ // Calculate total path length
211
+ let totalLength = 0;
212
+ const segmentLengths: number[] = [];
213
+
214
+ for (let i = 1; i < points.length; i++) {
215
+ const len = Math.sqrt(
216
+ Math.pow(points[i].x - points[i - 1].x, 2) + Math.pow(points[i].y - points[i - 1].y, 2)
217
+ );
218
+ segmentLengths.push(len);
219
+ totalLength += len;
220
+ }
221
+
222
+ // Find midpoint
223
+ const targetLength = totalLength / 2;
224
+ let currentLength = 0;
225
+
226
+ for (let i = 0; i < segmentLengths.length; i++) {
227
+ if (currentLength + segmentLengths[i] >= targetLength) {
228
+ // Midpoint is on this segment
229
+ const remaining = targetLength - currentLength;
230
+ const ratio = remaining / segmentLengths[i];
231
+ return {
232
+ x: points[i].x + (points[i + 1].x - points[i].x) * ratio,
233
+ y: points[i].y + (points[i + 1].y - points[i].y) * ratio,
234
+ };
235
+ }
236
+ currentLength += segmentLengths[i];
237
+ }
238
+
239
+ // Fallback to last point
240
+ return points[points.length - 1];
241
+ }
242
+
243
+ /**
244
+ * Closest point on a polyline to a target (for snapping labels onto the stroke).
245
+ */
246
+ export function closestPointOnPath(points: Point[], target: Point): Point {
247
+ if (points.length === 0) return { x: 0, y: 0 };
248
+ if (points.length === 1) return points[0];
249
+
250
+ let best = points[0];
251
+ let bestDist = Infinity;
252
+
253
+ for (let i = 1; i < points.length; i++) {
254
+ const a = points[i - 1];
255
+ const b = points[i];
256
+ const dx = b.x - a.x;
257
+ const dy = b.y - a.y;
258
+ const lenSq = dx * dx + dy * dy;
259
+ const t = lenSq === 0 ? 0 : Math.max(0, Math.min(1, ((target.x - a.x) * dx + (target.y - a.y) * dy) / lenSq));
260
+ const px = a.x + dx * t;
261
+ const py = a.y + dy * t;
262
+ const dist = Math.hypot(target.x - px, target.y - py);
263
+ if (dist < bestDist) {
264
+ bestDist = dist;
265
+ best = { x: px, y: py };
266
+ }
267
+ }
268
+
269
+ return best;
270
+ }
271
+
272
+ /**
273
+ * Polyline length in flow-space units (sum of segment lengths).
274
+ * @public Exported for testing
275
+ */
276
+ export function calculatePathLength(points: Point[]): number {
277
+ let total = 0;
278
+ for (let i = 1; i < points.length; i++) {
279
+ total += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);
280
+ }
281
+ return total;
282
+ }
283
+
284
+ /**
285
+ * Get ELK layout options based on configuration
286
+ */
287
+ function getElkOptions(options: ElkLayoutOptions): LayoutOptions {
288
+ const {
289
+ routingStyle = 'orthogonal',
290
+ nodeSpacing = 50,
291
+ edgeSpacing = 8,
292
+ edgeNodeSpacing = 10,
293
+ interLayerSpacing = 0,
294
+ edgeLabels,
295
+ direction = 'RIGHT',
296
+ } = options;
297
+
298
+ const baseOptions: LayoutOptions = {
299
+ 'elk.algorithm': 'layered',
300
+ 'elk.direction': direction,
301
+ // Spacing
302
+ 'elk.spacing.nodeNode': String(nodeSpacing),
303
+ 'elk.spacing.edgeEdge': String(edgeSpacing),
304
+ 'elk.spacing.edgeNode': String(edgeNodeSpacing),
305
+ 'elk.layered.spacing.edgeEdgeBetweenLayers': String(edgeSpacing),
306
+ 'elk.layered.spacing.edgeNodeBetweenLayers': String(edgeNodeSpacing),
307
+ 'elk.layered.spacing.nodeNodeBetweenLayers': String(interLayerSpacing),
308
+ // Port constraints - edges connect at specific sides
309
+ 'elk.portConstraints': 'FIXED_SIDE',
310
+ // Improve orthogonal routing quality
311
+ 'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',
312
+ 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
313
+ // Merge edges going same direction for cleaner routing
314
+ 'elk.layered.mergeEdges': 'true',
315
+ // Higher thoroughness = better edge routing (1-100)
316
+ 'elk.layered.thoroughness': '50',
317
+ };
318
+
319
+ // Reserve space for inline edge labels so they don't overlap nodes/edges.
320
+ if (edgeLabels?.enabled !== false) {
321
+ const placement = edgeLabels?.placement ?? 'CENTER';
322
+ baseOptions['elk.edgeLabels.inline'] = 'true';
323
+ baseOptions['elk.edgeLabels.inlinePlacement'] = placement;
324
+ baseOptions['elk.layered.edgeLabels.centerLabelPlacementStrategy'] = 'CENTER_LAYER';
325
+ }
326
+
327
+ // Set edge routing style
328
+ switch (routingStyle) {
329
+ case 'orthogonal':
330
+ baseOptions['elk.edgeRouting'] = 'ORTHOGONAL';
331
+ break;
332
+ case 'splines':
333
+ baseOptions['elk.edgeRouting'] = 'SPLINES';
334
+ break;
335
+ case 'polyline':
336
+ baseOptions['elk.edgeRouting'] = 'POLYLINE';
337
+ break;
338
+ }
339
+
340
+ return baseOptions;
341
+ }
342
+
343
+ /**
344
+ * Compute ELK layout for nodes and edges
345
+ *
346
+ * @param nodes - xyflow nodes
347
+ * @param edges - xyflow edges
348
+ * @param options - Layout options
349
+ * @returns Layout result with edge paths
350
+ */
351
+ export async function computeElkLayout(
352
+ nodes: Node[],
353
+ edges: Edge[],
354
+ options: ElkLayoutOptions = {}
355
+ ): Promise<ElkLayoutResult> {
356
+ const { preserveNodePositions = true } = options;
357
+ const edgeLabels = options.edgeLabels;
358
+ const direction = options.direction ?? 'RIGHT';
359
+
360
+ // Build a map of original node positions BEFORE passing to ELK
361
+ // (ELK mutates the input nodes in place, so we must save positions first)
362
+ const originalPositions = new Map<string, { x: number; y: number }>();
363
+ for (const node of nodes) {
364
+ originalPositions.set(node.id, { x: node.position.x, y: node.position.y });
365
+ }
366
+
367
+ // Convert nodes to ELK format with ports for clean edge routing
368
+ const elkNodes: ElkNode[] = nodes.map((node) => {
369
+ const width = node.measured?.width ?? node.width ?? 200;
370
+ const height = node.measured?.height ?? node.height ?? 100;
371
+
372
+ // Optional explicit layout layer, read from node data (e.g. our subsystem
373
+ // components carry `data.component.layer`). Forces ELK to place the node in
374
+ // that layer so the graph reads as a pipeline rather than a guess.
375
+ let layer: number | undefined;
376
+ const nd = node.data as { component?: { layer?: number }; layer?: number } | undefined;
377
+ layer = nd?.layer ?? nd?.component?.layer;
378
+
379
+ return {
380
+ id: node.id,
381
+ width,
382
+ height,
383
+ x: node.position.x,
384
+ y: node.position.y,
385
+ // Add ports on each side for edge connections
386
+ ports: [
387
+ { id: `${node.id}_top`, properties: { 'port.side': 'NORTH' } },
388
+ { id: `${node.id}_right`, properties: { 'port.side': 'EAST' } },
389
+ { id: `${node.id}_bottom`, properties: { 'port.side': 'SOUTH' } },
390
+ { id: `${node.id}_left`, properties: { 'port.side': 'WEST' } },
391
+ ],
392
+ properties: {
393
+ 'portConstraints': 'FIXED_SIDE',
394
+ ...(layer !== undefined ? { 'layering.layer': String(layer) } : {}),
395
+ },
396
+ };
397
+ });
398
+
399
+ // Map handle name to port suffix
400
+ // Handles may have suffixes like "-out" (e.g., "right-out" for source handles)
401
+ const handleToPortSuffix = (handle: string | null | undefined): string | null => {
402
+ if (!handle) return null;
403
+ // Remove -out or -in suffix if present
404
+ const h = handle.toLowerCase().replace(/-out$/, '').replace(/-in$/, '');
405
+ if (h === 'top' || h === 'north') return 'top';
406
+ if (h === 'bottom' || h === 'south') return 'bottom';
407
+ if (h === 'left' || h === 'west') return 'left';
408
+ if (h === 'right' || h === 'east') return 'right';
409
+ return null;
410
+ };
411
+
412
+ // Determine which port to use - prefer explicit handles, fallback to position-based
413
+ const getPortSide = (
414
+ sourceId: string,
415
+ targetId: string,
416
+ sourceHandle?: string | null,
417
+ targetHandle?: string | null
418
+ ): { sourcePort: string; targetPort: string } => {
419
+ // Try to use explicit handles first
420
+ const sourceSuffix = handleToPortSuffix(sourceHandle);
421
+ const targetSuffix = handleToPortSuffix(targetHandle);
422
+
423
+ if (sourceSuffix && targetSuffix) {
424
+ return {
425
+ sourcePort: `${sourceId}_${sourceSuffix}`,
426
+ targetPort: `${targetId}_${targetSuffix}`,
427
+ };
428
+ }
429
+
430
+ // When ELK repositions nodes (preserveNodePositions=false), the initial
431
+ // grid positions are unreliable — use the layout direction instead.
432
+ if (!preserveNodePositions) {
433
+ switch (direction) {
434
+ case 'RIGHT': return { sourcePort: `${sourceId}_right`, targetPort: `${targetId}_left` };
435
+ case 'LEFT': return { sourcePort: `${sourceId}_left`, targetPort: `${targetId}_right` };
436
+ case 'DOWN': return { sourcePort: `${sourceId}_bottom`, targetPort: `${targetId}_top` };
437
+ case 'UP': return { sourcePort: `${sourceId}_top`, targetPort: `${targetId}_bottom` };
438
+ }
439
+ }
440
+
441
+ // Fallback to position-based calculation (for preserveNodePositions=true)
442
+ const sourcePos = originalPositions.get(sourceId);
443
+ const targetPos = originalPositions.get(targetId);
444
+
445
+ if (!sourcePos || !targetPos) {
446
+ return { sourcePort: `${sourceId}_right`, targetPort: `${targetId}_left` };
447
+ }
448
+
449
+ const dx = targetPos.x - sourcePos.x;
450
+ const dy = targetPos.y - sourcePos.y;
451
+
452
+ // Determine primary direction
453
+ if (Math.abs(dx) > Math.abs(dy)) {
454
+ // Horizontal movement dominates
455
+ if (dx > 0) {
456
+ return { sourcePort: `${sourceId}_right`, targetPort: `${targetId}_left` };
457
+ } else {
458
+ return { sourcePort: `${sourceId}_left`, targetPort: `${targetId}_right` };
459
+ }
460
+ } else {
461
+ // Vertical movement dominates
462
+ if (dy > 0) {
463
+ return { sourcePort: `${sourceId}_bottom`, targetPort: `${targetId}_top` };
464
+ } else {
465
+ return { sourcePort: `${sourceId}_top`, targetPort: `${targetId}_bottom` };
466
+ }
467
+ }
468
+ };
469
+
470
+ // Track routing direction for each edge (true = horizontal-first, false = vertical-first)
471
+ const edgeRoutingDirection = new Map<string, boolean>();
472
+
473
+ // Convert edges to ELK format with port references
474
+ const elkEdges: ElkExtendedEdge[] = edges.map((edge) => {
475
+ const { sourcePort, targetPort } = getPortSide(
476
+ edge.source,
477
+ edge.target,
478
+ edge.sourceHandle,
479
+ edge.targetHandle
480
+ );
481
+
482
+ // Determine routing direction based on source port
483
+ // right/left ports = horizontal-first, top/bottom ports = vertical-first
484
+ const isHorizontalFirst = sourcePort.endsWith('_right') || sourcePort.endsWith('_left');
485
+ edgeRoutingDirection.set(edge.id, isHorizontalFirst);
486
+
487
+ const elkEdge: ElkExtendedEdge = {
488
+ id: edge.id,
489
+ sources: [sourcePort],
490
+ targets: [targetPort],
491
+ };
492
+ // Estimated label size so ELK reserves room to render the inline label
493
+ // without it overlapping nodes or sibling edges.
494
+ if (edgeLabels?.enabled !== false && typeof edge.label === 'string') {
495
+ const text = edge.label;
496
+ const labelWidth = Math.max(20, text.length * 7); // ~7px per mono char
497
+ const labelHeight = 14;
498
+ elkEdge.labels = [{ text, width: labelWidth, height: labelHeight }];
499
+ }
500
+ return elkEdge;
501
+ });
502
+
503
+ // Partition leaf nodes into compound parents when groups are given.
504
+ // Group shells themselves are NOT part of `nodes` — they are reconstructed
505
+ // by the caller from `groupBounds`. Only leaf ids in `memberIds` nest.
506
+ const groupDefs = (options.groups ?? []).filter((g) => g.memberIds.length > 0);
507
+ const memberToGroup = new Map<string, string>();
508
+ const groupedLeafIds = new Set<string>();
509
+ for (const g of groupDefs) {
510
+ for (const mid of g.memberIds) {
511
+ if (!memberToGroup.has(mid)) {
512
+ memberToGroup.set(mid, g.id);
513
+ groupedLeafIds.add(mid);
514
+ }
515
+ }
516
+ }
517
+ const elkById = new Map(elkNodes.map((n) => [n.id, n]));
518
+ const ungroupedElkNodes: ElkNode[] = [];
519
+ for (const n of elkNodes) {
520
+ if (!groupedLeafIds.has(n.id)) ungroupedElkNodes.push(n);
521
+ }
522
+ const elkParents: ElkNode[] = [];
523
+ for (const g of groupDefs) {
524
+ const children = g.memberIds
525
+ .map((mid) => elkById.get(mid))
526
+ .filter((n): n is ElkNode => !!n);
527
+ // Skip groups with <2 real members — a single-child frame adds noise;
528
+ // the caller drops the shell and leaves the node top-level.
529
+ if (children.length < 2) {
530
+ for (const c of children) ungroupedElkNodes.push(c);
531
+ memberToGroup.delete(g.memberIds[0]);
532
+ groupedLeafIds.delete(g.memberIds[0]);
533
+ continue;
534
+ }
535
+ elkParents.push({
536
+ id: g.id,
537
+ children,
538
+ layoutOptions: {
539
+ 'elk.algorithm': 'layered',
540
+ 'elk.direction': direction,
541
+ 'elk.padding': '[top=48,left=24,bottom=24,right=24]',
542
+ 'elk.spacing.nodeNode': '40',
543
+ },
544
+ });
545
+ }
546
+
547
+ // Create ELK graph
548
+ const rootOptions = getElkOptions(options);
549
+ if (elkParents.length > 0) {
550
+ rootOptions['elk.hierarchyHandling'] = 'INCLUDE_CHILDREN';
551
+ }
552
+ const elkGraph: ElkNode = {
553
+ id: 'root',
554
+ layoutOptions: rootOptions,
555
+ children: [...ungroupedElkNodes, ...elkParents],
556
+ edges: elkEdges,
557
+ };
558
+
559
+ // Run ELK layout
560
+ const layoutedGraph = await getElkInstance().layout(elkGraph);
561
+
562
+ // Build maps of ELK-computed positions. Nested children report
563
+ // parent-relative coords — flatten to absolute for edges, and keep the
564
+ // relative form for React Flow children (whose position is parent-relative).
565
+ // Absolute offset of every ELK node (parents included), accumulated down
566
+ // the ancestor chain — edge sections/labels are relative to their
567
+ // `container`, so each edge needs its container's absolute offset.
568
+ const elkAbsOffsets = new Map<string, { x: number; y: number }>();
569
+ const elkPositions = new Map<string, { x: number; y: number }>();
570
+ const elkRelativePositions = new Map<string, { x: number; y: number }>();
571
+ const groupBounds = new Map<string, { x: number; y: number; width: number; height: number }>();
572
+ const walkElk = (n: ElkNode, ox: number, oy: number) => {
573
+ const ax = ox + (n.x ?? 0);
574
+ const ay = oy + (n.y ?? 0);
575
+ elkAbsOffsets.set(n.id, { x: ax, y: ay });
576
+ for (const c of n.children ?? []) walkElk(c, ax, ay);
577
+ };
578
+ walkElk(layoutedGraph, 0, 0);
579
+ if (layoutedGraph.children) {
580
+ for (const child of layoutedGraph.children) {
581
+ if (child.children && child.children.length > 0 && groupDefs.some((g) => g.id === child.id)) {
582
+ const gx = child.x ?? 0;
583
+ const gy = child.y ?? 0;
584
+ groupBounds.set(child.id, {
585
+ x: gx,
586
+ y: gy,
587
+ width: child.width ?? 0,
588
+ height: child.height ?? 0,
589
+ });
590
+ for (const grand of child.children) {
591
+ const rx = grand.x ?? 0;
592
+ const ry = grand.y ?? 0;
593
+ elkRelativePositions.set(grand.id, { x: rx, y: ry });
594
+ elkPositions.set(grand.id, { x: gx + rx, y: gy + ry });
595
+ }
596
+ } else {
597
+ elkPositions.set(child.id, { x: child.x ?? 0, y: child.y ?? 0 });
598
+ elkRelativePositions.set(child.id, { x: child.x ?? 0, y: child.y ?? 0 });
599
+ }
600
+ }
601
+ }
602
+
603
+ // Extract results
604
+ const edgePaths = new Map<string, string>();
605
+ const edgeLabelPositions = new Map<string, { x: number; y: number }>();
606
+ const edgePathPoints = new Map<string, Point[]>();
607
+
608
+ // Process edges
609
+ if (layoutedGraph.edges) {
610
+ for (const edge of layoutedGraph.edges as ElkEdgeWithSections[]) {
611
+ if (edge.sections && edge.sections.length > 0) {
612
+ // Get source and target nodes for this edge
613
+ const sourceId = edges.find(e => e.id === edge.id)?.source;
614
+ const targetId = edges.find(e => e.id === edge.id)?.target;
615
+
616
+ // Calculate offset needed to translate from ELK positions to original positions
617
+ // We need to figure out which node each point is closest to and offset accordingly
618
+ const sourceOriginal = sourceId ? originalPositions.get(sourceId) : null;
619
+ const sourceElk = sourceId ? elkPositions.get(sourceId) : null;
620
+ const targetOriginal = targetId ? originalPositions.get(targetId) : null;
621
+ const targetElk = targetId ? elkPositions.get(targetId) : null;
622
+
623
+ // Collect all points from sections. Sections (and labels) are
624
+ // relative to the edge's `container` — intra-group edges live in
625
+ // the parent's frame, so translate to root-absolute flow coords.
626
+ const containerOffset = elkAbsOffsets.get(edge.container ?? 'root') ?? { x: 0, y: 0 };
627
+ const allPoints: Point[] = [];
628
+
629
+ for (const section of edge.sections) {
630
+ allPoints.push(section.startPoint);
631
+ if (section.bendPoints) {
632
+ allPoints.push(...section.bendPoints);
633
+ }
634
+ allPoints.push(section.endPoint);
635
+ }
636
+ if (containerOffset.x !== 0 || containerOffset.y !== 0) {
637
+ for (const p of allPoints) {
638
+ p.x += containerOffset.x;
639
+ p.y += containerOffset.y;
640
+ }
641
+ }
642
+
643
+ // If preserving positions, we need to offset the edge points
644
+ // The edge path is relative to ELK's layout, so we translate it
645
+ if (preserveNodePositions && sourceOriginal && sourceElk && targetOriginal && targetElk) {
646
+ // Calculate the offset from ELK space to original space
647
+ const sourceOffset = {
648
+ x: sourceOriginal.x - sourceElk.x,
649
+ y: sourceOriginal.y - sourceElk.y,
650
+ };
651
+ const targetOffset = {
652
+ x: targetOriginal.x - targetElk.x,
653
+ y: targetOriginal.y - targetElk.y,
654
+ };
655
+
656
+ // For orthogonal routing, use index-based offset assignment
657
+ // First point uses source offset, last point uses target offset
658
+ // Middle points use offset based on their position in the path
659
+ const lastIdx = allPoints.length - 1;
660
+ for (let i = 0; i < allPoints.length; i++) {
661
+ // Use path position (index) for interpolation instead of X position
662
+ // This preserves orthogonal segment shapes
663
+ const t = lastIdx > 0 ? i / lastIdx : 0;
664
+ allPoints[i] = {
665
+ x: allPoints[i].x + sourceOffset.x + (targetOffset.x - sourceOffset.x) * t,
666
+ y: allPoints[i].y + sourceOffset.y + (targetOffset.y - sourceOffset.y) * t,
667
+ };
668
+ }
669
+ }
670
+
671
+ // Use ELK's native label position (it accounts for node avoidance)
672
+ // and apply the same coordinate offset.
673
+ if (edge.labels && edge.labels.length > 0) {
674
+ const elkLabel = edge.labels[0];
675
+ // ELK reports the label's top-left; convert to center so screen-space
676
+ // overlays can anchor with translate(-50%, -50%) at any zoom.
677
+ let lx = (elkLabel.x ?? 0) + (elkLabel.width ?? 0) / 2 + containerOffset.x;
678
+ let ly = (elkLabel.y ?? 0) + (elkLabel.height ?? 0) / 2 + containerOffset.y;
679
+ if (preserveNodePositions && sourceOriginal && sourceElk && targetOriginal && targetElk) {
680
+ const sourceOffset = {
681
+ x: sourceOriginal.x - sourceElk.x,
682
+ y: sourceOriginal.y - sourceElk.y,
683
+ };
684
+ const targetOffset = {
685
+ x: targetOriginal.x - targetElk.x,
686
+ y: targetOriginal.y - targetElk.y,
687
+ };
688
+ // Interpolate offset based on label position along the edge
689
+ const startX = allPoints[0].x;
690
+ const endX = allPoints[allPoints.length - 1].x;
691
+ const rangeX = Math.abs(endX - startX) || 1;
692
+ const t = Math.min(1, Math.max(0, Math.abs(lx - startX) / rangeX));
693
+ lx += sourceOffset.x + (targetOffset.x - sourceOffset.x) * t;
694
+ ly += sourceOffset.y + (targetOffset.y - sourceOffset.y) * t;
695
+ }
696
+ // Snap onto the polyline stroke. ELK's label box can sit slightly
697
+ // off the route (side selection / reserved label space); we keep
698
+ // its along-edge placement but center on the actual path.
699
+ const onPath = closestPointOnPath(allPoints, { x: lx, y: ly });
700
+ edgeLabelPositions.set(edge.id, onPath);
701
+ }
702
+
703
+ // For orthogonal routing with preserved positions, the offset can distort
704
+ // ELK's bend points, so we rebuild the path. Otherwise, use ELK's bends
705
+ // as-is since they already account for label placement and node avoidance.
706
+ if (options.routingStyle === 'orthogonal' && preserveNodePositions) {
707
+ const start = allPoints[0];
708
+ const end = allPoints[allPoints.length - 1];
709
+ const dx = Math.abs(end.x - start.x);
710
+ const dy = Math.abs(end.y - start.y);
711
+
712
+ // Get the routing direction for this edge
713
+ const isHorizontalFirst = edgeRoutingDirection.get(edge.id) ?? true;
714
+
715
+ // Clear intermediate points and regenerate orthogonal path
716
+ allPoints.length = 0;
717
+ allPoints.push(start);
718
+
719
+ // If not aligned horizontally or vertically, insert bend points
720
+ if (dx > 1 && dy > 1) {
721
+ if (isHorizontalFirst) {
722
+ // Horizontal-first: go right/left, then up/down (for left/right port connections)
723
+ const midX = (start.x + end.x) / 2;
724
+ allPoints.push({ x: midX, y: start.y });
725
+ allPoints.push({ x: midX, y: end.y });
726
+ } else {
727
+ // Vertical-first: go up/down, then right/left (for top/bottom port connections)
728
+ const midY = (start.y + end.y) / 2;
729
+ allPoints.push({ x: start.x, y: midY });
730
+ allPoints.push({ x: end.x, y: midY });
731
+ }
732
+ }
733
+
734
+ allPoints.push(end);
735
+ }
736
+
737
+ // Convert to path
738
+ const path =
739
+ options.routingStyle === 'orthogonal'
740
+ ? pointsToSmoothPath(allPoints, 8)
741
+ : pointsToPath(allPoints);
742
+
743
+ edgePaths.set(edge.id, path);
744
+ edgePathPoints.set(edge.id, [...allPoints]);
745
+ }
746
+ }
747
+ }
748
+
749
+ // Process nodes (update positions if not preserving). Grouped children use
750
+ // parent-relative coords (React Flow child semantics); everything else uses
751
+ // absolute coords.
752
+ const resultNodes = preserveNodePositions
753
+ ? nodes
754
+ : nodes.map((node) => {
755
+ const rel = elkRelativePositions.get(node.id);
756
+ if (rel) {
757
+ return {
758
+ ...node,
759
+ position: { x: rel.x, y: rel.y },
760
+ };
761
+ }
762
+ return node;
763
+ });
764
+
765
+ return {
766
+ nodes: resultNodes,
767
+ edgePaths,
768
+ edgeLabelPositions,
769
+ edgePathPoints,
770
+ groupBounds,
771
+ };
772
+ }
773
+
774
+ /**
775
+ * Hook-friendly version that returns a layout function
776
+ */
777
+ export function createElkLayouter(options: ElkLayoutOptions = {}) {
778
+ return (nodes: Node[], edges: Edge[]) => computeElkLayout(nodes, edges, options);
779
+ }