@eventcatalog/core 3.29.1 → 3.30.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 (82) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-H5UC2A5F.js → chunk-6UG4JMUV.js} +1 -1
  6. package/dist/{chunk-4MSAPCV3.js → chunk-ATRBVTJ6.js} +1 -1
  7. package/dist/{chunk-PLNJC7NZ.js → chunk-K3ZVEX2Y.js} +13 -2
  8. package/dist/{chunk-V4OTI3PF.js → chunk-MVZKHUX2.js} +1 -1
  9. package/dist/{chunk-FVKDNLZK.js → chunk-RRBDF4MM.js} +1 -1
  10. package/dist/{chunk-24NGK43A.js → chunk-Z26P4PCB.js} +1 -1
  11. package/dist/constants.cjs +1 -1
  12. package/dist/constants.js +1 -1
  13. package/dist/eventcatalog.cjs +14 -3
  14. package/dist/eventcatalog.js +6 -6
  15. package/dist/generate.cjs +1 -1
  16. package/dist/generate.js +3 -3
  17. package/dist/utils/cli-logger.cjs +1 -1
  18. package/dist/utils/cli-logger.js +2 -2
  19. package/dist/watcher.cjs +13 -2
  20. package/dist/watcher.js +1 -1
  21. package/eventcatalog/astro.config.mjs +1 -1
  22. package/eventcatalog/public/logo.png +0 -0
  23. package/eventcatalog/src/components/CopyAsMarkdown.tsx +2 -2
  24. package/eventcatalog/src/components/EnvironmentDropdown.tsx +33 -21
  25. package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +3 -53
  26. package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +144 -91
  27. package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +112 -109
  28. package/eventcatalog/src/components/Header.astro +9 -19
  29. package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +12 -14
  30. package/eventcatalog/src/components/MDX/Accordion/AccordionGroup.astro +11 -3
  31. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +15 -5
  32. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +164 -53
  33. package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +1 -1
  34. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +4 -4
  35. package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +12 -10
  36. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +48 -77
  37. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +238 -169
  38. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +189 -230
  39. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +39 -36
  40. package/eventcatalog/src/components/Search/Search.astro +1 -1
  41. package/eventcatalog/src/components/Seo.astro +1 -1
  42. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -3
  43. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +229 -256
  44. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +78 -59
  45. package/eventcatalog/src/components/Tables/Discover/columns.tsx +130 -197
  46. package/eventcatalog/src/components/Tables/Table.tsx +21 -18
  47. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +79 -131
  48. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +104 -175
  49. package/eventcatalog/src/enterprise/auth/error.astro +1 -1
  50. package/eventcatalog/src/enterprise/auth/login.astro +1 -1
  51. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +95 -93
  52. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +174 -136
  53. package/eventcatalog/src/enterprise/fields/pages/fields.astro +10 -8
  54. package/eventcatalog/src/enterprise/integrations/eventcatalog-features.ts +0 -8
  55. package/eventcatalog/src/layouts/DirectoryLayout.astro +17 -88
  56. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +528 -146
  57. package/eventcatalog/src/layouts/VisualiserLayout.astro +7 -2
  58. package/eventcatalog/src/pages/_index.astro +5 -3
  59. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -3
  60. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +223 -73
  61. package/eventcatalog/src/pages/discover/[type]/index.astro +22 -141
  62. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +129 -29
  63. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +129 -29
  64. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +6 -2
  65. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +2 -2
  66. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +21 -18
  67. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +33 -32
  68. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +5 -1
  69. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +2 -2
  70. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +4 -6
  71. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +11 -4
  72. package/eventcatalog/src/pages/docs/users/[id]/index.astro +11 -4
  73. package/eventcatalog/src/pages/schemas/explorer/index.astro +10 -8
  74. package/eventcatalog/src/pages/studio.astro +1 -1
  75. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +2 -7
  76. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +2 -2
  77. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +2 -7
  78. package/eventcatalog/src/styles/theme.css +68 -12
  79. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +13 -0
  80. package/package.json +1 -1
  81. package/eventcatalog/public/logo.svg +0 -14
  82. package/eventcatalog/src/enterprise/plans/index.astro +0 -319
@@ -53,8 +53,8 @@ const getColor = (collection: string) => {
53
53
  ---
54
54
 
55
55
  <VerticalSideBarLayout title={title} description={description}>
56
- <div class="">
57
- <div class="">
56
+ <div class="visualiser-full-bleed">
57
+ <div>
58
58
  <!-- <div class="hidden md:block">
59
59
  <aside class="sm:fixed grow w-56 xl:w-[19em] overflow-y-auto h-full z-10 pb-20" id="visualiser-navigation">
60
60
  <div class="w-full">
@@ -144,6 +144,11 @@ const getColor = (collection: string) => {
144
144
  </script>
145
145
 
146
146
  <style>
147
+ .visualiser-full-bleed {
148
+ margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1);
149
+ margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1);
150
+ }
151
+
147
152
  #visualiser-navigation {
148
153
  transform: translateX(0);
149
154
  }
@@ -7,6 +7,8 @@ import { getCollection } from 'astro:content';
7
7
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
8
8
  import { BookOpenText, Workflow, TableProperties, BookUser, Code2, FileJson } from 'lucide-react';
9
9
 
10
+ const { showNestedSideBar = true } = Astro.props;
11
+
10
12
  // Lightweight data fetch: only raw collections, no hydration.
11
13
  // The homepage only needs counts + one item per type for default URLs.
12
14
  const [allDomains, allServices, allEvents, allCommands, allQueries, allFlows] = await Promise.all([
@@ -142,11 +144,11 @@ const quickActions = [
142
144
  ];
143
145
  ---
144
146
 
145
- <VerticalSideBarLayout title="EventCatalog">
147
+ <VerticalSideBarLayout title="EventCatalog" showNestedSideBar={showNestedSideBar}>
146
148
  <div class="min-h-screen font-inter">
147
149
  <!-- Hero Section -->
148
150
  <div class="relative">
149
- <main class="px-6 lg:px-8 pt-10 pb-8 max-w-[85em]">
151
+ <main class="px-0 pt-7 pb-8 max-w-[85em]">
150
152
  <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
151
153
  <div>
152
154
  <h1 class="text-2xl md:text-3xl font-bold mb-1 text-[rgb(var(--ec-page-text))] tracking-tight">
@@ -161,7 +163,7 @@ const quickActions = [
161
163
  </div>
162
164
 
163
165
  <!-- Main Content -->
164
- <main class="px-6 lg:px-8 pb-8 max-w-[85em]">
166
+ <main class="px-0 pb-8 max-w-[85em]">
165
167
  <!-- Stat Cards -->
166
168
  <section class="mb-10">
167
169
  <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
@@ -22,9 +22,9 @@ const specifications = type === 'services' ? getSpecificationsForService(props)
22
22
  ---
23
23
 
24
24
  <VerticalSideBarLayout title={pageTitle}>
25
- <main class="flex docs-layout h-full bg-[rgb(var(--ec-page-bg))]">
26
- <div class="flex docs-layout w-full pl-16">
27
- <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 bg-[rgb(var(--ec-page-bg))]">
25
+ <main class="flex docs-layout min-h-full bg-[rgb(var(--ec-page-bg))]">
26
+ <div class="flex docs-layout w-full">
27
+ <div class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
28
28
  {type === 'domains' && <DomainGrid domain={domain} client:load />}
29
29
  {type === 'services' && <MessageGrid service={props} specifications={specifications} client:load />}
30
30
  </div>
@@ -5,7 +5,7 @@ import VisualiserLayout from '@layouts/VisualiserLayout.astro';
5
5
  import components from '@components/MDX/components';
6
6
  import config from '@config';
7
7
  import { buildUrl } from '@utils/url-builder';
8
- import { ChevronDown, GitCompare, X, Rocket } from 'lucide-react';
8
+ import { GitCompare, X, Rocket, AlignLeft, HistoryIcon } from 'lucide-react';
9
9
  import CopyAsMarkdown from '@components/CopyAsMarkdown';
10
10
  import { isLLMSTxtEnabled, isEventCatalogChatEnabled, isDiagramComparisonEnabled } from '@utils/feature';
11
11
 
@@ -15,7 +15,7 @@ export const prerender = Page.prerender;
15
15
  export const getStaticPaths = Page.getStaticPaths;
16
16
 
17
17
  const props = await Page.getData(Astro);
18
- const { Content } = await render(props);
18
+ const { Content, headings } = await render(props);
19
19
 
20
20
  const pageTitle = `Diagram | ${props.data.name}`;
21
21
  const currentVersion = props.data.version;
@@ -29,73 +29,123 @@ const chatQuery = `Tell me about the "${props.data.name}" diagram (version ${pro
29
29
 
30
30
  <VisualiserLayout title={pageTitle} description={props.data.summary}>
31
31
  <div class="diagram-page min-h-[calc(100vh-64px)] bg-[rgb(var(--ec-page-bg))]">
32
- <header class="diagram-header border-b border-[rgb(var(--ec-page-border))]">
33
- <div class="px-6 py-6 flex items-center justify-between gap-4">
34
- <div class="min-w-0">
35
- <div class="flex items-center gap-3">
36
- <h1 class="text-2xl md:text-2xl font-bold text-[rgb(var(--ec-page-text))] truncate">
37
- {props.data.name}
38
- </h1>
39
- {
40
- hasMultipleVersions ? (
41
- <div class="relative flex-shrink-0">
42
- <select
43
- id="version-select"
44
- class="appearance-none text-xs font-medium text-[rgb(var(--ec-page-text-muted))] bg-transparent border border-[rgb(var(--ec-page-border))] rounded-md pl-2 pr-6 py-1 cursor-pointer hover:border-[rgb(var(--ec-accent))] transition-colors"
45
- >
46
- {allVersions.map((version: string) => (
47
- <option value={buildUrl(`/diagrams/${props.data.id}/${version}`)} selected={version === currentVersion}>
48
- v{version}
49
- {version === props.data.latestVersion ? ' (latest)' : ''}
50
- </option>
51
- ))}
52
- </select>
53
- <ChevronDown className="absolute right-1.5 top-1/2 -translate-y-1/2 w-3 h-3 text-[rgb(var(--ec-icon-color))] pointer-events-none" />
54
- </div>
55
- ) : (
56
- <span class="text-xs font-medium text-[rgb(var(--ec-page-text-muted))]">v{currentVersion}</span>
57
- )
58
- }
32
+ <div
33
+ class="flex docs-layout w-full"
34
+ style="padding-left: var(--ec-app-content-padding-left, 5rem); padding-right: var(--ec-app-content-padding-right, 5rem);"
35
+ >
36
+ <main class="diagram-content w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
37
+ <div class="border-b border-[rgb(var(--ec-page-border))] md:pb-2">
38
+ <div class="flex items-start justify-between gap-4">
39
+ <div class="min-w-0">
40
+ <h1 class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))] truncate">
41
+ {props.data.name}
42
+ </h1>
43
+ {
44
+ props.data.summary && (
45
+ <p class="pt-2 text-base font-light text-[rgb(var(--ec-page-text-muted))]">{props.data.summary}</p>
46
+ )
47
+ }
48
+ </div>
49
+
50
+ <div class="flex items-center gap-2">
51
+ <CopyAsMarkdown
52
+ client:only="react"
53
+ schemas={[]}
54
+ chatQuery={chatQuery}
55
+ chatEnabled={chatEnabled}
56
+ editUrl=""
57
+ markdownDownloadEnabled={markdownDownloadEnabled}
58
+ rssFeedEnabled={false}
59
+ preferChatAsDefault={chatEnabled}
60
+ chatButtonText="Ask about this diagram"
61
+ />
62
+ </div>
59
63
  </div>
60
- {props.data.summary && <p class="mt-1 text-sm text-[rgb(var(--ec-page-text-muted))]">{props.data.summary}</p>}
61
64
  </div>
62
65
 
63
- <div class="flex items-center gap-2">
64
- {
65
- hasMultipleVersions && (
66
- <button
67
- id="compare-btn"
68
- type="button"
69
- data-scale-enabled={scaleEnabled}
70
- class="inline-flex items-center justify-center gap-1.5 px-3 py-1.5 text-sm font-medium text-[rgb(var(--ec-dropdown-text))] bg-[rgb(var(--ec-dropdown-bg))] border border-[rgb(var(--ec-dropdown-border))] rounded-md shadow-xs hover:bg-[rgb(var(--ec-dropdown-hover))] focus:outline-hidden focus:ring-1 focus:ring-[rgb(var(--ec-accent))] transition-colors"
71
- >
72
- <GitCompare className="w-4 h-4" />
73
- Compare diagram versions
74
- </button>
75
- )
76
- }
77
- <CopyAsMarkdown
78
- client:only="react"
79
- schemas={[]}
80
- chatQuery={chatQuery}
81
- chatEnabled={chatEnabled}
82
- editUrl=""
83
- markdownDownloadEnabled={markdownDownloadEnabled}
84
- rssFeedEnabled={false}
85
- preferChatAsDefault={chatEnabled}
86
- chatButtonText="Ask about this diagram"
87
- />
88
- </div>
89
- </div>
90
- </header>
66
+ <article
67
+ class="diagram-article prose prose-md w-full max-w-none py-4 text-[15px] dark:prose-invert prose-headings:text-[rgb(var(--ec-page-text))] prose-p:text-[rgb(var(--ec-page-text))] prose-strong:text-[rgb(var(--ec-page-text))] prose-code:text-[rgb(var(--ec-page-text))]"
68
+ >
69
+ <Content components={components(props)} />
70
+ </article>
71
+ </main>
91
72
 
92
- <main class="diagram-content p-6">
93
- <article
94
- class="diagram-article prose prose-lg max-w-none dark:prose-invert prose-headings:text-[rgb(var(--ec-page-text))] prose-p:text-[rgb(var(--ec-page-text))] prose-strong:text-[rgb(var(--ec-page-text))] prose-code:text-[rgb(var(--ec-page-text))]"
95
- >
96
- <Content components={components(props)} />
97
- </article>
98
- </main>
73
+ {
74
+ headings.length > 0 && (
75
+ <aside
76
+ id="eventcatalog-docs-sidebar"
77
+ class="hidden xl:block sticky top-[4rem] self-start w-[280px] max-h-[calc(100vh-4rem)] overflow-y-auto py-2 flex-shrink-0 pr-10 bg-[rgb(var(--ec-page-bg))]"
78
+ >
79
+ <div class="mt-8 space-y-8">
80
+ <div>
81
+ <h3 class="text-xs text-[rgb(var(--ec-page-text))] font-semibold capitalize flex items-center gap-2 mb-4">
82
+ <AlignLeft className="w-4 h-4" />
83
+ On this page
84
+ </h3>
85
+ <nav class="text-xs border-l border-[rgb(var(--ec-page-border))]">
86
+ {headings.map((heading) => {
87
+ const level = heading.depth > 2 ? heading.depth : 1;
88
+ if (heading.depth > 3) {
89
+ return null;
90
+ }
91
+ return (
92
+ <a
93
+ href={`#${heading.slug}`}
94
+ class="block py-1.5 pr-2.5 leading-5 text-[rgb(var(--ec-page-text-muted))] hover:border-[rgb(var(--ec-page-border))] hover:text-[rgb(var(--ec-page-text))] border-l-2 border-transparent -ml-px transition-all duration-200"
95
+ style={`padding-left: ${level * 0.75}rem`}
96
+ >
97
+ {heading.text}
98
+ </a>
99
+ );
100
+ })}
101
+ </nav>
102
+ </div>
103
+ {hasMultipleVersions && (
104
+ <div>
105
+ <h3 class="text-xs text-[rgb(var(--ec-page-text))] font-semibold capitalize flex items-center gap-2 mb-4">
106
+ <HistoryIcon className="w-4 h-4" />
107
+ {`Versions (${allVersions.length})`}
108
+ </h3>
109
+ <ul role="list" class="space-y-2">
110
+ {allVersions.map((version: string) => {
111
+ const isCurrent = version === currentVersion;
112
+ return (
113
+ <li>
114
+ <a
115
+ href={buildUrl(`/diagrams/${props.data.id}/${version}`)}
116
+ class={`flex items-center gap-2 text-xs transition-colors ${
117
+ isCurrent
118
+ ? 'text-[rgb(var(--ec-accent))] font-medium'
119
+ : 'text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]'
120
+ }`}
121
+ >
122
+ <HistoryIcon className="h-4 w-4 flex-shrink-0" />
123
+ <span>{version === props.data.latestVersion ? `v${version} (latest)` : `v${version}`}</span>
124
+ </a>
125
+ </li>
126
+ );
127
+ })}
128
+ </ul>
129
+ </div>
130
+ )}
131
+ {hasMultipleVersions && (
132
+ <div>
133
+ <button
134
+ id="compare-btn"
135
+ type="button"
136
+ data-scale-enabled={scaleEnabled}
137
+ class="inline-flex w-full items-center justify-center gap-1.5 rounded-md border border-[rgb(var(--ec-dropdown-border))] bg-[rgb(var(--ec-dropdown-bg))] px-3 py-2 text-sm font-medium text-[rgb(var(--ec-dropdown-text))] shadow-xs transition-colors hover:bg-[rgb(var(--ec-dropdown-hover))] focus:outline-hidden focus:ring-1 focus:ring-[rgb(var(--ec-accent))]"
138
+ >
139
+ <GitCompare className="w-4 h-4" />
140
+ Compare diagram versions
141
+ </button>
142
+ </div>
143
+ )}
144
+ </div>
145
+ </aside>
146
+ )
147
+ }
148
+ </div>
99
149
  </div>
100
150
 
101
151
  {/* Upgrade modal - shown when Scale is not enabled */}
@@ -203,6 +253,21 @@ const chatQuery = `Tell me about the "${props.data.name}" diagram (version ${pro
203
253
  <ClientRouter />
204
254
  </VisualiserLayout>
205
255
 
256
+ <style is:global>
257
+ .diagram-page .prose {
258
+ max-width: none;
259
+ overflow: auto;
260
+ }
261
+
262
+ .toc-active-text {
263
+ color: rgb(var(--ec-accent));
264
+ }
265
+
266
+ .toc-active-border {
267
+ border-color: rgb(var(--ec-accent));
268
+ }
269
+ </style>
270
+
206
271
  <script is:inline define:vars={{ config, baseUrl: import.meta.env.BASE_URL }}>
207
272
  window.eventcatalog = window.eventcatalog || {};
208
273
  window.eventcatalog.mermaid = config?.mermaid;
@@ -213,14 +278,6 @@ const chatQuery = `Tell me about the "${props.data.name}" diagram (version ${pro
213
278
  import { destroyZoomInstances, renderMermaidWithZoom } from '@utils/mermaid-zoom';
214
279
 
215
280
  function initDiagramPage() {
216
- // Version selector
217
- const versionSelect = document.getElementById('version-select') as HTMLSelectElement;
218
- if (versionSelect) {
219
- versionSelect.onchange = () => {
220
- window.location.href = versionSelect.value;
221
- };
222
- }
223
-
224
281
  // Compare modal (Scale) and Upgrade modal (non-Scale)
225
282
  const compareBtn = document.getElementById('compare-btn') as HTMLButtonElement;
226
283
  const compareModal = document.getElementById('compare-modal');
@@ -316,6 +373,99 @@ const chatQuery = `Tell me about the "${props.data.name}" diagram (version ${pro
316
373
  document.addEventListener('astro:page-load', initDiagramPage);
317
374
  </script>
318
375
 
376
+ <script>
377
+ // @ts-nocheck
378
+ function setupDiagramTocObserver() {
379
+ try {
380
+ const tocRoot = document.getElementById('eventcatalog-docs-sidebar');
381
+ if (!tocRoot) return;
382
+
383
+ const observerOptions = {
384
+ rootMargin: '0px 0px -40% 0px',
385
+ threshold: 0.1,
386
+ };
387
+
388
+ let observerPaused = false;
389
+
390
+ function highlightTocItem(id) {
391
+ tocRoot.querySelectorAll('.active-toc-item').forEach((link) => {
392
+ link.classList.remove('active-toc-item', 'toc-active-text', 'font-medium', 'toc-active-border');
393
+ link.classList.add('border-transparent');
394
+ });
395
+
396
+ const tocLink = tocRoot.querySelector(`nav a[href="#${id}"]`);
397
+ if (tocLink) {
398
+ tocLink.classList.add('active-toc-item', 'toc-active-text', 'font-medium', 'toc-active-border');
399
+ tocLink.classList.remove('border-transparent');
400
+
401
+ setTimeout(() => {
402
+ tocLink.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
403
+ }, 10);
404
+ }
405
+ }
406
+
407
+ const observer = new IntersectionObserver((entries) => {
408
+ if (observerPaused) return;
409
+
410
+ entries.forEach((entry) => {
411
+ try {
412
+ const id = entry.target.getAttribute('id');
413
+ if (entry.isIntersecting && id) {
414
+ highlightTocItem(id);
415
+ }
416
+ } catch (entryError) {
417
+ console.error('Error processing intersection entry:', entryError);
418
+ }
419
+ });
420
+ }, observerOptions);
421
+
422
+ const prose = document.querySelector('.diagram-article.prose');
423
+ if (!prose) return;
424
+
425
+ const proseHeadings = prose.querySelectorAll('h1[id], h2[id], h3[id]');
426
+
427
+ if (proseHeadings.length > 0) {
428
+ proseHeadings.forEach((heading) => observer.observe(heading));
429
+ } else {
430
+ const allHeadings = prose.querySelectorAll('h1, h2, h3');
431
+
432
+ allHeadings.forEach((heading) => {
433
+ if (!heading.id) {
434
+ const text = heading.textContent || '';
435
+ const slug = text
436
+ .toLowerCase()
437
+ .replace(/[^\w\s-]/g, '')
438
+ .replace(/\s+/g, '-');
439
+ heading.id = slug;
440
+ }
441
+ observer.observe(heading);
442
+ });
443
+ }
444
+
445
+ const tocLinks = tocRoot.querySelectorAll('nav a[href^="#"]');
446
+ tocLinks.forEach((link) => {
447
+ link.addEventListener('click', () => {
448
+ const hrefAttr = link.getAttribute('href');
449
+ if (!hrefAttr) return;
450
+
451
+ const id = hrefAttr.substring(1);
452
+ highlightTocItem(id);
453
+ observerPaused = true;
454
+
455
+ setTimeout(() => {
456
+ observerPaused = false;
457
+ }, 500);
458
+ });
459
+ });
460
+ } catch (error) {
461
+ console.error('Error setting up diagram TOC highlighting:', error);
462
+ }
463
+ }
464
+
465
+ setupDiagramTocObserver();
466
+ document.addEventListener('astro:page-load', setupDiagramTocObserver);
467
+ </script>
468
+
319
469
  <script>
320
470
  import { renderPlantUMLWithZoom } from '@utils/mermaid-zoom';
321
471