@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
@@ -64,44 +64,49 @@ export const LlmAccessSettingsForm = ({ canEdit, initial, apiBase, llmsTxtUrl, l
64
64
  };
65
65
 
66
66
  return (
67
- <form onSubmit={(e) => e.preventDefault()} className="divide-y divide-[rgb(var(--ec-page-border))]">
67
+ <>
68
68
  <Toaster richColors closeButton position="bottom-right" theme="system" />
69
- {!canEdit && (
70
- <div className="pb-6">
71
- <ReadOnlyBanner />
72
- </div>
73
- )}
74
-
75
- <Row
76
- title="llms.txt"
77
- description="Generate llms.txt files so tools like Claude, ChatGPT, and Cursor can read your catalog at /llms.txt."
78
- canEdit={canEdit}
79
- dirty={dirty}
80
- saving={saving}
81
- onSave={save}
69
+ <form
70
+ onSubmit={(e) => e.preventDefault()}
71
+ className="divide-y divide-[rgb(var(--ec-page-border))] border-t border-[rgb(var(--ec-page-border))]"
82
72
  >
83
- <ToggleRow
84
- icon={<Bot className="h-4 w-4" aria-hidden />}
85
- label={llmsTxtEnabled ? 'Enabled' : 'Disabled'}
86
- hint={
87
- llmsTxtEnabled ? 'AI tools can read your catalog at /llms.txt.' : 'AI tools will not be able to read your catalog.'
88
- }
89
- checked={llmsTxtEnabled}
90
- disabled={!canEdit}
91
- onChange={setLlmsTxtEnabled}
92
- />
93
- {llmsTxtEnabled && <LlmsTxtPreview url={llmsTxtUrl} fullUrl={llmsFullTxtUrl} />}
94
- </Row>
73
+ {!canEdit && (
74
+ <div className="pb-6">
75
+ <ReadOnlyBanner />
76
+ </div>
77
+ )}
95
78
 
96
- <Row
97
- title="schemas.txt"
98
- description="Give LLMs access to every schema across your organization in one place — events, commands, queries, and services."
99
- canEdit={false}
100
- dirty={false}
101
- >
102
- <SchemasTxtAvailable url={schemasTxtUrl} />
103
- </Row>
104
- </form>
79
+ <Row
80
+ title="llms.txt"
81
+ description="Generate llms.txt files so tools like Claude, ChatGPT, and Cursor can read your catalog at /llms.txt."
82
+ canEdit={canEdit}
83
+ dirty={dirty}
84
+ saving={saving}
85
+ onSave={save}
86
+ >
87
+ <ToggleRow
88
+ icon={<Bot className="h-4 w-4" aria-hidden />}
89
+ label={llmsTxtEnabled ? 'Enabled' : 'Disabled'}
90
+ hint={
91
+ llmsTxtEnabled ? 'AI tools can read your catalog at /llms.txt.' : 'AI tools will not be able to read your catalog.'
92
+ }
93
+ checked={llmsTxtEnabled}
94
+ disabled={!canEdit}
95
+ onChange={setLlmsTxtEnabled}
96
+ />
97
+ {llmsTxtEnabled && <LlmsTxtPreview url={llmsTxtUrl} fullUrl={llmsFullTxtUrl} />}
98
+ </Row>
99
+
100
+ <Row
101
+ title="schemas.txt"
102
+ description="Give LLMs access to every schema across your organization in one place — events, commands, queries, and services."
103
+ canEdit={false}
104
+ dirty={false}
105
+ >
106
+ <SchemasTxtAvailable url={schemasTxtUrl} />
107
+ </Row>
108
+ </form>
109
+ </>
105
110
  );
106
111
  };
107
112
 
@@ -75,7 +75,7 @@ export const LogoUpload = ({ canEdit, initialSrc, apiUrl, onSrcChange }: LogoUpl
75
75
  onDrop={onDrop}
76
76
  className={`relative flex items-center gap-5 rounded-lg border border-dashed px-4 py-4 transition-colors ${
77
77
  dragOver
78
- ? 'border-[rgb(var(--ec-accent)/0.6)] bg-[rgb(var(--ec-accent-subtle)/0.4)]'
78
+ ? 'border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.04)]'
79
79
  : 'border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg)/0.4)]'
80
80
  }`}
81
81
  >
@@ -26,7 +26,8 @@ interface Props {
26
26
 
27
27
  export const McpSettingsForm = ({ inSSR, mcpUrl, domainServers }: Props) => {
28
28
  return (
29
- <div className="divide-y divide-[rgb(var(--ec-page-border))]">
29
+ // A top border, as on the other settings pages
30
+ <div className="divide-y divide-[rgb(var(--ec-page-border))] border-t border-[rgb(var(--ec-page-border))]">
30
31
  <Row
31
32
  title="Catalog MCP Server"
32
33
  description="Connect AI agents (Claude, Cursor, VS Code and any MCP client) to every resource in your catalog: domains, services, messages, flows, schemas and the teams that own them."
@@ -63,7 +64,7 @@ const LivePill = () => (
63
64
  const CatalogServer = ({ url }: { url: string }) => (
64
65
  <div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))]">
65
66
  <div className="flex items-start gap-3 px-4 py-3.5">
66
- <span className="flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg border border-[rgb(var(--ec-accent)/0.3)] bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]">
67
+ <span className="flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.05)] text-[rgb(var(--ec-page-text))]">
67
68
  <Library className="h-4 w-4" aria-hidden />
68
69
  </span>
69
70
  <div className="min-w-0 flex-1">
@@ -83,7 +84,7 @@ const CatalogServer = ({ url }: { url: string }) => (
83
84
  href={MCP_DOCS_URL}
84
85
  target="_blank"
85
86
  rel="noreferrer"
86
- className="mt-3 inline-flex items-center gap-1 text-[12px] font-medium text-[rgb(var(--ec-accent))] hover:underline"
87
+ className="mt-3 inline-flex items-center gap-1 text-[12px] font-medium text-[rgb(var(--ec-page-text))] hover:underline"
87
88
  >
88
89
  How to connect a client
89
90
  <ArrowUpRight className="h-3.5 w-3.5" aria-hidden />
@@ -100,7 +101,7 @@ const DomainServers = ({ servers }: { servers: DomainMcpServer[] }) => (
100
101
  <a
101
102
  href={server.docsUrl}
102
103
  title={server.summary}
103
- className="inline-flex min-w-0 items-center gap-2 text-[13px] font-medium text-[rgb(var(--ec-page-text))] hover:text-[rgb(var(--ec-accent))]"
104
+ className="inline-flex min-w-0 items-center gap-2 text-[13px] font-medium text-[rgb(var(--ec-page-text))] hover:underline"
104
105
  >
105
106
  <Boxes className="h-3.5 w-3.5 flex-shrink-0 self-center text-[rgb(var(--ec-icon-color))]" aria-hidden />
106
107
  <span className="truncate">{server.name}</span>
@@ -114,15 +115,15 @@ const DomainServers = ({ servers }: { servers: DomainMcpServer[] }) => (
114
115
  );
115
116
 
116
117
  const McpNeedsSSR = () => (
117
- <div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-accent)/0.4)] bg-gradient-to-br from-[rgb(var(--ec-accent)/0.1)] via-[rgb(var(--ec-accent)/0.05)] to-transparent">
118
+ <div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.03)]">
118
119
  <div className="flex items-start gap-3 px-4 py-3.5">
119
- <span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-[rgb(var(--ec-accent)/0.4)] bg-[rgb(var(--ec-accent)/0.1)] text-[rgb(var(--ec-accent))]">
120
+ <span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.05)] text-[rgb(var(--ec-page-text))]">
120
121
  <ServerCog className="h-4 w-4" aria-hidden />
121
122
  </span>
122
123
  <div className="flex-1 min-w-0">
123
124
  <div className="flex items-center gap-2">
124
125
  <p className="text-[13px] font-semibold text-[rgb(var(--ec-page-text))]">Server output mode required</p>
125
- <span className="inline-flex items-center gap-1 rounded-full border border-[rgb(var(--ec-accent)/0.4)] bg-[rgb(var(--ec-accent)/0.1)] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[rgb(var(--ec-accent))]">
126
+ <span className="inline-flex items-center gap-1 rounded-full border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.05)] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[rgb(var(--ec-page-text))]">
126
127
  SSR
127
128
  </span>
128
129
  </div>
@@ -134,7 +135,7 @@ const McpNeedsSSR = () => (
134
135
  href={MCP_DOCS_URL}
135
136
  target="_blank"
136
137
  rel="noreferrer"
137
- className="inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-accent))] px-3 py-1.5 text-[12px] font-semibold text-white shadow-sm transition-colors hover:bg-[rgb(var(--ec-accent-hover))]"
138
+ className="inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-page-text))] px-3 py-1.5 text-[12px] font-semibold text-[rgb(var(--ec-page-bg))] shadow-sm transition-colors hover:opacity-85"
138
139
  >
139
140
  MCP setup guide
140
141
  <ExternalLink className="h-3 w-3" aria-hidden />
@@ -3,7 +3,7 @@ import { AlertCircle } from 'lucide-react';
3
3
  export const cn = (...parts: Array<string | false | undefined | null>): string => parts.filter(Boolean).join(' ');
4
4
 
5
5
  export const inputBase =
6
- 'block w-full rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-input-bg,var(--ec-page-bg)))] px-3 py-2 text-[13px] text-[rgb(var(--ec-page-text))] placeholder:text-[rgb(var(--ec-page-text-muted)/0.7)] transition-colors focus:border-[rgb(var(--ec-accent)/0.6)] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent)/0.25)] disabled:cursor-not-allowed disabled:opacity-60';
6
+ 'block w-full rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-input-bg,var(--ec-page-bg)))] px-3 py-2 text-[13px] text-[rgb(var(--ec-page-text))] placeholder:text-[rgb(var(--ec-page-text-muted)/0.7)] transition-colors focus:border-[rgb(var(--ec-page-text)/0.5)] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-page-text)/0.15)] disabled:cursor-not-allowed disabled:opacity-60';
7
7
 
8
8
  export const monoInput = 'font-mono text-[12.5px] tracking-tight';
9
9
 
@@ -11,7 +11,9 @@ export const inputError = 'border-red-500/70 focus:border-red-500 focus:ring-red
11
11
 
12
12
  interface RowProps {
13
13
  title: string;
14
- description: string;
14
+ description: React.ReactNode;
15
+ /** Shown under the description, in the left column */
16
+ aside?: React.ReactNode;
15
17
  canEdit: boolean;
16
18
  dirty: boolean;
17
19
  saving?: boolean;
@@ -20,13 +22,14 @@ interface RowProps {
20
22
  children: React.ReactNode;
21
23
  }
22
24
 
23
- export const Row = ({ title, description, canEdit, dirty, saving, onSave, error, children }: RowProps) => (
25
+ export const Row = ({ title, description, aside, canEdit, dirty, saving, onSave, error, children }: RowProps) => (
24
26
  <section className="grid grid-cols-1 gap-6 py-8 md:grid-cols-[minmax(0,_18rem)_minmax(0,_1fr)] md:gap-12">
25
27
  <header className="space-y-1.5">
26
28
  <h3 className="text-[14px] font-semibold tracking-tight text-[rgb(var(--ec-page-text))]">{title}</h3>
27
29
  <p className="text-[13px] leading-relaxed text-[rgb(var(--ec-content-text-muted,var(--ec-page-text-muted)))]">
28
30
  {description}
29
31
  </p>
32
+ {aside && <div className="pt-3">{aside}</div>}
30
33
  </header>
31
34
  <div className="space-y-2.5">
32
35
  {children}
@@ -45,7 +48,7 @@ export const Row = ({ title, description, canEdit, dirty, saving, onSave, error,
45
48
  className={cn(
46
49
  'inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-[12px] font-medium transition-colors',
47
50
  dirty && !saving
48
- ? 'border-[rgb(var(--ec-accent)/0.5)] bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent-text))] hover:bg-[rgb(var(--ec-accent-subtle)/0.8)]'
51
+ ? 'border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.05)] text-[rgb(var(--ec-page-text))] hover:bg-[rgb(var(--ec-page-text)/0.08)]'
49
52
  : 'border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] text-[rgb(var(--ec-page-text-muted))]',
50
53
  'disabled:cursor-not-allowed disabled:opacity-60'
51
54
  )}
@@ -32,7 +32,7 @@ export const UrlPanel = ({ url }: UrlPanelProps) => {
32
32
  href={url}
33
33
  target="_blank"
34
34
  rel="noreferrer"
35
- className="flex-1 truncate rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] px-2.5 py-1.5 font-mono text-[12px] text-[rgb(var(--ec-page-text))] transition-colors hover:border-[rgb(var(--ec-accent)/0.5)] hover:text-[rgb(var(--ec-accent))]"
35
+ className="flex-1 truncate rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] px-2.5 py-1.5 font-mono text-[12px] text-[rgb(var(--ec-page-text))] transition-colors hover:border-[rgb(var(--ec-page-text)/0.4)]"
36
36
  >
37
37
  {fullUrl}
38
38
  </a>
@@ -72,13 +72,13 @@ export const Toggle = ({ checked, disabled, onChange }: ToggleProps) => (
72
72
  onClick={() => onChange(!checked)}
73
73
  className={cn(
74
74
  'relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer items-center rounded-full transition-colors',
75
- checked ? 'bg-[rgb(var(--ec-accent))]' : 'bg-[rgb(var(--ec-page-text-muted)/0.35)]',
75
+ checked ? 'bg-[rgb(var(--ec-page-text))]' : 'bg-[rgb(var(--ec-page-text-muted)/0.35)]',
76
76
  disabled && 'cursor-not-allowed opacity-50'
77
77
  )}
78
78
  >
79
79
  <span
80
80
  className={cn(
81
- 'inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform',
81
+ 'inline-block h-3.5 w-3.5 transform rounded-full bg-[rgb(var(--ec-page-bg))] shadow-sm transition-transform',
82
82
  checked ? 'translate-x-[1.125rem]' : 'translate-x-[0.1875rem]'
83
83
  )}
84
84
  />
@@ -21,57 +21,56 @@ import {
21
21
  type FavoriteItem,
22
22
  } from '@stores/favorites-store';
23
23
  import {
24
+ buildNodeLookup,
24
25
  canCollapseGroup,
25
26
  findNodeKeyByUrl,
26
27
  getBadgeClasses,
27
28
  getDefaultCollapsedState,
28
29
  hidesDefaultItemIcon,
29
30
  isGroupCollapsed,
31
+ resolveNavRef,
32
+ rootLevel,
33
+ stripBasePath,
30
34
  toggleGroupCollapsed,
35
+ type NavigationLevel,
36
+ type SidebarSnapshot,
31
37
  } from './utils';
32
38
  import { resolveIconUrl } from '@utils/icon';
33
39
 
34
40
  const cn = (...classes: (string | false | undefined)[]) => classes.filter(Boolean).join(' ');
35
41
 
36
- /**
37
- * Strip the configured base path from a pathname so URL pattern matching works
38
- * regardless of the base path setting (e.g. `/eventcatalog/docs/...` → `/docs/...`).
39
- */
40
- const stripBasePath = (pathname: string): string => {
41
- const base = __EC_BASE__.replace(/\/$/, ''); // e.g. '/eventcatalog'
42
- if (base && pathname.startsWith(base)) {
43
- return pathname.slice(base.length) || '/';
44
- }
45
- return pathname;
46
- };
47
-
48
42
  // ============================================
49
43
  // Component
50
44
  // ============================================
51
45
 
52
- type NavigationLevel = {
53
- key: string | null; // The key of the node that was drilled into (null for root)
54
- entries: ChildRef[];
55
- title: string;
56
- badge?: string; // Category badge (e.g., "Domain", "Service")
46
+ type NestedSideBarProps = {
47
+ /**
48
+ * This page's level, built on the server. It renders the sidebar into the HTML and keeps
49
+ * the island showing it until the full navigation data (sidebar-data.json) is loaded.
50
+ */
51
+ snapshot?: SidebarSnapshot;
57
52
  };
58
53
 
59
- export default function NestedSideBar() {
60
- const data = useStore(sidebarStore);
54
+ export default function NestedSideBar({ snapshot }: NestedSideBarProps) {
55
+ const storeData = useStore(sidebarStore);
61
56
  const favorites = useStore(favoritesStore);
57
+ // Drill-down, restoring state and favorites need the whole catalog; until it loads the
58
+ // snapshot only shows this page's level
59
+ const hasFullData = storeData !== null;
60
+ const data = storeData ?? snapshot?.data ?? null;
62
61
 
63
62
  // Guard against undefined data (e.g., during hydration)
64
63
  // Use useMemo to ensure stable references for roots and nodes
65
64
  const roots = useMemo(() => data?.roots ?? [], [data?.roots]);
66
65
  const nodes = useMemo(() => data?.nodes ?? {}, [data?.nodes]);
67
66
 
68
- const [navigationStack, setNavigationStack] = useState<NavigationLevel[]>(() => [
69
- { key: null, entries: [], title: 'Documentation' },
70
- ]);
67
+ const [navigationStack, setNavigationStack] = useState<NavigationLevel[]>(
68
+ () => snapshot?.stack ?? [{ key: null, entries: [], title: 'Documentation' }]
69
+ );
71
70
  const [animationKey, setAnimationKey] = useState(0);
72
71
  const [slideDirection, setSlideDirection] = useState<'forward' | 'backward' | null>(null);
73
72
  const [isInitialized, setIsInitialized] = useState(false);
74
- const [currentPath, setCurrentPath] = useState<string>('');
73
+ const [currentPath, setCurrentPath] = useState<string>(snapshot?.url ?? '');
75
74
  const [sectionCollapsePreferences, setSectionCollapsePreferences] = useState(loadCollapsedSections);
76
75
  const [showPathPreview, setShowPathPreview] = useState(false);
77
76
  const [showFullPath, setShowFullPath] = useState(false);
@@ -79,33 +78,7 @@ export default function NestedSideBar() {
79
78
 
80
79
  // Build a lookup map for faster URL navigation
81
80
  // Map format: "type:id" -> "nodeKey"
82
- const nodeLookup = useMemo(() => {
83
- const lookup = new Map<string, string>();
84
-
85
- Object.entries(nodes).forEach(([key, value]) => {
86
- // Skip keys that are references (string values pointing to other keys)
87
- // These are unversioned aliases like "domain:E-Commerce" -> "domain:E-Commerce:1.0.0"
88
- if (typeof value === 'string') return;
89
-
90
- // Key formats:
91
- // - "type:id:version" (e.g., "service:OrdersService:0.0.3")
92
- // - "type:id" (e.g., "user:john", "team:backend") - non-versioned resources
93
- // - "list:name" (e.g., "list:domains") - skip these
94
- const parts = key.split(':');
95
-
96
- // Skip list items
97
- if (parts[0] === 'list') return;
98
-
99
- if (parts.length >= 2) {
100
- // Store as "type:id"
101
- const type = parts[0];
102
- const id = parts[1];
103
- lookup.set(`${type}:${id}`, key);
104
- }
105
- });
106
-
107
- return lookup;
108
- }, [nodes]);
81
+ const nodeLookup = useMemo(() => buildNodeLookup(nodes), [nodes]);
109
82
 
110
83
  /**
111
84
  * Toggle section collapse state
@@ -126,7 +99,7 @@ export default function NestedSideBar() {
126
99
  setNavigationStack((prevStack) => {
127
100
  // Only update if the current stack has no entries (initial state)
128
101
  if (prevStack.length === 1 && prevStack[0].entries.length === 0) {
129
- return [{ key: null, entries: roots, title: 'Documentation' }];
102
+ return [rootLevel(roots)];
130
103
  }
131
104
  return prevStack;
132
105
  });
@@ -146,21 +119,7 @@ export default function NestedSideBar() {
146
119
  * Resolve a child reference to a NavNode
147
120
  * Handles both direct keys and string references (unversioned aliases pointing to versioned keys)
148
121
  */
149
- const resolveRef = useCallback(
150
- (ref: ChildRef): NavNode | null => {
151
- if (typeof ref === 'string') {
152
- const node = nodes[ref];
153
- if (!node) return null;
154
- // If node is a string, it's a reference to another key (e.g., unversioned alias)
155
- if (typeof node === 'string') {
156
- return (nodes[node] as NavNode) ?? null;
157
- }
158
- return node;
159
- }
160
- return ref;
161
- },
162
- [nodes]
163
- );
122
+ const resolveRef = useCallback((ref: ChildRef): NavNode | null => resolveNavRef(nodes, ref), [nodes]);
164
123
 
165
124
  /**
166
125
  * Check if a node is visible (default: true)
@@ -175,7 +134,7 @@ export default function NestedSideBar() {
175
134
  */
176
135
  const buildStackFromPath = useCallback(
177
136
  (path: string[]): NavigationLevel[] => {
178
- const stack: NavigationLevel[] = [{ key: null, entries: roots, title: 'Documentation' }];
137
+ const stack: NavigationLevel[] = [rootLevel(roots)];
179
138
 
180
139
  for (const key of path) {
181
140
  const node = resolveRef(key);
@@ -281,7 +240,7 @@ export default function NestedSideBar() {
281
240
  if (foundNode && foundNode.pages && foundNode.pages.length > 0) {
282
241
  // Fallback: Flattened navigation
283
242
  return [
284
- { key: null, entries: roots, title: 'Documentation' },
243
+ rootLevel(roots),
285
244
  { key: foundNodeKey, entries: foundNode.pages, title: foundNode.title, badge: foundNode.badge },
286
245
  ];
287
246
  }
@@ -296,7 +255,7 @@ export default function NestedSideBar() {
296
255
  setSlideDirection('backward');
297
256
  setAnimationKey((prev) => prev + 1);
298
257
  }
299
- return [{ key: null, entries: roots, title: 'Documentation' }];
258
+ return [rootLevel(roots)];
300
259
  });
301
260
  return true;
302
261
  }
@@ -309,14 +268,14 @@ export default function NestedSideBar() {
309
268
  * Restore state from localStorage on mount, or navigate to URL
310
269
  */
311
270
  useEffect(() => {
312
- if (!data || roots.length === 0 || isInitialized) return;
271
+ if (!hasFullData || roots.length === 0 || isInitialized) return;
313
272
 
314
273
  const currentUrl = window.location.pathname;
315
274
  const normalizedCurrentUrl = stripBasePath(currentUrl);
316
275
 
317
276
  // Force root navigation on homepage
318
277
  if (normalizedCurrentUrl === '/' || normalizedCurrentUrl === '') {
319
- setNavigationStack([{ key: null, entries: roots, title: 'Documentation' }]);
278
+ setNavigationStack([rootLevel(roots)]);
320
279
  setIsInitialized(true);
321
280
  return;
322
281
  }
@@ -347,7 +306,7 @@ export default function NestedSideBar() {
347
306
  const targetNode = resolveRef(targetKey);
348
307
  if (targetNode && targetNode.pages && targetNode.pages.length > 0) {
349
308
  finalStack = [
350
- { key: null, entries: roots, title: 'Documentation' },
309
+ rootLevel(roots),
351
310
  { key: targetKey, entries: targetNode.pages, title: targetNode.title, badge: targetNode.badge },
352
311
  ];
353
312
  }
@@ -355,13 +314,13 @@ export default function NestedSideBar() {
355
314
 
356
315
  // 3. Fallback to root
357
316
  if (!finalStack) {
358
- setNavigationStack([{ key: null, entries: roots, title: 'Documentation' }]);
317
+ setNavigationStack([rootLevel(roots)]);
359
318
  } else {
360
319
  setNavigationStack(finalStack);
361
320
  }
362
321
 
363
322
  setIsInitialized(true);
364
- }, [data, roots, isInitialized, buildStackFromPath, findNodeKeyForUrl, tryConnectStack, resolveRef]);
323
+ }, [hasFullData, roots, isInitialized, buildStackFromPath, findNodeKeyForUrl, tryConnectStack, resolveRef]);
365
324
 
366
325
  /**
367
326
  * Save state whenever navigation changes
@@ -575,6 +534,7 @@ export default function NestedSideBar() {
575
534
  * Handle drilling down into an item with children
576
535
  */
577
536
  const handleDrillDown = (node: NavNode, nodeKey: string | null) => {
537
+ if (!hasFullData) return;
578
538
  if (node.pages && node.pages.length > 0) {
579
539
  setSlideDirection('forward');
580
540
  setAnimationKey((prev) => prev + 1);
@@ -590,7 +550,7 @@ export default function NestedSideBar() {
590
550
  * Navigate back one level
591
551
  */
592
552
  const navigateBack = () => {
593
- if (navigationStack.length > 1) {
553
+ if (hasFullData && navigationStack.length > 1) {
594
554
  setSlideDirection('backward');
595
555
  setAnimationKey((prev) => prev + 1);
596
556
  setNavigationStack(navigationStack.slice(0, -1));
@@ -604,7 +564,7 @@ export default function NestedSideBar() {
604
564
  * Navigate to a specific level in the stack
605
565
  */
606
566
  const navigateToLevel = (levelIndex: number) => {
607
- if (levelIndex < navigationStack.length - 1) {
567
+ if (hasFullData && levelIndex < navigationStack.length - 1) {
608
568
  setSlideDirection('backward');
609
569
  setAnimationKey((prev) => prev + 1);
610
570
  setNavigationStack(navigationStack.slice(0, levelIndex + 1));
@@ -634,6 +594,7 @@ export default function NestedSideBar() {
634
594
  * Navigate to a favorited item
635
595
  */
636
596
  const navigateToFavorite = (favorite: FavoriteItem) => {
597
+ if (!hasFullData) return;
637
598
  // If it has an href and no children, just navigate to the URL
638
599
  const node = resolveRef(favorite.nodeKey);
639
600
  if (favorite.href && (!node?.pages || node.pages.length === 0)) {
@@ -107,6 +107,8 @@ export const saveCollapsedSections = (preferences: SectionCollapsePreferences):
107
107
  };
108
108
 
109
109
  export const loadCollapsedSections = (): SectionCollapsePreferences => {
110
+ // Rendered on the server for the first paint, where there is nothing saved yet
111
+ if (typeof localStorage === 'undefined') return createSectionCollapsePreferences();
110
112
  try {
111
113
  const stored = localStorage.getItem(SECTION_PREFERENCES_KEY);
112
114
  if (stored) {
@@ -1,9 +1,130 @@
1
1
  // Shared utilities for NestedSideBar components
2
2
 
3
- import type { NavNode } from '@stores/sidebar-store/state';
3
+ import type { ChildRef, NavigationData, NavNode } from '@stores/sidebar-store/state';
4
4
 
5
5
  export const SIDEBAR_GROUP_COLLAPSE_THRESHOLD = 5;
6
6
 
7
+ /**
8
+ * Strip the configured base path from a pathname so URL pattern matching works
9
+ * regardless of the base path setting (e.g. `/eventcatalog/docs/...` → `/docs/...`).
10
+ */
11
+ export const stripBasePath = (pathname: string): string => {
12
+ const base = __EC_BASE__.replace(/\/$/, ''); // e.g. '/eventcatalog'
13
+ if (base && pathname.startsWith(base)) {
14
+ return pathname.slice(base.length) || '/';
15
+ }
16
+ return pathname;
17
+ };
18
+
19
+ export type NavigationLevel = {
20
+ key: string | null; // The key of the node that was drilled into (null for root)
21
+ entries: ChildRef[];
22
+ title: string;
23
+ badge?: string; // Category badge (e.g., "Domain", "Service")
24
+ };
25
+
26
+ export const rootLevel = (roots: ChildRef[]): NavigationLevel => ({ key: null, entries: roots, title: 'Documentation' });
27
+
28
+ /** Resolve a child reference to a node, following unversioned aliases (string → key). */
29
+ export const resolveNavRef = (nodes: NavigationData['nodes'], ref: ChildRef): NavNode | null => {
30
+ if (typeof ref !== 'string') return ref;
31
+ const node = nodes[ref];
32
+ if (!node) return null;
33
+ if (typeof node === 'string') return (nodes[node] as NavNode) ?? null;
34
+ return node;
35
+ };
36
+
37
+ /** Map of "type:id" → versioned node key, used to match URLs to nodes. */
38
+ export const buildNodeLookup = (nodes: NavigationData['nodes']): Map<string, string> => {
39
+ const lookup = new Map<string, string>();
40
+
41
+ Object.entries(nodes).forEach(([key, value]) => {
42
+ // Skip keys that are references (string values pointing to other keys)
43
+ // These are unversioned aliases like "domain:E-Commerce" -> "domain:E-Commerce:1.0.0"
44
+ if (typeof value === 'string') return;
45
+
46
+ // Key formats:
47
+ // - "type:id:version" (e.g., "service:OrdersService:0.0.3")
48
+ // - "type:id" (e.g., "user:john", "team:backend") - non-versioned resources
49
+ // - "list:name" (e.g., "list:domains") - skip these
50
+ const parts = key.split(':');
51
+
52
+ // Skip list items
53
+ if (parts[0] === 'list') return;
54
+
55
+ if (parts.length >= 2) {
56
+ // Store as "type:id"
57
+ lookup.set(`${parts[0]}:${parts[1]}`, key);
58
+ }
59
+ });
60
+
61
+ return lookup;
62
+ };
63
+
64
+ /**
65
+ * What the sidebar shows for a URL before it knows the reader's saved drill-down path:
66
+ * the root level on the homepage or for pages without children, otherwise the matching
67
+ * resource's own level (the same "flattened" stack the client falls back to).
68
+ */
69
+ export const getStackForUrl = (
70
+ url: string,
71
+ data: NavigationData,
72
+ nodeLookup: ReadonlyMap<string, string> = buildNodeLookup(data.nodes)
73
+ ): NavigationLevel[] => {
74
+ const root = rootLevel(data.roots);
75
+ if (url === '/' || url === '') return [root];
76
+
77
+ const targetKey = findNodeKeyByUrl(url, data.nodes, nodeLookup);
78
+ const target = targetKey ? resolveNavRef(data.nodes, targetKey) : null;
79
+ if (!targetKey || !target?.pages?.length) return [root];
80
+
81
+ return [root, { key: targetKey, entries: target.pages, title: target.title, badge: target.badge }];
82
+ };
83
+
84
+ /**
85
+ * Everything the sidebar needs to render one page's level, without the whole catalog: the
86
+ * stack for the URL and only the nodes that level shows (its items, and the contents of its
87
+ * groups). Rendered on the server so the sidebar appears with the HTML, and handed to the
88
+ * island so it keeps showing it until the full navigation data loads.
89
+ */
90
+ export type SidebarSnapshot = {
91
+ url: string;
92
+ stack: NavigationLevel[];
93
+ data: NavigationData;
94
+ };
95
+
96
+ const nodeLookupCache = new WeakMap<NavigationData, Map<string, string>>();
97
+
98
+ export const buildSidebarSnapshot = (data: NavigationData, url: string): SidebarSnapshot => {
99
+ let nodeLookup = nodeLookupCache.get(data);
100
+ if (!nodeLookup) {
101
+ nodeLookup = buildNodeLookup(data.nodes);
102
+ nodeLookupCache.set(data, nodeLookup);
103
+ }
104
+
105
+ const stack = getStackForUrl(url, data, nodeLookup);
106
+ const level = stack[stack.length - 1];
107
+ const nodes: NavigationData['nodes'] = {};
108
+
109
+ const include = (ref: ChildRef) => {
110
+ if (typeof ref === 'string') {
111
+ if (ref in nodes) return;
112
+ const value = data.nodes[ref];
113
+ if (value === undefined) return;
114
+ nodes[ref] = value;
115
+ if (typeof value === 'string' && data.nodes[value] !== undefined) nodes[value] = data.nodes[value];
116
+ }
117
+ const node = resolveNavRef(data.nodes, ref);
118
+ // Groups render their children inline; items with children only need their own node
119
+ if (node?.type === 'group') node.pages?.forEach(include);
120
+ };
121
+
122
+ if (level.key) include(level.key);
123
+ level.entries.forEach(include);
124
+
125
+ return { url, stack, data: { roots: data.roots, nodes } };
126
+ };
127
+
7
128
  /**
8
129
  * The user's overrides of each group's default collapse state, keyed by group id.
9
130
  * A group is in at most one set; absence means "use the group's default".
@@ -3,10 +3,18 @@ import type { HTMLAttributes } from 'astro/types';
3
3
 
4
4
  interface Props extends Omit<HTMLAttributes<'div'>, 'children'> {}
5
5
  import NestedSideBar from './NestedSideBar';
6
+ import { buildSidebarSnapshot, stripBasePath } from './NestedSideBar/utils';
7
+ import { getNestedSideBarData } from '@stores/sidebar-store/state';
6
8
  import { ClientRouter } from 'astro:transitions';
9
+
10
+ // Render this page's sidebar level into the HTML so it shows before any JavaScript runs;
11
+ // the full navigation still loads from /api/sidebar-data.json (see SearchDataLoader)
12
+ const snapshot = buildSidebarSnapshot(await getNestedSideBarData(), stripBasePath(Astro.url.pathname));
7
13
  ---
8
14
 
9
15
  <div {...Astro.props}>
10
16
  <ClientRouter />
11
- <NestedSideBar client:only="react" />
17
+ <NestedSideBar client:only="react" snapshot={snapshot}>
18
+ <NestedSideBar slot="fallback" snapshot={snapshot} />
19
+ </NestedSideBar>
12
20
  </div>