@memberjunction/ng-flow-editor 6.1.0-edge.1 → 6.1.0-edge.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/LICENSE +180 -4
  2. package/dist/lib/agent-editor/agent-flow-transformer.service.d.ts +31 -0
  3. package/dist/lib/agent-editor/agent-flow-transformer.service.d.ts.map +1 -1
  4. package/dist/lib/agent-editor/agent-flow-transformer.service.js +85 -22
  5. package/dist/lib/agent-editor/agent-flow-transformer.service.js.map +1 -1
  6. package/dist/lib/agent-editor/flow-agent-editor.component.d.ts +46 -1
  7. package/dist/lib/agent-editor/flow-agent-editor.component.d.ts.map +1 -1
  8. package/dist/lib/agent-editor/flow-agent-editor.component.js +160 -97
  9. package/dist/lib/agent-editor/flow-agent-editor.component.js.map +1 -1
  10. package/dist/lib/components/flow-editor.component.d.ts +53 -5
  11. package/dist/lib/components/flow-editor.component.d.ts.map +1 -1
  12. package/dist/lib/components/flow-editor.component.js +354 -177
  13. package/dist/lib/components/flow-editor.component.js.map +1 -1
  14. package/dist/lib/components/flow-node.component.d.ts +5 -0
  15. package/dist/lib/components/flow-node.component.d.ts.map +1 -1
  16. package/dist/lib/components/flow-node.component.js +79 -44
  17. package/dist/lib/components/flow-node.component.js.map +1 -1
  18. package/dist/lib/components/flow-palette.component.d.ts +22 -3
  19. package/dist/lib/components/flow-palette.component.d.ts.map +1 -1
  20. package/dist/lib/components/flow-palette.component.js +48 -23
  21. package/dist/lib/components/flow-palette.component.js.map +1 -1
  22. package/dist/lib/components/flow-toolbar.component.d.ts +3 -1
  23. package/dist/lib/components/flow-toolbar.component.d.ts.map +1 -1
  24. package/dist/lib/components/flow-toolbar.component.js +32 -4
  25. package/dist/lib/components/flow-toolbar.component.js.map +1 -1
  26. package/dist/lib/flow-editor.module.d.ts +7 -6
  27. package/dist/lib/flow-editor.module.d.ts.map +1 -1
  28. package/dist/lib/flow-editor.module.js +6 -2
  29. package/dist/lib/flow-editor.module.js.map +1 -1
  30. package/dist/lib/interfaces/flow-types.d.ts +52 -3
  31. package/dist/lib/interfaces/flow-types.d.ts.map +1 -1
  32. package/dist/lib/interfaces/flow-types.js +33 -1
  33. package/dist/lib/interfaces/flow-types.js.map +1 -1
  34. package/package.json +10 -9
@@ -1,6 +1,7 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, HostListener } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
3
  import { FFlowComponent, FCanvasComponent } from '@foblex/flow';
4
+ import { FlowBeforeContextMenuEventArgs, FlowAfterContextMenuActionEventArgs, } from '../interfaces/flow-types';
4
5
  import { FlowStateService } from '../services/flow-state.service';
5
6
  import { FlowLayoutService } from '../services/flow-layout.service';
6
7
  import { UUIDsEqual, NormalizeUUID } from '@memberjunction/global';
@@ -15,208 +16,234 @@ import * as i7 from "./flow-palette.component";
15
16
  import * as i8 from "./flow-toolbar.component";
16
17
  import * as i9 from "./flow-status-bar.component";
17
18
  const _c0 = ["fZoomRef"];
18
- const _c1 = (a0, a1) => ({ x: a0, y: a1 });
19
+ const _c1 = ["canvasArea"];
20
+ const _c2 = (a0, a1) => ({ x: a0, y: a1 });
21
+ const _forTrack0 = ($index, $item) => $item.ID;
19
22
  function FlowEditorComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
20
23
  const _r2 = i0.ɵɵgetCurrentView();
21
- i0.ɵɵelementStart(0, "div", 2)(1, "mj-flow-toolbar", 17);
22
- 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)); });
24
+ i0.ɵɵelementStart(0, "div", 3)(1, "mj-flow-toolbar", 20);
25
+ 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)); })("MinimizeClicked", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_MinimizeClicked_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetToolbarVisibility("minimized")); })("HideClicked", function FlowEditorComponent_Conditional_1_Template_mj_flow_toolbar_HideClicked_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetToolbarVisibility("hidden")); });
23
26
  i0.ɵɵelementEnd()();
24
27
  } if (rf & 2) {
25
28
  const ctx_r2 = i0.ɵɵnextContext();
29
+ i0.ɵɵattribute("data-align", ctx_r2.ToolbarAlign);
26
30
  i0.ɵɵadvance();
27
31
  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);
28
32
  } }
33
+ function FlowEditorComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
34
+ const _r4 = i0.ɵɵgetCurrentView();
35
+ i0.ɵɵelementStart(0, "button", 21);
36
+ i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_2_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetToolbarVisibility("shown")); });
37
+ i0.ɵɵelement(1, "i", 22);
38
+ i0.ɵɵelementEnd();
39
+ } if (rf & 2) {
40
+ const ctx_r2 = i0.ɵɵnextContext();
41
+ i0.ɵɵattribute("data-align", ctx_r2.ToolbarAlign);
42
+ } }
29
43
  function FlowEditorComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
30
- i0.ɵɵelement(0, "mj-flow-palette", 4);
44
+ const _r5 = i0.ɵɵgetCurrentView();
45
+ i0.ɵɵelementStart(0, "button", 23);
46
+ i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_3_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetToolbarVisibility("shown")); });
47
+ i0.ɵɵelement(1, "i", 24);
48
+ i0.ɵɵelementEnd();
49
+ } if (rf & 2) {
50
+ const ctx_r2 = i0.ɵɵnextContext();
51
+ i0.ɵɵattribute("data-align", ctx_r2.ToolbarAlign);
52
+ } }
53
+ function FlowEditorComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
54
+ const _r6 = i0.ɵɵgetCurrentView();
55
+ i0.ɵɵelementStart(0, "mj-flow-palette", 25);
56
+ i0.ɵɵlistener("NodeTypeActivated", function FlowEditorComponent_Conditional_5_Template_mj_flow_palette_NodeTypeActivated_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onPaletteNodeTypeActivated($event)); });
57
+ i0.ɵɵelementEnd();
31
58
  } if (rf & 2) {
32
59
  const ctx_r2 = i0.ɵɵnextContext();
33
60
  i0.ɵɵproperty("NodeTypes", ctx_r2.NodeTypes);
34
61
  } }
35
- function FlowEditorComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
62
+ function FlowEditorComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
36
63
  i0.ɵɵelementStart(0, "f-background");
37
- i0.ɵɵelement(1, "f-circle-pattern", 18);
64
+ i0.ɵɵelement(1, "f-circle-pattern", 26);
38
65
  i0.ɵɵelementEnd();
39
66
  } if (rf & 2) {
40
67
  i0.ɵɵadvance();
41
68
  i0.ɵɵproperty("color", "#94a3b8")("radius", 24);
42
69
  } }
43
- function FlowEditorComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
44
- i0.ɵɵelement(0, "f-line-alignment", 7);
70
+ function FlowEditorComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
71
+ i0.ɵɵelement(0, "f-line-alignment", 10);
45
72
  } if (rf & 2) {
46
73
  i0.ɵɵproperty("fAlignThreshold", 10);
47
74
  } }
48
- function FlowEditorComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
49
- i0.ɵɵelementStart(0, "f-connection-for-create", 9);
75
+ function FlowEditorComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
76
+ i0.ɵɵelementStart(0, "f-connection-for-create", 12);
50
77
  i0.ɵɵnamespaceSVG();
51
- i0.ɵɵelementStart(1, "svg", 19);
52
- i0.ɵɵelement(2, "path", 20);
78
+ i0.ɵɵelementStart(1, "svg", 27);
79
+ i0.ɵɵelement(2, "path", 28);
53
80
  i0.ɵɵelementEnd()();
54
81
  } if (rf & 2) {
55
82
  i0.ɵɵproperty("fRadius", 8)("fOffset", 12);
56
83
  i0.ɵɵadvance();
57
84
  i0.ɵɵproperty("refX", 9)("refY", 5)("height", 10)("width", 10);
58
85
  } }
59
- function FlowEditorComponent_For_12_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
60
- i0.ɵɵelement(0, "i", 26);
86
+ function FlowEditorComponent_For_15_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
87
+ i0.ɵɵelement(0, "i", 34);
61
88
  } if (rf & 2) {
62
- const conn_r5 = i0.ɵɵnextContext(2).$implicit;
63
- i0.ɵɵstyleProp("color", conn_r5.LabelIconColor || "inherit");
64
- i0.ɵɵproperty("ngClass", conn_r5.LabelIcon);
89
+ const conn_r8 = i0.ɵɵnextContext(2).$implicit;
90
+ i0.ɵɵstyleProp("color", conn_r8.LabelIconColor || "inherit");
91
+ i0.ɵɵproperty("ngClass", conn_r8.LabelIcon);
65
92
  } }
66
- function FlowEditorComponent_For_12_Conditional_3_Template(rf, ctx) { if (rf & 1) {
67
- i0.ɵɵelementStart(0, "div", 24);
68
- i0.ɵɵconditionalCreate(1, FlowEditorComponent_For_12_Conditional_3_Conditional_1_Template, 1, 3, "i", 25);
93
+ function FlowEditorComponent_For_15_Conditional_3_Template(rf, ctx) { if (rf & 1) {
94
+ i0.ɵɵelementStart(0, "div", 32);
95
+ i0.ɵɵconditionalCreate(1, FlowEditorComponent_For_15_Conditional_3_Conditional_1_Template, 1, 3, "i", 33);
69
96
  i0.ɵɵtext(2);
70
97
  i0.ɵɵelementEnd();
71
98
  } if (rf & 2) {
72
- const conn_r5 = i0.ɵɵnextContext().$implicit;
99
+ const conn_r8 = i0.ɵɵnextContext().$implicit;
73
100
  const ctx_r2 = i0.ɵɵnextContext();
74
101
  i0.ɵɵstyleProp("background-color", ctx_r2.ConnectionLabelBackground)("border-color", ctx_r2.ConnectionLabelBorderColor)("color", ctx_r2.ConnectionLabelTextColor);
75
- i0.ɵɵproperty("position", 0.5)("title", conn_r5.LabelDetail || conn_r5.Label || "");
102
+ i0.ɵɵproperty("position", 0.5)("title", conn_r8.LabelDetail || conn_r8.Label || "");
76
103
  i0.ɵɵadvance();
77
- i0.ɵɵconditional(conn_r5.LabelIcon ? 1 : -1);
104
+ i0.ɵɵconditional(conn_r8.LabelIcon ? 1 : -1);
78
105
  i0.ɵɵadvance();
79
- i0.ɵɵtextInterpolate1(" ", conn_r5.Label, " ");
106
+ i0.ɵɵtextInterpolate1(" ", conn_r8.Label, " ");
80
107
  } }
81
- function FlowEditorComponent_For_12_Template(rf, ctx) { if (rf & 1) {
82
- const _r4 = i0.ɵɵgetCurrentView();
83
- i0.ɵɵelementStart(0, "f-connection", 21);
84
- 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)); });
108
+ function FlowEditorComponent_For_15_Template(rf, ctx) { if (rf & 1) {
109
+ const _r7 = i0.ɵɵgetCurrentView();
110
+ i0.ɵɵelementStart(0, "f-connection", 29);
111
+ i0.ɵɵlistener("click", function FlowEditorComponent_For_15_Template_f_connection_click_0_listener($event) { const conn_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onConnectionClicked($event, conn_r8)); })("contextmenu", function FlowEditorComponent_For_15_Template_f_connection_contextmenu_0_listener($event) { const conn_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onConnectionContextMenu($event, conn_r8)); });
85
112
  i0.ɵɵnamespaceSVG();
86
- i0.ɵɵelementStart(1, "svg", 19);
87
- i0.ɵɵelement(2, "path", 22);
113
+ i0.ɵɵelementStart(1, "svg", 27);
114
+ i0.ɵɵelement(2, "path", 30);
88
115
  i0.ɵɵelementEnd();
89
- i0.ɵɵconditionalCreate(3, FlowEditorComponent_For_12_Conditional_3_Template, 3, 10, "div", 23);
116
+ i0.ɵɵconditionalCreate(3, FlowEditorComponent_For_15_Conditional_3_Template, 3, 10, "div", 31);
90
117
  i0.ɵɵelementEnd();
91
118
  } if (rf & 2) {
92
- const conn_r5 = ctx.$implicit;
119
+ const conn_r8 = ctx.$implicit;
93
120
  const ctx_r2 = i0.ɵɵnextContext();
94
- i0.ɵɵstyleProp("--connection-color", conn_r5.Color || "#94a3b8");
95
- 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");
96
- i0.ɵɵproperty("fConnectionId", conn_r5.ID)("fOutputId", conn_r5.SourcePortID)("fInputId", conn_r5.TargetPortID)("fRadius", 8)("fOffset", 12)("fSelectionDisabled", false)("fReassignDisabled", ctx_r2.ReadOnly);
121
+ i0.ɵɵstyleProp("--connection-color", conn_r8.Color || "#94a3b8")("--connection-width", conn_r8.StrokeWidth || 2, "px");
122
+ i0.ɵɵclassProp("mj-flow-connection-colored", !!conn_r8.Color)("mj-flow-connection-dashed", conn_r8.Style === "dashed")("mj-flow-connection-dotted", conn_r8.Style === "dotted")("mj-flow-connection-animated", !!conn_r8.Animated);
123
+ i0.ɵɵproperty("fConnectionId", conn_r8.ID)("fOutputId", conn_r8.SourcePortID)("fInputId", conn_r8.TargetPortID)("fRadius", 8)("fOffset", 12)("fSelectionDisabled", false)("fReassignDisabled", ctx_r2.ReadOnly);
97
124
  i0.ɵɵadvance();
98
- i0.ɵɵproperty("refX", 9)("refY", 5)("height", 10)("width", 10);
125
+ i0.ɵɵproperty("refX", 9)("refY", 5)("height", conn_r8.Animated ? 16 : 10)("width", conn_r8.Animated ? 16 : 10);
99
126
  i0.ɵɵadvance();
100
- i0.ɵɵattribute("fill", conn_r5.Color || "#94a3b8");
127
+ i0.ɵɵattribute("fill", conn_r8.Color || "#94a3b8");
101
128
  i0.ɵɵadvance();
102
- i0.ɵɵconditional(conn_r5.Label ? 3 : -1);
129
+ i0.ɵɵconditional(conn_r8.Label ? 3 : -1);
103
130
  } }
104
- function FlowEditorComponent_For_14_Template(rf, ctx) { if (rf & 1) {
105
- const _r6 = i0.ɵɵgetCurrentView();
106
- i0.ɵɵelementStart(0, "mj-flow-node", 27);
107
- 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)); });
131
+ function FlowEditorComponent_For_17_Template(rf, ctx) { if (rf & 1) {
132
+ const _r9 = i0.ɵɵgetCurrentView();
133
+ i0.ɵɵelementStart(0, "mj-flow-node", 35);
134
+ i0.ɵɵlistener("contextmenu", function FlowEditorComponent_For_17_Template_mj_flow_node_contextmenu_0_listener($event) { const node_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onNodeContextMenu($event, node_r10)); });
108
135
  i0.ɵɵelementEnd();
109
136
  } if (rf & 2) {
110
- const node_r7 = ctx.$implicit;
137
+ const node_r10 = ctx.$implicit;
111
138
  const ctx_r2 = i0.ɵɵnextContext();
112
- 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);
139
+ i0.ɵɵproperty("fNodeId", node_r10.ID)("fNodePosition", i0.ɵɵpureFunction2(8, _c2, node_r10.Position.X, node_r10.Position.Y))("fNodeDraggingDisabled", ctx_r2.ReadOnly || ctx_r2.panMode || node_r10.Locked === true)("fNodeSelectionDisabled", ctx_r2.panMode)("fConnectOnNode", !ctx_r2.ReadOnly && !ctx_r2.panMode)("Node", node_r10)("TypeConfig", ctx_r2.GetTypeConfig(node_r10.Type))("ReadOnly", ctx_r2.ReadOnly);
113
140
  } }
114
- function FlowEditorComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
141
+ function FlowEditorComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
115
142
  i0.ɵɵelement(0, "f-selection-area");
116
143
  } }
117
- function FlowEditorComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
118
- i0.ɵɵelementStart(0, "div", 12);
119
- i0.ɵɵelement(1, "f-minimap", 28);
144
+ function FlowEditorComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) {
145
+ i0.ɵɵelementStart(0, "div", 15);
146
+ i0.ɵɵelement(1, "f-minimap", 36);
120
147
  i0.ɵɵelementEnd();
121
148
  } if (rf & 2) {
122
149
  i0.ɵɵadvance();
123
150
  i0.ɵɵproperty("fMinSize", 1500);
124
151
  } }
125
- function FlowEditorComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
126
- i0.ɵɵelementStart(0, "div", 13)(1, "div", 29);
152
+ function FlowEditorComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) {
153
+ i0.ɵɵelementStart(0, "div", 16)(1, "div", 37);
127
154
  i0.ɵɵtext(2, "Legend");
128
155
  i0.ɵɵelementEnd();
129
- i0.ɵɵelementStart(3, "div", 30)(4, "div", 31);
156
+ i0.ɵɵelementStart(3, "div", 38)(4, "div", 39);
130
157
  i0.ɵɵtext(5, "Connections");
131
158
  i0.ɵɵelementEnd();
132
- i0.ɵɵelementStart(6, "div", 32);
159
+ i0.ɵɵelementStart(6, "div", 40);
133
160
  i0.ɵɵnamespaceSVG();
134
- i0.ɵɵelementStart(7, "svg", 33);
135
- i0.ɵɵelement(8, "line", 34);
161
+ i0.ɵɵelementStart(7, "svg", 41);
162
+ i0.ɵɵelement(8, "line", 42);
136
163
  i0.ɵɵelementEnd();
137
164
  i0.ɵɵnamespaceHTML();
138
165
  i0.ɵɵelementStart(9, "span");
139
166
  i0.ɵɵtext(10, "Only exit path");
140
167
  i0.ɵɵelementEnd()();
141
- i0.ɵɵelementStart(11, "div", 32);
168
+ i0.ɵɵelementStart(11, "div", 40);
142
169
  i0.ɵɵnamespaceSVG();
143
- i0.ɵɵelementStart(12, "svg", 33);
144
- i0.ɵɵelement(13, "line", 35);
170
+ i0.ɵɵelementStart(12, "svg", 41);
171
+ i0.ɵɵelement(13, "line", 43);
145
172
  i0.ɵɵelementEnd();
146
173
  i0.ɵɵnamespaceHTML();
147
- i0.ɵɵelement(14, "i", 36);
174
+ i0.ɵɵelement(14, "i", 44);
148
175
  i0.ɵɵelementStart(15, "span");
149
176
  i0.ɵɵtext(16, "Default (fallback)");
150
177
  i0.ɵɵelementEnd()();
151
- i0.ɵɵelementStart(17, "div", 32);
178
+ i0.ɵɵelementStart(17, "div", 40);
152
179
  i0.ɵɵnamespaceSVG();
153
- i0.ɵɵelementStart(18, "svg", 33);
154
- i0.ɵɵelement(19, "line", 37);
180
+ i0.ɵɵelementStart(18, "svg", 41);
181
+ i0.ɵɵelement(19, "line", 45);
155
182
  i0.ɵɵelementEnd();
156
183
  i0.ɵɵnamespaceHTML();
157
184
  i0.ɵɵelementStart(20, "span");
158
185
  i0.ɵɵtext(21, "Conditional");
159
186
  i0.ɵɵelementEnd()();
160
- i0.ɵɵelementStart(22, "div", 32);
187
+ i0.ɵɵelementStart(22, "div", 40);
161
188
  i0.ɵɵnamespaceSVG();
162
- i0.ɵɵelementStart(23, "svg", 33);
163
- i0.ɵɵelement(24, "line", 38);
189
+ i0.ɵɵelementStart(23, "svg", 41);
190
+ i0.ɵɵelement(24, "line", 46);
164
191
  i0.ɵɵelementEnd();
165
192
  i0.ɵɵnamespaceHTML();
166
- i0.ɵɵelement(25, "i", 39);
193
+ i0.ɵɵelement(25, "i", 47);
167
194
  i0.ɵɵelementStart(26, "span");
168
195
  i0.ɵɵtext(27, "Duplicate defaults");
169
196
  i0.ɵɵelementEnd()()();
170
- i0.ɵɵelementStart(28, "div", 30)(29, "div", 31);
197
+ i0.ɵɵelementStart(28, "div", 38)(29, "div", 39);
171
198
  i0.ɵɵtext(30, "Step Types");
172
199
  i0.ɵɵelementEnd();
173
- i0.ɵɵelementStart(31, "div", 32);
174
- i0.ɵɵelement(32, "span", 40);
200
+ i0.ɵɵelementStart(31, "div", 40);
201
+ i0.ɵɵelement(32, "span", 48);
175
202
  i0.ɵɵelementStart(33, "span");
176
203
  i0.ɵɵtext(34, "Action");
177
204
  i0.ɵɵelementEnd()();
178
- i0.ɵɵelementStart(35, "div", 32);
179
- i0.ɵɵelement(36, "span", 41);
205
+ i0.ɵɵelementStart(35, "div", 40);
206
+ i0.ɵɵelement(36, "span", 49);
180
207
  i0.ɵɵelementStart(37, "span");
181
208
  i0.ɵɵtext(38, "Prompt");
182
209
  i0.ɵɵelementEnd()();
183
- i0.ɵɵelementStart(39, "div", 32);
184
- i0.ɵɵelement(40, "span", 42);
210
+ i0.ɵɵelementStart(39, "div", 40);
211
+ i0.ɵɵelement(40, "span", 50);
185
212
  i0.ɵɵelementStart(41, "span");
186
213
  i0.ɵɵtext(42, "Sub-Agent");
187
214
  i0.ɵɵelementEnd()();
188
- i0.ɵɵelementStart(43, "div", 32);
189
- i0.ɵɵelement(44, "span", 43);
215
+ i0.ɵɵelementStart(43, "div", 40);
216
+ i0.ɵɵelement(44, "span", 51);
190
217
  i0.ɵɵelementStart(45, "span");
191
218
  i0.ɵɵtext(46, "For Each");
192
219
  i0.ɵɵelementEnd()();
193
- i0.ɵɵelementStart(47, "div", 32);
194
- i0.ɵɵelement(48, "span", 44);
220
+ i0.ɵɵelementStart(47, "div", 40);
221
+ i0.ɵɵelement(48, "span", 52);
195
222
  i0.ɵɵelementStart(49, "span");
196
223
  i0.ɵɵtext(50, "While");
197
224
  i0.ɵɵelementEnd()()();
198
- i0.ɵɵelementStart(51, "div", 30)(52, "div", 31);
225
+ i0.ɵɵelementStart(51, "div", 38)(52, "div", 39);
199
226
  i0.ɵɵtext(53, "Borders");
200
227
  i0.ɵɵelementEnd();
201
- i0.ɵɵelementStart(54, "div", 32);
202
- i0.ɵɵelement(55, "span", 45);
228
+ i0.ɵɵelementStart(54, "div", 40);
229
+ i0.ɵɵelement(55, "span", 53);
203
230
  i0.ɵɵelementStart(56, "span");
204
231
  i0.ɵɵtext(57, "Start node");
205
232
  i0.ɵɵelementEnd()();
206
- i0.ɵɵelementStart(58, "div", 32);
207
- i0.ɵɵelement(59, "span", 46)(60, "i", 47);
233
+ i0.ɵɵelementStart(58, "div", 40);
234
+ i0.ɵɵelement(59, "span", 54)(60, "i", 55);
208
235
  i0.ɵɵelementStart(61, "span");
209
236
  i0.ɵɵtext(62, "Missing config");
210
237
  i0.ɵɵelementEnd()()()();
211
238
  } }
212
- function FlowEditorComponent_Conditional_18_Conditional_2_Template(rf, ctx) { if (rf & 1) {
213
- i0.ɵɵelementStart(0, "p", 49);
239
+ function FlowEditorComponent_Conditional_21_Conditional_2_Template(rf, ctx) { if (rf & 1) {
240
+ i0.ɵɵelementStart(0, "p", 57);
214
241
  i0.ɵɵtext(1, " Or use auto-layout after adding steps to arrange them automatically ");
215
242
  i0.ɵɵelementEnd();
216
243
  } }
217
- function FlowEditorComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
218
- i0.ɵɵelementStart(0, "div", 14)(1, "mj-empty-state", 48);
219
- i0.ɵɵconditionalCreate(2, FlowEditorComponent_Conditional_18_Conditional_2_Template, 2, 0, "p", 49);
244
+ function FlowEditorComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
245
+ i0.ɵɵelementStart(0, "div", 17)(1, "mj-empty-state", 56);
246
+ i0.ɵɵconditionalCreate(2, FlowEditorComponent_Conditional_21_Conditional_2_Template, 2, 0, "p", 57);
220
247
  i0.ɵɵelementEnd()();
221
248
  } if (rf & 2) {
222
249
  const ctx_r2 = i0.ɵɵnextContext();
@@ -225,33 +252,66 @@ function FlowEditorComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
225
252
  i0.ɵɵadvance();
226
253
  i0.ɵɵconditional(!ctx_r2.ReadOnly ? 2 : -1);
227
254
  } }
228
- function FlowEditorComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) {
229
- const _r8 = i0.ɵɵgetCurrentView();
230
- i0.ɵɵelementStart(0, "div", 50);
231
- i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_19_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r8); return i0.ɵɵresetView($event.stopPropagation()); });
232
- i0.ɵɵelementStart(1, "button", 51);
233
- 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")); });
234
- i0.ɵɵelement(2, "i", 52);
235
- i0.ɵɵelementStart(3, "span");
236
- i0.ɵɵtext(4);
237
- i0.ɵɵelementEnd()();
238
- i0.ɵɵelement(5, "div", 53);
239
- i0.ɵɵelementStart(6, "button", 54);
240
- 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")); });
241
- i0.ɵɵelement(7, "i", 55);
242
- i0.ɵɵelementStart(8, "span");
243
- i0.ɵɵtext(9);
244
- i0.ɵɵelementEnd()()();
255
+ function FlowEditorComponent_Conditional_22_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
256
+ i0.ɵɵelement(0, "i", 60);
257
+ } if (rf & 2) {
258
+ const item_r13 = i0.ɵɵnextContext().$implicit;
259
+ i0.ɵɵproperty("ngClass", item_r13.Icon);
260
+ } }
261
+ function FlowEditorComponent_Conditional_22_For_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
262
+ i0.ɵɵelementStart(0, "kbd", 61);
263
+ i0.ɵɵtext(1);
264
+ i0.ɵɵelementEnd();
265
+ } if (rf & 2) {
266
+ const item_r13 = i0.ɵɵnextContext().$implicit;
267
+ i0.ɵɵadvance();
268
+ i0.ɵɵtextInterpolate(item_r13.Shortcut);
269
+ } }
270
+ function FlowEditorComponent_Conditional_22_For_2_Conditional_5_Template(rf, ctx) { if (rf & 1) {
271
+ i0.ɵɵelement(0, "div", 62);
272
+ } }
273
+ function FlowEditorComponent_Conditional_22_For_2_Template(rf, ctx) { if (rf & 1) {
274
+ const _r12 = i0.ɵɵgetCurrentView();
275
+ i0.ɵɵelementStart(0, "button", 59);
276
+ i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_22_For_2_Template_button_click_0_listener() { const item_r13 = i0.ɵɵrestoreView(_r12).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onContextMenuAction(item_r13.ID)); });
277
+ i0.ɵɵconditionalCreate(1, FlowEditorComponent_Conditional_22_For_2_Conditional_1_Template, 1, 1, "i", 60);
278
+ i0.ɵɵelementStart(2, "span");
279
+ i0.ɵɵtext(3);
280
+ i0.ɵɵelementEnd();
281
+ i0.ɵɵconditionalCreate(4, FlowEditorComponent_Conditional_22_For_2_Conditional_4_Template, 2, 1, "kbd", 61);
282
+ i0.ɵɵelementEnd();
283
+ i0.ɵɵconditionalCreate(5, FlowEditorComponent_Conditional_22_For_2_Conditional_5_Template, 1, 0, "div", 62);
284
+ } if (rf & 2) {
285
+ const item_r13 = ctx.$implicit;
286
+ const $index_r14 = ctx.$index;
287
+ const ɵ$index_193_r15 = ctx.$index;
288
+ const ɵ$count_193_r16 = ctx.$count;
289
+ const ctx_r2 = i0.ɵɵnextContext(2);
290
+ i0.ɵɵclassProp("mj-flow-context-menu-item--danger", item_r13.Danger);
291
+ i0.ɵɵproperty("disabled", item_r13.Disabled);
292
+ i0.ɵɵadvance();
293
+ i0.ɵɵconditional(item_r13.Icon ? 1 : -1);
294
+ i0.ɵɵadvance(2);
295
+ i0.ɵɵtextInterpolate(item_r13.Label);
296
+ i0.ɵɵadvance();
297
+ i0.ɵɵconditional(item_r13.Shortcut ? 4 : -1);
298
+ i0.ɵɵadvance();
299
+ i0.ɵɵconditional(!(ɵ$index_193_r15 === ɵ$count_193_r16 - 1) && item_r13.Danger !== (ctx_r2.contextMenuItems[$index_r14 + 1] == null ? null : ctx_r2.contextMenuItems[$index_r14 + 1].Danger) ? 5 : -1);
300
+ } }
301
+ function FlowEditorComponent_Conditional_22_Template(rf, ctx) { if (rf & 1) {
302
+ const _r11 = i0.ɵɵgetCurrentView();
303
+ i0.ɵɵelementStart(0, "div", 58);
304
+ i0.ɵɵlistener("click", function FlowEditorComponent_Conditional_22_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r11); return i0.ɵɵresetView($event.stopPropagation()); });
305
+ i0.ɵɵrepeaterCreate(1, FlowEditorComponent_Conditional_22_For_2_Template, 6, 7, null, null, _forTrack0);
306
+ i0.ɵɵelementEnd();
245
307
  } if (rf & 2) {
246
308
  const ctx_r2 = i0.ɵɵnextContext();
247
309
  i0.ɵɵstyleProp("left", ctx_r2.contextMenuX, "px")("top", ctx_r2.contextMenuY, "px");
248
- i0.ɵɵadvance(4);
249
- i0.ɵɵtextInterpolate1("Edit ", ctx_r2.contextMenuTargetType === "node" ? "Node" : "Connection");
250
- i0.ɵɵadvance(5);
251
- i0.ɵɵtextInterpolate1("Remove ", ctx_r2.contextMenuTargetType === "node" ? "Node" : "Connection");
310
+ i0.ɵɵadvance();
311
+ i0.ɵɵrepeater(ctx_r2.contextMenuItems);
252
312
  } }
253
- function FlowEditorComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) {
254
- i0.ɵɵelement(0, "mj-flow-status-bar", 16);
313
+ function FlowEditorComponent_Conditional_23_Template(rf, ctx) { if (rf & 1) {
314
+ i0.ɵɵelement(0, "mj-flow-status-bar", 19);
255
315
  } if (rf & 2) {
256
316
  const ctx_r2 = i0.ɵɵnextContext();
257
317
  i0.ɵɵproperty("NodeCount", ctx_r2.Nodes.length)("ConnectionCount", ctx_r2.Connections.length)("SelectedCount", ctx_r2.selectedCount)("ZoomLevel", ctx_r2.zoomLevel);
@@ -284,21 +344,31 @@ export class FlowEditorComponent {
284
344
  ShowPalette = true;
285
345
  ShowGrid = true;
286
346
  ShowStatusBar = true;
347
+ /**
348
+ * Legacy on/off. `false` forces the toolbar fully off (no recover tab). Prefer
349
+ * `ToolbarVisibility` when the host wants shown / minimized / hidden with a way back.
350
+ */
287
351
  ShowToolbar = true;
352
+ /** Full bar, a restore chip, or a recover tab. Ignored when `ShowToolbar` is false. */
353
+ ToolbarVisibility = 'shown';
354
+ /** Left / center / right along the top of the canvas. */
355
+ ToolbarAlign = 'center';
288
356
  ShowLegend = true;
289
357
  GridSize = 20;
290
358
  AutoLayoutDirection = 'vertical';
291
359
  /** Background color for connection labels */
292
- ConnectionLabelBackground = '#fffef5';
360
+ // Design tokens, not literals — the canvas renders on both themes, and a near-white pill on a
361
+ // dark canvas made the least important element the most visually dominant thing on screen.
362
+ ConnectionLabelBackground = 'var(--mj-bg-surface)';
293
363
  /** Border color for connection labels */
294
- ConnectionLabelBorderColor = '#cbd5e1';
364
+ ConnectionLabelBorderColor = 'var(--mj-border-default)';
295
365
  /** Text color for connection labels */
296
- ConnectionLabelTextColor = '#334155';
366
+ ConnectionLabelTextColor = 'var(--mj-text-secondary)';
297
367
  /** Title for the canvas empty-state overlay — depends on read-only mode. */
298
368
  get EmptyStateTitle() {
299
369
  return this.ReadOnly
300
370
  ? 'No steps defined for this flow'
301
- : 'Drag steps from the palette to start building your flow';
371
+ : 'Click a step in the palette — or drag one onto the canvas — to start building your flow';
302
372
  }
303
373
  // ── Outputs ─────────────────────────────────────────────────
304
374
  NodeSelected = new EventEmitter();
@@ -315,17 +385,40 @@ export class FlowEditorComponent {
315
385
  ConnectionsChanged = new EventEmitter();
316
386
  NodeEditRequested = new EventEmitter();
317
387
  ConnectionEditRequested = new EventEmitter();
388
+ /** Host may replace `Items` or `Cancel` the menu. Debug attaches here — the canvas stays blind. */
389
+ BeforeContextMenu = new EventEmitter();
390
+ AfterContextMenuAction = new EventEmitter();
318
391
  GridToggled = new EventEmitter();
319
392
  MinimapToggled = new EventEmitter();
320
393
  LegendToggled = new EventEmitter();
394
+ /** The person changed shown / minimized / hidden from the canvas chrome. */
395
+ ToolbarVisibilityChanged = new EventEmitter();
396
+ /** What the template actually paints — `ShowToolbar === false` is a hard hide. */
397
+ get EffectiveToolbarVisibility() {
398
+ return this.ShowToolbar ? this.ToolbarVisibility : 'hidden';
399
+ }
400
+ SetToolbarVisibility(next) {
401
+ if (!this.ShowToolbar)
402
+ return;
403
+ if (this.ToolbarVisibility === next)
404
+ return;
405
+ this.ToolbarVisibility = next;
406
+ this.ToolbarVisibilityChanged.emit(next);
407
+ }
321
408
  // ── View Children ───────────────────────────────────────────
322
409
  fFlow;
323
410
  fCanvas;
324
411
  fZoom;
412
+ canvasArea;
325
413
  // ── Internal State ──────────────────────────────────────────
326
414
  zoomLevel = 100;
327
415
  canvasPosition = { x: 0, y: 0 };
328
416
  canvasScale = 1;
417
+ /** After the first auto-fit, later fLoaded events must not snap the viewport back. */
418
+ hasAutoFitted = false;
419
+ /** True once the person zooms or pans — remounts restore this viewport instead of fitting. */
420
+ viewportOwnedByUser = false;
421
+ applyingProgrammaticViewport = false;
329
422
  selectedNodeIDs = [];
330
423
  selectedConnectionIDs = [];
331
424
  panMode = false;
@@ -340,9 +433,15 @@ export class FlowEditorComponent {
340
433
  contextMenuTargetType = 'node';
341
434
  contextMenuNode = null;
342
435
  contextMenuConnection = null;
436
+ contextMenuItems = [];
343
437
  destroy$ = new Subject();
344
438
  initialized = false;
345
439
  lastConnectionClickTime = 0;
440
+ /** How many click-placed nodes are cascaded before the offset wraps back to the viewport centre. */
441
+ static CLICK_CASCADE_STEPS = 6;
442
+ /** Pixels (canvas units) each cascaded click-placed node is offset by. */
443
+ static CLICK_CASCADE_OFFSET = 40;
444
+ clickPlacementIndex = 0;
346
445
  constructor(cdr, stateService, layoutService) {
347
446
  this.cdr = cdr;
348
447
  this.stateService = stateService;
@@ -359,10 +458,14 @@ export class FlowEditorComponent {
359
458
  // ── Public Methods ──────────────────────────────────────────
360
459
  /** Fit all nodes within the viewport */
361
460
  ZoomToFit() {
461
+ this.applyingProgrammaticViewport = true;
362
462
  this.fCanvas?.fitToScreen({ x: 150, y: 150 }, true);
463
+ this.hasAutoFitted = true;
464
+ queueMicrotask(() => { this.applyingProgrammaticViewport = false; });
363
465
  }
364
466
  /** Zoom in one step */
365
467
  ZoomIn() {
468
+ this.viewportOwnedByUser = true;
366
469
  if (this.fZoom) {
367
470
  this.fZoom.zoomIn();
368
471
  }
@@ -374,6 +477,7 @@ export class FlowEditorComponent {
374
477
  }
375
478
  /** Zoom out one step */
376
479
  ZoomOut() {
480
+ this.viewportOwnedByUser = true;
377
481
  if (this.fZoom) {
378
482
  this.fZoom.zoomOut();
379
483
  }
@@ -512,15 +616,29 @@ export class FlowEditorComponent {
512
616
  // ── Foblex Event Handlers ──────────────────────────────────
513
617
  /** Called when Foblex flow finishes loading */
514
618
  onFlowLoaded() {
515
- if (this.Nodes.length > 0) {
516
- setTimeout(() => this.ZoomToFit(), 200);
619
+ if (this.Nodes.length === 0)
620
+ return;
621
+ if (this.viewportOwnedByUser || this.hasAutoFitted) {
622
+ this.restoreViewport();
623
+ return;
517
624
  }
625
+ this.hasAutoFitted = true;
626
+ setTimeout(() => this.ZoomToFit(), 200);
518
627
  }
519
628
  /** Canvas zoom/pan changed */
520
629
  onCanvasChange(event) {
521
630
  this.canvasPosition = event.position;
522
631
  this.canvasScale = event.scale;
523
632
  this.updateZoomLevel(event.scale);
633
+ if (!this.applyingProgrammaticViewport)
634
+ this.viewportOwnedByUser = true;
635
+ }
636
+ restoreViewport() {
637
+ if (!this.fCanvas)
638
+ return;
639
+ this.applyingProgrammaticViewport = true;
640
+ this.fCanvas.setScale(this.canvasScale);
641
+ queueMicrotask(() => { this.applyingProgrammaticViewport = false; });
524
642
  }
525
643
  /** User drew a new connection */
526
644
  onCreateConnection(event) {
@@ -607,11 +725,7 @@ export class FlowEditorComponent {
607
725
  }
608
726
  /** External item dropped from palette */
609
727
  onCreateNode(event) {
610
- if (this.ReadOnly)
611
- return;
612
- this.pushUndoState();
613
- const nodeType = event.data;
614
- const config = this.NodeTypes.find(nt => nt.Type === nodeType);
728
+ const config = this.NodeTypes.find(nt => nt.Type === event.data);
615
729
  if (!config)
616
730
  return;
617
731
  // event.rect is already normalized to canvas coordinates by Foblex's
@@ -619,13 +733,45 @@ export class FlowEditorComponent {
619
733
  // Do NOT pass it through getPositionInFlow() again — that double-normalizes
620
734
  // and places the node far from the actual drop point.
621
735
  // Use the center of the preview rect (≈ cursor position at drop time).
622
- const dropPosition = {
736
+ this.addNodeFromPalette(config, {
623
737
  X: event.rect.x + event.rect.width / 2,
624
738
  Y: event.rect.y + event.rect.height / 2
625
- };
739
+ });
740
+ }
741
+ /**
742
+ * A palette entry was clicked (or activated from the keyboard) rather than dragged.
743
+ *
744
+ * Deliberately routed into the SAME `addNodeFromPalette` the drop path uses. A separate
745
+ * click-only creation path would be a second way for a node to come into existence, and the two
746
+ * would drift — which is exactly the failure this fixes, one level up.
747
+ */
748
+ onPaletteNodeTypeActivated(config) {
749
+ this.addNodeFromPalette(config, this.nextClickDropPosition());
750
+ }
751
+ /** The one place a palette-originated node is created and announced. */
752
+ addNodeFromPalette(config, dropPosition) {
753
+ if (this.ReadOnly)
754
+ return;
755
+ this.pushUndoState();
626
756
  const newNode = this.createDefaultNode(config, dropPosition);
627
757
  this.NodeAdded.emit({ Node: newNode, DropPosition: dropPosition });
628
758
  }
759
+ /**
760
+ * Where a clicked step lands: the middle of what the author is currently looking at, cascaded so
761
+ * consecutive clicks don't bury each other under one box.
762
+ *
763
+ * A click carries no coordinates, so *some* position has to be invented. The viewport centre is
764
+ * the only choice that is guaranteed on screen at any zoom or pan — placing at the canvas origin
765
+ * would drop steps off-screen the moment the author has scrolled anywhere.
766
+ */
767
+ nextClickDropPosition() {
768
+ const rect = this.canvasArea?.nativeElement.getBoundingClientRect();
769
+ const scale = this.canvasScale || 1;
770
+ const centerX = ((rect?.width ?? 800) / 2 - this.canvasPosition.x) / scale;
771
+ const centerY = ((rect?.height ?? 600) / 2 - this.canvasPosition.y) / scale;
772
+ const step = (this.clickPlacementIndex++ % FlowEditorComponent.CLICK_CASCADE_STEPS) * FlowEditorComponent.CLICK_CASCADE_OFFSET;
773
+ return { X: Math.round(centerX + step), Y: Math.round(centerY + step) };
774
+ }
629
775
  /** Selection changed in Foblex */
630
776
  onSelectionChange(event) {
631
777
  const timeSinceClick = Date.now() - this.lastConnectionClickTime;
@@ -700,11 +846,9 @@ export class FlowEditorComponent {
700
846
  }
701
847
  // ── Context Menu ───────────────────────────────────────────
702
848
  onNodeContextMenu(event, node) {
703
- if (this.ReadOnly)
704
- return;
705
849
  event.preventDefault();
706
850
  event.stopPropagation();
707
- this.showContextMenu(event, 'node', node, null);
851
+ this.openContextMenu(event, 'node', node, null);
708
852
  }
709
853
  /** Direct click on a connection — ensures ConnectionSelected fires reliably */
710
854
  onConnectionClicked(event, conn) {
@@ -718,41 +862,53 @@ export class FlowEditorComponent {
718
862
  this.cdr.detectChanges();
719
863
  }
720
864
  onConnectionContextMenu(event, conn) {
721
- if (this.ReadOnly)
722
- return;
723
865
  event.preventDefault();
724
866
  event.stopPropagation();
725
- this.showContextMenu(event, 'connection', null, conn);
867
+ this.openContextMenu(event, 'connection', null, conn);
726
868
  }
727
869
  onContextMenuAction(action) {
870
+ // Capture the target BEFORE closing: hideContextMenu() nulls contextMenuNode and
871
+ // contextMenuConnection, so reading them afterwards found nothing and every branch below fell
872
+ // through — the menu closed and neither Remove nor Edit did anything, for nodes or connections.
873
+ const targetType = this.contextMenuTargetType;
874
+ const node = this.contextMenuNode;
875
+ const connection = this.contextMenuConnection;
728
876
  this.hideContextMenu();
729
- if (action === 'edit') {
730
- if (this.contextMenuTargetType === 'node' && this.contextMenuNode) {
731
- this.NodeEditRequested.emit(this.contextMenuNode);
877
+ if (!this.ReadOnly && action === 'edit') {
878
+ if (targetType === 'node' && node) {
879
+ this.NodeEditRequested.emit(node);
732
880
  }
733
- else if (this.contextMenuTargetType === 'connection' && this.contextMenuConnection) {
734
- this.ConnectionEditRequested.emit(this.contextMenuConnection);
881
+ else if (targetType === 'connection' && connection) {
882
+ this.ConnectionEditRequested.emit(connection);
735
883
  }
736
884
  }
737
- else if (action === 'remove') {
738
- this.pushUndoState();
739
- if (this.contextMenuTargetType === 'node' && this.contextMenuNode) {
740
- this.removeNodeById(this.contextMenuNode.ID);
885
+ else if (!this.ReadOnly && action === 'remove') {
886
+ if (targetType === 'node' && node) {
887
+ this.pushUndoState();
888
+ this.removeNodeById(node.ID);
741
889
  }
742
- else if (this.contextMenuTargetType === 'connection' && this.contextMenuConnection) {
743
- this.removeConnectionById(this.contextMenuConnection.ID);
890
+ else if (targetType === 'connection' && connection) {
891
+ this.pushUndoState();
892
+ this.removeConnectionById(connection.ID);
744
893
  }
745
894
  }
895
+ this.AfterContextMenuAction.emit(new FlowAfterContextMenuActionEventArgs(action, targetType, node, connection));
746
896
  }
747
897
  onDocumentClick() {
748
898
  if (this.contextMenuVisible) {
749
899
  this.hideContextMenu();
750
900
  }
751
901
  }
752
- showContextMenu(event, targetType, node, connection) {
902
+ openContextMenu(event, targetType, node, connection) {
903
+ const defaults = this.ReadOnly ? [] : defaultContextMenuItems(targetType);
904
+ const before = new FlowBeforeContextMenuEventArgs(targetType, node, connection, event, defaults);
905
+ this.BeforeContextMenu.emit(before);
906
+ if (before.Cancel || before.Items.length === 0)
907
+ return;
753
908
  this.contextMenuTargetType = targetType;
754
909
  this.contextMenuNode = node;
755
910
  this.contextMenuConnection = connection;
911
+ this.contextMenuItems = before.Items;
756
912
  this.contextMenuX = event.clientX;
757
913
  this.contextMenuY = event.clientY;
758
914
  this.contextMenuVisible = true;
@@ -927,78 +1083,79 @@ export class FlowEditorComponent {
927
1083
  }
928
1084
  static ɵfac = function FlowEditorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FlowEditorComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.FlowStateService), i0.ɵɵdirectiveInject(i2.FlowLayoutService)); };
929
1085
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FlowEditorComponent, selectors: [["mj-flow-editor"]], viewQuery: function FlowEditorComponent_Query(rf, ctx) { if (rf & 1) {
930
- i0.ɵɵviewQuery(FFlowComponent, 5)(FCanvasComponent, 5)(_c0, 5);
1086
+ i0.ɵɵviewQuery(FFlowComponent, 5)(FCanvasComponent, 5)(_c0, 5)(_c1, 5);
931
1087
  } if (rf & 2) {
932
1088
  let _t;
933
1089
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fFlow = _t.first);
934
1090
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fCanvas = _t.first);
935
1091
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fZoom = _t.first);
1092
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.canvasArea = _t.first);
936
1093
  } }, hostBindings: function FlowEditorComponent_HostBindings(rf, ctx) { if (rf & 1) {
937
1094
  i0.ɵɵlistener("click", function FlowEditorComponent_click_HostBindingHandler() { return ctx.onDocumentClick(); }, i0.ɵɵresolveDocument)("keydown", function FlowEditorComponent_keydown_HostBindingHandler($event) { return ctx.onKeyDown($event); });
938
- } }, 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"], ["Icon", "fa-solid fa-diagram-project", 3, "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) {
1095
+ } }, inputs: { Nodes: "Nodes", Connections: "Connections", NodeTypes: "NodeTypes", ReadOnly: "ReadOnly", ShowMinimap: "ShowMinimap", ShowPalette: "ShowPalette", ShowGrid: "ShowGrid", ShowStatusBar: "ShowStatusBar", ShowToolbar: "ShowToolbar", ToolbarVisibility: "ToolbarVisibility", ToolbarAlign: "ToolbarAlign", 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", BeforeContextMenu: "BeforeContextMenu", AfterContextMenuAction: "AfterContextMenuAction", GridToggled: "GridToggled", MinimapToggled: "MinimapToggled", LegendToggled: "LegendToggled", ToolbarVisibilityChanged: "ToolbarVisibilityChanged" }, standalone: false, features: [i0.ɵɵProvidersFeature([FlowStateService, FlowLayoutService])], decls: 24, vars: 25, consts: [["canvasArea", ""], ["fZoomRef", "fComponent"], ["tabindex", "0", 1, "mj-flow-editor"], [1, "mj-flow-editor-toolbar-area"], ["type", "button", "title", "Show canvas tools", "aria-label", "Show canvas tools", 1, "mj-flow-editor-toolbar-chip"], ["type", "button", "title", "Show canvas tools", "aria-label", "Show canvas tools", 1, "mj-flow-editor-toolbar-tab"], [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", "--connection-width", "mj-flow-connection-colored", "mj-flow-connection-dashed", "mj-flow-connection-dotted", "mj-flow-connection-animated"], ["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", "MinimizeClicked", "HideClicked", "ZoomLevel", "ShowGrid", "ShowMinimap", "ShowLegend", "ReadOnly", "PanMode"], ["type", "button", "title", "Show canvas tools", "aria-label", "Show canvas tools", 1, "mj-flow-editor-toolbar-chip", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-wrench"], ["type", "button", "title", "Show canvas tools", "aria-label", "Show canvas tools", 1, "mj-flow-editor-toolbar-tab", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-ellipsis"], [3, "NodeTypeActivated", "NodeTypes"], [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"], ["Icon", "fa-solid fa-diagram-project", 3, "Title"], [1, "mj-flow-editor-empty-subtitle"], [1, "mj-flow-context-menu", 3, "click"], ["type", "button", 1, "mj-flow-context-menu-item", 3, "click", "disabled"], [1, "fa-solid", 3, "ngClass"], [1, "mj-flow-context-menu-shortcut"], [1, "mj-flow-context-menu-divider"]], template: function FlowEditorComponent_Template(rf, ctx) { if (rf & 1) {
939
1096
  const _r1 = i0.ɵɵgetCurrentView();
940
- i0.ɵɵelementStart(0, "div", 1);
941
- i0.ɵɵconditionalCreate(1, FlowEditorComponent_Conditional_1_Template, 2, 6, "div", 2);
942
- i0.ɵɵelementStart(2, "div", 3);
943
- i0.ɵɵconditionalCreate(3, FlowEditorComponent_Conditional_3_Template, 1, 1, "mj-flow-palette", 4);
944
- i0.ɵɵelementStart(4, "div", 5)(5, "f-flow", 6);
945
- 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)); });
946
- i0.ɵɵconditionalCreate(6, FlowEditorComponent_Conditional_6_Template, 2, 2, "f-background");
947
- i0.ɵɵconditionalCreate(7, FlowEditorComponent_Conditional_7_Template, 1, 1, "f-line-alignment", 7);
948
- i0.ɵɵelementStart(8, "f-canvas", 8, 0);
949
- i0.ɵɵlistener("fCanvasChange", function FlowEditorComponent_Template_f_canvas_fCanvasChange_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCanvasChange($event)); });
950
- i0.ɵɵconditionalCreate(10, FlowEditorComponent_Conditional_10_Template, 3, 6, "f-connection-for-create", 9);
951
- i0.ɵɵrepeaterCreate(11, FlowEditorComponent_For_12_Template, 4, 21, "f-connection", 10, ctx.trackConnectionById, true);
952
- i0.ɵɵrepeaterCreate(13, FlowEditorComponent_For_14_Template, 1, 11, "mj-flow-node", 11, ctx.trackNodeById, true);
1097
+ i0.ɵɵelementStart(0, "div", 2);
1098
+ i0.ɵɵconditionalCreate(1, FlowEditorComponent_Conditional_1_Template, 2, 7, "div", 3)(2, FlowEditorComponent_Conditional_2_Template, 2, 1, "button", 4)(3, FlowEditorComponent_Conditional_3_Template, 2, 1, "button", 5);
1099
+ i0.ɵɵelementStart(4, "div", 6);
1100
+ i0.ɵɵconditionalCreate(5, FlowEditorComponent_Conditional_5_Template, 1, 1, "mj-flow-palette", 7);
1101
+ i0.ɵɵelementStart(6, "div", 8, 0)(8, "f-flow", 9);
1102
+ i0.ɵɵlistener("fLoaded", function FlowEditorComponent_Template_f_flow_fLoaded_8_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onFlowLoaded()); })("fCreateConnection", function FlowEditorComponent_Template_f_flow_fCreateConnection_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCreateConnection($event)); })("fReassignConnection", function FlowEditorComponent_Template_f_flow_fReassignConnection_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onReassignConnection($event)); })("fCreateNode", function FlowEditorComponent_Template_f_flow_fCreateNode_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCreateNode($event)); })("fSelectionChange", function FlowEditorComponent_Template_f_flow_fSelectionChange_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onSelectionChange($event)); })("fMoveNodes", function FlowEditorComponent_Template_f_flow_fMoveNodes_8_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onNodesMoved($event)); });
1103
+ i0.ɵɵconditionalCreate(9, FlowEditorComponent_Conditional_9_Template, 2, 2, "f-background");
1104
+ i0.ɵɵconditionalCreate(10, FlowEditorComponent_Conditional_10_Template, 1, 1, "f-line-alignment", 10);
1105
+ i0.ɵɵelementStart(11, "f-canvas", 11, 1);
1106
+ i0.ɵɵlistener("fCanvasChange", function FlowEditorComponent_Template_f_canvas_fCanvasChange_11_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onCanvasChange($event)); });
1107
+ i0.ɵɵconditionalCreate(13, FlowEditorComponent_Conditional_13_Template, 3, 6, "f-connection-for-create", 12);
1108
+ i0.ɵɵrepeaterCreate(14, FlowEditorComponent_For_15_Template, 4, 25, "f-connection", 13, ctx.trackConnectionById, true);
1109
+ i0.ɵɵrepeaterCreate(16, FlowEditorComponent_For_17_Template, 1, 11, "mj-flow-node", 14, ctx.trackNodeById, true);
953
1110
  i0.ɵɵelementEnd();
954
- i0.ɵɵconditionalCreate(15, FlowEditorComponent_Conditional_15_Template, 1, 0, "f-selection-area");
955
- i0.ɵɵconditionalCreate(16, FlowEditorComponent_Conditional_16_Template, 2, 1, "div", 12);
1111
+ i0.ɵɵconditionalCreate(18, FlowEditorComponent_Conditional_18_Template, 1, 0, "f-selection-area");
1112
+ i0.ɵɵconditionalCreate(19, FlowEditorComponent_Conditional_19_Template, 2, 1, "div", 15);
956
1113
  i0.ɵɵelementEnd();
957
- i0.ɵɵconditionalCreate(17, FlowEditorComponent_Conditional_17_Template, 63, 0, "div", 13);
958
- i0.ɵɵconditionalCreate(18, FlowEditorComponent_Conditional_18_Template, 3, 2, "div", 14);
1114
+ i0.ɵɵconditionalCreate(20, FlowEditorComponent_Conditional_20_Template, 63, 0, "div", 16);
1115
+ i0.ɵɵconditionalCreate(21, FlowEditorComponent_Conditional_21_Template, 3, 2, "div", 17);
959
1116
  i0.ɵɵelementEnd()();
960
- i0.ɵɵconditionalCreate(19, FlowEditorComponent_Conditional_19_Template, 10, 6, "div", 15);
961
- i0.ɵɵconditionalCreate(20, FlowEditorComponent_Conditional_20_Template, 1, 4, "mj-flow-status-bar", 16);
1117
+ i0.ɵɵconditionalCreate(22, FlowEditorComponent_Conditional_22_Template, 3, 4, "div", 18);
1118
+ i0.ɵɵconditionalCreate(23, FlowEditorComponent_Conditional_23_Template, 1, 4, "mj-flow-status-bar", 19);
962
1119
  i0.ɵɵelementEnd();
963
1120
  } if (rf & 2) {
964
1121
  i0.ɵɵclassProp("mj-flow-editor--readonly", ctx.ReadOnly);
965
1122
  i0.ɵɵadvance();
966
- i0.ɵɵconditional(ctx.ShowToolbar ? 1 : -1);
967
- i0.ɵɵadvance(2);
968
- i0.ɵɵconditional(ctx.ShowPalette && !ctx.ReadOnly ? 3 : -1);
1123
+ i0.ɵɵconditional(ctx.ShowToolbar && ctx.EffectiveToolbarVisibility === "shown" ? 1 : ctx.ShowToolbar && ctx.EffectiveToolbarVisibility === "minimized" ? 2 : ctx.ShowToolbar && ctx.EffectiveToolbarVisibility === "hidden" ? 3 : -1);
1124
+ i0.ɵɵadvance(4);
1125
+ i0.ɵɵconditional(ctx.ShowPalette && !ctx.ReadOnly ? 5 : -1);
969
1126
  i0.ɵɵadvance();
970
1127
  i0.ɵɵclassProp("mj-flow-editor-canvas--pan-mode", ctx.panMode);
971
- i0.ɵɵadvance();
1128
+ i0.ɵɵadvance(2);
972
1129
  i0.ɵɵproperty("fDraggableDisabled", false)("fCanvasMoveTrigger", ctx.canvasMoveTriggerFn)("vCellSize", ctx.GridSize)("hCellSize", ctx.GridSize)("fCellSizeWhileDragging", ctx.ShowGrid);
973
1130
  i0.ɵɵadvance();
974
- i0.ɵɵconditional(ctx.ShowGrid ? 6 : -1);
1131
+ i0.ɵɵconditional(ctx.ShowGrid ? 9 : -1);
975
1132
  i0.ɵɵadvance();
976
- i0.ɵɵconditional(!ctx.ReadOnly ? 7 : -1);
1133
+ i0.ɵɵconditional(!ctx.ReadOnly ? 10 : -1);
977
1134
  i0.ɵɵadvance();
978
1135
  i0.ɵɵproperty("fZoomMinimum", 0.15)("fZoomMaximum", 3)("fZoomStep", 0.1)("position", ctx.canvasPosition)("scale", ctx.canvasScale);
979
1136
  i0.ɵɵadvance(2);
980
- i0.ɵɵconditional(!ctx.ReadOnly ? 10 : -1);
1137
+ i0.ɵɵconditional(!ctx.ReadOnly ? 13 : -1);
981
1138
  i0.ɵɵadvance();
982
1139
  i0.ɵɵrepeater(ctx.Connections);
983
1140
  i0.ɵɵadvance(2);
984
1141
  i0.ɵɵrepeater(ctx.Nodes);
985
1142
  i0.ɵɵadvance(2);
986
- i0.ɵɵconditional(!ctx.ReadOnly ? 15 : -1);
1143
+ i0.ɵɵconditional(!ctx.ReadOnly ? 18 : -1);
987
1144
  i0.ɵɵadvance();
988
- i0.ɵɵconditional(ctx.ShowMinimap ? 16 : -1);
1145
+ i0.ɵɵconditional(ctx.ShowMinimap ? 19 : -1);
989
1146
  i0.ɵɵadvance();
990
- i0.ɵɵconditional(ctx.ShowLegend && ctx.Nodes.length > 0 ? 17 : -1);
1147
+ i0.ɵɵconditional(ctx.ShowLegend && ctx.Nodes.length > 0 ? 20 : -1);
991
1148
  i0.ɵɵadvance();
992
- i0.ɵɵconditional(ctx.Nodes.length === 0 ? 18 : -1);
1149
+ i0.ɵɵconditional(ctx.Nodes.length === 0 ? 21 : -1);
993
1150
  i0.ɵɵadvance();
994
- i0.ɵɵconditional(ctx.contextMenuVisible ? 19 : -1);
1151
+ i0.ɵɵconditional(ctx.contextMenuVisible ? 22 : -1);
995
1152
  i0.ɵɵadvance();
996
- i0.ɵɵconditional(ctx.ShowStatusBar ? 20 : -1);
997
- } }, 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.MJEmptyStateComponent, i6.FlowNodeComponent, i7.FlowPaletteComponent, i8.FlowToolbarComponent, i9.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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\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: var(--mj-bg-surface);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\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: var(--mj-border-strong);\n stroke: var(--mj-text-disabled);\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: color-mix(in srgb, var(--mj-brand-primary) 50%, var(--mj-bg-surface));\n stroke: var(--mj-brand-primary);\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: var(--mj-border-default);\n stroke: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n stroke: var(--mj-brand-primary);\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-subtitle {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface);\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n box-shadow: var(--mj-shadow-sm);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n --f-selection-area-border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: var(--mj-brand-primary);\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: var(--mj-text-disabled);\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: var(--mj-brand-primary);\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property,\n defaulting to neutral when no color is specified by the consumer */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, var(--mj-text-disabled));\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, var(--mj-brand-primary));\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: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(6px);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 10px 14px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 15;\n font-size: 11px;\n color: var(--mj-text-secondary);\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\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: var(--mj-text-secondary);\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: var(--mj-radius-sm);\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: var(--mj-radius-sm);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-lg);\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: var(--mj-text-sm);\n font-weight: 450;\n color: var(--mj-text-secondary);\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: var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.mj-flow-context-menu-item--danger {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: var(--mj-status-error-subtle);\n}\n\n.mj-flow-context-menu-item--danger i {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\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 var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 1px;\n border-radius: var(--mj-radius-md);\n}\n"], encapsulation: 2 });
1153
+ i0.ɵɵconditional(ctx.ShowStatusBar ? 23 : -1);
1154
+ } }, 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.MJEmptyStateComponent, i6.FlowNodeComponent, i7.FlowPaletteComponent, i8.FlowToolbarComponent, i9.FlowStatusBarComponent], styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n/* The HOST is a custom element, which is `display: inline` with auto height until told otherwise.\n Without this, `.mj-flow-editor { height: 100% }` below resolved against an auto-height parent \u2014\n which means auto \u2014 so the canvas fell back to its `min-height` floor and sat at 400px no matter\n how much room its pane actually had.\n\n `height: 100%` here is safe for hosts that DON'T give it a box: it resolves to auto in that case,\n and the floor takes over exactly as before. */\n:host {\n display: block;\n height: 100%;\n min-height: 0;\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\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.mj-flow-editor-toolbar-chip,\n.mj-flow-editor-toolbar-tab {\n position: absolute;\n top: 10px;\n z-index: 20;\n}\n.mj-flow-editor-toolbar-area[data-align=\"center\"],\n.mj-flow-editor-toolbar-chip[data-align=\"center\"],\n.mj-flow-editor-toolbar-tab[data-align=\"center\"] {\n left: 50%;\n transform: translateX(-50%);\n}\n.mj-flow-editor-toolbar-area[data-align=\"left\"],\n.mj-flow-editor-toolbar-chip[data-align=\"left\"],\n.mj-flow-editor-toolbar-tab[data-align=\"left\"] {\n left: 10px;\n}\n.mj-flow-editor-toolbar-area[data-align=\"right\"],\n.mj-flow-editor-toolbar-chip[data-align=\"right\"],\n.mj-flow-editor-toolbar-tab[data-align=\"right\"] {\n right: 10px;\n}\n\n.mj-flow-editor-toolbar-chip,\n.mj-flow-editor-toolbar-tab {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-elevated);\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n.mj-flow-editor-toolbar-chip:hover,\n.mj-flow-editor-toolbar-tab:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n.mj-flow-editor-toolbar-chip {\n width: 32px;\n height: 32px;\n border-radius: var(--mj-radius-md);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n}\n.mj-flow-editor-toolbar-tab {\n width: 22px;\n height: 18px;\n padding: 0;\n border-radius: var(--mj-radius-sm);\n opacity: 0.55;\n}\n.mj-flow-editor-toolbar-tab:hover { opacity: 1; }\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: var(--mj-bg-surface);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\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: var(--mj-border-strong);\n stroke: var(--mj-text-disabled);\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: color-mix(in srgb, var(--mj-brand-primary) 50%, var(--mj-bg-surface));\n stroke: var(--mj-brand-primary);\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: var(--mj-border-default);\n stroke: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n stroke: var(--mj-brand-primary);\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-subtitle {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface);\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n box-shadow: var(--mj-shadow-sm);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n --f-selection-area-border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: var(--mj-brand-primary);\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: var(--mj-text-disabled);\n stroke-width: var(--connection-width, 2px);\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: calc(var(--connection-width, 2px) + 1px);\n}\n\n.f-connection.f-selected path {\n stroke: var(--mj-brand-primary);\n stroke-width: max(3px, var(--connection-width, 2px));\n}\n\n/* Colored connections use the --connection-color custom property,\n defaulting to neutral when no color is specified by the consumer */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, var(--mj-text-disabled));\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, var(--mj-brand-primary));\n stroke-width: max(3px, var(--connection-width, 2px));\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.mj-flow-connection-animated path {\n stroke-dasharray: 14 10;\n stroke-linecap: round;\n stroke-width: var(--connection-width, 4.5px);\n animation: mj-flow-dash 0.5s linear infinite;\n filter: drop-shadow(0 0 5px color-mix(in srgb, var(--connection-color, var(--mj-brand-primary)) 70%, transparent));\n}\n@keyframes mj-flow-dash {\n to { stroke-dashoffset: -24; }\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(6px);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 10px 14px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 15;\n font-size: 11px;\n color: var(--mj-text-secondary);\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\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: var(--mj-text-secondary);\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: var(--mj-radius-sm);\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: var(--mj-radius-sm);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-lg);\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: var(--mj-text-sm);\n font-weight: 450;\n color: var(--mj-text-secondary);\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: var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.mj-flow-context-menu-shortcut {\n margin-left: auto;\n padding: 0 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 3px;\n font-size: 10px;\n font-family: inherit;\n color: var(--mj-text-muted);\n}\n\n.mj-flow-context-menu-item--danger {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: var(--mj-status-error-subtle);\n}\n\n.mj-flow-context-menu-item--danger i {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\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 var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 1px;\n border-radius: var(--mj-radius-md);\n}\n"], encapsulation: 2 });
998
1155
  }
999
1156
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowEditorComponent, [{
1000
1157
  type: Component,
1001
- 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 <mj-empty-state\n Icon=\"fa-solid fa-diagram-project\"\n [Title]=\"EmptyStateTitle\">\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 </mj-empty-state>\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\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: var(--mj-bg-surface);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\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: var(--mj-border-strong);\n stroke: var(--mj-text-disabled);\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: color-mix(in srgb, var(--mj-brand-primary) 50%, var(--mj-bg-surface));\n stroke: var(--mj-brand-primary);\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: var(--mj-border-default);\n stroke: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n stroke: var(--mj-brand-primary);\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-subtitle {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface);\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n box-shadow: var(--mj-shadow-sm);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n --f-selection-area-border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: var(--mj-brand-primary);\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: var(--mj-text-disabled);\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: var(--mj-brand-primary);\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property,\n defaulting to neutral when no color is specified by the consumer */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, var(--mj-text-disabled));\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, var(--mj-brand-primary));\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: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(6px);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 10px 14px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 15;\n font-size: 11px;\n color: var(--mj-text-secondary);\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\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: var(--mj-text-secondary);\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: var(--mj-radius-sm);\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: var(--mj-radius-sm);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-lg);\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: var(--mj-text-sm);\n font-weight: 450;\n color: var(--mj-text-secondary);\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: var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.mj-flow-context-menu-item--danger {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: var(--mj-status-error-subtle);\n}\n\n.mj-flow-context-menu-item--danger i {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\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 var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 1px;\n border-radius: var(--mj-radius-md);\n}\n"] }]
1158
+ 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 \u2014 shown / minimized / hidden (recover tab). Align is left/center/right. -->\n @if (ShowToolbar && EffectiveToolbarVisibility === 'shown') {\n <div class=\"mj-flow-editor-toolbar-area\" [attr.data-align]=\"ToolbarAlign\">\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 (MinimizeClicked)=\"SetToolbarVisibility('minimized')\"\n (HideClicked)=\"SetToolbarVisibility('hidden')\">\n </mj-flow-toolbar>\n </div>\n } @else if (ShowToolbar && EffectiveToolbarVisibility === 'minimized') {\n <button type=\"button\" class=\"mj-flow-editor-toolbar-chip\" [attr.data-align]=\"ToolbarAlign\"\n (click)=\"SetToolbarVisibility('shown')\"\n title=\"Show canvas tools\" aria-label=\"Show canvas tools\">\n <i class=\"fa-solid fa-wrench\" aria-hidden=\"true\"></i>\n </button>\n } @else if (ShowToolbar && EffectiveToolbarVisibility === 'hidden') {\n <button type=\"button\" class=\"mj-flow-editor-toolbar-tab\" [attr.data-align]=\"ToolbarAlign\"\n (click)=\"SetToolbarVisibility('shown')\"\n title=\"Show canvas tools\" aria-label=\"Show canvas tools\">\n <i class=\"fa-solid fa-ellipsis\" aria-hidden=\"true\"></i>\n </button>\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 (NodeTypeActivated)=\"onPaletteNodeTypeActivated($event)\">\n </mj-flow-palette>\n }\n\n <!-- Foblex Canvas -->\n <div class=\"mj-flow-editor-canvas\" #canvasArea\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 [style.--connection-width.px]=\"conn.StrokeWidth || 2\"\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 [class.mj-flow-connection-animated]=\"!!conn.Animated\"\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\"\n [height]=\"conn.Animated ? 16 : 10\"\n [width]=\"conn.Animated ? 16 : 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 <mj-empty-state\n Icon=\"fa-solid fa-diagram-project\"\n [Title]=\"EmptyStateTitle\">\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 </mj-empty-state>\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 @for (item of contextMenuItems; track item.ID; let last = $last) {\n <button type=\"button\" class=\"mj-flow-context-menu-item\"\n [class.mj-flow-context-menu-item--danger]=\"item.Danger\"\n [disabled]=\"item.Disabled\"\n (click)=\"onContextMenuAction(item.ID)\">\n @if (item.Icon) {\n <i class=\"fa-solid\" [ngClass]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Shortcut) {\n <kbd class=\"mj-flow-context-menu-shortcut\">{{ item.Shortcut }}</kbd>\n }\n </button>\n @if (!last && item.Danger !== contextMenuItems[$index + 1]?.Danger) {\n <div class=\"mj-flow-context-menu-divider\"></div>\n }\n }\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/* The HOST is a custom element, which is `display: inline` with auto height until told otherwise.\n Without this, `.mj-flow-editor { height: 100% }` below resolved against an auto-height parent \u2014\n which means auto \u2014 so the canvas fell back to its `min-height` floor and sat at 400px no matter\n how much room its pane actually had.\n\n `height: 100%` here is safe for hosts that DON'T give it a box: it resolves to auto in that case,\n and the floor takes over exactly as before. */\n:host {\n display: block;\n height: 100%;\n min-height: 0;\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\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.mj-flow-editor-toolbar-chip,\n.mj-flow-editor-toolbar-tab {\n position: absolute;\n top: 10px;\n z-index: 20;\n}\n.mj-flow-editor-toolbar-area[data-align=\"center\"],\n.mj-flow-editor-toolbar-chip[data-align=\"center\"],\n.mj-flow-editor-toolbar-tab[data-align=\"center\"] {\n left: 50%;\n transform: translateX(-50%);\n}\n.mj-flow-editor-toolbar-area[data-align=\"left\"],\n.mj-flow-editor-toolbar-chip[data-align=\"left\"],\n.mj-flow-editor-toolbar-tab[data-align=\"left\"] {\n left: 10px;\n}\n.mj-flow-editor-toolbar-area[data-align=\"right\"],\n.mj-flow-editor-toolbar-chip[data-align=\"right\"],\n.mj-flow-editor-toolbar-tab[data-align=\"right\"] {\n right: 10px;\n}\n\n.mj-flow-editor-toolbar-chip,\n.mj-flow-editor-toolbar-tab {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-elevated);\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n.mj-flow-editor-toolbar-chip:hover,\n.mj-flow-editor-toolbar-tab:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n}\n.mj-flow-editor-toolbar-chip {\n width: 32px;\n height: 32px;\n border-radius: var(--mj-radius-md);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n}\n.mj-flow-editor-toolbar-tab {\n width: 22px;\n height: 18px;\n padding: 0;\n border-radius: var(--mj-radius-sm);\n opacity: 0.55;\n}\n.mj-flow-editor-toolbar-tab:hover { opacity: 1; }\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: var(--mj-bg-surface);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\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: var(--mj-border-strong);\n stroke: var(--mj-text-disabled);\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: color-mix(in srgb, var(--mj-brand-primary) 50%, var(--mj-bg-surface));\n stroke: var(--mj-brand-primary);\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: var(--mj-border-default);\n stroke: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n stroke: var(--mj-brand-primary);\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-subtitle {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-disabled);\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: var(--mj-bg-surface);\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n box-shadow: var(--mj-shadow-sm);\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: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent);\n --f-selection-area-border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: var(--mj-brand-primary);\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: var(--mj-text-disabled);\n stroke-width: var(--connection-width, 2px);\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: calc(var(--connection-width, 2px) + 1px);\n}\n\n.f-connection.f-selected path {\n stroke: var(--mj-brand-primary);\n stroke-width: max(3px, var(--connection-width, 2px));\n}\n\n/* Colored connections use the --connection-color custom property,\n defaulting to neutral when no color is specified by the consumer */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, var(--mj-text-disabled));\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, var(--mj-brand-primary));\n stroke-width: max(3px, var(--connection-width, 2px));\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.mj-flow-connection-animated path {\n stroke-dasharray: 14 10;\n stroke-linecap: round;\n stroke-width: var(--connection-width, 4.5px);\n animation: mj-flow-dash 0.5s linear infinite;\n filter: drop-shadow(0 0 5px color-mix(in srgb, var(--connection-color, var(--mj-brand-primary)) 70%, transparent));\n}\n@keyframes mj-flow-dash {\n to { stroke-dashoffset: -24; }\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: var(--mj-text-disabled);\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: color-mix(in srgb, var(--mj-bg-surface) 92%, transparent);\n backdrop-filter: blur(6px);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 10px 14px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 15;\n font-size: 11px;\n color: var(--mj-text-secondary);\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-primary);\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: var(--mj-text-disabled);\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: var(--mj-text-secondary);\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: var(--mj-radius-sm);\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: var(--mj-radius-sm);\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: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-lg);\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: var(--mj-text-sm);\n font-weight: 450;\n color: var(--mj-text-secondary);\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: var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.mj-flow-context-menu-shortcut {\n margin-left: auto;\n padding: 0 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 3px;\n font-size: 10px;\n font-family: inherit;\n color: var(--mj-text-muted);\n}\n\n.mj-flow-context-menu-item--danger {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: var(--mj-status-error-subtle);\n}\n\n.mj-flow-context-menu-item--danger i {\n color: var(--mj-status-error);\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: var(--mj-border-default);\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 var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 1px;\n border-radius: var(--mj-radius-md);\n}\n"] }]
1002
1159
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i1.FlowStateService }, { type: i2.FlowLayoutService }], { Nodes: [{
1003
1160
  type: Input
1004
1161
  }], Connections: [{
@@ -1017,6 +1174,10 @@ export class FlowEditorComponent {
1017
1174
  type: Input
1018
1175
  }], ShowToolbar: [{
1019
1176
  type: Input
1177
+ }], ToolbarVisibility: [{
1178
+ type: Input
1179
+ }], ToolbarAlign: [{
1180
+ type: Input
1020
1181
  }], ShowLegend: [{
1021
1182
  type: Input
1022
1183
  }], GridSize: [{
@@ -1057,12 +1218,18 @@ export class FlowEditorComponent {
1057
1218
  type: Output
1058
1219
  }], ConnectionEditRequested: [{
1059
1220
  type: Output
1221
+ }], BeforeContextMenu: [{
1222
+ type: Output
1223
+ }], AfterContextMenuAction: [{
1224
+ type: Output
1060
1225
  }], GridToggled: [{
1061
1226
  type: Output
1062
1227
  }], MinimapToggled: [{
1063
1228
  type: Output
1064
1229
  }], LegendToggled: [{
1065
1230
  type: Output
1231
+ }], ToolbarVisibilityChanged: [{
1232
+ type: Output
1066
1233
  }], fFlow: [{
1067
1234
  type: ViewChild,
1068
1235
  args: [FFlowComponent]
@@ -1072,6 +1239,9 @@ export class FlowEditorComponent {
1072
1239
  }], fZoom: [{
1073
1240
  type: ViewChild,
1074
1241
  args: ['fZoomRef']
1242
+ }], canvasArea: [{
1243
+ type: ViewChild,
1244
+ args: ['canvasArea']
1075
1245
  }], onDocumentClick: [{
1076
1246
  type: HostListener,
1077
1247
  args: ['document:click']
@@ -1079,5 +1249,12 @@ export class FlowEditorComponent {
1079
1249
  type: HostListener,
1080
1250
  args: ['keydown', ['$event']]
1081
1251
  }] }); })();
1082
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FlowEditorComponent, { className: "FlowEditorComponent", filePath: "src/lib/components/flow-editor.component.ts", lineNumber: 44 }); })();
1252
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FlowEditorComponent, { className: "FlowEditorComponent", filePath: "src/lib/components/flow-editor.component.ts", lineNumber: 46 }); })();
1253
+ function defaultContextMenuItems(target) {
1254
+ const noun = target === 'node' ? 'Node' : 'Connection';
1255
+ return [
1256
+ { ID: 'edit', Label: `Edit ${noun}`, Icon: 'fa-pen-to-square' },
1257
+ { ID: 'remove', Label: `Remove ${noun}`, Icon: 'fa-trash-can', Danger: true },
1258
+ ];
1259
+ }
1083
1260
  //# sourceMappingURL=flow-editor.component.js.map