@eventcatalog/core 3.0.0-beta.1 → 3.0.0-beta.11
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.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-M6QG3NPM.js → chunk-EPAI7P6H.js} +1 -1
- package/dist/{chunk-3W6JYTHP.js → chunk-HHZD3IEE.js} +6 -2
- package/dist/{chunk-KE2IUVK6.js → chunk-NGLFVSLZ.js} +1 -1
- package/dist/chunk-SI5FCV46.js +44 -0
- package/dist/{chunk-FH5AN4Z6.js → chunk-WYPVB6OY.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +66 -20
- package/dist/eventcatalog.js +27 -16
- package/dist/generate.cjs +48 -2
- package/dist/generate.js +3 -1
- package/dist/utils/cli-logger.cjs +82 -0
- package/dist/utils/cli-logger.d.cts +10 -0
- package/dist/utils/cli-logger.d.ts +10 -0
- package/dist/utils/cli-logger.js +7 -0
- package/eventcatalog/integrations/ecstudio-watcher.mjs +1 -1
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +0 -2
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +8 -8
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +45 -3
- package/eventcatalog/src/components/Search/SearchModal.tsx +16 -7
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +4 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/domain.ts +7 -6
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +22 -8
- package/eventcatalog/src/components/SideNav/NestedSideBar/sidebar-builder.ts +57 -11
- package/eventcatalog/src/pages/_index.astro +5 -7
- package/eventcatalog/src/pages/auth/login.astro +2 -2
- package/eventcatalog/src/remark-plugins/directives.ts +30 -9
- package/package.json +3 -2
|
@@ -41,9 +41,9 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<div className={`rounded-xl overflow-hidden bg-
|
|
44
|
+
<div className={`rounded-xl overflow-hidden bg-white p-8 border-2 border-dashed border-pink-300`}>
|
|
45
45
|
{/* Service Title */}
|
|
46
|
-
<div className="flex items-center gap-2 mb-8">
|
|
46
|
+
{/* <div className="flex items-center gap-2 mb-8">
|
|
47
47
|
<ServerIcon className="h-6 w-6 text-pink-500" />
|
|
48
48
|
<h2 className="text-2xl font-semibold text-gray-900">{service.data.name}</h2>
|
|
49
49
|
<div className="flex gap-2 ml-auto">
|
|
@@ -60,7 +60,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
60
60
|
Read documentation
|
|
61
61
|
</a>
|
|
62
62
|
</div>
|
|
63
|
-
</div>
|
|
63
|
+
</div> */}
|
|
64
64
|
|
|
65
65
|
<div className="grid grid-cols-3 gap-8 relative">
|
|
66
66
|
{/* Left Column - Receives Messages & Reads From Containers */}
|
|
@@ -70,7 +70,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
70
70
|
<div className="mb-6">
|
|
71
71
|
<h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
|
|
72
72
|
<ServerIcon className="h-5 w-5 text-blue-500" />
|
|
73
|
-
|
|
73
|
+
Inbound Messages ({receives.length})
|
|
74
74
|
</h2>
|
|
75
75
|
</div>
|
|
76
76
|
{receives.length > 0 ? (
|
|
@@ -124,7 +124,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
124
124
|
</div>
|
|
125
125
|
|
|
126
126
|
{/* Service Information (Center) */}
|
|
127
|
-
<div className="bg-
|
|
127
|
+
<div className="bg-pink-50 border-2 border-pink-100 rounded-lg p-6 flex flex-col justify-center">
|
|
128
128
|
<div className="flex flex-col items-center gap-4">
|
|
129
129
|
<ServerIcon className="h-12 w-12 text-pink-500" />
|
|
130
130
|
<p className="text-xl font-semibold text-gray-900 text-center">{service.data.name}</p>
|
|
@@ -133,11 +133,11 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
133
133
|
<div className="w-full grid grid-cols-2 gap-3 mt-2">
|
|
134
134
|
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-200">
|
|
135
135
|
<div className="text-2xl font-bold text-blue-600">{receives.length}</div>
|
|
136
|
-
<div className="text-xs text-gray-600 mt-1">
|
|
136
|
+
<div className="text-xs text-gray-600 mt-1">Inbound Messages</div>
|
|
137
137
|
</div>
|
|
138
138
|
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-200">
|
|
139
139
|
<div className="text-2xl font-bold text-green-600">{sends.length}</div>
|
|
140
|
-
<div className="text-xs text-gray-600 mt-1">
|
|
140
|
+
<div className="text-xs text-gray-600 mt-1">Outbound Messages</div>
|
|
141
141
|
</div>
|
|
142
142
|
{readsFrom.length > 0 && (
|
|
143
143
|
<div className="text-center p-3 bg-orange-50 rounded-lg border border-orange-200">
|
|
@@ -168,7 +168,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
168
168
|
<div className="mb-6">
|
|
169
169
|
<h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
|
|
170
170
|
<ServerIcon className="h-5 w-5 text-emerald-500" />
|
|
171
|
-
|
|
171
|
+
Outbound Messages ({sends.length})
|
|
172
172
|
</h2>
|
|
173
173
|
</div>
|
|
174
174
|
{sends.length > 0 ? (
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import '@xyflow/react/dist/style.css';
|
|
20
20
|
import { ExternalLink, HistoryIcon } from 'lucide-react';
|
|
21
21
|
import { toPng } from 'html-to-image';
|
|
22
|
-
import { DocumentArrowDownIcon } from '@heroicons/react/24/outline';
|
|
22
|
+
import { DocumentArrowDownIcon, PresentationChartLineIcon } from '@heroicons/react/24/outline';
|
|
23
23
|
// Nodes and edges
|
|
24
24
|
import ServiceNode from './Nodes/Service';
|
|
25
25
|
import FlowNode from './Nodes/Flow';
|
|
@@ -135,6 +135,7 @@ const NodeGraphBuilder = ({
|
|
|
135
135
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
136
136
|
const [animateMessages, setAnimateMessages] = useState(false);
|
|
137
137
|
const [activeStepIndex, setActiveStepIndex] = useState<number | null>(null);
|
|
138
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
138
139
|
// const [isStudioModalOpen, setIsStudioModalOpen] = useState(false);
|
|
139
140
|
|
|
140
141
|
// Check if there are channels to determine if we need the visualizer functionality
|
|
@@ -349,6 +350,30 @@ const NodeGraphBuilder = ({
|
|
|
349
350
|
setIsStudioModalOpen(true);
|
|
350
351
|
};
|
|
351
352
|
|
|
353
|
+
const toggleFullScreen = useCallback(() => {
|
|
354
|
+
if (!document.fullscreenElement) {
|
|
355
|
+
reactFlowWrapperRef.current?.requestFullscreen().catch((err) => {
|
|
356
|
+
console.error(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
|
|
357
|
+
});
|
|
358
|
+
} else {
|
|
359
|
+
document.exitFullscreen();
|
|
360
|
+
}
|
|
361
|
+
}, []);
|
|
362
|
+
|
|
363
|
+
useEffect(() => {
|
|
364
|
+
const handleFullscreenChange = () => {
|
|
365
|
+
setIsFullscreen(!!document.fullscreenElement);
|
|
366
|
+
setTimeout(() => {
|
|
367
|
+
fitView({ duration: 800 });
|
|
368
|
+
}, 100);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
|
372
|
+
return () => {
|
|
373
|
+
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
|
374
|
+
};
|
|
375
|
+
}, [fitView]);
|
|
376
|
+
|
|
352
377
|
const handleExportVisual = useCallback(() => {
|
|
353
378
|
const imageWidth = 1024;
|
|
354
379
|
const imageHeight = 768;
|
|
@@ -563,7 +588,7 @@ const NodeGraphBuilder = ({
|
|
|
563
588
|
const isFlowVisualization = edges.some((edge: Edge) => edge.type === 'flow-edge');
|
|
564
589
|
|
|
565
590
|
return (
|
|
566
|
-
<div ref={reactFlowWrapperRef} className="w-full h-full">
|
|
591
|
+
<div ref={reactFlowWrapperRef} className="w-full h-full bg-gray-50">
|
|
567
592
|
<ReactFlow
|
|
568
593
|
nodeTypes={nodeTypes}
|
|
569
594
|
edgeTypes={edgeTypes}
|
|
@@ -583,7 +608,7 @@ const NodeGraphBuilder = ({
|
|
|
583
608
|
<Panel position="top-center" className="w-full pr-6 ">
|
|
584
609
|
<div className="flex space-x-2 justify-between items-center">
|
|
585
610
|
<div className="flex space-x-2 ml-4">
|
|
586
|
-
<div>
|
|
611
|
+
<div className="relative group">
|
|
587
612
|
<button
|
|
588
613
|
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
|
|
589
614
|
className="py-2.5 px-3 bg-white rounded-md shadow-md hover:bg-purple-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
|
|
@@ -591,6 +616,23 @@ const NodeGraphBuilder = ({
|
|
|
591
616
|
>
|
|
592
617
|
<CogIcon className="h-5 w-5 text-gray-600" />
|
|
593
618
|
</button>
|
|
619
|
+
<div className="absolute top-full left-0 mt-2 px-2 py-1 bg-gray-900 text-white text-xs rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">
|
|
620
|
+
Settings
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
<div className="relative group">
|
|
624
|
+
<button
|
|
625
|
+
onClick={toggleFullScreen}
|
|
626
|
+
className={`py-2.5 px-3 bg-white rounded-md shadow-md hover:bg-purple-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 ${
|
|
627
|
+
isFullscreen ? 'bg-purple-50 text-purple-600' : ''
|
|
628
|
+
}`}
|
|
629
|
+
aria-label={isFullscreen ? 'Exit presentation mode' : 'Enter presentation mode'}
|
|
630
|
+
>
|
|
631
|
+
<PresentationChartLineIcon className={`h-5 w-5 ${isFullscreen ? 'text-purple-600' : 'text-gray-600'}`} />
|
|
632
|
+
</button>
|
|
633
|
+
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-2 px-2 py-1 bg-gray-900 text-white text-xs rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-50">
|
|
634
|
+
{isFullscreen ? 'Exit Presentation Mode' : 'Presentation Mode'}
|
|
635
|
+
</div>
|
|
594
636
|
</div>
|
|
595
637
|
|
|
596
638
|
{title && (
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
ArrowUturnLeftIcon,
|
|
21
21
|
StarIcon,
|
|
22
22
|
Square2StackIcon,
|
|
23
|
+
ArrowsRightLeftIcon,
|
|
23
24
|
} from '@heroicons/react/24/outline';
|
|
24
25
|
import { StarIcon as StarIconSolid, CircleStackIcon } from '@heroicons/react/24/solid';
|
|
25
26
|
import { useStore } from '@nanostores/react';
|
|
@@ -33,7 +34,7 @@ const typeIcons: any = {
|
|
|
33
34
|
Command: ChatBubbleLeftIcon,
|
|
34
35
|
Query: QueryIcon,
|
|
35
36
|
Entity: CubeIcon,
|
|
36
|
-
Channel:
|
|
37
|
+
Channel: ArrowsRightLeftIcon,
|
|
37
38
|
Team: UserGroupIcon,
|
|
38
39
|
User: UserIcon,
|
|
39
40
|
Language: BookOpenIcon,
|
|
@@ -68,8 +69,6 @@ function classNames(...classes: (string | boolean | undefined)[]) {
|
|
|
68
69
|
|
|
69
70
|
// Helper to construct URL from key if href is missing
|
|
70
71
|
const getUrlForItem = (node: any, key: string) => {
|
|
71
|
-
if (node.href) return node.href;
|
|
72
|
-
|
|
73
72
|
const parts = key.split(':');
|
|
74
73
|
if (parts.length < 2) return null; // Need at least type:id
|
|
75
74
|
|
|
@@ -83,8 +82,16 @@ const getUrlForItem = (node: any, key: string) => {
|
|
|
83
82
|
// Only show items that have a version to avoid duplicates
|
|
84
83
|
if (!version) return null;
|
|
85
84
|
|
|
85
|
+
// If node has href, use it, otherwise construct from key
|
|
86
|
+
if (node.href) return node.href;
|
|
87
|
+
|
|
86
88
|
// Pluralize type for URL if needed
|
|
87
|
-
|
|
89
|
+
let pluralType = type;
|
|
90
|
+
if (['event', 'command', 'domain', 'service', 'flow', 'container', 'channel'].includes(type)) {
|
|
91
|
+
pluralType = type + 's';
|
|
92
|
+
} else if (type === 'query') {
|
|
93
|
+
pluralType = 'queries';
|
|
94
|
+
}
|
|
88
95
|
|
|
89
96
|
return `/docs/${pluralType}/${id}/${version}`;
|
|
90
97
|
};
|
|
@@ -171,6 +178,7 @@ export default function SearchModal() {
|
|
|
171
178
|
Team: 0,
|
|
172
179
|
Container: 0,
|
|
173
180
|
Design: 0,
|
|
181
|
+
Channel: 0,
|
|
174
182
|
};
|
|
175
183
|
|
|
176
184
|
itemsToCount.forEach((item) => {
|
|
@@ -195,6 +203,7 @@ export default function SearchModal() {
|
|
|
195
203
|
if (counts.Service > 0) dynamicFilters.push({ id: 'Service', name: `Services (${counts.Service})` });
|
|
196
204
|
if (counts.Message > 0) dynamicFilters.push({ id: 'Message', name: `Messages (${counts.Message})` });
|
|
197
205
|
if (counts.Container > 0) dynamicFilters.push({ id: 'Container', name: `Containers (${counts.Container})` });
|
|
206
|
+
if (counts.Channel > 0) dynamicFilters.push({ id: 'Channel', name: `Channels (${counts.Channel})` });
|
|
198
207
|
if (counts.Design > 0) dynamicFilters.push({ id: 'Design', name: `Designs (${counts.Design})` });
|
|
199
208
|
if (counts.Team > 0) dynamicFilters.push({ id: 'Team', name: `Teams & Users (${counts.Team})` });
|
|
200
209
|
|
|
@@ -224,7 +233,7 @@ export default function SearchModal() {
|
|
|
224
233
|
const filteredItems = useMemo(() => {
|
|
225
234
|
if (query === '') {
|
|
226
235
|
// Show favorites when search is empty
|
|
227
|
-
if (favorites.length > 0) {
|
|
236
|
+
if (favorites.length > 0 && activeFilter === 'all') {
|
|
228
237
|
return favorites
|
|
229
238
|
.slice(0, 5)
|
|
230
239
|
.map((fav) => {
|
|
@@ -375,14 +384,14 @@ export default function SearchModal() {
|
|
|
375
384
|
<p className={classNames('text-sm font-medium', active ? 'text-gray-900' : 'text-gray-700')}>
|
|
376
385
|
{item.name}
|
|
377
386
|
</p>
|
|
378
|
-
<div className="flex items-
|
|
387
|
+
<div className="flex items-center gap-2">
|
|
379
388
|
<p
|
|
380
389
|
className={classNames('text-sm flex-shrink-0', active ? 'text-gray-700' : 'text-gray-500')}
|
|
381
390
|
>
|
|
382
391
|
{item.type}
|
|
383
392
|
</p>
|
|
384
393
|
{item.rawNode.summary && (
|
|
385
|
-
<p className={classNames('text-
|
|
394
|
+
<p className={classNames('text-sm truncate', active ? 'text-gray-600' : 'text-gray-400')}>
|
|
386
395
|
• {item.rawNode.summary}
|
|
387
396
|
</p>
|
|
388
397
|
)}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
Database,
|
|
15
15
|
Waypoints,
|
|
16
16
|
SquareMousePointer,
|
|
17
|
+
ListOrdered,
|
|
18
|
+
ArrowLeftRight,
|
|
17
19
|
} from 'lucide-react';
|
|
18
20
|
import type { NavNode } from './sidebar-builder';
|
|
19
21
|
|
|
@@ -28,6 +30,7 @@ const getBadgeClasses = (badge: string): string => {
|
|
|
28
30
|
query: 'bg-purple-100 text-purple-700',
|
|
29
31
|
message: 'bg-indigo-100 text-indigo-700',
|
|
30
32
|
design: 'bg-teal-100 text-teal-700',
|
|
33
|
+
channel: 'bg-indigo-100 text-indigo-700',
|
|
31
34
|
};
|
|
32
35
|
return badgeColors[badge.toLowerCase()] || 'bg-gray-100 text-gray-600';
|
|
33
36
|
};
|
|
@@ -77,6 +80,7 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
|
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
const filterTypes = [
|
|
83
|
+
{ key: 'channel', label: 'Channels', badge: 'Channel', icon: ArrowLeftRight },
|
|
80
84
|
{ key: 'command', label: 'Commands', badge: 'Command', icon: MessageSquare },
|
|
81
85
|
{ key: 'container', label: 'Data Stores', badge: 'Container', icon: Database },
|
|
82
86
|
{ key: 'design', label: 'Designs', badge: 'Design', icon: SquareMousePointer },
|
|
@@ -65,6 +65,12 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
65
65
|
},
|
|
66
66
|
].filter(Boolean) as ChildRef[],
|
|
67
67
|
},
|
|
68
|
+
renderSubDomains && {
|
|
69
|
+
type: 'group',
|
|
70
|
+
title: 'Subdomains',
|
|
71
|
+
icon: 'Boxes',
|
|
72
|
+
pages: subDomains.map((domain) => `domain:${(domain as any).data.id}:${(domain as any).data.version}`),
|
|
73
|
+
},
|
|
68
74
|
hasFlows && {
|
|
69
75
|
type: 'group',
|
|
70
76
|
title: 'Flows',
|
|
@@ -81,12 +87,7 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
81
87
|
href: buildUrl(`/docs/entities/${(entity as any).data.id}/${(entity as any).data.version}`),
|
|
82
88
|
})),
|
|
83
89
|
},
|
|
84
|
-
|
|
85
|
-
type: 'group',
|
|
86
|
-
title: 'Subdomains',
|
|
87
|
-
icon: 'Boxes',
|
|
88
|
-
pages: subDomains.map((domain) => `domain:${(domain as any).data.id}:${(domain as any).data.version}`),
|
|
89
|
-
},
|
|
90
|
+
|
|
90
91
|
...(hasResourceGroups ? buildResourceGroupSections(resourceGroups, context) : []),
|
|
91
92
|
renderServices && {
|
|
92
93
|
type: 'group',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
4
4
|
import * as LucideIcons from 'lucide-react';
|
|
5
|
-
import { ChevronRight, ChevronLeft, ChevronDown, Home, Star } from 'lucide-react';
|
|
5
|
+
import { ChevronRight, ChevronLeft, ChevronDown, Home, Star, FileQuestion } from 'lucide-react';
|
|
6
6
|
import type { NavigationData, NavNode, ChildRef } from './sidebar-builder';
|
|
7
7
|
import SearchBar from './SearchBar';
|
|
8
8
|
import { saveState, loadState, saveCollapsedSections, loadCollapsedSections } from './storage';
|
|
@@ -25,6 +25,7 @@ const getBadgeClasses = (badge: string): string => {
|
|
|
25
25
|
query: 'bg-purple-100 text-purple-700',
|
|
26
26
|
message: 'bg-indigo-100 text-indigo-700',
|
|
27
27
|
design: 'bg-teal-100 text-teal-700',
|
|
28
|
+
channel: 'bg-indigo-100 text-indigo-700',
|
|
28
29
|
};
|
|
29
30
|
return badgeColors[badge.toLowerCase()] || 'bg-gray-100 text-gray-600';
|
|
30
31
|
};
|
|
@@ -786,17 +787,17 @@ export default function NestedSideBar() {
|
|
|
786
787
|
</div>
|
|
787
788
|
<div className="flex items-center gap-1 flex-shrink-0">
|
|
788
789
|
{canFavorite && (
|
|
789
|
-
<
|
|
790
|
+
<div
|
|
790
791
|
onClick={handleStarClick}
|
|
791
792
|
className={cn(
|
|
792
|
-
'flex items-center justify-center w-5 h-5 rounded transition-colors',
|
|
793
|
+
'flex items-center justify-center w-5 h-5 rounded transition-colors cursor-pointer',
|
|
793
794
|
isFav
|
|
794
795
|
? 'text-amber-400 hover:text-amber-500'
|
|
795
796
|
: 'text-gray-300 opacity-0 group-hover:opacity-100 hover:text-amber-400'
|
|
796
797
|
)}
|
|
797
798
|
>
|
|
798
799
|
<Star className={cn('w-3.5 h-3.5', isFav && 'fill-current')} />
|
|
799
|
-
</
|
|
800
|
+
</div>
|
|
800
801
|
)}
|
|
801
802
|
{itemHasChildren && (
|
|
802
803
|
<span className="flex items-center justify-center w-5 h-5 text-gray-400 group-hover:text-black group-hover:translate-x-0.5 transition-transform">
|
|
@@ -1038,15 +1039,15 @@ export default function NestedSideBar() {
|
|
|
1038
1039
|
{fav.badge}
|
|
1039
1040
|
</span>
|
|
1040
1041
|
)}
|
|
1041
|
-
<
|
|
1042
|
+
<div
|
|
1042
1043
|
onClick={(e) => {
|
|
1043
1044
|
e.stopPropagation();
|
|
1044
1045
|
if (node) toggleFavorite(fav.nodeKey, node);
|
|
1045
1046
|
}}
|
|
1046
|
-
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors"
|
|
1047
|
+
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors cursor-pointer"
|
|
1047
1048
|
>
|
|
1048
1049
|
<Star className="w-3.5 h-3.5 fill-current" />
|
|
1049
|
-
</
|
|
1050
|
+
</div>
|
|
1050
1051
|
{node?.pages && node.pages.length > 0 && (
|
|
1051
1052
|
<span className="flex items-center justify-center w-5 h-5 text-gray-400 group-hover:text-black">
|
|
1052
1053
|
<ChevronRight className="w-4 h-4" />
|
|
@@ -1060,7 +1061,20 @@ export default function NestedSideBar() {
|
|
|
1060
1061
|
</div>
|
|
1061
1062
|
)}
|
|
1062
1063
|
|
|
1063
|
-
{
|
|
1064
|
+
{/* Empty State */}
|
|
1065
|
+
{currentLevel.entries.length === 0 && favorites.length === 0 && (
|
|
1066
|
+
<div className="flex flex-col items-center justify-center px-6 py-12 text-center">
|
|
1067
|
+
<div className="mb-4 p-3 rounded-full bg-gray-100">
|
|
1068
|
+
<FileQuestion className="w-8 h-8 text-gray-400" />
|
|
1069
|
+
</div>
|
|
1070
|
+
<h3 className="text-sm font-semibold text-gray-900 mb-2">Your catalog is empty</h3>
|
|
1071
|
+
<p className="text-xs text-gray-500 leading-relaxed max-w-[240px]">
|
|
1072
|
+
Navigation will appear here when you add resources to your EventCatalog.
|
|
1073
|
+
</p>
|
|
1074
|
+
</div>
|
|
1075
|
+
)}
|
|
1076
|
+
|
|
1077
|
+
{currentLevel.entries.length > 0 && renderEntries(currentLevel.entries)}
|
|
1064
1078
|
</nav>
|
|
1065
1079
|
</>
|
|
1066
1080
|
)}
|
|
@@ -15,6 +15,7 @@ import { buildContainerNode } from './builders/container';
|
|
|
15
15
|
import { buildFlowNode } from './builders/flow';
|
|
16
16
|
import config from '@config';
|
|
17
17
|
import { getDesigns } from '@utils/collections/designs';
|
|
18
|
+
import { getChannels } from '@utils/collections/channels';
|
|
18
19
|
|
|
19
20
|
export type { NavigationData, NavNode, ChildRef };
|
|
20
21
|
|
|
@@ -29,16 +30,18 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
29
30
|
return memoryCache;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const [domains, services, { events, commands, queries }, containers, flows, users, teams, designs] =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
const [domains, services, { events, commands, queries }, containers, flows, users, teams, designs, channels] =
|
|
34
|
+
await Promise.all([
|
|
35
|
+
getDomains({ getAllVersions: false, includeServicesInSubdomains: false }),
|
|
36
|
+
getServices({ getAllVersions: false }),
|
|
37
|
+
getMessages({ getAllVersions: false }),
|
|
38
|
+
getContainers({ getAllVersions: false }),
|
|
39
|
+
getFlows({ getAllVersions: false }),
|
|
40
|
+
getUsers(),
|
|
41
|
+
getTeams(),
|
|
42
|
+
getDesigns(),
|
|
43
|
+
getChannels({ getAllVersions: false }),
|
|
44
|
+
]);
|
|
42
45
|
|
|
43
46
|
// Calculate derived lists to avoid extra fetches
|
|
44
47
|
const allSubDomainIds = new Set(domains.flatMap((d) => (d.data.domains || []).map((sd: any) => sd.data.id)));
|
|
@@ -178,6 +181,30 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
178
181
|
{} as Record<string, NavNode>
|
|
179
182
|
);
|
|
180
183
|
|
|
184
|
+
const channelNodes = channels.reduce(
|
|
185
|
+
(acc, channel) => {
|
|
186
|
+
acc[`channel:${channel.data.id}:${channel.data.version}`] = {
|
|
187
|
+
type: 'item',
|
|
188
|
+
title: channel.data.name,
|
|
189
|
+
badge: 'Channel',
|
|
190
|
+
summary: channel.data.summary,
|
|
191
|
+
href: buildUrl(`/docs/${channel.collection}/${channel.data.id}/${channel.data.version}`),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
if (channel.data.latestVersion === channel.data.version) {
|
|
195
|
+
acc[`channel:${channel.data.id}`] = {
|
|
196
|
+
type: 'item',
|
|
197
|
+
title: channel.data.name,
|
|
198
|
+
badge: 'Channel',
|
|
199
|
+
summary: channel.data.summary,
|
|
200
|
+
href: buildUrl(`/docs/${channel.collection}/${channel.data.id}/${channel.data.version}`),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return acc;
|
|
204
|
+
},
|
|
205
|
+
{} as Record<string, NavNode>
|
|
206
|
+
);
|
|
207
|
+
|
|
181
208
|
const teamNodes = teams.reduce(
|
|
182
209
|
(acc, team) => {
|
|
183
210
|
acc[`team:${team.data.id}`] = {
|
|
@@ -273,6 +300,13 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
273
300
|
pages: users.map((user) => `user:${user.data.id}`),
|
|
274
301
|
});
|
|
275
302
|
|
|
303
|
+
const channelList = createLeaf(channels, {
|
|
304
|
+
type: 'item',
|
|
305
|
+
title: 'Channels',
|
|
306
|
+
icon: 'ArrowRightLeft',
|
|
307
|
+
pages: channels.map((channel) => `channel:${channel.data.id}:${channel.data.version}`),
|
|
308
|
+
});
|
|
309
|
+
|
|
276
310
|
const messagesChildren = ['list:events', 'list:commands', 'list:queries'].filter(
|
|
277
311
|
(key, index) => [eventsList, commandsList, queriesList][index] !== undefined
|
|
278
312
|
);
|
|
@@ -303,12 +337,22 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
303
337
|
'list:domains',
|
|
304
338
|
'list:services',
|
|
305
339
|
'list:messages',
|
|
340
|
+
'list:channels',
|
|
306
341
|
'list:flows',
|
|
307
342
|
'list:containers',
|
|
308
343
|
'list:designs',
|
|
309
344
|
'list:people',
|
|
310
345
|
];
|
|
311
|
-
const allChildrenNodes = [
|
|
346
|
+
const allChildrenNodes = [
|
|
347
|
+
domainsList,
|
|
348
|
+
servicesList,
|
|
349
|
+
messagesList,
|
|
350
|
+
channelList,
|
|
351
|
+
flowsList,
|
|
352
|
+
containersList,
|
|
353
|
+
designsList,
|
|
354
|
+
peopleList,
|
|
355
|
+
];
|
|
312
356
|
|
|
313
357
|
const validAllChildren = allChildrenKeys.filter((_, idx) => allChildrenNodes[idx] !== undefined);
|
|
314
358
|
|
|
@@ -334,6 +378,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
334
378
|
...(designsList ? { 'list:designs': designsList } : {}),
|
|
335
379
|
...(teamsList ? { 'list:teams': teamsList } : {}),
|
|
336
380
|
...(usersList ? { 'list:users': usersList } : {}),
|
|
381
|
+
...(channelList ? { 'list:channels': channelList as NavNode } : {}),
|
|
337
382
|
...(peopleList ? { 'list:people': peopleList as NavNode } : {}),
|
|
338
383
|
...(allList ? { 'list:all': allList as NavNode } : {}),
|
|
339
384
|
};
|
|
@@ -343,6 +388,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
343
388
|
...domainNodes,
|
|
344
389
|
...serviceNodes,
|
|
345
390
|
...messageNodes,
|
|
391
|
+
...channelNodes,
|
|
346
392
|
...containerNodes,
|
|
347
393
|
...flowNodes,
|
|
348
394
|
...userNodes,
|
|
@@ -174,7 +174,7 @@ const topTiles = [
|
|
|
174
174
|
description: 'Business domains defined',
|
|
175
175
|
href: buildUrl('/discover/domains'),
|
|
176
176
|
icon: RectangleGroupIcon,
|
|
177
|
-
bgColor: 'bg-yellow-
|
|
177
|
+
bgColor: 'bg-yellow-500',
|
|
178
178
|
textColor: 'text-yellow-600',
|
|
179
179
|
arrowColor: 'text-yellow-600',
|
|
180
180
|
},
|
|
@@ -184,7 +184,7 @@ const topTiles = [
|
|
|
184
184
|
description: 'Services documented in the catalog',
|
|
185
185
|
href: buildUrl('/discover/services'),
|
|
186
186
|
icon: ServerIcon,
|
|
187
|
-
bgColor: 'bg-pink-
|
|
187
|
+
bgColor: 'bg-pink-500',
|
|
188
188
|
textColor: 'text-pink-600',
|
|
189
189
|
arrowColor: 'text-pink-600',
|
|
190
190
|
},
|
|
@@ -194,7 +194,7 @@ const topTiles = [
|
|
|
194
194
|
description: 'Messages documented in the catalog',
|
|
195
195
|
href: buildUrl('/discover/events'),
|
|
196
196
|
icon: ChatBubbleLeftIcon,
|
|
197
|
-
bgColor: 'bg-blue-
|
|
197
|
+
bgColor: 'bg-blue-500',
|
|
198
198
|
textColor: 'text-blue-600',
|
|
199
199
|
arrowColor: 'text-blue-600',
|
|
200
200
|
},
|
|
@@ -204,7 +204,7 @@ const topTiles = [
|
|
|
204
204
|
description: 'Business flows documented',
|
|
205
205
|
href: buildUrl('/discover/flows'),
|
|
206
206
|
icon: Workflow,
|
|
207
|
-
bgColor: 'bg-purple-
|
|
207
|
+
bgColor: 'bg-purple-500',
|
|
208
208
|
textColor: 'text-purple-600',
|
|
209
209
|
arrowColor: 'text-purple-600',
|
|
210
210
|
},
|
|
@@ -347,9 +347,7 @@ const quickActions = [
|
|
|
347
347
|
<div class="absolute inset-0 bg-gradient-to-br from-gray-50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
|
348
348
|
|
|
349
349
|
{/* Colored left border accent */}
|
|
350
|
-
<div
|
|
351
|
-
class={`absolute left-0 top-0 bottom-0 w-1 ${tile.bgColor} opacity-0 group-hover:opacity-100 transition-opacity duration-300`}
|
|
352
|
-
/>
|
|
350
|
+
<div class={`absolute left-0 top-0 bottom-0 w-1 ${tile.bgColor} transition-opacity duration-300`} />
|
|
353
351
|
|
|
354
352
|
<div class="relative">
|
|
355
353
|
<div class="flex items-center justify-between mb-4">
|
|
@@ -167,7 +167,7 @@ const providerConfig = {
|
|
|
167
167
|
|
|
168
168
|
<div class="text-center">
|
|
169
169
|
<a
|
|
170
|
-
href="
|
|
170
|
+
href="https://www.eventcatalog.dev/docs/development/authentication/introduction"
|
|
171
171
|
class="inline-flex items-center px-6 py-3 border border-transparent text-sm font-medium rounded-lg text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 transition-colors duration-200"
|
|
172
172
|
>
|
|
173
173
|
Read Authentication Documentation
|
|
@@ -237,7 +237,7 @@ const providerConfig = {
|
|
|
237
237
|
|
|
238
238
|
<div class="text-center">
|
|
239
239
|
<a
|
|
240
|
-
href="
|
|
240
|
+
href="https://www.eventcatalog.dev/docs/development/authentication/introduction"
|
|
241
241
|
class="inline-flex items-center px-6 py-3 border border-transparent text-sm font-medium rounded-lg text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 transition-colors duration-200"
|
|
242
242
|
>
|
|
243
243
|
Read Authentication Documentation
|
|
@@ -30,7 +30,33 @@ export function remarkDirectives() {
|
|
|
30
30
|
class: `rounded-lg p-4 my-4 ${blockTypes[node.name as keyof typeof blockTypes] || ''}`,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Check if there's a custom title (label) provided via :::note[Custom Title]
|
|
34
|
+
// In remark-directive, the label is stored in node.children as a paragraph node
|
|
35
|
+
// with data.directiveLabel = true
|
|
36
|
+
let titleChildren;
|
|
37
|
+
let contentChildren;
|
|
38
|
+
|
|
39
|
+
const firstChild = node.children && node.children.length > 0 ? node.children[0] : null;
|
|
40
|
+
const hasCustomTitle = firstChild && firstChild.data?.directiveLabel === true;
|
|
41
|
+
|
|
42
|
+
if (hasCustomTitle && firstChild) {
|
|
43
|
+
// Custom title was provided in the label - it contains markdown parsed as inline content
|
|
44
|
+
titleChildren = firstChild.children || [
|
|
45
|
+
{ type: 'text', value: node.name.charAt(0).toUpperCase() + node.name.slice(1) },
|
|
46
|
+
];
|
|
47
|
+
contentChildren = node.children.slice(1);
|
|
48
|
+
} else {
|
|
49
|
+
// No custom title, use default based on directive name
|
|
50
|
+
titleChildren = [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
value: node.name.charAt(0).toUpperCase() + node.name.slice(1),
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
contentChildren = node.children;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Create header div that will contain icon and title
|
|
34
60
|
const headerNode = {
|
|
35
61
|
type: 'element',
|
|
36
62
|
data: {
|
|
@@ -70,7 +96,7 @@ export function remarkDirectives() {
|
|
|
70
96
|
},
|
|
71
97
|
],
|
|
72
98
|
},
|
|
73
|
-
//
|
|
99
|
+
// Title (with support for markdown)
|
|
74
100
|
{
|
|
75
101
|
type: 'element',
|
|
76
102
|
data: {
|
|
@@ -79,12 +105,7 @@ export function remarkDirectives() {
|
|
|
79
105
|
class: '',
|
|
80
106
|
},
|
|
81
107
|
},
|
|
82
|
-
children:
|
|
83
|
-
{
|
|
84
|
-
type: 'text',
|
|
85
|
-
value: node.name.charAt(0).toUpperCase() + node.name.slice(1),
|
|
86
|
-
},
|
|
87
|
-
],
|
|
108
|
+
children: titleChildren,
|
|
88
109
|
},
|
|
89
110
|
],
|
|
90
111
|
};
|
|
@@ -98,7 +119,7 @@ export function remarkDirectives() {
|
|
|
98
119
|
class: 'prose prose-md w-full !max-w-none ',
|
|
99
120
|
},
|
|
100
121
|
},
|
|
101
|
-
children:
|
|
122
|
+
children: contentChildren,
|
|
102
123
|
};
|
|
103
124
|
|
|
104
125
|
// Replace node's children with header and content
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/event-catalog/eventcatalog.git"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"version": "3.0.0-beta.
|
|
9
|
+
"version": "3.0.0-beta.11",
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
@@ -87,10 +87,11 @@
|
|
|
87
87
|
"nanostores": "^1.1.0",
|
|
88
88
|
"pagefind": "^1.3.0",
|
|
89
89
|
"pako": "^2.1.0",
|
|
90
|
+
"picocolors": "^1.1.1",
|
|
90
91
|
"react": "^18.3.1",
|
|
91
92
|
"react-dom": "^18.3.1",
|
|
92
93
|
"react-markdown": "^10.1.0",
|
|
93
|
-
"react-syntax-highlighter": "^
|
|
94
|
+
"react-syntax-highlighter": "^16.1.0",
|
|
94
95
|
"rehype-autolink-headings": "^7.1.0",
|
|
95
96
|
"rehype-expressive-code": "^0.41.3",
|
|
96
97
|
"rehype-slug": "^6.0.0",
|