@likec4/diagram 1.44.0 → 1.45.0

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 (134) hide show
  1. package/bundle/index.js +18160 -17671
  2. package/dist/LikeC4Diagram.js +7 -5
  3. package/dist/LikeC4Diagram.props.d.ts +9 -8
  4. package/dist/LikeC4Styles.js +0 -3
  5. package/dist/LikeC4View.d.ts +3 -3
  6. package/dist/LikeC4View.js +7 -3
  7. package/dist/ReactLikeC4.d.ts +2 -2
  8. package/dist/base/Base.d.ts +1 -1
  9. package/dist/base/updateEdges.js +25 -15
  10. package/dist/base/updateNodes.d.ts +14 -0
  11. package/dist/base/updateNodes.js +51 -18
  12. package/dist/base-primitives/Markdown.d.ts +30 -4
  13. package/dist/base-primitives/Markdown.js +3 -2
  14. package/dist/base-primitives/compound/CompoundActionButton.d.ts +3 -2
  15. package/dist/base-primitives/compound/CompoundDetailsButton.d.ts +3 -2
  16. package/dist/base-primitives/edge/EdgeActionButton.d.ts +3 -2
  17. package/dist/base-primitives/edge/EdgeContainer.d.ts +1 -1
  18. package/dist/base-primitives/edge/EdgeContainer.js +4 -1
  19. package/dist/base-primitives/edge/EdgeLabel.d.ts +2 -2
  20. package/dist/base-primitives/edge/EdgePath.js +24 -4
  21. package/dist/base-primitives/element/ElementActionButtons.d.ts +3 -2
  22. package/dist/base-primitives/element/ElementActionButtons.js +2 -2
  23. package/dist/base-primitives/element/ElementData.d.ts +3 -3
  24. package/dist/base-primitives/element/ElementData.js +13 -7
  25. package/dist/base-primitives/element/ElementDetailsButton.d.ts +3 -2
  26. package/dist/base-primitives/element/ElementDetailsButton.js +3 -0
  27. package/dist/base-primitives/element/ElementShape.js +5 -5
  28. package/dist/base-primitives/element/ElementTags.d.ts +6 -4
  29. package/dist/context/DiagramFeatures.d.ts +1 -0
  30. package/dist/context/TagStylesContext.js +1 -1
  31. package/dist/custom/hooks.d.ts +2 -2
  32. package/dist/custom/index.js +1 -1
  33. package/dist/hooks/index.d.ts +2 -1
  34. package/dist/hooks/useDiagramCompareLayout.js +7 -2
  35. package/dist/hooks/useLikeC4Project.d.ts +6 -1
  36. package/dist/hooks/useLikeC4Project.js +14 -2
  37. package/dist/hooks/useMediaPrint.d.ts +6 -0
  38. package/dist/likec4diagram/DiagramXYFlow.js +11 -6
  39. package/dist/likec4diagram/custom/edges/EdgeDrifts.d.ts +6 -0
  40. package/dist/likec4diagram/custom/edges/EdgeDrifts.js +38 -0
  41. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +24 -14
  42. package/dist/likec4diagram/custom/edges/SequenceStepEdge.js +3 -1
  43. package/dist/likec4diagram/custom/edges/useControlPoints.js +3 -3
  44. package/dist/likec4diagram/custom/edges/useRelationshipEdgePath.js +5 -5
  45. package/dist/likec4diagram/custom/nodes/NodeDrifts.d.ts +6 -0
  46. package/dist/likec4diagram/custom/nodes/{ElementNodeDrifts.js → NodeDrifts.js} +8 -4
  47. package/dist/likec4diagram/custom/nodes/SequenceActorNode.js +4 -2
  48. package/dist/likec4diagram/custom/nodes/index.d.ts +1 -0
  49. package/dist/likec4diagram/custom/nodes/nodes.js +64 -23
  50. package/dist/likec4diagram/relationship-popover/RelationshipPopover.js +3 -3
  51. package/dist/likec4diagram/relationship-popover/actor.d.ts +1 -1
  52. package/dist/likec4diagram/state/DiagramActorProvider.d.ts +2 -2
  53. package/dist/likec4diagram/state/DiagramActorProvider.js +0 -1
  54. package/dist/likec4diagram/state/assign.d.ts +10 -6
  55. package/dist/likec4diagram/state/assign.js +17 -192
  56. package/dist/likec4diagram/state/createViewChange.js +15 -26
  57. package/dist/likec4diagram/state/diagram-api.d.ts +2 -6
  58. package/dist/likec4diagram/state/diagram-api.js +2 -10
  59. package/dist/likec4diagram/state/hotkeyActor.d.ts +0 -7
  60. package/dist/likec4diagram/state/hotkeyActor.js +1 -17
  61. package/dist/likec4diagram/state/machine.actions.d.ts +430 -746
  62. package/dist/likec4diagram/state/machine.actions.js +193 -284
  63. package/dist/likec4diagram/state/machine.actions.layout.d.ts +579 -0
  64. package/dist/likec4diagram/state/machine.actions.layout.js +129 -0
  65. package/dist/likec4diagram/state/machine.d.ts +17 -3712
  66. package/dist/likec4diagram/state/machine.js +59 -73
  67. package/dist/likec4diagram/state/machine.setup.d.ts +61 -18
  68. package/dist/likec4diagram/state/machine.setup.js +27 -5
  69. package/dist/likec4diagram/state/machine.state.initializing.d.ts +25 -0
  70. package/dist/likec4diagram/state/machine.state.initializing.js +5 -5
  71. package/dist/likec4diagram/state/machine.state.navigating.d.ts +5 -108
  72. package/dist/likec4diagram/state/machine.state.navigating.js +167 -34
  73. package/dist/likec4diagram/state/machine.state.ready.d.ts +2316 -2851
  74. package/dist/likec4diagram/state/machine.state.ready.focused.d.ts +386 -0
  75. package/dist/likec4diagram/state/machine.state.ready.focused.js +85 -0
  76. package/dist/likec4diagram/state/machine.state.ready.idle.d.ts +474 -0
  77. package/dist/likec4diagram/state/machine.state.ready.idle.js +96 -0
  78. package/dist/likec4diagram/state/machine.state.ready.js +144 -319
  79. package/dist/likec4diagram/state/machine.state.ready.printing.d.ts +128 -0
  80. package/dist/likec4diagram/state/machine.state.ready.printing.js +40 -0
  81. package/dist/likec4diagram/state/machine.state.ready.walkthrough.d.ts +511 -0
  82. package/dist/likec4diagram/state/machine.state.ready.walkthrough.js +177 -0
  83. package/dist/likec4diagram/state/mediaPrintActor.d.ts +12 -0
  84. package/dist/likec4diagram/state/mediaPrintActor.js +18 -0
  85. package/dist/likec4diagram/state/syncManualLayoutActor.d.ts +6 -240
  86. package/dist/likec4diagram/state/syncManualLayoutActor.js +150 -89
  87. package/dist/likec4diagram/state/types.d.ts +5 -7
  88. package/dist/likec4diagram/state/utils.d.ts +15 -2
  89. package/dist/likec4diagram/state/utils.js +43 -3
  90. package/dist/likec4diagram/types.d.ts +8 -13
  91. package/dist/likec4diagram/ui/notation/NotationPanel.css.js +5 -2
  92. package/dist/likec4diagram/useLayoutConstraints.js +107 -102
  93. package/dist/likec4diagram/xyflow-diagram/diagram-view.js +12 -15
  94. package/dist/likec4diagram/xyflow-sequence/sequence-layout.js +11 -8
  95. package/dist/navigationpanel/NavigationPanel.js +5 -1
  96. package/dist/navigationpanel/actor.d.ts +4 -204
  97. package/dist/navigationpanel/actor.js +1 -1
  98. package/dist/navigationpanel/controls/ToggleReadonly.js +8 -3
  99. package/dist/navigationpanel/walkthrough/DynamicViewControls.d.ts +66 -66
  100. package/dist/navigationpanel/walkthrough/WalkthroughPanel.js +4 -3
  101. package/dist/overlays/element-details/ElementDetailsCard.css.js +3 -3
  102. package/dist/overlays/element-details/MetadataValue.d.ts +2 -1
  103. package/dist/overlays/element-details/actor.d.ts +8 -83
  104. package/dist/overlays/element-details/actor.js +0 -8
  105. package/dist/overlays/overlay/Overlay.js +10 -10
  106. package/dist/overlays/overlaysActor.d.ts +7 -392
  107. package/dist/overlays/overlaysActor.js +67 -46
  108. package/dist/overlays/relationship-details/_types.d.ts +3 -4
  109. package/dist/overlays/relationship-details/actor.d.ts +6 -562
  110. package/dist/overlays/relationship-details/actor.js +1 -0
  111. package/dist/overlays/relationship-details/layout-to-xyflow.js +3 -5
  112. package/dist/overlays/relationship-details/layout.d.ts +3 -3
  113. package/dist/overlays/relationship-details/layout.js +4 -4
  114. package/dist/overlays/relationships-browser/_types.d.ts +2 -3
  115. package/dist/overlays/relationships-browser/actor.d.ts +7 -739
  116. package/dist/overlays/relationships-browser/layout.d.ts +2 -2
  117. package/dist/overlays/relationships-browser/layout.js +3 -3
  118. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +0 -3
  119. package/dist/overlays/types.d.ts +12 -13
  120. package/dist/search/components/_shared.css.d.ts +1 -1
  121. package/dist/search/searchActor.d.ts +3 -66
  122. package/dist/shadowroot/ShadowRoot.d.ts +5 -7
  123. package/dist/shadowroot/ShadowRoot.js +7 -5
  124. package/dist/styles.css2.js +1 -1
  125. package/dist/utils/vector.d.ts +5 -0
  126. package/dist/utils/vector.js +7 -0
  127. package/dist/utils/view-bounds.js +7 -6
  128. package/dist/utils/xyflow.d.ts +4 -2
  129. package/dist/utils/xyflow.js +18 -50
  130. package/package.json +19 -18
  131. package/panda.buildinfo.json +17 -5
  132. package/styles-min.css +1 -1
  133. package/styles.css +1 -1
  134. package/dist/likec4diagram/custom/nodes/ElementNodeDrifts.d.ts +0 -4
@@ -1,121 +1,16 @@
1
- import { BBox, invariant, nonNullable, nonexhaustive } from '@likec4/core';
2
- import { getViewportForBounds } from '@xyflow/react';
1
+ import { nonNullable, nonexhaustive, invariant } from '@likec4/core';
3
2
  import { nodeToRect } from '@xyflow/system';
4
- import { isNumber, isTruthy, hasAtLeast } from 'remeda';
3
+ import { produce } from 'immer';
4
+ import { isTruthy, hasAtLeast } from 'remeda';
5
5
  import { assertEvent } from 'xstate';
6
6
  import { createLayoutConstraints } from '../useLayoutConstraints.js';
7
7
  import { getAligner, toNodeRect } from './aligners.js';
8
- import { mergeXYNodesEdges, resetEdgeControlPoints, updateActiveWalkthrough, focusNodesEdges } from './assign.js';
8
+ import { resetEdgeControlPoints, mergeXYNodesEdges, focusNodesEdges } from './assign.js';
9
9
  import { createViewChange } from './createViewChange.js';
10
10
  import { machine } from './machine.setup.js';
11
- import { typedSystem, activeSequenceBounds, focusedBounds, findDiagramEdge, findDiagramNode } from './utils.js';
12
- import { MinZoom } from '../../base/const.js';
11
+ import { typedSystem, findDiagramEdge, findDiagramNode } from './utils.js';
13
12
  import { Base } from '../../base/Base.js';
14
13
 
15
- function nodeRef(node) {
16
- switch (node.type) {
17
- case "element":
18
- case "compound-element":
19
- case "seq-actor":
20
- return node.data.modelFqn;
21
- case "deployment":
22
- case "compound-deployment":
23
- return node.data.modelFqn ?? node.data.deploymentFqn;
24
- case "seq-parallel":
25
- case "view-group":
26
- return null;
27
- default:
28
- nonexhaustive(node);
29
- }
30
- }
31
- function findCorrespondingNode(context, event) {
32
- const fromNodeId = context.lastOnNavigate?.fromNode;
33
- const fromNode = fromNodeId && context.xynodes.find((n) => n.id === fromNodeId);
34
- const fromRef = fromNode && nodeRef(fromNode);
35
- const toNode = fromRef && event.xynodes.find((n) => nodeRef(n) === fromRef);
36
- return { fromNode, toNode };
37
- }
38
- const trigger = {
39
- navigateTo: () => machine.emit(({ context }) => ({
40
- type: "navigateTo",
41
- viewId: nonNullable(context.lastOnNavigate, "Invalid state, lastOnNavigate is null").toView
42
- }))
43
- };
44
- const xyflow = {
45
- fitDiagram: (params) => machine.createAction(({ context, event }) => {
46
- params ??= event.type === "fitDiagram" ? event : {};
47
- const {
48
- bounds = context.view.bounds,
49
- duration = 450
50
- } = params;
51
- const { width, height, panZoom, transform } = nonNullable(context.xystore).getState();
52
- const maxZoom = Math.max(1, transform[2]);
53
- const viewport = getViewportForBounds(
54
- bounds,
55
- width,
56
- height,
57
- MinZoom,
58
- maxZoom,
59
- context.fitViewPadding
60
- );
61
- viewport.x = Math.round(viewport.x);
62
- viewport.y = Math.round(viewport.y);
63
- panZoom?.setViewport(viewport, duration > 0 ? { duration, interpolate: "smooth" } : void 0).catch(
64
- console.error
65
- );
66
- }),
67
- fitFocusedBounds: () => machine.createAction(({ context }) => {
68
- const isActiveSequenceWalkthrough = !!context.activeWalkthrough && context.dynamicViewVariant === "sequence";
69
- const { bounds, duration = 450 } = isActiveSequenceWalkthrough ? activeSequenceBounds({ context }) : focusedBounds({ context });
70
- const { width, height, panZoom, transform } = nonNullable(context.xystore).getState();
71
- const maxZoom = Math.max(1, transform[2]);
72
- const viewport = getViewportForBounds(
73
- bounds,
74
- width,
75
- height,
76
- MinZoom,
77
- maxZoom,
78
- context.fitViewPadding
79
- );
80
- viewport.x = Math.round(viewport.x);
81
- viewport.y = Math.round(viewport.y);
82
- panZoom?.setViewport(viewport, duration > 0 ? { duration, interpolate: "smooth" } : void 0);
83
- }),
84
- alignNodeFromToAfterNavigate: (params) => machine.createAction(({ context }) => {
85
- const xyflow2 = nonNullable(context.xyflow, "xyflow is not initialized");
86
- const elFrom = xyflow2.getInternalNode(params.fromNode);
87
- if (!elFrom) return;
88
- const fromPos = xyflow2.flowToScreenPosition({
89
- x: elFrom.internals.positionAbsolute.x,
90
- y: elFrom.internals.positionAbsolute.y
91
- }), toPos = xyflow2.flowToScreenPosition(params.toPosition), diff = {
92
- x: Math.round(fromPos.x - toPos.x),
93
- y: Math.round(fromPos.y - toPos.y)
94
- };
95
- context.xystore.getState().panBy(diff);
96
- }),
97
- setViewportCenter: (params) => machine.createAction(({ context, event }) => {
98
- let center;
99
- if (params) {
100
- center = params;
101
- } else if (event.type === "update.view") {
102
- center = BBox.center(event.view.bounds);
103
- } else {
104
- center = BBox.center(context.view.bounds);
105
- }
106
- invariant(context.xyflow, "xyflow is not initialized");
107
- const zoom = context.xyflow.getZoom();
108
- context.xyflow.setCenter(Math.round(center.x), Math.round(center.y), { zoom });
109
- }),
110
- setViewport: (params) => machine.createAction(({ context }) => {
111
- const {
112
- viewport,
113
- duration = 350
114
- } = params;
115
- const panZoom = context.xystore?.getState().panZoom;
116
- panZoom?.setViewport(viewport, duration > 0 ? { duration, interpolate: "smooth" } : void 0);
117
- })
118
- };
119
14
  const disableCompareWithLatest = () => machine.assign(({ context }) => {
120
15
  return {
121
16
  toggledFeatures: {
@@ -143,7 +38,7 @@ const onEdgeDoubleClick = () => machine.assign(({ context, event }) => {
143
38
  ...e.data,
144
39
  controlPoints,
145
40
  labelBBox: e.data.labelBBox ? { ...e.data.labelBBox, ...pt } : null,
146
- labelXY: e.data.labelXY ? pt : null
41
+ labelXY: null
147
42
  }
148
43
  };
149
44
  }
@@ -151,60 +46,6 @@ const onEdgeDoubleClick = () => machine.assign(({ context, event }) => {
151
46
  })
152
47
  };
153
48
  });
154
- const emitOnChange = () => machine.enqueueActions(({ event, enqueue }) => {
155
- assertEvent(event, "emit.onChange");
156
- enqueue.assign({
157
- viewportChangedManually: true
158
- });
159
- enqueue.emit({
160
- type: "onChange",
161
- change: event.change
162
- });
163
- });
164
- const emitOnLayoutTypeChange = () => machine.enqueueActions(({ event, system, context, enqueue }) => {
165
- if (!context.features.enableCompareWithLatest) {
166
- console.warn("Layout type cannot be changed while CompareWithLatest feature is disabled");
167
- return;
168
- }
169
- const currentLayoutType = context.view._layout;
170
- let nextLayoutType = currentLayoutType === "auto" ? "manual" : "auto";
171
- if (event.type === "emit.onLayoutTypeChange") {
172
- nextLayoutType = event.layoutType;
173
- }
174
- if (currentLayoutType === nextLayoutType) {
175
- console.warn("Ignoring layout type change event, layout type is already", currentLayoutType);
176
- return;
177
- }
178
- if (context.toggledFeatures.enableCompareWithLatest === true) {
179
- if (currentLayoutType === "manual" && nextLayoutType === "auto") {
180
- const syncLayoutActor = typedSystem(system).syncLayoutActorRef;
181
- const syncState = syncLayoutActor?.getSnapshot().value;
182
- const isPending = syncLayoutActor && (syncState === "pending" || syncState === "paused");
183
- if (isPending) {
184
- enqueue.sendTo(syncLayoutActor, { type: "cancel" });
185
- enqueue.emit({
186
- type: "onChange",
187
- change: createViewChange(context)
188
- });
189
- }
190
- }
191
- const currentViewport = context.viewport;
192
- if (currentLayoutType === "auto") {
193
- enqueue.assign({
194
- viewportOnAutoLayout: currentViewport
195
- });
196
- }
197
- if (currentLayoutType === "manual") {
198
- enqueue.assign({
199
- viewportOnManualLayout: currentViewport
200
- });
201
- }
202
- }
203
- enqueue.emit({
204
- type: "onLayoutTypeChange",
205
- layoutType: nextLayoutType
206
- });
207
- });
208
49
  const assignLastClickedNode = () => machine.assign(({ context, event }) => {
209
50
  assertEvent(event, "xyflow.nodeClick");
210
51
  const { lastClickedNode } = context;
@@ -256,10 +97,23 @@ const updateInputs = () => machine.assign(({ event }) => {
256
97
  });
257
98
  const updateXYNodesEdges = () => machine.assign(({ context, event }) => {
258
99
  assertEvent(event, "update.view");
259
- return {
260
- ...mergeXYNodesEdges({ context, event }),
261
- lastClickedNode: null
262
- };
100
+ const update = mergeXYNodesEdges(context, event);
101
+ let { lastClickedNode, focusedNode } = context;
102
+ if (lastClickedNode || focusedNode) {
103
+ const nodeIds = new Set(update.xynodes.map((n) => n.id));
104
+ if (lastClickedNode && !nodeIds.has(lastClickedNode.id)) {
105
+ lastClickedNode = null;
106
+ }
107
+ if (focusedNode && !nodeIds.has(focusedNode)) {
108
+ focusedNode = null;
109
+ }
110
+ return {
111
+ ...update,
112
+ lastClickedNode,
113
+ focusedNode
114
+ };
115
+ }
116
+ return update;
263
117
  });
264
118
  const focusOnNodesAndEdges = () => machine.assign(focusNodesEdges);
265
119
  const undimEverything = () => machine.assign(({ context }) => ({
@@ -269,7 +123,6 @@ const undimEverything = () => machine.assign(({ context }) => ({
269
123
  active: false
270
124
  }))
271
125
  }));
272
- const updateActiveWalkthroughState = () => machine.assign(updateActiveWalkthrough);
273
126
  const assignDynamicViewVariant = () => machine.assign(({ event }) => {
274
127
  assertEvent(event, "switch.dynamicViewVariant");
275
128
  return {
@@ -306,9 +159,6 @@ const onNodeMouseLeave = (params) => machine.assign(({ context, event }) => {
306
159
  })
307
160
  };
308
161
  });
309
- const emitWalkthroughStopped = () => machine.emit(() => ({
310
- type: "walkthroughStopped"
311
- }));
312
162
  const emitPaneClick = () => machine.emit(() => ({
313
163
  type: "paneClick"
314
164
  }));
@@ -341,6 +191,10 @@ const emitNodeClick = () => machine.emit(({ context, event }) => {
341
191
  xynode: event.node
342
192
  };
343
193
  });
194
+ const emitNavigateTo = (params) => machine.emit(({ context }) => ({
195
+ type: "navigateTo",
196
+ viewId: params?.viewId ?? nonNullable(context.lastOnNavigate, "Invalid state, lastOnNavigate is null").toView
197
+ }));
344
198
  const emitEdgeClick = () => machine.emit(({ context, event }) => {
345
199
  assertEvent(event, "xyflow.edgeClick");
346
200
  const edge = nonNullable(findDiagramEdge(context, event.edge.id), `Edge ${event.edge.id} not found in diagram`);
@@ -350,21 +204,59 @@ const emitEdgeClick = () => machine.emit(({ context, event }) => {
350
204
  xyedge: event.edge
351
205
  };
352
206
  });
353
- const emitWalkthroughStarted = () => machine.emit(({ context }) => {
354
- const edge = context.xyedges.find((x) => x.id === context.activeWalkthrough?.stepId);
355
- invariant(edge, "Invalid walkthrough state");
356
- return {
357
- type: "walkthroughStarted",
358
- edge
359
- };
207
+ const emitOnChange = () => machine.enqueueActions(({ event, enqueue }) => {
208
+ assertEvent(event, "emit.onChange");
209
+ enqueue.assign({
210
+ viewportChangedManually: true
211
+ });
212
+ enqueue.emit({
213
+ type: "onChange",
214
+ change: event.change
215
+ });
360
216
  });
361
- const emitWalkthroughStep = () => machine.emit(({ context }) => {
362
- const edge = context.xyedges.find((x) => x.id === context.activeWalkthrough?.stepId);
363
- invariant(edge, "Invalid walkthrough state");
364
- return {
365
- type: "walkthroughStep",
366
- edge
367
- };
217
+ const emitOnLayoutTypeChange = () => machine.enqueueActions(({ event, system, context, enqueue }) => {
218
+ if (!context.features.enableCompareWithLatest) {
219
+ console.warn("Layout type cannot be changed while CompareWithLatest feature is disabled");
220
+ return;
221
+ }
222
+ const currentLayoutType = context.view._layout;
223
+ let nextLayoutType = currentLayoutType === "auto" ? "manual" : "auto";
224
+ if (event.type === "emit.onLayoutTypeChange") {
225
+ nextLayoutType = event.layoutType;
226
+ }
227
+ if (currentLayoutType === nextLayoutType) {
228
+ console.warn("Ignoring layout type change event, layout type is already", currentLayoutType);
229
+ return;
230
+ }
231
+ if (context.toggledFeatures.enableCompareWithLatest === true) {
232
+ if (currentLayoutType === "manual" && nextLayoutType === "auto") {
233
+ const syncLayoutActor = typedSystem(system).syncLayoutActorRef;
234
+ const syncState = syncLayoutActor?.getSnapshot().value;
235
+ const isPending = syncLayoutActor && (syncState === "pending" || syncState === "paused");
236
+ if (isPending) {
237
+ enqueue.sendTo(syncLayoutActor, { type: "cancel" });
238
+ enqueue.emit({
239
+ type: "onChange",
240
+ change: createViewChange(context)
241
+ });
242
+ }
243
+ }
244
+ const currentViewport = context.viewport;
245
+ if (currentLayoutType === "auto") {
246
+ enqueue.assign({
247
+ viewportOnAutoLayout: currentViewport
248
+ });
249
+ }
250
+ if (currentLayoutType === "manual") {
251
+ enqueue.assign({
252
+ viewportOnManualLayout: currentViewport
253
+ });
254
+ }
255
+ }
256
+ enqueue.emit({
257
+ type: "onLayoutTypeChange",
258
+ layoutType: nextLayoutType
259
+ });
368
260
  });
369
261
  const layoutAlign = (params) => machine.createAction(({ context, event }) => {
370
262
  let mode;
@@ -448,7 +340,7 @@ const tagHighlight = () => machine.assign(({ context, event }) => {
448
340
  })
449
341
  };
450
342
  });
451
- const toggleFeature = () => machine.assign(({ context, event }) => {
343
+ const assignToggledFeatures = () => machine.assign(({ context, event }) => {
452
344
  assertEvent(event, "toggle.feature");
453
345
  const currentValue = context.toggledFeatures[`enable${event.feature}`] ?? context.features[`enable${event.feature}`];
454
346
  const nextValue = event.forceValue ?? !currentValue;
@@ -471,29 +363,28 @@ const closeAllOverlays = () => machine.sendTo(
471
363
  type: "close.all"
472
364
  }
473
365
  );
474
- const isReadOnly = (context) => context.features.enableReadOnly || context.toggledFeatures.enableReadOnly === true;
475
366
  const stopSyncLayout = () => machine.enqueueActions(({ enqueue, system }) => {
476
367
  const syncLayoutActor = typedSystem(system).syncLayoutActorRef;
477
368
  if (!syncLayoutActor) return;
478
369
  enqueue.stopChild(syncLayoutActor);
479
370
  });
480
- const ensureSyncLayout = () => machine.enqueueActions(({ context, enqueue, system }) => {
371
+ const ensureSyncLayoutActor = () => machine.enqueueActions(({ context, enqueue, system, check }) => {
372
+ const isReadOnly = check("enabled: Readonly");
481
373
  const syncLayoutActor = typedSystem(system).syncLayoutActorRef;
482
- if (isReadOnly(context)) {
483
- if (syncLayoutActor) {
484
- enqueue.stopChild(syncLayoutActor);
485
- }
374
+ if (isReadOnly && syncLayoutActor) {
375
+ enqueue.stopChild(syncLayoutActor);
486
376
  return;
487
377
  }
488
- if (syncLayoutActor) return;
489
- enqueue.spawnChild("syncManualLayoutActorLogic", {
490
- id: "syncLayout",
491
- systemId: "syncLayout",
492
- input: {
493
- viewId: context.view.id
494
- },
495
- syncSnapshot: true
496
- });
378
+ if (!isReadOnly && !syncLayoutActor) {
379
+ enqueue.spawnChild("syncManualLayoutActorLogic", {
380
+ id: "syncLayout",
381
+ systemId: "syncLayout",
382
+ input: {
383
+ viewId: context.view.id
384
+ },
385
+ syncSnapshot: true
386
+ });
387
+ }
497
388
  });
498
389
  const startEditing = (subject = "node") => machine.sendTo(
499
390
  ({ system }) => typedSystem(system).syncLayoutActorRef,
@@ -502,6 +393,12 @@ const startEditing = (subject = "node") => machine.sendTo(
502
393
  subject
503
394
  }
504
395
  );
396
+ const sendSynced = () => machine.sendTo(
397
+ ({ system }) => typedSystem(system).syncLayoutActorRef,
398
+ {
399
+ type: "synced"
400
+ }
401
+ );
505
402
  const stopEditing = (wasChanged = true) => machine.sendTo(
506
403
  ({ system }) => typedSystem(system).syncLayoutActorRef,
507
404
  {
@@ -509,6 +406,12 @@ const stopEditing = (wasChanged = true) => machine.sendTo(
509
406
  wasChanged
510
407
  }
511
408
  );
409
+ const cancelEditing = () => machine.sendTo(
410
+ ({ system }) => typedSystem(system).syncLayoutActorRef,
411
+ {
412
+ type: "cancel"
413
+ }
414
+ );
512
415
  const hasModelFqn = (node) => "modelFqn" in node.data && isTruthy(node.data.modelFqn);
513
416
  const openElementDetails = (params) => machine.enqueueActions(({ context, event, enqueue, system }) => {
514
417
  let initiatedFrom = null;
@@ -584,8 +487,8 @@ const openSourceOfFocusedOrLastClickedNode = () => machine.enqueueActions(({ con
584
487
  enqueue.raise({ type: "open.source", element: diagramNode.modelRef });
585
488
  }
586
489
  });
587
- const ensureOverlaysActorState = () => machine.enqueueActions(({ enqueue, context: { features }, system }) => {
588
- const enableOverlays = features.enableElementDetails || features.enableRelationshipDetails || features.enableRelationshipBrowser;
490
+ const ensureOverlaysActor = () => machine.enqueueActions(({ enqueue, check, system }) => {
491
+ const enableOverlays = check("enabled: Overlays");
589
492
  const hasRunning = typedSystem(system).overlaysActorRef;
590
493
  if (enableOverlays && !hasRunning) {
591
494
  enqueue.spawnChild("overlaysActorLogic", { id: "overlays", systemId: "overlays" });
@@ -598,7 +501,7 @@ const ensureOverlaysActorState = () => machine.enqueueActions(({ enqueue, contex
598
501
  enqueue.stopChild("overlays");
599
502
  }
600
503
  });
601
- const ensureSearchActorState = () => machine.enqueueActions(({ enqueue, context: { features: { enableSearch } }, system }) => {
504
+ const ensureSearchActor = () => machine.enqueueActions(({ enqueue, context: { features: { enableSearch } }, system }) => {
602
505
  const hasRunning = typedSystem(system).searchActorRef;
603
506
  if (enableSearch && !hasRunning) {
604
507
  enqueue.spawnChild("searchActorLogic", { id: "search", systemId: "search" });
@@ -647,89 +550,95 @@ const onEdgeMouseLeave = () => machine.enqueueActions(({ enqueue, context, event
647
550
  event: event.event
648
551
  });
649
552
  });
650
- const cancelFitDiagram = () => machine.cancel("fitDiagram");
651
- const raiseFitDiagram = (params) => machine.enqueueActions(({ enqueue }) => {
652
- enqueue.cancel("fitDiagram");
653
- enqueue.raise(
654
- {
655
- type: "fitDiagram",
656
- ...isNumber(params?.duration) ? { duration: params.duration } : {}
657
- },
658
- {
659
- id: "fitDiagram",
660
- ...isNumber(params?.delay) ? { delay: params.delay } : {}
553
+ const startHotKeyActor = () => machine.spawnChild("hotkeyActorLogic", { id: "hotkey" });
554
+ const stopHotKeyActor = () => machine.stopChild("hotkey");
555
+ const handleNavigate = () => machine.enqueueActions(({ enqueue, context, event }) => {
556
+ assertEvent(event, ["navigate.to", "navigate.back", "navigate.forward"]);
557
+ console.log(`Handle ${event.type}`);
558
+ const {
559
+ view,
560
+ focusedNode,
561
+ activeWalkthrough,
562
+ dynamicViewVariant,
563
+ viewport,
564
+ viewportChangedManually,
565
+ viewportBefore,
566
+ navigationHistory: {
567
+ currentIndex,
568
+ history: _history
661
569
  }
662
- );
663
- });
664
- const updateView = () => machine.enqueueActions(
665
- ({ enqueue, event, check, context, system }) => {
666
- assertEvent(event, "update.view");
667
- const isAnotherView = check("is another view");
668
- enqueue(cancelFitDiagram());
669
- if (isAnotherView) {
670
- enqueue(stopSyncLayout());
671
- enqueue(closeAllOverlays());
672
- enqueue(closeSearch());
673
- enqueue.assign({
674
- focusedNode: null
675
- });
676
- enqueue(disableCompareWithLatest());
677
- const { fromNode, toNode } = findCorrespondingNode(context, event);
678
- if (fromNode && toNode) {
679
- enqueue(xyflow.alignNodeFromToAfterNavigate({
680
- fromNode: fromNode.id,
681
- toPosition: {
682
- x: toNode.data.x,
683
- y: toNode.data.y
684
- }
685
- }));
686
- enqueue(raiseFitDiagram({ delay: 80 }));
570
+ } = context;
571
+ let history = [..._history];
572
+ if (currentIndex < _history.length) {
573
+ const updatedEntry = produce(_history[currentIndex], (draft) => {
574
+ draft.viewport = { ...viewport };
575
+ draft.viewportChangedManually = viewportChangedManually;
576
+ draft.focusedNode = focusedNode;
577
+ if (view._type === "dynamic") {
578
+ draft.activeWalkthrough = activeWalkthrough?.stepId ?? null;
579
+ draft.dynamicViewVariant = dynamicViewVariant;
687
580
  } else {
688
- enqueue(
689
- xyflow.setViewportCenter()
690
- );
691
- enqueue(raiseFitDiagram({ duration: 200, delay: 25 }));
692
- }
693
- enqueue(updateXYNodesEdges());
694
- enqueue(ensureSyncLayout());
695
- return;
696
- }
697
- enqueue(updateXYNodesEdges());
698
- const syncLayoutActor = typedSystem(system).syncLayoutActorRef;
699
- if (syncLayoutActor) {
700
- enqueue.sendTo(syncLayoutActor, { type: "synced" });
701
- }
702
- const nextView = event.view;
703
- if (context.toggledFeatures.enableCompareWithLatest === true && context.view._layout !== nextView._layout) {
704
- if (nextView._layout === "auto" && context.viewportOnAutoLayout) {
705
- enqueue(
706
- xyflow.setViewport({
707
- viewport: context.viewportOnAutoLayout,
708
- duration: 0
709
- })
710
- );
711
- return;
581
+ draft.activeWalkthrough = null;
582
+ draft.dynamicViewVariant = null;
712
583
  }
713
- if (nextView._layout === "manual" && context.viewportOnManualLayout) {
714
- enqueue(
715
- xyflow.setViewport({
716
- viewport: context.viewportOnManualLayout,
717
- duration: 0
718
- })
719
- );
720
- return;
584
+ if (viewportBefore) {
585
+ draft.viewportBefore = viewportBefore;
586
+ } else {
587
+ delete draft.viewportBefore;
721
588
  }
589
+ });
590
+ history = [..._history];
591
+ history[currentIndex] = updatedEntry;
592
+ console.log("Update current history entry", {
593
+ before: _history[currentIndex],
594
+ after: updatedEntry
595
+ });
596
+ }
597
+ switch (event.type) {
598
+ case "navigate.to": {
599
+ enqueue.assign({
600
+ navigationHistory: {
601
+ currentIndex,
602
+ history
603
+ },
604
+ lastOnNavigate: {
605
+ fromView: context.view.id,
606
+ toView: event.viewId,
607
+ fromNode: event.fromNode ?? null
608
+ }
609
+ });
610
+ enqueue(emitNavigateTo());
611
+ break;
722
612
  }
723
- let recenter = !context.viewportChangedManually;
724
- recenter = recenter || nextView._type === "dynamic" && context.view._type === "dynamic" && nextView.variant !== context.view.variant;
725
- recenter = recenter || context.toggledFeatures.enableCompareWithLatest === true && !!nextView._layout && context.view._layout !== nextView._layout;
726
- if (recenter) {
727
- enqueue(
728
- xyflow.setViewportCenter(BBox.center(nextView.bounds))
729
- );
730
- enqueue(raiseFitDiagram({ delay: 50 }));
613
+ case "navigate.back": {
614
+ invariant(currentIndex > 0, "Cannot navigate back");
615
+ const stepBack = history[currentIndex - 1];
616
+ enqueue.assign({
617
+ navigationHistory: {
618
+ currentIndex: currentIndex - 1,
619
+ history
620
+ },
621
+ lastOnNavigate: null
622
+ });
623
+ enqueue(emitNavigateTo({ viewId: stepBack.viewId }));
624
+ break;
625
+ }
626
+ case "navigate.forward": {
627
+ invariant(currentIndex < history.length - 1, "Cannot navigate forward");
628
+ const stepForward = history[currentIndex + 1];
629
+ enqueue.assign({
630
+ navigationHistory: {
631
+ currentIndex: currentIndex + 1,
632
+ history
633
+ },
634
+ lastOnNavigate: null
635
+ });
636
+ enqueue(emitNavigateTo({ viewId: stepForward.viewId }));
637
+ break;
731
638
  }
639
+ default:
640
+ nonexhaustive(event);
732
641
  }
733
- );
642
+ });
734
643
 
735
- export { assignDynamicViewVariant, assignFocusedNode, assignLastClickedNode, cancelFitDiagram, closeAllOverlays, closeSearch, disableCompareWithLatest, emitEdgeClick, emitInitialized, emitNodeClick, emitOnChange, emitOnLayoutTypeChange, emitOpenSource, emitPaneClick, emitWalkthroughStarted, emitWalkthroughStep, emitWalkthroughStopped, ensureOverlaysActorState, ensureSearchActorState, ensureSyncLayout, findCorrespondingNode, focusOnNodesAndEdges, layoutAlign, nodeRef, notationsHighlight, onEdgeDoubleClick, onEdgeMouseEnter, onEdgeMouseLeave, onNodeMouseEnter, onNodeMouseLeave, openElementDetails, openSourceOfFocusedOrLastClickedNode, raiseFitDiagram, resetEdgesControlPoints, resetLastClickedNode, startEditing, stopEditing, stopSyncLayout, tagHighlight, toggleFeature, trigger, undimEverything, updateActiveWalkthroughState, updateFeatures, updateInputs, updateView, updateXYNodesEdges, xyflow };
644
+ export { assignDynamicViewVariant, assignFocusedNode, assignLastClickedNode, assignToggledFeatures, cancelEditing, closeAllOverlays, closeSearch, disableCompareWithLatest, emitEdgeClick, emitInitialized, emitNavigateTo, emitNodeClick, emitOnChange, emitOnLayoutTypeChange, emitOpenSource, emitPaneClick, ensureOverlaysActor, ensureSearchActor, ensureSyncLayoutActor, focusOnNodesAndEdges, handleNavigate, layoutAlign, notationsHighlight, onEdgeDoubleClick, onEdgeMouseEnter, onEdgeMouseLeave, onNodeMouseEnter, onNodeMouseLeave, openElementDetails, openSourceOfFocusedOrLastClickedNode, resetEdgesControlPoints, resetLastClickedNode, sendSynced, startEditing, startHotKeyActor, stopEditing, stopHotKeyActor, stopSyncLayout, tagHighlight, undimEverything, updateFeatures, updateInputs, updateXYNodesEdges };