@eventcatalog/core 2.30.7 → 2.31.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 (60) hide show
  1. package/README.md +3 -2
  2. package/dist/analytics/analytics.cjs +1 -1
  3. package/dist/analytics/analytics.js +2 -2
  4. package/dist/analytics/log-build.cjs +9 -3
  5. package/dist/analytics/log-build.d.cts +4 -1
  6. package/dist/analytics/log-build.d.ts +4 -1
  7. package/dist/analytics/log-build.js +3 -3
  8. package/dist/{chunk-SUJLBNZK.js → chunk-4S3UNXH2.js} +1 -1
  9. package/dist/{chunk-HINNLTBH.js → chunk-D7LV5JLL.js} +9 -3
  10. package/dist/{chunk-EFSBN3ZZ.js → chunk-I6OFOESY.js} +1 -1
  11. package/dist/{chunk-XMDPVKIJ.js → chunk-NJGR7XUU.js} +44 -1
  12. package/dist/constants.cjs +1 -1
  13. package/dist/constants.js +1 -1
  14. package/dist/eventcatalog.cjs +74 -14
  15. package/dist/eventcatalog.config.d.cts +28 -0
  16. package/dist/eventcatalog.config.d.ts +28 -0
  17. package/dist/eventcatalog.js +29 -16
  18. package/dist/features.cjs +46 -2
  19. package/dist/features.d.cts +2 -1
  20. package/dist/features.d.ts +2 -1
  21. package/dist/features.js +5 -3
  22. package/eventcatalog/public/images/custom-docs-placeholder.png +0 -0
  23. package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Custom.tsx +0 -2
  24. package/eventcatalog/src/components/MDX/Steps/Step.astro +1 -1
  25. package/eventcatalog/src/components/MDX/Steps/Steps.astro +15 -0
  26. package/eventcatalog/src/components/SideBars/FlowSideBar.astro +75 -0
  27. package/eventcatalog/src/components/SideNav/CustomDocsNav/CustomDocsNavWrapper.tsx +11 -0
  28. package/eventcatalog/src/components/SideNav/CustomDocsNav/components/NestedItem.tsx +183 -0
  29. package/eventcatalog/src/components/SideNav/CustomDocsNav/components/NoResultsFound.tsx +21 -0
  30. package/eventcatalog/src/components/SideNav/CustomDocsNav/index.tsx +250 -0
  31. package/eventcatalog/src/components/SideNav/CustomDocsNav/types.ts +29 -0
  32. package/eventcatalog/src/components/SideNav/CustomDocsNav.astro +9 -0
  33. package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +2 -2
  34. package/eventcatalog/src/content.config.ts +15 -24
  35. package/eventcatalog/src/enterprise/collections/custom-pages.ts +19 -0
  36. package/eventcatalog/src/enterprise/custom-documentation/collection.ts +16 -0
  37. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/CustomDocsNavWrapper.tsx +11 -0
  38. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/components/NestedItem.tsx +183 -0
  39. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/components/NoResultsFound.tsx +21 -0
  40. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +250 -0
  41. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/types.ts +29 -0
  42. package/eventcatalog/src/enterprise/custom-documentation/pages/index.astro +389 -0
  43. package/eventcatalog/src/enterprise/custom-documentation/utils/custom-docs.ts +118 -0
  44. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +58 -9
  45. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/index.astro +23 -3
  46. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +13 -3
  47. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +134 -17
  48. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/index.astro +21 -3
  49. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -1
  50. package/eventcatalog/src/pages/docs/[type]/[id]/language.astro +1 -1
  51. package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +260 -0
  52. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +5 -3
  53. package/eventcatalog/src/pages/docs/users/[id]/index.astro +9 -4
  54. package/eventcatalog/src/pages/pro/index.astro +272 -0
  55. package/eventcatalog/src/shared-collections.ts +25 -0
  56. package/eventcatalog/src/types/index.ts +1 -1
  57. package/eventcatalog/src/utils/eventcatalog-config/catalog.ts +12 -1
  58. package/eventcatalog/src/utils/feature.ts +5 -0
  59. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  60. package/package.json +1 -1
@@ -3,7 +3,14 @@ import Footer from '@layouts/Footer.astro';
3
3
 
4
4
  import type { PageTypes } from '@types';
5
5
  import { getChangeLogs } from '@utils/collections/changelogs';
6
- import { RectangleGroupIcon, ServerIcon, BoltIcon, ChatBubbleLeftIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
6
+ import {
7
+ RectangleGroupIcon,
8
+ ServerIcon,
9
+ BoltIcon,
10
+ ChatBubbleLeftIcon,
11
+ MagnifyingGlassIcon,
12
+ QueueListIcon,
13
+ } from '@heroicons/react/24/outline';
7
14
  import { pageDataLoader } from '@utils/page-loaders/page-data-loader';
8
15
  import { render, getEntry } from 'astro:content';
9
16
  import 'diff2html/bundles/css/diff2html.min.css';
@@ -15,7 +22,7 @@ import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
15
22
  import { ClientRouter } from 'astro:transitions';
16
23
 
17
24
  export async function getStaticPaths() {
18
- const itemTypes: PageTypes[] = ['events', 'commands', 'queries', 'services', 'domains'];
25
+ const itemTypes: PageTypes[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows'];
19
26
  const allItems = await Promise.all(itemTypes.map((type) => pageDataLoader[type]()));
20
27
 
21
28
  return allItems.flatMap((items, index) =>
@@ -44,7 +51,7 @@ const logs = await getChangeLogs(props);
44
51
  const { data } = props;
45
52
  const latestVersion = data.latestVersion;
46
53
 
47
- const renderedLogs = await logs.map(async (log) => {
54
+ const renderedLogs = logs.map(async (log) => {
48
55
  const logEntry = await getEntry('changelogs', log.id);
49
56
  const { Content } = await render(logEntry as any);
50
57
  return {
@@ -104,6 +111,9 @@ const getBadge = () => {
104
111
  class: 'text-yellow-400',
105
112
  };
106
113
  }
114
+ if (props.collection === 'flows') {
115
+ return { backgroundColor: 'teal', textColor: 'teal', content: 'Flow', icon: QueueListIcon, class: 'text-teal-400' };
116
+ }
107
117
  };
108
118
 
109
119
  const badges = [getBadge()];
@@ -11,12 +11,12 @@ import ServiceSideBar from '@components/SideBars/ServiceSideBar.astro';
11
11
  import MessageSideBar from '@components/SideBars/MessageSideBar.astro';
12
12
  import DomainSideBar from '@components/SideBars/DomainSideBar.astro';
13
13
  import ChannelSideBar from '@components/SideBars/ChannelSideBar.astro';
14
+ import FlowSideBar from '@components/SideBars/FlowSideBar.astro';
14
15
 
15
16
  import { QueueListIcon, RectangleGroupIcon, ServerIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/outline';
16
17
  import type { PageTypes } from '@types';
17
18
 
18
19
  import { buildUrl } from '@utils/url-builder';
19
- import { getFlows } from '@utils/collections/flows';
20
20
  import { pageDataLoader } from '@utils/page-loaders/page-data-loader';
21
21
  import { ClientRouter } from 'astro:transitions';
22
22
  import { render } from 'astro:content';
@@ -24,16 +24,9 @@ import { ArrowsRightLeftIcon } from '@heroicons/react/20/solid';
24
24
 
25
25
  import config from '@config';
26
26
 
27
- type PageTypesWithFlows = PageTypes | 'flows';
28
-
29
27
  export async function getStaticPaths() {
30
- const loaders = {
31
- ...pageDataLoader,
32
- flows: getFlows,
33
- };
34
-
35
- const itemTypes: PageTypesWithFlows[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'channels'];
36
- const allItems = await Promise.all(itemTypes.map((type) => loaders[type]()));
28
+ const itemTypes: PageTypes[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'channels'];
29
+ const allItems = await Promise.all(itemTypes.map((type) => pageDataLoader[type]()));
37
30
 
38
31
  return allItems.flatMap((items, index) =>
39
32
  items.map((item) => ({
@@ -123,10 +116,19 @@ const getSpecificationBadges = () => {
123
116
  };
124
117
 
125
118
  const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
119
+
120
+ // Index only the latest version
121
+ const pagefindAttributes =
122
+ props.data.version === props.data.latestVersion
123
+ ? {
124
+ 'data-pagefind-body': '',
125
+ 'data-pagefind-meta': `title:${pageTitle}`,
126
+ }
127
+ : {};
126
128
  ---
127
129
 
128
130
  <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
129
- <main class="flex sm:px-8 docs-layout h-full">
131
+ <main class="flex sm:px-8 docs-layout h-full" {...pagefindAttributes}>
130
132
  <div class="flex docs-layout w-full">
131
133
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8">
132
134
  <div class="border-b border-gray-200 flex justify-between items-start md:pb-2">
@@ -159,7 +161,7 @@ const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
159
161
  </div>
160
162
  </div>
161
163
 
162
- <div>
164
+ <div data-pagefind-ignore>
163
165
  {
164
166
  props.data.version !== props.data.latestVersion && (
165
167
  <div class="rounded-md bg-gradient-to-r from-purple-50 to-purple-100 p-4 not-prose my-4">
@@ -198,7 +200,7 @@ const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
198
200
  <div class="prose prose-md py-4 w-full">
199
201
  <Content components={components(props)} />
200
202
  </div>
201
- <div>
203
+ <div data-pagefind-ignore>
202
204
  <!-- @ts-ignore -->
203
205
  <SchemaViewer id={props.data.id} catalog={props.catalog} filePath={props.filePath} />
204
206
  <NodeGraph
@@ -215,7 +217,7 @@ const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
215
217
  </div>
216
218
  <Footer />
217
219
  </div>
218
- <aside class="hidden lg:block sticky top-0 pb-10 w-96 overflow-y-auto py-2">
220
+ <aside class="hidden lg:block sticky top-0 pb-10 w-96 overflow-y-auto py-2" data-pagefind-ignore>
219
221
  <!-- @ts-ignore -->
220
222
  {
221
223
  (props?.collection === 'events' || props.collection === 'commands' || props.collection === 'queries') && (
@@ -225,6 +227,7 @@ const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
225
227
  {props?.collection === 'services' && <ServiceSideBar service={props} />}
226
228
  {props?.collection === 'domains' && <DomainSideBar domain={props} />}
227
229
  {props?.collection === 'channels' && <ChannelSideBar channel={props} />}
230
+ {props?.collection === 'flows' && <FlowSideBar flow={props} />}
228
231
  </aside>
229
232
  </div>
230
233
  <ClientRouter />
@@ -265,12 +268,126 @@ const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
265
268
  if (document.getElementsByClassName('mermaid').length > 0) {
266
269
  renderDiagrams(graphs);
267
270
  }
271
+
272
+ // Set up TOC highlighting and scrolling
273
+ setupTOCHighlighting();
268
274
  });
269
275
 
270
276
  /**
271
- * @params {HTMLCollectionOf<HTMLElement>} graphs
277
+ * Setup TOC highlighting and scrolling
278
+ */
279
+ function setupTOCHighlighting() {
280
+ // Check if there's a sidebar with navigation
281
+ const sidebarNav = document.querySelector('aside nav');
282
+ if (!sidebarNav) return;
283
+
284
+ const observerOptions = {
285
+ rootMargin: '0px 0px -40% 0px',
286
+ threshold: 0.1,
287
+ };
288
+
289
+ // Flag to temporarily disable observer after click
290
+ let observerPaused = false;
291
+
292
+ /**
293
+ * Highlights a TOC item and scrolls it into view
294
+ * @param {string} id - The ID of the heading to highlight in the TOC
295
+ */
296
+ function highlightTocItem(id) {
297
+ // Remove active class from all links
298
+ document.querySelectorAll('.active-toc-item').forEach((link) => {
299
+ link.classList.remove('active-toc-item', 'text-primary-600', 'font-medium');
300
+ link.classList.add('text-gray-400');
301
+ });
302
+
303
+ // Add active class to current link
304
+ const tocLink = document.querySelector(`aside nav a[href="#${id}"]`);
305
+ if (tocLink) {
306
+ tocLink.classList.add('active-toc-item', 'text-primary-600', 'font-medium');
307
+ tocLink.classList.remove('text-gray-400');
308
+
309
+ // Scroll the highlighted item into view with a small delay to ensure DOM updates first
310
+ setTimeout(() => {
311
+ tocLink.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
312
+ }, 10);
313
+ }
314
+ }
315
+
316
+ // Set up the intersection observer for scrolling
317
+ const observer = new IntersectionObserver((entries) => {
318
+ // If observer is paused, don't process entries
319
+ if (observerPaused) return;
320
+
321
+ entries.forEach((entry) => {
322
+ try {
323
+ const id = entry.target.getAttribute('id');
324
+ if (entry.isIntersecting && id) {
325
+ highlightTocItem(id);
326
+ }
327
+ } catch (entryError) {
328
+ console.error('Error processing intersection entry:', entryError);
329
+ }
330
+ });
331
+ }, observerOptions);
332
+
333
+ // Find all headings in the content area
334
+ const prose = document.querySelector('.prose');
335
+ if (!prose) return;
336
+
337
+ // First try to find headings with IDs
338
+ const headings = prose.querySelectorAll('h1[id], h2[id], h3[id]');
339
+
340
+ if (headings.length > 0) {
341
+ headings.forEach((heading) => {
342
+ observer.observe(heading);
343
+ });
344
+ } else {
345
+ // Fallback: If no headings with IDs found, attach IDs to them
346
+ const allHeadings = prose.querySelectorAll('h1, h2, h3');
347
+
348
+ allHeadings.forEach((heading) => {
349
+ // Only add ID if it doesn't exist
350
+ if (!heading.id) {
351
+ const text = heading.textContent || '';
352
+ const slug = text
353
+ .toLowerCase()
354
+ .replace(/[^\w\s-]/g, '')
355
+ .replace(/\s+/g, '-');
356
+ heading.id = slug;
357
+ }
358
+ observer.observe(heading);
359
+ });
360
+ }
361
+
362
+ // Add click event listeners to all TOC links
363
+ const tocLinks = document.querySelectorAll('aside nav a[href^="#"]');
364
+ tocLinks.forEach((link) => {
365
+ link.addEventListener('click', (e) => {
366
+ // Get the ID from the href attribute
367
+ const hrefAttr = link.getAttribute('href');
368
+ if (!hrefAttr) return;
369
+
370
+ const id = hrefAttr.substring(1);
371
+
372
+ // Highlight the clicked item
373
+ highlightTocItem(id);
374
+
375
+ // Temporarily pause the observer to prevent immediate highlighting changes
376
+ observerPaused = true;
377
+
378
+ // Resume the observer after a delay
379
+ setTimeout(() => {
380
+ observerPaused = false;
381
+ }, 500);
382
+ });
383
+ });
384
+ }
385
+
386
+ /**
387
+ * Renders mermaid diagrams in the page
388
+ * @param {HTMLCollectionOf<HTMLElement>} graphs - The collection of mermaid graph elements
272
389
  */
273
- async function renderDiagrams(graphs: any) {
390
+ async function renderDiagrams(graphs) {
274
391
  const { default: mermaid } = await import('mermaid');
275
392
 
276
393
  if (window.eventcatalog.mermaid) {
@@ -278,7 +395,7 @@ const badges = [getBadge(), ...contentBadges, ...getSpecificationBadges()];
278
395
  const { iconPacks = [] } = window.eventcatalog.mermaid ?? {};
279
396
 
280
397
  if (iconPacks.length > 0) {
281
- const iconPacksToRegister = iconPacks.map((name: string) => {
398
+ const iconPacksToRegister = iconPacks.map((name) => {
282
399
  return {
283
400
  name,
284
401
  icons,
@@ -35,7 +35,7 @@ export async function getStaticPaths() {
35
35
  }
36
36
 
37
37
  // @ts-ignore
38
- const { data, catalog, filePath } = Astro.props;
38
+ const { collection, data, catalog, filePath } = Astro.props;
39
39
  const fileName = data.specifications?.openapiPath || 'openapi.yml';
40
40
 
41
41
  const directory = path.dirname(filePath || '');
@@ -44,12 +44,24 @@ const fileExists = fs.existsSync(pathToSpec);
44
44
 
45
45
  let content = '';
46
46
 
47
+ // Capitalize the first letter of a string
48
+ const pageTitle = `${collection} | ${data.name} | OpenAPI Spec`.replace(/^\w/, (c) => c.toUpperCase());
49
+
50
+ // Index only the latest version
51
+ const pagefindAttributes =
52
+ data.version === data.latestVersion
53
+ ? {
54
+ 'data-pagefind-body': '',
55
+ 'data-pagefind-meta': `title:${pageTitle}`,
56
+ }
57
+ : {};
58
+
47
59
  if (fileExists) {
48
60
  content = fs.readFileSync(pathToSpec, 'utf8');
49
61
  }
50
62
  ---
51
63
 
52
- <VerticalSideBarLayout title="OpenAPI Spec">
64
+ <VerticalSideBarLayout title={pageTitle}>
53
65
  {
54
66
  !fileExists ? (
55
67
  <div class="text-center h-screen flex flex-col justify-center ">
@@ -60,7 +72,13 @@ if (fileExists) {
60
72
  </p>
61
73
  </div>
62
74
  ) : (
63
- <div>
75
+ <div {...pagefindAttributes}>
76
+ {
77
+ // Currently, Pagefind does not index metadata (such as the title),
78
+ // so we need to ensure it is included as text on the page.
79
+ // https://github.com/CloudCannon/pagefind/issues/437
80
+ }
81
+ <h2 class="hidden">{pageTitle}</h2>
64
82
  <OpenAPISpec client:only="react" spec={content} />
65
83
  </div>
66
84
  )
@@ -68,7 +68,7 @@ const badges = [
68
68
  ---
69
69
 
70
70
  <VerticalSideBarLayout title={pageTitle} description={ubiquitousLanguage.summary}>
71
- <main class="flex sm:px-8 docs-layout h-full max-w-7xl">
71
+ <main class="flex sm:px-8 docs-layout h-full max-w-7xl" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
72
72
  <div class="flex docs-layout w-full">
73
73
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 min-h-[50em]">
74
74
  <nav class="flex mb-4" aria-label="Breadcrumb">
@@ -34,7 +34,7 @@ const ubiquitousLanguage = ubiquitousLanguages[0];
34
34
  ---
35
35
 
36
36
  <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
37
- <main class="flex sm:px-8 docs-layout h-full">
37
+ <main class="flex sm:px-8 docs-layout h-full" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
38
38
  <div class="flex docs-layout w-full">
39
39
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 min-h-[50em]">
40
40
  <nav class="flex mb-4" aria-label="Breadcrumb">
@@ -0,0 +1,260 @@
1
+ ---
2
+ import CustomDocumentationPage from '@enterprise/custom-documentation/pages/index.astro';
3
+ import { getCollection } from 'astro:content';
4
+ import type { GetStaticPaths } from 'astro';
5
+ import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
6
+ import { Code } from 'astro-expressive-code/components';
7
+ import { isEventCatalogProEnabled } from '@utils/feature';
8
+ import path from 'node:path';
9
+ import fs from 'node:fs';
10
+
11
+ const PROJECT_DIR = path.resolve(process.env.PROJECT_DIR || process.cwd());
12
+ const CUSTOM_DOCS_DIR = path.resolve(PROJECT_DIR, 'docs/');
13
+ const directoryExists = fs.existsSync(CUSTOM_DOCS_DIR);
14
+
15
+ export const getStaticPaths = (async () => {
16
+ const docs = await getCollection('customPages');
17
+ const paths = docs.map((doc) => ({
18
+ params: { path: doc.id.replace('docs/', '') },
19
+ props: doc,
20
+ }));
21
+ console.log(
22
+ 'paths',
23
+ paths.map((path) => path.params.path)
24
+ );
25
+ return paths;
26
+ }) satisfies GetStaticPaths;
27
+
28
+ const props = Astro.props;
29
+
30
+ // Example for folder structure
31
+ const folderStructureExample = `my-catalog/
32
+ └── docs/
33
+ ├── getting-started/
34
+ │ ├── 01-introduction.mdx
35
+ │ └── 02-quick-start.mdx
36
+ ├── architecture-decisions/
37
+ │ ├── 01-what-are-architecture-decisions.mdx
38
+ │ ├── 02-how-to-create-architecture-decisions.mdx
39
+ │ ├── published/
40
+ │ │ ├── 01-adr-001-event-driven.mdx
41
+ │ │ └── 02-adr-002-api-first.mdx
42
+ │ └── drafts/
43
+ │ ├── 01-adr-003-microservices.mdx
44
+ │ └── 02-adr-004-monolith.mdx
45
+ └`;
46
+ // Example MDX file content
47
+ const mdxFileExample = `---
48
+ title: Getting Started
49
+ description: How to get started with our event-driven architecture
50
+ ---
51
+
52
+ # Getting Started with our Event-Driven Architecture
53
+
54
+ This guide will help you understand how our services communicate using events.
55
+
56
+ ## Prerequisites
57
+
58
+ - Understanding of basic messaging patterns
59
+ - Node.js installed on your machine
60
+
61
+ ## Key Concepts
62
+
63
+ Events are the backbone of our architecture. They represent facts that have happened in our system.
64
+ `;
65
+
66
+ // Example config file
67
+ const configExample = `// eventcatalog.config.js
68
+
69
+ module.exports = {
70
+ // Your existing config...
71
+
72
+ customDocs: {
73
+ sidebar: [
74
+ {
75
+ label: 'Getting Started',
76
+ badge: {
77
+ text: 'New', color: 'green'
78
+ },
79
+ collapsed: false,
80
+ items: [
81
+ { label: 'Introduction', slug: 'getting-started/01-introduction' },
82
+ { label: 'Quick Start', slug: 'getting-started/02-quick-start' }
83
+ ]
84
+ },
85
+ {
86
+ label: 'Architecture Decisions',
87
+ badge: {
88
+ text: 'New', color: 'green'
89
+ },
90
+ collapsed: true,
91
+ items: [
92
+ {
93
+ label: 'What are Architecture Decisions?',
94
+ slug: 'architecture-decisions/01-what-are-architecture-decisions'
95
+ },
96
+ {
97
+ label: 'How to Create Architecture Decisions',
98
+ slug: 'architecture-decisions/02-how-to-create-architecture-decisions'
99
+ },
100
+ {
101
+ label: 'Published ADRs',
102
+ autogenerated: {
103
+ directory: 'architecture-decisions/published',
104
+ collapsed: true
105
+ }
106
+ },
107
+ {
108
+ label: 'Draft ADRs',
109
+ autogenerated: {
110
+ directory: 'architecture-decisions/drafts',
111
+ collapsed: true
112
+ }
113
+ }
114
+ ]
115
+ }
116
+ ]
117
+ }
118
+ }`;
119
+ ---
120
+
121
+ {
122
+ directoryExists && isEventCatalogProEnabled() ? (
123
+ <CustomDocumentationPage {...props} />
124
+ ) : (
125
+ <VerticalSideBarLayout title="Custom Documentation">
126
+ <body class="min-h-screen font-inter">
127
+ <main class="container px-8 lg:px-8 mx-auto py-8 max-w-[80em]">
128
+ <div class="mb-12">
129
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
130
+ <div>
131
+ <div class="flex flex-wrap items-center gap-3 mb-3">
132
+ <h1 class="text-4xl font-semibold text-gray-900 font-inter">Custom Documentation</h1>
133
+ <div class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800 border border-purple-200 shadow-sm">
134
+ Pro feature
135
+ </div>
136
+ </div>
137
+ <p class="text-base mb-0 text-gray-600 max-w-3xl">
138
+ Add custom documentation to EventCatalog to create a unified source of truth for your team. Document your
139
+ architecture decisions, patterns, and guidelines.
140
+ </p>
141
+ </div>
142
+ <div class="flex space-x-4 shrink-0">
143
+ <a
144
+ href="https://www.eventcatalog.dev/docs/custom-documentation"
145
+ class="inline-flex items-center justify-center px-5 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
146
+ >
147
+ Read documentation &rarr;
148
+ </a>
149
+ {!isEventCatalogProEnabled() && (
150
+ <a
151
+ href="https://www.eventcatalog.dev/pro/trial"
152
+ class="inline-flex items-center justify-center px-5 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-gradient-to-r from-purple-500 to-purple-700 hover:from-purple-600 hover:to-purple-800 shadow-sm"
153
+ >
154
+ Start 14-day trial
155
+ </a>
156
+ )}
157
+ </div>
158
+ </div>
159
+ </div>
160
+
161
+ <h2 class="text-2xl font-semibold mb-2 text-gray-900">Setup Guide</h2>
162
+ <p class="text-gray-600 mb-8 max-w-3xl">
163
+ Custom documentation let's you bring any documentation into EventCatalog. This is useful for documenting your
164
+ architecture decisions, patterns, and guidelines. Follow these steps to get started:
165
+ </p>
166
+
167
+ <div class="space-y-10 mb-12">
168
+ <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
169
+ <div class="flex items-start gap-4">
170
+ <div class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-blue-100 text-blue-600 text-lg font-medium shrink-0 mt-1">
171
+ 1
172
+ </div>
173
+ <div class="w-full">
174
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Create the content structure</h3>
175
+ <p class="text-gray-600 mb-4">Create a folder structure in your directory to organize your documentation.</p>
176
+ <Code code={folderStructureExample} lang="bash" frame="terminal" />
177
+ </div>
178
+ </div>
179
+ </div>
180
+
181
+ <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
182
+ <div class="flex items-start gap-4">
183
+ <div class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-blue-100 text-blue-600 text-lg font-medium shrink-0 mt-1">
184
+ 2
185
+ </div>
186
+ <div class="w-full">
187
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Add MDX files</h3>
188
+ <p class="text-gray-600 mb-4">Create MDX files with frontmatter and markdown content.</p>
189
+ <Code code={mdxFileExample} lang="mdx" frame="terminal" />
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
195
+ <div class="flex items-start gap-4">
196
+ <div class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-blue-100 text-blue-600 text-lg font-medium shrink-0 mt-1">
197
+ 3
198
+ </div>
199
+ <div class="w-full">
200
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Update your eventcatalog.config.js file</h3>
201
+ <p class="text-gray-600 mb-4">
202
+ Add the customDocs configuration to your eventcatalog.config.js file to define your sidebar structure.
203
+ </p>
204
+ <Code code={configExample} lang="js" frame="terminal" />
205
+ <p class="text-gray-600 mt-4">
206
+ This configuration defines the sidebar structure for your custom documentation:
207
+ </p>
208
+ <ul class="list-disc list-inside text-gray-600 mt-2 ml-2 space-y-1">
209
+ <li>
210
+ <strong>label</strong>: The display name for each sidebar section
211
+ </li>
212
+ <li>
213
+ <strong>badge</strong>: Optional badge to highlight new sections
214
+ </li>
215
+ <li>
216
+ <strong>collapsed</strong>: Whether the section is collapsed by default
217
+ </li>
218
+ <li>
219
+ <strong>autogenerated</strong>: Automatically generate sidebar items from a directory
220
+ </li>
221
+ <li>
222
+ <strong>slug</strong>: Direct link to a specific page
223
+ </li>
224
+ </ul>
225
+ </div>
226
+ </div>
227
+ </div>
228
+
229
+ <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
230
+ <div class="flex items-start gap-4">
231
+ <div class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-blue-100 text-blue-600 text-lg font-medium shrink-0 mt-1">
232
+ 4
233
+ </div>
234
+ <div class="w-full">
235
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Restart EventCatalog</h3>
236
+ <p class="text-gray-600 mb-4">
237
+ After configuring your documentation, restart EventCatalog to see your custom documentation.
238
+ </p>
239
+ <div class="mb-4">
240
+ <Code code="npm run dev" lang="bash" frame="terminal" />
241
+ </div>
242
+ <p class="text-gray-600 mb-4">
243
+ Once restarted, you'll see your custom documentation displayed with the sidebar structure you defined:
244
+ </p>
245
+ <div class="border border-gray-200 rounded-lg overflow-hidden">
246
+ <img
247
+ src="/images/custom-docs-placeholder.png"
248
+ alt="Example of custom documentation interface"
249
+ class="w-full"
250
+ />
251
+ </div>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ </div>
256
+ </main>
257
+ </body>
258
+ </VerticalSideBarLayout>
259
+ )
260
+ }
@@ -69,11 +69,13 @@ const ownedQueriesList = queries.map((p) => ({
69
69
  collection: p.collection,
70
70
  tag: `v${p.data.version}`,
71
71
  }));
72
+
73
+ const pageTitle = `Team | ${props.data.name}`;
72
74
  ---
73
75
 
74
- <VerticalSideBarLayout title={`Team | ${props.data.name}`} description={props.data.summary}>
76
+ <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
75
77
  <div class="flex min-h-screen docs-layout sm:px-8">
76
- <main class="flex-1 w-full pr-10 pt-4">
78
+ <main class="flex-1 w-full pr-10 pt-4" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
77
79
  <!-- <span class="text-purple-500 bg-purple-100 px-2 py-1 rounded-md">v{props.data.version}</span> -->
78
80
 
79
81
  <div class="border-b border-gray-200 py-4 pb-2">
@@ -131,7 +133,7 @@ const ownedQueriesList = queries.map((p) => ({
131
133
  <NodeGraph id={props.data.id} type={props?.catalog?.type} nodes={props.nodes} masterNode={{ name: props.data.name, id: props.data.id }} client:load />
132
134
  </div> -->
133
135
  </main>
134
- <aside class="sticky top-20 h-[calc(100vh-theme(spacing.16))] w-72 overflow-y-auto">
136
+ <aside class="sticky top-20 h-[calc(100vh-theme(spacing.16))] w-72 overflow-y-auto" data-pagefind-ignore>
135
137
  <div class="divide-y-2 divide-gray-100">
136
138
  <PillListFlat
137
139
  color="pink"
@@ -59,10 +59,12 @@ const associatedTeams = teams.map((o) => ({
59
59
  badge: 'Team',
60
60
  href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
61
61
  }));
62
+
63
+ const pageTitle = `User | ${props.data.name}`;
62
64
  ---
63
65
 
64
- <VerticalSideBarLayout title={`User | ${props.data.name}`}>
65
- <main class="flex sm:px-8 docs-layout h-full">
66
+ <VerticalSideBarLayout title={pageTitle}>
67
+ <main class="flex sm:px-8 docs-layout h-full" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
66
68
  <div class="flex docs-layout w-full">
67
69
  <div class="w-full lg:mr-6 pr-8 overflow-y-auto py-4">
68
70
  <!-- <span class="text-purple-500 bg-purple-100 px-2 py-1 rounded-md">v{props.data.version}</span> -->
@@ -105,7 +107,7 @@ const associatedTeams = teams.map((o) => ({
105
107
  </div>
106
108
  </div>
107
109
  </div>
108
- <div class="border-b border-gray-200">
110
+ <div class="border-b border-gray-200" data-pagefind-ignore>
109
111
  <div class="mx-auto max-w-7xl px-6 lg:px-8">
110
112
  <div class="mx-auto max-w-2xl lg:max-w-none">
111
113
  <dl
@@ -139,7 +141,10 @@ const associatedTeams = teams.map((o) => ({
139
141
  <Content components={components(props)} />
140
142
  </div>
141
143
  </div>
142
- <aside class="hidden lg:block sticky top-0 h-[calc(100vh-theme(spacing.16))] w-72 overflow-y-auto py-2">
144
+ <aside
145
+ class="hidden lg:block sticky top-0 h-[calc(100vh-theme(spacing.16))] w-72 overflow-y-auto py-2"
146
+ data-pagefind-ignore
147
+ >
143
148
  <div class="divide-y-2 divide-gray-100 pr-6">
144
149
  {
145
150
  ownedDomainsList.length > 0 && (