@eventcatalog/visualiser 5.0.1 → 5.1.1

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/studio.mjs CHANGED
@@ -10,23 +10,25 @@ import {
10
10
  ToolbarButton,
11
11
  ToolbarDivider,
12
12
  ZoomLevel,
13
+ addExportBranding,
13
14
  diagramNodeComponents,
15
+ eventcatalog_logo_default,
14
16
  getExportImageDimensions,
15
17
  injectExportStyles,
16
18
  memoNode
17
- } from "./chunk-KVTGV7VK.mjs";
18
- import "./chunk-Q3OM5AL5.mjs";
19
+ } from "./chunk-ZHXUMAPK.mjs";
20
+ import "./chunk-STJQWGKW.mjs";
19
21
  import {
20
22
  getMessagesLabel,
21
23
  getNodeSize,
22
24
  layoutWithElk
23
- } from "./chunk-NOHQEQD7.mjs";
25
+ } from "./chunk-P2DH6CFV.mjs";
24
26
  import {
25
27
  LEVELS,
26
28
  MESSAGE_TYPES,
27
29
  Model,
28
30
  validateModel
29
- } from "./chunk-R3QHQ7LF.mjs";
31
+ } from "./chunk-JB2NMDKX.mjs";
30
32
 
31
33
  // src/studio/canvas-doc.ts
32
34
  import * as Y from "yjs";
@@ -44,7 +46,18 @@ var EDGE_MARKER = {
44
46
  width: 18,
45
47
  height: 18
46
48
  };
47
- var PRODUCERS = ["service", "agent", "actor", "externalSystem", "view"];
49
+ var PRODUCERS = [
50
+ "service",
51
+ "agent",
52
+ "actor",
53
+ "externalSystem",
54
+ "view",
55
+ // Domains send messages themselves too (`sends` in a domain's frontmatter), as a box or a card. Studio on the
56
+ // web's model only has services sending and receiving (model-canvas.ts), so these edges are labelled but not
57
+ // kept as relations there
58
+ "domain-group",
59
+ "context-domain"
60
+ ];
48
61
  var PRODUCER_LABELS = {
49
62
  commands: "invokes",
50
63
  events: "publishes \nevent",
@@ -2413,7 +2426,15 @@ async function modelToCanvas(model, seed) {
2413
2426
  })
2414
2427
  );
2415
2428
  const edges = edgesFor(model, byId2);
2416
- const unplaced = nodes.filter((node) => !saved[node.id]).map((node) => node.id);
2429
+ const lookup = new Map(nodes.map((node) => [node.id, node]));
2430
+ const misplaced = (node) => {
2431
+ const parent = node.parentId ? lookup.get(node.parentId) : void 0;
2432
+ if (!parent || !saved[node.id]) return false;
2433
+ const { x, y } = node.position;
2434
+ const slack = 20;
2435
+ return x < -slack || y < -slack || parent.width !== void 0 && x > parent.width || parent.height !== void 0 && y > parent.height;
2436
+ };
2437
+ const unplaced = nodes.filter((node) => !saved[node.id] || misplaced(node)).map((node) => node.id);
2417
2438
  const seeded = unplaced.length === nodes.length && seed ? placesFromSeed(seed.nodes, nodes) : void 0;
2418
2439
  if (seeded) {
2419
2440
  nodes = nodes.map(
@@ -2435,9 +2456,28 @@ async function modelToCanvas(model, seed) {
2435
2456
  target
2436
2457
  }));
2437
2458
  const places = placeNodes(nodes, unplaced, links);
2438
- nodes = nodes.map(
2439
- (node) => places.has(node.id) ? { ...node, position: places.get(node.id) } : node
2440
- );
2459
+ const byNodeId = new Map(nodes.map((node) => [node.id, node]));
2460
+ const canvasAt = (node) => {
2461
+ const placed = places.get(node.id);
2462
+ if (placed) return placed;
2463
+ const parent = node.parentId ? byNodeId.get(node.parentId) : void 0;
2464
+ if (!parent) return node.position;
2465
+ const parentAt = canvasAt(parent);
2466
+ return {
2467
+ x: parentAt.x + node.position.x,
2468
+ y: parentAt.y + node.position.y
2469
+ };
2470
+ };
2471
+ nodes = nodes.map((node) => {
2472
+ const placed = places.get(node.id);
2473
+ if (!placed) return node;
2474
+ const parent = node.parentId ? byNodeId.get(node.parentId) : void 0;
2475
+ const parentAt = parent ? canvasAt(parent) : { x: 0, y: 0 };
2476
+ return {
2477
+ ...node,
2478
+ position: { x: placed.x - parentAt.x, y: placed.y - parentAt.y }
2479
+ };
2480
+ });
2441
2481
  }
2442
2482
  return {
2443
2483
  nodes: [...nodes, ...notesToNodes(model.notes ?? [], nodes)],
@@ -2836,11 +2876,6 @@ function pickDiagramFile() {
2836
2876
 
2837
2877
  // src/studio/export-image.ts
2838
2878
  import { toPng } from "html-to-image";
2839
-
2840
- // src/studio/eventcatalog-logo.svg?raw
2841
- var eventcatalog_logo_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="58 54 1133 1133" aria-hidden="true" fill="currentColor">\n<path fill-rule="evenodd" d="M 445 55 c -174.6 0.5 -188.1 0.7 -196.5 2.3 -20.7 4.1 -35.3 8.5 -51 15.3 -66.7 28.8 -114.7 86.9 -130.3 157.9 l -2.6 12 -0.4 371 c -0.2 261 0 373.8 0.8 380.5 4.6 40.7 21.8 79.6 49.8 113 36.8 43.9 89.5 72.4 144.2 77.9 5.9 0.6 148.2 1.1 372.5 1.3 345.2 0.3 363.5 0.2 373 -1.5 32.1 -5.7 58.7 -16.4 85.5 -34.2 53.2 -35.3 89.1 -93.6 96 -156 0.8 -7.2 1 -114.8 0.8 -381.5 l -0.3 -371.5 -2.2 -10.4 c -7.7 -35.9 -18.8 -61.1 -38.9 -88.2 -9.5 -12.8 -30.7 -34.1 -43.4 -43.5 -34.4 -25.6 -73.7 -40.9 -113.5 -44.3 -12.5 -1.1 -182.7 -1.1 -543.5 -0.1 z m -26.3 152.5 c 30.7 11.6 86.7 67 122.9 121.6 l 8.4 12.6 9.7 -2.3 c 13.3 -3.3 30.3 -6.1 44.8 -7.5 15.1 -1.5 51.8 -0.6 67.5 1.6 10.1 1.4 37.1 6.8 40.5 8.1 0.7 0.3 3.6 -3.4 7.1 -8.8 18.3 -29.1 37.9 -53.5 64.7 -80.7 31.8 -32.2 53.3 -47 68.5 -47.1 10.5 -0 16.2 4.1 22.3 16.1 11.6 23 17.3 80.8 15 153.1 l -1 33.7 6 14.7 c 3.2 8 7.8 20.4 10 27.5 2.3 7.1 4.5 12.9 4.9 12.9 0.4 -0 5 -1.5 10.1 -3.4 29.8 -10.8 61.4 -20.5 89.6 -27.5 20 -5.1 21.5 -5.1 25.9 -0.7 2.9 2.9 3.4 4.1 3.4 7.9 0 3.4 -0.6 5.3 -2.5 7.5 -2.9 3.5 -4.7 4.3 -18 7.7 -23.9 6.3 -83.7 25.2 -103 32.5 -2 0.8 -2 1.2 -1 10.6 0.5 5.3 1.5 9.9 2 10 0.6 0.2 16.8 2.4 36 4.9 69.6 9.1 85.9 11.5 89.4 13.3 10.1 5.1 7.6 20.4 -3.6 21.9 -3.7 0.5 -26.4 -2.1 -63.3 -7.2 -18.4 -2.6 -56.7 -7.5 -58 -7.5 -0.4 -0 -1 0.8 -1.2 1.8 -0.4 1.4 -2.9 2.3 -11.4 4.1 -50.2 10.3 -130.1 33.1 -167.9 47.8 -35.1 13.6 -69.4 33.4 -96.1 55.6 -6.6 5.4 -10.1 7.7 -10.9 7.1 -0.7 -0.5 -5.8 -4.6 -11.5 -9.2 -25 -20.2 -55.1 -37.5 -88.5 -50.7 -39.3 -15.6 -111.9 -36.5 -170.7 -49.1 -15.1 -3.2 -17.8 -4.1 -17.8 -5.6 0 -2.4 0.4 -2.4 -35 2.2 -16.8 2.2 -42.6 5.7 -57.5 7.6 -14.8 2 -28.8 3.3 -31 3 -5.9 -0.7 -9.5 -5.1 -9.5 -11.5 0 -5.7 2.4 -9.4 7.3 -11 3.2 -1.1 62.2 -9.4 103.2 -14.6 11.6 -1.5 21.3 -3 21.7 -3.3 0.3 -0.4 0.9 -5 1.3 -10.3 l 0.7 -9.6 -30.3 -10.1 c -24.9 -8.3 -73.2 -22.8 -85.2 -25.6 -1.5 -0.4 -4.1 -1.9 -5.8 -3.4 -2.6 -2.3 -3.3 -3.8 -3.7 -8 -0.4 -4.7 -0.2 -5.4 2.5 -8.1 1.7 -1.6 4.3 -3.3 5.8 -3.7 5.8 -1.4 46 9.3 88.5 23.7 16.2 5.5 30.3 10.3 31.2 10.6 1 0.3 2 -0.1 2.2 -0.8 4.6 -14.9 9 -27.2 14 -39.4 l 6.2 -15 -1 -32.5 c -1.5 -48.5 0.9 -93.8 6.4 -122.4 5 -25.8 12 -40.4 21.9 -45.6 5.4 -2.9 15.3 -2.6 23.8 0.5 z m -67.6 364.4 c 81.9 16.7 161.5 40.4 199.3 59.3 11.7 5.9 29.8 16.7 34.1 20.3 l 3 2.6 -9.5 -0.4 c -8.7 -0.4 -15.5 -2.1 -78.5 -19.7 -37.9 -10.6 -76 -21.2 -84.5 -23.5 -56.6 -15.7 -123.4 -34.6 -123.8 -35 -0.6 -0.6 13.5 -2.8 37.3 -5.8 6.4 -0.8 8.6 -0.6 22.6 2.2 z m 589.5 -0.4 c 10.4 1.4 20.4 2.8 22.3 3.2 3.4 0.6 -40.5 13.5 -170.9 50.2 -20.1 5.7 -42.6 12 -50 14.1 -50.1 14.2 -53.5 15 -60.8 15 -4 -0 -7.2 -0.3 -7.2 -0.8 0 -2.1 28.9 -19.2 45.8 -27.1 36.1 -16.7 110.6 -38.3 193.2 -55.9 6.6 -1.4 7.1 -1.4 27.6 1.3 z m -672.2 33.1 c 13.2 3.6 31.1 8.5 39.6 10.9 8.5 2.4 35.3 9.8 59.5 16.4 52.5 14.3 120.5 33 167.5 46.1 22.9 6.3 23.5 6.6 24.9 9.6 0.8 1.8 1.1 45.6 1.1 162.2 0 175.7 0.4 164.2 -5.9 164.2 -1.5 -0 -12.4 -2.7 -24.2 -6.1 -20.6 -5.8 -66.3 -18.6 -188.9 -52.7 -31.6 -8.8 -63.1 -17.5 -70 -19.2 -13 -3.3 -16.3 -4.8 -17.9 -7.8 -1.1 -2.1 -7.2 -104.7 -15.6 -262.7 -1.3 -25.9 -2.7 -50.5 -3.1 -54.7 -0.6 -7.3 -0.5 -7.8 2 -10.2 1.4 -1.5 3.5 -2.6 4.7 -2.6 1.2 -0 13 3 26.3 6.6 z m 748.1 -5.6 c 3.9 1.5 4.4 4.2 3.4 19.5 -0.8 12.4 -4.2 70.9 -15 256 -2.6 46 -3.3 53.3 -4.8 55.5 -1.9 2.6 1.3 1.6 -78.6 23.9 -9.9 2.8 -34.2 9.6 -54 15.1 -19.8 5.6 -50.4 14.1 -68 19 -17.6 4.9 -45.7 12.8 -62.5 17.5 -16.7 4.7 -31.7 8.5 -33.2 8.5 -6.3 -0 -5.9 11.3 -5.6 -165.1 0.2 -88.2 0.7 -161.2 1.2 -162.1 0.9 -1.9 4.6 -3.1 32.6 -10.7 10.2 -2.8 25.9 -7.1 35 -9.6 9.1 -2.5 32 -8.9 51 -14.1 47.4 -13.1 124.2 -34.5 168.5 -46.9 14.3 -4 26.3 -7.4 26.7 -7.4 0.4 -0.1 1.8 0.3 3.3 0.9 z m -351.7 95.3 c 0.9 0.7 1.2 35.1 1.2 166.7 l 0 165.9 -2.2 1.3 c -1.8 1 -10.2 1.3 -35.1 1.3 -31.4 -0 -32.9 -0.1 -34.3 -1.9 -2.1 -2.9 -2.1 -330.7 0 -332.8 1.1 -1.2 7.6 -1.4 35.2 -1.4 18.7 0.1 34.5 0.5 35.2 0.9 z"/>\n</svg>\n';
2842
-
2843
- // src/studio/export-image.ts
2844
2879
  var PIXEL_RATIO = 2;
2845
2880
  var translateOf = (element) => {
2846
2881
  const matrix = new DOMMatrix(getComputedStyle(element).transform);
@@ -2870,76 +2905,6 @@ function drawnBounds(viewport) {
2870
2905
  height: Math.max(...rects.map((r) => r.y + r.height)) - top
2871
2906
  };
2872
2907
  }
2873
- var loadImage = (src) => new Promise((resolve, reject) => {
2874
- const image = new Image();
2875
- image.onload = () => resolve(image);
2876
- image.onerror = reject;
2877
- image.src = src;
2878
- });
2879
- var themeColor = (name, alpha = 1) => {
2880
- const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim() || getComputedStyle(document.body).getPropertyValue(name).trim();
2881
- return value ? `rgb(${value} / ${alpha})` : alpha === 1 ? "#0f172a" : `rgb(15 23 42 / ${alpha})`;
2882
- };
2883
- async function withBranding(dataUrl) {
2884
- const image = await loadImage(dataUrl);
2885
- const canvas = document.createElement("canvas");
2886
- canvas.width = image.width;
2887
- canvas.height = image.height;
2888
- const context = canvas.getContext("2d");
2889
- if (!context) return dataUrl;
2890
- context.drawImage(image, 0, 0);
2891
- await document.fonts?.ready;
2892
- const r = Math.max(
2893
- PIXEL_RATIO,
2894
- Math.min(canvas.width / 1200, canvas.height / 500)
2895
- );
2896
- const text = themeColor("--ec-page-text");
2897
- const muted = themeColor("--ec-page-text-muted");
2898
- const font = (weight) => `${weight} ${12 * r}px Inter, ui-sans-serif, system-ui, sans-serif`;
2899
- const parts = [
2900
- { label: "Made with ", weight: 400, color: muted },
2901
- { label: "EventCatalog", weight: 600, color: text },
2902
- { label: " \xB7 eventcatalog.dev", weight: 400, color: muted }
2903
- ];
2904
- const widths = parts.map((part) => {
2905
- context.font = font(part.weight);
2906
- return context.measureText(part.label).width;
2907
- });
2908
- const logoSize = 16 * r;
2909
- const paddingX = 12 * r;
2910
- const gap = 7 * r;
2911
- const height = 30 * r;
2912
- const width = paddingX * 2 + logoSize + gap + widths.reduce((sum, w) => sum + w, 0);
2913
- const margin = 20 * r;
2914
- const x = canvas.width - width - margin;
2915
- const y = canvas.height - height - margin;
2916
- context.fillStyle = themeColor("--ec-card-bg", 0.96);
2917
- context.strokeStyle = themeColor("--ec-page-border");
2918
- context.lineWidth = r;
2919
- context.beginPath();
2920
- context.roundRect(x, y, width, height, height / 2);
2921
- context.fill();
2922
- context.stroke();
2923
- const logo = await loadImage(
2924
- `data:image/svg+xml;charset=utf-8,${encodeURIComponent(eventcatalog_logo_default.replace(/currentColor/g, text))}`
2925
- );
2926
- context.drawImage(
2927
- logo,
2928
- x + paddingX,
2929
- y + (height - logoSize) / 2,
2930
- logoSize,
2931
- logoSize
2932
- );
2933
- let textX = x + paddingX + logoSize + gap;
2934
- context.textBaseline = "middle";
2935
- parts.forEach((part, index) => {
2936
- context.font = font(part.weight);
2937
- context.fillStyle = part.color;
2938
- context.fillText(part.label, textX, y + height / 2 + r);
2939
- textX += widths[index];
2940
- });
2941
- return canvas.toDataURL("image/png");
2942
- }
2943
2908
  async function canvasImage(container) {
2944
2909
  const viewport = container.querySelector(
2945
2910
  ".react-flow__viewport"
@@ -2966,11 +2931,7 @@ async function canvasImage(container) {
2966
2931
  removeExportStyles();
2967
2932
  container.classList.remove("ec-exporting");
2968
2933
  }
2969
- try {
2970
- return await withBranding(dataUrl);
2971
- } catch {
2972
- return dataUrl;
2973
- }
2934
+ return addExportBranding(dataUrl, PIXEL_RATIO);
2974
2935
  }
2975
2936
 
2976
2937
  // src/studio/components/canvas-nodes.tsx
@@ -2991,7 +2952,8 @@ import {
2991
2952
  Position as Position2,
2992
2953
  ResizeControlVariant,
2993
2954
  useReactFlow,
2994
- useStoreApi as useStoreApi2
2955
+ useStoreApi as useStoreApi2,
2956
+ useUpdateNodeInternals
2995
2957
  } from "@xyflow/react";
2996
2958
 
2997
2959
  // src/studio/components/TeamNode.tsx
@@ -3395,6 +3357,10 @@ var GroupNode = memo2(function GroupNode2(props) {
3395
3357
  const locked = useContext(LockedContext);
3396
3358
  const data = useDomainData(props);
3397
3359
  const store = useStoreApi2();
3360
+ const updateNodeInternals = useUpdateNodeInternals();
3361
+ useEffect2(() => {
3362
+ updateNodeInternals(props.id);
3363
+ }, [updateNodeInternals, props.id, props.width, props.height]);
3398
3364
  const contents = useRef(null);
3399
3365
  const onResizeStart = useCallback(() => {
3400
3366
  const { nodeLookup } = store.getState();
@@ -6531,6 +6497,47 @@ function Field({
6531
6497
  children
6532
6498
  ] });
6533
6499
  }
6500
+
6501
+ // src/studio/components/RemeasureHandles.tsx
6502
+ import { memo as memo9, useEffect as useEffect10 } from "react";
6503
+ import {
6504
+ useStore as useStore2,
6505
+ useUpdateNodeInternals as useUpdateNodeInternals2
6506
+ } from "@xyflow/react";
6507
+ var selectDomNode = (state) => state.domNode;
6508
+ var RemeasureHandles = memo9(function RemeasureHandles2() {
6509
+ const domNode = useStore2(selectDomNode);
6510
+ const updateNodeInternals = useUpdateNodeInternals2();
6511
+ useEffect10(() => {
6512
+ if (!domNode) return;
6513
+ const pending = /* @__PURE__ */ new Set();
6514
+ let frame = 0;
6515
+ const flush = () => {
6516
+ frame = 0;
6517
+ updateNodeInternals([...pending]);
6518
+ pending.clear();
6519
+ };
6520
+ const remeasure = (event) => {
6521
+ const element = event.target;
6522
+ if (!element.classList?.contains("react-flow__node")) return;
6523
+ const resized = event.type === "transitionend" ? ["width", "height"].includes(
6524
+ event.propertyName
6525
+ ) : event.animationName === "studio-node-in";
6526
+ const id = element.dataset.id;
6527
+ if (!resized || !id) return;
6528
+ pending.add(id);
6529
+ frame || (frame = requestAnimationFrame(flush));
6530
+ };
6531
+ domNode.addEventListener("animationend", remeasure);
6532
+ domNode.addEventListener("transitionend", remeasure);
6533
+ return () => {
6534
+ cancelAnimationFrame(frame);
6535
+ domNode.removeEventListener("animationend", remeasure);
6536
+ domNode.removeEventListener("transitionend", remeasure);
6537
+ };
6538
+ }, [domNode, updateNodeInternals]);
6539
+ return null;
6540
+ });
6534
6541
  export {
6535
6542
  AgentMark,
6536
6543
  CANVAS_STATUSES,
@@ -6584,6 +6591,7 @@ export {
6584
6591
  PaletteItem,
6585
6592
  PaletteSection,
6586
6593
  Picture,
6594
+ RemeasureHandles,
6587
6595
  SEVERITIES,
6588
6596
  SHORTCUTS,
6589
6597
  STATUS,