@memberjunction/ng-flow-editor 5.11.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/agent-editor/agent-properties-panel.component.js +2 -2
- package/dist/lib/agent-editor/flow-agent-editor.component.js +2 -2
- package/dist/lib/components/flow-editor.component.js +2 -2
- package/dist/lib/components/flow-node.component.js +2 -2
- package/dist/lib/components/flow-palette.component.js +2 -2
- package/package.json +5 -5
|
@@ -1104,11 +1104,11 @@ export class AgentPropertiesPanelComponent {
|
|
|
1104
1104
|
i0.ɵɵconditional(ctx.isStepSelected ? 2 : -1);
|
|
1105
1105
|
i0.ɵɵadvance();
|
|
1106
1106
|
i0.ɵɵconditional(ctx.isConnectionSelected ? 3 : -1);
|
|
1107
|
-
} }, dependencies: [i1.NgClass, i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i3.CodeEditorComponent], styles: ["/* ============================================================\n Agent Properties Panel \u2014 Step/Connection configuration\n ============================================================ */\n\n.mj-agent-props {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #ffffff;\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.mj-agent-props *,\n.mj-agent-props *::before,\n.mj-agent-props *::after {\n box-sizing: border-box;\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-header {\n flex-shrink: 0;\n padding: 12px 14px;\n border-bottom: 1px solid #f1f5f9;\n border-left: 3px solid transparent;\n}\n\n.mj-agent-props-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.mj-agent-props-header-label {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 1.2px;\n text-transform: uppercase;\n color: #94a3b8;\n}\n\n.mj-agent-props-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: #94a3b8;\n cursor: pointer;\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-close:hover { background: #f1f5f9; color: #475569; }\n\n/* \u2500\u2500 Identity Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-identity {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mj-agent-props-identity-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n min-width: 32px;\n border-radius: 8px;\n color: #ffffff;\n font-size: 13px;\n flex-shrink: 0;\n}\n\n.mj-agent-props-identity-icon--path {\n border-radius: 50%;\n width: 28px;\n height: 28px;\n min-width: 28px;\n font-size: 11px;\n}\n\n.mj-agent-props-identity-info {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.mj-agent-props-identity-name {\n font-size: 14px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n word-break: break-word;\n}\n\n.mj-agent-props-identity-meta {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-type-pill {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n font-weight: 600;\n padding: 1px 7px;\n border-radius: 4px;\n line-height: 1.6;\n}\n\n.mj-agent-props-status-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.mj-agent-props-status-label {\n font-size: 11px;\n color: #64748b;\n}\n\n.mj-agent-props-start-badge {\n display: inline-flex;\n align-items: center;\n font-size: 10px;\n font-weight: 600;\n color: #059669;\n background: #ecfdf5;\n padding: 1px 6px;\n border-radius: 3px;\n letter-spacing: 0.2px;\n}\n\n.mj-agent-props-priority-badge {\n font-size: 11px;\n color: #64748b;\n font-weight: 500;\n}\n\n/* \u2500\u2500 Path Route Display \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-path-route {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-path-step-name {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-agent-props-path-arrow {\n font-size: 10px;\n color: #94a3b8;\n flex-shrink: 0;\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 */\n\n.mj-agent-props-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n flex: 1;\n}\n\n.mj-agent-props-empty i { font-size: 28px; color: #cbd5e1; margin-bottom: 12px; }\n.mj-agent-props-empty p { font-size: 13px; color: #94a3b8; margin: 0; }\n\n/* \u2500\u2500 Scrollable Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-content {\n flex: 1;\n overflow-y: auto;\n padding-bottom: 16px;\n}\n\n/* \u2500\u2500 Sections \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-section {\n border-bottom: 1px solid #f1f5f9;\n}\n\n.mj-agent-props-section:last-child { border-bottom: none; }\n\n.mj-agent-props-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.1s ease;\n}\n\n.mj-agent-props-section-header:hover {\n background: #f8fafc;\n}\n\n.mj-agent-props-section-header i {\n font-size: 10px;\n color: #94a3b8;\n transition: transform 0.15s ease;\n}\n\n.mj-agent-props-section-title {\n font-size: 11px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-agent-props-section-body {\n padding: 0 14px 12px;\n}\n\n/* \u2500\u2500 Fields \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field {\n margin-bottom: 12px;\n}\n\n.mj-agent-props-field label {\n display: block;\n font-size: 12px;\n font-weight: 500;\n color: #475569;\n margin-bottom: 4px;\n}\n\n.mj-agent-props-field--row {\n display: flex;\n align-items: center;\n}\n\n.mj-agent-props-input {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n font-size: 13px;\n color: #1e293b;\n background: #ffffff;\n transition: border-color 0.15s ease;\n outline: none;\n font-family: inherit;\n}\n\n.mj-agent-props-input:focus { border-color: #3b82f6; }\n.mj-agent-props-input:focus-visible { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); }\n\n/* Select dropdown with consistent cross-browser styling */\nselect.mj-agent-props-input {\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n padding-right: 28px;\n cursor: pointer;\n}\n\n.mj-agent-props-textarea {\n resize: vertical;\n min-height: 48px;\n}\n\n.mj-agent-props-checkbox-label {\n display: flex !important;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #475569;\n cursor: pointer;\n}\n\n.mj-agent-props-checkbox-label input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n accent-color: #3b82f6;\n cursor: pointer;\n}\n\n/* \u2500\u2500 Read-only Values \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-readonly-value {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: #1e293b;\n background: #f8fafc;\n border: 1px solid #f1f5f9;\n border-radius: 6px;\n padding: 6px 10px;\n min-height: 34px;\n word-break: break-word;\n}\n\n.mj-agent-props-readonly-value--muted {\n color: #64748b;\n font-style: italic;\n}\n\n/* \u2500\u2500 Code Editor in Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field mj-code-editor {\n display: block;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n overflow: hidden;\n min-height: 60px;\n max-height: 200px;\n}\n\n.mj-agent-props-field mj-code-editor .cm-editor {\n font-size: 12px;\n}\n\n/* \u2500\u2500 Hints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-hint {\n display: block;\n font-size: 11px;\n color: #94a3b8;\n margin-top: 3px;\n}\n\n/* \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-danger {\n padding: 12px 14px;\n}\n\n.mj-agent-props-delete-btn {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #fecaca;\n border-radius: 6px;\n background: #fef2f2;\n color: #dc2626;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-delete-btn:hover { background: #fee2e2; border-color: #fca5a5; }\n\n.mj-agent-props-delete-btn i { font-size: 12px; }\n"], encapsulation: 2 });
|
|
1107
|
+
} }, dependencies: [i1.NgClass, i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i3.CodeEditorComponent], styles: ["/* ============================================================\n Agent Properties Panel \u2014 Step/Connection configuration\n ============================================================ */\n\n.mj-agent-props {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.mj-agent-props *,\n.mj-agent-props *::before,\n.mj-agent-props *::after {\n box-sizing: border-box;\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-header {\n flex-shrink: 0;\n padding: 12px 14px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n border-left: 3px solid transparent;\n}\n\n.mj-agent-props-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.mj-agent-props-header-label {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 1.2px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n}\n\n.mj-agent-props-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n font-size: var(--mj-text-xs);\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-close:hover { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n\n/* \u2500\u2500 Identity Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-identity {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mj-agent-props-identity-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n min-width: 32px;\n border-radius: var(--mj-radius-md);\n color: white;\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n\n.mj-agent-props-identity-icon--path {\n border-radius: var(--mj-radius-full);\n width: 28px;\n height: 28px;\n min-width: 28px;\n font-size: 11px;\n}\n\n.mj-agent-props-identity-info {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.mj-agent-props-identity-name {\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n word-break: break-word;\n}\n\n.mj-agent-props-identity-meta {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-type-pill {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n font-weight: 600;\n padding: 1px 7px;\n border-radius: var(--mj-radius-sm);\n line-height: 1.6;\n}\n\n.mj-agent-props-status-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n border-radius: var(--mj-radius-full);\n flex-shrink: 0;\n}\n\n.mj-agent-props-status-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.mj-agent-props-start-badge {\n display: inline-flex;\n align-items: center;\n font-size: 10px;\n font-weight: 600;\n color: #059669;\n background: #ecfdf5;\n padding: 1px 6px;\n border-radius: var(--mj-radius-sm);\n letter-spacing: 0.2px;\n}\n\n.mj-agent-props-priority-badge {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n/* \u2500\u2500 Path Route Display \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-path-route {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-path-step-name {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-agent-props-path-arrow {\n font-size: 10px;\n color: var(--mj-text-disabled);\n flex-shrink: 0;\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 */\n\n.mj-agent-props-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n flex: 1;\n}\n\n.mj-agent-props-empty i { font-size: 28px; color: var(--mj-border-strong); margin-bottom: 12px; }\n.mj-agent-props-empty p { font-size: var(--mj-text-sm); color: var(--mj-text-disabled); margin: 0; }\n\n/* \u2500\u2500 Scrollable Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-content {\n flex: 1;\n overflow-y: auto;\n padding-bottom: 16px;\n}\n\n/* \u2500\u2500 Sections \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-section {\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.mj-agent-props-section:last-child { border-bottom: none; }\n\n.mj-agent-props-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.1s ease;\n}\n\n.mj-agent-props-section-header:hover {\n background: var(--mj-bg-page);\n}\n\n.mj-agent-props-section-header i {\n font-size: 10px;\n color: var(--mj-text-disabled);\n transition: transform 0.15s ease;\n}\n\n.mj-agent-props-section-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-agent-props-section-body {\n padding: 0 14px 12px;\n}\n\n/* \u2500\u2500 Fields \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field {\n margin-bottom: 12px;\n}\n\n.mj-agent-props-field label {\n display: block;\n font-size: var(--mj-text-xs);\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin-bottom: 4px;\n}\n\n.mj-agent-props-field--row {\n display: flex;\n align-items: center;\n}\n\n.mj-agent-props-input {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n font-size: var(--mj-text-sm);\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n outline: none;\n font-family: inherit;\n}\n\n.mj-agent-props-input:focus { border-color: var(--mj-brand-primary); }\n.mj-agent-props-input:focus-visible { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); }\n\n/* Select dropdown with consistent cross-browser styling */\nselect.mj-agent-props-input {\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n padding-right: 28px;\n cursor: pointer;\n}\n\n.mj-agent-props-textarea {\n resize: vertical;\n min-height: 48px;\n}\n\n.mj-agent-props-checkbox-label {\n display: flex !important;\n align-items: center;\n gap: 8px;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.mj-agent-props-checkbox-label input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n accent-color: var(--mj-brand-primary);\n cursor: pointer;\n}\n\n/* \u2500\u2500 Read-only Values \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-readonly-value {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n padding: 6px 10px;\n min-height: 34px;\n word-break: break-word;\n}\n\n.mj-agent-props-readonly-value--muted {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n/* \u2500\u2500 Code Editor in Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field mj-code-editor {\n display: block;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n min-height: 60px;\n max-height: 200px;\n}\n\n.mj-agent-props-field mj-code-editor .cm-editor {\n font-size: var(--mj-text-xs);\n}\n\n/* \u2500\u2500 Hints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-hint {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 3px;\n}\n\n/* \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-danger {\n padding: 12px 14px;\n}\n\n.mj-agent-props-delete-btn {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #fecaca;\n border-radius: var(--mj-radius-md);\n background: var(--mj-status-error-subtle);\n color: var(--mj-status-error);\n font-size: var(--mj-text-sm);\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-delete-btn:hover { background: var(--mj-status-error-subtle); border-color: #fca5a5; }\n\n.mj-agent-props-delete-btn i { font-size: var(--mj-text-xs); }\n"], encapsulation: 2 });
|
|
1108
1108
|
}
|
|
1109
1109
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AgentPropertiesPanelComponent, [{
|
|
1110
1110
|
type: Component,
|
|
1111
|
-
args: [{ standalone: false, selector: 'mj-agent-properties-panel', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"mj-agent-props\">\n\n <!-- Empty State -->\n @if (!isStepSelected && !isConnectionSelected) {\n <div class=\"mj-agent-props-empty\">\n <i class=\"fa-solid fa-mouse-pointer\"></i>\n <p>Select a step or connection to view its properties</p>\n </div>\n }\n\n <!-- ============================================ -->\n <!-- STEP PROPERTIES -->\n <!-- ============================================ -->\n @if (isStepSelected) {\n <!-- Step Header -->\n <div class=\"mj-agent-props-header mj-agent-props-header--step\"\n [style.border-left-color]=\"stepTypeColor\">\n <div class=\"mj-agent-props-header-top\">\n <span class=\"mj-agent-props-header-label\">STEP</span>\n <button class=\"mj-agent-props-close\" (click)=\"CloseRequested.emit()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Identity card -->\n <div class=\"mj-agent-props-identity\">\n <div class=\"mj-agent-props-identity-icon\" [style.background]=\"stepTypeColor\">\n <i class=\"fa-solid\" [ngClass]=\"stepTypeIcon\"></i>\n </div>\n <div class=\"mj-agent-props-identity-info\">\n <span class=\"mj-agent-props-identity-name\">{{ Step!.Name }}</span>\n <span class=\"mj-agent-props-identity-meta\">\n <span class=\"mj-agent-props-type-pill\" [style.color]=\"stepTypeColor\"\n [style.background]=\"stepTypeColor + '18'\">{{ stepTypeLabel }}</span>\n <span class=\"mj-agent-props-status-dot\" [style.background]=\"statusColor\"></span>\n <span class=\"mj-agent-props-status-label\">{{ Step!.Status }}</span>\n @if (Step!.StartingStep) {\n <span class=\"mj-agent-props-start-badge\">Start</span>\n }\n </span>\n </div>\n </div>\n </div>\n <div class=\"mj-agent-props-content\">\n <!-- \u2500\u2500 General Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('general')\">\n <span class=\"mj-agent-props-section-title\">General</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('general') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('general')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>Name</label>\n @if (!ReadOnly) {\n <input type=\"text\" [value]=\"Step!.Name\"\n (input)=\"OnNameChange(InputValue($event))\"\n placeholder=\"Step name...\" class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ Step!.Name }}</div>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Description</label>\n @if (!ReadOnly) {\n <textarea [value]=\"Step!.Description || ''\"\n (input)=\"OnDescriptionChange(InputValue($event))\"\n placeholder=\"Optional description...\" rows=\"2\"\n class=\"mj-agent-props-input mj-agent-props-textarea\"></textarea>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ Step!.Description || '\u2014' }}\n </div>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Status</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.Status\"\n (change)=\"OnStatusChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"Active\">Active</option>\n <option value=\"Disabled\">Disabled</option>\n <option value=\"Pending\">Pending</option>\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n <span class=\"mj-agent-props-status-dot\" [style.background]=\"statusColor\"></span>\n {{ Step!.Status }}\n </div>\n }\n </div>\n @if (!ReadOnly) {\n <div class=\"mj-agent-props-field mj-agent-props-field--row\">\n <label class=\"mj-agent-props-checkbox-label\">\n <input type=\"checkbox\" [checked]=\"Step!.StartingStep\"\n (change)=\"OnStartingStepChange(CheckedValue($event))\">\n Starting Step\n </label>\n </div>\n }\n @if (ReadOnly && Step!.StartingStep) {\n <div class=\"mj-agent-props-field\">\n <label>Starting Step</label>\n <div class=\"mj-agent-props-readonly-value\">\n <span class=\"mj-agent-props-start-badge\">Yes \u2014 this is the entry point</span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n <!-- \u2500\u2500 Configuration Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('config')\">\n <span class=\"mj-agent-props-section-title\">Configuration</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('config') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('config')) {\n <div class=\"mj-agent-props-section-body\">\n <!-- Loop Body Type (ForEach / While) -->\n @if (showLoopConfig) {\n <div class=\"mj-agent-props-field\">\n <label>Loop Body Type</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.LoopBodyType || 'Action'\"\n (change)=\"OnLoopBodyTypeChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"Action\">Action</option>\n <option value=\"Prompt\">Prompt</option>\n <option value=\"Sub-Agent\">Sub-Agent</option>\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ Step!.LoopBodyType || 'Action' }}\n </div>\n }\n </div>\n }\n <!-- Action Picker -->\n @if (showActionPicker) {\n <div class=\"mj-agent-props-field\">\n <label>Action</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.ActionID || ''\"\n (change)=\"OnActionChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"\">-- Select Action --</option>\n @for (action of Actions; track action) {\n <option [value]=\"action.ID\">{{ action.Name }}</option>\n }\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ selectedActionName }}</div>\n }\n </div>\n }\n <!-- Prompt Picker -->\n @if (showPromptPicker) {\n <div class=\"mj-agent-props-field\">\n <label>Prompt</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.PromptID || ''\"\n (change)=\"OnPromptChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"\">-- Select Prompt --</option>\n @for (prompt of Prompts; track prompt) {\n <option [value]=\"prompt.ID\">{{ prompt.Name }}</option>\n }\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ selectedPromptName }}</div>\n }\n </div>\n }\n <!-- Sub-Agent Picker -->\n @if (showAgentPicker) {\n <div class=\"mj-agent-props-field\">\n <label>Sub-Agent</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.SubAgentID || ''\"\n (change)=\"OnSubAgentChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"\">-- Select Agent --</option>\n @for (agent of Agents; track agent) {\n <option [value]=\"agent.ID\">{{ agent.Name }}</option>\n }\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ selectedAgentName }}</div>\n }\n </div>\n }\n <!-- Loop Configuration (JSON) -->\n @if (showLoopConfig) {\n <div class=\"mj-agent-props-field\">\n <label>Loop Configuration</label>\n <mj-code-editor\n [value]=\"FormatJsonDisplay(Step!.Configuration)\"\n language=\"json\"\n [readonly]=\"ReadOnly\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n (change)=\"OnConfigurationChange($any($event))\">\n </mj-code-editor>\n </div>\n }\n </div>\n }\n </div>\n <!-- \u2500\u2500 Data Mapping Section (Action steps) \u2500\u2500\u2500 -->\n @if (Step!.StepType === 'Action' || showActionPicker) {\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('mapping')\">\n <span class=\"mj-agent-props-section-title\">Data Mapping</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('mapping') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('mapping')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>Input Mapping</label>\n <mj-code-editor\n [value]=\"FormatJsonDisplay(Step!.ActionInputMapping)\"\n language=\"json\"\n [readonly]=\"ReadOnly\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n placeholder=\"Enter JSON input mapping...\"\n (change)=\"OnInputMappingChange($any($event))\">\n </mj-code-editor>\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Output Mapping</label>\n <mj-code-editor\n [value]=\"FormatJsonDisplay(Step!.ActionOutputMapping)\"\n language=\"json\"\n [readonly]=\"ReadOnly\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n placeholder=\"Enter JSON output mapping...\"\n (change)=\"OnOutputMappingChange($any($event))\">\n </mj-code-editor>\n </div>\n </div>\n }\n </div>\n }\n <!-- \u2500\u2500 Error Handling Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('errors')\">\n <span class=\"mj-agent-props-section-title\">Error Handling</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('errors') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('errors')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>On Error</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.OnErrorBehavior\"\n (change)=\"OnErrorBehaviorChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"fail\">Fail (stop flow)</option>\n <option value=\"continue\">Continue (ignore error)</option>\n <option value=\"retry\">Retry</option>\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ Step!.OnErrorBehavior === 'fail' ? 'Fail (stop flow)' :\n Step!.OnErrorBehavior === 'continue' ? 'Continue (ignore error)' : 'Retry' }}\n </div>\n }\n </div>\n @if (Step!.OnErrorBehavior === 'retry') {\n <div class=\"mj-agent-props-field\">\n <label>Retry Count</label>\n @if (!ReadOnly) {\n <input type=\"number\" [value]=\"Step!.RetryCount\" min=\"0\" max=\"10\"\n (input)=\"OnRetryCountChange(NumericValue($event))\"\n class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ Step!.RetryCount }}</div>\n }\n </div>\n }\n <div class=\"mj-agent-props-field\">\n <label>Timeout (seconds)</label>\n @if (!ReadOnly) {\n <input type=\"number\" [value]=\"Step!.TimeoutSeconds\" min=\"1\"\n (input)=\"OnTimeoutChange(NumericValue($event))\"\n class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ Step!.TimeoutSeconds }}s</div>\n }\n </div>\n </div>\n }\n </div>\n <!-- \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!ReadOnly) {\n <div class=\"mj-agent-props-section mj-agent-props-danger\">\n <button class=\"mj-agent-props-delete-btn\" (click)=\"OnDeleteStep()\">\n <i class=\"fa-solid fa-trash-can\"></i> Delete Step\n </button>\n </div>\n }\n </div>\n }\n\n <!-- ============================================ -->\n <!-- PATH PROPERTIES -->\n <!-- ============================================ -->\n @if (isConnectionSelected) {\n <!-- Path Header -->\n <div class=\"mj-agent-props-header mj-agent-props-header--path\"\n [style.border-left-color]=\"pathAccentColor\">\n <div class=\"mj-agent-props-header-top\">\n <span class=\"mj-agent-props-header-label\">PATH</span>\n <button class=\"mj-agent-props-close\" (click)=\"CloseRequested.emit()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Path identity card -->\n <div class=\"mj-agent-props-identity\">\n <div class=\"mj-agent-props-identity-icon mj-agent-props-identity-icon--path\"\n [style.background]=\"pathAccentColor\">\n <i class=\"fa-solid fa-arrow-right\"></i>\n </div>\n <div class=\"mj-agent-props-identity-info\">\n <span class=\"mj-agent-props-path-route\">\n <span class=\"mj-agent-props-path-step-name\">{{ originStepName }}</span>\n <i class=\"fa-solid fa-arrow-right mj-agent-props-path-arrow\"></i>\n <span class=\"mj-agent-props-path-step-name\">{{ destinationStepName }}</span>\n </span>\n <span class=\"mj-agent-props-identity-meta\">\n <span class=\"mj-agent-props-type-pill\"\n [style.color]=\"pathAccentColor\"\n [style.background]=\"pathAccentColor + '18'\">\n {{ isConditionalPath ? 'Conditional' : 'Always' }}\n </span>\n @if (PathEntity!.Priority !== 0) {\n <span class=\"mj-agent-props-priority-badge\">\n Priority: {{ PathEntity!.Priority }}\n </span>\n }\n </span>\n </div>\n </div>\n </div>\n <div class=\"mj-agent-props-content\">\n <!-- \u2500\u2500 Overview Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('path-overview')\">\n <span class=\"mj-agent-props-section-title\">Route</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('path-overview') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('path-overview')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>From</label>\n <div class=\"mj-agent-props-readonly-value mj-agent-props-readonly-value--muted\">\n {{ originStepName }}\n </div>\n </div>\n <div class=\"mj-agent-props-field\">\n <label>To</label>\n <div class=\"mj-agent-props-readonly-value mj-agent-props-readonly-value--muted\">\n {{ destinationStepName }}\n </div>\n </div>\n <span class=\"mj-agent-props-hint\">To change the route, rewire the connection on the canvas.</span>\n </div>\n }\n </div>\n <!-- \u2500\u2500 Path Settings Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('path-settings')\">\n <span class=\"mj-agent-props-section-title\">Settings</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('path-settings') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('path-settings')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>Description</label>\n @if (!ReadOnly) {\n <input type=\"text\" [value]=\"PathEntity!.Description || ''\"\n (input)=\"OnPathDescriptionChange(InputValue($event))\"\n placeholder=\"Path label...\" class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ PathEntity!.Description || '\u2014' }}\n </div>\n }\n @if (!ReadOnly) {\n <span class=\"mj-agent-props-hint\">Displayed as a label on the connection line</span>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Condition</label>\n @if (!ReadOnly) {\n <mj-code-editor\n [value]=\"PathEntity!.Condition || ''\"\n language=\"javascript\"\n [readonly]=\"false\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n placeholder=\"e.g. payload.status === 'active'\"\n (change)=\"OnPathConditionChange($any($event))\">\n </mj-code-editor>\n }\n @if (ReadOnly && PathEntity!.Condition) {\n <mj-code-editor\n [value]=\"PathEntity!.Condition\"\n language=\"javascript\"\n [readonly]=\"true\"\n setup=\"minimal\"\n [lineWrapping]=\"true\">\n </mj-code-editor>\n }\n @if (ReadOnly && !PathEntity!.Condition) {\n <div class=\"mj-agent-props-readonly-value mj-agent-props-readonly-value--muted\">\n No condition (always taken)\n </div>\n }\n @if (!ReadOnly) {\n <span class=\"mj-agent-props-hint\">\n JavaScript expression evaluated at runtime. Leave empty for unconditional.\n </span>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Priority</label>\n @if (!ReadOnly) {\n <input type=\"number\" [value]=\"PathEntity!.Priority\" min=\"-100\" max=\"100\"\n (input)=\"OnPathPriorityChange(NumericValue($event))\"\n class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ PathEntity!.Priority }}</div>\n }\n <span class=\"mj-agent-props-hint\">Higher priority paths are evaluated first when multiple paths leave a step.</span>\n </div>\n </div>\n }\n </div>\n <!-- \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!ReadOnly) {\n <div class=\"mj-agent-props-section mj-agent-props-danger\">\n <button class=\"mj-agent-props-delete-btn\" (click)=\"OnDeletePath()\">\n <i class=\"fa-solid fa-trash-can\"></i> Delete Path\n </button>\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Agent Properties Panel \u2014 Step/Connection configuration\n ============================================================ */\n\n.mj-agent-props {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #ffffff;\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.mj-agent-props *,\n.mj-agent-props *::before,\n.mj-agent-props *::after {\n box-sizing: border-box;\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-header {\n flex-shrink: 0;\n padding: 12px 14px;\n border-bottom: 1px solid #f1f5f9;\n border-left: 3px solid transparent;\n}\n\n.mj-agent-props-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.mj-agent-props-header-label {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 1.2px;\n text-transform: uppercase;\n color: #94a3b8;\n}\n\n.mj-agent-props-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: #94a3b8;\n cursor: pointer;\n font-size: 12px;\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-close:hover { background: #f1f5f9; color: #475569; }\n\n/* \u2500\u2500 Identity Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-identity {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mj-agent-props-identity-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n min-width: 32px;\n border-radius: 8px;\n color: #ffffff;\n font-size: 13px;\n flex-shrink: 0;\n}\n\n.mj-agent-props-identity-icon--path {\n border-radius: 50%;\n width: 28px;\n height: 28px;\n min-width: 28px;\n font-size: 11px;\n}\n\n.mj-agent-props-identity-info {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.mj-agent-props-identity-name {\n font-size: 14px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n word-break: break-word;\n}\n\n.mj-agent-props-identity-meta {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-type-pill {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n font-weight: 600;\n padding: 1px 7px;\n border-radius: 4px;\n line-height: 1.6;\n}\n\n.mj-agent-props-status-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.mj-agent-props-status-label {\n font-size: 11px;\n color: #64748b;\n}\n\n.mj-agent-props-start-badge {\n display: inline-flex;\n align-items: center;\n font-size: 10px;\n font-weight: 600;\n color: #059669;\n background: #ecfdf5;\n padding: 1px 6px;\n border-radius: 3px;\n letter-spacing: 0.2px;\n}\n\n.mj-agent-props-priority-badge {\n font-size: 11px;\n color: #64748b;\n font-weight: 500;\n}\n\n/* \u2500\u2500 Path Route Display \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-path-route {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 600;\n color: #1e293b;\n line-height: 1.3;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-path-step-name {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-agent-props-path-arrow {\n font-size: 10px;\n color: #94a3b8;\n flex-shrink: 0;\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 */\n\n.mj-agent-props-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n flex: 1;\n}\n\n.mj-agent-props-empty i { font-size: 28px; color: #cbd5e1; margin-bottom: 12px; }\n.mj-agent-props-empty p { font-size: 13px; color: #94a3b8; margin: 0; }\n\n/* \u2500\u2500 Scrollable Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-content {\n flex: 1;\n overflow-y: auto;\n padding-bottom: 16px;\n}\n\n/* \u2500\u2500 Sections \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-section {\n border-bottom: 1px solid #f1f5f9;\n}\n\n.mj-agent-props-section:last-child { border-bottom: none; }\n\n.mj-agent-props-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.1s ease;\n}\n\n.mj-agent-props-section-header:hover {\n background: #f8fafc;\n}\n\n.mj-agent-props-section-header i {\n font-size: 10px;\n color: #94a3b8;\n transition: transform 0.15s ease;\n}\n\n.mj-agent-props-section-title {\n font-size: 11px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-agent-props-section-body {\n padding: 0 14px 12px;\n}\n\n/* \u2500\u2500 Fields \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field {\n margin-bottom: 12px;\n}\n\n.mj-agent-props-field label {\n display: block;\n font-size: 12px;\n font-weight: 500;\n color: #475569;\n margin-bottom: 4px;\n}\n\n.mj-agent-props-field--row {\n display: flex;\n align-items: center;\n}\n\n.mj-agent-props-input {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n font-size: 13px;\n color: #1e293b;\n background: #ffffff;\n transition: border-color 0.15s ease;\n outline: none;\n font-family: inherit;\n}\n\n.mj-agent-props-input:focus { border-color: #3b82f6; }\n.mj-agent-props-input:focus-visible { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); }\n\n/* Select dropdown with consistent cross-browser styling */\nselect.mj-agent-props-input {\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n padding-right: 28px;\n cursor: pointer;\n}\n\n.mj-agent-props-textarea {\n resize: vertical;\n min-height: 48px;\n}\n\n.mj-agent-props-checkbox-label {\n display: flex !important;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #475569;\n cursor: pointer;\n}\n\n.mj-agent-props-checkbox-label input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n accent-color: #3b82f6;\n cursor: pointer;\n}\n\n/* \u2500\u2500 Read-only Values \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-readonly-value {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: #1e293b;\n background: #f8fafc;\n border: 1px solid #f1f5f9;\n border-radius: 6px;\n padding: 6px 10px;\n min-height: 34px;\n word-break: break-word;\n}\n\n.mj-agent-props-readonly-value--muted {\n color: #64748b;\n font-style: italic;\n}\n\n/* \u2500\u2500 Code Editor in Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field mj-code-editor {\n display: block;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n overflow: hidden;\n min-height: 60px;\n max-height: 200px;\n}\n\n.mj-agent-props-field mj-code-editor .cm-editor {\n font-size: 12px;\n}\n\n/* \u2500\u2500 Hints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-hint {\n display: block;\n font-size: 11px;\n color: #94a3b8;\n margin-top: 3px;\n}\n\n/* \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-danger {\n padding: 12px 14px;\n}\n\n.mj-agent-props-delete-btn {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #fecaca;\n border-radius: 6px;\n background: #fef2f2;\n color: #dc2626;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-delete-btn:hover { background: #fee2e2; border-color: #fca5a5; }\n\n.mj-agent-props-delete-btn i { font-size: 12px; }\n"] }]
|
|
1111
|
+
args: [{ standalone: false, selector: 'mj-agent-properties-panel', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"mj-agent-props\">\n\n <!-- Empty State -->\n @if (!isStepSelected && !isConnectionSelected) {\n <div class=\"mj-agent-props-empty\">\n <i class=\"fa-solid fa-mouse-pointer\"></i>\n <p>Select a step or connection to view its properties</p>\n </div>\n }\n\n <!-- ============================================ -->\n <!-- STEP PROPERTIES -->\n <!-- ============================================ -->\n @if (isStepSelected) {\n <!-- Step Header -->\n <div class=\"mj-agent-props-header mj-agent-props-header--step\"\n [style.border-left-color]=\"stepTypeColor\">\n <div class=\"mj-agent-props-header-top\">\n <span class=\"mj-agent-props-header-label\">STEP</span>\n <button class=\"mj-agent-props-close\" (click)=\"CloseRequested.emit()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Identity card -->\n <div class=\"mj-agent-props-identity\">\n <div class=\"mj-agent-props-identity-icon\" [style.background]=\"stepTypeColor\">\n <i class=\"fa-solid\" [ngClass]=\"stepTypeIcon\"></i>\n </div>\n <div class=\"mj-agent-props-identity-info\">\n <span class=\"mj-agent-props-identity-name\">{{ Step!.Name }}</span>\n <span class=\"mj-agent-props-identity-meta\">\n <span class=\"mj-agent-props-type-pill\" [style.color]=\"stepTypeColor\"\n [style.background]=\"stepTypeColor + '18'\">{{ stepTypeLabel }}</span>\n <span class=\"mj-agent-props-status-dot\" [style.background]=\"statusColor\"></span>\n <span class=\"mj-agent-props-status-label\">{{ Step!.Status }}</span>\n @if (Step!.StartingStep) {\n <span class=\"mj-agent-props-start-badge\">Start</span>\n }\n </span>\n </div>\n </div>\n </div>\n <div class=\"mj-agent-props-content\">\n <!-- \u2500\u2500 General Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('general')\">\n <span class=\"mj-agent-props-section-title\">General</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('general') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('general')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>Name</label>\n @if (!ReadOnly) {\n <input type=\"text\" [value]=\"Step!.Name\"\n (input)=\"OnNameChange(InputValue($event))\"\n placeholder=\"Step name...\" class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ Step!.Name }}</div>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Description</label>\n @if (!ReadOnly) {\n <textarea [value]=\"Step!.Description || ''\"\n (input)=\"OnDescriptionChange(InputValue($event))\"\n placeholder=\"Optional description...\" rows=\"2\"\n class=\"mj-agent-props-input mj-agent-props-textarea\"></textarea>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ Step!.Description || '\u2014' }}\n </div>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Status</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.Status\"\n (change)=\"OnStatusChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"Active\">Active</option>\n <option value=\"Disabled\">Disabled</option>\n <option value=\"Pending\">Pending</option>\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n <span class=\"mj-agent-props-status-dot\" [style.background]=\"statusColor\"></span>\n {{ Step!.Status }}\n </div>\n }\n </div>\n @if (!ReadOnly) {\n <div class=\"mj-agent-props-field mj-agent-props-field--row\">\n <label class=\"mj-agent-props-checkbox-label\">\n <input type=\"checkbox\" [checked]=\"Step!.StartingStep\"\n (change)=\"OnStartingStepChange(CheckedValue($event))\">\n Starting Step\n </label>\n </div>\n }\n @if (ReadOnly && Step!.StartingStep) {\n <div class=\"mj-agent-props-field\">\n <label>Starting Step</label>\n <div class=\"mj-agent-props-readonly-value\">\n <span class=\"mj-agent-props-start-badge\">Yes \u2014 this is the entry point</span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n <!-- \u2500\u2500 Configuration Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('config')\">\n <span class=\"mj-agent-props-section-title\">Configuration</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('config') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('config')) {\n <div class=\"mj-agent-props-section-body\">\n <!-- Loop Body Type (ForEach / While) -->\n @if (showLoopConfig) {\n <div class=\"mj-agent-props-field\">\n <label>Loop Body Type</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.LoopBodyType || 'Action'\"\n (change)=\"OnLoopBodyTypeChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"Action\">Action</option>\n <option value=\"Prompt\">Prompt</option>\n <option value=\"Sub-Agent\">Sub-Agent</option>\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ Step!.LoopBodyType || 'Action' }}\n </div>\n }\n </div>\n }\n <!-- Action Picker -->\n @if (showActionPicker) {\n <div class=\"mj-agent-props-field\">\n <label>Action</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.ActionID || ''\"\n (change)=\"OnActionChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"\">-- Select Action --</option>\n @for (action of Actions; track action) {\n <option [value]=\"action.ID\">{{ action.Name }}</option>\n }\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ selectedActionName }}</div>\n }\n </div>\n }\n <!-- Prompt Picker -->\n @if (showPromptPicker) {\n <div class=\"mj-agent-props-field\">\n <label>Prompt</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.PromptID || ''\"\n (change)=\"OnPromptChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"\">-- Select Prompt --</option>\n @for (prompt of Prompts; track prompt) {\n <option [value]=\"prompt.ID\">{{ prompt.Name }}</option>\n }\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ selectedPromptName }}</div>\n }\n </div>\n }\n <!-- Sub-Agent Picker -->\n @if (showAgentPicker) {\n <div class=\"mj-agent-props-field\">\n <label>Sub-Agent</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.SubAgentID || ''\"\n (change)=\"OnSubAgentChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"\">-- Select Agent --</option>\n @for (agent of Agents; track agent) {\n <option [value]=\"agent.ID\">{{ agent.Name }}</option>\n }\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ selectedAgentName }}</div>\n }\n </div>\n }\n <!-- Loop Configuration (JSON) -->\n @if (showLoopConfig) {\n <div class=\"mj-agent-props-field\">\n <label>Loop Configuration</label>\n <mj-code-editor\n [value]=\"FormatJsonDisplay(Step!.Configuration)\"\n language=\"json\"\n [readonly]=\"ReadOnly\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n (change)=\"OnConfigurationChange($any($event))\">\n </mj-code-editor>\n </div>\n }\n </div>\n }\n </div>\n <!-- \u2500\u2500 Data Mapping Section (Action steps) \u2500\u2500\u2500 -->\n @if (Step!.StepType === 'Action' || showActionPicker) {\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('mapping')\">\n <span class=\"mj-agent-props-section-title\">Data Mapping</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('mapping') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('mapping')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>Input Mapping</label>\n <mj-code-editor\n [value]=\"FormatJsonDisplay(Step!.ActionInputMapping)\"\n language=\"json\"\n [readonly]=\"ReadOnly\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n placeholder=\"Enter JSON input mapping...\"\n (change)=\"OnInputMappingChange($any($event))\">\n </mj-code-editor>\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Output Mapping</label>\n <mj-code-editor\n [value]=\"FormatJsonDisplay(Step!.ActionOutputMapping)\"\n language=\"json\"\n [readonly]=\"ReadOnly\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n placeholder=\"Enter JSON output mapping...\"\n (change)=\"OnOutputMappingChange($any($event))\">\n </mj-code-editor>\n </div>\n </div>\n }\n </div>\n }\n <!-- \u2500\u2500 Error Handling Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('errors')\">\n <span class=\"mj-agent-props-section-title\">Error Handling</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('errors') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('errors')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>On Error</label>\n @if (!ReadOnly) {\n <select [value]=\"Step!.OnErrorBehavior\"\n (change)=\"OnErrorBehaviorChange(InputValue($event))\"\n class=\"mj-agent-props-input\">\n <option value=\"fail\">Fail (stop flow)</option>\n <option value=\"continue\">Continue (ignore error)</option>\n <option value=\"retry\">Retry</option>\n </select>\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ Step!.OnErrorBehavior === 'fail' ? 'Fail (stop flow)' :\n Step!.OnErrorBehavior === 'continue' ? 'Continue (ignore error)' : 'Retry' }}\n </div>\n }\n </div>\n @if (Step!.OnErrorBehavior === 'retry') {\n <div class=\"mj-agent-props-field\">\n <label>Retry Count</label>\n @if (!ReadOnly) {\n <input type=\"number\" [value]=\"Step!.RetryCount\" min=\"0\" max=\"10\"\n (input)=\"OnRetryCountChange(NumericValue($event))\"\n class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ Step!.RetryCount }}</div>\n }\n </div>\n }\n <div class=\"mj-agent-props-field\">\n <label>Timeout (seconds)</label>\n @if (!ReadOnly) {\n <input type=\"number\" [value]=\"Step!.TimeoutSeconds\" min=\"1\"\n (input)=\"OnTimeoutChange(NumericValue($event))\"\n class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ Step!.TimeoutSeconds }}s</div>\n }\n </div>\n </div>\n }\n </div>\n <!-- \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!ReadOnly) {\n <div class=\"mj-agent-props-section mj-agent-props-danger\">\n <button class=\"mj-agent-props-delete-btn\" (click)=\"OnDeleteStep()\">\n <i class=\"fa-solid fa-trash-can\"></i> Delete Step\n </button>\n </div>\n }\n </div>\n }\n\n <!-- ============================================ -->\n <!-- PATH PROPERTIES -->\n <!-- ============================================ -->\n @if (isConnectionSelected) {\n <!-- Path Header -->\n <div class=\"mj-agent-props-header mj-agent-props-header--path\"\n [style.border-left-color]=\"pathAccentColor\">\n <div class=\"mj-agent-props-header-top\">\n <span class=\"mj-agent-props-header-label\">PATH</span>\n <button class=\"mj-agent-props-close\" (click)=\"CloseRequested.emit()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Path identity card -->\n <div class=\"mj-agent-props-identity\">\n <div class=\"mj-agent-props-identity-icon mj-agent-props-identity-icon--path\"\n [style.background]=\"pathAccentColor\">\n <i class=\"fa-solid fa-arrow-right\"></i>\n </div>\n <div class=\"mj-agent-props-identity-info\">\n <span class=\"mj-agent-props-path-route\">\n <span class=\"mj-agent-props-path-step-name\">{{ originStepName }}</span>\n <i class=\"fa-solid fa-arrow-right mj-agent-props-path-arrow\"></i>\n <span class=\"mj-agent-props-path-step-name\">{{ destinationStepName }}</span>\n </span>\n <span class=\"mj-agent-props-identity-meta\">\n <span class=\"mj-agent-props-type-pill\"\n [style.color]=\"pathAccentColor\"\n [style.background]=\"pathAccentColor + '18'\">\n {{ isConditionalPath ? 'Conditional' : 'Always' }}\n </span>\n @if (PathEntity!.Priority !== 0) {\n <span class=\"mj-agent-props-priority-badge\">\n Priority: {{ PathEntity!.Priority }}\n </span>\n }\n </span>\n </div>\n </div>\n </div>\n <div class=\"mj-agent-props-content\">\n <!-- \u2500\u2500 Overview Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('path-overview')\">\n <span class=\"mj-agent-props-section-title\">Route</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('path-overview') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('path-overview')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>From</label>\n <div class=\"mj-agent-props-readonly-value mj-agent-props-readonly-value--muted\">\n {{ originStepName }}\n </div>\n </div>\n <div class=\"mj-agent-props-field\">\n <label>To</label>\n <div class=\"mj-agent-props-readonly-value mj-agent-props-readonly-value--muted\">\n {{ destinationStepName }}\n </div>\n </div>\n <span class=\"mj-agent-props-hint\">To change the route, rewire the connection on the canvas.</span>\n </div>\n }\n </div>\n <!-- \u2500\u2500 Path Settings Section \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mj-agent-props-section\">\n <button class=\"mj-agent-props-section-header\" (click)=\"ToggleSection('path-settings')\">\n <span class=\"mj-agent-props-section-title\">Settings</span>\n <i class=\"fa-solid\" [ngClass]=\"IsSectionCollapsed('path-settings') ? 'fa-chevron-right' : 'fa-chevron-down'\"></i>\n </button>\n @if (!IsSectionCollapsed('path-settings')) {\n <div class=\"mj-agent-props-section-body\">\n <div class=\"mj-agent-props-field\">\n <label>Description</label>\n @if (!ReadOnly) {\n <input type=\"text\" [value]=\"PathEntity!.Description || ''\"\n (input)=\"OnPathDescriptionChange(InputValue($event))\"\n placeholder=\"Path label...\" class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">\n {{ PathEntity!.Description || '\u2014' }}\n </div>\n }\n @if (!ReadOnly) {\n <span class=\"mj-agent-props-hint\">Displayed as a label on the connection line</span>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Condition</label>\n @if (!ReadOnly) {\n <mj-code-editor\n [value]=\"PathEntity!.Condition || ''\"\n language=\"javascript\"\n [readonly]=\"false\"\n setup=\"minimal\"\n [lineWrapping]=\"true\"\n placeholder=\"e.g. payload.status === 'active'\"\n (change)=\"OnPathConditionChange($any($event))\">\n </mj-code-editor>\n }\n @if (ReadOnly && PathEntity!.Condition) {\n <mj-code-editor\n [value]=\"PathEntity!.Condition\"\n language=\"javascript\"\n [readonly]=\"true\"\n setup=\"minimal\"\n [lineWrapping]=\"true\">\n </mj-code-editor>\n }\n @if (ReadOnly && !PathEntity!.Condition) {\n <div class=\"mj-agent-props-readonly-value mj-agent-props-readonly-value--muted\">\n No condition (always taken)\n </div>\n }\n @if (!ReadOnly) {\n <span class=\"mj-agent-props-hint\">\n JavaScript expression evaluated at runtime. Leave empty for unconditional.\n </span>\n }\n </div>\n <div class=\"mj-agent-props-field\">\n <label>Priority</label>\n @if (!ReadOnly) {\n <input type=\"number\" [value]=\"PathEntity!.Priority\" min=\"-100\" max=\"100\"\n (input)=\"OnPathPriorityChange(NumericValue($event))\"\n class=\"mj-agent-props-input\">\n }\n @if (ReadOnly) {\n <div class=\"mj-agent-props-readonly-value\">{{ PathEntity!.Priority }}</div>\n }\n <span class=\"mj-agent-props-hint\">Higher priority paths are evaluated first when multiple paths leave a step.</span>\n </div>\n </div>\n }\n </div>\n <!-- \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!ReadOnly) {\n <div class=\"mj-agent-props-section mj-agent-props-danger\">\n <button class=\"mj-agent-props-delete-btn\" (click)=\"OnDeletePath()\">\n <i class=\"fa-solid fa-trash-can\"></i> Delete Path\n </button>\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Agent Properties Panel \u2014 Step/Connection configuration\n ============================================================ */\n\n.mj-agent-props {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.mj-agent-props *,\n.mj-agent-props *::before,\n.mj-agent-props *::after {\n box-sizing: border-box;\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-header {\n flex-shrink: 0;\n padding: 12px 14px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n border-left: 3px solid transparent;\n}\n\n.mj-agent-props-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.mj-agent-props-header-label {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 1.2px;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n}\n\n.mj-agent-props-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n font-size: var(--mj-text-xs);\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-close:hover { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n\n/* \u2500\u2500 Identity Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-identity {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mj-agent-props-identity-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n min-width: 32px;\n border-radius: var(--mj-radius-md);\n color: white;\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n\n.mj-agent-props-identity-icon--path {\n border-radius: var(--mj-radius-full);\n width: 28px;\n height: 28px;\n min-width: 28px;\n font-size: 11px;\n}\n\n.mj-agent-props-identity-info {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n\n.mj-agent-props-identity-name {\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n word-break: break-word;\n}\n\n.mj-agent-props-identity-meta {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-type-pill {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n font-weight: 600;\n padding: 1px 7px;\n border-radius: var(--mj-radius-sm);\n line-height: 1.6;\n}\n\n.mj-agent-props-status-dot {\n display: inline-block;\n width: 7px;\n height: 7px;\n border-radius: var(--mj-radius-full);\n flex-shrink: 0;\n}\n\n.mj-agent-props-status-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.mj-agent-props-start-badge {\n display: inline-flex;\n align-items: center;\n font-size: 10px;\n font-weight: 600;\n color: #059669;\n background: #ecfdf5;\n padding: 1px 6px;\n border-radius: var(--mj-radius-sm);\n letter-spacing: 0.2px;\n}\n\n.mj-agent-props-priority-badge {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n/* \u2500\u2500 Path Route Display \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-path-route {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n flex-wrap: wrap;\n}\n\n.mj-agent-props-path-step-name {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-agent-props-path-arrow {\n font-size: 10px;\n color: var(--mj-text-disabled);\n flex-shrink: 0;\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 */\n\n.mj-agent-props-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n text-align: center;\n flex: 1;\n}\n\n.mj-agent-props-empty i { font-size: 28px; color: var(--mj-border-strong); margin-bottom: 12px; }\n.mj-agent-props-empty p { font-size: var(--mj-text-sm); color: var(--mj-text-disabled); margin: 0; }\n\n/* \u2500\u2500 Scrollable Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-content {\n flex: 1;\n overflow-y: auto;\n padding-bottom: 16px;\n}\n\n/* \u2500\u2500 Sections \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-section {\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.mj-agent-props-section:last-child { border-bottom: none; }\n\n.mj-agent-props-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: none;\n cursor: pointer;\n transition: background-color 0.1s ease;\n}\n\n.mj-agent-props-section-header:hover {\n background: var(--mj-bg-page);\n}\n\n.mj-agent-props-section-header i {\n font-size: 10px;\n color: var(--mj-text-disabled);\n transition: transform 0.15s ease;\n}\n\n.mj-agent-props-section-title {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-agent-props-section-body {\n padding: 0 14px 12px;\n}\n\n/* \u2500\u2500 Fields \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field {\n margin-bottom: 12px;\n}\n\n.mj-agent-props-field label {\n display: block;\n font-size: var(--mj-text-xs);\n font-weight: 500;\n color: var(--mj-text-secondary);\n margin-bottom: 4px;\n}\n\n.mj-agent-props-field--row {\n display: flex;\n align-items: center;\n}\n\n.mj-agent-props-input {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n font-size: var(--mj-text-sm);\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n outline: none;\n font-family: inherit;\n}\n\n.mj-agent-props-input:focus { border-color: var(--mj-brand-primary); }\n.mj-agent-props-input:focus-visible { box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15); }\n\n/* Select dropdown with consistent cross-browser styling */\nselect.mj-agent-props-input {\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 8px center;\n padding-right: 28px;\n cursor: pointer;\n}\n\n.mj-agent-props-textarea {\n resize: vertical;\n min-height: 48px;\n}\n\n.mj-agent-props-checkbox-label {\n display: flex !important;\n align-items: center;\n gap: 8px;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.mj-agent-props-checkbox-label input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n accent-color: var(--mj-brand-primary);\n cursor: pointer;\n}\n\n/* \u2500\u2500 Read-only Values \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-readonly-value {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-primary);\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-md);\n padding: 6px 10px;\n min-height: 34px;\n word-break: break-word;\n}\n\n.mj-agent-props-readonly-value--muted {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n/* \u2500\u2500 Code Editor in Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-field mj-code-editor {\n display: block;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n min-height: 60px;\n max-height: 200px;\n}\n\n.mj-agent-props-field mj-code-editor .cm-editor {\n font-size: var(--mj-text-xs);\n}\n\n/* \u2500\u2500 Hints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-hint {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 3px;\n}\n\n/* \u2500\u2500 Danger Zone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-props-danger {\n padding: 12px 14px;\n}\n\n.mj-agent-props-delete-btn {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #fecaca;\n border-radius: var(--mj-radius-md);\n background: var(--mj-status-error-subtle);\n color: var(--mj-status-error);\n font-size: var(--mj-text-sm);\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s ease;\n}\n\n.mj-agent-props-delete-btn:hover { background: var(--mj-status-error-subtle); border-color: #fca5a5; }\n\n.mj-agent-props-delete-btn i { font-size: var(--mj-text-xs); }\n"] }]
|
|
1112
1112
|
}], null, { Step: [{
|
|
1113
1113
|
type: Input
|
|
1114
1114
|
}], SelectedConnection: [{
|
|
@@ -967,11 +967,11 @@ export class FlowAgentEditorComponent {
|
|
|
967
967
|
i0.ɵɵconditional(!ctx.isLoading ? 28 : -1);
|
|
968
968
|
i0.ɵɵadvance();
|
|
969
969
|
i0.ɵɵconditional(ctx.showUnsavedDialog ? 29 : -1);
|
|
970
|
-
} }, dependencies: [i2.NgClass, i3.FlowEditorComponent, i4.AgentPropertiesPanelComponent, i5.AgentStepListComponent, i2.DatePipe], styles: ["/* ============================================================\n Flow Agent Editor \u2014 MJ-specific flow editor wrapper\n ============================================================ */\n\n/* \u2500\u2500 Host element fullscreen positioning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen {\n position: fixed;\n top: 12px;\n left: 12px;\n right: 12px;\n padding-right: 25px;\n bottom: 12px;\n z-index: 10000;\n border-radius: 10px;\n height: auto !important;\n min-height: unset !important;\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.08);\n}\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen .mj-agent-editor {\n height: 100%;\n min-height: unset;\n border-radius: 10px;\n}\n\n.mj-agent-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 500px;\n background: #f8fafc;\n border-radius: 8px;\n overflow: hidden;\n position: relative;\n}\n\n/* \u2500\u2500 Backdrop overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9999;\n}\n\n/* \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: #ffffff;\n border-bottom: 1px solid #e2e8f0;\n gap: 12px;\n flex-shrink: 0;\n transition: background 0.2s ease, border-color 0.2s ease;\n}\n\n.mj-agent-editor-toolbar--editing {\n background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 50%, #f0fdf4 100%);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar {\n padding: 10px 16px;\n border-bottom: 1px solid #e2e8f0;\n background: #ffffff;\n border-radius: 10px 10px 0 0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar--editing {\n background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 50%, #f0fdf4 100%);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-agent-editor-toolbar-left,\n.mj-agent-editor-toolbar-center,\n.mj-agent-editor-toolbar-right {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-toolbar-right {\n gap: 10px;\n}\n\n.mj-agent-editor-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n background: #ffffff;\n color: #475569;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.mj-agent-editor-btn i { font-size: 12px; }\n\n.mj-agent-editor-btn:hover:not(:disabled) { background: #f1f5f9; border-color: #cbd5e1; }\n.mj-agent-editor-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n.mj-agent-editor-btn--primary {\n background: #3b82f6;\n border-color: #3b82f6;\n color: #ffffff;\n}\n\n.mj-agent-editor-btn--primary:hover:not(:disabled) { background: #2563eb; border-color: #2563eb; }\n\n.mj-agent-editor-btn--expand {\n background: #f0f9ff;\n border-color: #bae6fd;\n color: #0284c7;\n}\n\n.mj-agent-editor-btn--expand:hover:not(:disabled) { background: #e0f2fe; border-color: #7dd3fc; color: #0369a1; }\n\n.mj-agent-editor-btn--close {\n background: #fef2f2;\n border-color: #fecaca;\n color: #dc2626;\n}\n\n.mj-agent-editor-btn--close:hover:not(:disabled) { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n\n.mj-agent-editor-btn-divider {\n width: 1px;\n height: 22px;\n background: #e2e8f0;\n margin: 0 2px;\n}\n\n/* \u2500\u2500 View Toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-view-toggle {\n display: flex;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n overflow: hidden;\n}\n\n.mj-agent-editor-view-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: none;\n background: #ffffff;\n color: #64748b;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-agent-editor-view-btn i { font-size: 11px; }\n\n.mj-agent-editor-view-btn:not(:last-child) { border-right: 1px solid #e2e8f0; }\n\n.mj-agent-editor-view-btn--active {\n background: #eff6ff;\n color: #3b82f6;\n}\n\n.mj-agent-editor-view-btn:hover:not(.mj-agent-editor-view-btn--active) { background: #f8fafc; }\n\n/* \u2500\u2500 Fullscreen Title \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-title {\n font-size: 14px;\n font-weight: 600;\n color: #1e293b;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-right: 12px;\n}\n\n.mj-agent-editor-title i {\n color: #3b82f6;\n font-size: 14px;\n}\n\n/* \u2500\u2500 Status Indicators \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-status {\n font-size: 12px;\n color: #94a3b8;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mj-agent-editor-status--unsaved {\n color: #f59e0b;\n}\n\n.mj-agent-editor-status--unsaved i { font-size: 6px; }\n\n/* \u2500\u2500 Inline Floating Controls (non-fullscreen) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-agent-editor-inline-controls {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 100;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-inline-expand {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: 6px;\n background: rgba(255, 255, 255, 0.85);\n color: #475569;\n font-size: 12px;\n cursor: pointer;\n transition: all 0.15s ease;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);\n backdrop-filter: blur(4px);\n}\n\n.mj-agent-editor-inline-expand:hover {\n background: #ffffff;\n border-color: #94a3b8;\n color: #1e293b;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n transform: scale(1.05);\n}\n\n.mj-agent-editor-inline-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #f59e0b;\n box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 0 6px rgba(245, 158, 11, 0.4);\n animation: mj-inline-dot-pulse 2s ease-in-out infinite;\n}\n\n@keyframes mj-inline-dot-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n/* \u2500\u2500 Loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n flex: 1;\n font-size: 14px;\n color: #64748b;\n}\n\n.mj-agent-editor-loading i { font-size: 16px; }\n\n/* \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-body {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-agent-editor-diagram {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-agent-editor-diagram mj-flow-editor {\n flex: 1;\n display: flex;\n}\n\n.mj-agent-editor-list {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n.mj-agent-editor-list mj-agent-step-list {\n flex: 1;\n overflow-y: auto;\n}\n\n/* \u2500\u2500 Properties Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-properties {\n width: 320px;\n flex-shrink: 0;\n overflow: hidden;\n transform: translateX(320px);\n margin-right: -320px;\n transition: transform 0.2s ease, margin-right 0.2s ease;\n border-left: 1px solid #e2e8f0;\n}\n\n.mj-agent-editor-properties--visible {\n transform: translateX(0);\n margin-right: 0;\n}\n\n.mj-agent-editor-properties mj-agent-properties-panel {\n display: block;\n width: 320px;\n height: 100%;\n}\n\n/* \u2500\u2500 Mode Indicator Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-mode-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 12px;\n letter-spacing: 0.2px;\n}\n\n.mj-agent-editor-mode-badge i { font-size: 10px; }\n\n.mj-agent-editor-mode-badge--editing {\n background: #ecfdf5;\n color: #059669;\n}\n\n.mj-agent-editor-mode-badge--readonly {\n background: #f1f5f9;\n color: #64748b;\n}\n\n/* \u2500\u2500 Edit Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--edit {\n background: #ecfdf5;\n border-color: #a7f3d0;\n color: #059669;\n}\n\n.mj-agent-editor-btn--edit:hover:not(:disabled) {\n background: #d1fae5;\n border-color: #6ee7b7;\n color: #047857;\n}\n\n/* \u2500\u2500 Danger Outline Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--danger-outline {\n border-color: #fecaca;\n color: #dc2626;\n background: #ffffff;\n}\n\n.mj-agent-editor-btn--danger-outline:hover:not(:disabled) {\n background: #fef2f2;\n border-color: #fca5a5;\n}\n\n/* \u2500\u2500 Unsaved Changes Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-dialog-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n animation: mj-dialog-fade-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.mj-agent-editor-dialog {\n background: #ffffff;\n border-radius: 12px;\n padding: 28px 32px;\n max-width: 400px;\n width: 90%;\n box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.05);\n text-align: center;\n animation: mj-dialog-scale-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-scale-in {\n from { transform: scale(0.95); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n\n.mj-agent-editor-dialog-icon {\n font-size: 28px;\n color: #f59e0b;\n margin-bottom: 12px;\n}\n\n.mj-agent-editor-dialog-title {\n font-size: 16px;\n font-weight: 600;\n color: #1e293b;\n margin: 0 0 8px;\n}\n\n.mj-agent-editor-dialog-message {\n font-size: 13px;\n color: #64748b;\n margin: 0 0 20px;\n line-height: 1.5;\n}\n\n.mj-agent-editor-dialog-actions {\n display: flex;\n gap: 8px;\n justify-content: center;\n flex-wrap: wrap;\n}\n"], encapsulation: 2 });
|
|
970
|
+
} }, dependencies: [i2.NgClass, i3.FlowEditorComponent, i4.AgentPropertiesPanelComponent, i5.AgentStepListComponent, i2.DatePipe], styles: ["/* ============================================================\n Flow Agent Editor \u2014 MJ-specific flow editor wrapper\n ============================================================ */\n\n/* \u2500\u2500 Host element fullscreen positioning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen {\n position: fixed;\n top: 12px;\n left: 12px;\n right: 12px;\n padding-right: 25px;\n bottom: 12px;\n z-index: 10000;\n border-radius: var(--mj-radius-lg);\n height: auto !important;\n min-height: unset !important;\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.08);\n}\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen .mj-agent-editor {\n height: 100%;\n min-height: unset;\n border-radius: var(--mj-radius-lg);\n}\n\n.mj-agent-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 500px;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n position: relative;\n}\n\n/* \u2500\u2500 Backdrop overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9999;\n}\n\n/* \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n gap: 12px;\n flex-shrink: 0;\n transition: background 0.2s ease, border-color 0.2s ease;\n}\n\n.mj-agent-editor-toolbar--editing {\n background: var(--mj-status-success-subtle);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar {\n padding: 10px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: var(--mj-radius-lg) var(--mj-radius-lg) 0 0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar--editing {\n background: var(--mj-status-success-subtle);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-agent-editor-toolbar-left,\n.mj-agent-editor-toolbar-center,\n.mj-agent-editor-toolbar-right {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-toolbar-right {\n gap: 10px;\n}\n\n.mj-agent-editor-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-sm);\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.mj-agent-editor-btn i { font-size: var(--mj-text-xs); }\n\n.mj-agent-editor-btn:hover:not(:disabled) { background: var(--mj-bg-surface-sunken); border-color: var(--mj-border-strong); }\n.mj-agent-editor-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n.mj-agent-editor-btn--primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: white;\n}\n\n.mj-agent-editor-btn--primary:hover:not(:disabled) { background: var(--mj-brand-primary-hover); border-color: var(--mj-brand-primary-hover); }\n\n.mj-agent-editor-btn--expand {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-agent-editor-btn--expand:hover:not(:disabled) { background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)); border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, var(--mj-bg-surface)); color: var(--mj-brand-primary-hover); }\n\n.mj-agent-editor-btn--close {\n background: var(--mj-status-error-subtle);\n border-color: #fecaca;\n color: var(--mj-status-error);\n}\n\n.mj-agent-editor-btn--close:hover:not(:disabled) { background: var(--mj-status-error-subtle); border-color: #fca5a5; color: #b91c1c; }\n\n.mj-agent-editor-btn-divider {\n width: 1px;\n height: 22px;\n background: var(--mj-border-default);\n margin: 0 2px;\n}\n\n/* \u2500\u2500 View Toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-view-toggle {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n}\n\n.mj-agent-editor-view-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: none;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: var(--mj-text-xs);\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-agent-editor-view-btn i { font-size: 11px; }\n\n.mj-agent-editor-view-btn:not(:last-child) { border-right: 1px solid var(--mj-border-default); }\n\n.mj-agent-editor-view-btn--active {\n background: var(--mj-brand-primary-subtle);\n color: var(--mj-brand-primary);\n}\n\n.mj-agent-editor-view-btn:hover:not(.mj-agent-editor-view-btn--active) { background: var(--mj-bg-page); }\n\n/* \u2500\u2500 Fullscreen Title \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-title {\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-right: 12px;\n}\n\n.mj-agent-editor-title i {\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-sm);\n}\n\n/* \u2500\u2500 Status Indicators \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-status {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-disabled);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mj-agent-editor-status--unsaved {\n color: var(--mj-status-warning);\n}\n\n.mj-agent-editor-status--unsaved i { font-size: 6px; }\n\n/* \u2500\u2500 Inline Floating Controls (non-fullscreen) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-agent-editor-inline-controls {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 100;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-inline-expand {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: var(--mj-radius-md);\n background: rgba(255, 255, 255, 0.85);\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-xs);\n cursor: pointer;\n transition: all 0.15s ease;\n box-shadow: var(--mj-shadow-sm);\n backdrop-filter: blur(4px);\n}\n\n.mj-agent-editor-inline-expand:hover {\n background: var(--mj-bg-surface);\n border-color: var(--mj-text-disabled);\n color: var(--mj-text-primary);\n box-shadow: var(--mj-shadow-md);\n transform: scale(1.05);\n}\n\n.mj-agent-editor-inline-dot {\n width: 8px;\n height: 8px;\n border-radius: var(--mj-radius-full);\n background: var(--mj-status-warning);\n box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 0 6px rgba(245, 158, 11, 0.4);\n animation: mj-inline-dot-pulse 2s ease-in-out infinite;\n}\n\n@keyframes mj-inline-dot-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n/* \u2500\u2500 Loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n flex: 1;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-muted);\n}\n\n.mj-agent-editor-loading i { font-size: var(--mj-text-base); }\n\n/* \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-body {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-agent-editor-diagram {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-agent-editor-diagram mj-flow-editor {\n flex: 1;\n display: flex;\n}\n\n.mj-agent-editor-list {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n.mj-agent-editor-list mj-agent-step-list {\n flex: 1;\n overflow-y: auto;\n}\n\n/* \u2500\u2500 Properties Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-properties {\n width: 320px;\n flex-shrink: 0;\n overflow: hidden;\n transform: translateX(320px);\n margin-right: -320px;\n transition: transform 0.2s ease, margin-right 0.2s ease;\n border-left: 1px solid var(--mj-border-default);\n}\n\n.mj-agent-editor-properties--visible {\n transform: translateX(0);\n margin-right: 0;\n}\n\n.mj-agent-editor-properties mj-agent-properties-panel {\n display: block;\n width: 320px;\n height: 100%;\n}\n\n/* \u2500\u2500 Mode Indicator Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-mode-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: var(--mj-radius-lg);\n letter-spacing: 0.2px;\n}\n\n.mj-agent-editor-mode-badge i { font-size: 10px; }\n\n.mj-agent-editor-mode-badge--editing {\n background: #ecfdf5;\n color: #059669;\n}\n\n.mj-agent-editor-mode-badge--readonly {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* \u2500\u2500 Edit Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--edit {\n background: #ecfdf5;\n border-color: #a7f3d0;\n color: #059669;\n}\n\n.mj-agent-editor-btn--edit:hover:not(:disabled) {\n background: #d1fae5;\n border-color: #6ee7b7;\n color: #047857;\n}\n\n/* \u2500\u2500 Danger Outline Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--danger-outline {\n border-color: #fecaca;\n color: var(--mj-status-error);\n background: var(--mj-bg-surface);\n}\n\n.mj-agent-editor-btn--danger-outline:hover:not(:disabled) {\n background: var(--mj-status-error-subtle);\n border-color: #fca5a5;\n}\n\n/* \u2500\u2500 Unsaved Changes Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-dialog-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n animation: mj-dialog-fade-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.mj-agent-editor-dialog {\n background: var(--mj-bg-surface);\n border-radius: var(--mj-radius-lg);\n padding: 28px 32px;\n max-width: 400px;\n width: 90%;\n box-shadow: var(--mj-shadow-xl);\n text-align: center;\n animation: mj-dialog-scale-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-scale-in {\n from { transform: scale(0.95); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n\n.mj-agent-editor-dialog-icon {\n font-size: 28px;\n color: var(--mj-status-warning);\n margin-bottom: 12px;\n}\n\n.mj-agent-editor-dialog-title {\n font-size: var(--mj-text-base);\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 8px;\n}\n\n.mj-agent-editor-dialog-message {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-muted);\n margin: 0 0 20px;\n line-height: 1.5;\n}\n\n.mj-agent-editor-dialog-actions {\n display: flex;\n gap: 8px;\n justify-content: center;\n flex-wrap: wrap;\n}\n"], encapsulation: 2 });
|
|
971
971
|
}
|
|
972
972
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowAgentEditorComponent, [{
|
|
973
973
|
type: Component,
|
|
974
|
-
args: [{ standalone: false, selector: 'mj-flow-agent-editor', encapsulation: ViewEncapsulation.None, providers: [AgentFlowTransformerService], template: "<div class=\"mj-agent-editor\">\n\n <!-- \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <div class=\"mj-agent-editor-toolbar\" [class.mj-agent-editor-toolbar--editing]=\"isEditingActive\">\n <div class=\"mj-agent-editor-toolbar-left\">\n <!-- Undo / Redo (edit mode only) -->\n @if (isEditingActive) {\n <button class=\"mj-agent-editor-btn\"\n [disabled]=\"!flowEditor?.CanUndo\" (click)=\"flowEditor?.Undo()\" title=\"Undo (Ctrl+Z)\">\n <i class=\"fa-solid fa-rotate-left\"></i>\n </button>\n }\n @if (isEditingActive) {\n <button class=\"mj-agent-editor-btn\"\n [disabled]=\"!flowEditor?.CanRedo\" (click)=\"flowEditor?.Redo()\" title=\"Redo (Ctrl+Y)\">\n <i class=\"fa-solid fa-rotate-right\"></i>\n </button>\n }\n </div>\n\n <div class=\"mj-agent-editor-toolbar-center\">\n <!-- Fullscreen title -->\n <span class=\"mj-agent-editor-title\">\n <i class=\"fa-solid fa-diagram-project\"></i> Flow Configuration\n </span>\n\n <!-- View Mode Toggle -->\n <div class=\"mj-agent-editor-view-toggle\">\n <button class=\"mj-agent-editor-view-btn\"\n [class.mj-agent-editor-view-btn--active]=\"viewMode === 'diagram'\"\n (click)=\"viewMode = 'diagram'\">\n <i class=\"fa-solid fa-diagram-project\"></i> Diagram\n </button>\n <button class=\"mj-agent-editor-view-btn\"\n [class.mj-agent-editor-view-btn--active]=\"viewMode === 'list'\"\n (click)=\"viewMode = 'list'\">\n <i class=\"fa-solid fa-list\"></i> List\n </button>\n </div>\n\n <!-- Mode indicator badge -->\n @if (isEditingActive) {\n <span class=\"mj-agent-editor-mode-badge mj-agent-editor-mode-badge--editing\">\n <i class=\"fa-solid fa-pen\"></i> Editing\n </span>\n }\n @if (!isEditingActive) {\n <span class=\"mj-agent-editor-mode-badge mj-agent-editor-mode-badge--readonly\">\n <i class=\"fa-solid fa-lock\"></i> Read Only\n </span>\n }\n </div>\n\n <div class=\"mj-agent-editor-toolbar-right\">\n <!-- Status indicators -->\n @if (hasUnsavedChanges) {\n <span class=\"mj-agent-editor-status mj-agent-editor-status--unsaved\">\n <i class=\"fa-solid fa-circle\"></i> Unsaved\n </span>\n }\n @if (lastSaved) {\n <span class=\"mj-agent-editor-status\">\n Saved {{ lastSaved | date:'shortTime' }}\n </span>\n }\n\n <!-- Save + Cancel (when editing) -->\n @if (isEditingActive) {\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--primary\"\n [disabled]=\"isSaving || !hasUnsavedChanges\" (click)=\"Save()\">\n <i class=\"fa-solid\" [ngClass]=\"isSaving ? 'fa-spinner fa-spin' : 'fa-floppy-disk'\"></i>\n {{ isSaving ? 'Saving...' : 'Save' }}\n </button>\n }\n @if (fullscreenEditMode && !EditMode) {\n <button\n class=\"mj-agent-editor-btn\"\n (click)=\"cancelFullscreenEdit()\" title=\"Cancel editing\">\n <i class=\"fa-solid fa-xmark\"></i> Cancel\n </button>\n }\n\n @if (isEditingActive) {\n <div class=\"mj-agent-editor-btn-divider\"></div>\n }\n\n <!-- Fullscreen edit toggle (only when parent is NOT in edit mode) -->\n @if (!EditMode && !fullscreenEditMode && userCanUpdate) {\n <button\n class=\"mj-agent-editor-btn mj-agent-editor-btn--edit\"\n (click)=\"toggleFullscreenEditMode()\" title=\"Edit flow\">\n <i class=\"fa-solid fa-pen-to-square\"></i> Edit\n </button>\n }\n\n <!-- Expand / Close fullscreen -->\n @if (!FullScreen) {\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--expand\"\n (click)=\"toggleFullScreen()\" title=\"Expand to full screen\">\n <i class=\"fa-solid fa-up-right-and-down-left-from-center\"></i> Expand\n </button>\n }\n @if (FullScreen) {\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--close\"\n (click)=\"toggleFullScreen()\" title=\"Close full screen (Esc)\">\n <i class=\"fa-solid fa-xmark\"></i> Close\n </button>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Loading 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 -->\n @if (isLoading) {\n <div class=\"mj-agent-editor-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading flow...</span>\n </div>\n }\n\n <!-- \u2500\u2500 Main Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 @if (!isLoading) {\n <div class=\"mj-agent-editor-body\">\n <!-- Diagram View -->\n @if (viewMode === 'diagram') {\n <div class=\"mj-agent-editor-diagram\">\n <mj-flow-editor #flowEditor\n [Nodes]=\"nodes\"\n [Connections]=\"connections\"\n [NodeTypes]=\"nodeTypes\"\n [ReadOnly]=\"!isEditingActive\"\n [ShowMinimap]=\"prefShowMinimap\"\n [ShowPalette]=\"isEditingActive\"\n [ShowGrid]=\"prefShowGrid\"\n [ShowLegend]=\"prefShowLegend\"\n [ShowStatusBar]=\"true\"\n [ShowToolbar]=\"true\"\n [AutoLayoutDirection]=\"'vertical'\"\n (NodeSelected)=\"onNodeSelected($event)\"\n (NodeAdded)=\"onNodeAdded($event)\"\n (NodeRemoved)=\"onNodeRemoved($event)\"\n (ConnectionCreated)=\"onConnectionCreated($event)\"\n (ConnectionRemoved)=\"onConnectionRemoved($event)\"\n (ConnectionReassigned)=\"onConnectionReassigned($event)\"\n (ConnectionSelected)=\"onConnectionSelected($event)\"\n (NodesChanged)=\"onNodesChanged($event)\"\n (ConnectionsChanged)=\"onConnectionsChanged($event)\"\n (GridToggled)=\"onGridPrefChanged($event)\"\n (MinimapToggled)=\"onMinimapPrefChanged($event)\"\n (LegendToggled)=\"onLegendPrefChanged($event)\">\n </mj-flow-editor>\n <!-- Properties Panel -->\n <div class=\"mj-agent-editor-properties\"\n [class.mj-agent-editor-properties--visible]=\"showPropertiesPanel\">\n <mj-agent-properties-panel\n [Step]=\"selectedStep\"\n [SelectedConnection]=\"selectedConnection\"\n [PathEntity]=\"selectedPathEntity\"\n [ReadOnly]=\"!isEditingActive\"\n [Actions]=\"availableActions\"\n [Prompts]=\"availablePrompts\"\n [Agents]=\"availableAgents\"\n [AllSteps]=\"steps\"\n (StepChanged)=\"onStepChanged($event)\"\n (PathChanged)=\"onPathChanged($event)\"\n (DeleteStepRequested)=\"onDeleteStepRequested($event)\"\n (DeletePathRequested)=\"onDeletePathRequested($event)\"\n (CloseRequested)=\"onCloseProperties()\">\n </mj-agent-properties-panel>\n </div>\n </div>\n }\n <!-- List View -->\n @if (viewMode === 'list') {\n <div class=\"mj-agent-editor-list\">\n <mj-agent-step-list\n [Steps]=\"steps\"\n [Paths]=\"paths\"\n [SelectedStepID]=\"selectedStep?.ID || null\"\n (StepClicked)=\"onStepClickedInList($event)\">\n </mj-agent-step-list>\n <!-- Properties Panel in list mode too -->\n <div class=\"mj-agent-editor-properties\"\n [class.mj-agent-editor-properties--visible]=\"showPropertiesPanel\">\n <mj-agent-properties-panel\n [Step]=\"selectedStep\"\n [SelectedConnection]=\"selectedConnection\"\n [PathEntity]=\"selectedPathEntity\"\n [ReadOnly]=\"!isEditingActive\"\n [Actions]=\"availableActions\"\n [Prompts]=\"availablePrompts\"\n [Agents]=\"availableAgents\"\n [AllSteps]=\"steps\"\n (StepChanged)=\"onStepChanged($event)\"\n (PathChanged)=\"onPathChanged($event)\"\n (DeleteStepRequested)=\"onDeleteStepRequested($event)\"\n (DeletePathRequested)=\"onDeletePathRequested($event)\"\n (CloseRequested)=\"onCloseProperties()\">\n </mj-agent-properties-panel>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- \u2500\u2500 Unsaved Changes Dialog \u2500\u2500\u2500\u2500\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 @if (showUnsavedDialog) {\n <div class=\"mj-agent-editor-dialog-overlay\">\n <div class=\"mj-agent-editor-dialog\">\n <div class=\"mj-agent-editor-dialog-icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <h3 class=\"mj-agent-editor-dialog-title\">Unsaved Changes</h3>\n <p class=\"mj-agent-editor-dialog-message\">\n You have unsaved changes to this flow. What would you like to do?\n </p>\n <div class=\"mj-agent-editor-dialog-actions\">\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--primary\" (click)=\"onUnsavedDialogSave()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save & Close\n </button>\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--danger-outline\" (click)=\"onUnsavedDialogDiscard()\">\n Discard Changes\n </button>\n <button class=\"mj-agent-editor-btn\" (click)=\"onUnsavedDialogCancel()\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Agent Editor \u2014 MJ-specific flow editor wrapper\n ============================================================ */\n\n/* \u2500\u2500 Host element fullscreen positioning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen {\n position: fixed;\n top: 12px;\n left: 12px;\n right: 12px;\n padding-right: 25px;\n bottom: 12px;\n z-index: 10000;\n border-radius: 10px;\n height: auto !important;\n min-height: unset !important;\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.08);\n}\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen .mj-agent-editor {\n height: 100%;\n min-height: unset;\n border-radius: 10px;\n}\n\n.mj-agent-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 500px;\n background: #f8fafc;\n border-radius: 8px;\n overflow: hidden;\n position: relative;\n}\n\n/* \u2500\u2500 Backdrop overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9999;\n}\n\n/* \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: #ffffff;\n border-bottom: 1px solid #e2e8f0;\n gap: 12px;\n flex-shrink: 0;\n transition: background 0.2s ease, border-color 0.2s ease;\n}\n\n.mj-agent-editor-toolbar--editing {\n background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 50%, #f0fdf4 100%);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar {\n padding: 10px 16px;\n border-bottom: 1px solid #e2e8f0;\n background: #ffffff;\n border-radius: 10px 10px 0 0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar--editing {\n background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 50%, #f0fdf4 100%);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-agent-editor-toolbar-left,\n.mj-agent-editor-toolbar-center,\n.mj-agent-editor-toolbar-right {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-toolbar-right {\n gap: 10px;\n}\n\n.mj-agent-editor-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n background: #ffffff;\n color: #475569;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.mj-agent-editor-btn i { font-size: 12px; }\n\n.mj-agent-editor-btn:hover:not(:disabled) { background: #f1f5f9; border-color: #cbd5e1; }\n.mj-agent-editor-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n.mj-agent-editor-btn--primary {\n background: #3b82f6;\n border-color: #3b82f6;\n color: #ffffff;\n}\n\n.mj-agent-editor-btn--primary:hover:not(:disabled) { background: #2563eb; border-color: #2563eb; }\n\n.mj-agent-editor-btn--expand {\n background: #f0f9ff;\n border-color: #bae6fd;\n color: #0284c7;\n}\n\n.mj-agent-editor-btn--expand:hover:not(:disabled) { background: #e0f2fe; border-color: #7dd3fc; color: #0369a1; }\n\n.mj-agent-editor-btn--close {\n background: #fef2f2;\n border-color: #fecaca;\n color: #dc2626;\n}\n\n.mj-agent-editor-btn--close:hover:not(:disabled) { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n\n.mj-agent-editor-btn-divider {\n width: 1px;\n height: 22px;\n background: #e2e8f0;\n margin: 0 2px;\n}\n\n/* \u2500\u2500 View Toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-view-toggle {\n display: flex;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n overflow: hidden;\n}\n\n.mj-agent-editor-view-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: none;\n background: #ffffff;\n color: #64748b;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-agent-editor-view-btn i { font-size: 11px; }\n\n.mj-agent-editor-view-btn:not(:last-child) { border-right: 1px solid #e2e8f0; }\n\n.mj-agent-editor-view-btn--active {\n background: #eff6ff;\n color: #3b82f6;\n}\n\n.mj-agent-editor-view-btn:hover:not(.mj-agent-editor-view-btn--active) { background: #f8fafc; }\n\n/* \u2500\u2500 Fullscreen Title \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-title {\n font-size: 14px;\n font-weight: 600;\n color: #1e293b;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-right: 12px;\n}\n\n.mj-agent-editor-title i {\n color: #3b82f6;\n font-size: 14px;\n}\n\n/* \u2500\u2500 Status Indicators \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-status {\n font-size: 12px;\n color: #94a3b8;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mj-agent-editor-status--unsaved {\n color: #f59e0b;\n}\n\n.mj-agent-editor-status--unsaved i { font-size: 6px; }\n\n/* \u2500\u2500 Inline Floating Controls (non-fullscreen) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-agent-editor-inline-controls {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 100;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-inline-expand {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: 6px;\n background: rgba(255, 255, 255, 0.85);\n color: #475569;\n font-size: 12px;\n cursor: pointer;\n transition: all 0.15s ease;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);\n backdrop-filter: blur(4px);\n}\n\n.mj-agent-editor-inline-expand:hover {\n background: #ffffff;\n border-color: #94a3b8;\n color: #1e293b;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);\n transform: scale(1.05);\n}\n\n.mj-agent-editor-inline-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #f59e0b;\n box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 0 6px rgba(245, 158, 11, 0.4);\n animation: mj-inline-dot-pulse 2s ease-in-out infinite;\n}\n\n@keyframes mj-inline-dot-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n/* \u2500\u2500 Loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n flex: 1;\n font-size: 14px;\n color: #64748b;\n}\n\n.mj-agent-editor-loading i { font-size: 16px; }\n\n/* \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-body {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-agent-editor-diagram {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-agent-editor-diagram mj-flow-editor {\n flex: 1;\n display: flex;\n}\n\n.mj-agent-editor-list {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n.mj-agent-editor-list mj-agent-step-list {\n flex: 1;\n overflow-y: auto;\n}\n\n/* \u2500\u2500 Properties Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-properties {\n width: 320px;\n flex-shrink: 0;\n overflow: hidden;\n transform: translateX(320px);\n margin-right: -320px;\n transition: transform 0.2s ease, margin-right 0.2s ease;\n border-left: 1px solid #e2e8f0;\n}\n\n.mj-agent-editor-properties--visible {\n transform: translateX(0);\n margin-right: 0;\n}\n\n.mj-agent-editor-properties mj-agent-properties-panel {\n display: block;\n width: 320px;\n height: 100%;\n}\n\n/* \u2500\u2500 Mode Indicator Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-mode-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 12px;\n letter-spacing: 0.2px;\n}\n\n.mj-agent-editor-mode-badge i { font-size: 10px; }\n\n.mj-agent-editor-mode-badge--editing {\n background: #ecfdf5;\n color: #059669;\n}\n\n.mj-agent-editor-mode-badge--readonly {\n background: #f1f5f9;\n color: #64748b;\n}\n\n/* \u2500\u2500 Edit Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--edit {\n background: #ecfdf5;\n border-color: #a7f3d0;\n color: #059669;\n}\n\n.mj-agent-editor-btn--edit:hover:not(:disabled) {\n background: #d1fae5;\n border-color: #6ee7b7;\n color: #047857;\n}\n\n/* \u2500\u2500 Danger Outline Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--danger-outline {\n border-color: #fecaca;\n color: #dc2626;\n background: #ffffff;\n}\n\n.mj-agent-editor-btn--danger-outline:hover:not(:disabled) {\n background: #fef2f2;\n border-color: #fca5a5;\n}\n\n/* \u2500\u2500 Unsaved Changes Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-dialog-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n animation: mj-dialog-fade-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.mj-agent-editor-dialog {\n background: #ffffff;\n border-radius: 12px;\n padding: 28px 32px;\n max-width: 400px;\n width: 90%;\n box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.05);\n text-align: center;\n animation: mj-dialog-scale-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-scale-in {\n from { transform: scale(0.95); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n\n.mj-agent-editor-dialog-icon {\n font-size: 28px;\n color: #f59e0b;\n margin-bottom: 12px;\n}\n\n.mj-agent-editor-dialog-title {\n font-size: 16px;\n font-weight: 600;\n color: #1e293b;\n margin: 0 0 8px;\n}\n\n.mj-agent-editor-dialog-message {\n font-size: 13px;\n color: #64748b;\n margin: 0 0 20px;\n line-height: 1.5;\n}\n\n.mj-agent-editor-dialog-actions {\n display: flex;\n gap: 8px;\n justify-content: center;\n flex-wrap: wrap;\n}\n"] }]
|
|
974
|
+
args: [{ standalone: false, selector: 'mj-flow-agent-editor', encapsulation: ViewEncapsulation.None, providers: [AgentFlowTransformerService], template: "<div class=\"mj-agent-editor\">\n\n <!-- \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <div class=\"mj-agent-editor-toolbar\" [class.mj-agent-editor-toolbar--editing]=\"isEditingActive\">\n <div class=\"mj-agent-editor-toolbar-left\">\n <!-- Undo / Redo (edit mode only) -->\n @if (isEditingActive) {\n <button class=\"mj-agent-editor-btn\"\n [disabled]=\"!flowEditor?.CanUndo\" (click)=\"flowEditor?.Undo()\" title=\"Undo (Ctrl+Z)\">\n <i class=\"fa-solid fa-rotate-left\"></i>\n </button>\n }\n @if (isEditingActive) {\n <button class=\"mj-agent-editor-btn\"\n [disabled]=\"!flowEditor?.CanRedo\" (click)=\"flowEditor?.Redo()\" title=\"Redo (Ctrl+Y)\">\n <i class=\"fa-solid fa-rotate-right\"></i>\n </button>\n }\n </div>\n\n <div class=\"mj-agent-editor-toolbar-center\">\n <!-- Fullscreen title -->\n <span class=\"mj-agent-editor-title\">\n <i class=\"fa-solid fa-diagram-project\"></i> Flow Configuration\n </span>\n\n <!-- View Mode Toggle -->\n <div class=\"mj-agent-editor-view-toggle\">\n <button class=\"mj-agent-editor-view-btn\"\n [class.mj-agent-editor-view-btn--active]=\"viewMode === 'diagram'\"\n (click)=\"viewMode = 'diagram'\">\n <i class=\"fa-solid fa-diagram-project\"></i> Diagram\n </button>\n <button class=\"mj-agent-editor-view-btn\"\n [class.mj-agent-editor-view-btn--active]=\"viewMode === 'list'\"\n (click)=\"viewMode = 'list'\">\n <i class=\"fa-solid fa-list\"></i> List\n </button>\n </div>\n\n <!-- Mode indicator badge -->\n @if (isEditingActive) {\n <span class=\"mj-agent-editor-mode-badge mj-agent-editor-mode-badge--editing\">\n <i class=\"fa-solid fa-pen\"></i> Editing\n </span>\n }\n @if (!isEditingActive) {\n <span class=\"mj-agent-editor-mode-badge mj-agent-editor-mode-badge--readonly\">\n <i class=\"fa-solid fa-lock\"></i> Read Only\n </span>\n }\n </div>\n\n <div class=\"mj-agent-editor-toolbar-right\">\n <!-- Status indicators -->\n @if (hasUnsavedChanges) {\n <span class=\"mj-agent-editor-status mj-agent-editor-status--unsaved\">\n <i class=\"fa-solid fa-circle\"></i> Unsaved\n </span>\n }\n @if (lastSaved) {\n <span class=\"mj-agent-editor-status\">\n Saved {{ lastSaved | date:'shortTime' }}\n </span>\n }\n\n <!-- Save + Cancel (when editing) -->\n @if (isEditingActive) {\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--primary\"\n [disabled]=\"isSaving || !hasUnsavedChanges\" (click)=\"Save()\">\n <i class=\"fa-solid\" [ngClass]=\"isSaving ? 'fa-spinner fa-spin' : 'fa-floppy-disk'\"></i>\n {{ isSaving ? 'Saving...' : 'Save' }}\n </button>\n }\n @if (fullscreenEditMode && !EditMode) {\n <button\n class=\"mj-agent-editor-btn\"\n (click)=\"cancelFullscreenEdit()\" title=\"Cancel editing\">\n <i class=\"fa-solid fa-xmark\"></i> Cancel\n </button>\n }\n\n @if (isEditingActive) {\n <div class=\"mj-agent-editor-btn-divider\"></div>\n }\n\n <!-- Fullscreen edit toggle (only when parent is NOT in edit mode) -->\n @if (!EditMode && !fullscreenEditMode && userCanUpdate) {\n <button\n class=\"mj-agent-editor-btn mj-agent-editor-btn--edit\"\n (click)=\"toggleFullscreenEditMode()\" title=\"Edit flow\">\n <i class=\"fa-solid fa-pen-to-square\"></i> Edit\n </button>\n }\n\n <!-- Expand / Close fullscreen -->\n @if (!FullScreen) {\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--expand\"\n (click)=\"toggleFullScreen()\" title=\"Expand to full screen\">\n <i class=\"fa-solid fa-up-right-and-down-left-from-center\"></i> Expand\n </button>\n }\n @if (FullScreen) {\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--close\"\n (click)=\"toggleFullScreen()\" title=\"Close full screen (Esc)\">\n <i class=\"fa-solid fa-xmark\"></i> Close\n </button>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Loading 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 -->\n @if (isLoading) {\n <div class=\"mj-agent-editor-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading flow...</span>\n </div>\n }\n\n <!-- \u2500\u2500 Main Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 @if (!isLoading) {\n <div class=\"mj-agent-editor-body\">\n <!-- Diagram View -->\n @if (viewMode === 'diagram') {\n <div class=\"mj-agent-editor-diagram\">\n <mj-flow-editor #flowEditor\n [Nodes]=\"nodes\"\n [Connections]=\"connections\"\n [NodeTypes]=\"nodeTypes\"\n [ReadOnly]=\"!isEditingActive\"\n [ShowMinimap]=\"prefShowMinimap\"\n [ShowPalette]=\"isEditingActive\"\n [ShowGrid]=\"prefShowGrid\"\n [ShowLegend]=\"prefShowLegend\"\n [ShowStatusBar]=\"true\"\n [ShowToolbar]=\"true\"\n [AutoLayoutDirection]=\"'vertical'\"\n (NodeSelected)=\"onNodeSelected($event)\"\n (NodeAdded)=\"onNodeAdded($event)\"\n (NodeRemoved)=\"onNodeRemoved($event)\"\n (ConnectionCreated)=\"onConnectionCreated($event)\"\n (ConnectionRemoved)=\"onConnectionRemoved($event)\"\n (ConnectionReassigned)=\"onConnectionReassigned($event)\"\n (ConnectionSelected)=\"onConnectionSelected($event)\"\n (NodesChanged)=\"onNodesChanged($event)\"\n (ConnectionsChanged)=\"onConnectionsChanged($event)\"\n (GridToggled)=\"onGridPrefChanged($event)\"\n (MinimapToggled)=\"onMinimapPrefChanged($event)\"\n (LegendToggled)=\"onLegendPrefChanged($event)\">\n </mj-flow-editor>\n <!-- Properties Panel -->\n <div class=\"mj-agent-editor-properties\"\n [class.mj-agent-editor-properties--visible]=\"showPropertiesPanel\">\n <mj-agent-properties-panel\n [Step]=\"selectedStep\"\n [SelectedConnection]=\"selectedConnection\"\n [PathEntity]=\"selectedPathEntity\"\n [ReadOnly]=\"!isEditingActive\"\n [Actions]=\"availableActions\"\n [Prompts]=\"availablePrompts\"\n [Agents]=\"availableAgents\"\n [AllSteps]=\"steps\"\n (StepChanged)=\"onStepChanged($event)\"\n (PathChanged)=\"onPathChanged($event)\"\n (DeleteStepRequested)=\"onDeleteStepRequested($event)\"\n (DeletePathRequested)=\"onDeletePathRequested($event)\"\n (CloseRequested)=\"onCloseProperties()\">\n </mj-agent-properties-panel>\n </div>\n </div>\n }\n <!-- List View -->\n @if (viewMode === 'list') {\n <div class=\"mj-agent-editor-list\">\n <mj-agent-step-list\n [Steps]=\"steps\"\n [Paths]=\"paths\"\n [SelectedStepID]=\"selectedStep?.ID || null\"\n (StepClicked)=\"onStepClickedInList($event)\">\n </mj-agent-step-list>\n <!-- Properties Panel in list mode too -->\n <div class=\"mj-agent-editor-properties\"\n [class.mj-agent-editor-properties--visible]=\"showPropertiesPanel\">\n <mj-agent-properties-panel\n [Step]=\"selectedStep\"\n [SelectedConnection]=\"selectedConnection\"\n [PathEntity]=\"selectedPathEntity\"\n [ReadOnly]=\"!isEditingActive\"\n [Actions]=\"availableActions\"\n [Prompts]=\"availablePrompts\"\n [Agents]=\"availableAgents\"\n [AllSteps]=\"steps\"\n (StepChanged)=\"onStepChanged($event)\"\n (PathChanged)=\"onPathChanged($event)\"\n (DeleteStepRequested)=\"onDeleteStepRequested($event)\"\n (DeletePathRequested)=\"onDeletePathRequested($event)\"\n (CloseRequested)=\"onCloseProperties()\">\n </mj-agent-properties-panel>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- \u2500\u2500 Unsaved Changes Dialog \u2500\u2500\u2500\u2500\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 @if (showUnsavedDialog) {\n <div class=\"mj-agent-editor-dialog-overlay\">\n <div class=\"mj-agent-editor-dialog\">\n <div class=\"mj-agent-editor-dialog-icon\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </div>\n <h3 class=\"mj-agent-editor-dialog-title\">Unsaved Changes</h3>\n <p class=\"mj-agent-editor-dialog-message\">\n You have unsaved changes to this flow. What would you like to do?\n </p>\n <div class=\"mj-agent-editor-dialog-actions\">\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--primary\" (click)=\"onUnsavedDialogSave()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save & Close\n </button>\n <button class=\"mj-agent-editor-btn mj-agent-editor-btn--danger-outline\" (click)=\"onUnsavedDialogDiscard()\">\n Discard Changes\n </button>\n <button class=\"mj-agent-editor-btn\" (click)=\"onUnsavedDialogCancel()\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Agent Editor \u2014 MJ-specific flow editor wrapper\n ============================================================ */\n\n/* \u2500\u2500 Host element fullscreen positioning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen {\n position: fixed;\n top: 12px;\n left: 12px;\n right: 12px;\n padding-right: 25px;\n bottom: 12px;\n z-index: 10000;\n border-radius: var(--mj-radius-lg);\n height: auto !important;\n min-height: unset !important;\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.08);\n}\n\nmj-flow-agent-editor.mj-flow-agent-editor--fullscreen .mj-agent-editor {\n height: 100%;\n min-height: unset;\n border-radius: var(--mj-radius-lg);\n}\n\n.mj-agent-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 500px;\n background: var(--mj-bg-page);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n position: relative;\n}\n\n/* \u2500\u2500 Backdrop overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9999;\n}\n\n/* \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n gap: 12px;\n flex-shrink: 0;\n transition: background 0.2s ease, border-color 0.2s ease;\n}\n\n.mj-agent-editor-toolbar--editing {\n background: var(--mj-status-success-subtle);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar {\n padding: 10px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: var(--mj-radius-lg) var(--mj-radius-lg) 0 0;\n}\n\n.mj-flow-agent-editor--fullscreen .mj-agent-editor-toolbar--editing {\n background: var(--mj-status-success-subtle);\n border-bottom-color: #bbf7d0;\n}\n\n.mj-agent-editor-toolbar-left,\n.mj-agent-editor-toolbar-center,\n.mj-agent-editor-toolbar-right {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-toolbar-right {\n gap: 10px;\n}\n\n.mj-agent-editor-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-sm);\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.mj-agent-editor-btn i { font-size: var(--mj-text-xs); }\n\n.mj-agent-editor-btn:hover:not(:disabled) { background: var(--mj-bg-surface-sunken); border-color: var(--mj-border-strong); }\n.mj-agent-editor-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n.mj-agent-editor-btn--primary {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: white;\n}\n\n.mj-agent-editor-btn--primary:hover:not(:disabled) { background: var(--mj-brand-primary-hover); border-color: var(--mj-brand-primary-hover); }\n\n.mj-agent-editor-btn--expand {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-agent-editor-btn--expand:hover:not(:disabled) { background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)); border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, var(--mj-bg-surface)); color: var(--mj-brand-primary-hover); }\n\n.mj-agent-editor-btn--close {\n background: var(--mj-status-error-subtle);\n border-color: #fecaca;\n color: var(--mj-status-error);\n}\n\n.mj-agent-editor-btn--close:hover:not(:disabled) { background: var(--mj-status-error-subtle); border-color: #fca5a5; color: #b91c1c; }\n\n.mj-agent-editor-btn-divider {\n width: 1px;\n height: 22px;\n background: var(--mj-border-default);\n margin: 0 2px;\n}\n\n/* \u2500\u2500 View Toggle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-view-toggle {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n}\n\n.mj-agent-editor-view-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: none;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: var(--mj-text-xs);\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-agent-editor-view-btn i { font-size: 11px; }\n\n.mj-agent-editor-view-btn:not(:last-child) { border-right: 1px solid var(--mj-border-default); }\n\n.mj-agent-editor-view-btn--active {\n background: var(--mj-brand-primary-subtle);\n color: var(--mj-brand-primary);\n}\n\n.mj-agent-editor-view-btn:hover:not(.mj-agent-editor-view-btn--active) { background: var(--mj-bg-page); }\n\n/* \u2500\u2500 Fullscreen Title \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-title {\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-right: 12px;\n}\n\n.mj-agent-editor-title i {\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-sm);\n}\n\n/* \u2500\u2500 Status Indicators \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-status {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-disabled);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.mj-agent-editor-status--unsaved {\n color: var(--mj-status-warning);\n}\n\n.mj-agent-editor-status--unsaved i { font-size: 6px; }\n\n/* \u2500\u2500 Inline Floating Controls (non-fullscreen) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-agent-editor-inline-controls {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 100;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-agent-editor-inline-expand {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: var(--mj-radius-md);\n background: rgba(255, 255, 255, 0.85);\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-xs);\n cursor: pointer;\n transition: all 0.15s ease;\n box-shadow: var(--mj-shadow-sm);\n backdrop-filter: blur(4px);\n}\n\n.mj-agent-editor-inline-expand:hover {\n background: var(--mj-bg-surface);\n border-color: var(--mj-text-disabled);\n color: var(--mj-text-primary);\n box-shadow: var(--mj-shadow-md);\n transform: scale(1.05);\n}\n\n.mj-agent-editor-inline-dot {\n width: 8px;\n height: 8px;\n border-radius: var(--mj-radius-full);\n background: var(--mj-status-warning);\n box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 0 6px rgba(245, 158, 11, 0.4);\n animation: mj-inline-dot-pulse 2s ease-in-out infinite;\n}\n\n@keyframes mj-inline-dot-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n/* \u2500\u2500 Loading \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n flex: 1;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-muted);\n}\n\n.mj-agent-editor-loading i { font-size: var(--mj-text-base); }\n\n/* \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-body {\n flex: 1;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-agent-editor-diagram {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-agent-editor-diagram mj-flow-editor {\n flex: 1;\n display: flex;\n}\n\n.mj-agent-editor-list {\n display: flex;\n flex: 1;\n overflow: hidden;\n}\n\n.mj-agent-editor-list mj-agent-step-list {\n flex: 1;\n overflow-y: auto;\n}\n\n/* \u2500\u2500 Properties Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-properties {\n width: 320px;\n flex-shrink: 0;\n overflow: hidden;\n transform: translateX(320px);\n margin-right: -320px;\n transition: transform 0.2s ease, margin-right 0.2s ease;\n border-left: 1px solid var(--mj-border-default);\n}\n\n.mj-agent-editor-properties--visible {\n transform: translateX(0);\n margin-right: 0;\n}\n\n.mj-agent-editor-properties mj-agent-properties-panel {\n display: block;\n width: 320px;\n height: 100%;\n}\n\n/* \u2500\u2500 Mode Indicator Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-mode-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: var(--mj-radius-lg);\n letter-spacing: 0.2px;\n}\n\n.mj-agent-editor-mode-badge i { font-size: 10px; }\n\n.mj-agent-editor-mode-badge--editing {\n background: #ecfdf5;\n color: #059669;\n}\n\n.mj-agent-editor-mode-badge--readonly {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* \u2500\u2500 Edit Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--edit {\n background: #ecfdf5;\n border-color: #a7f3d0;\n color: #059669;\n}\n\n.mj-agent-editor-btn--edit:hover:not(:disabled) {\n background: #d1fae5;\n border-color: #6ee7b7;\n color: #047857;\n}\n\n/* \u2500\u2500 Danger Outline Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-btn--danger-outline {\n border-color: #fecaca;\n color: var(--mj-status-error);\n background: var(--mj-bg-surface);\n}\n\n.mj-agent-editor-btn--danger-outline:hover:not(:disabled) {\n background: var(--mj-status-error-subtle);\n border-color: #fca5a5;\n}\n\n/* \u2500\u2500 Unsaved Changes Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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-agent-editor-dialog-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n animation: mj-dialog-fade-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.mj-agent-editor-dialog {\n background: var(--mj-bg-surface);\n border-radius: var(--mj-radius-lg);\n padding: 28px 32px;\n max-width: 400px;\n width: 90%;\n box-shadow: var(--mj-shadow-xl);\n text-align: center;\n animation: mj-dialog-scale-in 0.15s ease-out;\n}\n\n@keyframes mj-dialog-scale-in {\n from { transform: scale(0.95); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n\n.mj-agent-editor-dialog-icon {\n font-size: 28px;\n color: var(--mj-status-warning);\n margin-bottom: 12px;\n}\n\n.mj-agent-editor-dialog-title {\n font-size: var(--mj-text-base);\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0 0 8px;\n}\n\n.mj-agent-editor-dialog-message {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-muted);\n margin: 0 0 20px;\n line-height: 1.5;\n}\n\n.mj-agent-editor-dialog-actions {\n display: flex;\n gap: 8px;\n justify-content: center;\n flex-wrap: wrap;\n}\n"] }]
|
|
975
975
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.AgentFlowTransformerService }, { type: i0.ElementRef }, { type: i0.Renderer2 }], { HostFullScreen: [{
|
|
976
976
|
type: HostBinding,
|
|
977
977
|
args: ['class.mj-flow-agent-editor--fullscreen']
|
|
@@ -1002,11 +1002,11 @@ export class FlowEditorComponent {
|
|
|
1002
1002
|
i0.ɵɵconditional(ctx.contextMenuVisible ? 19 : -1);
|
|
1003
1003
|
i0.ɵɵadvance();
|
|
1004
1004
|
i0.ɵɵconditional(ctx.ShowStatusBar ? 20 : -1);
|
|
1005
|
-
} }, dependencies: [i3.NgClass, i4.FFlowComponent, i4.FCanvasComponent, i4.FBackgroundComponent, i4.FCirclePatternComponent, i4.FZoomDirective, i4.FConnectionContent, i4.FConnectionMarker, i4.FConnectionComponent, i4.FConnectionForCreateComponent, i4.FLineAlignmentComponent, i4.FMinimapComponent, i4.FNodeDirective, i4.FDragHandleDirective, i4.FSelectionAreaComponent, i4.FDragBlockerDirective, i4.FDraggableDirective, i5.FlowNodeComponent, i6.FlowPaletteComponent, i7.FlowToolbarComponent, i8.FlowStatusBarComponent], styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n.mj-flow-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n overflow: hidden;\n outline: none;\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n}\n\n.mj-flow-editor--readonly .mj-flow-node-port {\n cursor: default;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Toolbar Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-toolbar-area {\n position: absolute;\n top: 10px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n}\n\n/* \u2500\u2500 Canvas Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-canvas-area {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-flow-editor-canvas {\n flex: 1;\n position: relative;\n overflow: hidden;\n background: #ffffff;\n}\n\n.mj-flow-editor-canvas f-flow {\n width: 100%;\n height: 100%;\n}\n\n/* Pan mode \u2014 grab cursor, nodes pass through pointer events to canvas */\n.mj-flow-editor-canvas--pan-mode {\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode:active {\n cursor: grabbing;\n}\n\n.mj-flow-editor-canvas--pan-mode mj-flow-node {\n pointer-events: none;\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode f-connection {\n pointer-events: none;\n}\n\n.mj-flow-editor-canvas--pan-mode .mj-flow-connection-label {\n pointer-events: none;\n}\n\n/* \u2500\u2500 Minimap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-minimap {\n position: absolute;\n bottom: 12px;\n right: 12px;\n width: 160px;\n height: 120px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n z-index: 15;\n}\n\n.mj-flow-editor-minimap f-minimap {\n width: 100%;\n height: 100%;\n}\n\n/* Foblex minimap SVG node rectangles (no default styles provided by Foblex) */\n.mj-flow-editor-minimap .f-minimap-node {\n fill: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: #93c5fd;\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: #e2e8f0;\n stroke: #94a3b8;\n stroke-width: 0.5;\n rx: 2;\n ry: 2;\n}\n\n/* Viewport indicator rectangle */\n.mj-flow-editor-minimap .f-minimap-view {\n fill: rgba(59, 130, 246, 0.08);\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-empty {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 5;\n}\n\n.mj-flow-editor-empty-content {\n text-align: center;\n padding: 40px;\n}\n\n.mj-flow-editor-empty-icon {\n font-size: 48px;\n color: #cbd5e1;\n margin-bottom: 16px;\n}\n\n.mj-flow-editor-empty-title {\n font-size: 15px;\n font-weight: 500;\n color: #64748b;\n margin: 0 0 8px;\n}\n\n.mj-flow-editor-empty-subtitle {\n font-size: 13px;\n color: #94a3b8;\n margin: 0;\n}\n\n/* \u2500\u2500 Connection Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-connection-label {\n display: inline-flex;\n align-items: flex-start;\n gap: 5px;\n background: #fffef5;\n border: 1.5px solid #cbd5e1;\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: #334155;\n white-space: nowrap;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n pointer-events: none;\n line-height: 1.4;\n text-align: center;\n}\n\n.mj-flow-connection-label-icon {\n font-size: 10px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Foblex Overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Canvas \u2014 transparent so the grid pattern shows through */\nf-canvas {\n background: transparent;\n}\n\n/* Selection area */\nf-selection-area {\n --f-selection-area-background: rgba(59, 130, 246, 0.08);\n --f-selection-area-border: 1px solid rgba(59, 130, 246, 0.3);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: #3b82f6;\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: #94a3b8;\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: 3;\n}\n\n.f-connection.f-selected path {\n stroke: #3b82f6;\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, #94a3b8);\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, #3b82f6);\n stroke-width: 3;\n filter: brightness(0.85);\n}\n\n/* Dashed connection style */\n.mj-flow-connection-dashed path {\n stroke-dasharray: 8 4;\n}\n\n/* Dotted connection style */\n.mj-flow-connection-dotted path {\n stroke-dasharray: 3 3;\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: #94a3b8;\n stroke-dasharray: 6 3;\n stroke-width: 2;\n}\n\n/* \u2500\u2500 Legend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-legend {\n position: absolute;\n bottom: 12px;\n left: 12px;\n background: rgba(255, 255, 255, 0.92);\n backdrop-filter: blur(6px);\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 10px 14px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n z-index: 15;\n font-size: 11px;\n color: #475569;\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: #1e293b;\n margin-bottom: 8px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-flow-editor-legend-section {\n margin-bottom: 8px;\n}\n\n.mj-flow-editor-legend-section:last-child {\n margin-bottom: 0;\n}\n\n.mj-flow-editor-legend-heading {\n font-size: 10px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 4px;\n}\n\n.mj-flow-editor-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 0;\n font-size: 11px;\n color: #475569;\n}\n\n.mj-flow-editor-legend-svg,\n.mj-flow-editor-legend-line-svg {\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-swatch {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-box {\n display: inline-block;\n width: 14px;\n height: 10px;\n border: 2px solid;\n border-radius: 3px;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Context Menu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-context-menu {\n position: fixed;\n min-width: 180px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.06);\n padding: 4px;\n z-index: 9999;\n animation: mj-ctx-fade-in 0.1s ease-out;\n}\n\n@keyframes mj-ctx-fade-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n}\n\n.mj-flow-context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 8px 12px;\n border: none;\n background: none;\n font-size: 13px;\n font-weight: 450;\n color: #334155;\n cursor: pointer;\n border-radius: 5px;\n text-align: left;\n transition: background-color 0.1s ease;\n}\n\n.mj-flow-context-menu-item:hover {\n background-color: #f1f5f9;\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: 12px;\n color: #64748b;\n}\n\n.mj-flow-context-menu-item--danger {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: #fef2f2;\n}\n\n.mj-flow-context-menu-item--danger i {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: #e2e8f0;\n margin: 4px 8px;\n}\n\n/* \u2500\u2500 Focus Indicators (Accessibility) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-toolbar-btn:focus-visible,\n.mj-agent-editor-btn:focus-visible,\n.mj-agent-editor-view-btn:focus-visible,\n.mj-agent-props-close:focus-visible,\n.mj-agent-props-delete-btn:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 1px;\n border-radius: 6px;\n}\n"], encapsulation: 2 });
|
|
1005
|
+
} }, dependencies: [i3.NgClass, i4.FFlowComponent, i4.FCanvasComponent, i4.FBackgroundComponent, i4.FCirclePatternComponent, i4.FZoomDirective, i4.FConnectionContent, i4.FConnectionMarker, i4.FConnectionComponent, i4.FConnectionForCreateComponent, i4.FLineAlignmentComponent, i4.FMinimapComponent, i4.FNodeDirective, i4.FDragHandleDirective, i4.FSelectionAreaComponent, i4.FDragBlockerDirective, i4.FDraggableDirective, i5.FlowNodeComponent, i6.FlowPaletteComponent, i7.FlowToolbarComponent, i8.FlowStatusBarComponent], styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n.mj-flow-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: 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: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: #93c5fd;\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: #e2e8f0;\n stroke: #94a3b8;\n stroke-width: 0.5;\n rx: 2;\n ry: 2;\n}\n\n/* Viewport indicator rectangle */\n.mj-flow-editor-minimap .f-minimap-view {\n fill: rgba(59, 130, 246, 0.08);\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-empty {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 5;\n}\n\n.mj-flow-editor-empty-content {\n text-align: center;\n padding: 40px;\n}\n\n.mj-flow-editor-empty-icon {\n font-size: 48px;\n color: var(--mj-border-strong);\n margin-bottom: 16px;\n}\n\n.mj-flow-editor-empty-title {\n font-size: var(--mj-text-base);\n font-weight: 500;\n color: var(--mj-text-muted);\n margin: 0 0 8px;\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: #fffef5;\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: rgba(59, 130, 246, 0.08);\n --f-selection-area-border: 1px solid rgba(59, 130, 246, 0.3);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: #3b82f6;\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: #94a3b8;\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: 3;\n}\n\n.f-connection.f-selected path {\n stroke: #3b82f6;\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, #94a3b8);\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, #3b82f6);\n stroke-width: 3;\n filter: brightness(0.85);\n}\n\n/* Dashed connection style */\n.mj-flow-connection-dashed path {\n stroke-dasharray: 8 4;\n}\n\n/* Dotted connection style */\n.mj-flow-connection-dotted path {\n stroke-dasharray: 3 3;\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: #94a3b8;\n stroke-dasharray: 6 3;\n stroke-width: 2;\n}\n\n/* \u2500\u2500 Legend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-legend {\n position: absolute;\n bottom: 12px;\n left: 12px;\n background: rgba(255, 255, 255, 0.92);\n backdrop-filter: blur(6px);\n border: 1px solid 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 });
|
|
1006
1006
|
}
|
|
1007
1007
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowEditorComponent, [{
|
|
1008
1008
|
type: Component,
|
|
1009
|
-
args: [{ standalone: false, selector: 'mj-flow-editor', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, providers: [FlowStateService, FlowLayoutService], template: "<div class=\"mj-flow-editor\" [class.mj-flow-editor--readonly]=\"ReadOnly\" tabindex=\"0\">\n\n <!-- Canvas Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-flow-editor-toolbar-area\">\n <mj-flow-toolbar\n [ZoomLevel]=\"zoomLevel\"\n [ShowGrid]=\"ShowGrid\"\n [ShowMinimap]=\"ShowMinimap\"\n [ShowLegend]=\"ShowLegend\"\n [ReadOnly]=\"ReadOnly\"\n [PanMode]=\"panMode\"\n (ZoomInClicked)=\"ZoomIn()\"\n (ZoomOutClicked)=\"ZoomOut()\"\n (FitToScreenClicked)=\"ZoomToFit()\"\n (AutoLayoutClicked)=\"AutoArrange()\"\n (GridToggled)=\"onGridToggled($event)\"\n (MinimapToggled)=\"onMinimapToggled($event)\"\n (LegendToggled)=\"onLegendToggled($event)\"\n (PanModeToggled)=\"onPanModeToggled($event)\">\n </mj-flow-toolbar>\n </div>\n }\n\n <!-- Main Canvas Area -->\n <div class=\"mj-flow-editor-canvas-area\">\n\n <!-- Palette Sidebar -->\n @if (ShowPalette && !ReadOnly) {\n <mj-flow-palette\n [NodeTypes]=\"NodeTypes\">\n </mj-flow-palette>\n }\n\n <!-- Foblex Canvas -->\n <div class=\"mj-flow-editor-canvas\"\n [class.mj-flow-editor-canvas--pan-mode]=\"panMode\">\n <f-flow fDraggable\n [fDraggableDisabled]=\"false\"\n [fCanvasMoveTrigger]=\"canvasMoveTriggerFn\"\n [vCellSize]=\"GridSize\"\n [hCellSize]=\"GridSize\"\n [fCellSizeWhileDragging]=\"ShowGrid\"\n (fLoaded)=\"onFlowLoaded()\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n (fMoveNodes)=\"onNodesMoved($event)\">\n\n <!-- Background grid \u2014 MUST be direct child of f-flow (Foblex content projection requires it) -->\n @if (ShowGrid) {\n <f-background>\n <f-circle-pattern [color]=\"'#94a3b8'\" [radius]=\"24\"></f-circle-pattern>\n </f-background>\n }\n\n <!-- Alignment snap lines \u2014 direct child of f-flow -->\n @if (!ReadOnly) {\n <f-line-alignment [fAlignThreshold]=\"10\"></f-line-alignment>\n }\n\n <f-canvas fZoom #fZoomRef=\"fComponent\"\n [fZoomMinimum]=\"0.15\"\n [fZoomMaximum]=\"3\"\n [fZoomStep]=\"0.1\"\n [position]=\"canvasPosition\"\n [scale]=\"canvasScale\"\n (fCanvasChange)=\"onCanvasChange($event)\">\n\n <!-- Interactive connection drawing -->\n @if (!ReadOnly) {\n <f-connection-for-create\n fType=\"bezier\"\n [fRadius]=\"8\"\n [fOffset]=\"12\">\n <svg viewBox=\"0 0 10 10\" fMarker type=\"end\"\n orient=\"auto\" [refX]=\"9\" [refY]=\"5\" [height]=\"10\" [width]=\"10\">\n <path d=\"M0 0L10 5L0 10Z\" fill=\"#94a3b8\"/>\n </svg>\n </f-connection-for-create>\n }\n\n <!-- Connections -->\n @for (conn of Connections; track trackConnectionById($index, conn)) {\n <f-connection\n [fConnectionId]=\"conn.ID\"\n [fOutputId]=\"conn.SourcePortID\"\n [fInputId]=\"conn.TargetPortID\"\n fType=\"bezier\"\n fBehavior=\"fixed\"\n [fRadius]=\"8\"\n [fOffset]=\"12\"\n [fSelectionDisabled]=\"false\"\n [fReassignDisabled]=\"ReadOnly\"\n [style.--connection-color]=\"conn.Color || '#94a3b8'\"\n [class.mj-flow-connection-colored]=\"!!conn.Color\"\n [class.mj-flow-connection-dashed]=\"conn.Style === 'dashed'\"\n [class.mj-flow-connection-dotted]=\"conn.Style === 'dotted'\"\n (click)=\"onConnectionClicked($event, conn)\"\n (contextmenu)=\"onConnectionContextMenu($event, conn)\">\n <!-- Arrow marker -->\n <svg viewBox=\"0 0 10 10\" fMarker type=\"end\"\n orient=\"auto\" [refX]=\"9\" [refY]=\"5\" [height]=\"10\" [width]=\"10\">\n <path d=\"M0 0L10 5L0 10Z\" [attr.fill]=\"conn.Color || '#94a3b8'\"/>\n </svg>\n <!-- Connection label -->\n @if (conn.Label) {\n <div fConnectionContent [position]=\"0.5\"\n class=\"mj-flow-connection-label\"\n [style.backgroundColor]=\"ConnectionLabelBackground\"\n [style.borderColor]=\"ConnectionLabelBorderColor\"\n [style.color]=\"ConnectionLabelTextColor\"\n [title]=\"conn.LabelDetail || conn.Label || ''\">\n @if (conn.LabelIcon) {\n <i\n class=\"fa-solid mj-flow-connection-label-icon\"\n [ngClass]=\"conn.LabelIcon\"\n [style.color]=\"conn.LabelIconColor || 'inherit'\"></i>\n }\n {{ conn.Label }}\n </div>\n }\n </f-connection>\n }\n\n <!-- Nodes -->\n @for (node of Nodes; track trackNodeById($index, node)) {\n <mj-flow-node\n fNode\n fDragHandle\n [fNodeId]=\"node.ID\"\n [fNodePosition]=\"{ x: node.Position.X, y: node.Position.Y }\"\n [fNodeDraggingDisabled]=\"ReadOnly || panMode || node.Locked === true\"\n [fNodeSelectionDisabled]=\"panMode\"\n [fConnectOnNode]=\"!ReadOnly && !panMode\"\n [Node]=\"node\"\n [TypeConfig]=\"GetTypeConfig(node.Type)\"\n [ReadOnly]=\"ReadOnly\"\n (contextmenu)=\"onNodeContextMenu($event, node)\">\n </mj-flow-node>\n }\n\n </f-canvas>\n\n <!-- Rubber-band selection \u2014 direct child of f-flow -->\n @if (!ReadOnly) {\n <f-selection-area></f-selection-area>\n }\n\n <!-- Minimap -->\n @if (ShowMinimap) {\n <div class=\"mj-flow-editor-minimap\" fDragBlocker>\n <f-minimap [fMinSize]=\"1500\"></f-minimap>\n </div>\n }\n </f-flow>\n\n <!-- Legend -->\n @if (ShowLegend && Nodes.length > 0) {\n <div class=\"mj-flow-editor-legend\">\n <div class=\"mj-flow-editor-legend-title\">Legend</div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Connections</div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#64748b\" stroke-width=\"2\"/></svg>\n <span>Only exit path</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#16a34a\" stroke-width=\"2\"/></svg>\n <i class=\"fa-solid fa-circle-check\" style=\"color:#16a34a;font-size:10px;\"></i>\n <span>Default (fallback)</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#f59e0b\" stroke-width=\"2\" stroke-dasharray=\"5 3\"/></svg>\n <span>Conditional</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#ef4444\" stroke-width=\"2\"/></svg>\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:#ef4444;font-size:10px;\"></i>\n <span>Duplicate defaults</span>\n </div>\n </div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Step Types</div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#3B82F6\"></span>\n <span>Action</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#8B5CF6\"></span>\n <span>Prompt</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#10B981\"></span>\n <span>Sub-Agent</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#F59E0B\"></span>\n <span>For Each</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#F97316\"></span>\n <span>While</span>\n </div>\n </div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Borders</div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-box\" style=\"border-color:#16a34a;background:#f0fdf4;\"></span>\n <span>Start node</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-box\" style=\"border-color:#ef4444;background:#fef2f2;\"></span>\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:#ea580c;font-size:10px;\"></i>\n <span>Missing config</span>\n </div>\n </div>\n </div>\n }\n\n <!-- Empty state overlay -->\n @if (Nodes.length === 0) {\n <div class=\"mj-flow-editor-empty\">\n <div class=\"mj-flow-editor-empty-content\">\n <i class=\"fa-solid fa-diagram-project mj-flow-editor-empty-icon\"></i>\n @if (!ReadOnly) {\n <p class=\"mj-flow-editor-empty-title\">Drag steps from the palette to start building your flow</p>\n }\n @if (ReadOnly) {\n <p class=\"mj-flow-editor-empty-title\">No steps defined for this flow</p>\n }\n @if (!ReadOnly) {\n <p class=\"mj-flow-editor-empty-subtitle\">\n Or use auto-layout after adding steps to arrange them automatically\n </p>\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Context Menu -->\n @if (contextMenuVisible) {\n <div\n class=\"mj-flow-context-menu\"\n [style.left.px]=\"contextMenuX\"\n [style.top.px]=\"contextMenuY\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"mj-flow-context-menu-item\" (click)=\"onContextMenuAction('edit')\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n <span>Edit {{ contextMenuTargetType === 'node' ? 'Node' : 'Connection' }}</span>\n </button>\n <div class=\"mj-flow-context-menu-divider\"></div>\n <button class=\"mj-flow-context-menu-item mj-flow-context-menu-item--danger\" (click)=\"onContextMenuAction('remove')\">\n <i class=\"fa-solid fa-trash-can\"></i>\n <span>Remove {{ contextMenuTargetType === 'node' ? 'Node' : 'Connection' }}</span>\n </button>\n </div>\n }\n\n <!-- Status Bar -->\n @if (ShowStatusBar) {\n <mj-flow-status-bar\n [NodeCount]=\"Nodes.length\"\n [ConnectionCount]=\"Connections.length\"\n [SelectedCount]=\"selectedCount\"\n [ZoomLevel]=\"zoomLevel\">\n </mj-flow-status-bar>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n.mj-flow-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n overflow: hidden;\n outline: none;\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n}\n\n.mj-flow-editor--readonly .mj-flow-node-port {\n cursor: default;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Toolbar Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-toolbar-area {\n position: absolute;\n top: 10px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n}\n\n/* \u2500\u2500 Canvas Area \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-canvas-area {\n display: flex;\n flex: 1;\n overflow: hidden;\n position: relative;\n}\n\n.mj-flow-editor-canvas {\n flex: 1;\n position: relative;\n overflow: hidden;\n background: #ffffff;\n}\n\n.mj-flow-editor-canvas f-flow {\n width: 100%;\n height: 100%;\n}\n\n/* Pan mode \u2014 grab cursor, nodes pass through pointer events to canvas */\n.mj-flow-editor-canvas--pan-mode {\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode:active {\n cursor: grabbing;\n}\n\n.mj-flow-editor-canvas--pan-mode mj-flow-node {\n pointer-events: none;\n cursor: grab;\n}\n\n.mj-flow-editor-canvas--pan-mode f-connection {\n pointer-events: none;\n}\n\n.mj-flow-editor-canvas--pan-mode .mj-flow-connection-label {\n pointer-events: none;\n}\n\n/* \u2500\u2500 Minimap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-minimap {\n position: absolute;\n bottom: 12px;\n right: 12px;\n width: 160px;\n height: 120px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n overflow: hidden;\n z-index: 15;\n}\n\n.mj-flow-editor-minimap f-minimap {\n width: 100%;\n height: 100%;\n}\n\n/* Foblex minimap SVG node rectangles (no default styles provided by Foblex) */\n.mj-flow-editor-minimap .f-minimap-node {\n fill: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: #93c5fd;\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: #e2e8f0;\n stroke: #94a3b8;\n stroke-width: 0.5;\n rx: 2;\n ry: 2;\n}\n\n/* Viewport indicator rectangle */\n.mj-flow-editor-minimap .f-minimap-view {\n fill: rgba(59, 130, 246, 0.08);\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-empty {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 5;\n}\n\n.mj-flow-editor-empty-content {\n text-align: center;\n padding: 40px;\n}\n\n.mj-flow-editor-empty-icon {\n font-size: 48px;\n color: #cbd5e1;\n margin-bottom: 16px;\n}\n\n.mj-flow-editor-empty-title {\n font-size: 15px;\n font-weight: 500;\n color: #64748b;\n margin: 0 0 8px;\n}\n\n.mj-flow-editor-empty-subtitle {\n font-size: 13px;\n color: #94a3b8;\n margin: 0;\n}\n\n/* \u2500\u2500 Connection Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-connection-label {\n display: inline-flex;\n align-items: flex-start;\n gap: 5px;\n background: #fffef5;\n border: 1.5px solid #cbd5e1;\n border-radius: 5px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n color: #334155;\n white-space: nowrap;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n pointer-events: none;\n line-height: 1.4;\n text-align: center;\n}\n\n.mj-flow-connection-label-icon {\n font-size: 10px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Foblex Overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Canvas \u2014 transparent so the grid pattern shows through */\nf-canvas {\n background: transparent;\n}\n\n/* Selection area */\nf-selection-area {\n --f-selection-area-background: rgba(59, 130, 246, 0.08);\n --f-selection-area-border: 1px solid rgba(59, 130, 246, 0.3);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: #3b82f6;\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: #94a3b8;\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: 3;\n}\n\n.f-connection.f-selected path {\n stroke: #3b82f6;\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, #94a3b8);\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, #3b82f6);\n stroke-width: 3;\n filter: brightness(0.85);\n}\n\n/* Dashed connection style */\n.mj-flow-connection-dashed path {\n stroke-dasharray: 8 4;\n}\n\n/* Dotted connection style */\n.mj-flow-connection-dotted path {\n stroke-dasharray: 3 3;\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: #94a3b8;\n stroke-dasharray: 6 3;\n stroke-width: 2;\n}\n\n/* \u2500\u2500 Legend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-legend {\n position: absolute;\n bottom: 12px;\n left: 12px;\n background: rgba(255, 255, 255, 0.92);\n backdrop-filter: blur(6px);\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 10px 14px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n z-index: 15;\n font-size: 11px;\n color: #475569;\n max-width: 200px;\n}\n\n.mj-flow-editor-legend-title {\n font-size: 11px;\n font-weight: 600;\n color: #1e293b;\n margin-bottom: 8px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.mj-flow-editor-legend-section {\n margin-bottom: 8px;\n}\n\n.mj-flow-editor-legend-section:last-child {\n margin-bottom: 0;\n}\n\n.mj-flow-editor-legend-heading {\n font-size: 10px;\n font-weight: 600;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 4px;\n}\n\n.mj-flow-editor-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 0;\n font-size: 11px;\n color: #475569;\n}\n\n.mj-flow-editor-legend-svg,\n.mj-flow-editor-legend-line-svg {\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-swatch {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n flex-shrink: 0;\n}\n\n.mj-flow-editor-legend-box {\n display: inline-block;\n width: 14px;\n height: 10px;\n border: 2px solid;\n border-radius: 3px;\n flex-shrink: 0;\n}\n\n/* \u2500\u2500 Context Menu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-context-menu {\n position: fixed;\n min-width: 180px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.06);\n padding: 4px;\n z-index: 9999;\n animation: mj-ctx-fade-in 0.1s ease-out;\n}\n\n@keyframes mj-ctx-fade-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n}\n\n.mj-flow-context-menu-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 8px 12px;\n border: none;\n background: none;\n font-size: 13px;\n font-weight: 450;\n color: #334155;\n cursor: pointer;\n border-radius: 5px;\n text-align: left;\n transition: background-color 0.1s ease;\n}\n\n.mj-flow-context-menu-item:hover {\n background-color: #f1f5f9;\n}\n\n.mj-flow-context-menu-item i {\n width: 16px;\n text-align: center;\n font-size: 12px;\n color: #64748b;\n}\n\n.mj-flow-context-menu-item--danger {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-item--danger:hover {\n background-color: #fef2f2;\n}\n\n.mj-flow-context-menu-item--danger i {\n color: #dc2626;\n}\n\n.mj-flow-context-menu-divider {\n height: 1px;\n background: #e2e8f0;\n margin: 4px 8px;\n}\n\n/* \u2500\u2500 Focus Indicators (Accessibility) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-toolbar-btn:focus-visible,\n.mj-agent-editor-btn:focus-visible,\n.mj-agent-editor-view-btn:focus-visible,\n.mj-agent-props-close:focus-visible,\n.mj-agent-props-delete-btn:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n}\n\n.mj-flow-palette-item:focus-visible {\n outline: 2px solid #3b82f6;\n outline-offset: 1px;\n border-radius: 6px;\n}\n"] }]
|
|
1009
|
+
args: [{ standalone: false, selector: 'mj-flow-editor', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, providers: [FlowStateService, FlowLayoutService], template: "<div class=\"mj-flow-editor\" [class.mj-flow-editor--readonly]=\"ReadOnly\" tabindex=\"0\">\n\n <!-- Canvas Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-flow-editor-toolbar-area\">\n <mj-flow-toolbar\n [ZoomLevel]=\"zoomLevel\"\n [ShowGrid]=\"ShowGrid\"\n [ShowMinimap]=\"ShowMinimap\"\n [ShowLegend]=\"ShowLegend\"\n [ReadOnly]=\"ReadOnly\"\n [PanMode]=\"panMode\"\n (ZoomInClicked)=\"ZoomIn()\"\n (ZoomOutClicked)=\"ZoomOut()\"\n (FitToScreenClicked)=\"ZoomToFit()\"\n (AutoLayoutClicked)=\"AutoArrange()\"\n (GridToggled)=\"onGridToggled($event)\"\n (MinimapToggled)=\"onMinimapToggled($event)\"\n (LegendToggled)=\"onLegendToggled($event)\"\n (PanModeToggled)=\"onPanModeToggled($event)\">\n </mj-flow-toolbar>\n </div>\n }\n\n <!-- Main Canvas Area -->\n <div class=\"mj-flow-editor-canvas-area\">\n\n <!-- Palette Sidebar -->\n @if (ShowPalette && !ReadOnly) {\n <mj-flow-palette\n [NodeTypes]=\"NodeTypes\">\n </mj-flow-palette>\n }\n\n <!-- Foblex Canvas -->\n <div class=\"mj-flow-editor-canvas\"\n [class.mj-flow-editor-canvas--pan-mode]=\"panMode\">\n <f-flow fDraggable\n [fDraggableDisabled]=\"false\"\n [fCanvasMoveTrigger]=\"canvasMoveTriggerFn\"\n [vCellSize]=\"GridSize\"\n [hCellSize]=\"GridSize\"\n [fCellSizeWhileDragging]=\"ShowGrid\"\n (fLoaded)=\"onFlowLoaded()\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n (fMoveNodes)=\"onNodesMoved($event)\">\n\n <!-- Background grid \u2014 MUST be direct child of f-flow (Foblex content projection requires it) -->\n @if (ShowGrid) {\n <f-background>\n <f-circle-pattern [color]=\"'#94a3b8'\" [radius]=\"24\"></f-circle-pattern>\n </f-background>\n }\n\n <!-- Alignment snap lines \u2014 direct child of f-flow -->\n @if (!ReadOnly) {\n <f-line-alignment [fAlignThreshold]=\"10\"></f-line-alignment>\n }\n\n <f-canvas fZoom #fZoomRef=\"fComponent\"\n [fZoomMinimum]=\"0.15\"\n [fZoomMaximum]=\"3\"\n [fZoomStep]=\"0.1\"\n [position]=\"canvasPosition\"\n [scale]=\"canvasScale\"\n (fCanvasChange)=\"onCanvasChange($event)\">\n\n <!-- Interactive connection drawing -->\n @if (!ReadOnly) {\n <f-connection-for-create\n fType=\"bezier\"\n [fRadius]=\"8\"\n [fOffset]=\"12\">\n <svg viewBox=\"0 0 10 10\" fMarker type=\"end\"\n orient=\"auto\" [refX]=\"9\" [refY]=\"5\" [height]=\"10\" [width]=\"10\">\n <path d=\"M0 0L10 5L0 10Z\" fill=\"#94a3b8\"/>\n </svg>\n </f-connection-for-create>\n }\n\n <!-- Connections -->\n @for (conn of Connections; track trackConnectionById($index, conn)) {\n <f-connection\n [fConnectionId]=\"conn.ID\"\n [fOutputId]=\"conn.SourcePortID\"\n [fInputId]=\"conn.TargetPortID\"\n fType=\"bezier\"\n fBehavior=\"fixed\"\n [fRadius]=\"8\"\n [fOffset]=\"12\"\n [fSelectionDisabled]=\"false\"\n [fReassignDisabled]=\"ReadOnly\"\n [style.--connection-color]=\"conn.Color || '#94a3b8'\"\n [class.mj-flow-connection-colored]=\"!!conn.Color\"\n [class.mj-flow-connection-dashed]=\"conn.Style === 'dashed'\"\n [class.mj-flow-connection-dotted]=\"conn.Style === 'dotted'\"\n (click)=\"onConnectionClicked($event, conn)\"\n (contextmenu)=\"onConnectionContextMenu($event, conn)\">\n <!-- Arrow marker -->\n <svg viewBox=\"0 0 10 10\" fMarker type=\"end\"\n orient=\"auto\" [refX]=\"9\" [refY]=\"5\" [height]=\"10\" [width]=\"10\">\n <path d=\"M0 0L10 5L0 10Z\" [attr.fill]=\"conn.Color || '#94a3b8'\"/>\n </svg>\n <!-- Connection label -->\n @if (conn.Label) {\n <div fConnectionContent [position]=\"0.5\"\n class=\"mj-flow-connection-label\"\n [style.backgroundColor]=\"ConnectionLabelBackground\"\n [style.borderColor]=\"ConnectionLabelBorderColor\"\n [style.color]=\"ConnectionLabelTextColor\"\n [title]=\"conn.LabelDetail || conn.Label || ''\">\n @if (conn.LabelIcon) {\n <i\n class=\"fa-solid mj-flow-connection-label-icon\"\n [ngClass]=\"conn.LabelIcon\"\n [style.color]=\"conn.LabelIconColor || 'inherit'\"></i>\n }\n {{ conn.Label }}\n </div>\n }\n </f-connection>\n }\n\n <!-- Nodes -->\n @for (node of Nodes; track trackNodeById($index, node)) {\n <mj-flow-node\n fNode\n fDragHandle\n [fNodeId]=\"node.ID\"\n [fNodePosition]=\"{ x: node.Position.X, y: node.Position.Y }\"\n [fNodeDraggingDisabled]=\"ReadOnly || panMode || node.Locked === true\"\n [fNodeSelectionDisabled]=\"panMode\"\n [fConnectOnNode]=\"!ReadOnly && !panMode\"\n [Node]=\"node\"\n [TypeConfig]=\"GetTypeConfig(node.Type)\"\n [ReadOnly]=\"ReadOnly\"\n (contextmenu)=\"onNodeContextMenu($event, node)\">\n </mj-flow-node>\n }\n\n </f-canvas>\n\n <!-- Rubber-band selection \u2014 direct child of f-flow -->\n @if (!ReadOnly) {\n <f-selection-area></f-selection-area>\n }\n\n <!-- Minimap -->\n @if (ShowMinimap) {\n <div class=\"mj-flow-editor-minimap\" fDragBlocker>\n <f-minimap [fMinSize]=\"1500\"></f-minimap>\n </div>\n }\n </f-flow>\n\n <!-- Legend -->\n @if (ShowLegend && Nodes.length > 0) {\n <div class=\"mj-flow-editor-legend\">\n <div class=\"mj-flow-editor-legend-title\">Legend</div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Connections</div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#64748b\" stroke-width=\"2\"/></svg>\n <span>Only exit path</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#16a34a\" stroke-width=\"2\"/></svg>\n <i class=\"fa-solid fa-circle-check\" style=\"color:#16a34a;font-size:10px;\"></i>\n <span>Default (fallback)</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#f59e0b\" stroke-width=\"2\" stroke-dasharray=\"5 3\"/></svg>\n <span>Conditional</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <svg class=\"mj-flow-editor-legend-line-svg\" width=\"28\" height=\"10\"><line x1=\"0\" y1=\"5\" x2=\"28\" y2=\"5\" stroke=\"#ef4444\" stroke-width=\"2\"/></svg>\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:#ef4444;font-size:10px;\"></i>\n <span>Duplicate defaults</span>\n </div>\n </div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Step Types</div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#3B82F6\"></span>\n <span>Action</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#8B5CF6\"></span>\n <span>Prompt</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#10B981\"></span>\n <span>Sub-Agent</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#F59E0B\"></span>\n <span>For Each</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-swatch\" style=\"background:#F97316\"></span>\n <span>While</span>\n </div>\n </div>\n <div class=\"mj-flow-editor-legend-section\">\n <div class=\"mj-flow-editor-legend-heading\">Borders</div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-box\" style=\"border-color:#16a34a;background:#f0fdf4;\"></span>\n <span>Start node</span>\n </div>\n <div class=\"mj-flow-editor-legend-item\">\n <span class=\"mj-flow-editor-legend-box\" style=\"border-color:#ef4444;background:#fef2f2;\"></span>\n <i class=\"fa-solid fa-triangle-exclamation\" style=\"color:#ea580c;font-size:10px;\"></i>\n <span>Missing config</span>\n </div>\n </div>\n </div>\n }\n\n <!-- Empty state overlay -->\n @if (Nodes.length === 0) {\n <div class=\"mj-flow-editor-empty\">\n <div class=\"mj-flow-editor-empty-content\">\n <i class=\"fa-solid fa-diagram-project mj-flow-editor-empty-icon\"></i>\n @if (!ReadOnly) {\n <p class=\"mj-flow-editor-empty-title\">Drag steps from the palette to start building your flow</p>\n }\n @if (ReadOnly) {\n <p class=\"mj-flow-editor-empty-title\">No steps defined for this flow</p>\n }\n @if (!ReadOnly) {\n <p class=\"mj-flow-editor-empty-subtitle\">\n Or use auto-layout after adding steps to arrange them automatically\n </p>\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Context Menu -->\n @if (contextMenuVisible) {\n <div\n class=\"mj-flow-context-menu\"\n [style.left.px]=\"contextMenuX\"\n [style.top.px]=\"contextMenuY\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"mj-flow-context-menu-item\" (click)=\"onContextMenuAction('edit')\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n <span>Edit {{ contextMenuTargetType === 'node' ? 'Node' : 'Connection' }}</span>\n </button>\n <div class=\"mj-flow-context-menu-divider\"></div>\n <button class=\"mj-flow-context-menu-item mj-flow-context-menu-item--danger\" (click)=\"onContextMenuAction('remove')\">\n <i class=\"fa-solid fa-trash-can\"></i>\n <span>Remove {{ contextMenuTargetType === 'node' ? 'Node' : 'Connection' }}</span>\n </button>\n </div>\n }\n\n <!-- Status Bar -->\n @if (ShowStatusBar) {\n <mj-flow-status-bar\n [NodeCount]=\"Nodes.length\"\n [ConnectionCount]=\"Connections.length\"\n [SelectedCount]=\"selectedCount\"\n [ZoomLevel]=\"zoomLevel\">\n </mj-flow-status-bar>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Editor \u2014 Main container styles\n ============================================================ */\n\n.mj-flow-editor {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 400px;\n background: 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: #cbd5e1;\n stroke: #94a3b8;\n stroke-width: 1;\n rx: 3;\n ry: 3;\n}\n\n.mj-flow-editor-minimap .f-minimap-node.f-selected {\n fill: #93c5fd;\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n.mj-flow-editor-minimap .f-minimap-group {\n fill: #e2e8f0;\n stroke: #94a3b8;\n stroke-width: 0.5;\n rx: 2;\n ry: 2;\n}\n\n/* Viewport indicator rectangle */\n.mj-flow-editor-minimap .f-minimap-view {\n fill: rgba(59, 130, 246, 0.08);\n stroke: #3b82f6;\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Empty State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-empty {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 5;\n}\n\n.mj-flow-editor-empty-content {\n text-align: center;\n padding: 40px;\n}\n\n.mj-flow-editor-empty-icon {\n font-size: 48px;\n color: var(--mj-border-strong);\n margin-bottom: 16px;\n}\n\n.mj-flow-editor-empty-title {\n font-size: var(--mj-text-base);\n font-weight: 500;\n color: var(--mj-text-muted);\n margin: 0 0 8px;\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: #fffef5;\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: rgba(59, 130, 246, 0.08);\n --f-selection-area-border: 1px solid rgba(59, 130, 246, 0.3);\n}\n\n/* Line alignment guides */\nf-line-alignment {\n --f-line-alignment-color: #3b82f6;\n}\n\n/* Connection paths \u2014 default stroke color prevents invisible connections */\n.f-connection path {\n stroke: #94a3b8;\n stroke-width: 2;\n fill: none;\n transition: stroke 0.15s ease, stroke-width 0.15s ease;\n}\n\n.f-connection:hover path {\n stroke-width: 3;\n}\n\n.f-connection.f-selected path {\n stroke: #3b82f6;\n stroke-width: 3;\n}\n\n/* Colored connections use the --connection-color custom property */\n.mj-flow-connection-colored path {\n stroke: var(--connection-color, #94a3b8);\n}\n\n.mj-flow-connection-colored.f-selected path {\n stroke: var(--connection-color, #3b82f6);\n stroke-width: 3;\n filter: brightness(0.85);\n}\n\n/* Dashed connection style */\n.mj-flow-connection-dashed path {\n stroke-dasharray: 8 4;\n}\n\n/* Dotted connection style */\n.mj-flow-connection-dotted path {\n stroke-dasharray: 3 3;\n}\n\n/* Connection for create (dragging) */\n.f-connection-for-create path {\n stroke: #94a3b8;\n stroke-dasharray: 6 3;\n stroke-width: 2;\n}\n\n/* \u2500\u2500 Legend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mj-flow-editor-legend {\n position: absolute;\n bottom: 12px;\n left: 12px;\n background: rgba(255, 255, 255, 0.92);\n backdrop-filter: blur(6px);\n border: 1px solid 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"] }]
|
|
1010
1010
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.FlowStateService }, { type: i2.FlowLayoutService }], { Nodes: [{
|
|
1011
1011
|
type: Input
|
|
1012
1012
|
}], Connections: [{
|
|
@@ -340,11 +340,11 @@ export class FlowNodeComponent {
|
|
|
340
340
|
i0.ɵɵconditional(ctx.Node.Badges && ctx.Node.Badges.length > 0 ? 17 : -1);
|
|
341
341
|
i0.ɵɵadvance();
|
|
342
342
|
i0.ɵɵconditional(ctx.outputPort ? 18 : -1);
|
|
343
|
-
} }, dependencies: [i1.NgClass, i2.FNodeInputDirective, i2.FNodeOutputDirective], styles: ["/* ============================================================\n Flow Node \u2014 Visual design for individual flow graph nodes\n ============================================================ */\n\nmj-flow-node {\n display: block;\n position: relative;\n cursor: default;\n transition: filter 0.15s ease, box-shadow 0.15s ease;\n}\n\n.mj-flow-node-body {\n position: relative;\n min-width: 200px;\n max-width: 280px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 10px;\n overflow: hidden;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);\n transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;\n}\n\n.mj-flow-node-body:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);\n border-color: #cbd5e1;\n}\n\n/* Selected state \u2014 applied by Foblex on the host fNode element */\n.f-selected .mj-flow-node-body {\n border-color: #3b82f6;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Status variants */\n.mj-flow-node-body--start {\n border: 2px solid #10b981;\n background: linear-gradient(135deg, #f0fdf4 0%, #ffffff 40%);\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12), 0 2px 8px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--start:hover {\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18), 0 4px 16px rgba(16, 185, 129, 0.2);\n}\n\n.f-selected .mj-flow-node-body--start {\n border-color: #10b981;\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.3), 0 4px 12px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--disabled {\n opacity: 0.5;\n filter: grayscale(0.5);\n}\n\n.mj-flow-node-body--pending {\n opacity: 0.7;\n border-style: dashed;\n}\n\n.mj-flow-node-body--running {\n border-color: #3b82f6;\n animation: mj-flow-node-pulse 1.5s ease-in-out infinite;\n}\n\n.mj-flow-node-body--warning {\n border: 2px solid #ea580c;\n background: linear-gradient(135deg, #fff7ed 0%, #ffffff 40%);\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.15), 0 2px 8px rgba(234, 88, 12, 0.1);\n}\n\n.mj-flow-node-body--warning:hover {\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.25), 0 4px 16px rgba(234, 88, 12, 0.15);\n}\n\n.f-selected .mj-flow-node-body--warning {\n border-color: #ea580c;\n box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.3), 0 4px 12px rgba(234, 88, 12, 0.15);\n}\n\n.mj-flow-node-body--error {\n border-color: #ef4444;\n background: #fef2f2;\n}\n\n.mj-flow-node-body--success {\n border-color: #10b981;\n}\n\n/* Color accent bar at top */\n.mj-flow-node-accent {\n height: 3px;\n width: 100%;\n}\n\n/* Header row */\n.mj-flow-node-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px 4px;\n gap: 8px;\n}\n\n.mj-flow-node-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n flex: 1;\n}\n\n.mj-flow-node-header-right {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.mj-flow-node-icon {\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.mj-flow-node-logo {\n width: 18px;\n height: 18px;\n border-radius: 3px;\n object-fit: contain;\n flex-shrink: 0;\n}\n\n.mj-flow-node-label {\n font-size: 13px;\n font-weight: 600;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.3;\n}\n\n.mj-flow-node-start-badge {\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.5px;\n color: #ffffff;\n background: linear-gradient(135deg, #10b981, #059669);\n border: none;\n border-radius: 4px;\n padding: 2px 6px;\n line-height: 1.4;\n box-shadow: 0 1px 3px rgba(16, 185, 129, 0.3);\n}\n\n/* Status indicator */\n.mj-flow-node-status {\n font-size: 12px;\n}\n\n.mj-flow-node-status--success { color: #10b981; }\n.mj-flow-node-status--error { color: #ef4444; }\n.mj-flow-node-status--warning { color: #ea580c; }\n.mj-flow-node-status--running { color: #3b82f6; }\n.mj-flow-node-status--disabled { color: #94a3b8; }\n.mj-flow-node-status--pending { color: #f59e0b; }\n\n/* Subtitle line */\n.mj-flow-node-subtitle {\n padding: 0 12px 2px;\n font-size: 12px;\n color: #475569;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.4;\n}\n\n/* Type badge */\n.mj-flow-node-type-badge {\n padding: 2px 12px 6px;\n font-size: 10px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n opacity: 0.7;\n}\n\n/* Warning banner \u2014 inline alert on misconfigured nodes */\n.mj-flow-node-warning-banner {\n display: flex;\n align-items: center;\n gap: 5px;\n margin: 2px 8px 6px;\n padding: 4px 8px;\n background: #fff7ed;\n border: 1px solid #fed7aa;\n border-radius: 5px;\n font-size: 10px;\n font-weight: 600;\n color: #c2410c;\n line-height: 1.3;\n}\n\n.mj-flow-node-warning-banner i {\n font-size: 10px;\n flex-shrink: 0;\n color: #ea580c;\n}\n\n.mj-flow-node-warning-banner span {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Badges row */\n.mj-flow-node-badges {\n display: flex;\n gap: 4px;\n padding: 0 10px 8px;\n flex-wrap: wrap;\n}\n\n.mj-flow-node-badge {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-size: 10px;\n color: #64748b;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n padding: 1px 6px;\n line-height: 1.4;\n}\n\n.mj-flow-node-badge i {\n font-size: 9px;\n}\n\n/* ============================================================\n Loop Node \u2014 distinctive container for ForEach/While\n ============================================================ */\n\n.mj-flow-node-body--loop {\n min-width: 230px;\n max-width: 300px;\n border-width: 2px;\n border-style: solid;\n border-image: none;\n background: linear-gradient(135deg, #fffbeb 0%, #ffffff 30%);\n}\n\n.mj-flow-node-body--loop:hover {\n box-shadow: 0 4px 14px rgba(245, 158, 11, 0.12), 0 2px 4px rgba(0, 0, 0, 0.06);\n}\n\n.f-selected .mj-flow-node-body--loop {\n border-color: #f59e0b;\n box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Loop section container */\n.mj-flow-loop-section {\n padding: 0 10px 8px;\n}\n\n/* Iteration summary (e.g., \"over items\" / \"while condition\") */\n.mj-flow-loop-iteration {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 3px 0 6px;\n font-size: 10px;\n font-weight: 500;\n color: #92400e;\n letter-spacing: 0.2px;\n}\n\n.mj-flow-loop-iteration-icon {\n font-size: 9px;\n color: #d97706;\n}\n\n.mj-flow-loop-max {\n color: #b45309;\n opacity: 0.7;\n}\n\n/* Inner body card \u2014 the \"iterated operation\" */\n.mj-flow-loop-body {\n border: 1.5px dashed #d1d5db;\n border-radius: 7px;\n background: #ffffff;\n transition: border-color 0.15s ease;\n}\n\n.mj-flow-loop-body:not(.mj-flow-loop-body--empty) {\n border-color: #e5e7eb;\n background: linear-gradient(180deg, #fafafa 0%, #ffffff 100%);\n}\n\n.mj-flow-loop-body-inner {\n padding: 6px 10px;\n}\n\n.mj-flow-loop-body-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 2px;\n}\n\n.mj-flow-loop-body-header i {\n font-size: 11px;\n}\n\n.mj-flow-loop-body-type {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.mj-flow-loop-body-name {\n font-size: 11px;\n font-weight: 500;\n color: #374151;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-left: 17px;\n line-height: 1.4;\n}\n\n.mj-flow-loop-body-name--empty {\n color: #9ca3af;\n font-style: italic;\n font-weight: 400;\n}\n\n/* Empty body card \u2014 no body type selected */\n.mj-flow-loop-body--empty {\n border-color: #fbbf24;\n border-style: dashed;\n background: #fffef5;\n}\n\n.mj-flow-loop-body-empty {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n font-size: 10px;\n font-weight: 500;\n color: #b45309;\n}\n\n.mj-flow-loop-body-empty i {\n font-size: 11px;\n color: #d97706;\n}\n\n/* Item variable badge */\n.mj-flow-loop-variable {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-top: 5px;\n font-size: 9px;\n font-weight: 500;\n color: #6b7280;\n background: #f3f4f6;\n border: 1px solid #e5e7eb;\n border-radius: 3px;\n padding: 1px 6px;\n}\n\n.mj-flow-loop-variable i {\n font-size: 8px;\n color: #9ca3af;\n}\n\n/* ============================================================\n Ports (Input/Output connectors)\n ============================================================ */\n\n.mj-flow-node-port {\n position: absolute;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #ffffff;\n border: 2px solid #94a3b8;\n z-index: 10;\n transition: border-color 0.15s ease, transform 0.15s ease, background-color 0.15s ease;\n cursor: crosshair;\n}\n\n.mj-flow-node-port:hover {\n border-color: #3b82f6;\n transform: scale(1.3);\n background: #eff6ff;\n}\n\n/* Position based on side */\n.mj-flow-node-port--top {\n top: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--top:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--bottom {\n bottom: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--bottom:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--left {\n left: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--left:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--right {\n right: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--right:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n/* Disabled port */\n.mj-flow-node-port.f-node-input-disabled,\n.mj-flow-node-port.f-node-output-disabled {\n opacity: 0;\n pointer-events: none;\n}\n\n/* ============================================================\n Animations\n ============================================================ */\n\n@keyframes mj-flow-node-pulse {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.3);\n }\n 50% {\n box-shadow: 0 0 0 6px rgba(59, 130, 246, 0);\n }\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
343
|
+
} }, dependencies: [i1.NgClass, i2.FNodeInputDirective, i2.FNodeOutputDirective], styles: ["/* ============================================================\n Flow Node \u2014 Visual design for individual flow graph nodes\n ============================================================ */\n\nmj-flow-node {\n display: block;\n position: relative;\n cursor: default;\n transition: filter 0.15s ease, box-shadow 0.15s ease;\n}\n\n.mj-flow-node-body {\n position: relative;\n min-width: 200px;\n max-width: 280px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n box-shadow: var(--mj-shadow-sm);\n transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;\n}\n\n.mj-flow-node-body:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-border-strong);\n}\n\n/* Selected state \u2014 applied by Foblex on the host fNode element */\n.f-selected .mj-flow-node-body {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Status variants */\n.mj-flow-node-body--start {\n border: 2px solid var(--mj-status-success);\n background: color-mix(in srgb, var(--mj-status-success) 5%, var(--mj-bg-surface));\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12), 0 2px 8px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--start:hover {\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18), 0 4px 16px rgba(16, 185, 129, 0.2);\n}\n\n.f-selected .mj-flow-node-body--start {\n border-color: var(--mj-status-success);\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.3), 0 4px 12px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--disabled {\n opacity: 0.5;\n filter: grayscale(0.5);\n}\n\n.mj-flow-node-body--pending {\n opacity: 0.7;\n border-style: dashed;\n}\n\n.mj-flow-node-body--running {\n border-color: var(--mj-brand-primary);\n animation: mj-flow-node-pulse 1.5s ease-in-out infinite;\n}\n\n.mj-flow-node-body--warning {\n border: 2px solid #ea580c;\n background: color-mix(in srgb, var(--mj-status-warning) 5%, var(--mj-bg-surface));\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.15), 0 2px 8px rgba(234, 88, 12, 0.1);\n}\n\n.mj-flow-node-body--warning:hover {\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.25), 0 4px 16px rgba(234, 88, 12, 0.15);\n}\n\n.f-selected .mj-flow-node-body--warning {\n border-color: #ea580c;\n box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.3), 0 4px 12px rgba(234, 88, 12, 0.15);\n}\n\n.mj-flow-node-body--error {\n border-color: var(--mj-status-error);\n background: var(--mj-status-error-subtle);\n}\n\n.mj-flow-node-body--success {\n border-color: var(--mj-status-success);\n}\n\n/* Color accent bar at top */\n.mj-flow-node-accent {\n height: 3px;\n width: 100%;\n}\n\n/* Header row */\n.mj-flow-node-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px 4px;\n gap: 8px;\n}\n\n.mj-flow-node-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n flex: 1;\n}\n\n.mj-flow-node-header-right {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.mj-flow-node-icon {\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n\n.mj-flow-node-logo {\n width: 18px;\n height: 18px;\n border-radius: var(--mj-radius-sm);\n object-fit: contain;\n flex-shrink: 0;\n}\n\n.mj-flow-node-label {\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.3;\n}\n\n.mj-flow-node-start-badge {\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.5px;\n color: white;\n background: var(--mj-status-success);\n border: none;\n border-radius: var(--mj-radius-sm);\n padding: 2px 6px;\n line-height: 1.4;\n box-shadow: 0 1px 3px rgba(16, 185, 129, 0.3);\n}\n\n/* Status indicator */\n.mj-flow-node-status {\n font-size: var(--mj-text-xs);\n}\n\n.mj-flow-node-status--success { color: var(--mj-status-success); }\n.mj-flow-node-status--error { color: var(--mj-status-error); }\n.mj-flow-node-status--warning { color: #ea580c; }\n.mj-flow-node-status--running { color: var(--mj-brand-primary); }\n.mj-flow-node-status--disabled { color: var(--mj-text-disabled); }\n.mj-flow-node-status--pending { color: var(--mj-status-warning); }\n\n/* Subtitle line */\n.mj-flow-node-subtitle {\n padding: 0 12px 2px;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.4;\n}\n\n/* Type badge */\n.mj-flow-node-type-badge {\n padding: 2px 12px 6px;\n font-size: 10px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n opacity: 0.7;\n}\n\n/* Warning banner \u2014 inline alert on misconfigured nodes */\n.mj-flow-node-warning-banner {\n display: flex;\n align-items: center;\n gap: 5px;\n margin: 2px 8px 6px;\n padding: 4px 8px;\n background: #fff7ed;\n border: 1px solid #fed7aa;\n border-radius: 5px;\n font-size: 10px;\n font-weight: 600;\n color: #c2410c;\n line-height: 1.3;\n}\n\n.mj-flow-node-warning-banner i {\n font-size: 10px;\n flex-shrink: 0;\n color: #ea580c;\n}\n\n.mj-flow-node-warning-banner span {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Badges row */\n.mj-flow-node-badges {\n display: flex;\n gap: 4px;\n padding: 0 10px 8px;\n flex-wrap: wrap;\n}\n\n.mj-flow-node-badge {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-size: 10px;\n color: var(--mj-text-muted);\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n padding: 1px 6px;\n line-height: 1.4;\n}\n\n.mj-flow-node-badge i {\n font-size: 9px;\n}\n\n/* ============================================================\n Loop Node \u2014 distinctive container for ForEach/While\n ============================================================ */\n\n.mj-flow-node-body--loop {\n min-width: 230px;\n max-width: 300px;\n border-width: 2px;\n border-style: solid;\n border-image: none;\n background: color-mix(in srgb, var(--mj-status-warning) 5%, var(--mj-bg-surface));\n}\n\n.mj-flow-node-body--loop:hover {\n box-shadow: 0 4px 14px rgba(245, 158, 11, 0.12), 0 2px 4px rgba(0, 0, 0, 0.06);\n}\n\n.f-selected .mj-flow-node-body--loop {\n border-color: var(--mj-status-warning);\n box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Loop section container */\n.mj-flow-loop-section {\n padding: 0 10px 8px;\n}\n\n/* Iteration summary (e.g., \"over items\" / \"while condition\") */\n.mj-flow-loop-iteration {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 3px 0 6px;\n font-size: 10px;\n font-weight: 500;\n color: #92400e;\n letter-spacing: 0.2px;\n}\n\n.mj-flow-loop-iteration-icon {\n font-size: 9px;\n color: #d97706;\n}\n\n.mj-flow-loop-max {\n color: #b45309;\n opacity: 0.7;\n}\n\n/* Inner body card \u2014 the \"iterated operation\" */\n.mj-flow-loop-body {\n border: 1.5px dashed var(--mj-border-strong);\n border-radius: 7px;\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n}\n\n.mj-flow-loop-body:not(.mj-flow-loop-body--empty) {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.mj-flow-loop-body-inner {\n padding: 6px 10px;\n}\n\n.mj-flow-loop-body-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 2px;\n}\n\n.mj-flow-loop-body-header i {\n font-size: 11px;\n}\n\n.mj-flow-loop-body-type {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.mj-flow-loop-body-name {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-left: 17px;\n line-height: 1.4;\n}\n\n.mj-flow-loop-body-name--empty {\n color: var(--mj-text-disabled);\n font-style: italic;\n font-weight: 400;\n}\n\n/* Empty body card \u2014 no body type selected */\n.mj-flow-loop-body--empty {\n border-color: #fbbf24;\n border-style: dashed;\n background: #fffef5;\n}\n\n.mj-flow-loop-body-empty {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n font-size: 10px;\n font-weight: 500;\n color: #b45309;\n}\n\n.mj-flow-loop-body-empty i {\n font-size: 11px;\n color: #d97706;\n}\n\n/* Item variable badge */\n.mj-flow-loop-variable {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-top: 5px;\n font-size: 9px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n padding: 1px 6px;\n}\n\n.mj-flow-loop-variable i {\n font-size: 8px;\n color: var(--mj-text-disabled);\n}\n\n/* ============================================================\n Ports (Input/Output connectors)\n ============================================================ */\n\n.mj-flow-node-port {\n position: absolute;\n width: 14px;\n height: 14px;\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-text-disabled);\n z-index: 10;\n transition: border-color 0.15s ease, transform 0.15s ease, background-color 0.15s ease;\n cursor: crosshair;\n}\n\n.mj-flow-node-port:hover {\n border-color: var(--mj-brand-primary);\n transform: scale(1.3);\n background: var(--mj-brand-primary-subtle);\n}\n\n/* Position based on side */\n.mj-flow-node-port--top {\n top: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--top:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--bottom {\n bottom: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--bottom:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--left {\n left: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--left:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--right {\n right: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--right:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n/* Disabled port */\n.mj-flow-node-port.f-node-input-disabled,\n.mj-flow-node-port.f-node-output-disabled {\n opacity: 0;\n pointer-events: none;\n}\n\n/* ============================================================\n Animations\n ============================================================ */\n\n@keyframes mj-flow-node-pulse {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.3);\n }\n 50% {\n box-shadow: 0 0 0 6px rgba(59, 130, 246, 0);\n }\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
344
344
|
}
|
|
345
345
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowNodeComponent, [{
|
|
346
346
|
type: Component,
|
|
347
|
-
args: [{ standalone: false, selector: 'mj-flow-node', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Input Port -->\n@if (inputPort) {\n <div\n class=\"mj-flow-node-port mj-flow-node-port--input\"\n [class]=\"'mj-flow-node-port--' + inputPort.Side\"\n fNodeInput\n [fInputId]=\"inputPort.ID\"\n [fInputDisabled]=\"inputPort.Disabled\"\n [fInputMultiple]=\"inputPort.Multiple\">\n </div>\n}\n\n<!-- Node Body -->\n<div class=\"mj-flow-node-body\"\n [class.mj-flow-node-body--start]=\"Node.IsStartNode\"\n [class.mj-flow-node-body--loop]=\"isLoopNode\"\n [class.mj-flow-node-body--disabled]=\"Node.Status === 'disabled'\"\n [class.mj-flow-node-body--pending]=\"Node.Status === 'pending'\"\n [class.mj-flow-node-body--running]=\"Node.Status === 'running'\"\n [class.mj-flow-node-body--warning]=\"Node.Status === 'warning'\"\n [class.mj-flow-node-body--error]=\"Node.Status === 'error'\"\n [class.mj-flow-node-body--success]=\"Node.Status === 'success'\">\n\n <!-- Color accent bar -->\n <div class=\"mj-flow-node-accent\" [style.backgroundColor]=\"headerColor\"></div>\n\n <!-- Header -->\n <div class=\"mj-flow-node-header\">\n <div class=\"mj-flow-node-header-left\">\n @if (logoURL) {\n <img class=\"mj-flow-node-logo\" [src]=\"logoURL\" [alt]=\"Node.Label\" />\n }\n @if (!logoURL) {\n <i class=\"fa-solid mj-flow-node-icon\" [ngClass]=\"nodeIcon\" [style.color]=\"headerColor\"></i>\n }\n <span class=\"mj-flow-node-label\">{{ Node.Label }}</span>\n </div>\n <div class=\"mj-flow-node-header-right\">\n @if (Node.IsStartNode) {\n <span class=\"mj-flow-node-start-badge\">START</span>\n }\n @if (hasStatus) {\n <i\n class=\"fa-solid mj-flow-node-status\"\n [ngClass]=\"statusIcon\"\n [class]=\"'mj-flow-node-status--' + statusClass\"\n [title]=\"Node.StatusMessage || ''\"></i>\n }\n </div>\n </div>\n\n <!-- Subtitle / Configured Item (hidden for loop nodes \u2014 they use the body card instead) -->\n @if (Node.Subtitle && !isLoopNode) {\n <div class=\"mj-flow-node-subtitle\">\n {{ Node.Subtitle }}\n </div>\n }\n\n <!-- Type badge -->\n <div class=\"mj-flow-node-type-badge\" [style.color]=\"headerColor\">\n {{ TypeConfig?.Label || Node.Type }}\n </div>\n\n <!-- \u2550\u2550\u2550 Loop Body Card \u2014 shown only for ForEach/While nodes \u2550\u2550\u2550 -->\n @if (isLoopNode) {\n <div class=\"mj-flow-loop-section\">\n <!-- Iteration summary line -->\n @if (loopIterationSummary) {\n <div class=\"mj-flow-loop-iteration\">\n <i class=\"fa-solid fa-repeat mj-flow-loop-iteration-icon\"></i>\n <span>{{ loopIterationSummary }}</span>\n @if (loopMaxIterations != null) {\n <span class=\"mj-flow-loop-max\">(max {{ loopMaxIterations }})</span>\n }\n </div>\n }\n <!-- Body type inner card -->\n <div class=\"mj-flow-loop-body\"\n [class.mj-flow-loop-body--empty]=\"!loopBodyType\">\n @if (loopBodyType) {\n <div class=\"mj-flow-loop-body-inner\">\n <div class=\"mj-flow-loop-body-header\">\n <i class=\"fa-solid\" [ngClass]=\"loopBodyIcon\" [style.color]=\"loopBodyColor\"></i>\n <span class=\"mj-flow-loop-body-type\" [style.color]=\"loopBodyColor\">{{ loopBodyType }}</span>\n </div>\n @if (loopBodyName) {\n <div class=\"mj-flow-loop-body-name\" [title]=\"loopBodyName\">\n {{ loopBodyName }}\n </div>\n }\n @if (!loopBodyName) {\n <div class=\"mj-flow-loop-body-name mj-flow-loop-body-name--empty\">\n Not configured\n </div>\n }\n </div>\n }\n @if (!loopBodyType) {\n <div class=\"mj-flow-loop-body-empty\">\n <i class=\"fa-solid fa-circle-question\"></i>\n <span>No body type selected</span>\n </div>\n }\n </div>\n <!-- Item variable badge -->\n @if (loopItemVariable) {\n <div class=\"mj-flow-loop-variable\">\n <i class=\"fa-solid fa-tag\"></i>\n <span>{{ loopItemVariable }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Warning banner (shown inline when step is misconfigured) -->\n @if (Node.Status === 'warning' && Node.StatusMessage) {\n <div\n class=\"mj-flow-node-warning-banner\"\n [title]=\"Node.StatusMessage\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ Node.StatusMessage }}</span>\n </div>\n }\n\n <!-- Badges row -->\n @if (Node.Badges && Node.Badges.length > 0) {\n <div class=\"mj-flow-node-badges\">\n @for (badge of Node.Badges; track badge) {\n <span\n class=\"mj-flow-node-badge\"\n [style.borderColor]=\"badge.Color || headerColor\"\n [title]=\"badge.Label\">\n @if (badge.Icon) {\n <i class=\"fa-solid\" [ngClass]=\"badge.Icon\"></i>\n }\n {{ badge.Value }}\n </span>\n }\n </div>\n }\n</div>\n\n<!-- Output Port -->\n@if (outputPort) {\n <div\n class=\"mj-flow-node-port mj-flow-node-port--output\"\n [class]=\"'mj-flow-node-port--' + outputPort.Side\"\n fNodeOutput\n [fOutputId]=\"outputPort.ID\"\n [fOutputDisabled]=\"outputPort.Disabled\"\n [fOutputMultiple]=\"outputPort.Multiple\"\n [isSelfConnectable]=\"false\">\n </div>\n}\n", styles: ["/* ============================================================\n Flow Node \u2014 Visual design for individual flow graph nodes\n ============================================================ */\n\nmj-flow-node {\n display: block;\n position: relative;\n cursor: default;\n transition: filter 0.15s ease, box-shadow 0.15s ease;\n}\n\n.mj-flow-node-body {\n position: relative;\n min-width: 200px;\n max-width: 280px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 10px;\n overflow: hidden;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);\n transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;\n}\n\n.mj-flow-node-body:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);\n border-color: #cbd5e1;\n}\n\n/* Selected state \u2014 applied by Foblex on the host fNode element */\n.f-selected .mj-flow-node-body {\n border-color: #3b82f6;\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Status variants */\n.mj-flow-node-body--start {\n border: 2px solid #10b981;\n background: linear-gradient(135deg, #f0fdf4 0%, #ffffff 40%);\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12), 0 2px 8px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--start:hover {\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18), 0 4px 16px rgba(16, 185, 129, 0.2);\n}\n\n.f-selected .mj-flow-node-body--start {\n border-color: #10b981;\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.3), 0 4px 12px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--disabled {\n opacity: 0.5;\n filter: grayscale(0.5);\n}\n\n.mj-flow-node-body--pending {\n opacity: 0.7;\n border-style: dashed;\n}\n\n.mj-flow-node-body--running {\n border-color: #3b82f6;\n animation: mj-flow-node-pulse 1.5s ease-in-out infinite;\n}\n\n.mj-flow-node-body--warning {\n border: 2px solid #ea580c;\n background: linear-gradient(135deg, #fff7ed 0%, #ffffff 40%);\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.15), 0 2px 8px rgba(234, 88, 12, 0.1);\n}\n\n.mj-flow-node-body--warning:hover {\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.25), 0 4px 16px rgba(234, 88, 12, 0.15);\n}\n\n.f-selected .mj-flow-node-body--warning {\n border-color: #ea580c;\n box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.3), 0 4px 12px rgba(234, 88, 12, 0.15);\n}\n\n.mj-flow-node-body--error {\n border-color: #ef4444;\n background: #fef2f2;\n}\n\n.mj-flow-node-body--success {\n border-color: #10b981;\n}\n\n/* Color accent bar at top */\n.mj-flow-node-accent {\n height: 3px;\n width: 100%;\n}\n\n/* Header row */\n.mj-flow-node-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px 4px;\n gap: 8px;\n}\n\n.mj-flow-node-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n flex: 1;\n}\n\n.mj-flow-node-header-right {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.mj-flow-node-icon {\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.mj-flow-node-logo {\n width: 18px;\n height: 18px;\n border-radius: 3px;\n object-fit: contain;\n flex-shrink: 0;\n}\n\n.mj-flow-node-label {\n font-size: 13px;\n font-weight: 600;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.3;\n}\n\n.mj-flow-node-start-badge {\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.5px;\n color: #ffffff;\n background: linear-gradient(135deg, #10b981, #059669);\n border: none;\n border-radius: 4px;\n padding: 2px 6px;\n line-height: 1.4;\n box-shadow: 0 1px 3px rgba(16, 185, 129, 0.3);\n}\n\n/* Status indicator */\n.mj-flow-node-status {\n font-size: 12px;\n}\n\n.mj-flow-node-status--success { color: #10b981; }\n.mj-flow-node-status--error { color: #ef4444; }\n.mj-flow-node-status--warning { color: #ea580c; }\n.mj-flow-node-status--running { color: #3b82f6; }\n.mj-flow-node-status--disabled { color: #94a3b8; }\n.mj-flow-node-status--pending { color: #f59e0b; }\n\n/* Subtitle line */\n.mj-flow-node-subtitle {\n padding: 0 12px 2px;\n font-size: 12px;\n color: #475569;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.4;\n}\n\n/* Type badge */\n.mj-flow-node-type-badge {\n padding: 2px 12px 6px;\n font-size: 10px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n opacity: 0.7;\n}\n\n/* Warning banner \u2014 inline alert on misconfigured nodes */\n.mj-flow-node-warning-banner {\n display: flex;\n align-items: center;\n gap: 5px;\n margin: 2px 8px 6px;\n padding: 4px 8px;\n background: #fff7ed;\n border: 1px solid #fed7aa;\n border-radius: 5px;\n font-size: 10px;\n font-weight: 600;\n color: #c2410c;\n line-height: 1.3;\n}\n\n.mj-flow-node-warning-banner i {\n font-size: 10px;\n flex-shrink: 0;\n color: #ea580c;\n}\n\n.mj-flow-node-warning-banner span {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Badges row */\n.mj-flow-node-badges {\n display: flex;\n gap: 4px;\n padding: 0 10px 8px;\n flex-wrap: wrap;\n}\n\n.mj-flow-node-badge {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-size: 10px;\n color: #64748b;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n padding: 1px 6px;\n line-height: 1.4;\n}\n\n.mj-flow-node-badge i {\n font-size: 9px;\n}\n\n/* ============================================================\n Loop Node \u2014 distinctive container for ForEach/While\n ============================================================ */\n\n.mj-flow-node-body--loop {\n min-width: 230px;\n max-width: 300px;\n border-width: 2px;\n border-style: solid;\n border-image: none;\n background: linear-gradient(135deg, #fffbeb 0%, #ffffff 30%);\n}\n\n.mj-flow-node-body--loop:hover {\n box-shadow: 0 4px 14px rgba(245, 158, 11, 0.12), 0 2px 4px rgba(0, 0, 0, 0.06);\n}\n\n.f-selected .mj-flow-node-body--loop {\n border-color: #f59e0b;\n box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Loop section container */\n.mj-flow-loop-section {\n padding: 0 10px 8px;\n}\n\n/* Iteration summary (e.g., \"over items\" / \"while condition\") */\n.mj-flow-loop-iteration {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 3px 0 6px;\n font-size: 10px;\n font-weight: 500;\n color: #92400e;\n letter-spacing: 0.2px;\n}\n\n.mj-flow-loop-iteration-icon {\n font-size: 9px;\n color: #d97706;\n}\n\n.mj-flow-loop-max {\n color: #b45309;\n opacity: 0.7;\n}\n\n/* Inner body card \u2014 the \"iterated operation\" */\n.mj-flow-loop-body {\n border: 1.5px dashed #d1d5db;\n border-radius: 7px;\n background: #ffffff;\n transition: border-color 0.15s ease;\n}\n\n.mj-flow-loop-body:not(.mj-flow-loop-body--empty) {\n border-color: #e5e7eb;\n background: linear-gradient(180deg, #fafafa 0%, #ffffff 100%);\n}\n\n.mj-flow-loop-body-inner {\n padding: 6px 10px;\n}\n\n.mj-flow-loop-body-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 2px;\n}\n\n.mj-flow-loop-body-header i {\n font-size: 11px;\n}\n\n.mj-flow-loop-body-type {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.mj-flow-loop-body-name {\n font-size: 11px;\n font-weight: 500;\n color: #374151;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-left: 17px;\n line-height: 1.4;\n}\n\n.mj-flow-loop-body-name--empty {\n color: #9ca3af;\n font-style: italic;\n font-weight: 400;\n}\n\n/* Empty body card \u2014 no body type selected */\n.mj-flow-loop-body--empty {\n border-color: #fbbf24;\n border-style: dashed;\n background: #fffef5;\n}\n\n.mj-flow-loop-body-empty {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n font-size: 10px;\n font-weight: 500;\n color: #b45309;\n}\n\n.mj-flow-loop-body-empty i {\n font-size: 11px;\n color: #d97706;\n}\n\n/* Item variable badge */\n.mj-flow-loop-variable {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-top: 5px;\n font-size: 9px;\n font-weight: 500;\n color: #6b7280;\n background: #f3f4f6;\n border: 1px solid #e5e7eb;\n border-radius: 3px;\n padding: 1px 6px;\n}\n\n.mj-flow-loop-variable i {\n font-size: 8px;\n color: #9ca3af;\n}\n\n/* ============================================================\n Ports (Input/Output connectors)\n ============================================================ */\n\n.mj-flow-node-port {\n position: absolute;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #ffffff;\n border: 2px solid #94a3b8;\n z-index: 10;\n transition: border-color 0.15s ease, transform 0.15s ease, background-color 0.15s ease;\n cursor: crosshair;\n}\n\n.mj-flow-node-port:hover {\n border-color: #3b82f6;\n transform: scale(1.3);\n background: #eff6ff;\n}\n\n/* Position based on side */\n.mj-flow-node-port--top {\n top: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--top:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--bottom {\n bottom: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--bottom:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--left {\n left: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--left:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--right {\n right: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--right:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n/* Disabled port */\n.mj-flow-node-port.f-node-input-disabled,\n.mj-flow-node-port.f-node-output-disabled {\n opacity: 0;\n pointer-events: none;\n}\n\n/* ============================================================\n Animations\n ============================================================ */\n\n@keyframes mj-flow-node-pulse {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.3);\n }\n 50% {\n box-shadow: 0 0 0 6px rgba(59, 130, 246, 0);\n }\n}\n"] }]
|
|
347
|
+
args: [{ standalone: false, selector: 'mj-flow-node', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Input Port -->\n@if (inputPort) {\n <div\n class=\"mj-flow-node-port mj-flow-node-port--input\"\n [class]=\"'mj-flow-node-port--' + inputPort.Side\"\n fNodeInput\n [fInputId]=\"inputPort.ID\"\n [fInputDisabled]=\"inputPort.Disabled\"\n [fInputMultiple]=\"inputPort.Multiple\">\n </div>\n}\n\n<!-- Node Body -->\n<div class=\"mj-flow-node-body\"\n [class.mj-flow-node-body--start]=\"Node.IsStartNode\"\n [class.mj-flow-node-body--loop]=\"isLoopNode\"\n [class.mj-flow-node-body--disabled]=\"Node.Status === 'disabled'\"\n [class.mj-flow-node-body--pending]=\"Node.Status === 'pending'\"\n [class.mj-flow-node-body--running]=\"Node.Status === 'running'\"\n [class.mj-flow-node-body--warning]=\"Node.Status === 'warning'\"\n [class.mj-flow-node-body--error]=\"Node.Status === 'error'\"\n [class.mj-flow-node-body--success]=\"Node.Status === 'success'\">\n\n <!-- Color accent bar -->\n <div class=\"mj-flow-node-accent\" [style.backgroundColor]=\"headerColor\"></div>\n\n <!-- Header -->\n <div class=\"mj-flow-node-header\">\n <div class=\"mj-flow-node-header-left\">\n @if (logoURL) {\n <img class=\"mj-flow-node-logo\" [src]=\"logoURL\" [alt]=\"Node.Label\" />\n }\n @if (!logoURL) {\n <i class=\"fa-solid mj-flow-node-icon\" [ngClass]=\"nodeIcon\" [style.color]=\"headerColor\"></i>\n }\n <span class=\"mj-flow-node-label\">{{ Node.Label }}</span>\n </div>\n <div class=\"mj-flow-node-header-right\">\n @if (Node.IsStartNode) {\n <span class=\"mj-flow-node-start-badge\">START</span>\n }\n @if (hasStatus) {\n <i\n class=\"fa-solid mj-flow-node-status\"\n [ngClass]=\"statusIcon\"\n [class]=\"'mj-flow-node-status--' + statusClass\"\n [title]=\"Node.StatusMessage || ''\"></i>\n }\n </div>\n </div>\n\n <!-- Subtitle / Configured Item (hidden for loop nodes \u2014 they use the body card instead) -->\n @if (Node.Subtitle && !isLoopNode) {\n <div class=\"mj-flow-node-subtitle\">\n {{ Node.Subtitle }}\n </div>\n }\n\n <!-- Type badge -->\n <div class=\"mj-flow-node-type-badge\" [style.color]=\"headerColor\">\n {{ TypeConfig?.Label || Node.Type }}\n </div>\n\n <!-- \u2550\u2550\u2550 Loop Body Card \u2014 shown only for ForEach/While nodes \u2550\u2550\u2550 -->\n @if (isLoopNode) {\n <div class=\"mj-flow-loop-section\">\n <!-- Iteration summary line -->\n @if (loopIterationSummary) {\n <div class=\"mj-flow-loop-iteration\">\n <i class=\"fa-solid fa-repeat mj-flow-loop-iteration-icon\"></i>\n <span>{{ loopIterationSummary }}</span>\n @if (loopMaxIterations != null) {\n <span class=\"mj-flow-loop-max\">(max {{ loopMaxIterations }})</span>\n }\n </div>\n }\n <!-- Body type inner card -->\n <div class=\"mj-flow-loop-body\"\n [class.mj-flow-loop-body--empty]=\"!loopBodyType\">\n @if (loopBodyType) {\n <div class=\"mj-flow-loop-body-inner\">\n <div class=\"mj-flow-loop-body-header\">\n <i class=\"fa-solid\" [ngClass]=\"loopBodyIcon\" [style.color]=\"loopBodyColor\"></i>\n <span class=\"mj-flow-loop-body-type\" [style.color]=\"loopBodyColor\">{{ loopBodyType }}</span>\n </div>\n @if (loopBodyName) {\n <div class=\"mj-flow-loop-body-name\" [title]=\"loopBodyName\">\n {{ loopBodyName }}\n </div>\n }\n @if (!loopBodyName) {\n <div class=\"mj-flow-loop-body-name mj-flow-loop-body-name--empty\">\n Not configured\n </div>\n }\n </div>\n }\n @if (!loopBodyType) {\n <div class=\"mj-flow-loop-body-empty\">\n <i class=\"fa-solid fa-circle-question\"></i>\n <span>No body type selected</span>\n </div>\n }\n </div>\n <!-- Item variable badge -->\n @if (loopItemVariable) {\n <div class=\"mj-flow-loop-variable\">\n <i class=\"fa-solid fa-tag\"></i>\n <span>{{ loopItemVariable }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Warning banner (shown inline when step is misconfigured) -->\n @if (Node.Status === 'warning' && Node.StatusMessage) {\n <div\n class=\"mj-flow-node-warning-banner\"\n [title]=\"Node.StatusMessage\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ Node.StatusMessage }}</span>\n </div>\n }\n\n <!-- Badges row -->\n @if (Node.Badges && Node.Badges.length > 0) {\n <div class=\"mj-flow-node-badges\">\n @for (badge of Node.Badges; track badge) {\n <span\n class=\"mj-flow-node-badge\"\n [style.borderColor]=\"badge.Color || headerColor\"\n [title]=\"badge.Label\">\n @if (badge.Icon) {\n <i class=\"fa-solid\" [ngClass]=\"badge.Icon\"></i>\n }\n {{ badge.Value }}\n </span>\n }\n </div>\n }\n</div>\n\n<!-- Output Port -->\n@if (outputPort) {\n <div\n class=\"mj-flow-node-port mj-flow-node-port--output\"\n [class]=\"'mj-flow-node-port--' + outputPort.Side\"\n fNodeOutput\n [fOutputId]=\"outputPort.ID\"\n [fOutputDisabled]=\"outputPort.Disabled\"\n [fOutputMultiple]=\"outputPort.Multiple\"\n [isSelfConnectable]=\"false\">\n </div>\n}\n", styles: ["/* ============================================================\n Flow Node \u2014 Visual design for individual flow graph nodes\n ============================================================ */\n\nmj-flow-node {\n display: block;\n position: relative;\n cursor: default;\n transition: filter 0.15s ease, box-shadow 0.15s ease;\n}\n\n.mj-flow-node-body {\n position: relative;\n min-width: 200px;\n max-width: 280px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n box-shadow: var(--mj-shadow-sm);\n transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;\n}\n\n.mj-flow-node-body:hover {\n box-shadow: var(--mj-shadow-md);\n border-color: var(--mj-border-strong);\n}\n\n/* Selected state \u2014 applied by Foblex on the host fNode element */\n.f-selected .mj-flow-node-body {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Status variants */\n.mj-flow-node-body--start {\n border: 2px solid var(--mj-status-success);\n background: color-mix(in srgb, var(--mj-status-success) 5%, var(--mj-bg-surface));\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12), 0 2px 8px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--start:hover {\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18), 0 4px 16px rgba(16, 185, 129, 0.2);\n}\n\n.f-selected .mj-flow-node-body--start {\n border-color: var(--mj-status-success);\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.3), 0 4px 12px rgba(16, 185, 129, 0.15);\n}\n\n.mj-flow-node-body--disabled {\n opacity: 0.5;\n filter: grayscale(0.5);\n}\n\n.mj-flow-node-body--pending {\n opacity: 0.7;\n border-style: dashed;\n}\n\n.mj-flow-node-body--running {\n border-color: var(--mj-brand-primary);\n animation: mj-flow-node-pulse 1.5s ease-in-out infinite;\n}\n\n.mj-flow-node-body--warning {\n border: 2px solid #ea580c;\n background: color-mix(in srgb, var(--mj-status-warning) 5%, var(--mj-bg-surface));\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.15), 0 2px 8px rgba(234, 88, 12, 0.1);\n}\n\n.mj-flow-node-body--warning:hover {\n box-shadow: 0 0 0 2px rgba(234, 88, 12, 0.25), 0 4px 16px rgba(234, 88, 12, 0.15);\n}\n\n.f-selected .mj-flow-node-body--warning {\n border-color: #ea580c;\n box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.3), 0 4px 12px rgba(234, 88, 12, 0.15);\n}\n\n.mj-flow-node-body--error {\n border-color: var(--mj-status-error);\n background: var(--mj-status-error-subtle);\n}\n\n.mj-flow-node-body--success {\n border-color: var(--mj-status-success);\n}\n\n/* Color accent bar at top */\n.mj-flow-node-accent {\n height: 3px;\n width: 100%;\n}\n\n/* Header row */\n.mj-flow-node-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px 4px;\n gap: 8px;\n}\n\n.mj-flow-node-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n flex: 1;\n}\n\n.mj-flow-node-header-right {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.mj-flow-node-icon {\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n\n.mj-flow-node-logo {\n width: 18px;\n height: 18px;\n border-radius: var(--mj-radius-sm);\n object-fit: contain;\n flex-shrink: 0;\n}\n\n.mj-flow-node-label {\n font-size: var(--mj-text-sm);\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.3;\n}\n\n.mj-flow-node-start-badge {\n font-size: 9px;\n font-weight: 700;\n letter-spacing: 0.5px;\n color: white;\n background: var(--mj-status-success);\n border: none;\n border-radius: var(--mj-radius-sm);\n padding: 2px 6px;\n line-height: 1.4;\n box-shadow: 0 1px 3px rgba(16, 185, 129, 0.3);\n}\n\n/* Status indicator */\n.mj-flow-node-status {\n font-size: var(--mj-text-xs);\n}\n\n.mj-flow-node-status--success { color: var(--mj-status-success); }\n.mj-flow-node-status--error { color: var(--mj-status-error); }\n.mj-flow-node-status--warning { color: #ea580c; }\n.mj-flow-node-status--running { color: var(--mj-brand-primary); }\n.mj-flow-node-status--disabled { color: var(--mj-text-disabled); }\n.mj-flow-node-status--pending { color: var(--mj-status-warning); }\n\n/* Subtitle line */\n.mj-flow-node-subtitle {\n padding: 0 12px 2px;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n line-height: 1.4;\n}\n\n/* Type badge */\n.mj-flow-node-type-badge {\n padding: 2px 12px 6px;\n font-size: 10px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n opacity: 0.7;\n}\n\n/* Warning banner \u2014 inline alert on misconfigured nodes */\n.mj-flow-node-warning-banner {\n display: flex;\n align-items: center;\n gap: 5px;\n margin: 2px 8px 6px;\n padding: 4px 8px;\n background: #fff7ed;\n border: 1px solid #fed7aa;\n border-radius: 5px;\n font-size: 10px;\n font-weight: 600;\n color: #c2410c;\n line-height: 1.3;\n}\n\n.mj-flow-node-warning-banner i {\n font-size: 10px;\n flex-shrink: 0;\n color: #ea580c;\n}\n\n.mj-flow-node-warning-banner span {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Badges row */\n.mj-flow-node-badges {\n display: flex;\n gap: 4px;\n padding: 0 10px 8px;\n flex-wrap: wrap;\n}\n\n.mj-flow-node-badge {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-size: 10px;\n color: var(--mj-text-muted);\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n padding: 1px 6px;\n line-height: 1.4;\n}\n\n.mj-flow-node-badge i {\n font-size: 9px;\n}\n\n/* ============================================================\n Loop Node \u2014 distinctive container for ForEach/While\n ============================================================ */\n\n.mj-flow-node-body--loop {\n min-width: 230px;\n max-width: 300px;\n border-width: 2px;\n border-style: solid;\n border-image: none;\n background: color-mix(in srgb, var(--mj-status-warning) 5%, var(--mj-bg-surface));\n}\n\n.mj-flow-node-body--loop:hover {\n box-shadow: 0 4px 14px rgba(245, 158, 11, 0.12), 0 2px 4px rgba(0, 0, 0, 0.06);\n}\n\n.f-selected .mj-flow-node-body--loop {\n border-color: var(--mj-status-warning);\n box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.25), 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n/* Loop section container */\n.mj-flow-loop-section {\n padding: 0 10px 8px;\n}\n\n/* Iteration summary (e.g., \"over items\" / \"while condition\") */\n.mj-flow-loop-iteration {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 3px 0 6px;\n font-size: 10px;\n font-weight: 500;\n color: #92400e;\n letter-spacing: 0.2px;\n}\n\n.mj-flow-loop-iteration-icon {\n font-size: 9px;\n color: #d97706;\n}\n\n.mj-flow-loop-max {\n color: #b45309;\n opacity: 0.7;\n}\n\n/* Inner body card \u2014 the \"iterated operation\" */\n.mj-flow-loop-body {\n border: 1.5px dashed var(--mj-border-strong);\n border-radius: 7px;\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease;\n}\n\n.mj-flow-loop-body:not(.mj-flow-loop-body--empty) {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.mj-flow-loop-body-inner {\n padding: 6px 10px;\n}\n\n.mj-flow-loop-body-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 2px;\n}\n\n.mj-flow-loop-body-header i {\n font-size: 11px;\n}\n\n.mj-flow-loop-body-type {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.mj-flow-loop-body-name {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-left: 17px;\n line-height: 1.4;\n}\n\n.mj-flow-loop-body-name--empty {\n color: var(--mj-text-disabled);\n font-style: italic;\n font-weight: 400;\n}\n\n/* Empty body card \u2014 no body type selected */\n.mj-flow-loop-body--empty {\n border-color: #fbbf24;\n border-style: dashed;\n background: #fffef5;\n}\n\n.mj-flow-loop-body-empty {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n font-size: 10px;\n font-weight: 500;\n color: #b45309;\n}\n\n.mj-flow-loop-body-empty i {\n font-size: 11px;\n color: #d97706;\n}\n\n/* Item variable badge */\n.mj-flow-loop-variable {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-top: 5px;\n font-size: 9px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n padding: 1px 6px;\n}\n\n.mj-flow-loop-variable i {\n font-size: 8px;\n color: var(--mj-text-disabled);\n}\n\n/* ============================================================\n Ports (Input/Output connectors)\n ============================================================ */\n\n.mj-flow-node-port {\n position: absolute;\n width: 14px;\n height: 14px;\n border-radius: var(--mj-radius-full);\n background: var(--mj-bg-surface);\n border: 2px solid var(--mj-text-disabled);\n z-index: 10;\n transition: border-color 0.15s ease, transform 0.15s ease, background-color 0.15s ease;\n cursor: crosshair;\n}\n\n.mj-flow-node-port:hover {\n border-color: var(--mj-brand-primary);\n transform: scale(1.3);\n background: var(--mj-brand-primary-subtle);\n}\n\n/* Position based on side */\n.mj-flow-node-port--top {\n top: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--top:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--bottom {\n bottom: -7px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.mj-flow-node-port--bottom:hover {\n transform: translateX(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--left {\n left: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--left:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n.mj-flow-node-port--right {\n right: -7px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n.mj-flow-node-port--right:hover {\n transform: translateY(-50%) scale(1.3);\n}\n\n/* Disabled port */\n.mj-flow-node-port.f-node-input-disabled,\n.mj-flow-node-port.f-node-output-disabled {\n opacity: 0;\n pointer-events: none;\n}\n\n/* ============================================================\n Animations\n ============================================================ */\n\n@keyframes mj-flow-node-pulse {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.3);\n }\n 50% {\n box-shadow: 0 0 0 6px rgba(59, 130, 246, 0);\n }\n}\n"] }]
|
|
348
348
|
}], null, { Node: [{
|
|
349
349
|
type: Input
|
|
350
350
|
}], TypeConfig: [{
|
|
@@ -96,11 +96,11 @@ export class FlowPaletteComponent {
|
|
|
96
96
|
i0.ɵɵconditional(!ctx.Collapsed ? 3 : -1);
|
|
97
97
|
i0.ɵɵadvance();
|
|
98
98
|
i0.ɵɵconditional(!ctx.Collapsed ? 4 : -1);
|
|
99
|
-
} }, dependencies: [i1.NgClass, i2.FExternalItemDirective], styles: ["/* ============================================================\n Flow Palette \u2014 Draggable node type sidebar\n ============================================================ */\n\n.mj-flow-palette {\n width: 180px;\n background:
|
|
99
|
+
} }, dependencies: [i1.NgClass, i2.FExternalItemDirective], styles: ["/* ============================================================\n Flow Palette \u2014 Draggable node type sidebar\n ============================================================ */\n\n.mj-flow-palette {\n width: 180px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n flex-shrink: 0;\n transition: width 0.2s ease;\n}\n\n.mj-flow-palette--collapsed {\n width: 44px;\n}\n\n.mj-flow-palette--collapsed .mj-flow-palette-header {\n justify-content: center;\n padding: 10px 0;\n}\n\n.mj-flow-palette-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 14px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-palette-header i {\n font-size: var(--mj-text-sm);\n}\n\n.mj-flow-palette-content {\n padding: 8px;\n flex: 1;\n}\n\n.mj-flow-palette-category {\n margin-bottom: 12px;\n}\n\n.mj-flow-palette-category-label {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding: 4px 6px;\n margin-bottom: 4px;\n}\n\n.mj-flow-palette-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border-radius: var(--mj-radius-md);\n cursor: grab;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n}\n\n.mj-flow-palette-item:hover {\n background: var(--mj-bg-page);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-flow-palette-item:active {\n cursor: grabbing;\n background: var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-palette-item-icon {\n width: 32px;\n height: 32px;\n border-radius: var(--mj-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n\n.mj-flow-palette-item-label {\n font-size: var(--mj-text-sm);\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
100
100
|
}
|
|
101
101
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowPaletteComponent, [{
|
|
102
102
|
type: Component,
|
|
103
|
-
args: [{ standalone: false, selector: 'mj-flow-palette', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mj-flow-palette\" [class.mj-flow-palette--collapsed]=\"Collapsed\">\n <div class=\"mj-flow-palette-header\">\n <i class=\"fa-solid fa-puzzle-piece\"></i>\n @if (!Collapsed) {\n <span>Steps</span>\n }\n </div>\n\n @if (!Collapsed) {\n <div class=\"mj-flow-palette-content\">\n @for (category of categories; track category) {\n <div class=\"mj-flow-palette-category\">\n <div class=\"mj-flow-palette-category-label\">{{ category }}</div>\n @for (nodeType of GetTypesForCategory(category); track nodeType) {\n <div\n class=\"mj-flow-palette-item\"\n fExternalItem\n [fData]=\"nodeType.Type\"\n [title]=\"nodeType.Label\">\n <div class=\"mj-flow-palette-item-icon\" [style.backgroundColor]=\"GetBgColor(nodeType.Color, 0.08)\" [style.color]=\"nodeType.Color\">\n <i class=\"fa-solid\" [ngClass]=\"nodeType.Icon\"></i>\n </div>\n <span class=\"mj-flow-palette-item-label\">{{ nodeType.Label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Palette \u2014 Draggable node type sidebar\n ============================================================ */\n\n.mj-flow-palette {\n width: 180px;\n background:
|
|
103
|
+
args: [{ standalone: false, selector: 'mj-flow-palette', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mj-flow-palette\" [class.mj-flow-palette--collapsed]=\"Collapsed\">\n <div class=\"mj-flow-palette-header\">\n <i class=\"fa-solid fa-puzzle-piece\"></i>\n @if (!Collapsed) {\n <span>Steps</span>\n }\n </div>\n\n @if (!Collapsed) {\n <div class=\"mj-flow-palette-content\">\n @for (category of categories; track category) {\n <div class=\"mj-flow-palette-category\">\n <div class=\"mj-flow-palette-category-label\">{{ category }}</div>\n @for (nodeType of GetTypesForCategory(category); track nodeType) {\n <div\n class=\"mj-flow-palette-item\"\n fExternalItem\n [fData]=\"nodeType.Type\"\n [title]=\"nodeType.Label\">\n <div class=\"mj-flow-palette-item-icon\" [style.backgroundColor]=\"GetBgColor(nodeType.Color, 0.08)\" [style.color]=\"nodeType.Color\">\n <i class=\"fa-solid\" [ngClass]=\"nodeType.Icon\"></i>\n </div>\n <span class=\"mj-flow-palette-item-label\">{{ nodeType.Label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Flow Palette \u2014 Draggable node type sidebar\n ============================================================ */\n\n.mj-flow-palette {\n width: 180px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n flex-shrink: 0;\n transition: width 0.2s ease;\n}\n\n.mj-flow-palette--collapsed {\n width: 44px;\n}\n\n.mj-flow-palette--collapsed .mj-flow-palette-header {\n justify-content: center;\n padding: 10px 0;\n}\n\n.mj-flow-palette-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 14px;\n font-size: var(--mj-text-xs);\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-palette-header i {\n font-size: var(--mj-text-sm);\n}\n\n.mj-flow-palette-content {\n padding: 8px;\n flex: 1;\n}\n\n.mj-flow-palette-category {\n margin-bottom: 12px;\n}\n\n.mj-flow-palette-category-label {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding: 4px 6px;\n margin-bottom: 4px;\n}\n\n.mj-flow-palette-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border-radius: var(--mj-radius-md);\n cursor: grab;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n user-select: none;\n}\n\n.mj-flow-palette-item:hover {\n background: var(--mj-bg-page);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-flow-palette-item:active {\n cursor: grabbing;\n background: var(--mj-bg-surface-sunken);\n}\n\n.mj-flow-palette-item-icon {\n width: 32px;\n height: 32px;\n border-radius: var(--mj-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--mj-text-sm);\n flex-shrink: 0;\n}\n\n.mj-flow-palette-item-label {\n font-size: var(--mj-text-sm);\n font-weight: 500;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n"] }]
|
|
104
104
|
}], null, { NodeTypes: [{
|
|
105
105
|
type: Input
|
|
106
106
|
}], Collapsed: [{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-flow-editor",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.0",
|
|
4
4
|
"description": "MemberJunction: Generic visual flow editor component powered by Foblex Flow, with an agent-specific Flow Agent Editor",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"@foblex/mediator": "1.1.3",
|
|
43
43
|
"@foblex/platform": "1.0.4",
|
|
44
44
|
"@foblex/utils": "1.1.1",
|
|
45
|
-
"@memberjunction/core": "5.
|
|
46
|
-
"@memberjunction/core-entities": "5.
|
|
47
|
-
"@memberjunction/global": "5.
|
|
48
|
-
"@memberjunction/ng-code-editor": "5.
|
|
45
|
+
"@memberjunction/core": "5.13.0",
|
|
46
|
+
"@memberjunction/core-entities": "5.13.0",
|
|
47
|
+
"@memberjunction/global": "5.13.0",
|
|
48
|
+
"@memberjunction/ng-code-editor": "5.13.0",
|
|
49
49
|
"rxjs": "^7.8.2",
|
|
50
50
|
"tslib": "^2.8.1"
|
|
51
51
|
},
|