@likec4/diagram 1.39.5 → 1.41.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 (140) hide show
  1. package/bundle/{chunk-CW_KClZe.js → chunk-FGcODVxo.js} +1059 -1023
  2. package/bundle/custom.js +61 -60
  3. package/bundle/index.js +2932 -764
  4. package/dist/LikeC4Diagram.d.ts +1 -1
  5. package/dist/LikeC4Diagram.js +7 -4
  6. package/dist/LikeC4Diagram.props.d.ts +6 -1
  7. package/dist/base/const.d.ts +9 -0
  8. package/dist/base/const.js +10 -1
  9. package/dist/base/index.d.ts +1 -0
  10. package/dist/base/primitives/MarkdownBlock.js +2 -0
  11. package/dist/base/primitives/edge/EdgeLabel.js +2 -1
  12. package/dist/base/primitives/edge/EdgeLabelContainer.js +10 -4
  13. package/dist/base/primitives/edge/EdgePath.js +17 -8
  14. package/dist/base/primitives/edge/edge.css.d.ts +0 -1
  15. package/dist/base/primitives/edge/edge.css.js +6 -14
  16. package/dist/base/primitives/element/ElementActionButtons.js +1 -3
  17. package/dist/base/primitives/element/ElementNodeContainer.d.ts +0 -6
  18. package/dist/base/primitives/element/ElementNodeContainer.js +3 -26
  19. package/dist/base/primitives/element/ElementTitle.js +2 -2
  20. package/dist/base/types.d.ts +3 -3
  21. package/dist/bundle/LikeC4View.js +4 -12
  22. package/dist/bundle/LikeC4View.props.d.ts +12 -3
  23. package/dist/bundle/custom.js +1 -1
  24. package/dist/components/Link.js +28 -20
  25. package/dist/custom/customNodes.d.ts +6 -0
  26. package/dist/custom/customNodes.js +2 -1
  27. package/dist/custom/index.js +1 -1
  28. package/dist/hooks/useDiagram.d.ts +5 -1
  29. package/dist/hooks/useDiagram.js +3 -0
  30. package/dist/hooks/useXYFlow.d.ts +4 -439
  31. package/dist/likec4diagram/DiagramUI.js +1 -1
  32. package/dist/likec4diagram/DiagramXYFlow.js +8 -4
  33. package/dist/likec4diagram/convert-to-xyflow.d.ts +14 -0
  34. package/dist/likec4diagram/convert-to-xyflow.js +29 -0
  35. package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +4 -4
  36. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +0 -1
  37. package/dist/likec4diagram/custom/edges/SequenceStepEdge.d.ts +3 -0
  38. package/dist/likec4diagram/custom/edges/SequenceStepEdge.js +71 -0
  39. package/dist/likec4diagram/custom/index.js +7 -2
  40. package/dist/likec4diagram/custom/nodes/ElementActions.d.ts +2 -1
  41. package/dist/likec4diagram/custom/nodes/SequenceActorNode.d.ts +4 -0
  42. package/dist/likec4diagram/custom/nodes/SequenceActorNode.js +157 -0
  43. package/dist/likec4diagram/custom/nodes/index.d.ts +1 -0
  44. package/dist/likec4diagram/custom/nodes/nodes.d.ts +1 -2
  45. package/dist/likec4diagram/relationship-popover/RelationshipPopover.js +31 -27
  46. package/dist/likec4diagram/types.d.ts +41 -7
  47. package/dist/likec4diagram/xyflow-diagram/diagram-view.d.ts +11 -0
  48. package/dist/likec4diagram/{useViewToNodesEdges.js → xyflow-diagram/diagram-view.js} +4 -13
  49. package/dist/likec4diagram/xyflow-sequence/_types.d.ts +41 -0
  50. package/dist/likec4diagram/xyflow-sequence/const.d.ts +17 -0
  51. package/dist/likec4diagram/xyflow-sequence/const.js +16 -0
  52. package/dist/likec4diagram/xyflow-sequence/layouter.d.ts +46 -0
  53. package/dist/likec4diagram/xyflow-sequence/layouter.js +408 -0
  54. package/dist/likec4diagram/xyflow-sequence/sequence-view.d.ts +7 -0
  55. package/dist/likec4diagram/xyflow-sequence/sequence-view.js +273 -0
  56. package/dist/likec4diagram/xyflow-sequence/utils.d.ts +14 -0
  57. package/dist/likec4diagram/xyflow-sequence/utils.js +92 -0
  58. package/dist/navigationpanel/NavigationPanelControls.js +2 -2
  59. package/dist/navigationpanel/controls/ViewDetailsCard.js +27 -12
  60. package/dist/navigationpanel/dropdown/ProjectsMenu.js +53 -44
  61. package/dist/navigationpanel/walkthrough/ActiveWalkthroughControls.d.ts +1 -2
  62. package/dist/navigationpanel/walkthrough/ActiveWalkthroughControls.js +10 -41
  63. package/dist/navigationpanel/walkthrough/DynamicViewControls.d.ts +292 -0
  64. package/dist/navigationpanel/walkthrough/DynamicViewControls.js +119 -0
  65. package/dist/navigationpanel/walkthrough/index.d.ts +2 -1
  66. package/dist/overlays/element-details/ElementDetailsCard.css.js +7 -1
  67. package/dist/overlays/element-details/ElementDetailsCard.js +17 -19
  68. package/dist/overlays/relationship-details/RelationshipDetails.js +4 -1
  69. package/dist/overlays/relationship-details/layout.d.ts +3 -2
  70. package/dist/overlays/relationship-details/layout.js +1 -2
  71. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +2 -1
  72. package/dist/overlays/relationships-browser/layout.js +1 -1
  73. package/dist/search/components/ElementsColumn.js +1 -1
  74. package/dist/state/DiagramActorProvider.d.ts +3 -2
  75. package/dist/state/DiagramActorProvider.js +40 -41
  76. package/dist/state/assign.js +11 -4
  77. package/dist/state/diagram-machine.d.ts +224 -194
  78. package/dist/state/diagram-machine.js +170 -63
  79. package/dist/state/utils.d.ts +7 -1
  80. package/dist/state/utils.js +67 -7
  81. package/dist/styles-min.css +1 -1
  82. package/dist/styles.css +1 -1
  83. package/dist/styles.css2.js +1 -1
  84. package/package.json +8 -7
  85. package/panda.buildinfo.json +45 -24
  86. package/src/LikeC4Diagram.props.ts +14 -1
  87. package/src/LikeC4Diagram.tsx +7 -4
  88. package/src/base/const.ts +12 -0
  89. package/src/base/index.ts +2 -0
  90. package/src/base/primitives/MarkdownBlock.tsx +2 -0
  91. package/src/base/primitives/edge/EdgeLabel.tsx +23 -19
  92. package/src/base/primitives/edge/EdgeLabelContainer.tsx +12 -7
  93. package/src/base/primitives/edge/EdgePath.tsx +25 -14
  94. package/src/base/primitives/edge/edge.css.ts +5 -14
  95. package/src/base/primitives/element/ElementActionButtons.tsx +1 -3
  96. package/src/base/primitives/element/ElementNodeContainer.tsx +3 -30
  97. package/src/base/primitives/element/ElementTitle.tsx +2 -3
  98. package/src/base/types.ts +7 -5
  99. package/src/bundle/LikeC4View.props.ts +14 -3
  100. package/src/bundle/LikeC4View.tsx +4 -14
  101. package/src/bundle/ReactLikeC4.tsx +1 -2
  102. package/src/components/Link.tsx +21 -18
  103. package/src/custom/customNodes.ts +7 -0
  104. package/src/hooks/useDiagram.ts +10 -0
  105. package/src/likec4diagram/DiagramUI.tsx +1 -1
  106. package/src/likec4diagram/DiagramXYFlow.tsx +10 -5
  107. package/src/likec4diagram/convert-to-xyflow.ts +45 -0
  108. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +0 -1
  109. package/src/likec4diagram/custom/edges/SequenceStepEdge.tsx +65 -0
  110. package/src/likec4diagram/custom/index.ts +7 -1
  111. package/src/likec4diagram/custom/nodes/ElementActions.tsx +4 -2
  112. package/src/likec4diagram/custom/nodes/SequenceActorNode.tsx +175 -0
  113. package/src/likec4diagram/custom/nodes/index.ts +1 -0
  114. package/src/likec4diagram/custom/nodes/nodes.tsx +1 -1
  115. package/src/likec4diagram/relationship-popover/RelationshipPopover.tsx +36 -30
  116. package/src/likec4diagram/types.ts +96 -13
  117. package/src/likec4diagram/{useViewToNodesEdges.ts → xyflow-diagram/diagram-view.ts} +7 -28
  118. package/src/likec4diagram/xyflow-sequence/_types.ts +45 -0
  119. package/src/likec4diagram/xyflow-sequence/const.ts +37 -0
  120. package/src/likec4diagram/xyflow-sequence/layouter.ts +567 -0
  121. package/src/likec4diagram/xyflow-sequence/sequence-view.ts +337 -0
  122. package/src/likec4diagram/xyflow-sequence/utils.ts +112 -0
  123. package/src/navigationpanel/NavigationPanelControls.tsx +2 -2
  124. package/src/navigationpanel/controls/ViewDetailsCard.tsx +29 -12
  125. package/src/navigationpanel/dropdown/ProjectsMenu.tsx +5 -1
  126. package/src/navigationpanel/walkthrough/ActiveWalkthroughControls.tsx +5 -40
  127. package/src/navigationpanel/walkthrough/DynamicViewControls.tsx +125 -0
  128. package/src/navigationpanel/walkthrough/index.tsx +2 -1
  129. package/src/overlays/element-details/ElementDetailsCard.css.ts +7 -1
  130. package/src/overlays/element-details/ElementDetailsCard.tsx +24 -25
  131. package/src/overlays/relationship-details/RelationshipDetails.tsx +5 -2
  132. package/src/overlays/relationship-details/layout.ts +4 -3
  133. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +2 -1
  134. package/src/overlays/relationships-browser/layout.ts +1 -1
  135. package/src/search/components/ElementsColumn.tsx +1 -1
  136. package/src/state/DiagramActorProvider.tsx +46 -51
  137. package/src/state/assign.ts +18 -5
  138. package/src/state/diagram-machine.ts +207 -75
  139. package/src/state/utils.ts +81 -7
  140. package/dist/likec4diagram/useViewToNodesEdges.d.ts +0 -12
@@ -0,0 +1,408 @@
1
+ import { nonNullable, invariant, nonexhaustive } from '@likec4/core/utils';
2
+ import * as kiwi from '@lume/kiwi';
3
+ import { last, map, only } from 'remeda';
4
+ import { FIRST_STEP_OFFSET, ACTOR_GAP, COLUMN_GAP, PORT_HEIGHT, STEP_LABEL_MARGIN, MIN_ROW_HEIGHT } from './const.js';
5
+ import { findParallelRects } from './utils.js';
6
+
7
+ class SequenceViewLayouter {
8
+ #solver = new kiwi.Solver();
9
+ #actors;
10
+ #compounds = [];
11
+ #viewportRight;
12
+ #viewportBottom;
13
+ #rowsTop;
14
+ #rows = [];
15
+ #parallelBoxes = [];
16
+ constructor({
17
+ actors,
18
+ steps,
19
+ compounds
20
+ }) {
21
+ this.#rowsTop = this.newVar(FIRST_STEP_OFFSET);
22
+ this.#viewportRight = this.newVar(0);
23
+ this.#viewportBottom = this.newVar(0);
24
+ this.#actors = this.addActors(actors);
25
+ for (const compound of compounds) {
26
+ const result = this.addCompound(compound);
27
+ const toplevel = result[0];
28
+ this.constraint(toplevel.y1, "==", 0, kiwi.Strength.strong);
29
+ this.put(this.#viewportBottom).after(toplevel.bottom);
30
+ this.put(this.#rowsTop).after(toplevel.y2);
31
+ this.#compounds.push(...result);
32
+ }
33
+ for (const step of steps) {
34
+ this.addStep(step);
35
+ }
36
+ for (const parallelRect of findParallelRects(steps)) {
37
+ this.addParallelRect(parallelRect);
38
+ }
39
+ const firstActor = this.#actors[0];
40
+ this.constraint(firstActor.offset.left, "==", 0, kiwi.Strength.strong);
41
+ const lastActor = this.#actors.reduce((prev, actor) => {
42
+ this.put(actor.x).after(prev.right, ACTOR_GAP);
43
+ this.put(actor.offset.left, kiwi.Strength.strong).after(prev.offset.right, COLUMN_GAP);
44
+ this.constraint(actor.centerY, "==", prev.centerY, kiwi.Strength.strong);
45
+ this.put(this.#rowsTop).after(actor.offset.bottom);
46
+ return actor;
47
+ });
48
+ this.put(this.#viewportRight).after(lastActor.offset.right);
49
+ this.put(this.#viewportBottom).after(last(this.#rows)?.bottom ?? this.#rowsTop);
50
+ if (compounds.length > 0) {
51
+ for (const compound of this.#compounds) {
52
+ const from = compound.from.column;
53
+ const to = compound.to.column;
54
+ let maxRow = Math.max(compound.from.maxRow, compound.to.maxRow);
55
+ for (let i = from + 1; i < to; i++) {
56
+ const actorBox = this.actorBox(i);
57
+ maxRow = Math.max(maxRow, actorBox.maxRow);
58
+ }
59
+ const lastRow = nonNullable(this.#rows[maxRow], `row ${maxRow} not found`);
60
+ this.put(compound.bottom).after(lastRow.bottom, 16);
61
+ this.put(this.#viewportBottom).after(compound.bottom);
62
+ }
63
+ }
64
+ this.#solver.updateVariables();
65
+ }
66
+ getParallelBoxes() {
67
+ return this.#parallelBoxes.map(({ parallelPrefix, x1, y1, x2, y2 }) => ({
68
+ parallelPrefix,
69
+ x: x1.value(),
70
+ y: y1.value(),
71
+ width: x2.value() - x1.value(),
72
+ height: y2.value() - y1.value()
73
+ }));
74
+ }
75
+ getActorBox(actor) {
76
+ const actorBox = this.actorBox(actor);
77
+ return {
78
+ x: actorBox.x.value(),
79
+ y: actorBox.y.value(),
80
+ width: actorBox.width,
81
+ height: actorBox.height
82
+ };
83
+ }
84
+ getCompoundBoxes() {
85
+ return this.#compounds.map(({ node, depth, x1, y1, x2, y2, bottom }) => ({
86
+ node,
87
+ depth,
88
+ x: x1.value(),
89
+ y: y1.value(),
90
+ width: x2.value() - x1.value(),
91
+ height: bottom.value() - y1.value()
92
+ }));
93
+ }
94
+ getPortCenter(step, type) {
95
+ const { column, row } = type === "source" ? step.from : step.to;
96
+ const x = this.actorBox(column).centerX;
97
+ const { y } = nonNullable(this.#rows[row]);
98
+ return {
99
+ cx: x.value(),
100
+ cy: y.value() + PORT_HEIGHT / 2 + step.offset,
101
+ height: type === "source" ? 40 : 24
102
+ };
103
+ }
104
+ getViewBounds() {
105
+ return {
106
+ x: 0,
107
+ y: 0,
108
+ width: this.#viewportRight.value(),
109
+ height: this.#viewportBottom.value()
110
+ // Max Y,
111
+ };
112
+ }
113
+ actorBox(actor) {
114
+ if (typeof actor !== "number") {
115
+ const id = typeof actor === "string" ? actor : actor.id;
116
+ actor = this.#actors.findIndex((a) => a.actor.id === id);
117
+ invariant(actor >= 0, `actor ${id} not found`);
118
+ }
119
+ return nonNullable(this.#actors[actor], `actor at index ${actor} not found`);
120
+ }
121
+ addActors(actors) {
122
+ let accX = 0;
123
+ return map(actors, (actor, column) => {
124
+ const x = this.newVar(accX);
125
+ accX += actor.width + ACTOR_GAP;
126
+ const y = this.newVar(0);
127
+ const actorBox = {
128
+ column,
129
+ actor,
130
+ x,
131
+ y,
132
+ centerX: x.plus(Math.round(actor.width / 2)),
133
+ centerY: y.plus(Math.round(actor.height / 2)),
134
+ width: actor.width,
135
+ height: actor.height,
136
+ right: x.plus(actor.width),
137
+ bottom: y.plus(actor.height),
138
+ minRow: Infinity,
139
+ maxRow: -Infinity
140
+ };
141
+ const top = this.newVar(0), left = this.newVar(0), right = this.newVar(0), bottom = this.newVar(0);
142
+ this.put(top, kiwi.Strength.strong).before(y);
143
+ this.put(left, kiwi.Strength.strong).before(x);
144
+ this.put(right, kiwi.Strength.strong).after(actorBox.right);
145
+ this.put(bottom, kiwi.Strength.strong).after(actorBox.bottom);
146
+ return {
147
+ ...actorBox,
148
+ offset: {
149
+ top,
150
+ left,
151
+ right,
152
+ bottom
153
+ }
154
+ };
155
+ });
156
+ }
157
+ addStep(step) {
158
+ const source = this.actorBox(step.source);
159
+ const target = this.actorBox(step.target);
160
+ source.minRow = Math.min(source.minRow, step.from.row);
161
+ source.maxRow = Math.max(source.maxRow, step.from.row);
162
+ target.minRow = Math.min(target.minRow, step.to.row);
163
+ target.maxRow = Math.max(target.maxRow, step.to.row);
164
+ const [left, right] = source.column <= target.column ? [source, target] : [target, source];
165
+ const width = (step.label?.width ?? 100) + STEP_LABEL_MARGIN;
166
+ if (left !== right) {
167
+ this.constraint(left.centerX.plus(width), "<=", right.centerX);
168
+ } else {
169
+ this.constraint(left.centerX.plus(width), "<=", left.offset.right);
170
+ }
171
+ let height = step.label?.height ? step.label.height + STEP_LABEL_MARGIN + PORT_HEIGHT / 2 : MIN_ROW_HEIGHT;
172
+ height = Math.max(height, MIN_ROW_HEIGHT) + step.offset;
173
+ this.ensureRow(step.from.row, height);
174
+ if (step.isSelfLoop) {
175
+ this.ensureRow(step.to.row, MIN_ROW_HEIGHT);
176
+ }
177
+ return this;
178
+ }
179
+ addParallelRect({
180
+ parallelPrefix,
181
+ min,
182
+ max
183
+ }) {
184
+ const x1 = this.actorBox(min.column).centerX.minus(30);
185
+ const x2 = this.actorBox(max.column).centerX.plus(30);
186
+ const firstRow = this.#rows[min.row];
187
+ const lastRow = this.#rows[max.row];
188
+ invariant(firstRow && lastRow, `parallel box invalid x1${x1} x2${x2} y1${firstRow} y2${lastRow}`);
189
+ const y1 = this.newVar(0);
190
+ this.put(y1).before(firstRow.y, 40);
191
+ const y2 = lastRow.bottom;
192
+ const rowBefore = min.row > 0 && this.#rows[min.row - 1];
193
+ if (rowBefore) {
194
+ this.put(y1).after(rowBefore.bottom, 16);
195
+ }
196
+ const rowAfter = max.row < this.#rows.length - 1 && this.#rows[max.row + 1];
197
+ if (rowAfter) {
198
+ this.put(y2).before(rowAfter.y, 16);
199
+ }
200
+ this.#parallelBoxes.push({
201
+ parallelPrefix,
202
+ x1,
203
+ y1,
204
+ x2,
205
+ y2
206
+ });
207
+ }
208
+ addCompound(compound) {
209
+ const PADDING = 32;
210
+ const PADDING_TOP = 40;
211
+ const PADDING_TOP_FROM_ACTOR = 52;
212
+ const children = [];
213
+ const nested = compound.nested.flatMap((c) => {
214
+ const result = this.addCompound(c);
215
+ children.push(result[0]);
216
+ return result;
217
+ });
218
+ const depth = Math.max(...nested.map((c) => c.depth + 1), 0);
219
+ const from = this.actorBox(compound.from);
220
+ const to = this.actorBox(compound.to);
221
+ const x1 = from.offset.left.minus(PADDING);
222
+ from.offset.left = x1;
223
+ const x2 = to.offset.right.plus(PADDING);
224
+ to.offset.right = x2;
225
+ const bottom = this.newVar(0);
226
+ const onlyChild = only(children);
227
+ let y1, y2;
228
+ switch (true) {
229
+ case !!onlyChild: {
230
+ y1 = onlyChild.y1.minus(PADDING_TOP);
231
+ y2 = onlyChild.y2.plus(PADDING);
232
+ this.put(bottom).after(onlyChild.bottom, PADDING);
233
+ break;
234
+ }
235
+ // Compound with single actor
236
+ case to === from: {
237
+ y1 = this.newVar(0);
238
+ y2 = this.newVar(0);
239
+ this.put(y1).before(from.offset.top, PADDING_TOP_FROM_ACTOR);
240
+ this.put(y2).after(from.offset.bottom, PADDING);
241
+ this.put(bottom).after(y2);
242
+ break;
243
+ }
244
+ // Compound nested compound, offset from it
245
+ case children.length > 0: {
246
+ y1 = this.newVar(0);
247
+ y2 = this.newVar(0);
248
+ for (const child of children) {
249
+ this.put(y1).before(child.y1, PADDING);
250
+ this.put(y2).after(child.y2, PADDING);
251
+ this.put(bottom).after(child.bottom, PADDING);
252
+ }
253
+ break;
254
+ }
255
+ default: {
256
+ y1 = this.newVar(0);
257
+ y2 = this.newVar(0);
258
+ for (var col = from.column; col <= to.column; col++) {
259
+ const offset = this.actorBox(col).offset;
260
+ this.put(y1).before(offset.top, PADDING_TOP_FROM_ACTOR);
261
+ this.put(y2).after(offset.bottom, PADDING);
262
+ }
263
+ this.put(bottom).after(y2);
264
+ break;
265
+ }
266
+ }
267
+ for (var col = from.column; col <= to.column; col++) {
268
+ const offset = this.actorBox(col).offset;
269
+ offset.top = y1;
270
+ offset.bottom = y2;
271
+ }
272
+ return [
273
+ {
274
+ node: compound.node,
275
+ depth,
276
+ from,
277
+ to,
278
+ x1,
279
+ y1,
280
+ x2,
281
+ y2,
282
+ bottom
283
+ },
284
+ ...nested
285
+ ];
286
+ }
287
+ ensureRow(row, rowHeight) {
288
+ while (row >= this.#rows.length) {
289
+ const prevRowY = this.#rows.length > 0 && this.#rows[this.#rows.length - 1]?.bottom || this.#rowsTop.plus(FIRST_STEP_OFFSET);
290
+ const y = this.newVar(this.#rows.length * MIN_ROW_HEIGHT);
291
+ this.put(y).after(prevRowY);
292
+ const height = this.newVar(MIN_ROW_HEIGHT);
293
+ this.require(height, ">=", MIN_ROW_HEIGHT);
294
+ this.#rows.push({
295
+ y,
296
+ height,
297
+ bottom: y.plus(height),
298
+ lastHeight: MIN_ROW_HEIGHT
299
+ });
300
+ }
301
+ const rowVar = nonNullable(this.#rows[row]);
302
+ if (rowHeight > rowVar.lastHeight) {
303
+ rowVar.lastHeight = rowHeight;
304
+ this.require(rowVar.height, ">=", rowHeight);
305
+ this.#solver.suggestValue(rowVar.height, rowHeight);
306
+ }
307
+ }
308
+ newVar(initialValue) {
309
+ const v = new kiwi.Variable();
310
+ this.#solver.addEditVariable(v, kiwi.Strength.weak);
311
+ if (initialValue) {
312
+ this.#solver.suggestValue(v, initialValue);
313
+ this.constraint(v, ">=", 0, kiwi.Strength.strong);
314
+ }
315
+ return v;
316
+ }
317
+ /**
318
+ * Adds a required constraint:
319
+ * Also adds a weak constraint == if the operator is <= or >=
320
+ */
321
+ require(left, op, right = void 0) {
322
+ this.constraint(left, op, right, kiwi.Strength.required);
323
+ switch (op) {
324
+ case "<=":
325
+ case ">=":
326
+ this.constraint(left, "==", right, kiwi.Strength.weak);
327
+ break;
328
+ case "<= 0":
329
+ case ">= 0":
330
+ this.constraint(left, "== 0", void 0, kiwi.Strength.weak);
331
+ break;
332
+ }
333
+ }
334
+ /**
335
+ * Adds a constraint with medium strength by default
336
+ */
337
+ constraint(left, op, right = void 0, strength = kiwi.Strength.medium) {
338
+ let operator;
339
+ switch (op) {
340
+ case "==":
341
+ operator = kiwi.Operator.Eq;
342
+ break;
343
+ case ">=":
344
+ operator = kiwi.Operator.Ge;
345
+ break;
346
+ case "<=":
347
+ operator = kiwi.Operator.Le;
348
+ break;
349
+ case "== 0": {
350
+ operator = kiwi.Operator.Eq;
351
+ right = 0;
352
+ break;
353
+ }
354
+ case ">= 0": {
355
+ operator = kiwi.Operator.Ge;
356
+ right = 0;
357
+ break;
358
+ }
359
+ case "<= 0": {
360
+ operator = kiwi.Operator.Le;
361
+ right = 0;
362
+ break;
363
+ }
364
+ default:
365
+ nonexhaustive(op);
366
+ }
367
+ this.#solver.addConstraint(new kiwi.Constraint(left, operator, right ?? 0, strength));
368
+ }
369
+ put(variable, strength = kiwi.Strength.required) {
370
+ const eqStrength = strength === kiwi.Strength.required ? kiwi.Strength.medium : kiwi.Strength.weak;
371
+ return {
372
+ before: (other, gap) => {
373
+ if (gap) {
374
+ other = other.minus(gap);
375
+ }
376
+ this.constraint(variable, "<=", other, strength);
377
+ this.constraint(variable, "==", other, eqStrength);
378
+ },
379
+ after: (other, gap) => {
380
+ if (gap) {
381
+ other = other.plus(gap);
382
+ }
383
+ this.constraint(variable, ">=", other, strength);
384
+ this.constraint(variable, "==", other, eqStrength);
385
+ }
386
+ };
387
+ }
388
+ // private recalcActorsRect() {
389
+ // this.#actorsRect = this.#columnRects.reduce((acc, rect, index, all) => {
390
+ // if (index === 0) {
391
+ // acc.minX = rect.min.x.value()
392
+ // }
393
+ // if (index === all.length - 1) {
394
+ // acc.maxX = rect.max.x.value()
395
+ // }
396
+ // acc.minY = Math.min(acc.minY, rect.min.y.value())
397
+ // acc.maxY = Math.max(acc.maxY, rect.max.y.value())
398
+ // return acc
399
+ // }, {
400
+ // minX: Infinity,
401
+ // minY: Infinity,
402
+ // maxX: -Infinity,
403
+ // maxY: -Infinity,
404
+ // })
405
+ // }
406
+ }
407
+
408
+ export { SequenceViewLayouter };
@@ -0,0 +1,7 @@
1
+ import { BBox, LayoutedDynamicView } from '@likec4/core/types';
2
+ import { Types } from '../types';
3
+ export declare function sequenceViewToXY(view: LayoutedDynamicView): {
4
+ bounds: BBox;
5
+ xynodes: Array<Types.SequenceActorNode | Types.SequenceParallelArea | Types.ViewGroupNode>;
6
+ xyedges: Array<Types.SequenceStepEdge>;
7
+ };
@@ -0,0 +1,273 @@
1
+ import { isStepEdgeId, getParallelStepsPrefix, RichText } from '@likec4/core/types';
2
+ import { DefaultMap, nonNullable, invariant } from '@likec4/core/utils';
3
+ import { hasAtLeast } from 'remeda';
4
+ import { CONTINUOUS_OFFSET, SeqZIndex, SeqParallelAreaColor } from './const.js';
5
+ import { SequenceViewLayouter } from './layouter.js';
6
+ import { buildCompounds } from './utils.js';
7
+
8
+ function sequenceViewToXY(view) {
9
+ const actors = [];
10
+ const actorPorts = new DefaultMap(() => []);
11
+ const steps = [];
12
+ const getNode = (id) => nonNullable(view.nodes.find((n) => n.id === id));
13
+ const parentsLookup = new DefaultMap(
14
+ (key) => {
15
+ const parent = key.parent ? getNode(key.parent) : null;
16
+ if (parent) {
17
+ return [parent, ...parentsLookup.get(parent)];
18
+ }
19
+ return [];
20
+ }
21
+ );
22
+ const addActor = (...[source, target]) => {
23
+ if (!actors.includes(source)) {
24
+ const indexOfTarget = actors.indexOf(target);
25
+ if (indexOfTarget > 0) {
26
+ actors.splice(indexOfTarget, 0, source);
27
+ } else {
28
+ actors.push(source);
29
+ }
30
+ }
31
+ if (!actors.includes(target)) {
32
+ actors.push(target);
33
+ }
34
+ };
35
+ let row = 0;
36
+ for (const edge of view.edges.filter((e) => isStepEdgeId(e.id))) {
37
+ const prevStep = steps.at(-1);
38
+ const source = getNode(edge.source);
39
+ const target = getNode(edge.target);
40
+ if (source.children.length || target.children.length) {
41
+ console.error("Sequence view does not support nested actors");
42
+ continue;
43
+ }
44
+ let sourceColumn = actors.indexOf(source);
45
+ let targetColumn = actors.indexOf(target);
46
+ const alreadyAdded = sourceColumn >= 0 && targetColumn >= 0;
47
+ if (!alreadyAdded) {
48
+ if (edge.dir === "back") {
49
+ addActor(target, source);
50
+ } else {
51
+ addActor(source, target);
52
+ }
53
+ sourceColumn = actors.indexOf(source);
54
+ targetColumn = actors.indexOf(target);
55
+ }
56
+ const isSelfLoop = source === target;
57
+ const isBack = sourceColumn > targetColumn;
58
+ const parallelPrefix = getParallelStepsPrefix(edge.id);
59
+ let isContinuing = false;
60
+ if (prevStep && prevStep.target == source && prevStep.parallelPrefix === parallelPrefix) {
61
+ isContinuing = prevStep.isSelfLoop !== isSelfLoop || prevStep.isBack === isBack;
62
+ }
63
+ if (!isContinuing) {
64
+ row++;
65
+ }
66
+ const step = {
67
+ id: edge.id,
68
+ from: {
69
+ column: sourceColumn,
70
+ row
71
+ },
72
+ to: {
73
+ column: targetColumn,
74
+ row: isSelfLoop ? ++row : row
75
+ },
76
+ edge,
77
+ isSelfLoop,
78
+ isBack,
79
+ parallelPrefix,
80
+ offset: isContinuing ? (prevStep?.offset ?? 0) + CONTINUOUS_OFFSET : 0,
81
+ source,
82
+ target,
83
+ label: edge.labelBBox ? {
84
+ height: edge.labelBBox.height + 8 + (edge.navigateTo ? 20 : 0),
85
+ width: edge.labelBBox.width + 16,
86
+ text: edge.label
87
+ } : null
88
+ };
89
+ steps.push(step);
90
+ actorPorts.get(source).push({ step, row, type: "source", position: isBack && !isSelfLoop ? "left" : "right" });
91
+ actorPorts.get(target).push({ step, row, type: "target", position: isBack || isSelfLoop ? "right" : "left" });
92
+ }
93
+ for (const step of steps) {
94
+ step.from.column = actors.indexOf(step.source);
95
+ step.to.column = actors.indexOf(step.target);
96
+ }
97
+ invariant(hasAtLeast(actors, 1), "actors array must not be empty");
98
+ const layout = new SequenceViewLayouter({
99
+ actors,
100
+ steps,
101
+ compounds: buildCompounds(actors, view.nodes)
102
+ });
103
+ const bounds = layout.getViewBounds();
104
+ return {
105
+ bounds,
106
+ xynodes: [
107
+ ...layout.getCompoundBoxes().map((box, i) => toCompoundArea(box, i, view)),
108
+ ...layout.getParallelBoxes().map((box) => toSeqParallelArea(box, view)),
109
+ ...actors.map(
110
+ (actor) => toSeqActorNode({
111
+ actor,
112
+ ports: actorPorts.get(actor),
113
+ bounds,
114
+ layout,
115
+ view
116
+ })
117
+ )
118
+ ],
119
+ xyedges: steps.map(({ id, edge, ...step }) => ({
120
+ id,
121
+ type: "seq-step",
122
+ data: {
123
+ id,
124
+ label: step.label?.text ?? null,
125
+ technology: edge.technology,
126
+ notes: RichText.from(edge.notes),
127
+ navigateTo: edge.navigateTo,
128
+ controlPoints: edge.controlPoints ?? null,
129
+ labelBBox: {
130
+ x: 0,
131
+ y: 0,
132
+ width: step.label?.width ?? edge.labelBBox?.width ?? 32,
133
+ height: step.label?.height ?? edge.labelBBox?.height ?? 32
134
+ },
135
+ labelXY: null,
136
+ points: edge.points,
137
+ color: edge.color ?? "gray",
138
+ line: edge.line ?? "solid",
139
+ dir: "forward",
140
+ head: "normal",
141
+ tail: "none"
142
+ },
143
+ selectable: true,
144
+ focusable: false,
145
+ zIndex: 20,
146
+ interactionWidth: 40,
147
+ source: step.source.id,
148
+ sourceHandle: id + "_source",
149
+ target: step.target.id,
150
+ targetHandle: id + "_target"
151
+ }))
152
+ };
153
+ }
154
+ function toCompoundArea({ node, x, y, width, height, depth }, index, view) {
155
+ return {
156
+ id: `${node.id}-${index}`,
157
+ type: "view-group",
158
+ data: {
159
+ id: node.id,
160
+ title: node.title,
161
+ color: node.color ?? "gray",
162
+ shape: node.shape,
163
+ style: node.style,
164
+ tags: node.tags,
165
+ position: [x, y],
166
+ viewId: view.id,
167
+ depth,
168
+ isViewGroup: true
169
+ },
170
+ // zIndex: SeqZIndex.compound,
171
+ position: {
172
+ x,
173
+ y
174
+ },
175
+ draggable: false,
176
+ selectable: false,
177
+ focusable: false,
178
+ style: {
179
+ pointerEvents: "none"
180
+ },
181
+ width,
182
+ initialWidth: width,
183
+ height,
184
+ initialHeight: height
185
+ };
186
+ }
187
+ function toSeqParallelArea({ parallelPrefix, x, y, width, height }, view) {
188
+ return {
189
+ id: `seq-parallel-${parallelPrefix}`,
190
+ type: "seq-parallel",
191
+ data: {
192
+ id: `seq-parallel-${parallelPrefix}`,
193
+ title: "PARALLEL",
194
+ technology: null,
195
+ color: SeqParallelAreaColor.default,
196
+ shape: "rectangle",
197
+ style: {},
198
+ tags: [],
199
+ position: [x, y],
200
+ level: 0,
201
+ icon: null,
202
+ width,
203
+ height,
204
+ description: RichText.EMPTY,
205
+ viewId: view.id,
206
+ parallelPrefix
207
+ },
208
+ zIndex: SeqZIndex.parallel,
209
+ position: {
210
+ x,
211
+ y
212
+ },
213
+ draggable: false,
214
+ deletable: false,
215
+ selectable: false,
216
+ focusable: false,
217
+ style: {
218
+ pointerEvents: "none"
219
+ },
220
+ width,
221
+ initialWidth: width,
222
+ height,
223
+ initialHeight: height
224
+ };
225
+ }
226
+ function toSeqActorNode({ actor, ports, bounds, layout, view }) {
227
+ const { x, y, width, height } = layout.getActorBox(actor);
228
+ return {
229
+ id: actor.id,
230
+ type: "seq-actor",
231
+ data: {
232
+ id: actor.id,
233
+ position: [x, y],
234
+ level: 0,
235
+ icon: actor.icon ?? null,
236
+ isMultiple: actor.style.multiple ?? false,
237
+ title: actor.title,
238
+ width,
239
+ height,
240
+ color: actor.color,
241
+ navigateTo: actor.navigateTo ?? null,
242
+ shape: actor.shape,
243
+ style: actor.style,
244
+ tags: actor.tags,
245
+ modelFqn: actor.modelRef ?? null,
246
+ technology: actor.technology,
247
+ description: RichText.from(actor.description),
248
+ viewHeight: bounds.height,
249
+ viewId: view.id,
250
+ ports: ports.map((p) => {
251
+ const bbox = layout.getPortCenter(p.step, p.type);
252
+ return {
253
+ id: p.step.id + "_" + p.type,
254
+ cx: bbox.cx - x,
255
+ cy: bbox.cy - y,
256
+ height: bbox.height,
257
+ type: p.type,
258
+ position: p.position
259
+ };
260
+ })
261
+ },
262
+ deletable: false,
263
+ selectable: true,
264
+ zIndex: SeqZIndex.actor,
265
+ position: { x, y },
266
+ width,
267
+ initialWidth: width,
268
+ height,
269
+ initialHeight: height
270
+ };
271
+ }
272
+
273
+ export { sequenceViewToXY };
@@ -0,0 +1,14 @@
1
+ import { DiagramNode } from '@likec4/core/types';
2
+ import { Compound, ParallelRect, Step } from './_types';
3
+ /**
4
+ * From steps find boxes that must be marked as parallel on the layout
5
+ */
6
+ export declare function findParallelRects(steps: Array<Step>): Array<ParallelRect>;
7
+ /**
8
+ * Builds a tree of compounds from actors and nodes.
9
+ * @param actors the actors in the sequence view
10
+ * @param nodes the nodes in likec4 diagram
11
+ * @returns an array of compounds where each compound is a node in the sequence view
12
+ * that is an ancestor of one of the actors
13
+ */
14
+ export declare function buildCompounds(actors: Array<DiagramNode>, nodes: Array<DiagramNode>): Array<Compound>;