@eventcatalog/core 5.0.0-beta.7 → 5.0.0-beta.8
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 +3 -1
- package/dist/analytics/log-build.js +4 -4
- package/dist/{chunk-BOKAG5XC.js → chunk-4W55ZOOS.js} +2 -2
- package/dist/{chunk-PIYJM642.js → chunk-AWOO63ME.js} +70 -44
- package/dist/{chunk-3VBB2ZMP.js → chunk-C6T2AAFR.js} +1 -1
- package/dist/{chunk-MDWB5HLO.js → chunk-ENAG6UH7.js} +5 -0
- package/dist/{chunk-S7ODHVII.js → chunk-PU25TOOJ.js} +1 -1
- package/dist/{chunk-FARBIZ7Y.js → chunk-RDIZJ7UT.js} +1 -1
- package/dist/{chunk-SJJKWZYZ.js → chunk-T4G3VVZ4.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +138 -90
- package/dist/eventcatalog.config.d.cts +47 -0
- package/dist/eventcatalog.config.d.ts +47 -0
- package/dist/eventcatalog.js +30 -11
- package/dist/features.cjs +8 -2
- package/dist/features.d.cts +3 -1
- package/dist/features.d.ts +3 -1
- package/dist/features.js +5 -3
- 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/dist/utils/license-status.cjs +70 -44
- package/dist/utils/license-status.d.cts +14 -5
- package/dist/utils/license-status.d.ts +14 -5
- package/dist/utils/license-status.js +1 -1
- package/eventcatalog/integrations/studio-server.mjs +15 -3
- package/eventcatalog/src/components/Header.astro +3 -3
- package/eventcatalog/src/features/integrations/eventcatalog-features.ts +10 -2
- package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +9 -1
- package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +9 -2
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +70 -69
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +69 -69
- package/eventcatalog/src/features/mcp/mcp-server.ts +5 -3
- package/eventcatalog/src/features/studio/agent-choreography.ts +8 -5
- package/eventcatalog/src/features/studio/canvas-actions.ts +148 -21
- package/eventcatalog/src/features/studio/canvas-doc.ts +138 -9
- package/eventcatalog/src/features/studio/canvas-preview.ts +53 -0
- package/eventcatalog/src/features/studio/catalog-resources.ts +5 -2
- package/eventcatalog/src/features/studio/clipboard.ts +97 -0
- package/eventcatalog/src/features/studio/components/CanvasControls.tsx +4 -3
- package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +39 -22
- package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +117 -0
- package/eventcatalog/src/features/studio/components/Comments.tsx +345 -30
- package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +140 -76
- package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +144 -0
- package/eventcatalog/src/features/studio/components/ContainerChooser.tsx +1 -1
- package/eventcatalog/src/features/studio/components/DragGhost.tsx +115 -0
- package/eventcatalog/src/features/studio/components/JoinForm.tsx +0 -24
- package/eventcatalog/src/features/studio/components/LeftPanel.tsx +131 -35
- package/eventcatalog/src/features/studio/components/Picture.tsx +21 -0
- package/eventcatalog/src/features/studio/components/Presence.tsx +2 -0
- package/eventcatalog/src/features/studio/components/PropertiesPanel.tsx +187 -50
- package/eventcatalog/src/features/studio/components/ShareDialog.tsx +260 -0
- package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +442 -40
- package/eventcatalog/src/features/studio/components/canvas-nodes.tsx +198 -22
- package/eventcatalog/src/features/studio/components/icons.ts +6 -1
- package/eventcatalog/src/features/studio/components/status.ts +30 -1
- package/eventcatalog/src/features/studio/edges.ts +2 -0
- package/eventcatalog/src/features/studio/grouping.ts +95 -2
- package/eventcatalog/src/features/studio/hooks/presence-store.ts +5 -1
- package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +13 -3
- package/eventcatalog/src/features/studio/hooks/use-comments.ts +8 -1
- package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +7 -2
- package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +174 -27
- package/eventcatalog/src/features/studio/identity.ts +35 -0
- package/eventcatalog/src/features/studio/layout.ts +7 -4
- package/eventcatalog/src/features/studio/levels.ts +138 -72
- package/eventcatalog/src/features/studio/node-types.ts +35 -3
- package/eventcatalog/src/features/studio/pages/[id].astro +7 -2
- package/eventcatalog/src/features/studio/pages/index.astro +555 -2
- package/eventcatalog/src/features/studio/pages/new.astro +24 -0
- package/eventcatalog/src/features/studio/pages/wake.ts +15 -0
- package/eventcatalog/src/features/studio/server/canvas-mcp.ts +48 -13
- package/eventcatalog/src/features/studio/server/runtime.ts +193 -14
- package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
- package/eventcatalog/src/features/studio/server/storage.ts +110 -0
- package/eventcatalog/src/features/studio/studio.css +184 -0
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +1 -1
- package/eventcatalog/src/utils/feature.ts +7 -3
- package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
- package/package.json +4 -4
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { Bot, Check, Copy, Globe, Link2, Mail, Pencil, X } from 'lucide-react';
|
|
3
|
+
import { usePeople, type Peer, type PresenceStore } from '../hooks/presence-store';
|
|
4
|
+
import Picture from './Picture';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sharing a canvas, like Miro's and Excalidraw's share dialogs: the link to invite people with (copied, emailed
|
|
8
|
+
* or as a ready-made message), who can join, who's on it now (jump to what they're looking at), your own name,
|
|
9
|
+
* and bringing an AI agent in.
|
|
10
|
+
*/
|
|
11
|
+
export default function ShareDialog({
|
|
12
|
+
url,
|
|
13
|
+
title,
|
|
14
|
+
keptInMemory = false,
|
|
15
|
+
presence,
|
|
16
|
+
clientId,
|
|
17
|
+
onRename,
|
|
18
|
+
onJumpTo,
|
|
19
|
+
onConnectAgent,
|
|
20
|
+
onClose,
|
|
21
|
+
}: {
|
|
22
|
+
url: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Canvases are lost when the server restarts */
|
|
25
|
+
keptInMemory?: boolean;
|
|
26
|
+
presence: PresenceStore | null;
|
|
27
|
+
clientId: number | null;
|
|
28
|
+
/** Not given when you're signed in */
|
|
29
|
+
onRename?: (name: string) => void;
|
|
30
|
+
onJumpTo: (peer: Peer) => void;
|
|
31
|
+
onConnectAgent: () => void;
|
|
32
|
+
onClose: () => void;
|
|
33
|
+
}) {
|
|
34
|
+
const people = usePeople(presence);
|
|
35
|
+
const you = people.find((peer) => peer.clientId === clientId);
|
|
36
|
+
const others = people.filter((peer) => peer.clientId !== clientId);
|
|
37
|
+
const name = title || 'this canvas';
|
|
38
|
+
const invite = `I'm designing ${title ? `"${title}"` : 'an architecture'} in EventCatalog Studio. Join me, we can work on it together live: ${url}`;
|
|
39
|
+
const email = `mailto:?subject=${encodeURIComponent(`Join me on ${name} in EventCatalog Studio`)}&body=${encodeURIComponent(invite)}`;
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const onKeyDown = (event: KeyboardEvent) => event.key === 'Escape' && onClose();
|
|
43
|
+
window.addEventListener('keydown', onKeyDown);
|
|
44
|
+
return () => window.removeEventListener('keydown', onKeyDown);
|
|
45
|
+
}, [onClose]);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4" onClick={onClose}>
|
|
49
|
+
<div
|
|
50
|
+
role="dialog"
|
|
51
|
+
aria-label="Share canvas"
|
|
52
|
+
onClick={(event) => event.stopPropagation()}
|
|
53
|
+
className="w-full max-w-md rounded-xl border shadow-2xl bg-[rgb(var(--ec-card-bg))] border-[rgb(var(--ec-page-border))] text-[rgb(var(--ec-page-text))]"
|
|
54
|
+
>
|
|
55
|
+
<header className="flex items-start justify-between gap-3 border-b px-5 py-4 border-[rgb(var(--ec-page-border))]">
|
|
56
|
+
<div className="min-w-0">
|
|
57
|
+
<h2 className="truncate text-base font-semibold">Share {title ? `"${title}"` : 'this canvas'}</h2>
|
|
58
|
+
<p className="mt-0.5 text-xs text-[rgb(var(--ec-page-text-muted))]">
|
|
59
|
+
Invite your team to design with you. Everyone with the link joins live: you'll see each other's pointers and changes
|
|
60
|
+
as they happen.
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
<button
|
|
64
|
+
onClick={onClose}
|
|
65
|
+
aria-label="Close"
|
|
66
|
+
className="shrink-0 rounded-md p-1 text-[rgb(var(--ec-icon-color))] hover:bg-[rgb(var(--ec-page-border)/0.5)] hover:text-[rgb(var(--ec-icon-hover))]"
|
|
67
|
+
>
|
|
68
|
+
<X size={16} />
|
|
69
|
+
</button>
|
|
70
|
+
</header>
|
|
71
|
+
|
|
72
|
+
<div className="space-y-5 px-5 py-4">
|
|
73
|
+
<section className="space-y-2">
|
|
74
|
+
<h3 className="text-xs font-semibold">Invite link</h3>
|
|
75
|
+
<div className="flex items-center gap-2">
|
|
76
|
+
<div className="flex min-w-0 flex-1 items-center gap-2 rounded-lg border px-2.5 py-1.5 bg-[rgb(var(--ec-input-bg))] border-[rgb(var(--ec-input-border))]">
|
|
77
|
+
<Link2 size={14} className="shrink-0 text-[rgb(var(--ec-icon-color))]" />
|
|
78
|
+
<input
|
|
79
|
+
readOnly
|
|
80
|
+
value={url}
|
|
81
|
+
onFocus={(event) => event.currentTarget.select()}
|
|
82
|
+
aria-label="Canvas link"
|
|
83
|
+
className="min-w-0 flex-1 bg-transparent text-xs text-[rgb(var(--ec-input-text))] focus:outline-none"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
<CopyButton text={url} label="Copy link" primary />
|
|
87
|
+
</div>
|
|
88
|
+
<div className="flex flex-wrap gap-2">
|
|
89
|
+
<a
|
|
90
|
+
href={email}
|
|
91
|
+
className="flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs font-medium border-[rgb(var(--ec-page-border))] hover:bg-[rgb(var(--ec-page-border)/0.4)]"
|
|
92
|
+
>
|
|
93
|
+
<Mail size={14} />
|
|
94
|
+
Email an invite
|
|
95
|
+
</a>
|
|
96
|
+
<CopyButton text={invite} label="Copy invite message" />
|
|
97
|
+
</div>
|
|
98
|
+
<p className="flex items-start gap-1.5 pt-1 text-[11px] text-[rgb(var(--ec-page-text-muted))]">
|
|
99
|
+
<Globe size={13} className="mt-px shrink-0" />
|
|
100
|
+
Anyone with the link can view and edit.
|
|
101
|
+
{keptInMemory && " Canvases are kept in this EventCatalog's memory, so they're lost when it restarts."}
|
|
102
|
+
</p>
|
|
103
|
+
</section>
|
|
104
|
+
|
|
105
|
+
<section className="space-y-2">
|
|
106
|
+
<h3 className="text-xs font-semibold">On this canvas now ({people.length})</h3>
|
|
107
|
+
<ul className="max-h-48 space-y-1 overflow-y-auto">
|
|
108
|
+
{you && <YouRow peer={you} onRename={onRename} />}
|
|
109
|
+
{others.map((peer) => (
|
|
110
|
+
<li key={peer.clientId} className="flex items-center gap-2.5 rounded-lg px-1.5 py-1">
|
|
111
|
+
<Avatar peer={peer} />
|
|
112
|
+
<div className="min-w-0 flex-1">
|
|
113
|
+
<p className="truncate text-xs font-medium">
|
|
114
|
+
{peer.name}
|
|
115
|
+
{peer.agent && (
|
|
116
|
+
<span className="ml-1.5 rounded px-1 py-px text-[10px] font-medium bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]">
|
|
117
|
+
AI agent
|
|
118
|
+
</span>
|
|
119
|
+
)}
|
|
120
|
+
</p>
|
|
121
|
+
{peer.activity && (
|
|
122
|
+
<p className="truncate text-[11px] text-[rgb(var(--ec-page-text-muted))]">{peer.activity}</p>
|
|
123
|
+
)}
|
|
124
|
+
</div>
|
|
125
|
+
{peer.viewport && (
|
|
126
|
+
<button
|
|
127
|
+
onClick={() => {
|
|
128
|
+
onJumpTo(peer);
|
|
129
|
+
onClose();
|
|
130
|
+
}}
|
|
131
|
+
className="shrink-0 rounded-md px-2 py-1 text-[11px] font-medium text-[rgb(var(--ec-accent))] hover:bg-[rgb(var(--ec-accent-subtle))]"
|
|
132
|
+
>
|
|
133
|
+
Go to
|
|
134
|
+
</button>
|
|
135
|
+
)}
|
|
136
|
+
</li>
|
|
137
|
+
))}
|
|
138
|
+
{others.length === 0 && (
|
|
139
|
+
<li className="px-1.5 py-1 text-xs text-[rgb(var(--ec-page-text-muted))]">
|
|
140
|
+
Just you so far. Send the link to bring people in.
|
|
141
|
+
</li>
|
|
142
|
+
)}
|
|
143
|
+
</ul>
|
|
144
|
+
</section>
|
|
145
|
+
|
|
146
|
+
<section className="flex items-center gap-3 rounded-lg border p-3 border-[rgb(var(--ec-page-border))]">
|
|
147
|
+
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]">
|
|
148
|
+
<Bot size={16} />
|
|
149
|
+
</span>
|
|
150
|
+
<div className="min-w-0 flex-1">
|
|
151
|
+
<p className="text-xs font-semibold">Bring in an AI agent</p>
|
|
152
|
+
<p className="text-[11px] text-[rgb(var(--ec-page-text-muted))]">
|
|
153
|
+
Claude, ChatGPT or another agent can join and design with you, with its own pointer.
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
<button
|
|
157
|
+
onClick={() => {
|
|
158
|
+
onClose();
|
|
159
|
+
onConnectAgent();
|
|
160
|
+
}}
|
|
161
|
+
className="shrink-0 rounded-lg border px-2.5 py-1.5 text-xs font-medium border-[rgb(var(--ec-page-border))] hover:bg-[rgb(var(--ec-page-border)/0.4)]"
|
|
162
|
+
>
|
|
163
|
+
Connect agent
|
|
164
|
+
</button>
|
|
165
|
+
</section>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const initials = (name: string) =>
|
|
173
|
+
name
|
|
174
|
+
.split(/\s+/)
|
|
175
|
+
.filter(Boolean)
|
|
176
|
+
.slice(0, 2)
|
|
177
|
+
.map((part) => part[0]!.toUpperCase())
|
|
178
|
+
.join('');
|
|
179
|
+
|
|
180
|
+
function Avatar({ peer }: { peer: Peer }) {
|
|
181
|
+
return (
|
|
182
|
+
<span
|
|
183
|
+
className="relative flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[11px] font-semibold text-white"
|
|
184
|
+
style={{ backgroundColor: peer.color }}
|
|
185
|
+
>
|
|
186
|
+
{peer.agent ? <Bot size={14} /> : initials(peer.name)}
|
|
187
|
+
<Picture src={peer.picture} />
|
|
188
|
+
</span>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** You, with your name (the name others see you by, which you can change unless you're signed in) */
|
|
193
|
+
function YouRow({ peer, onRename }: { peer: Peer; onRename?: (name: string) => void }) {
|
|
194
|
+
const [editing, setEditing] = useState(false);
|
|
195
|
+
const [draft, setDraft] = useState(peer.name);
|
|
196
|
+
const save = () => {
|
|
197
|
+
const trimmed = draft.trim();
|
|
198
|
+
if (trimmed && trimmed !== peer.name) onRename?.(trimmed);
|
|
199
|
+
setEditing(false);
|
|
200
|
+
};
|
|
201
|
+
return (
|
|
202
|
+
<li className="flex items-center gap-2.5 rounded-lg px-1.5 py-1">
|
|
203
|
+
<Avatar peer={peer} />
|
|
204
|
+
{editing ? (
|
|
205
|
+
<input
|
|
206
|
+
autoFocus
|
|
207
|
+
value={draft}
|
|
208
|
+
onChange={(event) => setDraft(event.target.value)}
|
|
209
|
+
onBlur={save}
|
|
210
|
+
onKeyDown={(event) => {
|
|
211
|
+
if (event.key === 'Enter') save();
|
|
212
|
+
if (event.key === 'Escape') {
|
|
213
|
+
event.stopPropagation();
|
|
214
|
+
setEditing(false);
|
|
215
|
+
}
|
|
216
|
+
}}
|
|
217
|
+
aria-label="Your name"
|
|
218
|
+
className="min-w-0 flex-1 rounded-md border px-2 py-1 text-xs bg-[rgb(var(--ec-input-bg))] border-[rgb(var(--ec-input-border))] text-[rgb(var(--ec-input-text))]"
|
|
219
|
+
/>
|
|
220
|
+
) : (
|
|
221
|
+
<p className="min-w-0 flex-1 truncate text-xs font-medium">
|
|
222
|
+
{peer.name} <span className="font-normal text-[rgb(var(--ec-page-text-muted))]">(you)</span>
|
|
223
|
+
</p>
|
|
224
|
+
)}
|
|
225
|
+
{!editing && onRename && (
|
|
226
|
+
<button
|
|
227
|
+
onClick={() => {
|
|
228
|
+
setDraft(peer.name);
|
|
229
|
+
setEditing(true);
|
|
230
|
+
}}
|
|
231
|
+
className="flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-page-border)/0.5)]"
|
|
232
|
+
>
|
|
233
|
+
<Pencil size={11} />
|
|
234
|
+
Rename
|
|
235
|
+
</button>
|
|
236
|
+
)}
|
|
237
|
+
</li>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function CopyButton({ text, label, primary = false }: { text: string; label: string; primary?: boolean }) {
|
|
242
|
+
const [copied, setCopied] = useState(false);
|
|
243
|
+
return (
|
|
244
|
+
<button
|
|
245
|
+
onClick={async () => {
|
|
246
|
+
await navigator.clipboard.writeText(text);
|
|
247
|
+
setCopied(true);
|
|
248
|
+
setTimeout(() => setCopied(false), 1500);
|
|
249
|
+
}}
|
|
250
|
+
className={`flex shrink-0 items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium ${
|
|
251
|
+
primary
|
|
252
|
+
? 'bg-[rgb(var(--ec-button-bg))] text-[rgb(var(--ec-button-text))] hover:bg-[rgb(var(--ec-button-bg-hover))]'
|
|
253
|
+
: 'border border-[rgb(var(--ec-page-border))] hover:bg-[rgb(var(--ec-page-border)/0.4)]'
|
|
254
|
+
}`}
|
|
255
|
+
>
|
|
256
|
+
{copied ? <Check size={14} /> : <Copy size={14} />}
|
|
257
|
+
{copied ? 'Copied' : label}
|
|
258
|
+
</button>
|
|
259
|
+
);
|
|
260
|
+
}
|