@eventcatalog/core 5.0.0-beta.9 → 5.0.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 (460) hide show
  1. package/LICENSE +13 -12
  2. package/README.md +5 -5
  3. package/dist/analytics/analytics.cjs +1 -1
  4. package/dist/analytics/analytics.js +2 -2
  5. package/dist/analytics/log-build.cjs +91 -44
  6. package/dist/analytics/log-build.d.cts +2 -2
  7. package/dist/analytics/log-build.d.ts +2 -2
  8. package/dist/analytics/log-build.js +6 -5
  9. package/dist/analytics/studio-usage.cjs +90 -0
  10. package/dist/analytics/studio-usage.d.cts +5 -0
  11. package/dist/analytics/studio-usage.d.ts +5 -0
  12. package/dist/analytics/studio-usage.js +10 -0
  13. package/dist/catalog-runtime.cjs +1 -1
  14. package/dist/{chunk-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
  15. package/dist/{chunk-AWOO63ME.js → chunk-AD7CL772.js} +11 -4
  16. package/dist/chunk-CA6WCQ2T.js +54 -0
  17. package/dist/{chunk-GAHKCOQM.js → chunk-JZN4RSMF.js} +1 -1
  18. package/dist/{chunk-JQMYHZWI.js → chunk-NEAVAFSJ.js} +16 -8
  19. package/dist/{chunk-4CFZUHGG.js → chunk-NIVST4WY.js} +1 -1
  20. package/dist/{chunk-ENAG6UH7.js → chunk-NKNOXQ62.js} +1 -6
  21. package/dist/{chunk-ZEY2XUJN.js → chunk-OX6TSVHY.js} +6 -6
  22. package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
  23. package/dist/{chunk-EUNQJO3J.js → chunk-RE7UTPWH.js} +1 -1
  24. package/dist/chunk-S5GWAUG5.js +29 -0
  25. package/dist/{chunk-TZVDMWQX.js → chunk-YQGRMDRB.js} +4 -2
  26. package/dist/constants.cjs +1 -1
  27. package/dist/constants.js +1 -1
  28. package/dist/docs/api/02-config.md +57 -7
  29. package/dist/docs/development/00-why-eventcatalog.md +11 -11
  30. package/dist/docs/development/01-fundamentals.md +20 -13
  31. package/dist/docs/development/01-getting-started/configuration-overview.md +2 -2
  32. package/dist/docs/development/01-getting-started/installation.md +9 -1
  33. package/dist/docs/development/_getting-started.mdx +1 -1
  34. package/dist/docs/development/ask-your-architecture/01-intro.md +5 -5
  35. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/01-what-is-eventcatalog-assistant.md +0 -4
  36. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/02-configuration.md +15 -3
  37. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/03-bring-your-own-tools.md +0 -3
  38. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/_category_.json +1 -1
  39. package/dist/docs/development/ask-your-architecture/02-llms.txt.md +1 -1
  40. package/dist/docs/development/ask-your-architecture/{05-skills/01-introduction.md → 03-install-skills.md} +57 -7
  41. package/dist/docs/development/ask-your-architecture/03-mcp-server/_category_.json +1 -1
  42. package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +86 -33
  43. package/dist/docs/development/ask-your-architecture/03-mcp-server/introduction.md +14 -4
  44. package/dist/docs/development/ask-your-architecture/03-schemas.txt.md +1 -1
  45. package/dist/docs/development/ask-your-architecture/04-agents/_category_.json +1 -1
  46. package/dist/docs/development/authentication/01-introduction.md +2 -23
  47. package/dist/docs/development/authentication/02-enabling-authentication.md +0 -20
  48. package/dist/docs/development/authentication/07-rbac-middleware.md +0 -2
  49. package/dist/docs/development/authentication/providers/03-setting-up-github.md +0 -2
  50. package/dist/docs/development/authentication/providers/03a-setting-up-google.md +0 -2
  51. package/dist/docs/development/authentication/providers/04-setting-up-azure-ad.md +0 -2
  52. package/dist/docs/development/authentication/providers/05-setting-up-okta.md +0 -3
  53. package/dist/docs/development/authentication/providers/06-setting-up-auth0.md +0 -2
  54. package/dist/docs/development/bring-your-own-documentation/01-introduction.md +0 -4
  55. package/dist/docs/development/bring-your-own-documentation/custom-pages/01-introduction.md +0 -3
  56. package/dist/docs/development/bring-your-own-documentation/custom-pages/02-adding-custom-docs.md +0 -17
  57. package/dist/docs/development/bring-your-own-documentation/custom-pages/03-components.md +0 -4
  58. package/dist/docs/development/bring-your-own-documentation/custom-pages/04-owners.md +0 -2
  59. package/dist/docs/development/bring-your-own-documentation/resource-docs/01-introduction.md +1 -3
  60. package/dist/docs/development/bring-your-own-documentation/resource-docs/02-adding-resource-docs.md +1 -4
  61. package/dist/docs/development/bring-your-own-documentation/resource-docs/03-categories.md +0 -2
  62. package/dist/docs/development/bring-your-own-documentation/resource-docs/04-versioning.md +1 -3
  63. package/dist/docs/development/components/components/11-flow.md +17 -0
  64. package/dist/docs/development/components/components/15-nodegraph.md +43 -10
  65. package/dist/docs/development/components/components/18-remote-schema.md +1 -4
  66. package/dist/docs/development/components/custom-components/07-reference.md +14 -0
  67. package/dist/docs/development/customization/02-themes.md +0 -5
  68. package/dist/docs/development/customization/03-search.md +1 -1
  69. package/dist/docs/development/customization/05-custom-pages-and-api-routes/01-introduction.md +0 -2
  70. package/dist/docs/development/customization/05-custom-pages-and-api-routes/04-reference.md +7 -1
  71. package/dist/docs/development/customization/05-resource-sidebar.md +1 -1
  72. package/dist/docs/development/customization/09-embed-eventcatalog.md +20 -25
  73. package/dist/docs/development/deployment/hosting-options.md +7 -2
  74. package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/05-fix-common-problems.md +16 -0
  75. package/dist/docs/development/developer-tools/eventcatalog-linter/reference/03-rules.md +31 -1
  76. package/dist/docs/development/developer-tools/eventcatalog-linter/reference/05-supported-resources.md +1 -1
  77. package/dist/docs/development/governance/architecture-change-detection/01-introduction.md +0 -10
  78. package/dist/docs/development/governance/architecture-change-detection/06-pipeline-gates.md +0 -4
  79. package/dist/docs/development/guides/97-architecture-graph.md +1 -1
  80. package/dist/docs/development/guides/99-adding-analytics.md +0 -7
  81. package/dist/docs/development/guides/domains/03-model-subdomains.md +2 -2
  82. package/dist/docs/development/guides/domains/04-add-resources-to-domains/01-add-systems-to-domains.md +3 -1
  83. package/dist/docs/development/guides/domains/04-add-resources-to-domains/02-add-services-to-domains.md +5 -1
  84. package/dist/docs/development/guides/domains/04-add-resources-to-domains/03-add-messages-to-domains.md +2 -2
  85. package/dist/docs/development/guides/domains/04-add-resources-to-domains/05-add-entities-to-domains.md +1 -1
  86. package/dist/docs/development/guides/domains/04-add-resources-to-domains/06-domain-resources-page.md +40 -0
  87. package/dist/docs/development/guides/domains/06-visualize-domains/01-domain-diagram.md +89 -0
  88. package/dist/docs/development/guides/domains/06-visualize-domains/_category_.json +1 -2
  89. package/dist/docs/development/guides/domains/09-reference.md +4 -4
  90. package/dist/docs/development/guides/owners/automated-teams-and-users/create-your-own-connector.md +0 -2
  91. package/dist/docs/development/guides/owners/automated-teams-and-users/sync-from-github.md +0 -3
  92. package/dist/docs/development/guides/owners/automated-teams-and-users/sync-from-microsoft-entra.md +0 -2
  93. package/dist/docs/development/guides/resources/adrs/03-linking-resources.md +8 -0
  94. package/dist/docs/development/guides/resources/adrs/06-reference.md +4 -4
  95. package/dist/docs/development/guides/resources/agents/11-reference.md +1 -1
  96. package/dist/docs/development/guides/resources/data/04-reference.md +1 -1
  97. package/dist/docs/development/guides/resources/data-products/07-reference.md +1 -1
  98. package/dist/docs/development/guides/resources/entities/05-entity-maps.md +1 -1
  99. package/dist/docs/development/guides/resources/entities/06-reference.md +2 -2
  100. package/dist/docs/development/guides/resources/flows/01-introduction.md +11 -1
  101. package/dist/docs/development/guides/resources/flows/03-model-flow-steps.md +65 -6
  102. package/dist/docs/development/guides/resources/flows/05-explore-flows.md +112 -0
  103. package/dist/docs/development/guides/resources/flows/06-reference.md +25 -4
  104. package/dist/docs/development/guides/resources/messages/01-what-are-messages.md +19 -0
  105. package/dist/docs/development/guides/resources/messages/04-message-channels/01-introduction.md +16 -0
  106. package/dist/docs/development/guides/resources/messages/04-message-channels/05-reference.md +2 -1
  107. package/dist/docs/development/guides/resources/messages/10-reference.md +6 -3
  108. package/dist/docs/development/guides/resources/schemas/04-explore-schemas/01-schema-explorer.md +2 -2
  109. package/dist/docs/development/guides/resources/schemas/04-explore-schemas/02-fields-explorer.md +0 -9
  110. package/dist/docs/development/guides/resources/schemas/05-schema-api.md +0 -3
  111. package/dist/docs/development/guides/resources/schemas/06-schema-mcp.md +11 -3
  112. package/dist/docs/development/guides/resources/services/04-add-resources-to-services/03-add-entities-to-services.md +1 -1
  113. package/dist/docs/development/guides/resources/services/05-resources-page.md +50 -0
  114. package/dist/docs/development/guides/resources/services/08-reference.md +2 -1
  115. package/dist/docs/development/guides/systems/01-introduction.md +1 -1
  116. package/dist/docs/development/guides/systems/02-create-system.md +2 -2
  117. package/dist/docs/development/guides/systems/03-add-resources-to-systems.md +35 -9
  118. package/dist/docs/development/guides/systems/04-model-relationships-and-actors.md +5 -3
  119. package/dist/docs/development/guides/systems/05-system-context-maps.md +39 -22
  120. package/dist/docs/development/guides/systems/06-reference.md +4 -4
  121. package/dist/docs/development/license/01-license-file.md +151 -0
  122. package/dist/docs/development/license/_category_.json +12 -0
  123. package/dist/docs/development/license-keys/_category_.json +3 -3
  124. package/dist/docs/development/license-keys/integrations.md +7 -37
  125. package/dist/docs/development/license-keys/license-validation.md +4 -0
  126. package/dist/docs/development/license-keys/overview.md +5 -1
  127. package/dist/docs/development/license-keys/plans.md +4 -2
  128. package/dist/docs/development/studio/01-introduction.md +60 -0
  129. package/dist/docs/development/studio/02-create-a-canvas.md +103 -0
  130. package/dist/docs/development/studio/03-set-up-persistent-storage.md +59 -0
  131. package/dist/docs/development/studio/04-collaborate-on-a-canvas.md +78 -0
  132. package/dist/docs/development/studio/05-connect-your-agent.md +102 -0
  133. package/dist/docs/development/studio/06-run-studio-in-production.md +54 -0
  134. package/dist/docs/development/studio/07-reference.md +294 -0
  135. package/dist/docs/development/studio/_category_.json +12 -0
  136. package/dist/docs/development/upgrading/v5.md +345 -0
  137. package/dist/docs/federation/00-overview.md +2 -8
  138. package/dist/docs/federation/01-first-federation.md +6 -18
  139. package/dist/docs/federation/explanation/how-it-works.md +0 -4
  140. package/dist/docs/federation/explanation/lockfile-and-cache.md +0 -4
  141. package/dist/docs/federation/explanation/mvp-status-and-feedback.md +0 -4
  142. package/dist/docs/federation/explanation/ownership-and-references.md +0 -4
  143. package/dist/docs/federation/how-to/configure-github-sources.md +6 -5
  144. package/dist/docs/federation/how-to/configure-validation-rules.md +0 -4
  145. package/dist/docs/federation/how-to/run-in-ci.md +2 -9
  146. package/dist/docs/federation/how-to/share-assets-and-components.md +0 -4
  147. package/dist/docs/federation/how-to/use-local-sources.md +0 -4
  148. package/dist/docs/federation/legacy-federation/00-introduction.md +4 -22
  149. package/dist/docs/federation/legacy-federation/01-installation.md +7 -58
  150. package/dist/docs/federation/legacy-federation/02-plugin-configuration.md +3 -21
  151. package/dist/docs/federation/legacy-federation/03-examples.md +3 -5
  152. package/dist/docs/federation/legacy-federation/04-configuration.md +3 -8
  153. package/dist/docs/federation/legacy-federation/05-setup-team-catalog.md +3 -6
  154. package/dist/docs/federation/reference/cli.md +2 -8
  155. package/dist/docs/federation/reference/configuration.md +0 -4
  156. package/dist/docs/federation/reference/diagnostic-rules.md +0 -5
  157. package/dist/docs/federation/reference/generated-output.md +0 -4
  158. package/dist/docs/federation/reference/supported-content.md +0 -4
  159. package/dist/docs/federation/reference/troubleshooting.md +0 -14
  160. package/dist/docs/plugins/02-generators.md +1 -1
  161. package/dist/docs/plugins/amazon-apigateway/00-intro.md +3 -28
  162. package/dist/docs/plugins/amazon-apigateway/01-installation.md +4 -56
  163. package/dist/docs/plugins/amazon-apigateway/02-plugin-configuration.md +1 -6
  164. package/dist/docs/plugins/apicurio/00-intro.md +2 -31
  165. package/dist/docs/plugins/apicurio/01-installation.md +2 -26
  166. package/dist/docs/plugins/apicurio/02-plugin-configuration.md +5 -13
  167. package/dist/docs/plugins/apicurio/03-features.md +1 -3
  168. package/dist/docs/plugins/asyncapi/00-intro.md +2 -28
  169. package/dist/docs/plugins/asyncapi/01-installation.md +3 -54
  170. package/dist/docs/plugins/asyncapi/02-plugin-configuration.md +2 -1
  171. package/dist/docs/plugins/aws-glue-registry/00-intro.md +4 -32
  172. package/dist/docs/plugins/aws-glue-registry/00a-installation.md +3 -53
  173. package/dist/docs/plugins/azure-schema-registry/00-intro.md +4 -32
  174. package/dist/docs/plugins/azure-schema-registry/01-installation.md +6 -33
  175. package/dist/docs/plugins/azure-schema-registry/02-plugin-configuration.md +1 -14
  176. package/dist/docs/plugins/azure-schema-registry/04-examples.md +0 -2
  177. package/dist/docs/plugins/backstage/00-overview.md +1 -7
  178. package/dist/docs/plugins/backstage/01-get-started.md +6 -15
  179. package/dist/docs/plugins/backstage/06-configuration.md +2 -8
  180. package/dist/docs/plugins/confluent-schema-registry/00-intro.md +2 -29
  181. package/dist/docs/plugins/confluent-schema-registry/01-installation.md +2 -26
  182. package/dist/docs/plugins/eventbridge/00-intro.md +0 -4
  183. package/dist/docs/plugins/eventbridge/00a-installation.md +1 -87
  184. package/dist/docs/plugins/eventbridge/03-api.md +2 -20
  185. package/dist/docs/plugins/github/00-intro.md +0 -31
  186. package/dist/docs/plugins/github/01-installation.md +2 -34
  187. package/dist/docs/plugins/graphql/00-intro.md +0 -30
  188. package/dist/docs/plugins/graphql/01-installation.md +1 -53
  189. package/dist/docs/plugins/graphql/02-plugin-configuration.md +0 -1
  190. package/dist/docs/plugins/hookdeck/01-intro.md +1 -1
  191. package/dist/docs/plugins/openapi/00-intro.md +0 -30
  192. package/dist/docs/plugins/openapi/01-installation.md +1 -57
  193. package/dist/docs/plugins/openapi/02-plugin-configuration.md +0 -5
  194. package/dist/docs/plugins/openapi/03-features.md +0 -4
  195. package/dist/eventcatalog-config-file-utils.cjs +34 -41
  196. package/dist/eventcatalog-config-file-utils.d.cts +56 -58
  197. package/dist/eventcatalog-config-file-utils.d.ts +56 -58
  198. package/dist/eventcatalog-config-file-utils.js +1 -1
  199. package/dist/eventcatalog.cjs +340 -249
  200. package/dist/eventcatalog.config.d.cts +18 -15
  201. package/dist/eventcatalog.config.d.ts +18 -15
  202. package/dist/eventcatalog.js +51 -33
  203. package/dist/features.cjs +36 -49
  204. package/dist/features.d.cts +1 -3
  205. package/dist/features.d.ts +1 -3
  206. package/dist/features.js +4 -6
  207. package/dist/federation/federate.cjs +37 -44
  208. package/dist/federation/federate.js +5 -5
  209. package/dist/federation/source-provider.js +2 -2
  210. package/dist/generate.cjs +37 -42
  211. package/dist/generate.js +4 -4
  212. package/dist/resolve-catalog-dependencies.cjs +34 -41
  213. package/dist/resolve-catalog-dependencies.js +2 -2
  214. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
  215. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
  216. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
  217. package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
  218. package/dist/runtime-dependencies/manifest.json +5 -1
  219. package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
  220. package/dist/utils/child-process-signals.cjs +53 -0
  221. package/dist/utils/child-process-signals.d.cts +19 -0
  222. package/dist/utils/child-process-signals.d.ts +19 -0
  223. package/dist/utils/child-process-signals.js +6 -0
  224. package/dist/utils/cli-logger.cjs +1 -1
  225. package/dist/utils/cli-logger.js +2 -2
  226. package/dist/utils/license-status.cjs +11 -4
  227. package/dist/utils/license-status.d.cts +3 -2
  228. package/dist/utils/license-status.d.ts +3 -2
  229. package/dist/utils/license-status.js +1 -1
  230. package/eventcatalog/astro.config.mjs +21 -1
  231. package/eventcatalog/integrations/config-loader.mjs +56 -58
  232. package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
  233. package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
  234. package/eventcatalog/integrations/storage.mjs +20 -0
  235. package/eventcatalog/integrations/studio-server.mjs +4 -1
  236. package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
  237. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
  238. package/eventcatalog/src/components/Homepage/CatalogDashboard.astro +342 -0
  239. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +60 -4
  240. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
  241. package/eventcatalog/src/components/NavigationProgress.astro +7 -0
  242. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
  243. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
  244. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
  245. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
  246. package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
  247. package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
  248. package/eventcatalog/src/components/Search/search-utils.ts +26 -0
  249. package/eventcatalog/src/components/Seo.astro +5 -1
  250. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +53 -48
  251. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +183 -178
  252. package/eventcatalog/src/components/Settings/LicenseSettings.tsx +140 -31
  253. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +40 -35
  254. package/eventcatalog/src/components/Settings/LogoUpload.tsx +1 -1
  255. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +9 -8
  256. package/eventcatalog/src/components/Settings/Row.tsx +7 -4
  257. package/eventcatalog/src/components/Settings/SettingsShared.tsx +3 -3
  258. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +37 -76
  259. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
  260. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +122 -1
  261. package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
  262. package/eventcatalog/src/components/TrialBadge.astro +20 -9
  263. package/eventcatalog/src/content.config.ts +19 -2
  264. package/eventcatalog/src/features/auth/error.astro +4 -2
  265. package/eventcatalog/src/features/auth/login.astro +2 -1
  266. package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +21 -1
  267. package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
  268. package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
  269. package/eventcatalog/src/features/integrations/eventcatalog-features.ts +13 -7
  270. package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
  271. package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +3 -3
  272. package/eventcatalog/src/features/mcp/apps/canvas/styles.css +3 -1
  273. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +7 -5
  274. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +123 -98
  275. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +118 -101
  276. package/eventcatalog/src/features/mcp/mcp-server.ts +20 -4
  277. package/eventcatalog/src/features/server/start.ts +25 -0
  278. package/eventcatalog/src/features/storage/database.ts +88 -0
  279. package/eventcatalog/src/features/storage/migrations.ts +85 -0
  280. package/eventcatalog/src/features/storage/sql/database.sql +5 -0
  281. package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
  282. package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
  283. package/eventcatalog/src/features/storage/sql.ts +28 -0
  284. package/eventcatalog/src/features/studio/agent-choreography.ts +13 -10
  285. package/eventcatalog/src/features/studio/agents.ts +28 -0
  286. package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
  287. package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
  288. package/eventcatalog/src/features/studio/api/client.ts +40 -0
  289. package/eventcatalog/src/features/studio/canvas-actions.ts +160 -34
  290. package/eventcatalog/src/features/studio/canvas-preview.ts +1 -2
  291. package/eventcatalog/src/features/studio/catalog.ts +16 -14
  292. package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +274 -105
  293. package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +15 -14
  294. package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +272 -119
  295. package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +1 -2
  296. package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
  297. package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
  298. package/eventcatalog/src/features/studio/components/JoinForm.tsx +101 -26
  299. package/eventcatalog/src/features/studio/components/LeftPanel.tsx +122 -230
  300. package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
  301. package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
  302. package/eventcatalog/src/features/studio/components/Presence.tsx +52 -45
  303. package/eventcatalog/src/features/studio/components/ShareDialog.tsx +277 -141
  304. package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
  305. package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +1314 -385
  306. package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
  307. package/eventcatalog/src/features/studio/document-name.ts +7 -0
  308. package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
  309. package/eventcatalog/src/features/studio/hooks/presence-sender.ts +19 -2
  310. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +9 -3
  311. package/eventcatalog/src/features/studio/hooks/use-comments.ts +2 -2
  312. package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +8 -3
  313. package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +244 -32
  314. package/eventcatalog/src/features/studio/identity.ts +25 -1
  315. package/eventcatalog/src/features/studio/limits.ts +2 -0
  316. package/eventcatalog/src/features/studio/pages/[id].astro +42 -16
  317. package/eventcatalog/src/features/studio/pages/index.astro +596 -373
  318. package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +12 -15
  319. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +8 -23
  320. package/eventcatalog/src/features/studio/server/runtime.ts +150 -46
  321. package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
  322. package/eventcatalog/src/features/studio/server/storage.ts +53 -73
  323. package/eventcatalog/src/features/studio/webmcp-relay/LICENSE +21 -0
  324. package/eventcatalog/src/features/studio/webmcp-relay/README.md +5 -0
  325. package/eventcatalog/src/features/studio/webmcp-relay/embed.js +2 -0
  326. package/eventcatalog/src/features/studio/webmcp-relay/widget.html +10 -0
  327. package/eventcatalog/src/features/studio/webmcp-relay/widget.js +1 -0
  328. package/eventcatalog/src/features/tools/catalog-tools.ts +3 -1
  329. package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
  330. package/eventcatalog/src/layouts/SettingsLayout.astro +2 -2
  331. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +3 -2
  332. package/eventcatalog/src/pages/_index.astro +5 -342
  333. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +2 -3
  334. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +0 -7
  335. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -1
  336. package/eventcatalog/src/pages/diagrams/[id]/[version]/_index.data.ts +0 -7
  337. package/eventcatalog/src/pages/diagrams/[id]/[version]/embed.astro +1 -0
  338. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +1 -0
  339. package/eventcatalog/src/pages/directory/[type]/_index.data.ts +5 -7
  340. package/eventcatalog/src/pages/directory/[type]/index.astro +3 -1
  341. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -7
  342. package/eventcatalog/src/pages/discover/[type]/index.astro +3 -1
  343. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +0 -7
  344. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -0
  345. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +0 -7
  346. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +1 -0
  347. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +0 -7
  348. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +3 -1
  349. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/_[filename].data.ts +0 -7
  350. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +0 -7
  351. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -0
  352. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +1 -0
  353. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/_[...filename].data.ts +0 -4
  354. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.ts +0 -7
  355. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/index.astro +1 -0
  356. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +3 -1
  357. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/_[filename].data.ts +0 -7
  358. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
  359. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +2 -3
  360. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +0 -7
  361. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +1 -0
  362. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +1 -0
  363. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/_[filename].data.ts +0 -7
  364. package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +0 -7
  365. package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +2 -1
  366. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/_index.data.ts +0 -7
  367. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -0
  368. package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +0 -7
  369. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +1 -0
  370. package/eventcatalog/src/pages/docs/custom/[...path]/_index.data.ts +0 -7
  371. package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +1 -0
  372. package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +1 -1
  373. package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +1 -0
  374. package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +1 -1
  375. package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +1 -1
  376. package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +1 -1
  377. package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +1 -1
  378. package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +0 -7
  379. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +1 -0
  380. package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +0 -7
  381. package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -0
  382. package/eventcatalog/src/pages/index.astro +4 -2
  383. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +0 -7
  384. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +9 -1
  385. package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +0 -7
  386. package/eventcatalog/src/pages/schemas/explorer/index.astro +4 -2
  387. package/eventcatalog/src/pages/settings/license.astro +9 -1
  388. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/_index.data.ts +0 -7
  389. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +1 -0
  390. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +0 -7
  391. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +0 -7
  392. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +1 -0
  393. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/_index.data.ts +0 -7
  394. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +1 -0
  395. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +1 -0
  396. package/eventcatalog/src/pages/visualiser/[type]/[id]/_index.data.ts +38 -0
  397. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +18 -41
  398. package/eventcatalog/src/pages/visualiser/designs/[id]/_index.data.ts +0 -7
  399. package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +2 -1
  400. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +0 -7
  401. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +1 -0
  402. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/index.astro +1 -0
  403. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/_index.data.ts +0 -7
  404. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/index.astro +1 -0
  405. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/_index.data.ts +0 -7
  406. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/index.astro +1 -0
  407. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/index.astro +1 -0
  408. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
  409. package/eventcatalog/src/utils/collections/flows.ts +20 -7
  410. package/eventcatalog/src/utils/collections/resource-owners.ts +8 -2
  411. package/eventcatalog/src/utils/collections/resources.ts +2 -0
  412. package/eventcatalog/src/utils/collections/util.ts +8 -4
  413. package/eventcatalog/src/utils/git.ts +11 -0
  414. package/eventcatalog/src/utils/homepage-dashboard.ts +143 -0
  415. package/eventcatalog/src/utils/license.ts +1 -1
  416. package/eventcatalog/src/utils/link-validation.ts +25 -19
  417. package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
  418. package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
  419. package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +4 -2
  420. package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
  421. package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
  422. package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
  423. package/eventcatalog/src/utils/page-loaders/hybrid-page.ts +4 -15
  424. package/eventcatalog/src/utils/page-title.ts +15 -0
  425. package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
  426. package/eventcatalog/src/utils/trial.ts +13 -0
  427. package/package.json +11 -11
  428. package/dist/docs/development/ask-your-architecture/05-skills/02-installation.md +0 -60
  429. package/dist/docs/development/ask-your-architecture/05-skills/_category_.json +0 -11
  430. package/dist/docs/development/ask-your-architecture/06-slack-integration/01-introduction.md +0 -63
  431. package/dist/docs/development/ask-your-architecture/06-slack-integration/02-slack-app-setup.md +0 -154
  432. package/dist/docs/development/ask-your-architecture/06-slack-integration/03-installation.md +0 -169
  433. package/dist/docs/development/ask-your-architecture/06-slack-integration/04-deployment.md +0 -236
  434. package/dist/docs/development/ask-your-architecture/06-slack-integration/05-usage.md +0 -140
  435. package/dist/docs/development/ask-your-architecture/06-slack-integration/06-troubleshooting.md +0 -268
  436. package/dist/docs/development/ask-your-architecture/06-slack-integration/_category_.json +0 -13
  437. package/dist/docs/development/guides/domains/06-visualize-domains/01-domain-integration-map.md +0 -40
  438. package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +0 -3
  439. package/eventcatalog/src/features/studio/canvas-doc.ts +0 -409
  440. package/eventcatalog/src/features/studio/clipboard.ts +0 -97
  441. package/eventcatalog/src/features/studio/components/CanvasControls.tsx +0 -154
  442. package/eventcatalog/src/features/studio/components/Comments.tsx +0 -770
  443. package/eventcatalog/src/features/studio/components/ContainerChooser.tsx +0 -145
  444. package/eventcatalog/src/features/studio/components/DragGhost.tsx +0 -115
  445. package/eventcatalog/src/features/studio/components/Picture.tsx +0 -21
  446. package/eventcatalog/src/features/studio/components/PropertiesPanel.tsx +0 -212
  447. package/eventcatalog/src/features/studio/components/canvas-nodes.tsx +0 -255
  448. package/eventcatalog/src/features/studio/components/icons.ts +0 -53
  449. package/eventcatalog/src/features/studio/components/status.ts +0 -51
  450. package/eventcatalog/src/features/studio/edges.ts +0 -56
  451. package/eventcatalog/src/features/studio/flow-state.ts +0 -92
  452. package/eventcatalog/src/features/studio/grouping.ts +0 -226
  453. package/eventcatalog/src/features/studio/layout.ts +0 -42
  454. package/eventcatalog/src/features/studio/levels.ts +0 -282
  455. package/eventcatalog/src/features/studio/node-types.ts +0 -205
  456. package/eventcatalog/src/features/studio/pages/new.astro +0 -24
  457. package/eventcatalog/src/features/studio/pages/wake.ts +0 -15
  458. package/eventcatalog/src/features/studio/placement.ts +0 -197
  459. package/eventcatalog/src/features/studio/studio.css +0 -273
  460. package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
@@ -1,409 +0,0 @@
1
- import * as Y from 'yjs';
2
- import type { Connection, Edge, Node, XYPosition } from '@xyflow/react';
3
- import { createEdge, getEdgeLabel } from './edges';
4
- import { fitGroup, getAbsolutePosition, withDescendants, withParent } from './grouping';
5
- import { getNodeDefinition, getNodeSize, isGroupType } from './node-types';
6
-
7
- /**
8
- * Everything a canvas holds, as operations on its Yjs document. The same functions run in the
9
- * browser (people, WebMCP) and on the server (MCP tools), so humans and agents edit a canvas the same way.
10
- */
11
-
12
- export type Author = {
13
- name: string;
14
- color: string;
15
- agent?: boolean;
16
- /** A picture from their sign-in provider (SSO), shown over their initials */
17
- picture?: string;
18
- };
19
- export type Message = { id: string; text: string; author: Author; createdAt: number };
20
- /** Where a comment is pinned: a canvas position, or a node (and the offset from its top left) so it moves with it */
21
- export type CommentAnchor = { position: XYPosition; nodeId?: string; offset?: XYPosition };
22
- export type Thread = CommentAnchor & { id: string; author: Author; createdAt: number; resolved: boolean; messages: Message[] };
23
- /** Where a canvas's design is: being worked on, up for review, agreed, or decided against */
24
- export const CANVAS_STATUSES = ['draft', 'proposed', 'accepted', 'rejected'] as const;
25
- export type CanvasStatus = (typeof CANVAS_STATUSES)[number];
26
- /** A canvas's status being changed: by whom, when, and why (e.g. "Agreed in the architecture review") */
27
- export type StatusChange = { status: CanvasStatus; by: Author; at: number; note?: string };
28
- export type CanvasMeta = {
29
- title?: string;
30
- /** Draft until someone changes it */
31
- status?: CanvasStatus;
32
- /** Every status change, oldest first (the last few hundred) */
33
- statusHistory?: StatusChange[];
34
- createdAt?: number;
35
- createdBy?: string;
36
- /** When the canvas was last laid out, so everyone's canvas glides the nodes into place */
37
- layoutAt?: number;
38
- };
39
-
40
- // Canvases' documents on the collaboration server are named `design:<canvasId>` (scripts and peers join by it)
41
- const DOCUMENT_PREFIX = 'design:';
42
- export const canvasDocumentName = (canvasId: string) => `${DOCUMENT_PREFIX}${canvasId}`;
43
- export const canvasIdFromDocumentName = (name: string) =>
44
- name.startsWith(DOCUMENT_PREFIX) ? name.slice(DOCUMENT_PREFIX.length) : undefined;
45
-
46
- export const shortId = () => crypto.randomUUID().slice(0, 8);
47
-
48
- export const getCanvasMaps = (doc: Y.Doc) => ({
49
- nodes: doc.getMap<Node>('nodes'),
50
- edges: doc.getMap<Edge>('edges'),
51
- threads: doc.getMap<Y.Map<unknown>>('threads'),
52
- meta: doc.getMap<unknown>('meta'),
53
- });
54
-
55
- // Only the design is shared. Selection, measured sizes and drag state stay with each user.
56
- export const toSharedNode = ({ id, type, position, data, width, height, parentId, zIndex }: Node): Node => ({
57
- id,
58
- type,
59
- position,
60
- data,
61
- ...(width ? { width } : {}),
62
- ...(height ? { height } : {}),
63
- // Inside a container (its position is then relative to the container)
64
- ...(parentId ? { parentId } : {}),
65
- ...(zIndex !== undefined ? { zIndex } : {}),
66
- });
67
-
68
- /** A new node of a type, centred on a canvas position */
69
- export const buildNode = (type: string, data: Record<string, unknown>, center: XYPosition): Node => {
70
- const definition = getNodeDefinition(type);
71
- const size = getNodeSize(type);
72
- return toSharedNode({
73
- id: `${type}-${shortId()}`,
74
- type,
75
- position: { x: center.x - size.width / 2, y: center.y - size.height / 2 },
76
- data,
77
- ...(definition?.defaultSize ? (definition.autoHeight ? { width: size.width } : size) : {}),
78
- // Containers sit behind what's in them and the connections between them
79
- ...(isGroupType(type) && { zIndex: -1 }),
80
- });
81
- };
82
-
83
- // ---- Meta ----
84
-
85
- export const readMeta = (doc: Y.Doc) => getCanvasMaps(doc).meta.toJSON() as CanvasMeta;
86
-
87
- export const setMeta = (doc: Y.Doc, meta: Partial<CanvasMeta>) =>
88
- doc.transact(() => {
89
- const map = getCanvasMaps(doc).meta;
90
- Object.entries(meta).forEach(([key, value]) => (value === undefined ? map.delete(key) : map.set(key, value)));
91
- });
92
-
93
- // ---- Status ----
94
-
95
- const MAX_STATUS_HISTORY = 200;
96
-
97
- export const getCanvasStatus = (meta: CanvasMeta): CanvasStatus => meta.status ?? 'draft';
98
-
99
- /** Changes a canvas's status (and records who did it). Returns false if it already had that status. */
100
- export const setCanvasStatus = (doc: Y.Doc, status: CanvasStatus, by: Author, note?: string) => {
101
- let changed = false;
102
- doc.transact(() => {
103
- const meta = readMeta(doc);
104
- if (getCanvasStatus(meta) === status) return;
105
- const change: StatusChange = { status, by, at: Date.now(), ...(note?.trim() && { note: note.trim() }) };
106
- const map = getCanvasMaps(doc).meta;
107
- map.set('status', status);
108
- map.set('statusHistory', [...(meta.statusHistory ?? []), change].slice(-MAX_STATUS_HISTORY));
109
- changed = true;
110
- });
111
- return changed;
112
- };
113
-
114
- /** Accepted and rejected canvases are decided: changing what's on them makes them a draft again */
115
- export const isDecided = (status: CanvasStatus) => status === 'accepted' || status === 'rejected';
116
-
117
- /** A decided canvas whose nodes or edges were changed goes back to draft, recorded as changed by `by` */
118
- export const reopenIfDecided = (doc: Y.Doc, by: Author) => {
119
- const status = getCanvasStatus(readMeta(doc));
120
- if (isDecided(status)) setCanvasStatus(doc, 'draft', by, `Changed after it was ${status}`);
121
- };
122
-
123
- /**
124
- * Runs `fn` (which may change the canvas), and if it changed nodes or edges, sends a decided canvas back to draft.
125
- * Comments don't count: reviewing an accepted design keeps it accepted.
126
- */
127
- export const reopeningIfEdited = <T>(doc: Y.Doc, by: Author, fn: () => T): T => {
128
- let result!: T;
129
- // In one transaction (the one it's already in, if it is): what it changed is known before it ends
130
- doc.transact((transaction) => {
131
- result = fn();
132
- if (changesNodesOrEdges(transaction)) reopenIfDecided(doc, by);
133
- });
134
- return result;
135
- };
136
-
137
- /** Whether a transaction changed the canvas's nodes or edges (not just its comments or meta) */
138
- export const changesNodesOrEdges = (transaction: Y.Transaction) => {
139
- const { nodes, edges } = getCanvasMaps(transaction.doc);
140
- return [...transaction.changed.keys()].some((type) => type === (nodes as unknown) || type === (edges as unknown));
141
- };
142
-
143
- // ---- Nodes and edges ----
144
-
145
- export const addNodes = (doc: Y.Doc, nodes: Node[], edges: Edge[] = []) =>
146
- doc.transact(() => {
147
- const maps = getCanvasMaps(doc);
148
- nodes.forEach((node) => maps.nodes.set(node.id, toSharedNode(node)));
149
- edges.forEach((edge) => maps.edges.set(edge.id, edge));
150
- });
151
-
152
- export const moveNode = (doc: Y.Doc, id: string, position: XYPosition) => {
153
- const { nodes } = getCanvasMaps(doc);
154
- const node = nodes.get(id);
155
- if (node) nodes.set(id, { ...node, position });
156
- return !!node;
157
- };
158
-
159
- /** Resize a node: both sides, or just its width or height (e.g. text, whose height follows what's in it) */
160
- export const resizeNode = (
161
- doc: Y.Doc,
162
- id: string,
163
- size: { width: number; height: number },
164
- attributes: true | 'width' | 'height' = true
165
- ) => {
166
- const { nodes } = getCanvasMaps(doc);
167
- const node = nodes.get(id);
168
- if (node)
169
- nodes.set(id, {
170
- ...node,
171
- ...(attributes !== 'height' && { width: size.width }),
172
- ...(attributes !== 'width' && { height: size.height }),
173
- });
174
- return !!node;
175
- };
176
-
177
- /** Grow a container to fit what's in it (moving them clear of its header if needed), never shrinking it */
178
- /**
179
- * The sizes nodes are rendered at (in the browser), by id. The document doesn't have them, so without them
180
- * containers are fitted to the size each type renders at at most.
181
- */
182
- export type RenderedSizes = ReadonlyMap<string, { width: number; height: number }>;
183
-
184
- const withRenderedSizes = (nodes: Node[], sizes?: RenderedSizes) =>
185
- sizes ? nodes.map((node) => (sizes.has(node.id) ? { ...node, measured: sizes.get(node.id) } : node)) : nodes;
186
-
187
- export const fitGroupToChildren = (doc: Y.Doc, groupId: string, sizes?: RenderedSizes) =>
188
- doc.transact(() => {
189
- const { nodes } = getCanvasMaps(doc);
190
- const fit = fitGroup(groupId, withRenderedSizes(Array.from(nodes.values()), sizes));
191
- const group = nodes.get(groupId);
192
- if (!fit || !group) return;
193
- if (fit.shift.x || fit.shift.y) {
194
- // Children move right/down and the container left/up by the same amount: nothing moves on the canvas
195
- nodes.forEach((child, id) => {
196
- if (child.parentId === groupId)
197
- nodes.set(id, { ...child, position: { x: child.position.x + fit.shift.x, y: child.position.y + fit.shift.y } });
198
- });
199
- }
200
- nodes.set(groupId, {
201
- ...group,
202
- position: { x: group.position.x - fit.shift.x, y: group.position.y - fit.shift.y },
203
- width: fit.width,
204
- height: fit.height,
205
- });
206
- });
207
-
208
- /** The containers a node is in grow to fit it (innermost first), e.g. after it's added or resized */
209
- export const fitContainersAround = (doc: Y.Doc, nodeId: string, sizes?: RenderedSizes) =>
210
- doc.transact(() => {
211
- const { nodes } = getCanvasMaps(doc);
212
- for (let id = nodes.get(nodeId)?.parentId; id; id = nodes.get(id)?.parentId) fitGroupToChildren(doc, id, sizes);
213
- });
214
-
215
- /** Put a node in a container (or take it out, without a parentId), keeping it where it is on the canvas */
216
- export const setNodeParent = (doc: Y.Doc, nodeId: string, parentId: string | undefined, sizes?: RenderedSizes) =>
217
- doc.transact(() => {
218
- const { nodes } = getCanvasMaps(doc);
219
- const node = nodes.get(nodeId);
220
- if (!node || node.parentId === parentId) return false;
221
- nodes.set(nodeId, toSharedNode(withParent(node, parentId, Array.from(nodes.values()))));
222
- if (parentId) fitGroupToChildren(doc, parentId, sizes);
223
- return true;
224
- });
225
-
226
- export const updateNodeData = (doc: Y.Doc, id: string, updater: (data: Record<string, unknown>) => Record<string, unknown>) => {
227
- const { nodes } = getCanvasMaps(doc);
228
- const node = nodes.get(id);
229
- if (node) nodes.set(id, { ...node, data: updater(node.data) });
230
- return !!node;
231
- };
232
-
233
- /** Connect two nodes with an EventCatalog-labelled edge. Returns the edge, or why it couldn't be made. */
234
- export const connectNodes = (
235
- doc: Y.Doc,
236
- connection: Pick<Connection, 'source' | 'target'> & Partial<Connection>,
237
- label?: string
238
- ): Edge | { error: string } => {
239
- const { nodes, edges } = getCanvasMaps(doc);
240
- const source = nodes.get(connection.source);
241
- const target = nodes.get(connection.target);
242
- if (!source) return { error: `Node "${connection.source}" is not on the canvas` };
243
- if (!target) return { error: `Node "${connection.target}" is not on the canvas` };
244
- if (source.id === target.id) return { error: 'A node cannot be connected to itself' };
245
- const existing = Array.from(edges.values()).find((edge) => edge.source === source.id && edge.target === target.id);
246
- if (existing) return existing;
247
- const edge = createEdge(
248
- {
249
- source: source.id,
250
- target: target.id,
251
- sourceHandle: connection.sourceHandle ?? null,
252
- targetHandle: connection.targetHandle ?? null,
253
- },
254
- source.type,
255
- target.type
256
- );
257
- const created = label === undefined ? edge : { ...edge, label };
258
- edges.set(created.id, created);
259
- return created;
260
- };
261
-
262
- /**
263
- * Moves a connection to new ends (dragged to another node), keeping it the same connection. Its label follows
264
- * the new ends if it was EventCatalog's wording, and is kept if someone wrote it.
265
- */
266
- export const reconnectEdge = (doc: Y.Doc, edgeId: string, connection: Connection): Edge | { error: string } =>
267
- doc.transact(() => {
268
- const { nodes, edges } = getCanvasMaps(doc);
269
- const edge = edges.get(edgeId);
270
- const source = nodes.get(connection.source);
271
- const target = nodes.get(connection.target);
272
- if (!edge) return { error: `Connection "${edgeId}" is not on the canvas` };
273
- if (!source || !target) return { error: 'Connections join two nodes on the canvas' };
274
- if (source.id === target.id) return { error: 'A node cannot be connected to itself' };
275
- const duplicate = Array.from(edges.values()).find(
276
- (other) => other.id !== edgeId && other.source === source.id && other.target === target.id
277
- );
278
- if (duplicate) return { error: 'Those nodes are already connected' };
279
-
280
- const defaultLabel = getEdgeLabel(nodes.get(edge.source)?.type, nodes.get(edge.target)?.type);
281
- const moved = createEdge(connection, source.type, target.type);
282
- const reconnected: Edge = {
283
- ...moved,
284
- id: edge.id,
285
- ...(edge.label !== undefined && edge.label !== defaultLabel && { label: edge.label }),
286
- };
287
- edges.set(edgeId, reconnected);
288
- return reconnected;
289
- });
290
-
291
- export const deleteEdges = (doc: Y.Doc, ids: string[]) =>
292
- doc.transact(() => ids.forEach((id) => getCanvasMaps(doc).edges.delete(id)));
293
-
294
- /** Delete nodes (containers with what's in them), the edges to them, and leave their comments where they were */
295
- export const deleteNodes = (doc: Y.Doc, ids: string[]) =>
296
- doc.transact(() => {
297
- const { nodes, edges, threads } = getCanvasMaps(doc);
298
- const all = Array.from(nodes.values());
299
- const lookup = new Map(all.map((node) => [node.id, node]));
300
- const removed = withDescendants(ids, all);
301
- threads.forEach((thread) => {
302
- const nodeId = thread.get('nodeId') as string | undefined;
303
- const node = nodeId && removed.has(nodeId) ? nodes.get(nodeId) : undefined;
304
- if (!node) return;
305
- const offset = (thread.get('offset') as XYPosition) ?? { x: 0, y: 0 };
306
- const position = getAbsolutePosition(node, lookup);
307
- thread.set('position', { x: position.x + offset.x, y: position.y + offset.y });
308
- thread.delete('nodeId');
309
- thread.delete('offset');
310
- });
311
- removed.forEach((id) => nodes.delete(id));
312
- edges.forEach((edge, id) => {
313
- if (removed.has(edge.source) || removed.has(edge.target)) edges.delete(id);
314
- });
315
- });
316
-
317
- // ---- Comments ----
318
- // Each thread is a nested Y.Map and its messages a Y.Array, so concurrent replies (and resolving while
319
- // someone replies) all merge.
320
-
321
- export const readThreads = (doc: Y.Doc) =>
322
- Array.from(getCanvasMaps(doc).threads.values())
323
- .map((thread) => thread.toJSON() as Thread)
324
- .sort((a, b) => a.createdAt - b.createdAt);
325
-
326
- const anchorEntries = (anchor: CommentAnchor): [string, unknown][] => [
327
- ['position', anchor.position],
328
- ...(anchor.nodeId && anchor.offset
329
- ? ([
330
- ['nodeId', anchor.nodeId],
331
- ['offset', anchor.offset],
332
- ] as [string, unknown][])
333
- : []),
334
- ];
335
-
336
- export const createThread = (doc: Y.Doc, anchor: CommentAnchor, text: string, author: Author) => {
337
- const id = shortId();
338
- const now = Date.now();
339
- const messages = new Y.Array<Message>();
340
- messages.push([{ id: shortId(), text, author, createdAt: now }]);
341
- const thread = new Y.Map<unknown>([
342
- ['id', id],
343
- ...anchorEntries(anchor),
344
- ['author', author],
345
- ['createdAt', now],
346
- ['resolved', false],
347
- ['messages', messages],
348
- ]);
349
- getCanvasMaps(doc).threads.set(id, thread);
350
- return id;
351
- };
352
-
353
- const getThread = (doc: Y.Doc, id: string) => getCanvasMaps(doc).threads.get(id);
354
-
355
- export const replyToThread = (doc: Y.Doc, threadId: string, text: string, author: Author) => {
356
- const messages = getThread(doc, threadId)?.get('messages') as Y.Array<Message> | undefined;
357
- messages?.push([{ id: shortId(), text, author, createdAt: Date.now() }]);
358
- return !!messages;
359
- };
360
-
361
- export const setThreadResolved = (doc: Y.Doc, threadId: string, resolved: boolean) => {
362
- const thread = getThread(doc, threadId);
363
- thread?.set('resolved', resolved);
364
- return !!thread;
365
- };
366
-
367
- /** Move a thread's pin, attaching it to a node (anchor.nodeId) or to the canvas */
368
- export const moveThread = (doc: Y.Doc, threadId: string, anchor: CommentAnchor) => {
369
- const thread = getThread(doc, threadId);
370
- if (!thread) return false;
371
- doc.transact(() => {
372
- thread.set('position', anchor.position);
373
- if (anchor.nodeId && anchor.offset) {
374
- thread.set('nodeId', anchor.nodeId);
375
- thread.set('offset', anchor.offset);
376
- } else {
377
- thread.delete('nodeId');
378
- thread.delete('offset');
379
- }
380
- });
381
- return true;
382
- };
383
-
384
- export const deleteThread = (doc: Y.Doc, threadId: string) => {
385
- const { threads } = getCanvasMaps(doc);
386
- const exists = threads.has(threadId);
387
- threads.delete(threadId);
388
- return exists;
389
- };
390
-
391
- /** Deletes comment threads in one change (one undo brings them all back) */
392
- export const deleteThreads = (doc: Y.Doc, threadIds: string[]) =>
393
- doc.transact(() => threadIds.forEach((id) => deleteThread(doc, id)));
394
-
395
- /** Moves comment threads in one change, e.g. ones that moved with what was dragged */
396
- export const moveThreads = (doc: Y.Doc, moves: { threadId: string; anchor: CommentAnchor }[]) =>
397
- doc.transact(() => moves.forEach(({ threadId, anchor }) => moveThread(doc, threadId, anchor)));
398
-
399
- // ---- Reading a whole canvas ----
400
-
401
- export const readCanvas = (doc: Y.Doc) => {
402
- const { nodes, edges } = getCanvasMaps(doc);
403
- return {
404
- meta: readMeta(doc),
405
- nodes: Array.from(nodes.values()),
406
- edges: Array.from(edges.values()),
407
- threads: readThreads(doc),
408
- };
409
- };
@@ -1,97 +0,0 @@
1
- import type { Edge, Node, XYPosition } from '@xyflow/react';
2
- import { shortId, toSharedNode } from './canvas-doc';
3
- import { findGroupAtPoint, getAbsolutePosition, sizeOf, sortByHierarchy, withDescendants } from './grouping';
4
- import { canGoInContainer } from './node-types';
5
-
6
- /**
7
- * Copying and pasting nodes, within a canvas or between canvases (through the system clipboard). What's copied is
8
- * the selected nodes, everything inside selected containers, and the connections between them. Comments aren't.
9
- */
10
-
11
- /** The clipboard type Studio reads first (other apps get a plain text list of what was copied) */
12
- export const CLIPBOARD_TYPE = 'application/x-eventcatalog-studio+json';
13
- const MARKER = 'eventcatalog-studio/nodes@1';
14
-
15
- export type ClipboardContent = { marker: typeof MARKER; nodes: Node[]; edges: Edge[] };
16
-
17
- /** What's copied when these nodes are selected, or null if there's nothing to copy */
18
- export const copyNodes = (nodes: Node[], edges: Edge[], selectedIds: string[]): ClipboardContent | null => {
19
- if (selectedIds.length === 0) return null;
20
- const lookup = new Map(nodes.map((node) => [node.id, node]));
21
- const ids = withDescendants(selectedIds, nodes);
22
- const copied = sortByHierarchy(nodes.filter((node) => ids.has(node.id))).map((node) => {
23
- const shared = toSharedNode(node);
24
- // Copied without its container: where it is on the canvas
25
- if (shared.parentId && !ids.has(shared.parentId)) {
26
- const { parentId: _, ...rest } = shared;
27
- return { ...rest, position: getAbsolutePosition(node, lookup) };
28
- }
29
- return shared;
30
- });
31
- return {
32
- marker: MARKER,
33
- nodes: copied,
34
- edges: edges.filter((edge) => ids.has(edge.source) && ids.has(edge.target)).map(({ selected: _, ...edge }) => edge),
35
- };
36
- };
37
-
38
- /** Copied content read back from the clipboard, if it's Studio's */
39
- export const readClipboard = (text: string | undefined): ClipboardContent | null => {
40
- if (!text) return null;
41
- try {
42
- const content = JSON.parse(text) as Partial<ClipboardContent>;
43
- return content.marker === MARKER && Array.isArray(content.nodes) && Array.isArray(content.edges)
44
- ? (content as ClipboardContent)
45
- : null;
46
- } catch {
47
- return null;
48
- }
49
- };
50
-
51
- /**
52
- * Copies of the content to add to the canvas, with new ids: centred on a point (e.g. the pointer), or moved by an
53
- * offset from where they were copied. Copies that land in a container on the canvas go inside it.
54
- */
55
- export const pasteNodes = (
56
- content: ClipboardContent,
57
- existing: Node[],
58
- place: { at: XYPosition } | { offset: XYPosition }
59
- ): { nodes: Node[]; edges: Edge[] } => {
60
- const ids = new Map(content.nodes.map((node) => [node.id, `${node.type ?? 'node'}-${shortId()}`]));
61
- const roots = content.nodes.filter((node) => !node.parentId);
62
- const offset = 'offset' in place ? place.offset : offsetToCentre(roots, place.at);
63
-
64
- const lookup = new Map(existing.map((node) => [node.id, node]));
65
- const nodes = content.nodes.map((node): Node => {
66
- const id = ids.get(node.id)!;
67
- if (node.parentId) return { ...node, id, parentId: ids.get(node.parentId) };
68
- const position = { x: node.position.x + offset.x, y: node.position.y + offset.y };
69
- const { width, height } = sizeOf(node);
70
- const container = canGoInContainer(node.type)
71
- ? findGroupAtPoint({ x: position.x + width / 2, y: position.y + height / 2 }, existing)
72
- : undefined;
73
- if (!container) return { ...node, id, position };
74
- const origin = getAbsolutePosition(container, lookup);
75
- return { ...node, id, parentId: container.id, position: { x: position.x - origin.x, y: position.y - origin.y } };
76
- });
77
-
78
- return {
79
- nodes,
80
- edges: content.edges.map((edge) => ({
81
- ...edge,
82
- id: `edge-${shortId()}`,
83
- source: ids.get(edge.source)!,
84
- target: ids.get(edge.target)!,
85
- })),
86
- };
87
- };
88
-
89
- /** How far to move nodes so the middle of them is at a point */
90
- const offsetToCentre = (roots: Node[], at: XYPosition): XYPosition => {
91
- if (roots.length === 0) return { x: 0, y: 0 };
92
- const left = Math.min(...roots.map((node) => node.position.x));
93
- const top = Math.min(...roots.map((node) => node.position.y));
94
- const right = Math.max(...roots.map((node) => node.position.x + sizeOf(node).width));
95
- const bottom = Math.max(...roots.map((node) => node.position.y + sizeOf(node).height));
96
- return { x: at.x - (left + right) / 2, y: at.y - (top + bottom) / 2 };
97
- };
@@ -1,154 +0,0 @@
1
- import { memo, type ReactNode } from 'react';
2
- import { useReactFlow, useStore } from '@xyflow/react';
3
- import { Maximize, MessageCircle, Redo, SendToBack, StickyNote, Undo, ZoomIn, ZoomOut } from 'lucide-react';
4
- import { LEVELS, type Level } from '../levels';
5
-
6
- // The canvas's bottom bar: layout, view, levels of detail, undo, comments and notes
7
-
8
- export const FIT_VIEW_OPTIONS = { padding: { top: '80px', right: '60px', bottom: '100px', left: '60px' } } as const;
9
-
10
- function ControlButton({
11
- label,
12
- onClick,
13
- disabled,
14
- active,
15
- children,
16
- }: {
17
- label: string;
18
- onClick: () => void;
19
- disabled?: boolean;
20
- active?: boolean;
21
- children: ReactNode;
22
- }) {
23
- return (
24
- <button
25
- title={label}
26
- aria-label={label}
27
- onClick={onClick}
28
- disabled={disabled}
29
- className={`rounded-full p-2 transition-colors ${
30
- disabled
31
- ? 'cursor-not-allowed opacity-40'
32
- : active
33
- ? 'bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]'
34
- : 'text-[rgb(var(--ec-icon-color))] hover:bg-[rgb(var(--ec-page-border)/0.5)] hover:text-[rgb(var(--ec-icon-hover))]'
35
- }`}
36
- >
37
- {children}
38
- </button>
39
- );
40
- }
41
-
42
- /** Isolated, so the bar only re-renders when the zoom changes (not on every pan) */
43
- function ZoomLevel() {
44
- const zoom = useStore((state) => Math.round(state.transform[2] * 100));
45
- return <div className="w-12 text-center text-xs font-medium tabular-nums text-[rgb(var(--ec-page-text-muted))]">{zoom}%</div>;
46
- }
47
-
48
- const Divider = () => <div className="mx-1 h-6 w-px bg-[rgb(var(--ec-page-border))]" />;
49
-
50
- function LevelSwitch({
51
- level,
52
- onChange,
53
- unavailable,
54
- }: {
55
- level: Level;
56
- onChange: (level: Level) => void;
57
- unavailable: Partial<Record<Level, string | undefined>>;
58
- }) {
59
- return (
60
- <div role="group" aria-label="Level of detail" className="flex items-center gap-0.5">
61
- {LEVELS.map(({ level: value, description }) => {
62
- const reason = unavailable[value];
63
- const active = value === level;
64
- return (
65
- <button
66
- key={value}
67
- title={`Level ${value}: ${description}${reason ? `. ${reason}` : ''}`}
68
- // Stays focusable when unavailable, so its title can say why
69
- aria-disabled={!!reason}
70
- aria-pressed={active}
71
- onClick={() => !reason && onChange(value)}
72
- className={`h-8 min-w-8 rounded-full px-2 text-xs font-semibold tabular-nums transition-colors ${
73
- reason
74
- ? 'cursor-not-allowed opacity-40'
75
- : active
76
- ? 'bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]'
77
- : 'text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-page-border)/0.5)]'
78
- }`}
79
- >
80
- L{value}
81
- </button>
82
- );
83
- })}
84
- </div>
85
- );
86
- }
87
-
88
- export default memo(function CanvasControls({
89
- level,
90
- onLevelChange,
91
- unavailableLevels,
92
- editable,
93
- onReorder,
94
- canUndo,
95
- canRedo,
96
- onUndo,
97
- onRedo,
98
- commentMode,
99
- onToggleCommentMode,
100
- onAddNote,
101
- }: {
102
- level: Level;
103
- onLevelChange: (level: Level) => void;
104
- unavailableLevels: Partial<Record<Level, string | undefined>>;
105
- /** L1 and L2 are read only views of the canvas */
106
- editable: boolean;
107
- onReorder: () => void;
108
- canUndo: boolean;
109
- canRedo: boolean;
110
- onUndo: () => void;
111
- onRedo: () => void;
112
- commentMode: boolean;
113
- onToggleCommentMode: () => void;
114
- onAddNote: () => void;
115
- }) {
116
- const { fitView, zoomIn, zoomOut } = useReactFlow();
117
-
118
- return (
119
- <div className="absolute bottom-4 left-1/2 z-10 -translate-x-1/2">
120
- <div className="flex items-center gap-1 rounded-full border px-2 py-1 shadow-lg bg-[rgb(var(--ec-card-bg))] border-[rgb(var(--ec-page-border))]">
121
- <ControlButton label="Reorder diagram (the visualiser's layout)" onClick={onReorder} disabled={!editable}>
122
- <SendToBack className="h-4 w-4" />
123
- </ControlButton>
124
- <ControlButton label="Fit view" onClick={() => void fitView({ ...FIT_VIEW_OPTIONS, duration: 500 })}>
125
- <Maximize className="h-4 w-4" />
126
- </ControlButton>
127
- <ControlButton label="Zoom out" onClick={() => void zoomOut({ duration: 200 })}>
128
- <ZoomOut className="h-4 w-4" />
129
- </ControlButton>
130
- <ZoomLevel />
131
- <ControlButton label="Zoom in" onClick={() => void zoomIn({ duration: 200 })}>
132
- <ZoomIn className="h-4 w-4" />
133
- </ControlButton>
134
- <Divider />
135
- <LevelSwitch level={level} onChange={onLevelChange} unavailable={unavailableLevels} />
136
- <Divider />
137
- <ControlButton label="Undo (⌘Z)" onClick={onUndo} disabled={!canUndo}>
138
- <Undo className="h-4 w-4" />
139
- </ControlButton>
140
- <ControlButton label="Redo (⇧⌘Z)" onClick={onRedo} disabled={!canRedo}>
141
- <Redo className="h-4 w-4" />
142
- </ControlButton>
143
- <Divider />
144
- <ControlButton label="Comment (C)" onClick={onToggleCommentMode} active={commentMode} disabled={!editable}>
145
- <MessageCircle className="h-4 w-4" />
146
- </ControlButton>
147
- {/* Notes can be added on every level, and are shown on the level they're added on */}
148
- <ControlButton label={editable ? 'Add sticky note' : `Add sticky note to L${level}`} onClick={onAddNote}>
149
- <StickyNote className="h-4 w-4 text-yellow-500" />
150
- </ControlButton>
151
- </div>
152
- </div>
153
- );
154
- });