@eventcatalog/core 2.61.3 → 2.61.4
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-LLJZTUOG.js → chunk-2MTIB6BG.js} +1 -1
- package/dist/{chunk-EICO7E3X.js → chunk-7I5Y4F5V.js} +1 -1
- package/dist/{chunk-BMTNHR7C.js → chunk-NVSSALX4.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 +3 -3
- package/eventcatalog/public/studio-bg.png +0 -0
- package/eventcatalog/src/components/Studio/StudioPageModal.tsx +150 -0
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +32 -2
- package/eventcatalog/src/pages/studio.astro +234 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-7I5Y4F5V.js";
|
|
4
|
+
import "../chunk-NVSSALX4.js";
|
|
5
|
+
import "../chunk-2MTIB6BG.js";
|
|
6
6
|
import "../chunk-UPONRQSN.js";
|
|
7
7
|
export {
|
|
8
8
|
log_build_default as default
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
package/dist/eventcatalog.cjs
CHANGED
package/dist/eventcatalog.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
} from "./chunk-PLNJC7NZ.js";
|
|
7
7
|
import {
|
|
8
8
|
log_build_default
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-7I5Y4F5V.js";
|
|
10
|
+
import "./chunk-NVSSALX4.js";
|
|
11
11
|
import {
|
|
12
12
|
catalogToAstro,
|
|
13
13
|
checkAndConvertMdToMdx
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import "./chunk-55D645EH.js";
|
|
16
16
|
import {
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-2MTIB6BG.js";
|
|
19
19
|
import {
|
|
20
20
|
getProjectOutDir,
|
|
21
21
|
isAuthEnabled,
|
|
Binary file
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import * as Dialog from '@radix-ui/react-dialog';
|
|
3
|
+
import { ExternalLinkIcon, CheckIcon, ClipboardIcon, Loader2 } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
const StudioPageModal: React.FC = () => {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const [copySuccess, setCopySuccess] = useState(false);
|
|
8
|
+
const [hasCopied, setHasCopied] = useState(false);
|
|
9
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
10
|
+
const [error, setError] = useState<string | null>(null);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const handleOpen = () => setIsOpen(true);
|
|
14
|
+
window.addEventListener('openStudioModal', handleOpen);
|
|
15
|
+
return () => window.removeEventListener('openStudioModal', handleOpen);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
const handleCopyResources = async () => {
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch('/api/catalog');
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error('Failed to fetch catalog data');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const catalogData = await response.json();
|
|
30
|
+
|
|
31
|
+
await navigator.clipboard.writeText(JSON.stringify(catalogData, null, 2));
|
|
32
|
+
setCopySuccess(true);
|
|
33
|
+
setHasCopied(true);
|
|
34
|
+
setTimeout(() => setCopySuccess(false), 2000);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error('Failed to copy resources:', err);
|
|
37
|
+
setError('Failed to copy resources. Please try again.');
|
|
38
|
+
} finally {
|
|
39
|
+
setIsLoading(false);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleOpenStudio = () => {
|
|
44
|
+
window.open(
|
|
45
|
+
'https://app.eventcatalog.studio/playground?import-resources=true&utm_source=eventcatalog&utm_medium=referral&utm_campaign=studio-page',
|
|
46
|
+
'_blank'
|
|
47
|
+
);
|
|
48
|
+
setIsOpen(false);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
|
53
|
+
<Dialog.Portal>
|
|
54
|
+
<Dialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-overlayShow z-50" />
|
|
55
|
+
<Dialog.Content className="fixed top-1/2 left-1/2 w-[90vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-xl focus:outline-none data-[state=open]:animate-contentShow z-[100]">
|
|
56
|
+
<Dialog.Title className="text-lg font-semibold text-gray-900 mb-3">Open EventCatalog Studio</Dialog.Title>
|
|
57
|
+
|
|
58
|
+
<Dialog.Description className="text-sm text-gray-600 mb-6">
|
|
59
|
+
Import your catalog resources into{' '}
|
|
60
|
+
<a
|
|
61
|
+
href="https://eventcatalog.studio"
|
|
62
|
+
className="text-gray-900 hover:text-gray-700 underline font-semibold"
|
|
63
|
+
target="_blank"
|
|
64
|
+
rel="noopener noreferrer"
|
|
65
|
+
>
|
|
66
|
+
EventCatalog Studio
|
|
67
|
+
</a>{' '}
|
|
68
|
+
to create architecture diagrams.
|
|
69
|
+
</Dialog.Description>
|
|
70
|
+
|
|
71
|
+
<div className="space-y-4">
|
|
72
|
+
{/* Step 1: Copy Resources */}
|
|
73
|
+
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
|
74
|
+
<h4 className="text-sm font-bold text-gray-900 mb-2">Step 1: Copy your resources</h4>
|
|
75
|
+
<p className="text-xs text-gray-600 mb-3">Copy your EventCatalog resources to your clipboard.</p>
|
|
76
|
+
|
|
77
|
+
<button
|
|
78
|
+
onClick={handleCopyResources}
|
|
79
|
+
disabled={isLoading}
|
|
80
|
+
className={`w-full flex items-center justify-center space-x-2 px-4 py-2 text-sm font-medium rounded-md border transition-colors ${
|
|
81
|
+
copySuccess
|
|
82
|
+
? 'bg-green-50 border-green-200 text-green-700'
|
|
83
|
+
: isLoading
|
|
84
|
+
? 'bg-gray-100 border-gray-300 text-gray-400 cursor-not-allowed'
|
|
85
|
+
: 'bg-white border-gray-300 text-gray-700 hover:bg-gray-50'
|
|
86
|
+
}`}
|
|
87
|
+
>
|
|
88
|
+
{isLoading ? (
|
|
89
|
+
<>
|
|
90
|
+
<Loader2 className="w-4 h-4 animate-spin" />
|
|
91
|
+
<span>Loading resources...</span>
|
|
92
|
+
</>
|
|
93
|
+
) : copySuccess ? (
|
|
94
|
+
<>
|
|
95
|
+
<CheckIcon className="w-4 h-4" />
|
|
96
|
+
<span>Copied!</span>
|
|
97
|
+
</>
|
|
98
|
+
) : (
|
|
99
|
+
<>
|
|
100
|
+
<ClipboardIcon className="w-4 h-4" />
|
|
101
|
+
<span>Copy resources to clipboard</span>
|
|
102
|
+
</>
|
|
103
|
+
)}
|
|
104
|
+
</button>
|
|
105
|
+
|
|
106
|
+
{error && <p className="text-xs text-red-600 mt-2">{error}</p>}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{/* Step 2: Open Studio */}
|
|
110
|
+
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
|
111
|
+
<h4 className="text-sm font-bold text-gray-900 mb-2">Step 2: Open EventCatalog Studio</h4>
|
|
112
|
+
<p className="text-xs text-gray-600 mb-3">
|
|
113
|
+
Go to EventCatalog Studio and paste your resources into the modal dialog.
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
<button
|
|
117
|
+
onClick={handleOpenStudio}
|
|
118
|
+
disabled={!hasCopied}
|
|
119
|
+
className={`w-full flex items-center justify-center space-x-2 px-4 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
120
|
+
hasCopied ? 'bg-gray-900 text-white hover:bg-gray-800' : 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
|
121
|
+
}`}
|
|
122
|
+
>
|
|
123
|
+
<ExternalLinkIcon className="w-4 h-4" />
|
|
124
|
+
<span>Open EventCatalog Studio</span>
|
|
125
|
+
</button>
|
|
126
|
+
|
|
127
|
+
<p className="text-[12px] text-gray-500 italic mt-4 mb-0">
|
|
128
|
+
All data is stored locally in your browser. Nothing is sent to external servers.
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div className="mt-6 flex justify-end">
|
|
134
|
+
<Dialog.Close asChild>
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors"
|
|
138
|
+
onClick={() => setIsOpen(false)}
|
|
139
|
+
>
|
|
140
|
+
Close
|
|
141
|
+
</button>
|
|
142
|
+
</Dialog.Close>
|
|
143
|
+
</div>
|
|
144
|
+
</Dialog.Content>
|
|
145
|
+
</Dialog.Portal>
|
|
146
|
+
</Dialog.Root>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default StudioPageModal;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
Sparkles,
|
|
17
17
|
Rocket,
|
|
18
18
|
FileText,
|
|
19
|
+
SquareDashedMousePointerIcon,
|
|
19
20
|
} from 'lucide-react';
|
|
20
21
|
import Header from '../components/Header.astro';
|
|
21
22
|
import SEO from '../components/Seo.astro';
|
|
@@ -35,6 +36,7 @@ import { isCollectionVisibleInCatalog } from '@eventcatalog';
|
|
|
35
36
|
import { buildUrl } from '@utils/url-builder';
|
|
36
37
|
import { getQueries } from '@utils/queries';
|
|
37
38
|
import { hasLandingPageForDocs } from '@utils/pages';
|
|
39
|
+
|
|
38
40
|
const events = await getEvents({ getAllVersions: false });
|
|
39
41
|
const commands = await getCommands({ getAllVersions: false });
|
|
40
42
|
const queries = await getQueries({ getAllVersions: false });
|
|
@@ -110,6 +112,7 @@ const navigationItems = [
|
|
|
110
112
|
current: currentPath.includes('/visualiser'),
|
|
111
113
|
sidebar: true,
|
|
112
114
|
},
|
|
115
|
+
|
|
113
116
|
{
|
|
114
117
|
id: '/discover',
|
|
115
118
|
label: 'Explore',
|
|
@@ -140,6 +143,15 @@ const navigationItems = [
|
|
|
140
143
|
return userSideBarOption ? userSideBarOption.visible : true;
|
|
141
144
|
});
|
|
142
145
|
|
|
146
|
+
const studioNavigationItem = {
|
|
147
|
+
id: '/studio',
|
|
148
|
+
label: 'EventCatalog Studio',
|
|
149
|
+
icon: SquareDashedMousePointerIcon,
|
|
150
|
+
href: buildUrl('/studio'),
|
|
151
|
+
current: currentPath.includes('/studio'),
|
|
152
|
+
sidebar: false,
|
|
153
|
+
};
|
|
154
|
+
|
|
143
155
|
const premiumFeatures = [
|
|
144
156
|
{
|
|
145
157
|
id: '/docs/custom',
|
|
@@ -163,7 +175,7 @@ const premiumFeatures = [
|
|
|
163
175
|
return userSideBarOption ? userSideBarOption.visible : true;
|
|
164
176
|
});
|
|
165
177
|
|
|
166
|
-
const currentNavigationItem = [...navigationItems, ...premiumFeatures].find((item) => item.current);
|
|
178
|
+
const currentNavigationItem = [...navigationItems, ...[studioNavigationItem], ...premiumFeatures].find((item) => item.current);
|
|
167
179
|
const { title, description } = Astro.props;
|
|
168
180
|
|
|
169
181
|
const showSideBarOnLoad = currentNavigationItem?.sidebar;
|
|
@@ -232,7 +244,7 @@ const canPageBeEmbedded = process.env.ENABLE_EMBED === 'true';
|
|
|
232
244
|
class="fixed flex flex-col items-center w-16 h-screen py-4 bg-white bg-gradient-to-b from-white to-gray-100 border-r border-gray-200 z-20 shadow-md justify-between"
|
|
233
245
|
>
|
|
234
246
|
<nav class="flex flex-col h-[calc(100vh-70px)] justify-between">
|
|
235
|
-
<div class="flex flex-col items-center flex-1 space-y-
|
|
247
|
+
<div class="flex flex-col items-center flex-1 space-y-6">
|
|
236
248
|
{
|
|
237
249
|
navigationItems
|
|
238
250
|
.filter((item) => !item.hidden)
|
|
@@ -261,6 +273,24 @@ const canPageBeEmbedded = process.env.ENABLE_EMBED === 'true';
|
|
|
261
273
|
|
|
262
274
|
<hr class="w-8 border-t border-gray-200" />
|
|
263
275
|
|
|
276
|
+
<a
|
|
277
|
+
id={studioNavigationItem.id}
|
|
278
|
+
data-role="nav-item"
|
|
279
|
+
href={studioNavigationItem.href}
|
|
280
|
+
class={`p-1.5 inline-block pt-1 pb-1 mt-0 mb-0 transition-colors duration-200 rounded-lg relative ${studioNavigationItem.current ? 'text-white bg-gradient-to-b from-purple-500 to-purple-700' : 'hover:bg-gradient-to-r hover:from-purple-500 hover:to-purple-700 hover:text-white text-gray-700'}`}
|
|
281
|
+
>
|
|
282
|
+
<div class="has-tooltip">
|
|
283
|
+
<span
|
|
284
|
+
class="tooltip rounded shadow-lg p-1 text-xs bg-gradient-to-l from-purple-500 to-purple-700 text-white ml-10"
|
|
285
|
+
>
|
|
286
|
+
{studioNavigationItem.label}
|
|
287
|
+
</span>
|
|
288
|
+
<studioNavigationItem.icon className="h-6 w-6" />
|
|
289
|
+
</div>
|
|
290
|
+
</a>
|
|
291
|
+
|
|
292
|
+
<hr class="w-8 border-t border-gray-200" />
|
|
293
|
+
|
|
264
294
|
{
|
|
265
295
|
premiumFeatures.map((item) => (
|
|
266
296
|
<a
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
---
|
|
2
|
+
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
3
|
+
import { getEvents } from '@utils/events';
|
|
4
|
+
import { getCommands } from '@utils/commands';
|
|
5
|
+
import { getServices } from '@utils/collections/services';
|
|
6
|
+
import { BoltIcon, ServerIcon, RectangleGroupIcon } from '@heroicons/react/24/outline';
|
|
7
|
+
import { SquareDashedMousePointerIcon, ArrowLeftRightIcon } from 'lucide-react';
|
|
8
|
+
import StudioPageModal from '@components/Studio/StudioPageModal';
|
|
9
|
+
import { getChannels } from '@utils/channels';
|
|
10
|
+
|
|
11
|
+
const [events, commands, services, channels] = await Promise.all([
|
|
12
|
+
getEvents().catch(() => []),
|
|
13
|
+
getCommands().catch(() => []),
|
|
14
|
+
getServices().catch(() => []),
|
|
15
|
+
getChannels().catch(() => []),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
// Get counts
|
|
19
|
+
const eventCount = events.length;
|
|
20
|
+
const serviceCount = services.length;
|
|
21
|
+
const commandCount = commands.length;
|
|
22
|
+
const channelCount = channels.length;
|
|
23
|
+
|
|
24
|
+
// Get a few sample resources to display
|
|
25
|
+
const sampleEvents = events.slice(0, 2);
|
|
26
|
+
const sampleServices = services.slice(0, 2);
|
|
27
|
+
const sampleChannels = channels.slice(0, 1);
|
|
28
|
+
|
|
29
|
+
// Determine which resources to show (fallback if some are missing)
|
|
30
|
+
const resourcesToShow = [
|
|
31
|
+
...sampleEvents.map((event, index) => ({ type: 'event', data: event, index })),
|
|
32
|
+
...sampleServices.map((service, index) => ({ type: 'service', data: service, index: index + 2 })),
|
|
33
|
+
...sampleChannels.map((channel, index) => ({ type: 'channel', data: channel, index: index + 4 })),
|
|
34
|
+
].slice(0, 5); // Max 5 resources
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<!doctype html>
|
|
38
|
+
<html lang="en">
|
|
39
|
+
<head>
|
|
40
|
+
<meta charset="UTF-8" />
|
|
41
|
+
<meta name="viewport" content="width=device-width" />
|
|
42
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
43
|
+
<meta name="generator" content={Astro.generator} />
|
|
44
|
+
<title>EventCatalog Studio</title>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<VerticalSideBarLayout title="EventCatalog Studio">
|
|
48
|
+
<div class="min-h-[calc(100vh-60px)] bg-white">
|
|
49
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
50
|
+
{/* Hero Section */}
|
|
51
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center mb-16">
|
|
52
|
+
<div>
|
|
53
|
+
<div class="inline-flex items-center px-4 py-2 rounded-full bg-gray-100 text-gray-900 font-medium text-sm mb-6">
|
|
54
|
+
<SquareDashedMousePointerIcon className="w-4 h-4 mr-2" />
|
|
55
|
+
EventCatalog Studio
|
|
56
|
+
</div>
|
|
57
|
+
<h1 class="text-4xl font-bold text-gray-900 tracking-tight mb-4">Turn your resources into designs</h1>
|
|
58
|
+
<p class="text-xl text-gray-600 mb-8">
|
|
59
|
+
Transform your documented messages, services, and domains into architecture diagrams. Drag, drop, and design with
|
|
60
|
+
what you already have.
|
|
61
|
+
</p>
|
|
62
|
+
<div class="flex flex-col sm:flex-row gap-4 mb-2">
|
|
63
|
+
<button
|
|
64
|
+
id="design-button"
|
|
65
|
+
class="inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-white bg-gray-900 hover:bg-gray-800 transition-colors duration-150"
|
|
66
|
+
>
|
|
67
|
+
<SquareDashedMousePointerIcon className="w-4 h-4 mr-2" />
|
|
68
|
+
Open EventCatalog Studio
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<p class="text-sm text-gray-500 italic mb-6">
|
|
73
|
+
{eventCount + serviceCount + channelCount + commandCount} resources ready to design with
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="relative">
|
|
78
|
+
<div class="relative">
|
|
79
|
+
{/* Animation container */}
|
|
80
|
+
<div class="relative h-[400px] flex items-center justify-center">
|
|
81
|
+
{/* Resource cards that animate in */}
|
|
82
|
+
<div class="absolute left-0 top-1/2 -translate-y-1/2 space-y-3">
|
|
83
|
+
{
|
|
84
|
+
resourcesToShow.map((resource) => {
|
|
85
|
+
if (resource.type === 'event') {
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
class="animate-float-in flex items-center gap-2 text-sm text-gray-700 px-4 py-3 bg-orange-50 rounded-lg border border-orange-200 shadow-sm"
|
|
89
|
+
style={`animation-delay: ${resource.index * 0.4}s;`}
|
|
90
|
+
>
|
|
91
|
+
<BoltIcon className="w-4 h-4 text-orange-600" />
|
|
92
|
+
{resource.data.data.name}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
} else if (resource.type === 'service') {
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
class="animate-float-in flex items-center gap-2 text-sm text-gray-700 px-4 py-3 bg-pink-50 rounded-lg border border-pink-200 shadow-sm"
|
|
99
|
+
style={`animation-delay: ${resource.index * 0.4}s;`}
|
|
100
|
+
>
|
|
101
|
+
<ServerIcon className="w-4 h-4 text-gray-600" />
|
|
102
|
+
{resource.data.data.name}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
} else if (resource.type === 'channel') {
|
|
106
|
+
return (
|
|
107
|
+
<div
|
|
108
|
+
class="animate-float-in flex items-center gap-2 text-sm text-gray-700 px-4 py-3 bg-gray-50 rounded-lg border border-gray-200 shadow-sm"
|
|
109
|
+
style={`animation-delay: ${resource.index * 0.4}s;`}
|
|
110
|
+
>
|
|
111
|
+
<ArrowLeftRightIcon className="w-4 h-4 text-gray-600" />
|
|
112
|
+
{resource.data.data.name}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{/* Arrow indicator */}
|
|
121
|
+
<div class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 animate-pulse">
|
|
122
|
+
<svg class="w-12 h-12 text-gray-400" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
123
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path>
|
|
124
|
+
</svg>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
{/* Design preview */}
|
|
128
|
+
<div class="absolute right-0 top-1/2 -translate-y-1/2 animate-fade-in-scale" style="animation-delay: 2s;">
|
|
129
|
+
<img
|
|
130
|
+
src="/studio-bg.png"
|
|
131
|
+
alt="Studio Design Preview"
|
|
132
|
+
class="rounded-xl shadow-xl border border-gray-200 w-64 h-auto"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
{/* Features Section */}
|
|
141
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
142
|
+
<div class="bg-white rounded-xl p-6 shadow-sm border border-gray-200">
|
|
143
|
+
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4">
|
|
144
|
+
<svg class="w-6 h-6 text-gray-900" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
145
|
+
<rect width="8" height="8" x="3" y="3" rx="2"></rect>
|
|
146
|
+
<path d="M7 11v4a2 2 0 0 0 2 2h4"></path>
|
|
147
|
+
<rect width="8" height="8" x="13" y="13" rx="2"></rect>
|
|
148
|
+
</svg>
|
|
149
|
+
</div>
|
|
150
|
+
<h3 class="text-lg font-semibold text-gray-900 mb-2">Real Resources</h3>
|
|
151
|
+
<p class="text-gray-600">
|
|
152
|
+
Drag and drop messages, services, and domains from your catalog. No more copying names or keeping things manually
|
|
153
|
+
in sync.
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="bg-white rounded-xl p-6 shadow-sm border border-gray-200">
|
|
158
|
+
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4">
|
|
159
|
+
<svg class="w-6 h-6 text-gray-900" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
160
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
|
|
161
|
+
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
|
162
|
+
<polyline points="7 3 7 8 15 8"></polyline>
|
|
163
|
+
</svg>
|
|
164
|
+
</div>
|
|
165
|
+
<h3 class="text-lg font-semibold text-gray-900 mb-2">Save & Version</h3>
|
|
166
|
+
<p class="text-gray-600">Save designs locally and store in Git. All designs and data is owned by you.</p>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div class="bg-white rounded-xl p-6 shadow-sm border border-gray-200">
|
|
170
|
+
<div class="w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center mb-4">
|
|
171
|
+
<svg class="w-6 h-6 text-gray-900" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
172
|
+
<polyline points="16 18 22 12 16 6"></polyline>
|
|
173
|
+
<polyline points="8 6 2 12 8 18"></polyline>
|
|
174
|
+
</svg>
|
|
175
|
+
</div>
|
|
176
|
+
<h3 class="text-lg font-semibold text-gray-900 mb-2">Embed Anywhere</h3>
|
|
177
|
+
<p class="text-gray-600">
|
|
178
|
+
Drop diagrams directly into documentation pages. One source of truth for your architecture.
|
|
179
|
+
</p>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{/* Studio Modal */}
|
|
186
|
+
<StudioPageModal client:only="react" />
|
|
187
|
+
</VerticalSideBarLayout>
|
|
188
|
+
|
|
189
|
+
<script>
|
|
190
|
+
// Handle opening the studio modal
|
|
191
|
+
const button = document.getElementById('design-button');
|
|
192
|
+
if (button) {
|
|
193
|
+
button.addEventListener('click', () => {
|
|
194
|
+
// Trigger the modal to open
|
|
195
|
+
window.dispatchEvent(new CustomEvent('openStudioModal'));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
</script>
|
|
199
|
+
|
|
200
|
+
<style>
|
|
201
|
+
@keyframes float-in {
|
|
202
|
+
0% {
|
|
203
|
+
opacity: 0;
|
|
204
|
+
transform: translateX(-30px);
|
|
205
|
+
}
|
|
206
|
+
100% {
|
|
207
|
+
opacity: 1;
|
|
208
|
+
transform: translateX(0);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@keyframes fade-in-scale {
|
|
213
|
+
0% {
|
|
214
|
+
opacity: 0;
|
|
215
|
+
transform: translateY(-50%) scale(0.9);
|
|
216
|
+
}
|
|
217
|
+
100% {
|
|
218
|
+
opacity: 1;
|
|
219
|
+
transform: translateY(-50%) scale(1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.animate-float-in {
|
|
224
|
+
animation: float-in 0.6s ease-out forwards;
|
|
225
|
+
opacity: 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.animate-fade-in-scale {
|
|
229
|
+
animation: fade-in-scale 0.8s ease-out forwards;
|
|
230
|
+
opacity: 0;
|
|
231
|
+
}
|
|
232
|
+
</style>
|
|
233
|
+
</body>
|
|
234
|
+
</html>
|