@eventcatalog/core 3.30.0 → 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 (69) 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-6UG4JMUV.js → chunk-7IGMIOQF.js} +1 -1
  6. package/dist/{chunk-Z26P4PCB.js → chunk-HVOLSUC2.js} +1 -1
  7. package/dist/{chunk-RRBDF4MM.js → chunk-LWVHWR77.js} +1 -1
  8. package/dist/{chunk-MVZKHUX2.js → chunk-QIJOBQZ7.js} +1 -1
  9. package/dist/{chunk-ATRBVTJ6.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 +10 -6
  19. package/eventcatalog/public/logo.png +0 -0
  20. package/eventcatalog/src/components/CopyAsMarkdown.tsx +29 -24
  21. package/eventcatalog/src/components/MDX/Design/Design.astro +1 -1
  22. package/eventcatalog/src/components/MDX/Tiles/Tile.astro +11 -8
  23. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +218 -0
  24. package/eventcatalog/src/components/Settings/BillingSettingsForm.tsx +265 -0
  25. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +371 -0
  26. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +183 -0
  27. package/eventcatalog/src/components/Settings/LogoUpload.tsx +137 -0
  28. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +91 -0
  29. package/eventcatalog/src/components/Settings/ReadOnlyBanner.tsx +18 -0
  30. package/eventcatalog/src/components/Settings/Row.tsx +59 -0
  31. package/eventcatalog/src/components/Settings/SettingsShared.tsx +176 -0
  32. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +17 -18
  33. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +45 -16
  34. package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +2 -2
  35. package/eventcatalog/src/content.config.ts +1 -1
  36. package/eventcatalog/src/enterprise/auth/middleware/middleware-auth.ts +11 -7
  37. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +4 -4
  38. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +70 -57
  39. package/eventcatalog/src/enterprise/feature.ts +2 -1
  40. package/eventcatalog/src/layouts/SettingsLayout.astro +116 -0
  41. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +62 -23
  42. package/eventcatalog/src/pages/_index.astro +250 -255
  43. package/eventcatalog/src/pages/api/settings/ai.ts +57 -0
  44. package/eventcatalog/src/pages/api/settings/general.ts +71 -0
  45. package/eventcatalog/src/pages/api/settings/logo.ts +113 -0
  46. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -1
  47. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +26 -32
  48. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +1 -1
  49. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +40 -31
  50. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -1
  51. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +2 -26
  52. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +5 -1
  53. package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -1
  54. package/eventcatalog/src/pages/settings/assistant.astro +37 -0
  55. package/eventcatalog/src/pages/settings/billing.astro +17 -0
  56. package/eventcatalog/src/pages/settings/general.astro +32 -0
  57. package/eventcatalog/src/pages/settings/index.astro +21 -0
  58. package/eventcatalog/src/pages/settings/llm-access.astro +34 -0
  59. package/eventcatalog/src/pages/settings/mcp.astro +14 -0
  60. package/eventcatalog/src/styles/theme.css +38 -29
  61. package/eventcatalog/src/styles/themes/forest.css +17 -9
  62. package/eventcatalog/src/styles/themes/ocean.css +10 -2
  63. package/eventcatalog/src/styles/themes/sapphire.css +10 -2
  64. package/eventcatalog/src/styles/themes/sunset.css +25 -17
  65. package/eventcatalog/src/utils/eventcatalog-config/config-schema.ts +49 -0
  66. package/eventcatalog/src/utils/eventcatalog-config/config-writer.ts +149 -0
  67. package/eventcatalog/src/utils/url-builder.ts +4 -2
  68. package/package.json +7 -5
  69. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +0 -81
@@ -0,0 +1,34 @@
1
+ ---
2
+ import SettingsLayout from '@layouts/SettingsLayout.astro';
3
+ import { LlmAccessSettingsForm } from '@components/Settings/LlmAccessSettingsForm';
4
+ import { isDevMode, isEventCatalogScaleEnabled } from '@utils/feature';
5
+ import { buildUrl } from '@utils/url-builder';
6
+ import config from '@config';
7
+
8
+ const canEdit = isDevMode();
9
+
10
+ const initial = {
11
+ llmsTxtEnabled: config?.llmsTxt?.enabled ?? true,
12
+ chatEnabled: config?.chat?.enabled ?? true,
13
+ };
14
+
15
+ const hasScalePlan = isEventCatalogScaleEnabled();
16
+
17
+ const apiBase = buildUrl('/api/settings');
18
+ const llmsTxtUrl = buildUrl('/docs/llm/llms.txt');
19
+ const llmsFullTxtUrl = buildUrl('/docs/llm/llms-full.txt');
20
+ const schemasTxtUrl = buildUrl('/docs/llm/schemas.txt');
21
+ ---
22
+
23
+ <SettingsLayout title="LLM Access" active="llm-access">
24
+ <LlmAccessSettingsForm
25
+ client:load
26
+ canEdit={canEdit}
27
+ initial={initial}
28
+ hasScalePlan={hasScalePlan}
29
+ apiBase={apiBase}
30
+ llmsTxtUrl={llmsTxtUrl}
31
+ llmsFullTxtUrl={llmsFullTxtUrl}
32
+ schemasTxtUrl={schemasTxtUrl}
33
+ />
34
+ </SettingsLayout>
@@ -0,0 +1,14 @@
1
+ ---
2
+ import SettingsLayout from '@layouts/SettingsLayout.astro';
3
+ import { McpSettingsForm } from '@components/Settings/McpSettingsForm';
4
+ import { isSSR, isEventCatalogScaleEnabled } from '@utils/feature';
5
+ import { buildUrl } from '@utils/url-builder';
6
+
7
+ const hasScalePlan = isEventCatalogScaleEnabled();
8
+ const inSSR = isSSR();
9
+ const mcpUrl = buildUrl('/docs/mcp');
10
+ ---
11
+
12
+ <SettingsLayout title="MCP" active="mcp">
13
+ <McpSettingsForm client:load hasScalePlan={hasScalePlan} inSSR={inSSR} mcpUrl={mcpUrl} />
14
+ </SettingsLayout>
@@ -17,19 +17,19 @@
17
17
  /* Header */
18
18
  --ec-header-bg: 255 255 255; /* #ffffff */
19
19
  --ec-header-text: 15 23 42; /* #0f172a (slate-900) */
20
- --ec-header-border: 226 232 240; /* #e2e8f0 (slate-200) */
20
+ --ec-header-border: 229 229 229; /* #e5e5e5 (neutral-200) */
21
21
 
22
22
  /* Primary brand color */
23
- --ec-primary: 168 85 247; /* #a855f7 */
24
- --ec-primary-hover: 147 51 234; /* #9333ea */
23
+ --ec-primary: 126 34 206; /* #7e22ce (purple-700) */
24
+ --ec-primary-hover: 107 33 168; /* #6b21a8 (purple-800) */
25
25
 
26
26
  /* Accent colors - used for selections, highlights, interactive elements */
27
- --ec-accent: 168 85 247; /* #a855f7 (purple-500) */
28
- --ec-accent-hover: 147 51 234; /* #9333ea (purple-600) */
29
- --ec-accent-subtle: 243 232 255; /* #f3e8ff (purple-100) */
30
- --ec-accent-text: 107 33 168; /* #6b21a8 (purple-800) */
31
- --ec-accent-gradient-from: 168 85 247; /* #a855f7 (purple-500) */
32
- --ec-accent-gradient-to: 126 34 206; /* #7e22ce (purple-700) */
27
+ --ec-accent: 126 34 206; /* #7e22ce (purple-700) */
28
+ --ec-accent-hover: 107 33 168; /* #6b21a8 (purple-800) */
29
+ --ec-accent-subtle: 233 213 255; /* #e9d5ff (purple-200) */
30
+ --ec-accent-text: 88 28 135; /* #581c87 (purple-900) */
31
+ --ec-accent-gradient-from: 126 34 206; /* #7e22ce (purple-700) */
32
+ --ec-accent-gradient-to: 88 28 135; /* #581c87 (purple-900) */
33
33
 
34
34
  /* Buttons */
35
35
  --ec-button-bg: 15 23 42; /* #0f172a */
@@ -49,18 +49,23 @@
49
49
  /* Sidebar / Vertical Nav (icon bar) */
50
50
  --ec-sidebar-bg: 255 255 255; /* #ffffff */
51
51
  --ec-sidebar-bg-gradient: 248 250 252; /* #f8fafc (slate-50) */
52
- --ec-sidebar-border: 226 232 240; /* #e2e8f0 (slate-200) */
52
+ --ec-sidebar-border: 229 229 229; /* #e5e5e5 (neutral-200) */
53
53
  --ec-sidebar-text: 71 85 105; /* #475569 (slate-600) */
54
54
  --ec-sidebar-active-bg: 15 23 42; /* #0f172a */
55
55
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
56
56
  --ec-sidebar-hover-bg: 15 23 42; /* #0f172a - black on hover like active */
57
57
 
58
+ /* Rail / inset-panel surface (5% off the sidebar bg). Use for navigation rails,
59
+ filter panels, and any inset surface that should sit slightly off the page. */
60
+ --ec-rail-bg: 250 250 250; /* #fafafa - 2% darker than white */
61
+ --ec-rail-active-bg: 218 218 218; /* ~10% darker than rail bg */
62
+
58
63
  /* Nested sidebar content */
59
64
  --ec-content-bg: 255 255 255; /* #ffffff */
60
65
  --ec-content-text: 15 23 42; /* #0f172a (slate-900) */
61
66
  --ec-content-text-muted: 100 116 139; /* #64748b (slate-500) */
62
67
  --ec-content-text-secondary: 71 85 105; /* #475569 (slate-600) */
63
- --ec-content-border: 226 232 240; /* #e2e8f0 (slate-200) */
68
+ --ec-content-border: 229 229 229; /* #e5e5e5 (neutral-200) */
64
69
  --ec-content-hover: 248 250 252; /* #f8fafc (slate-50) */
65
70
  --ec-content-active: 245 243 255; /* #f5f3ff (violet-50) */
66
71
 
@@ -71,14 +76,14 @@
71
76
  --ec-input-placeholder: 148 163 184; /* #94a3b8 (slate-400) */
72
77
 
73
78
  /* Group headers */
74
- --ec-group-icon-bg: 241 245 249; /* #f1f5f9 (slate-100) */
75
- --ec-group-icon-text: 71 85 105; /* #475569 (slate-600) */
79
+ --ec-group-icon-bg: 233 213 255; /* #e9d5ff (purple-200) */
80
+ --ec-group-icon-text: 88 28 135; /* #581c87 (purple-900) */
76
81
 
77
82
  /* Main content/docs area */
78
83
  --ec-page-bg: 255 255 255; /* #ffffff */
79
84
  --ec-page-text: 15 23 42; /* #0f172a */
80
85
  --ec-page-text-muted: 100 116 139; /* #64748b */
81
- --ec-page-border: 226 232 240; /* #e2e8f0 */
86
+ --ec-page-border: 229 229 229; /* #e5e5e5 (neutral-200) */
82
87
 
83
88
  /* Card/elevated surfaces */
84
89
  --ec-card-bg: 255 255 255; /* #ffffff */
@@ -119,19 +124,19 @@
119
124
  /* Header - deep black with cool border */
120
125
  --ec-header-bg: 9 13 13; /* #090d0d */
121
126
  --ec-header-text: 240 246 252; /* #f0f6fc - soft white */
122
- --ec-header-border: 24 27 32; /* #181b20 - subtle border */
127
+ --ec-header-border: 38 41 47; /* #26292f - lifted ~20% */
123
128
 
124
129
  /* Primary brand color */
125
- --ec-primary: 168 85 247; /* #a855f7 (purple-500) */
126
- --ec-primary-hover: 147 51 234; /* #9333ea (purple-600) */
130
+ --ec-primary: 147 51 234; /* #9333ea (purple-600) */
131
+ --ec-primary-hover: 126 34 206; /* #7e22ce (purple-700) */
127
132
 
128
133
  /* Accent colors - used for selections, highlights, interactive elements */
129
- --ec-accent: 168 85 247; /* #a855f7 (purple-500) */
130
- --ec-accent-hover: 147 51 234; /* #9333ea (purple-600) */
134
+ --ec-accent: 147 51 234; /* #9333ea (purple-600) */
135
+ --ec-accent-hover: 126 34 206; /* #7e22ce (purple-700) */
131
136
  --ec-accent-subtle: 88 28 135 / 0.3; /* purple-900/30 */
132
- --ec-accent-text: 216 180 254; /* #d8b4fe (purple-300) */
133
- --ec-accent-gradient-from: 168 85 247; /* #a855f7 (purple-500) */
134
- --ec-accent-gradient-to: 147 51 234; /* #9333ea (purple-600) */
137
+ --ec-accent-text: 192 132 252; /* #c084fc (purple-400) */
138
+ --ec-accent-gradient-from: 147 51 234; /* #9333ea (purple-600) */
139
+ --ec-accent-gradient-to: 126 34 206; /* #7e22ce (purple-700) */
135
140
 
136
141
  /* Buttons - accent color for better visibility */
137
142
  --ec-button-bg: 147 51 234; /* #9333ea - purple-600 */
@@ -149,20 +154,24 @@
149
154
  --ec-icon-hover: 240 246 252; /* #f0f6fc */
150
155
 
151
156
  /* Sidebar / Vertical Nav (icon bar) */
152
- --ec-sidebar-bg: 21 21 26; /* #15151a */
153
- --ec-sidebar-bg-gradient: 21 21 26; /* #15151a */
154
- --ec-sidebar-border: 33 38 45; /* #21262d */
157
+ --ec-sidebar-bg: 9 13 13; /* #090d0d - matches page bg */
158
+ --ec-sidebar-bg-gradient: 9 13 13; /* #090d0d */
159
+ --ec-sidebar-border: 51 57 65; /* #333941 - lifted ~20% */
155
160
  --ec-sidebar-text: 139 148 158; /* #8b949e */
156
161
  --ec-sidebar-active-bg: 48 54 61; /* #30363d - subtle highlight */
157
162
  --ec-sidebar-active-text: 240 246 252; /* #f0f6fc */
158
163
  --ec-sidebar-hover-bg: 33 38 45; /* #21262d */
159
164
 
165
+ /* Rail / inset-panel surface (~2% lifted from the page bg) */
166
+ --ec-rail-bg: 15 19 19; /* ~2% lighter than #090d0d */
167
+ --ec-rail-active-bg: 35 38 38; /* ~10% lifted off rail bg */
168
+
160
169
  /* Nested sidebar content */
161
- --ec-content-bg: 21 21 26; /* #15151a - lifted surface for nested sidebar */
170
+ --ec-content-bg: 9 13 13; /* #090d0d - matches page bg */
162
171
  --ec-content-text: 201 209 217; /* #c9d1d9 - softer than pure white */
163
172
  --ec-content-text-muted: 139 148 158; /* #8b949e */
164
173
  --ec-content-text-secondary: 170 178 186; /* #aab2ba - sidebar children text */
165
- --ec-content-border: 33 38 45; /* #21262d */
174
+ --ec-content-border: 51 57 65; /* #333941 - lifted ~20% */
166
175
  --ec-content-hover: 22 27 34; /* #161b22 */
167
176
  --ec-content-active: 33 38 45; /* #21262d */
168
177
 
@@ -174,13 +183,13 @@
174
183
 
175
184
  /* Group headers */
176
185
  --ec-group-icon-bg: 33 38 45; /* #21262d */
177
- --ec-group-icon-text: 201 209 217; /* #c9d1d9 */
186
+ --ec-group-icon-text: 192 132 252; /* #c084fc (purple-400) */
178
187
 
179
188
  /* Main content/docs area - base dark surface */
180
189
  --ec-page-bg: 9 13 13; /* #090d0d */
181
190
  --ec-page-text: 240 246 252; /* #f0f6fc */
182
191
  --ec-page-text-muted: 163 172 182; /* #a3acb6 */
183
- --ec-page-border: 24 27 32; /* #181b20 - more subtle */
192
+ --ec-page-border: 38 41 47; /* #26292f - lifted ~20% */
184
193
 
185
194
  /* Card/elevated surfaces - for depth */
186
195
  --ec-card-bg: 22 27 34; /* #161b22 */
@@ -11,7 +11,7 @@
11
11
  /* Header - clean white */
12
12
  --ec-header-bg: 255 255 255; /* #ffffff */
13
13
  --ec-header-text: 20 20 20; /* #141414 */
14
- --ec-header-border: 229 231 235; /* #e5e7eb gray-200 */
14
+ --ec-header-border: 229 229 229; /* #e5e5e5 (neutral-200) */
15
15
 
16
16
  /* Primary brand color - deep forest green */
17
17
  --ec-primary: 22 101 52; /* #166534 green-800 */
@@ -34,7 +34,7 @@
34
34
  --ec-dropdown-bg: 255 255 255; /* #ffffff */
35
35
  --ec-dropdown-text: 55 65 81; /* #374151 gray-700 */
36
36
  --ec-dropdown-hover: 240 253 244; /* #f0fdf4 green-50 */
37
- --ec-dropdown-border: 229 231 235; /* #e5e7eb gray-200 */
37
+ --ec-dropdown-border: 229 229 229; /* #e5e5e5 (neutral-200) */
38
38
 
39
39
  /* Icons */
40
40
  --ec-icon-color: 75 85 99; /* #4b5563 gray-600 */
@@ -43,18 +43,22 @@
43
43
  /* Sidebar / Vertical Nav */
44
44
  --ec-sidebar-bg: 255 255 255; /* #ffffff */
45
45
  --ec-sidebar-bg-gradient: 240 253 244; /* #f0fdf4 green-50 */
46
- --ec-sidebar-border: 229 231 235; /* #e5e7eb gray-200 */
46
+ --ec-sidebar-border: 229 229 229; /* #e5e5e5 (neutral-200) */
47
47
  --ec-sidebar-text: 75 85 99; /* #4b5563 gray-600 */
48
48
  --ec-sidebar-active-bg: 22 101 52; /* #166534 green-800 */
49
49
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
50
50
  --ec-sidebar-hover-bg: 22 101 52; /* #166534 green-800 */
51
51
 
52
+ /* Rail / inset-panel surface (5% off the sidebar bg) */
53
+ --ec-rail-bg: 250 250 250; /* #fafafa - 2% darker than white */
54
+ --ec-rail-active-bg: 218 218 218; /* ~10% darker than rail bg */
55
+
52
56
  /* Nested sidebar content */
53
57
  --ec-content-bg: 255 255 255; /* #ffffff */
54
58
  --ec-content-text: 17 24 39; /* #111827 gray-900 */
55
59
  --ec-content-text-muted: 75 85 99; /* #4b5563 gray-600 */
56
60
  --ec-content-text-secondary: 107 114 128; /* #6b7280 gray-500 */
57
- --ec-content-border: 229 231 235; /* #e5e7eb gray-200 */
61
+ --ec-content-border: 229 229 229; /* #e5e5e5 (neutral-200) */
58
62
  --ec-content-hover: 249 250 251; /* #f9fafb gray-50 */
59
63
  --ec-content-active: 220 252 231; /* #dcfce7 green-100 */
60
64
 
@@ -72,7 +76,7 @@
72
76
  --ec-page-bg: 255 255 255; /* #ffffff */
73
77
  --ec-page-text: 17 24 39; /* #111827 gray-900 */
74
78
  --ec-page-text-muted: 75 85 99; /* #4b5563 gray-600 */
75
- --ec-page-border: 229 231 235; /* #e5e7eb gray-200 */
79
+ --ec-page-border: 229 229 229; /* #e5e5e5 (neutral-200) */
76
80
 
77
81
  /* Card/elevated surfaces */
78
82
  --ec-card-bg: 255 255 255; /* #ffffff */
@@ -108,7 +112,7 @@
108
112
  /* Header - deep forest dark */
109
113
  --ec-header-bg: 3 7 18; /* #030712 gray-950 */
110
114
  --ec-header-text: 243 244 246; /* #f3f4f6 gray-100 */
111
- --ec-header-border: 17 24 39; /* #111827 gray-900 */
115
+ --ec-header-border: 31 41 55; /* #1f2937 gray-800 */
112
116
 
113
117
  /* Accent colors - muted forest green for dark */
114
118
  --ec-accent: 74 222 128; /* #4ade80 (green-400) */
@@ -136,18 +140,22 @@
136
140
  /* Sidebar / Vertical Nav */
137
141
  --ec-sidebar-bg: 3 7 18; /* #030712 gray-950 */
138
142
  --ec-sidebar-bg-gradient: 3 7 18; /* #030712 */
139
- --ec-sidebar-border: 17 24 39; /* #111827 gray-900 */
143
+ --ec-sidebar-border: 31 41 55; /* #1f2937 gray-800 */
140
144
  --ec-sidebar-text: 156 163 175; /* #9ca3af */
141
145
  --ec-sidebar-active-bg: 22 101 52; /* #166534 green-800 */
142
146
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
143
147
  --ec-sidebar-hover-bg: 31 41 55; /* #1f2937 */
144
148
 
149
+ /* Rail / inset-panel surface (5% lifted from the sidebar bg) */
150
+ --ec-rail-bg: 8 12 23; /* ~2% lighter than #030712 */
151
+ --ec-rail-active-bg: 34 38 49; /* ~10% lifted off rail bg */
152
+
145
153
  /* Nested sidebar content */
146
154
  --ec-content-bg: 3 7 18; /* #030712 */
147
155
  --ec-content-text: 243 244 246; /* #f3f4f6 */
148
156
  --ec-content-text-muted: 156 163 175; /* #9ca3af */
149
157
  --ec-content-text-secondary: 156 163 175; /* #9ca3af */
150
- --ec-content-border: 17 24 39; /* #111827 gray-900 */
158
+ --ec-content-border: 31 41 55; /* #1f2937 gray-800 */
151
159
  --ec-content-hover: 17 24 39; /* #111827 */
152
160
  --ec-content-active: 31 41 55; /* #1f2937 */
153
161
 
@@ -165,7 +173,7 @@
165
173
  --ec-page-bg: 3 7 18; /* #030712 */
166
174
  --ec-page-text: 243 244 246; /* #f3f4f6 */
167
175
  --ec-page-text-muted: 156 163 175; /* #9ca3af */
168
- --ec-page-border: 17 24 39; /* #111827 gray-900 */
176
+ --ec-page-border: 31 41 55; /* #1f2937 gray-800 */
169
177
 
170
178
  /* Card/elevated surfaces */
171
179
  --ec-card-bg: 3 7 18; /* #030712 */
@@ -49,6 +49,10 @@
49
49
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
50
50
  --ec-sidebar-hover-bg: 13 148 136; /* #0d9488 teal-600 - same as active for consistency */
51
51
 
52
+ /* Rail / inset-panel surface (5% off the sidebar bg) */
53
+ --ec-rail-bg: 250 250 250; /* #fafafa - 2% darker than white */
54
+ --ec-rail-active-bg: 218 218 218; /* ~10% darker than rail bg */
55
+
52
56
  /* Nested sidebar content */
53
57
  --ec-content-bg: 255 255 255; /* #ffffff */
54
58
  --ec-content-text: 15 23 42; /* #0f172a */
@@ -110,8 +114,8 @@
110
114
  * Creates a seamless, immersive dark experience
111
115
  */
112
116
 
113
- /* Header - slightly elevated from page for visual hierarchy */
114
- --ec-header-bg: 15 23 42; /* #0f172a slate-900 - subtle lift */
117
+ /* Header - matches page bg for unified look */
118
+ --ec-header-bg: 2 6 23; /* #020617 slate-950 - matches page bg */
115
119
  --ec-header-text: 226 232 240; /* #e2e8f0 */
116
120
  --ec-header-border: 30 41 59; /* #1e293b */
117
121
 
@@ -147,6 +151,10 @@
147
151
  --ec-sidebar-active-text: 2 6 23; /* #020617 */
148
152
  --ec-sidebar-hover-bg: 30 41 59; /* #1e293b */
149
153
 
154
+ /* Rail / inset-panel surface (5% lifted from the sidebar bg) */
155
+ --ec-rail-bg: 7 11 28; /* ~2% lighter than #020617 */
156
+ --ec-rail-active-bg: 33 37 54; /* ~10% lifted off rail bg */
157
+
150
158
  /* Nested sidebar content - same as page bg */
151
159
  --ec-content-bg: 2 6 23; /* #020617 */
152
160
  --ec-content-text: 226 232 240; /* #e2e8f0 */
@@ -49,6 +49,10 @@
49
49
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
50
50
  --ec-sidebar-hover-bg: 37 99 235; /* #2563eb blue-600 */
51
51
 
52
+ /* Rail / inset-panel surface (5% off the sidebar bg) */
53
+ --ec-rail-bg: 250 250 250; /* #fafafa - 2% darker than white */
54
+ --ec-rail-active-bg: 218 218 218; /* ~10% darker than rail bg */
55
+
52
56
  /* Nested sidebar content */
53
57
  --ec-content-bg: 255 255 255; /* #ffffff */
54
58
  --ec-content-text: 15 23 42; /* #0f172a */
@@ -105,8 +109,8 @@
105
109
 
106
110
  /* Sapphire Dark Mode */
107
111
  :root[data-catalog-theme="sapphire"][data-theme="dark"] {
108
- /* Header - slightly elevated for visual hierarchy */
109
- --ec-header-bg: 15 23 42; /* #0f172a slate-900 */
112
+ /* Header - matches page bg for unified look */
113
+ --ec-header-bg: 2 6 23; /* #020617 slate-950 - matches page bg */
110
114
  --ec-header-text: 226 232 240; /* #e2e8f0 */
111
115
  --ec-header-border: 30 41 59; /* #1e293b slate-800 */
112
116
 
@@ -142,6 +146,10 @@
142
146
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
143
147
  --ec-sidebar-hover-bg: 30 41 59; /* #1e293b */
144
148
 
149
+ /* Rail / inset-panel surface (5% lifted from the sidebar bg) */
150
+ --ec-rail-bg: 7 11 28; /* ~2% lighter than #020617 */
151
+ --ec-rail-active-bg: 33 37 54; /* ~10% lifted off rail bg */
152
+
145
153
  /* Nested sidebar content */
146
154
  --ec-content-bg: 2 6 23; /* #020617 */
147
155
  --ec-content-text: 226 232 240; /* #e2e8f0 */
@@ -14,16 +14,16 @@
14
14
  --ec-header-border: 229 229 229; /* #e5e5e5 neutral-200 */
15
15
 
16
16
  /* Primary brand color - sunset orange */
17
- --ec-primary: 249 115 22; /* #f97316 orange-500 */
18
- --ec-primary-hover: 234 88 12; /* #ea580c orange-600 */
17
+ --ec-primary: 255 106 0; /* #ff6a00 vivid orange */
18
+ --ec-primary-hover: 232 93 0; /* #e85d00 deeper vivid orange */
19
19
 
20
20
  /* Accent colors - sunset orange */
21
- --ec-accent: 249 115 22; /* #f97316 (orange-500) */
22
- --ec-accent-hover: 234 88 12; /* #ea580c (orange-600) */
23
- --ec-accent-subtle: 255 237 213; /* #ffedd5 (orange-100) */
24
- --ec-accent-text: 154 52 18; /* #9a3412 (orange-800) */
25
- --ec-accent-gradient-from: 249 115 22; /* #f97316 (orange-500) */
26
- --ec-accent-gradient-to: 234 88 12; /* #ea580c (orange-600) */
21
+ --ec-accent: 255 106 0; /* #ff6a00 vivid orange */
22
+ --ec-accent-hover: 232 93 0; /* #e85d00 deeper vivid orange */
23
+ --ec-accent-subtle: 255 232 209; /* #ffe8d1 (subtle vivid orange) */
24
+ --ec-accent-text: 122 46 16; /* #7a2e10 burnt orange */
25
+ --ec-accent-gradient-from: 255 106 0; /* #ff6a00 */
26
+ --ec-accent-gradient-to: 232 93 0; /* #e85d00 */
27
27
 
28
28
  /* Buttons */
29
29
  --ec-button-bg: 234 88 12; /* #ea580c orange-600 */
@@ -49,6 +49,10 @@
49
49
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
50
50
  --ec-sidebar-hover-bg: 234 88 12; /* #ea580c orange-600 */
51
51
 
52
+ /* Rail / inset-panel surface (5% off the sidebar bg) */
53
+ --ec-rail-bg: 250 250 250; /* #fafafa - 2% darker than white */
54
+ --ec-rail-active-bg: 218 218 218; /* ~10% darker than rail bg */
55
+
52
56
  /* Nested sidebar content */
53
57
  --ec-content-bg: 255 255 255; /* #ffffff */
54
58
  --ec-content-text: 23 23 23; /* #171717 neutral-900 */
@@ -105,18 +109,18 @@
105
109
 
106
110
  /* Sunset Dark Mode */
107
111
  :root[data-catalog-theme="sunset"][data-theme="dark"] {
108
- /* Header - warm dark */
109
- --ec-header-bg: 23 23 23; /* #171717 neutral-900 */
112
+ /* Header - matches page bg for unified look */
113
+ --ec-header-bg: 10 10 10; /* #0a0a0a neutral-950 - matches page bg */
110
114
  --ec-header-text: 245 245 245; /* #f5f5f5 neutral-100 */
111
115
  --ec-header-border: 38 38 38; /* #262626 neutral-800 */
112
116
 
113
117
  /* Accent colors - sunset orange for dark */
114
- --ec-accent: 251 146 60; /* #fb923c (orange-400) */
115
- --ec-accent-hover: 249 115 22; /* #f97316 (orange-500) */
116
- --ec-accent-subtle: 154 52 18 / 0.3; /* orange-800/30 */
117
- --ec-accent-text: 253 186 116; /* #fdba74 (orange-300) */
118
- --ec-accent-gradient-from: 251 146 60; /* #fb923c (orange-400) */
119
- --ec-accent-gradient-to: 249 115 22; /* #f97316 (orange-500) */
118
+ --ec-accent: 255 138 61; /* #ff8a3d punchier vivid orange */
119
+ --ec-accent-hover: 255 106 0; /* #ff6a00 vivid orange */
120
+ --ec-accent-subtle: 122 46 16 / 0.3; /* burnt orange/30 */
121
+ --ec-accent-text: 255 119 51; /* #ff7733 deep vivid orange */
122
+ --ec-accent-gradient-from: 255 138 61; /* #ff8a3d */
123
+ --ec-accent-gradient-to: 255 106 0; /* #ff6a00 */
120
124
 
121
125
  /* Buttons */
122
126
  --ec-button-bg: 249 115 22; /* #f97316 orange-500 */
@@ -142,6 +146,10 @@
142
146
  --ec-sidebar-active-text: 255 255 255; /* #ffffff */
143
147
  --ec-sidebar-hover-bg: 38 38 38; /* #262626 */
144
148
 
149
+ /* Rail / inset-panel surface (5% lifted from the sidebar bg) */
150
+ --ec-rail-bg: 15 15 15; /* ~2% lighter than #0a0a0a */
151
+ --ec-rail-active-bg: 41 41 41; /* ~10% lifted off rail bg */
152
+
145
153
  /* Nested sidebar content */
146
154
  --ec-content-bg: 10 10 10; /* #0a0a0a */
147
155
  --ec-content-text: 245 245 245; /* #f5f5f5 */
@@ -159,7 +167,7 @@
159
167
 
160
168
  /* Group headers */
161
169
  --ec-group-icon-bg: 38 38 38; /* #262626 */
162
- --ec-group-icon-text: 253 186 116; /* #fdba74 orange-300 */
170
+ --ec-group-icon-text: 255 119 51; /* #ff7733 deep vivid orange */
163
171
 
164
172
  /* Main content/docs area */
165
173
  --ec-page-bg: 10 10 10; /* #0a0a0a */
@@ -0,0 +1,49 @@
1
+ import { z } from 'zod';
2
+
3
+ export const KNOWN_THEMES = ['default', 'ocean', 'sapphire', 'sunset', 'forest'] as const;
4
+ export type KnownTheme = (typeof KNOWN_THEMES)[number];
5
+
6
+ const optionalString = (max: number) =>
7
+ z
8
+ .string()
9
+ .max(max)
10
+ .optional()
11
+ .transform((v) => (v === undefined || v.trim() === '' ? undefined : v));
12
+
13
+ const optionalUrl = z
14
+ .string()
15
+ .max(2048)
16
+ .optional()
17
+ .transform((v) => (v === undefined || v.trim() === '' ? undefined : v))
18
+ .refine((v) => v === undefined || /^https?:\/\//i.test(v), {
19
+ message: 'Must be a valid URL starting with http:// or https://',
20
+ });
21
+
22
+ export const generalSettingsSchema = z.object({
23
+ title: z.string().min(1, 'Title is required').max(100),
24
+ tagline: optionalString(500),
25
+ organizationName: optionalString(100),
26
+ homepageLink: optionalUrl,
27
+ editUrl: optionalUrl,
28
+ repositoryUrl: optionalUrl,
29
+ logo: z
30
+ .object({
31
+ alt: optionalString(200),
32
+ text: optionalString(50),
33
+ })
34
+ .partial()
35
+ .optional(),
36
+ // Allow unknown current theme value (e.g., user hand-edited) so we don't clobber it.
37
+ theme: z.string().min(1).max(100),
38
+ });
39
+
40
+ export type GeneralSettings = z.infer<typeof generalSettingsSchema>;
41
+
42
+ export const aiSettingsSchema = z.object({
43
+ llmsTxtEnabled: z.boolean(),
44
+ chatEnabled: z.boolean(),
45
+ });
46
+
47
+ export type AiSettings = z.infer<typeof aiSettingsSchema>;
48
+
49
+ export const isKnownTheme = (value: string): value is KnownTheme => (KNOWN_THEMES as readonly string[]).includes(value);
@@ -0,0 +1,149 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import * as recast from 'recast';
4
+ import babelParser from 'recast/parsers/babel.js';
5
+
6
+ const b = recast.types.builders;
7
+ type ASTNode = recast.types.ASTNode;
8
+ type ObjectExpression = recast.types.namedTypes.ObjectExpression;
9
+ type Property = recast.types.namedTypes.Property;
10
+ type ObjectProperty = recast.types.namedTypes.ObjectProperty;
11
+
12
+ export type ConfigPrimitive = string | boolean;
13
+
14
+ export type ConfigUpdate = {
15
+ // Primitive value → set; `null` → remove; object → recurse one level.
16
+ [key: string]: ConfigPrimitive | null | NestedConfigUpdate;
17
+ };
18
+
19
+ type NestedConfigUpdate = {
20
+ [key: string]: ConfigPrimitive | null;
21
+ };
22
+
23
+ const projectRoot = () => process.env.PROJECT_DIR ?? process.cwd();
24
+
25
+ export const getConfigPath = () => path.join(projectRoot(), 'eventcatalog.config.js');
26
+
27
+ export const readConfigSource = (): string => fs.readFileSync(getConfigPath(), 'utf8');
28
+
29
+ const parse = (source: string) => recast.parse(source, { parser: babelParser });
30
+
31
+ const isObjectProperty = (n: any): n is Property | ObjectProperty => n && (n.type === 'ObjectProperty' || n.type === 'Property');
32
+
33
+ const propertyKeyName = (prop: Property | ObjectProperty): string | null => {
34
+ const key: any = prop.key;
35
+ if (!key) return null;
36
+ if (key.type === 'Identifier') return key.name;
37
+ if (key.type === 'Literal' || key.type === 'StringLiteral') return String(key.value);
38
+ return null;
39
+ };
40
+
41
+ const findExportedObject = (ast: ASTNode): ObjectExpression => {
42
+ let result: ObjectExpression | null = null;
43
+ recast.visit(ast, {
44
+ visitExportDefaultDeclaration(p) {
45
+ const decl: any = p.node.declaration;
46
+ if (decl?.type === 'ObjectExpression') {
47
+ result = decl as ObjectExpression;
48
+ } else if (decl?.type === 'TSAsExpression' && decl.expression?.type === 'ObjectExpression') {
49
+ result = decl.expression as ObjectExpression;
50
+ }
51
+ return false;
52
+ },
53
+ });
54
+ if (!result) throw new Error('Could not find `export default { ... }` in eventcatalog.config.js');
55
+ return result;
56
+ };
57
+
58
+ const findProperty = (obj: ObjectExpression, name: string): (Property | ObjectProperty) | null => {
59
+ for (const prop of obj.properties) {
60
+ if (isObjectProperty(prop) && propertyKeyName(prop) === name) return prop as Property | ObjectProperty;
61
+ }
62
+ return null;
63
+ };
64
+
65
+ const removeProperty = (obj: ObjectExpression, name: string): boolean => {
66
+ const idx = obj.properties.findIndex((p) => isObjectProperty(p) && propertyKeyName(p) === name);
67
+ if (idx === -1) return false;
68
+ obj.properties.splice(idx, 1);
69
+ return true;
70
+ };
71
+
72
+ const buildLiteral = (value: ConfigPrimitive) => (typeof value === 'boolean' ? b.booleanLiteral(value) : b.stringLiteral(value));
73
+
74
+ const setPrimitiveProperty = (obj: ObjectExpression, name: string, value: ConfigPrimitive) => {
75
+ const existing = findProperty(obj, name);
76
+ const literal = buildLiteral(value);
77
+ if (existing) {
78
+ (existing as any).value = literal;
79
+ return;
80
+ }
81
+ obj.properties.push(b.property('init', b.identifier(name), literal) as any);
82
+ };
83
+
84
+ const ensureNestedObject = (obj: ObjectExpression, name: string): ObjectExpression => {
85
+ const existing = findProperty(obj, name);
86
+ if (existing && (existing as any).value?.type === 'ObjectExpression') {
87
+ return (existing as any).value as ObjectExpression;
88
+ }
89
+ const fresh = b.objectExpression([]);
90
+ if (existing) {
91
+ (existing as any).value = fresh;
92
+ } else {
93
+ obj.properties.push(b.property('init', b.identifier(name), fresh) as any);
94
+ }
95
+ return fresh;
96
+ };
97
+
98
+ const removeNestedObjectIfEmpty = (obj: ObjectExpression, name: string) => {
99
+ const existing = findProperty(obj, name);
100
+ if (!existing) return;
101
+ const value: any = (existing as any).value;
102
+ if (value?.type === 'ObjectExpression' && value.properties.length === 0) {
103
+ removeProperty(obj, name);
104
+ }
105
+ };
106
+
107
+ /**
108
+ * Apply an update to eventcatalog.config.js, preserving formatting/comments via recast.
109
+ *
110
+ * - String value → replace existing or insert new property
111
+ * - `null` value → remove property if present
112
+ * - Nested object value → recurse one level (sufficient for v1 fields like `logo`)
113
+ *
114
+ * Returns the updated source. Caller is responsible for writing it.
115
+ */
116
+ export const applyConfigUpdate = (source: string, update: ConfigUpdate): string => {
117
+ const ast = parse(source);
118
+ const root = findExportedObject(ast);
119
+
120
+ for (const [key, value] of Object.entries(update)) {
121
+ if (value === null) {
122
+ removeProperty(root, key);
123
+ continue;
124
+ }
125
+ if (typeof value === 'string' || typeof value === 'boolean') {
126
+ setPrimitiveProperty(root, key, value);
127
+ continue;
128
+ }
129
+ // nested object update
130
+ const nested = ensureNestedObject(root, key);
131
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
132
+ if (nestedValue === null) {
133
+ removeProperty(nested, nestedKey);
134
+ } else if (typeof nestedValue === 'string' || typeof nestedValue === 'boolean') {
135
+ setPrimitiveProperty(nested, nestedKey, nestedValue);
136
+ }
137
+ }
138
+ removeNestedObjectIfEmpty(root, key);
139
+ }
140
+
141
+ return recast.print(ast, { quote: 'single' }).code;
142
+ };
143
+
144
+ export const writeConfigUpdate = (update: ConfigUpdate): string => {
145
+ const source = readConfigSource();
146
+ const updated = applyConfigUpdate(source, update);
147
+ fs.writeFileSync(getConfigPath(), updated, 'utf8');
148
+ return updated;
149
+ };