@eventcatalog/core 3.8.2 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-OCD75GFW.js → chunk-CZG5RQXY.js} +1 -1
- package/dist/{chunk-I7HRERRK.js → chunk-FM44RPBS.js} +1 -1
- package/dist/{chunk-MOBOWLEW.js → chunk-HJOMVPCL.js} +1 -1
- package/dist/{chunk-275AT7XV.js → chunk-OQYLI4YJ.js} +1 -1
- package/dist/{chunk-KBMXUUXX.js → chunk-UYBPI4UO.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +50 -0
- package/eventcatalog/src/components/MDX/Design/Design.astro +4 -1
- package/eventcatalog/src/components/MDX/EntityMap/EntityMap.astro +4 -0
- package/eventcatalog/src/components/MDX/Flow/Flow.astro +4 -1
- package/eventcatalog/src/components/MDX/NodeGraph/MermaidView.tsx +240 -0
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +4 -0
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +333 -189
- package/eventcatalog/src/components/MDX/NodeGraph/VisualizerDropdownContent.tsx +224 -0
- package/eventcatalog/src/content.config.ts +1 -1
- package/eventcatalog/src/enterprise/ai/chat-api.ts +23 -0
- package/eventcatalog/src/enterprise/tools/catalog-tools.ts +96 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version].mermaid.ts +128 -0
- package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +4 -0
- package/eventcatalog/src/utils/clipboard.ts +22 -0
- package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +299 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ConnectionLineType,
|
|
7
7
|
Controls,
|
|
8
8
|
Panel,
|
|
9
|
+
MiniMap,
|
|
9
10
|
ReactFlowProvider,
|
|
10
11
|
useNodesState,
|
|
11
12
|
useEdgesState,
|
|
@@ -17,7 +18,24 @@ import {
|
|
|
17
18
|
type NodeTypes,
|
|
18
19
|
} from '@xyflow/react';
|
|
19
20
|
import '@xyflow/react/dist/style.css';
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
ExternalLink,
|
|
23
|
+
HistoryIcon,
|
|
24
|
+
CheckIcon,
|
|
25
|
+
ClipboardIcon,
|
|
26
|
+
ChevronDownIcon,
|
|
27
|
+
MoreVertical,
|
|
28
|
+
Zap,
|
|
29
|
+
EyeOff,
|
|
30
|
+
Code,
|
|
31
|
+
Share2,
|
|
32
|
+
Search,
|
|
33
|
+
Grid3x3,
|
|
34
|
+
Maximize2,
|
|
35
|
+
Map,
|
|
36
|
+
Sparkles,
|
|
37
|
+
} from 'lucide-react';
|
|
38
|
+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
21
39
|
import { toPng } from 'html-to-image';
|
|
22
40
|
import { DocumentArrowDownIcon, PresentationChartLineIcon } from '@heroicons/react/24/outline';
|
|
23
41
|
// Nodes and edges
|
|
@@ -47,11 +65,15 @@ import { navigate } from 'astro:transitions/client';
|
|
|
47
65
|
import type { CollectionTypes } from '@types';
|
|
48
66
|
import { buildUrl } from '@utils/url-builder';
|
|
49
67
|
import ChannelNode from './Nodes/Channel';
|
|
50
|
-
import { CogIcon } from '@heroicons/react/20/solid';
|
|
51
68
|
import { useEventCatalogVisualiser } from 'src/hooks/eventcatalog-visualizer';
|
|
52
69
|
import VisualiserSearch, { type VisualiserSearchRef } from './VisualiserSearch';
|
|
53
70
|
import StepWalkthrough from './StepWalkthrough';
|
|
54
71
|
import StudioModal from './StudioModal';
|
|
72
|
+
import MermaidView from './MermaidView';
|
|
73
|
+
import VisualizerDropdownContent from './VisualizerDropdownContent';
|
|
74
|
+
import { convertToMermaid } from '@utils/node-graphs/export-mermaid';
|
|
75
|
+
import { copyToClipboard } from '@utils/clipboard';
|
|
76
|
+
|
|
55
77
|
interface Props {
|
|
56
78
|
nodes: any;
|
|
57
79
|
edges: any;
|
|
@@ -70,6 +92,7 @@ interface Props {
|
|
|
70
92
|
designId?: string;
|
|
71
93
|
isStudioModalOpen?: boolean;
|
|
72
94
|
setIsStudioModalOpen?: (isOpen: boolean) => void;
|
|
95
|
+
isChatEnabled?: boolean;
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
const getVisualiserUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>) => {
|
|
@@ -91,6 +114,7 @@ const NodeGraphBuilder = ({
|
|
|
91
114
|
zoomOnScroll = false,
|
|
92
115
|
isStudioModalOpen,
|
|
93
116
|
setIsStudioModalOpen = () => {},
|
|
117
|
+
isChatEnabled = false,
|
|
94
118
|
}: Props) => {
|
|
95
119
|
const nodeTypes = useMemo(
|
|
96
120
|
() =>
|
|
@@ -135,10 +159,14 @@ const NodeGraphBuilder = ({
|
|
|
135
159
|
);
|
|
136
160
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
|
137
161
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
138
|
-
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
139
162
|
const [animateMessages, setAnimateMessages] = useState(false);
|
|
140
163
|
const [activeStepIndex, setActiveStepIndex] = useState<number | null>(null);
|
|
141
164
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
165
|
+
const [mermaidCode, setMermaidCode] = useState('');
|
|
166
|
+
const [isShareModalOpen, setIsShareModalOpen] = useState(false);
|
|
167
|
+
const [shareUrlCopySuccess, setShareUrlCopySuccess] = useState(false);
|
|
168
|
+
const [isMermaidView, setIsMermaidView] = useState(false);
|
|
169
|
+
const [showMinimap, setShowMinimap] = useState(false);
|
|
142
170
|
// const [isStudioModalOpen, setIsStudioModalOpen] = useState(false);
|
|
143
171
|
|
|
144
172
|
// Check if there are channels to determine if we need the visualizer functionality
|
|
@@ -234,6 +262,11 @@ const NodeGraphBuilder = ({
|
|
|
234
262
|
localStorage.setItem('EventCatalog:animateMessages', JSON.stringify(!animateMessages));
|
|
235
263
|
};
|
|
236
264
|
|
|
265
|
+
// Handle fit to view
|
|
266
|
+
const handleFitView = useCallback(() => {
|
|
267
|
+
fitView({ duration: 400, padding: 0.2 });
|
|
268
|
+
}, [fitView]);
|
|
269
|
+
|
|
237
270
|
// animate messages, between views
|
|
238
271
|
// URL parameter takes priority over localStorage
|
|
239
272
|
useEffect(() => {
|
|
@@ -270,6 +303,17 @@ const NodeGraphBuilder = ({
|
|
|
270
303
|
}, 150);
|
|
271
304
|
}, []);
|
|
272
305
|
|
|
306
|
+
// Generate mermaid code from nodes and edges
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
try {
|
|
309
|
+
const code = convertToMermaid(nodes, edges, { includeStyles: true, direction: 'LR' });
|
|
310
|
+
setMermaidCode(code);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error('Error generating mermaid code:', error);
|
|
313
|
+
setMermaidCode('');
|
|
314
|
+
}
|
|
315
|
+
}, [nodes, edges]);
|
|
316
|
+
|
|
273
317
|
// Handle scroll wheel events to forward to page when no modifier keys are pressed
|
|
274
318
|
// Only when zoomOnScroll is disabled
|
|
275
319
|
// This is a fix for when we embed node graphs into pages, and users are scrolling the documentation pages
|
|
@@ -335,7 +379,6 @@ const NodeGraphBuilder = ({
|
|
|
335
379
|
}, [zoomOnScroll]);
|
|
336
380
|
|
|
337
381
|
const handlePaneClick = useCallback(() => {
|
|
338
|
-
setIsSettingsOpen(false);
|
|
339
382
|
searchRef.current?.hideSuggestions();
|
|
340
383
|
resetNodesAndEdges();
|
|
341
384
|
fitView({ duration: 800 });
|
|
@@ -364,6 +407,21 @@ const NodeGraphBuilder = ({
|
|
|
364
407
|
setIsStudioModalOpen(true);
|
|
365
408
|
};
|
|
366
409
|
|
|
410
|
+
const openChat = useCallback(() => {
|
|
411
|
+
window.dispatchEvent(new CustomEvent('eventcatalog:open-chat'));
|
|
412
|
+
}, []);
|
|
413
|
+
|
|
414
|
+
const handleCopyArchitectureCode = useCallback(async () => {
|
|
415
|
+
await copyToClipboard(mermaidCode);
|
|
416
|
+
}, [mermaidCode]);
|
|
417
|
+
|
|
418
|
+
const handleCopyShareUrl = useCallback(async () => {
|
|
419
|
+
const url = typeof window !== 'undefined' ? window.location.href : '';
|
|
420
|
+
await copyToClipboard(url);
|
|
421
|
+
setShareUrlCopySuccess(true);
|
|
422
|
+
setTimeout(() => setShareUrlCopySuccess(false), 2000);
|
|
423
|
+
}, []);
|
|
424
|
+
|
|
367
425
|
const toggleFullScreen = useCallback(() => {
|
|
368
426
|
if (!document.fullscreenElement) {
|
|
369
427
|
reactFlowWrapperRef.current?.requestFullscreen().catch((err) => {
|
|
@@ -396,8 +454,7 @@ const NodeGraphBuilder = ({
|
|
|
396
454
|
const height = imageHeight > nodesBounds.height ? imageHeight : nodesBounds.height;
|
|
397
455
|
const viewport = getViewportForBounds(nodesBounds, width, height, 0.5, 2, 0);
|
|
398
456
|
|
|
399
|
-
// Hide
|
|
400
|
-
setIsSettingsOpen(false);
|
|
457
|
+
// Hide controls during export
|
|
401
458
|
const controls = document.querySelector('.react-flow__controls') as HTMLElement;
|
|
402
459
|
if (controls) controls.style.display = 'none';
|
|
403
460
|
|
|
@@ -603,204 +660,288 @@ const NodeGraphBuilder = ({
|
|
|
603
660
|
const isFlowVisualization = edges.some((edge: Edge) => edge.type === 'flow-edge');
|
|
604
661
|
|
|
605
662
|
return (
|
|
606
|
-
<div ref={reactFlowWrapperRef} className="w-full h-full bg-gray-50">
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
nodes={nodes}
|
|
612
|
-
edges={edges}
|
|
613
|
-
fitView
|
|
614
|
-
onNodesChange={onNodesChange}
|
|
615
|
-
onEdgesChange={onEdgesChange}
|
|
616
|
-
connectionLineType={ConnectionLineType.SmoothStep}
|
|
617
|
-
nodeOrigin={[0.1, 0.1]}
|
|
618
|
-
onNodeClick={handleNodeClick}
|
|
619
|
-
onPaneClick={handlePaneClick}
|
|
620
|
-
zoomOnScroll={zoomOnScroll}
|
|
621
|
-
className="relative"
|
|
622
|
-
>
|
|
623
|
-
<Panel position="top-center" className="w-full pr-6 ">
|
|
624
|
-
<div className="flex space-x-2 justify-between items-center">
|
|
663
|
+
<div ref={reactFlowWrapperRef} className="w-full h-full bg-gray-50 flex flex-col">
|
|
664
|
+
{isMermaidView ? (
|
|
665
|
+
<>
|
|
666
|
+
{/* Menu Bar for Mermaid View */}
|
|
667
|
+
<div className="w-full pr-6 flex space-x-2 justify-between items-center bg-[rgb(var(--ec-page-bg))] border-b border-[rgb(var(--ec-page-border))] p-4">
|
|
625
668
|
<div className="flex space-x-2 ml-4">
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
<
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
669
|
+
{/* Settings Dropdown Menu */}
|
|
670
|
+
<DropdownMenu.Root>
|
|
671
|
+
<DropdownMenu.Trigger asChild>
|
|
672
|
+
<button
|
|
673
|
+
className="py-2.5 px-4 bg-[rgb(var(--ec-page-bg))] hover:bg-[rgb(var(--ec-accent-subtle)/0.4)] border border-[rgb(var(--ec-page-border))] rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[rgb(var(--ec-accent))] flex items-center gap-3 transition-all duration-200 hover:border-[rgb(var(--ec-accent)/0.3)] group whitespace-nowrap"
|
|
674
|
+
aria-label="Open menu"
|
|
675
|
+
>
|
|
676
|
+
{title && (
|
|
677
|
+
<span className="text-base font-medium text-[rgb(var(--ec-page-text))] leading-tight">{title}</span>
|
|
678
|
+
)}
|
|
679
|
+
<MoreVertical className="h-5 w-5 text-[rgb(var(--ec-page-text-muted))] flex-shrink-0 group-hover:text-[rgb(var(--ec-accent))] transition-colors duration-150" />
|
|
680
|
+
</button>
|
|
681
|
+
</DropdownMenu.Trigger>
|
|
682
|
+
<DropdownMenu.Portal>
|
|
683
|
+
<DropdownMenu.Content
|
|
684
|
+
className="min-w-56 bg-[rgb(var(--ec-page-bg))] border border-[rgb(var(--ec-page-border))] rounded-lg shadow-xl z-50 py-1.5 animate-in fade-in zoom-in-95 duration-200"
|
|
685
|
+
sideOffset={0}
|
|
686
|
+
align="end"
|
|
687
|
+
alignOffset={-180}
|
|
688
|
+
>
|
|
689
|
+
<DropdownMenu.Arrow className="fill-[rgb(var(--ec-page-bg))] stroke-[rgb(var(--ec-page-border))] stroke-1" />
|
|
690
|
+
<VisualizerDropdownContent
|
|
691
|
+
isMermaidView={isMermaidView}
|
|
692
|
+
setIsMermaidView={setIsMermaidView}
|
|
693
|
+
animateMessages={animateMessages}
|
|
694
|
+
toggleAnimateMessages={toggleAnimateMessages}
|
|
695
|
+
hideChannels={hideChannels}
|
|
696
|
+
toggleChannelsVisibility={toggleChannelsVisibility}
|
|
697
|
+
hasChannels={hasChannels}
|
|
698
|
+
showMinimap={showMinimap}
|
|
699
|
+
setShowMinimap={setShowMinimap}
|
|
700
|
+
handleFitView={handleFitView}
|
|
701
|
+
searchRef={searchRef}
|
|
702
|
+
isChatEnabled={isChatEnabled}
|
|
703
|
+
openChat={openChat}
|
|
704
|
+
handleCopyArchitectureCode={handleCopyArchitectureCode}
|
|
705
|
+
handleExportVisual={handleExportVisual}
|
|
706
|
+
setIsShareModalOpen={setIsShareModalOpen}
|
|
707
|
+
toggleFullScreen={toggleFullScreen}
|
|
708
|
+
openStudioModal={openStudioModal}
|
|
709
|
+
/>
|
|
710
|
+
</DropdownMenu.Content>
|
|
711
|
+
</DropdownMenu.Portal>
|
|
712
|
+
</DropdownMenu.Root>
|
|
660
713
|
</div>
|
|
661
714
|
{mode === 'full' && showSearch && (
|
|
662
|
-
<div className="flex justify-end
|
|
663
|
-
|
|
715
|
+
<div className="flex justify-end items-center gap-2">
|
|
716
|
+
{!isMermaidView && (
|
|
717
|
+
<div className="w-96">
|
|
718
|
+
<VisualiserSearch ref={searchRef} nodes={nodes} onNodeSelect={handleNodeSelect} onClear={handleSearchClear} />
|
|
719
|
+
</div>
|
|
720
|
+
)}
|
|
664
721
|
</div>
|
|
665
722
|
)}
|
|
666
723
|
</div>
|
|
667
|
-
{
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
{isSettingsOpen && (
|
|
696
|
-
<div className="absolute top-[68px] left-5 w-72 p-4 bg-white rounded-lg shadow-lg z-30 border border-gray-200">
|
|
697
|
-
<h3 className="text-lg font-semibold mb-4">Visualizer Settings</h3>
|
|
698
|
-
<div className="space-y-4 ">
|
|
699
|
-
<div>
|
|
700
|
-
<div className="flex items-center justify-between">
|
|
701
|
-
<label htmlFor="message-animation-toggle" className="text-sm font-medium text-gray-700">
|
|
702
|
-
Simulate Messages
|
|
703
|
-
</label>
|
|
704
|
-
<button
|
|
705
|
-
id="message-animation-toggle"
|
|
706
|
-
onClick={toggleAnimateMessages}
|
|
707
|
-
className={`${
|
|
708
|
-
animateMessages ? 'bg-[rgb(var(--ec-accent))]' : 'bg-gray-200'
|
|
709
|
-
} relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:ring-offset-2`}
|
|
710
|
-
>
|
|
711
|
-
<span
|
|
712
|
-
className={`${
|
|
713
|
-
animateMessages ? 'translate-x-6' : 'translate-x-1'
|
|
714
|
-
} inline-block h-4 w-4 transform rounded-full bg-white transition-transform`}
|
|
715
|
-
/>
|
|
716
|
-
</button>
|
|
717
|
-
</div>
|
|
718
|
-
<p className="text-[10px] text-gray-500">Animate events, queries and commands.</p>
|
|
719
|
-
</div>
|
|
720
|
-
{hasChannels && (
|
|
721
|
-
<div>
|
|
722
|
-
<div className="flex items-center justify-between">
|
|
723
|
-
<label htmlFor="hide-channels-toggle" className="text-sm font-medium text-gray-700">
|
|
724
|
-
Hide Channels
|
|
725
|
-
</label>
|
|
724
|
+
{/* Mermaid View */}
|
|
725
|
+
<div className="flex-1 overflow-hidden">
|
|
726
|
+
<MermaidView nodes={nodes} edges={edges} />
|
|
727
|
+
</div>
|
|
728
|
+
</>
|
|
729
|
+
) : (
|
|
730
|
+
<ReactFlow
|
|
731
|
+
nodeTypes={nodeTypes}
|
|
732
|
+
edgeTypes={edgeTypes}
|
|
733
|
+
minZoom={0.07}
|
|
734
|
+
nodes={nodes}
|
|
735
|
+
edges={edges}
|
|
736
|
+
fitView
|
|
737
|
+
onNodesChange={onNodesChange}
|
|
738
|
+
onEdgesChange={onEdgesChange}
|
|
739
|
+
connectionLineType={ConnectionLineType.SmoothStep}
|
|
740
|
+
nodeOrigin={[0.1, 0.1]}
|
|
741
|
+
onNodeClick={handleNodeClick}
|
|
742
|
+
onPaneClick={handlePaneClick}
|
|
743
|
+
zoomOnScroll={zoomOnScroll}
|
|
744
|
+
className="relative"
|
|
745
|
+
>
|
|
746
|
+
<Panel position="top-center" className="w-full pr-6 ">
|
|
747
|
+
<div className="flex space-x-2 justify-between items-center">
|
|
748
|
+
<div className="flex space-x-2 ml-4">
|
|
749
|
+
{/* Settings Dropdown Menu */}
|
|
750
|
+
<DropdownMenu.Root>
|
|
751
|
+
<DropdownMenu.Trigger asChild>
|
|
726
752
|
<button
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
className={`${
|
|
730
|
-
hideChannels ? 'bg-[rgb(var(--ec-accent))]' : 'bg-gray-200'
|
|
731
|
-
} relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:ring-offset-2`}
|
|
753
|
+
className="py-2.5 px-4 bg-[rgb(var(--ec-page-bg))] hover:bg-[rgb(var(--ec-accent-subtle)/0.4)] border border-[rgb(var(--ec-page-border))] rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[rgb(var(--ec-accent))] flex items-center gap-3 transition-all duration-200 hover:border-[rgb(var(--ec-accent)/0.3)] group whitespace-nowrap"
|
|
754
|
+
aria-label="Open menu"
|
|
732
755
|
>
|
|
733
|
-
|
|
734
|
-
className={
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
/>
|
|
756
|
+
{title && (
|
|
757
|
+
<span className="text-base font-medium text-[rgb(var(--ec-page-text))] leading-tight">{title}</span>
|
|
758
|
+
)}
|
|
759
|
+
<MoreVertical className="h-5 w-5 text-[rgb(var(--ec-page-text-muted))] flex-shrink-0 group-hover:text-[rgb(var(--ec-accent))] transition-colors duration-150" />
|
|
738
760
|
</button>
|
|
739
|
-
</
|
|
740
|
-
<
|
|
761
|
+
</DropdownMenu.Trigger>
|
|
762
|
+
<DropdownMenu.Portal>
|
|
763
|
+
<DropdownMenu.Content
|
|
764
|
+
className="min-w-56 bg-[rgb(var(--ec-page-bg))] border border-[rgb(var(--ec-page-border))] rounded-lg shadow-xl z-50 py-1.5 animate-in fade-in zoom-in-95 duration-200"
|
|
765
|
+
sideOffset={0}
|
|
766
|
+
align="end"
|
|
767
|
+
alignOffset={-180}
|
|
768
|
+
>
|
|
769
|
+
<DropdownMenu.Arrow className="fill-[rgb(var(--ec-page-bg))] stroke-[rgb(var(--ec-page-border))] stroke-1" />
|
|
770
|
+
<VisualizerDropdownContent
|
|
771
|
+
isMermaidView={isMermaidView}
|
|
772
|
+
setIsMermaidView={setIsMermaidView}
|
|
773
|
+
animateMessages={animateMessages}
|
|
774
|
+
toggleAnimateMessages={toggleAnimateMessages}
|
|
775
|
+
hideChannels={hideChannels}
|
|
776
|
+
toggleChannelsVisibility={toggleChannelsVisibility}
|
|
777
|
+
hasChannels={hasChannels}
|
|
778
|
+
showMinimap={showMinimap}
|
|
779
|
+
setShowMinimap={setShowMinimap}
|
|
780
|
+
handleFitView={handleFitView}
|
|
781
|
+
searchRef={searchRef}
|
|
782
|
+
isChatEnabled={isChatEnabled}
|
|
783
|
+
openChat={openChat}
|
|
784
|
+
handleCopyArchitectureCode={handleCopyArchitectureCode}
|
|
785
|
+
handleExportVisual={handleExportVisual}
|
|
786
|
+
setIsShareModalOpen={setIsShareModalOpen}
|
|
787
|
+
toggleFullScreen={toggleFullScreen}
|
|
788
|
+
openStudioModal={openStudioModal}
|
|
789
|
+
/>
|
|
790
|
+
</DropdownMenu.Content>
|
|
791
|
+
</DropdownMenu.Portal>
|
|
792
|
+
</DropdownMenu.Root>
|
|
793
|
+
</div>
|
|
794
|
+
{mode === 'full' && showSearch && (
|
|
795
|
+
<div className="flex justify-end items-center gap-2">
|
|
796
|
+
{!isMermaidView && (
|
|
797
|
+
<div className="w-96">
|
|
798
|
+
<VisualiserSearch
|
|
799
|
+
ref={searchRef}
|
|
800
|
+
nodes={nodes}
|
|
801
|
+
onNodeSelect={handleNodeSelect}
|
|
802
|
+
onClear={handleSearchClear}
|
|
803
|
+
/>
|
|
804
|
+
</div>
|
|
805
|
+
)}
|
|
741
806
|
</div>
|
|
742
807
|
)}
|
|
743
|
-
<div className="pt-4 border-t border-gray-200 space-y-2">
|
|
744
|
-
<button
|
|
745
|
-
onClick={openStudioModal}
|
|
746
|
-
className="w-full flex items-center justify-center space-x-2 px-4 py-2 bg-black hover:bg-gray-800 text-white text-sm font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:ring-offset-2 transition-colors"
|
|
747
|
-
>
|
|
748
|
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
749
|
-
<path
|
|
750
|
-
strokeLinecap="round"
|
|
751
|
-
strokeLinejoin="round"
|
|
752
|
-
strokeWidth="2"
|
|
753
|
-
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
754
|
-
/>
|
|
755
|
-
</svg>
|
|
756
|
-
<span>Open in EventCatalog Studio</span>
|
|
757
|
-
</button>
|
|
758
|
-
<button
|
|
759
|
-
onClick={handleExportVisual}
|
|
760
|
-
className="w-full flex items-center justify-center border border-gray-200 space-x-2 px-4 py-2 bg-white text-gray-800 text-sm font-medium rounded-md hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:ring-offset-2"
|
|
761
|
-
>
|
|
762
|
-
<DocumentArrowDownIcon className="w-4 h-4" />
|
|
763
|
-
<span>Export as png</span>
|
|
764
|
-
</button>
|
|
765
|
-
</div>
|
|
766
808
|
</div>
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
mode={mode}
|
|
779
|
-
/>
|
|
780
|
-
</Panel>
|
|
781
|
-
)}
|
|
782
|
-
{includeKey && (
|
|
783
|
-
<Panel position="bottom-right">
|
|
784
|
-
<div className=" bg-white font-light px-4 text-[12px] shadow-md py-1 rounded-md">
|
|
785
|
-
<ul className="m-0 p-0 ">
|
|
786
|
-
{Object.entries(legend).map(([key, { count, colorClass, groupId }]) => (
|
|
787
|
-
<li
|
|
788
|
-
key={key}
|
|
789
|
-
className="flex space-x-2 items-center text-[10px] cursor-pointer hover:text-[rgb(var(--ec-accent))] hover:underline"
|
|
790
|
-
onClick={() => handleLegendClick(key, groupId)}
|
|
809
|
+
{links.length > 0 && (
|
|
810
|
+
<div className="flex justify-end mt-3">
|
|
811
|
+
<div className="relative flex items-center -mt-1">
|
|
812
|
+
<span className="absolute left-2 pointer-events-none flex items-center h-full">
|
|
813
|
+
<HistoryIcon className="h-4 w-4 text-gray-600" />
|
|
814
|
+
</span>
|
|
815
|
+
<select
|
|
816
|
+
value={links.find((link) => window.location.href.includes(link.url))?.url || links[0].url}
|
|
817
|
+
onChange={(e) => navigate(e.target.value)}
|
|
818
|
+
className="appearance-none pl-7 pr-6 py-0 text-[14px] bg-white rounded-md border border-gray-200 hover:bg-gray-100/50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[rgb(var(--ec-accent))]"
|
|
819
|
+
style={{ minWidth: 120, height: '26px' }}
|
|
791
820
|
>
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
821
|
+
{links.map((link) => (
|
|
822
|
+
<option key={link.url} value={link.url}>
|
|
823
|
+
{link.label}
|
|
824
|
+
</option>
|
|
825
|
+
))}
|
|
826
|
+
</select>
|
|
827
|
+
<span className="absolute right-2 pointer-events-none">
|
|
828
|
+
<svg className="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
|
829
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
|
830
|
+
</svg>
|
|
831
|
+
</span>
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
)}
|
|
800
835
|
</Panel>
|
|
801
|
-
|
|
802
|
-
|
|
836
|
+
|
|
837
|
+
{includeBackground && <Background color="#bbb" gap={16} />}
|
|
838
|
+
{includeBackground && <Controls />}
|
|
839
|
+
{showMinimap && (
|
|
840
|
+
<MiniMap
|
|
841
|
+
nodeStrokeWidth={3}
|
|
842
|
+
zoomable
|
|
843
|
+
pannable
|
|
844
|
+
style={{
|
|
845
|
+
backgroundColor: 'rgb(var(--ec-page-bg))',
|
|
846
|
+
border: '1px solid rgb(var(--ec-page-border))',
|
|
847
|
+
borderRadius: '8px',
|
|
848
|
+
}}
|
|
849
|
+
/>
|
|
850
|
+
)}
|
|
851
|
+
{isFlowVisualization && showFlowWalkthrough && (
|
|
852
|
+
<Panel position="bottom-left">
|
|
853
|
+
<StepWalkthrough
|
|
854
|
+
nodes={nodes}
|
|
855
|
+
edges={edges}
|
|
856
|
+
isFlowVisualization={isFlowVisualization}
|
|
857
|
+
onStepChange={handleStepChange}
|
|
858
|
+
mode={mode}
|
|
859
|
+
/>
|
|
860
|
+
</Panel>
|
|
861
|
+
)}
|
|
862
|
+
{includeKey && (
|
|
863
|
+
<Panel position="bottom-right" style={showMinimap ? { marginRight: '230px' } : undefined}>
|
|
864
|
+
<div className=" bg-white font-light px-4 text-[12px] shadow-md py-1 rounded-md">
|
|
865
|
+
<ul className="m-0 p-0 ">
|
|
866
|
+
{Object.entries(legend).map(([key, { count, colorClass, groupId }]) => (
|
|
867
|
+
<li
|
|
868
|
+
key={key}
|
|
869
|
+
className="flex space-x-2 items-center text-[10px] cursor-pointer hover:text-[rgb(var(--ec-accent))] hover:underline"
|
|
870
|
+
onClick={() => handleLegendClick(key, groupId)}
|
|
871
|
+
>
|
|
872
|
+
<span className={`w-2 h-2 block ${colorClass}`} />
|
|
873
|
+
<span className="block capitalize">
|
|
874
|
+
{key} ({count})
|
|
875
|
+
</span>
|
|
876
|
+
</li>
|
|
877
|
+
))}
|
|
878
|
+
</ul>
|
|
879
|
+
</div>
|
|
880
|
+
</Panel>
|
|
881
|
+
)}
|
|
882
|
+
</ReactFlow>
|
|
883
|
+
)}
|
|
803
884
|
<StudioModal isOpen={isStudioModalOpen || false} onClose={() => setIsStudioModalOpen(false)} />
|
|
885
|
+
|
|
886
|
+
{/* Share Link Modal */}
|
|
887
|
+
{isShareModalOpen && (
|
|
888
|
+
<>
|
|
889
|
+
<div
|
|
890
|
+
className="fixed inset-0 bg-black/20 z-40"
|
|
891
|
+
onClick={() => setIsShareModalOpen(false)}
|
|
892
|
+
style={{ animation: 'fadeIn 150ms ease-out' }}
|
|
893
|
+
/>
|
|
894
|
+
<div
|
|
895
|
+
className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-[rgb(var(--ec-page-bg))] rounded-lg shadow-xl z-50 w-full max-w-md p-6 border border-[rgb(var(--ec-page-border))]"
|
|
896
|
+
style={{ animation: 'slideInCenter 250ms ease-out' }}
|
|
897
|
+
>
|
|
898
|
+
<style>{`
|
|
899
|
+
@keyframes fadeIn {
|
|
900
|
+
from { opacity: 0; }
|
|
901
|
+
to { opacity: 1; }
|
|
902
|
+
}
|
|
903
|
+
@keyframes slideInCenter {
|
|
904
|
+
from { opacity: 0; transform: translate(-50%, -48%); }
|
|
905
|
+
to { opacity: 1; transform: translate(-50%, -50%); }
|
|
906
|
+
}
|
|
907
|
+
`}</style>
|
|
908
|
+
|
|
909
|
+
<div className="flex justify-between items-start mb-4">
|
|
910
|
+
<h3 className="text-lg font-semibold text-[rgb(var(--ec-page-text))]">Share Link</h3>
|
|
911
|
+
<button
|
|
912
|
+
onClick={() => setIsShareModalOpen(false)}
|
|
913
|
+
className="text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
|
|
914
|
+
aria-label="Close modal"
|
|
915
|
+
>
|
|
916
|
+
<ExternalLink className="w-5 h-5 rotate-180" />
|
|
917
|
+
</button>
|
|
918
|
+
</div>
|
|
919
|
+
|
|
920
|
+
<p className="text-sm text-[rgb(var(--ec-page-text-muted))] mb-4">
|
|
921
|
+
Share this link with your team to let them view this visualization.
|
|
922
|
+
</p>
|
|
923
|
+
|
|
924
|
+
<div className="flex gap-2">
|
|
925
|
+
<input
|
|
926
|
+
type="text"
|
|
927
|
+
readOnly
|
|
928
|
+
value={typeof window !== 'undefined' ? window.location.href : ''}
|
|
929
|
+
className="flex-1 px-3 py-2.5 bg-[rgb(var(--ec-input-bg))] border border-[rgb(var(--ec-input-border))] rounded-md text-[rgb(var(--ec-input-text))] text-sm focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent))]"
|
|
930
|
+
/>
|
|
931
|
+
<button
|
|
932
|
+
onClick={handleCopyShareUrl}
|
|
933
|
+
className={`px-4 py-2.5 rounded-md font-medium transition-all duration-200 flex items-center gap-2 ${
|
|
934
|
+
shareUrlCopySuccess ? 'bg-green-500 text-white' : 'bg-[rgb(var(--ec-accent))] text-white hover:opacity-90'
|
|
935
|
+
}`}
|
|
936
|
+
aria-label={shareUrlCopySuccess ? 'Copied!' : 'Copy link'}
|
|
937
|
+
>
|
|
938
|
+
{shareUrlCopySuccess ? <CheckIcon className="w-4 h-4" /> : <ClipboardIcon className="w-4 h-4" />}
|
|
939
|
+
<span>{shareUrlCopySuccess ? 'Copied!' : 'Copy'}</span>
|
|
940
|
+
</button>
|
|
941
|
+
</div>
|
|
942
|
+
</div>
|
|
943
|
+
</>
|
|
944
|
+
)}
|
|
804
945
|
</div>
|
|
805
946
|
);
|
|
806
947
|
};
|
|
@@ -823,6 +964,7 @@ interface NodeGraphProps {
|
|
|
823
964
|
showSearch?: boolean;
|
|
824
965
|
zoomOnScroll?: boolean;
|
|
825
966
|
designId?: string;
|
|
967
|
+
isChatEnabled?: boolean;
|
|
826
968
|
}
|
|
827
969
|
|
|
828
970
|
const NodeGraph = ({
|
|
@@ -843,6 +985,7 @@ const NodeGraph = ({
|
|
|
843
985
|
showSearch = true,
|
|
844
986
|
zoomOnScroll = false,
|
|
845
987
|
designId,
|
|
988
|
+
isChatEnabled = false,
|
|
846
989
|
}: NodeGraphProps) => {
|
|
847
990
|
const [elem, setElem] = useState(null);
|
|
848
991
|
const [showFooter, setShowFooter] = useState(true);
|
|
@@ -888,6 +1031,7 @@ const NodeGraph = ({
|
|
|
888
1031
|
designId={designId || id}
|
|
889
1032
|
isStudioModalOpen={isStudioModalOpen}
|
|
890
1033
|
setIsStudioModalOpen={setIsStudioModalOpen}
|
|
1034
|
+
isChatEnabled={isChatEnabled}
|
|
891
1035
|
/>
|
|
892
1036
|
|
|
893
1037
|
{showFooter && (
|