@flowgram.ai/free-layout-core 0.1.0-alpha.12 → 0.1.0-alpha.14

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 (36) hide show
  1. package/dist/esm/{chunk-CTGO4RKX.js → chunk-5BT3ZR4D.js} +2 -1
  2. package/dist/esm/chunk-5BT3ZR4D.js.map +1 -0
  3. package/dist/esm/{chunk-242F2JCI.js → chunk-U2XMPOSL.js} +2 -1
  4. package/dist/esm/{chunk-242F2JCI.js.map → chunk-U2XMPOSL.js.map} +1 -1
  5. package/dist/esm/index.js +254 -155
  6. package/dist/esm/index.js.map +1 -1
  7. package/dist/esm/typings/index.js +2 -2
  8. package/dist/esm/typings/workflow-json.js +1 -1
  9. package/dist/esm/typings/workflow-line.js +1 -1
  10. package/dist/index.d.mts +26 -34
  11. package/dist/index.d.ts +26 -34
  12. package/dist/index.js +236 -135
  13. package/dist/index.js.map +1 -1
  14. package/dist/typings/index.d.mts +1 -1
  15. package/dist/typings/index.d.ts +1 -1
  16. package/dist/typings/index.js +2 -0
  17. package/dist/typings/index.js.map +1 -1
  18. package/dist/typings/workflow-drag.d.mts +1 -1
  19. package/dist/typings/workflow-drag.d.ts +1 -1
  20. package/dist/typings/workflow-json.d.mts +1 -1
  21. package/dist/typings/workflow-json.d.ts +1 -1
  22. package/dist/typings/workflow-json.js +1 -0
  23. package/dist/typings/workflow-json.js.map +1 -1
  24. package/dist/typings/workflow-line.d.mts +1 -1
  25. package/dist/typings/workflow-line.d.ts +1 -1
  26. package/dist/typings/workflow-line.js +1 -0
  27. package/dist/typings/workflow-line.js.map +1 -1
  28. package/dist/typings/workflow-node.d.mts +1 -1
  29. package/dist/typings/workflow-node.d.ts +1 -1
  30. package/dist/typings/workflow-registry.d.mts +2 -1
  31. package/dist/typings/workflow-registry.d.ts +2 -1
  32. package/dist/typings/workflow-registry.js.map +1 -1
  33. package/dist/{workflow-line-entity-LhmV98jq.d.ts → workflow-line-entity-BmPrinpL.d.ts} +152 -45
  34. package/dist/{workflow-line-entity-Iq1OHmuK.d.mts → workflow-line-entity-Dn8glLaW.d.mts} +152 -45
  35. package/package.json +9 -9
  36. package/dist/esm/chunk-CTGO4RKX.js.map +0 -1
package/dist/index.js CHANGED
@@ -57,7 +57,6 @@ __export(src_exports, {
57
57
  WorkflowPortEntity: () => WorkflowPortEntity,
58
58
  WorkflowResetLayoutService: () => WorkflowResetLayoutService,
59
59
  WorkflowSelectService: () => WorkflowSelectService,
60
- WorkflowSimpleLineContribution: () => WorkflowSimpleLineContribution,
61
60
  bindConfigEntity: () => import_core3.bindConfigEntity,
62
61
  buildGroupJSON: () => buildGroupJSON,
63
62
  compose: () => import_utils.compose,
@@ -66,6 +65,7 @@ __export(src_exports, {
66
65
  domReactToBounds: () => domReactToBounds,
67
66
  fitView: () => fitView,
68
67
  getAntiOverlapPosition: () => getAntiOverlapPosition,
68
+ getLineCenter: () => getLineCenter,
69
69
  getPortEntityId: () => getPortEntityId,
70
70
  nanoid: () => nanoid,
71
71
  useConfigEntity: () => import_core25.useConfigEntity,
@@ -146,6 +146,16 @@ var buildGroupJSON = (json) => {
146
146
  };
147
147
  };
148
148
 
149
+ // src/utils/get-line-center.ts
150
+ function getLineCenter(from, to, bbox, linePadding) {
151
+ return {
152
+ x: bbox.center.x,
153
+ y: bbox.center.y,
154
+ labelX: bbox.center.x - bbox.x + linePadding,
155
+ labelY: bbox.center.y - bbox.y + linePadding
156
+ };
157
+ }
158
+
149
159
  // src/utils/nanoid.ts
150
160
  var import_nanoid = require("nanoid");
151
161
  function nanoid(n) {
@@ -156,8 +166,8 @@ function nanoid(n) {
156
166
  var import_utils = require("@flowgram.ai/utils");
157
167
 
158
168
  // src/utils/fit-view.ts
159
- var import_core = require("@flowgram.ai/core");
160
169
  var import_utils2 = require("@flowgram.ai/utils");
170
+ var import_core = require("@flowgram.ai/core");
161
171
  var fitView = (doc, playgroundConfig, easing = true) => {
162
172
  const bounds = import_utils2.Rectangle.enlarge(
163
173
  doc.getAllNodes().map((node) => node.getData(import_core.TransformData).bounds)
@@ -218,17 +228,18 @@ var import_document3 = require("@flowgram.ai/document");
218
228
  var import_core4 = require("@flowgram.ai/core");
219
229
  var PORT_SIZE = 24;
220
230
  var WorkflowPortEntity = class extends import_core4.Entity {
221
- // relativePosition
222
231
  constructor(opts) {
223
232
  super(opts);
224
233
  this.portID = "";
225
- this._disabled = false;
226
234
  this._hasError = false;
227
235
  this._onErrorChangedEmitter = new import_utils5.Emitter();
228
236
  this.onErrorChanged = this._onErrorChangedEmitter.event;
229
237
  this.portID = opts.portID || "";
230
238
  this.portType = opts.type;
231
- this._disabled = opts.disabled ?? false;
239
+ this._disabled = opts.disabled;
240
+ this._offset = opts.offset;
241
+ this._location = opts.location;
242
+ this._size = opts.size;
232
243
  this.node = opts.node;
233
244
  this.updateTargetElement(opts.targetElement);
234
245
  this.toDispose.push(this.node.getData(import_core4.TransformData).onDataChange(() => this.fireChange()));
@@ -237,6 +248,9 @@ var WorkflowPortEntity = class extends import_core4.Entity {
237
248
  static getPortEntityId(node, portType, portID = "") {
238
249
  return getPortEntityId(node, portType, portID);
239
250
  }
251
+ get position() {
252
+ return this._location;
253
+ }
240
254
  // 获取连线是否为错误态
241
255
  get hasError() {
242
256
  return this._hasError;
@@ -261,28 +275,65 @@ var WorkflowPortEntity = class extends import_core4.Entity {
261
275
  isErrorPort() {
262
276
  return this.node.document.isErrorPort(this, this.hasError);
263
277
  }
278
+ get location() {
279
+ if (this._location) {
280
+ return this._location;
281
+ }
282
+ if (this.portType === "input") {
283
+ return "left";
284
+ }
285
+ return "right";
286
+ }
264
287
  get point() {
265
288
  const { targetElement } = this;
266
289
  const { bounds } = this.node.getData(import_document3.FlowNodeTransformData);
290
+ const location2 = this.location;
267
291
  if (targetElement) {
268
292
  const pos = domReactToBounds(targetElement.getBoundingClientRect()).center;
269
- return this.entityManager.getEntity(import_core4.PlaygroundConfigEntity).getPosFromMouseEvent({
293
+ const point2 = this.entityManager.getEntity(import_core4.PlaygroundConfigEntity).getPosFromMouseEvent({
270
294
  clientX: pos.x,
271
295
  clientY: pos.y
272
296
  });
297
+ return {
298
+ x: point2.x,
299
+ y: point2.y,
300
+ location: location2
301
+ };
273
302
  }
274
- if (this.portType === "input") {
275
- return bounds.leftCenter;
303
+ let point = { x: 0, y: 0 };
304
+ const offset = this._offset || { x: 0, y: 0 };
305
+ switch (location2) {
306
+ case "left":
307
+ point = bounds.leftCenter;
308
+ break;
309
+ case "top":
310
+ point = bounds.topCenter;
311
+ break;
312
+ case "right":
313
+ point = bounds.rightCenter;
314
+ break;
315
+ case "bottom":
316
+ point = bounds.bottomCenter;
317
+ break;
276
318
  }
277
- return bounds.rightCenter;
319
+ return {
320
+ x: point.x + offset.x,
321
+ y: point.y + offset.y,
322
+ location: location2
323
+ };
278
324
  }
279
325
  /**
280
- * 点的区域
326
+ * 端口热区
281
327
  */
282
328
  get bounds() {
283
329
  const { point } = this;
284
- const halfSize = PORT_SIZE / 2;
285
- return new import_utils5.Rectangle(point.x - halfSize, point.y - halfSize, PORT_SIZE, PORT_SIZE);
330
+ const size = this._size || { width: PORT_SIZE, height: PORT_SIZE };
331
+ return new import_utils5.Rectangle(
332
+ point.x - size.width / 2,
333
+ point.y - size.height / 2,
334
+ size.width,
335
+ size.height
336
+ );
286
337
  }
287
338
  isHovered(x, y) {
288
339
  return this.bounds.contains(x, y);
@@ -349,6 +400,32 @@ var WorkflowPortEntity = class extends import_core4.Entity {
349
400
  });
350
401
  return lines;
351
402
  }
403
+ update(data) {
404
+ let changed = false;
405
+ if (data.targetElement !== this.targetElement) {
406
+ this.targetElement = data.targetElement;
407
+ changed = true;
408
+ }
409
+ if (data.location !== this._location) {
410
+ this._location = data.location;
411
+ changed = true;
412
+ }
413
+ if (import_utils5.Compare.isChanged(data.offset, this._offset)) {
414
+ this._offset = data.offset;
415
+ changed = true;
416
+ }
417
+ if (import_utils5.Compare.isChanged(data.size, this._size)) {
418
+ this._size = data.size;
419
+ changed = true;
420
+ }
421
+ if (data.disabled !== this._disabled) {
422
+ this._disabled = data.disabled;
423
+ changed = true;
424
+ }
425
+ if (changed) {
426
+ this.fireChange();
427
+ }
428
+ }
352
429
  dispose() {
353
430
  this.lines.forEach((l) => l.dispose());
354
431
  super.dispose();
@@ -387,17 +464,26 @@ var WorkflowNodePortsData = class extends import_core5.EntityData {
387
464
  return {};
388
465
  }
389
466
  /**
390
- * 更新静态的 ports 数据
467
+ * Update all ports data, includes static ports and dynamic ports
468
+ * @param ports
391
469
  */
392
- updateStaticPorts(ports) {
470
+ updateAllPorts(ports) {
393
471
  const meta = this.entity.getNodeMeta();
394
- this._staticPorts = ports;
472
+ if (ports) {
473
+ this._staticPorts = ports;
474
+ }
395
475
  if (meta.useDynamicPort) {
396
476
  this.updateDynamicPorts();
397
477
  } else {
398
478
  this.updatePorts(this._staticPorts);
399
479
  }
400
480
  }
481
+ /**
482
+ * @deprecated use `updateAllPorts` instead
483
+ */
484
+ updateStaticPorts(ports) {
485
+ this.updateAllPorts(ports);
486
+ }
401
487
  /**
402
488
  * 动态计算点位,通过 dom 的 data-port-key
403
489
  */
@@ -536,9 +622,7 @@ var WorkflowNodePortsData = class extends import_core5.EntityData {
536
622
  */
537
623
  updatePortEntity(portInfo) {
538
624
  const portEntity = this.getOrCreatePortEntity(portInfo);
539
- if (portInfo.targetElement) {
540
- portEntity.updateTargetElement(portInfo.targetElement);
541
- }
625
+ portEntity.update(portInfo);
542
626
  return portEntity;
543
627
  }
544
628
  };
@@ -579,6 +663,9 @@ var _WorkflowNodeLinesData = class _WorkflowNodeLinesData extends import_core6.E
579
663
  get allLines() {
580
664
  return this.data.inputLines.concat(this.data.outputLines);
581
665
  }
666
+ get availableLines() {
667
+ return this.allLines.filter((line) => !line.isDrawing && !line.isHidden);
668
+ }
582
669
  /**
583
670
  * 输入节点
584
671
  */
@@ -679,8 +766,8 @@ var WorkflowLineRenderData = class extends import_core7.EntityData {
679
766
  version: "",
680
767
  contributions: /* @__PURE__ */ new Map(),
681
768
  position: {
682
- from: { x: 0, y: 0 },
683
- to: { x: 0, y: 0 }
769
+ from: { x: 0, y: 0, location: "right" },
770
+ to: { x: 0, y: 0, location: "left" }
684
771
  }
685
772
  };
686
773
  }
@@ -720,22 +807,35 @@ var WorkflowLineRenderData = class extends import_core7.EntityData {
720
807
  get lineType() {
721
808
  return this.entity.renderType ?? this.entity.linesManager.lineType;
722
809
  }
810
+ /**
811
+ * 获取 center 位置
812
+ */
813
+ get center() {
814
+ return this.currentLine?.center || { x: 0, y: 0, labelX: 0, labelY: 0 };
815
+ }
723
816
  /**
724
817
  * 更新版本
725
818
  * WARNING: 这个方法,必须在 requestAnimationFrame / useLayoutEffect 中调用,否则会引起浏览器强制重排
726
819
  */
727
820
  updatePosition() {
728
821
  this.data.position.from = this.entity.from.getData(WorkflowNodePortsData).getOutputPoint(this.entity.info.fromPort);
729
- this.data.position.to = this.entity.info.drawingTo ?? this.entity.to?.getData(WorkflowNodePortsData)?.getInputPoint(this.entity.info.toPort) ?? {
730
- x: this.data.position.from.x,
731
- y: this.data.position.from.y
732
- };
822
+ if (this.entity.info.drawingTo) {
823
+ this.data.position.to = this.entity.info.drawingTo;
824
+ } else {
825
+ this.data.position.to = this.entity.to?.getData(WorkflowNodePortsData)?.getInputPoint(this.entity.info.toPort) ?? {
826
+ x: this.data.position.from.x,
827
+ y: this.data.position.from.y,
828
+ location: this.data.position.from.location === "right" ? "left" : "top"
829
+ };
830
+ }
733
831
  this.data.version = [
734
832
  this.lineType,
735
833
  this.data.position.from.x,
736
834
  this.data.position.from.y,
835
+ this.data.position.from.location,
737
836
  this.data.position.to.x,
738
- this.data.position.to.y
837
+ this.data.position.to.y,
838
+ this.data.position.to.location
739
839
  ].join("-");
740
840
  }
741
841
  get currentLine() {
@@ -765,13 +865,16 @@ var POINT_RADIUS = 10;
765
865
  var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity {
766
866
  constructor(opts) {
767
867
  super(opts);
868
+ this._onLineDataChangeEmitter = new import_utils8.Emitter();
869
+ this.onLineDataChange = this._onLineDataChangeEmitter.event;
768
870
  this._uiState = {
769
871
  hasError: false,
770
872
  flowing: false,
771
873
  disabled: false,
772
- vertical: false,
773
874
  hideArrow: false,
774
875
  reverse: false,
876
+ shrink: 10,
877
+ curvature: 0.25,
775
878
  highlightColor: "",
776
879
  lockedColor: ""
777
880
  };
@@ -789,7 +892,8 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
789
892
  to: opts.to,
790
893
  drawingTo: opts.drawingTo,
791
894
  fromPort: opts.fromPort,
792
- toPort: opts.toPort
895
+ toPort: opts.toPort,
896
+ data: opts.data
793
897
  });
794
898
  if (opts.drawingTo) {
795
899
  this.isDrawing = true;
@@ -802,6 +906,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
802
906
  this.fromPort?.validate();
803
907
  this.toPort?.validate();
804
908
  });
909
+ this.toDispose.push(this._onLineDataChangeEmitter);
805
910
  }
806
911
  /**
807
912
  * 转成线条 id
@@ -844,9 +949,13 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
844
949
  * 更新线条扩展数据
845
950
  * @param data
846
951
  */
847
- set lineData(data) {
848
- this._lineData = data;
849
- this.fireChange();
952
+ set lineData(newValue) {
953
+ const oldValue = this._lineData;
954
+ if (!(0, import_lodash_es2.isEqual)(oldValue, newValue)) {
955
+ this._lineData = newValue;
956
+ this._onLineDataChangeEmitter.fire({ oldValue, newValue });
957
+ this.fireChange();
958
+ }
850
959
  }
851
960
  /**
852
961
  * 获取线条的前置节点
@@ -869,20 +978,17 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
869
978
  }
870
979
  /**
871
980
  * 获取是否 testrun processing
872
- * @deprecated use `uiState.flowing` instead
981
+ * @deprecated use `flowing` instead
873
982
  */
874
983
  get processing() {
875
984
  return this._uiState.flowing;
876
985
  }
877
986
  /**
878
987
  * 设置 testrun processing 状态
879
- * @deprecated use `uiState.flowing` instead
988
+ * @deprecated use `flowing` instead
880
989
  */
881
990
  set processing(status) {
882
- if (this._uiState.flowing !== status) {
883
- this._uiState.flowing = status;
884
- this.fireChange();
885
- }
991
+ this.flowing = status;
886
992
  }
887
993
  // 获取连线是否为错误态
888
994
  get hasError() {
@@ -912,7 +1018,6 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
912
1018
  const { node, portID } = toPort;
913
1019
  this._to = node;
914
1020
  this.info.drawingTo = void 0;
915
- this.info.isDefaultLine = false;
916
1021
  this.info.to = node.id;
917
1022
  this.info.toPort = portID;
918
1023
  } else {
@@ -937,7 +1042,6 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
937
1042
  }
938
1043
  if (!oldDrawingTo || pos.x !== oldDrawingTo.x || pos.y !== oldDrawingTo.y) {
939
1044
  this.info.to = void 0;
940
- this.info.isDefaultLine = false;
941
1045
  this.info.drawingTo = pos;
942
1046
  this.fireChange();
943
1047
  }
@@ -970,6 +1074,9 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
970
1074
  get bounds() {
971
1075
  return this.getData(WorkflowLineRenderData).bounds;
972
1076
  }
1077
+ get center() {
1078
+ return this.getData(WorkflowLineRenderData).center;
1079
+ }
973
1080
  /**
974
1081
  * 获取点和线最接近的距离
975
1082
  */
@@ -1003,13 +1110,25 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
1003
1110
  get flowing() {
1004
1111
  return this.linesManager.isFlowingLine(this, this.uiState.flowing);
1005
1112
  }
1113
+ set flowing(flowing) {
1114
+ if (this._uiState.flowing !== flowing) {
1115
+ this._uiState.flowing = flowing;
1116
+ this.fireChange();
1117
+ }
1118
+ }
1006
1119
  /** 是否禁用 */
1007
1120
  get disabled() {
1008
1121
  return this.linesManager.isDisabledLine(this, this.uiState.disabled);
1009
1122
  }
1010
1123
  /** 是否竖向 */
1011
1124
  get vertical() {
1012
- return this.linesManager.isVerticalLine(this, this.uiState.vertical);
1125
+ const fromLocation = this.fromPort.location;
1126
+ const toLocation = this.toPort?.location;
1127
+ if (toLocation) {
1128
+ return toLocation === "top";
1129
+ } else {
1130
+ return fromLocation === "bottom";
1131
+ }
1013
1132
  }
1014
1133
  /** 获取线条渲染器类型 */
1015
1134
  get renderType() {
@@ -1017,7 +1136,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
1017
1136
  }
1018
1137
  /** 获取线条样式 */
1019
1138
  get className() {
1020
- return this.linesManager.setLineClassName(this) ?? "";
1139
+ return [this.linesManager.setLineClassName(this), this._uiState.className].filter((s) => !!s).join(" ");
1021
1140
  }
1022
1141
  get color() {
1023
1142
  return this.linesManager.getLineColor(this);
@@ -1031,6 +1150,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends import_core8.Entity
1031
1150
  this.info = info;
1032
1151
  this._from = this.document.getNode(info.from);
1033
1152
  this._to = info.to ? this.document.getNode(info.to) : void 0;
1153
+ this._lineData = info.data;
1034
1154
  this.fireChange();
1035
1155
  }
1036
1156
  }
@@ -1287,6 +1407,7 @@ var WorkflowContentChangeType = /* @__PURE__ */ ((WorkflowContentChangeType2) =>
1287
1407
  WorkflowContentChangeType2["NODE_DATA_CHANGE"] = "NODE_DATA_CHANGE";
1288
1408
  WorkflowContentChangeType2["ADD_LINE"] = "ADD_LINE";
1289
1409
  WorkflowContentChangeType2["DELETE_LINE"] = "DELETE_LINE";
1410
+ WorkflowContentChangeType2["LINE_DATA_CHANGE"] = "LINE_DATA_CHANGE";
1290
1411
  WorkflowContentChangeType2["META_CHANGE"] = "META_CHANGE";
1291
1412
  return WorkflowContentChangeType2;
1292
1413
  })(WorkflowContentChangeType || {});
@@ -1295,6 +1416,7 @@ var WorkflowContentChangeType = /* @__PURE__ */ ((WorkflowContentChangeType2) =>
1295
1416
  var LineType = /* @__PURE__ */ ((LineType2) => {
1296
1417
  LineType2[LineType2["BEZIER"] = 0] = "BEZIER";
1297
1418
  LineType2[LineType2["LINE_CHART"] = 1] = "LINE_CHART";
1419
+ LineType2[LineType2["STRAIGHT"] = 2] = "STRAIGHT";
1298
1420
  return LineType2;
1299
1421
  })(LineType || {});
1300
1422
  var LineColors = /* @__PURE__ */ ((LineColors2) => {
@@ -1460,6 +1582,9 @@ var WorkflowLinesManager = class {
1460
1582
  WorkflowLineEntity.portInfoToLineId(portInfo)
1461
1583
  );
1462
1584
  }
1585
+ getLineById(id) {
1586
+ return this.entityManager.getEntityById(id);
1587
+ }
1463
1588
  replaceLine(oldPortInfo, newPortInfo) {
1464
1589
  const oldLine = this.getLine(oldPortInfo);
1465
1590
  if (oldLine) {
@@ -1468,7 +1593,7 @@ var WorkflowLinesManager = class {
1468
1593
  return this.createLine(newPortInfo);
1469
1594
  }
1470
1595
  createLine(options) {
1471
- const { from, to, drawingTo, fromPort, toPort } = options;
1596
+ const { from, to, drawingTo, fromPort, toPort, data } = options;
1472
1597
  const available = Boolean(from && to);
1473
1598
  const key = options.key || WorkflowLineEntity.portInfoToLineId(options);
1474
1599
  let line = this.entityManager.getEntityById(key);
@@ -1491,7 +1616,8 @@ var WorkflowLinesManager = class {
1491
1616
  fromPort,
1492
1617
  toPort,
1493
1618
  to,
1494
- drawingTo
1619
+ drawingTo,
1620
+ data
1495
1621
  });
1496
1622
  this.registerData(line);
1497
1623
  fromNode.addLine(line);
@@ -1512,6 +1638,14 @@ var WorkflowLinesManager = class {
1512
1638
  });
1513
1639
  }
1514
1640
  });
1641
+ line.onLineDataChange(({ oldValue }) => {
1642
+ this.onAvailableLinesChangeEmitter.fire({
1643
+ type: "LINE_DATA_CHANGE" /* LINE_DATA_CHANGE */,
1644
+ toJSON: () => line.toJSON(),
1645
+ oldValue,
1646
+ entity: line
1647
+ });
1648
+ });
1515
1649
  if (available) {
1516
1650
  this.onAvailableLinesChangeEmitter.fire({
1517
1651
  type: "ADD_LINE" /* ADD_LINE */,
@@ -1575,12 +1709,6 @@ var WorkflowLinesManager = class {
1575
1709
  }
1576
1710
  return defaultValue;
1577
1711
  }
1578
- isVerticalLine(line, defaultValue = false) {
1579
- if (this.options.isVerticalLine) {
1580
- return this.options.isVerticalLine(line);
1581
- }
1582
- return defaultValue;
1583
- }
1584
1712
  setLineRenderType(line) {
1585
1713
  if (this.options.setLineRenderType) {
1586
1714
  return this.options.setLineRenderType(line);
@@ -1624,6 +1752,14 @@ var WorkflowLinesManager = class {
1624
1752
  if (fromPort === toPort || fromPort.node === toPort.node || fromPort.portType !== "output" || toPort.portType !== "input" || toPort.disabled) {
1625
1753
  return false;
1626
1754
  }
1755
+ const fromCanAdd = fromPort.node.getNodeRegistry().canAddLine;
1756
+ const toCanAdd = toPort.node.getNodeRegistry().canAddLine;
1757
+ if (fromCanAdd && !fromCanAdd(fromPort, toPort, this, silent)) {
1758
+ return false;
1759
+ }
1760
+ if (toCanAdd && !toCanAdd(fromPort, toPort, this, silent)) {
1761
+ return false;
1762
+ }
1627
1763
  if (this.options.canAddLine) {
1628
1764
  return this.options.canAddLine(fromPort, toPort, this, silent);
1629
1765
  }
@@ -1887,6 +2023,11 @@ var WorkflowDocument = class extends import_document8.FlowDocument {
1887
2023
  get loading() {
1888
2024
  return this._loading;
1889
2025
  }
2026
+ /**
2027
+ * use `ctx.tools.fitView()` instead
2028
+ * @deprecated
2029
+ * @param easing
2030
+ */
1890
2031
  async fitView(easing) {
1891
2032
  return fitView(this, this.playgroundConfig, easing).then(() => {
1892
2033
  this.linesManager.forceUpdate();
@@ -2194,10 +2335,13 @@ var WorkflowDocument = class extends import_document8.FlowDocument {
2194
2335
  from: line.from.id,
2195
2336
  to: line.to.id
2196
2337
  }));
2197
- const startNodeId = allNode.find((node) => node.isStart).id;
2198
- const endNodeId = allNode.find((node) => node.isNodeEnd).id;
2338
+ const startNodeId = allNode.find((node) => node.isStart)?.id;
2339
+ const endNodeId = allNode.find((node) => node.isNodeEnd)?.id;
2199
2340
  const nodeInContainer = allNode.filter((node) => node.parent?.getNodeMeta().isContainer).map((node) => node.id);
2200
- const associatedCache = /* @__PURE__ */ new Set([endNodeId, ...nodeInContainer]);
2341
+ const associatedCache = new Set(nodeInContainer);
2342
+ if (endNodeId) {
2343
+ associatedCache.add(endNodeId);
2344
+ }
2201
2345
  const bfs = (nodeId) => {
2202
2346
  if (associatedCache.has(nodeId)) {
2203
2347
  return;
@@ -2211,7 +2355,9 @@ var WorkflowDocument = class extends import_document8.FlowDocument {
2211
2355
  }, []);
2212
2356
  nextNodes.forEach(bfs);
2213
2357
  };
2214
- bfs(startNodeId);
2358
+ if (startNodeId) {
2359
+ bfs(startNodeId);
2360
+ }
2215
2361
  const associatedNodes = allNode.filter((node) => associatedCache.has(node.id));
2216
2362
  return associatedNodes;
2217
2363
  }
@@ -2333,6 +2479,11 @@ var WorkflowDocument = class extends import_document8.FlowDocument {
2333
2479
  * 导出数据
2334
2480
  */
2335
2481
  toJSON() {
2482
+ if (this.disposed) {
2483
+ throw new Error(
2484
+ "The WorkflowDocument has been disposed and it is no longer possible to call toJSON."
2485
+ );
2486
+ }
2336
2487
  const rootJSON = this.toNodeJSON(this.root);
2337
2488
  const json = {
2338
2489
  nodes: rootJSON.blocks ?? [],
@@ -2345,9 +2496,18 @@ var WorkflowDocument = class extends import_document8.FlowDocument {
2345
2496
  this._onReloadEmitter.dispose();
2346
2497
  }
2347
2498
  /**
2348
- * 逐层创建节点和线条
2499
+ * 批量添加节点
2500
+ * @deprecated use 'batchAddFromJSON' instead
2501
+ * @param json
2502
+ * @param options
2349
2503
  */
2350
2504
  renderJSON(json, options) {
2505
+ return this.batchAddFromJSON(json, options);
2506
+ }
2507
+ /**
2508
+ * 批量添加节点
2509
+ */
2510
+ batchAddFromJSON(json, options) {
2351
2511
  const { parent = this.root, isClone = false } = options ?? {};
2352
2512
  const containerID = this.getNodeSubCanvas(parent)?.canvasNode.id ?? parent.id;
2353
2513
  const processedJSON = buildGroupJSON(json);
@@ -2413,7 +2573,8 @@ var WorkflowDocument = class extends import_document8.FlowDocument {
2413
2573
  from: json.sourceNodeID,
2414
2574
  fromPort: json.sourcePortID,
2415
2575
  to: json.targetNodeID,
2416
- toPort: json.targetPortID
2576
+ toPort: json.targetPortID,
2577
+ data: json.data
2417
2578
  };
2418
2579
  if (!parentId) {
2419
2580
  return this.linesManager.createLine(lineInfo);
@@ -2880,10 +3041,16 @@ var WorkflowDragService = class {
2880
3041
  originLine.highlightColor = this.linesManager.lineColor.hidden;
2881
3042
  }
2882
3043
  dragSuccess = true;
3044
+ const pos = config.getPosFromMouseEvent(event);
2883
3045
  line = this.linesManager.createLine({
2884
3046
  from: fromPort.node.id,
2885
3047
  fromPort: fromPort.portID,
2886
- drawingTo: config.getPosFromMouseEvent(event)
3048
+ data: originLine?.lineData,
3049
+ drawingTo: {
3050
+ x: pos.x,
3051
+ y: pos.y,
3052
+ location: fromPort.location === "right" ? "left" : "top"
3053
+ }
2887
3054
  });
2888
3055
  if (!line) {
2889
3056
  return;
@@ -2918,9 +3085,17 @@ var WorkflowDragService = class {
2918
3085
  lineErrorReset = hasError;
2919
3086
  }
2920
3087
  if (line.toPort) {
2921
- line.drawingTo = { x: line.toPort.point.x, y: line.toPort.point.y };
3088
+ line.drawingTo = {
3089
+ x: line.toPort.point.x,
3090
+ y: line.toPort.point.y,
3091
+ location: line.toPort.location
3092
+ };
2922
3093
  } else {
2923
- line.drawingTo = { x: dragPos.x, y: dragPos.y };
3094
+ line.drawingTo = {
3095
+ x: dragPos.x,
3096
+ y: dragPos.y,
3097
+ location: line.fromPort.location === "right" ? "left" : "top"
3098
+ };
2924
3099
  }
2925
3100
  originLine?.validate();
2926
3101
  line.validate();
@@ -2964,7 +3139,8 @@ var WorkflowDragService = class {
2964
3139
  from: fromPort.node.id,
2965
3140
  fromPort: fromPort.portID,
2966
3141
  to: toPort.node.id,
2967
- toPort: toPort.portID
3142
+ toPort: toPort.portID,
3143
+ data: originLine?.lineData
2968
3144
  } : void 0;
2969
3145
  const isReset = originLine && toPort;
2970
3146
  if (isReset && !this.linesManager.canReset(
@@ -3234,6 +3410,7 @@ function usePlaygroundReadonlyState(listenChange) {
3234
3410
  function checkTargetDraggable(el) {
3235
3411
  return el && el.tagName !== "INPUT" && el.tagName !== "TEXTAREA" && !el.closest(".flow-canvas-not-draggable");
3236
3412
  }
3413
+ var isFirefox = navigator?.userAgent?.includes?.("Firefox");
3237
3414
  function useNodeRender(nodeFromProps) {
3238
3415
  const node = nodeFromProps || (0, import_react2.useContext)(import_core21.PlaygroundEntityContext);
3239
3416
  const renderData = node.getData(import_document13.FlowNodeRenderData);
@@ -3297,7 +3474,6 @@ function useNodeRender(nodeFromProps) {
3297
3474
  );
3298
3475
  const deleteNode = (0, import_react2.useCallback)(() => node.dispose(), [node]);
3299
3476
  (0, import_core21.useListenEvents)(portsData.onDataChange);
3300
- const isFirefox = navigator?.userAgent?.includes?.("Firefox");
3301
3477
  const onFocus = (0, import_react2.useCallback)(() => {
3302
3478
  if (isFirefox) {
3303
3479
  nodeRef.current?.setAttribute("draggable", "false");
@@ -3490,81 +3666,6 @@ var WorkflowDocumentContainerModule = new import_inversify10.ContainerModule(
3490
3666
  bind(WorkflowDocumentProvider).toDynamicValue((ctx) => () => ctx.container.get(WorkflowDocument)).inSingletonScope();
3491
3667
  }
3492
3668
  );
3493
-
3494
- // src/utils/simple-line.ts
3495
- var import_utils21 = require("@flowgram.ai/utils");
3496
- var LINE_PADDING = 12;
3497
- var WorkflowSimpleLineContribution = class {
3498
- constructor(entity) {
3499
- this.entity = entity;
3500
- }
3501
- get path() {
3502
- return this.data?.path ?? "";
3503
- }
3504
- calcDistance(pos) {
3505
- if (!this.data) {
3506
- return Number.MAX_SAFE_INTEGER;
3507
- }
3508
- const [start, end] = this.data.points;
3509
- return import_utils21.Point.getDistance(pos, this.projectPointOnLine(pos, start, end));
3510
- }
3511
- get bounds() {
3512
- if (!this.data) {
3513
- return new import_utils21.Rectangle();
3514
- }
3515
- return this.data.bbox;
3516
- }
3517
- update(params) {
3518
- const { fromPos, toPos } = params;
3519
- const { vertical } = this.entity;
3520
- const sourceOffset = {
3521
- x: vertical ? 0 : POINT_RADIUS,
3522
- y: vertical ? POINT_RADIUS : 0
3523
- };
3524
- const targetOffset = {
3525
- x: vertical ? 0 : -POINT_RADIUS,
3526
- y: vertical ? -POINT_RADIUS : 0
3527
- };
3528
- const points = [
3529
- {
3530
- x: fromPos.x + sourceOffset.x,
3531
- y: fromPos.y + sourceOffset.y
3532
- },
3533
- {
3534
- x: toPos.x + targetOffset.x,
3535
- y: toPos.y + targetOffset.y
3536
- }
3537
- ];
3538
- const bbox = import_utils21.Rectangle.createRectangleWithTwoPoints(points[0], points[1]);
3539
- const adjustedPoints = points.map((p) => ({
3540
- x: p.x - bbox.x + LINE_PADDING,
3541
- y: p.y - bbox.y + LINE_PADDING
3542
- }));
3543
- const path = `M ${adjustedPoints[0].x} ${adjustedPoints[0].y} L ${adjustedPoints[1].x} ${adjustedPoints[1].y}`;
3544
- this.data = {
3545
- points,
3546
- path,
3547
- bbox
3548
- };
3549
- }
3550
- projectPointOnLine(point, lineStart, lineEnd) {
3551
- const dx = lineEnd.x - lineStart.x;
3552
- const dy = lineEnd.y - lineStart.y;
3553
- if (dx === 0) {
3554
- return { x: lineStart.x, y: point.y };
3555
- }
3556
- if (dy === 0) {
3557
- return { x: point.x, y: lineStart.y };
3558
- }
3559
- const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
3560
- const clampedT = Math.max(0, Math.min(1, t));
3561
- return {
3562
- x: lineStart.x + clampedT * dx,
3563
- y: lineStart.y + clampedT * dy
3564
- };
3565
- }
3566
- };
3567
- WorkflowSimpleLineContribution.type = "WorkflowSimpleLineContribution";
3568
3669
  // Annotate the CommonJS export names for ESM import in node:
3569
3670
  0 && (module.exports = {
3570
3671
  EditorCursorState,
@@ -3596,7 +3697,6 @@ WorkflowSimpleLineContribution.type = "WorkflowSimpleLineContribution";
3596
3697
  WorkflowPortEntity,
3597
3698
  WorkflowResetLayoutService,
3598
3699
  WorkflowSelectService,
3599
- WorkflowSimpleLineContribution,
3600
3700
  bindConfigEntity,
3601
3701
  buildGroupJSON,
3602
3702
  compose,
@@ -3605,6 +3705,7 @@ WorkflowSimpleLineContribution.type = "WorkflowSimpleLineContribution";
3605
3705
  domReactToBounds,
3606
3706
  fitView,
3607
3707
  getAntiOverlapPosition,
3708
+ getLineCenter,
3608
3709
  getPortEntityId,
3609
3710
  nanoid,
3610
3711
  useConfigEntity,