@memberjunction/ng-flow-editor 0.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -43
- package/dist/lib/agent-editor/agent-flow-transformer.service.d.ts +83 -0
- package/dist/lib/agent-editor/agent-flow-transformer.service.d.ts.map +1 -0
- package/dist/lib/agent-editor/agent-flow-transformer.service.js +447 -0
- package/dist/lib/agent-editor/agent-flow-transformer.service.js.map +1 -0
- package/dist/lib/agent-editor/agent-properties-panel.component.d.ts +92 -0
- package/dist/lib/agent-editor/agent-properties-panel.component.d.ts.map +1 -0
- package/dist/lib/agent-editor/agent-properties-panel.component.js +1139 -0
- package/dist/lib/agent-editor/agent-properties-panel.component.js.map +1 -0
- package/dist/lib/agent-editor/agent-step-list.component.d.ts +17 -0
- package/dist/lib/agent-editor/agent-step-list.component.d.ts.map +1 -0
- package/dist/lib/agent-editor/agent-step-list.component.js +278 -0
- package/dist/lib/agent-editor/agent-step-list.component.js.map +1 -0
- package/dist/lib/agent-editor/flow-agent-editor.component.d.ts +134 -0
- package/dist/lib/agent-editor/flow-agent-editor.component.d.ts.map +1 -0
- package/dist/lib/agent-editor/flow-agent-editor.component.js +999 -0
- package/dist/lib/agent-editor/flow-agent-editor.component.js.map +1 -0
- package/dist/lib/components/flow-editor.component.d.ts +169 -0
- package/dist/lib/components/flow-editor.component.d.ts.map +1 -0
- package/dist/lib/components/flow-editor.component.js +1090 -0
- package/dist/lib/components/flow-editor.component.js.map +1 -0
- package/dist/lib/components/flow-node.component.d.ts +50 -0
- package/dist/lib/components/flow-node.component.d.ts.map +1 -0
- package/dist/lib/components/flow-node.component.js +358 -0
- package/dist/lib/components/flow-node.component.js.map +1 -0
- package/dist/lib/components/flow-palette.component.d.ts +17 -0
- package/dist/lib/components/flow-palette.component.d.ts.map +1 -0
- package/dist/lib/components/flow-palette.component.js +110 -0
- package/dist/lib/components/flow-palette.component.js.map +1 -0
- package/dist/lib/components/flow-status-bar.component.d.ts +13 -0
- package/dist/lib/components/flow-status-bar.component.d.ts.map +1 -0
- package/dist/lib/components/flow-status-bar.component.js +81 -0
- package/dist/lib/components/flow-status-bar.component.js.map +1 -0
- package/dist/lib/components/flow-toolbar.component.d.ts +25 -0
- package/dist/lib/components/flow-toolbar.component.d.ts.map +1 -0
- package/dist/lib/components/flow-toolbar.component.js +192 -0
- package/dist/lib/components/flow-toolbar.component.js.map +1 -0
- package/dist/lib/flow-editor.module.d.ts +19 -0
- package/dist/lib/flow-editor.module.d.ts.map +1 -0
- package/dist/lib/flow-editor.module.js +84 -0
- package/dist/lib/flow-editor.module.js.map +1 -0
- package/dist/lib/interfaces/flow-types.d.ts +153 -0
- package/dist/lib/interfaces/flow-types.d.ts.map +1 -0
- package/dist/lib/interfaces/flow-types.js +6 -0
- package/dist/lib/interfaces/flow-types.js.map +1 -0
- package/dist/lib/services/flow-layout.service.d.ts +21 -0
- package/dist/lib/services/flow-layout.service.d.ts.map +1 -0
- package/dist/lib/services/flow-layout.service.js +76 -0
- package/dist/lib/services/flow-layout.service.js.map +1 -0
- package/dist/lib/services/flow-state.service.d.ts +37 -0
- package/dist/lib/services/flow-state.service.d.ts.map +1 -0
- package/dist/lib/services/flow-state.service.js +89 -0
- package/dist/lib/services/flow-state.service.js.map +1 -0
- package/dist/public-api.d.ts +14 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +22 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +51 -6
|
@@ -0,0 +1,1090 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, HostListener } from '@angular/core';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
import { FFlowComponent, FCanvasComponent } from '@foblex/flow';
|
|
4
|
+
import { FlowStateService } from '../services/flow-state.service';
|
|
5
|
+
import { FlowLayoutService } from '../services/flow-layout.service';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
import * as i1 from "../services/flow-state.service";
|
|
8
|
+
import * as i2 from "../services/flow-layout.service";
|
|
9
|
+
import * as i3 from "@angular/common";
|
|
10
|
+
import * as i4 from "@foblex/flow";
|
|
11
|
+
import * as i5 from "./flow-node.component";
|
|
12
|
+
import * as i6 from "./flow-palette.component";
|
|
13
|
+
import * as i7 from "./flow-toolbar.component";
|
|
14
|
+
import * as i8 from "./flow-status-bar.component";
|
|
15
|
+
const _c0 = ["fZoomRef"];
|
|
16
|
+
const _c1 = (a0, a1) => ({ x: a0, y: a1 });
|
|
17
|
+
function FlowEditorComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
19
|
+
i0.ɵɵelementStart(0, "div", 2)(1, "mj-flow-toolbar", 17);
|
|
20
|
+
i0.ɵɵlistener("ZoomInClicked", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_ZoomInClicked_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ZoomIn()); })("ZoomOutClicked", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_ZoomOutClicked_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ZoomOut()); })("FitToScreenClicked", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_FitToScreenClicked_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ZoomToFit()); })("AutoLayoutClicked", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_AutoLayoutClicked_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.AutoArrange()); })("GridToggled", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_GridToggled_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onGridToggled($event)); })("MinimapToggled", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_MinimapToggled_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onMinimapToggled($event)); })("LegendToggled", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_LegendToggled_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onLegendToggled($event)); })("PanModeToggled", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_PanModeToggled_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onPanModeToggled($event)); });
|
|
21
|
+
i0.ɵɵelementEnd()();
|
|
22
|
+
} if (rf & 2) {
|
|
23
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
24
|
+
i0.ɵɵadvance();
|
|
25
|
+
i0.ɵɵproperty("ZoomLevel", ctx_r2.zoomLevel)("ShowGrid", ctx_r2.ShowGrid)("ShowMinimap", ctx_r2.ShowMinimap)("ShowLegend", ctx_r2.ShowLegend)("ReadOnly", ctx_r2.ReadOnly)("PanMode", ctx_r2.panMode);
|
|
26
|
+
} }
|
|
27
|
+
function FlowEditorComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
28
|
+
i0.ɵɵelement(0, "mj-flow-palette", 4);
|
|
29
|
+
} if (rf & 2) {
|
|
30
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
31
|
+
i0.ɵɵproperty("NodeTypes", ctx_r2.NodeTypes);
|
|
32
|
+
} }
|
|
33
|
+
function FlowEditorComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
34
|
+
i0.ɵɵelementStart(0, "f-background");
|
|
35
|
+
i0.ɵɵelement(1, "f-circle-pattern", 18);
|
|
36
|
+
i0.ɵɵelementEnd();
|
|
37
|
+
} if (rf & 2) {
|
|
38
|
+
i0.ɵɵadvance();
|
|
39
|
+
i0.ɵɵproperty("color", "#94a3b8")("radius", 24);
|
|
40
|
+
} }
|
|
41
|
+
function FlowEditorComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
42
|
+
i0.ɵɵelement(0, "f-line-alignment", 7);
|
|
43
|
+
} if (rf & 2) {
|
|
44
|
+
i0.ɵɵproperty("fAlignThreshold", 10);
|
|
45
|
+
} }
|
|
46
|
+
function FlowEditorComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
47
|
+
i0.ɵɵelementStart(0, "f-connection-for-create", 9);
|
|
48
|
+
i0.ɵɵnamespaceSVG();
|
|
49
|
+
i0.ɵɵelementStart(1, "svg", 19);
|
|
50
|
+
i0.ɵɵelement(2, "path", 20);
|
|
51
|
+
i0.ɵɵelementEnd()();
|
|
52
|
+
} if (rf & 2) {
|
|
53
|
+
i0.ɵɵproperty("fRadius", 8)("fOffset", 12);
|
|
54
|
+
i0.ɵɵadvance();
|
|
55
|
+
i0.ɵɵproperty("refX", 9)("refY", 5)("height", 10)("width", 10);
|
|
56
|
+
} }
|
|
57
|
+
function FlowEditorComponent_For_12_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
58
|
+
i0.ɵɵelement(0, "i", 26);
|
|
59
|
+
} if (rf & 2) {
|
|
60
|
+
const conn_r5 = i0.ɵɵnextContext(2).$implicit;
|
|
61
|
+
i0.ɵɵstyleProp("color", conn_r5.LabelIconColor || "inherit");
|
|
62
|
+
i0.ɵɵproperty("ngClass", conn_r5.LabelIcon);
|
|
63
|
+
} }
|
|
64
|
+
function FlowEditorComponent_For_12_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
65
|
+
i0.ɵɵelementStart(0, "div", 24);
|
|
66
|
+
i0.ɵɵconditionalCreate(1, FlowEditorComponent_For_12_Conditional_3_Conditional_1_Template, 1, 3, "i", 25);
|
|
67
|
+
i0.ɵɵtext(2);
|
|
68
|
+
i0.ɵɵelementEnd();
|
|
69
|
+
} if (rf & 2) {
|
|
70
|
+
const conn_r5 = i0.ɵɵnextContext().$implicit;
|
|
71
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
72
|
+
i0.ɵɵstyleProp("background-color", ctx_r2.ConnectionLabelBackground)("border-color", ctx_r2.ConnectionLabelBorderColor)("color", ctx_r2.ConnectionLabelTextColor);
|
|
73
|
+
i0.ɵɵproperty("position", 0.5)("title", conn_r5.LabelDetail || conn_r5.Label || "");
|
|
74
|
+
i0.ɵɵadvance();
|
|
75
|
+
i0.ɵɵconditional(conn_r5.LabelIcon ? 1 : -1);
|
|
76
|
+
i0.ɵɵadvance();
|
|
77
|
+
i0.ɵɵtextInterpolate1(" ", conn_r5.Label, " ");
|
|
78
|
+
} }
|
|
79
|
+
function FlowEditorComponent_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
80
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
81
|
+
i0.ɵɵelementStart(0, "f-connection", 21);
|
|
82
|
+
i0.ɵɵlistener("click", function FlowEditorComponent_For_12_Template_f_connection_click_0_listener($event) { const conn_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onConnectionClicked($event, conn_r5)); })("contextmenu", function FlowEditorComponent_For_12_Template_f_connection_contextmenu_0_listener($event) { const conn_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onConnectionContextMenu($event, conn_r5)); });
|
|
83
|
+
i0.ɵɵnamespaceSVG();
|
|
84
|
+
i0.ɵɵelementStart(1, "svg", 19);
|
|
85
|
+
i0.ɵɵelement(2, "path", 22);
|
|
86
|
+
i0.ɵɵelementEnd();
|
|
87
|
+
i0.ɵɵconditionalCreate(3, FlowEditorComponent_For_12_Conditional_3_Template, 3, 10, "div", 23);
|
|
88
|
+
i0.ɵɵelementEnd();
|
|
89
|
+
} if (rf & 2) {
|
|
90
|
+
const conn_r5 = ctx.$implicit;
|
|
91
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
92
|
+
i0.ɵɵstyleProp("--connection-color", conn_r5.Color || "#94a3b8");
|
|
93
|
+
i0.ɵɵclassProp("mj-flow-connection-colored", !!conn_r5.Color)("mj-flow-connection-dashed", conn_r5.Style === "dashed")("mj-flow-connection-dotted", conn_r5.Style === "dotted");
|
|
94
|
+
i0.ɵɵproperty("fConnectionId", conn_r5.ID)("fOutputId", conn_r5.SourcePortID)("fInputId", conn_r5.TargetPortID)("fRadius", 8)("fOffset", 12)("fSelectionDisabled", false)("fReassignDisabled", ctx_r2.ReadOnly);
|
|
95
|
+
i0.ɵɵadvance();
|
|
96
|
+
i0.ɵɵproperty("refX", 9)("refY", 5)("height", 10)("width", 10);
|
|
97
|
+
i0.ɵɵadvance();
|
|
98
|
+
i0.ɵɵattribute("fill", conn_r5.Color || "#94a3b8");
|
|
99
|
+
i0.ɵɵadvance();
|
|
100
|
+
i0.ɵɵconditional(conn_r5.Label ? 3 : -1);
|
|
101
|
+
} }
|
|
102
|
+
function FlowEditorComponent_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
103
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
104
|
+
i0.ɵɵelementStart(0, "mj-flow-node", 27);
|
|
105
|
+
i0.ɵɵlistener("contextmenu", function FlowEditorComponent_For_14_Template_mj_flow_node_contextmenu_0_listener($event) { const node_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onNodeContextMenu($event, node_r7)); });
|
|
106
|
+
i0.ɵɵelementEnd();
|
|
107
|
+
} if (rf & 2) {
|
|
108
|
+
const node_r7 = ctx.$implicit;
|
|
109
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
110
|
+
i0.ɵɵproperty("fNodeId", node_r7.ID)("fNodePosition", i0.ɵɵpureFunction2(8, _c1, node_r7.Position.X, node_r7.Position.Y))("fNodeDraggingDisabled", ctx_r2.ReadOnly || ctx_r2.panMode || node_r7.Locked === true)("fNodeSelectionDisabled", ctx_r2.panMode)("fConnectOnNode", !ctx_r2.ReadOnly && !ctx_r2.panMode)("Node", node_r7)("TypeConfig", ctx_r2.GetTypeConfig(node_r7.Type))("ReadOnly", ctx_r2.ReadOnly);
|
|
111
|
+
} }
|
|
112
|
+
function FlowEditorComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
113
|
+
i0.ɵɵelement(0, "f-selection-area");
|
|
114
|
+
} }
|
|
115
|
+
function FlowEditorComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
116
|
+
i0.ɵɵelementStart(0, "div", 12);
|
|
117
|
+
i0.ɵɵelement(1, "f-minimap", 28);
|
|
118
|
+
i0.ɵɵelementEnd();
|
|
119
|
+
} if (rf & 2) {
|
|
120
|
+
i0.ɵɵadvance();
|
|
121
|
+
i0.ɵɵproperty("fMinSize", 1500);
|
|
122
|
+
} }
|
|
123
|
+
function FlowEditorComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
124
|
+
i0.ɵɵelementStart(0, "div", 13)(1, "div", 29);
|
|
125
|
+
i0.ɵɵtext(2, "Legend");
|
|
126
|
+
i0.ɵɵelementEnd();
|
|
127
|
+
i0.ɵɵelementStart(3, "div", 30)(4, "div", 31);
|
|
128
|
+
i0.ɵɵtext(5, "Connections");
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵelementStart(6, "div", 32);
|
|
131
|
+
i0.ɵɵnamespaceSVG();
|
|
132
|
+
i0.ɵɵelementStart(7, "svg", 33);
|
|
133
|
+
i0.ɵɵelement(8, "line", 34);
|
|
134
|
+
i0.ɵɵelementEnd();
|
|
135
|
+
i0.ɵɵnamespaceHTML();
|
|
136
|
+
i0.ɵɵelementStart(9, "span");
|
|
137
|
+
i0.ɵɵtext(10, "Only exit path");
|
|
138
|
+
i0.ɵɵelementEnd()();
|
|
139
|
+
i0.ɵɵelementStart(11, "div", 32);
|
|
140
|
+
i0.ɵɵnamespaceSVG();
|
|
141
|
+
i0.ɵɵelementStart(12, "svg", 33);
|
|
142
|
+
i0.ɵɵelement(13, "line", 35);
|
|
143
|
+
i0.ɵɵelementEnd();
|
|
144
|
+
i0.ɵɵnamespaceHTML();
|
|
145
|
+
i0.ɵɵelement(14, "i", 36);
|
|
146
|
+
i0.ɵɵelementStart(15, "span");
|
|
147
|
+
i0.ɵɵtext(16, "Default (fallback)");
|
|
148
|
+
i0.ɵɵelementEnd()();
|
|
149
|
+
i0.ɵɵelementStart(17, "div", 32);
|
|
150
|
+
i0.ɵɵnamespaceSVG();
|
|
151
|
+
i0.ɵɵelementStart(18, "svg", 33);
|
|
152
|
+
i0.ɵɵelement(19, "line", 37);
|
|
153
|
+
i0.ɵɵelementEnd();
|
|
154
|
+
i0.ɵɵnamespaceHTML();
|
|
155
|
+
i0.ɵɵelementStart(20, "span");
|
|
156
|
+
i0.ɵɵtext(21, "Conditional");
|
|
157
|
+
i0.ɵɵelementEnd()();
|
|
158
|
+
i0.ɵɵelementStart(22, "div", 32);
|
|
159
|
+
i0.ɵɵnamespaceSVG();
|
|
160
|
+
i0.ɵɵelementStart(23, "svg", 33);
|
|
161
|
+
i0.ɵɵelement(24, "line", 38);
|
|
162
|
+
i0.ɵɵelementEnd();
|
|
163
|
+
i0.ɵɵnamespaceHTML();
|
|
164
|
+
i0.ɵɵelement(25, "i", 39);
|
|
165
|
+
i0.ɵɵelementStart(26, "span");
|
|
166
|
+
i0.ɵɵtext(27, "Duplicate defaults");
|
|
167
|
+
i0.ɵɵelementEnd()()();
|
|
168
|
+
i0.ɵɵelementStart(28, "div", 30)(29, "div", 31);
|
|
169
|
+
i0.ɵɵtext(30, "Step Types");
|
|
170
|
+
i0.ɵɵelementEnd();
|
|
171
|
+
i0.ɵɵelementStart(31, "div", 32);
|
|
172
|
+
i0.ɵɵelement(32, "span", 40);
|
|
173
|
+
i0.ɵɵelementStart(33, "span");
|
|
174
|
+
i0.ɵɵtext(34, "Action");
|
|
175
|
+
i0.ɵɵelementEnd()();
|
|
176
|
+
i0.ɵɵelementStart(35, "div", 32);
|
|
177
|
+
i0.ɵɵelement(36, "span", 41);
|
|
178
|
+
i0.ɵɵelementStart(37, "span");
|
|
179
|
+
i0.ɵɵtext(38, "Prompt");
|
|
180
|
+
i0.ɵɵelementEnd()();
|
|
181
|
+
i0.ɵɵelementStart(39, "div", 32);
|
|
182
|
+
i0.ɵɵelement(40, "span", 42);
|
|
183
|
+
i0.ɵɵelementStart(41, "span");
|
|
184
|
+
i0.ɵɵtext(42, "Sub-Agent");
|
|
185
|
+
i0.ɵɵelementEnd()();
|
|
186
|
+
i0.ɵɵelementStart(43, "div", 32);
|
|
187
|
+
i0.ɵɵelement(44, "span", 43);
|
|
188
|
+
i0.ɵɵelementStart(45, "span");
|
|
189
|
+
i0.ɵɵtext(46, "For Each");
|
|
190
|
+
i0.ɵɵelementEnd()();
|
|
191
|
+
i0.ɵɵelementStart(47, "div", 32);
|
|
192
|
+
i0.ɵɵelement(48, "span", 44);
|
|
193
|
+
i0.ɵɵelementStart(49, "span");
|
|
194
|
+
i0.ɵɵtext(50, "While");
|
|
195
|
+
i0.ɵɵelementEnd()()();
|
|
196
|
+
i0.ɵɵelementStart(51, "div", 30)(52, "div", 31);
|
|
197
|
+
i0.ɵɵtext(53, "Borders");
|
|
198
|
+
i0.ɵɵelementEnd();
|
|
199
|
+
i0.ɵɵelementStart(54, "div", 32);
|
|
200
|
+
i0.ɵɵelement(55, "span", 45);
|
|
201
|
+
i0.ɵɵelementStart(56, "span");
|
|
202
|
+
i0.ɵɵtext(57, "Start node");
|
|
203
|
+
i0.ɵɵelementEnd()();
|
|
204
|
+
i0.ɵɵelementStart(58, "div", 32);
|
|
205
|
+
i0.ɵɵelement(59, "span", 46)(60, "i", 47);
|
|
206
|
+
i0.ɵɵelementStart(61, "span");
|
|
207
|
+
i0.ɵɵtext(62, "Missing config");
|
|
208
|
+
i0.ɵɵelementEnd()()()();
|
|
209
|
+
} }
|
|
210
|
+
function FlowEditorComponent_Conditional_18_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
211
|
+
i0.ɵɵelementStart(0, "p", 50);
|
|
212
|
+
i0.ɵɵtext(1, "Drag steps from the palette to start building your flow");
|
|
213
|
+
i0.ɵɵelementEnd();
|
|
214
|
+
} }
|
|
215
|
+
function FlowEditorComponent_Conditional_18_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
216
|
+
i0.ɵɵelementStart(0, "p", 50);
|
|
217
|
+
i0.ɵɵtext(1, "No steps defined for this flow");
|
|
218
|
+
i0.ɵɵelementEnd();
|
|
219
|
+
} }
|
|
220
|
+
function FlowEditorComponent_Conditional_18_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
221
|
+
i0.ɵɵelementStart(0, "p", 51);
|
|
222
|
+
i0.ɵɵtext(1, " Or use auto-layout after adding steps to arrange them automatically ");
|
|
223
|
+
i0.ɵɵelementEnd();
|
|
224
|
+
} }
|
|
225
|
+
function FlowEditorComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
226
|
+
i0.ɵɵelementStart(0, "div", 14)(1, "div", 48);
|
|
227
|
+
i0.ɵɵelement(2, "i", 49);
|
|
228
|
+
i0.ɵɵconditionalCreate(3, FlowEditorComponent_Conditional_18_Conditional_3_Template, 2, 0, "p", 50);
|
|
229
|
+
i0.ɵɵconditionalCreate(4, FlowEditorComponent_Conditional_18_Conditional_4_Template, 2, 0, "p", 50);
|
|
230
|
+
i0.ɵɵconditionalCreate(5, FlowEditorComponent_Conditional_18_Conditional_5_Template, 2, 0, "p", 51);
|
|
231
|
+
i0.ɵɵelementEnd()();
|
|
232
|
+
} if (rf & 2) {
|
|
233
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
234
|
+
i0.ɵɵadvance(3);
|
|
235
|
+
i0.ɵɵconditional(!ctx_r2.ReadOnly ? 3 : -1);
|
|
236
|
+
i0.ɵɵadvance();
|
|
237
|
+
i0.ɵɵconditional(ctx_r2.ReadOnly ? 4 : -1);
|
|
238
|
+
i0.ɵɵadvance();
|
|
239
|
+
i0.ɵɵconditional(!ctx_r2.ReadOnly ? 5 : -1);
|
|
240
|
+
} }
|
|
241
|
+
function FlowEditorComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
242
|
+
const _r8 = i0.ɵɵgetCurrentView();
|
|
243
|
+
i0.ɵɵelementStart(0, "div", 52);
|
|
244
|
+
i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_19_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r8); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
245
|
+
i0.ɵɵelementStart(1, "button", 53);
|
|
246
|
+
i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_19_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r8); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onContextMenuAction("edit")); });
|
|
247
|
+
i0.ɵɵelement(2, "i", 54);
|
|
248
|
+
i0.ɵɵelementStart(3, "span");
|
|
249
|
+
i0.ɵɵtext(4);
|
|
250
|
+
i0.ɵɵelementEnd()();
|
|
251
|
+
i0.ɵɵelement(5, "div", 55);
|
|
252
|
+
i0.ɵɵelementStart(6, "button", 56);
|
|
253
|
+
i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_19_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r8); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onContextMenuAction("remove")); });
|
|
254
|
+
i0.ɵɵelement(7, "i", 57);
|
|
255
|
+
i0.ɵɵelementStart(8, "span");
|
|
256
|
+
i0.ɵɵtext(9);
|
|
257
|
+
i0.ɵɵelementEnd()()();
|
|
258
|
+
} if (rf & 2) {
|
|
259
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
260
|
+
i0.ɵɵstyleProp("left", ctx_r2.contextMenuX, "px")("top", ctx_r2.contextMenuY, "px");
|
|
261
|
+
i0.ɵɵadvance(4);
|
|
262
|
+
i0.ɵɵtextInterpolate1("Edit ", ctx_r2.contextMenuTargetType === "node" ? "Node" : "Connection");
|
|
263
|
+
i0.ɵɵadvance(5);
|
|
264
|
+
i0.ɵɵtextInterpolate1("Remove ", ctx_r2.contextMenuTargetType === "node" ? "Node" : "Connection");
|
|
265
|
+
} }
|
|
266
|
+
function FlowEditorComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
267
|
+
i0.ɵɵelement(0, "mj-flow-status-bar", 16);
|
|
268
|
+
} if (rf & 2) {
|
|
269
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
270
|
+
i0.ɵɵproperty("NodeCount", ctx_r2.Nodes.length)("ConnectionCount", ctx_r2.Connections.length)("SelectedCount", ctx_r2.selectedCount)("ZoomLevel", ctx_r2.zoomLevel);
|
|
271
|
+
} }
|
|
272
|
+
/**
|
|
273
|
+
* Generic, entity-agnostic visual flow editor component.
|
|
274
|
+
* Wraps Foblex Flow to provide a clean MJ-style API.
|
|
275
|
+
*
|
|
276
|
+
* Usage:
|
|
277
|
+
* ```html
|
|
278
|
+
* <mj-flow-editor
|
|
279
|
+
* [Nodes]="myNodes"
|
|
280
|
+
* [Connections]="myConnections"
|
|
281
|
+
* [NodeTypes]="myNodeTypes"
|
|
282
|
+
* (NodeSelected)="onNodeSelected($event)"
|
|
283
|
+
* (ConnectionCreated)="onConnectionCreated($event)">
|
|
284
|
+
* </mj-flow-editor>
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
export class FlowEditorComponent {
|
|
288
|
+
cdr;
|
|
289
|
+
stateService;
|
|
290
|
+
layoutService;
|
|
291
|
+
// ── Inputs ──────────────────────────────────────────────────
|
|
292
|
+
Nodes = [];
|
|
293
|
+
Connections = [];
|
|
294
|
+
NodeTypes = [];
|
|
295
|
+
ReadOnly = false;
|
|
296
|
+
ShowMinimap = true;
|
|
297
|
+
ShowPalette = true;
|
|
298
|
+
ShowGrid = true;
|
|
299
|
+
ShowStatusBar = true;
|
|
300
|
+
ShowToolbar = true;
|
|
301
|
+
ShowLegend = true;
|
|
302
|
+
GridSize = 20;
|
|
303
|
+
AutoLayoutDirection = 'vertical';
|
|
304
|
+
/** Background color for connection labels */
|
|
305
|
+
ConnectionLabelBackground = '#fffef5';
|
|
306
|
+
/** Border color for connection labels */
|
|
307
|
+
ConnectionLabelBorderColor = '#cbd5e1';
|
|
308
|
+
/** Text color for connection labels */
|
|
309
|
+
ConnectionLabelTextColor = '#334155';
|
|
310
|
+
// ── Outputs ─────────────────────────────────────────────────
|
|
311
|
+
NodeSelected = new EventEmitter();
|
|
312
|
+
NodeAdded = new EventEmitter();
|
|
313
|
+
NodeRemoved = new EventEmitter();
|
|
314
|
+
NodeMoved = new EventEmitter();
|
|
315
|
+
ConnectionCreated = new EventEmitter();
|
|
316
|
+
ConnectionRemoved = new EventEmitter();
|
|
317
|
+
ConnectionReassigned = new EventEmitter();
|
|
318
|
+
ConnectionSelected = new EventEmitter();
|
|
319
|
+
SelectionChanged = new EventEmitter();
|
|
320
|
+
CanvasClicked = new EventEmitter();
|
|
321
|
+
NodesChanged = new EventEmitter();
|
|
322
|
+
ConnectionsChanged = new EventEmitter();
|
|
323
|
+
NodeEditRequested = new EventEmitter();
|
|
324
|
+
ConnectionEditRequested = new EventEmitter();
|
|
325
|
+
GridToggled = new EventEmitter();
|
|
326
|
+
MinimapToggled = new EventEmitter();
|
|
327
|
+
LegendToggled = new EventEmitter();
|
|
328
|
+
// ── View Children ───────────────────────────────────────────
|
|
329
|
+
fFlow;
|
|
330
|
+
fCanvas;
|
|
331
|
+
fZoom;
|
|
332
|
+
// ── Internal State ──────────────────────────────────────────
|
|
333
|
+
zoomLevel = 100;
|
|
334
|
+
canvasPosition = { x: 0, y: 0 };
|
|
335
|
+
canvasScale = 1;
|
|
336
|
+
selectedNodeIDs = [];
|
|
337
|
+
selectedConnectionIDs = [];
|
|
338
|
+
panMode = false;
|
|
339
|
+
/** Trigger function for canvas panning — only allows panning in pan mode */
|
|
340
|
+
canvasMoveTriggerFn = (_event) => {
|
|
341
|
+
return this.panMode;
|
|
342
|
+
};
|
|
343
|
+
/* Context menu state */
|
|
344
|
+
contextMenuVisible = false;
|
|
345
|
+
contextMenuX = 0;
|
|
346
|
+
contextMenuY = 0;
|
|
347
|
+
contextMenuTargetType = 'node';
|
|
348
|
+
contextMenuNode = null;
|
|
349
|
+
contextMenuConnection = null;
|
|
350
|
+
destroy$ = new Subject();
|
|
351
|
+
initialized = false;
|
|
352
|
+
lastConnectionClickTime = 0;
|
|
353
|
+
constructor(cdr, stateService, layoutService) {
|
|
354
|
+
this.cdr = cdr;
|
|
355
|
+
this.stateService = stateService;
|
|
356
|
+
this.layoutService = layoutService;
|
|
357
|
+
}
|
|
358
|
+
// ── Lifecycle ───────────────────────────────────────────────
|
|
359
|
+
ngOnInit() {
|
|
360
|
+
this.initialized = true;
|
|
361
|
+
}
|
|
362
|
+
ngOnDestroy() {
|
|
363
|
+
this.destroy$.next();
|
|
364
|
+
this.destroy$.complete();
|
|
365
|
+
}
|
|
366
|
+
// ── Public Methods ──────────────────────────────────────────
|
|
367
|
+
/** Fit all nodes within the viewport */
|
|
368
|
+
ZoomToFit() {
|
|
369
|
+
this.fCanvas?.fitToScreen({ x: 150, y: 150 }, true);
|
|
370
|
+
}
|
|
371
|
+
/** Zoom in one step */
|
|
372
|
+
ZoomIn() {
|
|
373
|
+
if (this.fZoom) {
|
|
374
|
+
this.fZoom.zoomIn();
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
const newScale = Math.min(this.canvasScale + 0.15, 3);
|
|
378
|
+
this.fCanvas?.setScale(newScale);
|
|
379
|
+
this.updateZoomLevel(newScale);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/** Zoom out one step */
|
|
383
|
+
ZoomOut() {
|
|
384
|
+
if (this.fZoom) {
|
|
385
|
+
this.fZoom.zoomOut();
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
const newScale = Math.max(this.canvasScale - 0.15, 0.1);
|
|
389
|
+
this.fCanvas?.setScale(newScale);
|
|
390
|
+
this.updateZoomLevel(newScale);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/** Run auto-layout using dagre */
|
|
394
|
+
AutoArrange(direction) {
|
|
395
|
+
const dir = direction ?? this.AutoLayoutDirection;
|
|
396
|
+
this.pushUndoState();
|
|
397
|
+
const result = this.layoutService.CalculateLayout(this.Nodes, this.Connections, dir);
|
|
398
|
+
for (const node of this.Nodes) {
|
|
399
|
+
const newPos = result.Positions.get(node.ID);
|
|
400
|
+
if (newPos) {
|
|
401
|
+
node.Position = { ...newPos };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
this.NodesChanged.emit(this.Nodes);
|
|
405
|
+
this.cdr.detectChanges();
|
|
406
|
+
// Fit to screen after layout
|
|
407
|
+
setTimeout(() => this.ZoomToFit(), 100);
|
|
408
|
+
}
|
|
409
|
+
/** Center the canvas on a specific node */
|
|
410
|
+
CenterOnNode(nodeId) {
|
|
411
|
+
this.fCanvas?.centerGroupOrNode(nodeId, true);
|
|
412
|
+
}
|
|
413
|
+
/** Programmatically select a node */
|
|
414
|
+
SelectNode(nodeId) {
|
|
415
|
+
this.fFlow?.select([nodeId], []);
|
|
416
|
+
const node = this.Nodes.find(n => n.ID === nodeId) ?? null;
|
|
417
|
+
this.NodeSelected.emit(node);
|
|
418
|
+
}
|
|
419
|
+
/** Clear all selection */
|
|
420
|
+
ClearSelection() {
|
|
421
|
+
this.fFlow?.clearSelection();
|
|
422
|
+
this.selectedNodeIDs = [];
|
|
423
|
+
this.selectedConnectionIDs = [];
|
|
424
|
+
this.NodeSelected.emit(null);
|
|
425
|
+
this.ConnectionSelected.emit(null);
|
|
426
|
+
}
|
|
427
|
+
/** Update a node's visual status */
|
|
428
|
+
SetNodeStatus(nodeId, status, message) {
|
|
429
|
+
const node = this.Nodes.find(n => n.ID === nodeId);
|
|
430
|
+
if (node) {
|
|
431
|
+
node.Status = status;
|
|
432
|
+
node.StatusMessage = message;
|
|
433
|
+
this.cdr.detectChanges();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/** Update a node's visual properties and push a new object reference so
|
|
437
|
+
* OnPush child components (FlowNodeComponent) detect the change. */
|
|
438
|
+
UpdateNode(nodeId, changes) {
|
|
439
|
+
const idx = this.Nodes.findIndex(n => n.ID === nodeId);
|
|
440
|
+
if (idx === -1)
|
|
441
|
+
return;
|
|
442
|
+
// Create a new object reference so the OnPush FlowNodeComponent picks up the change
|
|
443
|
+
this.Nodes[idx] = { ...this.Nodes[idx], ...changes };
|
|
444
|
+
this.cdr.detectChanges();
|
|
445
|
+
}
|
|
446
|
+
/** Update a connection's visual properties in-place (label, color, style, etc.) */
|
|
447
|
+
UpdateConnection(connId, changes) {
|
|
448
|
+
const conn = this.Connections.find(c => c.ID === connId);
|
|
449
|
+
if (!conn)
|
|
450
|
+
return;
|
|
451
|
+
Object.assign(conn, changes);
|
|
452
|
+
this.cdr.detectChanges();
|
|
453
|
+
}
|
|
454
|
+
/** Highlight a sequence of nodes (e.g., execution path) */
|
|
455
|
+
HighlightPath(nodeIds) {
|
|
456
|
+
for (const node of this.Nodes) {
|
|
457
|
+
if (nodeIds.includes(node.ID)) {
|
|
458
|
+
node.Status = 'running';
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
// Highlight connections along the path
|
|
462
|
+
for (const conn of this.Connections) {
|
|
463
|
+
const srcIdx = nodeIds.indexOf(conn.SourceNodeID);
|
|
464
|
+
const tgtIdx = nodeIds.indexOf(conn.TargetNodeID);
|
|
465
|
+
if (srcIdx >= 0 && tgtIdx >= 0 && tgtIdx === srcIdx + 1) {
|
|
466
|
+
conn.Animated = true;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
this.cdr.detectChanges();
|
|
470
|
+
}
|
|
471
|
+
/** Clear all node status highlights */
|
|
472
|
+
ClearHighlights() {
|
|
473
|
+
for (const node of this.Nodes) {
|
|
474
|
+
node.Status = 'default';
|
|
475
|
+
node.StatusMessage = undefined;
|
|
476
|
+
}
|
|
477
|
+
for (const conn of this.Connections) {
|
|
478
|
+
conn.Animated = false;
|
|
479
|
+
}
|
|
480
|
+
this.cdr.detectChanges();
|
|
481
|
+
}
|
|
482
|
+
/** Undo the last operation */
|
|
483
|
+
Undo() {
|
|
484
|
+
const snapshot = this.stateService.Undo(this.Nodes, this.Connections);
|
|
485
|
+
if (snapshot) {
|
|
486
|
+
this.Nodes = snapshot.Nodes;
|
|
487
|
+
this.Connections = snapshot.Connections;
|
|
488
|
+
this.NodesChanged.emit(this.Nodes);
|
|
489
|
+
this.ConnectionsChanged.emit(this.Connections);
|
|
490
|
+
this.cdr.detectChanges();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/** Redo the last undone operation */
|
|
494
|
+
Redo() {
|
|
495
|
+
const snapshot = this.stateService.Redo(this.Nodes, this.Connections);
|
|
496
|
+
if (snapshot) {
|
|
497
|
+
this.Nodes = snapshot.Nodes;
|
|
498
|
+
this.Connections = snapshot.Connections;
|
|
499
|
+
this.NodesChanged.emit(this.Nodes);
|
|
500
|
+
this.ConnectionsChanged.emit(this.Connections);
|
|
501
|
+
this.cdr.detectChanges();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
get CanUndo() {
|
|
505
|
+
return this.stateService.CanUndo;
|
|
506
|
+
}
|
|
507
|
+
get CanRedo() {
|
|
508
|
+
return this.stateService.CanRedo;
|
|
509
|
+
}
|
|
510
|
+
/** Delete the currently selected nodes and connections */
|
|
511
|
+
DeleteSelected() {
|
|
512
|
+
if (this.ReadOnly || (this.selectedNodeIDs.length === 0 && this.selectedConnectionIDs.length === 0)) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
this.pushUndoState();
|
|
516
|
+
this.removeSelectedItems();
|
|
517
|
+
this.ClearSelection();
|
|
518
|
+
}
|
|
519
|
+
// ── Foblex Event Handlers ──────────────────────────────────
|
|
520
|
+
/** Called when Foblex flow finishes loading */
|
|
521
|
+
onFlowLoaded() {
|
|
522
|
+
if (this.Nodes.length > 0) {
|
|
523
|
+
setTimeout(() => this.ZoomToFit(), 200);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/** Canvas zoom/pan changed */
|
|
527
|
+
onCanvasChange(event) {
|
|
528
|
+
this.canvasPosition = event.position;
|
|
529
|
+
this.canvasScale = event.scale;
|
|
530
|
+
this.updateZoomLevel(event.scale);
|
|
531
|
+
}
|
|
532
|
+
/** User drew a new connection */
|
|
533
|
+
onCreateConnection(event) {
|
|
534
|
+
if (!event.fInputId || this.ReadOnly)
|
|
535
|
+
return;
|
|
536
|
+
this.pushUndoState();
|
|
537
|
+
// Derive node IDs from port IDs (ports are named nodeID-input / nodeID-output)
|
|
538
|
+
const sourceNodeID = this.findNodeByPortId(event.fOutputId);
|
|
539
|
+
const targetNodeID = this.findNodeByPortId(event.fInputId);
|
|
540
|
+
if (!sourceNodeID || !targetNodeID)
|
|
541
|
+
return;
|
|
542
|
+
// Resolve actual port IDs — when fConnectOnNode is enabled, Foblex may
|
|
543
|
+
// send the node ID itself instead of a port ID. Map to the correct port.
|
|
544
|
+
const sourcePortID = this.resolvePortId(event.fOutputId, sourceNodeID, 'output');
|
|
545
|
+
const targetPortID = this.resolvePortId(event.fInputId, targetNodeID, 'input');
|
|
546
|
+
// Prevent duplicate connections
|
|
547
|
+
const exists = this.Connections.some(c => c.SourcePortID === sourcePortID && c.TargetPortID === targetPortID);
|
|
548
|
+
if (exists)
|
|
549
|
+
return;
|
|
550
|
+
this.ConnectionCreated.emit({
|
|
551
|
+
SourceNodeID: sourceNodeID,
|
|
552
|
+
SourcePortID: sourcePortID,
|
|
553
|
+
TargetNodeID: targetNodeID,
|
|
554
|
+
TargetPortID: targetPortID
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/** User reassigned (dragged) an existing connection endpoint to a new node */
|
|
558
|
+
onReassignConnection(event) {
|
|
559
|
+
if (this.ReadOnly)
|
|
560
|
+
return;
|
|
561
|
+
const conn = this.Connections.find(c => c.ID === event.connectionId);
|
|
562
|
+
if (!conn)
|
|
563
|
+
return;
|
|
564
|
+
// If dropped on empty space (no new target/source), discard the reassignment
|
|
565
|
+
if (event.isTargetReassign && !event.newTargetId)
|
|
566
|
+
return;
|
|
567
|
+
if (event.isSourceReassign && !event.newSourceId)
|
|
568
|
+
return;
|
|
569
|
+
this.pushUndoState();
|
|
570
|
+
const oldSourceNodeID = conn.SourceNodeID;
|
|
571
|
+
const oldSourcePortID = conn.SourcePortID;
|
|
572
|
+
const oldTargetNodeID = conn.TargetNodeID;
|
|
573
|
+
const oldTargetPortID = conn.TargetPortID;
|
|
574
|
+
// Resolve new endpoints
|
|
575
|
+
let newSourceNodeID = oldSourceNodeID;
|
|
576
|
+
let newSourcePortID = oldSourcePortID;
|
|
577
|
+
let newTargetNodeID = oldTargetNodeID;
|
|
578
|
+
let newTargetPortID = oldTargetPortID;
|
|
579
|
+
if (event.isSourceReassign && event.newSourceId) {
|
|
580
|
+
newSourceNodeID = this.findNodeByPortId(event.newSourceId) ?? oldSourceNodeID;
|
|
581
|
+
newSourcePortID = this.resolvePortId(event.newSourceId, newSourceNodeID, 'output');
|
|
582
|
+
}
|
|
583
|
+
if (event.isTargetReassign && event.newTargetId) {
|
|
584
|
+
newTargetNodeID = this.findNodeByPortId(event.newTargetId) ?? oldTargetNodeID;
|
|
585
|
+
newTargetPortID = this.resolvePortId(event.newTargetId, newTargetNodeID, 'input');
|
|
586
|
+
}
|
|
587
|
+
// Prevent self-connections
|
|
588
|
+
if (newSourceNodeID === newTargetNodeID)
|
|
589
|
+
return;
|
|
590
|
+
// Prevent duplicates
|
|
591
|
+
const duplicate = this.Connections.some(c => c.ID !== conn.ID &&
|
|
592
|
+
c.SourcePortID === newSourcePortID &&
|
|
593
|
+
c.TargetPortID === newTargetPortID);
|
|
594
|
+
if (duplicate)
|
|
595
|
+
return;
|
|
596
|
+
// Update the connection in-place
|
|
597
|
+
conn.SourceNodeID = newSourceNodeID;
|
|
598
|
+
conn.SourcePortID = newSourcePortID;
|
|
599
|
+
conn.TargetNodeID = newTargetNodeID;
|
|
600
|
+
conn.TargetPortID = newTargetPortID;
|
|
601
|
+
this.ConnectionReassigned.emit({
|
|
602
|
+
ConnectionID: conn.ID,
|
|
603
|
+
OldSourceNodeID: oldSourceNodeID,
|
|
604
|
+
OldSourcePortID: oldSourcePortID,
|
|
605
|
+
OldTargetNodeID: oldTargetNodeID,
|
|
606
|
+
OldTargetPortID: oldTargetPortID,
|
|
607
|
+
NewSourceNodeID: newSourceNodeID,
|
|
608
|
+
NewSourcePortID: newSourcePortID,
|
|
609
|
+
NewTargetNodeID: newTargetNodeID,
|
|
610
|
+
NewTargetPortID: newTargetPortID
|
|
611
|
+
});
|
|
612
|
+
this.ConnectionsChanged.emit(this.Connections);
|
|
613
|
+
this.cdr.detectChanges();
|
|
614
|
+
}
|
|
615
|
+
/** External item dropped from palette */
|
|
616
|
+
onCreateNode(event) {
|
|
617
|
+
if (this.ReadOnly)
|
|
618
|
+
return;
|
|
619
|
+
this.pushUndoState();
|
|
620
|
+
const nodeType = event.data;
|
|
621
|
+
const config = this.NodeTypes.find(nt => nt.Type === nodeType);
|
|
622
|
+
if (!config)
|
|
623
|
+
return;
|
|
624
|
+
// event.rect is already normalized to canvas coordinates by Foblex's
|
|
625
|
+
// GetNormalizedElementRectExecution (via RoundedRect.fromCenter).
|
|
626
|
+
// Do NOT pass it through getPositionInFlow() again — that double-normalizes
|
|
627
|
+
// and places the node far from the actual drop point.
|
|
628
|
+
// Use the center of the preview rect (≈ cursor position at drop time).
|
|
629
|
+
const dropPosition = {
|
|
630
|
+
X: event.rect.x + event.rect.width / 2,
|
|
631
|
+
Y: event.rect.y + event.rect.height / 2
|
|
632
|
+
};
|
|
633
|
+
const newNode = this.createDefaultNode(config, dropPosition);
|
|
634
|
+
this.NodeAdded.emit({ Node: newNode, DropPosition: dropPosition });
|
|
635
|
+
}
|
|
636
|
+
/** Selection changed in Foblex */
|
|
637
|
+
onSelectionChange(event) {
|
|
638
|
+
const timeSinceClick = Date.now() - this.lastConnectionClickTime;
|
|
639
|
+
// If a direct connection click just fired, skip this Foblex callback
|
|
640
|
+
// to avoid double-processing / overwriting the direct click result.
|
|
641
|
+
if (timeSinceClick < 200) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
this.selectedNodeIDs = event.fNodeIds ?? [];
|
|
645
|
+
this.selectedConnectionIDs = event.fConnectionIds ?? [];
|
|
646
|
+
this.SelectionChanged.emit({
|
|
647
|
+
SelectedNodeIDs: this.selectedNodeIDs,
|
|
648
|
+
SelectedConnectionIDs: this.selectedConnectionIDs
|
|
649
|
+
});
|
|
650
|
+
// Emit specific node/connection selection events.
|
|
651
|
+
// IMPORTANT: When selecting a connection, emit NodeSelected(null) FIRST so
|
|
652
|
+
// that consumers clear their node state before the connection event opens
|
|
653
|
+
// the connection properties panel.
|
|
654
|
+
if (this.selectedNodeIDs.length === 1) {
|
|
655
|
+
const node = this.Nodes.find(n => n.ID === this.selectedNodeIDs[0]);
|
|
656
|
+
this.ConnectionSelected.emit(null);
|
|
657
|
+
this.NodeSelected.emit(node ?? null);
|
|
658
|
+
}
|
|
659
|
+
else if (this.selectedConnectionIDs.length === 1) {
|
|
660
|
+
const conn = this.Connections.find(c => c.ID === this.selectedConnectionIDs[0]);
|
|
661
|
+
this.NodeSelected.emit(null);
|
|
662
|
+
this.ConnectionSelected.emit(conn ?? null);
|
|
663
|
+
}
|
|
664
|
+
else if (this.selectedNodeIDs.length === 0 && this.selectedConnectionIDs.length === 0) {
|
|
665
|
+
this.NodeSelected.emit(null);
|
|
666
|
+
this.ConnectionSelected.emit(null);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/** Nodes moved on canvas */
|
|
670
|
+
onNodesMoved(event) {
|
|
671
|
+
if (this.ReadOnly || !event.fNodes || event.fNodes.length === 0)
|
|
672
|
+
return;
|
|
673
|
+
this.pushUndoState();
|
|
674
|
+
for (const moved of event.fNodes) {
|
|
675
|
+
const node = this.Nodes.find(n => n.ID === moved.id);
|
|
676
|
+
if (node) {
|
|
677
|
+
const oldPosition = { ...node.Position };
|
|
678
|
+
node.Position = { X: moved.position.x, Y: moved.position.y };
|
|
679
|
+
this.NodeMoved.emit({
|
|
680
|
+
NodeID: node.ID,
|
|
681
|
+
OldPosition: oldPosition,
|
|
682
|
+
NewPosition: node.Position
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
this.NodesChanged.emit(this.Nodes);
|
|
687
|
+
}
|
|
688
|
+
// ── Toolbar Event Handlers ─────────────────────────────────
|
|
689
|
+
onGridToggled(show) {
|
|
690
|
+
this.ShowGrid = show;
|
|
691
|
+
this.GridToggled.emit(show);
|
|
692
|
+
this.cdr.detectChanges();
|
|
693
|
+
}
|
|
694
|
+
onMinimapToggled(show) {
|
|
695
|
+
this.ShowMinimap = show;
|
|
696
|
+
this.MinimapToggled.emit(show);
|
|
697
|
+
this.cdr.detectChanges();
|
|
698
|
+
}
|
|
699
|
+
onLegendToggled(show) {
|
|
700
|
+
this.ShowLegend = show;
|
|
701
|
+
this.LegendToggled.emit(show);
|
|
702
|
+
this.cdr.detectChanges();
|
|
703
|
+
}
|
|
704
|
+
onPanModeToggled(enabled) {
|
|
705
|
+
this.panMode = enabled;
|
|
706
|
+
this.cdr.detectChanges();
|
|
707
|
+
}
|
|
708
|
+
// ── Context Menu ───────────────────────────────────────────
|
|
709
|
+
onNodeContextMenu(event, node) {
|
|
710
|
+
if (this.ReadOnly)
|
|
711
|
+
return;
|
|
712
|
+
event.preventDefault();
|
|
713
|
+
event.stopPropagation();
|
|
714
|
+
this.showContextMenu(event, 'node', node, null);
|
|
715
|
+
}
|
|
716
|
+
/** Direct click on a connection — ensures ConnectionSelected fires reliably */
|
|
717
|
+
onConnectionClicked(event, conn) {
|
|
718
|
+
// Do NOT stopPropagation — let Foblex handle its own visual selection.
|
|
719
|
+
// Record the time so onSelectionChange can skip double-processing.
|
|
720
|
+
this.lastConnectionClickTime = Date.now();
|
|
721
|
+
this.selectedConnectionIDs = [conn.ID];
|
|
722
|
+
this.selectedNodeIDs = [];
|
|
723
|
+
this.NodeSelected.emit(null);
|
|
724
|
+
this.ConnectionSelected.emit(conn);
|
|
725
|
+
this.cdr.detectChanges();
|
|
726
|
+
}
|
|
727
|
+
onConnectionContextMenu(event, conn) {
|
|
728
|
+
if (this.ReadOnly)
|
|
729
|
+
return;
|
|
730
|
+
event.preventDefault();
|
|
731
|
+
event.stopPropagation();
|
|
732
|
+
this.showContextMenu(event, 'connection', null, conn);
|
|
733
|
+
}
|
|
734
|
+
onContextMenuAction(action) {
|
|
735
|
+
this.hideContextMenu();
|
|
736
|
+
if (action === 'edit') {
|
|
737
|
+
if (this.contextMenuTargetType === 'node' && this.contextMenuNode) {
|
|
738
|
+
this.NodeEditRequested.emit(this.contextMenuNode);
|
|
739
|
+
}
|
|
740
|
+
else if (this.contextMenuTargetType === 'connection' && this.contextMenuConnection) {
|
|
741
|
+
this.ConnectionEditRequested.emit(this.contextMenuConnection);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
else if (action === 'remove') {
|
|
745
|
+
this.pushUndoState();
|
|
746
|
+
if (this.contextMenuTargetType === 'node' && this.contextMenuNode) {
|
|
747
|
+
this.removeNodeById(this.contextMenuNode.ID);
|
|
748
|
+
}
|
|
749
|
+
else if (this.contextMenuTargetType === 'connection' && this.contextMenuConnection) {
|
|
750
|
+
this.removeConnectionById(this.contextMenuConnection.ID);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
onDocumentClick() {
|
|
755
|
+
if (this.contextMenuVisible) {
|
|
756
|
+
this.hideContextMenu();
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
showContextMenu(event, targetType, node, connection) {
|
|
760
|
+
this.contextMenuTargetType = targetType;
|
|
761
|
+
this.contextMenuNode = node;
|
|
762
|
+
this.contextMenuConnection = connection;
|
|
763
|
+
this.contextMenuX = event.clientX;
|
|
764
|
+
this.contextMenuY = event.clientY;
|
|
765
|
+
this.contextMenuVisible = true;
|
|
766
|
+
this.cdr.detectChanges();
|
|
767
|
+
}
|
|
768
|
+
hideContextMenu() {
|
|
769
|
+
this.contextMenuVisible = false;
|
|
770
|
+
this.contextMenuNode = null;
|
|
771
|
+
this.contextMenuConnection = null;
|
|
772
|
+
this.cdr.detectChanges();
|
|
773
|
+
}
|
|
774
|
+
removeNodeById(nodeId) {
|
|
775
|
+
const node = this.Nodes.find(n => n.ID === nodeId);
|
|
776
|
+
if (!node)
|
|
777
|
+
return;
|
|
778
|
+
this.Nodes = this.Nodes.filter(n => n.ID !== nodeId);
|
|
779
|
+
/* Remove connections attached to this node */
|
|
780
|
+
const orphaned = this.Connections.filter(c => c.SourceNodeID === nodeId || c.TargetNodeID === nodeId);
|
|
781
|
+
this.Connections = this.Connections.filter(c => c.SourceNodeID !== nodeId && c.TargetNodeID !== nodeId);
|
|
782
|
+
for (const conn of orphaned) {
|
|
783
|
+
this.ConnectionRemoved.emit(conn);
|
|
784
|
+
}
|
|
785
|
+
this.NodeRemoved.emit(node);
|
|
786
|
+
this.NodesChanged.emit(this.Nodes);
|
|
787
|
+
this.ConnectionsChanged.emit(this.Connections);
|
|
788
|
+
this.cdr.detectChanges();
|
|
789
|
+
}
|
|
790
|
+
removeConnectionById(connId) {
|
|
791
|
+
const conn = this.Connections.find(c => c.ID === connId);
|
|
792
|
+
if (!conn)
|
|
793
|
+
return;
|
|
794
|
+
this.Connections = this.Connections.filter(c => c.ID !== connId);
|
|
795
|
+
this.ConnectionRemoved.emit(conn);
|
|
796
|
+
this.ConnectionsChanged.emit(this.Connections);
|
|
797
|
+
this.cdr.detectChanges();
|
|
798
|
+
}
|
|
799
|
+
// ── Keyboard Shortcuts ─────────────────────────────────────
|
|
800
|
+
onKeyDown(event) {
|
|
801
|
+
if (this.ReadOnly)
|
|
802
|
+
return;
|
|
803
|
+
const isCtrlOrMeta = event.ctrlKey || event.metaKey;
|
|
804
|
+
if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
805
|
+
// Don't delete if user is typing in an input
|
|
806
|
+
const target = event.target;
|
|
807
|
+
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
event.preventDefault();
|
|
811
|
+
this.DeleteSelected();
|
|
812
|
+
}
|
|
813
|
+
else if (isCtrlOrMeta && event.key === 'z' && !event.shiftKey) {
|
|
814
|
+
event.preventDefault();
|
|
815
|
+
this.Undo();
|
|
816
|
+
}
|
|
817
|
+
else if (isCtrlOrMeta && (event.key === 'y' || (event.key === 'z' && event.shiftKey))) {
|
|
818
|
+
event.preventDefault();
|
|
819
|
+
this.Redo();
|
|
820
|
+
}
|
|
821
|
+
else if (isCtrlOrMeta && event.key === 'a') {
|
|
822
|
+
event.preventDefault();
|
|
823
|
+
this.fFlow?.selectAll();
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// ── TrackBy Functions ───────────────────────────────────────
|
|
827
|
+
trackNodeById(_index, node) {
|
|
828
|
+
return node.ID;
|
|
829
|
+
}
|
|
830
|
+
trackConnectionById(_index, conn) {
|
|
831
|
+
return conn.ID;
|
|
832
|
+
}
|
|
833
|
+
// ── Helper Methods ─────────────────────────────────────────
|
|
834
|
+
GetTypeConfig(type) {
|
|
835
|
+
return this.NodeTypes.find(nt => nt.Type === type) ?? null;
|
|
836
|
+
}
|
|
837
|
+
/** Get the connectable side string for Foblex based on port side */
|
|
838
|
+
GetConnectableSide(side) {
|
|
839
|
+
switch (side) {
|
|
840
|
+
case 'left': return 'left';
|
|
841
|
+
case 'right': return 'right';
|
|
842
|
+
case 'top': return 'top';
|
|
843
|
+
case 'bottom': return 'bottom';
|
|
844
|
+
default: return 'auto';
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
get selectedCount() {
|
|
848
|
+
return this.selectedNodeIDs.length + this.selectedConnectionIDs.length;
|
|
849
|
+
}
|
|
850
|
+
pushUndoState() {
|
|
851
|
+
this.stateService.PushState(this.Nodes, this.Connections);
|
|
852
|
+
}
|
|
853
|
+
updateZoomLevel(scale) {
|
|
854
|
+
this.zoomLevel = Math.round(scale * 100);
|
|
855
|
+
this.cdr.detectChanges();
|
|
856
|
+
}
|
|
857
|
+
findNodeByPortId(portId) {
|
|
858
|
+
for (const node of this.Nodes) {
|
|
859
|
+
for (const port of node.Ports) {
|
|
860
|
+
if (port.ID === portId) {
|
|
861
|
+
return node.ID;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// When fConnectOnNode is enabled, Foblex may send the node ID itself
|
|
866
|
+
// instead of a port ID. Check for a direct node ID match as fallback.
|
|
867
|
+
const directMatch = this.Nodes.find(n => n.ID === portId);
|
|
868
|
+
if (directMatch) {
|
|
869
|
+
return directMatch.ID;
|
|
870
|
+
}
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* When fConnectOnNode is enabled, Foblex may send a node ID instead of a port ID.
|
|
875
|
+
* This resolves the raw ID to the correct port ID for the given direction.
|
|
876
|
+
*/
|
|
877
|
+
resolvePortId(rawId, nodeId, direction) {
|
|
878
|
+
const node = this.Nodes.find(n => n.ID === nodeId);
|
|
879
|
+
if (!node)
|
|
880
|
+
return rawId;
|
|
881
|
+
// If the rawId already matches a port on this node, use it as-is
|
|
882
|
+
const exactPort = node.Ports.find(p => p.ID === rawId);
|
|
883
|
+
if (exactPort)
|
|
884
|
+
return rawId;
|
|
885
|
+
// Otherwise, find the first port matching the expected direction
|
|
886
|
+
const dirPort = node.Ports.find(p => p.Direction === direction);
|
|
887
|
+
return dirPort ? dirPort.ID : rawId;
|
|
888
|
+
}
|
|
889
|
+
createDefaultNode(config, position) {
|
|
890
|
+
const id = this.generateId();
|
|
891
|
+
const defaultPorts = config.DefaultPorts ?? [
|
|
892
|
+
{ ID: `${id}-input`, Direction: 'input', Side: 'top', Multiple: true },
|
|
893
|
+
{ ID: `${id}-output`, Direction: 'output', Side: 'bottom', Multiple: true }
|
|
894
|
+
];
|
|
895
|
+
return {
|
|
896
|
+
ID: id,
|
|
897
|
+
Type: config.Type,
|
|
898
|
+
Label: config.Label,
|
|
899
|
+
Icon: config.Icon,
|
|
900
|
+
Status: 'default',
|
|
901
|
+
Position: position,
|
|
902
|
+
Ports: defaultPorts.map(p => ({
|
|
903
|
+
...p,
|
|
904
|
+
ID: p.ID.startsWith(id) ? p.ID : `${id}-${p.ID}`
|
|
905
|
+
}))
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
removeSelectedItems() {
|
|
909
|
+
// Remove selected connections
|
|
910
|
+
const removedConnections = this.Connections.filter(c => this.selectedConnectionIDs.includes(c.ID));
|
|
911
|
+
this.Connections = this.Connections.filter(c => !this.selectedConnectionIDs.includes(c.ID));
|
|
912
|
+
for (const conn of removedConnections) {
|
|
913
|
+
this.ConnectionRemoved.emit(conn);
|
|
914
|
+
}
|
|
915
|
+
// Remove selected nodes and their connections
|
|
916
|
+
const removedNodes = this.Nodes.filter(n => this.selectedNodeIDs.includes(n.ID));
|
|
917
|
+
this.Nodes = this.Nodes.filter(n => !this.selectedNodeIDs.includes(n.ID));
|
|
918
|
+
// Also remove connections attached to deleted nodes
|
|
919
|
+
const deletedNodeIDs = new Set(this.selectedNodeIDs);
|
|
920
|
+
const orphanedConnections = this.Connections.filter(c => deletedNodeIDs.has(c.SourceNodeID) || deletedNodeIDs.has(c.TargetNodeID));
|
|
921
|
+
this.Connections = this.Connections.filter(c => !deletedNodeIDs.has(c.SourceNodeID) && !deletedNodeIDs.has(c.TargetNodeID));
|
|
922
|
+
for (const conn of orphanedConnections) {
|
|
923
|
+
this.ConnectionRemoved.emit(conn);
|
|
924
|
+
}
|
|
925
|
+
for (const node of removedNodes) {
|
|
926
|
+
this.NodeRemoved.emit(node);
|
|
927
|
+
}
|
|
928
|
+
this.NodesChanged.emit(this.Nodes);
|
|
929
|
+
this.ConnectionsChanged.emit(this.Connections);
|
|
930
|
+
this.cdr.detectChanges();
|
|
931
|
+
}
|
|
932
|
+
generateId() {
|
|
933
|
+
return 'flow-' + Math.random().toString(36).substring(2, 11);
|
|
934
|
+
}
|
|
935
|
+
static ɵfac = function FlowEditorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FlowEditorComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.FlowStateService), i0.ɵɵdirectiveInject(i2.FlowLayoutService)); };
|
|
936
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FlowEditorComponent, selectors: [["mj-flow-editor"]], viewQuery: function FlowEditorComponent_Query(rf, ctx) { if (rf & 1) {
|
|
937
|
+
i0.ɵɵviewQuery(FFlowComponent, 5)(FCanvasComponent, 5)(_c0, 5);
|
|
938
|
+
} if (rf & 2) {
|
|
939
|
+
let _t;
|
|
940
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fFlow = _t.first);
|
|
941
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fCanvas = _t.first);
|
|
942
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fZoom = _t.first);
|
|
943
|
+
} }, hostBindings: function FlowEditorComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
944
|
+
i0.ɵɵlistener("click", function FlowEditorComponent_click_HostBindingHandler() { return ctx.onDocumentClick(); }, i0.ɵɵresolveDocument)("keydown", function FlowEditorComponent_keydown_HostBindingHandler($event) { return ctx.onKeyDown($event); });
|
|
945
|
+
} }, inputs: { Nodes: "Nodes", Connections: "Connections", NodeTypes: "NodeTypes", ReadOnly: "ReadOnly", ShowMinimap: "ShowMinimap", ShowPalette: "ShowPalette", ShowGrid: "ShowGrid", ShowStatusBar: "ShowStatusBar", ShowToolbar: "ShowToolbar", ShowLegend: "ShowLegend", GridSize: "GridSize", AutoLayoutDirection: "AutoLayoutDirection", ConnectionLabelBackground: "ConnectionLabelBackground", ConnectionLabelBorderColor: "ConnectionLabelBorderColor", ConnectionLabelTextColor: "ConnectionLabelTextColor" }, outputs: { NodeSelected: "NodeSelected", NodeAdded: "NodeAdded", NodeRemoved: "NodeRemoved", NodeMoved: "NodeMoved", ConnectionCreated: "ConnectionCreated", ConnectionRemoved: "ConnectionRemoved", ConnectionReassigned: "ConnectionReassigned", ConnectionSelected: "ConnectionSelected", SelectionChanged: "SelectionChanged", CanvasClicked: "CanvasClicked", NodesChanged: "NodesChanged", ConnectionsChanged: "ConnectionsChanged", NodeEditRequested: "NodeEditRequested", ConnectionEditRequested: "ConnectionEditRequested", GridToggled: "GridToggled", MinimapToggled: "MinimapToggled", LegendToggled: "LegendToggled" }, standalone: false, features: [i0.ɵɵProvidersFeature([FlowStateService, FlowLayoutService])], decls: 21, vars: 25, consts: [["fZoomRef", "fComponent"], ["tabindex", "0", 1, "mj-flow-editor"], [1, "mj-flow-editor-toolbar-area"], [1, "mj-flow-editor-canvas-area"], [3, "NodeTypes"], [1, "mj-flow-editor-canvas"], ["fDraggable", "", 3, "fLoaded", "fCreateConnection", "fReassignConnection", "fCreateNode", "fSelectionChange", "fMoveNodes", "fDraggableDisabled", "fCanvasMoveTrigger", "vCellSize", "hCellSize", "fCellSizeWhileDragging"], [3, "fAlignThreshold"], ["fZoom", "", 3, "fCanvasChange", "fZoomMinimum", "fZoomMaximum", "fZoomStep", "position", "scale"], ["fType", "bezier", 3, "fRadius", "fOffset"], ["fType", "bezier", "fBehavior", "fixed", 3, "fConnectionId", "fOutputId", "fInputId", "fRadius", "fOffset", "fSelectionDisabled", "fReassignDisabled", "--connection-color", "mj-flow-connection-colored", "mj-flow-connection-dashed", "mj-flow-connection-dotted"], ["fNode", "", "fDragHandle", "", 3, "fNodeId", "fNodePosition", "fNodeDraggingDisabled", "fNodeSelectionDisabled", "fConnectOnNode", "Node", "TypeConfig", "ReadOnly"], ["fDragBlocker", "", 1, "mj-flow-editor-minimap"], [1, "mj-flow-editor-legend"], [1, "mj-flow-editor-empty"], [1, "mj-flow-context-menu", 3, "left", "top"], [3, "NodeCount", "ConnectionCount", "SelectedCount", "ZoomLevel"], [3, "ZoomInClicked", "ZoomOutClicked", "FitToScreenClicked", "AutoLayoutClicked", "GridToggled", "MinimapToggled", "LegendToggled", "PanModeToggled", "ZoomLevel", "ShowGrid", "ShowMinimap", "ShowLegend", "ReadOnly", "PanMode"], [3, "color", "radius"], ["viewBox", "0 0 10 10", "fMarker", "", "type", "end", "orient", "auto", 3, "refX", "refY", "height", "width"], ["d", "M0 0L10 5L0 10Z", "fill", "#94a3b8"], ["fType", "bezier", "fBehavior", "fixed", 3, "click", "contextmenu", "fConnectionId", "fOutputId", "fInputId", "fRadius", "fOffset", "fSelectionDisabled", "fReassignDisabled"], ["d", "M0 0L10 5L0 10Z"], ["fConnectionContent", "", 1, "mj-flow-connection-label", 3, "position", "backgroundColor", "borderColor", "color", "title"], ["fConnectionContent", "", 1, "mj-flow-connection-label", 3, "position", "title"], [1, "fa-solid", "mj-flow-connection-label-icon", 3, "ngClass", "color"], [1, "fa-solid", "mj-flow-connection-label-icon", 3, "ngClass"], ["fNode", "", "fDragHandle", "", 3, "contextmenu", "fNodeId", "fNodePosition", "fNodeDraggingDisabled", "fNodeSelectionDisabled", "fConnectOnNode", "Node", "TypeConfig", "ReadOnly"], [3, "fMinSize"], [1, "mj-flow-editor-legend-title"], [1, "mj-flow-editor-legend-section"], [1, "mj-flow-editor-legend-heading"], [1, "mj-flow-editor-legend-item"], ["width", "28", "height", "10", 1, "mj-flow-editor-legend-line-svg"], ["x1", "0", "y1", "5", "x2", "28", "y2", "5", "stroke", "#64748b", "stroke-width", "2"], ["x1", "0", "y1", "5", "x2", "28", "y2", "5", "stroke", "#16a34a", "stroke-width", "2"], [1, "fa-solid", "fa-circle-check", 2, "color", "#16a34a", "font-size", "10px"], ["x1", "0", "y1", "5", "x2", "28", "y2", "5", "stroke", "#f59e0b", "stroke-width", "2", "stroke-dasharray", "5 3"], ["x1", "0", "y1", "5", "x2", "28", "y2", "5", "stroke", "#ef4444", "stroke-width", "2"], [1, "fa-solid", "fa-triangle-exclamation", 2, "color", "#ef4444", "font-size", "10px"], [1, "mj-flow-editor-legend-swatch", 2, "background", "#3B82F6"], [1, "mj-flow-editor-legend-swatch", 2, "background", "#8B5CF6"], [1, "mj-flow-editor-legend-swatch", 2, "background", "#10B981"], [1, "mj-flow-editor-legend-swatch", 2, "background", "#F59E0B"], [1, "mj-flow-editor-legend-swatch", 2, "background", "#F97316"], [1, "mj-flow-editor-legend-box", 2, "border-color", "#16a34a", "background", "#f0fdf4"], [1, "mj-flow-editor-legend-box", 2, "border-color", "#ef4444", "background", "#fef2f2"], [1, "fa-solid", "fa-triangle-exclamation", 2, "color", "#ea580c", "font-size", "10px"], [1, "mj-flow-editor-empty-content"], [1, "fa-solid", "fa-diagram-project", "mj-flow-editor-empty-icon"], [1, "mj-flow-editor-empty-title"], [1, "mj-flow-editor-empty-subtitle"], [1, "mj-flow-context-menu", 3, "click"], [1, "mj-flow-context-menu-item", 3, "click"], [1, "fa-solid", "fa-pen-to-square"], [1, "mj-flow-context-menu-divider"], [1, "mj-flow-context-menu-item", "mj-flow-context-menu-item--danger", 3, "click"], [1, "fa-solid", "fa-trash-can"]], template: function FlowEditorComponent_Template(rf, ctx) { if (rf & 1) {
|
|
946
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
947
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
948
|
+
i0.ɵɵconditionalCreate(1, FlowEditorComponent_Conditional_1_Template, 2, 6, "div", 2);
|
|
949
|
+
i0.ɵɵelementStart(2, "div", 3);
|
|
950
|
+
i0.ɵɵconditionalCreate(3, FlowEditorComponent_Conditional_3_Template, 1, 1, "mj-flow-palette", 4);
|
|
951
|
+
i0.ɵɵelementStart(4, "div", 5)(5, "f-flow", 6);
|
|
952
|
+
i0.ɵɵlistener("fLoaded", function FlowEditorComponent_Template_f_flow_fLoaded_5_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onFlowLoaded()); })("fCreateConnection", function FlowEditorComponent_Template_f_flow_fCreateConnection_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCreateConnection($event)); })("fReassignConnection", function FlowEditorComponent_Template_f_flow_fReassignConnection_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onReassignConnection($event)); })("fCreateNode", function FlowEditorComponent_Template_f_flow_fCreateNode_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCreateNode($event)); })("fSelectionChange", function FlowEditorComponent_Template_f_flow_fSelectionChange_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onSelectionChange($event)); })("fMoveNodes", function FlowEditorComponent_Template_f_flow_fMoveNodes_5_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onNodesMoved($event)); });
|
|
953
|
+
i0.ɵɵconditionalCreate(6, FlowEditorComponent_Conditional_6_Template, 2, 2, "f-background");
|
|
954
|
+
i0.ɵɵconditionalCreate(7, FlowEditorComponent_Conditional_7_Template, 1, 1, "f-line-alignment", 7);
|
|
955
|
+
i0.ɵɵelementStart(8, "f-canvas", 8, 0);
|
|
956
|
+
i0.ɵɵlistener("fCanvasChange", function FlowEditorComponent_Template_f_canvas_fCanvasChange_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCanvasChange($event)); });
|
|
957
|
+
i0.ɵɵconditionalCreate(10, FlowEditorComponent_Conditional_10_Template, 3, 6, "f-connection-for-create", 9);
|
|
958
|
+
i0.ɵɵrepeaterCreate(11, FlowEditorComponent_For_12_Template, 4, 21, "f-connection", 10, ctx.trackConnectionById, true);
|
|
959
|
+
i0.ɵɵrepeaterCreate(13, FlowEditorComponent_For_14_Template, 1, 11, "mj-flow-node", 11, ctx.trackNodeById, true);
|
|
960
|
+
i0.ɵɵelementEnd();
|
|
961
|
+
i0.ɵɵconditionalCreate(15, FlowEditorComponent_Conditional_15_Template, 1, 0, "f-selection-area");
|
|
962
|
+
i0.ɵɵconditionalCreate(16, FlowEditorComponent_Conditional_16_Template, 2, 1, "div", 12);
|
|
963
|
+
i0.ɵɵelementEnd();
|
|
964
|
+
i0.ɵɵconditionalCreate(17, FlowEditorComponent_Conditional_17_Template, 63, 0, "div", 13);
|
|
965
|
+
i0.ɵɵconditionalCreate(18, FlowEditorComponent_Conditional_18_Template, 6, 3, "div", 14);
|
|
966
|
+
i0.ɵɵelementEnd()();
|
|
967
|
+
i0.ɵɵconditionalCreate(19, FlowEditorComponent_Conditional_19_Template, 10, 6, "div", 15);
|
|
968
|
+
i0.ɵɵconditionalCreate(20, FlowEditorComponent_Conditional_20_Template, 1, 4, "mj-flow-status-bar", 16);
|
|
969
|
+
i0.ɵɵelementEnd();
|
|
970
|
+
} if (rf & 2) {
|
|
971
|
+
i0.ɵɵclassProp("mj-flow-editor--readonly", ctx.ReadOnly);
|
|
972
|
+
i0.ɵɵadvance();
|
|
973
|
+
i0.ɵɵconditional(ctx.ShowToolbar ? 1 : -1);
|
|
974
|
+
i0.ɵɵadvance(2);
|
|
975
|
+
i0.ɵɵconditional(ctx.ShowPalette && !ctx.ReadOnly ? 3 : -1);
|
|
976
|
+
i0.ɵɵadvance();
|
|
977
|
+
i0.ɵɵclassProp("mj-flow-editor-canvas--pan-mode", ctx.panMode);
|
|
978
|
+
i0.ɵɵadvance();
|
|
979
|
+
i0.ɵɵproperty("fDraggableDisabled", false)("fCanvasMoveTrigger", ctx.canvasMoveTriggerFn)("vCellSize", ctx.GridSize)("hCellSize", ctx.GridSize)("fCellSizeWhileDragging", ctx.ShowGrid);
|
|
980
|
+
i0.ɵɵadvance();
|
|
981
|
+
i0.ɵɵconditional(ctx.ShowGrid ? 6 : -1);
|
|
982
|
+
i0.ɵɵadvance();
|
|
983
|
+
i0.ɵɵconditional(!ctx.ReadOnly ? 7 : -1);
|
|
984
|
+
i0.ɵɵadvance();
|
|
985
|
+
i0.ɵɵproperty("fZoomMinimum", 0.15)("fZoomMaximum", 3)("fZoomStep", 0.1)("position", ctx.canvasPosition)("scale", ctx.canvasScale);
|
|
986
|
+
i0.ɵɵadvance(2);
|
|
987
|
+
i0.ɵɵconditional(!ctx.ReadOnly ? 10 : -1);
|
|
988
|
+
i0.ɵɵadvance();
|
|
989
|
+
i0.ɵɵrepeater(ctx.Connections);
|
|
990
|
+
i0.ɵɵadvance(2);
|
|
991
|
+
i0.ɵɵrepeater(ctx.Nodes);
|
|
992
|
+
i0.ɵɵadvance(2);
|
|
993
|
+
i0.ɵɵconditional(!ctx.ReadOnly ? 15 : -1);
|
|
994
|
+
i0.ɵɵadvance();
|
|
995
|
+
i0.ɵɵconditional(ctx.ShowMinimap ? 16 : -1);
|
|
996
|
+
i0.ɵɵadvance();
|
|
997
|
+
i0.ɵɵconditional(ctx.ShowLegend && ctx.Nodes.length > 0 ? 17 : -1);
|
|
998
|
+
i0.ɵɵadvance();
|
|
999
|
+
i0.ɵɵconditional(ctx.Nodes.length === 0 ? 18 : -1);
|
|
1000
|
+
i0.ɵɵadvance();
|
|
1001
|
+
i0.ɵɵconditional(ctx.contextMenuVisible ? 19 : -1);
|
|
1002
|
+
i0.ɵɵadvance();
|
|
1003
|
+
i0.ɵɵconditional(ctx.ShowStatusBar ? 20 : -1);
|
|
1004
|
+
} }, dependencies: [i3.NgClass, i4.FFlowComponent, i4.FCanvasComponent, i4.FBackgroundComponent, i4.FCirclePatternComponent, i4.FZoomDirective, i4.FConnectionContent, i4.FConnectionMarker, i4.FConnectionComponent, i4.FConnectionForCreateComponent, i4.FLineAlignmentComponent, i4.FMinimapComponent, i4.FNodeDirective, i4.FDragHandleDirective, i4.FSelectionAreaComponent, i4.FDragBlockerDirective, i4.FDraggableDirective, i5.FlowNodeComponent, i6.FlowPaletteComponent, i7.FlowToolbarComponent, i8.FlowStatusBarComponent], styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n.mj-flow-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n overflow: hidden;\n outline: none;\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n}\n\n.mj-flow-editor--readonly .mj-flow-node-port {\n cursor: default;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Toolbar Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-toolbar-area {\n position: absolute;\n top: 10px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n}\n\n/* \u2500\u2500 Canvas Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-canvas-area {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-flow-editor-canvas {\n flex: 1;\n position: relative;\n overflow: hidden;\n background: #ffffff;\n}\n\n.mj-flow-editor-canvas f-flow {\n width: 100%;\n height: 100%;\n}\n\n/* Pan mode \u2014 grab cursor, nodes pass through pointer events to canvas */\n.mj-flow-editor-canvas--pan-mode {\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode:active {\n cursor: grabbing;\n}\n\n.mj-flow-editor-canvas--pan-mode mj-flow-node {\n pointer-events: none;\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode f-connection {\n pointer-events: none;\n}\n\n.mj-flow-editor-canvas--pan-mode .mj-flow-connection-label {\n pointer-events: none;\n}\n\n/* \u2500\u2500 Minimap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-minimap {\n position: absolute;\n bottom: 12px;\n right: 12px;\n width: 160px;\n height: 120px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n z-index: 15;\n}\n\n.mj-flow-editor-minimap f-minimap {\n width: 100%;\n height: 100%;\n}\n\n/* Foblex minimap SVG node rectangles (no default styles provided by Foblex) */\n.mj-flow-editor-minimap .f-minimap-node {\n fill: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: #93c5fd;\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: #e2e8f0;\n stroke: #94a3b8;\n stroke-width: 0.5;\n rx: 2;\n ry: 2;\n}\n\n/* Viewport indicator rectangle */\n.mj-flow-editor-minimap .f-minimap-view {\n fill: rgba(59, 130, 246, 0.08);\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-empty {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 5;\n}\n\n.mj-flow-editor-empty-content {\n text-align: center;\n padding: 40px;\n}\n\n.mj-flow-editor-empty-icon {\n font-size: 48px;\n color: #cbd5e1;\n margin-bottom: 16px;\n}\n\n.mj-flow-editor-empty-title {\n font-size: 15px;\n font-weight: 500;\n color: #64748b;\n margin: 0 0 8px;\n}\n\n.mj-flow-editor-empty-subtitle {\n font-size: 13px;\n color: #94a3b8;\n margin: 0;\n}\n\n/* \u2500\u2500 Connection Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-connection-label {\n display: inline-flex;\n align-items: flex-start;\n gap: 5px;\n background: #fffef5;\n border: 1.5px solid #cbd5e1;\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: #334155;\n white-space: nowrap;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n pointer-events: none;\n line-height: 1.4;\n text-align: center;\n}\n\n.mj-flow-connection-label-icon {\n font-size: 10px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Foblex Overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Canvas \u2014 transparent so the grid pattern shows through */\nf-canvas {\n background: transparent;\n}\n\n/* Selection area */\nf-selection-area {\n --f-selection-area-background: rgba(59, 130, 246, 0.08);\n --f-selection-area-border: 1px solid rgba(59, 130, 246, 0.3);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: #3b82f6;\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: #94a3b8;\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: 3;\n}\n\n.f-connection.f-selected path {\n stroke: #3b82f6;\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, #94a3b8);\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, #3b82f6);\n stroke-width: 3;\n filter: brightness(0.85);\n}\n\n/* Dashed connection style */\n.mj-flow-connection-dashed path {\n stroke-dasharray: 8 4;\n}\n\n/* Dotted connection style */\n.mj-flow-connection-dotted path {\n stroke-dasharray: 3 3;\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: #94a3b8;\n stroke-dasharray: 6 3;\n stroke-width: 2;\n}\n\n/* \u2500\u2500 Legend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-legend {\n position: absolute;\n bottom: 12px;\n left: 12px;\n background: rgba(255, 255, 255, 0.92);\n backdrop-filter: blur(6px);\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 10px 14px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n z-index: 15;\n font-size: 11px;\n color: #475569;\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: #1e293b;\n margin-bottom: 8px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-flow-editor-legend-section {\n margin-bottom: 8px;\n}\n\n.mj-flow-editor-legend-section:last-child {\n margin-bottom: 0;\n}\n\n.mj-flow-editor-legend-heading {\n font-size: 10px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 4px;\n}\n\n.mj-flow-editor-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 0;\n font-size: 11px;\n color: #475569;\n}\n\n.mj-flow-editor-legend-svg,\n.mj-flow-editor-legend-line-svg {\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-swatch {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-box {\n display: inline-block;\n width: 14px;\n height: 10px;\n border: 2px solid;\n border-radius: 3px;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Context Menu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-context-menu {\n position: fixed;\n min-width: 180px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.06);\n padding: 4px;\n z-index: 9999;\n animation: mj-ctx-fade-in 0.1s ease-out;\n}\n\n@keyframes mj-ctx-fade-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n}\n\n.mj-flow-context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 8px 12px;\n border: none;\n background: none;\n font-size: 13px;\n font-weight: 450;\n color: #334155;\n cursor: pointer;\n border-radius: 5px;\n text-align: left;\n transition: background-color 0.1s ease;\n}\n\n.mj-flow-context-menu-item:hover {\n background-color: #f1f5f9;\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: 12px;\n color: #64748b;\n}\n\n.mj-flow-context-menu-item--danger {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: #fef2f2;\n}\n\n.mj-flow-context-menu-item--danger i {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: #e2e8f0;\n margin: 4px 8px;\n}\n\n/* \u2500\u2500 Focus Indicators (Accessibility) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-toolbar-btn:focus-visible,\n.mj-agent-editor-btn:focus-visible,\n.mj-agent-editor-view-btn:focus-visible,\n.mj-agent-props-close:focus-visible,\n.mj-agent-props-delete-btn:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 1px;\n border-radius: 6px;\n}\n"], encapsulation: 2 });
|
|
1005
|
+
}
|
|
1006
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowEditorComponent, [{
|
|
1007
|
+
type: Component,
|
|
1008
|
+
args: [{ standalone: false, selector: 'mj-flow-editor', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, providers: [FlowStateService, FlowLayoutService], template: "<div class=\"mj-flow-editor\" [class.mj-flow-editor--readonly]=\"ReadOnly\" tabindex=\"0\">\n\n <!-- Canvas Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-flow-editor-toolbar-area\">\n <mj-flow-toolbar\n [ZoomLevel]=\"zoomLevel\"\n [ShowGrid]=\"ShowGrid\"\n [ShowMinimap]=\"ShowMinimap\"\n [ShowLegend]=\"ShowLegend\"\n [ReadOnly]=\"ReadOnly\"\n [PanMode]=\"panMode\"\n (ZoomInClicked)=\"ZoomIn()\"\n (ZoomOutClicked)=\"ZoomOut()\"\n (FitToScreenClicked)=\"ZoomToFit()\"\n (AutoLayoutClicked)=\"AutoArrange()\"\n (GridToggled)=\"onGridToggled($event)\"\n (MinimapToggled)=\"onMinimapToggled($event)\"\n (LegendToggled)=\"onLegendToggled($event)\"\n (PanModeToggled)=\"onPanModeToggled($event)\">\n </mj-flow-toolbar>\n </div>\n }\n\n <!-- Main Canvas Area -->\n <div class=\"mj-flow-editor-canvas-area\">\n\n <!-- Palette Sidebar -->\n @if (ShowPalette && !ReadOnly) {\n <mj-flow-palette\n [NodeTypes]=\"NodeTypes\">\n </mj-flow-palette>\n }\n\n <!-- Foblex Canvas -->\n <div class=\"mj-flow-editor-canvas\"\n [class.mj-flow-editor-canvas--pan-mode]=\"panMode\">\n <f-flow fDraggable\n [fDraggableDisabled]=\"false\"\n [fCanvasMoveTrigger]=\"canvasMoveTriggerFn\"\n [vCellSize]=\"GridSize\"\n [hCellSize]=\"GridSize\"\n [fCellSizeWhileDragging]=\"ShowGrid\"\n (fLoaded)=\"onFlowLoaded()\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n (fMoveNodes)=\"onNodesMoved($event)\">\n\n <!-- Background grid \u2014 MUST be direct child of f-flow (Foblex content projection requires it) -->\n @if (ShowGrid) {\n <f-background>\n <f-circle-pattern [color]=\"'#94a3b8'\" [radius]=\"24\"></f-circle-pattern>\n </f-background>\n }\n\n <!-- Alignment snap lines \u2014 direct child of f-flow -->\n @if (!ReadOnly) {\n <f-line-alignment [fAlignThreshold]=\"10\"></f-line-alignment>\n }\n\n <f-canvas fZoom #fZoomRef=\"fComponent\"\n [fZoomMinimum]=\"0.15\"\n [fZoomMaximum]=\"3\"\n [fZoomStep]=\"0.1\"\n [position]=\"canvasPosition\"\n [scale]=\"canvasScale\"\n (fCanvasChange)=\"onCanvasChange($event)\">\n\n <!-- Interactive connection drawing -->\n @if (!ReadOnly) {\n <f-connection-for-create\n fType=\"bezier\"\n [fRadius]=\"8\"\n [fOffset]=\"12\">\n <svg viewBox=\"0 0 10 10\" fMarker type=\"end\"\n orient=\"auto\" [refX]=\"9\" [refY]=\"5\" [height]=\"10\" [width]=\"10\">\n <path d=\"M0 0L10 5L0 10Z\" fill=\"#94a3b8\"/>\n </svg>\n </f-connection-for-create>\n }\n\n <!-- Connections -->\n @for (conn of Connections; track trackConnectionById($index, conn)) {\n <f-connection\n [fConnectionId]=\"conn.ID\"\n [fOutputId]=\"conn.SourcePortID\"\n [fInputId]=\"conn.TargetPortID\"\n fType=\"bezier\"\n fBehavior=\"fixed\"\n [fRadius]=\"8\"\n [fOffset]=\"12\"\n [fSelectionDisabled]=\"false\"\n [fReassignDisabled]=\"ReadOnly\"\n [style.--connection-color]=\"conn.Color || '#94a3b8'\"\n [class.mj-flow-connection-colored]=\"!!conn.Color\"\n [class.mj-flow-connection-dashed]=\"conn.Style === 'dashed'\"\n [class.mj-flow-connection-dotted]=\"conn.Style === 'dotted'\"\n (click)=\"onConnectionClicked($event, conn)\"\n (contextmenu)=\"onConnectionContextMenu($event, conn)\">\n <!-- Arrow marker -->\n <svg viewBox=\"0 0 10 10\" fMarker type=\"end\"\n orient=\"auto\" [refX]=\"9\" [refY]=\"5\" [height]=\"10\" [width]=\"10\">\n <path d=\"M0 0L10 5L0 10Z\" [attr.fill]=\"conn.Color || '#94a3b8'\"/>\n </svg>\n <!-- Connection label -->\n @if (conn.Label) {\n <div fConnectionContent [position]=\"0.5\"\n class=\"mj-flow-connection-label\"\n [style.backgroundColor]=\"ConnectionLabelBackground\"\n [style.borderColor]=\"ConnectionLabelBorderColor\"\n [style.color]=\"ConnectionLabelTextColor\"\n [title]=\"conn.LabelDetail || conn.Label || ''\">\n @if (conn.LabelIcon) {\n <i\n class=\"fa-solid mj-flow-connection-label-icon\"\n [ngClass]=\"conn.LabelIcon\"\n [style.color]=\"conn.LabelIconColor || 'inherit'\"></i>\n }\n {{ conn.Label }}\n </div>\n }\n </f-connection>\n }\n\n <!-- Nodes -->\n @for (node of Nodes; track trackNodeById($index, node)) {\n <mj-flow-node\n fNode\n fDragHandle\n [fNodeId]=\"node.ID\"\n [fNodePosition]=\"{ x: node.Position.X, y: node.Position.Y }\"\n [fNodeDraggingDisabled]=\"ReadOnly || panMode || node.Locked === true\"\n [fNodeSelectionDisabled]=\"panMode\"\n [fConnectOnNode]=\"!ReadOnly && !panMode\"\n [Node]=\"node\"\n [TypeConfig]=\"GetTypeConfig(node.Type)\"\n [ReadOnly]=\"ReadOnly\"\n (contextmenu)=\"onNodeContextMenu($event, node)\">\n </mj-flow-node>\n }\n\n </f-canvas>\n\n <!-- Rubber-band selection \u2014 direct child of f-flow -->\n @if (!ReadOnly) {\n <f-selection-area></f-selection-area>\n }\n\n <!-- Minimap -->\n @if (ShowMinimap) {\n <div class=\"mj-flow-editor-minimap\" fDragBlocker>\n <f-minimap [fMinSize]=\"1500\"></f-minimap>\n </div>\n }\n </f-flow>\n\n <!-- Legend -->\n @if (ShowLegend && Nodes.length > 0) {\n <div class=\"mj-flow-editor-legend\">\n <div class=\"mj-flow-editor-legend-title\">Legend</div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Connections</div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#64748b\" stroke-width=\"2\"/></svg>\n <span>Only exit path</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#16a34a\" stroke-width=\"2\"/></svg>\n <i class=\"fa-solid fa-circle-check\" style=\"color:#16a34a;font-size:10px;\"></i>\n <span>Default (fallback)</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#f59e0b\" stroke-width=\"2\" stroke-dasharray=\"5 3\"/></svg>\n <span>Conditional</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#ef4444\" stroke-width=\"2\"/></svg>\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:#ef4444;font-size:10px;\"></i>\n <span>Duplicate defaults</span>\n </div>\n </div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Step Types</div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#3B82F6\"></span>\n <span>Action</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#8B5CF6\"></span>\n <span>Prompt</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#10B981\"></span>\n <span>Sub-Agent</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#F59E0B\"></span>\n <span>For Each</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#F97316\"></span>\n <span>While</span>\n </div>\n </div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Borders</div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-box\" style=\"border-color:#16a34a;background:#f0fdf4;\"></span>\n <span>Start node</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-box\" style=\"border-color:#ef4444;background:#fef2f2;\"></span>\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:#ea580c;font-size:10px;\"></i>\n <span>Missing config</span>\n </div>\n </div>\n </div>\n }\n\n <!-- Empty state overlay -->\n @if (Nodes.length === 0) {\n <div class=\"mj-flow-editor-empty\">\n <div class=\"mj-flow-editor-empty-content\">\n <i class=\"fa-solid fa-diagram-project mj-flow-editor-empty-icon\"></i>\n @if (!ReadOnly) {\n <p class=\"mj-flow-editor-empty-title\">Drag steps from the palette to start building your flow</p>\n }\n @if (ReadOnly) {\n <p class=\"mj-flow-editor-empty-title\">No steps defined for this flow</p>\n }\n @if (!ReadOnly) {\n <p class=\"mj-flow-editor-empty-subtitle\">\n Or use auto-layout after adding steps to arrange them automatically\n </p>\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Context Menu -->\n @if (contextMenuVisible) {\n <div\n class=\"mj-flow-context-menu\"\n [style.left.px]=\"contextMenuX\"\n [style.top.px]=\"contextMenuY\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"mj-flow-context-menu-item\" (click)=\"onContextMenuAction('edit')\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n <span>Edit {{ contextMenuTargetType === 'node' ? 'Node' : 'Connection' }}</span>\n </button>\n <div class=\"mj-flow-context-menu-divider\"></div>\n <button class=\"mj-flow-context-menu-item mj-flow-context-menu-item--danger\" (click)=\"onContextMenuAction('remove')\">\n <i class=\"fa-solid fa-trash-can\"></i>\n <span>Remove {{ contextMenuTargetType === 'node' ? 'Node' : 'Connection' }}</span>\n </button>\n </div>\n }\n\n <!-- Status Bar -->\n @if (ShowStatusBar) {\n <mj-flow-status-bar\n [NodeCount]=\"Nodes.length\"\n [ConnectionCount]=\"Connections.length\"\n [SelectedCount]=\"selectedCount\"\n [ZoomLevel]=\"zoomLevel\">\n </mj-flow-status-bar>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n.mj-flow-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n overflow: hidden;\n outline: none;\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n}\n\n.mj-flow-editor--readonly .mj-flow-node-port {\n cursor: default;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Toolbar Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-toolbar-area {\n position: absolute;\n top: 10px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n}\n\n/* \u2500\u2500 Canvas Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-canvas-area {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-flow-editor-canvas {\n flex: 1;\n position: relative;\n overflow: hidden;\n background: #ffffff;\n}\n\n.mj-flow-editor-canvas f-flow {\n width: 100%;\n height: 100%;\n}\n\n/* Pan mode \u2014 grab cursor, nodes pass through pointer events to canvas */\n.mj-flow-editor-canvas--pan-mode {\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode:active {\n cursor: grabbing;\n}\n\n.mj-flow-editor-canvas--pan-mode mj-flow-node {\n pointer-events: none;\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode f-connection {\n pointer-events: none;\n}\n\n.mj-flow-editor-canvas--pan-mode .mj-flow-connection-label {\n pointer-events: none;\n}\n\n/* \u2500\u2500 Minimap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-minimap {\n position: absolute;\n bottom: 12px;\n right: 12px;\n width: 160px;\n height: 120px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n z-index: 15;\n}\n\n.mj-flow-editor-minimap f-minimap {\n width: 100%;\n height: 100%;\n}\n\n/* Foblex minimap SVG node rectangles (no default styles provided by Foblex) */\n.mj-flow-editor-minimap .f-minimap-node {\n fill: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: #93c5fd;\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: #e2e8f0;\n stroke: #94a3b8;\n stroke-width: 0.5;\n rx: 2;\n ry: 2;\n}\n\n/* Viewport indicator rectangle */\n.mj-flow-editor-minimap .f-minimap-view {\n fill: rgba(59, 130, 246, 0.08);\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-empty {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 5;\n}\n\n.mj-flow-editor-empty-content {\n text-align: center;\n padding: 40px;\n}\n\n.mj-flow-editor-empty-icon {\n font-size: 48px;\n color: #cbd5e1;\n margin-bottom: 16px;\n}\n\n.mj-flow-editor-empty-title {\n font-size: 15px;\n font-weight: 500;\n color: #64748b;\n margin: 0 0 8px;\n}\n\n.mj-flow-editor-empty-subtitle {\n font-size: 13px;\n color: #94a3b8;\n margin: 0;\n}\n\n/* \u2500\u2500 Connection Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-connection-label {\n display: inline-flex;\n align-items: flex-start;\n gap: 5px;\n background: #fffef5;\n border: 1.5px solid #cbd5e1;\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: #334155;\n white-space: nowrap;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n pointer-events: none;\n line-height: 1.4;\n text-align: center;\n}\n\n.mj-flow-connection-label-icon {\n font-size: 10px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Foblex Overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Canvas \u2014 transparent so the grid pattern shows through */\nf-canvas {\n background: transparent;\n}\n\n/* Selection area */\nf-selection-area {\n --f-selection-area-background: rgba(59, 130, 246, 0.08);\n --f-selection-area-border: 1px solid rgba(59, 130, 246, 0.3);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: #3b82f6;\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: #94a3b8;\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: 3;\n}\n\n.f-connection.f-selected path {\n stroke: #3b82f6;\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, #94a3b8);\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, #3b82f6);\n stroke-width: 3;\n filter: brightness(0.85);\n}\n\n/* Dashed connection style */\n.mj-flow-connection-dashed path {\n stroke-dasharray: 8 4;\n}\n\n/* Dotted connection style */\n.mj-flow-connection-dotted path {\n stroke-dasharray: 3 3;\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: #94a3b8;\n stroke-dasharray: 6 3;\n stroke-width: 2;\n}\n\n/* \u2500\u2500 Legend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-legend {\n position: absolute;\n bottom: 12px;\n left: 12px;\n background: rgba(255, 255, 255, 0.92);\n backdrop-filter: blur(6px);\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 10px 14px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n z-index: 15;\n font-size: 11px;\n color: #475569;\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: #1e293b;\n margin-bottom: 8px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-flow-editor-legend-section {\n margin-bottom: 8px;\n}\n\n.mj-flow-editor-legend-section:last-child {\n margin-bottom: 0;\n}\n\n.mj-flow-editor-legend-heading {\n font-size: 10px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 4px;\n}\n\n.mj-flow-editor-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 0;\n font-size: 11px;\n color: #475569;\n}\n\n.mj-flow-editor-legend-svg,\n.mj-flow-editor-legend-line-svg {\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-swatch {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-box {\n display: inline-block;\n width: 14px;\n height: 10px;\n border: 2px solid;\n border-radius: 3px;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Context Menu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-context-menu {\n position: fixed;\n min-width: 180px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.06);\n padding: 4px;\n z-index: 9999;\n animation: mj-ctx-fade-in 0.1s ease-out;\n}\n\n@keyframes mj-ctx-fade-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n}\n\n.mj-flow-context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 8px 12px;\n border: none;\n background: none;\n font-size: 13px;\n font-weight: 450;\n color: #334155;\n cursor: pointer;\n border-radius: 5px;\n text-align: left;\n transition: background-color 0.1s ease;\n}\n\n.mj-flow-context-menu-item:hover {\n background-color: #f1f5f9;\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: 12px;\n color: #64748b;\n}\n\n.mj-flow-context-menu-item--danger {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: #fef2f2;\n}\n\n.mj-flow-context-menu-item--danger i {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: #e2e8f0;\n margin: 4px 8px;\n}\n\n/* \u2500\u2500 Focus Indicators (Accessibility) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-toolbar-btn:focus-visible,\n.mj-agent-editor-btn:focus-visible,\n.mj-agent-editor-view-btn:focus-visible,\n.mj-agent-props-close:focus-visible,\n.mj-agent-props-delete-btn:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 1px;\n border-radius: 6px;\n}\n"] }]
|
|
1009
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.FlowStateService }, { type: i2.FlowLayoutService }], { Nodes: [{
|
|
1010
|
+
type: Input
|
|
1011
|
+
}], Connections: [{
|
|
1012
|
+
type: Input
|
|
1013
|
+
}], NodeTypes: [{
|
|
1014
|
+
type: Input
|
|
1015
|
+
}], ReadOnly: [{
|
|
1016
|
+
type: Input
|
|
1017
|
+
}], ShowMinimap: [{
|
|
1018
|
+
type: Input
|
|
1019
|
+
}], ShowPalette: [{
|
|
1020
|
+
type: Input
|
|
1021
|
+
}], ShowGrid: [{
|
|
1022
|
+
type: Input
|
|
1023
|
+
}], ShowStatusBar: [{
|
|
1024
|
+
type: Input
|
|
1025
|
+
}], ShowToolbar: [{
|
|
1026
|
+
type: Input
|
|
1027
|
+
}], ShowLegend: [{
|
|
1028
|
+
type: Input
|
|
1029
|
+
}], GridSize: [{
|
|
1030
|
+
type: Input
|
|
1031
|
+
}], AutoLayoutDirection: [{
|
|
1032
|
+
type: Input
|
|
1033
|
+
}], ConnectionLabelBackground: [{
|
|
1034
|
+
type: Input
|
|
1035
|
+
}], ConnectionLabelBorderColor: [{
|
|
1036
|
+
type: Input
|
|
1037
|
+
}], ConnectionLabelTextColor: [{
|
|
1038
|
+
type: Input
|
|
1039
|
+
}], NodeSelected: [{
|
|
1040
|
+
type: Output
|
|
1041
|
+
}], NodeAdded: [{
|
|
1042
|
+
type: Output
|
|
1043
|
+
}], NodeRemoved: [{
|
|
1044
|
+
type: Output
|
|
1045
|
+
}], NodeMoved: [{
|
|
1046
|
+
type: Output
|
|
1047
|
+
}], ConnectionCreated: [{
|
|
1048
|
+
type: Output
|
|
1049
|
+
}], ConnectionRemoved: [{
|
|
1050
|
+
type: Output
|
|
1051
|
+
}], ConnectionReassigned: [{
|
|
1052
|
+
type: Output
|
|
1053
|
+
}], ConnectionSelected: [{
|
|
1054
|
+
type: Output
|
|
1055
|
+
}], SelectionChanged: [{
|
|
1056
|
+
type: Output
|
|
1057
|
+
}], CanvasClicked: [{
|
|
1058
|
+
type: Output
|
|
1059
|
+
}], NodesChanged: [{
|
|
1060
|
+
type: Output
|
|
1061
|
+
}], ConnectionsChanged: [{
|
|
1062
|
+
type: Output
|
|
1063
|
+
}], NodeEditRequested: [{
|
|
1064
|
+
type: Output
|
|
1065
|
+
}], ConnectionEditRequested: [{
|
|
1066
|
+
type: Output
|
|
1067
|
+
}], GridToggled: [{
|
|
1068
|
+
type: Output
|
|
1069
|
+
}], MinimapToggled: [{
|
|
1070
|
+
type: Output
|
|
1071
|
+
}], LegendToggled: [{
|
|
1072
|
+
type: Output
|
|
1073
|
+
}], fFlow: [{
|
|
1074
|
+
type: ViewChild,
|
|
1075
|
+
args: [FFlowComponent]
|
|
1076
|
+
}], fCanvas: [{
|
|
1077
|
+
type: ViewChild,
|
|
1078
|
+
args: [FCanvasComponent]
|
|
1079
|
+
}], fZoom: [{
|
|
1080
|
+
type: ViewChild,
|
|
1081
|
+
args: ['fZoomRef']
|
|
1082
|
+
}], onDocumentClick: [{
|
|
1083
|
+
type: HostListener,
|
|
1084
|
+
args: ['document:click']
|
|
1085
|
+
}], onKeyDown: [{
|
|
1086
|
+
type: HostListener,
|
|
1087
|
+
args: ['keydown', ['$event']]
|
|
1088
|
+
}] }); })();
|
|
1089
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FlowEditorComponent, { className: "FlowEditorComponent", filePath: "src/lib/components/flow-editor.component.ts", lineNumber: 43 }); })();
|
|
1090
|
+
//# sourceMappingURL=flow-editor.component.js.map
|