@likec4/diagram 1.27.1 → 1.27.3

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 (123) hide show
  1. package/bundle/index.js +2152 -6757
  2. package/dist/LikeC4Diagram.js +1 -1
  3. package/dist/LikeC4Diagram.props.d.ts +5 -3
  4. package/dist/StaticLikeC4Diagram.js +1 -1
  5. package/dist/_virtual/set.js +5 -0
  6. package/dist/_virtual/set2.js +3 -0
  7. package/dist/base/BaseXYFlow.d.ts +2 -1
  8. package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
  9. package/dist/base/primitives/compound/CompoundNodeContainer.js +0 -1
  10. package/dist/base/primitives/edge/EdgeLabelContainer.js +3 -2
  11. package/dist/base/primitives/element/ElementActionButtons.js +8 -11
  12. package/dist/base/primitives/element/ElementNodeContainer.css.js +4 -4
  13. package/dist/base/updateEdges.js +1 -9
  14. package/dist/base/updateNodes.js +1 -12
  15. package/dist/bundle/LikeC4View.js +2 -2
  16. package/dist/bundle/LikeC4View.props.d.ts +7 -7
  17. package/dist/hooks/useCurrentViewId.js +8 -2
  18. package/dist/hooks/useDiagram.d.ts +1522 -685
  19. package/dist/hooks/useDiagram.js +21 -24
  20. package/dist/likec4diagram/DiagramXYFlow.js +6 -6
  21. package/dist/likec4diagram/ui/notation/NotationPanel.css.js +3 -3
  22. package/dist/likec4diagram/ui/notation/NotationPanel.js +10 -4
  23. package/dist/node_modules/.pnpm/mnemonist@0.40.3_patch_hash_5c12abb43fcff6fd81e53021b89075d53249640d165a2556cc1e58d71201a13a/node_modules/mnemonist/set.js +369 -0
  24. package/dist/overlays/Overlays.js +3 -1
  25. package/dist/overlays/element-details/actor.d.ts +2769 -173
  26. package/dist/overlays/overlay/Overlay.css.d.ts +26 -2
  27. package/dist/overlays/overlay/Overlay.css.js +66 -18
  28. package/dist/overlays/overlay/Overlay.d.ts +7 -6
  29. package/dist/overlays/overlay/Overlay.js +12 -10
  30. package/dist/overlays/overlaysActor.js +2 -2
  31. package/dist/overlays/relationship-details/RelationshipDetails.js +113 -70
  32. package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
  33. package/dist/overlays/relationship-details/SelectEdge.js +15 -9
  34. package/dist/overlays/relationship-details/_types.d.ts +4 -1
  35. package/dist/overlays/relationship-details/actor.d.ts +425 -128
  36. package/dist/overlays/relationship-details/actor.js +157 -53
  37. package/dist/overlays/relationship-details/compute.d.ts +12 -0
  38. package/dist/overlays/relationship-details/compute.js +139 -0
  39. package/dist/overlays/relationship-details/custom/ElementActions.js +27 -5
  40. package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
  41. package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +2 -1
  42. package/dist/overlays/relationship-details/hooks.d.ts +336 -6
  43. package/dist/overlays/relationship-details/hooks.js +11 -12
  44. package/dist/overlays/relationship-details/layout-to-xyflow.d.ts +12 -0
  45. package/dist/overlays/relationship-details/layout-to-xyflow.js +145 -0
  46. package/dist/overlays/relationship-details/layout.d.ts +22 -0
  47. package/dist/overlays/relationship-details/layout.js +314 -0
  48. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +3 -1
  49. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -2
  50. package/dist/overlays/relationships-browser/_types.d.ts +2 -0
  51. package/dist/overlays/relationships-browser/actor.d.ts +130 -14
  52. package/dist/overlays/relationships-browser/actor.js +30 -16
  53. package/dist/overlays/relationships-browser/custom/ElementActions.js +5 -3
  54. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +3 -1
  55. package/dist/overlays/relationships-browser/custom/edgeTypes.js +1 -1
  56. package/dist/overlays/relationships-browser/hooks.d.ts +3 -1
  57. package/dist/overlays/relationships-browser/hooks.js +6 -9
  58. package/dist/overlays/relationships-browser/{-useRelationshipsView.d.ts → layout.d.ts} +3 -1
  59. package/dist/overlays/relationships-browser/{-useRelationshipsView.js → layout.js} +21 -8
  60. package/dist/overlays/relationships-browser/useViewToNodesEdges.d.ts +1 -1
  61. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +3 -1
  62. package/dist/packages/core/src/compute-view/relationships-view/compute.js +1 -1
  63. package/dist/packages/core/src/model/connection/model/ConnectionModel.js +121 -0
  64. package/dist/packages/core/src/model/connection/model/find.js +39 -0
  65. package/dist/packages/core/src/model/connection/ops.js +7 -0
  66. package/dist/packages/core/src/utils/const.js +3 -0
  67. package/dist/packages/core/src/utils/fqn.js +9 -1
  68. package/dist/packages/core/src/utils/iterable/find.js +21 -0
  69. package/dist/packages/core/src/utils/iterable/some.js +21 -0
  70. package/dist/packages/core/src/utils/set.js +48 -0
  71. package/dist/packages/core/src/utils/string-hash.js +13 -0
  72. package/dist/state/diagram-machine.d.ts +1552 -703
  73. package/dist/state/diagram-machine.js +39 -3
  74. package/dist/state/syncManualLayoutActor.d.ts +1 -1
  75. package/dist/styles.css.js +1 -1
  76. package/dist/utils/index.d.ts +0 -1
  77. package/package.json +11 -11
  78. package/src/LikeC4Diagram.props.ts +7 -3
  79. package/src/LikeC4Diagram.tsx +1 -1
  80. package/src/StaticLikeC4Diagram.tsx +1 -1
  81. package/src/base/BaseXYFlow.tsx +2 -1
  82. package/src/base/primitives/compound/CompoundNodeContainer.tsx +0 -2
  83. package/src/base/primitives/edge/EdgeLabelContainer.tsx +7 -5
  84. package/src/base/primitives/element/ElementActionButtons.tsx +10 -11
  85. package/src/base/primitives/element/ElementNodeContainer.css.ts +4 -4
  86. package/src/base/updateEdges.ts +8 -8
  87. package/src/base/updateNodes.ts +11 -11
  88. package/src/bundle/LikeC4View.props.ts +7 -7
  89. package/src/bundle/LikeC4View.tsx +2 -2
  90. package/src/hooks/useCurrentViewId.ts +4 -2
  91. package/src/hooks/useDiagram.ts +22 -24
  92. package/src/likec4diagram/DiagramXYFlow.tsx +6 -6
  93. package/src/likec4diagram/ui/notation/NotationPanel.css.ts +2 -2
  94. package/src/likec4diagram/ui/notation/NotationPanel.tsx +8 -6
  95. package/src/overlays/Overlays.tsx +4 -2
  96. package/src/overlays/overlay/Overlay.css.ts +67 -17
  97. package/src/overlays/overlay/Overlay.tsx +27 -24
  98. package/src/overlays/overlaysActor.ts +1 -1
  99. package/src/overlays/relationship-details/RelationshipDetails.tsx +131 -68
  100. package/src/overlays/relationship-details/SelectEdge.tsx +19 -12
  101. package/src/overlays/relationship-details/_types.ts +3 -1
  102. package/src/overlays/relationship-details/actor.ts +212 -68
  103. package/src/overlays/relationship-details/compute.ts +186 -0
  104. package/src/overlays/relationship-details/custom/ElementActions.tsx +27 -18
  105. package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
  106. package/src/overlays/relationship-details/hooks.ts +12 -13
  107. package/src/overlays/relationship-details/layout-to-xyflow.ts +172 -0
  108. package/src/overlays/relationship-details/layout.ts +470 -0
  109. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -2
  110. package/src/overlays/relationships-browser/_types.ts +2 -0
  111. package/src/overlays/relationships-browser/actor.ts +39 -14
  112. package/src/overlays/relationships-browser/custom/ElementActions.tsx +5 -3
  113. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +1 -1
  114. package/src/overlays/relationships-browser/hooks.ts +7 -10
  115. package/src/overlays/relationships-browser/{-useRelationshipsView.ts → layout.ts} +26 -33
  116. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +3 -1
  117. package/src/state/diagram-machine.ts +51 -6
  118. package/src/utils/index.ts +0 -1
  119. package/dist/overlays/relationship-details/useLayoutedDetails.d.ts +0 -19
  120. package/dist/overlays/relationship-details/useLayoutedDetails.js +0 -267
  121. package/dist/utils/types.d.ts +0 -8
  122. package/src/overlays/relationship-details/useLayoutedDetails.ts +0 -376
  123. package/src/utils/types.ts +0 -6
@@ -1,10 +1,26 @@
1
1
  import { invariant } from '@likec4/core';
2
- import { applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
3
- import { prop } from 'remeda';
4
- import { setup, assign, cancel, raise, enqueueActions } from 'xstate';
2
+ import { getViewportForBounds, applyEdgeChanges, applyNodeChanges } from '@xyflow/react';
3
+ import { shallowEqual } from 'fast-equals';
4
+ import { prop, isString } from 'remeda';
5
+ import { setup, assign, assertEvent, cancel, raise, enqueueActions } from 'xstate';
5
6
  import { MinZoom } from '../../base/const.js';
7
+ import { updateEdges } from '../../base/updateEdges.js';
8
+ import { updateNodes } from '../../base/updateNodes.js';
9
+ import { layoutResultToXYFlow } from './layout-to-xyflow.js';
6
10
  import { Base } from '../../base/types.js';
7
11
 
12
+ function inputToSubject(input) {
13
+ if ("edgeId" in input) {
14
+ invariant(isString(input.edgeId), "edgeId is required");
15
+ return {
16
+ edgeId: input.edgeId
17
+ };
18
+ }
19
+ return {
20
+ source: input.source,
21
+ target: input.target
22
+ };
23
+ }
8
24
  const relationshipDetailsLogic = setup({
9
25
  types: {
10
26
  context: {},
@@ -13,16 +29,18 @@ const relationshipDetailsLogic = setup({
13
29
  },
14
30
  actions: {
15
31
  "xyflow:fitDiagram": ({ context }, params) => {
16
- const {
17
- duration = 450,
18
- bounds
19
- } = params ?? {};
20
- const { xyflow } = context;
32
+ let { duration, bounds } = params ?? {};
33
+ duration ??= 450;
34
+ const { xyflow, xystore } = context;
21
35
  invariant(xyflow, "xyflow is not initialized");
36
+ invariant(xystore, "xystore is not initialized");
37
+ bounds ??= context.bounds;
38
+ const maxZoom = Math.max(xyflow.getZoom(), 1);
22
39
  if (bounds) {
23
- xyflow.fitBounds(bounds, duration > 0 ? { duration } : void 0);
40
+ const { width, height } = xystore.getState();
41
+ const viewport = getViewportForBounds(bounds, width, height, MinZoom, maxZoom, 0.1);
42
+ xyflow.setViewport(viewport, duration > 0 ? { duration } : void 0);
24
43
  } else {
25
- const maxZoom = Math.max(xyflow.getZoom(), 1);
26
44
  xyflow.fitView({
27
45
  minZoom: MinZoom,
28
46
  maxZoom,
@@ -30,46 +48,117 @@ const relationshipDetailsLogic = setup({
30
48
  ...duration > 0 && { duration }
31
49
  });
32
50
  }
33
- }
51
+ },
52
+ "xyflow:updateNodeInternals": ({ context }) => {
53
+ invariant(context.xystore, "xystore is not initialized");
54
+ const { domNode, updateNodeInternals } = context.xystore.getState();
55
+ const nodeIds = new Set(context.xyedges.flatMap((e) => [e.source, e.target]));
56
+ if (nodeIds.size === 0 || !domNode) {
57
+ return;
58
+ }
59
+ const updates = /* @__PURE__ */ new Map();
60
+ for (const updateId of nodeIds) {
61
+ const nodeElement = domNode.querySelector(`.react-flow__node[data-id="${updateId}"]`);
62
+ if (nodeElement) {
63
+ updates.set(updateId, { id: updateId, nodeElement, force: true });
64
+ }
65
+ }
66
+ updateNodeInternals(updates, { triggerFitView: false });
67
+ },
68
+ "updateXYFlow": assign(({ context, event }) => {
69
+ assertEvent(event, "xyflow.init");
70
+ let initialized = context.initialized;
71
+ if (!initialized.xyflow) {
72
+ initialized = {
73
+ ...initialized,
74
+ xyflow: true
75
+ };
76
+ }
77
+ return {
78
+ initialized,
79
+ xyflow: event.instance,
80
+ xystore: event.store
81
+ };
82
+ }),
83
+ "updateLayoutData": assign(({ context, event }) => {
84
+ assertEvent(event, "update.layoutData");
85
+ const { xynodes, xyedges, bounds } = layoutResultToXYFlow(event.data);
86
+ let initialized = context.initialized;
87
+ if (!initialized.xydata) {
88
+ initialized = {
89
+ ...initialized,
90
+ xydata: true
91
+ };
92
+ }
93
+ return {
94
+ initialized,
95
+ xynodes: updateNodes(context.xynodes, xynodes),
96
+ xyedges: updateEdges(context.xyedges, xyedges),
97
+ bounds: shallowEqual(context.bounds, bounds) ? context.bounds : bounds
98
+ };
99
+ })
34
100
  },
35
101
  guards: {
102
+ "isReady": ({ context }) => context.initialized.xydata && context.initialized.xyflow,
36
103
  "enable: navigate.to": () => true
37
104
  }
38
105
  }).createMachine({
39
- initial: "opening",
106
+ initial: "initializing",
40
107
  context: ({ input }) => ({
41
- ...input,
42
- initialized: false,
108
+ subject: inputToSubject(input),
109
+ viewId: input.viewId,
110
+ bounds: {
111
+ x: 0,
112
+ y: 0,
113
+ width: 200,
114
+ height: 200
115
+ },
116
+ initialized: {
117
+ xydata: false,
118
+ xyflow: false
119
+ },
43
120
  xyflow: null,
121
+ xystore: null,
44
122
  xynodes: [],
45
123
  xyedges: []
46
124
  }),
47
125
  states: {
48
- "opening": {
126
+ "initializing": {
49
127
  on: {
50
128
  "xyflow.init": {
51
- actions: assign({
52
- initialized: true,
53
- xyflow: ({ event }) => event.instance
54
- }),
55
- target: "active"
129
+ actions: "updateXYFlow",
130
+ target: "isReady"
131
+ },
132
+ "update.layoutData": {
133
+ actions: "updateLayoutData",
134
+ target: "isReady"
56
135
  },
57
136
  "close": {
58
137
  target: "closed"
59
138
  }
60
139
  }
61
140
  },
62
- "active": {
63
- entry: {
64
- type: "xyflow:fitDiagram",
65
- params: { duration: 0 }
66
- },
141
+ "isReady": {
142
+ always: [{
143
+ guard: "isReady",
144
+ actions: [
145
+ { type: "xyflow:fitDiagram", params: { duration: 0 } },
146
+ raise({ type: "xyflow.updateNodeInternals" }, { delay: 50 })
147
+ ],
148
+ target: "ready"
149
+ }, {
150
+ target: "initializing"
151
+ }]
152
+ },
153
+ "ready": {
67
154
  on: {
68
155
  "xyflow.edgeMouseEnter": {
69
156
  actions: [
70
157
  assign({
71
158
  xyedges: ({ context, event }) => {
72
- const hasDimmed = context.xyedges.some((edge) => edge.data.dimmed === true);
159
+ const hasDimmed = context.xyedges.some(
160
+ (edge) => edge.data.dimmed === true || edge.data.dimmed === "immediate"
161
+ );
73
162
  return context.xyedges.map((edge) => {
74
163
  if (edge.id === event.edge.id) {
75
164
  return Base.setData(edge, {
@@ -77,7 +166,7 @@ const relationshipDetailsLogic = setup({
77
166
  dimmed: false
78
167
  });
79
168
  }
80
- return hasDimmed && !edge.data.dimmed && !edge.selected ? Base.setDimmed(edge, "immediate") : edge;
169
+ return hasDimmed && !edge.selected ? Base.setDimmed(edge, "immediate") : edge;
81
170
  });
82
171
  }
83
172
  }),
@@ -125,21 +214,53 @@ const relationshipDetailsLogic = setup({
125
214
  }
126
215
  })
127
216
  },
128
- "navigate.to": {
217
+ "update.layoutData": {
129
218
  actions: [
130
- assign({
131
- edgeId: ({ event }) => event.edgeId
132
- }),
133
- raise({ type: "fitDiagram" }, { delay: 50 })
219
+ "updateLayoutData",
220
+ cancel("fitDiagram"),
221
+ raise({ type: "fitDiagram", duration: 0 }, { id: "fitDiagram", delay: 50 }),
222
+ raise({ type: "xyflow.updateNodeInternals" }, { delay: 75 })
134
223
  ]
135
224
  },
225
+ "xyflow.init": {
226
+ actions: "updateXYFlow"
227
+ },
228
+ "xyflow.applyNodeChanges": {
229
+ actions: assign({
230
+ xynodes: ({ context, event }) => {
231
+ return applyNodeChanges(event.changes, context.xynodes);
232
+ }
233
+ })
234
+ },
235
+ "xyflow.applyEdgeChanges": {
236
+ actions: assign({
237
+ xyedges: ({ context, event }) => {
238
+ return applyEdgeChanges(event.changes, context.xyedges);
239
+ }
240
+ })
241
+ },
242
+ "xyflow.paneDblClick": {
243
+ actions: "xyflow:fitDiagram"
244
+ },
245
+ "navigate.to": {
246
+ actions: assign({
247
+ subject: ({ event }) => inputToSubject(event.params),
248
+ viewId: ({ context, event }) => event.params.viewId ?? context.viewId
249
+ })
250
+ },
136
251
  "close": {
137
252
  target: "closed"
138
253
  }
139
254
  },
140
255
  exit: assign({
141
- initialized: false,
142
- xyflow: null
256
+ xyedges: [],
257
+ xynodes: [],
258
+ initialized: {
259
+ xydata: false,
260
+ xyflow: false
261
+ },
262
+ xyflow: null,
263
+ xystore: null
143
264
  })
144
265
  },
145
266
  "closed": {
@@ -159,25 +280,8 @@ const relationshipDetailsLogic = setup({
159
280
  raise({ type: "fitDiagram" }, { id: "fitDiagram", delay: 200 })
160
281
  ]
161
282
  },
162
- "update.xydata": {
163
- actions: assign({
164
- xynodes: ({ event }) => event.xynodes,
165
- xyedges: ({ event }) => event.xyedges
166
- })
167
- },
168
- "xyflow.applyNodeChanges": {
169
- actions: assign({
170
- xynodes: ({ context, event }) => {
171
- return applyNodeChanges(event.changes, context.xynodes);
172
- }
173
- })
174
- },
175
- "xyflow.applyEdgeChanges": {
176
- actions: assign({
177
- xyedges: ({ context, event }) => {
178
- return applyEdgeChanges(event.changes, context.xyedges);
179
- }
180
- })
283
+ "xyflow.updateNodeInternals": {
284
+ actions: "xyflow:updateNodeInternals"
181
285
  }
182
286
  }
183
287
  });
@@ -0,0 +1,12 @@
1
+ import { EdgeId, NonEmptyReadonlyArray } from '@likec4/core';
2
+ import { AnyAux, ElementModel, LikeC4ViewModel, RelationshipModel } from '@likec4/core/model';
3
+ export interface RelationshipDetailsViewData {
4
+ sources: ReadonlySet<ElementModel>;
5
+ relationships: ReadonlySet<RelationshipModel>;
6
+ targets: ReadonlySet<ElementModel>;
7
+ }
8
+ export declare function computeEdgeDetailsViewData(edges: NonEmptyReadonlyArray<EdgeId>, view: LikeC4ViewModel<AnyAux>): RelationshipDetailsViewData;
9
+ export declare function computeRelationshipDetailsViewData({ source, target, }: {
10
+ source: ElementModel;
11
+ target: ElementModel;
12
+ }): RelationshipDetailsViewData;
@@ -0,0 +1,139 @@
1
+ import { hasAtLeast, invariant, isAncestor, sortParentsFirst } from '@likec4/core';
2
+ import { treeFromElements } from '../../packages/core/src/compute-view/relationships-view/utils.js';
3
+ import { findConnection } from '../../packages/core/src/model/connection/model/find.js';
4
+
5
+ const finalize = (elements, explicits) => {
6
+ if (elements.size > 2 && explicits.size !== elements.size) {
7
+ return new Set(sortParentsFirst([
8
+ ...treeFromElements(elements).flatten(),
9
+ ...explicits
10
+ ]));
11
+ }
12
+ if (elements.size > 1) {
13
+ return new Set(sortParentsFirst([...elements]));
14
+ }
15
+ return elements;
16
+ };
17
+ function computeEdgeDetailsViewData(edges, view) {
18
+ const sources = /* @__PURE__ */ new Set();
19
+ const relationships = /* @__PURE__ */ new Set();
20
+ const targets = /* @__PURE__ */ new Set();
21
+ const explicit = {
22
+ sources: /* @__PURE__ */ new Set(),
23
+ targets: /* @__PURE__ */ new Set()
24
+ };
25
+ const addExplicit = (el, type) => {
26
+ if (type === "source") {
27
+ sources.add(el);
28
+ explicit.sources.add(el);
29
+ } else {
30
+ targets.add(el);
31
+ explicit.targets.add(el);
32
+ }
33
+ };
34
+ for (const edgeId of edges) {
35
+ const edge = view.findEdge(edgeId);
36
+ const _relationships = edge ? [...edge.relationships("model")] : [];
37
+ if (!edge || !hasAtLeast(_relationships, 1) || !edge.source.hasElement() || !edge.target.hasElement()) {
38
+ continue;
39
+ }
40
+ const source = edge.source.element;
41
+ const target = edge.target.element;
42
+ addExplicit(source, "source");
43
+ addExplicit(target, "target");
44
+ for (const relationship of _relationships) {
45
+ relationships.add(relationship);
46
+ if (relationship.source !== source) {
47
+ addExplicit(relationship.source, "source");
48
+ for (const parent of relationship.source.ancestors()) {
49
+ if (parent === source) {
50
+ break;
51
+ }
52
+ sources.add(parent);
53
+ }
54
+ }
55
+ if (relationship.target !== target) {
56
+ addExplicit(relationship.target, "target");
57
+ for (const parent of relationship.target.ancestors()) {
58
+ if (parent === target) {
59
+ break;
60
+ }
61
+ targets.add(parent);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return {
67
+ sources: finalize(sources, explicit.sources),
68
+ targets: finalize(targets, explicit.targets),
69
+ relationships
70
+ };
71
+ }
72
+ function computeRelationshipDetailsViewData({
73
+ // relationships: _relationships,
74
+ source,
75
+ target
76
+ }) {
77
+ const sources = /* @__PURE__ */ new Set();
78
+ const relationships = /* @__PURE__ */ new Set();
79
+ const targets = /* @__PURE__ */ new Set();
80
+ const explicit = {
81
+ sources: /* @__PURE__ */ new Set(),
82
+ targets: /* @__PURE__ */ new Set()
83
+ };
84
+ const addExplicit = (el, type) => {
85
+ if (type === "source") {
86
+ sources.add(el);
87
+ explicit.sources.add(el);
88
+ } else {
89
+ targets.add(el);
90
+ explicit.targets.add(el);
91
+ }
92
+ };
93
+ if (source) {
94
+ addExplicit(source, "source");
95
+ }
96
+ if (target) {
97
+ addExplicit(target, "target");
98
+ }
99
+ const [connection] = findConnection(source, target, "directed");
100
+ if (!connection) {
101
+ return {
102
+ sources,
103
+ targets,
104
+ relationships
105
+ };
106
+ }
107
+ for (const relationship of connection.relations) {
108
+ const relationSource = relationship.source;
109
+ const relationTarget = relationship.target;
110
+ addExplicit(relationSource, "source");
111
+ addExplicit(relationTarget, "target");
112
+ relationships.add(relationship);
113
+ if (source !== relationSource) {
114
+ invariant(isAncestor(source, relationSource), `${source.id} is not an ancestor of ${relationSource.id}`);
115
+ for (const parent of relationSource.ancestors()) {
116
+ if (parent === source) {
117
+ break;
118
+ }
119
+ sources.add(parent);
120
+ }
121
+ }
122
+ if (target !== relationTarget) {
123
+ invariant(isAncestor(target, relationTarget), `${target.id} is not an ancestor of ${relationTarget.id}`);
124
+ for (const parent of relationTarget.ancestors()) {
125
+ if (parent === target) {
126
+ break;
127
+ }
128
+ targets.add(parent);
129
+ }
130
+ }
131
+ }
132
+ return {
133
+ sources: finalize(sources, explicit.sources),
134
+ targets: finalize(targets, explicit.targets),
135
+ relationships
136
+ };
137
+ }
138
+
139
+ export { computeEdgeDetailsViewData, computeRelationshipDetailsViewData };
@@ -1,15 +1,17 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { IconZoomScan } from '@tabler/icons-react';
3
- import { useEnabledFeature } from '../../../context/DiagramFeatures.js';
2
+ import { IconZoomScan, IconTransform, IconFileSymlink } from '@tabler/icons-react';
3
+ import { useEnabledFeatures } from '../../../context/DiagramFeatures.js';
4
+ import { useCurrentViewId } from '../../../hooks/useCurrentViewId.js';
4
5
  import { useDiagram } from '../../../hooks/useDiagram.js';
5
6
  import { ElementActionButtons } from '../../../base/primitives/element/ElementActionButtons.js';
6
7
 
7
8
  const ElementActions = (props) => {
8
- const { enableNavigateTo } = useEnabledFeature("NavigateTo");
9
+ const { enableNavigateTo, enableVscode } = useEnabledFeatures();
9
10
  const diagram = useDiagram();
11
+ const currentViewId = useCurrentViewId();
10
12
  const buttons = [];
11
- const { navigateTo } = props.data;
12
- if (navigateTo && enableNavigateTo) {
13
+ const { navigateTo, fqn } = props.data;
14
+ if (navigateTo && enableNavigateTo && currentViewId !== navigateTo) {
13
15
  buttons.push({
14
16
  key: "navigate",
15
17
  icon: /* @__PURE__ */ jsx(IconZoomScan, {}),
@@ -19,6 +21,26 @@ const ElementActions = (props) => {
19
21
  }
20
22
  });
21
23
  }
24
+ if (fqn) {
25
+ buttons.push({
26
+ key: "relationships",
27
+ icon: /* @__PURE__ */ jsx(IconTransform, {}),
28
+ onClick: (e) => {
29
+ e.stopPropagation();
30
+ diagram.openRelationshipsBrowser(fqn);
31
+ }
32
+ });
33
+ }
34
+ if (fqn && enableVscode) {
35
+ buttons.push({
36
+ key: "goToSource",
37
+ icon: /* @__PURE__ */ jsx(IconFileSymlink, {}),
38
+ onClick: (e) => {
39
+ e.stopPropagation();
40
+ diagram.openSource({ element: fqn });
41
+ }
42
+ });
43
+ }
22
44
  return /* @__PURE__ */ jsx(
23
45
  ElementActionButtons,
24
46
  {
@@ -30,7 +30,7 @@ const RelationshipEdge = customEdge((props) => {
30
30
  translate: "translate(-50%, 0)"
31
31
  },
32
32
  style: {
33
- maxWidth: Math.abs(props.targetX - props.sourceX - 70)
33
+ maxWidth: Math.abs(props.targetX - props.sourceX - 100)
34
34
  },
35
35
  children: /* @__PURE__ */ jsx(EdgeLabel, { edgeProps: props, children: enableNavigateTo && navigateTo && /* @__PURE__ */ jsx(
36
36
  EdgeActionButton,
@@ -12,6 +12,7 @@ export declare const ElementNode: NamedExoticComponent<NodeProps<{
12
12
  color: Color;
13
13
  shape: "rectangle" | "person" | "browser" | "mobile" | "cylinder" | "storage" | "queue";
14
14
  navigateTo: ViewId<string> | null | undefined;
15
+ column: RelationshipDetailsTypes.Column;
15
16
  fqn: Fqn;
16
17
  icon: IconUrl;
17
18
  ports: RelationshipDetailsTypes.Ports;
@@ -22,7 +23,7 @@ export declare const CompoundNode: NamedExoticComponent<NodeProps<{
22
23
  style: ElementStyle;
23
24
  title: string;
24
25
  color: Color;
25
- shape: "rectangle" | "person" | "browser" | "mobile" | "cylinder" | "storage" | "queue";
26
+ column: RelationshipDetailsTypes.Column;
26
27
  fqn: Fqn;
27
28
  depth: number;
28
29
  icon?: IconUrl;