@eventcatalog/core 5.0.0-beta.8 → 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 (467) hide show
  1. package/LICENSE +234 -134
  2. package/README.md +5 -5
  3. package/dist/analytics/analytics.cjs +18 -3
  4. package/dist/analytics/analytics.d.cts +1 -1
  5. package/dist/analytics/analytics.d.ts +1 -1
  6. package/dist/analytics/analytics.js +3 -2
  7. package/dist/analytics/log-build.cjs +157 -76
  8. package/dist/analytics/log-build.d.cts +2 -2
  9. package/dist/analytics/log-build.d.ts +2 -2
  10. package/dist/analytics/log-build.js +7 -5
  11. package/dist/analytics/studio-usage.cjs +90 -0
  12. package/dist/analytics/studio-usage.d.cts +5 -0
  13. package/dist/analytics/studio-usage.d.ts +5 -0
  14. package/dist/analytics/studio-usage.js +10 -0
  15. package/dist/analytics/telemetry.cjs +42 -0
  16. package/dist/analytics/telemetry.d.cts +20 -0
  17. package/dist/analytics/telemetry.d.ts +20 -0
  18. package/dist/analytics/telemetry.js +8 -0
  19. package/dist/catalog-runtime.cjs +1 -1
  20. package/dist/{chunk-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
  21. package/dist/{chunk-AWOO63ME.js → chunk-AD7CL772.js} +11 -4
  22. package/dist/chunk-CA6WCQ2T.js +54 -0
  23. package/dist/chunk-CPW6G62V.js +17 -0
  24. package/dist/{chunk-PU25TOOJ.js → chunk-JZN4RSMF.js} +1 -1
  25. package/dist/{chunk-4W55ZOOS.js → chunk-NEAVAFSJ.js} +66 -35
  26. package/dist/{chunk-C6T2AAFR.js → chunk-NIVST4WY.js} +9 -3
  27. package/dist/{chunk-ENAG6UH7.js → chunk-NKNOXQ62.js} +1 -6
  28. package/dist/{chunk-AYNZXUB5.js → chunk-OX6TSVHY.js} +5 -5
  29. package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
  30. package/dist/{chunk-RDIZJ7UT.js → chunk-RE7UTPWH.js} +1 -1
  31. package/dist/chunk-S5GWAUG5.js +29 -0
  32. package/dist/{chunk-T4G3VVZ4.js → chunk-YQGRMDRB.js} +4 -2
  33. package/dist/constants.cjs +1 -1
  34. package/dist/constants.js +1 -1
  35. package/dist/docs/api/02-config.md +57 -7
  36. package/dist/docs/development/00-why-eventcatalog.md +11 -11
  37. package/dist/docs/development/01-fundamentals.md +20 -13
  38. package/dist/docs/development/01-getting-started/configuration-overview.md +2 -2
  39. package/dist/docs/development/01-getting-started/installation.md +9 -1
  40. package/dist/docs/development/_getting-started.mdx +1 -1
  41. package/dist/docs/development/ask-your-architecture/01-intro.md +5 -5
  42. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/01-what-is-eventcatalog-assistant.md +0 -4
  43. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/02-configuration.md +15 -3
  44. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/03-bring-your-own-tools.md +0 -3
  45. package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/_category_.json +1 -1
  46. package/dist/docs/development/ask-your-architecture/02-llms.txt.md +1 -1
  47. package/dist/docs/development/ask-your-architecture/{05-skills/01-introduction.md → 03-install-skills.md} +57 -7
  48. package/dist/docs/development/ask-your-architecture/03-mcp-server/_category_.json +1 -1
  49. package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +86 -33
  50. package/dist/docs/development/ask-your-architecture/03-mcp-server/introduction.md +14 -4
  51. package/dist/docs/development/ask-your-architecture/03-schemas.txt.md +1 -1
  52. package/dist/docs/development/ask-your-architecture/04-agents/_category_.json +1 -1
  53. package/dist/docs/development/authentication/01-introduction.md +2 -23
  54. package/dist/docs/development/authentication/02-enabling-authentication.md +0 -20
  55. package/dist/docs/development/authentication/07-rbac-middleware.md +0 -2
  56. package/dist/docs/development/authentication/providers/03-setting-up-github.md +0 -2
  57. package/dist/docs/development/authentication/providers/03a-setting-up-google.md +0 -2
  58. package/dist/docs/development/authentication/providers/04-setting-up-azure-ad.md +0 -2
  59. package/dist/docs/development/authentication/providers/05-setting-up-okta.md +0 -3
  60. package/dist/docs/development/authentication/providers/06-setting-up-auth0.md +0 -2
  61. package/dist/docs/development/bring-your-own-documentation/01-introduction.md +0 -4
  62. package/dist/docs/development/bring-your-own-documentation/custom-pages/01-introduction.md +0 -3
  63. package/dist/docs/development/bring-your-own-documentation/custom-pages/02-adding-custom-docs.md +0 -17
  64. package/dist/docs/development/bring-your-own-documentation/custom-pages/03-components.md +0 -4
  65. package/dist/docs/development/bring-your-own-documentation/custom-pages/04-owners.md +0 -2
  66. package/dist/docs/development/bring-your-own-documentation/resource-docs/01-introduction.md +1 -3
  67. package/dist/docs/development/bring-your-own-documentation/resource-docs/02-adding-resource-docs.md +1 -4
  68. package/dist/docs/development/bring-your-own-documentation/resource-docs/03-categories.md +0 -2
  69. package/dist/docs/development/bring-your-own-documentation/resource-docs/04-versioning.md +1 -3
  70. package/dist/docs/development/components/components/11-flow.md +17 -0
  71. package/dist/docs/development/components/components/15-nodegraph.md +43 -10
  72. package/dist/docs/development/components/components/18-remote-schema.md +1 -4
  73. package/dist/docs/development/components/custom-components/07-reference.md +14 -0
  74. package/dist/docs/development/customization/02-themes.md +0 -5
  75. package/dist/docs/development/customization/03-search.md +1 -1
  76. package/dist/docs/development/customization/05-custom-pages-and-api-routes/01-introduction.md +0 -2
  77. package/dist/docs/development/customization/05-custom-pages-and-api-routes/04-reference.md +7 -1
  78. package/dist/docs/development/customization/05-resource-sidebar.md +1 -1
  79. package/dist/docs/development/customization/09-embed-eventcatalog.md +20 -25
  80. package/dist/docs/development/deployment/hosting-options.md +7 -2
  81. package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/05-fix-common-problems.md +16 -0
  82. package/dist/docs/development/developer-tools/eventcatalog-linter/reference/03-rules.md +31 -1
  83. package/dist/docs/development/developer-tools/eventcatalog-linter/reference/05-supported-resources.md +1 -1
  84. package/dist/docs/development/governance/architecture-change-detection/01-introduction.md +0 -10
  85. package/dist/docs/development/governance/architecture-change-detection/06-pipeline-gates.md +0 -4
  86. package/dist/docs/development/guides/97-architecture-graph.md +1 -1
  87. package/dist/docs/development/guides/99-adding-analytics.md +0 -7
  88. package/dist/docs/development/guides/domains/03-model-subdomains.md +2 -2
  89. package/dist/docs/development/guides/domains/04-add-resources-to-domains/01-add-systems-to-domains.md +3 -1
  90. package/dist/docs/development/guides/domains/04-add-resources-to-domains/02-add-services-to-domains.md +5 -1
  91. package/dist/docs/development/guides/domains/04-add-resources-to-domains/03-add-messages-to-domains.md +2 -2
  92. package/dist/docs/development/guides/domains/04-add-resources-to-domains/05-add-entities-to-domains.md +1 -1
  93. package/dist/docs/development/guides/domains/04-add-resources-to-domains/06-domain-resources-page.md +40 -0
  94. package/dist/docs/development/guides/domains/06-visualize-domains/01-domain-diagram.md +89 -0
  95. package/dist/docs/development/guides/domains/06-visualize-domains/_category_.json +1 -2
  96. package/dist/docs/development/guides/domains/09-reference.md +4 -4
  97. package/dist/docs/development/guides/owners/automated-teams-and-users/create-your-own-connector.md +0 -2
  98. package/dist/docs/development/guides/owners/automated-teams-and-users/sync-from-github.md +0 -3
  99. package/dist/docs/development/guides/owners/automated-teams-and-users/sync-from-microsoft-entra.md +0 -2
  100. package/dist/docs/development/guides/resources/adrs/03-linking-resources.md +8 -0
  101. package/dist/docs/development/guides/resources/adrs/06-reference.md +4 -4
  102. package/dist/docs/development/guides/resources/agents/11-reference.md +1 -1
  103. package/dist/docs/development/guides/resources/data/04-reference.md +1 -1
  104. package/dist/docs/development/guides/resources/data-products/07-reference.md +1 -1
  105. package/dist/docs/development/guides/resources/entities/05-entity-maps.md +1 -1
  106. package/dist/docs/development/guides/resources/entities/06-reference.md +2 -2
  107. package/dist/docs/development/guides/resources/flows/01-introduction.md +11 -1
  108. package/dist/docs/development/guides/resources/flows/03-model-flow-steps.md +65 -6
  109. package/dist/docs/development/guides/resources/flows/05-explore-flows.md +112 -0
  110. package/dist/docs/development/guides/resources/flows/06-reference.md +25 -4
  111. package/dist/docs/development/guides/resources/messages/01-what-are-messages.md +19 -0
  112. package/dist/docs/development/guides/resources/messages/04-message-channels/01-introduction.md +16 -0
  113. package/dist/docs/development/guides/resources/messages/04-message-channels/05-reference.md +2 -1
  114. package/dist/docs/development/guides/resources/messages/10-reference.md +6 -3
  115. package/dist/docs/development/guides/resources/schemas/04-explore-schemas/01-schema-explorer.md +2 -2
  116. package/dist/docs/development/guides/resources/schemas/04-explore-schemas/02-fields-explorer.md +0 -9
  117. package/dist/docs/development/guides/resources/schemas/05-schema-api.md +0 -3
  118. package/dist/docs/development/guides/resources/schemas/06-schema-mcp.md +11 -3
  119. package/dist/docs/development/guides/resources/services/04-add-resources-to-services/03-add-entities-to-services.md +1 -1
  120. package/dist/docs/development/guides/resources/services/05-resources-page.md +50 -0
  121. package/dist/docs/development/guides/resources/services/08-reference.md +2 -1
  122. package/dist/docs/development/guides/systems/01-introduction.md +1 -1
  123. package/dist/docs/development/guides/systems/02-create-system.md +2 -2
  124. package/dist/docs/development/guides/systems/03-add-resources-to-systems.md +35 -9
  125. package/dist/docs/development/guides/systems/04-model-relationships-and-actors.md +5 -3
  126. package/dist/docs/development/guides/systems/05-system-context-maps.md +39 -22
  127. package/dist/docs/development/guides/systems/06-reference.md +4 -4
  128. package/dist/docs/development/license/01-license-file.md +151 -0
  129. package/dist/docs/development/license/_category_.json +12 -0
  130. package/dist/docs/development/license-keys/_category_.json +3 -3
  131. package/dist/docs/development/license-keys/integrations.md +7 -37
  132. package/dist/docs/development/license-keys/license-validation.md +4 -0
  133. package/dist/docs/development/license-keys/overview.md +5 -1
  134. package/dist/docs/development/license-keys/plans.md +4 -2
  135. package/dist/docs/development/studio/01-introduction.md +60 -0
  136. package/dist/docs/development/studio/02-create-a-canvas.md +103 -0
  137. package/dist/docs/development/studio/03-set-up-persistent-storage.md +59 -0
  138. package/dist/docs/development/studio/04-collaborate-on-a-canvas.md +78 -0
  139. package/dist/docs/development/studio/05-connect-your-agent.md +102 -0
  140. package/dist/docs/development/studio/06-run-studio-in-production.md +54 -0
  141. package/dist/docs/development/studio/07-reference.md +294 -0
  142. package/dist/docs/development/studio/_category_.json +12 -0
  143. package/dist/docs/development/upgrading/v5.md +345 -0
  144. package/dist/docs/federation/00-overview.md +2 -8
  145. package/dist/docs/federation/01-first-federation.md +6 -18
  146. package/dist/docs/federation/explanation/how-it-works.md +0 -4
  147. package/dist/docs/federation/explanation/lockfile-and-cache.md +0 -4
  148. package/dist/docs/federation/explanation/mvp-status-and-feedback.md +0 -4
  149. package/dist/docs/federation/explanation/ownership-and-references.md +0 -4
  150. package/dist/docs/federation/how-to/configure-github-sources.md +6 -5
  151. package/dist/docs/federation/how-to/configure-validation-rules.md +0 -4
  152. package/dist/docs/federation/how-to/run-in-ci.md +2 -9
  153. package/dist/docs/federation/how-to/share-assets-and-components.md +0 -4
  154. package/dist/docs/federation/how-to/use-local-sources.md +0 -4
  155. package/dist/docs/federation/legacy-federation/00-introduction.md +4 -22
  156. package/dist/docs/federation/legacy-federation/01-installation.md +7 -58
  157. package/dist/docs/federation/legacy-federation/02-plugin-configuration.md +3 -21
  158. package/dist/docs/federation/legacy-federation/03-examples.md +3 -5
  159. package/dist/docs/federation/legacy-federation/04-configuration.md +3 -8
  160. package/dist/docs/federation/legacy-federation/05-setup-team-catalog.md +3 -6
  161. package/dist/docs/federation/reference/cli.md +2 -8
  162. package/dist/docs/federation/reference/configuration.md +0 -4
  163. package/dist/docs/federation/reference/diagnostic-rules.md +0 -5
  164. package/dist/docs/federation/reference/generated-output.md +0 -4
  165. package/dist/docs/federation/reference/supported-content.md +0 -4
  166. package/dist/docs/federation/reference/troubleshooting.md +0 -14
  167. package/dist/docs/plugins/02-generators.md +1 -1
  168. package/dist/docs/plugins/amazon-apigateway/00-intro.md +3 -28
  169. package/dist/docs/plugins/amazon-apigateway/01-installation.md +4 -56
  170. package/dist/docs/plugins/amazon-apigateway/02-plugin-configuration.md +1 -6
  171. package/dist/docs/plugins/apicurio/00-intro.md +2 -31
  172. package/dist/docs/plugins/apicurio/01-installation.md +2 -26
  173. package/dist/docs/plugins/apicurio/02-plugin-configuration.md +5 -13
  174. package/dist/docs/plugins/apicurio/03-features.md +1 -3
  175. package/dist/docs/plugins/asyncapi/00-intro.md +2 -28
  176. package/dist/docs/plugins/asyncapi/01-installation.md +3 -54
  177. package/dist/docs/plugins/asyncapi/02-plugin-configuration.md +2 -1
  178. package/dist/docs/plugins/aws-glue-registry/00-intro.md +4 -32
  179. package/dist/docs/plugins/aws-glue-registry/00a-installation.md +3 -53
  180. package/dist/docs/plugins/azure-schema-registry/00-intro.md +4 -32
  181. package/dist/docs/plugins/azure-schema-registry/01-installation.md +6 -33
  182. package/dist/docs/plugins/azure-schema-registry/02-plugin-configuration.md +1 -14
  183. package/dist/docs/plugins/azure-schema-registry/04-examples.md +0 -2
  184. package/dist/docs/plugins/backstage/00-overview.md +1 -7
  185. package/dist/docs/plugins/backstage/01-get-started.md +6 -15
  186. package/dist/docs/plugins/backstage/06-configuration.md +2 -8
  187. package/dist/docs/plugins/confluent-schema-registry/00-intro.md +2 -29
  188. package/dist/docs/plugins/confluent-schema-registry/01-installation.md +2 -26
  189. package/dist/docs/plugins/eventbridge/00-intro.md +0 -4
  190. package/dist/docs/plugins/eventbridge/00a-installation.md +1 -87
  191. package/dist/docs/plugins/eventbridge/03-api.md +2 -20
  192. package/dist/docs/plugins/github/00-intro.md +0 -31
  193. package/dist/docs/plugins/github/01-installation.md +2 -34
  194. package/dist/docs/plugins/graphql/00-intro.md +0 -30
  195. package/dist/docs/plugins/graphql/01-installation.md +1 -53
  196. package/dist/docs/plugins/graphql/02-plugin-configuration.md +0 -1
  197. package/dist/docs/plugins/hookdeck/01-intro.md +1 -1
  198. package/dist/docs/plugins/openapi/00-intro.md +0 -30
  199. package/dist/docs/plugins/openapi/01-installation.md +1 -57
  200. package/dist/docs/plugins/openapi/02-plugin-configuration.md +0 -5
  201. package/dist/docs/plugins/openapi/03-features.md +0 -4
  202. package/dist/eventcatalog-config-file-utils.cjs +34 -41
  203. package/dist/eventcatalog-config-file-utils.d.cts +56 -58
  204. package/dist/eventcatalog-config-file-utils.d.ts +56 -58
  205. package/dist/eventcatalog-config-file-utils.js +1 -1
  206. package/dist/eventcatalog.cjs +408 -281
  207. package/dist/eventcatalog.config.d.cts +26 -15
  208. package/dist/eventcatalog.config.d.ts +26 -15
  209. package/dist/eventcatalog.js +46 -27
  210. package/dist/features.cjs +36 -49
  211. package/dist/features.d.cts +1 -3
  212. package/dist/features.d.ts +1 -3
  213. package/dist/features.js +4 -6
  214. package/dist/federation/federate.cjs +37 -44
  215. package/dist/federation/federate.js +5 -5
  216. package/dist/federation/source-provider.js +2 -2
  217. package/dist/generate.cjs +37 -42
  218. package/dist/generate.js +4 -4
  219. package/dist/resolve-catalog-dependencies.cjs +34 -41
  220. package/dist/resolve-catalog-dependencies.js +2 -2
  221. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
  222. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
  223. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
  224. package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
  225. package/dist/runtime-dependencies/manifest.json +5 -1
  226. package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
  227. package/dist/utils/child-process-signals.cjs +53 -0
  228. package/dist/utils/child-process-signals.d.cts +19 -0
  229. package/dist/utils/child-process-signals.d.ts +19 -0
  230. package/dist/utils/child-process-signals.js +6 -0
  231. package/dist/utils/cli-logger.cjs +1 -1
  232. package/dist/utils/cli-logger.js +2 -2
  233. package/dist/utils/license-status.cjs +11 -4
  234. package/dist/utils/license-status.d.cts +3 -2
  235. package/dist/utils/license-status.d.ts +3 -2
  236. package/dist/utils/license-status.js +1 -1
  237. package/eventcatalog/astro.config.mjs +21 -1
  238. package/eventcatalog/integrations/config-loader.mjs +56 -58
  239. package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
  240. package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
  241. package/eventcatalog/integrations/storage.mjs +20 -0
  242. package/eventcatalog/integrations/studio-server.mjs +4 -1
  243. package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
  244. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
  245. package/eventcatalog/src/components/Homepage/CatalogDashboard.astro +342 -0
  246. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +60 -4
  247. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
  248. package/eventcatalog/src/components/NavigationProgress.astro +7 -0
  249. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
  250. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
  251. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
  252. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
  253. package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
  254. package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
  255. package/eventcatalog/src/components/Search/search-utils.ts +26 -0
  256. package/eventcatalog/src/components/Seo.astro +5 -1
  257. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +53 -48
  258. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +183 -178
  259. package/eventcatalog/src/components/Settings/LicenseSettings.tsx +140 -31
  260. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +40 -35
  261. package/eventcatalog/src/components/Settings/LogoUpload.tsx +1 -1
  262. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +9 -8
  263. package/eventcatalog/src/components/Settings/Row.tsx +7 -4
  264. package/eventcatalog/src/components/Settings/SettingsShared.tsx +3 -3
  265. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +37 -76
  266. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
  267. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +122 -1
  268. package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
  269. package/eventcatalog/src/components/TrialBadge.astro +20 -9
  270. package/eventcatalog/src/content.config.ts +19 -2
  271. package/eventcatalog/src/features/auth/error.astro +4 -2
  272. package/eventcatalog/src/features/auth/login.astro +2 -1
  273. package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +21 -1
  274. package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
  275. package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
  276. package/eventcatalog/src/features/integrations/eventcatalog-features.ts +13 -7
  277. package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
  278. package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +3 -3
  279. package/eventcatalog/src/features/mcp/apps/canvas/styles.css +3 -1
  280. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +7 -5
  281. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +123 -98
  282. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +118 -101
  283. package/eventcatalog/src/features/mcp/mcp-server.ts +20 -4
  284. package/eventcatalog/src/features/server/start.ts +25 -0
  285. package/eventcatalog/src/features/storage/database.ts +88 -0
  286. package/eventcatalog/src/features/storage/migrations.ts +85 -0
  287. package/eventcatalog/src/features/storage/sql/database.sql +5 -0
  288. package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
  289. package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
  290. package/eventcatalog/src/features/storage/sql.ts +28 -0
  291. package/eventcatalog/src/features/studio/agent-choreography.ts +13 -10
  292. package/eventcatalog/src/features/studio/agents.ts +28 -0
  293. package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
  294. package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
  295. package/eventcatalog/src/features/studio/api/client.ts +40 -0
  296. package/eventcatalog/src/features/studio/canvas-actions.ts +160 -34
  297. package/eventcatalog/src/features/studio/canvas-preview.ts +1 -2
  298. package/eventcatalog/src/features/studio/catalog.ts +16 -14
  299. package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +274 -105
  300. package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +15 -14
  301. package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +272 -119
  302. package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +1 -2
  303. package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
  304. package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
  305. package/eventcatalog/src/features/studio/components/JoinForm.tsx +101 -26
  306. package/eventcatalog/src/features/studio/components/LeftPanel.tsx +122 -230
  307. package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
  308. package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
  309. package/eventcatalog/src/features/studio/components/Presence.tsx +52 -45
  310. package/eventcatalog/src/features/studio/components/ShareDialog.tsx +277 -141
  311. package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
  312. package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +1314 -385
  313. package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
  314. package/eventcatalog/src/features/studio/document-name.ts +7 -0
  315. package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
  316. package/eventcatalog/src/features/studio/hooks/presence-sender.ts +19 -2
  317. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +9 -3
  318. package/eventcatalog/src/features/studio/hooks/use-comments.ts +2 -2
  319. package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +8 -3
  320. package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +244 -32
  321. package/eventcatalog/src/features/studio/identity.ts +25 -1
  322. package/eventcatalog/src/features/studio/limits.ts +2 -0
  323. package/eventcatalog/src/features/studio/pages/[id].astro +42 -16
  324. package/eventcatalog/src/features/studio/pages/index.astro +596 -373
  325. package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +12 -15
  326. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +8 -23
  327. package/eventcatalog/src/features/studio/server/runtime.ts +150 -46
  328. package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
  329. package/eventcatalog/src/features/studio/server/storage.ts +53 -73
  330. package/eventcatalog/src/features/studio/webmcp-relay/LICENSE +21 -0
  331. package/eventcatalog/src/features/studio/webmcp-relay/README.md +5 -0
  332. package/eventcatalog/src/features/studio/webmcp-relay/embed.js +2 -0
  333. package/eventcatalog/src/features/studio/webmcp-relay/widget.html +10 -0
  334. package/eventcatalog/src/features/studio/webmcp-relay/widget.js +1 -0
  335. package/eventcatalog/src/features/tools/catalog-tools.ts +3 -1
  336. package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
  337. package/eventcatalog/src/layouts/SettingsLayout.astro +2 -2
  338. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +3 -2
  339. package/eventcatalog/src/pages/_index.astro +5 -342
  340. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +2 -3
  341. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +0 -7
  342. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -1
  343. package/eventcatalog/src/pages/diagrams/[id]/[version]/_index.data.ts +0 -7
  344. package/eventcatalog/src/pages/diagrams/[id]/[version]/embed.astro +1 -0
  345. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +1 -0
  346. package/eventcatalog/src/pages/directory/[type]/_index.data.ts +5 -7
  347. package/eventcatalog/src/pages/directory/[type]/index.astro +3 -1
  348. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -7
  349. package/eventcatalog/src/pages/discover/[type]/index.astro +3 -1
  350. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +0 -7
  351. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -0
  352. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +0 -7
  353. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +1 -0
  354. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +0 -7
  355. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +3 -1
  356. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/_[filename].data.ts +0 -7
  357. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +0 -7
  358. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -0
  359. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +1 -0
  360. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/_[...filename].data.ts +0 -4
  361. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.ts +0 -7
  362. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/index.astro +1 -0
  363. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +3 -1
  364. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/_[filename].data.ts +0 -7
  365. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
  366. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +2 -3
  367. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +0 -7
  368. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +1 -0
  369. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +1 -0
  370. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/_[filename].data.ts +0 -7
  371. package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +0 -7
  372. package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +2 -1
  373. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/_index.data.ts +0 -7
  374. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -0
  375. package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +0 -7
  376. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +1 -0
  377. package/eventcatalog/src/pages/docs/custom/[...path]/_index.data.ts +0 -7
  378. package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +1 -0
  379. package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +1 -1
  380. package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +1 -0
  381. package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +1 -1
  382. package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +1 -1
  383. package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +1 -1
  384. package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +1 -1
  385. package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +0 -7
  386. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +1 -0
  387. package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +0 -7
  388. package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -0
  389. package/eventcatalog/src/pages/index.astro +4 -2
  390. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +0 -7
  391. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +9 -1
  392. package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +0 -7
  393. package/eventcatalog/src/pages/schemas/explorer/index.astro +4 -2
  394. package/eventcatalog/src/pages/settings/license.astro +9 -1
  395. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/_index.data.ts +0 -7
  396. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +1 -0
  397. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +0 -7
  398. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +0 -7
  399. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +1 -0
  400. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/_index.data.ts +0 -7
  401. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +1 -0
  402. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +1 -0
  403. package/eventcatalog/src/pages/visualiser/[type]/[id]/_index.data.ts +38 -0
  404. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +18 -41
  405. package/eventcatalog/src/pages/visualiser/designs/[id]/_index.data.ts +0 -7
  406. package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +2 -1
  407. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +0 -7
  408. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +1 -0
  409. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/index.astro +1 -0
  410. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/_index.data.ts +0 -7
  411. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/index.astro +1 -0
  412. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/_index.data.ts +0 -7
  413. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/index.astro +1 -0
  414. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/index.astro +1 -0
  415. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
  416. package/eventcatalog/src/utils/collections/flows.ts +20 -7
  417. package/eventcatalog/src/utils/collections/resource-owners.ts +8 -2
  418. package/eventcatalog/src/utils/collections/resources.ts +2 -0
  419. package/eventcatalog/src/utils/collections/util.ts +8 -4
  420. package/eventcatalog/src/utils/git.ts +11 -0
  421. package/eventcatalog/src/utils/homepage-dashboard.ts +143 -0
  422. package/eventcatalog/src/utils/license.ts +1 -1
  423. package/eventcatalog/src/utils/link-validation.ts +25 -19
  424. package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
  425. package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
  426. package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +4 -2
  427. package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
  428. package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
  429. package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
  430. package/eventcatalog/src/utils/page-loaders/hybrid-page.ts +4 -15
  431. package/eventcatalog/src/utils/page-title.ts +15 -0
  432. package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
  433. package/eventcatalog/src/utils/trial.ts +13 -0
  434. package/package.json +11 -11
  435. package/dist/docs/development/ask-your-architecture/05-skills/02-installation.md +0 -60
  436. package/dist/docs/development/ask-your-architecture/05-skills/_category_.json +0 -11
  437. package/dist/docs/development/ask-your-architecture/06-slack-integration/01-introduction.md +0 -63
  438. package/dist/docs/development/ask-your-architecture/06-slack-integration/02-slack-app-setup.md +0 -154
  439. package/dist/docs/development/ask-your-architecture/06-slack-integration/03-installation.md +0 -169
  440. package/dist/docs/development/ask-your-architecture/06-slack-integration/04-deployment.md +0 -236
  441. package/dist/docs/development/ask-your-architecture/06-slack-integration/05-usage.md +0 -140
  442. package/dist/docs/development/ask-your-architecture/06-slack-integration/06-troubleshooting.md +0 -268
  443. package/dist/docs/development/ask-your-architecture/06-slack-integration/_category_.json +0 -13
  444. package/dist/docs/development/guides/domains/06-visualize-domains/01-domain-integration-map.md +0 -40
  445. package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +0 -3
  446. package/eventcatalog/src/features/studio/canvas-doc.ts +0 -409
  447. package/eventcatalog/src/features/studio/clipboard.ts +0 -97
  448. package/eventcatalog/src/features/studio/components/CanvasControls.tsx +0 -154
  449. package/eventcatalog/src/features/studio/components/Comments.tsx +0 -770
  450. package/eventcatalog/src/features/studio/components/ContainerChooser.tsx +0 -145
  451. package/eventcatalog/src/features/studio/components/DragGhost.tsx +0 -115
  452. package/eventcatalog/src/features/studio/components/Picture.tsx +0 -21
  453. package/eventcatalog/src/features/studio/components/PropertiesPanel.tsx +0 -212
  454. package/eventcatalog/src/features/studio/components/canvas-nodes.tsx +0 -255
  455. package/eventcatalog/src/features/studio/components/icons.ts +0 -53
  456. package/eventcatalog/src/features/studio/components/status.ts +0 -51
  457. package/eventcatalog/src/features/studio/edges.ts +0 -56
  458. package/eventcatalog/src/features/studio/flow-state.ts +0 -92
  459. package/eventcatalog/src/features/studio/grouping.ts +0 -226
  460. package/eventcatalog/src/features/studio/layout.ts +0 -42
  461. package/eventcatalog/src/features/studio/levels.ts +0 -282
  462. package/eventcatalog/src/features/studio/node-types.ts +0 -205
  463. package/eventcatalog/src/features/studio/pages/new.astro +0 -24
  464. package/eventcatalog/src/features/studio/pages/wake.ts +0 -15
  465. package/eventcatalog/src/features/studio/placement.ts +0 -197
  466. package/eventcatalog/src/features/studio/studio.css +0 -273
  467. package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
@@ -15,7 +15,6 @@ import {
15
15
  import {
16
16
  addNodes,
17
17
  buildNode,
18
- canvasDocumentName,
19
18
  connectNodes,
20
19
  deleteEdges,
21
20
  deleteNodes as deleteNodesInDoc,
@@ -24,7 +23,9 @@ import {
24
23
  fitGroupToChildren,
25
24
  getCanvasMaps,
26
25
  moveNode,
26
+ placeNode,
27
27
  changesNodesOrEdges,
28
+ getStructureKey,
28
29
  getCanvasStatus,
29
30
  readMeta,
30
31
  reopenIfDecided,
@@ -33,16 +34,41 @@ import {
33
34
  setMeta,
34
35
  type Author,
35
36
  type CanvasStatus,
37
+ type DiagramLevels,
38
+ type LevelPlace,
39
+ readLevelLayout,
40
+ setLevelPlaces,
36
41
  type StatusChange,
37
- setNodeParent,
42
+ setNodeParents,
38
43
  type RenderedSizes,
39
44
  updateNodeData as updateNodeDataInDoc,
40
- } from '../canvas-doc';
45
+ keepDragged,
46
+ LOCAL_EDGE_KEYS,
47
+ LOCAL_NODE_KEYS,
48
+ patchShared,
49
+ withPositions,
50
+ createSmoother,
51
+ containerFor,
52
+ toRelativePosition,
53
+ type NodePreview,
54
+ type LayoutResult,
55
+ canGoInContainer,
56
+ getNodeDefinition,
57
+ isGroupType,
58
+ createHotspot as createHotspotInDoc,
59
+ updateHotspot as updateHotspotInDoc,
60
+ deleteHotspot as deleteHotspotInDoc,
61
+ readHotspots,
62
+ getLock,
63
+ setLock,
64
+ type Hotspot,
65
+ type Editing,
66
+ addThreads,
67
+ addHotspots,
68
+ type ModelCanvasContent,
69
+ } from '@eventcatalog/visualiser/studio';
70
+ import { CANVAS_DELETED_REASON, canvasDocumentName } from '../document-name';
41
71
  import { applyLayout } from '../agent-choreography';
42
- import { keepDragged, LOCAL_EDGE_KEYS, LOCAL_NODE_KEYS, patchShared, withPositions } from '../flow-state';
43
- import { findGroupAtPoint, toRelativePosition, type NodePreview } from '../grouping';
44
- import type { LayoutResult } from '../layout';
45
- import { canGoInContainer, getNodeDefinition, isGroupType } from '../node-types';
46
72
  import { startToolSync, type ToolSync } from '../tool-sync';
47
73
  import { createPresenceSender, type PresenceSender } from './presence-sender';
48
74
  import { createPresenceStore, type PresenceStore } from './presence-store';
@@ -66,6 +92,14 @@ const LOCAL_ORIGIN = Symbol('studio-local');
66
92
  export const BROWSER_AGENT_ORIGIN = Symbol('studio-browser-agent');
67
93
  /** Status changes: synced, but not undone (undo takes back edits, not decisions about the canvas) */
68
94
  const STATUS_ORIGIN = Symbol('studio-status');
95
+ /** A diagram file opened into a new canvas: what it starts with, synced, not undone (nothing to go back to) */
96
+ const OPEN_ORIGIN = Symbol('studio-open');
97
+ /** Where L1 and L2 put what's new on them, kept so it stays there: synced, not undone (nobody did it) */
98
+ const LEVEL_PLACEMENT_ORIGIN = Symbol('studio-level-placement');
99
+
100
+ const NO_LEVEL_LAYOUTS = { 1: new Map(), 2: new Map() } as const;
101
+ /** What someone changing a note or text shows the others as they change it (see `editing` in their presence) */
102
+ const EDITED_FIELDS = ['text', 'fontSize', 'angle'] as const;
69
103
 
70
104
  export type Status = 'connecting' | 'connected' | 'disconnected';
71
105
  export type Transport = 'websocket' | 'tools';
@@ -89,6 +123,7 @@ export function useStudioFlow({
89
123
  name,
90
124
  color,
91
125
  picture,
126
+ agent,
92
127
  syncViaTools,
93
128
  onLayout,
94
129
  onContainerResize,
@@ -100,6 +135,8 @@ export function useStudioFlow({
100
135
  color: string;
101
136
  /** From your sign-in provider, when you're signed in */
102
137
  picture?: string;
138
+ /** You're an AI agent here (e.g. a chat's canvas view, which joins as the chat's agent) */
139
+ agent?: boolean;
103
140
  /** Sync through MCP tool calls when the WebSocket can't connect (e.g. inside a chat's sandbox) */
104
141
  syncViaTools?: ToolSync;
105
142
  /** Someone laid the canvas out (us included): called as the new positions arrive, so they can glide there */
@@ -120,10 +157,25 @@ export function useStudioFlow({
120
157
  const [doc, setDoc] = useState<Y.Doc | null>(null);
121
158
  const [history, setHistory] = useState({ canUndo: false, canRedo: false });
122
159
  const [title, setTitle] = useState<string | undefined>();
160
+ // L1 and L2 of the diagram the canvas was opened from, if it was
161
+ const [diagramLevels, setDiagramLevels] = useState<DiagramLevels | undefined>();
162
+ // What's on the canvas and how it's connected (getStructureKey): worked out when the document's nodes or edges
163
+ // change, which drags don't do (they're in presence until the drop)
164
+ const [structureKey, setStructureKey] = useState('');
165
+ // Where things are on L1 and L2, as people arranged them
166
+ const [levelLayouts, setLevelLayouts] = useState<Record<1 | 2, ReadonlyMap<string, LevelPlace>>>(NO_LEVEL_LAYOUTS);
123
167
  const [canvasStatus, setCanvasStatusState] = useState<{ status: CanvasStatus; history: StatusChange[] }>(NEW_CANVAS_STATUS);
124
168
  const [transport, setTransport] = useState<Transport>('websocket');
169
+ // Someone deleted the canvas: the server closed it for everyone, and won't open it again
170
+ const [deleted, setDeleted] = useState(false);
171
+ // What's flagged on the canvas (risks, problems, the key parts), on nodes or at places
172
+ const [hotspots, setHotspots] = useState<Hotspot[]>([]);
173
+ // Who locked the canvas (for everyone on it), if it's locked
174
+ const [lockedBy, setLockedBy] = useState<string | null>(null);
175
+ // The server's copy of the canvas has arrived (what's on it is known, e.g. that it's empty)
176
+ const [synced, setSynced] = useState(false);
125
177
  const user = useRef<Author>({ name, color });
126
- user.current = { name, color, ...(picture && { picture }) };
178
+ user.current = { name, color, ...(picture && { picture }), ...(agent && { agent }) };
127
179
  const onLayoutRef = useRef(onLayout);
128
180
  onLayoutRef.current = onLayout;
129
181
  const onContainerResizeRef = useRef(onContainerResize);
@@ -136,16 +188,41 @@ export function useStudioFlow({
136
188
  setEdges([]);
137
189
 
138
190
  const doc = new Y.Doc();
139
- const { nodes: yNodes, edges: yEdges, threads: yThreads, meta: yMeta } = getCanvasMaps(doc);
191
+ const {
192
+ nodes: yNodes,
193
+ edges: yEdges,
194
+ threads: yThreads,
195
+ meta: yMeta,
196
+ levelLayouts: yLevelLayouts,
197
+ hotspots: yHotspots,
198
+ } = getCanvasMaps(doc);
199
+ const syncLevelLayouts = (event: Y.YMapEvent<LevelPlace>) => {
200
+ const changed = new Set([...event.keysChanged].map((key) => Number(key.split(':')[0]) as 1 | 2));
201
+ setLevelLayouts((current) => {
202
+ const next = { ...current };
203
+ changed.forEach((level) => (next[level] = readLevelLayout(doc, level)));
204
+ return next;
205
+ });
206
+ };
207
+ yLevelLayouts.observe(syncLevelLayouts);
208
+ setLevelLayouts(NO_LEVEL_LAYOUTS);
140
209
  const syncMeta = (event: Y.YMapEvent<unknown>) => {
141
210
  const meta = readMeta(doc);
142
211
  setTitle(meta.title);
212
+ if (event.keysChanged.has('diagramLevels')) setDiagramLevels(meta.diagramLevels);
143
213
  if (event.keysChanged.has('status') || event.keysChanged.has('statusHistory'))
144
214
  setCanvasStatusState({ status: getCanvasStatus(meta), history: meta.statusHistory ?? [] });
145
215
  if (event.keysChanged.has('layoutAt')) onLayoutRef.current?.();
216
+ if (event.keysChanged.has('lockedBy')) setLockedBy(getLock(doc));
146
217
  };
147
218
  yMeta.observe(syncMeta);
219
+ setLockedBy(null);
220
+ const syncHotspots = () => setHotspots(readHotspots(doc));
221
+ yHotspots.observe(syncHotspots);
222
+ setHotspots([]);
148
223
  setTitle(undefined);
224
+ setStructureKey('');
225
+ setDiagramLevels(undefined);
149
226
  setCanvasStatusState(NEW_CANVAS_STATUS);
150
227
  // Changing an accepted (or rejected) canvas here makes it a draft again, for everyone. Changes from others arrive
151
228
  // already reopened by whoever made them.
@@ -166,6 +243,12 @@ export function useStudioFlow({
166
243
  );
167
244
  };
168
245
  doc.on('afterTransaction', reopenOnEdit);
246
+ const syncStructure = (transaction: Y.Transaction) => {
247
+ // Drops and resizes here only move and size things (putting something in a container isn't LOCAL_ORIGIN)
248
+ if (transaction.origin !== LOCAL_ORIGIN && changesNodesOrEdges(transaction))
249
+ setStructureKey(getStructureKey(Array.from(yNodes.values()), Array.from(yEdges.values())));
250
+ };
251
+ doc.on('afterTransaction', syncStructure);
169
252
  // One presence for whichever connection is used
170
253
  const awareness = new Awareness(doc);
171
254
  awareness.setLocalStateField('user', user.current);
@@ -180,6 +263,15 @@ export function useStudioFlow({
180
263
  let fallback: ReturnType<typeof setTimeout> | undefined;
181
264
  // Declared before the provider: it reports its first status while it's being created
182
265
  let socketStatus: Status = 'connecting';
266
+ let canvasDeleted = false;
267
+ const onCanvasDeleted = () => {
268
+ canvasDeleted = true;
269
+ clearTimeout(fallback);
270
+ stopToolSync?.();
271
+ provider.disconnect();
272
+ setDeleted(true);
273
+ setStatus('disconnected');
274
+ };
183
275
  // Some views can't use the WebSocket (a chat's sandbox may block it, or open it without the person's session):
184
276
  // sync through tool calls instead
185
277
  const syncThroughTools = () => {
@@ -201,23 +293,31 @@ export function useStudioFlow({
201
293
  // Open isn't connected until the server lets us in (onAuthenticated)
202
294
  if (!stopToolSync) setStatus(status === 'connected' && !letIn ? 'connecting' : (status as Status));
203
295
  },
296
+ onSynced: ({ state }) => setSynced(state),
204
297
  onAuthenticated: () => {
205
298
  socketConnected = letIn = true;
206
299
  if (!stopToolSync) setStatus('connected');
207
300
  },
301
+ // Deleted while it was open here
302
+ onClose: ({ event }) => {
303
+ if (event.reason === CANVAS_DELETED_REASON) onCanvasDeleted();
304
+ },
208
305
  // Not signed in (or the session ran out): a chat's view syncs through its tools, a page shows it's offline
209
- // (coming back to the tab tries again, e.g. after signing in again elsewhere)
210
- onAuthenticationFailed: () => {
306
+ // (coming back to the tab tries again, e.g. after signing in again elsewhere). Or the canvas was deleted.
307
+ onAuthenticationFailed: ({ reason }) => {
308
+ if (reason === CANVAS_DELETED_REASON) return onCanvasDeleted();
211
309
  if (syncViaTools) return syncThroughTools();
212
310
  provider.disconnect();
213
311
  setStatus('disconnected');
214
312
  },
215
313
  });
216
314
  setTransport('websocket');
315
+ setDeleted(false);
316
+ setSynced(false);
217
317
 
218
318
  // Back online, or back on the tab: reconnect now rather than when the next retry is due
219
319
  const reconnect = () => {
220
- if (!stopToolSync && socketStatus === 'disconnected') void provider.connect();
320
+ if (!stopToolSync && !canvasDeleted && socketStatus === 'disconnected') void provider.connect();
221
321
  };
222
322
  // Away from the tab: our pointer goes from everyone's canvas
223
323
  const onVisibilityChange = () => {
@@ -231,7 +331,7 @@ export function useStudioFlow({
231
331
  // A socket that isn't let in within a few seconds (blocked, or never answered) is given up for tool calls
232
332
  if (syncViaTools) fallback = setTimeout(() => !socketConnected && syncThroughTools(), SOCKET_TIMEOUT_MS);
233
333
  // Undo only takes back your own changes: other people's arrive with the provider as origin, agents' with theirs
234
- const undoManager = new Y.UndoManager([yNodes, yEdges, yThreads], {
334
+ const undoManager = new Y.UndoManager([yNodes, yEdges, yThreads, yLevelLayouts, yHotspots], {
235
335
  captureTimeout: 500,
236
336
  trackedOrigins: new Set([null, LOCAL_ORIGIN]),
237
337
  });
@@ -248,6 +348,13 @@ export function useStudioFlow({
248
348
  // Where others are dragging nodes, from their presence, until they drop them
249
349
  let remoteMoves = new Map<string, XYPosition>();
250
350
  const settling = new Set<ReturnType<typeof setTimeout>>();
351
+ // Shown gliding, a step a frame: at most one update a frame however many people drag (each presence arriving
352
+ // was an update of its own: ~5 a frame with 7 people dragging), and smooth between their ~30 updates a second.
353
+ // Not what's been dropped since (its drop is in the document, see syncNodes).
354
+ const glide = createSmoother((shown) => {
355
+ const moved = new Map([...shown].filter(([id]) => remoteMoves.has(id)));
356
+ if (moved.size) setNodes((local) => withPositions(local, moved));
357
+ });
251
358
  const syncMoves = (_: unknown, origin: unknown) => {
252
359
  if (origin === 'local') return;
253
360
  const next = new Map<string, XYPosition>();
@@ -255,10 +362,10 @@ export function useStudioFlow({
255
362
  if (id === doc.clientID || !state.moving) return;
256
363
  Object.entries(state.moving as Record<string, XYPosition>).forEach(([nodeId, position]) => next.set(nodeId, position));
257
364
  });
258
- const moved = new Map([...next].filter(([id, { x, y }]) => remoteMoves.get(id)?.x !== x || remoteMoves.get(id)?.y !== y));
365
+ const moved = [...next].some(([id, { x, y }]) => remoteMoves.get(id)?.x !== x || remoteMoves.get(id)?.y !== y);
259
366
  const dropped = [...remoteMoves.keys()].filter((id) => !next.has(id));
260
367
  remoteMoves = next;
261
- if (moved.size) setNodes((local) => withPositions(local, moved));
368
+ if (moved || dropped.length) glide.set(next);
262
369
  if (dropped.length === 0) return;
263
370
  // The drop is saved in the document. If it doesn't arrive, back to where the document has it.
264
371
  const settle = setTimeout(() => {
@@ -273,6 +380,40 @@ export function useStudioFlow({
273
380
  };
274
381
  awareness.on('change', syncMoves);
275
382
 
383
+ // What others are writing on notes and text, and how they're scaling and turning text, as they do it (from their
384
+ // presence): shown on just those nodes, and back to what the document has when they're done (their result is
385
+ // written to the document before their presence is cleared, so it doesn't go back in between)
386
+ let remoteEdits = '';
387
+ let remoteEdited = new Set<string>();
388
+ const syncEdits = (_: unknown, origin: unknown) => {
389
+ if (origin === 'local') return;
390
+ const edits = new Map<string, Record<string, unknown>>();
391
+ awareness.getStates().forEach((state, id) => {
392
+ const editing = state.editing as Editing | null | undefined;
393
+ if (id === doc.clientID || !editing?.id) return;
394
+ const { id: nodeId, ...fields } = editing;
395
+ edits.set(nodeId, fields);
396
+ });
397
+ const key = JSON.stringify([...edits]);
398
+ if (key === remoteEdits) return;
399
+ remoteEdits = key;
400
+ const before = remoteEdited;
401
+ remoteEdited = new Set(edits.keys());
402
+ setNodes((local) => {
403
+ let next: Node[] | undefined;
404
+ local.forEach((node, index) => {
405
+ const edit = edits.get(node.id);
406
+ const savedData = before.has(node.id) ? yNodes.get(node.id)?.data : undefined;
407
+ const changes = edit ?? (savedData && Object.fromEntries(EDITED_FIELDS.map((field) => [field, savedData[field]])));
408
+ if (!changes || Object.entries(changes).every(([field, value]) => node.data[field] === value)) return;
409
+ next ??= [...local];
410
+ next[index] = { ...node, data: { ...node.data, ...changes } };
411
+ });
412
+ return next ?? local;
413
+ });
414
+ };
415
+ awareness.on('change', syncEdits);
416
+
276
417
  // Only what changed is replaced, so React Flow re-renders just those nodes
277
418
  const syncNodes = (event: Y.YMapEvent<Node>) => {
278
419
  if (event.transaction.origin === LOCAL_ORIGIN) return;
@@ -308,12 +449,17 @@ export function useStudioFlow({
308
449
  window.removeEventListener('online', reconnect);
309
450
  document.removeEventListener('visibilitychange', onVisibilityChange);
310
451
  awareness.off('change', syncMoves);
452
+ awareness.off('change', syncEdits);
453
+ glide.stop();
311
454
  sender.destroy();
312
455
  stopToolSync?.();
313
456
  yNodes.unobserve(syncNodes);
457
+ yLevelLayouts.unobserve(syncLevelLayouts);
314
458
  yEdges.unobserve(syncEdges);
315
459
  yMeta.unobserve(syncMeta);
460
+ yHotspots.unobserve(syncHotspots);
316
461
  doc.off('afterTransaction', reopenOnEdit);
462
+ doc.off('afterTransaction', syncStructure);
317
463
  presenceStore.destroy();
318
464
  undoManager.destroy();
319
465
  provider.destroy();
@@ -366,10 +512,10 @@ export function useStudioFlow({
366
512
  dragged.current.forEach((position, id) => moveNode(doc, id, position));
367
513
  // Containers as they grew around what was dragged, and what else is in them (moved the other way if they grew up
368
514
  // or left). Dragged nodes are where React Flow dropped them, already in their containers' grown coordinates.
369
- dragPreview.current.forEach(({ position, width, height }, id) => {
370
- const node = getCanvasMaps(doc).nodes.get(id);
371
- if (node) getCanvasMaps(doc).nodes.set(id, { ...node, position, ...(width !== undefined && { width, height }) });
372
- });
515
+ // Only what actually moved or grew is written (the preview has everything in those containers)
516
+ dragPreview.current.forEach(({ position, width, height }, id) =>
517
+ placeNode(doc, id, position, width !== undefined && height !== undefined ? { width, height } : undefined)
518
+ );
373
519
  dragPreview.current = new Map();
374
520
  dragged.current.clear();
375
521
  const shared = sharedRef.current;
@@ -456,12 +602,18 @@ export function useStudioFlow({
456
602
  * on the canvas, and returns edges to add with it (e.g. a catalog resource's relationships).
457
603
  */
458
604
  const insertNode = useCallback(
459
- (type: string, data: Record<string, unknown>, center: XYPosition, connect?: (id: string, existing: Node[]) => Edge[]) => {
605
+ (
606
+ type: string,
607
+ data: Record<string, unknown>,
608
+ center: XYPosition,
609
+ connect?: (id: string, existing: Node[]) => Edge[],
610
+ inside?: string
611
+ ) => {
460
612
  const built = buildNode(type, data, center);
461
613
  withDoc((doc) => {
462
614
  const existing = Array.from(getCanvasMaps(doc).nodes.values());
463
- // Dropped on a container: it goes in it
464
- const group = canGoInContainer(type) ? findGroupAtPoint(center, existing) : undefined;
615
+ // In the container asked for, else the one it's dropped on
616
+ const group = canGoInContainer(type) ? containerFor(center, existing, inside) : undefined;
465
617
  const node = group
466
618
  ? { ...built, parentId: group.id, position: toRelativePosition(built.position, group.id, existing) }
467
619
  : built;
@@ -473,11 +625,11 @@ export function useStudioFlow({
473
625
  []
474
626
  );
475
627
 
476
- /** Add a node of a registry type, centred on a canvas position */
628
+ /** Add a node of a component (its type, or its key in the components list), centred on a canvas position (in a container, if asked) */
477
629
  const addNode = useCallback(
478
- (type: string, center: XYPosition) => {
479
- const definition = getNodeDefinition(type);
480
- return definition ? insertNode(type, definition.createData(), center) : undefined;
630
+ (component: string, center: XYPosition, inside?: string) => {
631
+ const definition = getNodeDefinition(component);
632
+ return definition ? insertNode(definition.type, definition.createData(), center, undefined, inside) : undefined;
481
633
  },
482
634
  [insertNode]
483
635
  );
@@ -492,6 +644,12 @@ export function useStudioFlow({
492
644
 
493
645
  /** Moves nodes (and resizes containers) from a layout in one change, so it undoes in one go */
494
646
  const applyPositions = useCallback((layout: LayoutResult) => withDoc((doc) => applyLayout(doc, layout)), []);
647
+ /** Puts things where they go on L1 or L2: moved there by someone (undone like any edit), or placed by the level */
648
+ const setLevelLayout = useCallback(
649
+ (level: 1 | 2, places: ReadonlyMap<string, LevelPlace>, by: 'person' | 'level' = 'person') =>
650
+ withDoc((doc) => setLevelPlaces(doc, level, places), by === 'level' ? LEVEL_PLACEMENT_ORIGIN : null),
651
+ []
652
+ );
495
653
 
496
654
  /** Adds ready-made nodes and edges (e.g. a container with what's in it) in one change */
497
655
  const insertNodes = useCallback(
@@ -507,13 +665,12 @@ export function useStudioFlow({
507
665
  []
508
666
  );
509
667
 
510
- /** Puts a node in a container (or takes it out), e.g. after it's dragged there */
511
- const setParent = useCallback(
512
- (nodeId: string, parentId: string | undefined) =>
668
+ /** Puts nodes in containers (or takes them out) together, e.g. a selection dropped in one, as one change */
669
+ const setParents = useCallback(
670
+ (moves: { id: string; parentId: string | undefined; at?: XYPosition }[]) =>
513
671
  withDoc((doc) => {
514
- // Where it was dropped goes first, so it's moved into (or out of) the container from there
515
672
  if (dragged.current.size) saveDrop(doc);
516
- return setNodeParent(doc, nodeId, parentId, renderedSizes());
673
+ return setNodeParents(doc, moves, renderedSizes());
517
674
  }),
518
675
  []
519
676
  );
@@ -537,6 +694,47 @@ export function useStudioFlow({
537
694
  sharedRef.current?.sender.send({ viewport });
538
695
  }, []);
539
696
 
697
+ /**
698
+ * The note or text being changed here, as it changes (what's written, a text's size and angle), for the others to
699
+ * see as it happens; `null` when done (after the result is saved), sent straight away
700
+ */
701
+ const sendEditing = useCallback((editing: Editing | null) => {
702
+ const sender = sharedRef.current?.sender;
703
+ if (!sender) return;
704
+ sender.send({ editing: editing && { ...editing, ...(editing.text !== undefined && { text: editing.text.slice(0, 2000) }) } });
705
+ if (!editing) sender.flush();
706
+ }, []);
707
+
708
+ /**
709
+ * What a diagram file opened into this (new) canvas starts with: its nodes and connections, comments, hotspots and
710
+ * where things are on L1 and L2, in one change that isn't undone
711
+ */
712
+ const addContent = useCallback(
713
+ (content: ModelCanvasContent) =>
714
+ withDoc((doc) => {
715
+ addNodes(doc, content.nodes, content.edges);
716
+ if (content.threads?.length) addThreads(doc, content.threads);
717
+ if (content.hotspots?.length) addHotspots(doc, content.hotspots);
718
+ setLevelPlaces(doc, 1, content.levelLayouts[1]);
719
+ setLevelPlaces(doc, 2, content.levelLayouts[2]);
720
+ }, OPEN_ORIGIN),
721
+ []
722
+ );
723
+
724
+ // ---- Hotspots and the lock ----
725
+
726
+ const createHotspot = useCallback(
727
+ (hotspot: Omit<Hotspot, 'id' | 'createdAt'>) => withDoc((doc) => createHotspotInDoc(doc, hotspot)),
728
+ []
729
+ );
730
+ const updateHotspot = useCallback(
731
+ (id: string, fields: Parameters<typeof updateHotspotInDoc>[2]) => withDoc((doc) => updateHotspotInDoc(doc, id, fields)),
732
+ []
733
+ );
734
+ const deleteHotspot = useCallback((id: string) => withDoc((doc) => deleteHotspotInDoc(doc, id)), []);
735
+ /** Locks the canvas for everyone (by a name), or unlocks it (null): looked at, not changed */
736
+ const setLocked = useCallback((by: string | null) => withDoc((doc) => setLock(doc, by), STATUS_ORIGIN), []);
737
+
540
738
  return {
541
739
  nodes,
542
740
  edges,
@@ -554,13 +752,27 @@ export function useStudioFlow({
554
752
  updateNodeData,
555
753
  deleteNodes,
556
754
  applyPositions,
755
+ levelLayouts,
756
+ setLevelLayout,
557
757
  insertNodes,
558
- setParent,
758
+ setParents,
559
759
  title,
760
+ diagramLevels,
761
+ structureKey,
560
762
  rename,
561
763
  canvasStatus,
562
764
  changeStatus,
563
765
  transport,
766
+ deleted,
767
+ hotspots,
768
+ createHotspot,
769
+ updateHotspot,
770
+ deleteHotspot,
771
+ lockedBy,
772
+ setLocked,
773
+ sendEditing,
774
+ synced,
775
+ addContent,
564
776
  undo,
565
777
  redo,
566
778
  ...history,
@@ -1,6 +1,11 @@
1
1
  // Who you are in Studio: the name you give on your first visit, kept in this browser and used on every canvas
2
- const COLORS = ['#ef4444', '#f97316', '#eab308', '#22c55e', '#06b6d4', '#3b82f6', '#8b5cf6', '#ec4899'];
2
+ /** The colours people can go by (as Studio on the web's): on their pointer, avatar and comments */
3
+ export const PERSON_COLORS = ['#ef4444', '#f97316', '#eab308', '#22c55e', '#06b6d4', '#3b82f6', '#8b5cf6', '#ec4899'];
4
+ const COLORS = PERSON_COLORS;
3
5
  const NAME_KEY = 'eventcatalog-studio-name';
6
+ const COLOR_KEY = 'eventcatalog-studio-color';
7
+ /** Names people give are at most this long */
8
+ export const MAX_NAME_LENGTH = 60;
4
9
 
5
10
  export const getStoredName = () => {
6
11
  try {
@@ -33,3 +38,22 @@ export const colorForName = (name: string) => {
33
38
  for (const char of name) hash = (hash * 31 + char.charCodeAt(0)) | 0;
34
39
  return COLORS[Math.abs(hash) % COLORS.length];
35
40
  };
41
+
42
+ /** The colour you picked, kept in this browser (else it follows from your name) */
43
+ export const getStoredColor = () => {
44
+ try {
45
+ const color = localStorage.getItem(COLOR_KEY);
46
+ return color && PERSON_COLORS.includes(color) ? color : null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ };
51
+
52
+ export const storeColor = (color: string) => {
53
+ try {
54
+ localStorage.setItem(COLOR_KEY, color);
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ };
@@ -0,0 +1,2 @@
1
+ /** Canvas titles are at most this long (no imports, so the browser can check it without loading a canvas) */
2
+ export const MAX_TITLE_LENGTH = 200;
@@ -1,13 +1,16 @@
1
1
  ---
2
2
  // Visualiser styles ship in the page head so ClientRouter navigations keep them (see NodeGraph.astro).
3
3
  import '@eventcatalog/visualiser/styles-core.css';
4
- import '../studio.css';
4
+ import '@eventcatalog/visualiser/studio.css';
5
+ import '@eventcatalog/visualiser/studio-fonts.css';
5
6
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
6
7
  import { buildUrl } from '@utils/url-builder';
7
8
  import StudioDesigner from '../components/StudioDesigner';
9
+ import StudioNotice from '../components/StudioNotice.astro';
8
10
  import { getCatalogResources } from '../catalog-resources';
9
11
  import { startStudio, STUDIO_SOCKET_PATH } from '../server/runtime';
10
12
  import { getSignedInUser, isSignedIn } from '../server/sign-in';
13
+ import { CANVASES_API_PATH } from '../api/canvases-api';
11
14
 
12
15
  export const prerender = false;
13
16
 
@@ -17,27 +20,50 @@ if (!id || !/^[0-9a-f-]{36}$/i.test(id)) {
17
20
  return Astro.redirect(buildUrl('/studio'));
18
21
  }
19
22
 
20
- const { resources, relations } = await getCatalogResources();
21
- const keptInMemory = (await startStudio({ isSignedIn })).storage.type === 'memory';
23
+ // Studio can't start when its storage can't be used (e.g. the database was updated by a newer EventCatalog)
24
+ const started = await startStudio({ isSignedIn }).catch((error: Error) => error);
25
+ const unavailable = started instanceof Error ? started.message : undefined;
26
+ const runtime = started instanceof Error ? undefined : started;
27
+ // A deleted canvas's link (anyone who still had it open was told)
28
+ const deleted = runtime?.isDeleted(id) ?? false;
29
+ if (unavailable) Astro.response.status = 503;
30
+ else if (deleted) Astro.response.status = 410;
31
+ const { resources, relations } = runtime && !deleted ? await getCatalogResources() : { resources: [], relations: [] };
32
+ const keptInMemory = runtime?.storage.type === 'memory';
22
33
  const signedInAs = getSignedInUser(Astro.locals);
23
34
  ---
24
35
 
25
- <VerticalSideBarLayout title="Studio - EventCatalog" showNestedSideBar={false}>
36
+ <VerticalSideBarLayout title="Studio" showNestedSideBar={false}>
26
37
  <main
27
38
  class="min-h-0 overflow-hidden bg-[rgb(var(--ec-page-bg))]"
28
39
  style="margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1); margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1); height: calc(100dvh - 60px);"
29
40
  >
30
- <StudioDesigner
31
- client:only="react"
32
- canvasId={id}
33
- socketPath={buildUrl(STUDIO_SOCKET_PATH, true)}
34
- newCanvasUrl={`${buildUrl('/studio')}?new=1`}
35
- mcpUrl={buildUrl('/docs/mcp', true)}
36
- relayScriptPath={buildUrl('/webmcp-relay/embed.js', true)}
37
- keptInMemory={keptInMemory}
38
- signedInAs={signedInAs}
39
- resources={resources}
40
- relations={relations}
41
- />
41
+ {
42
+ unavailable ? (
43
+ <StudioNotice title="Studio isn't available" message={unavailable} />
44
+ ) : deleted ? (
45
+ <StudioNotice
46
+ title="This canvas was deleted"
47
+ message="Someone deleted it, so it can't be opened any more."
48
+ href={buildUrl('/studio')}
49
+ linkText="Back to Studio"
50
+ />
51
+ ) : (
52
+ <StudioDesigner
53
+ client:only="react"
54
+ canvasId={id}
55
+ socketPath={buildUrl(STUDIO_SOCKET_PATH, true)}
56
+ newCanvasUrl={`${buildUrl('/studio')}?new=1`}
57
+ studioUrl={buildUrl('/studio')}
58
+ canvasesApiUrl={buildUrl(CANVASES_API_PATH, true)}
59
+ mcpUrl={buildUrl('/docs/mcp', true)}
60
+ relayScriptPath={buildUrl('/webmcp-relay/embed.js', true)}
61
+ keptInMemory={keptInMemory}
62
+ signedInAs={signedInAs}
63
+ resources={resources}
64
+ relations={relations}
65
+ />
66
+ )
67
+ }
42
68
  </main>
43
69
  </VerticalSideBarLayout>