@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
@@ -7,16 +7,15 @@ import { AlignLeftIcon, UserIcon, UsersIcon } from 'lucide-react';
7
7
  import mdxComponents from '@components/MDX/components';
8
8
 
9
9
  import { getOwner } from '@utils/collections/owners';
10
- import { buildUrl } from '@utils/url-builder';
10
+ import { buildUrl, buildEditUrlForResource } from '@utils/url-builder';
11
11
  import { resourceToCollectionMap } from '@utils/collections/util';
12
12
  import { getMDXComponentsByName } from '@utils/markdown';
13
- import { getAdjacentPages } from '@enterprise/custom-documentation/utils/custom-docs';
14
- import { getCustomDocsContentBadgeClasses } from '@enterprise/custom-documentation/utils/badge-styles';
15
-
13
+ import { getAdjacentPages, getNavigationItems } from '@enterprise/custom-documentation/utils/custom-docs';
16
14
  import CustomDocsNav from '@enterprise/custom-documentation/components/CustomDocsNav/CustomDocsNav.astro';
17
15
  import NodeGraph from '@components/MDX/NodeGraph/NodeGraph.astro';
16
+ import CopyAsMarkdown from '@components/CopyAsMarkdown';
18
17
 
19
- import { isVisualiserEnabled } from '@utils/feature';
18
+ import { isVisualiserEnabled, isMarkdownDownloadEnabled, isRSSEnabled, isEventCatalogChatEnabled } from '@utils/feature';
20
19
 
21
20
  const props = Astro.props;
22
21
  const doc = props.data;
@@ -24,44 +23,6 @@ const { Content, headings } = await render(props as any);
24
23
  const currentSlug = props.id;
25
24
 
26
25
  const nodeGraphs = getMDXComponentsByName(props.body, 'NodeGraph') || [];
27
- // Get sidebar data
28
- const sidebar = config?.customDocs?.sidebar || [];
29
-
30
- // Flatten the sidebar to find previous and next pages
31
- type FlatItem = {
32
- label: string;
33
- slug: string;
34
- };
35
-
36
- // Define sidebar section type
37
- type SidebarSection = {
38
- label: string;
39
- slug?: string;
40
- items?: Array<{
41
- label: string;
42
- slug: string;
43
- }>;
44
- };
45
-
46
- const flattenedItems: FlatItem[] = [];
47
-
48
- // Process all sidebar sections to create a flattened array
49
- sidebar.forEach((section: SidebarSection) => {
50
- if (section.slug && !section.items) {
51
- flattenedItems.push({
52
- label: section.label,
53
- slug: section.slug,
54
- });
55
- }
56
- if (section.items) {
57
- section.items.forEach((item: { label: string; slug: string }) => {
58
- flattenedItems.push({
59
- label: item.label,
60
- slug: item.slug,
61
- });
62
- });
63
- }
64
- });
65
26
 
66
27
  const { prev, next } = await getAdjacentPages(currentSlug);
67
28
 
@@ -79,17 +40,40 @@ const ownersList = filteredOwners.map((o) => ({
79
40
 
80
41
  const badges = doc?.badges || [];
81
42
 
82
- const getCustomDocBadgeClasses = (badge: any) => {
83
- const color = badge?.backgroundColor || badge?.textColor;
84
- return `${getCustomDocsContentBadgeClasses(color)} ${badge?.class ? badge.class : ''}`;
43
+ // Find the immediate parent group label, for the accent header label above the title.
44
+ // Walks the processed navigation tree (which expands `autogenerated` directories into nested groups)
45
+ // so that nested groups like "Creating new microservices" are surfaced rather than only the top-level section.
46
+ const normalizeSlug = (s: string | undefined) => (s || '').replace(/^\//, '').replace(/^docs\//, '');
47
+ const currentSlugNorm = normalizeSlug(currentSlug);
48
+ const navigationItems = await getNavigationItems();
49
+
50
+ type NavItem = { label: string; slug?: string; items?: NavItem[] };
51
+
52
+ const findParentLabel = (items: NavItem[], parentLabel: string): string | null => {
53
+ for (const item of items) {
54
+ if (item.slug && normalizeSlug(item.slug) === currentSlugNorm) {
55
+ return parentLabel;
56
+ }
57
+ if (item.items?.length) {
58
+ const found = findParentLabel(item.items, item.label);
59
+ if (found !== null) return found;
60
+ }
61
+ }
62
+ return null;
85
63
  };
64
+
65
+ const sectionLabel = findParentLabel(navigationItems as NavItem[], '') || '';
66
+
67
+ const editUrl =
68
+ (doc as any)?.editUrl ||
69
+ (config.editUrl && (props as any)?.filePath ? buildEditUrlForResource(config.editUrl, (props as any).filePath) : '');
86
70
  ---
87
71
 
88
72
  <VerticalSideBarLayout title={doc.title || 'Documentation'} showNestedSideBar={false}>
89
73
  <div class="custom-docs-shell flex w-full" data-pagefind-body data-pagefind-meta={`title:${doc.title}`}>
90
74
  <!-- Left Sidebar Navigation -->
91
75
  <aside
92
- class="sidebar-transition fixed top-0 bottom-0 left-[var(--ec-vertical-nav-width,14rem)] z-10 w-[var(--ec-custom-docs-sidebar-width,20rem)] overflow-hidden border-r border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))]"
76
+ class="sidebar-transition fixed top-0 bottom-0 left-[var(--ec-vertical-nav-width,14rem)] z-10 w-[var(--ec-custom-docs-sidebar-width,20rem)] overflow-hidden border-r border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-rail-bg))]"
93
77
  >
94
78
  <div class="h-full">
95
79
  <CustomDocsNav />
@@ -99,20 +83,49 @@ const getCustomDocBadgeClasses = (badge: any) => {
99
83
  <div class="sidebar-transition flex w-full min-w-0" style="margin-left: var(--ec-custom-docs-sidebar-width, 20rem);">
100
84
  <main class="min-w-0 flex-1">
101
85
  <div
102
- class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]"
86
+ class="w-full lg:mr-2 pr-10 py-10 bg-[rgb(var(--ec-page-bg))]"
103
87
  style="padding-left: var(--ec-app-content-padding-left, 5rem);"
104
88
  >
105
- <div class="border-b border-[rgb(var(--ec-page-border))] flex justify-between items-start md:pb-2">
106
- <div>
107
- <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">{doc.title}</h2>
108
- <p class="text-base pt-2 text-[rgb(var(--ec-page-text-muted))] font-light">{doc.summary}</p>
89
+ <div class="border-b border-[rgb(var(--ec-page-border))] md:pb-4">
90
+ <div class="flex flex-col gap-4">
91
+ {
92
+ sectionLabel && (
93
+ <span class="text-xs md:text-sm font-semibold text-[rgb(var(--ec-accent))] capitalize">{sectionLabel}</span>
94
+ )
95
+ }
96
+ <div class="flex justify-between items-center gap-4">
97
+ <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">
98
+ {doc.title}
99
+ </h2>
100
+ <div class="hidden lg:block shrink-0">
101
+ <CopyAsMarkdown
102
+ client:only="react"
103
+ schemas={[]}
104
+ chatEnabled={isEventCatalogChatEnabled()}
105
+ markdownDownloadEnabled={isMarkdownDownloadEnabled()}
106
+ rssFeedEnabled={isRSSEnabled()}
107
+ editUrl={editUrl}
108
+ preferChatAsDefault={isEventCatalogChatEnabled()}
109
+ />
110
+ </div>
111
+ </div>
112
+ <p class="text-base text-[rgb(var(--ec-page-text-muted))] font-light">{doc.summary}</p>
109
113
  {
110
- badges && (
111
- <div class="flex flex-wrap gap-3 py-4">
114
+ badges && badges.length > 0 && (
115
+ <div class="flex flex-wrap gap-3 py-2">
112
116
  {badges.map((badge: any) => {
113
117
  return (
114
118
  <a href={badge.url || '#'}>
115
- <span id={badge.id || ''} class={`${getCustomDocBadgeClasses(badge)} mr-2`}>
119
+ <span
120
+ id={badge.id || ''}
121
+ class={`
122
+ inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium
123
+ bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))]
124
+ text-[rgb(var(--ec-page-text))]
125
+ shadow-xs
126
+ ${badge.class ? badge.class : ''}
127
+ `}
128
+ >
116
129
  {badge.icon && <badge.icon className="w-4 h-4 flex-shrink-0 text-[rgb(var(--ec-icon-color))]" />}
117
130
  {badge.iconURL && <img src={badge.iconURL} class="w-4 h-4 flex-shrink-0 opacity-80" alt="" />}
118
131
  <span>{badge.content}</span>
@@ -125,7 +138,7 @@ const getCustomDocBadgeClasses = (badge: any) => {
125
138
  }
126
139
  </div>
127
140
  </div>
128
- <div class="prose prose-md py-4 w-full text-[15px]">
141
+ <div class="prose prose-md max-w-none py-4 w-full text-[15px]">
129
142
  <Content components={{ ...mdxComponents(props) }} />
130
143
  </div>
131
144
 
@@ -194,7 +207,7 @@ const getCustomDocBadgeClasses = (badge: any) => {
194
207
  <!-- Right Sidebar TOC -->
195
208
  <aside
196
209
  id="eventcatalog-docs-sidebar"
197
- class="hidden xl:block sticky top-[4rem] self-start w-[280px] max-h-[calc(100vh-4rem)] overflow-y-auto py-2 flex-shrink-0 pr-10 bg-[rgb(var(--ec-page-bg))]"
210
+ class="hidden xl:block sticky top-[4rem] self-start w-[200px] max-h-[calc(100vh-4rem)] overflow-y-auto py-2 flex-shrink-0 bg-[rgb(var(--ec-page-bg))]"
198
211
  >
199
212
  <div class="mt-8 space-y-8">
200
213
  {
@@ -44,7 +44,8 @@ export const isEventCatalogChatEnabled = () => {
44
44
  const isFeatureEnabledFromPlan = isEventCatalogStarterEnabled() || isEventCatalogScaleEnabled();
45
45
  const directory = process.env.PROJECT_DIR || process.cwd();
46
46
  const hasChatConfigurationFile = fs.existsSync(join(directory, 'eventcatalog.chat.js'));
47
- return isFeatureEnabledFromPlan && hasChatConfigurationFile && isSSR();
47
+ const isEnabledInConfig = config?.chat?.enabled ?? true;
48
+ return isFeatureEnabledFromPlan && hasChatConfigurationFile && isSSR() && isEnabledInConfig;
48
49
  };
49
50
 
50
51
  export const isEventCatalogUpgradeEnabled = () => !isEventCatalogStarterEnabled() && !isEventCatalogScaleEnabled();
@@ -0,0 +1,116 @@
1
+ ---
2
+ import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
3
+ import { buildUrl } from '@utils/url-builder';
4
+ import { Settings, Server, Bot, FileText, CreditCard } from 'lucide-react';
5
+
6
+ export type SettingsSection = 'general' | 'mcp' | 'assistant' | 'llm-access' | 'billing';
7
+
8
+ interface Props {
9
+ title: string;
10
+ active: SettingsSection;
11
+ }
12
+
13
+ const { title, active } = Astro.props;
14
+
15
+ interface SectionItem {
16
+ id: SettingsSection;
17
+ label: string;
18
+ href: string;
19
+ icon: any;
20
+ }
21
+
22
+ interface SectionGroup {
23
+ label: string;
24
+ items: SectionItem[];
25
+ }
26
+
27
+ const groups: SectionGroup[] = [
28
+ {
29
+ label: 'Project Settings',
30
+ items: [
31
+ {
32
+ id: 'general',
33
+ label: 'General',
34
+ href: buildUrl('/settings/general'),
35
+ icon: Settings,
36
+ },
37
+ ],
38
+ },
39
+ {
40
+ label: 'AI Settings',
41
+ items: [
42
+ {
43
+ id: 'mcp',
44
+ label: 'MCP',
45
+ href: buildUrl('/settings/mcp'),
46
+ icon: Server,
47
+ },
48
+ {
49
+ id: 'assistant',
50
+ label: 'Agent',
51
+ href: buildUrl('/settings/assistant'),
52
+ icon: Bot,
53
+ },
54
+ {
55
+ id: 'llm-access',
56
+ label: 'LLM Access',
57
+ href: buildUrl('/settings/llm-access'),
58
+ icon: FileText,
59
+ },
60
+ ],
61
+ },
62
+ {
63
+ label: 'Workspace',
64
+ items: [
65
+ {
66
+ id: 'billing',
67
+ label: 'Billing',
68
+ href: buildUrl('/settings/billing'),
69
+ icon: CreditCard,
70
+ },
71
+ ],
72
+ },
73
+ ];
74
+ ---
75
+
76
+ <VerticalSideBarLayout title={`${title} | Settings`} showNestedSideBar={true}>
77
+ <Fragment slot="sidebar-content">
78
+ <div class="settings-rail flex h-full flex-col bg-[rgb(var(--ec-rail-bg))]">
79
+ <nav class="flex flex-1 flex-col gap-4 overflow-y-auto px-3 pt-6 pb-4">
80
+ {
81
+ groups.map((group) => (
82
+ <div class="space-y-1">
83
+ <p class="rail-section-label px-3 pb-1 text-[0.65rem] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-sidebar-text)/0.5)]">
84
+ {group.label}
85
+ </p>
86
+ <div class="flex flex-col gap-1">
87
+ {group.items.map((item) => {
88
+ const isActive = item.id === active;
89
+ return (
90
+ <a
91
+ href={item.href}
92
+ aria-current={isActive ? 'page' : undefined}
93
+ class:list={[
94
+ 'nav-secondary-item flex items-center gap-3 px-3 py-2.5 text-[13px] font-medium transition-colors duration-150',
95
+ isActive
96
+ ? 'font-semibold text-[rgb(var(--ec-page-text))]'
97
+ : 'text-[rgb(var(--ec-sidebar-text))] hover:text-[rgb(var(--ec-page-text))]',
98
+ ]}
99
+ >
100
+ <item.icon className="h-3.5 w-3.5 flex-shrink-0" aria-hidden="true" />
101
+ <span class="nav-item-label">{item.label}</span>
102
+ </a>
103
+ );
104
+ })}
105
+ </div>
106
+ </div>
107
+ ))
108
+ }
109
+ </nav>
110
+ </div>
111
+ </Fragment>
112
+
113
+ <div class="settings-page w-full px-8 py-8">
114
+ <slot />
115
+ </div>
116
+ </VerticalSideBarLayout>
@@ -19,6 +19,7 @@ import {
19
19
  Database,
20
20
  UserRound,
21
21
  UsersRound,
22
+ Settings,
22
23
  } from 'lucide-react';
23
24
  import {
24
25
  RectangleGroupIcon,
@@ -269,6 +270,15 @@ const organizationItems = [
269
270
  },
270
271
  ];
271
272
 
273
+ const settingsItems = [
274
+ {
275
+ label: 'Settings',
276
+ icon: Settings,
277
+ href: buildUrl('/settings/general'),
278
+ current: currentPath.startsWith(buildUrl('/settings')),
279
+ },
280
+ ];
281
+
272
282
  const currentNavigationItem = [...navigationItems, ...studioNavigationItem, ...premiumFeatures].find((item) => item.current);
273
283
  const { title, description, showNestedSideBar = true, showHeader = true } = Astro.props;
274
284
 
@@ -336,17 +346,9 @@ const canPageBeEmbedded = isEmbedEnabled();
336
346
  }
337
347
  .vertical-nav-rail {
338
348
  width: var(--ec-vertical-nav-width);
339
- border-right: 1px solid rgb(var(--ec-sidebar-border) / 1);
340
- background: linear-gradient(
341
- 180deg,
342
- rgb(var(--ec-accent) / 0.1) 0%,
343
- rgb(var(--ec-sidebar-bg-gradient)) 16%,
344
- rgb(var(--ec-sidebar-bg)) 48%,
345
- rgb(var(--ec-sidebar-bg)) 100%
346
- );
347
- box-shadow:
348
- inset -1px 0 0 rgb(var(--ec-page-border) / 0.45),
349
- 0 24px 44px -34px rgb(var(--ec-page-text) / 0.28);
349
+ border-right: 1px solid rgb(var(--ec-page-border));
350
+ background-color: rgb(var(--ec-page-bg));
351
+ box-shadow: 0 24px 44px -34px rgb(var(--ec-page-text) / 0.28);
350
352
  overflow: hidden;
351
353
  }
352
354
  :root[data-vertical-nav-collapsed='true'] .vertical-nav-rail .nav-brand-text,
@@ -401,7 +403,7 @@ const canPageBeEmbedded = isEmbedEnabled();
401
403
  min-width: var(--ec-sidebar-panel-width);
402
404
  height: 100vh;
403
405
  border-right: 1px solid rgb(var(--ec-content-border));
404
- background: rgb(var(--ec-page-bg));
406
+ background: rgb(var(--ec-rail-bg));
405
407
  display: flex;
406
408
  flex-direction: column;
407
409
  position: fixed;
@@ -507,7 +509,7 @@ const canPageBeEmbedded = isEmbedEnabled();
507
509
  aria-label={item.label}
508
510
  class={`flex items-center gap-3 px-3 py-2.5 rounded-xl border text-[13px] font-medium transition-all duration-150 ${
509
511
  item.current
510
- ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent-text))] shadow-sm'
512
+ ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent))] shadow-sm'
511
513
  : 'border-transparent text-[rgb(var(--ec-sidebar-text))] hover:border-[rgb(var(--ec-sidebar-border)/0.65)] hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]'
512
514
  }`}
513
515
  title={item.label}
@@ -522,7 +524,7 @@ const canPageBeEmbedded = isEmbedEnabled();
522
524
  <hr class="rail-divider my-3 border-t border-[rgb(var(--ec-sidebar-border)/0.7)]" />
523
525
 
524
526
  <div
525
- class="rail-section-label px-3 pb-1 text-[0.65rem] font-semibold tracking-[0.18em] uppercase text-[rgb(var(--ec-sidebar-text))]"
527
+ class="rail-section-label px-3 pb-1 text-[0.65rem] font-semibold tracking-[0.18em] uppercase text-[rgb(var(--ec-sidebar-text)/0.5)]"
526
528
  >
527
529
  Browse
528
530
  </div>
@@ -535,7 +537,7 @@ const canPageBeEmbedded = isEmbedEnabled();
535
537
  aria-label={item.label}
536
538
  class={`nav-secondary-item flex items-center gap-3 px-3 py-2.5 rounded-xl border text-[13px] font-medium transition-all duration-150 ${
537
539
  item.current
538
- ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent-text))] shadow-sm'
540
+ ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent))] shadow-sm'
539
541
  : 'border-transparent text-[rgb(var(--ec-sidebar-text))] hover:border-[rgb(var(--ec-sidebar-border)/0.65)] hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]'
540
542
  }`}
541
543
  title={item.label}
@@ -549,7 +551,7 @@ const canPageBeEmbedded = isEmbedEnabled();
549
551
  <hr class="rail-divider my-3 border-t border-[rgb(var(--ec-sidebar-border)/0.7)]" />
550
552
 
551
553
  <div
552
- class="rail-section-label px-3 pb-1 text-[0.65rem] font-semibold tracking-[0.18em] uppercase text-[rgb(var(--ec-sidebar-text))]"
554
+ class="rail-section-label px-3 pb-1 text-[0.65rem] font-semibold tracking-[0.18em] uppercase text-[rgb(var(--ec-sidebar-text)/0.5)]"
553
555
  >
554
556
  Organization
555
557
  </div>
@@ -562,7 +564,7 @@ const canPageBeEmbedded = isEmbedEnabled();
562
564
  aria-label={item.label}
563
565
  class={`nav-secondary-item flex items-center gap-3 px-3 py-2.5 rounded-xl border text-[13px] font-medium transition-all duration-150 ${
564
566
  item.current
565
- ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent-text))] shadow-sm'
567
+ ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent))] shadow-sm'
566
568
  : 'border-transparent text-[rgb(var(--ec-sidebar-text))] hover:border-[rgb(var(--ec-sidebar-border)/0.65)] hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]'
567
569
  }`}
568
570
  title={item.label}
@@ -584,7 +586,7 @@ const canPageBeEmbedded = isEmbedEnabled();
584
586
  aria-label={item.label}
585
587
  class={`flex items-center gap-3 px-3 py-2.5 rounded-xl border text-[13px] font-medium transition-all duration-150 ${
586
588
  item.current
587
- ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent-text))] shadow-sm'
589
+ ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent))] shadow-sm'
588
590
  : 'border-transparent text-[rgb(var(--ec-sidebar-text))] hover:border-[rgb(var(--ec-sidebar-border)/0.65)] hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]'
589
591
  }`}
590
592
  title={item.label}
@@ -594,6 +596,28 @@ const canPageBeEmbedded = isEmbedEnabled();
594
596
  </a>
595
597
  ))
596
598
  }
599
+
600
+ <div class="mt-auto pt-3">
601
+ <hr class="rail-divider mb-3 border-t border-[rgb(var(--ec-sidebar-border)/0.7)]" />
602
+ {
603
+ settingsItems.map((item) => (
604
+ <a
605
+ href={item.href}
606
+ data-role="secondary-nav-item"
607
+ aria-label={item.label}
608
+ class={`nav-secondary-item flex items-center gap-3 px-3 py-2.5 rounded-xl border text-[13px] font-medium transition-all duration-150 ${
609
+ item.current
610
+ ? 'border-[rgb(var(--ec-accent)/0.2)] bg-[rgb(var(--ec-page-bg)/0.88)] text-[rgb(var(--ec-accent))] shadow-sm'
611
+ : 'border-transparent text-[rgb(var(--ec-sidebar-text))] hover:border-[rgb(var(--ec-sidebar-border)/0.65)] hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]'
612
+ }`}
613
+ title={item.label}
614
+ >
615
+ <item.icon className="h-4 w-4 flex-shrink-0" aria-hidden="true" />
616
+ <span class="nav-item-label">{item.label}</span>
617
+ </a>
618
+ ))
619
+ }
620
+ </div>
597
621
  </nav>
598
622
 
599
623
  <div class="border-t border-[rgb(var(--ec-sidebar-border)/0.7)] px-3 py-3 flex-shrink-0">
@@ -609,11 +633,26 @@ const canPageBeEmbedded = isEmbedEnabled();
609
633
  </div>
610
634
  </div>
611
635
  </aside>
612
- {showNestedSideBar && <SideNav id="sidebar" class={`sidebar-panel sidebar-transition`} />}
636
+ {
637
+ showNestedSideBar &&
638
+ (Astro.slots.has('sidebar-content') ? (
639
+ <aside id="sidebar" class="sidebar-panel sidebar-transition">
640
+ <slot name="sidebar-content" />
641
+ </aside>
642
+ ) : (
643
+ <SideNav id="sidebar" class={`sidebar-panel sidebar-transition`} />
644
+ ))
645
+ }
613
646
  <main class="content-panel sidebar-transition w-full bg-[rgb(var(--ec-page-bg))]" id="content">
614
- <div class="app-content-wrapper">
615
- <slot />
616
- </div>
647
+ {
648
+ Astro.slots.has('sidebar-content') ? (
649
+ <slot />
650
+ ) : (
651
+ <div class="app-content-wrapper">
652
+ <slot />
653
+ </div>
654
+ )
655
+ }
617
656
  </main>
618
657
 
619
658
  <!-- Create a overlay that tells people to purchase backstage plugin if they want to embed the page -->
@@ -647,7 +686,7 @@ const canPageBeEmbedded = isEmbedEnabled();
647
686
  const ACTIVE_NAV_CLASSES = [
648
687
  'border-[rgb(var(--ec-accent)/0.2)]',
649
688
  'bg-[rgb(var(--ec-page-bg)/0.88)]',
650
- 'text-[rgb(var(--ec-accent-text))]',
689
+ 'text-[rgb(var(--ec-accent))]',
651
690
  'shadow-sm',
652
691
  ];
653
692
  const INACTIVE_NAV_CLASSES = [