@flowgram.ai/free-lines-plugin 0.1.0-alpha.10

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.
@@ -0,0 +1,1362 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/components/workflow-port-render/index.tsx
13
+ import ReactDOM from "react-dom";
14
+ import React2, { useEffect, useState } from "react";
15
+ import classNames from "clsx";
16
+ import {
17
+ WorkflowHoverService,
18
+ usePlaygroundReadonlyState,
19
+ WorkflowLinesManager
20
+ } from "@flowgram.ai/free-layout-core";
21
+ import { MouseTouchEvent, useService } from "@flowgram.ai/core";
22
+
23
+ // src/constants/points.ts
24
+ var STROKE_WIDTH_SLECTED = 3;
25
+ var STROKE_WIDTH = 2;
26
+ var PORT_BG_CLASS_NAME = "workflow-port-bg";
27
+
28
+ // src/components/workflow-port-render/style.ts
29
+ import styled from "styled-components";
30
+ var WorkflowPointStyle = styled.div`
31
+ width: 20px;
32
+ height: 20px;
33
+ border-radius: 50%;
34
+ margin-top: -10px;
35
+ margin-left: -10px;
36
+ left: 50%;
37
+ top: 50%;
38
+ position: absolute;
39
+ // 非 hover 状态下的样式
40
+ border: none;
41
+
42
+ & > .symbol {
43
+ opacity: 0;
44
+ }
45
+
46
+ .bg-circle {
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ position: absolute;
51
+ border-radius: 50%;
52
+ width: 20px;
53
+ height: 20px;
54
+ background-color: var(--g-workflow-port-color-background, #fff);
55
+ transform: scale(0.5);
56
+ transition: all 0.2s linear 0s;
57
+ }
58
+
59
+ .bg {
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ position: relative;
64
+ width: 100%;
65
+ height: 100%;
66
+ border-radius: 50%;
67
+ background: var(--g-workflow-port-color-secondary, #9197f1);
68
+ transform: scale(0.4, 0.4);
69
+ transition: all 0.2s linear 0s;
70
+
71
+ &.hasError {
72
+ background: var(--g-workflow-port-color-error, red);
73
+ }
74
+
75
+ .symbol {
76
+ position: absolute;
77
+ width: 14px;
78
+ height: 14px;
79
+ opacity: 0;
80
+ pointer-events: none;
81
+ color: var(--g-workflow-port-color-background, #fff);
82
+ transition: opacity 0.2s linear 0s;
83
+
84
+ & > svg {
85
+ width: 14px;
86
+ height: 14px;
87
+ }
88
+ }
89
+
90
+ .focus-circle {
91
+ position: absolute;
92
+ display: flex;
93
+ justify-content: center;
94
+ align-items: center;
95
+ width: 8px;
96
+ height: 8px;
97
+ opacity: 0;
98
+ background: var(--g-workflow-port-color-secondary, #9197f1);
99
+ border-radius: 50%;
100
+ transition: opacity 0.2s linear 0s;
101
+ }
102
+ }
103
+
104
+ &.linked .bg:not(.hasError) {
105
+ background: var(--g-workflow-port-color-primary, #4d53e8);
106
+ }
107
+
108
+ &.hovered .bg:not(.hasError) {
109
+ border: none;
110
+ cursor: crosshair;
111
+ transform: scale(1, 1);
112
+ background: var(--g-workflow-port-color-primary, #4d53e8);
113
+
114
+ & > .symbol {
115
+ opacity: 1;
116
+ }
117
+ }
118
+
119
+ .cross-hair {
120
+ position: relative;
121
+ left: 2px;
122
+ top: 2px;
123
+
124
+ &::after,
125
+ &::before {
126
+ content: '';
127
+ background: var(--g-workflow-port-color-background, #fff);
128
+ border-radius: 2px;
129
+ position: absolute;
130
+ }
131
+
132
+ &::after {
133
+ left: 4px;
134
+ width: 2px;
135
+ height: 6px;
136
+ box-shadow: 0 4px var(--g-workflow-port-color-background, #fff);
137
+ }
138
+
139
+ &::before {
140
+ top: 4px;
141
+ width: 6px;
142
+ height: 2px;
143
+ box-shadow: 4px 0 var(--g-workflow-port-color-background, #fff);
144
+ }
145
+ `;
146
+
147
+ // src/components/workflow-port-render/cross-hair.tsx
148
+ import React from "react";
149
+ function CrossHair() {
150
+ return /* @__PURE__ */ React.createElement("div", { className: "symbol" }, /* @__PURE__ */ React.createElement("div", { className: "cross-hair" }));
151
+ }
152
+
153
+ // src/components/workflow-port-render/index.tsx
154
+ var WorkflowPortRender = (
155
+ // eslint-disable-next-line react/display-name
156
+ React2.memo((props) => {
157
+ const hoverService = useService(WorkflowHoverService);
158
+ const linesManager = useService(WorkflowLinesManager);
159
+ const { entity, onClick } = props;
160
+ const { portType, relativePosition, disabled } = entity;
161
+ const [targetElement, setTargetElement] = useState(entity.targetElement);
162
+ const [posX, updatePosX] = useState(relativePosition.x);
163
+ const [posY, updatePosY] = useState(relativePosition.y);
164
+ const [hovered, setHovered] = useState(false);
165
+ const [linked, setLinked] = useState(Boolean(entity?.lines?.length));
166
+ const [hasError, setHasError] = useState(props.entity.hasError);
167
+ const readonly = usePlaygroundReadonlyState();
168
+ useEffect(() => {
169
+ entity.validate();
170
+ setHasError(entity.hasError);
171
+ const dispose = entity.onEntityChange(() => {
172
+ if (entity.targetElement) {
173
+ if (entity.targetElement !== targetElement) {
174
+ setTargetElement(entity.targetElement);
175
+ }
176
+ return;
177
+ }
178
+ const newPos = entity.relativePosition;
179
+ updatePosX(Math.round(newPos.x));
180
+ updatePosY(Math.round(newPos.y));
181
+ });
182
+ const dispose2 = hoverService.onHoveredChange((id) => {
183
+ setHovered(hoverService.isHovered(entity.id));
184
+ });
185
+ const dispose3 = entity.onErrorChanged(() => {
186
+ setHasError(entity.hasError);
187
+ });
188
+ const dispose4 = linesManager.onAvailableLinesChange(() => {
189
+ setTimeout(() => {
190
+ if (linesManager.disposed || entity.disposed) return;
191
+ setLinked(Boolean(entity.lines.length));
192
+ }, 0);
193
+ });
194
+ return () => {
195
+ dispose.dispose();
196
+ dispose2.dispose();
197
+ dispose3.dispose();
198
+ dispose4.dispose();
199
+ };
200
+ }, [hoverService, entity, targetElement]);
201
+ const className = classNames("workflow-port-render", props.className || "", {
202
+ hovered: !readonly && hovered && !disabled && portType !== "input",
203
+ // 有线条链接的时候深蓝色小圆点
204
+ linked
205
+ });
206
+ const colorStyles = {};
207
+ if (props.primaryColor) {
208
+ colorStyles["--g-workflow-port-color-primary"] = props.primaryColor;
209
+ }
210
+ if (props.secondaryColor) {
211
+ colorStyles["--g-workflow-port-color-secondary"] = props.secondaryColor;
212
+ }
213
+ if (props.errorColor) {
214
+ colorStyles["--g-workflow-port-color-error"] = props.errorColor;
215
+ }
216
+ if (props.backgroundColor) {
217
+ colorStyles["--g-workflow-port-color-background"] = props.backgroundColor;
218
+ }
219
+ const combinedStyle = targetElement ? { ...props.style, ...colorStyles } : { ...props.style, ...colorStyles, left: posX, top: posY };
220
+ const content = /* @__PURE__ */ React2.createElement(
221
+ WorkflowPointStyle,
222
+ {
223
+ className,
224
+ style: combinedStyle,
225
+ onClick: (e) => onClick?.(e, entity),
226
+ onTouchStart: (e) => {
227
+ if (!onClick) {
228
+ return;
229
+ }
230
+ MouseTouchEvent.onTouched(e, (mouseEvent) => {
231
+ onClick(mouseEvent, entity);
232
+ });
233
+ },
234
+ "data-port-entity-id": entity.id,
235
+ "data-port-entity-type": entity.portType,
236
+ "data-testid": "sdk.workflow.canvas.node.port"
237
+ },
238
+ /* @__PURE__ */ React2.createElement("div", { className: classNames("bg-circle", "workflow-bg-circle") }),
239
+ /* @__PURE__ */ React2.createElement(
240
+ "div",
241
+ {
242
+ className: classNames({
243
+ bg: true,
244
+ [PORT_BG_CLASS_NAME]: true,
245
+ "workflow-point-bg": true,
246
+ hasError
247
+ })
248
+ },
249
+ /* @__PURE__ */ React2.createElement(CrossHair, null)
250
+ ),
251
+ /* @__PURE__ */ React2.createElement("div", { className: "focus-circle" })
252
+ );
253
+ if (targetElement) {
254
+ return ReactDOM.createPortal(content, targetElement);
255
+ }
256
+ return content;
257
+ })
258
+ );
259
+
260
+ // src/constants/lines.ts
261
+ var LINE_OFFSET = 6;
262
+ var LINE_PADDING = 12;
263
+
264
+ // src/create-free-lines-plugin.ts
265
+ import { WorkflowLinesManager as WorkflowLinesManager2 } from "@flowgram.ai/free-layout-core";
266
+ import { definePluginCreator } from "@flowgram.ai/core";
267
+
268
+ // src/layer/workflow-lines-layer.tsx
269
+ import ReactDOM2 from "react-dom";
270
+ import React5, { useLayoutEffect, useState as useState2 } from "react";
271
+ import { inject, injectable } from "inversify";
272
+ import { domUtils } from "@flowgram.ai/utils";
273
+ import { FlowRendererRegistry } from "@flowgram.ai/renderer";
274
+ import { StackingContextManager } from "@flowgram.ai/free-stack-plugin";
275
+ import {
276
+ nanoid,
277
+ WorkflowDocument,
278
+ WorkflowHoverService as WorkflowHoverService2,
279
+ WorkflowLineEntity,
280
+ WorkflowLineRenderData as WorkflowLineRenderData2,
281
+ WorkflowNodeEntity,
282
+ WorkflowPortEntity,
283
+ WorkflowSelectService
284
+ } from "@flowgram.ai/free-layout-core";
285
+ import { Layer, observeEntities, observeEntityDatas, TransformData } from "@flowgram.ai/core";
286
+
287
+ // src/components/workflow-line-render/index.tsx
288
+ import { memo } from "react";
289
+
290
+ // src/components/workflow-line-render/line-svg.tsx
291
+ import React4 from "react";
292
+ import clsx from "clsx";
293
+ import { POINT_RADIUS } from "@flowgram.ai/free-layout-core";
294
+ import { WorkflowLineRenderData } from "@flowgram.ai/free-layout-core";
295
+
296
+ // src/components/workflow-line-render/index.style.ts
297
+ import styled2 from "styled-components";
298
+ var LineStyle = styled2.div.attrs({
299
+ className: "gedit-flow-activity-edge"
300
+ })`
301
+ position: absolute;
302
+
303
+ @keyframes flowingDash {
304
+ to {
305
+ stroke-dashoffset: -13;
306
+ }
307
+ }
308
+
309
+ .dashed-line {
310
+ stroke-dasharray: 8, 5;
311
+ }
312
+
313
+ .flowing-line {
314
+ animation: flowingDash 0.5s linear infinite;
315
+ }
316
+ `;
317
+
318
+ // src/components/workflow-line-render/arrow.tsx
319
+ import React3 from "react";
320
+ function ArrowRenderer({
321
+ id,
322
+ pos,
323
+ reverseArrow,
324
+ strokeWidth,
325
+ vertical,
326
+ hide
327
+ }) {
328
+ if (hide) {
329
+ return null;
330
+ }
331
+ const arrowPath = vertical ? reverseArrow ? `M ${pos.x - LINE_OFFSET},${pos.y} L ${pos.x},${pos.y - LINE_OFFSET} L ${pos.x + LINE_OFFSET},${pos.y}` : `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${pos.x + LINE_OFFSET},${pos.y - LINE_OFFSET}` : reverseArrow ? `M ${pos.x},${pos.y + LINE_OFFSET} L ${pos.x - LINE_OFFSET},${pos.y} L ${pos.x},${pos.y - LINE_OFFSET}` : `M ${pos.x - LINE_OFFSET},${pos.y - LINE_OFFSET} L ${pos.x},${pos.y} L ${pos.x - LINE_OFFSET},${pos.y + LINE_OFFSET}`;
332
+ return /* @__PURE__ */ React3.createElement(
333
+ "path",
334
+ {
335
+ d: arrowPath,
336
+ strokeLinecap: "round",
337
+ stroke: `url(#${id})`,
338
+ fill: "none",
339
+ strokeWidth
340
+ }
341
+ );
342
+ }
343
+
344
+ // src/components/workflow-line-render/line-svg.tsx
345
+ var PADDING = 12;
346
+ var LineSVG = (props) => {
347
+ const { line, color, selected, children, strokePrefix, rendererRegistry } = props;
348
+ const { position, reverse, vertical, hideArrow } = line;
349
+ const renderData = line.getData(WorkflowLineRenderData);
350
+ const { bounds, path: bezierPath } = renderData;
351
+ const toRelative = (p) => ({
352
+ x: p.x - bounds.x + PADDING,
353
+ y: p.y - bounds.y + PADDING
354
+ });
355
+ const fromPos = toRelative(position.from);
356
+ const toPos = toRelative(position.to);
357
+ const arrowToPos = vertical ? { x: toPos.x, y: toPos.y - POINT_RADIUS } : { x: toPos.x - POINT_RADIUS, y: toPos.y };
358
+ const arrowFromPos = vertical ? { x: fromPos.x, y: fromPos.y + POINT_RADIUS + LINE_OFFSET } : { x: fromPos.x + POINT_RADIUS + LINE_OFFSET, y: fromPos.y };
359
+ const strokeWidth = selected ? STROKE_WIDTH_SLECTED : STROKE_WIDTH;
360
+ const strokeID = strokePrefix ? `${strokePrefix}-${line.id}` : line.id;
361
+ const CustomArrowRenderer = rendererRegistry?.tryToGetRendererComponent("arrow-renderer")?.renderer;
362
+ const ArrowComponent = CustomArrowRenderer || ArrowRenderer;
363
+ const path = /* @__PURE__ */ React4.createElement(
364
+ "path",
365
+ {
366
+ d: bezierPath,
367
+ fill: "none",
368
+ stroke: `url(#${strokeID})`,
369
+ strokeWidth,
370
+ className: clsx(
371
+ line.className,
372
+ // 显示流动线条的条件:没有自定义线条class,并且线条处于流动或处理中
373
+ !line.className && (line.processing || line.flowing ? "dashed-line flowing-line" : "")
374
+ )
375
+ }
376
+ );
377
+ return /* @__PURE__ */ React4.createElement(
378
+ LineStyle,
379
+ {
380
+ style: {
381
+ left: bounds.x - PADDING,
382
+ top: bounds.y - PADDING,
383
+ position: "absolute"
384
+ }
385
+ },
386
+ children,
387
+ /* @__PURE__ */ React4.createElement("svg", { width: bounds.width + PADDING * 2, height: bounds.height + PADDING * 2 }, /* @__PURE__ */ React4.createElement("defs", null, /* @__PURE__ */ React4.createElement(
388
+ "linearGradient",
389
+ {
390
+ x1: vertical ? "100%" : "0%",
391
+ y1: vertical ? "0%" : "100%",
392
+ x2: "100%",
393
+ y2: "100%",
394
+ id: strokeID,
395
+ gradientUnits: "userSpaceOnUse"
396
+ },
397
+ /* @__PURE__ */ React4.createElement("stop", { stopColor: color, offset: "0%" }),
398
+ /* @__PURE__ */ React4.createElement("stop", { stopColor: color, offset: "100%" })
399
+ )), /* @__PURE__ */ React4.createElement("g", null, path, /* @__PURE__ */ React4.createElement(
400
+ ArrowComponent,
401
+ {
402
+ id: strokeID,
403
+ reverseArrow: reverse,
404
+ pos: reverse ? arrowFromPos : arrowToPos,
405
+ strokeWidth,
406
+ vertical,
407
+ hide: hideArrow,
408
+ line
409
+ }
410
+ )))
411
+ );
412
+ };
413
+
414
+ // src/components/workflow-line-render/index.tsx
415
+ var WorkflowLineRender = memo(
416
+ LineSVG,
417
+ (prevProps, nextProps) => prevProps.version === nextProps.version
418
+ );
419
+
420
+ // src/layer/workflow-lines-layer.tsx
421
+ var WorkflowLinesLayer = class extends Layer {
422
+ constructor() {
423
+ super(...arguments);
424
+ this.layerID = nanoid();
425
+ this.mountedLines = /* @__PURE__ */ new Map();
426
+ this._version = 0;
427
+ /**
428
+ * 节点线条
429
+ */
430
+ this.node = domUtils.createDivWithClass("gedit-playground-layer gedit-flow-lines-layer");
431
+ }
432
+ onZoom(scale) {
433
+ this.node.style.transform = `scale(${scale})`;
434
+ }
435
+ onReady() {
436
+ this.pipelineNode.appendChild(this.node);
437
+ this.toDispose.pushAll([
438
+ this.selectService.onSelectionChanged(() => this.render()),
439
+ this.hoverService.onHoveredChange(() => this.render()),
440
+ this.workflowDocument.linesManager.onForceUpdate(() => {
441
+ this.mountedLines.clear();
442
+ this.bumpVersion();
443
+ this.render();
444
+ })
445
+ ]);
446
+ }
447
+ dispose() {
448
+ this.mountedLines.clear();
449
+ }
450
+ render() {
451
+ const [, forceUpdate] = useState2({});
452
+ useLayoutEffect(() => {
453
+ const updateLines = () => {
454
+ let needsUpdate = false;
455
+ this.lines.forEach((line) => {
456
+ const renderData = line.getData(WorkflowLineRenderData2);
457
+ const oldVersion = renderData.renderVersion;
458
+ renderData.update();
459
+ if (renderData.renderVersion !== oldVersion) {
460
+ needsUpdate = true;
461
+ }
462
+ });
463
+ if (needsUpdate) {
464
+ forceUpdate({});
465
+ }
466
+ };
467
+ const rafId = requestAnimationFrame(updateLines);
468
+ return () => cancelAnimationFrame(rafId);
469
+ }, [this.lines]);
470
+ const lines = this.lines.map((line) => this.renderLine(line));
471
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, lines);
472
+ }
473
+ // 用来绕过 memo
474
+ bumpVersion() {
475
+ this._version = this._version + 1;
476
+ if (this._version === Number.MAX_SAFE_INTEGER) {
477
+ this._version = 0;
478
+ }
479
+ }
480
+ lineProps(line) {
481
+ const { lineType } = this.workflowDocument.linesManager;
482
+ const selected = this.selectService.isSelected(line.id);
483
+ const hovered = this.hoverService.isHovered(line.id);
484
+ const version = this.lineVersion(line);
485
+ return {
486
+ key: line.id,
487
+ color: line.color,
488
+ selected,
489
+ hovered,
490
+ line,
491
+ lineType,
492
+ version,
493
+ strokePrefix: this.layerID,
494
+ rendererRegistry: this.rendererRegistry
495
+ };
496
+ }
497
+ lineVersion(line) {
498
+ const renderData = line.getData(WorkflowLineRenderData2);
499
+ const { renderVersion } = renderData;
500
+ const selected = this.selectService.isSelected(line.id);
501
+ const hovered = this.hoverService.isHovered(line.id);
502
+ const { version: lineVersion, color } = line;
503
+ const version = `v:${this._version},lv:${lineVersion},rv:${renderVersion},c:${color},s:${selected ? "T" : "F"},h:${hovered ? "T" : "F"}`;
504
+ return version;
505
+ }
506
+ lineComponent(props) {
507
+ const RenderInsideLine = this.options.renderInsideLine ?? (() => /* @__PURE__ */ React5.createElement(React5.Fragment, null));
508
+ return /* @__PURE__ */ React5.createElement(WorkflowLineRender, { ...props }, /* @__PURE__ */ React5.createElement(RenderInsideLine, { ...props }));
509
+ }
510
+ renderLine(line) {
511
+ const lineProps = this.lineProps(line);
512
+ const cache = this.mountedLines.get(line.id);
513
+ const isCached = cache !== void 0;
514
+ const { portal: cachedPortal, version: cachedVersion } = cache ?? {};
515
+ if (isCached && cachedVersion === lineProps.version) {
516
+ return cachedPortal;
517
+ }
518
+ if (!isCached) {
519
+ this.renderElement.appendChild(line.node);
520
+ line.onDispose(() => {
521
+ this.mountedLines.delete(line.id);
522
+ line.node.remove();
523
+ });
524
+ }
525
+ const portal = ReactDOM2.createPortal(this.lineComponent(lineProps), line.node);
526
+ this.mountedLines.set(line.id, { line, portal, version: lineProps.version });
527
+ return portal;
528
+ }
529
+ get renderElement() {
530
+ return this.stackContext.node;
531
+ }
532
+ };
533
+ WorkflowLinesLayer.type = "WorkflowLinesLayer";
534
+ __decorateClass([
535
+ inject(WorkflowHoverService2)
536
+ ], WorkflowLinesLayer.prototype, "hoverService", 2);
537
+ __decorateClass([
538
+ inject(WorkflowSelectService)
539
+ ], WorkflowLinesLayer.prototype, "selectService", 2);
540
+ __decorateClass([
541
+ inject(StackingContextManager)
542
+ ], WorkflowLinesLayer.prototype, "stackContext", 2);
543
+ __decorateClass([
544
+ inject(FlowRendererRegistry)
545
+ ], WorkflowLinesLayer.prototype, "rendererRegistry", 2);
546
+ __decorateClass([
547
+ observeEntities(WorkflowLineEntity)
548
+ ], WorkflowLinesLayer.prototype, "lines", 2);
549
+ __decorateClass([
550
+ observeEntities(WorkflowPortEntity)
551
+ ], WorkflowLinesLayer.prototype, "ports", 2);
552
+ __decorateClass([
553
+ observeEntityDatas(WorkflowNodeEntity, TransformData)
554
+ ], WorkflowLinesLayer.prototype, "trans", 2);
555
+ __decorateClass([
556
+ inject(WorkflowDocument)
557
+ ], WorkflowLinesLayer.prototype, "workflowDocument", 2);
558
+ WorkflowLinesLayer = __decorateClass([
559
+ injectable()
560
+ ], WorkflowLinesLayer);
561
+
562
+ // src/contributions/bezier/bezier-controls.ts
563
+ import { Rectangle } from "@flowgram.ai/utils";
564
+ var BezierControlType = /* @__PURE__ */ ((BezierControlType2) => {
565
+ BezierControlType2[BezierControlType2["RIGHT_TOP"] = 0] = "RIGHT_TOP";
566
+ BezierControlType2[BezierControlType2["RIGHT_BOTTOM"] = 1] = "RIGHT_BOTTOM";
567
+ BezierControlType2[BezierControlType2["LEFT_TOP"] = 2] = "LEFT_TOP";
568
+ BezierControlType2[BezierControlType2["LEFT_BOTTOM"] = 3] = "LEFT_BOTTOM";
569
+ return BezierControlType2;
570
+ })(BezierControlType || {});
571
+ var CONTROL_MAX = 300;
572
+ function getBezierHorizontalControlPoints(fromPos, toPos) {
573
+ const rect = Rectangle.createRectangleWithTwoPoints(fromPos, toPos);
574
+ let type;
575
+ if (fromPos.x <= toPos.x) {
576
+ type = fromPos.y <= toPos.y ? 1 /* RIGHT_BOTTOM */ : 0 /* RIGHT_TOP */;
577
+ } else {
578
+ type = fromPos.y <= toPos.y ? 3 /* LEFT_BOTTOM */ : 2 /* LEFT_TOP */;
579
+ }
580
+ let controls;
581
+ switch (type) {
582
+ case 0 /* RIGHT_TOP */:
583
+ controls = [
584
+ {
585
+ x: rect.rightBottom.x - rect.width / 2,
586
+ y: rect.rightBottom.y
587
+ },
588
+ {
589
+ x: rect.leftTop.x + rect.width / 2,
590
+ y: rect.leftTop.y
591
+ }
592
+ ];
593
+ break;
594
+ case 1 /* RIGHT_BOTTOM */:
595
+ controls = [
596
+ {
597
+ x: rect.rightTop.x - rect.width / 2,
598
+ y: rect.rightTop.y
599
+ },
600
+ {
601
+ x: rect.leftBottom.x + rect.width / 2,
602
+ y: rect.leftBottom.y
603
+ }
604
+ ];
605
+ break;
606
+ case 2 /* LEFT_TOP */:
607
+ controls = [
608
+ {
609
+ x: rect.rightBottom.x + Math.min(rect.width, CONTROL_MAX),
610
+ y: rect.rightBottom.y
611
+ },
612
+ {
613
+ x: rect.leftTop.x - Math.min(rect.width, CONTROL_MAX),
614
+ y: rect.leftTop.y
615
+ }
616
+ ];
617
+ break;
618
+ case 3 /* LEFT_BOTTOM */:
619
+ controls = [
620
+ {
621
+ x: rect.rightTop.x + Math.min(rect.width, CONTROL_MAX),
622
+ y: rect.rightTop.y
623
+ },
624
+ {
625
+ x: rect.leftBottom.x - Math.min(rect.width, CONTROL_MAX),
626
+ y: rect.leftBottom.y
627
+ }
628
+ ];
629
+ }
630
+ return controls;
631
+ }
632
+ function getBezierVerticalControlPoints(fromPos, toPos) {
633
+ const rect = Rectangle.createRectangleWithTwoPoints(fromPos, toPos);
634
+ let type;
635
+ if (fromPos.y <= toPos.y) {
636
+ type = fromPos.x <= toPos.x ? 1 /* RIGHT_BOTTOM */ : 3 /* LEFT_BOTTOM */;
637
+ } else {
638
+ type = fromPos.x <= toPos.x ? 0 /* RIGHT_TOP */ : 2 /* LEFT_TOP */;
639
+ }
640
+ let controls;
641
+ switch (type) {
642
+ case 1 /* RIGHT_BOTTOM */:
643
+ controls = [
644
+ {
645
+ x: rect.leftTop.x,
646
+ y: rect.leftTop.y + rect.height / 2
647
+ },
648
+ {
649
+ x: rect.rightBottom.x,
650
+ y: rect.rightBottom.y - rect.height / 2
651
+ }
652
+ ];
653
+ break;
654
+ case 3 /* LEFT_BOTTOM */:
655
+ controls = [
656
+ {
657
+ x: rect.rightTop.x,
658
+ y: rect.rightTop.y + rect.height / 2
659
+ },
660
+ {
661
+ x: rect.leftBottom.x,
662
+ y: rect.leftBottom.y - rect.height / 2
663
+ }
664
+ ];
665
+ break;
666
+ case 0 /* RIGHT_TOP */:
667
+ controls = [
668
+ {
669
+ x: rect.leftBottom.x,
670
+ y: rect.leftBottom.y + Math.min(rect.height, CONTROL_MAX)
671
+ },
672
+ {
673
+ x: rect.rightTop.x,
674
+ y: rect.rightTop.y - Math.min(rect.height, CONTROL_MAX)
675
+ }
676
+ ];
677
+ break;
678
+ case 2 /* LEFT_TOP */:
679
+ controls = [
680
+ {
681
+ x: rect.rightBottom.x,
682
+ y: rect.rightBottom.y + Math.min(rect.height, CONTROL_MAX)
683
+ },
684
+ {
685
+ x: rect.leftTop.x,
686
+ y: rect.leftTop.y - Math.min(rect.height, CONTROL_MAX)
687
+ }
688
+ ];
689
+ break;
690
+ }
691
+ return controls;
692
+ }
693
+
694
+ // src/contributions/bezier/index.ts
695
+ import { Bezier } from "bezier-js";
696
+ import { Point, Rectangle as Rectangle2 } from "@flowgram.ai/utils";
697
+ import {
698
+ POINT_RADIUS as POINT_RADIUS2
699
+ } from "@flowgram.ai/free-layout-core";
700
+ import { LineType } from "@flowgram.ai/free-layout-core";
701
+ var WorkflowBezierLineContribution = class {
702
+ constructor(entity) {
703
+ this.entity = entity;
704
+ }
705
+ get path() {
706
+ return this.data?.path ?? "";
707
+ }
708
+ calcDistance(pos) {
709
+ if (!this.data) {
710
+ return Number.MAX_SAFE_INTEGER;
711
+ }
712
+ return Point.getDistance(pos, this.data.bezier.project(pos));
713
+ }
714
+ get bounds() {
715
+ if (!this.data) {
716
+ return new Rectangle2();
717
+ }
718
+ return this.data.bbox;
719
+ }
720
+ update(params) {
721
+ this.data = this.calcBezier(params.fromPos, params.toPos);
722
+ }
723
+ calcBezier(fromPos, toPos) {
724
+ const controls = this.entity.vertical ? getBezierVerticalControlPoints(fromPos, toPos) : getBezierHorizontalControlPoints(fromPos, toPos);
725
+ const bezier = new Bezier([fromPos, ...controls, toPos]);
726
+ const bbox = bezier.bbox();
727
+ const bboxBounds = new Rectangle2(
728
+ bbox.x.min,
729
+ bbox.y.min,
730
+ bbox.x.max - bbox.x.min,
731
+ bbox.y.max - bbox.y.min
732
+ );
733
+ const path = this.getPath({ bbox: bboxBounds, fromPos, toPos, controls });
734
+ this.data = {
735
+ fromPos,
736
+ toPos,
737
+ bezier,
738
+ bbox: bboxBounds,
739
+ controls,
740
+ path
741
+ };
742
+ return this.data;
743
+ }
744
+ getPath(params) {
745
+ const { bbox } = params;
746
+ const toRelative = (p) => ({
747
+ x: p.x - bbox.x + LINE_PADDING,
748
+ y: p.y - bbox.y + LINE_PADDING
749
+ });
750
+ const fromPos = toRelative(params.fromPos);
751
+ const toPos = toRelative(params.toPos);
752
+ const controls = params.controls.map((c) => toRelative(c));
753
+ const renderToPos = this.entity.vertical ? { x: toPos.x, y: toPos.y - POINT_RADIUS2 } : { x: toPos.x - POINT_RADIUS2, y: toPos.y };
754
+ const getPathData = () => {
755
+ const controlPoints = controls.map((s) => `${s.x} ${s.y}`).join(",");
756
+ const curveType = controls.length === 1 ? "S" : "C";
757
+ if (this.entity.vertical) {
758
+ return `M${fromPos.x} ${fromPos.y + POINT_RADIUS2} ${curveType} ${controlPoints}, ${renderToPos.x} ${renderToPos.y}`;
759
+ }
760
+ return `M${fromPos.x + POINT_RADIUS2} ${fromPos.y} ${curveType} ${controlPoints}, ${renderToPos.x} ${renderToPos.y}`;
761
+ };
762
+ const path = getPathData();
763
+ return path;
764
+ }
765
+ };
766
+ WorkflowBezierLineContribution.type = LineType.BEZIER;
767
+
768
+ // src/contributions/fold/index.ts
769
+ import { Rectangle as Rectangle4 } from "@flowgram.ai/utils";
770
+ import {
771
+ POINT_RADIUS as POINT_RADIUS3
772
+ } from "@flowgram.ai/free-layout-core";
773
+ import { LineType as LineType2 } from "@flowgram.ai/free-layout-core";
774
+
775
+ // src/contributions/fold/fold-line.ts
776
+ import { Point as Point2, Rectangle as Rectangle3 } from "@flowgram.ai/utils";
777
+ var getPointToSegmentDistance = (point, segStart, segEnd) => {
778
+ const { x: px, y: py } = point;
779
+ const { x: x1, y: y1 } = segStart;
780
+ const { x: x2, y: y2 } = segEnd;
781
+ const A = px - x1;
782
+ const B = py - y1;
783
+ const C = x2 - x1;
784
+ const D = y2 - y1;
785
+ const dot = A * C + B * D;
786
+ const lenSq = C * C + D * D;
787
+ const param = lenSq === 0 ? -1 : dot / lenSq;
788
+ let xx;
789
+ let yy;
790
+ if (param < 0) {
791
+ xx = x1;
792
+ yy = y1;
793
+ } else if (param > 1) {
794
+ xx = x2;
795
+ yy = y2;
796
+ } else {
797
+ xx = x1 + param * C;
798
+ yy = y1 + param * D;
799
+ }
800
+ const dx = px - xx;
801
+ const dy = py - yy;
802
+ return Math.sqrt(dx * dx + dy * dy);
803
+ };
804
+ var FoldLine;
805
+ ((FoldLine2) => {
806
+ const EDGE_RADIUS = 5;
807
+ const OFFSET = 20;
808
+ function getEdgeCenter({ source, target }) {
809
+ const xOffset = Math.abs(target.x - source.x) / 2;
810
+ const centerX = target.x < source.x ? target.x + xOffset : target.x - xOffset;
811
+ const yOffset = Math.abs(target.y - source.y) / 2;
812
+ const centerY = target.y < source.y ? target.y + yOffset : target.y - yOffset;
813
+ return [centerX, centerY];
814
+ }
815
+ const getDirection = ({ source, target }) => source.x < target.x ? { x: 1, y: 0 } : { x: -1, y: 0 };
816
+ function getPoints({
817
+ source,
818
+ target,
819
+ vertical = false
820
+ }) {
821
+ const sourceDir = vertical ? { x: 0, y: 1 } : { x: 1, y: 0 };
822
+ const targetDir = vertical ? { x: 0, y: -1 } : { x: -1, y: 0 };
823
+ const sourceGapped = {
824
+ x: source.x + sourceDir.x * OFFSET,
825
+ y: source.y + sourceDir.y * OFFSET
826
+ };
827
+ const targetGapped = {
828
+ x: target.x + targetDir.x * OFFSET,
829
+ y: target.y + targetDir.y * OFFSET
830
+ };
831
+ const dir = vertical ? { x: 0, y: sourceGapped.y < targetGapped.y ? 1 : -1 } : getDirection({ source: sourceGapped, target: targetGapped });
832
+ const dirAccessor = dir.x !== 0 ? "x" : "y";
833
+ const currDir = dir[dirAccessor];
834
+ let points = [];
835
+ let centerX, centerY;
836
+ const [defaultCenterX, defaultCenterY] = getEdgeCenter({
837
+ source,
838
+ target
839
+ });
840
+ if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
841
+ centerX = defaultCenterX;
842
+ centerY = defaultCenterY;
843
+ const verticalSplit = [
844
+ { x: centerX, y: sourceGapped.y },
845
+ { x: centerX, y: targetGapped.y }
846
+ ];
847
+ const horizontalSplit = [
848
+ { x: sourceGapped.x, y: centerY },
849
+ { x: targetGapped.x, y: centerY }
850
+ ];
851
+ if (sourceDir[dirAccessor] === currDir) {
852
+ points = dirAccessor === "x" ? verticalSplit : horizontalSplit;
853
+ } else {
854
+ points = dirAccessor === "x" ? horizontalSplit : verticalSplit;
855
+ }
856
+ } else {
857
+ const sourceTarget = [{ x: sourceGapped.x, y: targetGapped.y }];
858
+ const targetSource = [{ x: targetGapped.x, y: sourceGapped.y }];
859
+ if (dirAccessor === "x") {
860
+ points = sourceDir.x === currDir ? targetSource : sourceTarget;
861
+ } else {
862
+ points = sourceDir.y === currDir ? sourceTarget : targetSource;
863
+ }
864
+ const dirAccessorOpposite = dirAccessor === "x" ? "y" : "x";
865
+ const isSameDir = sourceDir[dirAccessor] === targetDir[dirAccessorOpposite];
866
+ const sourceGtTargetOppo = sourceGapped[dirAccessorOpposite] > targetGapped[dirAccessorOpposite];
867
+ const sourceLtTargetOppo = sourceGapped[dirAccessorOpposite] < targetGapped[dirAccessorOpposite];
868
+ const flipSourceTarget = sourceDir[dirAccessor] === 1 && (!isSameDir && sourceGtTargetOppo || isSameDir && sourceLtTargetOppo) || sourceDir[dirAccessor] !== 1 && (!isSameDir && sourceLtTargetOppo || isSameDir && sourceGtTargetOppo);
869
+ if (flipSourceTarget) {
870
+ points = dirAccessor === "x" ? sourceTarget : targetSource;
871
+ }
872
+ const sourceGapPoint = { x: sourceGapped.x, y: sourceGapped.y };
873
+ const targetGapPoint = { x: targetGapped.x, y: targetGapped.y };
874
+ const maxXDistance = Math.max(
875
+ Math.abs(sourceGapPoint.x - points[0].x),
876
+ Math.abs(targetGapPoint.x - points[0].x)
877
+ );
878
+ const maxYDistance = Math.max(
879
+ Math.abs(sourceGapPoint.y - points[0].y),
880
+ Math.abs(targetGapPoint.y - points[0].y)
881
+ );
882
+ if (maxXDistance >= maxYDistance) {
883
+ centerX = (sourceGapPoint.x + targetGapPoint.x) / 2;
884
+ centerY = points[0].y;
885
+ } else {
886
+ centerX = points[0].x;
887
+ centerY = (sourceGapPoint.y + targetGapPoint.y) / 2;
888
+ }
889
+ }
890
+ const pathPoints = [
891
+ source,
892
+ { x: sourceGapped.x, y: sourceGapped.y },
893
+ ...points,
894
+ { x: targetGapped.x, y: targetGapped.y },
895
+ target
896
+ ];
897
+ return pathPoints;
898
+ }
899
+ FoldLine2.getPoints = getPoints;
900
+ function getBend(a, b, c) {
901
+ const bendSize = Math.min(
902
+ Point2.getDistance(a, b) / 2,
903
+ Point2.getDistance(b, c) / 2,
904
+ EDGE_RADIUS
905
+ );
906
+ const { x, y } = b;
907
+ if (a.x === x && x === c.x || a.y === y && y === c.y) {
908
+ return `L${x} ${y}`;
909
+ }
910
+ if (a.y === y) {
911
+ const xDir2 = a.x < c.x ? -1 : 1;
912
+ const yDir2 = a.y < c.y ? 1 : -1;
913
+ return `L ${x + bendSize * xDir2},${y}Q ${x},${y} ${x},${y + bendSize * yDir2}`;
914
+ }
915
+ const xDir = a.x < c.x ? 1 : -1;
916
+ const yDir = a.y < c.y ? -1 : 1;
917
+ return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
918
+ }
919
+ function getSmoothStepPath(points) {
920
+ const path = points.reduce((res, p, i) => {
921
+ let segment = "";
922
+ if (i > 0 && i < points.length - 1) {
923
+ segment = getBend(points[i - 1], p, points[i + 1]);
924
+ } else {
925
+ segment = `${i === 0 ? "M" : "L"}${p.x} ${p.y}`;
926
+ }
927
+ res += segment;
928
+ return res;
929
+ }, "");
930
+ return path;
931
+ }
932
+ FoldLine2.getSmoothStepPath = getSmoothStepPath;
933
+ function getBounds(points) {
934
+ const xList = points.map((p) => p.x);
935
+ const yList = points.map((p) => p.y);
936
+ const left = Math.min(...xList);
937
+ const right = Math.max(...xList);
938
+ const top = Math.min(...yList);
939
+ const bottom = Math.max(...yList);
940
+ return Rectangle3.createRectangleWithTwoPoints(
941
+ {
942
+ x: left,
943
+ y: top
944
+ },
945
+ {
946
+ x: right,
947
+ y: bottom
948
+ }
949
+ );
950
+ }
951
+ FoldLine2.getBounds = getBounds;
952
+ FoldLine2.getFoldLineToPointDistance = (points, pos) => {
953
+ if (points.length === 0) {
954
+ return Infinity;
955
+ }
956
+ if (points.length === 1) {
957
+ return Point2.getDistance(points[0], pos);
958
+ }
959
+ const lines = [];
960
+ for (let i = 0; i < points.length - 1; i++) {
961
+ lines.push([points[i], points[i + 1]]);
962
+ }
963
+ const distances = lines.map((line) => {
964
+ const [p1, p2] = line;
965
+ return getPointToSegmentDistance(pos, p1, p2);
966
+ });
967
+ return Math.min(...distances);
968
+ };
969
+ })(FoldLine || (FoldLine = {}));
970
+
971
+ // src/contributions/fold/index.ts
972
+ var WorkflowFoldLineContribution = class {
973
+ constructor(entity) {
974
+ this.entity = entity;
975
+ }
976
+ get path() {
977
+ return this.data?.path ?? "";
978
+ }
979
+ calcDistance(pos) {
980
+ if (!this.data) {
981
+ return Number.MAX_SAFE_INTEGER;
982
+ }
983
+ return FoldLine.getFoldLineToPointDistance(this.data.points, pos);
984
+ }
985
+ get bounds() {
986
+ if (!this.data) {
987
+ return new Rectangle4();
988
+ }
989
+ return this.data.bbox;
990
+ }
991
+ update(params) {
992
+ const { fromPos, toPos } = params;
993
+ const { vertical } = this.entity;
994
+ const sourceOffset = {
995
+ x: vertical ? 0 : POINT_RADIUS3,
996
+ y: vertical ? POINT_RADIUS3 : 0
997
+ };
998
+ const targetOffset = {
999
+ x: vertical ? 0 : -POINT_RADIUS3,
1000
+ y: vertical ? -POINT_RADIUS3 : 0
1001
+ };
1002
+ const points = FoldLine.getPoints({
1003
+ source: {
1004
+ x: fromPos.x + sourceOffset.x,
1005
+ y: fromPos.y + sourceOffset.y
1006
+ },
1007
+ target: {
1008
+ x: toPos.x + targetOffset.x,
1009
+ y: toPos.y + targetOffset.y
1010
+ },
1011
+ vertical
1012
+ });
1013
+ const bbox = FoldLine.getBounds(points);
1014
+ const adjustedPoints = points.map((p) => ({
1015
+ x: p.x - bbox.x + LINE_PADDING,
1016
+ y: p.y - bbox.y + LINE_PADDING
1017
+ }));
1018
+ const path = FoldLine.getSmoothStepPath(adjustedPoints);
1019
+ this.data = {
1020
+ points,
1021
+ path,
1022
+ bbox
1023
+ };
1024
+ }
1025
+ };
1026
+ WorkflowFoldLineContribution.type = LineType2.LINE_CHART;
1027
+
1028
+ // src/contributions/straight/index.ts
1029
+ import { Point as Point3, Rectangle as Rectangle5 } from "@flowgram.ai/utils";
1030
+ import {
1031
+ POINT_RADIUS as POINT_RADIUS4
1032
+ } from "@flowgram.ai/free-layout-core";
1033
+
1034
+ // src/contributions/straight/point-on-line.ts
1035
+ function projectPointOnLine(point, lineStart, lineEnd) {
1036
+ const dx = lineEnd.x - lineStart.x;
1037
+ const dy = lineEnd.y - lineStart.y;
1038
+ if (dx === 0) {
1039
+ return { x: lineStart.x, y: point.y };
1040
+ }
1041
+ if (dy === 0) {
1042
+ return { x: point.x, y: lineStart.y };
1043
+ }
1044
+ const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (dx * dx + dy * dy);
1045
+ const clampedT = Math.max(0, Math.min(1, t));
1046
+ return {
1047
+ x: lineStart.x + clampedT * dx,
1048
+ y: lineStart.y + clampedT * dy
1049
+ };
1050
+ }
1051
+
1052
+ // src/contributions/straight/index.ts
1053
+ var WorkflowStraightLineContribution = class {
1054
+ constructor(entity) {
1055
+ this.entity = entity;
1056
+ }
1057
+ get path() {
1058
+ return this.data?.path ?? "";
1059
+ }
1060
+ calcDistance(pos) {
1061
+ if (!this.data) {
1062
+ return Number.MAX_SAFE_INTEGER;
1063
+ }
1064
+ const [start, end] = this.data.points;
1065
+ return Point3.getDistance(pos, projectPointOnLine(pos, start, end));
1066
+ }
1067
+ get bounds() {
1068
+ if (!this.data) {
1069
+ return new Rectangle5();
1070
+ }
1071
+ return this.data.bbox;
1072
+ }
1073
+ update(params) {
1074
+ const { fromPos, toPos } = params;
1075
+ const { vertical } = this.entity;
1076
+ const sourceOffset = {
1077
+ x: vertical ? 0 : POINT_RADIUS4,
1078
+ y: vertical ? POINT_RADIUS4 : 0
1079
+ };
1080
+ const targetOffset = {
1081
+ x: vertical ? 0 : -POINT_RADIUS4,
1082
+ y: vertical ? -POINT_RADIUS4 : 0
1083
+ };
1084
+ const points = [
1085
+ {
1086
+ x: fromPos.x + sourceOffset.x,
1087
+ y: fromPos.y + sourceOffset.y
1088
+ },
1089
+ {
1090
+ x: toPos.x + targetOffset.x,
1091
+ y: toPos.y + targetOffset.y
1092
+ }
1093
+ ];
1094
+ const bbox = Rectangle5.createRectangleWithTwoPoints(points[0], points[1]);
1095
+ const adjustedPoints = points.map((p) => ({
1096
+ x: p.x - bbox.x + LINE_PADDING,
1097
+ y: p.y - bbox.y + LINE_PADDING
1098
+ }));
1099
+ const path = `M ${adjustedPoints[0].x} ${adjustedPoints[0].y} L ${adjustedPoints[1].x} ${adjustedPoints[1].y}`;
1100
+ this.data = {
1101
+ points,
1102
+ path,
1103
+ bbox
1104
+ };
1105
+ }
1106
+ };
1107
+ WorkflowStraightLineContribution.type = "WorkflowStraightLineContribution";
1108
+
1109
+ // src/contributions/arc/index.ts
1110
+ import { Point as Point4, Rectangle as Rectangle6 } from "@flowgram.ai/utils";
1111
+ import {
1112
+ POINT_RADIUS as POINT_RADIUS5
1113
+ } from "@flowgram.ai/free-layout-core";
1114
+ var WorkflowArkLineContribution = class {
1115
+ constructor(entity) {
1116
+ this.entity = entity;
1117
+ }
1118
+ get path() {
1119
+ return this.data?.path ?? "";
1120
+ }
1121
+ calcDistance(pos) {
1122
+ if (!this.data) {
1123
+ return Number.MAX_SAFE_INTEGER;
1124
+ }
1125
+ const { fromPos, toPos, bbox } = this.data;
1126
+ if (!bbox.contains(pos.x, pos.y)) {
1127
+ const dx = Math.max(bbox.x - pos.x, 0, pos.x - (bbox.x + bbox.width));
1128
+ const dy = Math.max(bbox.y - pos.y, 0, pos.y - (bbox.y + bbox.height));
1129
+ return Math.sqrt(dx * dx + dy * dy);
1130
+ }
1131
+ const center = {
1132
+ x: (fromPos.x + toPos.x) / 2,
1133
+ y: (fromPos.y + toPos.y) / 2
1134
+ };
1135
+ const radius = Point4.getDistance(fromPos, center);
1136
+ const distanceToCenter = Point4.getDistance(pos, center);
1137
+ return Math.abs(distanceToCenter - radius);
1138
+ }
1139
+ get bounds() {
1140
+ if (!this.data) {
1141
+ return new Rectangle6();
1142
+ }
1143
+ return this.data.bbox;
1144
+ }
1145
+ update(params) {
1146
+ const { fromPos, toPos } = params;
1147
+ const { vertical } = this.entity;
1148
+ const sourceOffset = {
1149
+ x: vertical ? 0 : POINT_RADIUS5,
1150
+ y: vertical ? POINT_RADIUS5 : 0
1151
+ };
1152
+ const targetOffset = {
1153
+ x: vertical ? 0 : -POINT_RADIUS5,
1154
+ y: vertical ? -POINT_RADIUS5 : 0
1155
+ };
1156
+ const start = {
1157
+ x: fromPos.x + sourceOffset.x,
1158
+ y: fromPos.y + sourceOffset.y
1159
+ };
1160
+ const end = {
1161
+ x: toPos.x + targetOffset.x,
1162
+ y: toPos.y + targetOffset.y
1163
+ };
1164
+ const bbox = this.calculateArcBBox(start, end);
1165
+ const path = this.getArcPath(start, end, bbox);
1166
+ this.data = {
1167
+ fromPos: start,
1168
+ toPos: end,
1169
+ path,
1170
+ bbox
1171
+ };
1172
+ }
1173
+ calculateArcBBox(start, end) {
1174
+ const dx = end.x - start.x;
1175
+ const dy = end.y - start.y;
1176
+ const radius = Math.sqrt(dx * dx + dy * dy) / 2;
1177
+ const centerX = (start.x + end.x) / 2;
1178
+ const centerY = (start.y + end.y) / 2;
1179
+ return new Rectangle6(centerX - radius, centerY - radius, radius * 2, radius * 2);
1180
+ }
1181
+ getArcPath(start, end, bbox) {
1182
+ const dx = end.x - start.x;
1183
+ const dy = end.y - start.y;
1184
+ const distance = Math.sqrt(dx * dx + dy * dy);
1185
+ const startRel = {
1186
+ x: start.x - bbox.x + LINE_PADDING,
1187
+ y: start.y - bbox.y + LINE_PADDING
1188
+ };
1189
+ const endRel = {
1190
+ x: end.x - bbox.x + LINE_PADDING,
1191
+ y: end.y - bbox.y + LINE_PADDING
1192
+ };
1193
+ return `M ${startRel.x} ${startRel.y} A ${distance / 2} ${distance / 2} 0 0 1 ${endRel.x} ${endRel.y}`;
1194
+ }
1195
+ };
1196
+ WorkflowArkLineContribution.type = "WorkflowArkLineContribution";
1197
+
1198
+ // src/contributions/manhattan/index.ts
1199
+ import { Point as Point5, Rectangle as Rectangle7 } from "@flowgram.ai/utils";
1200
+ import {
1201
+ POINT_RADIUS as POINT_RADIUS6
1202
+ } from "@flowgram.ai/free-layout-core";
1203
+ var WorkflowManhattanLineContribution = class {
1204
+ constructor(entity) {
1205
+ this.entity = entity;
1206
+ }
1207
+ get path() {
1208
+ return this.data?.path ?? "";
1209
+ }
1210
+ calcDistance(pos) {
1211
+ if (!this.data) {
1212
+ return Number.MAX_SAFE_INTEGER;
1213
+ }
1214
+ return Math.min(
1215
+ ...this.data.points.slice(1).map((point, index) => {
1216
+ const prevPoint = this.data.points[index];
1217
+ return this.getDistanceToLineSegment(pos, prevPoint, point);
1218
+ })
1219
+ );
1220
+ }
1221
+ getDistanceToLineSegment(point, start, end) {
1222
+ const dx = end.x - start.x;
1223
+ const dy = end.y - start.y;
1224
+ if (dx === 0 && dy === 0) {
1225
+ return Point5.getDistance(point, start);
1226
+ }
1227
+ const t = ((point.x - start.x) * dx + (point.y - start.y) * dy) / (dx * dx + dy * dy);
1228
+ if (t < 0) return Point5.getDistance(point, start);
1229
+ if (t > 1) return Point5.getDistance(point, end);
1230
+ const projectionPoint = {
1231
+ x: start.x + t * dx,
1232
+ y: start.y + t * dy
1233
+ };
1234
+ return Point5.getDistance(point, projectionPoint);
1235
+ }
1236
+ get bounds() {
1237
+ if (!this.data) {
1238
+ return new Rectangle7();
1239
+ }
1240
+ return this.data.bbox;
1241
+ }
1242
+ update(params) {
1243
+ const { fromPos, toPos } = params;
1244
+ const { vertical } = this.entity;
1245
+ const sourceOffset = {
1246
+ x: vertical ? 0 : POINT_RADIUS6,
1247
+ y: vertical ? POINT_RADIUS6 : 0
1248
+ };
1249
+ const targetOffset = {
1250
+ x: vertical ? 0 : -POINT_RADIUS6,
1251
+ y: vertical ? -POINT_RADIUS6 : 0
1252
+ };
1253
+ const points = this.getManhattanPoints({
1254
+ source: {
1255
+ x: fromPos.x + sourceOffset.x,
1256
+ y: fromPos.y + sourceOffset.y
1257
+ },
1258
+ target: {
1259
+ x: toPos.x + targetOffset.x,
1260
+ y: toPos.y + targetOffset.y
1261
+ },
1262
+ vertical
1263
+ });
1264
+ const bbox = Rectangle7.createRectangleWithTwoPoints(
1265
+ points.reduce(
1266
+ (min, p) => ({
1267
+ x: Math.min(min.x, p.x),
1268
+ y: Math.min(min.y, p.y)
1269
+ }),
1270
+ points[0]
1271
+ ),
1272
+ points.reduce(
1273
+ (max, p) => ({
1274
+ x: Math.max(max.x, p.x),
1275
+ y: Math.max(max.y, p.y)
1276
+ }),
1277
+ points[0]
1278
+ )
1279
+ );
1280
+ const adjustedPoints = points.map((p) => ({
1281
+ x: p.x - bbox.x + LINE_PADDING,
1282
+ y: p.y - bbox.y + LINE_PADDING
1283
+ }));
1284
+ const path = this.getPathFromPoints(adjustedPoints);
1285
+ this.data = {
1286
+ points,
1287
+ path,
1288
+ bbox
1289
+ };
1290
+ }
1291
+ getManhattanPoints(params) {
1292
+ const { source, target, vertical } = params;
1293
+ const points = [source];
1294
+ if (vertical) {
1295
+ if (source.y !== target.y) {
1296
+ points.push({ x: source.x, y: target.y });
1297
+ }
1298
+ if (source.x !== target.x) {
1299
+ points.push({ x: target.x, y: target.y });
1300
+ }
1301
+ } else {
1302
+ if (source.x !== target.x) {
1303
+ points.push({ x: target.x, y: source.y });
1304
+ }
1305
+ if (source.y !== target.y) {
1306
+ points.push({ x: target.x, y: target.y });
1307
+ }
1308
+ }
1309
+ if (points[points.length - 1] !== target) {
1310
+ points.push(target);
1311
+ }
1312
+ return points;
1313
+ }
1314
+ getPathFromPoints(points) {
1315
+ return points.reduce((path, point, index) => {
1316
+ if (index === 0) {
1317
+ return `M ${point.x} ${point.y}`;
1318
+ }
1319
+ return `${path} L ${point.x} ${point.y}`;
1320
+ }, "");
1321
+ }
1322
+ };
1323
+ WorkflowManhattanLineContribution.type = "WorkflowManhattanLineContribution";
1324
+
1325
+ // src/create-free-lines-plugin.ts
1326
+ var createFreeLinesPlugin = definePluginCreator({
1327
+ singleton: true,
1328
+ onInit: (ctx, opts) => {
1329
+ ctx.playground.registerLayer(WorkflowLinesLayer, {
1330
+ ...opts
1331
+ });
1332
+ },
1333
+ onReady: (ctx, opts) => {
1334
+ const linesManager = ctx.container.get(WorkflowLinesManager2);
1335
+ linesManager.registerContribution(WorkflowBezierLineContribution).registerContribution(WorkflowFoldLineContribution);
1336
+ if (opts.contributions) {
1337
+ opts.contributions.forEach((contribution) => {
1338
+ linesManager.registerContribution(contribution);
1339
+ });
1340
+ }
1341
+ if (opts.defaultLineType) {
1342
+ linesManager.switchLineType(opts.defaultLineType);
1343
+ }
1344
+ }
1345
+ });
1346
+ export {
1347
+ BezierControlType,
1348
+ LINE_OFFSET,
1349
+ LINE_PADDING,
1350
+ WorkflowLinesLayer as LinesLayer,
1351
+ WorkflowArkLineContribution,
1352
+ WorkflowBezierLineContribution,
1353
+ WorkflowFoldLineContribution,
1354
+ WorkflowLinesLayer,
1355
+ WorkflowManhattanLineContribution,
1356
+ WorkflowPortRender,
1357
+ WorkflowStraightLineContribution,
1358
+ createFreeLinesPlugin,
1359
+ getBezierHorizontalControlPoints,
1360
+ getBezierVerticalControlPoints
1361
+ };
1362
+ //# sourceMappingURL=index.js.map