@flowgram.ai/free-layout-core 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (66) hide show
  1. package/dist/esm/chunk-3UW6BHP2.js +7 -0
  2. package/dist/esm/chunk-3UW6BHP2.js.map +1 -0
  3. package/dist/esm/chunk-5BT3ZR4D.js +23 -0
  4. package/dist/esm/chunk-5BT3ZR4D.js.map +1 -0
  5. package/dist/esm/{chunk-DE4324TR.js → chunk-IKQUOAWQ.js} +1 -1
  6. package/dist/esm/chunk-IKQUOAWQ.js.map +1 -0
  7. package/dist/esm/chunk-TQLT57GW.js +1 -0
  8. package/dist/esm/chunk-TQLT57GW.js.map +1 -0
  9. package/dist/esm/{chunk-J5FVRRUV.js → chunk-U2XMPOSL.js} +2 -1
  10. package/dist/esm/chunk-U2XMPOSL.js.map +1 -0
  11. package/dist/esm/index.js +1392 -642
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/esm/typings/index.js +12 -7
  14. package/dist/esm/typings/workflow-drag.js +2 -0
  15. package/dist/esm/typings/workflow-drag.js.map +1 -0
  16. package/dist/esm/typings/workflow-json.js +1 -1
  17. package/dist/esm/typings/workflow-line.js +1 -1
  18. package/dist/esm/typings/workflow-operation.js +8 -0
  19. package/dist/esm/typings/workflow-operation.js.map +1 -0
  20. package/dist/index.d.mts +261 -189
  21. package/dist/index.d.ts +261 -189
  22. package/dist/index.js +1392 -644
  23. package/dist/index.js.map +1 -1
  24. package/dist/typings/index.d.mts +10 -5
  25. package/dist/typings/index.d.ts +10 -5
  26. package/dist/typings/index.js +16 -7
  27. package/dist/typings/index.js.map +1 -1
  28. package/dist/typings/workflow-drag.d.mts +7 -0
  29. package/dist/typings/workflow-drag.d.ts +7 -0
  30. package/dist/typings/workflow-drag.js +19 -0
  31. package/dist/typings/workflow-drag.js.map +1 -0
  32. package/dist/typings/workflow-edge.d.mts +5 -0
  33. package/dist/typings/workflow-edge.d.ts +5 -0
  34. package/dist/typings/workflow-edge.js.map +1 -1
  35. package/dist/typings/workflow-json.d.mts +3 -4
  36. package/dist/typings/workflow-json.d.ts +3 -4
  37. package/dist/typings/workflow-json.js +1 -0
  38. package/dist/typings/workflow-json.js.map +1 -1
  39. package/dist/typings/workflow-line.d.mts +3 -4
  40. package/dist/typings/workflow-line.d.ts +3 -4
  41. package/dist/typings/workflow-line.js +8 -5
  42. package/dist/typings/workflow-line.js.map +1 -1
  43. package/dist/typings/workflow-node.d.mts +2 -3
  44. package/dist/typings/workflow-node.d.ts +2 -3
  45. package/dist/typings/workflow-node.js.map +1 -1
  46. package/dist/typings/workflow-operation.d.mts +38 -0
  47. package/dist/typings/workflow-operation.d.ts +38 -0
  48. package/dist/typings/workflow-operation.js +31 -0
  49. package/dist/typings/workflow-operation.js.map +1 -0
  50. package/dist/typings/workflow-registry.d.mts +9 -4
  51. package/dist/typings/workflow-registry.d.ts +9 -4
  52. package/dist/typings/workflow-registry.js.map +1 -1
  53. package/dist/typings/workflow-sub-canvas.d.mts +6 -1
  54. package/dist/typings/workflow-sub-canvas.d.ts +6 -1
  55. package/dist/typings/workflow-sub-canvas.js.map +1 -1
  56. package/dist/workflow-node-entity-DH5qlw7I.d.mts +1147 -0
  57. package/dist/workflow-node-entity-DWVtlA2a.d.ts +1147 -0
  58. package/package.json +15 -15
  59. package/dist/esm/chunk-DE4324TR.js.map +0 -1
  60. package/dist/esm/chunk-J5FVRRUV.js.map +0 -1
  61. package/dist/esm/chunk-PT4ZVDZZ.js +0 -20
  62. package/dist/esm/chunk-PT4ZVDZZ.js.map +0 -1
  63. package/dist/workflow-line-entity-BJQBRDgJ.d.mts +0 -747
  64. package/dist/workflow-line-entity-CEitdjhk.d.ts +0 -747
  65. package/dist/workflow-sub-canvas-DOVla1mw.d.mts +0 -15
  66. package/dist/workflow-sub-canvas-DOVla1mw.d.ts +0 -15
package/dist/esm/index.js CHANGED
@@ -1,20 +1,24 @@
1
1
  import {
2
2
  URLParams
3
- } from "./chunk-DE4324TR.js";
3
+ } from "./chunk-IKQUOAWQ.js";
4
+ import "./chunk-KNYZRMIO.js";
5
+ import "./chunk-NU6G5HF4.js";
6
+ import "./chunk-TQLT57GW.js";
4
7
  import "./chunk-CGOMTQ3G.js";
5
8
  import {
6
9
  WorkflowContentChangeType
7
- } from "./chunk-J5FVRRUV.js";
10
+ } from "./chunk-U2XMPOSL.js";
8
11
  import {
9
12
  LineColors,
10
13
  LineType
11
- } from "./chunk-PT4ZVDZZ.js";
14
+ } from "./chunk-5BT3ZR4D.js";
15
+ import "./chunk-DDJTYHXN.js";
16
+ import {
17
+ WorkflowOperationBaseService
18
+ } from "./chunk-3UW6BHP2.js";
12
19
  import {
13
20
  __decorateClass
14
21
  } from "./chunk-EUXUH3YW.js";
15
- import "./chunk-DDJTYHXN.js";
16
- import "./chunk-KNYZRMIO.js";
17
- import "./chunk-NU6G5HF4.js";
18
22
 
19
23
  // src/workflow-commands.ts
20
24
  var WorkflowCommands = /* @__PURE__ */ ((WorkflowCommands2) => {
@@ -39,16 +43,20 @@ import {
39
43
  useEntities,
40
44
  useEntityFromContext as useEntityFromContext3,
41
45
  useEntityDataFromContext,
42
- useRefresh as useRefresh2,
43
- usePlaygroundLatest
46
+ useRefresh as useRefresh2
44
47
  } from "@flowgram.ai/core";
45
48
 
46
49
  // src/hooks/use-node-render.tsx
47
50
  import { useCallback, useEffect as useEffect2, useRef, useState, useContext, useMemo } from "react";
48
51
  import { useObserve } from "@flowgram.ai/reactive";
49
52
  import { getNodeForm } from "@flowgram.ai/node";
50
- import { FlowNodeRenderData as FlowNodeRenderData2 } from "@flowgram.ai/document";
51
- import { PlaygroundEntityContext, useListenEvents, useService } from "@flowgram.ai/core";
53
+ import { FlowNodeRenderData as FlowNodeRenderData3 } from "@flowgram.ai/document";
54
+ import {
55
+ MouseTouchEvent as MouseTouchEvent2,
56
+ PlaygroundEntityContext,
57
+ useListenEvents,
58
+ useService
59
+ } from "@flowgram.ai/core";
52
60
 
53
61
  // src/service/workflow-select-service.ts
54
62
  import { inject, injectable } from "inversify";
@@ -63,6 +71,44 @@ import { Rectangle as Rectangle6, SizeSchema } from "@flowgram.ai/utils";
63
71
  import { bindConfigEntity } from "@flowgram.ai/core";
64
72
  import { delay } from "@flowgram.ai/utils";
65
73
 
74
+ // src/utils/build-group-json.ts
75
+ import { FlowNodeBaseType } from "@flowgram.ai/document";
76
+ var buildGroupJSON = (json) => {
77
+ const { nodes, edges } = json;
78
+ const groupJSONs = nodes.filter(
79
+ (nodeJSON) => nodeJSON.type === FlowNodeBaseType.GROUP
80
+ );
81
+ const nodeJSONMap = new Map(nodes.map((n) => [n.id, n]));
82
+ const groupNodeJSONs = groupJSONs.map((groupJSON) => {
83
+ const groupBlocks = (groupJSON.data.blockIDs ?? []).map((blockID) => nodeJSONMap.get(blockID)).filter(Boolean);
84
+ const groupEdges = edges?.filter(
85
+ (edge) => groupBlocks.some((block) => block.id === edge.sourceNodeID || block.id === edge.targetNodeID)
86
+ );
87
+ const groupNodeJSON = {
88
+ ...groupJSON,
89
+ blocks: groupBlocks,
90
+ edges: groupEdges
91
+ };
92
+ return groupNodeJSON;
93
+ });
94
+ const groupBlockSet = new Set(groupJSONs.map((groupJSON) => groupJSON.data.blockIDs).flat());
95
+ const processedNodes = nodes.filter((nodeJSON) => !groupBlockSet.has(nodeJSON.id)).concat(groupNodeJSONs);
96
+ return {
97
+ nodes: processedNodes,
98
+ edges
99
+ };
100
+ };
101
+
102
+ // src/utils/get-line-center.ts
103
+ function getLineCenter(from, to, bbox, linePadding) {
104
+ return {
105
+ x: bbox.center.x,
106
+ y: bbox.center.y,
107
+ labelX: bbox.center.x - bbox.x + linePadding,
108
+ labelY: bbox.center.y - bbox.y + linePadding
109
+ };
110
+ }
111
+
66
112
  // src/utils/nanoid.ts
67
113
  import { nanoid as nanoidOrigin } from "nanoid";
68
114
  function nanoid(n) {
@@ -73,8 +119,8 @@ function nanoid(n) {
73
119
  import { compose, composeAsync } from "@flowgram.ai/utils";
74
120
 
75
121
  // src/utils/fit-view.ts
76
- import { TransformData } from "@flowgram.ai/core";
77
122
  import { Rectangle } from "@flowgram.ai/utils";
123
+ import { TransformData } from "@flowgram.ai/core";
78
124
  var fitView = (doc, playgroundConfig, easing = true) => {
79
125
  const bounds = Rectangle.enlarge(
80
126
  doc.getAllNodes().map((node) => node.getData(TransformData).bounds)
@@ -121,7 +167,7 @@ var WorkflowNodeEntity = FlowNodeEntity;
121
167
 
122
168
  // src/entities/workflow-line-entity.ts
123
169
  import { isEqual as isEqual2 } from "lodash-es";
124
- import { domUtils } from "@flowgram.ai/utils";
170
+ import { domUtils, Emitter as Emitter2 } from "@flowgram.ai/utils";
125
171
  import { Entity as Entity2 } from "@flowgram.ai/core";
126
172
 
127
173
  // src/entity-datas/workflow-node-ports-data.ts
@@ -130,25 +176,49 @@ import { FlowNodeRenderData } from "@flowgram.ai/document";
130
176
  import { EntityData, SizeData } from "@flowgram.ai/core";
131
177
 
132
178
  // src/entities/workflow-port-entity.ts
133
- import { Rectangle as Rectangle3, Emitter } from "@flowgram.ai/utils";
179
+ import { Rectangle as Rectangle3, Emitter, Compare } from "@flowgram.ai/utils";
180
+ import { FlowNodeTransformData } from "@flowgram.ai/document";
134
181
  import {
135
182
  Entity,
136
183
  PlaygroundConfigEntity,
137
184
  TransformData as TransformData3
138
185
  } from "@flowgram.ai/core";
186
+
187
+ // src/utils/location-config-to-point.ts
188
+ function locationConfigToPoint(bounds, config, _offset = { x: 0, y: 0 }) {
189
+ const offset = { ..._offset };
190
+ if (config.left !== void 0) {
191
+ offset.x += typeof config.left === "string" ? parseFloat(config.left) * 0.01 * bounds.width : config.left;
192
+ } else if (config.right !== void 0) {
193
+ offset.x += bounds.width - (typeof config.right === "string" ? parseFloat(config.right) * 0.01 * bounds.width : config.right);
194
+ }
195
+ if (config.top !== void 0) {
196
+ offset.y += typeof config.top === "string" ? parseFloat(config.top) * 0.01 * bounds.height : config.top;
197
+ } else if (config.bottom !== void 0) {
198
+ offset.y += bounds.height - (typeof config.bottom === "string" ? parseFloat(config.bottom) * 0.01 * bounds.height : config.bottom);
199
+ }
200
+ return {
201
+ x: bounds.x + offset.x,
202
+ y: bounds.y + offset.y
203
+ };
204
+ }
205
+
206
+ // src/entities/workflow-port-entity.ts
139
207
  var PORT_SIZE = 24;
140
208
  var WorkflowPortEntity = class extends Entity {
141
- // relativePosition
142
209
  constructor(opts) {
143
210
  super(opts);
144
211
  this.portID = "";
145
- this._disabled = false;
146
212
  this._hasError = false;
147
213
  this._onErrorChangedEmitter = new Emitter();
148
214
  this.onErrorChanged = this._onErrorChangedEmitter.event;
149
215
  this.portID = opts.portID || "";
150
216
  this.portType = opts.type;
151
- this._disabled = opts.disabled ?? false;
217
+ this._disabled = opts.disabled;
218
+ this._offset = opts.offset;
219
+ this._locationConfig = opts.locationConfig;
220
+ this._location = opts.location;
221
+ this._size = opts.size;
152
222
  this.node = opts.node;
153
223
  this.updateTargetElement(opts.targetElement);
154
224
  this.toDispose.push(this.node.getData(TransformData3).onDataChange(() => this.fireChange()));
@@ -157,51 +227,98 @@ var WorkflowPortEntity = class extends Entity {
157
227
  static getPortEntityId(node, portType, portID = "") {
158
228
  return getPortEntityId(node, portType, portID);
159
229
  }
230
+ get position() {
231
+ return this._location;
232
+ }
160
233
  // 获取连线是否为错误态
161
234
  get hasError() {
162
235
  return this._hasError;
163
236
  }
164
237
  // 设置连线的错误态,外部应使用 validate 进行更新
165
238
  set hasError(hasError) {
166
- this._hasError = hasError;
167
- this._onErrorChangedEmitter.fire();
239
+ if (hasError !== this._hasError) {
240
+ this._hasError = hasError;
241
+ this._onErrorChangedEmitter.fire();
242
+ }
168
243
  }
169
244
  validate() {
170
245
  const anyLineHasError = this.allLines.some((line) => {
171
246
  if (line.disposed || line.isHidden) {
172
247
  return false;
173
248
  }
174
- line.validateSelf();
175
249
  return line.hasError;
176
250
  });
177
251
  const isPortHasError = this.node.document.isErrorPort(this);
178
252
  this.hasError = anyLineHasError || isPortHasError;
179
253
  }
180
254
  isErrorPort() {
181
- return this.node.document.isErrorPort(this);
255
+ return this.node.document.isErrorPort(this, this.hasError);
256
+ }
257
+ get location() {
258
+ if (this._location) {
259
+ return this._location;
260
+ }
261
+ if (this.portType === "input") {
262
+ return "left";
263
+ }
264
+ return "right";
182
265
  }
183
266
  get point() {
184
- const { targetElement } = this;
185
- const { bounds } = this.node.getData(TransformData3);
267
+ const { targetElement, _locationConfig } = this;
268
+ const { bounds } = this.node.getData(FlowNodeTransformData);
269
+ const location2 = this.location;
186
270
  if (targetElement) {
187
271
  const pos = domReactToBounds(targetElement.getBoundingClientRect()).center;
188
- return this.entityManager.getEntity(PlaygroundConfigEntity).getPosFromMouseEvent({
272
+ const point2 = this.entityManager.getEntity(PlaygroundConfigEntity).getPosFromMouseEvent({
189
273
  clientX: pos.x,
190
274
  clientY: pos.y
191
275
  });
276
+ return {
277
+ x: point2.x,
278
+ y: point2.y,
279
+ location: location2
280
+ };
192
281
  }
193
- if (this.portType === "input") {
194
- return bounds.leftCenter;
282
+ if (_locationConfig) {
283
+ return {
284
+ ...locationConfigToPoint(bounds, _locationConfig, this._offset),
285
+ location: location2
286
+ };
287
+ }
288
+ const offset = this._offset || { x: 0, y: 0 };
289
+ let point = { x: 0, y: 0 };
290
+ switch (location2) {
291
+ case "left":
292
+ point = bounds.leftCenter;
293
+ break;
294
+ case "top":
295
+ point = bounds.topCenter;
296
+ break;
297
+ case "right":
298
+ point = bounds.rightCenter;
299
+ break;
300
+ case "bottom":
301
+ point = bounds.bottomCenter;
302
+ break;
195
303
  }
196
- return bounds.rightCenter;
304
+ return {
305
+ x: point.x + offset.x,
306
+ y: point.y + offset.y,
307
+ location: location2
308
+ };
197
309
  }
198
310
  /**
199
- * 点的区域
311
+ * 端口热区
200
312
  */
201
313
  get bounds() {
202
314
  const { point } = this;
203
- const halfSize = PORT_SIZE / 2;
204
- return new Rectangle3(point.x - halfSize, point.y - halfSize, PORT_SIZE, PORT_SIZE);
315
+ const size = this._size || { width: PORT_SIZE, height: PORT_SIZE };
316
+ return new Rectangle3(
317
+ point.x - size.width / 2,
318
+ point.y - size.height / 2,
319
+ size.width,
320
+ size.height
321
+ );
205
322
  }
206
323
  isHovered(x, y) {
207
324
  return this.bounds.contains(x, y);
@@ -211,7 +328,7 @@ var WorkflowPortEntity = class extends Entity {
211
328
  */
212
329
  get relativePosition() {
213
330
  const { point } = this;
214
- const { bounds } = this.node.getData(TransformData3);
331
+ const { bounds } = this.node.getData(FlowNodeTransformData);
215
332
  return {
216
333
  x: point.x - bounds.x,
217
334
  y: point.y - bounds.y
@@ -242,10 +359,17 @@ var WorkflowPortEntity = class extends Entity {
242
359
  }
243
360
  /**
244
361
  * 当前点位上连接的线条
362
+ * @deprecated use `availableLines` instead
245
363
  */
246
364
  get lines() {
247
365
  return this.allLines.filter((line) => !line.isDrawing);
248
366
  }
367
+ /**
368
+ * 当前有效的线条,不包含正在画的线条和隐藏的线条(这个出现在线条重连会先把原来的线条隐藏)
369
+ */
370
+ get availableLines() {
371
+ return this.allLines.filter((line) => !line.isDrawing && !line.isHidden);
372
+ }
249
373
  /**
250
374
  * 当前点位上连接的线条(包含 isDrawing === true 的线条)
251
375
  */
@@ -261,6 +385,36 @@ var WorkflowPortEntity = class extends Entity {
261
385
  });
262
386
  return lines;
263
387
  }
388
+ update(data) {
389
+ let changed = false;
390
+ if (data.targetElement !== this.targetElement) {
391
+ this.targetElement = data.targetElement;
392
+ changed = true;
393
+ }
394
+ if (data.location !== this._location) {
395
+ this._location = data.location;
396
+ changed = true;
397
+ }
398
+ if (Compare.isChanged(data.offset, this._offset)) {
399
+ this._offset = data.offset;
400
+ changed = true;
401
+ }
402
+ if (Compare.isChanged(data.locationConfig, this._locationConfig)) {
403
+ this._locationConfig = data.locationConfig;
404
+ changed = true;
405
+ }
406
+ if (Compare.isChanged(data.size, this._size)) {
407
+ this._size = data.size;
408
+ changed = true;
409
+ }
410
+ if (data.disabled !== this._disabled) {
411
+ this._disabled = data.disabled;
412
+ changed = true;
413
+ }
414
+ if (changed) {
415
+ this.fireChange();
416
+ }
417
+ }
264
418
  dispose() {
265
419
  this.lines.forEach((l) => l.dispose());
266
420
  super.dispose();
@@ -299,17 +453,26 @@ var WorkflowNodePortsData = class extends EntityData {
299
453
  return {};
300
454
  }
301
455
  /**
302
- * 更新静态的 ports 数据
456
+ * Update all ports data, includes static ports and dynamic ports
457
+ * @param ports
303
458
  */
304
- updateStaticPorts(ports) {
459
+ updateAllPorts(ports) {
305
460
  const meta = this.entity.getNodeMeta();
306
- this._staticPorts = ports;
461
+ if (ports) {
462
+ this._staticPorts = ports;
463
+ }
307
464
  if (meta.useDynamicPort) {
308
465
  this.updateDynamicPorts();
309
466
  } else {
310
467
  this.updatePorts(this._staticPorts);
311
468
  }
312
469
  }
470
+ /**
471
+ * @deprecated use `updateAllPorts` instead
472
+ */
473
+ updateStaticPorts(ports) {
474
+ this.updateAllPorts(ports);
475
+ }
313
476
  /**
314
477
  * 动态计算点位,通过 dom 的 data-port-key
315
478
  */
@@ -323,6 +486,7 @@ var WorkflowNodePortsData = class extends EntityData {
323
486
  ...Array.from(elements).map((element) => ({
324
487
  portID: element.getAttribute("data-port-id"),
325
488
  type: element.getAttribute("data-port-type"),
489
+ location: element.getAttribute("data-port-location"),
326
490
  targetElement: element
327
491
  }))
328
492
  );
@@ -358,7 +522,6 @@ var WorkflowNodePortsData = class extends EntityData {
358
522
  port.allLines.forEach((line) => {
359
523
  line.validate();
360
524
  });
361
- port.validate();
362
525
  });
363
526
  }
364
527
  /**
@@ -449,9 +612,7 @@ var WorkflowNodePortsData = class extends EntityData {
449
612
  */
450
613
  updatePortEntity(portInfo) {
451
614
  const portEntity = this.getOrCreatePortEntity(portInfo);
452
- if (portInfo.targetElement) {
453
- portEntity.updateTargetElement(portInfo.targetElement);
454
- }
615
+ portEntity.update(portInfo);
455
616
  return portEntity;
456
617
  }
457
618
  };
@@ -470,7 +631,7 @@ var _WorkflowNodeLinesData = class _WorkflowNodeLinesData extends EntityData2 {
470
631
  constructor(entity) {
471
632
  super(entity);
472
633
  this.entity = entity;
473
- this.toDispose.push(
634
+ this.entity.preDispose.push(
474
635
  Disposable.create(() => {
475
636
  this.inputLines.slice().forEach((line) => line.dispose());
476
637
  this.outputLines.slice().forEach((line) => line.dispose());
@@ -489,6 +650,12 @@ var _WorkflowNodeLinesData = class _WorkflowNodeLinesData extends EntityData2 {
489
650
  get outputLines() {
490
651
  return this.data.outputLines;
491
652
  }
653
+ get allLines() {
654
+ return this.data.inputLines.concat(this.data.outputLines);
655
+ }
656
+ get availableLines() {
657
+ return this.allLines.filter((line) => !line.isDrawing && !line.isHidden);
658
+ }
492
659
  /**
493
660
  * 输入节点
494
661
  */
@@ -589,8 +756,8 @@ var WorkflowLineRenderData = class extends EntityData3 {
589
756
  version: "",
590
757
  contributions: /* @__PURE__ */ new Map(),
591
758
  position: {
592
- from: { x: 0, y: 0 },
593
- to: { x: 0, y: 0 }
759
+ from: { x: 0, y: 0, location: "right" },
760
+ to: { x: 0, y: 0, location: "left" }
594
761
  }
595
762
  };
596
763
  }
@@ -630,22 +797,27 @@ var WorkflowLineRenderData = class extends EntityData3 {
630
797
  get lineType() {
631
798
  return this.entity.renderType ?? this.entity.linesManager.lineType;
632
799
  }
800
+ /**
801
+ * 获取 center 位置
802
+ */
803
+ get center() {
804
+ return this.currentLine?.center || { x: 0, y: 0, labelX: 0, labelY: 0 };
805
+ }
633
806
  /**
634
807
  * 更新版本
635
808
  * WARNING: 这个方法,必须在 requestAnimationFrame / useLayoutEffect 中调用,否则会引起浏览器强制重排
636
809
  */
637
810
  updatePosition() {
638
- this.data.position.from = this.entity.from.getData(WorkflowNodePortsData).getOutputPoint(this.entity.info.fromPort);
639
- this.data.position.to = this.entity.info.drawingTo ?? this.entity.to?.getData(WorkflowNodePortsData)?.getInputPoint(this.entity.info.toPort) ?? {
640
- x: this.data.position.from.x,
641
- y: this.data.position.from.y
642
- };
811
+ this.data.position.from = this.entity.drawingFrom || this.entity.fromPort.point;
812
+ this.data.position.to = this.entity.drawingTo || this.entity.toPort.point;
643
813
  this.data.version = [
644
814
  this.lineType,
645
815
  this.data.position.from.x,
646
816
  this.data.position.from.y,
817
+ this.data.position.from.location,
647
818
  this.data.position.to.x,
648
- this.data.position.to.y
819
+ this.data.position.to.y,
820
+ this.data.position.to.location
649
821
  ].join("-");
650
822
  }
651
823
  get currentLine() {
@@ -675,8 +847,20 @@ var POINT_RADIUS = 10;
675
847
  var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
676
848
  constructor(opts) {
677
849
  super(opts);
678
- this._processing = false;
679
- this._hasError = false;
850
+ this._onLineDataChangeEmitter = new Emitter2();
851
+ this.onLineDataChange = this._onLineDataChangeEmitter.event;
852
+ this._uiState = {
853
+ hasError: false,
854
+ flowing: false,
855
+ disabled: false,
856
+ hideArrow: false,
857
+ reverse: false,
858
+ shrink: 10,
859
+ curvature: 0.25,
860
+ highlightColor: "",
861
+ lockedColor: ""
862
+ };
863
+ this.stackIndex = 0;
680
864
  /**
681
865
  * 线条数据
682
866
  */
@@ -690,11 +874,22 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
690
874
  to: opts.to,
691
875
  drawingTo: opts.drawingTo,
692
876
  fromPort: opts.fromPort,
693
- toPort: opts.toPort
877
+ drawingFrom: opts.drawingFrom,
878
+ toPort: opts.toPort,
879
+ data: opts.data
694
880
  });
695
- if (opts.drawingTo) {
881
+ if (opts.drawingTo || opts.drawingFrom) {
696
882
  this.isDrawing = true;
697
883
  }
884
+ this.onEntityChange(() => {
885
+ this.fromPort?.validate();
886
+ this.toPort?.validate();
887
+ });
888
+ this.onDispose(() => {
889
+ this.fromPort?.validate();
890
+ this.toPort?.validate();
891
+ });
892
+ this.toDispose.push(this._onLineDataChangeEmitter);
698
893
  }
699
894
  /**
700
895
  * 转成线条 id
@@ -704,6 +899,47 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
704
899
  const { from, to, fromPort, toPort } = info;
705
900
  return `${from}_${fromPort || ""}-${to || ""}_${toPort || ""}`;
706
901
  }
902
+ /**
903
+ * 线条的 UI 状态
904
+ */
905
+ get uiState() {
906
+ return this._uiState;
907
+ }
908
+ /**
909
+ * 更新线条的 ui 状态
910
+ * @param newState
911
+ */
912
+ updateUIState(newState) {
913
+ let changed = false;
914
+ Object.keys(newState).forEach((key) => {
915
+ const value = newState[key];
916
+ if (this._uiState[key] !== value) {
917
+ this._uiState[key] = value;
918
+ changed = true;
919
+ }
920
+ });
921
+ if (changed) {
922
+ this.fireChange();
923
+ }
924
+ }
925
+ /**
926
+ * 线条的扩展数据
927
+ */
928
+ get lineData() {
929
+ return this._lineData;
930
+ }
931
+ /**
932
+ * 更新线条扩展数据
933
+ * @param data
934
+ */
935
+ set lineData(newValue) {
936
+ const oldValue = this._lineData;
937
+ if (!isEqual2(oldValue, newValue)) {
938
+ this._lineData = newValue;
939
+ this._onLineDataChangeEmitter.fire({ oldValue, newValue });
940
+ this.fireChange();
941
+ }
942
+ }
707
943
  /**
708
944
  * 获取线条的前置节点
709
945
  */
@@ -725,29 +961,27 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
725
961
  }
726
962
  /**
727
963
  * 获取是否 testrun processing
964
+ * @deprecated use `flowing` instead
728
965
  */
729
966
  get processing() {
730
- return this._processing;
967
+ return this._uiState.flowing;
731
968
  }
732
969
  /**
733
970
  * 设置 testrun processing 状态
971
+ * @deprecated use `flowing` instead
734
972
  */
735
973
  set processing(status) {
736
- if (this._processing !== status) {
737
- this._processing = status;
738
- this.fireChange();
739
- }
974
+ this.flowing = status;
740
975
  }
741
976
  // 获取连线是否为错误态
742
977
  get hasError() {
743
- return this._hasError;
978
+ return this.uiState.hasError;
744
979
  }
745
980
  // 设置连线的错误态
746
981
  set hasError(hasError) {
747
- if (this._hasError !== hasError) {
748
- this._hasError = hasError;
749
- this.fireChange();
750
- }
982
+ this.updateUIState({
983
+ hasError
984
+ });
751
985
  if (this._node) {
752
986
  this._node.dataset.hasError = this.hasError ? "true" : "false";
753
987
  }
@@ -762,11 +996,11 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
762
996
  if (this.toPort === toPort) {
763
997
  return;
764
998
  }
999
+ const prePort = this.toPort;
765
1000
  if (toPort && toPort.portType === "input" && this.linesManager.canAddLine(this.fromPort, toPort, true)) {
766
1001
  const { node, portID } = toPort;
767
1002
  this._to = node;
768
1003
  this.info.drawingTo = void 0;
769
- this.info.isDefaultLine = false;
770
1004
  this.info.to = node.id;
771
1005
  this.info.toPort = portID;
772
1006
  } else {
@@ -774,6 +1008,33 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
774
1008
  this.info.to = void 0;
775
1009
  this.info.toPort = "";
776
1010
  }
1011
+ if (prePort) {
1012
+ prePort.validate();
1013
+ }
1014
+ this.fireChange();
1015
+ }
1016
+ setFromPort(fromPort) {
1017
+ if (!this.isDrawing) {
1018
+ throw new Error("[setFromPort] only support drawing line.");
1019
+ }
1020
+ if (this.fromPort === fromPort) {
1021
+ return;
1022
+ }
1023
+ const prePort = this.fromPort;
1024
+ if (fromPort && fromPort.portType === "output" && this.linesManager.canAddLine(fromPort, this.toPort, true)) {
1025
+ const { node, portID } = fromPort;
1026
+ this._from = node;
1027
+ this.info.drawingFrom = void 0;
1028
+ this.info.from = node.id;
1029
+ this.info.fromPort = portID;
1030
+ } else {
1031
+ this._from = void 0;
1032
+ this.info.from = void 0;
1033
+ this.info.fromPort = "";
1034
+ }
1035
+ if (prePort) {
1036
+ prePort.validate();
1037
+ }
777
1038
  this.fireChange();
778
1039
  }
779
1040
  /**
@@ -788,11 +1049,26 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
788
1049
  }
789
1050
  if (!oldDrawingTo || pos.x !== oldDrawingTo.x || pos.y !== oldDrawingTo.y) {
790
1051
  this.info.to = void 0;
791
- this.info.isDefaultLine = false;
792
1052
  this.info.drawingTo = pos;
793
1053
  this.fireChange();
794
1054
  }
795
1055
  }
1056
+ set drawingFrom(pos) {
1057
+ const oldDrawingFrom = this.info.drawingFrom;
1058
+ if (!pos) {
1059
+ this.info.drawingFrom = void 0;
1060
+ this.fireChange();
1061
+ return;
1062
+ }
1063
+ if (!oldDrawingFrom || pos.x !== oldDrawingFrom.x || pos.y !== oldDrawingFrom.y) {
1064
+ this.info.from = void 0;
1065
+ this.info.drawingFrom = pos;
1066
+ this.fireChange();
1067
+ }
1068
+ }
1069
+ get drawingFrom() {
1070
+ return this.info.drawingFrom;
1071
+ }
796
1072
  /**
797
1073
  * 获取线条正在画线的位置
798
1074
  */
@@ -800,13 +1076,20 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
800
1076
  return this.info.drawingTo;
801
1077
  }
802
1078
  get highlightColor() {
803
- return this.info.highlightColor || "";
1079
+ return this.uiState.highlightColor || "";
804
1080
  }
805
- set highlightColor(color) {
806
- if (this.info.highlightColor !== color) {
807
- this.info.highlightColor = color;
808
- this.fireChange();
809
- }
1081
+ set highlightColor(highlightColor) {
1082
+ this.updateUIState({
1083
+ highlightColor
1084
+ });
1085
+ }
1086
+ get lockedColor() {
1087
+ return this.uiState.lockedColor;
1088
+ }
1089
+ set lockedColor(lockedColor) {
1090
+ this.updateUIState({
1091
+ lockedColor
1092
+ });
810
1093
  }
811
1094
  /**
812
1095
  * 获取线条的边框位置大小
@@ -814,6 +1097,9 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
814
1097
  get bounds() {
815
1098
  return this.getData(WorkflowLineRenderData).bounds;
816
1099
  }
1100
+ get center() {
1101
+ return this.getData(WorkflowLineRenderData).center;
1102
+ }
817
1103
  /**
818
1104
  * 获取点和线最接近的距离
819
1105
  */
@@ -821,13 +1107,16 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
821
1107
  return this.getData(WorkflowLineRenderData).calcDistance(pos);
822
1108
  }
823
1109
  get fromPort() {
824
- return this.from.getData(WorkflowNodePortsData).getPortEntityByKey("output", this.info.fromPort);
1110
+ if (!this.from) {
1111
+ return void 0;
1112
+ }
1113
+ return this.from.ports.getPortEntityByKey("output", this.info.fromPort);
825
1114
  }
826
1115
  get toPort() {
827
1116
  if (!this.to) {
828
1117
  return void 0;
829
1118
  }
830
- return this.to.getData(WorkflowNodePortsData).getPortEntityByKey("input", this.info.toPort);
1119
+ return this.to.ports.getPortEntityByKey("input", this.info.toPort);
831
1120
  }
832
1121
  /**
833
1122
  * 获取线条真实的输入输出节点坐标
@@ -837,23 +1126,37 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
837
1126
  }
838
1127
  /** 是否反转箭头 */
839
1128
  get reverse() {
840
- return this.linesManager.isReverseLine(this);
1129
+ return this.linesManager.isReverseLine(this, this.uiState.reverse);
841
1130
  }
842
1131
  /** 是否隐藏箭头 */
843
1132
  get hideArrow() {
844
- return this.linesManager.isHideArrowLine(this);
1133
+ return this.linesManager.isHideArrowLine(this, this.uiState.hideArrow);
845
1134
  }
846
1135
  /** 是否流动 */
847
1136
  get flowing() {
848
- return this.linesManager.isFlowingLine(this);
1137
+ return this.linesManager.isFlowingLine(this, this.uiState.flowing);
1138
+ }
1139
+ set flowing(flowing) {
1140
+ if (this._uiState.flowing !== flowing) {
1141
+ this._uiState.flowing = flowing;
1142
+ this.fireChange();
1143
+ }
849
1144
  }
850
1145
  /** 是否禁用 */
851
1146
  get disabled() {
852
- return this.linesManager.isDisabledLine(this);
1147
+ return this.linesManager.isDisabledLine(this, this.uiState.disabled);
853
1148
  }
854
- /** 是否竖向 */
1149
+ /**
1150
+ * @deprecated
1151
+ */
855
1152
  get vertical() {
856
- return this.linesManager.isVerticalLine(this);
1153
+ const fromLocation = this.fromPort?.location;
1154
+ const toLocation = this.toPort?.location;
1155
+ if (toLocation) {
1156
+ return toLocation === "top";
1157
+ } else {
1158
+ return fromLocation === "bottom";
1159
+ }
857
1160
  }
858
1161
  /** 获取线条渲染器类型 */
859
1162
  get renderType() {
@@ -861,7 +1164,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
861
1164
  }
862
1165
  /** 获取线条样式 */
863
1166
  get className() {
864
- return this.linesManager.setLineClassName(this) ?? "";
1167
+ return [this.linesManager.setLineClassName(this), this._uiState.className].filter((s) => !!s).join(" ");
865
1168
  }
866
1169
  get color() {
867
1170
  return this.linesManager.getLineColor(this);
@@ -873,22 +1176,24 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
873
1176
  initInfo(info) {
874
1177
  if (!isEqual2(info, this.info)) {
875
1178
  this.info = info;
876
- this._from = this.document.getNode(info.from);
1179
+ this._from = info.from ? this.document.getNode(info.from) : void 0;
877
1180
  this._to = info.to ? this.document.getNode(info.to) : void 0;
1181
+ this._lineData = info.data;
878
1182
  this.fireChange();
879
1183
  }
880
1184
  }
881
1185
  // 校验连线是否为错误态
882
1186
  validate() {
883
- const { fromPort, toPort } = this;
884
1187
  this.validateSelf();
885
- fromPort?.validate();
886
- toPort?.validate();
887
1188
  }
1189
+ /**
1190
+ * use `validate` instead
1191
+ * @deprecated
1192
+ */
888
1193
  validateSelf() {
889
1194
  const { fromPort, toPort } = this;
890
1195
  if (fromPort) {
891
- this.hasError = this.linesManager.isErrorLine(fromPort, toPort);
1196
+ this.hasError = this.linesManager.isErrorLine(fromPort, toPort, this.uiState.hasError);
892
1197
  }
893
1198
  }
894
1199
  is(line) {
@@ -905,7 +1210,7 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
905
1210
  this._node = domUtils.createDivWithClass("gedit-flow-activity-line");
906
1211
  this._node.dataset.testid = "sdk.workflow.canvas.line";
907
1212
  this._node.dataset.lineId = this.id;
908
- this._node.dataset.fromNodeId = this.from.id;
1213
+ this._node.dataset.fromNodeId = this.from?.id ?? "";
909
1214
  this._node.dataset.fromPortId = this.fromPort?.id ?? "";
910
1215
  this._node.dataset.toNodeId = this.to?.id ?? "";
911
1216
  this._node.dataset.toPortId = this.toPort?.id ?? "";
@@ -919,6 +1224,9 @@ var _WorkflowLineEntity = class _WorkflowLineEntity extends Entity2 {
919
1224
  sourcePortID: this.info.fromPort,
920
1225
  targetPortID: this.info.toPort
921
1226
  };
1227
+ if (this._lineData !== void 0) {
1228
+ json.data = this._lineData;
1229
+ }
922
1230
  if (!json.sourcePortID) {
923
1231
  delete json.sourcePortID;
924
1232
  }
@@ -1031,12 +1339,14 @@ WorkflowSelectService = __decorateClass([
1031
1339
 
1032
1340
  // src/service/workflow-hover-service.ts
1033
1341
  import { inject as inject2, injectable as injectable2 } from "inversify";
1342
+ import { Emitter as Emitter3 } from "@flowgram.ai/utils";
1034
1343
  import { EntityManager } from "@flowgram.ai/core";
1035
- import { Emitter as Emitter2 } from "@flowgram.ai/utils";
1036
1344
  var WorkflowHoverService = class {
1037
1345
  constructor() {
1038
- this.onHoveredChangeEmitter = new Emitter2();
1346
+ this.onHoveredChangeEmitter = new Emitter3();
1347
+ this.onUpdateHoverPositionEmitter = new Emitter3();
1039
1348
  this.onHoveredChange = this.onHoveredChangeEmitter.event;
1349
+ this.onUpdateHoverPosition = this.onUpdateHoverPositionEmitter.event;
1040
1350
  // 当前鼠标 hover 位置
1041
1351
  this.hoveredPos = { x: 0, y: 0 };
1042
1352
  /**
@@ -1056,6 +1366,13 @@ var WorkflowHoverService = class {
1056
1366
  this.onHoveredChangeEmitter.fire(hoveredKey);
1057
1367
  }
1058
1368
  }
1369
+ updateHoverPosition(position, target) {
1370
+ this.hoveredPos = position;
1371
+ this.onUpdateHoverPositionEmitter.fire({
1372
+ position,
1373
+ target
1374
+ });
1375
+ }
1059
1376
  /**
1060
1377
  * 清空 hover 内容
1061
1378
  */
@@ -1075,10 +1392,17 @@ var WorkflowHoverService = class {
1075
1392
  }
1076
1393
  /**
1077
1394
  * 获取被 hover 的节点或线条
1395
+ * @deprecated use 'someHovered' instead
1078
1396
  */
1079
1397
  get hoveredNode() {
1080
1398
  return this.entityManager.getEntityById(this.hoveredKey);
1081
1399
  }
1400
+ /**
1401
+ * 获取被 hover 的节点或线条
1402
+ */
1403
+ get someHovered() {
1404
+ return this.entityManager.getEntityById(this.hoveredKey);
1405
+ }
1082
1406
  };
1083
1407
  __decorateClass([
1084
1408
  inject2(EntityManager)
@@ -1093,33 +1417,32 @@ import { inject as inject6, injectable as injectable6, postConstruct as postCons
1093
1417
  import {
1094
1418
  domUtils as domUtils2,
1095
1419
  PromiseDeferred,
1096
- Emitter as Emitter5,
1420
+ Emitter as Emitter6,
1097
1421
  DisposableCollection as DisposableCollection2,
1098
1422
  Rectangle as Rectangle8,
1099
- delay as delay2
1423
+ delay as delay2,
1424
+ Point
1100
1425
  } from "@flowgram.ai/utils";
1101
- import {
1102
- FlowNodeTransformData as FlowNodeTransformData5,
1103
- FlowOperationBaseService
1104
- } from "@flowgram.ai/document";
1105
- import { FlowNodeBaseType as FlowNodeBaseType2 } from "@flowgram.ai/document";
1426
+ import { FlowNodeTransformData as FlowNodeTransformData6 } from "@flowgram.ai/document";
1427
+ import { FlowNodeBaseType as FlowNodeBaseType3 } from "@flowgram.ai/document";
1106
1428
  import {
1107
1429
  CommandService,
1430
+ MouseTouchEvent,
1108
1431
  PlaygroundConfigEntity as PlaygroundConfigEntity5,
1109
1432
  PlaygroundDrag,
1110
- TransformData as TransformData9
1433
+ TransformData as TransformData8
1111
1434
  } from "@flowgram.ai/core";
1112
1435
 
1113
1436
  // src/workflow-lines-manager.ts
1114
1437
  import { last } from "lodash-es";
1115
1438
  import { inject as inject3, injectable as injectable3 } from "inversify";
1116
- import { Disposable as Disposable2, DisposableCollection, Emitter as Emitter3 } from "@flowgram.ai/utils";
1117
- import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
1118
- import { EntityManager as EntityManager2, PlaygroundConfigEntity as PlaygroundConfigEntity2, TransformData as TransformData6 } from "@flowgram.ai/core";
1439
+ import { DisposableCollection, Emitter as Emitter4 } from "@flowgram.ai/utils";
1440
+ import { FlowNodeRenderData as FlowNodeRenderData2, FlowNodeTransformData as FlowNodeTransformData3 } from "@flowgram.ai/document";
1441
+ import { EntityManager as EntityManager2, PlaygroundConfigEntity as PlaygroundConfigEntity2 } from "@flowgram.ai/core";
1119
1442
 
1120
1443
  // src/workflow-document-option.ts
1121
1444
  import { FlowNodeErrorData } from "@flowgram.ai/form-core";
1122
- import { FlowNodeTransformData } from "@flowgram.ai/document";
1445
+ import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
1123
1446
  import { TransformData as TransformData5 } from "@flowgram.ai/core";
1124
1447
 
1125
1448
  // src/utils/flow-node-form-data.ts
@@ -1132,31 +1455,36 @@ function toFormJSON(node) {
1132
1455
  if (!formData || !node.getNodeRegistry().formMeta) return void 0;
1133
1456
  return formData.toJSON();
1134
1457
  }
1135
- function initFormDataFromJSON(node, json) {
1458
+ function initFormDataFromJSON(node, json, isFirstCreate) {
1136
1459
  const formData = node.getData(FlowNodeFormData);
1137
1460
  const registry = node.getNodeRegistry();
1138
1461
  const { formMeta } = registry;
1139
1462
  if (formData && formMeta) {
1140
- formData.createForm(formMeta, json.data);
1141
- formData.onDataChange(() => {
1142
- node.document.fireContentChange({
1143
- type: "NODE_DATA_CHANGE" /* NODE_DATA_CHANGE */,
1144
- toJSON: () => formData.toJSON(),
1145
- entity: node
1463
+ if (isFirstCreate) {
1464
+ formData.createForm(formMeta, json.data);
1465
+ formData.onDataChange(() => {
1466
+ node.document.fireContentChange({
1467
+ type: "NODE_DATA_CHANGE" /* NODE_DATA_CHANGE */,
1468
+ toJSON: () => formData.toJSON(),
1469
+ entity: node
1470
+ });
1146
1471
  });
1147
- });
1472
+ } else {
1473
+ formData.updateFormValues(json.data);
1474
+ }
1148
1475
  }
1149
1476
  }
1150
1477
 
1151
1478
  // src/workflow-document-option.ts
1152
1479
  var WorkflowDocumentOptions = Symbol("WorkflowDocumentOptions");
1153
1480
  var WorkflowDocumentOptionsDefault = {
1154
- cursors: {
1155
- grab: 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMCAyMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC40ODczIDIuNjIzNzhDOS45MDczMSAyLjYyMzc4IDkuNDM3MTMgMy4wOTM5NiA5LjQzNzEzIDMuNjczOTZWNS4xNDM3NkM5LjM5NDI4IDQuNDAyNzQgOC43Nzk3OCAzLjgxNTA0IDguMDI4MDIgMy44MTUwNEM3LjI0ODQ4IDMuODE1MDQgNi42MTY1MyA0LjQ0Njk5IDYuNjE2NTMgNS4yMjY1M1YxMS44Mjg5TDUuNjc0MTggMTEuMDA0OUM1LjE1NDg3IDEwLjU1MDkgNC40MDk1IDEwLjQ2MzYgMy43OTkzOCAxMC43ODU1TDMuNjk2OTQgMTAuODM5NkMzLjA2MjE3IDExLjE3NDUgMi45MjI2IDEyLjAyMjggMy40MTY2MiAxMi41NDM0TDcuMzM5NTkgMTYuNjc3NVYxNy4zMjU5QzcuMzM5NTkgMTcuNzg2MiA3LjcxMjY5IDE4LjE1OTMgOC4xNzI5MiAxOC4xNTkzSDEzLjgwODRDMTQuMjY4NyAxOC4xNTkzIDE0LjY0MTcgMTcuNzg2MiAxNC42NDE3IDE3LjMyNTlWMTYuNzkzNUMxNS44MDk0IDE1LjY0ODUgMTYuNDY3MyAxNC4wODE5IDE2LjQ2NzMgMTIuNDQ2NVYxMS40OTY3TDE2LjQ2NzEgNi42MzY4NUMxNi40NjcxIDUuOTU2MyAxNS45MTU0IDUuNDA0NjEgMTUuMjM0OCA1LjQwNDYxQzE0LjU1NDMgNS40MDQ2MSAxNC4wMDI2IDUuOTU2MyAxNC4wMDI2IDYuNjM2ODVMMTQuMDAyMSA1LjA0NzI4QzE0LjAwMjEgNC4zNjY3MyAxMy40NTA0IDMuODE1MDQgMTIuNzY5OCAzLjgxNTA0QzEyLjA4OTMgMy44MTUwNCAxMS41Mzc2IDQuMzY2NzMgMTEuNTM3NiA1LjA0NzI4TDExLjUzNzUgMy42NzM5NUMxMS41Mzc1IDMuMDkzOTYgMTEuMDY3MyAyLjYyMzc4IDEwLjQ4NzMgMi42MjM3OFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNDg3NCAxLjM3NDAyQzExLjM2MTIgMS4zNzQwMiAxMi4xMjExIDEuODYxMTggMTIuNTEwNSAyLjU3ODY4QzEyLjU5NTggMi41Njk4MyAxMi42ODIzIDIuNTY1MjggMTIuNzcgMi41NjUyOEMxMy44Mjc4IDIuNTY1MjggMTQuNzMxMSAzLjIyNzAxIDE1LjA4ODUgNC4xNTkxMUMxNS4xMzcgNC4xNTYyOSAxNS4xODU4IDQuMTU0ODYgMTUuMjM1IDQuMTU0ODZDMTYuNjA1OSA0LjE1NDg2IDE3LjcxNzIgNS4yNjYxOSAxNy43MTcyIDYuNjM3MDlMMTcuNzE3NCAxMi40NDY3QzE3LjcxNzQgMTQuMjM1NSAxNy4wNjQ0IDE1Ljk1NTkgMTUuODkxOSAxNy4yOTA0VjE3LjMyNjJDMTUuODkxOSAxOC40NzY4IDE0Ljk1OTEgMTkuNDA5NSAxMy44MDg1IDE5LjQwOTVIOC4xNzMwNkM3LjAyMjQ3IDE5LjQwOTUgNi4wODk3MyAxOC40NzY4IDYuMDg5NzMgMTcuMzI2MlYxNy4xNzY0TDIuNTEwMDMgMTMuNDA0MUMxLjQ0NTk5IDEyLjI4MjggMS43NDY2IDEwLjQ1NTUgMy4xMTM3OSA5LjczNDI0TDMuMjE2MjQgOS42ODAxOUMzLjg5MTY4IDkuMzIzODMgNC42NjE4NSA5LjI1NDAxIDUuMzY2NjYgOS40NTE5OFY1LjIyNjc4QzUuMzY2NjYgMy43NTY4NyA2LjU1ODI2IDIuNTY1MjggOC4wMjgxNiAyLjU2NTI4QzguMTcyOTMgMi41NjUyOCA4LjMxNDk5IDIuNTc2ODQgOC40NTM0NyAyLjU5OTA3QzguODM5NDMgMS44NzA0MiA5LjYwNTQ2IDEuMzc0MDIgMTAuNDg3NCAxLjM3NDAyWk0xMi40NDc2IDMuODU3ODdWOS40NzY0NkMxMi40NDc2IDkuNzI4NTIgMTIuMjQzMyA5LjkzMjg1IDExLjk5MTMgOS45MzI4NUMxMS43MzkyIDkuOTMyODUgMTEuNTM0OSA5LjcyODUyIDExLjUzNDkgOS40NzY0NlYzLjc5NjU1QzExLjUzNDkgMy43Nzk1NiAxMS41MzU4IDMuNzYyNzcgMTEuNTM3NiAzLjc0NjI2VjMuNjc0MkMxMS41Mzc2IDMuNDMyODIgMTEuNDU2MiAzLjIxMDQ2IDExLjMxOTMgMy4wMzMwOUMxMS4xMjcyIDIuNzg0MjggMTAuODI2MSAyLjYyNDAyIDEwLjQ4NzQgMi42MjQwMkMxMC4xMjM4IDIuNjI0MDIgOS44MDMzMiAyLjgwODg2IDkuNjE0ODMgMy4wODk3QzkuNTAyNjkgMy4yNTY3OSA5LjQzNzI2IDMuNDU3ODUgOS40MzcyNiAzLjY3NDJWMy43ODU3M0M5LjQzNzM1IDMuNzg5MzMgOS40MzczOSAzLjc5Mjk0IDkuNDM3MzkgMy43OTY1NVY5LjkwMTdDOS40MzczOSAxMC4xNTM3IDkuMjMzMDYgMTAuMzU4MSA4Ljk4MTAxIDEwLjM1ODFDOC43Mjg5NSAxMC4zNTgxIDguNTI0NjIgMTAuMTUzNyA4LjUyNDYyIDkuOTAxN1YzLjkwNTA3QzguNDE3NzMgMy44NjQ5IDguMzA0NjggMy44MzczMiA4LjE4NzI2IDMuODI0MTVDOC4xMzUwNCAzLjgxODI5IDguMDgxOTUgMy44MTUyOCA4LjAyODE2IDMuODE1MjhDNy4yNDg2MSAzLjgxNTI4IDYuNjE2NjYgNC40NDcyMyA2LjYxNjY2IDUuMjI2NzhWMTEuODI5Mkw1LjY3NDMxIDExLjAwNTJDNS41Nzg2OCAxMC45MjE2IDUuNDc1MzcgMTAuODUwNCA1LjM2NjY2IDEwLjc5MTlDNC44ODUwNiAxMC41MzI5IDQuMjk3MjggMTAuNTIzMSAzLjc5OTUyIDEwLjc4NThMMy42OTcwNyAxMC44Mzk4QzMuMDYyMzEgMTEuMTc0NyAyLjkyMjczIDEyLjAyMzEgMy40MTY3NSAxMi41NDM3TDcuMzM5NzMgMTYuNjc3N1YxNy4zMjYyQzcuMzM5NzMgMTcuNzg2NCA3LjcxMjgyIDE4LjE1OTUgOC4xNzMwNiAxOC4xNTk1SDEzLjgwODVDMTQuMjY4OCAxOC4xNTk1IDE0LjY0MTkgMTcuNzg2NCAxNC42NDE5IDE3LjMyNjJWMTYuNzkzOEMxNS43Mzc5IDE1LjcxOSAxNi4zODQ3IDE0LjI3MjggMTYuNDYgMTIuNzQ3QzE2LjQ2NDEgMTIuNjY0MSAxNi40NjY1IDEyLjU4MDkgMTYuNDY3MiAxMi40OTc1TDE2LjQ2NzQgMTIuNDQ2N0wxNi40NjcyIDYuNjM3MDlDMTYuNDY3MiA1Ljk2MjMgMTUuOTI0OCA1LjQxNDE5IDE1LjI1MjIgNS40MDQ5N0wxNS4yMzUgNS40MDQ4NkMxNS4xMjQ2IDUuNDA0ODYgMTUuMDE3NyA1LjQxOTM2IDE0LjkxNTkgNS40NDY1NlY5LjYwMjI2QzE0LjkxNTkgOS44NTQzMSAxNC43MTE2IDEwLjA1ODYgMTQuNDU5NSAxMC4wNTg2QzE0LjIwNzUgMTAuMDU4NiAxNC4wMDMxIDkuODU0MzEgMTQuMDAzMSA5LjYwMjI2VjYuNjA1MTRDMTQuMDAyOSA2LjYxNTc2IDE0LjAwMjcgNi42MjY0MSAxNC4wMDI3IDYuNjM3MDlWOS4yNzcwNUwxNC4wMDIyIDUuMDQ3NTJDMTQuMDAyMiA0Ljg2OTEzIDEzLjk2NDMgNC42OTk2IDEzLjg5NjEgNC41NDY1M0MxMy43MDY0IDQuMTIwNzIgMTMuMjgyMiAzLjgyMjM1IDEyLjc4NzYgMy44MTU0MUwxMi43NyAzLjgxNTI4QzEyLjY1ODQgMy44MTUyOCAxMi41NTA0IDMuODMwMSAxMi40NDc2IDMuODU3ODdaIiBmaWxsPSIjMUQxQzIzIi8+Cjwvc3ZnPg=="), auto',
1156
- grabbing: 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMCAyMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02LjYxODE3IDUuNTk4NzVDNi42MTgxNyA0LjgxOTIgNy4yNTAxMiA0LjE4NzI2IDguMDI5NjcgNC4xODcyNkM4Ljc3ODczIDQuMTg3MjYgOS4zOTE1MiA0Ljc3MDc1IDkuNDM4MjkgNS41MDgwMUM5LjQ1OTkyIDQuOTQ3MSA5LjkyMTQ3IDQuNDk5MDIgMTAuNDg3NyA0LjQ5OTAyQzExLjA2NzcgNC40OTkwMiAxMS41Mzc4IDQuOTY5MiAxMS41Mzc4IDUuNTQ5MTlWOC43NjI0NkwxMS41Mzc5IDYuNzExNUMxMS41Mzc5IDYuMDMwOTUgMTIuMDg5NiA1LjQ3OTI2IDEyLjc3MDIgNS40NzkyNkMxMy40NTA3IDUuNDc5MjYgMTQuMDAyNCA2LjAzMDk1IDE0LjAwMjQgNi43MTE1TDE0LjAwMjQgOC43NjI0NkwxNC4wMDI5IDguMDE5ODNDMTQuMDAyOSA3LjMzOTI5IDE0LjU1NDYgNi43ODc1OSAxNS4yMzUyIDYuNzg3NTlDMTUuOTE1NyA2Ljc4NzU5IDE2LjQ2NzQgNy4zMzkyOCAxNi40Njc0IDguMDE5ODNWMTEuNDk3TDE2LjQ2NzUgMTIuNDQ2N0MxNi40Njc1IDE0LjA4MjEgMTUuODA5NiAxNS42NDg3IDE0LjY0MiAxNi43OTM4VjE3LjMyNjJDMTQuNjQyIDE3Ljc4NjQgMTQuMjY4OSAxOC4xNTk1IDEzLjgwODcgMTguMTU5NUg4LjE3MzE3QzcuNzEyOTMgMTguMTU5NSA3LjMzOTg0IDE3Ljc4NjQgNy4zMzk4NCAxNy4zMjYyVjE1Ljk0MjRMNS4zNDU2MiAxNC43NTM0QzQuNTg5MjQgMTQuMzAyNCA0LjEyNTkxIDEzLjQ4NjcgNC4xMjU4OSAxMi42MDYxTDQuMTI1ODMgOS4yODM4M0M0LjEyNTgyIDguOTU0MjcgNC4zMjAwMyA4LjY1NTY2IDQuNjIxMjkgOC41MjIwNUw2LjYxODE3IDcuNjM2MzRWNS41OTg3NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNDg3OCAzLjI0OTAyQzExLjI3OTYgMy4yNDkwMiAxMS45Nzc4IDMuNjQ5MDIgMTIuMzkxNyA0LjI1Nzk2QzEyLjUxNTEgNC4yMzkwNiAxMi42NDE2IDQuMjI5MjYgMTIuNzcwMyA0LjIyOTI2QzEzLjcyMjQgNC4yMjkyNiAxNC41NDkzIDQuNzY1MzEgMTQuOTY1NyA1LjU1MjA3QzE1LjA1NDMgNS41NDI1IDE1LjE0NDIgNS41Mzc1OSAxNS4yMzUzIDUuNTM3NTlDMTYuNjA2MiA1LjUzNzU5IDE3LjcxNzYgNi42NDg5MyAxNy43MTc2IDguMDE5ODNMMTcuNzE3NyAxMi40NDY3QzE3LjcxNzcgMTQuMjM1NSAxNy4wNjQ3IDE1Ljk1NTkgMTUuODkyMSAxNy4yOTA0VjE3LjMyNjJDMTUuODkyMSAxOC40NzY4IDE0Ljk1OTQgMTkuNDA5NSAxMy44MDg4IDE5LjQwOTVIOC4xNzMzMkM3LjAyMjczIDE5LjQwOTUgNi4wODk5OCAxOC40NzY4IDYuMDg5OTggMTcuMzI2MlYxNi42NTI0TDQuNzA1NjMgMTUuODI3QzMuNTcxMDYgMTUuMTUwNSAyLjg3NjA3IDEzLjkyNzEgMi44NzYwNCAxMi42MDYxTDIuODc1OTggOS4yODM4NUMyLjg3NTk2IDguNDU5OTYgMy4zNjE0OSA3LjcxMzQ1IDQuMTE0NjIgNy4zNzk0TDUuMzY4MzIgNi44MjMzM1Y1LjU5ODc1QzUuMzY4MzIgNC4xMjg4NSA2LjU1OTkxIDIuOTM3MjYgOC4wMjk4MiAyLjkzNzI2QzguNjA4MzEgMi45MzcyNiA5LjE0MzU1IDMuMTIxNyA5LjU4MDA1IDMuNDM1MDVDOS44NTg1MyAzLjMxNTMyIDEwLjE2NTQgMy4yNDkwMiAxMC40ODc4IDMuMjQ5MDJaTTEyLjQ0NzkgNS41MjE4NlY5LjQ3NTU3QzEyLjQ0NzkgOS43Mjc2MiAxMi4yNDM2IDkuOTMxOTUgMTEuOTkxNiA5LjkzMTk1QzExLjc1NjggOS45MzE5NSAxMS41NjM0IDkuNzU0NjUgMTEuNTM4IDkuNTI2NjNDMTEuNTM2MSA5LjUwOTg3IDExLjUzNTIgOS40OTI4MyAxMS41MzUyIDkuNDc1NTdWNS40NzE1OEMxMS41MTU0IDUuMjAwODMgMTEuMzkzIDQuOTU4NTggMTEuMjA2NiA0Ljc4MzU4QzExLjAxODggNC42MDcxMSAxMC43NjU5IDQuNDk5MDIgMTAuNDg3OCA0LjQ5OTAyQzEwLjQ3NjYgNC40OTkwMiAxMC40NjU0IDQuNDk5MTkgMTAuNDU0MiA0LjQ5OTU0QzkuOTAzNDcgNC41MTY4NCA5LjQ1OTY0IDQuOTU4MjQgOS40Mzg0NCA1LjUwODAxQzkuNDM4MiA1LjUwNDMgOS40Mzc5NSA1LjUwMDU4IDkuNDM3NjkgNS40OTY4OFY5LjkwMjQzQzkuNDM3NjkgMTAuMTU0NSA5LjIzMzM2IDEwLjM1ODggOC45ODEzMSAxMC4zNTg4QzguNzI5MjUgMTAuMzU4OCA4LjUyNDkyIDEwLjE1NDUgOC41MjQ5MiA5LjkwMjQzVjQuMjc2NTNDOC4zNzA4NiA0LjIxODgyIDguMjA0MDIgNC4xODcyNiA4LjAyOTgyIDQuMTg3MjZDNy4yNTAyNyA0LjE4NzI2IDYuNjE4MzIgNC44MTkyIDYuNjE4MzIgNS41OTg3NUw2LjYxODI3IDkuOTc1OTlDNi42MTgyNyAxMC4yMjggNi40MTM5NCAxMC40MzI0IDYuMTYxODkgMTAuNDMyNEM1LjkwOTgzIDEwLjQzMjQgNS43MDU1IDEwLjIyOCA1LjcwNTUgOS45NzU5OVY4LjA0MTIyTDQuNjIxNDQgOC41MjIwNUM0LjMyMDE4IDguNjU1NjYgNC4xMjU5NyA4Ljk1NDI3IDQuMTI1OTggOS4yODM4M0w0LjEyNjA0IDEyLjYwNjFDNC4xMjYwNiAxMy40ODY3IDQuNTg5MzkgMTQuMzAyNCA1LjM0NTc2IDE0Ljc1MzRMNy4zMzk5OCAxNS45NDI0VjE3LjMyNjJDNy4zMzk5OCAxNy43ODY0IDcuNzEzMDggMTguMTU5NSA4LjE3MzMyIDE4LjE1OTVIMTMuODA4OEMxNC4yNjkgMTguMTU5NSAxNC42NDIxIDE3Ljc4NjQgMTQuNjQyMSAxNy4zMjYyVjE2Ljc5MzhDMTUuNzM4MSAxNS43MTkgMTYuMzg1IDE0LjI3MjggMTYuNDYwMyAxMi43NDdDMTYuNDY0NiAxMi42NiAxNi40NjcgMTIuNTcyOCAxNi40Njc2IDEyLjQ4NTRMMTYuNDY3NyAxMi40NDY3TDE2LjQ2NzYgOC4wMTk4M0MxNi40Njc2IDcuMzQ1MDQgMTUuOTI1MiA2Ljc5NjkzIDE1LjI1MjUgNi43ODc3MUwxNS4yMzUzIDYuNzg3NTlDMTUuMTI1IDYuNzg3NTkgMTUuMDE4IDYuODAyMSAxNC45MTYyIDYuODI5MzFWOS42MDEzNkMxNC45MTYyIDkuODUzNDIgMTQuNzExOSAxMC4wNTc3IDE0LjQ1OTggMTAuMDU3N0MxNC4yMDc4IDEwLjA1NzcgMTQuMDAzNCA5Ljg1MzQxIDE0LjAwMzQgOS42MDEzNlY3Ljk4OTg1QzE0LjAwMzIgNy45OTk4MiAxNC4wMDMxIDguMDA5ODEgMTQuMDAzMSA4LjAxOTgzTDE0LjAwMzQgOS42MDEzNkwxNC4wMDI1IDYuNzExNUMxNC4wMDI1IDYuNDQ5NzQgMTMuOTIwOSA2LjIwNzA1IDEzLjc4MTggNi4wMDc0OEMxMy41NjIgNS42OTI0MiAxMy4xOTg5IDUuNDg0ODMgMTIuNzg3IDUuNDc5MzdMMTIuNzcwMyA1LjQ3OTI2QzEyLjY1ODggNS40NzkyNiAxMi41NTA3IDUuNDk0MDggMTIuNDQ3OSA1LjUyMTg2WiIgZmlsbD0iIzFEMUMyMyIvPgo8L3N2Zz4="), auto'
1157
- },
1158
- fromNodeJSON(node, json) {
1159
- initFormDataFromJSON(node, json);
1481
+ // cursors: {
1482
+ // grab: 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMCAyMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMC40ODczIDIuNjIzNzhDOS45MDczMSAyLjYyMzc4IDkuNDM3MTMgMy4wOTM5NiA5LjQzNzEzIDMuNjczOTZWNS4xNDM3NkM5LjM5NDI4IDQuNDAyNzQgOC43Nzk3OCAzLjgxNTA0IDguMDI4MDIgMy44MTUwNEM3LjI0ODQ4IDMuODE1MDQgNi42MTY1MyA0LjQ0Njk5IDYuNjE2NTMgNS4yMjY1M1YxMS44Mjg5TDUuNjc0MTggMTEuMDA0OUM1LjE1NDg3IDEwLjU1MDkgNC40MDk1IDEwLjQ2MzYgMy43OTkzOCAxMC43ODU1TDMuNjk2OTQgMTAuODM5NkMzLjA2MjE3IDExLjE3NDUgMi45MjI2IDEyLjAyMjggMy40MTY2MiAxMi41NDM0TDcuMzM5NTkgMTYuNjc3NVYxNy4zMjU5QzcuMzM5NTkgMTcuNzg2MiA3LjcxMjY5IDE4LjE1OTMgOC4xNzI5MiAxOC4xNTkzSDEzLjgwODRDMTQuMjY4NyAxOC4xNTkzIDE0LjY0MTcgMTcuNzg2MiAxNC42NDE3IDE3LjMyNTlWMTYuNzkzNUMxNS44MDk0IDE1LjY0ODUgMTYuNDY3MyAxNC4wODE5IDE2LjQ2NzMgMTIuNDQ2NVYxMS40OTY3TDE2LjQ2NzEgNi42MzY4NUMxNi40NjcxIDUuOTU2MyAxNS45MTU0IDUuNDA0NjEgMTUuMjM0OCA1LjQwNDYxQzE0LjU1NDMgNS40MDQ2MSAxNC4wMDI2IDUuOTU2MyAxNC4wMDI2IDYuNjM2ODVMMTQuMDAyMSA1LjA0NzI4QzE0LjAwMjEgNC4zNjY3MyAxMy40NTA0IDMuODE1MDQgMTIuNzY5OCAzLjgxNTA0QzEyLjA4OTMgMy44MTUwNCAxMS41Mzc2IDQuMzY2NzMgMTEuNTM3NiA1LjA0NzI4TDExLjUzNzUgMy42NzM5NUMxMS41Mzc1IDMuMDkzOTYgMTEuMDY3MyAyLjYyMzc4IDEwLjQ4NzMgMi42MjM3OFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNDg3NCAxLjM3NDAyQzExLjM2MTIgMS4zNzQwMiAxMi4xMjExIDEuODYxMTggMTIuNTEwNSAyLjU3ODY4QzEyLjU5NTggMi41Njk4MyAxMi42ODIzIDIuNTY1MjggMTIuNzcgMi41NjUyOEMxMy44Mjc4IDIuNTY1MjggMTQuNzMxMSAzLjIyNzAxIDE1LjA4ODUgNC4xNTkxMUMxNS4xMzcgNC4xNTYyOSAxNS4xODU4IDQuMTU0ODYgMTUuMjM1IDQuMTU0ODZDMTYuNjA1OSA0LjE1NDg2IDE3LjcxNzIgNS4yNjYxOSAxNy43MTcyIDYuNjM3MDlMMTcuNzE3NCAxMi40NDY3QzE3LjcxNzQgMTQuMjM1NSAxNy4wNjQ0IDE1Ljk1NTkgMTUuODkxOSAxNy4yOTA0VjE3LjMyNjJDMTUuODkxOSAxOC40NzY4IDE0Ljk1OTEgMTkuNDA5NSAxMy44MDg1IDE5LjQwOTVIOC4xNzMwNkM3LjAyMjQ3IDE5LjQwOTUgNi4wODk3MyAxOC40NzY4IDYuMDg5NzMgMTcuMzI2MlYxNy4xNzY0TDIuNTEwMDMgMTMuNDA0MUMxLjQ0NTk5IDEyLjI4MjggMS43NDY2IDEwLjQ1NTUgMy4xMTM3OSA5LjczNDI0TDMuMjE2MjQgOS42ODAxOUMzLjg5MTY4IDkuMzIzODMgNC42NjE4NSA5LjI1NDAxIDUuMzY2NjYgOS40NTE5OFY1LjIyNjc4QzUuMzY2NjYgMy43NTY4NyA2LjU1ODI2IDIuNTY1MjggOC4wMjgxNiAyLjU2NTI4QzguMTcyOTMgMi41NjUyOCA4LjMxNDk5IDIuNTc2ODQgOC40NTM0NyAyLjU5OTA3QzguODM5NDMgMS44NzA0MiA5LjYwNTQ2IDEuMzc0MDIgMTAuNDg3NCAxLjM3NDAyWk0xMi40NDc2IDMuODU3ODdWOS40NzY0NkMxMi40NDc2IDkuNzI4NTIgMTIuMjQzMyA5LjkzMjg1IDExLjk5MTMgOS45MzI4NUMxMS43MzkyIDkuOTMyODUgMTEuNTM0OSA5LjcyODUyIDExLjUzNDkgOS40NzY0NlYzLjc5NjU1QzExLjUzNDkgMy43Nzk1NiAxMS41MzU4IDMuNzYyNzcgMTEuNTM3NiAzLjc0NjI2VjMuNjc0MkMxMS41Mzc2IDMuNDMyODIgMTEuNDU2MiAzLjIxMDQ2IDExLjMxOTMgMy4wMzMwOUMxMS4xMjcyIDIuNzg0MjggMTAuODI2MSAyLjYyNDAyIDEwLjQ4NzQgMi42MjQwMkMxMC4xMjM4IDIuNjI0MDIgOS44MDMzMiAyLjgwODg2IDkuNjE0ODMgMy4wODk3QzkuNTAyNjkgMy4yNTY3OSA5LjQzNzI2IDMuNDU3ODUgOS40MzcyNiAzLjY3NDJWMy43ODU3M0M5LjQzNzM1IDMuNzg5MzMgOS40MzczOSAzLjc5Mjk0IDkuNDM3MzkgMy43OTY1NVY5LjkwMTdDOS40MzczOSAxMC4xNTM3IDkuMjMzMDYgMTAuMzU4MSA4Ljk4MTAxIDEwLjM1ODFDOC43Mjg5NSAxMC4zNTgxIDguNTI0NjIgMTAuMTUzNyA4LjUyNDYyIDkuOTAxN1YzLjkwNTA3QzguNDE3NzMgMy44NjQ5IDguMzA0NjggMy44MzczMiA4LjE4NzI2IDMuODI0MTVDOC4xMzUwNCAzLjgxODI5IDguMDgxOTUgMy44MTUyOCA4LjAyODE2IDMuODE1MjhDNy4yNDg2MSAzLjgxNTI4IDYuNjE2NjYgNC40NDcyMyA2LjYxNjY2IDUuMjI2NzhWMTEuODI5Mkw1LjY3NDMxIDExLjAwNTJDNS41Nzg2OCAxMC45MjE2IDUuNDc1MzcgMTAuODUwNCA1LjM2NjY2IDEwLjc5MTlDNC44ODUwNiAxMC41MzI5IDQuMjk3MjggMTAuNTIzMSAzLjc5OTUyIDEwLjc4NThMMy42OTcwNyAxMC44Mzk4QzMuMDYyMzEgMTEuMTc0NyAyLjkyMjczIDEyLjAyMzEgMy40MTY3NSAxMi41NDM3TDcuMzM5NzMgMTYuNjc3N1YxNy4zMjYyQzcuMzM5NzMgMTcuNzg2NCA3LjcxMjgyIDE4LjE1OTUgOC4xNzMwNiAxOC4xNTk1SDEzLjgwODVDMTQuMjY4OCAxOC4xNTk1IDE0LjY0MTkgMTcuNzg2NCAxNC42NDE5IDE3LjMyNjJWMTYuNzkzOEMxNS43Mzc5IDE1LjcxOSAxNi4zODQ3IDE0LjI3MjggMTYuNDYgMTIuNzQ3QzE2LjQ2NDEgMTIuNjY0MSAxNi40NjY1IDEyLjU4MDkgMTYuNDY3MiAxMi40OTc1TDE2LjQ2NzQgMTIuNDQ2N0wxNi40NjcyIDYuNjM3MDlDMTYuNDY3MiA1Ljk2MjMgMTUuOTI0OCA1LjQxNDE5IDE1LjI1MjIgNS40MDQ5N0wxNS4yMzUgNS40MDQ4NkMxNS4xMjQ2IDUuNDA0ODYgMTUuMDE3NyA1LjQxOTM2IDE0LjkxNTkgNS40NDY1NlY5LjYwMjI2QzE0LjkxNTkgOS44NTQzMSAxNC43MTE2IDEwLjA1ODYgMTQuNDU5NSAxMC4wNTg2QzE0LjIwNzUgMTAuMDU4NiAxNC4wMDMxIDkuODU0MzEgMTQuMDAzMSA5LjYwMjI2VjYuNjA1MTRDMTQuMDAyOSA2LjYxNTc2IDE0LjAwMjcgNi42MjY0MSAxNC4wMDI3IDYuNjM3MDlWOS4yNzcwNUwxNC4wMDIyIDUuMDQ3NTJDMTQuMDAyMiA0Ljg2OTEzIDEzLjk2NDMgNC42OTk2IDEzLjg5NjEgNC41NDY1M0MxMy43MDY0IDQuMTIwNzIgMTMuMjgyMiAzLjgyMjM1IDEyLjc4NzYgMy44MTU0MUwxMi43NyAzLjgxNTI4QzEyLjY1ODQgMy44MTUyOCAxMi41NTA0IDMuODMwMSAxMi40NDc2IDMuODU3ODdaIiBmaWxsPSIjMUQxQzIzIi8+Cjwvc3ZnPg=="), auto',
1483
+ // grabbing:
1484
+ // 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMCAyMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02LjYxODE3IDUuNTk4NzVDNi42MTgxNyA0LjgxOTIgNy4yNTAxMiA0LjE4NzI2IDguMDI5NjcgNC4xODcyNkM4Ljc3ODczIDQuMTg3MjYgOS4zOTE1MiA0Ljc3MDc1IDkuNDM4MjkgNS41MDgwMUM5LjQ1OTkyIDQuOTQ3MSA5LjkyMTQ3IDQuNDk5MDIgMTAuNDg3NyA0LjQ5OTAyQzExLjA2NzcgNC40OTkwMiAxMS41Mzc4IDQuOTY5MiAxMS41Mzc4IDUuNTQ5MTlWOC43NjI0NkwxMS41Mzc5IDYuNzExNUMxMS41Mzc5IDYuMDMwOTUgMTIuMDg5NiA1LjQ3OTI2IDEyLjc3MDIgNS40NzkyNkMxMy40NTA3IDUuNDc5MjYgMTQuMDAyNCA2LjAzMDk1IDE0LjAwMjQgNi43MTE1TDE0LjAwMjQgOC43NjI0NkwxNC4wMDI5IDguMDE5ODNDMTQuMDAyOSA3LjMzOTI5IDE0LjU1NDYgNi43ODc1OSAxNS4yMzUyIDYuNzg3NTlDMTUuOTE1NyA2Ljc4NzU5IDE2LjQ2NzQgNy4zMzkyOCAxNi40Njc0IDguMDE5ODNWMTEuNDk3TDE2LjQ2NzUgMTIuNDQ2N0MxNi40Njc1IDE0LjA4MjEgMTUuODA5NiAxNS42NDg3IDE0LjY0MiAxNi43OTM4VjE3LjMyNjJDMTQuNjQyIDE3Ljc4NjQgMTQuMjY4OSAxOC4xNTk1IDEzLjgwODcgMTguMTU5NUg4LjE3MzE3QzcuNzEyOTMgMTguMTU5NSA3LjMzOTg0IDE3Ljc4NjQgNy4zMzk4NCAxNy4zMjYyVjE1Ljk0MjRMNS4zNDU2MiAxNC43NTM0QzQuNTg5MjQgMTQuMzAyNCA0LjEyNTkxIDEzLjQ4NjcgNC4xMjU4OSAxMi42MDYxTDQuMTI1ODMgOS4yODM4M0M0LjEyNTgyIDguOTU0MjcgNC4zMjAwMyA4LjY1NTY2IDQuNjIxMjkgOC41MjIwNUw2LjYxODE3IDcuNjM2MzRWNS41OTg3NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNDg3OCAzLjI0OTAyQzExLjI3OTYgMy4yNDkwMiAxMS45Nzc4IDMuNjQ5MDIgMTIuMzkxNyA0LjI1Nzk2QzEyLjUxNTEgNC4yMzkwNiAxMi42NDE2IDQuMjI5MjYgMTIuNzcwMyA0LjIyOTI2QzEzLjcyMjQgNC4yMjkyNiAxNC41NDkzIDQuNzY1MzEgMTQuOTY1NyA1LjU1MjA3QzE1LjA1NDMgNS41NDI1IDE1LjE0NDIgNS41Mzc1OSAxNS4yMzUzIDUuNTM3NTlDMTYuNjA2MiA1LjUzNzU5IDE3LjcxNzYgNi42NDg5MyAxNy43MTc2IDguMDE5ODNMMTcuNzE3NyAxMi40NDY3QzE3LjcxNzcgMTQuMjM1NSAxNy4wNjQ3IDE1Ljk1NTkgMTUuODkyMSAxNy4yOTA0VjE3LjMyNjJDMTUuODkyMSAxOC40NzY4IDE0Ljk1OTQgMTkuNDA5NSAxMy44MDg4IDE5LjQwOTVIOC4xNzMzMkM3LjAyMjczIDE5LjQwOTUgNi4wODk5OCAxOC40NzY4IDYuMDg5OTggMTcuMzI2MlYxNi42NTI0TDQuNzA1NjMgMTUuODI3QzMuNTcxMDYgMTUuMTUwNSAyLjg3NjA3IDEzLjkyNzEgMi44NzYwNCAxMi42MDYxTDIuODc1OTggOS4yODM4NUMyLjg3NTk2IDguNDU5OTYgMy4zNjE0OSA3LjcxMzQ1IDQuMTE0NjIgNy4zNzk0TDUuMzY4MzIgNi44MjMzM1Y1LjU5ODc1QzUuMzY4MzIgNC4xMjg4NSA2LjU1OTkxIDIuOTM3MjYgOC4wMjk4MiAyLjkzNzI2QzguNjA4MzEgMi45MzcyNiA5LjE0MzU1IDMuMTIxNyA5LjU4MDA1IDMuNDM1MDVDOS44NTg1MyAzLjMxNTMyIDEwLjE2NTQgMy4yNDkwMiAxMC40ODc4IDMuMjQ5MDJaTTEyLjQ0NzkgNS41MjE4NlY5LjQ3NTU3QzEyLjQ0NzkgOS43Mjc2MiAxMi4yNDM2IDkuOTMxOTUgMTEuOTkxNiA5LjkzMTk1QzExLjc1NjggOS45MzE5NSAxMS41NjM0IDkuNzU0NjUgMTEuNTM4IDkuNTI2NjNDMTEuNTM2MSA5LjUwOTg3IDExLjUzNTIgOS40OTI4MyAxMS41MzUyIDkuNDc1NTdWNS40NzE1OEMxMS41MTU0IDUuMjAwODMgMTEuMzkzIDQuOTU4NTggMTEuMjA2NiA0Ljc4MzU4QzExLjAxODggNC42MDcxMSAxMC43NjU5IDQuNDk5MDIgMTAuNDg3OCA0LjQ5OTAyQzEwLjQ3NjYgNC40OTkwMiAxMC40NjU0IDQuNDk5MTkgMTAuNDU0MiA0LjQ5OTU0QzkuOTAzNDcgNC41MTY4NCA5LjQ1OTY0IDQuOTU4MjQgOS40Mzg0NCA1LjUwODAxQzkuNDM4MiA1LjUwNDMgOS40Mzc5NSA1LjUwMDU4IDkuNDM3NjkgNS40OTY4OFY5LjkwMjQzQzkuNDM3NjkgMTAuMTU0NSA5LjIzMzM2IDEwLjM1ODggOC45ODEzMSAxMC4zNTg4QzguNzI5MjUgMTAuMzU4OCA4LjUyNDkyIDEwLjE1NDUgOC41MjQ5MiA5LjkwMjQzVjQuMjc2NTNDOC4zNzA4NiA0LjIxODgyIDguMjA0MDIgNC4xODcyNiA4LjAyOTgyIDQuMTg3MjZDNy4yNTAyNyA0LjE4NzI2IDYuNjE4MzIgNC44MTkyIDYuNjE4MzIgNS41OTg3NUw2LjYxODI3IDkuOTc1OTlDNi42MTgyNyAxMC4yMjggNi40MTM5NCAxMC40MzI0IDYuMTYxODkgMTAuNDMyNEM1LjkwOTgzIDEwLjQzMjQgNS43MDU1IDEwLjIyOCA1LjcwNTUgOS45NzU5OVY4LjA0MTIyTDQuNjIxNDQgOC41MjIwNUM0LjMyMDE4IDguNjU1NjYgNC4xMjU5NyA4Ljk1NDI3IDQuMTI1OTggOS4yODM4M0w0LjEyNjA0IDEyLjYwNjFDNC4xMjYwNiAxMy40ODY3IDQuNTg5MzkgMTQuMzAyNCA1LjM0NTc2IDE0Ljc1MzRMNy4zMzk5OCAxNS45NDI0VjE3LjMyNjJDNy4zMzk5OCAxNy43ODY0IDcuNzEzMDggMTguMTU5NSA4LjE3MzMyIDE4LjE1OTVIMTMuODA4OEMxNC4yNjkgMTguMTU5NSAxNC42NDIxIDE3Ljc4NjQgMTQuNjQyMSAxNy4zMjYyVjE2Ljc5MzhDMTUuNzM4MSAxNS43MTkgMTYuMzg1IDE0LjI3MjggMTYuNDYwMyAxMi43NDdDMTYuNDY0NiAxMi42NiAxNi40NjcgMTIuNTcyOCAxNi40Njc2IDEyLjQ4NTRMMTYuNDY3NyAxMi40NDY3TDE2LjQ2NzYgOC4wMTk4M0MxNi40Njc2IDcuMzQ1MDQgMTUuOTI1MiA2Ljc5NjkzIDE1LjI1MjUgNi43ODc3MUwxNS4yMzUzIDYuNzg3NTlDMTUuMTI1IDYuNzg3NTkgMTUuMDE4IDYuODAyMSAxNC45MTYyIDYuODI5MzFWOS42MDEzNkMxNC45MTYyIDkuODUzNDIgMTQuNzExOSAxMC4wNTc3IDE0LjQ1OTggMTAuMDU3N0MxNC4yMDc4IDEwLjA1NzcgMTQuMDAzNCA5Ljg1MzQxIDE0LjAwMzQgOS42MDEzNlY3Ljk4OTg1QzE0LjAwMzIgNy45OTk4MiAxNC4wMDMxIDguMDA5ODEgMTQuMDAzMSA4LjAxOTgzTDE0LjAwMzQgOS42MDEzNkwxNC4wMDI1IDYuNzExNUMxNC4wMDI1IDYuNDQ5NzQgMTMuOTIwOSA2LjIwNzA1IDEzLjc4MTggNi4wMDc0OEMxMy41NjIgNS42OTI0MiAxMy4xOTg5IDUuNDg0ODMgMTIuNzg3IDUuNDc5MzdMMTIuNzcwMyA1LjQ3OTI2QzEyLjY1ODggNS40NzkyNiAxMi41NTA3IDUuNDk0MDggMTIuNDQ3OSA1LjUyMTg2WiIgZmlsbD0iIzFEMUMyMyIvPgo8L3N2Zz4="), auto',
1485
+ // },
1486
+ fromNodeJSON(node, json, isFirstCreate) {
1487
+ initFormDataFromJSON(node, json, isFirstCreate);
1160
1488
  return;
1161
1489
  },
1162
1490
  toNodeJSON(node) {
@@ -1170,7 +1498,7 @@ var WorkflowDocumentOptionsDefault = {
1170
1498
  const nodeMeta = node.getNodeMeta();
1171
1499
  const subCanvas = nodeMeta.subCanvas?.(node);
1172
1500
  if (subCanvas?.isCanvas === false) {
1173
- const canvasNodeTransform = subCanvas.canvasNode.getData(FlowNodeTransformData);
1501
+ const canvasNodeTransform = subCanvas.canvasNode.getData(FlowNodeTransformData2);
1174
1502
  const { x, y } = canvasNodeTransform.transform.position;
1175
1503
  metaData.canvasPosition = { x, y };
1176
1504
  }
@@ -1193,8 +1521,8 @@ var WorkflowLinesManager = class {
1193
1521
  this.toDispose = new DisposableCollection();
1194
1522
  // 线条类型
1195
1523
  this._lineType = 0 /* BEZIER */;
1196
- this.onAvailableLinesChangeEmitter = new Emitter3();
1197
- this.onForceUpdateEmitter = new Emitter3();
1524
+ this.onAvailableLinesChangeEmitter = new Emitter4();
1525
+ this.onForceUpdateEmitter = new Emitter4();
1198
1526
  /**
1199
1527
  * 有效的线条被添加或者删除时候触发,未连上的线条不算
1200
1528
  */
@@ -1220,12 +1548,13 @@ var WorkflowLinesManager = class {
1220
1548
  }
1221
1549
  get lineColor() {
1222
1550
  const color = {
1223
- default: "#4d53e8" /* DEFUALT */,
1224
- error: "red" /* ERROR */,
1225
- hidden: "transparent" /* HIDDEN */,
1226
- drawing: "#5DD6E3" /* DRAWING */,
1227
- hovered: "#37d0ff" /* HOVER */,
1228
- selected: "#37d0ff" /* HOVER */
1551
+ default: "var(--g-workflow-line-color-default,#4d53e8)" /* DEFUALT */,
1552
+ error: "var(--g-workflow-line-color-error,red)" /* ERROR */,
1553
+ hidden: "var(--g-workflow-line-color-hidden,transparent)" /* HIDDEN */,
1554
+ drawing: "var(--g-workflow-line-color-drawing, #5DD6E3)" /* DRAWING */,
1555
+ hovered: "var(--g-workflow-line-color-hover,#37d0ff)" /* HOVER */,
1556
+ selected: "var(--g-workflow-line-color-selected,#37d0ff)" /* SELECTED */,
1557
+ flowing: "var(--g-workflow-line-color-flowing,#4d53e8)" /* FLOWING */
1229
1558
  };
1230
1559
  if (this.options.lineColor) {
1231
1560
  Object.assign(color, this.options.lineColor);
@@ -1254,6 +1583,9 @@ var WorkflowLinesManager = class {
1254
1583
  getAllLines() {
1255
1584
  return this.entityManager.getEntities(WorkflowLineEntity);
1256
1585
  }
1586
+ getAllAvailableLines() {
1587
+ return this.getAllLines().filter((l) => !l.isDrawing && !l.isHidden);
1588
+ }
1257
1589
  hasLine(portInfo) {
1258
1590
  return !!this.entityManager.getEntityById(
1259
1591
  WorkflowLineEntity.portInfoToLineId(portInfo)
@@ -1264,6 +1596,9 @@ var WorkflowLinesManager = class {
1264
1596
  WorkflowLineEntity.portInfoToLineId(portInfo)
1265
1597
  );
1266
1598
  }
1599
+ getLineById(id) {
1600
+ return this.entityManager.getEntityById(id);
1601
+ }
1267
1602
  replaceLine(oldPortInfo, newPortInfo) {
1268
1603
  const oldLine = this.getLine(oldPortInfo);
1269
1604
  if (oldLine) {
@@ -1272,7 +1607,7 @@ var WorkflowLinesManager = class {
1272
1607
  return this.createLine(newPortInfo);
1273
1608
  }
1274
1609
  createLine(options) {
1275
- const { from, to, drawingTo, fromPort, toPort } = options;
1610
+ const { from, to, drawingTo, fromPort, drawingFrom, toPort, data } = options;
1276
1611
  const available = Boolean(from && to);
1277
1612
  const key = options.key || WorkflowLineEntity.portInfoToLineId(options);
1278
1613
  let line = this.entityManager.getEntityById(key);
@@ -1281,12 +1616,12 @@ var WorkflowLinesManager = class {
1281
1616
  line.validate();
1282
1617
  return line;
1283
1618
  }
1284
- const fromNode = this.entityManager.getEntityById(from)?.getData(WorkflowNodeLinesData);
1619
+ const fromNode = from ? this.entityManager.getEntityById(from).getData(WorkflowNodeLinesData) : void 0;
1285
1620
  const toNode = to ? this.entityManager.getEntityById(to).getData(WorkflowNodeLinesData) : void 0;
1286
- if (!fromNode) {
1621
+ if (!fromNode && !toNode) {
1287
1622
  return;
1288
1623
  }
1289
- this.isDrawing = Boolean(drawingTo);
1624
+ this.isDrawing = Boolean(drawingTo || drawingFrom);
1290
1625
  line = this.entityManager.createEntity(WorkflowLineEntity, {
1291
1626
  id: key,
1292
1627
  document: this.document,
@@ -1295,30 +1630,35 @@ var WorkflowLinesManager = class {
1295
1630
  fromPort,
1296
1631
  toPort,
1297
1632
  to,
1298
- drawingTo
1633
+ drawingTo,
1634
+ drawingFrom,
1635
+ data
1299
1636
  });
1300
1637
  this.registerData(line);
1301
- fromNode.addLine(line);
1638
+ fromNode?.addLine(line);
1302
1639
  toNode?.addLine(line);
1303
1640
  line.onDispose(() => {
1304
- if (drawingTo) {
1305
- this.isDrawing = false;
1306
- }
1307
- fromNode.removeLine(line);
1641
+ this.isDrawing = false;
1642
+ fromNode?.removeLine(line);
1308
1643
  toNode?.removeLine(line);
1309
- line.validate();
1310
1644
  });
1311
- line.toDispose.push(
1312
- Disposable2.create(() => {
1313
- if (available) {
1314
- this.onAvailableLinesChangeEmitter.fire({
1315
- type: "DELETE_LINE" /* DELETE_LINE */,
1316
- toJSON: () => line.toJSON(),
1317
- entity: line
1318
- });
1319
- }
1320
- })
1321
- );
1645
+ line.onDispose(() => {
1646
+ if (available) {
1647
+ this.onAvailableLinesChangeEmitter.fire({
1648
+ type: "DELETE_LINE" /* DELETE_LINE */,
1649
+ toJSON: () => line.toJSON(),
1650
+ entity: line
1651
+ });
1652
+ }
1653
+ });
1654
+ line.onLineDataChange(({ oldValue }) => {
1655
+ this.onAvailableLinesChangeEmitter.fire({
1656
+ type: "LINE_DATA_CHANGE" /* LINE_DATA_CHANGE */,
1657
+ toJSON: () => line.toJSON(),
1658
+ oldValue,
1659
+ entity: line
1660
+ });
1661
+ });
1322
1662
  if (available) {
1323
1663
  this.onAvailableLinesChangeEmitter.fire({
1324
1664
  type: "ADD_LINE" /* ADD_LINE */,
@@ -1352,41 +1692,35 @@ var WorkflowLinesManager = class {
1352
1692
  get disposed() {
1353
1693
  return this.toDispose.disposed;
1354
1694
  }
1355
- isErrorLine(fromPort, toPort) {
1695
+ isErrorLine(fromPort, toPort, defaultValue) {
1356
1696
  if (this.options.isErrorLine) {
1357
1697
  return this.options.isErrorLine(fromPort, toPort, this);
1358
1698
  }
1359
- return false;
1699
+ return !!defaultValue;
1360
1700
  }
1361
- isReverseLine(line) {
1701
+ isReverseLine(line, defaultValue = false) {
1362
1702
  if (this.options.isReverseLine) {
1363
1703
  return this.options.isReverseLine(line);
1364
1704
  }
1365
- return false;
1705
+ return defaultValue;
1366
1706
  }
1367
- isHideArrowLine(line) {
1707
+ isHideArrowLine(line, defaultValue = false) {
1368
1708
  if (this.options.isHideArrowLine) {
1369
1709
  return this.options.isHideArrowLine(line);
1370
1710
  }
1371
- return false;
1711
+ return defaultValue;
1372
1712
  }
1373
- isFlowingLine(line) {
1713
+ isFlowingLine(line, defaultValue = false) {
1374
1714
  if (this.options.isFlowingLine) {
1375
1715
  return this.options.isFlowingLine(line);
1376
1716
  }
1377
- return false;
1717
+ return defaultValue;
1378
1718
  }
1379
- isDisabledLine(line) {
1719
+ isDisabledLine(line, defaultValue = false) {
1380
1720
  if (this.options.isDisabledLine) {
1381
1721
  return this.options.isDisabledLine(line);
1382
1722
  }
1383
- return false;
1384
- }
1385
- isVerticalLine(line) {
1386
- if (this.options.isVerticalLine) {
1387
- return this.options.isVerticalLine(line);
1388
- }
1389
- return false;
1723
+ return defaultValue;
1390
1724
  }
1391
1725
  setLineRenderType(line) {
1392
1726
  if (this.options.setLineRenderType) {
@@ -1404,6 +1738,9 @@ var WorkflowLinesManager = class {
1404
1738
  if (line.isHidden) {
1405
1739
  return this.lineColor.hidden;
1406
1740
  }
1741
+ if (line.lockedColor) {
1742
+ return line.lockedColor;
1743
+ }
1407
1744
  if (line.hasError) {
1408
1745
  return this.lineColor.error;
1409
1746
  }
@@ -1419,10 +1756,21 @@ var WorkflowLinesManager = class {
1419
1756
  if (this.selectService.isSelected(line.id)) {
1420
1757
  return this.lineColor.selected;
1421
1758
  }
1759
+ if (this.isFlowingLine(line)) {
1760
+ return this.lineColor.flowing;
1761
+ }
1422
1762
  return this.lineColor.default;
1423
1763
  }
1424
1764
  canAddLine(fromPort, toPort, silent) {
1425
- if (fromPort === toPort || fromPort.node === toPort.node || fromPort.portType !== "output" || toPort.portType !== "input" || toPort.disabled) {
1765
+ if (fromPort === toPort || fromPort.node === toPort.node || fromPort.portType !== "output" || toPort.portType !== "input" || fromPort.disabled || toPort.disabled) {
1766
+ return false;
1767
+ }
1768
+ const fromCanAdd = fromPort.node.getNodeRegistry().canAddLine;
1769
+ const toCanAdd = toPort.node.getNodeRegistry().canAddLine;
1770
+ if (fromCanAdd && !fromCanAdd(fromPort, toPort, this, silent)) {
1771
+ return false;
1772
+ }
1773
+ if (toCanAdd && !toCanAdd(fromPort, toPort, this, silent)) {
1426
1774
  return false;
1427
1775
  }
1428
1776
  if (this.options.canAddLine) {
@@ -1442,8 +1790,8 @@ var WorkflowLinesManager = class {
1442
1790
  }
1443
1791
  return true;
1444
1792
  }
1445
- canReset(fromPort, oldToPort, newToPort) {
1446
- if (this.options && this.options.canResetLine && !this.options.canResetLine(fromPort, oldToPort, newToPort, this)) {
1793
+ canReset(oldLine, newLineInfo) {
1794
+ if (this.options && this.options.canResetLine && !this.options.canResetLine(oldLine, newLineInfo, this)) {
1447
1795
  return false;
1448
1796
  }
1449
1797
  return true;
@@ -1452,11 +1800,18 @@ var WorkflowLinesManager = class {
1452
1800
  * 根据鼠标位置找到 port
1453
1801
  * @param pos
1454
1802
  */
1455
- getPortFromMousePos(pos) {
1456
- const allPorts = this.entityManager.getEntities(WorkflowPortEntity).filter((port) => port.node.flowNodeType !== "root");
1803
+ getPortFromMousePos(pos, portType) {
1804
+ const allNodes = this.getSortedNodes().reverse();
1805
+ const allPorts = allNodes.map((node) => {
1806
+ if (!portType) {
1807
+ return node.ports.allPorts;
1808
+ }
1809
+ return portType === "input" ? node.ports.inputPorts : node.ports.outputPorts;
1810
+ }).flat();
1457
1811
  const targetPort = allPorts.find((port) => port.isHovered(pos.x, pos.y));
1458
1812
  if (targetPort) {
1459
- const targetNode = this.document.getAllNodes().slice().reverse().filter((node) => targetPort.node?.parent?.id !== node.id).find((node) => node.getData(TransformData6).contains(pos.x, pos.y));
1813
+ const containNodes = this.getContainNodesFromMousePos(pos);
1814
+ const targetNode = last(containNodes);
1460
1815
  if (targetNode && targetNode !== targetPort.node) {
1461
1816
  return;
1462
1817
  }
@@ -1468,16 +1823,8 @@ var WorkflowLinesManager = class {
1468
1823
  * @param pos - 鼠标位置
1469
1824
  */
1470
1825
  getNodeFromMousePos(pos) {
1471
- const allNodes = this.document.getAllNodes();
1472
- const containNodes = [];
1473
1826
  const { selection } = this.selectService;
1474
- const zoom = this.entityManager.getEntity(PlaygroundConfigEntity2)?.config?.zoom || 1;
1475
- allNodes.forEach((node) => {
1476
- const { bounds } = node.getData(FlowNodeTransformData2);
1477
- if (bounds.clone().pad(4 / zoom).contains(pos.x, pos.y)) {
1478
- containNodes.push(node);
1479
- }
1480
- });
1827
+ const containNodes = this.getContainNodesFromMousePos(pos);
1481
1828
  if (selection?.length) {
1482
1829
  const filteredNodes = containNodes.filter(
1483
1830
  (node) => selection.some((_node) => node.id === _node.id)
@@ -1495,6 +1842,25 @@ var WorkflowLinesManager = class {
1495
1842
  registerData(line) {
1496
1843
  line.addData(WorkflowLineRenderData);
1497
1844
  }
1845
+ getSortedNodes() {
1846
+ return this.document.getAllNodes().sort((a, b) => this.getNodeIndex(a) - this.getNodeIndex(b));
1847
+ }
1848
+ /** 获取鼠标坐标位置的所有节点(stackIndex 从小到大排序) */
1849
+ getContainNodesFromMousePos(pos) {
1850
+ const allNodes = this.getSortedNodes();
1851
+ const zoom = this.entityManager.getEntity(PlaygroundConfigEntity2)?.config?.zoom || 1;
1852
+ const containNodes = allNodes.map((node) => {
1853
+ const { bounds } = node.getData(FlowNodeTransformData3);
1854
+ if (bounds.clone().pad(4 / zoom).contains(pos.x, pos.y)) {
1855
+ return node;
1856
+ }
1857
+ }).filter(Boolean);
1858
+ return containNodes;
1859
+ }
1860
+ getNodeIndex(node) {
1861
+ const nodeRenderData = node.getData(FlowNodeRenderData2);
1862
+ return nodeRenderData.stackIndex;
1863
+ }
1498
1864
  };
1499
1865
  __decorateClass([
1500
1866
  inject3(WorkflowHoverService)
@@ -1515,28 +1881,32 @@ WorkflowLinesManager = __decorateClass([
1515
1881
  // src/workflow-document.ts
1516
1882
  import { customAlphabet } from "nanoid";
1517
1883
  import { inject as inject5, injectable as injectable5, optional, postConstruct } from "inversify";
1518
- import { Disposable as Disposable3, Emitter as Emitter4 } from "@flowgram.ai/utils";
1884
+ import { Emitter as Emitter5 } from "@flowgram.ai/utils";
1519
1885
  import { NodeEngineContext } from "@flowgram.ai/form-core";
1520
- import { FlowDocument, FlowNodeBaseType, FlowNodeTransformData as FlowNodeTransformData4 } from "@flowgram.ai/document";
1886
+ import {
1887
+ FlowDocument,
1888
+ FlowNodeBaseType as FlowNodeBaseType2,
1889
+ FlowNodeTransformData as FlowNodeTransformData5
1890
+ } from "@flowgram.ai/document";
1521
1891
  import {
1522
1892
  injectPlaygroundContext,
1523
1893
  PlaygroundConfigEntity as PlaygroundConfigEntity4,
1524
1894
  PositionData,
1525
- TransformData as TransformData8
1895
+ TransformData as TransformData7
1526
1896
  } from "@flowgram.ai/core";
1527
1897
 
1528
1898
  // src/layout/free-layout.ts
1529
1899
  import { inject as inject4, injectable as injectable4 } from "inversify";
1530
- import {
1531
- FlowDocumentProvider,
1532
- FlowNodeTransformData as FlowNodeTransformData3
1533
- } from "@flowgram.ai/document";
1534
- import { PlaygroundConfigEntity as PlaygroundConfigEntity3, TransformData as TransformData7 } from "@flowgram.ai/core";
1535
1900
  import {
1536
1901
  PaddingSchema,
1537
1902
  Rectangle as Rectangle7,
1538
1903
  SizeSchema as SizeSchema2
1539
1904
  } from "@flowgram.ai/utils";
1905
+ import {
1906
+ FlowDocumentProvider,
1907
+ FlowNodeTransformData as FlowNodeTransformData4
1908
+ } from "@flowgram.ai/document";
1909
+ import { PlaygroundConfigEntity as PlaygroundConfigEntity3, TransformData as TransformData6 } from "@flowgram.ai/core";
1540
1910
  var FREE_LAYOUT_KEY = "free-layout";
1541
1911
  var FreeLayout = class {
1542
1912
  constructor() {
@@ -1549,12 +1919,12 @@ var FreeLayout = class {
1549
1919
  * 更新布局
1550
1920
  */
1551
1921
  update() {
1552
- if (this.document.root.getData(FlowNodeTransformData3)?.localDirty) {
1922
+ if (this.document.root.getData(FlowNodeTransformData4)?.localDirty) {
1553
1923
  this.document.root.clearMemoGlobal();
1554
1924
  }
1555
1925
  }
1556
1926
  syncTransform(node) {
1557
- const transform = node.getData(FlowNodeTransformData3);
1927
+ const transform = node.getData(FlowNodeTransformData4);
1558
1928
  if (!transform.localDirty) {
1559
1929
  return;
1560
1930
  }
@@ -1568,16 +1938,31 @@ var FreeLayout = class {
1568
1938
  }
1569
1939
  node.parent.clearMemoGlobal();
1570
1940
  node.parent.clearMemoLocal();
1571
- const parentTransform = node.parent.getData(FlowNodeTransformData3);
1941
+ const parentTransform = node.parent.getData(FlowNodeTransformData4);
1572
1942
  parentTransform.transform.fireChange();
1573
1943
  }
1944
+ /**
1945
+ * 更新所有受影响的上下游节点
1946
+ */
1947
+ updateAffectedTransform(node) {
1948
+ const transformData = node.transform;
1949
+ if (!transformData.localDirty) {
1950
+ return;
1951
+ }
1952
+ const allParents = this.getAllParents(node);
1953
+ const allBlocks = this.getAllBlocks(node).reverse();
1954
+ const affectedNodes = [...allBlocks, ...allParents];
1955
+ affectedNodes.forEach((node2) => {
1956
+ this.fireChange(node2);
1957
+ });
1958
+ }
1574
1959
  /**
1575
1960
  * 获取节点的 padding 数据
1576
1961
  * @param node
1577
1962
  */
1578
1963
  getPadding(node) {
1579
1964
  const { padding } = node.getNodeMeta();
1580
- const transform = node.getData(FlowNodeTransformData3);
1965
+ const transform = node.getData(FlowNodeTransformData4);
1581
1966
  if (padding) {
1582
1967
  return typeof padding === "function" ? padding(transform) : padding;
1583
1968
  }
@@ -1589,7 +1974,7 @@ var FreeLayout = class {
1589
1974
  */
1590
1975
  getInitScroll(contentSize) {
1591
1976
  const bounds = Rectangle7.enlarge(
1592
- this.document.getAllNodes().map((node) => node.getData(TransformData7).bounds)
1977
+ this.document.getAllNodes().map((node) => node.getData(TransformData6).bounds)
1593
1978
  ).pad(30, 30);
1594
1979
  const viewport = this.playgroundConfig.getViewport(false);
1595
1980
  const zoom = SizeSchema2.fixSize(bounds, viewport);
@@ -1602,13 +1987,13 @@ var FreeLayout = class {
1602
1987
  * 获取默认输入点
1603
1988
  */
1604
1989
  getDefaultInputPoint(node) {
1605
- return node.getData(TransformData7).bounds.leftCenter;
1990
+ return node.getData(TransformData6).bounds.leftCenter;
1606
1991
  }
1607
1992
  /**
1608
1993
  * 获取默认输出点
1609
1994
  */
1610
1995
  getDefaultOutputPoint(node) {
1611
- return node.getData(TransformData7).bounds.rightCenter;
1996
+ return node.getData(TransformData6).bounds.rightCenter;
1612
1997
  }
1613
1998
  /**
1614
1999
  * 水平中心点
@@ -1616,6 +2001,30 @@ var FreeLayout = class {
1616
2001
  getDefaultNodeOrigin() {
1617
2002
  return { x: 0.5, y: 0 };
1618
2003
  }
2004
+ getAllParents(node) {
2005
+ const parents = [];
2006
+ let current = node.parent;
2007
+ while (current) {
2008
+ parents.push(current);
2009
+ current = current.parent;
2010
+ }
2011
+ return parents;
2012
+ }
2013
+ getAllBlocks(node) {
2014
+ return node.blocks.reduce(
2015
+ (acc, child) => [...acc, ...this.getAllBlocks(child)],
2016
+ [node]
2017
+ );
2018
+ }
2019
+ fireChange(node) {
2020
+ const transformData = node?.transform;
2021
+ if (!node || !transformData?.localDirty) {
2022
+ return;
2023
+ }
2024
+ node.clearMemoGlobal();
2025
+ node.clearMemoLocal();
2026
+ transformData.transform.fireChange();
2027
+ }
1619
2028
  };
1620
2029
  __decorateClass([
1621
2030
  inject4(PlaygroundConfigEntity3)
@@ -1633,12 +2042,11 @@ var WorkflowDocumentProvider = Symbol("WorkflowDocumentProvider");
1633
2042
  var WorkflowDocument = class extends FlowDocument {
1634
2043
  constructor() {
1635
2044
  super(...arguments);
1636
- this._onContentChangeEmitter = new Emitter4();
1637
- this.onLoadedEmitter = new Emitter4();
2045
+ this._onContentChangeEmitter = new Emitter5();
2046
+ this.onLoadedEmitter = new Emitter5();
1638
2047
  this.onContentChange = this._onContentChangeEmitter.event;
1639
- this._onReloadEmitter = new Emitter4();
2048
+ this._onReloadEmitter = new Emitter5();
1640
2049
  this.onReload = this._onReloadEmitter.event;
1641
- this.disposed = false;
1642
2050
  /**
1643
2051
  * 数据加载完成
1644
2052
  */
@@ -1649,6 +2057,11 @@ var WorkflowDocument = class extends FlowDocument {
1649
2057
  get loading() {
1650
2058
  return this._loading;
1651
2059
  }
2060
+ /**
2061
+ * use `ctx.tools.fitView()` instead
2062
+ * @deprecated
2063
+ * @param easing
2064
+ */
1652
2065
  async fitView(easing) {
1653
2066
  return fitView(this, this.playgroundConfig, easing).then(() => {
1654
2067
  this.linesManager.forceUpdate();
@@ -1667,12 +2080,17 @@ var WorkflowDocument = class extends FlowDocument {
1667
2080
  });
1668
2081
  }
1669
2082
  async load() {
2083
+ if (this.disposed) return;
1670
2084
  this._loading = true;
1671
2085
  await super.load();
1672
2086
  this._loading = false;
1673
2087
  this.onLoadedEmitter.fire();
1674
2088
  }
2089
+ /**
2090
+ * @deprecated use `ctx.operation.fromJSON` instead
2091
+ */
1675
2092
  async reload(json, delayTime = 0) {
2093
+ if (this.disposed) return;
1676
2094
  this._loading = true;
1677
2095
  this.clear();
1678
2096
  this.fromJSON(json);
@@ -1685,10 +2103,13 @@ var WorkflowDocument = class extends FlowDocument {
1685
2103
  * @param json
1686
2104
  */
1687
2105
  fromJSON(json, fireRender = true) {
1688
- const { flattenJSON, nodeBlocks, nodeEdges } = this.flatJSON(json);
1689
- const nestedJSON = this.nestJSON(flattenJSON, nodeBlocks, nodeEdges);
2106
+ if (this.disposed) return;
2107
+ const workflowJSON = {
2108
+ nodes: json.nodes ?? [],
2109
+ edges: json.edges ?? []
2110
+ };
1690
2111
  this.entityManager.changeEntityLocked = true;
1691
- this.renderJSON(nestedJSON);
2112
+ this.batchAddFromJSON(workflowJSON);
1692
2113
  this.entityManager.changeEntityLocked = false;
1693
2114
  this.transformer.loading = false;
1694
2115
  if (fireRender) {
@@ -1708,9 +2129,18 @@ var WorkflowDocument = class extends FlowDocument {
1708
2129
  * 创建流程节点
1709
2130
  * @param json
1710
2131
  */
1711
- createWorkflowNode(json, isClone = false, parentId) {
1712
- const isExistedNode = this.getNode(json.id);
1713
- const parent = this.getNode(parentId ?? this.root.id) ?? this.root;
2132
+ createWorkflowNode(json, isClone = false, parentID) {
2133
+ return this._createWorkflowNode(json, { parentID });
2134
+ }
2135
+ /**
2136
+ * 创建流程节点
2137
+ * @param json
2138
+ */
2139
+ _createWorkflowNode(json, options) {
2140
+ const { parentID, onNodeCreated, onEdgeCreated } = options ?? {};
2141
+ const existedNode = this.getNode(json.id);
2142
+ const isExistedNode = existedNode && existedNode.flowNodeType === json.type;
2143
+ const parent = this.getNode(parentID ?? this.root.id) ?? this.root;
1714
2144
  const node = this.addNode(
1715
2145
  {
1716
2146
  ...json,
@@ -1723,36 +2153,44 @@ var WorkflowDocument = class extends FlowDocument {
1723
2153
  const { formMeta } = registry;
1724
2154
  const meta = node.getNodeMeta();
1725
2155
  const formData = getFlowNodeFormData(node);
1726
- const transform = node.getData(FlowNodeTransformData4);
2156
+ const transform = node.getData(FlowNodeTransformData5);
1727
2157
  const freeLayout = this.layout;
1728
- transform.onDataChange(() => {
1729
- freeLayout.syncTransform(node);
1730
- });
2158
+ if (!isExistedNode) {
2159
+ transform.onDataChange(() => {
2160
+ freeLayout.syncTransform(node);
2161
+ });
2162
+ }
1731
2163
  let { position } = meta;
1732
2164
  if (!position) {
1733
2165
  position = this.getNodeDefaultPosition(json.type);
1734
2166
  }
1735
- node.getData(TransformData8).update({
2167
+ node.getData(TransformData7).update({
1736
2168
  position
1737
2169
  });
1738
- if (formMeta && formData && !formData.formModel.initialized) {
1739
- formData.createForm(formMeta, json.data);
1740
- formData.onDataChange(() => {
2170
+ if (formMeta && formData) {
2171
+ if (!formData.formModel.initialized) {
2172
+ formData.createForm(formMeta, json.data);
2173
+ formData.onDataChange(() => {
2174
+ this.fireContentChange({
2175
+ type: "NODE_DATA_CHANGE" /* NODE_DATA_CHANGE */,
2176
+ toJSON: () => formData.toJSON(),
2177
+ entity: node
2178
+ });
2179
+ });
2180
+ } else {
2181
+ formData.updateFormValues(json.data);
2182
+ }
2183
+ }
2184
+ const positionData = node.getData(PositionData);
2185
+ if (!isExistedNode) {
2186
+ positionData.onDataChange(() => {
1741
2187
  this.fireContentChange({
1742
- type: "NODE_DATA_CHANGE" /* NODE_DATA_CHANGE */,
1743
- toJSON: () => formData.toJSON(),
2188
+ type: "MOVE_NODE" /* MOVE_NODE */,
2189
+ toJSON: () => positionData.toJSON(),
1744
2190
  entity: node
1745
2191
  });
1746
2192
  });
1747
2193
  }
1748
- const positionData = node.getData(PositionData);
1749
- positionData.onDataChange(() => {
1750
- this.fireContentChange({
1751
- type: "MOVE_NODE" /* MOVE_NODE */,
1752
- toJSON: () => positionData.toJSON(),
1753
- entity: node
1754
- });
1755
- });
1756
2194
  const subCanvas = this.getNodeSubCanvas(node);
1757
2195
  if (!isExistedNode && !subCanvas?.isCanvas) {
1758
2196
  this.fireContentChange({
@@ -1760,54 +2198,132 @@ var WorkflowDocument = class extends FlowDocument {
1760
2198
  entity: node,
1761
2199
  toJSON: () => this.toNodeJSON(node)
1762
2200
  });
1763
- node.toDispose.push(
1764
- Disposable3.create(() => {
1765
- this.fireContentChange({
1766
- type: "DELETE_NODE" /* DELETE_NODE */,
1767
- entity: node,
1768
- toJSON: () => this.toNodeJSON(node)
1769
- });
1770
- })
1771
- );
1772
- node.toDispose.push(
1773
- Disposable3.create(() => {
1774
- if (!node.parent || node.parent.flowNodeType === FlowNodeBaseType.ROOT) {
1775
- return;
1776
- }
1777
- const parentTransform = node.parent.getData(FlowNodeTransformData4);
1778
- setTimeout(() => {
1779
- parentTransform.fireChange();
1780
- }, 0);
1781
- })
1782
- );
2201
+ node.onDispose(() => {
2202
+ if (!node.parent || node.parent.flowNodeType === FlowNodeBaseType2.ROOT) {
2203
+ return;
2204
+ }
2205
+ const parentTransform = node.parent.getData(FlowNodeTransformData5);
2206
+ parentTransform.fireChange();
2207
+ });
2208
+ let lastDeleteNodeData;
2209
+ node.preDispose.onDispose(() => {
2210
+ lastDeleteNodeData = this.toNodeJSON(node);
2211
+ });
2212
+ node.onDispose(() => {
2213
+ this.fireContentChange({
2214
+ type: "DELETE_NODE" /* DELETE_NODE */,
2215
+ entity: node,
2216
+ toJSON: () => lastDeleteNodeData
2217
+ });
2218
+ });
1783
2219
  }
1784
2220
  if (json.blocks) {
1785
- this.renderJSON(
2221
+ this.batchAddFromJSON(
1786
2222
  { nodes: json.blocks, edges: json.edges ?? [] },
1787
2223
  {
1788
2224
  parent: node,
1789
- isClone
2225
+ onNodeCreated,
2226
+ onEdgeCreated
1790
2227
  }
1791
2228
  );
1792
2229
  }
1793
2230
  if (subCanvas) {
1794
- const canvasTransform = subCanvas.canvasNode.getData(TransformData8);
2231
+ const canvasTransform = subCanvas.canvasNode.getData(TransformData7);
1795
2232
  canvasTransform.update({
1796
2233
  position: subCanvas.parentNode.getNodeMeta()?.canvasPosition
1797
2234
  });
1798
- subCanvas.parentNode.onDispose(() => {
1799
- subCanvas.canvasNode.dispose();
2235
+ if (!isExistedNode) {
2236
+ subCanvas.parentNode.onDispose(() => {
2237
+ subCanvas.canvasNode.dispose();
2238
+ });
2239
+ subCanvas.canvasNode.onDispose(() => {
2240
+ subCanvas.parentNode.dispose();
2241
+ });
2242
+ }
2243
+ }
2244
+ if (!isExistedNode) {
2245
+ this.onNodeCreateEmitter.fire({
2246
+ node,
2247
+ data: json,
2248
+ json
1800
2249
  });
1801
- subCanvas.canvasNode.onDispose(() => {
1802
- subCanvas.parentNode.dispose();
2250
+ } else {
2251
+ this.onNodeUpdateEmitter.fire({
2252
+ node,
2253
+ data: json,
2254
+ json
1803
2255
  });
1804
2256
  }
1805
- this.onNodeCreateEmitter.fire({
1806
- node,
1807
- data: json
2257
+ return node;
2258
+ }
2259
+ /**
2260
+ * 添加节点,如果节点已经存在则不会重复创建
2261
+ * @param data
2262
+ * @param addedNodes
2263
+ */
2264
+ addNode(data, addedNodes, ignoreCreateAndUpdateEvent) {
2265
+ const { id, type = "block", originParent, parent, meta, hidden, index } = data;
2266
+ let node = this.getNode(id);
2267
+ let isNew = false;
2268
+ const register = this.getNodeRegistry(type, data.originParent);
2269
+ if (node && node.flowNodeType !== data.type) {
2270
+ node.dispose();
2271
+ node = void 0;
2272
+ }
2273
+ if (!node) {
2274
+ const { dataRegistries } = register;
2275
+ node = this.entityManager.createEntity(WorkflowNodeEntity, {
2276
+ id,
2277
+ document: this,
2278
+ flowNodeType: type,
2279
+ originParent,
2280
+ meta
2281
+ });
2282
+ this.options.preNodeCreate?.(node);
2283
+ const datas = dataRegistries ? this.nodeDataRegistries.concat(...dataRegistries) : this.nodeDataRegistries;
2284
+ node.addInitializeData(datas);
2285
+ node.ports = node.getData(WorkflowNodePortsData);
2286
+ node.lines = node.getData(WorkflowNodeLinesData);
2287
+ node.onDispose(() => this.onNodeDisposeEmitter.fire({ node }));
2288
+ this.options.fromNodeJSON?.(node, data, true);
2289
+ isNew = true;
2290
+ } else {
2291
+ this.options.fromNodeJSON?.(node, data, false);
2292
+ }
2293
+ node.initData({
2294
+ originParent,
2295
+ parent,
2296
+ meta,
2297
+ hidden,
2298
+ index
1808
2299
  });
2300
+ addedNodes?.push(node);
2301
+ if (register.onCreate) {
2302
+ const extendNodes = register.onCreate(node, data);
2303
+ if (extendNodes && addedNodes) {
2304
+ addedNodes.push(...extendNodes);
2305
+ }
2306
+ }
2307
+ if (!ignoreCreateAndUpdateEvent) {
2308
+ if (isNew) {
2309
+ this.onNodeCreateEmitter.fire({
2310
+ node,
2311
+ data,
2312
+ json: data
2313
+ });
2314
+ } else {
2315
+ this.onNodeUpdateEmitter.fire({ node, data, json: data });
2316
+ }
2317
+ }
1809
2318
  return node;
1810
2319
  }
2320
+ get layout() {
2321
+ const layout = this.layouts.find((layout2) => layout2.name == this.currentLayoutKey);
2322
+ if (!layout) {
2323
+ throw new Error(`Unknown flow layout: ${this.currentLayoutKey}`);
2324
+ }
2325
+ return layout;
2326
+ }
1811
2327
  /**
1812
2328
  * 获取默认的 x y 坐标, 默认为当前画布可视区域中心
1813
2329
  * @param type
@@ -1839,7 +2355,7 @@ var WorkflowDocument = class extends FlowDocument {
1839
2355
  throw new Error(`[WorkflowDocument.createWorkflowNodeByType] Node Id "${id}" duplicated.`);
1840
2356
  }
1841
2357
  }
1842
- return this.createWorkflowNode(
2358
+ return this._createWorkflowNode(
1843
2359
  {
1844
2360
  ...json,
1845
2361
  id,
@@ -1850,15 +2366,17 @@ var WorkflowDocument = class extends FlowDocument {
1850
2366
  blocks: json?.blocks,
1851
2367
  edges: json?.edges
1852
2368
  },
1853
- false,
1854
- parentID
2369
+ { parentID }
1855
2370
  );
1856
2371
  }
1857
2372
  getAllNodes() {
1858
- return this.entityManager.getEntities(WorkflowNodeEntity).filter((n) => n.id !== FlowNodeBaseType.ROOT);
2373
+ return this.entityManager.getEntities(WorkflowNodeEntity).filter((n) => n.id !== FlowNodeBaseType2.ROOT);
2374
+ }
2375
+ getAllEdges() {
2376
+ return this.entityManager.getEntities(WorkflowLineEntity);
1859
2377
  }
1860
2378
  getAllPorts() {
1861
- return this.entityManager.getEntities(WorkflowPortEntity).filter((p) => p.node.id !== FlowNodeBaseType.ROOT);
2379
+ return this.entityManager.getEntities(WorkflowPortEntity).filter((p) => p.node.id !== FlowNodeBaseType2.ROOT);
1862
2380
  }
1863
2381
  /**
1864
2382
  * 获取画布中的非游离节点
@@ -1873,10 +2391,13 @@ var WorkflowDocument = class extends FlowDocument {
1873
2391
  from: line.from.id,
1874
2392
  to: line.to.id
1875
2393
  }));
1876
- const startNodeId = allNode.find((node) => node.isStart).id;
1877
- const endNodeId = allNode.find((node) => node.isNodeEnd).id;
1878
- const nodeInSubCanvas = allNode.filter((node) => node.parent?.flowNodeType === FlowNodeBaseType.SUB_CANVAS).map((node) => node.id);
1879
- const associatedCache = /* @__PURE__ */ new Set([endNodeId, ...nodeInSubCanvas]);
2394
+ const startNodeId = allNode.find((node) => node.isStart)?.id;
2395
+ const endNodeId = allNode.find((node) => node.isNodeEnd)?.id;
2396
+ const nodeInContainer = allNode.filter((node) => node.parent?.getNodeMeta().isContainer).map((node) => node.id);
2397
+ const associatedCache = new Set(nodeInContainer);
2398
+ if (endNodeId) {
2399
+ associatedCache.add(endNodeId);
2400
+ }
1880
2401
  const bfs = (nodeId) => {
1881
2402
  if (associatedCache.has(nodeId)) {
1882
2403
  return;
@@ -1890,7 +2411,9 @@ var WorkflowDocument = class extends FlowDocument {
1890
2411
  }, []);
1891
2412
  nextNodes.forEach(bfs);
1892
2413
  };
1893
- bfs(startNodeId);
2414
+ if (startNodeId) {
2415
+ bfs(startNodeId);
2416
+ }
1894
2417
  const associatedNodes = allNode.filter((node) => associatedCache.has(node.id));
1895
2418
  return associatedNodes;
1896
2419
  }
@@ -1952,7 +2475,7 @@ var WorkflowDocument = class extends FlowDocument {
1952
2475
  x: json.meta.position.x + 30,
1953
2476
  y: json.meta.position.y + 30
1954
2477
  };
1955
- return this.createWorkflowNode(
2478
+ return this._createWorkflowNode(
1956
2479
  {
1957
2480
  id: newNodeId || `1${nanoid2()}`,
1958
2481
  type: node.flowNodeType,
@@ -1964,16 +2487,17 @@ var WorkflowDocument = class extends FlowDocument {
1964
2487
  blocks: json.blocks,
1965
2488
  edges: json.edges
1966
2489
  },
1967
- true,
1968
- node.parent?.id
2490
+ {
2491
+ parentID: node.parent?.id
2492
+ }
1969
2493
  );
1970
2494
  }
1971
- copyNodeFromJSON(flowNodeType, nodeJSON, newNodeId, position, parentId) {
2495
+ copyNodeFromJSON(flowNodeType, nodeJSON, newNodeId, position, parentID) {
1972
2496
  position = position || {
1973
2497
  x: nodeJSON.meta.position.x + 30,
1974
2498
  y: nodeJSON.meta.position.y + 30
1975
2499
  };
1976
- return this.createWorkflowNode(
2500
+ return this._createWorkflowNode(
1977
2501
  {
1978
2502
  id: newNodeId || `1${nanoid2()}`,
1979
2503
  type: flowNodeType,
@@ -1985,8 +2509,9 @@ var WorkflowDocument = class extends FlowDocument {
1985
2509
  blocks: nodeJSON.blocks,
1986
2510
  edges: nodeJSON.edges
1987
2511
  },
1988
- true,
1989
- parentId
2512
+ {
2513
+ parentID
2514
+ }
1990
2515
  );
1991
2516
  }
1992
2517
  canRemove(node, silent) {
@@ -2002,136 +2527,59 @@ var WorkflowDocument = class extends FlowDocument {
2002
2527
  /**
2003
2528
  * 判断端口是否为错误态
2004
2529
  */
2005
- isErrorPort(port) {
2530
+ isErrorPort(port, defaultValue = false) {
2006
2531
  if (typeof this.options.isErrorPort === "function") {
2007
2532
  return this.options.isErrorPort(port);
2008
2533
  }
2009
- return false;
2534
+ return defaultValue;
2010
2535
  }
2011
2536
  /**
2012
2537
  * 导出数据
2013
2538
  */
2014
2539
  toJSON() {
2540
+ if (this.disposed) {
2541
+ throw new Error(
2542
+ "The WorkflowDocument has been disposed and it is no longer possible to call toJSON."
2543
+ );
2544
+ }
2015
2545
  const rootJSON = this.toNodeJSON(this.root);
2016
- return {
2546
+ const json = {
2017
2547
  nodes: rootJSON.blocks ?? [],
2018
2548
  edges: rootJSON.edges ?? []
2019
2549
  };
2550
+ return json;
2020
2551
  }
2021
2552
  dispose() {
2022
- if (this.disposed) {
2023
- return;
2024
- }
2025
2553
  super.dispose();
2026
- this.disposed = true;
2027
2554
  this._onReloadEmitter.dispose();
2028
2555
  }
2029
- getEdgeID(edge) {
2030
- return WorkflowLineEntity.portInfoToLineId({
2031
- from: edge.sourceNodeID,
2032
- to: edge.targetNodeID,
2033
- fromPort: edge.sourcePortID,
2034
- toPort: edge.targetPortID
2035
- });
2036
- }
2037
- /**
2038
- * 拍平树形json结构,将结构信息提取到map
2039
- */
2040
- flatJSON(json = { nodes: [], edges: [] }) {
2041
- const nodeBlocks = /* @__PURE__ */ new Map();
2042
- const nodeEdges = /* @__PURE__ */ new Map();
2043
- const rootNodes = json.nodes ?? [];
2044
- const rootEdges = json.edges ?? [];
2045
- const flattenNodeJSONs = [...rootNodes];
2046
- const flattenEdgeJSONs = [...rootEdges];
2047
- const rootBlockIDs = rootNodes.map((node) => node.id);
2048
- const rootEdgeIDs = rootEdges.map((edge) => this.getEdgeID(edge));
2049
- nodeBlocks.set(FlowNodeBaseType.ROOT, rootBlockIDs);
2050
- nodeEdges.set(FlowNodeBaseType.ROOT, rootEdgeIDs);
2051
- rootNodes.forEach((nodeJSON) => {
2052
- const { blocks, edges } = nodeJSON;
2053
- if (blocks) {
2054
- flattenNodeJSONs.push(...blocks);
2055
- const blockIDs = [];
2056
- blocks.forEach((block) => {
2057
- blockIDs.push(block.id);
2058
- });
2059
- nodeBlocks.set(nodeJSON.id, blockIDs);
2060
- delete nodeJSON.blocks;
2061
- }
2062
- if (edges) {
2063
- flattenEdgeJSONs.push(...edges);
2064
- const edgeIDs = [];
2065
- edges.forEach((edge) => {
2066
- const edgeID = this.getEdgeID(edge);
2067
- edgeIDs.push(edgeID);
2068
- });
2069
- nodeEdges.set(nodeJSON.id, edgeIDs);
2070
- delete nodeJSON.edges;
2071
- }
2072
- });
2073
- const flattenJSON = {
2074
- nodes: flattenNodeJSONs,
2075
- edges: flattenEdgeJSONs
2076
- };
2077
- return {
2078
- flattenJSON,
2079
- nodeBlocks,
2080
- nodeEdges
2081
- };
2082
- }
2083
2556
  /**
2084
- * 对JSON进行分层
2557
+ * 批量添加节点
2558
+ * @deprecated use 'batchAddFromJSON' instead
2559
+ * @param json
2560
+ * @param options
2085
2561
  */
2086
- nestJSON(flattenJSON, nodeBlocks, nodeEdges) {
2087
- const nestJSON = {
2088
- nodes: [],
2089
- edges: []
2090
- };
2091
- const nodeMap = /* @__PURE__ */ new Map();
2092
- const edgeMap = /* @__PURE__ */ new Map();
2093
- const rootBlockSet = new Set(nodeBlocks.get(FlowNodeBaseType.ROOT) ?? []);
2094
- const rootEdgeSet = new Set(nodeEdges.get(FlowNodeBaseType.ROOT) ?? []);
2095
- flattenJSON.nodes.forEach((nodeJSON) => {
2096
- nodeMap.set(nodeJSON.id, nodeJSON);
2097
- });
2098
- flattenJSON.edges.forEach((edgeJSON) => {
2099
- const edgeID = this.getEdgeID(edgeJSON);
2100
- edgeMap.set(edgeID, edgeJSON);
2101
- });
2102
- flattenJSON.nodes.forEach((nodeJSON) => {
2103
- if (rootBlockSet.has(nodeJSON.id)) {
2104
- nestJSON.nodes.push(nodeJSON);
2105
- }
2106
- if (nodeBlocks.has(nodeJSON.id)) {
2107
- const blockIDs = nodeBlocks.get(nodeJSON.id);
2108
- const blockJSONs = blockIDs.map((blockID) => nodeMap.get(blockID)).filter(Boolean);
2109
- nodeJSON.blocks = blockJSONs;
2110
- }
2111
- if (nodeEdges.has(nodeJSON.id)) {
2112
- const edgeIDs = nodeEdges.get(nodeJSON.id);
2113
- const edgeJSONs = edgeIDs.map((edgeID) => edgeMap.get(edgeID)).filter(Boolean);
2114
- nodeJSON.edges = edgeJSONs;
2115
- }
2116
- });
2117
- flattenJSON.edges.forEach((edgeJSON) => {
2118
- const edgeID = this.getEdgeID(edgeJSON);
2119
- if (rootEdgeSet.has(edgeID)) {
2120
- nestJSON.edges.push(edgeJSON);
2121
- }
2122
- });
2123
- return nestJSON;
2562
+ renderJSON(json, options) {
2563
+ return this.batchAddFromJSON(json, options);
2124
2564
  }
2125
2565
  /**
2126
- * 逐层创建节点和线条
2566
+ * 批量添加节点
2127
2567
  */
2128
- renderJSON(json, options) {
2129
- const { parent = this.root, isClone = false } = options ?? {};
2130
- const containerID = this.getNodeSubCanvas(parent)?.canvasNode.id ?? parent.id;
2131
- json.nodes.forEach((nodeJSON) => {
2132
- this.createWorkflowNode(nodeJSON, isClone, containerID);
2133
- }), // 创建线条
2134
- json.edges.forEach((edge) => this.createWorkflowLine(edge, containerID));
2568
+ batchAddFromJSON(json, options) {
2569
+ const { parent = this.root, onNodeCreated, onEdgeCreated } = options ?? {};
2570
+ const parentID = this.getNodeSubCanvas(parent)?.canvasNode.id ?? parent.id;
2571
+ const processedJSON = buildGroupJSON(json);
2572
+ const nodes = processedJSON.nodes.map(
2573
+ (nodeJSON) => this._createWorkflowNode(nodeJSON, {
2574
+ parentID,
2575
+ onNodeCreated,
2576
+ onEdgeCreated
2577
+ })
2578
+ );
2579
+ const edges = processedJSON.edges.map((edge) => this.createWorkflowLine(edge, parentID)).filter(Boolean);
2580
+ nodes.forEach((node) => options?.onNodeCreated?.(node));
2581
+ edges.forEach((edge) => options?.onEdgeCreated?.(edge));
2582
+ return { nodes, edges };
2135
2583
  }
2136
2584
  getNodeSubCanvas(node) {
2137
2585
  if (!node) return;
@@ -2140,18 +2588,24 @@ var WorkflowDocument = class extends FlowDocument {
2140
2588
  return subCanvas;
2141
2589
  }
2142
2590
  getNodeChildren(node) {
2143
- if (!node) return [];
2591
+ if (!node || node.flowNodeType === FlowNodeBaseType2.GROUP) return [];
2144
2592
  const subCanvas = this.getNodeSubCanvas(node);
2145
- const childrenWithCanvas = subCanvas ? subCanvas.canvasNode.collapsedChildren : node.collapsedChildren;
2146
- const children = childrenWithCanvas.filter((child) => {
2593
+ const realChildren = subCanvas ? subCanvas.canvasNode.blocks : node.blocks;
2594
+ const childrenWithoutSubCanvas = realChildren.filter((child) => {
2147
2595
  const childMeta = child.getNodeMeta();
2148
2596
  return !childMeta.subCanvas?.(node)?.isCanvas;
2149
2597
  }).filter(Boolean);
2598
+ const children = childrenWithoutSubCanvas.map((child) => {
2599
+ if (child.flowNodeType === FlowNodeBaseType2.GROUP) {
2600
+ return [child, ...child.blocks];
2601
+ }
2602
+ return child;
2603
+ }).flat();
2150
2604
  return children;
2151
2605
  }
2152
2606
  toLineJSON(line) {
2153
2607
  const lineJSON = line.toJSON();
2154
- if (!line.to || !line.info.to || !line.toPort) {
2608
+ if (!line.from || !line.info.from || !line.fromPort || !line.to || !line.info.to || !line.toPort) {
2155
2609
  return;
2156
2610
  }
2157
2611
  const fromSubCanvas = this.getNodeSubCanvas(line.from);
@@ -2173,7 +2627,7 @@ var WorkflowDocument = class extends FlowDocument {
2173
2627
  }
2174
2628
  return lineJSON;
2175
2629
  }
2176
- createWorkflowLine(json, parentId) {
2630
+ createWorkflowLine(json, parentID) {
2177
2631
  const fromNode = this.getNode(json.sourceNodeID);
2178
2632
  const toNode = this.getNode(json.targetNodeID);
2179
2633
  if (!fromNode || !toNode) {
@@ -2183,12 +2637,13 @@ var WorkflowDocument = class extends FlowDocument {
2183
2637
  from: json.sourceNodeID,
2184
2638
  fromPort: json.sourcePortID,
2185
2639
  to: json.targetNodeID,
2186
- toPort: json.targetPortID
2640
+ toPort: json.targetPortID,
2641
+ data: json.data
2187
2642
  };
2188
- if (!parentId) {
2643
+ if (!parentID) {
2189
2644
  return this.linesManager.createLine(lineInfo);
2190
2645
  }
2191
- const canvasNode = this.getNode(parentId);
2646
+ const canvasNode = this.getNode(parentID);
2192
2647
  if (!canvasNode) {
2193
2648
  return this.linesManager.createLine(lineInfo);
2194
2649
  }
@@ -2246,12 +2701,24 @@ function checkDragSuccess(time, e, originLine) {
2246
2701
  }
2247
2702
  return false;
2248
2703
  }
2704
+ function reverseLocation(sourceLocation) {
2705
+ switch (sourceLocation) {
2706
+ case "bottom":
2707
+ return "top";
2708
+ case "left":
2709
+ return "right";
2710
+ case "top":
2711
+ return "bottom";
2712
+ case "right":
2713
+ return "left";
2714
+ }
2715
+ }
2249
2716
  var WorkflowDragService = class {
2250
2717
  constructor() {
2251
- this._onDragLineEventEmitter = new Emitter5();
2718
+ this._onDragLineEventEmitter = new Emitter6();
2252
2719
  this.onDragLineEventChange = this._onDragLineEventEmitter.event;
2253
2720
  this.isDragging = false;
2254
- this._nodesDragEmitter = new Emitter5();
2721
+ this._nodesDragEmitter = new Emitter6();
2255
2722
  this.onNodesDrag = this._nodesDragEmitter.event;
2256
2723
  this._toDispose = new DisposableCollection2();
2257
2724
  this._droppableTransforms = [];
@@ -2269,86 +2736,80 @@ var WorkflowDragService = class {
2269
2736
  }
2270
2737
  /**
2271
2738
  * 拖拽选中节点
2272
- * @param event
2739
+ * @param triggerEvent
2273
2740
  */
2274
- startDragSelectedNodes(event) {
2741
+ async startDragSelectedNodes(triggerEvent) {
2275
2742
  let { selectedNodes } = this.selectService;
2276
- if (selectedNodes.length === 0 || this.playgroundConfig.readonly || this.playgroundConfig.disabled) {
2743
+ if (selectedNodes.length === 0 || this.playgroundConfig.readonly || this.playgroundConfig.disabled || this.isDragging) {
2277
2744
  return Promise.resolve(false);
2278
2745
  }
2279
- const sameParent = this.childrenOfContainer(selectedNodes);
2280
- if (sameParent && sameParent.flowNodeType !== FlowNodeBaseType2.ROOT) {
2281
- selectedNodes = [sameParent];
2282
- }
2283
- const { altKey } = event;
2746
+ this.isDragging = true;
2284
2747
  let startPosition = this.getNodesPosition(selectedNodes);
2285
2748
  let startPositions = selectedNodes.map((node) => {
2286
- const transform = node.getData(TransformData9);
2749
+ const transform = node.getData(TransformData8);
2287
2750
  return { x: transform.position.x, y: transform.position.y };
2288
2751
  });
2289
2752
  let dragSuccess = false;
2290
2753
  const startTime = Date.now();
2291
2754
  const dragger = new PlaygroundDrag({
2292
- onDragStart: () => {
2293
- this.isDragging = true;
2755
+ onDragStart: (dragEvent) => {
2756
+ this._nodesDragEmitter.fire({
2757
+ type: "onDragStart",
2758
+ nodes: selectedNodes,
2759
+ startPositions,
2760
+ dragEvent,
2761
+ triggerEvent,
2762
+ dragger
2763
+ });
2294
2764
  },
2295
- onDrag: (e) => {
2296
- if (!dragSuccess && checkDragSuccess(Date.now() - startTime, e)) {
2765
+ onDrag: (dragEvent) => {
2766
+ if (!dragSuccess && checkDragSuccess(Date.now() - startTime, dragEvent)) {
2297
2767
  dragSuccess = true;
2298
- if (altKey) {
2299
- const tryCopyNodes = selectedNodes;
2300
- if (tryCopyNodes.length > 0) {
2301
- this.selectService.clear();
2302
- this.commandService.executeCommand("PASTE_NODES" /* PASTE_NODES */, tryCopyNodes, true).then((newNodes) => {
2303
- if (newNodes && Array.isArray(newNodes) && newNodes.length > 0) {
2304
- selectedNodes = newNodes;
2305
- startPosition = this.getNodesPosition(tryCopyNodes);
2306
- startPositions = tryCopyNodes.filter((n) => !n.getNodeMeta().copyDisable).map((node) => {
2307
- const transform = node.getData(TransformData9);
2308
- return {
2309
- x: transform.position.x,
2310
- y: transform.position.y
2311
- };
2312
- });
2313
- }
2314
- });
2315
- }
2316
- }
2317
2768
  }
2318
2769
  const offset = this.getDragPosOffset({
2319
- event: e,
2770
+ event: dragEvent,
2320
2771
  selectedNodes,
2321
2772
  startPosition
2322
2773
  });
2774
+ const positions = [];
2323
2775
  selectedNodes.forEach((node, index) => {
2324
- const transform = node.getData(TransformData9);
2776
+ const transform = node.getData(TransformData8);
2325
2777
  const nodeStartPosition = startPositions[index];
2326
2778
  const newPosition = {
2327
2779
  x: nodeStartPosition.x + offset.x,
2328
2780
  y: nodeStartPosition.y + offset.y
2329
2781
  };
2330
- if (node.collapsedChildren?.length > 0) {
2331
- node.collapsedChildren.forEach((childNode) => {
2332
- const childNodeTransformData = childNode.getData(FlowNodeTransformData5);
2333
- childNodeTransformData.fireChange();
2334
- });
2335
- }
2336
2782
  transform.update({
2337
2783
  position: newPosition
2338
2784
  });
2785
+ this.document.layout.updateAffectedTransform(node);
2786
+ positions.push(newPosition);
2787
+ });
2788
+ this._nodesDragEmitter.fire({
2789
+ type: "onDragging",
2790
+ nodes: selectedNodes,
2791
+ startPositions,
2792
+ positions,
2793
+ dragEvent,
2794
+ triggerEvent,
2795
+ dragger
2339
2796
  });
2340
2797
  },
2341
- onDragEnd: () => {
2798
+ onDragEnd: (dragEvent) => {
2342
2799
  this.isDragging = false;
2343
2800
  this._nodesDragEmitter.fire({
2344
2801
  type: "onDragEnd",
2345
2802
  nodes: selectedNodes,
2346
2803
  startPositions,
2347
- altKey
2804
+ dragEvent,
2805
+ triggerEvent,
2806
+ dragger
2348
2807
  });
2808
+ this.resetContainerInternalPosition(selectedNodes);
2349
2809
  }
2350
2810
  });
2351
- return dragger.start(event.clientX, event.clientY, this.playgroundConfig).then(() => dragSuccess);
2811
+ const { clientX, clientY } = MouseTouchEvent.getEventCoord(triggerEvent);
2812
+ return dragger.start(clientX, clientY, this.playgroundConfig)?.then(() => dragSuccess);
2352
2813
  }
2353
2814
  /**
2354
2815
  * 通过拖入卡片添加
@@ -2385,7 +2846,7 @@ var WorkflowDragService = class {
2385
2846
  const targetNode = event.currentTarget;
2386
2847
  domNode = cloneNode ? cloneNode(e) : targetNode.cloneNode(true);
2387
2848
  const bounds = targetNode.getBoundingClientRect();
2388
- startPos = { x: bounds.left, y: bounds.top };
2849
+ startPos = { x: bounds.left + window.scrollX, y: bounds.top + window.scrollY };
2389
2850
  domUtils2.setStyle(domNode, {
2390
2851
  zIndex: 1e3,
2391
2852
  position: "absolute",
@@ -2420,7 +2881,12 @@ var WorkflowDragService = class {
2420
2881
  },
2421
2882
  onDragEnd: async (e) => {
2422
2883
  const dropNode = this._dropNode;
2423
- const dragNode = await this.dropCard(type, e, data, dropNode);
2884
+ const { allowDrop } = this.canDropToNode({
2885
+ dragNodeType: type,
2886
+ dropNodeType: dropNode?.flowNodeType,
2887
+ dropNode
2888
+ });
2889
+ const dragNode = allowDrop ? await this.dropCard(type, e, data, dropNode) : void 0;
2424
2890
  this.clearDrop();
2425
2891
  if (dragNode) {
2426
2892
  domNode.remove();
@@ -2442,25 +2908,25 @@ var WorkflowDragService = class {
2442
2908
  /**
2443
2909
  * 如果存在容器节点,且传入鼠标坐标,需要用容器的坐标减去传入的鼠标坐标
2444
2910
  */
2445
- adjustSubNodePosition(subNodeType, containerNode, mousePos, resetEmptyPos = true) {
2911
+ adjustSubNodePosition(subNodeType, containerNode, mousePos) {
2446
2912
  if (!mousePos) {
2447
2913
  return { x: 0, y: 0 };
2448
2914
  }
2449
- if (!subNodeType || !containerNode || containerNode.flowNodeType === FlowNodeBaseType2.ROOT) {
2915
+ if (!subNodeType || !containerNode || containerNode.flowNodeType === FlowNodeBaseType3.ROOT) {
2450
2916
  return mousePos;
2451
2917
  }
2452
2918
  const isParentEmpty = !containerNode.children || containerNode.children.length === 0;
2453
2919
  const parentPadding = this.document.layout.getPadding(containerNode);
2454
- const parentTransform = containerNode.getData(TransformData9);
2455
- if (isParentEmpty && resetEmptyPos) {
2920
+ const containerWorldTransform = containerNode.transform.transform.worldTransform;
2921
+ if (isParentEmpty) {
2456
2922
  return {
2457
2923
  x: 0,
2458
2924
  y: parentPadding.top
2459
2925
  };
2460
2926
  } else {
2461
2927
  return {
2462
- x: mousePos.x - parentTransform.position.x,
2463
- y: mousePos.y - parentTransform.position.y
2928
+ x: mousePos.x - containerWorldTransform.tx,
2929
+ y: mousePos.y - containerWorldTransform.ty
2464
2930
  };
2465
2931
  }
2466
2932
  }
@@ -2473,6 +2939,35 @@ var WorkflowDragService = class {
2473
2939
  dispose: () => this.posAdjusters.delete(adjuster)
2474
2940
  };
2475
2941
  }
2942
+ /**
2943
+ * 判断是否可以放置节点
2944
+ */
2945
+ canDropToNode(params) {
2946
+ const { canDropToNode } = this.document.options;
2947
+ const { dragNodeType, dropNode } = params;
2948
+ if (canDropToNode) {
2949
+ const result = canDropToNode(params);
2950
+ if (result) {
2951
+ return {
2952
+ allowDrop: true,
2953
+ dropNode
2954
+ };
2955
+ }
2956
+ return {
2957
+ allowDrop: false
2958
+ };
2959
+ }
2960
+ if (!dragNodeType) {
2961
+ return {
2962
+ allowDrop: false,
2963
+ message: "Please select a node to drop"
2964
+ };
2965
+ }
2966
+ return {
2967
+ allowDrop: true,
2968
+ dropNode
2969
+ };
2970
+ }
2476
2971
  /**
2477
2972
  * 获取拖拽偏移
2478
2973
  */
@@ -2503,23 +2998,24 @@ var WorkflowDragService = class {
2503
2998
  return offset;
2504
2999
  }
2505
3000
  updateDroppableTransforms() {
2506
- this._droppableTransforms = this.document.getRenderDatas(FlowNodeTransformData5, false).filter((transform) => {
3001
+ this._droppableTransforms = this.document.getRenderDatas(FlowNodeTransformData6, false).filter((transform) => {
2507
3002
  const { entity } = transform;
2508
3003
  if (entity.originParent) {
2509
3004
  return this.nodeSelectable(entity) && this.nodeSelectable(entity.originParent);
2510
3005
  }
2511
3006
  return this.nodeSelectable(entity);
2512
- }).filter((transform) => {
2513
- const { entity } = transform;
2514
- return entity.flowNodeType === FlowNodeBaseType2.SUB_CANVAS;
2515
- });
3007
+ }).filter((transform) => this.isContainer(transform.entity));
3008
+ }
3009
+ /** 是否容器节点 */
3010
+ isContainer(node) {
3011
+ return node?.getNodeMeta().isContainer ?? false;
2516
3012
  }
2517
3013
  /**
2518
3014
  * 获取节点整体位置
2519
3015
  */
2520
3016
  getNodesPosition(nodes) {
2521
3017
  const selectedBounds = Rectangle8.enlarge(
2522
- nodes.map((n) => n.getData(FlowNodeTransformData5).bounds)
3018
+ nodes.map((n) => n.getData(FlowNodeTransformData6).bounds)
2523
3019
  );
2524
3020
  const position = {
2525
3021
  x: selectedBounds.x,
@@ -2558,18 +3054,30 @@ var WorkflowDragService = class {
2558
3054
  line.highlightColor = color;
2559
3055
  this.hoverService.clearHovered();
2560
3056
  }
2561
- handleDragOnNode(toNode, fromPort, line, toPort, originLine) {
2562
- if (toPort && (originLine?.toPort === toPort || toPort.portType === "input" && this.linesManager.canAddLine(fromPort, toPort, true))) {
2563
- this.hoverService.updateHoveredKey(toPort.id);
2564
- line.setToPort(toPort);
3057
+ checkDraggingPort(isDrawingTo, line, draggingNode, draggingPort, originLine) {
3058
+ let successDrawing = false;
3059
+ if (isDrawingTo) {
3060
+ successDrawing = !!(draggingPort && // 同一条线条则不用在判断 canAddLine
3061
+ (originLine?.toPort === draggingPort || draggingPort.portType === "input" && this.linesManager.canAddLine(line.fromPort, draggingPort, true)));
3062
+ } else {
3063
+ successDrawing = !!(draggingPort && // 同一条线条则不用在判断 canAddLine
3064
+ (originLine?.fromPort === draggingPort || draggingPort.portType === "output" && this.linesManager.canAddLine(draggingPort, line.toPort, true)));
3065
+ }
3066
+ if (successDrawing) {
3067
+ this.hoverService.updateHoveredKey(draggingPort.id);
3068
+ if (isDrawingTo) {
3069
+ line.setToPort(draggingPort);
3070
+ } else {
3071
+ line.setFromPort(draggingPort);
3072
+ }
2565
3073
  this._onDragLineEventEmitter.fire({
2566
3074
  type: "onDrag",
2567
- onDragNodeId: toNode.id
3075
+ onDragNodeId: draggingNode.id
2568
3076
  });
2569
3077
  return {
2570
3078
  hasError: false
2571
3079
  };
2572
- } else if (toNode.flowNodeType === FlowNodeBaseType2.SUB_CANVAS) {
3080
+ } else if (this.isContainer(draggingNode)) {
2573
3081
  return {
2574
3082
  hasError: false
2575
3083
  };
@@ -2580,12 +3088,44 @@ var WorkflowDragService = class {
2580
3088
  };
2581
3089
  }
2582
3090
  }
3091
+ /**
3092
+ * 容器内子节点总体位置重置为0
3093
+ */
3094
+ resetContainerInternalPosition(nodes) {
3095
+ const container = this.childrenOfContainer(nodes);
3096
+ if (!container) {
3097
+ return;
3098
+ }
3099
+ const bounds = Rectangle8.enlarge(
3100
+ container.blocks.map((node) => {
3101
+ const x = node.transform.position.x - node.transform.bounds.width / 2;
3102
+ const y = node.transform.position.y;
3103
+ const width = node.transform.bounds.width;
3104
+ const height = node.transform.bounds.height;
3105
+ return new Rectangle8(x, y, width, height);
3106
+ })
3107
+ );
3108
+ const containerTransform = container.getData(TransformData8);
3109
+ this.operationService.updateNodePosition(container, {
3110
+ x: containerTransform.position.x + bounds.x,
3111
+ y: containerTransform.position.y + bounds.y
3112
+ });
3113
+ this.document.layout.updateAffectedTransform(container);
3114
+ container.blocks.forEach((node) => {
3115
+ const transform = node.getData(TransformData8);
3116
+ this.operationService.updateNodePosition(node, {
3117
+ x: transform.position.x - bounds.x,
3118
+ y: transform.position.y - bounds.y
3119
+ });
3120
+ this.document.layout.updateAffectedTransform(node);
3121
+ });
3122
+ }
2583
3123
  childrenOfContainer(nodes) {
2584
3124
  if (nodes.length === 0) {
2585
3125
  return;
2586
3126
  }
2587
3127
  const sourceContainer = nodes[0]?.parent;
2588
- if (!sourceContainer || sourceContainer.collapsedChildren.length !== nodes.length) {
3128
+ if (!sourceContainer || sourceContainer.flowNodeType === FlowNodeBaseType3.ROOT) {
2589
3129
  return;
2590
3130
  }
2591
3131
  const valid = nodes.every((node) => node?.parent === sourceContainer);
@@ -2599,15 +3139,18 @@ var WorkflowDragService = class {
2599
3139
  * @param opts
2600
3140
  * @param event
2601
3141
  */
2602
- async startDrawingLine(fromPort, event, originLine) {
2603
- const isFromInActivePort = !originLine && fromPort.isErrorPort() && fromPort.disabled;
2604
- if (originLine?.disabled || isFromInActivePort || this.playgroundConfig.readonly || this.playgroundConfig.disabled) {
3142
+ async startDrawingLine(port, event, originLine) {
3143
+ const isDrawingTo = port.portType === "output";
3144
+ const isInActivePort = !originLine && port.isErrorPort() && port.disabled;
3145
+ if (originLine?.disabled || isInActivePort || this.playgroundConfig.readonly || this.playgroundConfig.disabled) {
2605
3146
  return { dragSuccess: false, newLine: void 0 };
2606
3147
  }
3148
+ this.selectService.clear();
2607
3149
  const config = this.playgroundConfig;
2608
3150
  const deferred = new PromiseDeferred();
2609
3151
  const preCursor = config.cursor;
2610
- let line, toPort, toNode, lineErrorReset = false;
3152
+ let line;
3153
+ let newLineInfo;
2611
3154
  const startTime = Date.now();
2612
3155
  let dragSuccess = false;
2613
3156
  const dragger = new PlaygroundDrag({
@@ -2617,57 +3160,49 @@ var WorkflowDragService = class {
2617
3160
  originLine.highlightColor = this.linesManager.lineColor.hidden;
2618
3161
  }
2619
3162
  dragSuccess = true;
2620
- line = this.linesManager.createLine({
2621
- from: fromPort.node.id,
2622
- fromPort: fromPort.portID,
2623
- drawingTo: config.getPosFromMouseEvent(event)
2624
- });
3163
+ const pos = config.getPosFromMouseEvent(event);
3164
+ if (isDrawingTo) {
3165
+ line = this.linesManager.createLine({
3166
+ from: port.node.id,
3167
+ fromPort: port.portID,
3168
+ data: originLine?.lineData,
3169
+ drawingTo: {
3170
+ x: pos.x,
3171
+ y: pos.y,
3172
+ location: port.location === "right" ? "left" : "top"
3173
+ }
3174
+ });
3175
+ } else {
3176
+ line = this.linesManager.createLine({
3177
+ to: port.node.id,
3178
+ toPort: port.portID,
3179
+ data: originLine?.lineData,
3180
+ drawingFrom: {
3181
+ x: pos.x,
3182
+ y: pos.y,
3183
+ location: port.location === "left" ? "right" : "bottom"
3184
+ }
3185
+ });
3186
+ }
2625
3187
  if (!line) {
2626
3188
  return;
2627
3189
  }
2628
3190
  config.updateCursor("grab");
2629
- line.highlightColor = this.linesManager.lineColor.drawing;
3191
+ line.highlightColor = originLine?.lockedColor || this.linesManager.lineColor.drawing;
2630
3192
  this.hoverService.updateHoveredKey("");
2631
3193
  }
2632
3194
  if (!line) {
2633
3195
  return;
2634
3196
  }
2635
- lineErrorReset = false;
2636
3197
  const dragPos = config.getPosFromMouseEvent(e);
2637
- toNode = this.linesManager.getNodeFromMousePos(dragPos);
2638
- toPort = this.linesManager.getPortFromMousePos(dragPos);
2639
- if (!toPort) {
2640
- line.setToPort(void 0);
2641
- } else if (!this.linesManager.canAddLine(fromPort, toPort, true)) {
2642
- line.highlightColor = this.linesManager.lineColor.error;
2643
- lineErrorReset = true;
2644
- line.setToPort(void 0);
2645
- } else {
2646
- line.setToPort(toPort);
2647
- }
2648
- this._onDragLineEventEmitter.fire({
2649
- type: "onDrag"
2650
- });
2651
- this.setLineColor(line, this.linesManager.lineColor.drawing);
2652
- if (toNode && toNode.flowNodeType !== FlowNodeBaseType2.SUB_CANVAS) {
2653
- const portsData = toNode.getData(WorkflowNodePortsData);
2654
- toPort = portsData.inputPorts[0];
2655
- const { hasError } = this.handleDragOnNode(toNode, fromPort, line, toPort, originLine);
2656
- lineErrorReset = hasError;
2657
- }
2658
- if (line.toPort) {
2659
- line.drawingTo = { x: line.toPort.point.x, y: line.toPort.point.y };
2660
- } else {
2661
- line.drawingTo = { x: dragPos.x, y: dragPos.y };
2662
- }
2663
- originLine?.validate();
2664
- line.validate();
3198
+ newLineInfo = this.updateDrawingLine(isDrawingTo, line, dragPos, originLine);
2665
3199
  },
2666
3200
  // eslint-disable-next-line complexity
2667
3201
  onDragEnd: async (e) => {
2668
3202
  const dragPos = config.getPosFromMouseEvent(e);
2669
3203
  const onDragLineEndCallbacks = Array.from(this._onDragLineEndCallbacks.values());
2670
3204
  config.updateCursor(preCursor);
3205
+ const { fromPort, toPort, hasError } = newLineInfo || {};
2671
3206
  await Promise.all(
2672
3207
  onDragLineEndCallbacks.map(
2673
3208
  (callback) => callback({
@@ -2692,35 +3227,32 @@ var WorkflowDragService = class {
2692
3227
  deferred.resolve({ dragSuccess });
2693
3228
  };
2694
3229
  if (dragSuccess) {
2695
- if (originLine && originLine.toPort === toPort) {
3230
+ if (originLine && originLine.toPort === toPort && originLine.fromPort === fromPort) {
2696
3231
  return end();
2697
3232
  }
2698
- if (toPort && toPort.portType !== "input") {
3233
+ if (toPort && toPort.portType !== "input" || fromPort && fromPort.portType !== "output") {
2699
3234
  return end();
2700
3235
  }
2701
- const newLineInfo = toPort ? {
3236
+ const newLinePortInfo = toPort && fromPort ? {
2702
3237
  from: fromPort.node.id,
2703
3238
  fromPort: fromPort.portID,
2704
3239
  to: toPort.node.id,
2705
- toPort: toPort.portID
3240
+ toPort: toPort.portID,
3241
+ data: originLine?.lineData
2706
3242
  } : void 0;
2707
- const isReset = originLine && toPort;
2708
- if (isReset && !this.linesManager.canReset(
2709
- originLine.fromPort,
2710
- originLine.toPort,
2711
- toPort
2712
- )) {
3243
+ const isReset = originLine && newLinePortInfo;
3244
+ if (isReset && !this.linesManager.canReset(originLine, newLinePortInfo)) {
2713
3245
  return end();
2714
3246
  }
2715
- if (originLine && (!this.linesManager.canRemove(originLine, newLineInfo, false) || lineErrorReset)) {
3247
+ if (originLine && (!this.linesManager.canRemove(originLine, newLinePortInfo, false) || hasError)) {
2716
3248
  return end();
2717
3249
  } else {
2718
3250
  originLine?.dispose();
2719
3251
  }
2720
- if (!toPort || !this.linesManager.canAddLine(fromPort, toPort, false)) {
3252
+ if (!newLinePortInfo || !this.linesManager.canAddLine(fromPort, toPort, false)) {
2721
3253
  return end();
2722
3254
  }
2723
- const newLine = this.linesManager.createLine(newLineInfo);
3255
+ const newLine = this.linesManager.createLine(newLinePortInfo);
2724
3256
  if (!newLine) {
2725
3257
  end();
2726
3258
  }
@@ -2733,17 +3265,113 @@ var WorkflowDragService = class {
2733
3265
  }
2734
3266
  }
2735
3267
  });
2736
- await dragger.start(event.clientX, event.clientY, config);
3268
+ const { clientX, clientY } = MouseTouchEvent.getEventCoord(event);
3269
+ await dragger.start(clientX, clientY, config);
2737
3270
  return deferred.promise;
2738
3271
  }
3272
+ updateDrawingLine(isDrawingTo, line, dragPos, originLine) {
3273
+ let hasError = false;
3274
+ const mouseNode = this.linesManager.getNodeFromMousePos(dragPos);
3275
+ let toNode;
3276
+ let toPort;
3277
+ let fromPort;
3278
+ let fromNode;
3279
+ if (isDrawingTo) {
3280
+ fromPort = line.fromPort;
3281
+ toNode = mouseNode;
3282
+ toPort = this.linesManager.getPortFromMousePos(dragPos, "input");
3283
+ if (toNode && this.canBuildContainerLine(toNode, dragPos)) {
3284
+ toPort = this.getNearestPort(toNode, dragPos, "input");
3285
+ hasError = this.checkDraggingPort(isDrawingTo, line, toNode, toPort, originLine).hasError;
3286
+ }
3287
+ if (!toPort) {
3288
+ line.setToPort(void 0);
3289
+ } else if (!this.linesManager.canAddLine(fromPort, toPort, true)) {
3290
+ hasError = true;
3291
+ line.setToPort(void 0);
3292
+ } else {
3293
+ line.setToPort(toPort);
3294
+ }
3295
+ if (line.toPort) {
3296
+ line.drawingTo = {
3297
+ x: line.toPort.point.x,
3298
+ y: line.toPort.point.y,
3299
+ location: line.toPort.location
3300
+ };
3301
+ } else {
3302
+ line.drawingTo = {
3303
+ x: dragPos.x,
3304
+ y: dragPos.y,
3305
+ location: reverseLocation(line.fromPort.location)
3306
+ };
3307
+ }
3308
+ } else {
3309
+ toPort = line.toPort;
3310
+ fromNode = mouseNode;
3311
+ fromPort = this.linesManager.getPortFromMousePos(dragPos, "output");
3312
+ if (fromNode && this.canBuildContainerLine(fromNode, dragPos)) {
3313
+ fromPort = this.getNearestPort(fromNode, dragPos, "output");
3314
+ hasError = this.checkDraggingPort(
3315
+ isDrawingTo,
3316
+ line,
3317
+ fromNode,
3318
+ fromPort,
3319
+ originLine
3320
+ ).hasError;
3321
+ }
3322
+ if (!fromPort) {
3323
+ line.setFromPort(void 0);
3324
+ } else if (!this.linesManager.canAddLine(fromPort, toPort, true)) {
3325
+ hasError = true;
3326
+ line.setFromPort(void 0);
3327
+ } else {
3328
+ line.setFromPort(fromPort);
3329
+ }
3330
+ if (line.fromPort) {
3331
+ line.drawingFrom = {
3332
+ x: line.fromPort.point.x,
3333
+ y: line.fromPort.point.y,
3334
+ location: line.fromPort.location
3335
+ };
3336
+ } else {
3337
+ line.drawingFrom = {
3338
+ x: dragPos.x,
3339
+ y: dragPos.y,
3340
+ location: reverseLocation(line.toPort.location)
3341
+ };
3342
+ }
3343
+ }
3344
+ this._onDragLineEventEmitter.fire({
3345
+ type: "onDrag"
3346
+ });
3347
+ if (hasError) {
3348
+ this.setLineColor(line, this.linesManager.lineColor.error);
3349
+ } else {
3350
+ this.setLineColor(line, originLine?.lockedColor || this.linesManager.lineColor.drawing);
3351
+ }
3352
+ originLine?.validate();
3353
+ line.validate();
3354
+ return {
3355
+ fromPort,
3356
+ toPort,
3357
+ hasError
3358
+ };
3359
+ }
2739
3360
  /**
2740
3361
  * 重新连接线条
2741
3362
  * @param line
2742
3363
  * @param e
2743
3364
  */
2744
3365
  async resetLine(line, e) {
2745
- const { fromPort } = line;
2746
- const { dragSuccess } = await this.startDrawingLine(fromPort, e, line);
3366
+ const { fromPort, toPort } = line;
3367
+ const mousePos = this.playgroundConfig.getPosFromMouseEvent(e);
3368
+ const distanceFrom = Point.getDistance(fromPort.point, mousePos);
3369
+ const distanceTo = Point.getDistance(toPort.point, mousePos);
3370
+ const { dragSuccess } = await this.startDrawingLine(
3371
+ distanceTo <= distanceFrom || !this.document.options.twoWayConnection ? fromPort : toPort,
3372
+ e,
3373
+ line
3374
+ );
2747
3375
  if (!dragSuccess) {
2748
3376
  this.selectService.select(line);
2749
3377
  }
@@ -2758,6 +3386,36 @@ var WorkflowDragService = class {
2758
3386
  }
2759
3387
  };
2760
3388
  }
3389
+ /** 能否建立容器连线 */
3390
+ canBuildContainerLine(node, mousePos) {
3391
+ const isContainer = this.isContainer(node);
3392
+ if (!isContainer) {
3393
+ return true;
3394
+ }
3395
+ const { padding, bounds } = node.transform;
3396
+ const DEFAULT_DELTA = 10;
3397
+ const leftDelta = padding.left * 2 / 3 || DEFAULT_DELTA;
3398
+ const rightDelta = padding.right * 2 / 3 || DEFAULT_DELTA;
3399
+ const bottomDelta = padding.bottom * 2 / 3 || DEFAULT_DELTA;
3400
+ const topDelta = padding.top * 2 / 3 || DEFAULT_DELTA;
3401
+ const rectangles = [
3402
+ new Rectangle8(bounds.x, bounds.y, leftDelta, bounds.height),
3403
+ // left
3404
+ new Rectangle8(bounds.x, bounds.y, bounds.width, topDelta),
3405
+ // top
3406
+ new Rectangle8(bounds.x, bounds.y + bounds.height - bottomDelta, bounds.width, bottomDelta),
3407
+ // bottom
3408
+ new Rectangle8(bounds.x + bounds.width - rightDelta, bounds.y, rightDelta, bounds.height)
3409
+ // right
3410
+ ];
3411
+ return rectangles.some((rect) => rect.contains(mousePos.x, mousePos.y));
3412
+ }
3413
+ /** 获取最近的 port */
3414
+ getNearestPort(node, mousePos, portType = "input") {
3415
+ const portsData = node.ports;
3416
+ const distanceSortedPorts = (portType === "input" ? portsData.inputPorts : portsData.outputPorts).sort((a, b) => Point.getDistance(mousePos, a.point) - Point.getDistance(mousePos, b.point));
3417
+ return distanceSortedPorts[0];
3418
+ }
2761
3419
  };
2762
3420
  __decorateClass([
2763
3421
  inject6(PlaygroundConfigEntity5)
@@ -2778,7 +3436,7 @@ __decorateClass([
2778
3436
  inject6(WorkflowSelectService)
2779
3437
  ], WorkflowDragService.prototype, "selectService", 2);
2780
3438
  __decorateClass([
2781
- inject6(FlowOperationBaseService)
3439
+ inject6(WorkflowOperationBaseService)
2782
3440
  ], WorkflowDragService.prototype, "operationService", 2);
2783
3441
  __decorateClass([
2784
3442
  inject6(WorkflowDocumentOptions)
@@ -2794,16 +3452,16 @@ WorkflowDragService = __decorateClass([
2794
3452
  import { inject as inject7, injectable as injectable7, postConstruct as postConstruct3 } from "inversify";
2795
3453
  import { PlaygroundConfigEntity as PlaygroundConfigEntity6 } from "@flowgram.ai/core";
2796
3454
  import { EntityManager as EntityManager3 } from "@flowgram.ai/core";
2797
- import { DisposableCollection as DisposableCollection3, Emitter as Emitter6 } from "@flowgram.ai/utils";
3455
+ import { DisposableCollection as DisposableCollection3, Emitter as Emitter7 } from "@flowgram.ai/utils";
2798
3456
 
2799
3457
  // src/utils/layout-to-positions.ts
2800
- import { FlowNodeTransformData as FlowNodeTransformData6 } from "@flowgram.ai/document";
2801
- import { TransformData as TransformData10, startTween } from "@flowgram.ai/core";
3458
+ import { FlowNodeTransformData as FlowNodeTransformData7 } from "@flowgram.ai/document";
3459
+ import { TransformData as TransformData9, startTween } from "@flowgram.ai/core";
2802
3460
  var layoutToPositions = async (nodes, nodePositionMap) => {
2803
3461
  const newNodePositionMap = {};
2804
3462
  nodes.forEach((node) => {
2805
- const transform = node.getData(TransformData10);
2806
- const nodeTransform = node.getData(FlowNodeTransformData6);
3463
+ const transform = node.getData(TransformData9);
3464
+ const nodeTransform = node.getData(FlowNodeTransformData7);
2807
3465
  newNodePositionMap[node.id] = {
2808
3466
  x: transform.position.x,
2809
3467
  y: transform.position.y + nodeTransform.bounds.height / 2
@@ -2816,21 +3474,17 @@ var layoutToPositions = async (nodes, nodePositionMap) => {
2816
3474
  duration: 300,
2817
3475
  onUpdate: (v) => {
2818
3476
  nodes.forEach((node) => {
2819
- const transform = node.getData(TransformData10);
3477
+ const transform = node.getData(TransformData9);
2820
3478
  const deltaX = (nodePositionMap[node.id].x - transform.position.x) * v.d / 100;
2821
3479
  const deltaY = (nodePositionMap[node.id].y - transform.bounds.height / 2 - transform.position.y) * v.d / 100;
2822
- if (node.collapsedChildren?.length > 0) {
2823
- node.collapsedChildren.forEach((childNode) => {
2824
- const childNodeTransformData = childNode.getData(FlowNodeTransformData6);
2825
- childNodeTransformData.fireChange();
2826
- });
2827
- }
2828
3480
  transform.update({
2829
3481
  position: {
2830
3482
  x: transform.position.x + deltaX,
2831
3483
  y: transform.position.y + deltaY
2832
3484
  }
2833
3485
  });
3486
+ const document2 = node.document;
3487
+ document2.layout.updateAffectedTransform(node);
2834
3488
  });
2835
3489
  },
2836
3490
  onComplete: () => {
@@ -2843,7 +3497,7 @@ var layoutToPositions = async (nodes, nodePositionMap) => {
2843
3497
  // src/service/workflow-reset-layout-service.ts
2844
3498
  var WorkflowResetLayoutService = class {
2845
3499
  constructor() {
2846
- this._resetLayoutEmitter = new Emitter6();
3500
+ this._resetLayoutEmitter = new Emitter7();
2847
3501
  /**
2848
3502
  * reset layout事件
2849
3503
  */
@@ -2900,6 +3554,95 @@ WorkflowResetLayoutService = __decorateClass([
2900
3554
  injectable7()
2901
3555
  ], WorkflowResetLayoutService);
2902
3556
 
3557
+ // src/service/workflow-operation-base-service.ts
3558
+ import { inject as inject8 } from "inversify";
3559
+ import { Emitter as Emitter8 } from "@flowgram.ai/utils";
3560
+ import { FlowOperationBaseServiceImpl } from "@flowgram.ai/document";
3561
+ import { TransformData as TransformData10 } from "@flowgram.ai/core";
3562
+ var WorkflowOperationBaseServiceImpl = class extends FlowOperationBaseServiceImpl {
3563
+ constructor() {
3564
+ super(...arguments);
3565
+ this.onNodePostionUpdateEmitter = new Emitter8();
3566
+ this.onNodePostionUpdate = this.onNodePostionUpdateEmitter.event;
3567
+ }
3568
+ updateNodePosition(nodeOrId, position) {
3569
+ const node = this.toNodeEntity(nodeOrId);
3570
+ if (!node) {
3571
+ return;
3572
+ }
3573
+ const transformData = node.getData(TransformData10);
3574
+ const oldPosition = {
3575
+ x: transformData.position.x,
3576
+ y: transformData.position.y
3577
+ };
3578
+ transformData.update({
3579
+ position
3580
+ });
3581
+ this.onNodePostionUpdateEmitter.fire({
3582
+ node,
3583
+ oldPosition,
3584
+ newPosition: position
3585
+ });
3586
+ }
3587
+ fromJSON(json) {
3588
+ if (this.document.disposed) return;
3589
+ const workflowJSON = {
3590
+ nodes: json.nodes ?? [],
3591
+ edges: json.edges ?? []
3592
+ };
3593
+ const oldNodes = this.document.getAllNodes();
3594
+ const oldEdges = this.linesManager.getAllLines();
3595
+ const oldPositionMap = new Map(
3596
+ oldNodes.map((node) => [
3597
+ node.id,
3598
+ {
3599
+ x: node.transform.transform.position.x,
3600
+ y: node.transform.transform.position.y
3601
+ }
3602
+ ])
3603
+ );
3604
+ const newNodes = [];
3605
+ const newEdges = [];
3606
+ this.document.batchAddFromJSON(workflowJSON, {
3607
+ onNodeCreated: (node) => newNodes.push(node),
3608
+ onEdgeCreated: (edge) => newEdges.push(edge)
3609
+ });
3610
+ const newEdgeIDSet = new Set(newEdges.map((edge) => edge.id));
3611
+ oldEdges.forEach((edge) => {
3612
+ if (!newEdgeIDSet.has(edge.id)) {
3613
+ edge.dispose();
3614
+ return;
3615
+ }
3616
+ });
3617
+ const newNodeIDSet = new Set(newNodes.map((node) => node.id));
3618
+ oldNodes.forEach((node) => {
3619
+ if (!newNodeIDSet.has(node.id)) {
3620
+ node.dispose();
3621
+ return;
3622
+ }
3623
+ const oldPosition = oldPositionMap.get(node.id);
3624
+ const newPosition = {
3625
+ x: node.transform.transform.position.x,
3626
+ y: node.transform.transform.position.y
3627
+ };
3628
+ if (oldPosition && (oldPosition.x !== newPosition.x || oldPosition.y !== newPosition.y)) {
3629
+ this.onNodePostionUpdateEmitter.fire({
3630
+ node,
3631
+ oldPosition,
3632
+ newPosition
3633
+ });
3634
+ }
3635
+ });
3636
+ this.document.fireRender();
3637
+ }
3638
+ };
3639
+ __decorateClass([
3640
+ inject8(WorkflowDocument)
3641
+ ], WorkflowOperationBaseServiceImpl.prototype, "document", 2);
3642
+ __decorateClass([
3643
+ inject8(WorkflowLinesManager)
3644
+ ], WorkflowOperationBaseServiceImpl.prototype, "linesManager", 2);
3645
+
2903
3646
  // src/hooks/use-playground-readonly-state.ts
2904
3647
  import { useEffect } from "react";
2905
3648
  import { usePlayground, useRefresh } from "@flowgram.ai/core";
@@ -2920,14 +3663,18 @@ function usePlaygroundReadonlyState(listenChange) {
2920
3663
  function checkTargetDraggable(el) {
2921
3664
  return el && el.tagName !== "INPUT" && el.tagName !== "TEXTAREA" && !el.closest(".flow-canvas-not-draggable");
2922
3665
  }
3666
+ var isFirefox = typeof navigator !== "undefined" && navigator?.userAgent?.includes?.("Firefox");
2923
3667
  function useNodeRender(nodeFromProps) {
2924
3668
  const node = nodeFromProps || useContext(PlaygroundEntityContext);
2925
- const renderData = node.getData(FlowNodeRenderData2);
2926
- const portsData = node.getData(WorkflowNodePortsData);
3669
+ const renderData = node.getData(FlowNodeRenderData3);
3670
+ const portsData = node.ports;
2927
3671
  const readonly = usePlaygroundReadonlyState();
2928
3672
  const dragService = useService(WorkflowDragService);
2929
3673
  const selectionService = useService(WorkflowSelectService);
2930
3674
  const isDragging = useRef(false);
3675
+ const [formValueVersion, updateFormValueVersion] = useState(0);
3676
+ const formValueDependRef = useRef(false);
3677
+ formValueDependRef.current = false;
2931
3678
  const nodeRef = useRef(null);
2932
3679
  const [linkingNodeId, setLinkingNodeId] = useState("");
2933
3680
  useEffect2(() => {
@@ -2944,15 +3691,17 @@ function useNodeRender(nodeFromProps) {
2944
3691
  }, []);
2945
3692
  const startDrag = useCallback(
2946
3693
  (e) => {
2947
- e.preventDefault();
3694
+ MouseTouchEvent2.preventDefault(e);
2948
3695
  if (!selectionService.isSelected(node.id)) {
2949
3696
  selectNode(e);
2950
3697
  }
2951
- if (!checkTargetDraggable(e.target) || !checkTargetDraggable(document.activeElement)) {
2952
- return;
3698
+ if (!MouseTouchEvent2.isTouchEvent(e)) {
3699
+ if (!checkTargetDraggable(e.target) || !checkTargetDraggable(document.activeElement)) {
3700
+ return;
3701
+ }
2953
3702
  }
2954
3703
  isDragging.current = true;
2955
- dragService.startDragSelectedNodes(e).finally(
3704
+ dragService.startDragSelectedNodes(e)?.finally(
2956
3705
  () => setTimeout(() => {
2957
3706
  isDragging.current = false;
2958
3707
  })
@@ -2965,7 +3714,7 @@ function useNodeRender(nodeFromProps) {
2965
3714
  if (isDragging.current) {
2966
3715
  return;
2967
3716
  }
2968
- if (e.metaKey || e.shiftKey || e.ctrlKey) {
3717
+ if (e.shiftKey) {
2969
3718
  selectionService.toggleSelect(node);
2970
3719
  } else {
2971
3720
  selectionService.selectNode(node);
@@ -2978,7 +3727,6 @@ function useNodeRender(nodeFromProps) {
2978
3727
  );
2979
3728
  const deleteNode = useCallback(() => node.dispose(), [node]);
2980
3729
  useListenEvents(portsData.onDataChange);
2981
- const isFirefox = navigator?.userAgent?.includes?.("Firefox");
2982
3730
  const onFocus = useCallback(() => {
2983
3731
  if (isFirefox) {
2984
3732
  nodeRef.current?.setAttribute("draggable", "false");
@@ -2991,8 +3739,8 @@ function useNodeRender(nodeFromProps) {
2991
3739
  }, []);
2992
3740
  const getExtInfo = useCallback(() => node.getExtInfo(), [node]);
2993
3741
  const updateExtInfo = useCallback(
2994
- (data) => {
2995
- node.updateExtInfo(data);
3742
+ (data, fullUpdate) => {
3743
+ node.updateExtInfo(data, fullUpdate);
2996
3744
  },
2997
3745
  [node]
2998
3746
  );
@@ -3001,44 +3749,94 @@ function useNodeRender(nodeFromProps) {
3001
3749
  const toggleExpand = useCallback(() => {
3002
3750
  renderData.toggleExpand();
3003
3751
  }, [renderData]);
3004
- return {
3005
- node,
3006
- selected: selectionService.isSelected(node.id),
3007
- activated: selectionService.isActivated(node.id),
3008
- expanded: renderData.expanded,
3009
- startDrag,
3010
- ports: portsData.allPorts,
3011
- deleteNode,
3012
- selectNode,
3013
- readonly,
3014
- linkingNodeId,
3015
- nodeRef,
3016
- onFocus,
3017
- onBlur,
3018
- getExtInfo,
3019
- updateExtInfo,
3020
- toggleExpand,
3021
- get form() {
3022
- if (!form) return void 0;
3023
- return {
3024
- ...form,
3025
- get values() {
3752
+ const selected = selectionService.isSelected(node.id);
3753
+ const activated = selectionService.isActivated(node.id);
3754
+ const expanded = renderData.expanded;
3755
+ useEffect2(() => {
3756
+ const toDispose = form?.onFormValuesChange(() => {
3757
+ if (formValueDependRef.current) {
3758
+ updateFormValueVersion((v) => v + 1);
3759
+ }
3760
+ });
3761
+ return () => toDispose?.dispose();
3762
+ }, [form]);
3763
+ return useMemo(
3764
+ () => ({
3765
+ id: node.id,
3766
+ type: node.flowNodeType,
3767
+ get data() {
3768
+ if (form) {
3769
+ formValueDependRef.current = true;
3026
3770
  return form.values;
3027
- },
3028
- get state() {
3029
- return formState;
3030
3771
  }
3031
- };
3032
- }
3033
- };
3772
+ return getExtInfo();
3773
+ },
3774
+ updateData(values) {
3775
+ if (form) {
3776
+ form.updateFormValues(values);
3777
+ } else {
3778
+ updateExtInfo(values, true);
3779
+ }
3780
+ },
3781
+ node,
3782
+ selected,
3783
+ activated,
3784
+ expanded,
3785
+ startDrag,
3786
+ get ports() {
3787
+ return portsData.allPorts;
3788
+ },
3789
+ deleteNode,
3790
+ selectNode,
3791
+ readonly,
3792
+ linkingNodeId,
3793
+ nodeRef,
3794
+ onFocus,
3795
+ onBlur,
3796
+ getExtInfo,
3797
+ updateExtInfo,
3798
+ toggleExpand,
3799
+ get form() {
3800
+ if (!form) return void 0;
3801
+ return {
3802
+ ...form,
3803
+ get values() {
3804
+ formValueDependRef.current = true;
3805
+ return form.values;
3806
+ },
3807
+ get state() {
3808
+ return formState;
3809
+ }
3810
+ };
3811
+ }
3812
+ }),
3813
+ [
3814
+ node,
3815
+ selected,
3816
+ activated,
3817
+ expanded,
3818
+ startDrag,
3819
+ deleteNode,
3820
+ selectNode,
3821
+ readonly,
3822
+ linkingNodeId,
3823
+ nodeRef,
3824
+ onFocus,
3825
+ onBlur,
3826
+ getExtInfo,
3827
+ updateExtInfo,
3828
+ toggleExpand,
3829
+ formValueVersion
3830
+ ]
3831
+ );
3034
3832
  }
3035
3833
 
3036
3834
  // src/hooks/use-current-dom-node.ts
3037
- import { FlowNodeRenderData as FlowNodeRenderData3 } from "@flowgram.ai/document";
3835
+ import { FlowNodeRenderData as FlowNodeRenderData4 } from "@flowgram.ai/document";
3038
3836
  import { useEntityFromContext } from "@flowgram.ai/core";
3039
3837
  function useCurrentDomNode() {
3040
3838
  const entity = useEntityFromContext();
3041
- const renderData = entity.getData(FlowNodeRenderData3);
3839
+ const renderData = entity.getData(FlowNodeRenderData4);
3042
3840
  return renderData.node;
3043
3841
  }
3044
3842
 
@@ -3068,20 +3866,20 @@ var InteractiveType = /* @__PURE__ */ ((InteractiveType2) => {
3068
3866
 
3069
3867
  // src/workflow-document-container-module.ts
3070
3868
  import { ContainerModule } from "inversify";
3071
- import { FlowDocument as FlowDocument2, FlowDocumentContribution } from "@flowgram.ai/document";
3072
3869
  import { bindContributions } from "@flowgram.ai/utils";
3870
+ import { FlowDocument as FlowDocument2, FlowDocumentContribution } from "@flowgram.ai/document";
3073
3871
 
3074
3872
  // src/workflow-document-contribution.ts
3075
- import { injectable as injectable8, inject as inject8 } from "inversify";
3873
+ import { injectable as injectable8, inject as inject9 } from "inversify";
3076
3874
  import {
3077
- FlowNodeRenderData as FlowNodeRenderData4,
3078
- FlowNodeTransformData as FlowNodeTransformData7
3875
+ FlowNodeRenderData as FlowNodeRenderData5,
3876
+ FlowNodeTransformData as FlowNodeTransformData8
3079
3877
  } from "@flowgram.ai/document";
3080
3878
  var WorkflowDocumentContribution = class {
3081
3879
  registerDocument(document2) {
3082
3880
  document2.registerNodeDatas(
3083
- FlowNodeTransformData7,
3084
- FlowNodeRenderData4,
3881
+ FlowNodeTransformData8,
3882
+ FlowNodeRenderData5,
3085
3883
  WorkflowNodePortsData,
3086
3884
  WorkflowNodeLinesData
3087
3885
  );
@@ -3089,7 +3887,7 @@ var WorkflowDocumentContribution = class {
3089
3887
  }
3090
3888
  };
3091
3889
  __decorateClass([
3092
- inject8(FreeLayout)
3890
+ inject9(FreeLayout)
3093
3891
  ], WorkflowDocumentContribution.prototype, "freeLayout", 2);
3094
3892
  WorkflowDocumentContribution = __decorateClass([
3095
3893
  injectable8()
@@ -3097,11 +3895,35 @@ WorkflowDocumentContribution = __decorateClass([
3097
3895
 
3098
3896
  // src/utils/get-url-params.ts
3099
3897
  function getUrlParams() {
3100
- return location.search.replace(/^\?/, "").split("&").reduce((res, key) => {
3898
+ const paramsMap = /* @__PURE__ */ new Map();
3899
+ location.search.replace(/^\?/, "").split("&").forEach((key) => {
3900
+ if (!key) return;
3101
3901
  const [k, v] = key.split("=");
3102
- res[k] = v;
3103
- return res;
3104
- }, {});
3902
+ if (k) {
3903
+ const decodedKey = decodeURIComponent(k.trim());
3904
+ const decodedValue = v ? decodeURIComponent(v.trim()) : "";
3905
+ const dangerousProps = [
3906
+ "__proto__",
3907
+ "constructor",
3908
+ "prototype",
3909
+ "__defineGetter__",
3910
+ "__defineSetter__",
3911
+ "__lookupGetter__",
3912
+ "__lookupSetter__",
3913
+ "hasOwnProperty",
3914
+ "isPrototypeOf",
3915
+ "propertyIsEnumerable",
3916
+ "toString",
3917
+ "valueOf",
3918
+ "toLocaleString"
3919
+ ];
3920
+ if (dangerousProps.includes(decodedKey.toLowerCase())) {
3921
+ return;
3922
+ }
3923
+ paramsMap.set(decodedKey, decodedValue);
3924
+ }
3925
+ });
3926
+ return Object.fromEntries(paramsMap);
3105
3927
  }
3106
3928
 
3107
3929
  // src/workflow-document-container-module.ts
@@ -3114,6 +3936,7 @@ var WorkflowDocumentContainerModule = new ContainerModule(
3114
3936
  bind(WorkflowSelectService).toSelf().inSingletonScope();
3115
3937
  bind(WorkflowHoverService).toSelf().inSingletonScope();
3116
3938
  bind(WorkflowResetLayoutService).toSelf().inSingletonScope();
3939
+ bind(WorkflowOperationBaseService).to(WorkflowOperationBaseServiceImpl).inSingletonScope();
3117
3940
  bind(URLParams).toDynamicValue(() => getUrlParams()).inSingletonScope();
3118
3941
  bindContributions(bind, WorkflowDocumentContribution, [FlowDocumentContribution]);
3119
3942
  bind(WorkflowDocumentOptions).toConstantValue({
@@ -3123,81 +3946,6 @@ var WorkflowDocumentContainerModule = new ContainerModule(
3123
3946
  bind(WorkflowDocumentProvider).toDynamicValue((ctx) => () => ctx.container.get(WorkflowDocument)).inSingletonScope();
3124
3947
  }
3125
3948
  );
3126
-
3127
- // src/utils/simple-line.ts
3128
- import { Point, Rectangle as Rectangle9 } from "@flowgram.ai/utils";
3129
- var LINE_PADDING = 12;
3130
- var WorkflowSimpleLineContribution = class {
3131
- constructor(entity) {
3132
- this.entity = entity;
3133
- }
3134
- get path() {
3135
- return this.data?.path ?? "";
3136
- }
3137
- calcDistance(pos) {
3138
- if (!this.data) {
3139
- return Number.MAX_SAFE_INTEGER;
3140
- }
3141
- const [start, end] = this.data.points;
3142
- return Point.getDistance(pos, this.projectPointOnLine(pos, start, end));
3143
- }
3144
- get bounds() {
3145
- if (!this.data) {
3146
- return new Rectangle9();
3147
- }
3148
- return this.data.bbox;
3149
- }
3150
- update(params) {
3151
- const { fromPos, toPos } = params;
3152
- const { vertical } = this.entity;
3153
- const sourceOffset = {
3154
- x: vertical ? 0 : POINT_RADIUS,
3155
- y: vertical ? POINT_RADIUS : 0
3156
- };
3157
- const targetOffset = {
3158
- x: vertical ? 0 : -POINT_RADIUS,
3159
- y: vertical ? -POINT_RADIUS : 0
3160
- };
3161
- const points = [
3162
- {
3163
- x: fromPos.x + sourceOffset.x,
3164
- y: fromPos.y + sourceOffset.y
3165
- },
3166
- {
3167
- x: toPos.x + targetOffset.x,
3168
- y: toPos.y + targetOffset.y
3169
- }
3170
- ];
3171
- const bbox = Rectangle9.createRectangleWithTwoPoints(points[0], points[1]);
3172
- const adjustedPoints = points.map((p) => ({
3173
- x: p.x - bbox.x + LINE_PADDING,
3174
- y: p.y - bbox.y + LINE_PADDING
3175
- }));
3176
- const path = `M ${adjustedPoints[0].x} ${adjustedPoints[0].y} L ${adjustedPoints[1].x} ${adjustedPoints[1].y}`;
3177
- this.data = {
3178
- points,
3179
- path,
3180
- bbox
3181
- };
3182
- }
3183
- projectPointOnLine(point, lineStart, lineEnd) {
3184
- const dx = lineEnd.x - lineStart.x;
3185
- const dy = lineEnd.y - lineStart.y;
3186
- if (dx === 0) {
3187
- return { x: lineStart.x, y: point.y };
3188
- }
3189
- if (dy === 0) {
3190
- return { x: point.x, y: lineStart.y };
3191
- }
3192
- const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
3193
- const clampedT = Math.max(0, Math.min(1, t));
3194
- return {
3195
- x: lineStart.x + clampedT * dx,
3196
- y: lineStart.y + clampedT * dy
3197
- };
3198
- }
3199
- };
3200
- WorkflowSimpleLineContribution.type = "WorkflowSimpleLineContribution";
3201
3949
  export {
3202
3950
  EditorCursorState,
3203
3951
  InteractiveType,
@@ -3223,17 +3971,20 @@ export {
3223
3971
  WorkflowNodeEntity,
3224
3972
  WorkflowNodeLinesData,
3225
3973
  WorkflowNodePortsData,
3974
+ WorkflowOperationBaseService,
3975
+ WorkflowOperationBaseServiceImpl,
3226
3976
  WorkflowPortEntity,
3227
3977
  WorkflowResetLayoutService,
3228
3978
  WorkflowSelectService,
3229
- WorkflowSimpleLineContribution,
3230
3979
  bindConfigEntity,
3980
+ buildGroupJSON,
3231
3981
  compose,
3232
3982
  composeAsync,
3233
3983
  delay,
3234
3984
  domReactToBounds,
3235
3985
  fitView,
3236
3986
  getAntiOverlapPosition,
3987
+ getLineCenter,
3237
3988
  getPortEntityId,
3238
3989
  nanoid,
3239
3990
  useConfigEntity,
@@ -3247,7 +3998,6 @@ export {
3247
3998
  usePlayground2 as usePlayground,
3248
3999
  usePlaygroundContainer,
3249
4000
  usePlaygroundContext,
3250
- usePlaygroundLatest,
3251
4001
  usePlaygroundReadonlyState,
3252
4002
  useRefresh2 as useRefresh,
3253
4003
  useService3 as useService,