@eventcatalog/core 3.29.2 → 3.31.1

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 (113) 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-36IA4UE4.js → chunk-7IGMIOQF.js} +1 -1
  6. package/dist/{chunk-EGQGCB2B.js → chunk-HVOLSUC2.js} +1 -1
  7. package/dist/{chunk-DB4IQ3GB.js → chunk-LWVHWR77.js} +1 -1
  8. package/dist/{chunk-VEUNSJ6Z.js → chunk-QIJOBQZ7.js} +1 -1
  9. package/dist/{chunk-MEJOYC5Z.js → chunk-UY5QDWK7.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/astro.config.mjs +11 -7
  19. package/eventcatalog/public/logo.png +0 -0
  20. package/eventcatalog/src/components/CopyAsMarkdown.tsx +29 -24
  21. package/eventcatalog/src/components/EnvironmentDropdown.tsx +33 -21
  22. package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +3 -53
  23. package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +144 -91
  24. package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +112 -109
  25. package/eventcatalog/src/components/Header.astro +9 -19
  26. package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +12 -14
  27. package/eventcatalog/src/components/MDX/Accordion/AccordionGroup.astro +11 -3
  28. package/eventcatalog/src/components/MDX/Design/Design.astro +1 -1
  29. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +15 -5
  30. package/eventcatalog/src/components/MDX/Tiles/Tile.astro +11 -8
  31. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +164 -53
  32. package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +1 -1
  33. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +4 -4
  34. package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +12 -10
  35. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +48 -77
  36. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +238 -169
  37. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +189 -230
  38. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +39 -36
  39. package/eventcatalog/src/components/Search/Search.astro +1 -1
  40. package/eventcatalog/src/components/Seo.astro +1 -1
  41. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +218 -0
  42. package/eventcatalog/src/components/Settings/BillingSettingsForm.tsx +265 -0
  43. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +371 -0
  44. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +183 -0
  45. package/eventcatalog/src/components/Settings/LogoUpload.tsx +137 -0
  46. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +91 -0
  47. package/eventcatalog/src/components/Settings/ReadOnlyBanner.tsx +18 -0
  48. package/eventcatalog/src/components/Settings/Row.tsx +59 -0
  49. package/eventcatalog/src/components/Settings/SettingsShared.tsx +176 -0
  50. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -3
  51. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +233 -261
  52. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +116 -68
  53. package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +2 -2
  54. package/eventcatalog/src/components/Tables/Discover/columns.tsx +130 -197
  55. package/eventcatalog/src/components/Tables/Table.tsx +21 -18
  56. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +79 -131
  57. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +104 -175
  58. package/eventcatalog/src/content.config.ts +1 -1
  59. package/eventcatalog/src/enterprise/auth/error.astro +1 -1
  60. package/eventcatalog/src/enterprise/auth/login.astro +1 -1
  61. package/eventcatalog/src/enterprise/auth/middleware/middleware-auth.ts +11 -7
  62. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +97 -95
  63. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +232 -181
  64. package/eventcatalog/src/enterprise/feature.ts +2 -1
  65. package/eventcatalog/src/enterprise/fields/pages/fields.astro +10 -8
  66. package/eventcatalog/src/enterprise/integrations/eventcatalog-features.ts +0 -8
  67. package/eventcatalog/src/layouts/DirectoryLayout.astro +17 -88
  68. package/eventcatalog/src/layouts/SettingsLayout.astro +116 -0
  69. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +562 -141
  70. package/eventcatalog/src/layouts/VisualiserLayout.astro +7 -2
  71. package/eventcatalog/src/pages/_index.astro +253 -256
  72. package/eventcatalog/src/pages/api/settings/ai.ts +57 -0
  73. package/eventcatalog/src/pages/api/settings/general.ts +71 -0
  74. package/eventcatalog/src/pages/api/settings/logo.ts +113 -0
  75. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -3
  76. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +223 -73
  77. package/eventcatalog/src/pages/discover/[type]/index.astro +22 -141
  78. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +130 -30
  79. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +147 -53
  80. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +6 -2
  81. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +2 -2
  82. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +22 -19
  83. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +71 -61
  84. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +5 -1
  85. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +3 -3
  86. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +6 -32
  87. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +5 -1
  88. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +11 -4
  89. package/eventcatalog/src/pages/docs/users/[id]/index.astro +12 -5
  90. package/eventcatalog/src/pages/schemas/explorer/index.astro +10 -8
  91. package/eventcatalog/src/pages/settings/assistant.astro +37 -0
  92. package/eventcatalog/src/pages/settings/billing.astro +17 -0
  93. package/eventcatalog/src/pages/settings/general.astro +32 -0
  94. package/eventcatalog/src/pages/settings/index.astro +21 -0
  95. package/eventcatalog/src/pages/settings/llm-access.astro +34 -0
  96. package/eventcatalog/src/pages/settings/mcp.astro +14 -0
  97. package/eventcatalog/src/pages/studio.astro +1 -1
  98. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +2 -7
  99. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +2 -2
  100. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +2 -7
  101. package/eventcatalog/src/styles/theme.css +95 -30
  102. package/eventcatalog/src/styles/themes/forest.css +17 -9
  103. package/eventcatalog/src/styles/themes/ocean.css +10 -2
  104. package/eventcatalog/src/styles/themes/sapphire.css +10 -2
  105. package/eventcatalog/src/styles/themes/sunset.css +25 -17
  106. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +13 -0
  107. package/eventcatalog/src/utils/eventcatalog-config/config-schema.ts +49 -0
  108. package/eventcatalog/src/utils/eventcatalog-config/config-writer.ts +149 -0
  109. package/eventcatalog/src/utils/url-builder.ts +4 -2
  110. package/package.json +7 -5
  111. package/eventcatalog/public/logo.svg +0 -14
  112. package/eventcatalog/src/enterprise/plans/index.astro +0 -319
  113. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +0 -81
@@ -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
  }
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  import { buildUrl } from '@utils/url-builder';
3
- import { ArrowRightIcon, PlusIcon } from '@heroicons/react/24/outline';
4
3
  import config from '@config';
5
4
 
6
5
  import { getCollection } from 'astro:content';
7
6
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
8
- import { BookOpenText, Workflow, TableProperties, BookUser, Code2, FileJson } from 'lucide-react';
7
+ import { BookOpenText, Workflow, TableProperties, BookUser, Code2, FileJson, ArrowUpRight, ArrowRight, Plus } from 'lucide-react';
8
+
9
+ const { showNestedSideBar = true } = Astro.props;
9
10
 
10
11
  // Lightweight data fetch: only raw collections, no hydration.
11
- // The homepage only needs counts + one item per type for default URLs.
12
12
  const [allDomains, allServices, allEvents, allCommands, allQueries, allFlows] = await Promise.all([
13
13
  getCollection('domains'),
14
14
  getCollection('services'),
@@ -23,11 +23,13 @@ const isCurrentVersion = (item: { data: { hidden?: boolean }; filePath?: string
23
23
 
24
24
  const domains = allDomains.filter(isCurrentVersion);
25
25
  const services = allServices.filter(isCurrentVersion);
26
- const messages = [...allEvents, ...allCommands, ...allQueries].filter(isCurrentVersion);
26
+ const events = allEvents.filter(isCurrentVersion);
27
+ const commands = allCommands.filter(isCurrentVersion);
28
+ const queries = allQueries.filter(isCurrentVersion);
29
+ const messages = [...events, ...commands, ...queries];
27
30
  const flows = allFlows.filter(isCurrentVersion);
28
31
 
29
- // Check if catalog has content
30
- const hasContent = domains.length > 0 || services.length > 0 || messages.length > 0 || flows.length > 0;
32
+ const totalResources = domains.length + services.length + messages.length + flows.length;
31
33
 
32
34
  const getDefaultUrl = (route: string, defaultValue: string) => {
33
35
  if (domains.length > 0) return buildUrl(`/${route}/domains/${domains[0].data.id}/${domains[0].data.version}`);
@@ -36,328 +38,323 @@ const getDefaultUrl = (route: string, defaultValue: string) => {
36
38
  return buildUrl(defaultValue);
37
39
  };
38
40
 
39
- const topTiles = [
41
+ interface StatTile {
42
+ label: string;
43
+ count: number;
44
+ href: string;
45
+ emptyHref: string;
46
+ tone: string; // CSS color expression — drives accent rail + count gradient
47
+ }
48
+
49
+ const statTiles: StatTile[] = [
40
50
  {
41
- title: 'Domains',
51
+ label: 'Domains',
42
52
  count: domains.length,
43
- description: 'Business domains',
44
53
  href: buildUrl('/discover/domains'),
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)',
48
- emptyText: 'No domains yet',
49
- addHref: 'https://www.eventcatalog.dev/docs/domains',
54
+ emptyHref: 'https://www.eventcatalog.dev/docs/domains',
55
+ tone: '250 204 21', // amber-400
50
56
  },
51
57
  {
52
- title: 'Services',
58
+ label: 'Services',
53
59
  count: services.length,
54
- description: 'Documented services',
55
60
  href: buildUrl('/discover/services'),
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
- emptyText: 'No services yet',
60
- addHref: 'https://www.eventcatalog.dev/docs/services',
61
+ emptyHref: 'https://www.eventcatalog.dev/docs/services',
62
+ tone: '244 114 182', // pink-400
61
63
  },
62
64
  {
63
- title: 'Messages',
65
+ label: 'Messages',
64
66
  count: messages.length,
65
- description: 'Events, commands & queries',
66
67
  href: buildUrl('/discover/events'),
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)',
70
- emptyText: 'No messages yet',
71
- addHref: 'https://www.eventcatalog.dev/docs/messages',
68
+ emptyHref: 'https://www.eventcatalog.dev/docs/messages',
69
+ tone: '167 139 250', // violet-400
72
70
  },
73
71
  {
74
- title: 'Flows',
72
+ label: 'Flows',
75
73
  count: flows.length,
76
- description: 'Business flows',
77
74
  href: buildUrl('/discover/flows'),
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)',
81
- emptyText: 'No flows yet',
82
- addHref: 'https://www.eventcatalog.dev/docs/flows',
75
+ emptyHref: 'https://www.eventcatalog.dev/docs/flows',
76
+ tone: '94 234 212', // teal-300
83
77
  },
84
78
  ];
85
79
 
86
- const quickActions = [
80
+ interface ExploreCard {
81
+ title: string;
82
+ description: string;
83
+ icon: any;
84
+ href: string;
85
+ external?: boolean;
86
+ /** When true, renders as the lead "feature" card with extra width and CTA styling. */
87
+ feature?: boolean;
88
+ tone: string; // RGB triple for the icon tile
89
+ }
90
+
91
+ const exploreCards: ExploreCard[] = [
87
92
  {
88
93
  title: 'Documentation',
89
- description: 'Explore all your events, commands, queries, and services in one place.',
94
+ description: 'Browse every domain, service, message, and flow your teams have published.',
90
95
  icon: BookOpenText,
91
96
  href: getDefaultUrl('docs', 'domains'),
92
- iconBg: 'bg-blue-50 dark:bg-blue-500/10',
93
- iconColor: 'text-blue-600 dark:text-blue-400',
94
- span: 'lg:col-span-2',
97
+ feature: true,
98
+ tone: '96 165 250', // blue-400
95
99
  },
96
100
  {
97
101
  title: 'Visualizer',
98
- description: 'Visualize service relationships and message flows across your architecture.',
102
+ description: 'See how services connect message flows across your architecture.',
99
103
  icon: Workflow,
100
104
  href: getDefaultUrl('visualiser', 'domains'),
101
- iconBg: 'bg-[rgb(var(--ec-accent-subtle))]',
102
- iconColor: 'text-[rgb(var(--ec-accent))]',
103
- span: '',
105
+ tone: '167 139 250', // violet-400
104
106
  },
105
107
  {
106
108
  title: 'Discover',
107
- description: 'Quickly find any event, service, or team across your entire catalog.',
109
+ description: 'Search every event, service, and team across the catalog.',
108
110
  icon: TableProperties,
109
111
  href: buildUrl('/discover/events'),
110
- iconBg: 'bg-teal-50 dark:bg-teal-500/10',
111
- iconColor: 'text-teal-600 dark:text-teal-400',
112
- span: '',
112
+ tone: '94 234 212', // teal-300
113
113
  },
114
114
  {
115
115
  title: 'Schema Explorer',
116
116
  description: 'Track schema evolution, compare versions, and catch breaking changes.',
117
117
  icon: FileJson,
118
118
  href: buildUrl('/schemas/explorer'),
119
- iconBg: 'bg-amber-50 dark:bg-amber-500/10',
120
- iconColor: 'text-amber-600 dark:text-amber-400',
121
- span: '',
119
+ tone: '250 204 21', // amber-400
122
120
  },
123
121
  {
124
122
  title: 'Team Directory',
125
- description: 'See who owns what find team contacts and service ownership at a glance.',
123
+ description: 'Find owners, on-call contacts, and the team behind every service.',
126
124
  icon: BookUser,
127
125
  href: buildUrl('/directory/users'),
128
- iconBg: 'bg-orange-50 dark:bg-orange-500/10',
129
- iconColor: 'text-orange-600 dark:text-orange-400',
130
- span: '',
126
+ tone: '251 146 60', // orange-400
131
127
  },
132
128
  {
133
129
  title: 'API & SDK',
134
- description: 'Query and manage your catalog programmatically with a fully-typed SDK.',
130
+ description: 'Programmatic access — query and manage your catalog with a typed SDK.',
135
131
  icon: Code2,
136
132
  href: 'https://www.eventcatalog.dev/docs/sdk',
137
- iconBg: 'bg-indigo-50 dark:bg-indigo-500/10',
138
- iconColor: 'text-indigo-600 dark:text-indigo-400',
139
133
  external: true,
140
- span: 'lg:col-span-2',
134
+ tone: '129 140 248', // indigo-400
141
135
  },
142
136
  ];
143
137
  ---
144
138
 
145
- <VerticalSideBarLayout title="EventCatalog">
146
- <div class="min-h-screen font-inter">
147
- <!-- Hero Section -->
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>
158
- </div>
139
+ <VerticalSideBarLayout title="EventCatalog" showNestedSideBar={showNestedSideBar}>
140
+ <div class="ec-home min-h-screen">
141
+ <!-- Hero -->
142
+ <header class="ec-hero">
143
+ <div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-accent))]">Catalog</div>
144
+ <div class="mt-3 flex flex-col gap-3 md:flex-row md:items-end md:justify-between">
145
+ <div>
146
+ <h1 class="text-[28px] font-semibold leading-[1.1] tracking-tight text-[rgb(var(--ec-page-text))] md:text-[32px]">
147
+ {config?.organizationName || 'EventCatalog'}
148
+ </h1>
149
+ <p class="mt-2 max-w-2xl text-[14px] leading-relaxed text-[rgb(var(--ec-page-text-muted))]">
150
+ {config.tagline || 'Explore and understand your event-driven architecture.'}
151
+ </p>
159
152
  </div>
160
- </main>
161
- </div>
153
+ <div class="flex items-center gap-2">
154
+ <a
155
+ href={getDefaultUrl('docs', 'domains')}
156
+ class="ec-btn-primary inline-flex items-center gap-1.5 rounded-md bg-[rgb(var(--ec-page-bg))] border border-[rgb(var(--ec-page-border))] px-3.5 py-2 text-[12.5px] font-semibold text-[rgb(var(--ec-page-text-muted))] transition-colors hover:border-[rgb(var(--ec-accent)/0.5)] hover:text-[rgb(var(--ec-accent))]"
157
+ >
158
+ Explore Catalog
159
+ <ArrowRight className="h-3.5 w-3.5" />
160
+ </a>
161
+ </div>
162
+ </div>
163
+ </header>
164
+
165
+ <div class="ec-divider"></div>
162
166
 
163
- <!-- Main Content -->
164
- <main class="px-6 lg:px-8 pb-8 max-w-[85em]">
165
- <!-- Stat Cards -->
166
- <section class="mb-10">
167
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
168
- {
169
- topTiles.map((tile: any) =>
170
- tile.count > 0 ? (
171
- <a
172
- href={tile.href}
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);`}
175
- >
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>
182
- </div>
183
- <div class="text-3xl font-bold text-[rgb(var(--ec-page-text))] mb-1">{tile.count}</div>
184
- <div class="text-sm text-[rgb(var(--ec-page-text-muted))]">{tile.description}</div>
185
- </a>
167
+ <!-- Stat strip -->
168
+ <section class="ec-stats">
169
+ {
170
+ statTiles.map((tile) => (
171
+ <a
172
+ href={tile.count > 0 ? tile.href : tile.emptyHref}
173
+ target={tile.count === 0 ? '_blank' : undefined}
174
+ rel={tile.count === 0 ? 'noreferrer' : undefined}
175
+ class="ec-stat group"
176
+ style={`--tone: ${tile.tone};`}
177
+ >
178
+ <div class="flex items-baseline justify-between gap-2">
179
+ <span class="text-[11px] font-medium uppercase tracking-[0.14em] text-[rgb(var(--tone))]">{tile.label}</span>
180
+ {tile.count > 0 ? (
181
+ <ArrowUpRight className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))] transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-[rgb(var(--ec-accent))]" />
186
182
  ) : (
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>
192
- </div>
193
- <div class="text-sm font-medium text-[rgb(var(--ec-page-text-muted))] mb-2">{tile.emptyText}</div>
194
- <a
195
- href={tile.addHref}
196
- target="_blank"
197
- class="inline-flex items-center gap-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]"
198
- >
199
- <PlusIcon className="w-3 h-3" />
200
- Add {tile.title.toLowerCase()}
201
- </a>
202
- </div>
203
- )
204
- )
205
- }
206
- </div>
207
- </section>
183
+ <Plus className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))] transition-colors group-hover:text-[rgb(var(--ec-accent))]" />
184
+ )}
185
+ </div>
186
+ <div class="mt-3 flex items-baseline gap-2">
187
+ <span class="font-mono text-[34px] font-semibold leading-none tracking-tight text-[rgb(var(--ec-page-text))]">
188
+ {tile.count}
189
+ </span>
190
+ {tile.count === 0 && <span class="text-[12px] text-[rgb(var(--ec-page-text-muted))]">— add some</span>}
191
+ </div>
192
+ </a>
193
+ ))
194
+ }
195
+ </section>
208
196
 
209
- <!-- Explore Section - Full Width -->
210
- <section class="mb-10">
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">
218
- {
219
- quickActions.map((action: any) => (
220
- <a
221
- href={action.href}
222
- target={action.external ? '_blank' : undefined}
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));"
225
- >
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">
231
- {action.title}
232
- {action.external && (
233
- <svg
234
- class="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))]"
235
- fill="none"
236
- stroke="currentColor"
237
- viewBox="0 0 24 24"
238
- >
239
- <path
240
- stroke-linecap="round"
241
- stroke-linejoin="round"
242
- stroke-width="2"
243
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
244
- />
245
- </svg>
246
- )}
247
- </span>
248
- </div>
249
- <p class="text-sm text-[rgb(var(--ec-page-text-muted))] leading-relaxed">{action.description}</p>
250
- </a>
251
- ))
252
- }
253
- </div>
254
- </section>
197
+ <!-- Eyebrow + Explore grid -->
198
+ <section class="mt-12">
199
+ <div class="ec-section-eyebrow">
200
+ <span class="text-[11px] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-accent))]">Explore</span>
201
+ <span class="ec-section-rule"></span>
202
+ <span class="font-mono text-[11px] text-[rgb(var(--ec-page-text-muted)/0.7)]">{exploreCards.length} sections</span>
203
+ </div>
255
204
 
256
- <!-- Resources & Community - Compact Footer Style -->
257
- <section class="border-t border-[rgb(var(--ec-page-border))] pt-8 pb-4">
258
- <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-6">
259
- <div class="flex flex-wrap items-center gap-x-6 gap-y-3">
260
- <a
261
- href="https://www.eventcatalog.dev/docs/development/getting-started/introduction"
262
- target="_blank"
263
- class="text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
264
- >
265
- Getting Started
266
- </a>
267
- <a
268
- href="https://www.eventcatalog.dev/integrations"
269
- target="_blank"
270
- class="text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
271
- >
272
- Integrations
273
- </a>
274
- <a
275
- href="https://www.eventcatalog.dev/docs"
276
- target="_blank"
277
- class="text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
278
- >
279
- Documentation
280
- </a>
281
- </div>
282
- <div class="flex items-center gap-4">
283
- <a
284
- href="https://discord.gg/3rjaZMmrAm"
285
- target="_blank"
286
- class="flex items-center gap-2 text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
287
- title="Join Discord"
288
- >
289
- <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
290
- <path
291
- d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"
292
- ></path>
293
- </svg>
294
- <span class="hidden sm:inline">Discord</span>
295
- </a>
205
+ <div class="ec-explore-grid mt-4">
206
+ {
207
+ exploreCards.map((card) => (
296
208
  <a
297
- href="https://github.com/event-catalog/eventcatalog"
298
- target="_blank"
299
- class="flex items-center gap-2 text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
300
- title="GitHub"
209
+ href={card.href}
210
+ target={card.external ? '_blank' : undefined}
211
+ rel={card.external ? 'noreferrer' : undefined}
212
+ class:list={['ec-card group', card.feature ? 'ec-card--feature' : '']}
213
+ style={`--tone: ${card.tone};`}
301
214
  >
302
- <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
303
- <path
304
- fill-rule="evenodd"
305
- d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
306
- clip-rule="evenodd"></path>
307
- </svg>
308
- <span class="hidden sm:inline">GitHub</span>
215
+ <div class="flex items-start justify-between gap-3">
216
+ <div class="ec-card-icon">
217
+ <card.icon className="h-4 w-4" />
218
+ </div>
219
+ <ArrowUpRight className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))] transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-[rgb(var(--ec-accent))]" />
220
+ </div>
221
+ <h3 class="mt-4 text-[15px] font-semibold tracking-tight text-[rgb(var(--ec-page-text))]">{card.title}</h3>
222
+ <p class="mt-1.5 text-[13px] leading-relaxed text-[rgb(var(--ec-page-text-muted))]">{card.description}</p>
309
223
  </a>
310
- </div>
311
- </div>
312
- </section>
313
- </main>
224
+ ))
225
+ }
226
+ </div>
227
+ </section>
314
228
  </div>
315
229
  </VerticalSideBarLayout>
316
230
 
317
231
  <style is:global>
318
- .ec-stat-card {
232
+ .ec-home {
233
+ /* Negate the layout wrapper's horizontal padding so the homepage controls its own. */
234
+ margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1);
235
+ margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1);
236
+ padding: 2rem 2rem 0;
237
+ }
238
+ .ec-hero {
239
+ padding-bottom: 1.5rem;
240
+ }
241
+ .ec-divider {
242
+ height: 1px;
243
+ background: linear-gradient(
244
+ to right,
245
+ transparent,
246
+ rgb(var(--ec-page-border)) 12%,
247
+ rgb(var(--ec-page-border)) 88%,
248
+ transparent
249
+ );
250
+ margin: 0.25rem 0 1.5rem;
251
+ }
252
+ .ec-stats {
253
+ display: grid;
254
+ grid-template-columns: repeat(2, minmax(0, 1fr));
255
+ gap: 0.75rem;
256
+ }
257
+ @media (min-width: 768px) {
258
+ .ec-stats {
259
+ grid-template-columns: repeat(4, minmax(0, 1fr));
260
+ }
261
+ }
262
+ .ec-stat {
319
263
  position: relative;
264
+ display: flex;
265
+ flex-direction: column;
266
+ overflow: hidden;
267
+ border: 1px solid rgb(var(--ec-page-border));
268
+ border-radius: 0.75rem;
269
+ background:
270
+ radial-gradient(80% 100% at 0% 0%, rgb(var(--tone) / 0.05), transparent 60%), rgb(var(--ec-card-bg, var(--ec-page-bg)));
271
+ padding: 1rem 1.1rem 1.1rem;
272
+ transition:
273
+ border-color 200ms,
274
+ transform 200ms,
275
+ box-shadow 200ms;
276
+ text-decoration: none;
320
277
  }
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;
278
+ .ec-stat:hover {
279
+ border-color: rgb(var(--ec-accent) / 0.5);
280
+ transform: translateY(-1px);
330
281
  }
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%);
282
+
283
+ .ec-section-eyebrow {
284
+ display: flex;
285
+ align-items: center;
286
+ gap: 0.75rem;
334
287
  }
335
- :root[data-theme='dark'] .ec-stat-card:hover::before {
336
- opacity: 0.15;
288
+ .ec-section-rule {
289
+ flex: 1;
290
+ height: 1px;
291
+ background: linear-gradient(to right, rgb(var(--ec-accent) / 0.4), rgb(var(--ec-page-border)) 30%);
292
+ }
293
+
294
+ /* Explore grid: bordered cells with breathing room between them. */
295
+ .ec-explore-grid {
296
+ display: grid;
297
+ grid-template-columns: repeat(1, minmax(0, 1fr));
298
+ gap: 1rem;
337
299
  }
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));
300
+ @media (min-width: 640px) {
301
+ .ec-explore-grid {
302
+ grid-template-columns: repeat(2, minmax(0, 1fr));
303
+ }
342
304
  }
343
- :root:not([data-theme='dark']) .ec-stat-card:hover::before,
344
- :root[data-theme='light'] .ec-stat-card:hover::before {
345
- opacity: 1;
305
+ @media (min-width: 1024px) {
306
+ .ec-explore-grid {
307
+ grid-template-columns: repeat(3, minmax(0, 1fr));
308
+ }
346
309
  }
347
- .ec-stat-card > * {
310
+
311
+ .ec-card {
348
312
  position: relative;
349
- z-index: 1;
313
+ display: flex;
314
+ flex-direction: column;
315
+ padding: 1.5rem 1.5rem 1.4rem;
316
+ text-decoration: none;
317
+ border: 1px solid rgb(var(--ec-page-border));
318
+ border-radius: 0.875rem;
319
+ background: rgb(var(--ec-card-bg, var(--ec-page-bg)));
320
+ transition:
321
+ background 200ms,
322
+ border-color 200ms;
323
+ min-height: 9.5rem;
324
+ }
325
+ .ec-card:hover {
326
+ background: rgb(var(--ec-page-bg) / 0.4);
327
+ border-color: rgb(var(--ec-accent) / 0.5);
328
+ }
329
+
330
+ .ec-card--feature {
331
+ background:
332
+ radial-gradient(60% 90% at 0% 0%, rgb(var(--tone) / 0.07), transparent 60%), rgb(var(--ec-card-bg, var(--ec-page-bg)));
333
+ }
334
+ .ec-card--feature:hover {
335
+ background: radial-gradient(60% 90% at 0% 0%, rgb(var(--ec-accent) / 0.12), transparent 60%), rgb(var(--ec-page-bg) / 0.4);
350
336
  }
351
- </style>
352
337
 
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>
338
+ .ec-card-icon {
339
+ display: inline-flex;
340
+ height: 2.25rem;
341
+ width: 2.25rem;
342
+ align-items: center;
343
+ justify-content: center;
344
+ border-radius: 0.5rem;
345
+ background: rgb(var(--tone) / 0.12);
346
+ color: rgb(var(--tone));
347
+ transition:
348
+ background 200ms,
349
+ color 200ms;
350
+ }
351
+ .ec-card:hover .ec-card-icon {
352
+ background: rgb(var(--ec-accent) / 0.2);
353
+ color: rgb(var(--ec-accent));
354
+ }
355
+ .ec-card--feature .ec-card-icon {
356
+ height: 2.5rem;
357
+ width: 2.5rem;
358
+ border-radius: 0.625rem;
359
+ }
360
+ </style>