@eventcatalog/core 3.17.6 → 3.18.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 (42) 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-JB32XRPB.js → chunk-3XKMODP4.js} +1 -1
  6. package/dist/{chunk-VY646YFE.js → chunk-52VBVLRT.js} +1 -1
  7. package/dist/{chunk-CSCCSFEV.js → chunk-7CZBLKHQ.js} +1 -1
  8. package/dist/{chunk-JR3DP5PU.js → chunk-GSHSX7NU.js} +1 -1
  9. package/dist/{chunk-7N35ESMH.js → chunk-OQ6UFIEN.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +1 -1
  13. package/dist/eventcatalog.js +5 -5
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/src/components/EnvironmentDropdown.tsx +1 -1
  19. package/eventcatalog/src/components/Header.astro +5 -5
  20. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +10 -6
  21. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraphPortal.tsx +1 -1
  22. package/eventcatalog/src/components/SchemaExplorer/ApiAccessSection.tsx +10 -10
  23. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +15 -13
  24. package/eventcatalog/src/components/Search/Search.astro +2 -2
  25. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +1 -1
  26. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +5 -6
  27. package/eventcatalog/src/content.config.ts +18 -3
  28. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +2 -26
  29. package/eventcatalog/src/pages/_index.astro +138 -113
  30. package/eventcatalog/src/pages/index.astro +17 -5
  31. package/eventcatalog/src/styles/theme.css +7 -2
  32. package/eventcatalog/src/styles/themes/forest.css +7 -7
  33. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +5 -1
  34. package/eventcatalog/src/utils/node-graphs/data-products-node-graph.ts +3 -1
  35. package/eventcatalog/src/utils/node-graphs/domains-canvas.ts +8 -2
  36. package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +1 -1
  37. package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +2 -2
  38. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +13 -4
  39. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +4 -1
  40. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +80 -17
  41. package/eventcatalog/tailwind.config.mjs +11 -2
  42. package/package.json +3 -3
@@ -1,36 +1,38 @@
1
1
  ---
2
2
  import { buildUrl } from '@utils/url-builder';
3
- import {
4
- ChatBubbleLeftIcon,
5
- RectangleGroupIcon,
6
- ServerIcon,
7
- CodeBracketIcon,
8
- DocumentTextIcon,
9
- ArrowRightIcon,
10
- PlusIcon,
11
- } from '@heroicons/react/24/outline';
3
+ import { ArrowRightIcon, PlusIcon } from '@heroicons/react/24/outline';
12
4
  import config from '@config';
13
5
 
14
- import { getMessages } from '@utils/collections/messages';
15
- import { getDomains } from '@utils/collections/domains';
16
- import { getServices } from '@utils/collections/services';
17
- import { getFlows } from '@utils/collections/flows';
6
+ import { getCollection } from 'astro:content';
18
7
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
19
- import { BookOpenText, Workflow, TableProperties, BookUser, Zap, Terminal, Code2, FileJson } from 'lucide-react';
8
+ import { BookOpenText, Workflow, TableProperties, BookUser, Code2, FileJson } from 'lucide-react';
20
9
 
21
- const { commands = [], events = [], queries = [] } = await getMessages({ getAllVersions: false });
22
- const messages = [...events, ...queries, ...commands];
23
- const domains = await getDomains({ getAllVersions: false });
24
- const services = await getServices({ getAllVersions: false });
25
- const flows = await getFlows({ getAllVersions: false });
10
+ // Lightweight data fetch: only raw collections, no hydration.
11
+ // The homepage only needs counts + one item per type for default URLs.
12
+ const [allDomains, allServices, allEvents, allCommands, allQueries, allFlows] = await Promise.all([
13
+ getCollection('domains'),
14
+ getCollection('services'),
15
+ getCollection('events'),
16
+ getCollection('commands'),
17
+ getCollection('queries'),
18
+ getCollection('flows'),
19
+ ]);
20
+
21
+ const isCurrentVersion = (item: { data: { hidden?: boolean }; filePath?: string }) =>
22
+ item.data.hidden !== true && !item.filePath?.includes('versioned');
23
+
24
+ const domains = allDomains.filter(isCurrentVersion);
25
+ const services = allServices.filter(isCurrentVersion);
26
+ const messages = [...allEvents, ...allCommands, ...allQueries].filter(isCurrentVersion);
27
+ const flows = allFlows.filter(isCurrentVersion);
26
28
 
27
29
  // Check if catalog has content
28
30
  const hasContent = domains.length > 0 || services.length > 0 || messages.length > 0 || flows.length > 0;
29
31
 
30
32
  const getDefaultUrl = (route: string, defaultValue: string) => {
31
- if (domains.length > 0) return buildUrl(`/${route}/domains/${domains[0].data.id}/${domains[0].data.latestVersion}`);
32
- if (services.length > 0) return buildUrl(`/${route}/services/${services[0].data.id}/${services[0].data.latestVersion}`);
33
- if (flows.length > 0) return buildUrl(`/${route}/flows/${flows[0].data.id}/${flows[0].data.latestVersion}`);
33
+ if (domains.length > 0) return buildUrl(`/${route}/domains/${domains[0].data.id}/${domains[0].data.version}`);
34
+ if (services.length > 0) return buildUrl(`/${route}/services/${services[0].data.id}/${services[0].data.version}`);
35
+ if (flows.length > 0) return buildUrl(`/${route}/flows/${flows[0].data.id}/${flows[0].data.version}`);
34
36
  return buildUrl(defaultValue);
35
37
  };
36
38
 
@@ -40,10 +42,9 @@ const topTiles = [
40
42
  count: domains.length,
41
43
  description: 'Business domains',
42
44
  href: buildUrl('/discover/domains'),
43
- icon: RectangleGroupIcon,
44
- bgColor: 'bg-yellow-500',
45
- borderColor: 'border-yellow-200',
46
- textColor: 'text-yellow-600',
45
+ badgeBg: 'bg-[rgb(var(--ec-badge-domain-bg))]',
46
+ badgeText: 'text-[rgb(var(--ec-badge-domain-text))]',
47
+ glowColor: 'var(--ec-badge-domain-text)',
47
48
  emptyText: 'No domains yet',
48
49
  addHref: 'https://www.eventcatalog.dev/docs/domains',
49
50
  },
@@ -52,10 +53,9 @@ const topTiles = [
52
53
  count: services.length,
53
54
  description: 'Documented services',
54
55
  href: buildUrl('/discover/services'),
55
- icon: ServerIcon,
56
- bgColor: 'bg-pink-500',
57
- borderColor: 'border-pink-200',
58
- textColor: 'text-pink-600',
56
+ badgeBg: 'bg-[rgb(var(--ec-badge-service-bg))]',
57
+ badgeText: 'text-[rgb(var(--ec-badge-service-text))]',
58
+ glowColor: 'var(--ec-badge-service-text)',
59
59
  emptyText: 'No services yet',
60
60
  addHref: 'https://www.eventcatalog.dev/docs/services',
61
61
  },
@@ -64,10 +64,9 @@ const topTiles = [
64
64
  count: messages.length,
65
65
  description: 'Events, commands & queries',
66
66
  href: buildUrl('/discover/events'),
67
- icon: ChatBubbleLeftIcon,
68
- bgColor: 'bg-blue-500',
69
- borderColor: 'border-blue-200',
70
- textColor: 'text-blue-600',
67
+ badgeBg: 'bg-[rgb(var(--ec-badge-message-bg))]',
68
+ badgeText: 'text-[rgb(var(--ec-badge-message-text))]',
69
+ glowColor: 'var(--ec-badge-message-text)',
71
70
  emptyText: 'No messages yet',
72
71
  addHref: 'https://www.eventcatalog.dev/docs/messages',
73
72
  },
@@ -76,10 +75,9 @@ const topTiles = [
76
75
  count: flows.length,
77
76
  description: 'Business flows',
78
77
  href: buildUrl('/discover/flows'),
79
- icon: Workflow,
80
- bgColor: 'bg-[rgb(var(--ec-accent))]',
81
- borderColor: 'border-[rgb(var(--ec-accent)/0.3)]',
82
- textColor: 'text-[rgb(var(--ec-accent))]',
78
+ badgeBg: 'bg-[rgb(var(--ec-badge-query-bg))]',
79
+ badgeText: 'text-[rgb(var(--ec-badge-query-text))]',
80
+ glowColor: 'var(--ec-badge-query-text)',
83
81
  emptyText: 'No flows yet',
84
82
  addHref: 'https://www.eventcatalog.dev/docs/flows',
85
83
  },
@@ -88,52 +86,58 @@ const topTiles = [
88
86
  const quickActions = [
89
87
  {
90
88
  title: 'Documentation',
91
- description: 'Browse all documented resources',
89
+ description: 'Explore all your events, commands, queries, and services in one place.',
92
90
  icon: BookOpenText,
93
91
  href: getDefaultUrl('docs', 'domains'),
94
92
  iconBg: 'bg-blue-50 dark:bg-blue-500/10',
95
93
  iconColor: 'text-blue-600 dark:text-blue-400',
94
+ span: 'lg:col-span-2',
96
95
  },
97
96
  {
98
97
  title: 'Visualizer',
99
- description: 'Interactive architecture diagrams',
98
+ description: 'Visualize service relationships and message flows across your architecture.',
100
99
  icon: Workflow,
101
100
  href: getDefaultUrl('visualiser', 'domains'),
102
101
  iconBg: 'bg-[rgb(var(--ec-accent-subtle))]',
103
102
  iconColor: 'text-[rgb(var(--ec-accent))]',
103
+ span: '',
104
104
  },
105
105
  {
106
106
  title: 'Discover',
107
- description: 'Search and filter all resources',
107
+ description: 'Quickly find any event, service, or team across your entire catalog.',
108
108
  icon: TableProperties,
109
109
  href: buildUrl('/discover/events'),
110
110
  iconBg: 'bg-teal-50 dark:bg-teal-500/10',
111
111
  iconColor: 'text-teal-600 dark:text-teal-400',
112
+ span: '',
112
113
  },
113
114
  {
114
115
  title: 'Schema Explorer',
115
- description: 'Browse and compare schemas',
116
+ description: 'Track schema evolution, compare versions, and catch breaking changes.',
116
117
  icon: FileJson,
117
118
  href: buildUrl('/schemas/explorer'),
118
119
  iconBg: 'bg-amber-50 dark:bg-amber-500/10',
119
120
  iconColor: 'text-amber-600 dark:text-amber-400',
121
+ span: '',
120
122
  },
121
123
  {
122
124
  title: 'Team Directory',
123
- description: 'Ownership & contacts',
125
+ description: 'See who owns what — find team contacts and service ownership at a glance.',
124
126
  icon: BookUser,
125
127
  href: buildUrl('/directory/users'),
126
128
  iconBg: 'bg-orange-50 dark:bg-orange-500/10',
127
129
  iconColor: 'text-orange-600 dark:text-orange-400',
130
+ span: '',
128
131
  },
129
132
  {
130
133
  title: 'API & SDK',
131
- description: 'Programmatic access',
134
+ description: 'Query and manage your catalog programmatically with a fully-typed SDK.',
132
135
  icon: Code2,
133
136
  href: 'https://www.eventcatalog.dev/docs/sdk',
134
137
  iconBg: 'bg-indigo-50 dark:bg-indigo-500/10',
135
138
  iconColor: 'text-indigo-600 dark:text-indigo-400',
136
139
  external: true,
140
+ span: 'lg:col-span-2',
137
141
  },
138
142
  ];
139
143
  ---
@@ -141,83 +145,50 @@ const quickActions = [
141
145
  <VerticalSideBarLayout title="EventCatalog">
142
146
  <div class="min-h-screen font-inter">
143
147
  <!-- Hero Section -->
144
- <div class="relative bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] border-b border-[rgb(var(--ec-page-border))]">
145
- <main class="container px-6 lg:px-8 mx-auto py-10 max-w-[85em]">
146
- <div class="max-w-3xl">
147
- <h1 class="text-3xl md:text-4xl font-bold mb-3 text-[rgb(var(--ec-page-text))] tracking-tight">
148
- {config?.organizationName || 'EventCatalog'}
149
- </h1>
150
- <p class="text-base text-[rgb(var(--ec-page-text-muted))] leading-relaxed mb-6">
151
- {
152
- config.tagline ||
153
- 'Explore and understand your event-driven architecture. Browse documentation, visualize dependencies, and discover how your systems communicate.'
154
- }
155
- </p>
156
-
157
- <!-- Primary CTAs -->
158
- <div class="flex flex-wrap items-center gap-3">
159
- <a
160
- href={getDefaultUrl('docs', 'domains')}
161
- class="inline-flex items-center gap-2 px-5 py-2.5 bg-[rgb(var(--ec-button-bg))] text-[rgb(var(--ec-button-text))] rounded-lg font-medium hover:bg-[rgb(var(--ec-button-bg-hover))] transition-colors"
162
- >
163
- <BookOpenText className="w-4 h-4" />
164
- Explore Docs
165
- </a>
166
- <a
167
- href={getDefaultUrl('visualiser', 'domains')}
168
- class="inline-flex items-center gap-2 px-5 py-2.5 bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] text-[rgb(var(--ec-page-text))] border border-[rgb(var(--ec-page-border))] rounded-lg font-medium hover:bg-[rgb(var(--ec-content-hover))] hover:border-[rgb(var(--ec-accent))] transition-colors"
169
- >
170
- <Workflow className="w-4 h-4" />
171
- View Architecture
172
- </a>
148
+ <div class="relative">
149
+ <main class="px-6 lg:px-8 pt-10 pb-8 max-w-[85em]">
150
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
151
+ <div>
152
+ <h1 class="text-2xl md:text-3xl font-bold mb-1 text-[rgb(var(--ec-page-text))] tracking-tight">
153
+ Welcome to <span class="italic text-[rgb(var(--ec-accent))]">{config?.organizationName || 'EventCatalog'}</span>
154
+ </h1>
155
+ <p class="text-sm text-[rgb(var(--ec-page-text-muted))] leading-relaxed">
156
+ {config.tagline || 'Explore and understand your event-driven architecture'}
157
+ </p>
173
158
  </div>
174
159
  </div>
175
160
  </main>
176
161
  </div>
177
162
 
178
163
  <!-- Main Content -->
179
- <main class="container px-6 lg:px-8 mx-auto py-8 max-w-[85em]">
180
- <!-- Architecture Stats -->
164
+ <main class="px-6 lg:px-8 pb-8 max-w-[85em]">
165
+ <!-- Stat Cards -->
181
166
  <section class="mb-10">
182
- <div class="flex items-center justify-between mb-4">
183
- <h2 class="text-sm font-semibold uppercase tracking-wider text-[rgb(var(--ec-page-text-muted))]">Your Catalog</h2>
184
- {
185
- hasContent && (
186
- <a
187
- href={buildUrl('/discover/events')}
188
- class="text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] flex items-center gap-1"
189
- >
190
- View all
191
- <ArrowRightIcon className="w-3 h-3" />
192
- </a>
193
- )
194
- }
195
- </div>
196
167
  <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
197
168
  {
198
169
  topTiles.map((tile: any) =>
199
170
  tile.count > 0 ? (
200
171
  <a
201
172
  href={tile.href}
202
- class={`group relative bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] p-5 rounded-xl border border-[rgb(var(--ec-page-border))] hover:border-t-[rgb(var(--ec-accent))] hover:border-r-[rgb(var(--ec-accent))] hover:border-b-[rgb(var(--ec-accent))] hover:shadow-sm transition-all`}
173
+ class="ec-stat-card group relative overflow-hidden bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] p-5 rounded-xl border transition-all"
174
+ style={`--glow-color: rgb(${tile.glowColor}); border-color: rgb(${tile.glowColor} / 0.2);`}
203
175
  >
204
- <div class={`absolute left-0 top-0 bottom-0 w-1 ${tile.bgColor} rounded-l-xl`} />
205
- <div class="flex items-start justify-between mb-3">
206
- <div class={`p-2 rounded-lg ${tile.iconBg || 'bg-[rgb(var(--ec-content-hover))]'}`}>
207
- <tile.icon className={`w-4 h-4 ${tile.textColor}`} />
208
- </div>
209
- <ArrowRightIcon className="w-4 h-4 text-[rgb(var(--ec-icon-color))] group-hover:text-[rgb(var(--ec-page-text-muted))] transition-colors" />
176
+ <div class="flex items-center justify-between mb-4">
177
+ <span
178
+ class={`inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-semibold ${tile.badgeBg} ${tile.badgeText}`}
179
+ >
180
+ {tile.title}
181
+ </span>
210
182
  </div>
211
- <div class="text-2xl font-bold text-[rgb(var(--ec-page-text))] mb-0.5">{tile.count}</div>
183
+ <div class="text-3xl font-bold text-[rgb(var(--ec-page-text))] mb-1">{tile.count}</div>
212
184
  <div class="text-sm text-[rgb(var(--ec-page-text-muted))]">{tile.description}</div>
213
185
  </a>
214
186
  ) : (
215
- <div class="relative bg-[rgb(var(--ec-content-hover))] p-5 rounded-xl border border-dashed border-[rgb(var(--ec-page-border))]">
216
- <div class={`absolute left-0 top-0 bottom-0 w-1 bg-[rgb(var(--ec-icon-color))] rounded-l-xl`} />
217
- <div class="flex items-start justify-between mb-3">
218
- <div class="p-2 rounded-lg bg-[rgb(var(--ec-content-hover))]">
219
- <tile.icon className="w-4 h-4 text-[rgb(var(--ec-icon-color))]" />
220
- </div>
187
+ <div class="relative overflow-hidden bg-[rgb(var(--ec-content-hover))] p-5 rounded-xl border border-dashed border-[rgb(var(--ec-page-border))]">
188
+ <div class="flex items-center justify-between mb-4">
189
+ <span class="inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-semibold bg-[rgb(var(--ec-badge-default-bg))] text-[rgb(var(--ec-badge-default-text))]">
190
+ {tile.title}
191
+ </span>
221
192
  </div>
222
193
  <div class="text-sm font-medium text-[rgb(var(--ec-page-text-muted))] mb-2">{tile.emptyText}</div>
223
194
  <a
@@ -237,20 +208,26 @@ const quickActions = [
237
208
 
238
209
  <!-- Explore Section - Full Width -->
239
210
  <section class="mb-10">
240
- <h2 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-4">Explore</h2>
241
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
211
+ <div class="flex items-center gap-4 mb-4">
212
+ <h2 class="text-sm font-semibold uppercase tracking-wider text-[rgb(var(--ec-page-text-muted))] flex-shrink-0">
213
+ Explore
214
+ </h2>
215
+ <div class="flex-1 h-px bg-[rgb(var(--ec-page-border))]"></div>
216
+ </div>
217
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
242
218
  {
243
219
  quickActions.map((action: any) => (
244
220
  <a
245
221
  href={action.href}
246
222
  target={action.external ? '_blank' : undefined}
247
- class="group flex items-start gap-4 bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] p-5 rounded-xl border border-[rgb(var(--ec-page-border))] hover:border-[rgb(var(--ec-accent))] hover:shadow-sm transition-all"
223
+ class={`ec-stat-card group flex flex-col bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] p-6 rounded-xl border border-[rgb(var(--ec-page-border))] hover:border-[rgb(var(--ec-accent)/0.5)] transition-all min-h-[160px] ${action.span}`}
224
+ style="--glow-color: rgb(var(--ec-accent));"
248
225
  >
249
- <div class={`${action.iconBg} p-3 rounded-xl flex-shrink-0`}>
250
- <action.icon className={`w-5 h-5 ${action.iconColor}`} />
251
- </div>
252
- <div class="flex-1 min-w-0 pt-0.5">
253
- <div class="font-semibold text-[rgb(var(--ec-page-text))] mb-1 flex items-center gap-1.5">
226
+ <div class="flex items-center gap-3 mb-3">
227
+ <div class={`${action.iconBg} p-2.5 rounded-lg`}>
228
+ <action.icon className={`w-5 h-5 ${action.iconColor}`} />
229
+ </div>
230
+ <span class="font-semibold text-[rgb(var(--ec-page-text))] flex items-center gap-1.5">
254
231
  {action.title}
255
232
  {action.external && (
256
233
  <svg
@@ -267,9 +244,9 @@ const quickActions = [
267
244
  />
268
245
  </svg>
269
246
  )}
270
- </div>
271
- <div class="text-sm text-[rgb(var(--ec-page-text-muted))] leading-relaxed">{action.description}</div>
247
+ </span>
272
248
  </div>
249
+ <p class="text-sm text-[rgb(var(--ec-page-text-muted))] leading-relaxed">{action.description}</p>
273
250
  </a>
274
251
  ))
275
252
  }
@@ -336,3 +313,51 @@ const quickActions = [
336
313
  </main>
337
314
  </div>
338
315
  </VerticalSideBarLayout>
316
+
317
+ <style is:global>
318
+ .ec-stat-card {
319
+ position: relative;
320
+ }
321
+ .ec-stat-card::before {
322
+ content: '';
323
+ position: absolute;
324
+ inset: 0;
325
+ border-radius: inherit;
326
+ transition: opacity 0.3s ease;
327
+ opacity: 0;
328
+ pointer-events: none;
329
+ z-index: 0;
330
+ }
331
+ /* Dark mode: radial glow that follows cursor */
332
+ :root[data-theme='dark'] .ec-stat-card::before {
333
+ background: radial-gradient(400px circle at var(--mouse-x, 50%) var(--mouse-y, 50%), var(--glow-color) 0%, transparent 70%);
334
+ }
335
+ :root[data-theme='dark'] .ec-stat-card:hover::before {
336
+ opacity: 0.15;
337
+ }
338
+ /* Light mode: subtle solid background */
339
+ :root:not([data-theme='dark']) .ec-stat-card::before,
340
+ :root[data-theme='light'] .ec-stat-card::before {
341
+ background: rgb(var(--ec-content-hover));
342
+ }
343
+ :root:not([data-theme='dark']) .ec-stat-card:hover::before,
344
+ :root[data-theme='light'] .ec-stat-card:hover::before {
345
+ opacity: 1;
346
+ }
347
+ .ec-stat-card > * {
348
+ position: relative;
349
+ z-index: 1;
350
+ }
351
+ </style>
352
+
353
+ <script>
354
+ document.querySelectorAll('.ec-stat-card').forEach((card) => {
355
+ card.addEventListener('mousemove', (e) => {
356
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
357
+ const x = (e as MouseEvent).clientX - rect.left;
358
+ const y = (e as MouseEvent).clientY - rect.top;
359
+ (e.currentTarget as HTMLElement).style.setProperty('--mouse-x', `${x}px`);
360
+ (e.currentTarget as HTMLElement).style.setProperty('--mouse-y', `${y}px`);
361
+ });
362
+ });
363
+ </script>
@@ -1,9 +1,6 @@
1
1
  ---
2
2
  import Footer from '@layouts/Footer.astro';
3
3
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
4
- import components from '@components/MDX/page-components';
5
- import mdxComponents from '@components/MDX/components';
6
- import NodeGraph from '@components/MDX/NodeGraph/NodeGraph.astro';
7
4
  import { getMDXComponentsByName } from '@utils/markdown';
8
5
  import { resourceToCollectionMap } from '@utils/collections/util';
9
6
  import { buildUrl } from '@utils/url-builder';
@@ -13,6 +10,8 @@ import config from '@config';
13
10
 
14
11
  let nodeGraphs: any[] = [];
15
12
  let CustomContent = null;
13
+ let NodeGraph = null;
14
+ let customHomepageComponents: Record<string, any> = {};
16
15
 
17
16
  import path from 'path';
18
17
  import { existsSync, readFileSync } from 'fs';
@@ -26,8 +25,20 @@ if (existsSync(pathToUserDefinedLandingPage) && isCustomLandingPageEnabled()) {
26
25
  // @ts-ignore
27
26
  CustomContent = Object.values(customPages)[0]?.default || null;
28
27
 
28
+ const [{ default: pageComponents }, { default: mdxComponents }] = await Promise.all([
29
+ import('@components/MDX/page-components'),
30
+ import('@components/MDX/components'),
31
+ ]);
32
+ customHomepageComponents = { ...pageComponents, ...mdxComponents(props) };
33
+
29
34
  const rawContent = readFileSync(pathToUserDefinedLandingPage, 'utf-8');
30
35
  nodeGraphs = getMDXComponentsByName(rawContent, 'NodeGraph') || [];
36
+
37
+ // Avoid pulling visualizer CSS into the default homepage bundle.
38
+ // Only load the NodeGraph component when the custom homepage actually uses it.
39
+ if (nodeGraphs.length > 0) {
40
+ NodeGraph = (await import('@components/MDX/NodeGraph/NodeGraph.astro')).default;
41
+ }
31
42
  }
32
43
 
33
44
  if (config.landingPage) {
@@ -42,12 +53,13 @@ if (config.landingPage) {
42
53
  <div class="flex docs-layout w-full">
43
54
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8">
44
55
  <div class="w-full !max-w-none">
45
- <CustomContent components={{ ...components, ...mdxComponents(props) }} />
56
+ <CustomContent components={customHomepageComponents} />
46
57
  </div>
47
58
  <Footer />
48
59
  </div>
49
60
  </div>
50
- {nodeGraphs.length > 0 &&
61
+ {NodeGraph &&
62
+ nodeGraphs.length > 0 &&
51
63
  nodeGraphs.map((nodeGraph: any) => {
52
64
  const collection = resourceToCollectionMap[nodeGraph.type as keyof typeof resourceToCollectionMap];
53
65
  return (
@@ -157,7 +157,7 @@
157
157
  --ec-content-bg: 13 17 23; /* #0d1117 */
158
158
  --ec-content-text: 201 209 217; /* #c9d1d9 - softer than pure white */
159
159
  --ec-content-text-muted: 139 148 158; /* #8b949e */
160
- --ec-content-text-secondary: 139 148 158; /* #8b949e - muted for sidebar children */
160
+ --ec-content-text-secondary: 170 178 186; /* #aab2ba - sidebar children text */
161
161
  --ec-content-border: 33 38 45; /* #21262d */
162
162
  --ec-content-hover: 22 27 34; /* #161b22 */
163
163
  --ec-content-active: 33 38 45; /* #21262d */
@@ -175,7 +175,7 @@
175
175
  /* Main content/docs area - base dark surface */
176
176
  --ec-page-bg: 13 17 23; /* #0d1117 */
177
177
  --ec-page-text: 240 246 252; /* #f0f6fc */
178
- --ec-page-text-muted: 139 148 158; /* #8b949e */
178
+ --ec-page-text-muted: 163 172 182; /* #a3acb6 */
179
179
  --ec-page-border: 33 38 45; /* #21262d */
180
180
 
181
181
  /* Card/elevated surfaces - for depth */
@@ -257,3 +257,8 @@
257
257
  :root[data-theme="dark"] .prose table td {
258
258
  border-color: rgb(33 38 45); /* #21262d */
259
259
  }
260
+
261
+ /* Invert dark SVG icons in dark mode so they stay visible */
262
+ :root[data-theme="dark"] .ec-themed-icon {
263
+ filter: invert(1);
264
+ }
@@ -106,9 +106,9 @@
106
106
  /* Forest Dark Mode */
107
107
  :root[data-catalog-theme="forest"][data-theme="dark"] {
108
108
  /* Header - deep forest dark */
109
- --ec-header-bg: 17 24 39; /* #111827 gray-900 */
109
+ --ec-header-bg: 3 7 18; /* #030712 gray-950 */
110
110
  --ec-header-text: 243 244 246; /* #f3f4f6 gray-100 */
111
- --ec-header-border: 31 41 55; /* #1f2937 gray-800 */
111
+ --ec-header-border: 17 24 39; /* #111827 gray-900 */
112
112
 
113
113
  /* Accent colors - muted forest green for dark */
114
114
  --ec-accent: 74 222 128; /* #4ade80 (green-400) */
@@ -127,7 +127,7 @@
127
127
  --ec-dropdown-bg: 17 24 39; /* #111827 */
128
128
  --ec-dropdown-text: 209 213 219; /* #d1d5db */
129
129
  --ec-dropdown-hover: 31 41 55; /* #1f2937 */
130
- --ec-dropdown-border: 55 65 81; /* #374151 */
130
+ --ec-dropdown-border: 31 41 55; /* #1f2937 gray-800 */
131
131
 
132
132
  /* Icons */
133
133
  --ec-icon-color: 156 163 175; /* #9ca3af */
@@ -136,7 +136,7 @@
136
136
  /* Sidebar / Vertical Nav */
137
137
  --ec-sidebar-bg: 3 7 18; /* #030712 gray-950 */
138
138
  --ec-sidebar-bg-gradient: 3 7 18; /* #030712 */
139
- --ec-sidebar-border: 31 41 55; /* #1f2937 */
139
+ --ec-sidebar-border: 17 24 39; /* #111827 gray-900 */
140
140
  --ec-sidebar-text: 156 163 175; /* #9ca3af */
141
141
  --ec-sidebar-active-bg: 22 101 52; /* #166534 green-800 */
142
142
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
@@ -147,13 +147,13 @@
147
147
  --ec-content-text: 243 244 246; /* #f3f4f6 */
148
148
  --ec-content-text-muted: 156 163 175; /* #9ca3af */
149
149
  --ec-content-text-secondary: 156 163 175; /* #9ca3af */
150
- --ec-content-border: 31 41 55; /* #1f2937 */
150
+ --ec-content-border: 17 24 39; /* #111827 gray-900 */
151
151
  --ec-content-hover: 17 24 39; /* #111827 */
152
152
  --ec-content-active: 31 41 55; /* #1f2937 */
153
153
 
154
154
  /* Input fields */
155
155
  --ec-input-bg: 17 24 39; /* #111827 */
156
- --ec-input-border: 55 65 81; /* #374151 */
156
+ --ec-input-border: 31 41 55; /* #1f2937 gray-800 */
157
157
  --ec-input-text: 243 244 246; /* #f3f4f6 */
158
158
  --ec-input-placeholder: 107 114 128; /* #6b7280 */
159
159
 
@@ -165,7 +165,7 @@
165
165
  --ec-page-bg: 3 7 18; /* #030712 */
166
166
  --ec-page-text: 243 244 246; /* #f3f4f6 */
167
167
  --ec-page-text-muted: 156 163 175; /* #9ca3af */
168
- --ec-page-border: 31 41 55; /* #1f2937 */
168
+ --ec-page-border: 17 24 39; /* #111827 gray-900 */
169
169
 
170
170
  /* Card/elevated surfaces */
171
171
  --ec-card-bg: 3 7 18; /* #030712 */
@@ -8,6 +8,8 @@ import {
8
8
  generateIdForNode,
9
9
  buildContextMenuForService,
10
10
  buildContextMenuForResource,
11
+ DEFAULT_NODE_WIDTH,
12
+ DEFAULT_NODE_HEIGHT,
11
13
  } from './utils/utils';
12
14
  import { MarkerType } from '@xyflow/react';
13
15
  import { findMatchingNodes } from '@utils/collections/util';
@@ -84,6 +86,7 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
84
86
  type: 'default',
85
87
  style: {
86
88
  strokeWidth: 1,
89
+ stroke: 'var(--ec-edge-stroke, #6b7280)',
87
90
  },
88
91
  });
89
92
  }
@@ -119,6 +122,7 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
119
122
  type: 'default',
120
123
  style: {
121
124
  strokeWidth: 1,
125
+ stroke: 'var(--ec-edge-stroke, #6b7280)',
122
126
  },
123
127
  });
124
128
  }
@@ -277,7 +281,7 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
277
281
  });
278
282
 
279
283
  nodes.forEach((node: any) => {
280
- flow.setNode(node.id, { width: 150, height: 100 });
284
+ flow.setNode(node.id, { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT });
281
285
  });
282
286
 
283
287
  edges.forEach((edge: any) => {
@@ -8,6 +8,8 @@ import {
8
8
  createEdge,
9
9
  getColorFromString,
10
10
  buildContextMenuForResource,
11
+ DEFAULT_NODE_WIDTH,
12
+ DEFAULT_NODE_HEIGHT,
11
13
  } from '@utils/node-graphs/utils/utils';
12
14
 
13
15
  import { findInMap, createVersionedMap, mergeMaps, collectionToResourceMap } from '@utils/collections/util';
@@ -208,7 +210,7 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
208
210
  });
209
211
 
210
212
  nodes.forEach((node: any) => {
211
- flow.setNode(node.id, { width: 150, height: 100 });
213
+ flow.setNode(node.id, { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT });
212
214
  });
213
215
 
214
216
  edges.forEach((edge: any) => {
@@ -1,6 +1,12 @@
1
1
  import { getCollection, type CollectionEntry } from 'astro:content';
2
2
  import dagre from 'dagre';
3
- import { generateIdForNode, createDagreGraph, calculatedNodes, createEdge } from '@utils/node-graphs/utils/utils';
3
+ import {
4
+ generateIdForNode,
5
+ createDagreGraph,
6
+ calculatedNodes,
7
+ createEdge,
8
+ getOperationFields,
9
+ } from '@utils/node-graphs/utils/utils';
4
10
  import { findInMap, createVersionedMap } from '@utils/collections/util';
5
11
  import type { Node, Edge } from '@xyflow/react';
6
12
  import { getDomains } from '@utils/collections/domains';
@@ -170,7 +176,7 @@ export const getDomainsCanvasData = async (): Promise<DomainCanvasData> => {
170
176
  position: { x: 0, y: 0 }, // Temporary position, will be calculated by dagre
171
177
  data: {
172
178
  mode: 'simple',
173
- message: { ...messageObject.data },
179
+ message: { ...messageObject.data, ...getOperationFields(messageObject.data) },
174
180
  },
175
181
  sourcePosition: 'right',
176
182
  targetPosition: 'left',
@@ -55,7 +55,7 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
55
55
  const servicesCount = domainServices.length;
56
56
  const SERVICES_PER_ROW = 1;
57
57
  const SERVICE_WIDTH = 330;
58
- const SERVICE_HEIGHT = 100;
58
+ const SERVICE_HEIGHT = 120;
59
59
  const PADDING = 40;
60
60
  const TITLE_HEIGHT = 20;
61
61
 
@@ -1,6 +1,6 @@
1
1
  import { getCollection, type CollectionEntry } from 'astro:content';
2
2
  import dagre from 'dagre';
3
- import { createDagreGraph, calculatedNodes } from '@utils/node-graphs/utils/utils';
3
+ import { createDagreGraph, calculatedNodes, DEFAULT_NODE_WIDTH, DEFAULT_NODE_HEIGHT } from '@utils/node-graphs/utils/utils';
4
4
  import { MarkerType } from '@xyflow/react';
5
5
  import type { Node as NodeType } from '@xyflow/react';
6
6
  import { createVersionedMap, findInMap } from '@utils/collections/util';
@@ -153,7 +153,7 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
153
153
  });
154
154
 
155
155
  nodes.forEach((node: any) => {
156
- graph.setNode(node.id, { width: 150, height: 100 });
156
+ graph.setNode(node.id, { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT });
157
157
  });
158
158
 
159
159
  edges.forEach((edge: any) => {