@eventcatalog/core 5.0.0-beta.3 → 5.0.0-beta.31

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 (277) 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 +160 -77
  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-BH2F5WPN.js → chunk-2UCCTEIH.js} +1 -1
  21. package/dist/{chunk-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
  22. package/dist/{chunk-TQLRG35T.js → chunk-AD7CL772.js} +78 -45
  23. package/dist/{chunk-NDJOAITI.js → chunk-BNSLGNQI.js} +9 -3
  24. package/dist/chunk-CA6WCQ2T.js +54 -0
  25. package/dist/chunk-CPW6G62V.js +17 -0
  26. package/dist/{chunk-MDWB5HLO.js → chunk-NKNOXQ62.js} +1 -1
  27. package/dist/{chunk-423S2KHN.js → chunk-OUEGYMBA.js} +4 -2
  28. package/dist/{chunk-3PKS2IIP.js → chunk-OX6TSVHY.js} +1 -1
  29. package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
  30. package/dist/chunk-S5GWAUG5.js +29 -0
  31. package/dist/{chunk-GYIVJXOJ.js → chunk-WADZVNCA.js} +1 -1
  32. package/dist/{chunk-6OC46WQJ.js → chunk-XRFL642Z.js} +66 -35
  33. package/dist/constants.cjs +1 -1
  34. package/dist/constants.js +1 -1
  35. package/dist/eventcatalog-config-file-utils.cjs +34 -41
  36. package/dist/eventcatalog-config-file-utils.d.cts +56 -58
  37. package/dist/eventcatalog-config-file-utils.d.ts +56 -58
  38. package/dist/eventcatalog-config-file-utils.js +1 -1
  39. package/dist/eventcatalog.cjs +531 -356
  40. package/dist/eventcatalog.config.d.cts +59 -1
  41. package/dist/eventcatalog.config.d.ts +59 -1
  42. package/dist/eventcatalog.js +63 -25
  43. package/dist/features.cjs +34 -41
  44. package/dist/features.js +2 -2
  45. package/dist/federation/federate.cjs +37 -44
  46. package/dist/federation/federate.js +2 -2
  47. package/dist/generate.cjs +37 -42
  48. package/dist/generate.js +4 -4
  49. package/dist/resolve-catalog-dependencies.cjs +34 -41
  50. package/dist/resolve-catalog-dependencies.js +2 -2
  51. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
  52. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
  53. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
  54. package/dist/runtime-dependencies/@hocuspocus__provider.mjs +3 -0
  55. package/dist/runtime-dependencies/@hocuspocus__server.mjs +3 -0
  56. package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +3 -0
  57. package/dist/runtime-dependencies/@mcp-b__webmcp-polyfill.mjs +3 -0
  58. package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
  59. package/dist/runtime-dependencies/manifest.json +13 -1
  60. package/dist/runtime-dependencies/react-hotkeys-hook.mjs +3 -0
  61. package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
  62. package/dist/runtime-dependencies/y-protocols.mjs +3 -0
  63. package/dist/runtime-dependencies/y-protocols__awareness.mjs +3 -0
  64. package/dist/runtime-dependencies/yjs.mjs +3 -0
  65. package/dist/utils/child-process-signals.cjs +53 -0
  66. package/dist/utils/child-process-signals.d.cts +19 -0
  67. package/dist/utils/child-process-signals.d.ts +19 -0
  68. package/dist/utils/child-process-signals.js +6 -0
  69. package/dist/utils/cli-logger.cjs +1 -1
  70. package/dist/utils/cli-logger.js +2 -2
  71. package/dist/utils/license-status.cjs +78 -45
  72. package/dist/utils/license-status.d.cts +15 -5
  73. package/dist/utils/license-status.d.ts +15 -5
  74. package/dist/utils/license-status.js +1 -1
  75. package/eventcatalog/astro.config.mjs +23 -1
  76. package/eventcatalog/integrations/config-loader.mjs +56 -58
  77. package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
  78. package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
  79. package/eventcatalog/integrations/storage.mjs +20 -0
  80. package/eventcatalog/integrations/studio-server.mjs +32 -0
  81. package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
  82. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
  83. package/eventcatalog/src/components/Header.astro +3 -3
  84. package/eventcatalog/src/components/Homepage/CatalogDashboard.astro +342 -0
  85. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +60 -4
  86. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
  87. package/eventcatalog/src/components/NavigationProgress.astro +7 -0
  88. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
  89. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
  90. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
  91. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
  92. package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
  93. package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
  94. package/eventcatalog/src/components/Search/search-utils.ts +26 -0
  95. package/eventcatalog/src/components/Seo.astro +5 -1
  96. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +53 -48
  97. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +183 -178
  98. package/eventcatalog/src/components/Settings/LicenseSettings.tsx +140 -31
  99. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +40 -35
  100. package/eventcatalog/src/components/Settings/LogoUpload.tsx +1 -1
  101. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +9 -8
  102. package/eventcatalog/src/components/Settings/Row.tsx +7 -4
  103. package/eventcatalog/src/components/Settings/SettingsShared.tsx +3 -3
  104. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +53 -83
  105. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
  106. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +131 -0
  107. package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
  108. package/eventcatalog/src/components/TrialBadge.astro +20 -9
  109. package/eventcatalog/src/content.config.ts +19 -2
  110. package/eventcatalog/src/features/auth/error.astro +4 -2
  111. package/eventcatalog/src/features/auth/login.astro +2 -1
  112. package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +10 -0
  113. package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
  114. package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
  115. package/eventcatalog/src/features/integrations/eventcatalog-features.ts +34 -1
  116. package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
  117. package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +47 -0
  118. package/eventcatalog/src/features/mcp/apps/canvas/styles.css +24 -0
  119. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +278 -0
  120. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +272 -0
  121. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +65 -65
  122. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +2 -1
  123. package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -3
  124. package/eventcatalog/src/features/mcp/mcp-server.ts +36 -5
  125. package/eventcatalog/src/features/server/start.ts +25 -0
  126. package/eventcatalog/src/features/storage/database.ts +88 -0
  127. package/eventcatalog/src/features/storage/migrations.ts +85 -0
  128. package/eventcatalog/src/features/storage/sql/database.sql +5 -0
  129. package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
  130. package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
  131. package/eventcatalog/src/features/storage/sql.ts +28 -0
  132. package/eventcatalog/src/features/studio/agent-choreography.ts +384 -0
  133. package/eventcatalog/src/features/studio/agents.ts +28 -0
  134. package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
  135. package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
  136. package/eventcatalog/src/features/studio/api/client.ts +40 -0
  137. package/eventcatalog/src/features/studio/canvas-actions.ts +540 -0
  138. package/eventcatalog/src/features/studio/canvas-preview.ts +52 -0
  139. package/eventcatalog/src/features/studio/catalog-resources.ts +155 -0
  140. package/eventcatalog/src/features/studio/catalog.ts +173 -0
  141. package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +518 -0
  142. package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +118 -0
  143. package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +434 -0
  144. package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +143 -0
  145. package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
  146. package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
  147. package/eventcatalog/src/features/studio/components/JoinForm.tsx +109 -0
  148. package/eventcatalog/src/features/studio/components/LeftPanel.tsx +206 -0
  149. package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
  150. package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
  151. package/eventcatalog/src/features/studio/components/Presence.tsx +293 -0
  152. package/eventcatalog/src/features/studio/components/ShareDialog.tsx +396 -0
  153. package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
  154. package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +2091 -0
  155. package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
  156. package/eventcatalog/src/features/studio/document-name.ts +7 -0
  157. package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
  158. package/eventcatalog/src/features/studio/hooks/presence-sender.ts +63 -0
  159. package/eventcatalog/src/features/studio/hooks/presence-store.ts +119 -0
  160. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +353 -0
  161. package/eventcatalog/src/features/studio/hooks/use-comments.ts +54 -0
  162. package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +109 -0
  163. package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +782 -0
  164. package/eventcatalog/src/features/studio/identity.ts +59 -0
  165. package/eventcatalog/src/features/studio/limits.ts +2 -0
  166. package/eventcatalog/src/features/studio/pages/[id].astro +69 -0
  167. package/eventcatalog/src/features/studio/pages/index.astro +784 -0
  168. package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +27 -0
  169. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +495 -0
  170. package/eventcatalog/src/features/studio/server/runtime.ts +498 -0
  171. package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
  172. package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
  173. package/eventcatalog/src/features/studio/server/storage.ts +90 -0
  174. package/eventcatalog/src/features/studio/server/tool-icons.ts +30 -0
  175. package/eventcatalog/src/features/studio/tool-sync.ts +75 -0
  176. package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
  177. package/eventcatalog/src/layouts/SettingsLayout.astro +2 -2
  178. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -5
  179. package/eventcatalog/src/pages/_index.astro +5 -342
  180. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +2 -3
  181. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +0 -7
  182. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -1
  183. package/eventcatalog/src/pages/diagrams/[id]/[version]/_index.data.ts +0 -7
  184. package/eventcatalog/src/pages/diagrams/[id]/[version]/embed.astro +1 -0
  185. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +1 -0
  186. package/eventcatalog/src/pages/directory/[type]/_index.data.ts +5 -7
  187. package/eventcatalog/src/pages/directory/[type]/index.astro +3 -1
  188. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -7
  189. package/eventcatalog/src/pages/discover/[type]/index.astro +3 -1
  190. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +0 -7
  191. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -0
  192. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +0 -7
  193. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +1 -0
  194. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +0 -7
  195. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +3 -1
  196. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/_[filename].data.ts +0 -7
  197. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +0 -7
  198. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -0
  199. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +1 -0
  200. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/_[...filename].data.ts +0 -4
  201. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.ts +0 -7
  202. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/index.astro +1 -0
  203. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +3 -1
  204. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/_[filename].data.ts +0 -7
  205. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
  206. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +2 -3
  207. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +0 -7
  208. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +1 -0
  209. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +1 -0
  210. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/_[filename].data.ts +0 -7
  211. package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +0 -7
  212. package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +2 -1
  213. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/_index.data.ts +0 -7
  214. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -0
  215. package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +0 -7
  216. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +1 -0
  217. package/eventcatalog/src/pages/docs/custom/[...path]/_index.data.ts +0 -7
  218. package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +1 -0
  219. package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +1 -1
  220. package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +1 -0
  221. package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +1 -1
  222. package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +1 -1
  223. package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +1 -1
  224. package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +1 -1
  225. package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +0 -7
  226. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +1 -0
  227. package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +0 -7
  228. package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -0
  229. package/eventcatalog/src/pages/index.astro +4 -2
  230. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +0 -7
  231. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +1 -0
  232. package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +0 -7
  233. package/eventcatalog/src/pages/schemas/explorer/index.astro +4 -2
  234. package/eventcatalog/src/pages/settings/license.astro +9 -1
  235. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/_index.data.ts +0 -7
  236. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +1 -0
  237. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +0 -7
  238. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +0 -7
  239. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +1 -0
  240. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/_index.data.ts +0 -7
  241. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +1 -0
  242. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +1 -0
  243. package/eventcatalog/src/pages/visualiser/[type]/[id]/_index.data.ts +38 -0
  244. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +18 -41
  245. package/eventcatalog/src/pages/visualiser/designs/[id]/_index.data.ts +0 -7
  246. package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +2 -1
  247. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +0 -7
  248. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +1 -0
  249. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/index.astro +1 -0
  250. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/_index.data.ts +0 -7
  251. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/index.astro +1 -0
  252. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/_index.data.ts +0 -7
  253. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/index.astro +1 -0
  254. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/index.astro +1 -0
  255. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
  256. package/eventcatalog/src/utils/collections/util.ts +8 -4
  257. package/eventcatalog/src/utils/feature.ts +8 -0
  258. package/eventcatalog/src/utils/git.ts +22 -5
  259. package/eventcatalog/src/utils/homepage-dashboard.ts +143 -0
  260. package/eventcatalog/src/utils/license.ts +1 -1
  261. package/eventcatalog/src/utils/link-validation.ts +25 -19
  262. package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
  263. package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
  264. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
  265. package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +4 -2
  266. package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
  267. package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
  268. package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
  269. package/eventcatalog/src/utils/page-loaders/hybrid-page.ts +4 -15
  270. package/eventcatalog/src/utils/page-title.ts +15 -0
  271. package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
  272. package/eventcatalog/src/utils/trial.ts +14 -1
  273. package/package.json +19 -13
  274. package/dist/runtime-dependencies/shelljs.mjs +0 -3
  275. package/eventcatalog/public/studio-bg.png +0 -0
  276. package/eventcatalog/src/components/Studio/StudioPageModal.tsx +0 -150
  277. package/eventcatalog/src/pages/studio.astro +0 -316
@@ -94,16 +94,16 @@ const providerName = user?.provider ? providerNames[user.provider] || user.provi
94
94
  <img
95
95
  src={userImage}
96
96
  alt=""
97
- class="h-8 w-8 rounded-full object-cover ring-1 ring-[rgb(var(--ec-page-border))]"
97
+ class="h-7 w-7 rounded-full object-cover ring-1 ring-[rgb(var(--ec-page-border))]"
98
98
  />
99
99
  ) : (
100
- <span class="h-8 w-8 rounded-full bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))] ring-1 ring-[rgb(var(--ec-page-border))] flex items-center justify-center text-xs font-semibold">
100
+ <span class="h-7 w-7 rounded-full bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))] ring-1 ring-[rgb(var(--ec-page-border))] flex items-center justify-center text-[11px] font-semibold">
101
101
  {userInitials}
102
102
  </span>
103
103
  )}
104
104
  <svg
105
105
  viewBox="0 0 24 24"
106
- class="h-4 w-4 transition-transform group-aria-expanded:rotate-180"
106
+ class="h-3.5 w-3.5 transition-transform group-aria-expanded:rotate-180"
107
107
  fill="none"
108
108
  stroke="currentColor"
109
109
  stroke-width="2"
@@ -0,0 +1,342 @@
1
+ ---
2
+ import config from '@config';
3
+ import { buildUrl } from '@utils/url-builder';
4
+ import { getColorForCollection } from '@utils/collection-colors';
5
+ import { isArchitectureGraphEnabled, isEventCatalogChatEnabled } from '@utils/feature';
6
+ import CatalogForceGraph from '@components/CatalogGraph/CatalogForceGraph';
7
+ import { getCatalogForceGraph, toCatalogForceGraphWire } from '@utils/node-graphs/catalog-force-graph';
8
+ import { getDashboard, type DashboardCollection } from '@utils/homepage-dashboard';
9
+ import { Boxes, Clock, FileJson, Mail, Network, Search, Server, Sparkles, Users, Waypoints } from 'lucide-react';
10
+
11
+ interface Props {
12
+ /** The heading, the catalog's title by default */
13
+ title?: string;
14
+ /** Under the heading, the catalog's tagline by default */
15
+ description?: string;
16
+ }
17
+
18
+ const {
19
+ title = config?.title || config?.organizationName || 'EventCatalog',
20
+ description = config?.tagline || 'Explore and understand your event-driven architecture.',
21
+ } = Astro.props;
22
+
23
+ const dashboard = await getDashboard();
24
+ const { counts, recentlyUpdated } = dashboard;
25
+ const chatEnabled = isEventCatalogChatEnabled();
26
+ // The architecture graph leads the dashboard; it's opt-in while in beta, and without it the dashboard shows what changed recently
27
+ const graph = isArchitectureGraphEnabled() ? toCatalogForceGraphWire(await getCatalogForceGraph()) : undefined;
28
+
29
+ const TYPE_LABELS: Record<DashboardCollection, string> = {
30
+ domains: 'Domain',
31
+ services: 'Service',
32
+ events: 'Event',
33
+ commands: 'Command',
34
+ queries: 'Query',
35
+ flows: 'Flow',
36
+ };
37
+ // The colour each kind of resource has across the catalog, from the theme's badge colours
38
+ const badgeStyle = (collection: string) => {
39
+ const color = getColorForCollection(collection);
40
+ return `color: rgb(var(--ec-badge-color-${color}-text)); background: rgb(var(--ec-badge-color-${color}-background));`;
41
+ };
42
+
43
+ const stats = [
44
+ { label: 'Domains', value: counts.domains, href: buildUrl('/discover/domains'), icon: Boxes },
45
+ { label: 'Services', value: counts.services, href: buildUrl('/discover/services'), icon: Server },
46
+ { label: 'Messages', value: counts.messages, href: buildUrl('/discover/events'), icon: Mail },
47
+ { label: 'Flows', value: counts.flows, href: buildUrl('/discover/flows'), icon: Waypoints },
48
+ { label: 'Schemas', value: counts.schemas, href: buildUrl('/schemas/explorer'), icon: FileJson },
49
+ { label: 'Teams', value: counts.teams, href: buildUrl('/directory/teams'), icon: Users },
50
+ ];
51
+
52
+ const formatDate = (timestamp: number) =>
53
+ new Date(timestamp).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
54
+ ---
55
+
56
+ <div class="ec-dashboard">
57
+ <!-- Header -->
58
+ <header class="flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between">
59
+ <div class="min-w-0">
60
+ <h1 class="text-[26px] font-semibold leading-tight tracking-tight text-[rgb(var(--ec-page-text))] md:text-[30px]">
61
+ {title}
62
+ </h1>
63
+ <p class="mt-1.5 max-w-2xl text-[14px] leading-relaxed text-[rgb(var(--ec-page-text-muted))]">
64
+ {description}
65
+ </p>
66
+ </div>
67
+ <div class="flex w-full items-center gap-2 lg:w-auto">
68
+ <button type="button" data-dashboard-search class="ec-dashboard-search group">
69
+ <Search className="h-4 w-4 text-[rgb(var(--ec-page-text-muted))]" />
70
+ <span class="flex-1 text-left">Search the catalog</span>
71
+ <kbd class="ec-dashboard-kbd">⌘K</kbd>
72
+ </button>
73
+ {
74
+ chatEnabled && (
75
+ <button type="button" data-dashboard-ask class="ec-dashboard-button">
76
+ <Sparkles className="h-4 w-4" />
77
+ Ask
78
+ </button>
79
+ )
80
+ }
81
+ </div>
82
+ </header>
83
+
84
+ <!-- Counts -->
85
+ <section class="ec-dashboard-stats mt-8" aria-label="Catalog at a glance">
86
+ {
87
+ stats.map((stat) => (
88
+ <a href={stat.href} class="ec-dashboard-card ec-dashboard-stat group">
89
+ <div class="flex items-center justify-between">
90
+ <span class="text-[12px] font-medium text-[rgb(var(--ec-page-text-muted))]">{stat.label}</span>
91
+ <stat.icon className="h-4 w-4 text-[rgb(var(--ec-page-text-muted))] transition-colors group-hover:text-[rgb(var(--ec-page-text))]" />
92
+ </div>
93
+ <span class="mt-2 block text-[28px] font-semibold leading-none tracking-tight tabular-nums text-[rgb(var(--ec-page-text))]">
94
+ {stat.value}
95
+ </span>
96
+ </a>
97
+ ))
98
+ }
99
+ </section>
100
+
101
+ <!-- The architecture graph, or what changed recently without it -->
102
+ <div class="mt-6">
103
+ {
104
+ graph ? (
105
+ <section class="ec-dashboard-card overflow-hidden">
106
+ <div class="ec-dashboard-card-header">
107
+ <h2 class="ec-dashboard-title">
108
+ <Network className="h-4 w-4" /> Architecture
109
+ </h2>
110
+ </div>
111
+ <div class="relative h-[34rem] border-t border-[rgb(var(--ec-page-border))]">
112
+ <CatalogForceGraph
113
+ client:only="react"
114
+ nodes={graph.nodes}
115
+ links={graph.links}
116
+ linkLabels={graph.linkLabels}
117
+ initialLens="domains"
118
+ showSearch
119
+ showLegend
120
+ showLensPicker
121
+ syncUrl={false}
122
+ zoomOnScroll={false}
123
+ openInGraphUrl={buildUrl('/visualiser/graph')}
124
+ openInGraphLabel="Open full screen"
125
+ />
126
+ </div>
127
+ </section>
128
+ ) : (
129
+ <section class="ec-dashboard-card">
130
+ <div class="ec-dashboard-card-header">
131
+ <h2 class="ec-dashboard-title">
132
+ <Clock className="h-4 w-4" /> Recently updated
133
+ </h2>
134
+ </div>
135
+ {recentlyUpdated.length === 0 ? (
136
+ <p class="px-5 pb-5 text-[13px] text-[rgb(var(--ec-page-text-muted))]">Nothing to show yet.</p>
137
+ ) : (
138
+ <ul class="divide-y divide-[rgb(var(--ec-page-border))]">
139
+ {recentlyUpdated.map((resource) => (
140
+ <li>
141
+ <a href={resource.href} class="ec-dashboard-row">
142
+ <span class="ec-dashboard-type" style={badgeStyle(resource.collection)}>
143
+ {TYPE_LABELS[resource.collection]}
144
+ </span>
145
+ <span class="min-w-0 flex-1">
146
+ <span class="block truncate text-[13.5px] font-medium text-[rgb(var(--ec-page-text))]">
147
+ {resource.name}
148
+ <span class="ml-1.5 font-mono text-[11.5px] font-normal text-[rgb(var(--ec-page-text-muted))]">
149
+ v{resource.version}
150
+ </span>
151
+ </span>
152
+ {resource.summary && (
153
+ <span class="block truncate text-[12.5px] text-[rgb(var(--ec-page-text-muted))]">{resource.summary}</span>
154
+ )}
155
+ </span>
156
+ {resource.owners[0] && (
157
+ <span class="hidden shrink-0 text-[12px] text-[rgb(var(--ec-page-text-muted))] md:block">
158
+ {resource.owners[0].name}
159
+ </span>
160
+ )}
161
+ <time
162
+ class="w-24 shrink-0 text-right text-[12px] tabular-nums text-[rgb(var(--ec-page-text-muted))]"
163
+ datetime={new Date(resource.updatedAt!).toISOString()}
164
+ data-relative-time
165
+ >
166
+ {formatDate(resource.updatedAt!)}
167
+ </time>
168
+ </a>
169
+ </li>
170
+ ))}
171
+ </ul>
172
+ )}
173
+ </section>
174
+ )
175
+ }
176
+ </div>
177
+ </div>
178
+
179
+ <script>
180
+ // The search bar and Ask button open the catalog's search and assistant
181
+ const bind = () => {
182
+ document.querySelector('[data-dashboard-search]')?.addEventListener('click', () => {
183
+ (window as any).searchModalState?.open();
184
+ });
185
+ document.querySelector('[data-dashboard-ask]')?.addEventListener('click', () => {
186
+ window.dispatchEvent(new CustomEvent('eventcatalog:open-chat'));
187
+ });
188
+
189
+ // "3 days ago", counted in the browser so a prerendered page stays current
190
+ const format = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' });
191
+ const units: [Intl.RelativeTimeFormatUnit, number][] = [
192
+ ['year', 365 * 24 * 3600],
193
+ ['month', 30 * 24 * 3600],
194
+ ['week', 7 * 24 * 3600],
195
+ ['day', 24 * 3600],
196
+ ['hour', 3600],
197
+ ['minute', 60],
198
+ ];
199
+ document.querySelectorAll<HTMLTimeElement>('time[data-relative-time]').forEach((time) => {
200
+ const seconds = (new Date(time.dateTime).getTime() - Date.now()) / 1000;
201
+ const [unit, size] = units.find(([, size]) => Math.abs(seconds) >= size) ?? ['minute', 60];
202
+ time.textContent = format.format(Math.round(seconds / size), unit);
203
+ time.title = new Date(time.dateTime).toLocaleString();
204
+ });
205
+ };
206
+ bind();
207
+ document.addEventListener('astro:page-load', bind);
208
+ </script>
209
+
210
+ <style is:global>
211
+ .ec-dashboard {
212
+ max-width: 96rem;
213
+ padding-bottom: 2.5rem;
214
+ }
215
+ .ec-dashboard-card {
216
+ border: 1px solid rgb(var(--ec-page-border));
217
+ border-radius: 0.875rem;
218
+ background: rgb(var(--ec-card-bg, var(--ec-page-bg)));
219
+ }
220
+ .ec-dashboard-card-header {
221
+ display: flex;
222
+ align-items: center;
223
+ justify-content: space-between;
224
+ gap: 0.75rem;
225
+ padding: 1rem 1.25rem 0.75rem;
226
+ }
227
+ .ec-dashboard-title {
228
+ display: inline-flex;
229
+ align-items: center;
230
+ gap: 0.5rem;
231
+ font-size: 13.5px;
232
+ font-weight: 600;
233
+ color: rgb(var(--ec-page-text));
234
+ }
235
+ .ec-dashboard-title svg {
236
+ color: rgb(var(--ec-page-text-muted));
237
+ }
238
+ .ec-dashboard-link {
239
+ display: inline-flex;
240
+ align-items: center;
241
+ gap: 0.25rem;
242
+ font-size: 12px;
243
+ font-weight: 500;
244
+ color: rgb(var(--ec-page-text-muted));
245
+ }
246
+ .ec-dashboard-link:hover {
247
+ color: rgb(var(--ec-page-text));
248
+ }
249
+ .ec-dashboard-search {
250
+ display: flex;
251
+ flex: 1;
252
+ align-items: center;
253
+ gap: 0.625rem;
254
+ min-width: 0;
255
+ height: 2.5rem;
256
+ padding: 0 0.75rem;
257
+ border: 1px solid rgb(var(--ec-page-border));
258
+ border-radius: 0.625rem;
259
+ background: rgb(var(--ec-card-bg, var(--ec-page-bg)));
260
+ font-size: 13px;
261
+ color: rgb(var(--ec-page-text-muted));
262
+ transition: border-color 150ms;
263
+ }
264
+ @media (min-width: 1024px) {
265
+ .ec-dashboard-search {
266
+ width: 22rem;
267
+ flex: none;
268
+ }
269
+ }
270
+ .ec-dashboard-search:hover {
271
+ border-color: rgb(var(--ec-page-text) / 0.35);
272
+ }
273
+ .ec-dashboard-kbd {
274
+ border: 1px solid rgb(var(--ec-page-border));
275
+ border-radius: 0.375rem;
276
+ padding: 0.05rem 0.375rem;
277
+ font-family: inherit;
278
+ font-size: 11px;
279
+ }
280
+ .ec-dashboard-button {
281
+ display: inline-flex;
282
+ align-items: center;
283
+ gap: 0.375rem;
284
+ height: 2.5rem;
285
+ padding: 0 0.875rem;
286
+ border-radius: 0.625rem;
287
+ background: rgb(var(--ec-page-text));
288
+ font-size: 13px;
289
+ font-weight: 500;
290
+ color: rgb(var(--ec-page-bg));
291
+ transition: opacity 150ms;
292
+ }
293
+ .ec-dashboard-button:hover {
294
+ opacity: 0.85;
295
+ }
296
+ .ec-dashboard-stats {
297
+ display: grid;
298
+ grid-template-columns: repeat(2, minmax(0, 1fr));
299
+ gap: 0.75rem;
300
+ }
301
+ @media (min-width: 768px) {
302
+ .ec-dashboard-stats {
303
+ grid-template-columns: repeat(3, minmax(0, 1fr));
304
+ }
305
+ }
306
+ @media (min-width: 1280px) {
307
+ .ec-dashboard-stats {
308
+ grid-template-columns: repeat(6, minmax(0, 1fr));
309
+ }
310
+ }
311
+ .ec-dashboard-stat {
312
+ display: block;
313
+ padding: 1rem 1.1rem;
314
+ transition: border-color 150ms;
315
+ }
316
+ .ec-dashboard-stat:hover {
317
+ border-color: rgb(var(--ec-page-text) / 0.35);
318
+ }
319
+ .ec-dashboard-row {
320
+ display: flex;
321
+ align-items: center;
322
+ gap: 0.875rem;
323
+ padding: 0.7rem 1.25rem;
324
+ transition: background 150ms;
325
+ }
326
+ .ec-dashboard-row:hover {
327
+ background: rgb(var(--ec-page-text) / 0.03);
328
+ }
329
+ .ec-dashboard-type {
330
+ flex-shrink: 0;
331
+ width: 4.75rem;
332
+ border-radius: 0.375rem;
333
+ padding: 0.15rem 0;
334
+ text-align: center;
335
+ font-size: 11px;
336
+ font-weight: 600;
337
+ }
338
+ @media (min-width: 640px) {
339
+ }
340
+ @media (min-width: 1280px) {
341
+ }
342
+ </style>
@@ -12,15 +12,38 @@
12
12
  * CSS ships in the page head and survives ClientRouter navigations (see NodeGraph.astro).
13
13
  */
14
14
 
15
- import { useCallback, lazy, Suspense } from 'react';
15
+ import { useCallback, useEffect, useState, lazy, Suspense } from 'react';
16
+ import { createPortal } from 'react-dom';
16
17
  import type { Node, Edge } from '@xyflow/react';
17
- import type { FlowGroupBy } from '@eventcatalog/visualiser';
18
+ import type { FlowGroupBy, OpenInStudioRequest } from '@eventcatalog/visualiser';
18
19
  import { buildUrl } from '@utils/url-builder';
20
+ import { createCanvasThroughApi } from '@features/studio/api/client';
21
+ import { getStoredName } from '@features/studio/identity';
22
+
23
+ // Studio's canvas code (what turns a diagram into a canvas, and the dialog asking for its name) is only loaded when a
24
+ // diagram is opened in Studio: every page with a diagram would load it otherwise, with Studio on or not
25
+ const OpenInStudioDialog = lazy(() => import('@features/studio/components/OpenInStudioDialog'));
19
26
 
20
27
  const NodeGraph = lazy(() =>
21
28
  import('@eventcatalog/visualiser').then((module) => ({ default: module.NodeGraph }))
22
29
  ) as React.LazyExoticComponent<React.ComponentType<AstroNodeGraphProps & Record<string, unknown>>>;
23
30
 
31
+ /**
32
+ * "Loading graph..." in the box the graph is drawn into, while the visualiser loads. Not where this island sits: the
33
+ * graph is portalled from there, and on the homepage that's beside the page's content (top right).
34
+ */
35
+ const LoadingGraph = ({ portalId }: { portalId: string }) => {
36
+ const [container, setContainer] = useState<HTMLElement | null>(null);
37
+ useEffect(() => setContainer(document.getElementById(portalId)), [portalId]);
38
+ if (!container) return null;
39
+ return createPortal(
40
+ <div className="flex h-full min-h-[8rem] items-center justify-center text-sm text-[rgb(var(--ec-page-text-muted))]">
41
+ Loading graph...
42
+ </div>,
43
+ container
44
+ );
45
+ };
46
+
24
47
  interface AstroNodeGraphProps {
25
48
  id: string;
26
49
  nodes: Node[];
@@ -57,9 +80,11 @@ interface AstroNodeGraphProps {
57
80
  preferenceScope?: string;
58
81
  // Group a flow's steps into swimlanes to start with
59
82
  swimlanes?: FlowGroupBy;
83
+ // The Studio API's address, when Studio is on: the menu can then start a canvas from the diagram
84
+ studioApiUrl?: string;
60
85
  }
61
86
 
62
- const AstroNodeGraph = ({ isDevMode = false, resourceKey, ...otherProps }: AstroNodeGraphProps) => {
87
+ const AstroNodeGraph = ({ isDevMode = false, resourceKey, studioApiUrl, ...otherProps }: AstroNodeGraphProps) => {
63
88
  // Astro-specific navigation handler
64
89
  const handleNavigate = useCallback((url: string) => {
65
90
  // Use window.location for navigation since we can't import astro:transitions/client in a React component
@@ -108,8 +133,32 @@ const AstroNodeGraph = ({ isDevMode = false, resourceKey, ...otherProps }: Astro
108
133
  }
109
134
  }, []);
110
135
 
136
+ // Opening the diagram in Studio: asks what to call the canvas, then makes it with what the diagram shows and opens
137
+ // it (in this tab) at the level shown. What it shows is only taken once it's named (laying a level out can take a
138
+ // moment), and anything that goes wrong is shown in the dialog.
139
+ const [toOpen, setToOpen] = useState<OpenInStudioRequest | null>(null);
140
+ const closeOpenInStudio = useCallback(() => setToOpen(null), []);
141
+ const openInStudio = useCallback(
142
+ async (title: string | undefined) => {
143
+ if (!studioApiUrl || !toOpen) return;
144
+ const snapshot = await toOpen.getSnapshot();
145
+ const { canvasFromVisualiser } = await import('@features/studio/from-visualiser');
146
+ const { nodes, edges, levels } = canvasFromVisualiser(snapshot);
147
+ if (nodes.length === 0) throw new Error('Nothing on this diagram can go on a canvas yet');
148
+ const canvas = await createCanvasThroughApi(studioApiUrl, {
149
+ title,
150
+ createdBy: getStoredName() ?? undefined,
151
+ nodes,
152
+ edges,
153
+ levels,
154
+ });
155
+ window.location.assign(snapshot.level === 3 ? canvas.url : `${canvas.url}?level=${snapshot.level}`);
156
+ },
157
+ [studioApiUrl, toOpen]
158
+ );
159
+
111
160
  return (
112
- <Suspense fallback={<div>Loading graph...</div>}>
161
+ <Suspense fallback={<LoadingGraph portalId={otherProps.portalId || `${otherProps.id}-portal`} />}>
113
162
  <NodeGraph
114
163
  {...otherProps}
115
164
  resourceKey={resourceKey}
@@ -118,7 +167,14 @@ const AstroNodeGraph = ({ isDevMode = false, resourceKey, ...otherProps }: Astro
118
167
  onBuildUrl={handleBuildUrl}
119
168
  onSaveLayout={handleSaveLayout}
120
169
  onResetLayout={handleResetLayout}
170
+ onOpenInStudio={studioApiUrl ? setToOpen : undefined}
121
171
  />
172
+ {toOpen && (
173
+ // Its own, so the diagram isn't swapped for "Loading graph..." while the dialog loads
174
+ <Suspense fallback={null}>
175
+ <OpenInStudioDialog title={toOpen.title} onOpen={openInStudio} onClose={closeOpenInStudio} />
176
+ </Suspense>
177
+ )}
122
178
  </Suspense>
123
179
  );
124
180
  };
@@ -31,11 +31,14 @@ import { pageDataLoader } from '@utils/page-loaders/page-data-loader';
31
31
  import { getNodesAndEdges as getNodesAndEdgesForContainer } from '@utils/node-graphs/container-node-graph';
32
32
  import { getNodesAndEdges as getNodesAndEdgesForChannel } from '@utils/node-graphs/channel-node-graph';
33
33
  import config from '@config';
34
- import { isEventCatalogChatVisible, isDevMode } from '@utils/feature';
34
+ import { isEventCatalogChatVisible, isDevMode, isCanvasEnabled } from '@utils/feature';
35
+ import { CANVASES_API_PATH } from '@features/studio/api/canvases-api';
35
36
  import { loadSavedLayout, applyLayoutToNodes, buildResourceKey } from '@utils/node-graphs/layout-persistence';
36
37
  import { compactVisualiserGraph } from '@utils/node-graphs/compact-visualiser-graph';
37
38
 
38
39
  const isChatEnabled = isEventCatalogChatVisible();
40
+ // With Studio on, the menu can start a canvas from the diagram
41
+ const studioApiUrl = isCanvasEnabled() ? buildUrl(CANVASES_API_PATH, true) : undefined;
39
42
 
40
43
  interface Props {
41
44
  id: string;
@@ -257,6 +260,7 @@ if (overview && levelsOverview) {
257
260
  hiddenMessagesGraph={hiddenMessagesGraph}
258
261
  preferenceScope={collection}
259
262
  swimlanes={lanes}
263
+ studioApiUrl={studioApiUrl}
260
264
  />
261
265
  </div>
262
266
 
@@ -22,12 +22,19 @@
22
22
  opacity: 1;
23
23
  }
24
24
 
25
+ /* On every page, hidden until a navigation takes a while: paused until then (a running animation costs a style
26
+ recalc and paint on every frame, even when it can't be seen) */
25
27
  .navigation-progress__bar {
26
28
  width: 35%;
27
29
  height: 100%;
28
30
  background: rgb(var(--ec-accent));
29
31
  box-shadow: 0 0 10px rgb(var(--ec-accent) / 0.55);
30
32
  animation: navigation-progress 1.1s ease-in-out infinite;
33
+ animation-play-state: paused;
34
+ }
35
+
36
+ #navigation-progress[data-visible='true'] .navigation-progress__bar {
37
+ animation-play-state: running;
31
38
  }
32
39
 
33
40
  :global([data-navigation-pending='true']) {
@@ -4,6 +4,7 @@ import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
4
4
  import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5
5
  import { getLanguageForHighlight, getSchemaTypeLabel } from './utils';
6
6
  import { useDarkMode } from './useDarkMode';
7
+ import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
7
8
  import type { SchemaItem } from './types';
8
9
 
9
10
  interface ApiContentViewerProps {
@@ -135,6 +136,7 @@ export default function ApiContentViewer({ message, onCopy, copiedId }: ApiConte
135
136
 
136
137
  <div className="border-t" style={{ borderColor: dividerColor }}>
137
138
  <SyntaxHighlighter
139
+ renderer={renderHighlightedRows}
138
140
  language={getLanguageForHighlight(message.schemaExtension)}
139
141
  style={isDarkMode ? oneDark : oneLight}
140
142
  customStyle={{
@@ -13,6 +13,7 @@ import { MarkdownCodeGroup } from '../MDX/CodeGroup/MarkdownCodeGroup';
13
13
  import { MarkdownColumns, MarkdownColumn } from '../MDX/Columns/MarkdownColumns';
14
14
  import { remarkMarkdownColumns } from '../../remark-plugins/markdown-columns';
15
15
  import { getLanguageForHighlight } from './utils';
16
+ import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
16
17
 
17
18
  interface ExamplesViewerProps {
18
19
  examples: MessageExample[];
@@ -40,6 +41,7 @@ function CodeBlock({ language, code }: { language?: string; code: string }) {
40
41
  const content = code.replace(/\s+$/, '');
41
42
  const highlighter = (
42
43
  <SyntaxHighlighter
44
+ renderer={renderHighlightedRows}
43
45
  language={language || 'text'}
44
46
  style={isDarkMode ? oneDark : oneLight}
45
47
  customStyle={{
@@ -6,6 +6,7 @@ import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
6
6
  import { getLanguageForHighlight } from './utils';
7
7
  import type { SchemaItem } from './types';
8
8
  import { useDarkMode } from './useDarkMode';
9
+ import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
9
10
 
10
11
  interface SchemaCodeModalProps {
11
12
  isOpen: boolean;
@@ -60,6 +61,7 @@ export default function SchemaCodeModal({ isOpen, onOpenChange, message, onCopy,
60
61
  {/* Content */}
61
62
  <div className="flex-1 overflow-auto p-6">
62
63
  <SyntaxHighlighter
64
+ renderer={renderHighlightedRows}
63
65
  language={getLanguageForHighlight(message.schemaExtension)}
64
66
  style={isDarkMode ? oneDark : oneLight}
65
67
  customStyle={{
@@ -9,6 +9,7 @@ import ProtobufSchemaViewer from './ProtobufSchemaViewer';
9
9
  import { getLanguageForHighlight, SCHEMA_CODE_STYLE } from './utils';
10
10
  import type { SchemaItem } from './types';
11
11
  import { useDarkMode } from './useDarkMode';
12
+ import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
12
13
 
13
14
  interface SchemaContentViewerProps {
14
15
  message: SchemaItem;
@@ -112,6 +113,7 @@ export default function SchemaContentViewer({
112
113
 
113
114
  <div className="min-h-0 flex-1 overflow-auto">
114
115
  <SyntaxHighlighter
116
+ renderer={renderHighlightedRows}
115
117
  language={getLanguageForHighlight(message.schemaExtension)}
116
118
  style={isDarkMode ? oneDark : oneLight}
117
119
  customStyle={{ ...SCHEMA_CODE_STYLE, height: '100%' }}
@@ -16,34 +16,22 @@
16
16
  import { setSidebarData, sidebarStore } from '@stores/sidebar-store';
17
17
  import { buildUrl } from '@utils/url-builder';
18
18
 
19
- // Only fetch if we haven't already loaded the data
19
+ // Only fetch if we haven't already loaded the data. Fetched straight away rather than when
20
+ // the browser is idle: the sidebar's first paint comes from the server (see SideNav.astro),
21
+ // but drilling down and search wait on this data
20
22
  if (!sidebarStore.get()) {
21
- const apiUrl = buildUrl('/api/sidebar-data.json', true);
22
-
23
- const loadSidebarData = () => {
24
- // Double-check cache in case another component loaded data while waiting for idle
25
- if (sidebarStore.get()) return;
26
-
27
- fetch(apiUrl)
28
- .then((response) => {
29
- if (!response.ok) {
30
- throw new Error(`Failed to fetch sidebar data: ${response.status}`);
31
- }
32
- return response.json();
33
- })
34
- .then((data) => {
35
- setSidebarData(data);
36
- })
37
- .catch((error) => {
38
- console.error('Error loading sidebar data:', error);
39
- });
40
- };
41
-
42
- // Defer non-critical sidebar payload fetch until browser is idle
43
- if (typeof window.requestIdleCallback === 'function') {
44
- window.requestIdleCallback(loadSidebarData, { timeout: 1200 });
45
- } else {
46
- setTimeout(loadSidebarData, 300);
47
- }
23
+ fetch(buildUrl('/api/sidebar-data.json', true))
24
+ .then((response) => {
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch sidebar data: ${response.status}`);
27
+ }
28
+ return response.json();
29
+ })
30
+ .then((data) => {
31
+ setSidebarData(data);
32
+ })
33
+ .catch((error) => {
34
+ console.error('Error loading sidebar data:', error);
35
+ });
48
36
  }
49
37
  </script>
@@ -34,6 +34,7 @@ import {
34
34
  getUrlForSearchItem,
35
35
  highlightQuery,
36
36
  mapPagefindResultsToSearchItems,
37
+ rankResourceSearchItems,
37
38
  type SearchItem,
38
39
  type SearchNode,
39
40
  } from './search-utils';
@@ -316,16 +317,7 @@ export default function SearchModal() {
316
317
  return items;
317
318
  }
318
319
 
319
- const lowerQuery = query.toLowerCase();
320
- return items.filter((item) => {
321
- if (item.name.toLowerCase().includes(lowerQuery)) return true;
322
- // Key format is "type:id:version" (e.g. "service:OrdersService:0.0.3").
323
- // Match against the id so users can find resources by their raw id too.
324
- const keyParts = item.key?.split(':') ?? [];
325
- const id = keyParts[1];
326
- if (id?.toLowerCase().includes(lowerQuery)) return true;
327
- return !!item.rawNode.summary && item.rawNode.summary.toLowerCase().includes(lowerQuery);
328
- });
320
+ return rankResourceSearchItems(items, query);
329
321
  }, [indexedItems, isIndexedSearch, items, query]);
330
322
 
331
323
  const filters = useMemo(() => {