@eventcatalog/core 5.0.0-beta.11 → 5.0.0-beta.13

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.
Files changed (92) hide show
  1. package/README.md +1 -1
  2. package/dist/analytics/analytics.cjs +1 -1
  3. package/dist/analytics/analytics.js +2 -2
  4. package/dist/analytics/log-build.cjs +1 -1
  5. package/dist/analytics/log-build.js +4 -4
  6. package/dist/catalog-runtime.cjs +1 -1
  7. package/dist/{chunk-MFEMKQZ6.js → chunk-BO2HHVZ3.js} +1 -1
  8. package/dist/{chunk-4YFM63DS.js → chunk-JPT3QC5V.js} +1 -1
  9. package/dist/{chunk-76DAAJLV.js → chunk-PLTLW45Q.js} +5 -5
  10. package/dist/{chunk-WM4QYMIA.js → chunk-T75DOVFP.js} +1 -1
  11. package/dist/{chunk-LQZ7IG7B.js → chunk-XA5W6DXX.js} +1 -1
  12. package/dist/constants.cjs +1 -1
  13. package/dist/constants.js +1 -1
  14. package/dist/eventcatalog.cjs +2 -2
  15. package/dist/eventcatalog.js +14 -14
  16. package/dist/federation/federate.js +3 -3
  17. package/dist/generate.cjs +1 -1
  18. package/dist/generate.js +3 -3
  19. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
  20. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
  21. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
  22. package/dist/runtime-dependencies/manifest.json +3 -0
  23. package/dist/utils/cli-logger.cjs +1 -1
  24. package/dist/utils/cli-logger.js +2 -2
  25. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
  26. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +42 -3
  27. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
  28. package/eventcatalog/src/components/NavigationProgress.astro +7 -0
  29. package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
  30. package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +3 -3
  31. package/eventcatalog/src/features/mcp/apps/canvas/styles.css +3 -1
  32. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +7 -5
  33. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +132 -70
  34. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +71 -71
  35. package/eventcatalog/src/features/studio/agent-choreography.ts +13 -10
  36. package/eventcatalog/src/features/studio/agents.ts +28 -0
  37. package/eventcatalog/src/features/studio/api/canvases-api.ts +64 -15
  38. package/eventcatalog/src/features/studio/api/client.ts +6 -1
  39. package/eventcatalog/src/features/studio/canvas-actions.ts +160 -34
  40. package/eventcatalog/src/features/studio/canvas-preview.ts +1 -2
  41. package/eventcatalog/src/features/studio/catalog.ts +16 -14
  42. package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +263 -115
  43. package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +15 -14
  44. package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +272 -119
  45. package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +1 -2
  46. package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +28 -44
  47. package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
  48. package/eventcatalog/src/features/studio/components/JoinForm.tsx +101 -26
  49. package/eventcatalog/src/features/studio/components/LeftPanel.tsx +119 -230
  50. package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
  51. package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
  52. package/eventcatalog/src/features/studio/components/Presence.tsx +52 -45
  53. package/eventcatalog/src/features/studio/components/ShareDialog.tsx +277 -141
  54. package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
  55. package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +1286 -411
  56. package/eventcatalog/src/features/studio/document-name.ts +7 -0
  57. package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
  58. package/eventcatalog/src/features/studio/hooks/presence-sender.ts +19 -2
  59. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +9 -3
  60. package/eventcatalog/src/features/studio/hooks/use-comments.ts +2 -2
  61. package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +8 -3
  62. package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +223 -30
  63. package/eventcatalog/src/features/studio/identity.ts +23 -1
  64. package/eventcatalog/src/features/studio/limits.ts +2 -0
  65. package/eventcatalog/src/features/studio/pages/[id].astro +2 -1
  66. package/eventcatalog/src/features/studio/pages/index.astro +2 -2
  67. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +8 -23
  68. package/eventcatalog/src/features/studio/server/runtime.ts +16 -9
  69. package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
  70. package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
  71. package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
  72. package/package.json +3 -3
  73. package/eventcatalog/src/features/studio/canvas-doc.ts +0 -414
  74. package/eventcatalog/src/features/studio/clipboard.ts +0 -97
  75. package/eventcatalog/src/features/studio/components/CanvasControls.tsx +0 -154
  76. package/eventcatalog/src/features/studio/components/Comments.tsx +0 -770
  77. package/eventcatalog/src/features/studio/components/ContainerChooser.tsx +0 -145
  78. package/eventcatalog/src/features/studio/components/DragGhost.tsx +0 -115
  79. package/eventcatalog/src/features/studio/components/Picture.tsx +0 -21
  80. package/eventcatalog/src/features/studio/components/PropertiesPanel.tsx +0 -212
  81. package/eventcatalog/src/features/studio/components/canvas-nodes.tsx +0 -255
  82. package/eventcatalog/src/features/studio/components/icons.ts +0 -53
  83. package/eventcatalog/src/features/studio/components/status.ts +0 -53
  84. package/eventcatalog/src/features/studio/edges.ts +0 -56
  85. package/eventcatalog/src/features/studio/flow-state.ts +0 -92
  86. package/eventcatalog/src/features/studio/grouping.ts +0 -226
  87. package/eventcatalog/src/features/studio/layout.ts +0 -42
  88. package/eventcatalog/src/features/studio/levels.ts +0 -282
  89. package/eventcatalog/src/features/studio/node-types.ts +0 -205
  90. package/eventcatalog/src/features/studio/placement.ts +0 -197
  91. package/eventcatalog/src/features/studio/studio.css +0 -273
  92. package/dist/{chunk-KHACCTW2.js → chunk-ZEY2XUJN.js} +4 -4
@@ -1,205 +0,0 @@
1
- /**
2
- * The kinds of node people (and agents) can put on a canvas, and what's in their data. Shared by the browser and
3
- * the server, so it has no UI in it: the icons for each type are in components/icons.ts.
4
- */
5
-
6
- export type NodeCategory = 'boundaries' | 'messages' | 'architecture' | 'other';
7
-
8
- export const NODE_CATEGORIES: { id: NodeCategory; label: string }[] = [
9
- { id: 'boundaries', label: 'Boundaries' },
10
- { id: 'messages', label: 'Messages' },
11
- { id: 'architecture', label: 'Architecture' },
12
- { id: 'other', label: 'Other' },
13
- ];
14
-
15
- /** Domains and systems as containers: boxes other nodes sit in */
16
- export const GROUP_TYPES = { domain: 'domain-group', system: 'system-group' } as const;
17
- export const isGroupType = (type?: string) => type === GROUP_TYPES.domain || type === GROUP_TYPES.system;
18
-
19
- export type NodeDefinition = {
20
- /** React Flow node type, matches the visualiser node component keys */
21
- type: string;
22
- label: string;
23
- category: NodeCategory;
24
- /** Not in the components list (still drawn, for canvases that have them) */
25
- unlisted?: boolean;
26
- /** Where the resource lives inside node.data (e.g. `service` for data.service). Undefined = data root */
27
- resourceKey?: string;
28
- /** Nodes that are sized on the canvas (containers, notes, text) start at this size */
29
- defaultSize?: { width: number; height: number };
30
- /** Only its width is set: its height follows what's in it (text) */
31
- autoHeight?: boolean;
32
- createData: () => Record<string, unknown>;
33
- };
34
-
35
- const resource = (name: string, summary: string) => ({ name, version: '0.0.1', summary, owners: [] });
36
-
37
- export const nodeDefinitions: NodeDefinition[] = [
38
- {
39
- type: GROUP_TYPES.domain,
40
- label: 'Domain',
41
- category: 'boundaries',
42
- resourceKey: 'domain',
43
- defaultSize: { width: 720, height: 460 },
44
- createData: () => ({ domain: { name: 'New Domain', version: '0.0.1' } }),
45
- },
46
- {
47
- type: GROUP_TYPES.system,
48
- label: 'System',
49
- category: 'boundaries',
50
- resourceKey: 'system',
51
- defaultSize: { width: 560, height: 360 },
52
- createData: () => ({ system: { name: 'New System', version: '0.0.1' } }),
53
- },
54
- {
55
- type: 'service',
56
- category: 'architecture',
57
- label: 'Service',
58
- resourceKey: 'service',
59
- createData: () => ({
60
- mode: 'full',
61
- service: { ...resource('New Service', 'Describe what this service does.'), sends: [], receives: [] },
62
- }),
63
- },
64
- {
65
- type: 'event',
66
- category: 'messages',
67
- label: 'Event',
68
- resourceKey: 'message',
69
- createData: () => ({ mode: 'full', message: resource('NewEvent', 'Something happened.') }),
70
- },
71
- {
72
- type: 'command',
73
- category: 'messages',
74
- label: 'Command',
75
- resourceKey: 'message',
76
- createData: () => ({ mode: 'full', message: resource('NewCommand', 'Asks a service to do something.') }),
77
- },
78
- {
79
- type: 'query',
80
- category: 'messages',
81
- label: 'Query',
82
- resourceKey: 'message',
83
- createData: () => ({ mode: 'full', message: resource('NewQuery', 'Asks for some information.') }),
84
- },
85
- {
86
- type: 'channel',
87
- category: 'architecture',
88
- label: 'Channel',
89
- resourceKey: 'channel',
90
- createData: () => ({ mode: 'full', channel: { ...resource('new.channel', 'Where messages are routed.'), protocols: [] } }),
91
- },
92
- {
93
- type: 'data',
94
- category: 'architecture',
95
- label: 'Data Store',
96
- resourceKey: 'data',
97
- createData: () => ({ mode: 'full', data: { ...resource('New Database', 'Stores data.'), container_type: 'database' } }),
98
- },
99
- {
100
- type: 'view',
101
- category: 'architecture',
102
- label: 'View',
103
- resourceKey: 'view',
104
- createData: () => ({ mode: 'full', view: resource('New View', 'What the user sees.') }),
105
- },
106
- {
107
- type: 'externalSystem',
108
- category: 'architecture',
109
- unlisted: true,
110
- label: 'External System',
111
- resourceKey: 'externalSystem',
112
- createData: () => ({ mode: 'full', externalSystem: resource('External System', 'A third party system.') }),
113
- },
114
- {
115
- type: 'agent',
116
- category: 'architecture',
117
- label: 'Agent',
118
- resourceKey: 'agent',
119
- createData: () => ({ mode: 'full', agent: { ...resource('New Agent', 'What this agent does.'), sends: [], receives: [] } }),
120
- },
121
- {
122
- type: 'actor',
123
- category: 'other',
124
- label: 'Actor',
125
- createData: () => ({ mode: 'full', name: 'Customer', summary: 'A person using the system.' }),
126
- },
127
- {
128
- type: 'text',
129
- category: 'other',
130
- label: 'Text',
131
- defaultSize: { width: 240, height: 40 },
132
- autoHeight: true,
133
- createData: () => ({ text: 'Add some text' }),
134
- },
135
- {
136
- type: 'note',
137
- category: 'other',
138
- label: 'Sticky Note',
139
- // About square, like a post-it (taller if more is written on it)
140
- defaultSize: { width: 200, height: 176 },
141
- autoHeight: true,
142
- createData: () => ({ text: '', color: 'yellow' }),
143
- },
144
- ];
145
-
146
- /** The types people and agents can add (not the unlisted ones) */
147
- export const COMPONENT_TYPES = nodeDefinitions
148
- .filter((definition) => !definition.unlisted)
149
- .map((definition) => definition.type) as [string, ...string[]];
150
-
151
- const definitionsByType = new Map(nodeDefinitions.map((definition) => [definition.type, definition]));
152
-
153
- // Node types that only come from the catalog (not in the components list)
154
- const catalogNodeTypes: Record<string, { label: string; resourceKey: string }> = {
155
- system: { label: 'System', resourceKey: 'system' },
156
- 'context-domain': { label: 'Domain', resourceKey: 'domain' },
157
- };
158
-
159
- export const getNodeDefinition = (type?: string) => (type ? definitionsByType.get(type) : undefined);
160
-
161
- export const getNodeLabel = (type?: string) => getNodeDefinition(type)?.label ?? catalogNodeTypes[type ?? '']?.label ?? 'Node';
162
-
163
- // About the size the visualiser's resource cards render at
164
- export const DEFAULT_NODE_SIZE = { width: 240, height: 112 };
165
-
166
- /** The size a new node of a type starts at (or, for cards, about the size it renders at) */
167
- export const getNodeSize = (type?: string) => getNodeDefinition(type)?.defaultSize ?? DEFAULT_NODE_SIZE;
168
-
169
- const getResourceKey = (type?: string) => getNodeDefinition(type)?.resourceKey ?? catalogNodeTypes[type ?? '']?.resourceKey;
170
-
171
- /** The resource object for a node (e.g. data.service), or the data itself for flat nodes */
172
- export const getResource = (type: string | undefined, data: Record<string, unknown>): Record<string, unknown> => {
173
- const key = getResourceKey(type);
174
- return key ? ((data[key] as Record<string, unknown>) ?? {}) : data;
175
- };
176
-
177
- /** Sticky notes sit on the canvas wherever they're put, never inside a container */
178
- export const canGoInContainer = (type?: string) => type !== 'note';
179
-
180
- /**
181
- * The level (L1, L2 or L3) a sticky note was added on: it's only shown there. Notes from before levels had notes
182
- * are on L3, the canvas people edit. Other nodes are on every level they fit in, so they have none.
183
- */
184
- export const getNoteLevel = (node: { type?: string; data: Record<string, unknown> }): 1 | 2 | 3 | undefined =>
185
- node.type === 'note' ? ((node.data.level as 1 | 2 | undefined) ?? 3) : undefined;
186
-
187
- /** Whether a node is on a level's canvas as itself: everything on L3, but notes only on the level they were added on */
188
- export const isOnLevel = (node: { type?: string; data: Record<string, unknown> }, level: 1 | 2 | 3) =>
189
- node.type !== 'note' || getNoteLevel(node) === level;
190
-
191
- /** Notes and text: written on the canvas itself (their text is their data, not a resource with a name) */
192
- export const isWrittenOnCanvas = (type?: string) => type === 'note' || type === 'text';
193
-
194
- /** Whether a node's resource has a version (actors and notes don't) */
195
- export const isVersioned = (type?: string) => !!getResourceKey(type);
196
-
197
- /** Returns new node data with fields of the resource changed */
198
- export const updateResource = (type: string | undefined, data: Record<string, unknown>, fields: Record<string, unknown>) => {
199
- const key = getResourceKey(type);
200
- return key ? { ...data, [key]: { ...getResource(type, data), ...fields } } : { ...data, ...fields };
201
- };
202
-
203
- /** A node's name, as people and agents see it */
204
- export const getNodeName = (type: string | undefined, data: Record<string, unknown>, fallback = 'node') =>
205
- String(getResource(type, data).name ?? data.text ?? fallback);
@@ -1,197 +0,0 @@
1
- import type { Node, XYPosition } from '@xyflow/react';
2
- import { fitGroup, getAbsolutePosition, GROUP_PADDING, sizeOf } from './grouping';
3
-
4
- /**
5
- * Where agents put what they add without a position, the way an architect would: next to what it connects to,
6
- * following the flow left to right (what sends to it on its left, what it sends to on its right), lined up with
7
- * it, and stacked around it when several things connect to the same node. Nothing already on the canvas moves.
8
- * Things connected to nothing go in a row below what's there. Containers are filled first, so what goes around them
9
- * leaves room for what they grow to hold.
10
- */
11
-
12
- /** A connection between two nodes, from the one that sends to the one that receives */
13
- export type Link = { source: string; target: string };
14
-
15
- /** Between connected nodes side by side (room for the labels on their connections), and nodes stacked in a column */
16
- const GAP = { x: 200, y: 80 };
17
- /** The least room left around nodes */
18
- const CLEARANCE = { x: 120, y: 60 };
19
- /** Between what's on the canvas and things connected to nothing added below it */
20
- const BELOW_GAP = 120;
21
- /** Things connected to nothing go side by side (so connecting them later is easy), this many to a row */
22
- const ROW_LENGTH = 4;
23
- /** How far to look for free space before giving up and going below everything */
24
- const MAX_ROWS = 12;
25
- const MAX_COLUMNS = 8;
26
-
27
- type Rect = { x: number; y: number; width: number; height: number };
28
- type Anchor = { rect: Rect; centerY: number; upstream: boolean };
29
-
30
- const rectOf = (node: Node): Rect => ({ ...node.position, ...sizeOf(node) });
31
-
32
- const tooClose = (a: Rect, b: Rect) =>
33
- a.x < b.x + b.width + CLEARANCE.x &&
34
- b.x < a.x + a.width + CLEARANCE.x &&
35
- a.y < b.y + b.height + CLEARANCE.y &&
36
- b.y < a.y + a.height + CLEARANCE.y;
37
-
38
- const below = (rects: Rect[]): XYPosition => ({
39
- x: Math.min(...rects.map((rect) => rect.x)),
40
- y: Math.max(...rects.map((rect) => rect.y + rect.height)) + BELOW_GAP,
41
- });
42
-
43
- /** 0, 1, -1, 2, -2, …: rows below and above in turn, so things stacked around a node stay centred on it */
44
- const rowOffset = (row: number) => (row % 2 ? (row + 1) / 2 : -row / 2);
45
-
46
- /**
47
- * Where the nodes to place go on the canvas (not relative to their containers: containers that grow to fit them
48
- * move what's in them, but nothing moves on the canvas). They're among `nodes`, with their containers set.
49
- * Everything else in `nodes` stays where it is.
50
- */
51
- export const placeNodes = (nodes: Node[], toPlace: string[], links: Link[]): Map<string, XYPosition> => {
52
- const working = new Map(nodes.map((node) => [node.id, node]));
53
- const remaining = new Set(toPlace.filter((id) => working.has(id)));
54
-
55
- const neighbours = new Map<string, { id: string; upstream: boolean }[]>();
56
- const addNeighbour = (id: string, neighbour: { id: string; upstream: boolean }) =>
57
- neighbours.set(id, [...(neighbours.get(id) ?? []), neighbour]);
58
- for (const { source, target } of links) {
59
- addNeighbour(source, { id: target, upstream: false });
60
- addNeighbour(target, { id: source, upstream: true });
61
- }
62
-
63
- const depthOf = (id: string | undefined): number => {
64
- const node = id ? working.get(id) : undefined;
65
- return node ? 1 + depthOf(node.parentId) : 0;
66
- };
67
-
68
- /** The node, or the container it's in, that sits directly in a level (undefined for the canvas itself) */
69
- const atLevel = (id: string, level: string | undefined) => {
70
- let node = working.get(id);
71
- while (node && node.parentId !== level) node = node.parentId ? working.get(node.parentId) : undefined;
72
- return node;
73
- };
74
-
75
- /** The containers things are put in grow to fit them (innermost first), as they will on the canvas */
76
- const growing = new Set<string>();
77
- for (const id of remaining) {
78
- for (let parentId = working.get(id)!.parentId; parentId; parentId = working.get(parentId)?.parentId) growing.add(parentId);
79
- }
80
- const fitContainers = () => {
81
- const containers = [...growing].filter((id) => working.has(id)).sort((a, b) => depthOf(b) - depthOf(a));
82
- for (const id of containers) {
83
- const fit = fitGroup(
84
- id,
85
- [...working.values()].filter((node) => node.parentId !== id || !remaining.has(node.id))
86
- );
87
- if (!fit) continue;
88
- const container = working.get(id)!;
89
- // Growing up or left: what's in it moves the other way, so nothing moves on the canvas
90
- if (fit.shift.x || fit.shift.y) {
91
- for (const child of working.values()) {
92
- if (child.parentId !== id) continue;
93
- working.set(child.id, {
94
- ...child,
95
- position: { x: child.position.x + fit.shift.x, y: child.position.y + fit.shift.y },
96
- });
97
- }
98
- }
99
- working.set(id, {
100
- ...container,
101
- position: { x: container.position.x - fit.shift.x, y: container.position.y - fit.shift.y },
102
- width: fit.width,
103
- height: fit.height,
104
- });
105
- }
106
- };
107
-
108
- // Innermost containers first, then outwards to the canvas
109
- const levels = [...new Set([...remaining].map((id) => working.get(id)!.parentId))].sort((a, b) => depthOf(b) - depthOf(a));
110
-
111
- for (const level of levels) {
112
- fitContainers();
113
- const parent = level ? working.get(level) : undefined;
114
- const origin = parent ? getAbsolutePosition(parent, working) : { x: 0, y: 0 };
115
- // Inside a container: clear of its header, so it grows down rather than up
116
- const start = parent ? { x: GROUP_PADDING.left, y: GROUP_PADDING.top } : { x: 0, y: 0 };
117
- const todo = [...remaining].filter((id) => working.get(id)!.parentId === level);
118
- // Things connected to nothing: a row below what was there, each to the right of what's been placed so far
119
- let row: { y: number; length: number } | undefined;
120
- const placedHere: Rect[] = [];
121
- const obstacles = () =>
122
- [...working.values()].filter((node) => node.parentId === level && !remaining.has(node.id)).map(rectOf);
123
-
124
- /** What a node connects to that's already placed: the node itself, or the container it's in, at this level */
125
- const anchorsOf = (id: string): Anchor[] =>
126
- (neighbours.get(id) ?? []).flatMap(({ id: other, upstream }) => {
127
- const shown = atLevel(other, level);
128
- if (!shown || shown.id === id || remaining.has(shown.id)) return [];
129
- const node = working.get(other)!;
130
- // Lined up with the node itself, even when it's inside a container
131
- const centerY = getAbsolutePosition(node, working).y + sizeOf(node).height / 2 - origin.y;
132
- return [{ rect: rectOf(shown), centerY, upstream }];
133
- });
134
-
135
- /** Nothing placed to start from: the first one with nothing sending to it, so the flow grows left to right */
136
- const startOf = (ids: string[]) =>
137
- ids.find(
138
- (id) =>
139
- !(neighbours.get(id) ?? []).some(({ id: other, upstream }) => {
140
- const shown = atLevel(other, level);
141
- return upstream && shown && shown.id !== id && ids.includes(shown.id);
142
- })
143
- ) ?? ids[0];
144
-
145
- const findSpot = (from: XYPosition, size: { width: number; height: number }, direction: 1 | -1): XYPosition => {
146
- const rects = obstacles();
147
- for (let column = 0; column < MAX_COLUMNS; column++) {
148
- const x = from.x + direction * column * (size.width + GAP.x);
149
- for (let row = 0; row < MAX_ROWS * 2; row++) {
150
- const spot = { x, y: from.y + rowOffset(row) * (size.height + GAP.y) };
151
- if (parent && spot.y < GROUP_PADDING.top) continue;
152
- if (!rects.some((rect) => tooClose({ ...spot, ...size }, rect))) return spot;
153
- }
154
- }
155
- return rects.length ? below(rects) : start;
156
- };
157
-
158
- while (todo.length > 0) {
159
- const candidates = todo.map((id) => ({ id, anchors: anchorsOf(id) }));
160
- const connected = candidates.reduce((best, candidate) =>
161
- candidate.anchors.length > best.anchors.length ? candidate : best
162
- );
163
- const { id, anchors } = connected.anchors.length > 0 ? connected : { id: startOf(todo), anchors: [] };
164
- const node = working.get(id)!;
165
- const size = sizeOf(node);
166
-
167
- let spot: XYPosition;
168
- if (anchors.length > 0) {
169
- const upstream = anchors.filter((anchor) => anchor.upstream);
170
- const downstream = anchors.filter((anchor) => !anchor.upstream);
171
- const x = upstream.length
172
- ? Math.max(...upstream.map(({ rect }) => rect.x + rect.width)) + GAP.x
173
- : Math.min(...downstream.map(({ rect }) => rect.x)) - GAP.x - size.width;
174
- const centerY = anchors.reduce((sum, anchor) => sum + anchor.centerY, 0) / anchors.length;
175
- spot = findSpot({ x, y: centerY - size.height / 2 }, size, upstream.length ? 1 : -1);
176
- } else {
177
- const rects = obstacles();
178
- if (!row || row.length === ROW_LENGTH) {
179
- spot = findSpot(rects.length ? below(rects) : start, size, 1);
180
- row = { y: spot.y, length: 1 };
181
- } else {
182
- spot = findSpot({ x: Math.max(...placedHere.map((rect) => rect.x + rect.width)) + GAP.x, y: row.y }, size, 1);
183
- row.length++;
184
- }
185
- }
186
-
187
- const position = { x: Math.round(spot.x), y: Math.round(spot.y) };
188
- working.set(id, { ...node, position });
189
- placedHere.push({ ...position, ...size });
190
- remaining.delete(id);
191
- todo.splice(todo.indexOf(id), 1);
192
- }
193
- }
194
-
195
- fitContainers();
196
- return new Map(toPlace.filter((id) => working.has(id)).map((id) => [id, getAbsolutePosition(working.get(id)!, working)]));
197
- };
@@ -1,273 +0,0 @@
1
- /*
2
- * The visualiser hides node handles (it is read only), here they are the connection points,
3
- * so show them on hover, on selection and while connecting.
4
- */
5
- .studio-canvas .react-flow__handle {
6
- opacity: 0 !important;
7
- width: 11px;
8
- height: 11px;
9
- background: rgb(var(--ec-card-bg));
10
- border: 2px solid rgb(var(--ec-accent));
11
- transition: opacity 0.15s ease;
12
- z-index: 30;
13
- }
14
-
15
- .studio-canvas .react-flow__node:hover .react-flow__handle,
16
- .studio-canvas .react-flow__node.selected .react-flow__handle,
17
- .studio-canvas .react-flow__pane.connecting ~ * .react-flow__handle,
18
- .studio-canvas .react-flow__handle.connectingto {
19
- opacity: 1 !important;
20
- }
21
-
22
- .studio-canvas .react-flow__handle:hover,
23
- .studio-canvas .react-flow__handle.connectingto {
24
- background: rgb(var(--ec-accent));
25
- }
26
-
27
- .studio-canvas .react-flow__handle.connectingto.valid {
28
- background: rgb(var(--ec-badge-color-green-text));
29
- border-color: rgb(var(--ec-badge-color-green-text));
30
- }
31
-
32
- .studio-canvas .react-flow__edge.selected .react-flow__edge-path,
33
- .studio-canvas .react-flow__edge:hover .react-flow__edge-path {
34
- stroke: rgb(var(--ec-accent)) !important;
35
- stroke-width: 2px !important;
36
- }
37
-
38
- /*
39
- * A connection being dragged (a new one, or an end moved to another node): once it's on a node it can connect
40
- * to, it turns the hover colour, so you know letting go will connect it
41
- */
42
- .studio-canvas .react-flow__connection-path {
43
- stroke-width: 2px;
44
- transition: stroke 100ms ease;
45
- }
46
- /* Like a hovered edge in the visualiser: glowing, with the dashes flowing (ec-edge-dash, from its styles) */
47
- .studio-canvas .react-flow__connection.valid .react-flow__connection-path {
48
- stroke: #2563eb;
49
- stroke-width: 4px;
50
- filter: drop-shadow(0 0 8px #2563eb);
51
- stroke-dasharray: 5 5;
52
- animation: ec-edge-dash 0.5s linear infinite;
53
- }
54
- [data-theme='dark'] .studio-canvas .react-flow__connection.valid .react-flow__connection-path {
55
- stroke: #00d9ff;
56
- filter: drop-shadow(0 0 8px #00d9ff);
57
- }
58
-
59
- /* Comment mode (C): click anywhere to drop a comment */
60
- .studio-canvas.comment-mode .react-flow__pane,
61
- .studio-canvas.comment-mode .react-flow__node {
62
- cursor: crosshair !important;
63
- }
64
-
65
- /* After a layout or a level change, nodes glide to their new place */
66
- .studio-canvas.is-layout-transition .react-flow__node {
67
- transition: transform 450ms ease;
68
- }
69
-
70
- /* Edges are drawn where the nodes end up, so they wait for the glide to finish */
71
- .studio-canvas .react-flow__edges {
72
- transition: opacity 200ms ease;
73
- }
74
- .studio-canvas.is-layout-transition .react-flow__edges {
75
- opacity: 0;
76
- transition: none;
77
- }
78
-
79
- /* Containers grow smoothly when they're fitted to what's in them, or someone else resizes them */
80
- .studio-canvas.is-container-resize :is(.react-flow__node-domain-group, .react-flow__node-system-group) {
81
- transition:
82
- width 300ms ease,
83
- height 300ms ease,
84
- transform 300ms ease;
85
- }
86
-
87
- /* Nodes pop in when they're added (by anyone, people or agents) */
88
- @keyframes studio-node-in {
89
- from {
90
- opacity: 0;
91
- scale: 0.85;
92
- }
93
- to {
94
- opacity: 1;
95
- scale: 1;
96
- }
97
- }
98
- .studio-canvas .react-flow__node {
99
- animation: studio-node-in 220ms ease-out;
100
- }
101
-
102
- /* No React Flow attribution on the canvas */
103
- .studio-canvas .react-flow__attribution {
104
- display: none;
105
- }
106
-
107
- /*
108
- * Containers let the pointer through their body to the canvas (so dragging inside one draws a selection box),
109
- * and are moved, selected and resized by their header and resize handles. React Flow sets pointer-events inline.
110
- */
111
- .studio-canvas .react-flow__node-domain-group,
112
- .studio-canvas .react-flow__node-system-group {
113
- pointer-events: none !important;
114
- }
115
- .studio-canvas .react-flow__node-domain-group :is(.ec-group-grip, .react-flow__resize-control),
116
- .studio-canvas .react-flow__node-system-group :is(.ec-group-grip, .react-flow__resize-control) {
117
- pointer-events: auto;
118
- }
119
-
120
- /*
121
- * Selected nodes get an accent outline and glow around them, and hovered ones a faint outline (like Figma and
122
- * Miro). Drawn by the node's wrapper, so it fits every kind of node. Containers draw their own.
123
- */
124
- .studio-canvas .react-flow__node:not(.react-flow__node-domain-group, .react-flow__node-system-group)::after {
125
- content: '';
126
- position: absolute;
127
- inset: -6px;
128
- border-radius: 12px;
129
- border: 2px solid transparent;
130
- pointer-events: none;
131
- transition:
132
- border-color 120ms ease,
133
- box-shadow 120ms ease;
134
- }
135
- .studio-canvas .react-flow__node:not(.react-flow__node-domain-group, .react-flow__node-system-group):hover::after {
136
- border-color: rgb(var(--ec-accent) / 0.35);
137
- }
138
- .studio-canvas .react-flow__node.selected:not(.react-flow__node-domain-group, .react-flow__node-system-group)::after {
139
- border-color: rgb(var(--ec-accent));
140
- box-shadow: 0 0 0 4px rgb(var(--ec-accent) / 0.15);
141
- }
142
-
143
- /* A selected or hovered connection shows the ends you can drag to another node */
144
- .studio-canvas .react-flow__edge .react-flow__edgeupdater {
145
- cursor: grab;
146
- }
147
- .studio-canvas .react-flow__edge:is(.selected, :hover) .react-flow__edgeupdater {
148
- fill: rgb(var(--ec-accent) / 0.2);
149
- stroke: rgb(var(--ec-accent));
150
- stroke-width: 1.5;
151
- }
152
-
153
- /* A selected container */
154
- .studio-canvas .ec-group-selected {
155
- position: absolute;
156
- inset: 0;
157
- border-radius: 14px;
158
- box-shadow: 0 0 0 2px rgb(var(--ec-accent) / 0.7);
159
- pointer-events: none;
160
- }
161
-
162
- /* A container something's dragged over: it goes in it when it's dropped (in the theme's button colours, which stay
163
- strong in light and dark themes) */
164
- .studio-canvas .ec-drop-target {
165
- position: absolute;
166
- inset: -5px;
167
- border-radius: 17px;
168
- border: 3px solid rgb(var(--ec-button-bg));
169
- background: rgb(var(--ec-button-bg) / 0.1);
170
- box-shadow: 0 0 0 6px rgb(var(--ec-button-bg) / 0.2);
171
- pointer-events: none;
172
- animation: ec-drop-target-in 150ms ease-out;
173
- }
174
- .studio-canvas .ec-drop-target-label {
175
- position: absolute;
176
- top: -16px;
177
- left: 50%;
178
- translate: -50% 0;
179
- white-space: nowrap;
180
- border-radius: 9999px;
181
- padding: 4px 12px;
182
- font-size: 12px;
183
- font-weight: 600;
184
- color: rgb(var(--ec-button-text));
185
- background: rgb(var(--ec-button-bg));
186
- box-shadow: 0 2px 8px rgb(0 0 0 / 0.25);
187
- }
188
- @keyframes ec-drop-target-in {
189
- from {
190
- opacity: 0;
191
- scale: 0.98;
192
- }
193
- }
194
-
195
- /*
196
- * What's dragged from the left panel (DragGhost): just the node, on nothing. The visualiser's class gives it the page
197
- * background, and its handles are hidden on the canvas only.
198
- */
199
- .studio-drag-preview,
200
- .studio-drag-preview .react-flow,
201
- .studio-drag-preview .react-flow__renderer,
202
- .studio-drag-preview .react-flow__pane {
203
- background: transparent !important;
204
- }
205
- .studio-drag-preview .react-flow__handle {
206
- display: none !important;
207
- }
208
-
209
- /* Text written on the canvas: no card around it, in the page's text colour */
210
- .studio-canvas .studio-text,
211
- .studio-drag-preview .studio-text {
212
- min-width: 80px;
213
- padding: 2px 4px;
214
- }
215
- .studio-text .studio-text-body {
216
- margin: 0;
217
- font-size: 16px;
218
- line-height: 1.4;
219
- color: rgb(var(--ec-page-text));
220
- white-space: pre-wrap;
221
- overflow-wrap: anywhere;
222
- }
223
-
224
- .studio-canvas .studio-width-handle {
225
- border-color: rgb(var(--ec-accent)) !important;
226
- }
227
-
228
- /* A sticky note: about square like a post-it, taller if more is written on it (dark text: the paper is light in
229
- every theme) */
230
- .studio-note {
231
- position: relative;
232
- min-height: 176px;
233
- height: 100%;
234
- padding: 12px 14px;
235
- border: 1px solid;
236
- border-radius: 6px;
237
- box-shadow:
238
- 0 1px 2px rgb(0 0 0 / 0.06),
239
- 0 6px 16px -6px rgb(0 0 0 / 0.18);
240
- color: #1f2937;
241
- }
242
- .studio-note .studio-note-body {
243
- margin: 0;
244
- font-size: 13px;
245
- line-height: 1.45;
246
- color: inherit;
247
- white-space: pre-wrap;
248
- overflow-wrap: anywhere;
249
- }
250
- .studio-note .studio-note-body::placeholder {
251
- color: inherit;
252
- opacity: 0.5;
253
- }
254
- /* Its colours, above it while it's selected */
255
- .studio-note .studio-note-colors {
256
- position: absolute;
257
- bottom: calc(100% + 8px);
258
- left: 0;
259
- display: flex;
260
- gap: 4px;
261
- padding: 4px;
262
- border-radius: 9999px;
263
- border: 1px solid rgb(var(--ec-page-border));
264
- background: rgb(var(--ec-card-bg));
265
- box-shadow: 0 2px 8px rgb(0 0 0 / 0.15);
266
- }
267
- .studio-note .studio-note-color {
268
- width: 16px;
269
- height: 16px;
270
- border-radius: 9999px;
271
- border: 2px solid;
272
- cursor: pointer;
273
- }
@@ -1,3 +1,7 @@
1
+ import {
2
+ getFederationDiagnostics,
3
+ resolveFederationRules
4
+ } from "./chunk-JJPB6EOZ.js";
1
5
  import {
2
6
  withFederationOutputTransaction
3
7
  } from "./chunk-WRNH5C3U.js";
@@ -10,10 +14,6 @@ import {
10
14
  import {
11
15
  createFederationContentCache
12
16
  } from "./chunk-R7Z5ALYI.js";
13
- import {
14
- getFederationDiagnostics,
15
- resolveFederationRules
16
- } from "./chunk-JJPB6EOZ.js";
17
17
  import {
18
18
  getEventCatalogConfigFile
19
19
  } from "./chunk-RFIXKATC.js";