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

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-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
  21. package/dist/{chunk-6OC46WQJ.js → chunk-55QETQZ3.js} +66 -35
  22. package/dist/{chunk-TQLRG35T.js → chunk-AD7CL772.js} +78 -45
  23. package/dist/chunk-CA6WCQ2T.js +54 -0
  24. package/dist/chunk-CPW6G62V.js +17 -0
  25. package/dist/{chunk-423S2KHN.js → chunk-FLAZ2JVN.js} +4 -2
  26. package/dist/{chunk-BH2F5WPN.js → chunk-GQI5UYXZ.js} +1 -1
  27. package/dist/{chunk-GYIVJXOJ.js → chunk-NA7OTBWW.js} +1 -1
  28. package/dist/{chunk-MDWB5HLO.js → chunk-NKNOXQ62.js} +1 -1
  29. package/dist/{chunk-3PKS2IIP.js → chunk-OX6TSVHY.js} +1 -1
  30. package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
  31. package/dist/chunk-S5GWAUG5.js +29 -0
  32. package/dist/{chunk-NDJOAITI.js → chunk-T3OROMAU.js} +9 -3
  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
@@ -168,6 +168,32 @@ export const getIndexedResultRank = ({
168
168
  return 0;
169
169
  };
170
170
 
171
+ /**
172
+ * How well a resource matches a resource search query: an exact name (5), a name starting with
173
+ * the query (4) or containing it (3), then the id (2), then the summary (1). 0 is no match.
174
+ */
175
+ export const getResourceResultRank = (item: Pick<SearchItem, 'name' | 'key' | 'rawNode'>, query: string) => {
176
+ const lowerQuery = query.toLowerCase();
177
+ const name = item.name.toLowerCase();
178
+ if (name === lowerQuery) return 5;
179
+ if (name.startsWith(lowerQuery)) return 4;
180
+ if (name.includes(lowerQuery)) return 3;
181
+ // Key format is "type:id:version" (e.g. "service:OrdersService:0.0.3").
182
+ // Match against the id so users can find resources by their raw id too.
183
+ const id = item.key?.split(':')[1];
184
+ if (id?.toLowerCase().includes(lowerQuery)) return 2;
185
+ if (item.rawNode.summary?.toLowerCase().includes(lowerQuery)) return 1;
186
+ return 0;
187
+ };
188
+
189
+ /** Resources that match a resource search query, best matches first (index order within a rank) */
190
+ export const rankResourceSearchItems = <T extends Pick<SearchItem, 'name' | 'key' | 'rawNode'>>(items: T[], query: string) =>
191
+ items
192
+ .map((item, index) => ({ item, index, rank: getResourceResultRank(item, query) }))
193
+ .filter(({ rank }) => rank > 0)
194
+ .sort((a, b) => b.rank - a.rank || a.index - b.index)
195
+ .map(({ item }) => item);
196
+
171
197
  export const applyActiveFilter = <T extends { type: string }>(items: T[], activeFilter: string) => {
172
198
  if (activeFilter === 'all') {
173
199
  return items;
@@ -3,8 +3,12 @@ import { SEO } from 'astro-seo';
3
3
  import config from '@config';
4
4
  import defaultImageFile from '../../public/opengraph.png';
5
5
  import { buildUrl } from '@utils/url-builder';
6
+ import { formatPageTitle } from '@utils/page-title';
6
7
 
7
- const { title = config.title, description = config.tagline, ogTitle = title, ogType = 'website' } = Astro.props;
8
+ const { description = config.tagline, ogType = 'website' } = Astro.props;
9
+ // The page's own title (none for the homepage), followed by the catalog's
10
+ const title = formatPageTitle(Astro.props.title);
11
+ const ogTitle = title;
8
12
 
9
13
  const siteBaseURL = new URL(Astro.url);
10
14
  const defaultImage = new URL(defaultImageFile.src, siteBaseURL);
@@ -64,53 +64,58 @@ export const AssistantSettingsForm = ({ canEdit, initial, chatAvailable, inSSR,
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="Assistant Agent"
77
- description="Assistant agent that answers questions about your architecture directly in your catalog."
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
- <div className="space-y-3">
84
- <ToggleRow
85
- icon={<MessageSquare className="h-4 w-4" aria-hidden />}
86
- label={chatEnabled ? 'Enabled' : 'Disabled'}
87
- hint={
88
- chatEnabled
89
- ? 'Event Catalog Assistant is visible in this catalog.'
90
- : 'Event Catalog Assistant is hidden from this catalog.'
91
- }
92
- checked={chatEnabled}
93
- disabled={!canEdit}
94
- onChange={setChatEnabled}
95
- />
96
- {chatEnabled &&
97
- (chatAvailable ? (
98
- <ConfigurationRequired />
99
- ) : !inSSR ? (
100
- <AssistantNeedsSSR />
101
- ) : !hasChatConfigFile ? (
102
- <AssistantNeedsConfigFile />
103
- ) : null)}
104
- </div>
105
- </Row>
106
- </form>
73
+ {!canEdit && (
74
+ <div className="pb-6">
75
+ <ReadOnlyBanner />
76
+ </div>
77
+ )}
78
+
79
+ <Row
80
+ title="Assistant Agent"
81
+ description="Assistant agent that answers questions about your architecture directly in your catalog."
82
+ canEdit={canEdit}
83
+ dirty={dirty}
84
+ saving={saving}
85
+ onSave={save}
86
+ >
87
+ <div className="space-y-3">
88
+ <ToggleRow
89
+ icon={<MessageSquare className="h-4 w-4" aria-hidden />}
90
+ label={chatEnabled ? 'Enabled' : 'Disabled'}
91
+ hint={
92
+ chatEnabled
93
+ ? 'Event Catalog Assistant is visible in this catalog.'
94
+ : 'Event Catalog Assistant is hidden from this catalog.'
95
+ }
96
+ checked={chatEnabled}
97
+ disabled={!canEdit}
98
+ onChange={setChatEnabled}
99
+ />
100
+ {chatEnabled &&
101
+ (chatAvailable ? (
102
+ <ConfigurationRequired />
103
+ ) : !inSSR ? (
104
+ <AssistantNeedsSSR />
105
+ ) : !hasChatConfigFile ? (
106
+ <AssistantNeedsConfigFile />
107
+ ) : null)}
108
+ </div>
109
+ </Row>
110
+ </form>
111
+ </>
107
112
  );
108
113
  };
109
114
 
110
115
  const ConfigurationRequired = () => (
111
- <div className="rounded-lg border border-[rgb(var(--ec-accent)/0.4)] bg-[rgb(var(--ec-accent)/0.06)] px-4 py-3">
116
+ <div className="rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.03)] px-4 py-3">
112
117
  <div className="flex items-start gap-3">
113
- <span className="flex h-7 w-7 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))]">
118
+ <span className="flex h-7 w-7 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))]">
114
119
  <Wrench className="h-3.5 w-3.5" aria-hidden />
115
120
  </span>
116
121
  <div className="flex-1 min-w-0">
@@ -125,7 +130,7 @@ const ConfigurationRequired = () => (
125
130
  target="_blank"
126
131
  rel="noreferrer"
127
132
  className={cn(
128
- 'mt-2 inline-flex items-center gap-1 text-[12px] font-medium text-[rgb(var(--ec-accent))] hover:underline'
133
+ 'mt-2 inline-flex items-center gap-1 text-[12px] font-medium text-[rgb(var(--ec-page-text))] hover:underline'
129
134
  )}
130
135
  >
131
136
  Configure the Agent
@@ -137,15 +142,15 @@ const ConfigurationRequired = () => (
137
142
  );
138
143
 
139
144
  const AssistantNeedsSSR = () => (
140
- <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">
145
+ <div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.03)]">
141
146
  <div className="flex items-start gap-3 px-4 py-3.5">
142
- <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))]">
147
+ <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))]">
143
148
  <ServerCog className="h-4 w-4" aria-hidden />
144
149
  </span>
145
150
  <div className="flex-1 min-w-0">
146
151
  <div className="flex items-center gap-2">
147
152
  <p className="text-[13px] font-semibold text-[rgb(var(--ec-page-text))]">Server output mode required</p>
148
- <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))]">
153
+ <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))]">
149
154
  SSR
150
155
  </span>
151
156
  </div>
@@ -157,7 +162,7 @@ const AssistantNeedsSSR = () => (
157
162
  href={ASSISTANT_CONFIGURATION_DOCS_URL}
158
163
  target="_blank"
159
164
  rel="noreferrer"
160
- 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))]"
165
+ 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"
161
166
  >
162
167
  Configuration guide
163
168
  <ExternalLink className="h-3 w-3" aria-hidden />
@@ -178,9 +183,9 @@ const AssistantNeedsSSR = () => (
178
183
  );
179
184
 
180
185
  const AssistantNeedsConfigFile = () => (
181
- <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">
186
+ <div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.03)]">
182
187
  <div className="flex items-start gap-3 px-4 py-3.5">
183
- <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))]">
188
+ <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))]">
184
189
  <Wrench className="h-4 w-4" aria-hidden />
185
190
  </span>
186
191
  <div className="flex-1 min-w-0">
@@ -195,7 +200,7 @@ const AssistantNeedsConfigFile = () => (
195
200
  href={ASSISTANT_CONFIGURATION_DOCS_URL}
196
201
  target="_blank"
197
202
  rel="noreferrer"
198
- 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))]"
203
+ 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"
199
204
  >
200
205
  Configuration guide
201
206
  <ExternalLink className="h-3 w-3" aria-hidden />
@@ -179,193 +179,198 @@ export const GeneralSettingsForm = ({ canEdit, initial, apiBase }: Props) => {
179
179
  };
180
180
 
181
181
  return (
182
- <form onSubmit={(e) => e.preventDefault()} className="divide-y divide-[rgb(var(--ec-page-border))]">
182
+ <>
183
183
  <Toaster richColors closeButton position="bottom-right" theme="system" />
184
- {!canEdit && (
185
- <div className="pb-6">
186
- <ReadOnlyBanner />
187
- </div>
188
- )}
189
-
190
- <Row
191
- title="Theme"
192
- description="Pick a built-in colour theme. Selection updates the preview live; click Save to persist."
193
- canEdit={canEdit}
194
- dirty={dirtyKeys.has('theme')}
195
- saving={savingKey === 'theme'}
196
- onSave={() => saveRow('theme')}
197
- error={errors.theme}
184
+ <form
185
+ onSubmit={(e) => e.preventDefault()}
186
+ className="divide-y divide-[rgb(var(--ec-page-border))] border-t border-[rgb(var(--ec-page-border))]"
198
187
  >
199
- <div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
200
- {themeOptions(form.theme).map((t) => {
201
- const preview = THEME_PREVIEWS[t] ?? { accent: '#888', bg: '#1a1a1a', label: t };
202
- const isActive = form.theme === t;
203
- const isCustom = !isKnownTheme(t);
204
- return (
205
- <button
206
- type="button"
207
- key={t}
208
- onClick={() => setField('theme', t)}
209
- disabled={!canEdit}
210
- aria-pressed={isActive}
211
- className={cn(
212
- 'group relative flex flex-col overflow-hidden rounded-lg border text-left transition-all',
213
- 'disabled:cursor-not-allowed disabled:opacity-50',
214
- isActive
215
- ? 'border-[rgb(var(--ec-accent))] ring-2 ring-[rgb(var(--ec-accent)/0.25)]'
216
- : 'border-[rgb(var(--ec-page-border))] hover:border-[rgb(var(--ec-page-text-muted)/0.6)]'
217
- )}
218
- >
219
- <div className="relative h-14 w-full overflow-hidden" style={{ background: preview.bg }}>
220
- <div
221
- className="absolute -bottom-2 -right-2 h-12 w-12 rounded-full opacity-90 blur-md"
222
- style={{ background: preview.accent }}
223
- aria-hidden
224
- />
225
- <div
226
- className="absolute left-3 top-3 h-1.5 w-10 rounded-full opacity-90"
227
- style={{ background: preview.accent }}
228
- aria-hidden
229
- />
230
- <div
231
- className="absolute left-3 top-6 h-1 w-7 rounded-full opacity-60"
232
- style={{ background: 'rgba(255,255,255,0.4)' }}
233
- aria-hidden
234
- />
235
- {isActive && (
236
- <span className="absolute right-1.5 top-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-white text-black shadow-sm">
237
- <Check className="h-2.5 w-2.5" aria-hidden />
238
- </span>
239
- )}
240
- </div>
241
- <div className="flex items-center justify-between gap-2 px-3 py-2">
242
- <span className="text-[12px] font-medium capitalize text-[rgb(var(--ec-page-text))]">{preview.label}</span>
243
- {isCustom && (
244
- <span className="rounded-full border border-[rgb(var(--ec-page-border))] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[rgb(var(--ec-page-text-muted))]">
245
- custom
246
- </span>
188
+ {!canEdit && (
189
+ <div className="pb-6">
190
+ <ReadOnlyBanner />
191
+ </div>
192
+ )}
193
+
194
+ <Row
195
+ title="Theme"
196
+ description="Pick a built-in colour theme. Selection updates the preview live; click Save to persist."
197
+ canEdit={canEdit}
198
+ dirty={dirtyKeys.has('theme')}
199
+ saving={savingKey === 'theme'}
200
+ onSave={() => saveRow('theme')}
201
+ error={errors.theme}
202
+ >
203
+ <div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
204
+ {themeOptions(form.theme).map((t) => {
205
+ const preview = THEME_PREVIEWS[t] ?? { accent: '#888', bg: '#1a1a1a', label: t };
206
+ const isActive = form.theme === t;
207
+ const isCustom = !isKnownTheme(t);
208
+ return (
209
+ <button
210
+ type="button"
211
+ key={t}
212
+ onClick={() => setField('theme', t)}
213
+ disabled={!canEdit}
214
+ aria-pressed={isActive}
215
+ className={cn(
216
+ 'group relative flex flex-col overflow-hidden rounded-lg border text-left transition-all',
217
+ 'disabled:cursor-not-allowed disabled:opacity-50',
218
+ isActive
219
+ ? 'border-[rgb(var(--ec-page-text))] ring-2 ring-[rgb(var(--ec-page-text)/0.15)]'
220
+ : 'border-[rgb(var(--ec-page-border))] hover:border-[rgb(var(--ec-page-text-muted)/0.6)]'
247
221
  )}
248
- </div>
249
- </button>
250
- );
251
- })}
252
- </div>
253
- </Row>
222
+ >
223
+ <div className="relative h-14 w-full overflow-hidden" style={{ background: preview.bg }}>
224
+ <div
225
+ className="absolute -bottom-2 -right-2 h-12 w-12 rounded-full opacity-90 blur-md"
226
+ style={{ background: preview.accent }}
227
+ aria-hidden
228
+ />
229
+ <div
230
+ className="absolute left-3 top-3 h-1.5 w-10 rounded-full opacity-90"
231
+ style={{ background: preview.accent }}
232
+ aria-hidden
233
+ />
234
+ <div
235
+ className="absolute left-3 top-6 h-1 w-7 rounded-full opacity-60"
236
+ style={{ background: 'rgba(255,255,255,0.4)' }}
237
+ aria-hidden
238
+ />
239
+ {isActive && (
240
+ <span className="absolute right-1.5 top-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-white text-black shadow-sm">
241
+ <Check className="h-2.5 w-2.5" aria-hidden />
242
+ </span>
243
+ )}
244
+ </div>
245
+ <div className="flex items-center justify-between gap-2 px-3 py-2">
246
+ <span className="text-[12px] font-medium capitalize text-[rgb(var(--ec-page-text))]">{preview.label}</span>
247
+ {isCustom && (
248
+ <span className="rounded-full border border-[rgb(var(--ec-page-border))] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[rgb(var(--ec-page-text-muted))]">
249
+ custom
250
+ </span>
251
+ )}
252
+ </div>
253
+ </button>
254
+ );
255
+ })}
256
+ </div>
257
+ </Row>
254
258
 
255
- <Row
256
- title="Organization"
257
- description="Used as the catalog name, the brand line next to the logo, and OpenGraph metadata."
258
- canEdit={canEdit}
259
- dirty={dirtyKeys.has('organization')}
260
- saving={savingKey === 'organization'}
261
- onSave={() => saveRow('organization')}
262
- error={errors.organization}
263
- >
264
- <input
265
- type="text"
266
- value={form.organization}
267
- onChange={handleChange('organization')}
268
- disabled={!canEdit}
269
- maxLength={100}
270
- placeholder="Acme Inc."
271
- className={cn(inputBase, errors.organization && inputError)}
272
- required
273
- />
274
- </Row>
259
+ <Row
260
+ title="Organization"
261
+ description="Used as the catalog name, the brand line next to the logo, and OpenGraph metadata."
262
+ canEdit={canEdit}
263
+ dirty={dirtyKeys.has('organization')}
264
+ saving={savingKey === 'organization'}
265
+ onSave={() => saveRow('organization')}
266
+ error={errors.organization}
267
+ >
268
+ <input
269
+ type="text"
270
+ value={form.organization}
271
+ onChange={handleChange('organization')}
272
+ disabled={!canEdit}
273
+ maxLength={100}
274
+ placeholder="Acme Inc."
275
+ className={cn(inputBase, errors.organization && inputError)}
276
+ required
277
+ />
278
+ </Row>
275
279
 
276
- <Row
277
- title="Tagline"
278
- description="A short subtitle shown beneath the organization name on the homepage."
279
- canEdit={canEdit}
280
- dirty={dirtyKeys.has('tagline')}
281
- saving={savingKey === 'tagline'}
282
- onSave={() => saveRow('tagline')}
283
- error={errors.tagline}
284
- >
285
- <textarea
286
- value={form.tagline}
287
- onChange={handleChange('tagline')}
288
- disabled={!canEdit}
289
- maxLength={500}
290
- rows={2}
291
- placeholder="Discover and explore our events, services, and architecture."
292
- className={cn(inputBase, 'resize-y', errors.tagline && inputError)}
293
- />
294
- </Row>
280
+ <Row
281
+ title="Tagline"
282
+ description="A short subtitle shown beneath the organization name on the homepage."
283
+ canEdit={canEdit}
284
+ dirty={dirtyKeys.has('tagline')}
285
+ saving={savingKey === 'tagline'}
286
+ onSave={() => saveRow('tagline')}
287
+ error={errors.tagline}
288
+ >
289
+ <textarea
290
+ value={form.tagline}
291
+ onChange={handleChange('tagline')}
292
+ disabled={!canEdit}
293
+ maxLength={500}
294
+ rows={2}
295
+ placeholder="Discover and explore our events, services, and architecture."
296
+ className={cn(inputBase, 'resize-y', errors.tagline && inputError)}
297
+ />
298
+ </Row>
295
299
 
296
- <Row
297
- title="Homepage link"
298
- description="Where the logo links to. Useful for pointing back to your main site."
299
- canEdit={canEdit}
300
- dirty={dirtyKeys.has('homepageLink')}
301
- saving={savingKey === 'homepageLink'}
302
- onSave={() => saveRow('homepageLink')}
303
- error={errors.homepageLink}
304
- >
305
- <input
306
- type="url"
307
- value={form.homepageLink}
308
- onChange={handleChange('homepageLink')}
309
- onBlur={handleUrlBlur('homepageLink')}
310
- disabled={!canEdit}
311
- placeholder="https://example.com"
312
- className={cn(inputBase, monoInput, errors.homepageLink && inputError)}
313
- />
314
- </Row>
300
+ <Row
301
+ title="Homepage link"
302
+ description="Where the logo links to. Useful for pointing back to your main site."
303
+ canEdit={canEdit}
304
+ dirty={dirtyKeys.has('homepageLink')}
305
+ saving={savingKey === 'homepageLink'}
306
+ onSave={() => saveRow('homepageLink')}
307
+ error={errors.homepageLink}
308
+ >
309
+ <input
310
+ type="url"
311
+ value={form.homepageLink}
312
+ onChange={handleChange('homepageLink')}
313
+ onBlur={handleUrlBlur('homepageLink')}
314
+ disabled={!canEdit}
315
+ placeholder="https://example.com"
316
+ className={cn(inputBase, monoInput, errors.homepageLink && inputError)}
317
+ />
318
+ </Row>
315
319
 
316
- <Row
317
- title="Edit URL"
318
- description="Base URL for the “Edit on GitHub” links shown on resource pages."
319
- canEdit={canEdit}
320
- dirty={dirtyKeys.has('editUrl')}
321
- saving={savingKey === 'editUrl'}
322
- onSave={() => saveRow('editUrl')}
323
- error={errors.editUrl}
324
- >
325
- <input
326
- type="url"
327
- value={form.editUrl}
328
- onChange={handleChange('editUrl')}
329
- onBlur={handleUrlBlur('editUrl')}
330
- disabled={!canEdit}
331
- placeholder="https://github.com/org/repo/edit/main"
332
- className={cn(inputBase, monoInput, errors.editUrl && inputError)}
333
- />
334
- </Row>
320
+ <Row
321
+ title="Edit URL"
322
+ description="Base URL for the “Edit on GitHub” links shown on resource pages."
323
+ canEdit={canEdit}
324
+ dirty={dirtyKeys.has('editUrl')}
325
+ saving={savingKey === 'editUrl'}
326
+ onSave={() => saveRow('editUrl')}
327
+ error={errors.editUrl}
328
+ >
329
+ <input
330
+ type="url"
331
+ value={form.editUrl}
332
+ onChange={handleChange('editUrl')}
333
+ onBlur={handleUrlBlur('editUrl')}
334
+ disabled={!canEdit}
335
+ placeholder="https://github.com/org/repo/edit/main"
336
+ className={cn(inputBase, monoInput, errors.editUrl && inputError)}
337
+ />
338
+ </Row>
335
339
 
336
- <Row
337
- title="Repository URL"
338
- description="Link to your catalog's source repository. Shown in the header for quick navigation back to the code."
339
- canEdit={canEdit}
340
- dirty={dirtyKeys.has('repositoryUrl')}
341
- saving={savingKey === 'repositoryUrl'}
342
- onSave={() => saveRow('repositoryUrl')}
343
- error={errors.repositoryUrl}
344
- >
345
- <input
346
- type="url"
347
- value={form.repositoryUrl}
348
- onChange={handleChange('repositoryUrl')}
349
- onBlur={handleUrlBlur('repositoryUrl')}
350
- disabled={!canEdit}
351
- placeholder="https://github.com/org/repo"
352
- className={cn(inputBase, monoInput, errors.repositoryUrl && inputError)}
353
- />
354
- </Row>
340
+ <Row
341
+ title="Repository URL"
342
+ description="Link to your catalog's source repository. Shown in the header for quick navigation back to the code."
343
+ canEdit={canEdit}
344
+ dirty={dirtyKeys.has('repositoryUrl')}
345
+ saving={savingKey === 'repositoryUrl'}
346
+ onSave={() => saveRow('repositoryUrl')}
347
+ error={errors.repositoryUrl}
348
+ >
349
+ <input
350
+ type="url"
351
+ value={form.repositoryUrl}
352
+ onChange={handleChange('repositoryUrl')}
353
+ onBlur={handleUrlBlur('repositoryUrl')}
354
+ disabled={!canEdit}
355
+ placeholder="https://github.com/org/repo"
356
+ className={cn(inputBase, monoInput, errors.repositoryUrl && inputError)}
357
+ />
358
+ </Row>
355
359
 
356
- <Row
357
- title="Logo"
358
- description="Image shown in the top-left corner. PNG, JPG, SVG, or WebP up to 2MB."
359
- canEdit={canEdit}
360
- // Logo upload saves itself directly; no inline Save button needed.
361
- dirty={false}
362
- >
363
- <LogoUpload
360
+ <Row
361
+ title="Logo"
362
+ description="Image shown in the top-left corner. PNG, JPG, SVG, or WebP up to 2MB."
364
363
  canEdit={canEdit}
365
- initialSrc={initial.logo?.src ? `${initial.logo.src}?t=${Date.now()}` : undefined}
366
- apiUrl={`${apiBase}/logo`}
367
- />
368
- </Row>
369
- </form>
364
+ // Logo upload saves itself directly; no inline Save button needed.
365
+ dirty={false}
366
+ >
367
+ <LogoUpload
368
+ canEdit={canEdit}
369
+ initialSrc={initial.logo?.src ? `${initial.logo.src}?t=${Date.now()}` : undefined}
370
+ apiUrl={`${apiBase}/logo`}
371
+ />
372
+ </Row>
373
+ </form>
374
+ </>
370
375
  );
371
376
  };