@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/esm/index.js CHANGED
@@ -7,11 +7,11 @@ import "./chunk-TQLT57GW.js";
7
7
  import "./chunk-CGOMTQ3G.js";
8
8
  import {
9
9
  WorkflowContentChangeType
10
- } from "./chunk-242F2JCI.js";
10
+ } from "./chunk-U2XMPOSL.js";
11
11
  import {
12
12
  LineColors,
13
13
  LineType
14
- } from "./chunk-CTGO4RKX.js";
14
+ } from "./chunk-5BT3ZR4D.js";
15
15
  import "./chunk-DDJTYHXN.js";
16
16
  import {
17
17
  WorkflowOperationBaseService
@@ -100,6 +100,16 @@ var buildGroupJSON = (json) => {
100
100
  };
101
101
  };
102
102
 
103
+ // src/utils/get-line-center.ts
104
+ function getLineCenter(from, to, bbox, linePadding) {
105
+ return {
106
+ x: bbox.center.x,
107
+ y: bbox.center.y,
108
+ labelX: bbox.center.x - bbox.x + linePadding,
109
+ labelY: bbox.center.y - bbox.y + linePadding
110
+ };
111
+ }
112
+
103
113
  // src/utils/nanoid.ts
104
114
  import { nanoid as nanoidOrigin } from "nanoid";
105
115
  function nanoid(n) {
@@ -110,8 +120,8 @@ function nanoid(n) {
110
120
  import { compose, composeAsync } from "@flowgram.ai/utils";
111
121
 
112
122
  // src/utils/fit-view.ts
113
- import { TransformData } from "@flowgram.ai/core";
114
123
  import { Rectangle } from "@flowgram.ai/utils";
124
+ import { TransformData } from "@flowgram.ai/core";
115
125
  var fitView = (doc, playgroundConfig, easing = true) => {
116
126
  const bounds = Rectangle.enlarge(
117
127
  doc.getAllNodes().map((node) => node.getData(TransformData).bounds)
@@ -158,7 +168,7 @@ var WorkflowNodeEntity = FlowNodeEntity;
158
168
 
159
169
  // src/entities/workflow-line-entity.ts
160
170
  import { isEqual as isEqual2 } from "lodash-es";
161
- import { domUtils } from "@flowgram.ai/utils";
171
+ import { domUtils, Emitter as Emitter2 } from "@flowgram.ai/utils";
162
172
  import { Entity as Entity2 } from "@flowgram.ai/core";
163
173
 
164
174
  // src/entity-datas/workflow-node-ports-data.ts
@@ -167,7 +177,7 @@ import { FlowNodeRenderData } from "@flowgram.ai/document";
167
177
  import { EntityData, SizeData } from "@flowgram.ai/core";
168
178
 
169
179
  // src/entities/workflow-port-entity.ts
170
- import { Rectangle as Rectangle3, Emitter } from "@flowgram.ai/utils";
180
+ import { Rectangle as Rectangle3, Emitter, Compare } from "@flowgram.ai/utils";
171
181
  import { FlowNodeTransformData } from "@flowgram.ai/document";
172
182
  import {
173
183
  Entity,
@@ -176,17 +186,18 @@ import {
176
186
  } from "@flowgram.ai/core";
177
187
  var PORT_SIZE = 24;
178
188
  var WorkflowPortEntity = class extends Entity {
179
- // relativePosition
180
189
  constructor(opts) {
181
190
  super(opts);
182
191
  this.portID = "";
183
- this._disabled = false;
184
192
  this._hasError = false;
185
193
  this._onErrorChangedEmitter = new Emitter();
186
194
  this.onErrorChanged = this._onErrorChangedEmitter.event;
187
195
  this.portID = opts.portID || "";
188
196
  this.portType = opts.type;
189
- this._disabled = opts.disabled ?? false;
197
+ this._disabled = opts.disabled;
198
+ this._offset = opts.offset;
199
+ this._location = opts.location;
200
+ this._size = opts.size;
190
201
  this.node = opts.node;
191
202
  this.updateTargetElement(opts.targetElement);
192
203
  this.toDispose.push(this.node.getData(TransformData3).onDataChange(() => this.fireChange()));
@@ -195,6 +206,9 @@ var WorkflowPortEntity = class extends Entity {
195
206
  static getPortEntityId(node, portType, portID = "") {
196
207
  return getPortEntityId(node, portType, portID);
197
208
  }
209
+ get position() {
210
+ return this._location;
211
+ }
198
212
  // 获取连线是否为错误态
199
213
  get hasError() {
200
214
  return this._hasError;
@@ -219,28 +233,65 @@ var WorkflowPortEntity = class extends Entity {
219
233
  isErrorPort() {
220
234
  return this.node.document.isErrorPort(this, this.hasError);
221
235
  }
236
+ get location() {
237
+ if (this._location) {
238
+ return this._location;
239
+ }
240
+ if (this.portType === "input") {
241
+ return "left";
242
+ }
243
+ return "right";
244
+ }
222
245
  get point() {
223
246
  const { targetElement } = this;
224
247
  const { bounds } = this.node.getData(FlowNodeTransformData);
248
+ const location2 = this.location;
225
249
  if (targetElement) {
226
250
  const pos = domReactToBounds(targetElement.getBoundingClientRect()).center;
227
- return this.entityManager.getEntity(PlaygroundConfigEntity).getPosFromMouseEvent({
251
+ const point2 = this.entityManager.getEntity(PlaygroundConfigEntity).getPosFromMouseEvent({
228
252
  clientX: pos.x,
229
253
  clientY: pos.y
230
254
  });
255
+ return {
256
+ x: point2.x,
257
+ y: point2.y,
258
+ location: location2
259
+ };
231
260
  }
232
- if (this.portType === "input") {
233
- return bounds.leftCenter;
261
+ let point = { x: 0, y: 0 };
262
+ const offset = this._offset || { x: 0, y: 0 };
263
+ switch (location2) {
264
+ case "left":
265
+ point = bounds.leftCenter;
266
+ break;
267
+ case "top":
268
+ point = bounds.topCenter;
269
+ break;
270
+ case "right":
271
+ point = bounds.rightCenter;
272
+ break;
273
+ case "bottom":
274
+ point = bounds.bottomCenter;
275
+ break;
234
276
  }
235
- return bounds.rightCenter;
277
+ return {
278
+ x: point.x + offset.x,
279
+ y: point.y + offset.y,
280
+ location: location2
281
+ };
236
282
  }
237
283
  /**
238
- * 点的区域
284
+ * 端口热区
239
285
  */
240
286
  get bounds() {
241
287
  const { point } = this;
242
- const halfSize = PORT_SIZE / 2;
243
- return new Rectangle3(point.x - halfSize, point.y - halfSize, PORT_SIZE, PORT_SIZE);
288
+ const size = this._size || { width: PORT_SIZE, height: PORT_SIZE };
289
+ return new Rectangle3(
290
+ point.x - size.width / 2,
291
+ point.y - size.height / 2,
292
+ size.width,
293
+ size.height
294
+ );
244
295
  }
245
296
  isHovered(x, y) {
246
297
  return this.bounds.contains(x, y);
@@ -307,6 +358,32 @@ var WorkflowPortEntity = class extends Entity {
307
358
  });
308
359
  return lines;
309
360
  }
361
+ update(data) {
362
+ let changed = false;
363
+ if (data.targetElement !== this.targetElement) {
364
+ this.targetElement = data.targetElement;
365
+ changed = true;
366
+ }
367
+ if (data.location !== this._location) {
368
+ this._location = data.location;
369
+ changed = true;
370
+ }
371
+ if (Compare.isChanged(data.offset, this._offset)) {
372
+ this._offset = data.offset;
373
+ changed = true;
374
+ }
375
+ if (Compare.isChanged(data.size, this._size)) {
376
+ this._size = data.size;
377
+ changed = true;
378
+ }
379
+ if (data.disabled !== this._disabled) {
380
+ this._disabled = data.disabled;
381
+ changed = true;
382
+ }
383
+ if (changed) {
384
+ this.fireChange();
385
+ }
386
+ }
310
387
  dispose() {
311
388
  this.lines.forEach((l) => l.dispose());
312
389
  super.dispose();
@@ -345,17 +422,26 @@ var WorkflowNodePortsData = class extends EntityData {
345
422
  return {};
346
423
  }
347
424
  /**
348
- * 更新静态的 ports 数据
425
+ * Update all ports data, includes static ports and dynamic ports
426
+ * @param ports
349
427
  */
350
- updateStaticPorts(ports) {
428
+ updateAllPorts(ports) {
351
429
  const meta = this.entity.getNodeMeta();
352
- this._staticPorts = ports;
430
+ if (ports) {
431
+ this._staticPorts = ports;
432
+ }
353
433
  if (meta.useDynamicPort) {
354
434
  this.updateDynamicPorts();
355
435
  } else {
356
436
  this.updatePorts(this._staticPorts);
357
437
  }
358
438
  }
439
+ /**
440
+ * @deprecated use `updateAllPorts` instead
441
+ */
442
+ updateStaticPorts(ports) {
443
+ this.updateAllPorts(ports);
444
+ }
359
445
  /**
360
446
  * 动态计算点位,通过 dom 的 data-port-key
361
447
  */
@@ -494,9 +580,7 @@ var WorkflowNodePortsData = class extends EntityData {
494
580
  */
495
581
  updatePortEntity(portInfo) {
496
582
  const portEntity = this.getOrCreatePortEntity(portInfo);
497
- if (portInfo.targetElement) {
498
- portEntity.updateTargetElement(portInfo.targetElement);
499
- }
583
+ portEntity.update(portInfo);
500
584
  return portEntity;
501
585
  }
502
586
  };
@@ -537,6 +621,9 @@ var _WorkflowNodeLinesData = class _WorkflowNodeLinesData extends EntityData2 {
537
621
  get allLines() {
538
622
  return this.data.inputLines.concat(this.data.outputLines);
539
623
  }
624
+ get availableLines() {
625
+ return this.allLines.filter((line) => !line.isDrawing && !line.isHidden);
626
+ }
540
627
  /**
541
628
  * 输入节点
542
629
  */
@@ -637,8 +724,8 @@ var WorkflowLineRenderData = class extends EntityData3 {
637
724
  version: "",
638
725
  contributions: /* @__PURE__ */ new Map(),
639
726
  position: {
640
- from: { x: 0, y: 0 },
641
- to: { x: 0, y: 0 }
727
+ from: { x: 0, y: 0, location: "right" },
728
+ to: { x: 0, y: 0, location: "left" }
642
729
  }
643
730
  };
644
731
  }
@@ -678,22 +765,35 @@ var WorkflowLineRenderData = class extends EntityData3 {
678
765
  get lineType() {
679
766
  return this.entity.renderType ?? this.entity.linesManager.lineType;
680
767
  }
768
+ /**
769
+ * 获取 center 位置
770
+ */
771
+ get center() {
772
+ return this.currentLine?.center || { x: 0, y: 0, labelX: 0, labelY: 0 };
773
+ }
681
774
  /**
682
775
  * 更新版本
683
776
  * WARNING: 这个方法,必须在 requestAnimationFrame / useLayoutEffect 中调用,否则会引起浏览器强制重排
684
777
  */
685
778
  updatePosition() {
686
779
  this.data.position.from = this.entity.from.getData(WorkflowNodePortsData).getOutputPoint(this.entity.info.fromPort);
687
- this.data.position.to = this.entity.info.drawingTo ?? this.entity.to?.getData(WorkflowNodePortsData)?.getInputPoint(this.entity.info.toPort) ?? {
688
- x: this.data.position.from.x,
689
- y: this.data.position.from.y
690
- };
780
+ if (this.entity.info.drawingTo) {
781
+ this.data.position.to = this.entity.info.drawingTo;
782
+ } else {
783
+ this.data.position.to = this.entity.to?.getData(WorkflowNodePortsData)?.getInputPoint(this.entity.info.toPort) ?? {
784
+ x: this.data.position.from.x,
785
+ y: this.data.position.from.y,
786
+ location: this.data.position.from.location === "right" ? "left" : "top"
787
+ };
788
+ }
691
789
  this.data.version = [
692
790
  this.lineType,
693
791
  this.data.position.from.x,
694
792
  this.data.position.from.y,
793
+ this.data.position.from.location,
695
794
  this.data.position.to.x,
696
- this.data.position.to.y
795
+ this.data.position.to.y,
796
+ this.data.position.to.location
697
797
  ].join("-");
698
798
  }
699
799
  get currentLine() {
@@ -723,13 +823,16 @@ var POINT_RADIUS = 10;
723
823
  var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
724
824
  constructor(opts) {
725
825
  super(opts);
826
+ this._onLineDataChangeEmitter = new Emitter2();
827
+ this.onLineDataChange = this._onLineDataChangeEmitter.event;
726
828
  this._uiState = {
727
829
  hasError: false,
728
830
  flowing: false,
729
831
  disabled: false,
730
- vertical: false,
731
832
  hideArrow: false,
732
833
  reverse: false,
834
+ shrink: 10,
835
+ curvature: 0.25,
733
836
  highlightColor: "",
734
837
  lockedColor: ""
735
838
  };
@@ -747,7 +850,8 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
747
850
  to: opts.to,
748
851
  drawingTo: opts.drawingTo,
749
852
  fromPort: opts.fromPort,
750
- toPort: opts.toPort
853
+ toPort: opts.toPort,
854
+ data: opts.data
751
855
  });
752
856
  if (opts.drawingTo) {
753
857
  this.isDrawing = true;
@@ -760,6 +864,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
760
864
  this.fromPort?.validate();
761
865
  this.toPort?.validate();
762
866
  });
867
+ this.toDispose.push(this._onLineDataChangeEmitter);
763
868
  }
764
869
  /**
765
870
  * 转成线条 id
@@ -802,9 +907,13 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
802
907
  * 更新线条扩展数据
803
908
  * @param data
804
909
  */
805
- set lineData(data) {
806
- this._lineData = data;
807
- this.fireChange();
910
+ set lineData(newValue) {
911
+ const oldValue = this._lineData;
912
+ if (!isEqual2(oldValue, newValue)) {
913
+ this._lineData = newValue;
914
+ this._onLineDataChangeEmitter.fire({ oldValue, newValue });
915
+ this.fireChange();
916
+ }
808
917
  }
809
918
  /**
810
919
  * 获取线条的前置节点
@@ -827,20 +936,17 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
827
936
  }
828
937
  /**
829
938
  * 获取是否 testrun processing
830
- * @deprecated use `uiState.flowing` instead
939
+ * @deprecated use `flowing` instead
831
940
  */
832
941
  get processing() {
833
942
  return this._uiState.flowing;
834
943
  }
835
944
  /**
836
945
  * 设置 testrun processing 状态
837
- * @deprecated use `uiState.flowing` instead
946
+ * @deprecated use `flowing` instead
838
947
  */
839
948
  set processing(status) {
840
- if (this._uiState.flowing !== status) {
841
- this._uiState.flowing = status;
842
- this.fireChange();
843
- }
949
+ this.flowing = status;
844
950
  }
845
951
  // 获取连线是否为错误态
846
952
  get hasError() {
@@ -870,7 +976,6 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
870
976
  const { node, portID } = toPort;
871
977
  this._to = node;
872
978
  this.info.drawingTo = void 0;
873
- this.info.isDefaultLine = false;
874
979
  this.info.to = node.id;
875
980
  this.info.toPort = portID;
876
981
  } else {
@@ -895,7 +1000,6 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
895
1000
  }
896
1001
  if (!oldDrawingTo || pos.x !== oldDrawingTo.x || pos.y !== oldDrawingTo.y) {
897
1002
  this.info.to = void 0;
898
- this.info.isDefaultLine = false;
899
1003
  this.info.drawingTo = pos;
900
1004
  this.fireChange();
901
1005
  }
@@ -928,6 +1032,9 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
928
1032
  get bounds() {
929
1033
  return this.getData(WorkflowLineRenderData).bounds;
930
1034
  }
1035
+ get center() {
1036
+ return this.getData(WorkflowLineRenderData).center;
1037
+ }
931
1038
  /**
932
1039
  * 获取点和线最接近的距离
933
1040
  */
@@ -961,13 +1068,25 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
961
1068
  get flowing() {
962
1069
  return this.linesManager.isFlowingLine(this, this.uiState.flowing);
963
1070
  }
1071
+ set flowing(flowing) {
1072
+ if (this._uiState.flowing !== flowing) {
1073
+ this._uiState.flowing = flowing;
1074
+ this.fireChange();
1075
+ }
1076
+ }
964
1077
  /** 是否禁用 */
965
1078
  get disabled() {
966
1079
  return this.linesManager.isDisabledLine(this, this.uiState.disabled);
967
1080
  }
968
1081
  /** 是否竖向 */
969
1082
  get vertical() {
970
- return this.linesManager.isVerticalLine(this, this.uiState.vertical);
1083
+ const fromLocation = this.fromPort.location;
1084
+ const toLocation = this.toPort?.location;
1085
+ if (toLocation) {
1086
+ return toLocation === "top";
1087
+ } else {
1088
+ return fromLocation === "bottom";
1089
+ }
971
1090
  }
972
1091
  /** 获取线条渲染器类型 */
973
1092
  get renderType() {
@@ -975,7 +1094,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
975
1094
  }
976
1095
  /** 获取线条样式 */
977
1096
  get className() {
978
- return this.linesManager.setLineClassName(this) ?? "";
1097
+ return [this.linesManager.setLineClassName(this), this._uiState.className].filter((s) => !!s).join(" ");
979
1098
  }
980
1099
  get color() {
981
1100
  return this.linesManager.getLineColor(this);
@@ -989,6 +1108,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
989
1108
  this.info = info;
990
1109
  this._from = this.document.getNode(info.from);
991
1110
  this._to = info.to ? this.document.getNode(info.to) : void 0;
1111
+ this._lineData = info.data;
992
1112
  this.fireChange();
993
1113
  }
994
1114
  }
@@ -1149,12 +1269,12 @@ WorkflowSelectService = __decorateClass([
1149
1269
 
1150
1270
  // src/service/workflow-hover-service.ts
1151
1271
  import { inject as inject2, injectable as injectable2 } from "inversify";
1152
- import { Emitter as Emitter2 } from "@flowgram.ai/utils";
1272
+ import { Emitter as Emitter3 } from "@flowgram.ai/utils";
1153
1273
  import { EntityManager } from "@flowgram.ai/core";
1154
1274
  var WorkflowHoverService = class {
1155
1275
  constructor() {
1156
- this.onHoveredChangeEmitter = new Emitter2();
1157
- this.onUpdateHoverPositionEmitter = new Emitter2();
1276
+ this.onHoveredChangeEmitter = new Emitter3();
1277
+ this.onUpdateHoverPositionEmitter = new Emitter3();
1158
1278
  this.onHoveredChange = this.onHoveredChangeEmitter.event;
1159
1279
  this.onUpdateHoverPosition = this.onUpdateHoverPositionEmitter.event;
1160
1280
  // 当前鼠标 hover 位置
@@ -1220,7 +1340,7 @@ import { inject as inject6, injectable as injectable6, postConstruct as postCons
1220
1340
  import {
1221
1341
  domUtils as domUtils2,
1222
1342
  PromiseDeferred,
1223
- Emitter as Emitter5,
1343
+ Emitter as Emitter6,
1224
1344
  DisposableCollection as DisposableCollection2,
1225
1345
  Rectangle as Rectangle8,
1226
1346
  delay as delay2
@@ -1241,7 +1361,7 @@ import {
1241
1361
  // src/workflow-lines-manager.ts
1242
1362
  import { last } from "lodash-es";
1243
1363
  import { inject as inject3, injectable as injectable3 } from "inversify";
1244
- import { DisposableCollection, Emitter as Emitter3 } from "@flowgram.ai/utils";
1364
+ import { DisposableCollection, Emitter as Emitter4 } from "@flowgram.ai/utils";
1245
1365
  import { FlowNodeRenderData as FlowNodeRenderData2, FlowNodeTransformData as FlowNodeTransformData3 } from "@flowgram.ai/document";
1246
1366
  import { EntityManager as EntityManager2, PlaygroundConfigEntity as PlaygroundConfigEntity2 } from "@flowgram.ai/core";
1247
1367
 
@@ -1325,8 +1445,8 @@ var WorkflowLinesManager = class {
1325
1445
  this.toDispose = new DisposableCollection();
1326
1446
  // 线条类型
1327
1447
  this._lineType = 0 /* BEZIER */;
1328
- this.onAvailableLinesChangeEmitter = new Emitter3();
1329
- this.onForceUpdateEmitter = new Emitter3();
1448
+ this.onAvailableLinesChangeEmitter = new Emitter4();
1449
+ this.onForceUpdateEmitter = new Emitter4();
1330
1450
  /**
1331
1451
  * 有效的线条被添加或者删除时候触发,未连上的线条不算
1332
1452
  */
@@ -1397,6 +1517,9 @@ var WorkflowLinesManager = class {
1397
1517
  WorkflowLineEntity.portInfoToLineId(portInfo)
1398
1518
  );
1399
1519
  }
1520
+ getLineById(id) {
1521
+ return this.entityManager.getEntityById(id);
1522
+ }
1400
1523
  replaceLine(oldPortInfo, newPortInfo) {
1401
1524
  const oldLine = this.getLine(oldPortInfo);
1402
1525
  if (oldLine) {
@@ -1405,7 +1528,7 @@ var WorkflowLinesManager = class {
1405
1528
  return this.createLine(newPortInfo);
1406
1529
  }
1407
1530
  createLine(options) {
1408
- const { from, to, drawingTo, fromPort, toPort } = options;
1531
+ const { from, to, drawingTo, fromPort, toPort, data } = options;
1409
1532
  const available = Boolean(from && to);
1410
1533
  const key = options.key || WorkflowLineEntity.portInfoToLineId(options);
1411
1534
  let line = this.entityManager.getEntityById(key);
@@ -1428,7 +1551,8 @@ var WorkflowLinesManager = class {
1428
1551
  fromPort,
1429
1552
  toPort,
1430
1553
  to,
1431
- drawingTo
1554
+ drawingTo,
1555
+ data
1432
1556
  });
1433
1557
  this.registerData(line);
1434
1558
  fromNode.addLine(line);
@@ -1449,6 +1573,14 @@ var WorkflowLinesManager = class {
1449
1573
  });
1450
1574
  }
1451
1575
  });
1576
+ line.onLineDataChange(({ oldValue }) => {
1577
+ this.onAvailableLinesChangeEmitter.fire({
1578
+ type: "LINE_DATA_CHANGE" /* LINE_DATA_CHANGE */,
1579
+ toJSON: () => line.toJSON(),
1580
+ oldValue,
1581
+ entity: line
1582
+ });
1583
+ });
1452
1584
  if (available) {
1453
1585
  this.onAvailableLinesChangeEmitter.fire({
1454
1586
  type: "ADD_LINE" /* ADD_LINE */,
@@ -1512,12 +1644,6 @@ var WorkflowLinesManager = class {
1512
1644
  }
1513
1645
  return defaultValue;
1514
1646
  }
1515
- isVerticalLine(line, defaultValue = false) {
1516
- if (this.options.isVerticalLine) {
1517
- return this.options.isVerticalLine(line);
1518
- }
1519
- return defaultValue;
1520
- }
1521
1647
  setLineRenderType(line) {
1522
1648
  if (this.options.setLineRenderType) {
1523
1649
  return this.options.setLineRenderType(line);
@@ -1561,6 +1687,14 @@ var WorkflowLinesManager = class {
1561
1687
  if (fromPort === toPort || fromPort.node === toPort.node || fromPort.portType !== "output" || toPort.portType !== "input" || toPort.disabled) {
1562
1688
  return false;
1563
1689
  }
1690
+ const fromCanAdd = fromPort.node.getNodeRegistry().canAddLine;
1691
+ const toCanAdd = toPort.node.getNodeRegistry().canAddLine;
1692
+ if (fromCanAdd && !fromCanAdd(fromPort, toPort, this, silent)) {
1693
+ return false;
1694
+ }
1695
+ if (toCanAdd && !toCanAdd(fromPort, toPort, this, silent)) {
1696
+ return false;
1697
+ }
1564
1698
  if (this.options.canAddLine) {
1565
1699
  return this.options.canAddLine(fromPort, toPort, this, silent);
1566
1700
  }
@@ -1664,7 +1798,7 @@ WorkflowLinesManager = __decorateClass([
1664
1798
  // src/workflow-document.ts
1665
1799
  import { customAlphabet } from "nanoid";
1666
1800
  import { inject as inject5, injectable as injectable5, optional, postConstruct } from "inversify";
1667
- import { Emitter as Emitter4 } from "@flowgram.ai/utils";
1801
+ import { Emitter as Emitter5 } from "@flowgram.ai/utils";
1668
1802
  import { NodeEngineContext } from "@flowgram.ai/form-core";
1669
1803
  import {
1670
1804
  FlowDocument,
@@ -1825,10 +1959,10 @@ var WorkflowDocumentProvider = Symbol("WorkflowDocumentProvider");
1825
1959
  var WorkflowDocument = class extends FlowDocument {
1826
1960
  constructor() {
1827
1961
  super(...arguments);
1828
- this._onContentChangeEmitter = new Emitter4();
1829
- this.onLoadedEmitter = new Emitter4();
1962
+ this._onContentChangeEmitter = new Emitter5();
1963
+ this.onLoadedEmitter = new Emitter5();
1830
1964
  this.onContentChange = this._onContentChangeEmitter.event;
1831
- this._onReloadEmitter = new Emitter4();
1965
+ this._onReloadEmitter = new Emitter5();
1832
1966
  this.onReload = this._onReloadEmitter.event;
1833
1967
  /**
1834
1968
  * 数据加载完成
@@ -1840,6 +1974,11 @@ var WorkflowDocument = class extends FlowDocument {
1840
1974
  get loading() {
1841
1975
  return this._loading;
1842
1976
  }
1977
+ /**
1978
+ * use `ctx.tools.fitView()` instead
1979
+ * @deprecated
1980
+ * @param easing
1981
+ */
1843
1982
  async fitView(easing) {
1844
1983
  return fitView(this, this.playgroundConfig, easing).then(() => {
1845
1984
  this.linesManager.forceUpdate();
@@ -2147,10 +2286,13 @@ var WorkflowDocument = class extends FlowDocument {
2147
2286
  from: line.from.id,
2148
2287
  to: line.to.id
2149
2288
  }));
2150
- const startNodeId = allNode.find((node) => node.isStart).id;
2151
- const endNodeId = allNode.find((node) => node.isNodeEnd).id;
2289
+ const startNodeId = allNode.find((node) => node.isStart)?.id;
2290
+ const endNodeId = allNode.find((node) => node.isNodeEnd)?.id;
2152
2291
  const nodeInContainer = allNode.filter((node) => node.parent?.getNodeMeta().isContainer).map((node) => node.id);
2153
- const associatedCache = /* @__PURE__ */ new Set([endNodeId, ...nodeInContainer]);
2292
+ const associatedCache = new Set(nodeInContainer);
2293
+ if (endNodeId) {
2294
+ associatedCache.add(endNodeId);
2295
+ }
2154
2296
  const bfs = (nodeId) => {
2155
2297
  if (associatedCache.has(nodeId)) {
2156
2298
  return;
@@ -2164,7 +2306,9 @@ var WorkflowDocument = class extends FlowDocument {
2164
2306
  }, []);
2165
2307
  nextNodes.forEach(bfs);
2166
2308
  };
2167
- bfs(startNodeId);
2309
+ if (startNodeId) {
2310
+ bfs(startNodeId);
2311
+ }
2168
2312
  const associatedNodes = allNode.filter((node) => associatedCache.has(node.id));
2169
2313
  return associatedNodes;
2170
2314
  }
@@ -2286,6 +2430,11 @@ var WorkflowDocument = class extends FlowDocument {
2286
2430
  * 导出数据
2287
2431
  */
2288
2432
  toJSON() {
2433
+ if (this.disposed) {
2434
+ throw new Error(
2435
+ "The WorkflowDocument has been disposed and it is no longer possible to call toJSON."
2436
+ );
2437
+ }
2289
2438
  const rootJSON = this.toNodeJSON(this.root);
2290
2439
  const json = {
2291
2440
  nodes: rootJSON.blocks ?? [],
@@ -2298,9 +2447,18 @@ var WorkflowDocument = class extends FlowDocument {
2298
2447
  this._onReloadEmitter.dispose();
2299
2448
  }
2300
2449
  /**
2301
- * 逐层创建节点和线条
2450
+ * 批量添加节点
2451
+ * @deprecated use 'batchAddFromJSON' instead
2452
+ * @param json
2453
+ * @param options
2302
2454
  */
2303
2455
  renderJSON(json, options) {
2456
+ return this.batchAddFromJSON(json, options);
2457
+ }
2458
+ /**
2459
+ * 批量添加节点
2460
+ */
2461
+ batchAddFromJSON(json, options) {
2304
2462
  const { parent = this.root, isClone = false } = options ?? {};
2305
2463
  const containerID = this.getNodeSubCanvas(parent)?.canvasNode.id ?? parent.id;
2306
2464
  const processedJSON = buildGroupJSON(json);
@@ -2366,7 +2524,8 @@ var WorkflowDocument = class extends FlowDocument {
2366
2524
  from: json.sourceNodeID,
2367
2525
  fromPort: json.sourcePortID,
2368
2526
  to: json.targetNodeID,
2369
- toPort: json.targetPortID
2527
+ toPort: json.targetPortID,
2528
+ data: json.data
2370
2529
  };
2371
2530
  if (!parentId) {
2372
2531
  return this.linesManager.createLine(lineInfo);
@@ -2431,10 +2590,10 @@ function checkDragSuccess(time, e, originLine) {
2431
2590
  }
2432
2591
  var WorkflowDragService = class {
2433
2592
  constructor() {
2434
- this._onDragLineEventEmitter = new Emitter5();
2593
+ this._onDragLineEventEmitter = new Emitter6();
2435
2594
  this.onDragLineEventChange = this._onDragLineEventEmitter.event;
2436
2595
  this.isDragging = false;
2437
- this._nodesDragEmitter = new Emitter5();
2596
+ this._nodesDragEmitter = new Emitter6();
2438
2597
  this.onNodesDrag = this._nodesDragEmitter.event;
2439
2598
  this._toDispose = new DisposableCollection2();
2440
2599
  this._droppableTransforms = [];
@@ -2833,10 +2992,16 @@ var WorkflowDragService = class {
2833
2992
  originLine.highlightColor = this.linesManager.lineColor.hidden;
2834
2993
  }
2835
2994
  dragSuccess = true;
2995
+ const pos = config.getPosFromMouseEvent(event);
2836
2996
  line = this.linesManager.createLine({
2837
2997
  from: fromPort.node.id,
2838
2998
  fromPort: fromPort.portID,
2839
- drawingTo: config.getPosFromMouseEvent(event)
2999
+ data: originLine?.lineData,
3000
+ drawingTo: {
3001
+ x: pos.x,
3002
+ y: pos.y,
3003
+ location: fromPort.location === "right" ? "left" : "top"
3004
+ }
2840
3005
  });
2841
3006
  if (!line) {
2842
3007
  return;
@@ -2871,9 +3036,17 @@ var WorkflowDragService = class {
2871
3036
  lineErrorReset = hasError;
2872
3037
  }
2873
3038
  if (line.toPort) {
2874
- line.drawingTo = { x: line.toPort.point.x, y: line.toPort.point.y };
3039
+ line.drawingTo = {
3040
+ x: line.toPort.point.x,
3041
+ y: line.toPort.point.y,
3042
+ location: line.toPort.location
3043
+ };
2875
3044
  } else {
2876
- line.drawingTo = { x: dragPos.x, y: dragPos.y };
3045
+ line.drawingTo = {
3046
+ x: dragPos.x,
3047
+ y: dragPos.y,
3048
+ location: line.fromPort.location === "right" ? "left" : "top"
3049
+ };
2877
3050
  }
2878
3051
  originLine?.validate();
2879
3052
  line.validate();
@@ -2917,7 +3090,8 @@ var WorkflowDragService = class {
2917
3090
  from: fromPort.node.id,
2918
3091
  fromPort: fromPort.portID,
2919
3092
  to: toPort.node.id,
2920
- toPort: toPort.portID
3093
+ toPort: toPort.portID,
3094
+ data: originLine?.lineData
2921
3095
  } : void 0;
2922
3096
  const isReset = originLine && toPort;
2923
3097
  if (isReset && !this.linesManager.canReset(
@@ -3030,7 +3204,7 @@ WorkflowDragService = __decorateClass([
3030
3204
  import { inject as inject7, injectable as injectable7, postConstruct as postConstruct3 } from "inversify";
3031
3205
  import { PlaygroundConfigEntity as PlaygroundConfigEntity6 } from "@flowgram.ai/core";
3032
3206
  import { EntityManager as EntityManager3 } from "@flowgram.ai/core";
3033
- import { DisposableCollection as DisposableCollection3, Emitter as Emitter6 } from "@flowgram.ai/utils";
3207
+ import { DisposableCollection as DisposableCollection3, Emitter as Emitter7 } from "@flowgram.ai/utils";
3034
3208
 
3035
3209
  // src/utils/layout-to-positions.ts
3036
3210
  import { FlowNodeTransformData as FlowNodeTransformData7 } from "@flowgram.ai/document";
@@ -3075,7 +3249,7 @@ var layoutToPositions = async (nodes, nodePositionMap) => {
3075
3249
  // src/service/workflow-reset-layout-service.ts
3076
3250
  var WorkflowResetLayoutService = class {
3077
3251
  constructor() {
3078
- this._resetLayoutEmitter = new Emitter6();
3252
+ this._resetLayoutEmitter = new Emitter7();
3079
3253
  /**
3080
3254
  * reset layout事件
3081
3255
  */
@@ -3134,13 +3308,13 @@ WorkflowResetLayoutService = __decorateClass([
3134
3308
 
3135
3309
  // src/service/workflow-operation-base-service.ts
3136
3310
  import { inject as inject8 } from "inversify";
3137
- import { Emitter as Emitter7 } from "@flowgram.ai/utils";
3311
+ import { Emitter as Emitter8 } from "@flowgram.ai/utils";
3138
3312
  import { FlowOperationBaseServiceImpl } from "@flowgram.ai/document";
3139
3313
  import { TransformData as TransformData10 } from "@flowgram.ai/core";
3140
3314
  var WorkflowOperationBaseServiceImpl = class extends FlowOperationBaseServiceImpl {
3141
3315
  constructor() {
3142
3316
  super(...arguments);
3143
- this.onNodePostionUpdateEmitter = new Emitter7();
3317
+ this.onNodePostionUpdateEmitter = new Emitter8();
3144
3318
  this.onNodePostionUpdate = this.onNodePostionUpdateEmitter.event;
3145
3319
  }
3146
3320
  updateNodePosition(nodeOrId, position) {
@@ -3187,6 +3361,7 @@ function usePlaygroundReadonlyState(listenChange) {
3187
3361
  function checkTargetDraggable(el) {
3188
3362
  return el && el.tagName !== "INPUT" && el.tagName !== "TEXTAREA" && !el.closest(".flow-canvas-not-draggable");
3189
3363
  }
3364
+ var isFirefox = navigator?.userAgent?.includes?.("Firefox");
3190
3365
  function useNodeRender(nodeFromProps) {
3191
3366
  const node = nodeFromProps || useContext(PlaygroundEntityContext);
3192
3367
  const renderData = node.getData(FlowNodeRenderData3);
@@ -3250,7 +3425,6 @@ function useNodeRender(nodeFromProps) {
3250
3425
  );
3251
3426
  const deleteNode = useCallback(() => node.dispose(), [node]);
3252
3427
  useListenEvents(portsData.onDataChange);
3253
- const isFirefox = navigator?.userAgent?.includes?.("Firefox");
3254
3428
  const onFocus = useCallback(() => {
3255
3429
  if (isFirefox) {
3256
3430
  nodeRef.current?.setAttribute("draggable", "false");
@@ -3446,81 +3620,6 @@ var WorkflowDocumentContainerModule = new ContainerModule(
3446
3620
  bind(WorkflowDocumentProvider).toDynamicValue((ctx) => () => ctx.container.get(WorkflowDocument)).inSingletonScope();
3447
3621
  }
3448
3622
  );
3449
-
3450
- // src/utils/simple-line.ts
3451
- import { Point, Rectangle as Rectangle9 } from "@flowgram.ai/utils";
3452
- var LINE_PADDING = 12;
3453
- var WorkflowSimpleLineContribution = class {
3454
- constructor(entity) {
3455
- this.entity = entity;
3456
- }
3457
- get path() {
3458
- return this.data?.path ?? "";
3459
- }
3460
- calcDistance(pos) {
3461
- if (!this.data) {
3462
- return Number.MAX_SAFE_INTEGER;
3463
- }
3464
- const [start, end] = this.data.points;
3465
- return Point.getDistance(pos, this.projectPointOnLine(pos, start, end));
3466
- }
3467
- get bounds() {
3468
- if (!this.data) {
3469
- return new Rectangle9();
3470
- }
3471
- return this.data.bbox;
3472
- }
3473
- update(params) {
3474
- const { fromPos, toPos } = params;
3475
- const { vertical } = this.entity;
3476
- const sourceOffset = {
3477
- x: vertical ? 0 : POINT_RADIUS,
3478
- y: vertical ? POINT_RADIUS : 0
3479
- };
3480
- const targetOffset = {
3481
- x: vertical ? 0 : -POINT_RADIUS,
3482
- y: vertical ? -POINT_RADIUS : 0
3483
- };
3484
- const points = [
3485
- {
3486
- x: fromPos.x + sourceOffset.x,
3487
- y: fromPos.y + sourceOffset.y
3488
- },
3489
- {
3490
- x: toPos.x + targetOffset.x,
3491
- y: toPos.y + targetOffset.y
3492
- }
3493
- ];
3494
- const bbox = Rectangle9.createRectangleWithTwoPoints(points[0], points[1]);
3495
- const adjustedPoints = points.map((p) => ({
3496
- x: p.x - bbox.x + LINE_PADDING,
3497
- y: p.y - bbox.y + LINE_PADDING
3498
- }));
3499
- const path = `M ${adjustedPoints[0].x} ${adjustedPoints[0].y} L ${adjustedPoints[1].x} ${adjustedPoints[1].y}`;
3500
- this.data = {
3501
- points,
3502
- path,
3503
- bbox
3504
- };
3505
- }
3506
- projectPointOnLine(point, lineStart, lineEnd) {
3507
- const dx = lineEnd.x - lineStart.x;
3508
- const dy = lineEnd.y - lineStart.y;
3509
- if (dx === 0) {
3510
- return { x: lineStart.x, y: point.y };
3511
- }
3512
- if (dy === 0) {
3513
- return { x: point.x, y: lineStart.y };
3514
- }
3515
- const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
3516
- const clampedT = Math.max(0, Math.min(1, t));
3517
- return {
3518
- x: lineStart.x + clampedT * dx,
3519
- y: lineStart.y + clampedT * dy
3520
- };
3521
- }
3522
- };
3523
- WorkflowSimpleLineContribution.type = "WorkflowSimpleLineContribution";
3524
3623
  export {
3525
3624
  EditorCursorState,
3526
3625
  InteractiveType,
@@ -3551,7 +3650,6 @@ export {
3551
3650
  WorkflowPortEntity,
3552
3651
  WorkflowResetLayoutService,
3553
3652
  WorkflowSelectService,
3554
- WorkflowSimpleLineContribution,
3555
3653
  bindConfigEntity,
3556
3654
  buildGroupJSON,
3557
3655
  compose,
@@ -3560,6 +3658,7 @@ export {
3560
3658
  domReactToBounds,
3561
3659
  fitView,
3562
3660
  getAntiOverlapPosition,
3661
+ getLineCenter,
3563
3662
  getPortEntityId,
3564
3663
  nanoid,
3565
3664
  useConfigEntity,