@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,850 @@
1
+ /**
2
+ * Subsystem component-graph data model + converters.
3
+ *
4
+ * A subsystem snapshot (see the "Subsystem artifact: facets" topic) is captured
5
+ * as component nodes (construct-tagged source units), file refs, integration edges,
6
+ * and entry points. This module defines the minimal document shape for the
7
+ * *graph* facet and converts it to React Flow nodes/edges — packages render as
8
+ * subgraphs, only cross-package edges leave the box, and shared seams
9
+ * (registries/barrels/facades) are edge targets, never member nodes.
10
+ *
11
+ * Self-contained in this package (not yet promoted to `@principal-ai/core`), so
12
+ * we can iterate on the UI + story without publishing a dependency.
13
+ */
14
+
15
+ import {
16
+ MarkerType,
17
+ type Edge,
18
+ type Node,
19
+ } from '@xyflow/react';
20
+ import { computeElkLayout, calculatePathLength } from '../utils/elkLayout';
21
+ import type { GraphifyComponentDetail } from '../graphify';
22
+ import type { SubsystemDeclarationRef } from './declarationRef';
23
+
24
+ export type SubsystemComponentConstruct =
25
+ | 'class'
26
+ | 'function'
27
+ | 'method'
28
+ | 'interface'
29
+ | 'type_alias'
30
+ | 'enum'
31
+ | 'module'
32
+ | 'store'
33
+ | 'external';
34
+
35
+ /**
36
+ * Semantic role — where the node sits in the topology, orthogonal to
37
+ * `construct` (what it is). Roles have *inherited* anatomy: an entry renders
38
+ * as its real code shape (function dispatcher, type contract); a service has
39
+ * no source at all (`construct: 'external'` + purl identity). Contrast with
40
+ * `construct: 'store'`, which introduces its own anatomy (the state block) —
41
+ * that is why store is a construct and not a role. Role nodes must stay anchored to real code: an
42
+ * `entry` is a boundary element (route dispatcher, message contract) carrying
43
+ * the wire address as identity; a `service` is an external system the process
44
+ * calls out to (identity via purl, no `process` — it belongs to no region).
45
+ */
46
+ export type SubsystemComponentRole = 'entry' | 'service';
47
+
48
+ /**
49
+ * Framework that owns a stereotype vocabulary (open string).
50
+ * Examples: `react`, `vue`, `nestjs`, `django`, `spring`.
51
+ * Empty when the node is language-only / framework-agnostic.
52
+ */
53
+ export type SubsystemFramework = string;
54
+
55
+ /**
56
+ * Framework-level pattern stamped on a language construct (open string).
57
+ * Examples: `component`, `hook`, `middleware`, `controller`, `guard`.
58
+ * Empty when no framework pattern applies. Pair with `framework` when set —
59
+ * a React component stays `construct: 'function'` with
60
+ * `framework: 'react'` + `stereotype: 'component'`.
61
+ */
62
+ export type SubsystemStereotype = string;
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Declaration tokens — structured source representation
66
+ // ---------------------------------------------------------------------------
67
+
68
+ export type SubsystemDeclTokenKind =
69
+ | 'keyword'
70
+ | 'name'
71
+ | 'member'
72
+ | 'type'
73
+ | 'punctuation'
74
+ | 'string'
75
+ | 'newline';
76
+
77
+ export interface SubsystemDeclToken {
78
+ text: string;
79
+ kind: SubsystemDeclTokenKind;
80
+ /** Shiki/Pierre foreground when tokenized client-side; omitted on legacy wire tokens. */
81
+ color?: string;
82
+ }
83
+
84
+ export type SubsystemEdgeMechanism =
85
+ | 'imports'
86
+ | 'imports_from'
87
+ | 're_exports'
88
+ | 'defines'
89
+ | 'calls'
90
+ | 'extends'
91
+ | 'inherits'
92
+ | 'implements'
93
+ | 'mixes_in'
94
+ | 'uses'
95
+ | 'method'
96
+ | 'references'
97
+ | 'contains'
98
+ | 'feeds'
99
+ | 'produces'
100
+ | 'writes'
101
+ | 'reads'
102
+ | 'watches'
103
+ | 'registers-into';
104
+
105
+ /** A component node — the named unit, construct-tagged; `file` is its location. */
106
+ export interface SubsystemComponent {
107
+ id: string;
108
+ name: string;
109
+ /**
110
+ * The node's construct — what it IS as a declaration (class, function,
111
+ * method, interface, type alias, enum, store, external), driving node
112
+ * anatomy, color, badge, and the verification strategy. Every construct
113
+ * anchors to a definition; runtime occurrences (variables, activations,
114
+ * instances) are NOT constructs — they belong to a future execution-mode
115
+ * graph whose occurrence nodes reference these definitions. Ontology:
116
+ * construct = what it is, framework + stereotype = which framework pattern
117
+ * it plays, role = where it sits, process = where it runs. Prefer
118
+ * `framework` + `stereotype` over inventing framework-specific constructs
119
+ * (a React component is still `construct: 'function'`).
120
+ */
121
+ construct: SubsystemComponentConstruct;
122
+ /** Source location the component lives in (repo-root-relative path). */
123
+ file: string;
124
+ /** PURL identifying the repo or package this component lives in (for subgraph grouping). */
125
+ purl: string;
126
+ /** One-line purpose shown on the node. */
127
+ purpose?: string;
128
+ /**
129
+ * Semantic role — where the node sits in the topology (boundary element,
130
+ * external system), orthogonal to `construct` (what it is). Drives the
131
+ * glyph and the edge-pairing rules: boundary-crossing edges must terminate
132
+ * at an entry or a service. Retained state is NOT a role — it is
133
+ * `construct: 'store'` (state-block anatomy, `writes`/`reads`/`watches`
134
+ * inbound, `produces` outbound).
135
+ */
136
+ role?: SubsystemComponentRole;
137
+ /**
138
+ * Framework that owns the stereotype vocabulary (e.g. `react`, `nestjs`).
139
+ * Orthogonal to `construct` — leave empty for language-only units.
140
+ */
141
+ framework?: SubsystemFramework;
142
+ /**
143
+ * Framework pattern this declaration plays (e.g. `component`, `hook`).
144
+ * When set, the node badge prefers this label over the construct name so
145
+ * a React UI unit reads as "component" rather than "function".
146
+ */
147
+ stereotype?: SubsystemStereotype;
148
+ /**
149
+ * Runtime process membership — which deployment unit this node is a
150
+ * member of (e.g. `principal-studio/host`, `principal-studio/renderer`). Nodes
151
+ * sharing a `process` are drawn inside one boundary region (grouping is
152
+ * `process ?? purl`); nodes without one sit outside every boundary
153
+ * (external actors, services, libraries).
154
+ */
155
+ process?: string;
156
+ /** A symbol this component exposes / is (the node's identity). */
157
+ symbol?: string;
158
+ /**
159
+ * Semantic layer/phase for the layout (e.g. `1` = input, `2` = processing,
160
+ * `3` = output). When set, ELK places the component in this layer so the
161
+ * graph reads as a left-to-right pipeline and *conveys the idea* rather than
162
+ * letting ELK guess the order.
163
+ */
164
+ layer?: number;
165
+ /**
166
+ * How this session *occupied* the component — `edited` (modified it) vs
167
+ * `analyzed` (read deeply / built an understanding) vs `referenced`
168
+ * (touched in passing). Drives clustering: edits strengthen a subsystem,
169
+ * but a read-only investigation can still form one around an analyzed seam.
170
+ */
171
+ capture?: 'edited' | 'analyzed' | 'referenced';
172
+ /**
173
+ * Graphify drill-down detail for this component, when the facet is anchored
174
+ * to a graphify node. Discriminated by kind (class/function/type/module/store/
175
+ * external); rendered in the detail panel on click.
176
+ */
177
+ detail?: GraphifyComponentDetail;
178
+ /**
179
+ * Where the drill-down `detail` came from. `verified` = extracted from
180
+ * source by tooling (graphify AST, signature extraction) — may be trusted
181
+ * as matching the code. `authored` = written by the authoring agent/human
182
+ * to highlight specific inputs/outputs — informative, not checked against
183
+ * source. Detail without provenance is treated as `authored`; only tooling
184
+ * may claim `verified`.
185
+ */
186
+ detailProvenance?: 'verified' | 'authored';
187
+ /**
188
+ * Pre-tokenized declaration for the detail panel. When present, the
189
+ * renderer skips client-side tokenization. Tokens are language-agnostic;
190
+ * a different language just needs a different tokenizer and text joiner.
191
+ */
192
+ tokens?: SubsystemDeclToken[];
193
+ /**
194
+ * Anchored declaration location: graphify start line + hash of that line's
195
+ * content at capture time. Populated by verify when an exact anchor resolves.
196
+ */
197
+ declarationRef?: SubsystemDeclarationRef;
198
+ }
199
+
200
+ /** A cross-component edge in the subsystem graph. */
201
+ export interface SubsystemComponentEdge {
202
+ id: string;
203
+ from: string; // component id
204
+ to: string; // component id or external target label
205
+ mechanism: SubsystemEdgeMechanism;
206
+ /** Concrete file/symbol refs backing the edge (the seam). */
207
+ refs?: string[];
208
+ }
209
+
210
+ /**
211
+ * A single site on an existing edge — the exact `file:line` where that edge's
212
+ * seam manifests for a given flow. The edge stays the abstract contract
213
+ * (`from`, `to`, `mechanism`); a throughline step picks the concrete
214
+ * manifestation. One edge can appear in many steps.
215
+ */
216
+ export interface SubsystemThroughlineStep {
217
+ /** Id of the existing edge this hop traverses. */
218
+ edgeId: string;
219
+ /** Repo-root-relative path of the file where the edge fires. */
220
+ file: string;
221
+ /** 1-based line of the site within `file`. */
222
+ line: number;
223
+ /**
224
+ * Frame name for this hop — the function/method/symbol on the stack at
225
+ * this site. Optional so existing throughlines keep working; when set the
226
+ * flows list shows it instead of mechanism + filename.
227
+ */
228
+ symbol?: string;
229
+ /**
230
+ * Free-text note anchored to this hop's site line. Optional — informative
231
+ * only, never verified against source; the Pierre throughline code view
232
+ * surfaces it in the annotation column next to the highlighted line.
233
+ */
234
+ annotation?: string;
235
+ }
236
+
237
+ /**
238
+ * An ordered execution story over a graph's edges — each step references an
239
+ * existing edge and the exact site where that relationship fires for a flow;
240
+ * ordering is the array. One throughline per flow (save flow, load flow, …).
241
+ */
242
+ export interface SubsystemThroughline {
243
+ id: string;
244
+ title: string;
245
+ steps: SubsystemThroughlineStep[];
246
+ }
247
+
248
+ /**
249
+ * Derive a consistent display `name` from a code `symbol` + construct.
250
+ *
251
+ * `symbol` is the source of truth (fully-qualified code identity). The name
252
+ * is the symbol itself:
253
+ * - class/type/module/function/script/... symbol → symbol (e.g. `SessionReader`)
254
+ * - method `Owner.method` → last segment (e.g. `SessionReader.normalize` → `normalize`)
255
+ * - module, no symbol → basename of `file` (e.g. `transcript.ts` → `transcript`) —
256
+ * a common-sense convention for whole-file modules, not a real TS name
257
+ * - otherwise no symbol → fall back to an existing name
258
+ */
259
+ export function deriveNameFromSymbol(
260
+ symbol: string | undefined,
261
+ construct: SubsystemComponentConstruct,
262
+ existingName?: string,
263
+ file?: string,
264
+ stereotype?: string,
265
+ ): string {
266
+ let name: string | undefined;
267
+ if (symbol && symbol.trim()) {
268
+ name = symbol;
269
+ } else if (construct === 'module' && file) {
270
+ const base = file.split('/').pop() ?? '';
271
+ const clean = base.replace(/\.[^.]+$/, ''); // strip extension
272
+ if (clean) name = clean;
273
+ }
274
+ if (!name) name = existingName ?? 'untitled';
275
+
276
+ // Decorations = what the drill-down shows. Framework stereotypes can override
277
+ // the language decoration (a React component wears `<>` instead of `()`).
278
+ // Executable constructs wear `()`; brace-bodied constructs (interface,
279
+ // type_alias, enum) wear ` {}`. Classes render bare — the construct badge
280
+ // already says "class".
281
+ // Everything else (class, store, module, external) renders bare.
282
+ if (stereotype === 'component' && !name.startsWith('<')) {
283
+ return `<${name}>`;
284
+ }
285
+ if ((construct === 'function' || construct === 'method') && !name.endsWith('()')) {
286
+ name = `${name}()`;
287
+ }
288
+ if (
289
+ (construct === 'interface' ||
290
+ construct === 'type_alias' ||
291
+ construct === 'enum') &&
292
+ !name.endsWith('{}')
293
+ ) {
294
+ name = `${name} {}`;
295
+ }
296
+ return name;
297
+ }
298
+
299
+ /**
300
+ * Human-readable purl identity — drops the `pkg:<type>/` scheme wrapper and
301
+ * trailing version, keeping the package / owner-repo identity:
302
+ *
303
+ * pkg:npm/@principal-ai/core → `@principal-ai/core`
304
+ * pkg:github/principal-ai/my-repo → `principal-ai/my-repo`
305
+ * pkg:npm/left-pad@1.3.0 → `left-pad`
306
+ * pkg:generic/local--Users-me-my-app → `Users-me-my-app (local)`
307
+ *
308
+ * Local purls encode the absolute path with dashes for slashes, which is not
309
+ * losslessly decodable — shown as-is with a `(local)` marker.
310
+ */
311
+ export function formatPurl(purl: string): string {
312
+ const match = /^pkg:[^/#?]+\/(.+)$/.exec(purl);
313
+ if (!match) return purl;
314
+ let identity = match[1].split(/[?#]/)[0];
315
+ const at = identity.indexOf('@');
316
+ if (at > 0) identity = identity.slice(0, at); // trailing @version
317
+ if (identity.startsWith('local--')) {
318
+ return `${identity.slice('local--'.length)} (local)`;
319
+ }
320
+ return identity;
321
+ }
322
+
323
+ export interface SubsystemModelDocument {
324
+ components: SubsystemComponent[];
325
+ edges: SubsystemComponentEdge[];
326
+ /** Ordered execution stories over the graph's edges (one per flow). */
327
+ throughlines?: SubsystemThroughline[];
328
+ }
329
+
330
+ // ---------------------------------------------------------------------------
331
+ // React Flow conversion
332
+ // ---------------------------------------------------------------------------
333
+
334
+ export type SubsystemGraphNodeType = 'subsystem-component' | 'subsystem-group';
335
+
336
+ /**
337
+ * One process boundary region — all components sharing a `process` value.
338
+ * Nodes without a `process` sit outside every boundary (no region).
339
+ */
340
+ export interface SubsystemProcessRegion {
341
+ /** The `process` value (e.g. `principal-studio/host`). */
342
+ key: string;
343
+ /** Display label for the boundary frame. */
344
+ label: string;
345
+ /** Component ids that are members of this region. */
346
+ memberIds: string[];
347
+ }
348
+
349
+ /** React Flow id for a process boundary group node. */
350
+ export function processGroupNodeId(processKey: string): string {
351
+ return `process:${processKey}`;
352
+ }
353
+
354
+ /**
355
+ * Derive boundary regions from a document — one per distinct non-empty
356
+ * `process` value, in first-appearance order.
357
+ */
358
+ export function getSubsystemRegions(
359
+ doc: Pick<SubsystemModelDocument, 'components'>,
360
+ ): SubsystemProcessRegion[] {
361
+ const byProcess = new Map<string, string[]>();
362
+ for (const c of doc.components) {
363
+ const p = c.process?.trim();
364
+ if (!p) continue;
365
+ const list = byProcess.get(p) ?? [];
366
+ list.push(c.id);
367
+ byProcess.set(p, list);
368
+ }
369
+ return [...byProcess.entries()].map(([key, memberIds]) => ({
370
+ key,
371
+ label: key,
372
+ memberIds,
373
+ }));
374
+ }
375
+
376
+ export interface SubsystemGraphNodeData extends Record<string, unknown> {
377
+ component: SubsystemComponent;
378
+ /** Set while a file is open in the drawer: true if this node's component
379
+ * lives in that file (spotlighted), false otherwise (dimmed). Absent when
380
+ * no file is open — render neutrally. */
381
+ fileMatch?: boolean;
382
+ /** True while this node is on an opened-but-unselected flow. */
383
+ dimmed?: boolean;
384
+ }
385
+
386
+ export interface SubsystemGroupNodeData extends Record<string, unknown> {
387
+ region: SubsystemProcessRegion;
388
+ /** True while the region's members are dimmed by flow focus. */
389
+ dimmed?: boolean;
390
+ }
391
+
392
+ export type SubsystemGraphNode =
393
+ | Node<SubsystemGraphNodeData, 'subsystem-component'>
394
+ | Node<SubsystemGroupNodeData, 'subsystem-group'>;
395
+
396
+ export interface SubsystemGraphEdgeData extends Record<string, unknown> {
397
+ mechanism: SubsystemEdgeMechanism;
398
+ refs?: string[];
399
+ /** True while another edge is selected — render this edge (and its label)
400
+ * dimmed to focus the selected relationship. */
401
+ dimmed?: boolean;
402
+ /** ELK-computed label midpoint (from the actual edge path, not node centers). */
403
+ labelX?: number;
404
+ labelY?: number;
405
+ /** Polyline length in flow-space units (for capping screen-space label size). */
406
+ pathLength?: number;
407
+ /** ELK-computed SVG edge path (overrides React Flow's default path). */
408
+ elkPath?: string;
409
+ }
410
+
411
+ export type SubsystemGraphEdge = Edge<SubsystemGraphEdgeData>;
412
+
413
+ export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
414
+ imports: '#0893d2', // blue
415
+ imports_from: '#5aa9e6', // light blue
416
+ re_exports: '#3aa5c9', // cyan-blue
417
+ defines: '#2e86ab', // steel blue
418
+ calls: '#4ec9b0', // teal
419
+ extends: '#b48ead', // purple
420
+ inherits: '#9b6fd0', // purple
421
+ implements: '#c586c0', // magenta
422
+ mixes_in: '#d474a8', // pink-magenta
423
+ uses: '#e3b341', // gold
424
+ method: '#c586c0', // magenta
425
+ references: '#e07a5f', // terracotta
426
+ contains: '#6c5ce7', // indigo
427
+ feeds: '#22c55e', // green — data-flow into a processor
428
+ produces: '#e07a5f', // terracotta — emits an output type
429
+ writes: '#2f9e44', // deep green — mutates retained state
430
+ reads: '#0ea5e9', // sky — pulls from retained state
431
+ watches: '#9ca3af', // gray — observes, owns nothing
432
+ 'registers-into': '#ff6b35', // orange
433
+ };
434
+
435
+ export const MECHANISM_STYLE: Record<SubsystemEdgeMechanism, 'solid' | 'dashed' | 'dotted'> = {
436
+ imports: 'solid',
437
+ imports_from: 'solid',
438
+ re_exports: 'solid',
439
+ defines: 'solid',
440
+ calls: 'solid',
441
+ extends: 'dashed',
442
+ inherits: 'dashed',
443
+ implements: 'dashed',
444
+ mixes_in: 'dashed',
445
+ uses: 'solid',
446
+ method: 'solid',
447
+ references: 'dotted',
448
+ contains: 'solid',
449
+ feeds: 'solid',
450
+ produces: 'solid',
451
+ writes: 'solid',
452
+ reads: 'solid',
453
+ watches: 'dashed',
454
+ 'registers-into': 'dashed',
455
+ };
456
+
457
+ /** Mechanism → [description, verifiable-with-graphify]. Drives the "not
458
+ * directly verifiable" styling of edge labels. */
459
+ export const MECHANISM_DESCRIPTIONS: [SubsystemEdgeMechanism, string, boolean][] = [
460
+ ['imports', 'import statement (code-level dependency)', true],
461
+ ['imports_from', 'imported by (reverse dependency)', true],
462
+ ['re_exports', 're-exports symbols from', true],
463
+ ['defines', 'defines / declares symbol', true],
464
+ ['calls', 'function/method call (call graph edge)', true],
465
+ ['extends', 'class inheritance', true],
466
+ ['inherits', 'class inheritance', true],
467
+ ['implements', 'implements interface / protocol', true],
468
+ ['mixes_in', 'applies mixin', true],
469
+ ['uses', 'general dependency (import, call, or reference)', false],
470
+ ['method', 'structural: has method / member', true],
471
+ ['references', 'type / symbol reference (not a call)', true],
472
+ ['contains', 'structural: contains / encapsulates', true],
473
+ ['feeds', 'data flow: output feeds into input', false],
474
+ ['produces', 'data flow: produces / outputs', false],
475
+ ['writes', 'state access: mutates retained state', true],
476
+ ['reads', 'state access: reads retained state', true],
477
+ ['watches', 'observes retained state without owning it', false],
478
+ ['registers-into', 'registration pattern', false],
479
+ ];
480
+
481
+ /** Package color palette (derived deterministically from the package name). */
482
+ export function packageColor(name: string): string {
483
+ const palette = [
484
+ '#0893d2',
485
+ '#4ec9b0',
486
+ '#ff6b35',
487
+ '#b48ead',
488
+ '#e3b341',
489
+ '#5aa9e6',
490
+ ];
491
+ let h = 0;
492
+ for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
493
+ return palette[h % palette.length];
494
+ }
495
+
496
+ /** Color per semantic role — applied ONLY to the role badge (top-right tab on
497
+ * nodes that carry a role). The node border stays construct-colored. Note:
498
+ * `service` currently equals the old class hex; harmless while role badges
499
+ * are the only surface using it, but pick a distinct value if roles ever
500
+ * take over node borders. */
501
+ export const ROLE_COLOR: Record<SubsystemComponentRole, string> = {
502
+ entry: '#ff6b35', // orange — boundary element
503
+ service: '#0893d2', // blue — external system
504
+ };
505
+
506
+ export const ROLE_LABEL: Record<SubsystemComponentRole, string> = {
507
+ entry: 'entry',
508
+ service: 'service',
509
+ };
510
+
511
+ /**
512
+ * Primary badge text for a node: prefer framework stereotype over the
513
+ * language construct so a React UI unit reads as "component" / "hook"
514
+ * rather than "function". When both framework and stereotype are set,
515
+ * show `framework · stereotype` (e.g. `react · component`).
516
+ */
517
+ export function constructBadgeLabel(component: {
518
+ construct: SubsystemComponentConstruct;
519
+ framework?: string;
520
+ stereotype?: string;
521
+ }): string {
522
+ const constructLabel =
523
+ component.construct === 'type_alias' ? 'type alias' : component.construct;
524
+ if (component.stereotype && component.framework) {
525
+ return `${component.framework} · ${component.stereotype}`;
526
+ }
527
+ if (component.stereotype) return component.stereotype;
528
+ return constructLabel ?? '';
529
+ }
530
+
531
+ /** Default CSS floor for component nodes (padding aside). */
532
+ export const NODE_CSS_MIN_WIDTH = 150;
533
+ /** Inset of each top badge from the node edge (`left` / `right` style). */
534
+ export const BADGE_EDGE_INSET = 5;
535
+ /** Minimum gap between left construct badge and right role badge. */
536
+ const BADGE_PAIR_GAP = 8;
537
+ /** Badge box chrome: padding 5+5 + border 1+1. */
538
+ const BADGE_BOX_CHROME = 12;
539
+ /** Approx monospace uppercase width incl. letter-spacing (~0.5px). */
540
+ const BADGE_CHAR_WIDTH = 8;
541
+
542
+ /** Estimated rendered width of a top tab badge label. */
543
+ export function estimateBadgeLabelWidth(label: string): number {
544
+ return (label?.length ?? 0) * BADGE_CHAR_WIDTH + BADGE_BOX_CHROME;
545
+ }
546
+
547
+ /**
548
+ * Minimum node width so top badges stay on one line and (when both are
549
+ * present) don't overlap — badges are absolutely positioned, so they don't
550
+ * contribute to layout unless we widen the node explicitly.
551
+ */
552
+ export function nodeMinWidthForBadges(component: {
553
+ construct: SubsystemComponentConstruct;
554
+ framework?: string;
555
+ stereotype?: string;
556
+ role?: SubsystemComponentRole;
557
+ }): number {
558
+ const left = estimateBadgeLabelWidth(constructBadgeLabel(component));
559
+ if (component.role == null) {
560
+ return Math.max(NODE_CSS_MIN_WIDTH, BADGE_EDGE_INSET + left + BADGE_EDGE_INSET);
561
+ }
562
+ const right = estimateBadgeLabelWidth(ROLE_LABEL[component.role]);
563
+ return Math.max(
564
+ NODE_CSS_MIN_WIDTH,
565
+ BADGE_EDGE_INSET + left + BADGE_PAIR_GAP + right + BADGE_EDGE_INSET,
566
+ );
567
+ }
568
+
569
+ /**
570
+ * Convert a subsystem graph document into React Flow nodes. Components that
571
+ * carry a `process` get a `parentId` pointing at their boundary group node
572
+ * (`process:<process>`); nodes without one stay top-level (outside every
573
+ * boundary). The initial grid groups by `process ?? purl` so the pre-ELK
574
+ * positions are already clustered; ELK then refines with compound layout.
575
+ */
576
+ export function convertSubsystemToNodes(
577
+ doc: SubsystemModelDocument,
578
+ opts: { maxNodeWidth?: number } = {},
579
+ ): SubsystemGraphNode[] {
580
+ const { maxNodeWidth } = opts;
581
+ // Group components into boundary regions by process when authored, else by
582
+ // package. Process is runtime membership (drawn as one boundary region);
583
+ // purl is code identity — nodes without a process (external actors,
584
+ // services, libraries) fall back to purl and sit outside every boundary.
585
+ const byPkg = new Map<string, SubsystemComponent[]>();
586
+ for (const c of doc.components) {
587
+ const regionKey = c.process ?? c.purl;
588
+ const list = byPkg.get(regionKey) ?? [];
589
+ list.push(c);
590
+ byPkg.set(regionKey, list);
591
+ }
592
+
593
+ const nodes: SubsystemGraphNode[] = [];
594
+ const COLS = 2;
595
+ const COL_W = 240;
596
+ const ROW_H = 150;
597
+ const PAD = 30;
598
+ const GROUP_GAP = 60;
599
+
600
+ let cursorY = PAD;
601
+ for (const comps of byPkg.values()) {
602
+ const rows = Math.ceil(comps.length / COLS);
603
+ const heightPx = ROW_H * rows;
604
+
605
+ comps.forEach((c, i) => {
606
+ const col = i % COLS;
607
+ const row = Math.floor(i / COLS);
608
+ // Estimate rendered node width from the longest text line so ELK reserves
609
+ // the right space (names can wrap, so we cap at the configurable max).
610
+ const text = [c.symbol, c.name, c.file?.split('/').pop() ?? '']
611
+ .filter((t): t is string => !!t)
612
+ .sort((a, b) => b.length - a.length)[0];
613
+ const cap = maxNodeWidth ?? 300;
614
+ const textWidth = Math.min(cap, Math.max(60, (text?.length ?? 10) * 8));
615
+ // Account for CSS minWidth (incl. top badges) and padding/border so ELK's
616
+ // port positions match the actual rendered node boundaries.
617
+ const cssMinWidth = nodeMinWidthForBadges(c);
618
+ const cssPadding = 20; // horizontal padding (left + right)
619
+ const cssBorder = 4; // 2px border each side
620
+ const rawWidth = Math.max(cssMinWidth, textWidth + cssPadding + cssBorder);
621
+ const nodeWidth = Math.max(cssMinWidth, Math.min(cap, rawWidth));
622
+ const processKey = c.process?.trim();
623
+ nodes.push({
624
+ id: c.id,
625
+ type: 'subsystem-component',
626
+ ...(processKey ? { parentId: processGroupNodeId(processKey) } : {}),
627
+ position: { x: PAD + col * COL_W, y: cursorY + row * ROW_H },
628
+ width: nodeWidth,
629
+ height: 84,
630
+ data: { component: c },
631
+ });
632
+ });
633
+ cursorY += heightPx + PAD * 2 + GROUP_GAP;
634
+ }
635
+ return nodes;
636
+ }
637
+
638
+ /**
639
+ * Convert boundary regions into React Flow parent (group) nodes. One per
640
+ * distinct `process` value; member components point at these via `parentId`.
641
+ * Positions/sizes are placeholders — ELK compound layout overwrites them.
642
+ */
643
+ export function convertSubsystemToGroups(
644
+ doc: Pick<SubsystemModelDocument, 'components'>,
645
+ ): SubsystemGraphNode[] {
646
+ return getSubsystemRegions(doc).map((region) => ({
647
+ id: processGroupNodeId(region.key),
648
+ type: 'subsystem-group',
649
+ position: { x: 0, y: 0 },
650
+ width: 400,
651
+ height: 300,
652
+ data: { region },
653
+ }));
654
+ }
655
+
656
+ /**
657
+ * Convert a subsystem graph document into React Flow edges. Edges whose target
658
+ * is an external label (not a component id) point at a synthetic stub so the
659
+ * relationship is visible without a member node.
660
+ */
661
+ export function convertSubsystemToEdges(doc: SubsystemModelDocument): SubsystemGraphEdge[] {
662
+ const compIds = new Set(doc.components.map((c) => c.id));
663
+ const edges: SubsystemGraphEdge[] = [];
664
+
665
+ for (const e of doc.edges) {
666
+ const color = MECHANISM_COLOR[e.mechanism];
667
+ const style = MECHANISM_STYLE[e.mechanism];
668
+ // If `to` is a real component, connect directly; otherwise point at a stub node.
669
+ const isExternal = !compIds.has(e.to);
670
+ const targetId = isExternal ? `external:${e.to}` : e.to;
671
+
672
+ edges.push({
673
+ id: e.id,
674
+ source: e.from,
675
+ target: targetId,
676
+ data: { mechanism: e.mechanism, refs: e.refs },
677
+ type: 'subsystem-edge',
678
+ markerEnd: { type: MarkerType.ArrowClosed, color, width: 32, height: 32 },
679
+ style: { color, stroke: color, strokeDasharray: style === 'dashed' ? '6 4' : undefined },
680
+ // `label` feeds ELK's label-space reservation only; the visible label is
681
+ // rendered by the custom SubsystemEdge as an HTML overlay.
682
+ label: e.mechanism,
683
+ // Mechanism label rendered via the custom SubsystemEdge (an HTML overlay
684
+ // above the SVG edges, so it can't be hidden behind other edge lines).
685
+ });
686
+ }
687
+ return edges;
688
+ }
689
+
690
+ /** Stable key for layout-affecting graph fields (ignores declarationRef, etc.). */
691
+ export function subsystemGraphLayoutKey(
692
+ doc: Pick<SubsystemModelDocument, 'components' | 'edges'>,
693
+ ): string {
694
+ const components = doc.components
695
+ .map(({ id, purl, name, symbol, construct, file, purpose, process }) =>
696
+ [id, purl, name, symbol ?? '', construct, file, purpose ?? '', process ?? ''].join('\0'))
697
+ .sort()
698
+ .join('\n');
699
+ const edgeKey = doc.edges
700
+ .map(({ id, from, to, mechanism }) => [id, from, to, mechanism].join('\0'))
701
+ .sort()
702
+ .join('\n');
703
+ return `${components}|${edgeKey}`;
704
+ }
705
+
706
+ /**
707
+ * Build the full React Flow graph (nodes + edges) using **ELK auto-layout** to
708
+ * position every node (including external stub targets), so the graph is
709
+ * layered with minimized crossings.
710
+ */
711
+ export async function buildSubsystemGraph(
712
+ doc: SubsystemModelDocument,
713
+ opts: { maxNodeWidth?: number; showEdgeLabels?: boolean; measuredWidths?: Map<string, number>; measuredHeights?: Map<string, number> } = {},
714
+ ): Promise<{
715
+ nodes: SubsystemGraphNode[];
716
+ edges: SubsystemGraphEdge[];
717
+ regions: SubsystemProcessRegion[];
718
+ }> {
719
+ const { maxNodeWidth, showEdgeLabels, measuredWidths, measuredHeights } = opts;
720
+ const nodes = convertSubsystemToNodes(doc, { maxNodeWidth });
721
+ const edges = convertSubsystemToEdges(doc);
722
+ // Boundary regions: one per multi-member process. Singletons get no frame —
723
+ // strip the parentId convertSubsystemToNodes stamped so React Flow never
724
+ // points at a non-existent parent.
725
+ const regions = getSubsystemRegions(doc).filter((r) => r.memberIds.length >= 2);
726
+ const regionKeys = new Set(regions.map((r) => r.key));
727
+ for (const n of nodes) {
728
+ if (n.type !== 'subsystem-component') continue;
729
+ const proc = (n.data as SubsystemGraphNodeData).component?.process?.trim();
730
+ if (proc && !regionKeys.has(proc)) {
731
+ delete (n as { parentId?: string }).parentId;
732
+ }
733
+ }
734
+
735
+ // External edge targets that aren't real components → create stub nodes so
736
+ // cross-package edges have something to land on.
737
+ const realIds = new Set(doc.components.map((c) => c.id));
738
+ const externalIds: string[] = [];
739
+ for (const e of doc.edges) {
740
+ if (!realIds.has(e.to)) {
741
+ const extId = `external:${e.to}`;
742
+ if (!externalIds.includes(extId)) externalIds.push(extId);
743
+ }
744
+ }
745
+ for (const extId of externalIds) {
746
+ const label = extId.replace(/^external:/, '');
747
+ const extTextWidth = Math.min(300, Math.max(150, label.length * 8));
748
+ nodes.push({
749
+ id: extId,
750
+ type: 'subsystem-component',
751
+ position: { x: 0, y: 0 },
752
+ width: Math.max(150, extTextWidth + 24),
753
+ height: 60,
754
+ data: {
755
+ component: {
756
+ id: extId,
757
+ name: label,
758
+ construct: 'external',
759
+ purl: 'external',
760
+ file: '',
761
+ purpose: 'cross-package integration target (not a member node)',
762
+ },
763
+ },
764
+ draggable: false,
765
+ });
766
+ }
767
+
768
+ // Apply measured dimensions (second pass) so ELK gets the real node sizes.
769
+ if (measuredWidths && measuredWidths.size > 0) {
770
+ for (const n of nodes) {
771
+ const mw = measuredWidths.get(n.id);
772
+ if (mw) n.width = mw;
773
+ }
774
+ }
775
+ if (measuredHeights && measuredHeights.size > 0) {
776
+ for (const n of nodes) {
777
+ const mh = measuredHeights.get(n.id);
778
+ if (mh) n.height = mh;
779
+ }
780
+ }
781
+
782
+ // ELK auto-layout: position nodes (layered, minimized crossings) with
783
+ // process partitions as compound parents so boundaries shape the layout.
784
+ let placedNodes = nodes;
785
+ let labelPositions = new Map<string, { x: number; y: number }>();
786
+ let elkPathStrings = new Map<string, string>();
787
+ let elkPathPoints = new Map<string, { x: number; y: number }[]>();
788
+ if (nodes.length > 0) {
789
+ try {
790
+ const result = await computeElkLayout(nodes, edges, {
791
+ routingStyle: 'orthogonal',
792
+ direction: 'RIGHT',
793
+ nodeSpacing: 60,
794
+ edgeSpacing: 30,
795
+ edgeNodeSpacing: 60,
796
+ interLayerSpacing: 120,
797
+ preserveNodePositions: false,
798
+ edgeLabels: showEdgeLabels === false ? { enabled: false } : { enabled: true, placement: 'CENTER' },
799
+ groups: regions.map((r) => ({
800
+ id: processGroupNodeId(r.key),
801
+ memberIds: r.memberIds,
802
+ })),
803
+ });
804
+ const groupNodes: SubsystemGraphNode[] = regions.flatMap((region) => {
805
+ const bounds = result.groupBounds.get(processGroupNodeId(region.key));
806
+ if (!bounds) return [];
807
+ const group: SubsystemGraphNode = {
808
+ id: processGroupNodeId(region.key),
809
+ type: 'subsystem-group',
810
+ position: { x: bounds.x, y: bounds.y },
811
+ width: Math.max(200, bounds.width),
812
+ height: Math.max(160, bounds.height),
813
+ data: { region },
814
+ };
815
+ return [group];
816
+ });
817
+ // Parents first — React Flow resolves children via parentId.
818
+ placedNodes = [...groupNodes, ...(result.nodes as SubsystemGraphNode[])];
819
+ labelPositions = result.edgeLabelPositions;
820
+ elkPathStrings = result.edgePaths;
821
+ elkPathPoints = result.edgePathPoints;
822
+ } catch (err) {
823
+ // Fall back to the (unpositioned) grid if ELK is unavailable.
824
+ console.warn('[subsystem-graph] ELK layout failed, using manual positions:', err);
825
+ }
826
+ }
827
+
828
+ // Stamp ELK-computed label midpoints onto edge data so the overlay can
829
+ // position labels at the actual path midpoint (not the node-center midpoint).
830
+ for (const e of edges) {
831
+ const pos = labelPositions.get(e.id);
832
+ if (pos) {
833
+ const d = (e as SubsystemGraphEdge).data as SubsystemGraphEdgeData;
834
+ d.labelX = pos.x;
835
+ d.labelY = pos.y;
836
+ }
837
+ const elkP = elkPathStrings.get(e.id);
838
+ if (elkP) {
839
+ const d = (e as SubsystemGraphEdge).data as SubsystemGraphEdgeData;
840
+ d.elkPath = elkP;
841
+ }
842
+ const pts = elkPathPoints.get(e.id);
843
+ if (pts && pts.length > 1) {
844
+ const d = (e as SubsystemGraphEdge).data as SubsystemGraphEdgeData;
845
+ d.pathLength = calculatePathLength(pts);
846
+ }
847
+ }
848
+
849
+ return { nodes: placedNodes, edges, regions };
850
+ }