@eventcatalog/visualiser 5.0.0-beta.5 → 5.0.0-beta.7
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/chunk-Q3OM5AL5.mjs +1 -0
- package/dist/chunk-Q3OM5AL5.mjs.map +1 -0
- package/dist/chunk-R3QHQ7LF.mjs +427 -0
- package/dist/chunk-R3QHQ7LF.mjs.map +1 -0
- package/dist/chunk-Y2IJ5XR6.mjs +15705 -0
- package/dist/chunk-Y2IJ5XR6.mjs.map +1 -0
- package/dist/chunk-Z3G354GF.mjs +464 -0
- package/dist/chunk-Z3G354GF.mjs.map +1 -0
- package/dist/fonts/Excalifont-Regular.woff2 +0 -0
- package/dist/fonts/OFL.txt +93 -0
- package/dist/fonts/README.md +2 -0
- package/dist/index.js +749 -719
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +104 -16003
- package/dist/index.mjs.map +1 -1
- package/dist/layout.mjs +10 -374
- package/dist/layout.mjs.map +1 -1
- package/dist/model.d.mts +18 -0
- package/dist/model.mjs +51 -0
- package/dist/model.mjs.map +1 -0
- package/dist/schema-DKA0EQ_H.d.mts +484 -0
- package/dist/studio-fonts.css +10 -0
- package/dist/studio.css +509 -0
- package/dist/studio.d.mts +1312 -0
- package/dist/studio.mjs +6719 -0
- package/dist/studio.mjs.map +1 -0
- package/dist/styles-core.css +580 -0
- package/dist/styles.css +580 -0
- package/package.json +26 -8
|
@@ -0,0 +1,1312 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { XYPosition, Node, Edge, Connection, EdgeMarker, NodeTypes, FitViewOptions, NodeProps } from '@xyflow/react';
|
|
3
|
+
import { E as EdgeRoute, L as LayoutGraph, a as LayoutSize } from './elk-layout-BVUJjv6i.mjs';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import { RefObject, ReactNode, DragEvent, SVGProps } from 'react';
|
|
6
|
+
import { R as Resource, M as Model, L as Level$1, A as ArchitecturePayload } from './schema-DKA0EQ_H.mjs';
|
|
7
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
8
|
+
import { LucideIcon } from 'lucide-react';
|
|
9
|
+
import 'zod';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Everything a canvas holds, as operations on its Yjs document: nodes and edges, where things are on each level,
|
|
13
|
+
* comment threads, hotspots and the canvas's status. The same functions run in the browser (people, WebMCP) and on a
|
|
14
|
+
* server (MCP tools), so people and agents edit a canvas the same way.
|
|
15
|
+
*/
|
|
16
|
+
type Author = {
|
|
17
|
+
name: string;
|
|
18
|
+
color: string;
|
|
19
|
+
agent?: boolean;
|
|
20
|
+
/** A picture from their sign-in provider (SSO), shown over their initials */
|
|
21
|
+
picture?: string;
|
|
22
|
+
};
|
|
23
|
+
type Message = {
|
|
24
|
+
id: string;
|
|
25
|
+
text: string;
|
|
26
|
+
author: Author;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
};
|
|
29
|
+
/** Where a comment is pinned: a canvas position, or a node (and the offset from its top left) so it moves with it */
|
|
30
|
+
type CommentAnchor = {
|
|
31
|
+
position: XYPosition;
|
|
32
|
+
nodeId?: string;
|
|
33
|
+
offset?: XYPosition;
|
|
34
|
+
level?: 1 | 2;
|
|
35
|
+
};
|
|
36
|
+
type Thread = CommentAnchor & {
|
|
37
|
+
id: string;
|
|
38
|
+
author: Author;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
resolved: boolean;
|
|
41
|
+
messages: Message[];
|
|
42
|
+
};
|
|
43
|
+
type HotspotSeverity = "critical" | "warning" | "info";
|
|
44
|
+
/** A hotspot: flagged on a node (shown on it, wherever it goes), or at a place on the canvas */
|
|
45
|
+
type Hotspot = {
|
|
46
|
+
id: string;
|
|
47
|
+
severity: HotspotSeverity;
|
|
48
|
+
title: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
nodeId?: string;
|
|
51
|
+
/** Where it is when it's not on a node */
|
|
52
|
+
position: XYPosition;
|
|
53
|
+
/** The level that place is on (L1 or L2: they're laid out on their own); none, L3 */
|
|
54
|
+
level?: 1 | 2;
|
|
55
|
+
author: Author;
|
|
56
|
+
createdAt: number;
|
|
57
|
+
};
|
|
58
|
+
/** Where a canvas's design is: being worked on, up for review, agreed, or decided against */
|
|
59
|
+
declare const CANVAS_STATUSES: readonly ["draft", "proposed", "accepted", "rejected"];
|
|
60
|
+
type CanvasStatus = (typeof CANVAS_STATUSES)[number];
|
|
61
|
+
/** A canvas's status being changed: by whom, when, and why (e.g. "Agreed in the architecture review") */
|
|
62
|
+
type StatusChange = {
|
|
63
|
+
status: CanvasStatus;
|
|
64
|
+
by: Author;
|
|
65
|
+
at: number;
|
|
66
|
+
note?: string;
|
|
67
|
+
};
|
|
68
|
+
type CanvasMeta = {
|
|
69
|
+
title?: string;
|
|
70
|
+
/** Draft until someone changes it */
|
|
71
|
+
status?: CanvasStatus;
|
|
72
|
+
/** Every status change, oldest first (the last few hundred) */
|
|
73
|
+
statusHistory?: StatusChange[];
|
|
74
|
+
createdAt?: number;
|
|
75
|
+
createdBy?: string;
|
|
76
|
+
/** When the canvas was last laid out, so everyone's canvas glides the nodes into place */
|
|
77
|
+
layoutAt?: number;
|
|
78
|
+
/** Locked (looked at, not changed), by whom: for everyone on it, in a live session too */
|
|
79
|
+
lockedBy?: string | null;
|
|
80
|
+
/** L1 and L2 as the diagram the canvas was opened from showed them (see DiagramLevels) */
|
|
81
|
+
diagramLevels?: DiagramLevels;
|
|
82
|
+
};
|
|
83
|
+
/** A level of detail as a diagram showed it: its nodes (laid out) and edges, by canvas node ids where they're the same */
|
|
84
|
+
type DiagramLevel = {
|
|
85
|
+
nodes: Node[];
|
|
86
|
+
edges: Edge[];
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* L1 and L2 of the diagram a canvas was opened from (the visualiser's "Open in Studio"), shown as they were while the
|
|
90
|
+
* canvas's structure is what was opened (`structure`, from getStructureKey): moving nodes keeps them, changing what's
|
|
91
|
+
* on the canvas or how it's connected doesn't (the levels are then worked out from the canvas)
|
|
92
|
+
*/
|
|
93
|
+
type DiagramLevels = {
|
|
94
|
+
structure: string;
|
|
95
|
+
1?: DiagramLevel;
|
|
96
|
+
2?: DiagramLevel;
|
|
97
|
+
/** Levels the diagram doesn't have, and why (as the diagram says) */
|
|
98
|
+
unavailable?: {
|
|
99
|
+
1?: string;
|
|
100
|
+
2?: string;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
/** Who locked the canvas, if it's locked */
|
|
104
|
+
declare const getLock: (doc: Y.Doc) => string | null;
|
|
105
|
+
/** Locks the canvas for everyone (by a name), or unlocks it (null); nothing written if it's already so */
|
|
106
|
+
declare const setLock: (doc: Y.Doc, by: string | null) => void;
|
|
107
|
+
declare const shortId: () => string;
|
|
108
|
+
declare const getCanvasMaps: (doc: Y.Doc) => {
|
|
109
|
+
nodes: Y.Map<Node>;
|
|
110
|
+
edges: Y.Map<Edge>;
|
|
111
|
+
threads: Y.Map<Y.Map<unknown>>;
|
|
112
|
+
/** Each hotspot whole (edited by replacing it: they're short) */
|
|
113
|
+
hotspots: Y.Map<Hotspot>;
|
|
114
|
+
meta: Y.Map<unknown>;
|
|
115
|
+
/** Where things are on L1 and L2 (see LevelPlace), one entry per node and level (`1:<node id>`) */
|
|
116
|
+
levelLayouts: Y.Map<LevelPlace>;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Where a node is on L1 or L2, as people arranged it there (or as the diagram it was opened from had it): its
|
|
120
|
+
* top-left corner, relative to the container it's shown in on that level, and a container's size
|
|
121
|
+
*/
|
|
122
|
+
type LevelPlace = {
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
125
|
+
width?: number;
|
|
126
|
+
height?: number;
|
|
127
|
+
};
|
|
128
|
+
/** The places on a level, by node id */
|
|
129
|
+
declare const readLevelLayout: (doc: Y.Doc, level: 1 | 2) => Map<string, LevelPlace>;
|
|
130
|
+
/**
|
|
131
|
+
* Puts nodes where they go on a level (in one transaction). Places that haven't changed aren't written: everyone
|
|
132
|
+
* looking at the level works out the same places for what's new on it, and each write re-arranges the level for
|
|
133
|
+
* everyone else.
|
|
134
|
+
*/
|
|
135
|
+
declare const setLevelPlaces: (doc: Y.Doc, level: 1 | 2, places: ReadonlyMap<string, LevelPlace>) => void;
|
|
136
|
+
declare const toSharedNode: ({ id, type, position, data, width, height, parentId, zIndex, }: Node) => Node;
|
|
137
|
+
/** A new node of a type, centred on a canvas position */
|
|
138
|
+
declare const buildNode: (type: string, data: Record<string, unknown>, center: XYPosition) => Node;
|
|
139
|
+
declare const readMeta: (doc: Y.Doc) => CanvasMeta;
|
|
140
|
+
declare const setMeta: (doc: Y.Doc, meta: Partial<CanvasMeta>) => void;
|
|
141
|
+
declare const getCanvasStatus: (meta: CanvasMeta) => CanvasStatus;
|
|
142
|
+
/** Changes a canvas's status (and records who did it). Returns false if it already had that status. */
|
|
143
|
+
declare const setCanvasStatus: (doc: Y.Doc, status: CanvasStatus, by: Author, note?: string) => boolean;
|
|
144
|
+
/** Accepted and rejected canvases are decided: changing what's on them makes them a draft again */
|
|
145
|
+
declare const isDecided: (status: CanvasStatus) => status is "accepted" | "rejected";
|
|
146
|
+
/** A decided canvas whose nodes or edges were changed goes back to draft, recorded as changed by `by` */
|
|
147
|
+
declare const reopenIfDecided: (doc: Y.Doc, by: Author) => void;
|
|
148
|
+
/**
|
|
149
|
+
* Runs `fn` (which may change the canvas), and if it changed nodes or edges, sends a decided canvas back to draft.
|
|
150
|
+
* Comments don't count: reviewing an accepted design keeps it accepted.
|
|
151
|
+
*/
|
|
152
|
+
declare const reopeningIfEdited: <T>(doc: Y.Doc, by: Author, fn: () => T) => T;
|
|
153
|
+
/** Whether a transaction changed the canvas's nodes or edges (not just its comments or meta) */
|
|
154
|
+
declare const changesNodesOrEdges: (transaction: Y.Transaction) => boolean;
|
|
155
|
+
declare const addNodes: (doc: Y.Doc, nodes: Node[], edges?: Edge[]) => void;
|
|
156
|
+
/**
|
|
157
|
+
* Moves a node, and resizes it if a size is given, in one write. Nodes are only written when they change: every
|
|
158
|
+
* write is sent to everyone, re-renders the node for them, and is kept in the document (and the undo stack), even
|
|
159
|
+
* when it writes what was already there.
|
|
160
|
+
*/
|
|
161
|
+
declare const placeNode: (doc: Y.Doc, id: string, position: XYPosition, size?: {
|
|
162
|
+
width: number;
|
|
163
|
+
height: number;
|
|
164
|
+
}) => boolean;
|
|
165
|
+
declare const moveNode: (doc: Y.Doc, id: string, position: XYPosition) => boolean;
|
|
166
|
+
/** Resize a node: both sides, or just its width or height (e.g. text, whose height follows what's in it) */
|
|
167
|
+
declare const resizeNode: (doc: Y.Doc, id: string, size: {
|
|
168
|
+
width: number;
|
|
169
|
+
height: number;
|
|
170
|
+
}, attributes?: true | "width" | "height") => boolean;
|
|
171
|
+
/** Grow a container to fit what's in it (moving them clear of its header if needed), never shrinking it */
|
|
172
|
+
/**
|
|
173
|
+
* The sizes nodes are rendered at (in the browser), by id. The document doesn't have them, so without them
|
|
174
|
+
* containers are fitted to the size each type renders at at most.
|
|
175
|
+
*/
|
|
176
|
+
type RenderedSizes = ReadonlyMap<string, {
|
|
177
|
+
width: number;
|
|
178
|
+
height: number;
|
|
179
|
+
}>;
|
|
180
|
+
declare const fitGroupToChildren: (doc: Y.Doc, groupId: string, sizes?: RenderedSizes) => void;
|
|
181
|
+
/** The containers a node is in grow to fit it (innermost first), e.g. after it's added or resized */
|
|
182
|
+
declare const fitContainersAround: (doc: Y.Doc, nodeId: string, sizes?: RenderedSizes) => void;
|
|
183
|
+
/**
|
|
184
|
+
* A node moved into (or out of) a container: where it is on the canvas, or put at `at` (in its new parent), e.g. when
|
|
185
|
+
* it's moved on L1 or L2, where it was dropped isn't a place on the canvas
|
|
186
|
+
*/
|
|
187
|
+
declare const setNodeParent: (doc: Y.Doc, nodeId: string, parentId: string | undefined, sizes?: RenderedSizes, at?: XYPosition) => boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Nodes moved into (or out of) containers together, e.g. a selection dropped in one, as one change: each where it is
|
|
190
|
+
* on the canvas, or at its `at` (in its new parent). The containers they went in (and those around them) are fitted
|
|
191
|
+
* once they're all in, so one growing doesn't move where the next was put. Returns the ids of those moved.
|
|
192
|
+
*/
|
|
193
|
+
declare const setNodeParents: (doc: Y.Doc, moves: {
|
|
194
|
+
id: string;
|
|
195
|
+
parentId: string | undefined;
|
|
196
|
+
at?: XYPosition;
|
|
197
|
+
}[], sizes?: RenderedSizes) => string[];
|
|
198
|
+
declare const updateNodeData: (doc: Y.Doc, id: string, updater: (data: Record<string, unknown>) => Record<string, unknown>) => boolean;
|
|
199
|
+
|
|
200
|
+
/** An edge's route, if it has one */
|
|
201
|
+
declare const getEdgeRoute: (edge: Pick<Edge, "data">) => EdgeRoute | undefined;
|
|
202
|
+
/** Routes connections (by edge id), in one transaction. Connections not in it lose any route they had. */
|
|
203
|
+
declare const setEdgeRoutes: (doc: Y.Doc, routes: ReadonlyMap<string, EdgeRoute>) => void;
|
|
204
|
+
/**
|
|
205
|
+
* What's on a canvas and how it's connected, not where: its nodes (not notes), what they're in and called, and its
|
|
206
|
+
* connections. The same for the same canvas wherever it's worked out (the server, any browser).
|
|
207
|
+
*/
|
|
208
|
+
declare const getStructureKey: (nodes: Node[], edges: Edge[]) => string;
|
|
209
|
+
/** The diagram's L1 or L2 while the canvas still has the structure it was opened with (`structure`, its key now) */
|
|
210
|
+
declare const getDiagramLevel: (levels: DiagramLevels | undefined, level: 1 | 2, structure: string) => DiagramLevel | undefined;
|
|
211
|
+
/** Why the diagram doesn't have a level, while the canvas still has the structure it was opened with */
|
|
212
|
+
declare const getDiagramLevelUnavailable: (levels: DiagramLevels | undefined, level: 1 | 2, structure: string) => string | undefined;
|
|
213
|
+
/** What a new canvas starts with: its nodes, the connections between them (by node id) with their routes, and the
|
|
214
|
+
* levels of the diagram it's opened from */
|
|
215
|
+
type CanvasContent = {
|
|
216
|
+
nodes: Node[];
|
|
217
|
+
connections: {
|
|
218
|
+
from: string;
|
|
219
|
+
to: string;
|
|
220
|
+
label?: string;
|
|
221
|
+
route?: Pick<EdgeRoute, "points" | "label">;
|
|
222
|
+
}[];
|
|
223
|
+
levels?: Pick<DiagramLevels, 1 | 2 | "unavailable">;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Puts a new canvas's content on it (in one transaction), with its containers fitted to what's in them. Routes
|
|
227
|
+
* are from where the nodes are put.
|
|
228
|
+
*/
|
|
229
|
+
declare const addCanvasContent: (doc: Y.Doc, { nodes, connections, levels }: CanvasContent) => void;
|
|
230
|
+
declare const connectNodes: (doc: Y.Doc, connection: Pick<Connection, "source" | "target"> & Partial<Connection>, label?: string) => Edge | {
|
|
231
|
+
error: string;
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Moves a connection to new ends (dragged to another node), keeping it the same connection. Its label follows
|
|
235
|
+
* the new ends if it was EventCatalog's wording, and is kept if someone wrote it.
|
|
236
|
+
*/
|
|
237
|
+
declare const reconnectEdge: (doc: Y.Doc, edgeId: string, connection: Connection) => Edge | {
|
|
238
|
+
error: string;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* What a connection says: written by someone (kept as it's written, and in the model), or, with nothing written,
|
|
242
|
+
* EventCatalog's wording for what it joins again
|
|
243
|
+
*/
|
|
244
|
+
declare const setEdgeLabel: (doc: Y.Doc, edgeId: string, label: string | undefined) => boolean;
|
|
245
|
+
declare const deleteEdges: (doc: Y.Doc, ids: string[]) => void;
|
|
246
|
+
/** Delete nodes (containers with what's in them), the edges to them, and leave their comments where they were */
|
|
247
|
+
declare const deleteNodes: (doc: Y.Doc, ids: string[]) => void;
|
|
248
|
+
declare const readThreads: (doc: Y.Doc) => Thread[];
|
|
249
|
+
/** Threads as they were (from a saved canvas: their ids, authors and messages kept) */
|
|
250
|
+
declare const addThreads: (doc: Y.Doc, threads: Thread[]) => void;
|
|
251
|
+
declare const createThread: (doc: Y.Doc, anchor: CommentAnchor, text: string, author: Author) => string;
|
|
252
|
+
declare const replyToThread: (doc: Y.Doc, threadId: string, text: string, author: Author) => boolean;
|
|
253
|
+
declare const setThreadResolved: (doc: Y.Doc, threadId: string, resolved: boolean) => boolean;
|
|
254
|
+
/** Move a thread's pin, attaching it to a node (anchor.nodeId) or to the canvas */
|
|
255
|
+
declare const moveThread: (doc: Y.Doc, threadId: string, anchor: CommentAnchor) => boolean;
|
|
256
|
+
declare const deleteThread: (doc: Y.Doc, threadId: string) => boolean;
|
|
257
|
+
/** Deletes comment threads in one change (one undo brings them all back) */
|
|
258
|
+
declare const deleteThreads: (doc: Y.Doc, threadIds: string[]) => void;
|
|
259
|
+
/** Moves comment threads in one change, e.g. ones that moved with what was dragged */
|
|
260
|
+
declare const moveThreads: (doc: Y.Doc, moves: {
|
|
261
|
+
threadId: string;
|
|
262
|
+
anchor: CommentAnchor;
|
|
263
|
+
}[]) => void;
|
|
264
|
+
declare const readHotspots: (doc: Y.Doc) => Hotspot[];
|
|
265
|
+
/** Hotspots as they were (from a saved canvas, or the agent's diagram) */
|
|
266
|
+
declare const addHotspots: (doc: Y.Doc, hotspots: Hotspot[]) => void;
|
|
267
|
+
declare const createHotspot: (doc: Y.Doc, hotspot: Omit<Hotspot, "id" | "createdAt">) => string;
|
|
268
|
+
/** Change what a hotspot says (or where it is: on a node, or at a place) */
|
|
269
|
+
declare const updateHotspot: (doc: Y.Doc, id: string, fields: Partial<Omit<Hotspot, "id" | "createdAt" | "author">>) => boolean;
|
|
270
|
+
declare const deleteHotspot: (doc: Y.Doc, id: string) => boolean;
|
|
271
|
+
declare const readCanvas: (doc: Y.Doc) => {
|
|
272
|
+
meta: CanvasMeta;
|
|
273
|
+
nodes: Node[];
|
|
274
|
+
edges: Edge[];
|
|
275
|
+
threads: Thread[];
|
|
276
|
+
hotspots: Hotspot[];
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Copying and pasting nodes, within a canvas or between canvases (through the system clipboard). What's copied is
|
|
281
|
+
* the selected nodes, everything inside selected containers, and the connections between them. Comments aren't.
|
|
282
|
+
*/
|
|
283
|
+
/** The clipboard type Studio reads first (other apps get a plain text list of what was copied) */
|
|
284
|
+
declare const CLIPBOARD_TYPE = "application/x-eventcatalog-studio+json";
|
|
285
|
+
declare const MARKER = "eventcatalog-studio/nodes@1";
|
|
286
|
+
type ClipboardContent = {
|
|
287
|
+
marker: typeof MARKER;
|
|
288
|
+
nodes: Node[];
|
|
289
|
+
edges: Edge[];
|
|
290
|
+
};
|
|
291
|
+
/** What's copied when these nodes are selected, or null if there's nothing to copy */
|
|
292
|
+
declare const copyNodes: (nodes: Node[], edges: Edge[], selectedIds: string[]) => ClipboardContent | null;
|
|
293
|
+
/** Copied content read back from the clipboard, if it's Studio's */
|
|
294
|
+
declare const readClipboard: (text: string | undefined) => ClipboardContent | null;
|
|
295
|
+
/**
|
|
296
|
+
* Copies of the content to add to the canvas, with new ids: centred on a point (e.g. the pointer), or moved by an
|
|
297
|
+
* offset from where they were copied. Copies that land in a container on the canvas go inside it.
|
|
298
|
+
*/
|
|
299
|
+
declare const pasteNodes: (content: ClipboardContent, existing: Node[], place: {
|
|
300
|
+
at: XYPosition;
|
|
301
|
+
} | {
|
|
302
|
+
offset: XYPosition;
|
|
303
|
+
}) => {
|
|
304
|
+
nodes: Node[];
|
|
305
|
+
edges: Edge[];
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/** Connections on a canvas, labelled with EventCatalog's wording (e.g. "publishes event", "subscribed by") */
|
|
309
|
+
/** Node types that are messages, and their catalog collection */
|
|
310
|
+
declare const MESSAGE_COLLECTIONS: Record<string, string>;
|
|
311
|
+
declare const EDGE_COLOR = "#6b7280";
|
|
312
|
+
declare const EDGE_MARKER: EdgeMarker;
|
|
313
|
+
declare const getEdgeLabel: (sourceType?: string, targetType?: string) => string;
|
|
314
|
+
/** Message edges get the animated envelope edge from the visualiser, everything else a labelled smooth step edge */
|
|
315
|
+
declare const createEdge: (connection: Connection, sourceType?: string, targetType?: string) => Edge;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Where a connection joins an entity: a property's row (the visualiser's `<property>-target` on its left and
|
|
319
|
+
* `<property>-source` on its right), or the entity as a whole: its header (the visualiser's target there, and a
|
|
320
|
+
* source the canvas adds beside it).
|
|
321
|
+
*/
|
|
322
|
+
declare const ENTITY_SOURCE_HANDLE = "__entity-source";
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The design (nodes and edges) comes from the shared document; some state stays with each person: what they
|
|
326
|
+
* have selected, what they're dragging and resizing, and the sizes React Flow measured in their browser.
|
|
327
|
+
*/
|
|
328
|
+
declare const LOCAL_NODE_KEYS: readonly ["selected", "dragging", "resizing", "measured"];
|
|
329
|
+
declare const LOCAL_EDGE_KEYS: readonly ["selected"];
|
|
330
|
+
/** A shared item with the local-only state of the one already shown. Nothing else is kept from the local one. */
|
|
331
|
+
declare const withLocalState: <T extends object>(shared: T, local: T | undefined, localKeys: readonly (keyof T)[]) => T;
|
|
332
|
+
/**
|
|
333
|
+
* Applies changes to shared items (by id) to the items shown, keeping every unchanged item as the same object
|
|
334
|
+
* (so React Flow doesn't re-render it) and the order they were first seen in (new ones go at the end).
|
|
335
|
+
* `getShared` returns an item's shared value, or undefined if it was deleted. Returns `local` itself when
|
|
336
|
+
* nothing changed.
|
|
337
|
+
*/
|
|
338
|
+
declare const patchShared: <T extends {
|
|
339
|
+
id: string;
|
|
340
|
+
}>(local: T[], changedIds: Iterable<string>, getShared: (id: string) => T | undefined, localKeys: readonly (keyof T)[]) => T[];
|
|
341
|
+
/**
|
|
342
|
+
* Items worked out again (e.g. a level arranged again), keeping each one that hasn't changed as the object already
|
|
343
|
+
* shown (so React Flow doesn't re-render it), and the local-only state of the ones that have. Returns `previous`
|
|
344
|
+
* itself when nothing changed.
|
|
345
|
+
*/
|
|
346
|
+
declare const keepUnchanged: <T extends {
|
|
347
|
+
id: string;
|
|
348
|
+
}>(previous: T[], next: T[], localKeys: readonly (keyof T)[]) => T[];
|
|
349
|
+
/** Where nodes being dragged are, by id, until they're dropped (and saved) */
|
|
350
|
+
type Moves = ReadonlyMap<string, XYPosition>;
|
|
351
|
+
/**
|
|
352
|
+
* Nodes moved to the positions given (e.g. where someone else is dragging them), except ones being dragged
|
|
353
|
+
* here. Returns `nodes` itself when none move.
|
|
354
|
+
*/
|
|
355
|
+
declare const withPositions: (nodes: Node[], positions: Moves) => Node[];
|
|
356
|
+
/**
|
|
357
|
+
* After shared changes are applied (`next`, from `previous`): nodes being dragged here stay where they're being
|
|
358
|
+
* dragged (when they're dropped, the drop is saved and wins). Returns `next` itself when none are.
|
|
359
|
+
*/
|
|
360
|
+
declare const keepDragged: (next: Node[], previous: Node[]) => Node[];
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Containers: domains and systems shown as boxes that other nodes sit in.
|
|
364
|
+
* A node inside one has its parentId, and its position is relative to the container.
|
|
365
|
+
*/
|
|
366
|
+
/** Room around what's in a container, and for its header */
|
|
367
|
+
declare const GROUP_PADDING: {
|
|
368
|
+
top: number;
|
|
369
|
+
right: number;
|
|
370
|
+
bottom: number;
|
|
371
|
+
left: number;
|
|
372
|
+
};
|
|
373
|
+
type Rect = {
|
|
374
|
+
x: number;
|
|
375
|
+
y: number;
|
|
376
|
+
width: number;
|
|
377
|
+
height: number;
|
|
378
|
+
};
|
|
379
|
+
/** A node's size: as rendered (in the browser), as set (containers, notes), or what its type renders at */
|
|
380
|
+
declare const sizeOf: (node: Node) => {
|
|
381
|
+
width: number;
|
|
382
|
+
height: number;
|
|
383
|
+
};
|
|
384
|
+
/** Nodes are positioned relative to their container, so walk up the chain to get the canvas position */
|
|
385
|
+
declare const getAbsolutePosition: (node: Node, lookup: Map<string, Node>) => XYPosition;
|
|
386
|
+
declare const getAbsoluteRect: (node: Node, lookup: Map<string, Node>) => Rect;
|
|
387
|
+
/** React Flow needs containers before what's in them in the nodes array */
|
|
388
|
+
declare const sortByHierarchy: (nodes: Node[]) => Node[];
|
|
389
|
+
/** The innermost container under the centre of a node (never itself, or one of its own children) */
|
|
390
|
+
declare const findDropTarget: (nodeId: string, nodes: Node[]) => Node | undefined;
|
|
391
|
+
/**
|
|
392
|
+
* Where what's dragged goes when it's dropped: the containers it moves into (or out of, with no target), for the
|
|
393
|
+
* ones whose container changes. A selection follows the node held by the pointer: when that one goes into a
|
|
394
|
+
* container, or out of one, everything with it that can go there goes too (sticky notes and text never go in one).
|
|
395
|
+
* When it stays where it is, each one goes into the container under its middle, as one dragged alone does.
|
|
396
|
+
*/
|
|
397
|
+
declare const findDropTargets: (grabbedId: string, draggedIds: Iterable<string>, nodes: Node[]) => {
|
|
398
|
+
id: string;
|
|
399
|
+
target: Node | undefined;
|
|
400
|
+
}[];
|
|
401
|
+
/** The innermost container at a canvas point, for things dropped there (that can hold a kind of node, when given) */
|
|
402
|
+
declare const findGroupAtPoint: (point: XYPosition, nodes: Node[], type?: string) => Node | undefined;
|
|
403
|
+
/** The container something added goes in: the one asked for (by id), else the one it's dropped on */
|
|
404
|
+
declare const containerFor: (point: XYPosition, nodes: Node[], inside?: string, type?: string) => Node | undefined;
|
|
405
|
+
/** A node moved into (or out of, without a parentId) a container, staying in the same place on the canvas */
|
|
406
|
+
declare const withParent: (node: Node, parentId: string | undefined, nodes: Node[]) => Node;
|
|
407
|
+
/** The least room left between something dropped in a container and what's already in it */
|
|
408
|
+
declare const DROP_GAP = 32;
|
|
409
|
+
/**
|
|
410
|
+
* Where things dropped in containers go in them (relative to the container): where they were dropped when that's
|
|
411
|
+
* free and inside it, else the nearest free place, keeping inside the container (so it doesn't grow over what's around
|
|
412
|
+
* it) unless there's no room. What's dropped stays clear of what's in the container and of each other. `moves` are
|
|
413
|
+
* what `findDropTargets` returns; ones taken out of a container aren't placed (they stay where they were dropped).
|
|
414
|
+
* `nudged` when any had to move from where it was dropped (to show it gliding there).
|
|
415
|
+
*/
|
|
416
|
+
declare const placeDropped: (nodes: Node[], moves: {
|
|
417
|
+
id: string;
|
|
418
|
+
target: Node | undefined;
|
|
419
|
+
}[], gap?: number) => {
|
|
420
|
+
places: Map<string, XYPosition>;
|
|
421
|
+
nudged: boolean;
|
|
422
|
+
};
|
|
423
|
+
/** A canvas point, relative to a container */
|
|
424
|
+
declare const toRelativePosition: (point: XYPosition, parentId: string, nodes: Node[]) => XYPosition;
|
|
425
|
+
/** Nodes and everything inside them (deleted with their container) */
|
|
426
|
+
declare const withDescendants: (ids: string[], nodes: Node[]) => Set<string>;
|
|
427
|
+
/**
|
|
428
|
+
* The size a container needs to fit what's inside it, never smaller than it is, and how far its children
|
|
429
|
+
* must shift when some sit above or left of its content area (so the header stays clear).
|
|
430
|
+
*/
|
|
431
|
+
declare const fitGroup: (groupId: string, nodes: Node[]) => {
|
|
432
|
+
shift: {
|
|
433
|
+
x: number;
|
|
434
|
+
y: number;
|
|
435
|
+
};
|
|
436
|
+
width: number;
|
|
437
|
+
height: number;
|
|
438
|
+
} | undefined;
|
|
439
|
+
/** How far a dragged node's middle can go past a container's edge and still be kept in it (growing it) */
|
|
440
|
+
declare const KEEP_IN_CONTAINER_MARGIN = 160;
|
|
441
|
+
/** A node's place and size as shown while something's dragged */
|
|
442
|
+
type NodePreview = {
|
|
443
|
+
position: XYPosition;
|
|
444
|
+
width?: number;
|
|
445
|
+
height?: number;
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Containers growing to fit what's being dragged in them, on every side, as a preview of where things will be.
|
|
449
|
+
* `containerIds` are the containers around what's dragged, innermost first. `start` is the canvas when the drag
|
|
450
|
+
* started, and `now` the canvas as shown (with the dragged nodes where they are now). A dragged node is kept in a
|
|
451
|
+
* container while its middle is within `KEEP_IN_CONTAINER_MARGIN` of where the container was, and dropping it
|
|
452
|
+
* further away takes it out, so the container goes back to its size. A container growing up or left moves, and
|
|
453
|
+
* what's in it moves the other way, so nothing moves on the canvas.
|
|
454
|
+
*
|
|
455
|
+
* Returns, by id, where containers and the nodes in them that aren't dragged go (positions relative to their
|
|
456
|
+
* containers), and their sizes for containers.
|
|
457
|
+
*/
|
|
458
|
+
declare const previewContainerGrowth: (containerIds: string[], draggedIds: ReadonlySet<string>, start: ReadonlyMap<string, Node>, now: ReadonlyMap<string, Node>) => Map<string, NodePreview>;
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Lays a canvas out with the same ELK layout as EventCatalog's visualiser (left to right, edges routed
|
|
462
|
+
* around nodes, containers laid out with what's in them and sized to fit). Nodes are sized as rendered when
|
|
463
|
+
* they have been (in the browser), or by their type.
|
|
464
|
+
*/
|
|
465
|
+
declare const layoutGraph: (nodes: Node[], edges: Edge[]) => Promise<LayoutGraph>;
|
|
466
|
+
/** The layout sizes containers with style.width/height; the canvas sizes them with width/height */
|
|
467
|
+
declare const withGroupSizes: (nodes: Node[]) => Node[];
|
|
468
|
+
type LayoutResult = {
|
|
469
|
+
/** Where each node goes (relative to its container, if it's in one), and containers' new sizes */
|
|
470
|
+
nodes: Map<string, {
|
|
471
|
+
position: XYPosition;
|
|
472
|
+
size?: {
|
|
473
|
+
width: number;
|
|
474
|
+
height: number;
|
|
475
|
+
};
|
|
476
|
+
}>;
|
|
477
|
+
/** How each connection is drawn (around the nodes), by edge id */
|
|
478
|
+
routes: Map<string, EdgeRoute>;
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* The canvas laid out: new positions for its nodes, new sizes for containers, and routes for the connections
|
|
482
|
+
* (drawn like the visualiser's: around the nodes, following them when they're moved, and as a plain step once
|
|
483
|
+
* they've moved too far for it). Notes added on L1 or L2 aren't on this canvas, so they stay where they are.
|
|
484
|
+
*/
|
|
485
|
+
declare const getLayoutPositions: (nodes: Node[], edges: Edge[]) => Promise<LayoutResult>;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* L1 and L2 as people arrange them. A level's content comes from the canvas (see levels.ts), but where things are on
|
|
489
|
+
* it is its own: each level keeps a layout (a LevelPlace per node, in the canvas), so things stay where they're put.
|
|
490
|
+
* Nothing on a level moves when something's added: new things are placed next to what they connect to (like agents
|
|
491
|
+
* place what they add) and containers grow to fit.
|
|
492
|
+
*/
|
|
493
|
+
type Graph = {
|
|
494
|
+
nodes: Node[];
|
|
495
|
+
edges: Edge[];
|
|
496
|
+
};
|
|
497
|
+
/**
|
|
498
|
+
* The size something new of a type is shown at on L1 or L2: L1 shows domains and systems with nothing in them as
|
|
499
|
+
* cards, L2 as empty containers
|
|
500
|
+
*/
|
|
501
|
+
declare const getLevelNodeSize: (type: string | undefined, level: 1 | 2) => LayoutSize;
|
|
502
|
+
/** A level's nodes where they are, by id (not its notes, which are kept on the canvas): what its layout keeps */
|
|
503
|
+
declare const levelPlaces: (nodes: Node[]) => Map<string, LevelPlace>;
|
|
504
|
+
/**
|
|
505
|
+
* A level arranged: each node where the level's layout has it, else where the diagram the canvas was opened from had
|
|
506
|
+
* it (`fallback`), and anything with neither placed next to what it connects to. Containers grow to fit what's in
|
|
507
|
+
* them. `placed` is where things the layout didn't have were put, and containers that grew (to keep, so they stay
|
|
508
|
+
* there). Undefined when nothing on the level has a place yet: it's laid out from scratch then.
|
|
509
|
+
*/
|
|
510
|
+
declare const arrangeLevel: (graph: Graph, layout: ReadonlyMap<string, LevelPlace>, fallback?: ReadonlyMap<string, LevelPlace>) => {
|
|
511
|
+
graph: Graph;
|
|
512
|
+
placed: Map<string, LevelPlace>;
|
|
513
|
+
} | undefined;
|
|
514
|
+
/**
|
|
515
|
+
* A level's edges drawn along the routes the diagram the canvas was opened from had for the same connections (between
|
|
516
|
+
* the same two nodes), once the level's worked out from the canvas: routes follow their nodes when they move, so
|
|
517
|
+
* connections the diagram had stay drawn around what's in their way
|
|
518
|
+
*/
|
|
519
|
+
declare const withDiagramRoutes: (edges: Edge[], diagramEdges?: Edge[]) => Edge[];
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Levels of detail, the same as EventCatalog's diagrams:
|
|
523
|
+
* - L1: domains, systems and how they relate (services folded into the system or domain that contains them)
|
|
524
|
+
* - L2: services and data stores, with messages, channels and agents hidden (the nodes either side connected directly)
|
|
525
|
+
* - L3: everything, including messages and channels (the canvas people edit)
|
|
526
|
+
*
|
|
527
|
+
* What a domain or system contains is what sits inside it (as a container), or what it's connected to with
|
|
528
|
+
* "contains" (as a card, which catalog domains and systems get when their services are on the canvas).
|
|
529
|
+
*/
|
|
530
|
+
type Level = 1 | 2 | 3;
|
|
531
|
+
declare const LEVELS: {
|
|
532
|
+
level: Level;
|
|
533
|
+
description: string;
|
|
534
|
+
}[];
|
|
535
|
+
/**
|
|
536
|
+
* The graph for a level (not laid out: positions come from the layout). Notes aren't in it: the ones added on the
|
|
537
|
+
* level are shown where they were put, over its layout.
|
|
538
|
+
*/
|
|
539
|
+
declare const getLevelGraph: (nodes: Node[], edges: Edge[], level: Level) => {
|
|
540
|
+
edges: Edge[];
|
|
541
|
+
nodes: Node[];
|
|
542
|
+
};
|
|
543
|
+
/** Why a level can't be shown for this canvas, if it can't */
|
|
544
|
+
declare const getLevelUnavailableReason: (nodes: Node[], level: Level) => string | undefined;
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* What the others on a live canvas are doing that the canvas shows as they do it, whatever carries it between them
|
|
548
|
+
* (Yjs awareness, or an encrypted relay): what they're dragging, and the note or text they're changing. What they end
|
|
549
|
+
* up with arrives in the document.
|
|
550
|
+
*/
|
|
551
|
+
/**
|
|
552
|
+
* A node someone's dragging, where it is now (relative to its container, as nodes are), and on which level (L1 and L2
|
|
553
|
+
* are arranged on their own; none is the canvas itself, L3). A comment pin or hotspot marker has its `kind` (and its
|
|
554
|
+
* thread's or hotspot's id), where it is on the canvas.
|
|
555
|
+
*/
|
|
556
|
+
type Moving = {
|
|
557
|
+
id: string;
|
|
558
|
+
x: number;
|
|
559
|
+
y: number;
|
|
560
|
+
level?: 1 | 2;
|
|
561
|
+
kind?: "comment" | "hotspot";
|
|
562
|
+
};
|
|
563
|
+
/** A note or text being changed: what's written as they type, a text's size as they scale it, its angle as they turn it */
|
|
564
|
+
type Editing = {
|
|
565
|
+
id: string;
|
|
566
|
+
text?: string;
|
|
567
|
+
fontSize?: number;
|
|
568
|
+
angle?: number;
|
|
569
|
+
};
|
|
570
|
+
/** Someone on the canvas, as far as what they move and change goes */
|
|
571
|
+
type LivePeer = {
|
|
572
|
+
/** What they're dragging, while they drag (the drop itself arrives in the document) */
|
|
573
|
+
moving?: Moving[] | null;
|
|
574
|
+
editing?: Editing | null;
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* Everyone's presence, for what draws it: read now, and told on every change, outside React (it changes many times a
|
|
578
|
+
* second; in React state it would re-render the whole canvas on every move)
|
|
579
|
+
*/
|
|
580
|
+
type PresenceSource<P extends LivePeer = LivePeer> = {
|
|
581
|
+
current: () => ReadonlyMap<string, P>;
|
|
582
|
+
subscribe: (listener: (peers: ReadonlyMap<string, P>) => void) => () => void;
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Comment pins and hotspot markers being dragged in a live room: what I drag goes out with my presence (`send`, and
|
|
587
|
+
* `null` once it's dropped: the drop goes in the document), and what the others drag comes from theirs (`source`).
|
|
588
|
+
* On the level shown, as their places are.
|
|
589
|
+
*/
|
|
590
|
+
type LiveMoves = {
|
|
591
|
+
send: (moving: Moving[] | null) => void;
|
|
592
|
+
source: PresenceSource;
|
|
593
|
+
level: 1 | 2 | 3;
|
|
594
|
+
};
|
|
595
|
+
/** Sends where something of this kind is now, as it's dragged (or `null`, dropped) */
|
|
596
|
+
declare const sendMove: (live: LiveMoves | undefined, kind: NonNullable<Moving["kind"]>, id: string, at: XYPosition | null) => void | undefined;
|
|
597
|
+
/**
|
|
598
|
+
* Where the others are dragging things of this kind on the level shown (by id), read from their presence. Changes
|
|
599
|
+
* (re-rendering only the layer using it) only while someone drags one.
|
|
600
|
+
*/
|
|
601
|
+
declare function useRemoteMoves(live: LiveMoves | undefined, kind: NonNullable<Moving["kind"]>): ReadonlyMap<string, XYPosition>;
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* The kinds of node people (and agents) can put on a canvas, and what's in their data. Shared by the browser and
|
|
605
|
+
* the server, so it has no UI in it: the icons for each type are in components/icons.ts.
|
|
606
|
+
*/
|
|
607
|
+
type NodeCategory = "boundaries" | "people" | "messages" | "architecture" | "data" | "other";
|
|
608
|
+
declare const NODE_CATEGORIES: {
|
|
609
|
+
id: NodeCategory;
|
|
610
|
+
label: string;
|
|
611
|
+
}[];
|
|
612
|
+
/** The catalog collections whose resources can go on a canvas */
|
|
613
|
+
declare const CATALOG_COLLECTIONS: readonly ["domains", "systems", "services", "events", "commands", "queries", "channels", "containers"];
|
|
614
|
+
type CatalogCollection = (typeof CATALOG_COLLECTIONS)[number];
|
|
615
|
+
declare const isCatalogCollection: (collection: string) => collection is CatalogCollection;
|
|
616
|
+
/** Domains and systems as containers: boxes other nodes sit in */
|
|
617
|
+
declare const GROUP_TYPES: {
|
|
618
|
+
readonly domain: "domain-group";
|
|
619
|
+
readonly system: "system-group";
|
|
620
|
+
};
|
|
621
|
+
declare const isGroupType: (type?: string) => type is "domain-group" | "system-group";
|
|
622
|
+
type NodeDefinition = {
|
|
623
|
+
/** React Flow node type, matches the visualiser node component keys */
|
|
624
|
+
type: string;
|
|
625
|
+
/** Its key in the components list when several share a node type (the kinds of data store); else its type */
|
|
626
|
+
id?: string;
|
|
627
|
+
label: string;
|
|
628
|
+
/** What it is, in a few words (beside its name in the components list) */
|
|
629
|
+
summary?: string;
|
|
630
|
+
category: NodeCategory;
|
|
631
|
+
/** Not in the components list (still drawn, for canvases that have them) */
|
|
632
|
+
unlisted?: boolean;
|
|
633
|
+
/** Where the resource lives inside node.data (e.g. `service` for data.service). Undefined = data root */
|
|
634
|
+
resourceKey?: string;
|
|
635
|
+
/** Nodes that are sized on the canvas (containers, notes, text) start at this size */
|
|
636
|
+
defaultSize?: {
|
|
637
|
+
width: number;
|
|
638
|
+
height: number;
|
|
639
|
+
};
|
|
640
|
+
/** Only its width is set: its height follows what's in it (notes) */
|
|
641
|
+
autoHeight?: boolean;
|
|
642
|
+
/** Neither is set: it's as big as what's in it (text, as Excalidraw's; defaultSize is only where it's put) */
|
|
643
|
+
autoSize?: boolean;
|
|
644
|
+
/**
|
|
645
|
+
* The first level of detail that shows it (it's on the more detailed ones too, see levels.ts): L1 domains, systems
|
|
646
|
+
* and actors, L2 services, data stores, text and the like, L3 messages and channels. Notes are on every level.
|
|
647
|
+
*/
|
|
648
|
+
firstLevel: 1 | 2 | 3;
|
|
649
|
+
createData: () => Record<string, unknown>;
|
|
650
|
+
};
|
|
651
|
+
declare const nodeDefinitions: NodeDefinition[];
|
|
652
|
+
/** A component's key in the components list (and what's dragged from it) */
|
|
653
|
+
declare const componentKey: (definition: NodeDefinition) => string;
|
|
654
|
+
/** The types people and agents can add (not the unlisted ones), each once (the kinds of data store are all `data`) */
|
|
655
|
+
declare const COMPONENT_TYPES: [string, ...string[]];
|
|
656
|
+
declare const getNodeDefinition: (type?: string) => NodeDefinition | undefined;
|
|
657
|
+
declare const getNodeLabel: (type?: string) => string;
|
|
658
|
+
declare const DEFAULT_NODE_SIZE: {
|
|
659
|
+
width: number;
|
|
660
|
+
height: number;
|
|
661
|
+
};
|
|
662
|
+
/** The size a new node of a type starts at (or, for cards, about the size it renders at) */
|
|
663
|
+
declare const getNodeSize: (type?: string) => {
|
|
664
|
+
width: number;
|
|
665
|
+
height: number;
|
|
666
|
+
};
|
|
667
|
+
/** The resource object for a node (e.g. data.service), or the data itself for flat nodes */
|
|
668
|
+
declare const getResource: (type: string | undefined, data: Record<string, unknown>) => Record<string, unknown>;
|
|
669
|
+
/** Sticky notes sit on the canvas wherever they're put, never inside a container */
|
|
670
|
+
declare const canGoInContainer: (type?: string) => boolean;
|
|
671
|
+
/**
|
|
672
|
+
* Whether a container can hold a kind of node, as EventCatalog's domains and systems do: a domain holds anything
|
|
673
|
+
* (subdomains, systems, services...), a system anything but a domain
|
|
674
|
+
*/
|
|
675
|
+
declare const canHold: (containerType?: string, type?: string) => boolean;
|
|
676
|
+
/**
|
|
677
|
+
* The level (L1, L2 or L3) a sticky note or text was written on: it's only shown there. Ones from before they had
|
|
678
|
+
* levels are on L3, the canvas people edit. Other nodes are on every level they fit in, so they have none.
|
|
679
|
+
*/
|
|
680
|
+
declare const getNoteLevel: (node: {
|
|
681
|
+
type?: string;
|
|
682
|
+
data: Record<string, unknown>;
|
|
683
|
+
}) => 1 | 2 | 3 | undefined;
|
|
684
|
+
/** Whether a node is on a level's canvas as itself: everything on L3, but notes and text only on their level */
|
|
685
|
+
declare const isOnLevel: (node: {
|
|
686
|
+
type?: string;
|
|
687
|
+
data: Record<string, unknown>;
|
|
688
|
+
}, level: 1 | 2 | 3) => boolean;
|
|
689
|
+
/** Whether a level of detail shows a kind of node (so it can be added there, and stay there) */
|
|
690
|
+
declare const isShownOnLevel: (type: string | undefined, level: 1 | 2 | 3) => boolean;
|
|
691
|
+
/** Whether a node's resource has a version (actors, notes and text don't) */
|
|
692
|
+
declare const isVersioned: (type?: string) => boolean;
|
|
693
|
+
/** Notes and text: written on the canvas itself (their text is their data, not a resource with a name) */
|
|
694
|
+
declare const isWrittenOnCanvas: (type?: string) => type is "note" | "text";
|
|
695
|
+
/** Returns new node data with fields of the resource changed */
|
|
696
|
+
declare const updateResource: (type: string | undefined, data: Record<string, unknown>, fields: Record<string, unknown>) => {
|
|
697
|
+
[x: string]: unknown;
|
|
698
|
+
};
|
|
699
|
+
/** A node's name, as people and agents see it */
|
|
700
|
+
declare const getNodeName: (type: string | undefined, data: Record<string, unknown>, fallback?: string) => string;
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Where agents put what they add without a position, the way an architect would: next to what it connects to,
|
|
704
|
+
* following the flow left to right (what sends to it on its left, what it sends to on its right), lined up with
|
|
705
|
+
* it, and stacked around it when several things connect to the same node. Nothing already on the canvas moves.
|
|
706
|
+
* Things connected to nothing go in a row below what's there. Containers are filled first, so what goes around them
|
|
707
|
+
* leaves room for what they grow to hold.
|
|
708
|
+
*/
|
|
709
|
+
/** A connection between two nodes, from the one that sends to the one that receives */
|
|
710
|
+
type Link = {
|
|
711
|
+
source: string;
|
|
712
|
+
target: string;
|
|
713
|
+
};
|
|
714
|
+
/**
|
|
715
|
+
* Where the nodes to place go on the canvas (not relative to their containers: containers that grow to fit them
|
|
716
|
+
* move what's in them, but nothing moves on the canvas). They're among `nodes`, with their containers set.
|
|
717
|
+
* Everything else in `nodes` stays where it is.
|
|
718
|
+
*/
|
|
719
|
+
declare const placeNodes: (nodes: Node[], toPlace: string[], links: Link[]) => Map<string, XYPosition>;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Smooths what the others in a live room are moving. Their positions arrive about 30 times a second (and unevenly,
|
|
723
|
+
* as the network goes), while the screen draws 60 to 120 times a second: shown as they arrive, what they drag steps
|
|
724
|
+
* along and stutters. Here each one glides to where it was last said to be, a step every frame, easing in about
|
|
725
|
+
* EASE_MS (a little behind them, smoothly). Frames only run while something's still gliding.
|
|
726
|
+
*/
|
|
727
|
+
type XY = {
|
|
728
|
+
x: number;
|
|
729
|
+
y: number;
|
|
730
|
+
};
|
|
731
|
+
type Smoother = {
|
|
732
|
+
/** Where each thing (by id) is now, as the others say; anything left out stops being shown straight away */
|
|
733
|
+
set: (targets: ReadonlyMap<string, XY>) => void;
|
|
734
|
+
stop: () => void;
|
|
735
|
+
};
|
|
736
|
+
declare function createSmoother(apply: (shown: ReadonlyMap<string, XY>) => void): Smoother;
|
|
737
|
+
|
|
738
|
+
/** 1 / zoom, for things drawn in the canvas's coordinates that stay the same size on screen (`scale(var(...))`) */
|
|
739
|
+
declare const COUNTER_SCALE = "--studio-counter-scale";
|
|
740
|
+
/**
|
|
741
|
+
* Keeps COUNTER_SCALE on an element as the canvas zooms, outside React: what's counter-scaled with it (e.g. comment
|
|
742
|
+
* pins) stays the same size on screen without re-rendering on every frame of a zoom
|
|
743
|
+
*/
|
|
744
|
+
declare function useCounterScale(ref: RefObject<HTMLElement | null>): void;
|
|
745
|
+
|
|
746
|
+
/** Instead of React Flow's `EdgeLabelRenderer`: HTML drawn over the edges, in the canvas's coordinates */
|
|
747
|
+
declare function EdgeLabelPortal({ children }: {
|
|
748
|
+
children: ReactNode;
|
|
749
|
+
}): react.ReactPortal | null;
|
|
750
|
+
/** Instead of React Flow's `ViewportPortal`: drawn above the nodes, in the canvas's coordinates */
|
|
751
|
+
declare function CanvasPortal({ children }: {
|
|
752
|
+
children: ReactNode;
|
|
753
|
+
}): react.ReactPortal | null;
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* The model (what agents send, links carry and files keep) as Studio's canvas (React Flow nodes and edges, as
|
|
757
|
+
* Studio's canvas-doc keeps them), and back. Each resource is a node with the resource's id, so what's arranged,
|
|
758
|
+
* linked and saved lines up with the agent's ids. Relations are edges (what each edge stands for is kept on it);
|
|
759
|
+
* notes are sticky note and text nodes, on their level; where things are goes in the model's layout.
|
|
760
|
+
*/
|
|
761
|
+
|
|
762
|
+
declare const resourceTypeOf: (nodeType?: string) => "system" | "actor" | "domain" | "service" | "agent" | "event" | "query" | "command" | "channel" | "view" | "team" | "entity" | "container" | "external-system";
|
|
763
|
+
type ModelCanvasContent = {
|
|
764
|
+
nodes: Node[];
|
|
765
|
+
edges: Edge[];
|
|
766
|
+
levelLayouts: Record<1 | 2, Map<string, LevelPlace>>;
|
|
767
|
+
/** Comment threads people left on it */
|
|
768
|
+
threads?: Thread[];
|
|
769
|
+
/** What's flagged on it (by people, or the agent) */
|
|
770
|
+
hotspots?: Hotspot[];
|
|
771
|
+
/** A live room's document as this browser kept it: the canvas starts from that instead (see local-canvas.ts) */
|
|
772
|
+
doc?: Uint8Array;
|
|
773
|
+
};
|
|
774
|
+
/** A resource's node data, as Studio's components for its type create it (and the resource itself, to keep what the canvas doesn't show) */
|
|
775
|
+
declare function resourceNodeData(resource: Resource): Record<string, unknown>;
|
|
776
|
+
declare function modelToCanvas(model: Model, seed?: {
|
|
777
|
+
nodes: Node[];
|
|
778
|
+
}): Promise<ModelCanvasContent>;
|
|
779
|
+
/** The canvas as the model: its resources, relations and notes (through what's still on it), and where things are */
|
|
780
|
+
declare function canvasToModel(nodes: Node[], edges: Edge[], levelLayouts: Record<1 | 2, ReadonlyMap<string, LevelPlace>>, base: Model, threads?: Thread[], hotspots?: Hotspot[]): Model;
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* "Save to file" and "Open": a diagram as a .eventcatalog file (JSON: the model, with its notes, comments and
|
|
784
|
+
* hotspots, and where it was focused), as drawing tools save theirs. The same file in Studio on the web and in
|
|
785
|
+
* EventCatalog's Studio, so a diagram moves between them.
|
|
786
|
+
*/
|
|
787
|
+
|
|
788
|
+
declare const DIAGRAM_FILE_EXTENSION = ".eventcatalog";
|
|
789
|
+
declare const fileNameFor: (title: string, extension: string) => string;
|
|
790
|
+
declare function toDiagramFile(model: Model, { focus, level }?: {
|
|
791
|
+
focus?: string;
|
|
792
|
+
level?: Level$1;
|
|
793
|
+
}): string;
|
|
794
|
+
/** The diagram in a .eventcatalog file (or a bare model, as the agent writes them), or why it can't be opened */
|
|
795
|
+
declare function readDiagramFile(file: File): Promise<Pick<ArchitecturePayload, "model" | "focus" | "level"> | {
|
|
796
|
+
error: string;
|
|
797
|
+
}>;
|
|
798
|
+
/** Downloads a file (text, or base64 for binary files such as images) */
|
|
799
|
+
declare function downloadFile({ name, mimeType, text, base64, }: {
|
|
800
|
+
name: string;
|
|
801
|
+
mimeType: string;
|
|
802
|
+
text?: string;
|
|
803
|
+
base64?: string;
|
|
804
|
+
}): boolean;
|
|
805
|
+
/** A name typed for the file, made safe to save, with the extension on the end */
|
|
806
|
+
declare function cleanFileName(name: string, extension: string): string;
|
|
807
|
+
type WritableFile = {
|
|
808
|
+
createWritable: () => Promise<{
|
|
809
|
+
write: (data: string) => Promise<void>;
|
|
810
|
+
close: () => Promise<void>;
|
|
811
|
+
}>;
|
|
812
|
+
name: string;
|
|
813
|
+
};
|
|
814
|
+
type SavedFileHandle = WritableFile;
|
|
815
|
+
declare const canPickWhereToSave: () => boolean;
|
|
816
|
+
/** Writes to a file picked (or picked before): the handle, undefined when they cancelled */
|
|
817
|
+
declare function saveToPickedFile(text: string, { suggestedName, handle, }: {
|
|
818
|
+
suggestedName: string;
|
|
819
|
+
handle?: SavedFileHandle;
|
|
820
|
+
}): Promise<WritableFile | undefined>;
|
|
821
|
+
/** Asks for a .eventcatalog file to open */
|
|
822
|
+
declare function pickDiagramFile(): Promise<File | undefined>;
|
|
823
|
+
|
|
824
|
+
/** The canvas in `container` as a PNG (a data URL), or undefined when there's nothing on it */
|
|
825
|
+
declare function canvasImage(container: HTMLElement): Promise<string | undefined>;
|
|
826
|
+
|
|
827
|
+
/** How the canvas draws each kind of node: the visualiser's nodes, notes and text written on the canvas, and resizable containers */
|
|
828
|
+
type UpdateNodeData = (id: string, updater: (data: Record<string, unknown>) => Record<string, unknown>) => void;
|
|
829
|
+
declare const UpdateNodeDataContext: react.Context<UpdateNodeData>;
|
|
830
|
+
/** Tells a live room what's being changed on a note or text as it changes (nothing: done) */
|
|
831
|
+
declare const EditingContext: react.Context<(editing: Editing | null) => void>;
|
|
832
|
+
declare const DropTargetContext: react.Context<string | null>;
|
|
833
|
+
declare const StartWritingContext: react.Context<string | null>;
|
|
834
|
+
declare const LockedContext: react.Context<boolean>;
|
|
835
|
+
/**
|
|
836
|
+
* Text written straight on the canvas (titles, labels, notes to self), as Excalidraw's: as big as what's written,
|
|
837
|
+
* in a box with corners to scale it (its font grows with it) and a handle above to turn it. Double click it to write.
|
|
838
|
+
*/
|
|
839
|
+
/** Text's size when it's first written */
|
|
840
|
+
declare const TEXT_FONT_SIZE = 22;
|
|
841
|
+
declare const nodeTypes: NodeTypes;
|
|
842
|
+
|
|
843
|
+
declare const timeAgo: (timestamp: number) => string;
|
|
844
|
+
/** Where a pin sits on the canvas: on its node (so it moves with it), or where it was dropped */
|
|
845
|
+
declare const getAnchorPosition: (anchor: CommentAnchor, nodesById: Map<string, Node>) => XYPosition;
|
|
846
|
+
/** Pinned to a node, a comment keeps its offset from the node so it moves with it */
|
|
847
|
+
declare const anchorAt: (position: XYPosition, node?: Node, nodesById?: Map<string, Node>) => CommentAnchor;
|
|
848
|
+
/**
|
|
849
|
+
* Comment pins (and the open thread) on the canvas, in its own coordinates (React Flow's viewport portal) so
|
|
850
|
+
* they move with it without re-rendering, counter-scaled to stay the same size on screen. Each pin follows
|
|
851
|
+
* only its own node, so other nodes moving don't re-render them.
|
|
852
|
+
*/
|
|
853
|
+
declare const CommentLayer: react.NamedExoticComponent<{
|
|
854
|
+
threads: Thread[];
|
|
855
|
+
author: Author;
|
|
856
|
+
openThreadId: string | null;
|
|
857
|
+
/** Threads that are part of what's selected */
|
|
858
|
+
selectedIds: ReadonlySet<string>;
|
|
859
|
+
/** How far what's selected has been dragged: selected pins not on a node move with it */
|
|
860
|
+
selectionOffset: XYPosition | null;
|
|
861
|
+
draft: CommentAnchor | null;
|
|
862
|
+
showResolved: boolean;
|
|
863
|
+
onOpen: (threadId: string | null) => void;
|
|
864
|
+
onCreate: (text: string) => void;
|
|
865
|
+
onCancelDraft: () => void;
|
|
866
|
+
onReply: (threadId: string, text: string) => void;
|
|
867
|
+
onResolve: (threadId: string, resolved: boolean) => void;
|
|
868
|
+
onDelete: (threadId: string) => void;
|
|
869
|
+
onMove: (threadId: string, anchor: CommentAnchor) => void;
|
|
870
|
+
onMoveDraft: (anchor: CommentAnchor) => void;
|
|
871
|
+
/** In a live room: pins the others are dragging move here as they drag, and mine move for them */
|
|
872
|
+
live?: LiveMoves;
|
|
873
|
+
}>;
|
|
874
|
+
/** All threads, for the left panel. Clicking one opens it on the canvas. */
|
|
875
|
+
declare function CommentList({ threads, showResolved, onToggleResolved, onOpen, }: {
|
|
876
|
+
threads: Thread[];
|
|
877
|
+
showResolved: boolean;
|
|
878
|
+
onToggleResolved: () => void;
|
|
879
|
+
onOpen: (thread: Thread) => void;
|
|
880
|
+
}): react_jsx_runtime.JSX.Element;
|
|
881
|
+
|
|
882
|
+
type ContextMenuState = {
|
|
883
|
+
x: number;
|
|
884
|
+
y: number;
|
|
885
|
+
flowPosition: XYPosition;
|
|
886
|
+
node?: Node;
|
|
887
|
+
} | null;
|
|
888
|
+
/** Something to ask the agent about the node right clicked */
|
|
889
|
+
type MenuQuestion = {
|
|
890
|
+
label: string;
|
|
891
|
+
onSelect: () => void;
|
|
892
|
+
};
|
|
893
|
+
declare const ContextMenu: react.NamedExoticComponent<{
|
|
894
|
+
menu: ContextMenuState;
|
|
895
|
+
/** For the node right clicked */
|
|
896
|
+
questions?: MenuQuestion[];
|
|
897
|
+
onCopy: () => void;
|
|
898
|
+
onDuplicate: () => void;
|
|
899
|
+
/** Paste what was last copied where the menu was opened, if anything was */
|
|
900
|
+
onPaste?: () => void;
|
|
901
|
+
/** A sticky note where the menu was opened (on the canvas), or beside the node */
|
|
902
|
+
onAddNote: () => void;
|
|
903
|
+
/** A comment pinned where the menu was opened (on the canvas or the node), on L3 */
|
|
904
|
+
onAddComment?: () => void;
|
|
905
|
+
/** A hotspot on the node right clicked (or where the menu was opened), on L3 */
|
|
906
|
+
onAddHotspot?: () => void;
|
|
907
|
+
/** Opens the node's details (not for notes and text, which are written on the canvas) */
|
|
908
|
+
onEditDetails?: () => void;
|
|
909
|
+
/** Lays the level shown out again (the visualiser's layout), from the canvas's menu */
|
|
910
|
+
onReorder?: () => void;
|
|
911
|
+
onDeleteNode: () => void;
|
|
912
|
+
onClose: () => void;
|
|
913
|
+
}>;
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Dragging something from the left panel onto the canvas shows the node it becomes, under the pointer, at the
|
|
917
|
+
* canvas's zoom and where it will land (centred on the pointer, as it's dropped). Drawn here rather than as the
|
|
918
|
+
* browser's drag image, which browsers shrink, fade and put a backing behind.
|
|
919
|
+
*/
|
|
920
|
+
/** What's dragged: the node it becomes */
|
|
921
|
+
type DragPreview = {
|
|
922
|
+
type: string;
|
|
923
|
+
data: Record<string, unknown>;
|
|
924
|
+
};
|
|
925
|
+
/** Starts dragging `value` as `dragType`, showing `preview` as what's dragged */
|
|
926
|
+
type StartDrag = (dragType: string, value: string, preview: DragPreview) => (event: DragEvent) => void;
|
|
927
|
+
/** The ghost of what's being dragged, and how to start dragging something with one */
|
|
928
|
+
declare function useDragGhost(): {
|
|
929
|
+
startDrag: StartDrag;
|
|
930
|
+
ghost: react_jsx_runtime.JSX.Element | null;
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
declare const SEVERITIES: Record<HotspotSeverity, {
|
|
934
|
+
label: string;
|
|
935
|
+
color: string;
|
|
936
|
+
icon: LucideIcon;
|
|
937
|
+
rank: number;
|
|
938
|
+
}>;
|
|
939
|
+
/** Most important first (then oldest first) */
|
|
940
|
+
declare const bySeverity: (a: Hotspot, b: Hotspot) => number;
|
|
941
|
+
declare const topSeverity: (hotspots: Hotspot[]) => HotspotSeverity;
|
|
942
|
+
/** Hotspots shown together: on a node (its own, or on L1 and L2 also the ones inside it), or one at a place */
|
|
943
|
+
type HotspotSpot = {
|
|
944
|
+
key: string;
|
|
945
|
+
nodeId?: string;
|
|
946
|
+
position?: XYPosition;
|
|
947
|
+
hotspots: Hotspot[];
|
|
948
|
+
inside?: boolean;
|
|
949
|
+
};
|
|
950
|
+
/** A hotspot being added: on a node, or at a place */
|
|
951
|
+
type HotspotDraft = {
|
|
952
|
+
nodeId?: string;
|
|
953
|
+
position: XYPosition;
|
|
954
|
+
level?: 1 | 2;
|
|
955
|
+
};
|
|
956
|
+
type HotspotFields = {
|
|
957
|
+
severity: HotspotSeverity;
|
|
958
|
+
title: string;
|
|
959
|
+
description?: string;
|
|
960
|
+
};
|
|
961
|
+
/** Where a hotspot is put: on a node (it moves with it), or at a place */
|
|
962
|
+
type HotspotPlace = {
|
|
963
|
+
nodeId?: string;
|
|
964
|
+
position: XYPosition;
|
|
965
|
+
};
|
|
966
|
+
declare const HotspotLayer: react.NamedExoticComponent<{
|
|
967
|
+
spots: HotspotSpot[];
|
|
968
|
+
openKey: string | null;
|
|
969
|
+
draft: HotspotDraft | null;
|
|
970
|
+
locked: boolean;
|
|
971
|
+
/** Markers can be dragged to another node, or a place (on the canvas itself, L3) */
|
|
972
|
+
movable: boolean;
|
|
973
|
+
/** Hotspots that are part of what's selected (after selecting everything) */
|
|
974
|
+
selectedIds: ReadonlySet<string> | null;
|
|
975
|
+
/** Moved: the hotspots at a marker, onto a node or to a place */
|
|
976
|
+
onMove: (ids: string[], to: HotspotPlace) => void;
|
|
977
|
+
/** The node a marker being dragged would go on (and its colour), to light it up; none when it's dropped */
|
|
978
|
+
onDropTarget: (target: {
|
|
979
|
+
nodeId: string;
|
|
980
|
+
severity: HotspotSeverity;
|
|
981
|
+
} | null) => void;
|
|
982
|
+
onOpen: (key: string | null) => void;
|
|
983
|
+
onCreate: (fields: HotspotFields) => void;
|
|
984
|
+
onCancelDraft: () => void;
|
|
985
|
+
onUpdate: (id: string, fields: HotspotFields) => void;
|
|
986
|
+
onDelete: (id: string) => void;
|
|
987
|
+
onGo: (hotspot: Hotspot) => void;
|
|
988
|
+
/** In a live room: markers the others are dragging move here as they drag, and mine move for them */
|
|
989
|
+
live?: LiveMoves;
|
|
990
|
+
}>;
|
|
991
|
+
/** All of them, most important first, for the panel at the bottom left: clicking one goes to it */
|
|
992
|
+
declare function HotspotList({ hotspots, nameOf, onOpen, }: {
|
|
993
|
+
hotspots: Hotspot[];
|
|
994
|
+
nameOf: (nodeId: string) => string | undefined;
|
|
995
|
+
onOpen: (hotspot: Hotspot) => void;
|
|
996
|
+
}): react_jsx_runtime.JSX.Element;
|
|
997
|
+
|
|
998
|
+
/** How each kind of node, and each catalog collection, looks in the canvas's lists (the colours are each kind's own) */
|
|
999
|
+
type NodeIcon = {
|
|
1000
|
+
icon: LucideIcon;
|
|
1001
|
+
className: string;
|
|
1002
|
+
};
|
|
1003
|
+
declare const NODE_ICONS: Record<string, NodeIcon>;
|
|
1004
|
+
/** The catalog's collections, in the order the catalog list shows them, with the icon of the node each becomes */
|
|
1005
|
+
declare const CATALOG_GROUPS: ({
|
|
1006
|
+
collection: string;
|
|
1007
|
+
label: string;
|
|
1008
|
+
} & NodeIcon)[];
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Status and destructive colours for the canvas, from the theme's badge colours (EventCatalog has no status
|
|
1012
|
+
* variables), so they follow light, dark and custom themes. Kept here rather than repeated in each component.
|
|
1013
|
+
*/
|
|
1014
|
+
declare const STATUS: {
|
|
1015
|
+
readonly success: {
|
|
1016
|
+
readonly text: "text-[rgb(var(--ec-badge-color-green-text))]";
|
|
1017
|
+
readonly dot: "bg-[rgb(var(--ec-badge-color-green-text))]";
|
|
1018
|
+
};
|
|
1019
|
+
readonly warning: {
|
|
1020
|
+
readonly text: "text-[rgb(var(--ec-badge-color-yellow-text))]";
|
|
1021
|
+
readonly dot: "bg-[rgb(var(--ec-badge-color-yellow-text))]";
|
|
1022
|
+
};
|
|
1023
|
+
readonly danger: {
|
|
1024
|
+
readonly text: "text-[rgb(var(--ec-badge-color-red-text))]";
|
|
1025
|
+
readonly dot: "bg-[rgb(var(--ec-badge-color-red-text))]";
|
|
1026
|
+
readonly hover: "hover:bg-[rgb(var(--ec-badge-color-red-background))]";
|
|
1027
|
+
/** A button that deletes something */
|
|
1028
|
+
readonly button: "bg-[rgb(var(--ec-badge-color-red-text))] text-white hover:bg-[rgb(var(--ec-badge-color-red-text)/0.85)]";
|
|
1029
|
+
};
|
|
1030
|
+
};
|
|
1031
|
+
/** How each canvas status looks and what it means (from the theme's badge colours) */
|
|
1032
|
+
declare const CANVAS_STATUS_LOOK: Record<CanvasStatus, {
|
|
1033
|
+
label: string;
|
|
1034
|
+
description: string;
|
|
1035
|
+
pill: string;
|
|
1036
|
+
dot: string;
|
|
1037
|
+
}>;
|
|
1038
|
+
|
|
1039
|
+
declare const FIT_VIEW_OPTIONS: {
|
|
1040
|
+
readonly padding: {
|
|
1041
|
+
readonly top: "100px";
|
|
1042
|
+
readonly right: "60px";
|
|
1043
|
+
readonly bottom: "80px";
|
|
1044
|
+
readonly left: "60px";
|
|
1045
|
+
};
|
|
1046
|
+
readonly maxZoom: 1;
|
|
1047
|
+
};
|
|
1048
|
+
declare const _default$1: react.NamedExoticComponent<{
|
|
1049
|
+
level: Level;
|
|
1050
|
+
onLevelChange: (level: Level) => void;
|
|
1051
|
+
/** Levels this canvas can't show, and why (greyed out, with the reason) */
|
|
1052
|
+
unavailableLevels?: Partial<Record<Level, string | undefined>>;
|
|
1053
|
+
/** L3, the canvas itself (comments are there; L1 and L2 are arranged on their own) */
|
|
1054
|
+
editable: boolean;
|
|
1055
|
+
/** Nothing on the canvas can be changed (until it's unlocked) */
|
|
1056
|
+
locked: boolean;
|
|
1057
|
+
onToggleLock: () => void;
|
|
1058
|
+
/** Who locked it (it's locked for everyone on it) */
|
|
1059
|
+
lockedBy?: string | null;
|
|
1060
|
+
/** Selecting and moving what's on the canvas (no tool picked) */
|
|
1061
|
+
selectMode: boolean;
|
|
1062
|
+
onSelectMode: () => void;
|
|
1063
|
+
/** The hand: dragging moves around the canvas (nothing on it is picked up) */
|
|
1064
|
+
handMode: boolean;
|
|
1065
|
+
onHandMode: () => void;
|
|
1066
|
+
/** The sticky note tool, on: a click on the canvas puts a note there */
|
|
1067
|
+
noteMode: boolean;
|
|
1068
|
+
onToggleNoteMode: () => void;
|
|
1069
|
+
/** The comment tool, on: a click on the canvas (or a node) pins a comment there */
|
|
1070
|
+
commentMode: boolean;
|
|
1071
|
+
onToggleCommentMode: () => void;
|
|
1072
|
+
/** The hotspot tool, on: a click on a node (or the canvas) flags a hotspot there */
|
|
1073
|
+
hotspotMode: boolean;
|
|
1074
|
+
onToggleHotspotMode: () => void;
|
|
1075
|
+
/** The text tool, on: a click on the canvas writes there */
|
|
1076
|
+
textMode: boolean;
|
|
1077
|
+
onToggleTextMode: () => void;
|
|
1078
|
+
/** More buttons at the end of the bar (e.g. replaying the agent's walkthrough) */
|
|
1079
|
+
extras?: ReactNode;
|
|
1080
|
+
}>;
|
|
1081
|
+
|
|
1082
|
+
/** The bottom-left controls, as Excalidraw's: zooming, undo and redo, then fitting it to the view and laying it out again */
|
|
1083
|
+
declare const ViewControls: react.NamedExoticComponent<{
|
|
1084
|
+
canUndo: boolean;
|
|
1085
|
+
canRedo: boolean;
|
|
1086
|
+
onUndo: () => void;
|
|
1087
|
+
onRedo: () => void;
|
|
1088
|
+
/** Lays it out again with the visualiser's layout */
|
|
1089
|
+
onReorder: () => void;
|
|
1090
|
+
reorderLabel: string;
|
|
1091
|
+
locked: boolean;
|
|
1092
|
+
/** How the canvas is fitted to the view (clear of what's over it, e.g. the palette) */
|
|
1093
|
+
fitOptions?: FitViewOptions;
|
|
1094
|
+
/** The comments: how many are open, and showing them all */
|
|
1095
|
+
comments?: {
|
|
1096
|
+
open: number;
|
|
1097
|
+
active: boolean;
|
|
1098
|
+
onToggle: () => void;
|
|
1099
|
+
};
|
|
1100
|
+
/** The hotspots: how many, the most important's severity, and listing them all */
|
|
1101
|
+
hotspots?: {
|
|
1102
|
+
count: number;
|
|
1103
|
+
severity?: HotspotSeverity;
|
|
1104
|
+
active: boolean;
|
|
1105
|
+
onToggle: () => void;
|
|
1106
|
+
};
|
|
1107
|
+
}>;
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* Asked when a domain or system is put on the canvas: is it a card (connected to what it contains), or a container
|
|
1111
|
+
* that what it contains sits inside? Styled like the visualiser's group-by picker (boxes or lanes). One from a catalog
|
|
1112
|
+
* can bring what it contains in the catalog inside it (`contents`).
|
|
1113
|
+
*/
|
|
1114
|
+
type ContainerChoice = {
|
|
1115
|
+
as: "card";
|
|
1116
|
+
} | {
|
|
1117
|
+
as: "container";
|
|
1118
|
+
withContents?: boolean;
|
|
1119
|
+
};
|
|
1120
|
+
declare const CardPreview: () => react_jsx_runtime.JSX.Element;
|
|
1121
|
+
declare function ContainerChooser({ kind, name, contents, position, onChoose, onCancel, }: {
|
|
1122
|
+
kind: "domain" | "system";
|
|
1123
|
+
/** Its name, when it has one already (e.g. from a catalog) */
|
|
1124
|
+
name?: string;
|
|
1125
|
+
/** What it contains in the catalog that isn't on the canvas yet (offered to bring inside it, as a container) */
|
|
1126
|
+
contents?: {
|
|
1127
|
+
systems: number;
|
|
1128
|
+
services: number;
|
|
1129
|
+
};
|
|
1130
|
+
/** Where to show it (screen coordinates) */
|
|
1131
|
+
position: {
|
|
1132
|
+
x: number;
|
|
1133
|
+
y: number;
|
|
1134
|
+
};
|
|
1135
|
+
onChoose: (choice: ContainerChoice) => void;
|
|
1136
|
+
onCancel: () => void;
|
|
1137
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1138
|
+
|
|
1139
|
+
declare const _default: react.NamedExoticComponent<{
|
|
1140
|
+
nodeId: string;
|
|
1141
|
+
locked: boolean;
|
|
1142
|
+
onClick: () => void;
|
|
1143
|
+
}>;
|
|
1144
|
+
|
|
1145
|
+
/** What's dragged from the palette onto the canvas: a component's key (its type, or its kind of data store) */
|
|
1146
|
+
declare const COMPONENT_DRAG_TYPE = "application/eventcatalog-studio";
|
|
1147
|
+
/**
|
|
1148
|
+
* A tile in the palette: its icon, its name and what it is, picked up and dragged onto the canvas (or clicked, to
|
|
1149
|
+
* add it). Greyed out (`usable` false) where it can't be added, still saying why.
|
|
1150
|
+
*/
|
|
1151
|
+
declare function PaletteItem({ icon: Icon, iconClassName, label, description, usable, onDragStart, onClick, title, trailing, }: {
|
|
1152
|
+
icon: LucideIcon;
|
|
1153
|
+
iconClassName?: string;
|
|
1154
|
+
label: string;
|
|
1155
|
+
description?: ReactNode;
|
|
1156
|
+
usable?: boolean;
|
|
1157
|
+
onDragStart?: (event: DragEvent) => void;
|
|
1158
|
+
onClick?: () => void;
|
|
1159
|
+
title?: string;
|
|
1160
|
+
/** At the end instead of the drag handle (e.g. "on the canvas") */
|
|
1161
|
+
trailing?: ReactNode;
|
|
1162
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1163
|
+
/** A section of tiles, under a small heading */
|
|
1164
|
+
declare function PaletteSection({ title, divided, children, }: {
|
|
1165
|
+
title: ReactNode;
|
|
1166
|
+
/** A line above it (e.g. what this level doesn't show, at the bottom) */
|
|
1167
|
+
divided?: boolean;
|
|
1168
|
+
children: ReactNode;
|
|
1169
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1170
|
+
/** A tab an app adds beside the components: what it lists, given how to start dragging something onto the canvas */
|
|
1171
|
+
type PaletteTab = {
|
|
1172
|
+
id: string;
|
|
1173
|
+
label: string;
|
|
1174
|
+
/** A count beside its name (e.g. open comments) */
|
|
1175
|
+
badge?: number;
|
|
1176
|
+
render: (startDrag: StartDrag) => ReactNode;
|
|
1177
|
+
};
|
|
1178
|
+
declare const Palette: react.NamedExoticComponent<{
|
|
1179
|
+
/** The level shown: only what it shows is listed */
|
|
1180
|
+
level: Level;
|
|
1181
|
+
onAddComponent: (key: string) => void;
|
|
1182
|
+
/** At the bottom, under the list (e.g. taking the diagram to EventCatalog) */
|
|
1183
|
+
footer?: ReactNode;
|
|
1184
|
+
/** More tabs beside the components (the components are the first) */
|
|
1185
|
+
tabs?: PaletteTab[];
|
|
1186
|
+
/** The tab shown, when the app chooses it (it opens the palette if it's folded away) */
|
|
1187
|
+
tab?: string;
|
|
1188
|
+
onTabChange?: (tab: string) => void;
|
|
1189
|
+
/** Starts folded away, whatever this browser remembers (and doesn't remember it), e.g. in a chat's small view */
|
|
1190
|
+
startCollapsed?: boolean;
|
|
1191
|
+
}>;
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Help: the keyboard shortcuts, as drawing tools list them, in two columns where there's room. Each app shows them in
|
|
1195
|
+
* its own dialog, leaving out what it doesn't have (by label).
|
|
1196
|
+
*/
|
|
1197
|
+
declare const SHORTCUTS: {
|
|
1198
|
+
group: string;
|
|
1199
|
+
items: [string, string[]][];
|
|
1200
|
+
}[];
|
|
1201
|
+
declare function ShortcutsList({ omit }: {
|
|
1202
|
+
omit?: string[];
|
|
1203
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1204
|
+
|
|
1205
|
+
type LogoProps = SVGProps<SVGSVGElement>;
|
|
1206
|
+
declare const ClaudeLogo: (props: LogoProps) => react_jsx_runtime.JSX.Element;
|
|
1207
|
+
declare const ClaudeCodeLogo: (props: LogoProps) => react_jsx_runtime.JSX.Element;
|
|
1208
|
+
declare const OpenAILogo: (props: LogoProps) => react_jsx_runtime.JSX.Element;
|
|
1209
|
+
declare const CursorLogo: (props: LogoProps) => react_jsx_runtime.JSX.Element;
|
|
1210
|
+
/**
|
|
1211
|
+
* The logo of the company behind an agent, from the name it joined with (agentName on the MCP tools, e.g. "Claude
|
|
1212
|
+
* Code", "Codex", "Cursor"), when it's one we know: shown instead of a robot on its avatar, pointer and comments
|
|
1213
|
+
*/
|
|
1214
|
+
declare const agentLogoFor: (name?: string) => ((props: LogoProps) => react_jsx_runtime.JSX.Element) | undefined;
|
|
1215
|
+
/** An agent's mark: its company's logo when it's one we know (agentLogoFor), else a robot */
|
|
1216
|
+
declare function AgentMark({ name, size, }: {
|
|
1217
|
+
name?: string;
|
|
1218
|
+
size?: number;
|
|
1219
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1220
|
+
|
|
1221
|
+
type EmptyCanvasProps = {
|
|
1222
|
+
/** Opening a .eventcatalog file (not offered when not given, e.g. inside a chat) */
|
|
1223
|
+
onOpen?: () => void;
|
|
1224
|
+
onHelp: () => void;
|
|
1225
|
+
/** How to connect an AI agent to this server (when there's one to connect to) */
|
|
1226
|
+
onConnect?: () => void;
|
|
1227
|
+
/** The resources panel is on the left, to point to */
|
|
1228
|
+
palette?: boolean;
|
|
1229
|
+
/** Live sessions can be started (from Share, top right), to point to */
|
|
1230
|
+
live?: boolean;
|
|
1231
|
+
/** The welcome in the middle (the brand, what it's for, Open, Connect, Help); without it, just the hints */
|
|
1232
|
+
welcome?: boolean;
|
|
1233
|
+
};
|
|
1234
|
+
declare function EmptyCanvas({ welcome, onOpen, onHelp, onConnect, palette, live, }: EmptyCanvasProps): react_jsx_runtime.JSX.Element | null;
|
|
1235
|
+
|
|
1236
|
+
/** pointsAt: how far down the node's middle is (for an arrow from the modal to it), when the modal is beside it */
|
|
1237
|
+
type Anchor = {
|
|
1238
|
+
top: number;
|
|
1239
|
+
left: number;
|
|
1240
|
+
side: "right" | "left";
|
|
1241
|
+
pointsAt?: number;
|
|
1242
|
+
};
|
|
1243
|
+
/**
|
|
1244
|
+
* Where to show the modal: next to the node (on its right, or its left when there is no room),
|
|
1245
|
+
* kept inside the diagram and following the node as the diagram is panned or zoomed.
|
|
1246
|
+
*/
|
|
1247
|
+
declare function useNodeAnchor(nodeId: string, containerRef: RefObject<HTMLElement | null>, modalRef: RefObject<HTMLElement | null>, bottomInset?: number): Anchor | null;
|
|
1248
|
+
/** "Saved", for a moment after a change is: what an edit form says under its fields */
|
|
1249
|
+
declare function useSavedFlash(): {
|
|
1250
|
+
saved: boolean;
|
|
1251
|
+
showSaved: () => void;
|
|
1252
|
+
};
|
|
1253
|
+
/** Under an edit form's fields: that changes save as they're typed, or that one just was */
|
|
1254
|
+
declare function EditStatus({ saved }: {
|
|
1255
|
+
saved: boolean;
|
|
1256
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1257
|
+
/**
|
|
1258
|
+
* The card: next to the node (pointing at it), its header (the kicker, e.g. the node's type, with a close button,
|
|
1259
|
+
* and what's under it, e.g. its edit form) and its body. Pressing anything outside it, or Escape, closes it, saving
|
|
1260
|
+
* what's being typed first.
|
|
1261
|
+
*/
|
|
1262
|
+
declare function NodeCard({ nodeId, containerRef, label, kicker, header, children, width, bottomInset, onClose, }: {
|
|
1263
|
+
nodeId: string;
|
|
1264
|
+
/** The canvas the node is on, to place the card next to it */
|
|
1265
|
+
containerRef: RefObject<HTMLElement | null>;
|
|
1266
|
+
/** What the card is about, for screen readers */
|
|
1267
|
+
label: string;
|
|
1268
|
+
/** The top line, e.g. the node's type and where it is */
|
|
1269
|
+
kicker: ReactNode;
|
|
1270
|
+
/** The rest of the header, e.g. the node's name and summary, or the form to edit them */
|
|
1271
|
+
header: ReactNode;
|
|
1272
|
+
/** Under the header, e.g. what it's connected to */
|
|
1273
|
+
children?: ReactNode;
|
|
1274
|
+
width?: number;
|
|
1275
|
+
/** Room to keep clear at the bottom of the canvas (e.g. its controls) */
|
|
1276
|
+
bottomInset?: number;
|
|
1277
|
+
onClose: () => void;
|
|
1278
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1279
|
+
/**
|
|
1280
|
+
* A field of the resource's, saved as it's typed (and when it's left, or the card closes): there's nothing to press
|
|
1281
|
+
* to keep a change. Escape puts it back as it was when it was clicked into.
|
|
1282
|
+
*/
|
|
1283
|
+
declare function EditableText({ value, onSave, multiline, placeholder, className, label, required, onSaved, }: {
|
|
1284
|
+
value: string;
|
|
1285
|
+
onSave: (value: string) => void;
|
|
1286
|
+
multiline?: boolean;
|
|
1287
|
+
placeholder?: string;
|
|
1288
|
+
className?: string;
|
|
1289
|
+
label: string;
|
|
1290
|
+
required?: boolean;
|
|
1291
|
+
/** Something was saved (to say so) */
|
|
1292
|
+
onSaved?: () => void;
|
|
1293
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1294
|
+
/** A field with its name above it */
|
|
1295
|
+
declare function Field({ label, children, className, }: {
|
|
1296
|
+
label: string;
|
|
1297
|
+
children: ReactNode;
|
|
1298
|
+
className?: string;
|
|
1299
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* Someone's picture from their sign-in provider (SSO), loaded from there. By default it covers their avatar's
|
|
1303
|
+
* initials, which show again if it doesn't load (blocked, expired, or no longer there).
|
|
1304
|
+
*/
|
|
1305
|
+
declare function Picture({ src, className, }: {
|
|
1306
|
+
src?: string;
|
|
1307
|
+
className?: string;
|
|
1308
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
1309
|
+
|
|
1310
|
+
declare const TeamNode: react.NamedExoticComponent<NodeProps>;
|
|
1311
|
+
|
|
1312
|
+
export { AgentMark, type Author, CANVAS_STATUSES, CANVAS_STATUS_LOOK, CATALOG_COLLECTIONS, CATALOG_GROUPS, CLIPBOARD_TYPE, COMPONENT_DRAG_TYPE, COMPONENT_TYPES, COUNTER_SCALE, type CanvasContent, _default$1 as CanvasControls, type CanvasMeta, CanvasPortal, type CanvasStatus, CardPreview, type CatalogCollection, ClaudeCodeLogo, ClaudeLogo, type ClipboardContent, type CommentAnchor, CommentLayer, CommentList, type ContainerChoice, ContainerChooser, ContextMenu, type ContextMenuState, CursorLogo, DEFAULT_NODE_SIZE, DIAGRAM_FILE_EXTENSION, DROP_GAP, type DiagramLevel, type DiagramLevels, type DragPreview, DropTargetContext, EDGE_COLOR, EDGE_MARKER, ENTITY_SOURCE_HANDLE, EdgeLabelPortal, EdgeRoute, _default as EditButton, EditStatus, EditableText, type Editing, EditingContext, EmptyCanvas, FIT_VIEW_OPTIONS, Field, GROUP_PADDING, GROUP_TYPES, type Hotspot, type HotspotDraft, type HotspotFields, HotspotLayer, HotspotList, type HotspotPlace, type HotspotSeverity, type HotspotSpot, KEEP_IN_CONTAINER_MARGIN, LEVELS, LOCAL_EDGE_KEYS, LOCAL_NODE_KEYS, type LayoutResult, type Level, type LevelPlace, type Link, type LiveMoves, type LivePeer, LockedContext, MESSAGE_COLLECTIONS, type MenuQuestion, type Message, type ModelCanvasContent, type Moves, type Moving, NODE_CATEGORIES, NODE_ICONS, NodeCard, type NodeCategory, type NodeDefinition, type NodeIcon, type NodePreview, OpenAILogo, Palette, PaletteItem, PaletteSection, type PaletteTab, Picture, type PresenceSource, type RenderedSizes, SEVERITIES, SHORTCUTS, STATUS, type SavedFileHandle, ShortcutsList, type Smoother, type StartDrag, StartWritingContext, type StatusChange, TEXT_FONT_SIZE, TeamNode, type Thread, type UpdateNodeData, UpdateNodeDataContext, ViewControls, addCanvasContent, addHotspots, addNodes, addThreads, agentLogoFor, anchorAt, arrangeLevel, buildNode, bySeverity, canGoInContainer, canHold, canPickWhereToSave, canvasImage, canvasToModel, changesNodesOrEdges, cleanFileName, componentKey, connectNodes, containerFor, copyNodes, createEdge, createHotspot, createSmoother, createThread, deleteEdges, deleteHotspot, deleteNodes, deleteThread, deleteThreads, downloadFile, fileNameFor, findDropTarget, findDropTargets, findGroupAtPoint, fitContainersAround, fitGroup, fitGroupToChildren, getAbsolutePosition, getAbsoluteRect, getAnchorPosition, getCanvasMaps, getCanvasStatus, getDiagramLevel, getDiagramLevelUnavailable, getEdgeLabel, getEdgeRoute, getLayoutPositions, getLevelGraph, getLevelNodeSize, getLevelUnavailableReason, getLock, getNodeDefinition, getNodeLabel, getNodeName, getNodeSize, getNoteLevel, getResource, getStructureKey, isCatalogCollection, isDecided, isGroupType, isOnLevel, isShownOnLevel, isVersioned, isWrittenOnCanvas, keepDragged, keepUnchanged, layoutGraph, levelPlaces, modelToCanvas, moveNode, moveThread, moveThreads, nodeDefinitions, nodeTypes, pasteNodes, patchShared, pickDiagramFile, placeDropped, placeNode, placeNodes, previewContainerGrowth, readCanvas, readClipboard, readDiagramFile, readHotspots, readLevelLayout, readMeta, readThreads, reconnectEdge, reopenIfDecided, reopeningIfEdited, replyToThread, resizeNode, resourceNodeData, resourceTypeOf, saveToPickedFile, sendMove, setCanvasStatus, setEdgeLabel, setEdgeRoutes, setLevelPlaces, setLock, setMeta, setNodeParent, setNodeParents, setThreadResolved, shortId, sizeOf, sortByHierarchy, timeAgo, toDiagramFile, toRelativePosition, toSharedNode, topSeverity, updateHotspot, updateNodeData, updateResource, useCounterScale, useDragGhost, useNodeAnchor, useRemoteMoves, useSavedFlash, withDescendants, withDiagramRoutes, withGroupSizes, withLocalState, withParent, withPositions };
|